In-Memory Catalogue Search Service in Go
High-performance Go microservice that holds an in-memory product catalogue snapshot and serves search queries without database round-trips using lock-free atomic pointer swaps.
An in-memory product search microservice in Go, serving the Tree Coffee storefront. Postgres remains the source of truth; this service loads a read-only snapshot into RAM and answers every search from memory, with zero database round-trips per query.
Highlights
- Lock-free reads through an atomic snapshot pointer — the store wraps its snapshot in
atomic.Pointer[Snapshot], so readers never take a lock and writers never block them. A refresh builds a whole new snapshot and swaps the pointer, which means there is no torn read and no half-updated catalogue visible to a request mid-swap. - A failed refresh keeps serving the old data — if reloading from Postgres fails, the previous snapshot stays in place. The service degrades to stale results instead of returning errors, which is the right failure mode for a search box.
- The port does not open until the data is there —
Bootstrap()retries with backoff and must succeed before the HTTP listener starts, so the service is never reachable in an empty state that would return "no products found" to real customers. - Normalisation happens once at load, not per request — name, description, brand and tags are lowercased when the snapshot is built. Doing it per query would repeat the same work on every keystroke of a search-as-you-type box.
- Tiered ranking that imitates full-text search without a database — exact name match outranks name prefix, which outranks name contains, which outranks word match, which outranks a weak match. It reproduces the useful part of Postgres full-text ranking in memory.
- Inactive products never enter RAM — the loader filters
is_active = truein SQL, so deactivated stock cannot leak into results through a forgotten check in application code. - The API DTO is deliberately narrower than the model — flash-sale stock, sold counts and priority exist in the query and the internal model but are intentionally not exposed, so internal merchandising state does not become a public field by accident.
Key Features
- Product search endpoint — tiered relevance ranking over the in-memory snapshot, answering without touching Postgres.
- Authenticated refresh —
POST /admin/refreshrebuilds the snapshot on demand behind a bearer token, so a catalogue change can be published without a redeploy. - Health endpoint —
/healthz, wired to Fly.io's 15-second health check. - Snapshot bootstrap with backoff — startup retries until the catalogue loads, then opens the port.
- Multi-stage Docker build — a small runtime image produced from a full build stage.
- Scale-to-zero deployment — machines auto-stop and auto-start with
min_machines_running = 0on a 512 MB shared-CPU VM.
Tech Stack
- Go 1.26.4
- Fiber v2.52 (HTTP framework, on fasthttp)
- pgx/v5 + pgxpool (Postgres access against Supabase)
- Docker multi-stage build
- Fly.io, primary region
sin(Singapore)
Status & Maturity
Active, serving both the SvelteKit storefront and the Android app. A single module rather than a monorepo — main.go plus three packages (internal/config, internal/handlers, internal/store) — with no Makefile, no linter and no CI in the repository, so go build, go vet, gofmt -l and go test run manually as the minimum line of defence. The five tests use only the standard library, with no testify and no mocks.
Measured Metrics
| Commits | 12 |
| Date Range | 9 Jul 2026 – 15 Jul 2026 |
| Lines of code | 3,120 |
| Tests | 5 tests, all passing |