package supply import ( "encoding/json" "fmt" "sort" "sync" "sync/atomic" ) // Tracker routes optional consumer events to the local producer attempt. type Tracker struct { mu sync.RWMutex runs map[string]*Observer LocalConsumer bool ConsumerActive atomic.Bool } func NewTracker(localConsumer bool) *Tracker { return &Tracker{runs: map[string]*Observer{}, LocalConsumer: localConsumer} } func (t *Tracker) Put(run string, o *Observer) { t.mu.Lock(); t.runs[run] = o; t.mu.Unlock() } func (t *Tracker) Remove(run string) { t.mu.Lock(); delete(t.runs, run); t.mu.Unlock() } func (t *Tracker) Empty(run, key string) { t.mu.RLock() defer t.mu.RUnlock() if o := t.runs[run]; o != nil { o.Empty(key) } } func (t *Tracker) Processing(run, key string, delta int) { t.mu.RLock() defer t.mu.RUnlock() if o := t.runs[run]; o != nil { o.Processing(key, delta) } } func (o *Observer) Restore(previous Snapshot) { o.mu.Lock() defer o.mu.Unlock() o.partial = true o.retained = previous.Summary o.truncated = previous.Truncated for _, c := range previous.Channels { if s := o.channels[c.Key]; s != nil { if c.Startup.TotalMS+c.Confirmed.TotalMS > 0 { o.retained.Confirmed-- } o.retained.TotalMS -= c.Startup.TotalMS + c.Confirmed.TotalMS o.retained.UnknownMS -= c.UnknownMS o.retained.UnconfirmedMS -= c.Unconfirmed.TotalMS o.retained.ConsumerMS -= c.Consumer.TotalMS s.Channel = c if s.Stages == nil { s.Stages = map[string]int64{} } s.Termination = "" s.MissingSamples++ s.close() } } } func (o *Observer) Encode() ([]byte, error) { snapshot := o.Snapshot() encoded, err := json.Marshal(snapshot) if err != nil || len(encoded) <= 256*1024 { return encoded, err } snapshot.Truncated = true snapshot.Partial = true sort.Slice(snapshot.Channels, func(i, j int) bool { a, b := snapshot.Channels[i], snapshot.Channels[j] av, bv := max(a.Startup.LongestMS, a.Confirmed.LongestMS), max(b.Startup.LongestMS, b.Confirmed.LongestMS) if av == bv { return a.Key < b.Key } return av > bv }) snapshot.Channels = snapshot.Channels[:min(100, len(snapshot.Channels))] for { encoded, err = json.Marshal(snapshot) if err != nil || len(encoded) <= 256*1024 { return encoded, err } if len(snapshot.Channels) == 0 { return nil, fmt.Errorf("supply summary exceeds snapshot limit") } snapshot.Channels = snapshot.Channels[:len(snapshot.Channels)-1] } }