0.2.64 update
This commit is contained in:
@@ -328,8 +328,10 @@ func (b *Buffer) append(event Event) {
|
||||
b.events = append(b.events, event)
|
||||
}
|
||||
if b.history != nil {
|
||||
ordered := b.orderedEventsLocked()
|
||||
_ = b.history.append(event, ordered)
|
||||
// append asks for the ordered ring only when a compaction is actually due.
|
||||
// Previously this copied every retained event for every log line, even though
|
||||
// 4,999 out of 5,000 appends only write the new JSONL record.
|
||||
_ = b.history.append(event, b)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -341,11 +343,13 @@ func (b *Buffer) orderedEventsLocked() []Event {
|
||||
return ordered
|
||||
}
|
||||
|
||||
func (h *historyFile) append(event Event, retained []Event) error {
|
||||
// append is called while buffer.mu is held. This lock order is always buffer then history;
|
||||
// compaction may therefore read the already-locked ring without another lock or snapshot.
|
||||
func (h *historyFile) append(event Event, buffer *Buffer) error {
|
||||
h.mu.Lock()
|
||||
defer h.mu.Unlock()
|
||||
if event.Sequence-h.lastCompacted >= int64(h.capacity) {
|
||||
if err := h.rewriteLocked(retained); err != nil {
|
||||
if err := h.rewriteLocked(buffer.orderedEventsLocked()); err != nil {
|
||||
return err
|
||||
}
|
||||
h.lastCompacted = event.Sequence
|
||||
@@ -419,13 +423,12 @@ func (b *Buffer) Events(after int64, limit int) EventPage {
|
||||
page.Dropped = page.Oldest - after - 1
|
||||
after = page.Oldest - 1
|
||||
}
|
||||
start := len(b.events)
|
||||
for i := range b.events {
|
||||
event := b.events[(b.start+i)%len(b.events)]
|
||||
if event.Sequence > after {
|
||||
start = i
|
||||
break
|
||||
}
|
||||
// Sequences are contiguous, so cursor-to-ring position is arithmetic. The old scan
|
||||
// walked the full ring on every no-news poll—the most common request this endpoint
|
||||
// receives while an operator has the log open.
|
||||
start := 0
|
||||
if after >= page.Oldest {
|
||||
start = int(min(after-page.Oldest+1, int64(len(b.events))))
|
||||
}
|
||||
end := min(start+limit, len(b.events))
|
||||
for i := start; i < end; i++ {
|
||||
|
||||
@@ -2,6 +2,7 @@ package logging
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
"log/slog"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
@@ -138,6 +139,22 @@ func TestBufferedLoggerRetainsStructuredEventsWithCursorPagination(t *testing.T)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTailCursorReadDoesNotCopyTheRing(t *testing.T) {
|
||||
logger, buffer := NewBuffered(io.Discard, slog.LevelInfo, 5_000, FormatConsole)
|
||||
for range 5_000 {
|
||||
logger.Info("request complete")
|
||||
}
|
||||
allocations := testing.AllocsPerRun(100, func() {
|
||||
page := buffer.Events(5_000, 1_000)
|
||||
if len(page.Events) != 0 || page.HasMore {
|
||||
t.Fatalf("tail cursor unexpectedly returned events: %+v", page)
|
||||
}
|
||||
})
|
||||
if allocations > 2 {
|
||||
t.Fatalf("tail cursor allocated %.1f objects; the ring may be getting copied", allocations)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPersistentBufferRestoresTheRetainedTail(t *testing.T) {
|
||||
path := filepath.Join(t.TempDir(), "events.jsonl")
|
||||
var output bytes.Buffer
|
||||
|
||||
Reference in New Issue
Block a user