From 280a7976840bc706c3200d71023e97c8ef5ed89f Mon Sep 17 00:00:00 2001 From: AI Dev Date: Thu, 8 Oct 2026 18:09:47 +0000 Subject: [PATCH] Crashes: count the daily limits on the clock that stamps the event Insert counted a client's and a camera's crashes since a.now() - 24h, but stamped each event with the database's now(). With the API's clock and the database's apart -- as in TestMonthCap, whose clock is fixed at 2026-10-08 12:00 UTC -- the window counted the wrong events: from 17:00 UTC that day the test's earlier sends all fell inside it and the sixth was refused 429, so master's CI fails from then on, on every day. Insert now takes the event's time, stamps received_at with it, and counts the day before it: the window and what it counts come from one clock. --- service/internal/crashes/api.go | 2 +- service/internal/crashes/store.go | 16 +++++++++------- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/service/internal/crashes/api.go b/service/internal/crashes/api.go index decd9bd2..e99b0670 100644 --- a/service/internal/crashes/api.go +++ b/service/internal/crashes/api.go @@ -139,7 +139,7 @@ func (a *API) Submit(w http.ResponseWriter, r *http.Request, member, channel str } e.Meta = json.RawMessage(red) } - id, sig, dup, err := a.Store().Insert(ctx, e, a.now().Add(-24*time.Hour)) + id, sig, dup, err := a.Store().Insert(ctx, e, a.now()) if errors.Is(err, ErrQuota) { w.Header().Set("Retry-After", "3600") a.refuse(w, http.StatusTooManyRequests, fmt.Sprintf("%d crashes a day from one address, %d from one camera, is the limit", DailyPerClient, DailyPerCamera)) diff --git a/service/internal/crashes/store.go b/service/internal/crashes/store.go index fb35b6d3..6a9bf5a0 100644 --- a/service/internal/crashes/store.go +++ b/service/internal/crashes/store.go @@ -58,10 +58,12 @@ var ErrQuota = errors.New("the daily limit is reached") // Insert stores the event, its bundle and its signature. A crash already // stored from the same camera (the same records, sent again) is not stored // twice: dup is true and the stored event's id and signature are returned. -// Otherwise the client's and the camera's crashes since are counted under -// their locks, so uploads racing each other cannot pass the daily limits -// together. -func (s *Store) Insert(ctx context.Context, e *Event, since time.Time) (id, signature string, dup bool, err error) { +// Otherwise the client's and the camera's crashes in the day before at are +// counted under their locks, so uploads racing each other cannot pass the +// daily limits together. at stamps the event too: the window and what it +// counts are read off one clock. +func (s *Store) Insert(ctx context.Context, e *Event, at time.Time) (id, signature string, dup bool, err error) { + since := at.Add(-24 * time.Hour) c := e.Crash fatal, _ := json.Marshal(c.Fatal) before, _ := json.Marshal(orEmpty(c.Before)) @@ -126,13 +128,13 @@ func (s *Store) Insert(ctx context.Context, e *Event, since time.Time) (id, sign _, err = tx.Exec(ctx, ` INSERT INTO crash_events (id, content_sum, signature_id, channel, mac_key, member_id, client_key, kind, in_irq, self_inflicted, title, firmware, majestic, soc, sensor, board, machine, kernel, kernel_build, kernel_built, - cmdline, uptime, records, modules, fatal, before, anomalies, leadup, meta, redacted, bundle_sha256) + cmdline, uptime, records, modules, fatal, before, anomalies, leadup, meta, redacted, bundle_sha256, received_at) VALUES ($1, $2, $3, $4, $5, nullif($6, ''), $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20, - $21, $22, $23, $24, $25, $26, $27, $28, $29, $30, $31)`, + $21, $22, $23, $24, $25, $26, $27, $28, $29, $30, $31, $32)`, e.ID, c.ContentSum, e.SignatureID, e.Channel, e.MACKey, e.Member, e.ClientKey, c.Kind, c.Fatal.InIRQ, c.SelfInflicted, c.Fatal.Title(), e.Firmware, e.Majestic, e.SoC, e.Sensor, c.Board, c.Machine, c.Kernel, c.KernelBuild, built, c.Cmdline, uptime, c.Records, orEmpty(c.Modules), fatal, before, anomalies, leadup, - meta, e.Redacted, e.BundleSHA256) + meta, e.Redacted, e.BundleSHA256, at) return err }) return id, signature, dup, err