feat(servers): Phase 3 cutover (single listener, datastore type-swap, Keycloak removal)#206
Merged
Merged
Conversation
Port the RANK_TYPE_* name table from proto so the datastore can derive RankShort without importing proto. The gap at 24/25 and the decimal fallback for uncataloged ids are kept, so RankShort stays byte-identical after the swap to types. RankShort() does the prefix strip the datastore does today at four call sites. > *This was generated by AI*
The Datastore interface and the Mysql implementation now build types.* wire
structs directly. The rest handlers drop their *FromProto mappers and consume
the datastore results as-is.
- Delete FindProfileByKeycloakID and extractKeycloakID. The new profile types
carry no keycloak field, so nothing populates one.
- Remove the vestigial xenforo.ConnectedAccountJoin from the roster, profile,
position, s1-uniforms, and gamertag queries. It stays on FindProfileByDiscordID,
whose WHERE filters the connected-account columns. DiscordId still populates
from the Preload(clause.Associations) load, so the roster goldens hold.
- Derive RankShort through types.RankType, byte-identical to the old proto path.
- Move the ticket allocation discipline into the datastore: participants and
categoryAncestorIds serialize as [] when empty, customFields as {}, matching
the frozen tickets goldens.
The two xf_post aggregation SQL consts are untouched.
> *This was generated by AI*
Convert the rest fakes, the contract recording fake, and the datastore harness
tests to build types.* so they satisfy the swapped Datastore interface. Drop
the keycloak seed fields and the keycloak fake method along with them.
Retarget the contract golden harness at rest.New: the gRPC server and the
grpc-gateway are gone, so the corpus now replays directly against the stdlib
net/http stack instead of dialing a gRPC server behind a gateway. The same
goldens hold.
Guard the roster handlers so a nil Profiles map still serializes as
{"profiles":{}}, never null. The real datastore always allocates, but a nil map
slipping through used to be caught by the old per-handler mapper; the guard
keeps the frozen empty-result form.
> *This was generated by AI*
Bring the three boot-lifecycle pieces from servers/sentry.go into the rest package so they survive the deletion of the old stack: sentryDialCheck (warn- only DSN reachability check), sentryStartupProbe (one canary flushed at boot), and FlushSentryOnShutdown (the SIGTERM flush handler). SetupSentry now drives the dial-check and probe, matching what servers.Start did. The probe-stall and dial-check tests come along, adapted to the rest seam (sentryTransport). Add DocsHandler: it serves the embedded Swagger UI and OpenAPI specs at the same URLs the old gateway used, and stamps the spec's info.version from the build-time version. That replaces the release workflow's proto-sed step; a dev build leaves the sentinel untouched. > *This was generated by AI*
Rebuild servers.Start around one public listener and one internal metrics listener. The public :11000 listener serves rest.New(ds, rc) under /api and DocsHandler everywhere else, at the same URLs as before. The metrics handler binds on its own :9090 listener. The :10000 gRPC listener and the grpc-gateway translation layer are gone. InitTrustedProxies stays first, fatal on error (ADR 0005). The reference cache still warms before serving (New + Refresh + the refresh goroutine), since rest.New panics on a cold cache. SetupSentry(version) replaces the old Phase 0 setupSentry, and FlushSentryOnShutdown installs only when capture is enabled. Delete servers/grpc and servers/gateway whole, plus servers/sentry.go and its test. PORT was only the gateway's gRPC dial target, so servers.New takes no address and serve.go no longer reads it. cmd/example.go is the only remaining proto importer, left for Phase 4. > *This was generated by AI*
Review follow-up on the #134 cutover. - The internal-only metrics listener no longer takes the public API down. A bind failure now logs and serves without metrics, and a runtime Serve error is logged instead of calling Error.Fatalf. The public listener stays fatal since it is the service. The old dual stack made every listener fatal because both served traffic, which the metrics listener does not. - Extract buildPublicRouter from servPublic so the /api-vs-docs split is unit-testable without opening a listener. - Pin the Sentry boot wiring so FlushSentryOnShutdown is installed only inside the SetupSentry gate, using the same AST guard idiom as the trusted-proxy wiring. Those functions just changed packages, which is exactly when a refactor drops the call. > *This was generated by AI*
Match the sibling enums (rostertype and recordtype inline FormatInt/TrimPrefix) and point the tests at the public String()/RankShort() methods instead of the removed private helpers. Behavior is unchanged and RankShort stays byte-identical. > *This was generated by AI*
The cutover deleted servers/grpc, servers/gateway, and servers/sentry.go but left comments describing the old dual-stack world. Fix the load-bearing ones: a dangling reference to the deleted servers/gateway/gateway.go (rest/sentry.go), the rest package doc (it is the live single listener now, not test-mounted), the "legacy gateway reuses this until cutover" cluster across auth/gzip/metrics, and the metrics deploy-check (public /metrics returns 404 now, not 401). Also two test comments naming removed symbols. Comments only, no behavior change. > *This was generated by AI*
SyniRon
added a commit
that referenced
this pull request
Jun 20, 2026
… second port (#135) (#207) Phase 3 (#134/#206) collapsed serving to a single HTTP listener, leaving the proto/buf machinery referenced only by generated code and the gRPC example client. This removes all of it. Deleted surfaces: - proto/ in full: milpacs.proto, tickets.proto, every generated .pb.go, .pb.gw.go, and _grpc.pb.go, plus the proto-coupled deprecation_test.go. - buf config: buf.gen.yaml, buf.lock, buf.yaml, and the codegen tool pins in tools.go. - cmd/example.go, the only non-generated proto consumer (the gRPC sample client). serve still works; root's description no longer claims gRPC. - Generated Swagger 2.0 assets (milpacs.swagger.json, tickets.swagger.json) and openapi/openapi_test.go, which asserted the 2.0 keycloak surface. Docs serving now points at the 3.1 spec. Per the #117 ruling (no consumer codegens from the served spec), the hand-owned OpenAPI 3.1 document (openapi/openapi.yaml) replaces the retired 2.0 files at the docs URL. DocsHandler serves it at /openapi.yaml with build-version stamping, and the Swagger UI shell loads it. The golden corpus already validates this document, so the public contract stays covered. Build, CI, and deploy: - Makefile and magefile drop install/generate and the buf lint/breaking targets. make lint is now go vet. - go.yml drops the buf install and make generate steps; the lint job runs go vet. - build_and_push.yml drops the release-time proto-version sed. The version reaches the spec at runtime via the build-time version var. - Dockerfile drops make install, buf install, and make generate, and stops exposing the gRPC port. docker-compose and the nginx template drop the :10000 second port and its grpc_pass block, so there is one listener in code and in config. go mod tidy shrinks the build graph from 209 to 90 modules (about 119 removed), in line with the PRD's ~100-module estimate. grpc, grpc-gateway, buf, and protoc are gone from the graph entirely. The throwaway request-log analytics and the duration= obligation were already retired with the old gateway in Phase 3, so there is nothing to remove here. *This was generated by AI*
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Implements #134, the Phase 3 cutover of PRD #112 ("Goodbye gRPC"). This is one revertible change: the public surface moves onto the new net/http stack, the datastore returns the hand-written types instead of generated proto, and the gRPC server and grpc-gateway are removed.
What changed
Single listener. One public listener on :11000 serves the API under /api and the Swagger UI plus specs everywhere else. The spec's info.version is stamped in-process now, which replaces the CI proto-sed step. Prometheus moves to an internal-only :9090 listener. The gRPC server on :10000 and the grpc-gateway are gone.
Datastore type-swap. The Datastore interface, mysql.go, and tickets.go return types.* instead of proto.*, and the per-handler proto-to-wire mapping is deleted. A new types.RankType carries the rank-name table so RankShort stays byte-identical. The vestigial xf_user_connected_account JOIN is dropped from the roster, profile, position, s1, and gamertag queries; it stays only on the Discord lookup, where the WHERE filters its columns.
Keycloak removed. The lookup route now 404s, keycloakId is gone from both profile shapes, and the datastore method is deleted.
Old stack deleted. servers/grpc, servers/gateway, and servers/sentry.go are removed. The Sentry boot-lifecycle (dial-check, startup probe, SIGTERM flush) was ported into rest/. proto/, buf, and cmd/example.go stay for Phase 4 (#135); only cmd/example.go still imports proto.
Verification
Deploy notes
Known follow-up (not blocking)
The served docs still render the generated swagger, which still documents the Keycloak route. The route itself 404s and the fields are absent from responses; only the docs lag. The generated specs are deleted in Phase 4 (#135), when the hand-written openapi.yaml becomes the served spec.
After deploy
Close #94 (gateway dial deprecations, superseded) and #100 (Keycloak removal, delivered).
Stats: 58 files, +1,522 / -4,367.