mirror of
https://github.com/hanzoai/o11y.git
synced 2026-10-15 12:58:33 +00:00
github.com/hanzoai/metrics carries a NOTICE naming this module its successor, and it is archived and read-only. What the NOTICE did not say is that the code was still running: hanzoai/cloud went on importing the archive and mounting it for /v1/metrics, /v1/logs and /v1/traces, so a repository nobody can push to held eleven live routes and the store behind them. A succession is only finished when the successor has the code. It arrives whole — the per-tenant WAL store, the ZAP MetricBatch receiver, the routes and their tests — and the tests pass here unchanged but for the addresses. The addresses fold per HIP-1241: every route is under /v1/metrics, so the two foreign roots the archive answered on become /v1/metrics/logs/* and /v1/metrics/traces/*. Forward only; nothing answers at the old spelling. This store is its own. The event.* columnar plane the rest of this module reads is a different store of a different shape, and the succession note's claim that o11y already held this signal store was true only of that plane — these eleven routes had no home here until now. They share the module and nothing else. The tenant decision still comes from the host (Deps.Org), so the boundary that authenticates keeps naming the org and this package never reads a header.
86 lines
2.0 KiB
Go
86 lines
2.0 KiB
Go
package metrics
|
|
|
|
import (
|
|
"bufio"
|
|
"encoding/binary"
|
|
"io"
|
|
"os"
|
|
"path/filepath"
|
|
"sync"
|
|
)
|
|
|
|
// WAL is a simple append-only write-ahead log of length-prefixed records. It
|
|
// gives the in-memory stores durability: every write is appended here and the
|
|
// log is replayed on startup. This is intentionally a single flat segment — a
|
|
// production deployment adds rotation/compaction behind the same Append/Replay
|
|
// API without touching callers.
|
|
type WAL struct {
|
|
mu sync.Mutex
|
|
f *os.File
|
|
w *bufio.Writer
|
|
}
|
|
|
|
// OpenWAL opens (creating parent dirs and the file as needed) an append log.
|
|
func OpenWAL(path string) (*WAL, error) {
|
|
if err := os.MkdirAll(filepath.Dir(path), 0o755); err != nil {
|
|
return nil, err
|
|
}
|
|
f, err := os.OpenFile(path, os.O_CREATE|os.O_RDWR, 0o644)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &WAL{f: f, w: bufio.NewWriter(f)}, nil
|
|
}
|
|
|
|
// Replay invokes fn for every record from the start, then positions the file at
|
|
// the end so subsequent Appends extend the log. Call once on startup.
|
|
func (w *WAL) Replay(fn func([]byte)) error {
|
|
w.mu.Lock()
|
|
defer w.mu.Unlock()
|
|
if _, err := w.f.Seek(0, io.SeekStart); err != nil {
|
|
return err
|
|
}
|
|
r := bufio.NewReader(w.f)
|
|
var hdr [4]byte
|
|
for {
|
|
if _, err := io.ReadFull(r, hdr[:]); err != nil {
|
|
if err == io.EOF {
|
|
break
|
|
}
|
|
return err
|
|
}
|
|
rec := make([]byte, binary.LittleEndian.Uint32(hdr[:]))
|
|
if _, err := io.ReadFull(r, rec); err != nil {
|
|
return err
|
|
}
|
|
fn(rec)
|
|
}
|
|
_, err := w.f.Seek(0, io.SeekEnd)
|
|
return err
|
|
}
|
|
|
|
// Append writes one length-prefixed record and flushes it to disk.
|
|
func (w *WAL) Append(rec []byte) error {
|
|
w.mu.Lock()
|
|
defer w.mu.Unlock()
|
|
var hdr [4]byte
|
|
binary.LittleEndian.PutUint32(hdr[:], uint32(len(rec)))
|
|
if _, err := w.w.Write(hdr[:]); err != nil {
|
|
return err
|
|
}
|
|
if _, err := w.w.Write(rec); err != nil {
|
|
return err
|
|
}
|
|
return w.w.Flush()
|
|
}
|
|
|
|
// Close flushes and closes the log.
|
|
func (w *WAL) Close() error {
|
|
w.mu.Lock()
|
|
defer w.mu.Unlock()
|
|
if w.w != nil {
|
|
_ = w.w.Flush()
|
|
}
|
|
return w.f.Close()
|
|
}
|