Skip to content

Commit 997edb4

Browse files
committed
review: remove dead RingBuffer count field, fix FileWriter mutex doc, add concurrent publish+read race test
1 parent 18fdb6d commit 997edb4

2 files changed

Lines changed: 16 additions & 20 deletions

File tree

server/lib/events/filewriter.go

Lines changed: 9 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
package events
22

33
import (
4-
"bytes"
54
"encoding/json"
65
"fmt"
76
"os"
@@ -10,8 +9,8 @@ import (
109
)
1110

1211
// FileWriter is a per-category JSONL appender. It opens each log file lazily on
13-
// first write (O_APPEND|O_CREATE|O_WRONLY) and serialises concurrent writes
14-
// within a category with a single mutex.
12+
// first write (O_APPEND|O_CREATE|O_WRONLY) and serialises all concurrent writes
13+
// with a single mutex.
1514
type FileWriter struct {
1615
mu sync.Mutex
1716
files map[EventCategory]*os.File
@@ -25,16 +24,17 @@ func NewFileWriter(dir string) *FileWriter {
2524
}
2625

2726
// Write serialises ev to JSON and appends it as a single JSONL line to the
28-
// per-category log file. The mutex is held for the entire open+marshal+write
29-
// sequence to prevent TOCTOU races and to guarantee whole-line atomicity for
30-
// events larger than PIPE_BUF.
27+
// per-category log file. The mutex guarantees whole-line atomicity across
28+
// concurrent callers.
3129
func (fw *FileWriter) Write(ev BrowserEvent) error {
32-
cat := CategoryFor(ev.Type)
30+
cat := ev.Category
31+
if cat == "" {
32+
return fmt.Errorf("filewriter: event %q has empty category", ev.Type)
33+
}
3334

3435
fw.mu.Lock()
3536
defer fw.mu.Unlock()
3637

37-
// Lazy open.
3838
f, ok := fw.files[cat]
3939
if !ok {
4040
path := filepath.Join(fw.dir, string(cat)+".log")
@@ -51,11 +51,7 @@ func (fw *FileWriter) Write(ev BrowserEvent) error {
5151
return fmt.Errorf("filewriter: marshal: %w", err)
5252
}
5353

54-
var buf bytes.Buffer
55-
buf.Write(data)
56-
buf.WriteByte('\n')
57-
58-
if _, err := f.Write(buf.Bytes()); err != nil {
54+
if _, err := f.Write(append(data, '\n')); err != nil {
5955
return fmt.Errorf("filewriter: write: %w", err)
6056
}
6157

server/lib/events/ringbuffer.go

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ type RingBuffer struct {
1515
mu sync.RWMutex
1616
buf []BrowserEvent
1717
head int // next write position (mod cap)
18-
count int // items currently stored (0..cap)
1918
written uint64 // total ever published (monotonic)
2019
notify chan struct{}
2120
}
@@ -35,9 +34,6 @@ func (rb *RingBuffer) Publish(ev BrowserEvent) {
3534
rb.mu.Lock()
3635
rb.buf[rb.head] = ev
3736
rb.head = (rb.head + 1) % len(rb.buf)
38-
if rb.count < len(rb.buf) {
39-
rb.count++
40-
}
4137
rb.written++
4238
old := rb.notify
4339
rb.notify = make(chan struct{})
@@ -54,7 +50,7 @@ func (rb *RingBuffer) oldestSeq() uint64 {
5450
return rb.written - uint64(len(rb.buf))
5551
}
5652

57-
// NewReader returns a Reader positioned at seq 0.
53+
// NewReader returns a Reader positioned at publish index 0 (the very beginning of the ring).
5854
// If the ring has already published events, the reader will receive an
5955
// events_dropped BrowserEvent on the first Read call if it has fallen behind
6056
// the oldest retained event.
@@ -63,9 +59,13 @@ func (rb *RingBuffer) NewReader() *Reader {
6359
}
6460

6561
// Reader tracks an independent read position in a RingBuffer.
62+
// A Reader must not be used concurrently from multiple goroutines.
63+
//
64+
// nextSeq is a monotonic count of publishes consumed by this reader — it is
65+
// an index into the ring, not the BrowserEvent.Seq field.
6666
type Reader struct {
6767
rb *RingBuffer
68-
nextSeq uint64
68+
nextSeq uint64 // publish index, not BrowserEvent.Seq
6969
}
7070

7171
// Read blocks until the next event is available or ctx is cancelled.
@@ -85,7 +85,7 @@ func (r *Reader) Read(ctx context.Context) (BrowserEvent, error) {
8585
r.nextSeq = oldest
8686
r.rb.mu.RUnlock()
8787
data := json.RawMessage(fmt.Sprintf(`{"dropped":%d}`, dropped))
88-
return BrowserEvent{Type: "events_dropped", Data: data}, nil
88+
return BrowserEvent{Type: "events.dropped", Category: CategorySystem, SourceKind: SourceKernelAPI, Data: data}, nil
8989
}
9090

9191
// Event is available — read it.

0 commit comments

Comments
 (0)