Documentation
¶
Overview ¶
Package record holds the token ledger: the index of nova-tokens' day TSVs in the fleet Redis, one hash per day (docs/SPEC-STATE.md test 17, #2201). Draft 1 of SPEC-STATE put it in a Postgres table (#3243); Postgres was retired under #2623, so the same rows and the same monthly GROUP BY live here on Redis with no new dependency. The day files stay the record; the ledger is what a month query reads instead of every file.
KEY LAYOUT. tokens:ledger:<YYYY-MM-DD> is a hash. Each field is one row's key, the JSON array ["<card>","<model>","<repo>"], so (day, card, model, repo) is the primary key; each value is the JSON object {"provider","tokens","rough","sources"} whose "tokens" array carries the five types in LedgerTypes order with null for a type no source reported -- never 0, because a dash in the day file is an absence. A month is the day keys of its calendar days, read in one pipelined round trip: no SCAN, no index key to keep true.
Index ¶
Constants ¶
const LedgerPrefix = "tokens:ledger:"
LedgerPrefix is the owner prefix of every key this package writes.
Variables ¶
var ErrLedgerKey = errors.New("a ledger row names day, card, model and repo")
ErrLedgerKey is a row that does not name its whole key.
var LedgerGroupings = map[string][]string{
"model": {"model"},
"repo": {"repo"},
"day": {"day"},
"tuple": {"day", "model", "repo"},
}
LedgerGroupings are the --by values the report takes, and the key columns each groups on.
var LedgerTypes = [5]string{"input", "output", "cache_write", "cache_read", "reasoning"}
LedgerTypes are the five token types in the order every row and report prints them.
Functions ¶
func CheckLedgerDay ¶
func CheckLedgerDay(day string, entries []LedgerEntry) error
CheckLedgerDay refuses a day that is not a calendar day (it names a key), and a batch that is not all of that day or repeats a key: a caller that meant two rows under one key meant their sum.
func SortLedgerTotals ¶
func SortLedgerTotals(t []LedgerTotal)
SortLedgerTotals is the report's order: by the key columns ascending.
Types ¶
type LedgerEntry ¶
type LedgerEntry struct {
Day, Card, Model, Repo string
Provider string
Tokens [5]int64
Known [5]bool
Rough int
Sources string
}
LedgerEntry is one ledger row. Tokens[i] counts only when Known[i].
type LedgerStore ¶
type LedgerStore interface {
ReplaceLedgerDay(ctx context.Context, day string, entries []LedgerEntry) error
LedgerReport(ctx context.Context, month, by string) ([]LedgerTotal, int, int, error)
Close() error
}
LedgerStore is the durable side of the token ledger. ReplaceLedgerDay swaps one day's rows for the given ones atomically, so indexing a day twice is the same ledger as once. LedgerReport is the monthly GROUP BY; indexed is how many calendar-day keys existed and missing is how many did not.
type LedgerTotal ¶
LedgerTotal is one GROUP BY group: the key columns the report grouped on (empty when not grouped on), how many ledger rows it summed, and each type's sum and whether any row reported it.
func GroupLedger ¶
func GroupLedger(entries []LedgerEntry, by string) ([]LedgerTotal, error)
GroupLedger is the GROUP BY: rows summed per the key columns of by, each type the SUM over the rows that reported it and unknown when none did -- the fold's per-type rule.
type RedisLedger ¶
type RedisLedger struct {
// contains filtered or unexported fields
}
RedisLedger is the ledger on the fleet Redis. It dials nothing itself: the caller opens the client, and Close closes it.
func DialLedger ¶
func DialLedger(addr, user, password string) *RedisLedger
DialLedger opens a client on addr (host:port) as the ACL user with its password ("" for the default user, "" for no password). The fleet Redis has its default user off, so a seat's password without its user is WRONGPASS (#3461). It silences go-redis's own logger first (#3463): a dial failure otherwise prints the library's untyped, local-time "connection pool: failed to dial after 5 attempts" lines to the process's stderr ahead of the verb's one typed FAILED line. The error they carry is not lost: it is the error Ping returns, which the verb prints.
func NewRedisLedger ¶
func NewRedisLedger(rdb *redis.Client) *RedisLedger
NewRedisLedger wraps a client the caller opened.
func (*RedisLedger) LedgerReport ¶
func (s *RedisLedger) LedgerReport(ctx context.Context, month, by string) ([]LedgerTotal, int, int, error)
LedgerReport reads every day hash of the month in one pipelined round trip and groups it. A field or value that does not decode is an error naming its key, never a skipped row. It returns the totals, how many calendar-day keys existed (indexed), and how many did not (missing).
func (*RedisLedger) Ping ¶
func (s *RedisLedger) Ping(ctx context.Context) error
Ping is one PING, so a verb names an unreachable store before it reads any file.
func (*RedisLedger) ReplaceLedgerDay ¶
func (s *RedisLedger) ReplaceLedgerDay(ctx context.Context, day string, entries []LedgerEntry) error
ReplaceLedgerDay is DEL then HSET of the day's hash in one MULTI/EXEC, so a re-fold's re-index replaces the day rather than merging into it, and no reader sees half a day.