You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: apps/api/README.md
+7-3Lines changed: 7 additions & 3 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -7,18 +7,21 @@ Minimal Go service for the public `events.vercount.one` surface.
7
7
-`/`
8
8
-`/healthz`
9
9
-`/js`
10
+
-`/bench/write`
10
11
-`/log`
11
12
-`/api/v1/log`
12
13
-`/api/v2/log`
13
14
15
+
`GET /bench/write` is a public benchmark helper for external probe tools. It always writes to the fixed synthetic target `https://bench.vercount.one/gurt`, returns the same v2-style JSON envelope used by the modern public API, and sends no-store cache headers so latency checks hit the Go service instead of cached content.
16
+
14
17
## Internal structure
15
18
16
19
The Go API stays intentionally small and is organized by runtime surface instead of generic web layers:
17
20
18
21
-`internal/app/runtime.go` - env/config loading and logger setup
19
22
-`internal/app/server.go` - dependency wiring and chi route registration
20
23
-`internal/api/public.go` - `/`, `/healthz`, and `/js`
21
-
-`internal/api/log.go` - `/log`, `/api/v1/log`, and `/api/v2/log`
24
+
-`internal/api/log.go` - `/bench/write`, `/log`, `/api/v1/log`, and `/api/v2/log`
22
25
-`internal/counter/*` - counter reads/writes and Redis-backed counting behavior
23
26
24
27
## Environment
@@ -123,8 +126,9 @@ If GHCR creates the package with private visibility on first publish, update the
123
126
124
127
1. Build the web app script so `apps/web/public/js/client.min.js` is up to date.
125
128
2. Deploy this service behind `events.vercount.one`.
126
-
3. Verify `/js`, `/log`, `/api/v1/log`, and `/api/v2/log` on the new host.
127
-
4. Confirm the dashboard in `apps/web` still reads the shared Redis data correctly.
129
+
3. Verify `/js`, `/bench/write`, `/log`, `/api/v1/log`, and `/api/v2/log` on the new host.
130
+
4. Confirm `/bench/write` writes only to the fixed synthetic benchmark target `https://bench.vercount.one/gurt` and returns an uncached v2-style success envelope.
131
+
5. Confirm the dashboard in `apps/web` still reads the shared Redis data correctly.
The Go events service currently exposes public metadata routes in `internal/api/public.go` and counter routes in `internal/api/log.go`. Real browser traffic writes counters through `POST /api/v2/log` with a JSON body, URL validation, rate limiting, Redis-backed UV/PV updates, and a v2 success envelope. External benchmarking tools such as itdog.cn can easily probe `GET` endpoints but cannot reproduce the browser's `POST` body flow, which makes existing public routes a poor fit for measuring real counter write latency from many regions.
4
+
5
+
The benchmark route needs to stay public, stable, and safe. It should measure the same counter write hot path without polluting customer domains, and it must avoid cache hits that would hide application latency.
6
+
7
+
## Goals / Non-Goals
8
+
9
+
**Goals:**
10
+
- Expose a public `GET /bench/write` route on the Go events host.
11
+
- Reuse the real Redis-backed counter write path so benchmark results reflect counter update latency rather than a no-op handler.
12
+
- Keep benchmark traffic isolated under the fixed synthetic tracked URL `https://bench.vercount.one/gurt`.
13
+
- Return a stable response contract that is easy to inspect from external benchmark tools.
14
+
- Prevent intermediary caches from turning the benchmark route into a static response.
15
+
16
+
**Non-Goals:**
17
+
- Reproduce browser-only behavior such as CORS preflight or JSON body decoding.
18
+
- Add a user-configurable benchmark target URL.
19
+
- Replace `/api/v1/log` or `/api/v2/log` as the primary embed-facing public API.
20
+
- Create a high-volume load-testing endpoint that bypasses the service's normal protections.
21
+
22
+
## Decisions
23
+
24
+
### Add a dedicated GET benchmark route that lives with the counter handlers
25
+
26
+
`GET /bench/write` will be registered as a public route in the Go API and implemented alongside the counter handlers so it can reuse existing validation, write, logging, and response helpers. Keeping the route near `V2Post` avoids duplicating the counter write orchestration and makes the benchmark path a thin facade over the existing write flow.
27
+
28
+
**Alternatives considered:**
29
+
- Put `/bench/write` in `public.go`: rejected because the route is logically a counter write operation and would otherwise need cross-package helper exposure.
30
+
- Ask probe tools to call `/api/v2/log` directly: rejected because the target tool only supports simple `GET` probes.
31
+
32
+
### Use a fixed synthetic target URL and force the full write path
33
+
34
+
The benchmark route will always target `https://bench.vercount.one/gurt` and will invoke the write flow with deterministic benchmark-owned inputs instead of reading query parameters. The route should execute the full counter write path, including the UV update branch, so the benchmark reflects the heavier real write path instead of a lighter read-mostly variation.
35
+
36
+
**Alternatives considered:**
37
+
- Accept a caller-supplied `url` parameter: rejected because it would mix benchmark traffic into customer namespaces and make results non-repeatable.
38
+
- Treat benchmark requests as returning visitors only: rejected because it would skip the fully write-backed UV path and underrepresent the cost of a fresh write.
39
+
40
+
### Return the v2-style success envelope and disable caching
41
+
42
+
The benchmark route will return a v2-style success response with the current counter data so the response shape stays familiar and machine-readable. The route will also send no-store cache headers so CDN or intermediary caches do not collapse repeated benchmark requests into a static hit.
43
+
44
+
**Alternatives considered:**
45
+
- Return `204 No Content`: rejected because benchmark tools and manual checks benefit from a visible response payload.
46
+
- Return plain v1 JSON: rejected because the benchmark route is modeling the modern public counter surface, which is v2-oriented.
47
+
48
+
### Reuse the existing request protections and logging middleware
49
+
50
+
The benchmark route will continue to flow through the existing request logging middleware and rate-limit checks. This keeps the route observable, preserves abuse protection, and keeps the benchmark representative of the public service path instead of introducing a privileged fast lane.
51
+
52
+
**Alternatives considered:**
53
+
- Exempt `/bench/write` from rate limiting: rejected because it increases abuse risk and makes the benchmark less representative of the public service path.
54
+
- Add separate benchmark-only logging: rejected because the current request and counter event logging is already sufficient.
55
+
56
+
## Risks / Trade-offs
57
+
58
+
-[Synthetic benchmark traffic inflates benchmark namespace counters] → Keep all benchmark writes pinned to `bench.vercount.one/gurt` so no customer-owned domains are affected.
59
+
-[GET benchmark results are not a byte-for-byte reproduction of browser POST behavior] → Document that `/bench/write` is a probe-friendly proxy for the real write path, not a full browser simulation.
60
+
-[Rate limiting may affect aggressive probe schedules] → Preserve the existing limit for realism and safety, and document that the endpoint is intended for external latency probes rather than sustained load generation.
61
+
-[Cache misconfiguration could hide real latency] → Require explicit no-store headers in the benchmark response.
62
+
63
+
## Migration Plan
64
+
65
+
1. Add the benchmark route and implementation in the Go events service.
66
+
2. Update the public service metadata/documentation to include `/bench/write`.
67
+
3. Deploy the Go API and verify repeated benchmark requests hit application logs rather than cache-only responses.
68
+
4. Use itdog.cn or similar probes against `/bench/write` to establish baseline regional latency.
69
+
70
+
Rollback is straightforward: remove the route from the router and documentation. The synthetic benchmark Redis keys can remain because they are isolated from customer traffic and expire under the existing counter TTL rules.
71
+
72
+
## Open Questions
73
+
74
+
- None for artifact readiness. The fixed route, target URL, and benchmark intent are now defined well enough to implement.
0 commit comments