Skip to content

feat(data): add PostgresSource for live config reads#900

Open
zulkhair wants to merge 6 commits into
mainfrom
daim/adr057-postgres-source
Open

feat(data): add PostgresSource for live config reads#900
zulkhair wants to merge 6 commits into
mainfrom
daim/adr057-postgres-source

Conversation

@zulkhair

@zulkhair zulkhair commented Jun 11, 2026

Copy link
Copy Markdown
Contributor

What

The data plugin can read each config kind live from Postgres instead of build-time embedded JSON — so a deployment changes config without a rebuild. PickSource chooses the source at runtime by DB_DSN.

Source selection (PickSource)

flowchart LR
    K["config kind read"] --> PICK{"DB_DSN set?"}
    PICK -->|yes| PG["PostgresSource<br/>(fail-loud, no embed fallback)"]
    PICK -->|no| EM["EmbedSource<br/>(embedded JSON, unchanged)"]
    PG --> DB[("Postgres<br/>(id, doc) tables")]
Loading

DB_DSN set but unusable → panic (never silently serve stale embedded config).

Table contract

One table per kind; one row = one record, JSON in a doc jsonb column. Table name = the kind's Name(), registered when the kind registers — shard main.go stays unchanged.

kind query
array SELECT coalesce(json_agg(doc ORDER BY id), '[]'::json)::text FROM <t>
singleton (Singleton marker) SELECT doc::text FROM <t> LIMIT 1

Notes

  • Single-version (like EmbedSource): ignores the requested content hash, returns current rows.
  • Tests: postgres_internal_test.go — fake configReader, no DB (table resolution + registrar override, read shapes, rows+sha256, error propagation).
  • Tidy: brief comments + package decls hoisted to top (no behavior change).
  • DB_DSN is the same env the monorepo docker backend injects (paired change: Argus-Labs/monorepo#711).

@zulkhair zulkhair force-pushed the daim/adr057-postgres-source branch from 734786c to 324ac5a Compare June 23, 2026 13:54
@zulkhair zulkhair marked this pull request as ready for review June 23, 2026 14:00

@rmrt1n rmrt1n left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

mostly code style nits, though I don't mind merging now and refactoring later if it already works.

}

// tableFor returns the table registered for file, falling back to the name derived from the file.
func (p *PostgresSource) tableFor(file string) string {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: I'd inline this, and tableFromFile, into Fetch so u don't have to jump between 3 different functions just to understand what Fetch does.

Comment on lines +38 to +45
type SingletonRegistrar interface {
RegisterSingleton(file string)
}

// KindRegistrar is implemented by sources that accept an explicit table name for a kind's JSON file.
type KindRegistrar interface {
RegisterKind(file, table string)
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: Remove these. Either add these to Source or use a tagged union pattern in Register.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: We don't need these tests. The function that's most complicated is readTableJSON, which is better tested w an integration test against a real DB, but that takes a lot more work and is probably overkill ATM. Testing with fakeReader doesn't do much here as the logic being tested is Go's stdlib functions (e.g. sha256), and the other functions are pretty straightforward, so I think we should just get rid of this file.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants