@@ -20,6 +20,9 @@ import (
2020 "fmt"
2121 "testing"
2222
23+ "github.com/stretchr/testify/assert"
24+ "github.com/stretchr/testify/require"
25+
2326 binlogdatapb "vitess.io/vitess/go/vt/proto/binlogdata"
2427 qh "vitess.io/vitess/go/vt/vttablet/tabletmanager/vreplication/queryhistory"
2528)
@@ -314,3 +317,140 @@ func TestJournalTableMixed(t *testing.T) {
314317 deleteAllVReplicationStreams (t )
315318 expectDeleteQueries (t )
316319}
320+
321+ // TestJournalTablesLookupVindexIgnored guards against resharding journals
322+ // written by MoveTables SwitchTraffic/ReverseTraffic destroying lookup
323+ // vindex backfill streams (https://github.com/vitessio/vitess/issues/20915).
324+ // A CreateLookupIndex stream must ignore a TABLES journal -- its
325+ // keyspace_id() filter cannot be planned in another keyspace -- and keep
326+ // replicating from its current source, which the paired workflow keeps
327+ // feeding after the switch. SHARDS (Reshard) journals are still followed;
328+ // that behavior is pinned by TestJournalOneToOne/TestJournalOneToMany.
329+ func TestJournalTablesLookupVindexIgnored (t * testing.T ) {
330+ if runNoBlobTest {
331+ t .Skip ("CreateLookupIndex workflows do not support binlog_row_image=noblob" )
332+ }
333+ // This test's teardown uses defer, not t.Cleanup: execStatements and
334+ // the other framework helpers run on t.Context(), which is already
335+ // canceled by the time t.Cleanup callbacks execute.
336+ defer deleteTablet (addTablet (100 ))
337+ defer deleteTablet (addOtherTablet (101 , "other_keyspace" , "0" ))
338+
339+ execStatements (t , []string {
340+ "create table t(id int, val varbinary(128), primary key(id))" ,
341+ fmt .Sprintf ("create table %s.t(id int, val varbinary(128), primary key(id))" , vrepldb ),
342+ })
343+ defer execStatements (t , []string {
344+ "drop table t" ,
345+ fmt .Sprintf ("drop table %s.t" , vrepldb ),
346+ })
347+
348+ filter := & binlogdatapb.Filter {
349+ Rules : []* binlogdatapb.Rule {{
350+ Match : "t" ,
351+ }},
352+ }
353+ bls := & binlogdatapb.BinlogSource {
354+ Keyspace : env .KeyspaceName ,
355+ Shard : env .ShardName ,
356+ Filter : filter ,
357+ OnDdl : binlogdatapb .OnDDLAction_IGNORE ,
358+ }
359+ cancel , _ := startVReplicationWithWorkflowType (t , bls , "" , binlogdatapb .VReplicationWorkflowType_CreateLookupIndex )
360+ defer cancel ()
361+
362+ journal := & binlogdatapb.Journal {
363+ Id : 1 ,
364+ MigrationType : binlogdatapb .MigrationType_TABLES ,
365+ Participants : []* binlogdatapb.KeyspaceShard {{
366+ Keyspace : "vttest" ,
367+ Shard : "0" ,
368+ }},
369+ Tables : []string {"t" },
370+ ShardGtids : []* binlogdatapb.ShardGtid {{
371+ Keyspace : "other_keyspace" ,
372+ Shard : "0" ,
373+ Gtid : "MySQL56/7b04699f-f5e9-11e9-bf88-9cb6d089e1c3:1-10" ,
374+ }},
375+ }
376+ query := fmt .Sprintf ("insert into _vt.resharding_journal(id, db_name, val) values (1, 'vttest', %v)" , encodeString (journal .String ()))
377+ execStatements (t , []string {query })
378+ defer execStatements (t , []string {"delete from _vt.resharding_journal" })
379+
380+ // The journal must be ignored: no stream deletion/recreation. The
381+ // stream must still be replicating from the original source, which
382+ // this insert proves end to end.
383+ execStatements (t , []string {"insert into t values(1, 'aaa')" })
384+ expectDBClientQueries (t , qh .Expect (
385+ "begin" ,
386+ "insert into t(id,val) values (1,_binary'aaa')" ,
387+ "/update _vt.vreplication set pos=" ,
388+ "commit" ,
389+ ))
390+ }
391+
392+ // TestJournalRegisterLookupVindexRefused guards the engine-side insurance
393+ // for https://github.com/vitessio/vitess/issues/20915: even if a future
394+ // caller bypasses the vplayer's TABLES-journal gate, registerJournal must
395+ // refuse to transition a lookup vindex workflow rather than let
396+ // transitionJournal destroy its streams.
397+ func TestJournalRegisterLookupVindexRefused (t * testing.T ) {
398+ if runNoBlobTest {
399+ t .Skip ("CreateLookupIndex workflows do not support binlog_row_image=noblob" )
400+ }
401+ // This test's teardown uses defer, not t.Cleanup: execStatements and
402+ // the other framework helpers run on t.Context(), which is already
403+ // canceled by the time t.Cleanup callbacks execute.
404+ defer deleteTablet (addTablet (100 ))
405+ defer deleteTablet (addOtherTablet (101 , "other_keyspace" , "0" ))
406+
407+ execStatements (t , []string {
408+ "create table t(id int, val varbinary(128), primary key(id))" ,
409+ fmt .Sprintf ("create table %s.t(id int, val varbinary(128), primary key(id))" , vrepldb ),
410+ })
411+ defer execStatements (t , []string {
412+ "drop table t" ,
413+ fmt .Sprintf ("drop table %s.t" , vrepldb ),
414+ })
415+
416+ filter := & binlogdatapb.Filter {
417+ Rules : []* binlogdatapb.Rule {{
418+ Match : "t" ,
419+ }},
420+ }
421+ bls := & binlogdatapb.BinlogSource {
422+ Keyspace : env .KeyspaceName ,
423+ Shard : env .ShardName ,
424+ Filter : filter ,
425+ OnDdl : binlogdatapb .OnDDLAction_IGNORE ,
426+ }
427+ cancel , id := startVReplicationWithWorkflowType (t , bls , "" , binlogdatapb .VReplicationWorkflowType_CreateLookupIndex )
428+ defer cancel ()
429+
430+ journal := & binlogdatapb.Journal {
431+ Id : 2 ,
432+ MigrationType : binlogdatapb .MigrationType_TABLES ,
433+ Participants : []* binlogdatapb.KeyspaceShard {{
434+ Keyspace : "vttest" ,
435+ Shard : "0" ,
436+ }},
437+ Tables : []string {"t" },
438+ ShardGtids : []* binlogdatapb.ShardGtid {{
439+ Keyspace : "other_keyspace" ,
440+ Shard : "0" ,
441+ Gtid : "MySQL56/7b04699f-f5e9-11e9-bf88-9cb6d089e1c3:1-10" ,
442+ }},
443+ }
444+
445+ err := playerEngine .registerJournal (journal , int32 (id ))
446+ require .ErrorContains (t , err , "lookup vindex" )
447+
448+ // The stream must be untouched: no journaler entry queued, no
449+ // transition, the controller still registered.
450+ playerEngine .mu .Lock ()
451+ _ , ok := playerEngine .controllers [int32 (id )]
452+ journalerEmpty := len (playerEngine .journaler ) == 0
453+ playerEngine .mu .Unlock ()
454+ assert .True (t , ok , "controller was removed" )
455+ assert .True (t , journalerEmpty , "journaler entry was created" )
456+ }
0 commit comments