G
YouTube TikTok Games
Skip to content

relay: event expiry is checked against a clock captured before the stream #953

Closed

Description

@chappie-daemon

handlers/subscribe.rs: is_expired_event(&event, now) uses a now captured once before the stored-read loop, while the comment beside it claims the guard is asked per event so a mid-read AUTH applies. The authorization genuinely is per event; the clock is not, so an event that expires while earlier records are being fetched is still delivered. The TypeScript path reads the clock per event.

Fix: read the clock per event.

Evidence: #948.

Activity

  1. added
    bugSomething isn't working
    rewritenostream 4.0 Rust/LMDB rewrite
    rustRust implementation work
    factoryBuilt by the software factory
    on Oct 24, 2026
  2. chappie-daemon commented on Nov 10, 2026

    @chappie-daemon
    CollaboratorAuthor

    PR open: #1108 — factory/027-expiry-clock at 3245656, base rewrite/main.

    • Two hunks in handlers/subscribe.rs: the pre-loop let now = context.now_secs(); deleted, context.now_secs() called at the expiry check, with a comment stating why. The clock seam already existed, so no new abstraction.
    • Oracle red at base (left: 3 / right: 2, the mid-read-expired 498f766a… record delivered) and green at head; a count-only assertion was shown to pass a wrong delivery, so the per-frame id checks are load-bearing; a now_ms() as i64 slip fails at the first record arrived: [].
    • One round: code PASS, architecture PASS; security/performance/ux/ui stood down on diff-level reasons. Static stage: 0 findings from 1 provider with status ran at this sha — a verdict, not an absence.
    • Gates: fmt 0, both clippy 0, 1247 passed / 0 failed / 4 ignored, release 0. Rollup at 3245656: 16 checks, 0 failing, CLEAN.
    • Cost: $0.96 actual vs $1.50 expected (−35.8%), 1 round, 0 resumes. Underspend recorded as actual + delta + note, no regret line.

    Two defects this run found and deliberately did not fold in: #1106 (60 production clock reads outside the Clock seam, eight files) and #1107 (nothing asserts expiry on the stored-read path). This issue stays open until the merge — rewrite/main is not the default branch, so no keyword can close it.

  3. chappie-daemon commented on Nov 10, 2026

    @chappie-daemon
    CollaboratorAuthor

    Fixed by #1108, merged as b42f8333663781572ceda76e13e486d1aded4bb4 into rewrite/main at 2026-11-10T15:56:01Z — a true merge of 2c151f2 + 3245656, tree byte-identical to the reviewed head.

    The expiry check now asks the clock as each record is judged instead of reusing an instant captured before the stored read, so an event that expires while earlier records are still being fetched is no longer delivered. One expression: is_expired_event(&event, context.now_secs()), replacing a hoisted now.

    Verified red-then-green: at base the fixture delivers 3 frames including the expired one; with the fix, 2. The new test in tests/connection.rs asserts by event id rather than frame count, which is what catches the plausible wrong fix (now_ms() as i64, milliseconds read as seconds) that a count-based assertion cannot see.

    Cost: $0.96 actual against $1.50 (−36%), one round, zero resumes, zero escapes. Static round 1 was a genuine zero — one provider ran, no findings at 3245656.

    Two classes this job deliberately left out of scope are filed: #1106 (60 production sites reading the system clock outside the Clock seam) and #1107 (nothing asserts that expiry withholds a stored event from a reader; the acceptance there is a red-at-2c151f2a test).

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingfactoryBuilt by the software factoryrewritenostream 4.0 Rust/LMDB rewriterustRust implementation work

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions