Skip to content

Phase 1 follow-ups: unmatched rides, dedupe, env config, rewind-safe commits - #2

Merged
coderconnoisseur merged 9 commits into
phase1-correctnessfrom
phase1-followups
Oct 15, 2026
Merged

coderconnoisseur merged 9 commits into
phase1-correctnessfrom
phase1-followups

Conversation

@coderconnoisseur

Copy link
Copy Markdown
Owner

Summary

Follow-ups to Phase 1 and prep for Phase 2. Stacked on #1: review and merge #1 first, then retarget this PR to master.

Rewind-safe commits and panic recovery (pkg/kafkaconsumer)

  • The tracker is now epoch-stamped. When a consumer-group generation change makes kafka-go re-fetch from the last committed offset, the partition's state resets into a new epoch. Completions from stale pre-reset copies are ignored, so they can never commit past an unfinished re-fetched message. This is needed before going multi-partition.
  • A handler panic is recovered, logged with its stack, and dead-lettered as a permanent error. The service keeps running.

Env-var configuration (all services)

  • KAFKA_BROKERS, REDIS_ADDR, HTTP_ADDR and METRICS_ADDR replace the hardcoded localhost addresses.

  • Matching tuning:

    Variable Default Rule
    MATCH_WORKERS NumCPU*25 (was NumCPU*5) positive integer
    MATCH_DEADLINE 500ms (was 800 ms) must be greater than the 200 ms batch deadline
    REDIS_POOL_SIZE MATCH_WORKERS*5 sized so the parallel SETNX calls don't queue on the go-redis pool
  • Invalid values stop the service at startup. Each service has table-tested LoadConfig.

Unmatched rides (roadmap 1c)

  • Matching publishes ride.unmatched (RideID, Reason = no_drivers | match_deadline, RequestedAt, FailedAt) instead of only logging. Publish failures are retried.
  • Assignment errors (matching.ErrAssign) are always retried, even after the match deadline. A retry is safe because the existing assignment is replayed.

assignment-service (roadmap 1e)

  • Duplicate ride.matched events are deduped. assignments_total and end-to-end latency count each ride once. Duplicates go to assignment_duplicates_total.
  • It now consumes ride.unmatched in its own consumer group, assignment-service-unmatched, so a not-yet-created unmatched topic can't stall ride.matched.
  • If both outcomes arrive for a ride, matched wins. New GET /rides/<id> returns matched, unmatched, or 404. /assignments/<id> and /count are unchanged.

rider-simulator

  • A batched 20 ms ticker with a fractional accumulator lifts the ~400 RPS ceiling caused by Windows timer resolution.
  • When the client is saturated, it skips sends and counts them as Dropped instead of blocking.
  • Graceful shutdown.

Docs

  • README env-var table, /rides/<id>, refreshed known limitations.
  • docs/at-least-once-consumers.md, docs/driver-state-model.md and docs/rps-benchmarking-setup.md updated.

Known gaps (documented)

  • The new matcher defaults are unmeasured, so the benchmark ladder must be re-run.
  • A ride can flip from unmatched to matched if ride.priced is redelivered. A short-TTL outcome record is planned.
  • Rewind detection only fires when a re-fetched offset is at or below the last tracked one.
  • Multi-partition topics are still to do.

Testing

  • go vet, go build and go test -count=1 with GOWORK=off (CI mode), plus go test -race, pass in all 8 CI modules.
  • New tests:
    • tracker rewind sequences
    • panic sent to the DLQ
    • config parsing for every service
    • unmatched publishing and failure classification
    • store dedupe and precedence
    • metric gating
    • /rides/<id> responses
    • rider-sim pacing
  • Not yet run end to end against real Kafka and Redis.

@coderconnoisseur
coderconnoisseur merged commit 8bfa382 into phase1-correctness Oct 15, 2026
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant