diff --git a/go/vt/vttablet/endtoend/main_test.go b/go/vt/vttablet/endtoend/main_test.go index b2b50e8fc6a..b17935e443a 100644 --- a/go/vt/vttablet/endtoend/main_test.go +++ b/go/vt/vttablet/endtoend/main_test.go @@ -139,6 +139,9 @@ create table vitess_bool(auto int auto_increment, bval tinyint(1) default 0, sva create table vitess_seq(id int default 0, next_id bigint default null, cache bigint default null, increment bigint default null, primary key(id)) comment 'vitess_sequence'; insert into vitess_seq(id, next_id, cache) values(0, 1, 3); +create table vitess_reset_seq(id int default 0, next_id bigint default null, cache bigint default null, increment bigint default null, primary key(id)) comment 'vitess_sequence'; +insert into vitess_reset_seq(id, next_id, cache) values(0, 1, 3); + create table vitess_part(id int, data varchar(16), primary key(id)); alter table vitess_part partition by range (id) (partition p0 values less than (10), partition p1 values less than (maxvalue)); @@ -200,6 +203,13 @@ var tableACLConfig = `{ "writers": ["dev"], "admins": ["dev"] }, + { + "name": "vitess_reset_seq", + "table_names_or_prefixes": ["vitess_reset_seq"], + "readers": ["dev"], + "writers": ["dev"], + "admins": ["dev"] + }, { "name": "vitess_message", "table_names_or_prefixes": ["vitess_message"], diff --git a/go/vt/vttablet/endtoend/sequence_test.go b/go/vt/vttablet/endtoend/sequence_test.go index a481f828848..4ca830134ca 100644 --- a/go/vt/vttablet/endtoend/sequence_test.go +++ b/go/vt/vttablet/endtoend/sequence_test.go @@ -24,6 +24,7 @@ import ( "github.com/youtube/vitess/go/vt/vttablet/endtoend/framework" querypb "github.com/youtube/vitess/go/vt/proto/query" + topodatapb "github.com/youtube/vitess/go/vt/proto/topodata" ) func TestSequence(t *testing.T) { @@ -41,8 +42,7 @@ func TestSequence(t *testing.T) { want.Rows[0][0] = sqltypes.NewInt64(wantval) qr, err := framework.NewClient().Execute("select next 2 values from vitess_seq", nil) if err != nil { - t.Error(err) - return + t.Fatal(err) } if !reflect.DeepEqual(*qr, want) { t.Errorf("Execute: \n%#v, want \n%#v", *qr, want) @@ -58,11 +58,51 @@ func TestSequence(t *testing.T) { } qr, err := framework.NewClient().Execute("select next_id, cache from vitess_seq", nil) if err != nil { - t.Error(err) - return + t.Fatal(err) } qr.Fields = nil if !reflect.DeepEqual(*qr, want) { t.Errorf("Execute: \n%#v, want \n%#v", *qr, want) } } + +func TestResetSequence(t *testing.T) { + client := framework.NewClient() + want := sqltypes.Result{ + Fields: []*querypb.Field{{ + Name: "nextval", + Type: sqltypes.Int64, + }}, + RowsAffected: 1, + Rows: [][]sqltypes.Value{{ + sqltypes.NewInt64(1), + }}, + } + qr, err := client.Execute("select next value from vitess_reset_seq", nil) + if err != nil { + t.Fatal(err) + } + if !reflect.DeepEqual(*qr, want) { + t.Errorf("Execute: \n%#v, want \n%#v", *qr, want) + } + + // Reset mastership + err = client.SetServingType(topodatapb.TabletType_REPLICA) + if err != nil { + t.Fatal(err) + } + err = client.SetServingType(topodatapb.TabletType_MASTER) + if err != nil { + t.Fatal(err) + } + + // Ensure the next value skips previously cached values. + want.Rows[0][0] = sqltypes.NewInt64(4) + qr, err = client.Execute("select next value from vitess_reset_seq", nil) + if err != nil { + t.Fatal(err) + } + if !reflect.DeepEqual(*qr, want) { + t.Errorf("Execute: \n%#v, want \n%#v", *qr, want) + } +} diff --git a/go/vt/vttablet/tabletserver/schema/engine.go b/go/vt/vttablet/tabletserver/schema/engine.go index 91999cfab7f..91ef405e462 100644 --- a/go/vt/vttablet/tabletserver/schema/engine.go +++ b/go/vt/vttablet/tabletserver/schema/engine.go @@ -189,6 +189,22 @@ func (se *Engine) Close() { se.isOpen = false } +// MakeNonMaster clears the sequence caches to make sure that +// they don't get accidentally reused after losing mastership. +func (se *Engine) MakeNonMaster() { + // This function is tested through endtoend test. + se.mu.Lock() + defer se.mu.Unlock() + for _, t := range se.tables { + if t.SequenceInfo != nil { + t.SequenceInfo.Lock() + t.SequenceInfo.NextVal = 0 + t.SequenceInfo.LastVal = 0 + t.SequenceInfo.Unlock() + } + } +} + // Reload reloads the schema info from the db. // Any tables that have changed since the last load are updated. // This is a no-op if the Engine is closed. diff --git a/go/vt/vttablet/tabletserver/tabletserver.go b/go/vt/vttablet/tabletserver/tabletserver.go index 25b884b23ca..57e63e96d3a 100644 --- a/go/vt/vttablet/tabletserver/tabletserver.go +++ b/go/vt/vttablet/tabletserver/tabletserver.go @@ -466,6 +466,9 @@ func (tsv *TabletServer) serveNewType() (err error) { tsv.te.Close(true) tsv.watcher.Open(tsv.dbconfigs, tsv.mysqld) tsv.txThrottler.Close() + + // Reset the sequences. + tsv.se.MakeNonMaster() } tsv.transition(StateServing) return nil