10 const commandResultCapacity = 256
12 // Ledger keeps the first result of each of the most recent 256 commands for one
13 // device, in insertion order. Its methods are safe for concurrent use. A single
14 // queue consumer checks Lookup before acting and calls Remember before sending
15 // the result, also after a failed send. The ledger does not reserve physical
16 // work, persist records, or prevent replay after eviction.
20 results map[string]ResultRecord
24 // NewLedger creates an empty result ledger for one canonical device ID.
25 func NewLedger(deviceID string) (*Ledger, error) {
26 if !protocol.ValidID(deviceID) {
27 return nil, fmt.Errorf("ledger device ID must be a canonical ULID")
32 results: make(map[string]ResultRecord),
33 order: make([]string, 0, commandResultCapacity),
37 // Lookup returns an independent copy of a result without changing its age.
38 func (l *Ledger) Lookup(commandID string) (ResultRecord, bool) {
42 result, exists := l.results[commandID]
44 return result.clone(), exists
47 // Remember stores the first validated result before publication. An existing
48 // result wins even when a duplicate proposes another status or reason. Its ID
49 // and observation time remain stable after failed publication and on replay.
50 func (l *Ledger) Remember(commandID string, payload protocol.ResultPayload) (ResultRecord, error) {
54 result, exists := l.results[commandID]
56 return result.clone(), nil
59 metadata, err := newMetadata(protocol.Metadata{CorrelationID: commandID})
61 return ResultRecord{}, fmt.Errorf("create result metadata: %w", err)
64 _, err = protocol.BuildResult(l.deviceID, commandID, metadata, payload)
66 return ResultRecord{}, fmt.Errorf("validate result: %w", err)
69 if len(l.order) == commandResultCapacity {
70 delete(l.results, l.order[0])
75 result = ResultRecord{
79 l.results[commandID] = result
80 l.order = append(l.order, commandID)
82 return result.clone(), nil