diff --git a/go/vt/vttablet/endtoend/sequence_test.go b/go/vt/vttablet/endtoend/sequence_test.go index f779aaf47f1..6a415ea4a04 100644 --- a/go/vt/vttablet/endtoend/sequence_test.go +++ b/go/vt/vttablet/endtoend/sequence_test.go @@ -20,6 +20,8 @@ import ( "reflect" "testing" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "vitess.io/vitess/go/sqltypes" "vitess.io/vitess/go/vt/vttablet/endtoend/framework" @@ -28,7 +30,7 @@ import ( ) func TestSequence(t *testing.T) { - want := sqltypes.Result{ + want := &sqltypes.Result{ Fields: []*querypb.Field{{ Name: "nextval", Type: sqltypes.Int64, @@ -41,29 +43,69 @@ func TestSequence(t *testing.T) { for wantval := int64(1); wantval < 10; wantval += 2 { want.Rows[0][0] = sqltypes.NewInt64(wantval) qr, err := framework.NewClient().Execute("select next 2 values from vitess_seq", nil) - if err != nil { - t.Fatal(err) - } - if !reflect.DeepEqual(*qr, want) { - t.Errorf("Execute: \n%#v, want \n%#v", *qr, want) - } + require.NoError(t, err) + assert.Equal(t, want, qr) } + // Verify that the table got updated according to chunk size. - want = sqltypes.Result{ + qr, err := framework.NewClient().Execute("select next_id, cache from vitess_seq", nil) + require.NoError(t, err) + qr.Fields = nil + + want = &sqltypes.Result{ RowsAffected: 1, Rows: [][]sqltypes.Value{{ sqltypes.NewInt64(13), sqltypes.NewInt64(3), }}, } - qr, err := framework.NewClient().Execute("select next_id, cache from vitess_seq", nil) - if err != nil { - t.Fatal(err) + assert.Equal(t, want, qr) + + // Mess up the sequence by reducing next_id + _, err = framework.NewClient().Execute("update vitess_seq set next_id=1", nil) + require.NoError(t, err) + qr, err = framework.NewClient().Execute("select next 3 values from vitess_seq", nil) + require.NoError(t, err) + qr.Fields = nil + + // Next value generated should be based on the LastVal + want = &sqltypes.Result{ + RowsAffected: 1, + Rows: [][]sqltypes.Value{{ + sqltypes.NewInt64(13), + }}, } + assert.Equal(t, want, qr) + + // next_id should be reset to LastVal+cache + qr, err = framework.NewClient().Execute("select next_id, cache from vitess_seq", nil) + require.NoError(t, err) qr.Fields = nil - if !reflect.DeepEqual(*qr, want) { - t.Errorf("Execute: \n%#v, want \n%#v", *qr, want) + + want = &sqltypes.Result{ + RowsAffected: 1, + Rows: [][]sqltypes.Value{{ + sqltypes.NewInt64(16), + sqltypes.NewInt64(3), + }}, + } + assert.Equal(t, want, qr) + + // Change next_id to a very high value + _, err = framework.NewClient().Execute("update vitess_seq set next_id=100", nil) + require.NoError(t, err) + qr, err = framework.NewClient().Execute("select next 3 values from vitess_seq", nil) + require.NoError(t, err) + qr.Fields = nil + + // Next value should jump to the high value + want = &sqltypes.Result{ + RowsAffected: 1, + Rows: [][]sqltypes.Value{{ + sqltypes.NewInt64(100), + }}, } + assert.Equal(t, want, qr) } func TestResetSequence(t *testing.T) { diff --git a/go/vt/vttablet/tabletserver/query_executor.go b/go/vt/vttablet/tabletserver/query_executor.go index 590b47b3348..3925207122b 100644 --- a/go/vt/vttablet/tabletserver/query_executor.go +++ b/go/vt/vttablet/tabletserver/query_executor.go @@ -480,9 +480,16 @@ func (qre *QueryExecutor) execNextval() (*sqltypes.Result, error) { if err != nil { return nil, vterrors.Wrapf(err, "error loading sequence %s", tableName) } - // Initialize SequenceInfo.NextVal if it wasn't already. - if t.SequenceInfo.NextVal == 0 { + // If LastVal does not match next ID, then either: + // VTTablet just started, and we're initializing the cache, or + // Someone reset the id underneath us. + if t.SequenceInfo.LastVal != nextID { + if nextID < t.SequenceInfo.LastVal { + log.Warningf("Sequence next ID value %v is below the currently cached max %v, updating it to max", nextID, t.SequenceInfo.LastVal) + nextID = t.SequenceInfo.LastVal + } t.SequenceInfo.NextVal = nextID + t.SequenceInfo.LastVal = nextID } cache, err := sqltypes.ToInt64(qr.Rows[0][1]) if err != nil {