Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 8 additions & 40 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,20 +10,13 @@ When many users hit `/book` at once, classic row-level booking patterns contend

![Architecture](./architecture.png)

<<<<<<< HEAD
Flow (current implementation):
1. Client sends `POST /book` with `X-User-Id`.
2. Redis Lua allocation is used for atomic allocation + idempotent replay behavior.
3. Booking events are written to a Redis Stream.
4. A separate **worker** process (`go run . worker`) consumes the stream and persists sold ticket ownership to MySQL.
1. Client sends `POST /book` with `X-User-Id` (optional `X-API-Key` if configured).
2. Redis Lua allocation: `HGET` prior booking, or `LPOP` + `HSET` + `XADD` (atomic + idempotent replay).
3. Booking events go to a Redis Stream.
4. A separate **worker** process (`go run . worker`) consumes the stream and persists sold ticket ownership to MySQL. Replay re-enqueues persist if Redis says booked but MySQL lags.

**Important:** the API server (`go run .`) does **not** run the persist consumer. A serve-only deployment accepts bookings into Redis but does not write `sold` rows to MySQL until a worker is running.
=======
1. Client `POST /book` with `X-User-Id` (optional `X-API-Key` if configured)
2. Redis Lua: `HGET` prior booking → or `LPOP` + `HSET` + `XADD`
3. Stream consumer writes sold state to MySQL
4. Replay path re-enqueues persist if Redis says booked but MySQL lags
>>>>>>> origin/main

## Tech stack

Expand All @@ -32,22 +25,11 @@ Flow (current implementation):
| API | Go 1.22, `net/http` |
| Allocation | Redis 7, embedded Lua (`internal/booking`) |
| Persist | Redis Streams consumer (`internal/persist`), MySQL 8 via GORM |
| Ops | Docker Compose (mysql, redis, seed, server), Makefile test targets |
| Ops | Docker Compose (mysql, redis, seed, server, worker), Makefile test targets |
| Load | k6 (`test.js`), legacy wrk Lua (`test.lua`) |
| UI | React + Vite dashboards under `frontend/` (Dashboard, Login, Queue Simulation) |

<<<<<<< HEAD
## API surface
| Method | Endpoint | Purpose |
|---|---|---|
| `POST` | `/book` | Attempt booking for one user (`X-User-Id` required) |
| `GET` | `/tickets/count` | Remaining tickets in Redis queue |
| `GET` | `/metrics` | Request/success/failure counters |
| `GET` | `/healthz` | Liveness probe |
| `GET` | `/readyz` | Readiness probe (Redis + MySQL; consumer check is worker-only in a split deployment) |
=======
## Key engineering decisions
>>>>>>> origin/main

- **Redis owns hot allocation**; MySQL owns durable sold state (eventual consistency by design)
- **Idempotent user booking**: same `user_id` retries return the same `ticket_id` with HTTP 200
Expand All @@ -72,7 +54,7 @@ Flow (current implementation):
| GET | `/tickets/count` | Remaining Redis queue length |
| GET | `/metrics` | Request / success / failure counters |
| GET | `/healthz` | Liveness |
| GET | `/readyz` | Readiness (Redis + MySQL + consumer checks) |
| GET | `/readyz` | Readiness (Redis + MySQL; consumer check is worker-only in a split deployment) |

```bash
curl -X POST http://localhost:8080/book -H "X-User-Id: 101"
Expand All @@ -82,7 +64,7 @@ curl -X POST http://localhost:8080/book -H "X-User-Id: 101"

**No published throughput or latency numbers yet.** `docs/BENCHMARKS.md` is a measurement plan only — append runs with git SHA when executed. The repo includes k6 scripts and correctness checks (unique ticket↔user mapping, inventory conservation) to support that work.

Do **not** treat the old GitHub description claim of “100k requests” as a verified result.
Do **not** treat any old GitHub description claim of unverified request volume as a measured result.

## Getting started

Expand All @@ -91,17 +73,15 @@ docker compose up
# API: http://localhost:8080
```

<<<<<<< HEAD
This starts:
- `mysql` (MySQL 8)
- `redis` (Redis 7)
- one-shot `seed` service (`go run . seed`)
- `server` service (`go run .`) — HTTP API only
- `worker` service (`go run . worker`) — stream consumer (required for MySQL persistence)

Default API endpoint: `http://localhost:8080`

## Local development

If you already run MySQL and Redis locally:

```bash
Expand All @@ -119,7 +99,6 @@ go run . reconcile
```

## Configuration
Environment variables used by runtime and compose setup:

| Variable | Purpose |
|---|---|
Expand All @@ -132,18 +111,7 @@ Environment variables used by runtime and compose setup:
| `TICKET_API_KEY` | Optional API key for protected booking access |
| `TICKET_CORS_ORIGIN` | Allowed CORS origin (default `*`) |

## Testing and verification
=======
Local (MySQL + Redis already running):

```bash
go run . seed
go run .
go run . reconcile # optional
```

## Testing
>>>>>>> origin/main

```bash
make test
Expand Down
Loading