Skip to content

fix(exchange): in-flight reservations disappear from daily headroom at UTC rollover #657

Closed

Description

@cristim

Summary

Active RI exchange reservations can stop counting at midnight UTC before their execution settles. This is separate from the same-day AUTO concurrency fix in LeanerCloud/cloud-commitments-go#41.

Current behaviour

At platform baseline 3131779, internal/config/store_postgres.go:3059-3065 sums completed and processing rows only when COALESCE(completed_at,updated_at) lies inside the requested UTC day. The go#41 atomic reservation change reuses this daily-window policy, so an unsettled prior-day processing row remains excluded after rollover.

Steps to verify the gap

Use a frozen clock, fixture execution and real local PostgreSQL; do not make purchases. Hold a day-A AUTO reservation of1000 in execution across midnight. A day-B daily read sees zero from that hold and permits another1000 reservation. If both fresh quotes settle for950 on dayB, the day-B accepted total becomes1900 under cap1000. This is a code-derived scenario, not an observed production purchase.

Expected behaviour

Active reservations remain counted conservatively until reconciled, including across UTC midnight. Completed prior-day spend resets according to a clearly defined acceptance-day policy.

Proposed fix and acceptance

Define acceptance-day accounting and keep active processing holds included across rollover under the same global reservation lock. Add frozen-clock real-database coverage spanning midnight, exact UTC boundaries, completed prior-day reset and active-hold retention. Relevant path: internal/config/store_postgres.go, GetRIExchangeDailySpend and atomic ReserveRIExchange.

References and severity

Refs LeanerCloud/cloud-commitments-go#41. High severity when triggered; limited timing-dependent impact. No failing regression or production incident is claimed by this tracking issue.

Activity

  1. cristim commented on Oct 9, 2026

    @cristim
    MemberAuthor

    Deployed: merged in #690 as cf0b1a0 (squash, head 6bafb61).

    Fix: the daily spend query now counts every processing row regardless of day (an unsettled hold), and completed rows on their completed_at UTC day.

    Verified (real PostgreSQL 16, local image, no purchases or cloud calls):

    • a frozen day-B read counts a day-A unsettled $1000 hold and not the completed day-A $500
    • exact UTC boundary: completed at 23:59:59.999999 counts for day A only, at 00:00:00 for day B only
    • a 30h-old processing hold still reduces headroom for a new AUTO reservation
    • mutation check: reverting to the day-windowed query fails the rollover tests (independent reviewer reproduced); all 25 required CI checks green at the merged head

    Test note: the table has a BEFORE UPDATE trigger that rewrites updated_at, so back-dating in tests bypasses it with session_replication_role=replica on the dedicated test DB.

    Known consequence: a row stuck in processing (crash mid-exchange) holds headroom on every later day until settled; tracked in #692. Recommend closing.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions