6.4 KiB
Runtime and Components
The diagram below focuses on the deployed galaxy/user process and its
runtime dependencies.
flowchart LR
subgraph Callers
Auth["Auth / Session Service"]
Gateway["Edge Gateway"]
Lobby["Game Lobby Service"]
Geo["Geo Profile Service"]
Admin["Trusted admin callers"]
end
subgraph User["User Service process"]
InternalHTTP["Trusted internal HTTP listener\n/api/v1/internal/*"]
AdminHTTP["Optional admin HTTP listener\n/metrics"]
Services["Application services"]
Telemetry["Logs, traces, metrics"]
end
Redis["Redis\nkeyspace + domain-events stream"]
Auth --> InternalHTTP
Gateway --> InternalHTTP
Lobby --> InternalHTTP
Geo --> InternalHTTP
Admin --> InternalHTTP
InternalHTTP --> Services
Services --> Redis
InternalHTTP --> Telemetry
AdminHTTP --> Telemetry
Listeners
userservice exposes two HTTP listeners:
| Listener | Default addr | Purpose |
|---|---|---|
| Internal HTTP | :8091 |
Trusted business API under /api/v1/internal/* |
| Admin HTTP | disabled | Optional Prometheus metrics on /metrics |
Shared listener defaults:
- read-header timeout:
2s - read timeout:
10s - idle timeout:
1m
The internal application timeout is configured separately through
USERSERVICE_INTERNAL_HTTP_REQUEST_TIMEOUT.
Intentional omissions:
- no public listener
- no authenticated edge gRPC listener
- no built-in
/healthz - no built-in
/readyz
Startup Wiring
cmd/userservice loads config, constructs logging and telemetry, and then
creates the runtime through internal/app.NewRuntime.
The runtime wires, in order:
- one shared
*redis.Clientopened throughpkg/redisconnplus a Ping - one PostgreSQL pool opened through
pkg/postgres, instrumented withdb.sql.connection.*metrics, pinged, and migrated forward via the embeddedinternal/adapters/postgres/migrationsfilesystem - the PostgreSQL-backed user store from
internal/adapters/postgres/userstore(accounts, blocked-emails, entitlement snapshot/history/lifecycle, sanction history/lifecycle, limit history/lifecycle, listing index) - two Redis Stream publishers
(
internal/adapters/redis/domaineventsfor auxiliary domain events,internal/adapters/redis/lifecycleeventsfor trusted user-lifecycle events) sharing the same*redis.Client - the trusted internal HTTP router
- the optional admin metrics listener
- service-local helpers for clock, IDs, and validation/policy adapters
Startup fails fast when Redis or PostgreSQL connectivity is unavailable, the mandatory connection-topology environment variables are missing, the embedded migration sequence cannot be applied, or configuration is otherwise invalid. The HTTP listeners do not open until every dependency check passes.
Storage Backends
The service is split between two backends per
../../ARCHITECTURE.md §Persistence Backends:
PostgreSQL holds source-of-truth durable state in the user schema:
accounts(withemailanduser_nameUNIQUE;deleted_atrecords the Stage 22 soft-delete state)blocked_emails(one row per blocked address)entitlement_recordsplus the denormalisedentitlement_snapshotsone-row-per-user current viewsanction_recordsplussanction_active(user_id, sanction_code)limit_recordspluslimit_active(user_id, limit_code)
Indexes carry the listing surface (accounts(created_at DESC, user_id DESC)), reverse-lookup filters (accounts(declared_country),
entitlement_snapshots(plan_code, is_paid),
entitlement_snapshots(ends_at) WHERE is_paid AND ends_at IS NOT NULL,
sanction_active(sanction_code), limit_active(limit_code)), and the
per-user history scans.
Redis hosts only the two Stream publishers
(USERSERVICE_REDIS_DOMAIN_EVENTS_STREAM,
USERSERVICE_REDIS_LIFECYCLE_EVENTS_STREAM). It does not store any
durable user state after Stage 3 of PG_PLAN.md.
Decision records:
postgres-migration.md for the schema and
storage decisions.
Configuration Groups
Required for all process starts:
USERSERVICE_REDIS_MASTER_ADDRUSERSERVICE_REDIS_PASSWORDUSERSERVICE_POSTGRES_PRIMARY_DSN
Core process config:
USERSERVICE_SHUTDOWN_TIMEOUTUSERSERVICE_LOG_LEVEL
Internal HTTP config:
USERSERVICE_INTERNAL_HTTP_ADDRUSERSERVICE_INTERNAL_HTTP_READ_HEADER_TIMEOUTUSERSERVICE_INTERNAL_HTTP_READ_TIMEOUTUSERSERVICE_INTERNAL_HTTP_IDLE_TIMEOUTUSERSERVICE_INTERNAL_HTTP_REQUEST_TIMEOUT
Admin HTTP config:
USERSERVICE_ADMIN_HTTP_ADDRUSERSERVICE_ADMIN_HTTP_READ_HEADER_TIMEOUTUSERSERVICE_ADMIN_HTTP_READ_TIMEOUTUSERSERVICE_ADMIN_HTTP_IDLE_TIMEOUT
Redis connectivity (consumed by pkg/redisconn):
USERSERVICE_REDIS_REPLICA_ADDRS(optional, comma-separated)USERSERVICE_REDIS_DBUSERSERVICE_REDIS_OPERATION_TIMEOUT
Stream-shape (kept service-local):
USERSERVICE_REDIS_DOMAIN_EVENTS_STREAMUSERSERVICE_REDIS_DOMAIN_EVENTS_STREAM_MAX_LENUSERSERVICE_REDIS_LIFECYCLE_EVENTS_STREAMUSERSERVICE_REDIS_LIFECYCLE_EVENTS_STREAM_MAX_LEN
PostgreSQL connectivity (consumed by pkg/postgres):
USERSERVICE_POSTGRES_REPLICA_DSNS(optional, comma-separated)USERSERVICE_POSTGRES_OPERATION_TIMEOUTUSERSERVICE_POSTGRES_MAX_OPEN_CONNSUSERSERVICE_POSTGRES_MAX_IDLE_CONNSUSERSERVICE_POSTGRES_CONN_MAX_LIFETIME
The retired Redis variables USERSERVICE_REDIS_ADDR,
USERSERVICE_REDIS_USERNAME, USERSERVICE_REDIS_TLS_ENABLED,
USERSERVICE_REDIS_KEYSPACE_PREFIX produce a startup error from
pkg/redisconn if set; unset them before starting the service.
Telemetry:
OTEL_SERVICE_NAMEOTEL_TRACES_EXPORTEROTEL_METRICS_EXPORTEROTEL_EXPORTER_OTLP_PROTOCOLOTEL_EXPORTER_OTLP_TRACES_PROTOCOLOTEL_EXPORTER_OTLP_METRICS_PROTOCOLUSERSERVICE_OTEL_STDOUT_TRACES_ENABLEDUSERSERVICE_OTEL_STDOUT_METRICS_ENABLED
Runtime Notes
- The service remains internal REST only; gateway owns external authenticated gRPC and FlatBuffers.
- Gateway self-service traffic reaches this service over REST/JSON after gateway-side authentication and FlatBuffers transcoding.
- Current direct synchronous callers are
Auth / Session Service,Edge Gateway,Game Lobby Service,Geo Profile Service, and trusted admin callers. - Domain-event publication is auxiliary. A failed auxiliary consumer must not become the source of truth for current account state.