···11+package splitter
22+33+import (
44+ "github.com/prometheus/client_golang/prometheus"
55+ "github.com/prometheus/client_golang/prometheus/promauto"
66+)
77+88+var eventsSentCounter = promauto.NewCounterVec(prometheus.CounterOpts{
99+ Name: "spl_events_sent_counter",
1010+ Help: "The total number of events sent to consumers",
1111+}, []string{"remote_addr", "user_agent"})
+144
splitter/ringbuf.go
···11+package splitter
22+33+import (
44+ "context"
55+ "sync"
66+77+ events "github.com/bluesky-social/indigo/events"
88+ "github.com/bluesky-social/indigo/models"
99+)
1010+1111+func NewEventRingBuffer(chunkSize, nchunks int) *EventRingBuffer {
1212+ return &EventRingBuffer{
1313+ chunkSize: chunkSize,
1414+ maxChunkCount: nchunks,
1515+ }
1616+}
1717+1818+type EventRingBuffer struct {
1919+ lk sync.Mutex
2020+ chunks []*ringChunk
2121+ chunkSize int
2222+ maxChunkCount int
2323+2424+ broadcast func(*events.XRPCStreamEvent)
2525+}
2626+2727+type ringChunk struct {
2828+ lk sync.Mutex
2929+ buf []*events.XRPCStreamEvent
3030+}
3131+3232+func (rc *ringChunk) append(evt *events.XRPCStreamEvent) {
3333+ rc.lk.Lock()
3434+ defer rc.lk.Unlock()
3535+ rc.buf = append(rc.buf, evt)
3636+}
3737+3838+func (rc *ringChunk) events() []*events.XRPCStreamEvent {
3939+ rc.lk.Lock()
4040+ defer rc.lk.Unlock()
4141+ return rc.buf
4242+}
4343+4444+func (er *EventRingBuffer) Persist(ctx context.Context, evt *events.XRPCStreamEvent) error {
4545+ er.lk.Lock()
4646+ defer er.lk.Unlock()
4747+4848+ if len(er.chunks) == 0 {
4949+ er.chunks = []*ringChunk{new(ringChunk)}
5050+ }
5151+5252+ last := er.chunks[len(er.chunks)-1]
5353+ if len(last.buf) >= er.chunkSize {
5454+ last = new(ringChunk)
5555+ er.chunks = append(er.chunks, last)
5656+ if len(er.chunks) > er.maxChunkCount {
5757+ er.chunks = er.chunks[1:]
5858+ }
5959+ }
6060+6161+ last.append(evt)
6262+6363+ er.broadcast(evt)
6464+ return nil
6565+}
6666+6767+func (er *EventRingBuffer) Flush(context.Context) error {
6868+ return nil
6969+}
7070+7171+func (er *EventRingBuffer) Playback(ctx context.Context, since int64, cb func(*events.XRPCStreamEvent) error) error {
7272+ // run playback a few times to get as close to 'live' as possible before returning
7373+ for i := 0; i < 10; i++ {
7474+ n, err := er.playbackRound(ctx, since, cb)
7575+ if err != nil {
7676+ return err
7777+ }
7878+7979+ // playback had no new events
8080+ if n-since == 0 {
8181+ return nil
8282+ }
8383+ since = n
8484+ }
8585+8686+ return nil
8787+}
8888+8989+func (er *EventRingBuffer) playbackRound(ctx context.Context, since int64, cb func(*events.XRPCStreamEvent) error) (int64, error) {
9090+ // grab a snapshot of the current chunks
9191+ er.lk.Lock()
9292+ chunks := er.chunks
9393+ er.lk.Unlock()
9494+9595+ i := len(chunks) - 1
9696+ for ; i >= 0; i-- {
9797+ c := chunks[i]
9898+ evts := c.events()
9999+ if since > sequenceForEvent(evts[len(evts)-1]) {
100100+ i++
101101+ break
102102+ }
103103+ }
104104+ if i < 0 {
105105+ i = 0
106106+ }
107107+108108+ var lastSeq int64 = since
109109+ for _, c := range chunks[i:] {
110110+ var nread int
111111+ evts := c.events()
112112+ for nread < len(evts) {
113113+ for _, e := range evts[nread:] {
114114+ nread++
115115+ seq := sequenceForEvent(e)
116116+ if seq <= since {
117117+ continue
118118+ }
119119+120120+ if err := cb(e); err != nil {
121121+ return 0, err
122122+ }
123123+ lastSeq = seq
124124+ }
125125+126126+ // recheck evts buffer to see if more were added while we were here
127127+ evts = c.events()
128128+ }
129129+ }
130130+131131+ return lastSeq, nil
132132+}
133133+134134+func (er *EventRingBuffer) SetEventBroadcaster(brc func(*events.XRPCStreamEvent)) {
135135+ er.broadcast = brc
136136+}
137137+138138+func (er *EventRingBuffer) Shutdown(context.Context) error {
139139+ return nil
140140+}
141141+142142+func (er *EventRingBuffer) TakeDownRepo(context.Context, models.Uid) error {
143143+ return nil
144144+}