Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
56b9204
docs: hyperpb migration implementation plan (Waves 0-2)
jfberry Jul 6, 2026
748d7d1
feat: pogoshim typed accessors over protoreflect/hyperpb
jfberry Jul 6, 2026
4679340
feat: per-method proto decode engine (hyperpb arenas + PGO, std fallb…
jfberry Jul 6, 2026
d7ef277
fix: atomic PGO-done flag and hyperpb-wiring test assertions
jfberry Jul 6, 2026
2e85832
feat: sampled shadow verification of hyperpb vs std decode
jfberry Jul 6, 2026
75d5b5d
fix: shadow digest coverage — missing fields, uniform presence foldin…
jfberry Jul 6, 2026
6b0ffa1
feat: Encounter/DiskEncounter on the proto engine (Wave 1)
jfberry Jul 6, 2026
9872086
feat: fort/station/incident decode on pogoshim (Wave 2a)
jfberry Jul 6, 2026
3dc2411
feat: pokemon/weather decode on pogoshim, weather consensus retains v…
jfberry Jul 6, 2026
c6439dc
feat: GMO decode on the hyperpb engine (Wave 2)
jfberry Jul 6, 2026
e94c20b
fix: hoist arena-backed timestamp out of ProactiveIVSwitch goroutine …
jfberry Jul 6, 2026
f14f1e0
docs: proto engine config, PGO targets, and engine architecture notes
jfberry Jul 6, 2026
eab6274
docs: correct PGO and shadow-logging descriptions
jfberry Jul 6, 2026
6e1c326
fix: clone strings/bytes out of hyperpb arenas in pogoshim getters
jfberry Jul 6, 2026
c4705e0
fix: shadow digest covers fort active_pokemon (lure pipeline)
jfberry Jul 6, 2026
778fa30
docs/ops: PGO wording, go mod tidy, engine-value warning, cache comment
jfberry Jul 6, 2026
b07b553
docs: Wave 3 implementation plan
jfberry Jul 7, 2026
943a406
feat: engine foundation for Wave 3 (roots, handles, config default/ov…
jfberry Jul 7, 2026
9e2fc8f
docs: guide for adding a new proto to the decode pipeline
jfberry Jul 7, 2026
0d4c736
feat: fort details, gym info, quests on the proto engine (Wave 3a)
jfberry Jul 7, 2026
565f14f
fix: clone repeated-string list elements out of hyperpb arenas (Strin…
jfberry Jul 7, 2026
b90b2e0
feat: map forts, routes, incidents on the proto engine (Wave 3b)
jfberry Jul 7, 2026
564b9a1
feat: tappables, stations, contests, rsvps, social on the proto engin…
jfberry Jul 7, 2026
3d87419
docs: Wave 3 engine coverage, config resolution, and pipeline guide a…
jfberry Jul 7, 2026
4b45aa9
docs: comment out legacy proto_engine keys in the example (overrides …
jfberry Jul 7, 2026
482a9da
fix: adapt map-fort cache flow test to OtterCache API post-rebase
jfberry Jul 7, 2026
4373d33
feat: dump shadow-mismatch payloads to disk for offline diagnosis
jfberry Jul 7, 2026
63a6ce0
fix: free pending PGO profiles at the warmup deadline
jfberry Jul 7, 2026
229c056
test: deadline releases pending profile, keeps baseline parser
jfberry Jul 7, 2026
39f28af
fix: disable hyperpb runtime PGO by default — Recompile corrupts repe…
jfberry Jul 7, 2026
d95db62
docs: link upstream issue bufbuild/hyperpb-go#39 from PGO canary and …
jfberry Jul 7, 2026
07558e9
build(docker): fix missing package during docker build
na-ji Jul 7, 2026
31ef639
fix: rename cache -> ottercache in map-fort cache flow test (post-reb…
jfberry Jul 7, 2026
2423811
revert: restore cache/ to .dockerignore (root cause fixed by ottercac…
jfberry Jul 7, 2026
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
46 changes: 46 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,52 @@ Level 30+ is required for most methods to ensure data quality.

Processing is gated by `ScanParameters` — a set of boolean flags resolved from config scan rules based on the request's `scan_context` and geographic location. This allows different scan areas to process different entity types.

### Proto decode engines

Golbat supports two protobuf decoders selected per method via `[proto_engine]`
config. See `docs/adding-a-proto.md` for the full pipeline (accessor
generation → engine registration → decode wiring → shadow coverage → tests)
to follow when adding a new method or proto.

**Standard decoder** (`"std"`): protobuf-go, the original stable decoder.

**Hyperpb decoder** (`"hyperpb"`): High-performance arena-allocated decoder via `buf.build/go/hyperpb`, wrapped by generated `pogoshim` accessors. Generated by `cmd/pogoshimgen` via `scripts/genshim.sh` after `vbase.pb.go` updates. Arena-allocated message access is faster (cache-friendly layout, reduced allocations) but requires careful lifetime management:
- Shim accessors are generated as thin wrappers. They never escape `decodeWithArena()` — no storage in caches, channels, or goroutines; data is copied on demand.
- Payload bytes are owned by the arena and freed when `decodeWithArena()` returns. Getters that need persistence copy into independently-allocated structures (strings/bytes are cloned out of the arena; repeated strings via `StringList`).
- The `pogoshim` generated file is several hundred KB and treated as generated code (spot-check only on review). `pogoshim/manual.go` hand-writes the one construct the generator doesn't emit — `map<K,V>` fields — following the generator's own conventions; see that file's header before extending it.

**Coverage**: as of Wave 3, every client-proto method Golbat decodes runs
through the proto engine — GMO, encounters, fort details, gym info, quests,
map forts, routes, incidents, invasions, battle state, contests, size-
leaderboard entries, station details, tappables, event RSVPs, and social
(proxy request/response, friend details, player search). The one exception is
the push-gateway lobby-count path (`decode_push_gateway.go`): it extracts a
handful of scalars and nothing else, so it stays on plain `proto.Unmarshal`
unconditionally — there's no `*pogo.X`/`pogoshim.X` retention hazard to design
around, and std is already as cheap as that low-volume message type needs
(see the doc comment on `decodePushGateway`).

**Engine selection** (`[proto_engine]` config) resolves per method in this order (see `engineFor` in `protoengine.go`):
1. A legacy explicit key (`gmo`, `encounter`, `disk_encounter`), if non-empty — wins outright, preserving pre-Wave-3 config compatibility.
2. `[proto_engine.overrides].<method>`, if present and non-empty — one entry per method key (see `protoengine.go`'s `engMethod*` constants and `engineSpecs` table for the full list).
3. `default` (config default: `"hyperpb"`).

An unrecognized value (anything other than `"std"`, `"hyperpb"`, or `""`) logs a one-time `[PROTO_ENGINE]` warning at startup and falls back to running on `"std"`.

- **Rollback**: set the method to `"std"` via its legacy key (gmo/encounter/disk_encounter) or an `[proto_engine.overrides]` entry, then restart — no rebuild needed.

**Shadow verification** (config `shadow_sample_rate`, default 0.01):
- Sampled dual-decode: both engines process a subset of packets, compare decoded field digests.
- Mismatch → `golbat_proto_shadow_total{result="mismatch"}` incremented, `[PROTO_SHADOW]` log at ERROR.
- Set to 0 to disable; useful for validating engine parity after updates.

**Go compiler PGO** (`default.pgo`, build-time):
- `default.pgo`, when present, is automatically applied by Go (>= 1.21) when building the main package via the default `-pgo=auto` build flag. It is captured via `make pgo-capture` (see below) and should be committed to the repo once a production profile exists — not yet present on this branch.

**Hyperpb runtime PGO** (config `proto_engine.pgo`, default `true`):
- Runtime warmup (`proto_engine.pgo`, DEFAULT OFF): when enabled, the first 256 packets per method (or 10 minutes, whichever comes first) record a live-traffic profile and the parser tables are recompiled from it. Off by default because hyperpb v0.1.3's Recompile duplicates repeated-string elements (see the TestHyperpbRecompileRepeatedStringDuplication canary — it fails when an upstream upgrade fixes this, which is the signal to re-enable).
- Refresh periodically: `GOLBAT_URL=https://host:9001 GOLBAT_SECRET=... make pgo-capture` on a production instance captures a profile and commits it.

## Entity Model

### Struct Pattern
Expand Down
25 changes: 25 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,29 @@ FLAGS = go_json
golbat: FORCE
go build -tags $(FLAGS) golbat

# --- Profile-guided optimization --------------------------------------------
# Go (>= 1.21) automatically applies ./default.pgo when building the golbat
# main package — locally and in the Docker build (which copies the repo). So
# committing default.pgo makes every build profile-guided; no flag needed.
# Refresh it occasionally (game updates shift hot paths), then commit:
#
# GOLBAT_URL=https://host:9001 GOLBAT_SECRET=... make pgo-capture
#
GOLBAT_URL ?= http://127.0.0.1:9001
GOLBAT_SECRET ?=
PGO_SECONDS ?= 120

pgo-capture: FORCE ## capture a CPU profile from a running golbat into default.pgo
curl -fsS $(if $(GOLBAT_SECRET),-H "X-Golbat-Secret: $(GOLBAT_SECRET)") \
"$(GOLBAT_URL)/debug/pprof/profile?seconds=$(PGO_SECONDS)" -o default.pgo.tmp
mv default.pgo.tmp default.pgo
@echo "captured default.pgo ($$(du -h default.pgo | cut -f1)) — commit it to apply to all builds"

pgo-status: FORCE ## report whether builds will be profile-guided
@if [ -f default.pgo ]; then \
echo "default.pgo present ($$(du -h default.pgo | cut -f1)) — builds are profile-guided"; \
else \
echo "no default.pgo — builds are NOT profile-guided (run make pgo-capture against prod)"; \
fi

FORCE: ;
Loading
Loading