Contents Menu Expand Light mode Dark mode Auto light/dark, in light mode Auto light/dark, in dark mode Skip to content
Adriano
Logo
Adriano
  • Users
    • Signing in
    • Concepts
    • Searching the catalog
    • Reading a decision
    • Errata: corrections to decisions
    • Reviewing decisions
    • Writing decisions and errata
    • Checking and refreshing sources
  • Operators
    • Deploying Adriano
      • Docker Compose
      • Kubernetes
    • Configuration reference
    • The catalog file
    • Authentication and access
    • Connecting sources
    • Pushing candidates from CI
    • Polling sources
    • Health checks and logs
    • Backup, restore and retention
    • Upgrading
    • Migrating from the ADR viewer
    • Running several replicas
    • Troubleshooting
    • Command-line reference
  • Developers
    • Architecture
    • Design decisions
      • 0001: Keep the first release governance-first
      • 0002: One backend, one SPA, one database
      • 0003: PostgreSQL for storage and full-text search
      • 0004: Serve every read from the local index
      • 0005: Poll publishes, push proposes, one ingest path
      • 0006: Bind approval to identity and content digest
      • 0007: Approval history lives in the database, append-only
      • 0008: Schedule polling outside the web workers
      • 0009: One configuration file, read at startup
      • 0010: Standard OIDC, stateless bearer tokens
      • 0011: A gated development login
      • 0012: Per-registration push tokens for CI
      • 0013: Keep records without a valid UUID, never invent one
      • 0014: Derive display status from links
      • 0015: Errata are separate records reviewed like decisions
      • 0016: A filesystem source type beside GitLab
      • 0017: Vuetify, and Vuetify first
      • 0018: Compute relationship issues at read time
      • 0019: Derive approval state on read, in SQL and in Python
      • 0020: One container image for the API and the web UI
      • 0021: One TLS context for outbound HTTPS, with additive extra CAs
    • Domain model
      • Records
      • Content hashing
      • Approval
      • Errata
      • Database schema
    • Invariants
    • Security
    • Performance
    • Getting started
    • Frontend
    • Testing
    • Code style
    • Contributing
    • HTTP API
    • Python API reference
      • app
      • app.catalog_config
      • app.cli
      • app.composition
      • app.config
      • app.database
      • app.errors
      • app.main
      • app.source_config
      • app.tls
      • app.validation_messages
      • app.web
      • app.auth
      • app.auth.deps
      • app.auth.dev
      • app.auth.oidc
      • app.auth.push
      • app.auth.rbac
      • app.auth.tokens
      • app.domain
      • app.domain.approval
      • app.domain.errata
      • app.domain.fields
      • app.domain.frontmatter
      • app.domain.hashing
      • app.domain.issues
      • app.domain.records
      • app.domain.relationships
      • app.domain.titles
      • app.domain.validation
      • app.domain.values
      • app.routers
      • app.routers.auth_dev
      • app.routers.candidates
      • app.routers.decisions
      • app.routers.facets
      • app.routers.health
      • app.routers.me
      • app.routers.records
      • app.routers.sources
      • app.schemas
      • app.schemas.approval
      • app.schemas.auth
      • app.schemas.records
      • app.schemas.sources
      • app.services
      • app.services.approval_view
      • app.services.decisions
      • app.services.dev_roles
      • app.services.diff
      • app.services.errata_view
      • app.services.ingest
      • app.services.intake
      • app.services.intake_checks
      • app.services.poll_plan
      • app.services.poll_trigger
      • app.services.polling
      • app.services.record_detail
      • app.services.record_relationships
      • app.services.registrations
      • app.services.search
      • app.services.seed_synthetic
      • app.services.seed_synthetic_errata
      • app.services.seed_synthetic_gen
      • app.services.seed_synthetic_text
      • app.services.sources_view
      • app.sources
      • app.sources.filesystem
      • app.sources.gitlab
      • app.sources.gitlab_api
      • app.sources.gitlab_settings
      • app.sources.port
      • app.sources.registry
      • app.storage
      • app.storage.errata_values
      • app.storage.port
      • app.storage.postgres
      • app.storage.postgres.convert
      • app.storage.postgres.errata
      • app.storage.postgres.events
      • app.storage.postgres.migrations
      • app.storage.postgres.models
      • app.storage.postgres.records
      • app.storage.postgres.registrations
      • app.storage.postgres.search
      • app.storage.postgres.store
      • app.storage.values
Back to top
View this page

0003: PostgreSQL for storage and full-text search¶

Status:

Accepted

Date:

2026-10-19

Context¶

Approval evidence must be durable (an in-memory store is not acceptable), and search must cover titles, Markdown bodies and useful metadata with a P95 under one second. The specification also asked that catalog logic not depend on a particular database product.

Decision¶

Use PostgreSQL through psycopg 3, SQLAlchemy 2 and Alembic. Search uses a weighted tsvector column on record (title A; UUID, project and path B; body C with the simple configuration; body D with english stemming), maintained by a trigger and indexed with GIN. Services depend only on the CatalogStore protocol in app.storage.port; app.storage.postgres is its only implementation.

Consequences¶

  • No second stateful service for search.

  • The simple configuration keeps identifiers, UUIDs and paths searchable as exact tokens; english adds stemmed body matches.

  • Concurrency relies on PostgreSQL features (advisory locks, ON CONFLICT, partial unique indexes); a port implementation for another database would have to provide equivalents.

  • Measured headroom is large; see Performance.

Next
0004: Serve every read from the local index
Previous
0002: One backend, one SPA, one database
Copyright © Michel Albert
Made with Sphinx and @pradyunsg's Furo
On this page
  • 0003: PostgreSQL for storage and full-text search
    • Context
    • Decision
    • Consequences