···578578 return nil
579579}
580580581581-func (p *DbPersistence) RebaseRepoEvents(ctx context.Context, usr models.Uid) error {
582582- // a little weird that this is the same action as a takedown
583583- return p.deleteAllEventsForUser(ctx, usr)
584584-}
585585-586581func (p *DbPersistence) Shutdown(context.Context) error {
587582 return nil
588583}
+49-23
events/diskpersist.go
···620620 return err
621621 }
622622623623- for _, lf := range logs {
624624- if err := dp.readEventsFrom(ctx, since, filepath.Join(dp.primaryDir, lf.Path), cb); err != nil {
623623+ for i := 0; i < 10; i++ {
624624+ lastSeq, err := dp.PlaybackLogfiles(ctx, since, cb, logs)
625625+ if err != nil {
626626+ return err
627627+ }
628628+629629+ // No lastSeq implies that we read until the end of known events
630630+ if lastSeq == nil {
631631+ break
632632+ }
633633+634634+ if err := dp.meta.Debug().Order("seq_start asc").Find(&logs, "seq_start >= ?", *lastSeq).Error; err != nil {
625635 return err
626636 }
627627- since = 0
637637+ since = *lastSeq
628638 }
629639630640 return nil
631641}
632642643643+func (dp *DiskPersistence) PlaybackLogfiles(ctx context.Context, since int64, cb func(*XRPCStreamEvent) error, logFiles []LogFileRef) (*int64, error) {
644644+ for i, lf := range logFiles {
645645+ lastSeq, err := dp.readEventsFrom(ctx, since, filepath.Join(dp.primaryDir, lf.Path), cb)
646646+ if err != nil {
647647+ return nil, err
648648+ }
649649+ since = 0
650650+ if i == len(logFiles)-1 &&
651651+ lastSeq != nil &&
652652+ (*lastSeq-lf.SeqStart) == dp.eventsPerFile-1 {
653653+ // There may be more log files to read since the last one was full
654654+ return lastSeq, nil
655655+ }
656656+ }
657657+658658+ return nil, nil
659659+}
660660+633661func postDoNotEmit(flags uint32) bool {
634662 if flags&(EvtFlagRebased|EvtFlagTakedown) != 0 {
635663 return true
···638666 return false
639667}
640668641641-func (dp *DiskPersistence) readEventsFrom(ctx context.Context, since int64, fn string, cb func(*XRPCStreamEvent) error) error {
669669+func (dp *DiskPersistence) readEventsFrom(ctx context.Context, since int64, fn string, cb func(*XRPCStreamEvent) error) (*int64, error) {
642670 fi, err := os.OpenFile(fn, os.O_RDONLY, 0)
643671 if err != nil {
644644- return err
672672+ return nil, err
645673 }
646674647675 if since != 0 {
648676 lastSeq, err := scanForLastSeq(fi, since)
649677 if err != nil {
650650- return err
678678+ return nil, err
651679 }
652680 if since > lastSeq {
653681 log.Errorw("playback cursor is greater than last seq of file checked",
···655683 "lastSeq", lastSeq,
656684 "filename", fn,
657685 )
658658- return nil
686686+ return nil, nil
659687 }
660688 }
661689662690 bufr := bufio.NewReader(fi)
663691692692+ lastSeq := int64(0)
693693+664694 scratch := make([]byte, headerSize)
665695 for {
666696 h, err := readHeader(bufr, scratch)
667697 if err != nil {
668698 if errors.Is(err, io.EOF) {
669669- return nil
699699+ return &lastSeq, nil
670700 }
671701672672- return err
702702+ return nil, err
673703 }
704704+705705+ lastSeq = h.Seq
674706675707 if postDoNotEmit(h.Flags) {
676708 // event taken down, skip
677709 _, err := io.CopyN(io.Discard, bufr, h.Len64()) // would be really nice if the buffered reader had a 'skip' method that does a seek under the hood
678710 if err != nil {
679679- return fmt.Errorf("failed while skipping event (seq: %d, fn: %q): %w", h.Seq, fn, err)
711711+ return nil, fmt.Errorf("failed while skipping event (seq: %d, fn: %q): %w", h.Seq, fn, err)
680712 }
681713 continue
682714 }
···685717 case evtKindCommit:
686718 var evt atproto.SyncSubscribeRepos_Commit
687719 if err := evt.UnmarshalCBOR(io.LimitReader(bufr, h.Len64())); err != nil {
688688- return err
720720+ return nil, err
689721 }
690722 evt.Seq = h.Seq
691723 if err := cb(&XRPCStreamEvent{RepoCommit: &evt}); err != nil {
692692- return err
724724+ return nil, err
693725 }
694726 case evtKindHandle:
695727 var evt atproto.SyncSubscribeRepos_Handle
696728 if err := evt.UnmarshalCBOR(io.LimitReader(bufr, h.Len64())); err != nil {
697697- return err
729729+ return nil, err
698730 }
699731 evt.Seq = h.Seq
700732 if err := cb(&XRPCStreamEvent{RepoHandle: &evt}); err != nil {
701701- return err
733733+ return nil, err
702734 }
703735 case evtKindTombstone:
704736 var evt atproto.SyncSubscribeRepos_Tombstone
705737 if err := evt.UnmarshalCBOR(io.LimitReader(bufr, h.Len64())); err != nil {
706706- return err
738738+ return nil, err
707739 }
708740 evt.Seq = h.Seq
709741 if err := cb(&XRPCStreamEvent{RepoTombstone: &evt}); err != nil {
710710- return err
742742+ return nil, err
711743 }
712744 default:
713745 log.Warnw("unrecognized event kind coming from log file", "seq", h.Seq, "kind", h.Kind)
714714- return fmt.Errorf("halting on unrecognized event kind")
746746+ return nil, fmt.Errorf("halting on unrecognized event kind")
715747 }
716748 }
717749}
···842874 return fmt.Errorf("failed to seek: %w", err)
843875 }
844876 }
845845-}
846846-847847-func (dp *DiskPersistence) RebaseRepoEvents(ctx context.Context, usr models.Uid) error {
848848- return dp.forEachShardWithUserEvents(ctx, usr, func(ctx context.Context, fn string) error {
849849- return dp.mutateUserEventsInLog(ctx, usr, fn, EvtFlagRebased, false)
850850- })
851877}
852878853879func (dp *DiskPersistence) Flush(ctx context.Context) error {
+89-28
events/events.go
···144144 return nil
145145}
146146147147-var ErrPlaybackShutdown = fmt.Errorf("playback shutting down")
147147+var (
148148+ ErrPlaybackShutdown = fmt.Errorf("playback shutting down")
149149+ ErrCaughtUp = fmt.Errorf("caught up")
150150+)
148151149152func (em *EventManager) Subscribe(ctx context.Context, ident string, filter func(*XRPCStreamEvent) bool, since *int64) (<-chan *XRPCStreamEvent, func(), error) {
150153 if filter == nil {
···161164 broadcastCounter: eventsBroadcast.WithLabelValues(ident),
162165 }
163166167167+ cleanup := func() {
168168+ close(done)
169169+ em.rmSubscriber(sub)
170170+ }
171171+172172+ if since == nil {
173173+ em.addSubscriber(sub)
174174+ return sub.outgoing, cleanup, nil
175175+ }
176176+177177+ out := make(chan *XRPCStreamEvent, em.bufferSize)
178178+164179 go func() {
165165- if since != nil {
166166- if err := em.persister.Playback(ctx, *since, func(e *XRPCStreamEvent) error {
167167- select {
168168- case <-done:
169169- return ErrPlaybackShutdown
170170- case sub.outgoing <- e:
171171- return nil
180180+ lastSeq := *since
181181+ // run playback to get through *most* of the events, getting our current cursor close to realtime
182182+ if err := em.persister.Playback(ctx, *since, func(e *XRPCStreamEvent) error {
183183+ select {
184184+ case <-done:
185185+ return ErrPlaybackShutdown
186186+ case out <- e:
187187+ seq := sequenceForEvent(e)
188188+ if seq > 0 {
189189+ lastSeq = seq
172190 }
173173- }); err != nil {
174174- if errors.Is(err, ErrPlaybackShutdown) {
175175- log.Warnf("events playback: %s", err)
176176- } else {
177177- log.Errorf("events playback: %s", err)
178178- }
191191+ return nil
192192+ }
193193+ }); err != nil {
194194+ if errors.Is(err, ErrPlaybackShutdown) {
195195+ log.Warnf("events playback: %s", err)
196196+ } else {
197197+ log.Errorf("events playback: %s", err)
179198 }
180180- }
181199182182- // if cancel happens before playback completes
183183- select {
184184- case <-done:
200200+ // TODO: send an error frame or something?
201201+ close(out)
185202 return
186186- default:
187203 }
188204205205+ // now, start buffering events from the live stream
189206 em.addSubscriber(sub)
207207+208208+ first := <-sub.outgoing
209209+210210+ // run playback again to get us to the events that have started buffering
211211+ if err := em.persister.Playback(ctx, lastSeq, func(e *XRPCStreamEvent) error {
212212+ seq := sequenceForEvent(e)
213213+ if seq > sequenceForEvent(first) {
214214+ return ErrCaughtUp
215215+ }
216216+217217+ select {
218218+ case <-done:
219219+ return ErrPlaybackShutdown
220220+ case out <- e:
221221+ return nil
222222+ }
223223+ }); err != nil {
224224+ if !errors.Is(err, ErrCaughtUp) {
225225+ log.Errorf("events playback: %s", err)
226226+227227+ // TODO: send an error frame or something?
228228+ close(out)
229229+ em.rmSubscriber(sub)
230230+ return
231231+ }
232232+ }
233233+234234+ // now that we are caught up, just copy events from the channel over
235235+ for evt := range sub.outgoing {
236236+ select {
237237+ case out <- evt:
238238+ case <-done:
239239+ em.rmSubscriber(sub)
240240+ return
241241+ }
242242+ }
190243 }()
191244192192- cleanup := func() {
193193- close(done)
194194- em.rmSubscriber(sub)
245245+ return out, cleanup, nil
246246+}
247247+248248+func sequenceForEvent(evt *XRPCStreamEvent) int64 {
249249+ switch {
250250+ case evt.RepoCommit != nil:
251251+ return evt.RepoCommit.Seq
252252+ case evt.RepoHandle != nil:
253253+ return evt.RepoHandle.Seq
254254+ case evt.RepoMigrate != nil:
255255+ return evt.RepoMigrate.Seq
256256+ case evt.RepoTombstone != nil:
257257+ return evt.RepoTombstone.Seq
258258+ case evt.RepoInfo != nil:
259259+ return -1
260260+ default:
261261+ return -1
195262 }
196196-197197- return sub.outgoing, cleanup, nil
198263}
199264200265func (em *EventManager) rmSubscriber(sub *Subscriber) {
···220285func (em *EventManager) TakeDownRepo(ctx context.Context, user models.Uid) error {
221286 return em.persister.TakeDownRepo(ctx, user)
222287}
223223-224224-func (em *EventManager) HandleRebase(ctx context.Context, user models.Uid) error {
225225- return em.persister.RebaseRepoEvents(ctx, user)
226226-}
-5
events/persist.go
···1313 Persist(ctx context.Context, e *XRPCStreamEvent) error
1414 Playback(ctx context.Context, since int64, cb func(*XRPCStreamEvent) error) error
1515 TakeDownRepo(ctx context.Context, usr models.Uid) error
1616- RebaseRepoEvents(ctx context.Context, usr models.Uid) error
1716 Flush(context.Context) error
1817 Shutdown(context.Context) error
1918···81808281func (mp *MemPersister) TakeDownRepo(ctx context.Context, uid models.Uid) error {
8382 return fmt.Errorf("repo takedowns not currently supported by memory persister, test usage only")
8484-}
8585-8686-func (mp *MemPersister) RebaseRepoEvents(ctx context.Context, usr models.Uid) error {
8787- return fmt.Errorf("repo rebases not currently supported by memory persister, test usage only")
8883}
89849085func (mp *MemPersister) Flush(ctx context.Context) error {
-4
events/yolopersist.go
···5252 return fmt.Errorf("repo takedowns not currently supported by memory persister, test usage only")
5353}
54545555-func (yp *YoloPersister) RebaseRepoEvents(ctx context.Context, usr models.Uid) error {
5656- return fmt.Errorf("repo rebases not currently supported by memory persister, test usage only")
5757-}
5858-5955func (yp *YoloPersister) SetEventBroadcaster(brc func(*XRPCStreamEvent)) {
6056 yp.broadcast = brc
6157}