feat: use postgres

This commit is contained in:
Ilia Denisov
2026-04-26 20:34:39 +02:00
committed by GitHub
parent 48b0056b49
commit fe829285a6
365 changed files with 29223 additions and 24049 deletions
+72
View File
@@ -137,6 +137,78 @@ To avoid divergence:
- Geo Profile Service must then synchronously update the current value in `User Service`.
- A version should become effective only after the `User Service` update succeeds.
## Persistence Backends
The service follows the platform-wide split described in
[`../ARCHITECTURE.md §Persistence Backends`](../ARCHITECTURE.md#persistence-backends);
the staged migration plan that established this split is
[`../PG_PLAN.md`](../PG_PLAN.md). Per-service decisions and any deviation
from the platform-wide rules will be captured in
`docs/postgres-migration.md` once implementation begins, in the same
shape as
[`../user/docs/postgres-migration.md`](../user/docs/postgres-migration.md),
[`../mail/docs/postgres-migration.md`](../mail/docs/postgres-migration.md),
[`../notification/docs/postgres-migration.md`](../notification/docs/postgres-migration.md),
and [`../lobby/docs/postgres-migration.md`](../lobby/docs/postgres-migration.md).
Geo Profile Service owns the `geoprofile` schema in the shared `galaxy`
PostgreSQL database. A dedicated `geoprofile` PG role connects with grants
restricted to its own schema (defense-in-depth, expressed in the initial
migration).
PostgreSQL is the source of truth for all durable
[§Data Entities](#data-entities) of the service:
- `country_observation` — durable observed-country fact rows.
- `device_session_country_score` — per-`device_session_id` weighted
ranking.
- `device_session_geo_state` — current `usual_connection_country` per
`device_session_id`.
- `user_review_state``country_review_recommended` plus last evaluation
timestamp.
- `declared_country_version` — immutable history of approved
`declared_country` changes (status `recorded` / `applied` /
`sync_failed`).
- `session_block_action` — local audit of block-request outcomes.
- Ingest-queue lifecycle (`accepted` / `processing` / `processed` /
`failed`, see [§Internal Queue and Worker Pipeline](#internal-queue-and-worker-pipeline))
is materialised as `status` / `next_attempt_at` columns on the durable
observation row and worked off via
`SELECT ... FOR UPDATE SKIP LOCKED` — the same pattern Mail and
Notification already use for their durable retry schedules.
Redis carries only ephemeral runtime-coordination signals if and when
they appear during implementation (short worker leases on processing
claims, transition-deduplication windows for review-flag notifications).
No durable business state lives on Redis. The `notification:intents`
Redis Stream is used solely as a producer channel through which this
service publishes `geo.review_recommended` intents (see
[§Integration with Notification Service](#integration-with-notification-service));
that connection is built via `pkg/redisconn`.
Stack:
- driver `github.com/jackc/pgx/v5`, exposed as `*sql.DB` via
`github.com/jackc/pgx/v5/stdlib`;
- query layer `github.com/go-jet/jet/v2` (PostgreSQL dialect) with
generated code committed under `internal/adapters/postgres/jet/` and
regenerated by `make jet`;
- migrations via `github.com/pressly/goose/v3` library API embedded with
`//go:embed`, applied at service startup before any listener becomes
ready (non-zero exit on failure);
- testcontainers-backed unit tests using
`github.com/testcontainers/testcontainers-go/modules/postgres`;
- all Postgres connections are opened through
[`pkg/postgres`](../pkg/postgres); all Redis connections through
[`pkg/redisconn`](../pkg/redisconn).
Every `time.Time` value crossing the SQL boundary is normalised to UTC
on bind and scan, per the platform-wide rule on `timestamptz` handling.
The full target environment-variable matrix
(`GEOPROFILE_POSTGRES_*`, `GEOPROFILE_REDIS_*`) is fixed in
[`PLAN.md` Stage 00](PLAN.md#stage-00--persistence-stack-and-backend-assignment).
## High-Level Architecture
```mermaid