MCPcopy Create free account
hub / github.com/couchbase/sync_gateway / TestChannelRace

Function TestChannelRace

db/change_cache_test.go:1025–1121  ·  view source on GitHub ↗

Test race condition causing skipped sequences in changes feed. Channel feeds are processed sequentially in the main changes.go iteration loop, without a lock on the underlying channel caches. The following sequence is possible while running a changes feed for channels "A", "B": 1. Sequence 100, Ch

(t *testing.T)

Source from the content-addressed store, hash-verified

1023// base.Infof(base.KeyChanges, "Simulate slow processing time for channel %s - sleeping for 100 ms", channel)
1024// time.Sleep(100 * time.Millisecond)
1025func TestChannelRace(t *testing.T) {
1026 base.SetUpTestLogging(t, base.LevelInfo, base.KeyChanges)
1027
1028 db, ctx := setupTestDBWithCacheOptions(t, shortWaitCache())
1029 defer db.Close(ctx)
1030
1031 // Create a user with access to channels "Odd", "Even"
1032 authenticator := db.Authenticator(ctx)
1033 user, err := authenticator.NewUser("naomi", "letmein", channels.BaseSetOf(t, "Even", "Odd"))
1034 require.NoError(t, err)
1035 require.NoError(t, authenticator.Save(user))
1036
1037 collection := GetSingleDatabaseCollection(t, db.DatabaseContext)
1038
1039 // Write initial sequences
1040 WriteDirect(t, collection, []string{"Odd"}, 1)
1041 WriteDirect(t, collection, []string{"Even"}, 2)
1042 WriteDirect(t, collection, []string{"Odd"}, 3)
1043
1044 require.NoError(t, db.changeCache.waitForSequence(ctx, 3, base.DefaultWaitForSequence))
1045 db.user, err = authenticator.GetUser("naomi")
1046 require.NoError(t, err)
1047
1048 // Start changes feed
1049 dbCollection, ctx := GetSingleDatabaseCollectionWithUser(ctx, t, db)
1050
1051 var options ChangesOptions
1052 options.Since = SequenceID{Seq: 0}
1053 ctx, changesCtxCancel := context.WithCancel(ctx)
1054 options.ChangesCtx = ctx
1055 options.Continuous = true
1056 options.Wait = true
1057 feed, err := dbCollection.MultiChangesFeed(ctx, base.SetOf("Even", "Odd"), options)
1058 assert.True(t, err == nil)
1059 feedClosed := false
1060
1061 // Go-routine to work the feed channel and write to an array for use by assertions
1062 var changes struct {
1063 lock sync.RWMutex
1064 entries []*ChangeEntry
1065 }
1066 changes.entries = make([]*ChangeEntry, 0, 50)
1067 go func() {
1068 for feedClosed == false {
1069 select {
1070 case entry, ok := <-feed:
1071 if ok {
1072 // feed sends nil after each continuous iteration
1073 if entry != nil {
1074 log.Println("Changes entry:", entry.Seq)
1075 changes.lock.Lock()
1076 changes.entries = append(changes.entries, entry)
1077 changes.lock.Unlock()
1078 }
1079 } else {
1080 log.Println("Closing feed")
1081 feedClosed = true
1082 }

Callers

nothing calls this directly

Calls 15

SetUpTestLoggingFunction · 0.92
BaseSetOfFunction · 0.92
SetOfFunction · 0.92
shortWaitCacheFunction · 0.85
WriteDirectFunction · 0.85
NewUserMethod · 0.80
SaveMethod · 0.80
waitForSequenceMethod · 0.80

Tested by

no test coverage detected