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)
| 1023 | // base.Infof(base.KeyChanges, "Simulate slow processing time for channel %s - sleeping for 100 ms", channel) |
| 1024 | // time.Sleep(100 * time.Millisecond) |
| 1025 | func 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 | } |
nothing calls this directly
no test coverage detected