mirror of
https://github.com/AmanTahiliani/box-box.git
synced 2026-08-07 19:56:18 -04:00
feat(#76): harvest request-scoped availability and freshness truth
Backend-only re-cut of the #76 availability work onto main, stacked on the canonical Weekend Context API. Adds request-scoped freshness reporting so aggregate responses cannot report fresh when a component is stale, plus local-first driver summary resolution and cache/pacing truth. The frontend half of #76 is deliberately excluded: it is built on the Weekend shell that failed owner review, including the full-width Partial banner treatment. Availability presentation is re-cut with the shell in #89. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
@@ -26,8 +27,12 @@ type requestPacer struct {
|
||||
|
||||
// wait blocks until this caller's reserved slot arrives.
|
||||
func (p *requestPacer) wait() {
|
||||
_ = p.waitContext(context.Background())
|
||||
}
|
||||
|
||||
func (p *requestPacer) waitContext(ctx context.Context) error {
|
||||
if p == nil || p.interval <= 0 {
|
||||
return
|
||||
return nil
|
||||
}
|
||||
p.mu.Lock()
|
||||
now := time.Now()
|
||||
@@ -38,8 +43,18 @@ func (p *requestPacer) wait() {
|
||||
p.next = p.next.Add(p.interval)
|
||||
p.mu.Unlock()
|
||||
if sleep > 0 {
|
||||
time.Sleep(sleep)
|
||||
timer := time.NewTimer(sleep)
|
||||
defer timer.Stop()
|
||||
select {
|
||||
case <-timer.C:
|
||||
case <-ctx.Done():
|
||||
// Keep the unused reservation in the schedule. Blindly reclaiming an
|
||||
// interval can collide with later callers that already reserved their
|
||||
// wake times, releasing two requests simultaneously.
|
||||
return ctx.Err()
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type OpenF1Client struct {
|
||||
@@ -56,6 +71,26 @@ type OpenF1Client struct {
|
||||
staleFlag int32
|
||||
}
|
||||
|
||||
// Scoped returns a lightweight request-scoped view of the client. Network,
|
||||
// pacing and cache resources are shared, while the stale fallback indicator is
|
||||
// deliberately not shared. Web handlers use this view so a stale fallback in
|
||||
// one concurrent HTTP request can never mark an unrelated response as stale.
|
||||
//
|
||||
// The legacy client-wide stale flag remains available for the TUI, whose loads
|
||||
// are intentionally aggregated into one navigation-level notice.
|
||||
func (c *OpenF1Client) Scoped() *OpenF1Client {
|
||||
if c == nil {
|
||||
return nil
|
||||
}
|
||||
return &OpenF1Client{
|
||||
url: c.url,
|
||||
apiKey: c.apiKey,
|
||||
httpClient: c.httpClient,
|
||||
cache: c.cache,
|
||||
pacer: c.pacer,
|
||||
}
|
||||
}
|
||||
|
||||
func NewOpenF1Client(url string, timeout time.Duration) *OpenF1Client {
|
||||
return &OpenF1Client{
|
||||
url: url,
|
||||
|
||||
Reference in New Issue
Block a user