@@ -39,6 +39,9 @@ const (
3939 metadataChangeStreamCollectionName = "changeStream"
4040)
4141
42+ // ChangeEventRecheckBuffer buffers change events recheck docs in memory as a map of namespace -> _ids.
43+ type ChangeEventRecheckBuffer map [string ][]interface {}
44+
4245type UnknownEventError struct {
4346 Event * ParsedEvent
4447}
@@ -49,7 +52,7 @@ func (uee UnknownEventError) Error() string {
4952
5053// HandleChangeStreamEvent performs the necessary work for change stream events that occur during
5154// operation.
52- func (verifier * Verifier ) HandleChangeStreamEvent (ctx context. Context , changeEvent * ParsedEvent ) error {
55+ func (verifier * Verifier ) HandleChangeStreamEvent (changeEvent * ParsedEvent ) error {
5356 if changeEvent .ClusterTime != nil &&
5457 (verifier .lastChangeEventTime == nil ||
5558 verifier .lastChangeEventTime .Compare (* changeEvent .ClusterTime ) < 0 ) {
@@ -63,7 +66,8 @@ func (verifier *Verifier) HandleChangeStreamEvent(ctx context.Context, changeEve
6366 case "replace" :
6467 fallthrough
6568 case "update" :
66- return verifier .InsertChangeEventRecheckDoc (ctx , changeEvent )
69+ verifier .changeEventRecheckBuf [changeEvent .Ns .String ()] = append (verifier .changeEventRecheckBuf [changeEvent .Ns .String ()], changeEvent .DocKey .ID )
70+ return nil
6771 default :
6872 return UnknownEventError {Event : changeEvent }
6973 }
@@ -100,19 +104,30 @@ func (verifier *Verifier) iterateChangeStream(ctx context.Context, cs *mongo.Cha
100104 return err
101105 }
102106
103- readOneChangeEvent := func () (bool , error ) {
104- gotEvent := cs .TryNext (ctx )
105- if gotEvent {
107+ readAndHandleOneChangeEventBatch := func () (bool , error ) {
108+ eventsRead := 0
109+ for hasEventInBatch := true ; hasEventInBatch ; hasEventInBatch = cs .RemainingBatchLength () > 0 {
110+ gotEvent := cs .TryNext (ctx )
111+ if ! gotEvent {
112+ break
113+ }
114+
106115 if err := cs .Decode (& changeEvent ); err != nil {
107116 return false , errors .Wrap (err , "failed to decode change event" )
108117 }
109- err := verifier .HandleChangeStreamEvent (ctx , & changeEvent )
118+ err := verifier .HandleChangeStreamEvent (& changeEvent )
110119 if err != nil {
111120 return false , errors .Wrap (err , "failed to handle change event" )
112121 }
122+
123+ eventsRead ++
113124 }
114125
115- return gotEvent , errors .Wrap (cs .Err (), "change stream iteration failed" )
126+ if err := verifier .flushAllBufferedChangeEventRechecks (ctx ); err != nil {
127+ return false , errors .Wrap (err , "failed to flush buffered change event rechecks" )
128+ }
129+
130+ return eventsRead > 0 , errors .Wrap (cs .Err (), "change stream iteration failed" )
116131 }
117132
118133 for {
@@ -136,7 +151,7 @@ func (verifier *Verifier) iterateChangeStream(ctx context.Context, cs *mongo.Cha
136151 // Read all change events until the source reports no events.
137152 // (i.e., the `getMore` call returns empty)
138153 for {
139- gotEvent , err = readOneChangeEvent ()
154+ gotEvent , err = readAndHandleOneChangeEventBatch ()
140155
141156 if ! gotEvent || err != nil {
142157 break
@@ -148,7 +163,7 @@ func (verifier *Verifier) iterateChangeStream(ctx context.Context, cs *mongo.Cha
148163 }
149164
150165 default :
151- _ , err = readOneChangeEvent ()
166+ _ , err = readAndHandleOneChangeEventBatch ()
152167 }
153168
154169 if err == nil {
@@ -177,10 +192,14 @@ func (verifier *Verifier) iterateChangeStream(ctx context.Context, cs *mongo.Cha
177192}
178193
179194// StartChangeStream starts the change stream.
180- func (verifier * Verifier ) StartChangeStream (ctx context.Context ) error {
195+ func (verifier * Verifier ) StartChangeStream (ctx context.Context , batchSize * int32 ) error {
181196 pipeline := verifier .GetChangeStreamFilter ()
182197 opts := options .ChangeStream ().SetMaxAwaitTime (1 * time .Second )
183198
199+ if batchSize != nil {
200+ opts = opts .SetBatchSize (* batchSize )
201+ }
202+
184203 savedResumeToken , err := verifier .loadChangeStreamResumeToken (ctx )
185204 if err != nil {
186205 return errors .Wrap (err , "failed to load persisted change stream resume token" )
@@ -340,3 +359,30 @@ func getClusterTimeFromSession(sess mongo.Session) (primitive.Timestamp, error)
340359
341360 return ctStruct .ClusterTime .ClusterTime , nil
342361}
362+
363+ func (verifier * Verifier ) flushAllBufferedChangeEventRechecks (ctx context.Context ) error {
364+ for namespace , ids := range verifier .changeEventRecheckBuf {
365+ if len (ids ) == 0 {
366+ return nil
367+ }
368+
369+ // We don't know the document sizes for documents for all change events,
370+ // so just be conservative and assume they are maximum size.
371+ //
372+ // Note that this prevents us from being able to report a meaningful
373+ // total data size for noninitial generations in the log.
374+ dataSizes := make ([]int , len (ids ))
375+ for i , _ := range ids {
376+ dataSizes [i ] = maxBSONObjSize
377+ }
378+
379+ dbName , collName := SplitNamespace (namespace )
380+ if err := verifier .insertRecheckDocs (ctx , dbName , collName , ids , dataSizes ); err != nil {
381+ return errors .Wrapf (err , "failed to insert recheck docs for namespace %s" , namespace )
382+ }
383+
384+ delete (verifier .changeEventRecheckBuf , namespace )
385+ }
386+
387+ return nil
388+ }
0 commit comments