-
Notifications
You must be signed in to change notification settings - Fork 0
feat(schema): provision schema externally; drop runtime DDL and defaults seed #27
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,5 +1,49 @@ | ||
| # Lib-systemplane Changelog | ||
|
|
||
| ## [Unreleased] | ||
|
|
||
| ### Changed | ||
|
|
||
| - **lib-systemplane no longer creates its schema or seeds defaults at | ||
| runtime.** The Postgres store and the multi-tenant `Manager` previously ran | ||
| `CREATE TABLE` / `CREATE FUNCTION` / `CREATE TRIGGER` and an | ||
| `INSERT ... ON CONFLICT DO NOTHING` defaults seed on first use | ||
| (`Store.Start` / `OnTenantActivated`). Those runtime DDL/seed paths are | ||
| removed. Consumers provision `systemplane_entries` (plus | ||
| `systemplane_notify_v3()` and the INSERT/DELETE and UPDATE NOTIFY triggers) | ||
| and any default values externally — e.g. via their migration pipeline — | ||
| using the DDL published by `SchemaSQL()` / `DefaultSeedSQL()`. The runtime | ||
| database role needs only DML (`SELECT`/`INSERT`/`UPDATE`/`DELETE`) + | ||
| `LISTEN`; it no longer needs `CREATE` on the schema. This aligns with the | ||
| least-privilege per-tenant roles handed back by the tenant-manager (which | ||
| reject runtime DDL with `permission denied for schema ... (42501)`). No | ||
| current consumers depend on the removed runtime bootstrap, so this is a | ||
| behavior change with no expected real-world breakage. | ||
| - Warm-load (`OnTenantActivated`) now tolerates a not-yet-provisioned table: | ||
| if `systemplane_entries` does not exist yet (SQLSTATE `42P01`) it logs at | ||
| WARN and proceeds with an empty cache instead of failing activation; | ||
| LISTEN/poll refreshes the cache once the consumer's migration creates the | ||
| table. Reads return not-found / zero-value as before. | ||
|
|
||
| ### Removed | ||
|
|
||
| - Postgres store: `runSchema` and the `CREATE ...` DDL builders, the | ||
| `ensureSchema` / `schemaOnce` / `schemaErr` lazy-bootstrap machinery, and | ||
| the `Start`/`resolveDB` calls into them. | ||
| - Manager: `runSchemaAndSeed`, `runSchema`, and the runtime `seedDefaults` | ||
| defaults seed. | ||
|
|
||
| ### Unchanged | ||
|
|
||
| - `SchemaSQL()` and `DefaultSeedSQL()` are intact and are now the ONLY way the | ||
| schema and defaults are expressed, for consumers to vendor into migrations. | ||
|
|
||
| > Recommended version: **v1.7.0** (next beta `v1.7.0-beta.1`) — minor bump | ||
| > continuing the v1.6.x line that introduced `SchemaSQL()` / `DefaultSeedSQL()`. | ||
| > Tag owned by the maintainer; not tagged here. | ||
|
Comment on lines
+41
to
+43
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Update the recommended version to a major bump. Line 41 recommends a minor release ( 🤖 Prompt for AI Agents |
||
|
|
||
| --- | ||
|
|
||
| ## [1.5.0](https://github.com/LerianStudio/lib-systemplane/releases/tag/v1.5.0) | ||
|
|
||
| - **Features** | ||
|
|
@@ -35,8 +79,9 @@ Contributors: @bedatty, @fredcamaral, @jeffersonrodrigues92 | |
| identical v1.4.0 behaviour. | ||
| - Schema bootstrap + defaults seed via `INSERT ... ON CONFLICT DO NOTHING` | ||
| happen at `OnTenantActivated` time. Operator-set values are never | ||
| overwritten. This removes the need for hand-rolled plugin-side migrations | ||
| that seeded systemplane defaults. | ||
| overwritten. (Superseded by the Unreleased change above: runtime schema | ||
| creation and the defaults seed were removed — provision the schema and | ||
| defaults externally via `SchemaSQL()` / `DefaultSeedSQL()`.) | ||
| - Six new OpenTelemetry metrics: `systemplane.manager.tenants_active`, | ||
| `cache_entries`, `notify_received_total`, `listen_disconnects_total`, | ||
| `warmload_latency_seconds`, `get_cache_hits_total`. Tenant-id cardinality | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -26,7 +26,28 @@ The library supports two modes; pick at construction time: | |
| | Single-tenant | `db *sql.DB` / `*mongo.Client` | In-process cache | Through cache + store | LISTEN/NOTIFY (Postgres) or change stream (MongoDB) | | ||
| | Multi-tenant | May be nil | Resolved per-call via tenant-manager ctx | Same | Disabled — `OnChange` returns `ErrNotSupportedInMultiTenant` | | ||
|
|
||
| In multi-tenant mode the library does NOT hold an in-process cache. Every `Get` reads through the resolved tenant database. The lib expects the caller to wire `lib-commons/v5/commons/tenant-manager/middleware.TenantMiddleware` with `WithPG(pgManager, "<module>")` (Postgres) or `WithMB(mongoManager, "<module>")` (MongoDB) where `<module>` matches the lib's `WithModule(...)` option (default `"systemplane"`). The middleware populates the request context; the lib calls `tmcore.GetPGContext` / `tmcore.GetMBContext` to resolve the tenant database, lazily ensures the schema on first use per database, and runs the read/write against that handle. | ||
| In multi-tenant mode the library does NOT hold an in-process cache. Every `Get` reads through the resolved tenant database. The lib expects the caller to wire `lib-commons/v5/commons/tenant-manager/middleware.TenantMiddleware` with `WithPG(pgManager, "<module>")` (Postgres) or `WithMB(mongoManager, "<module>")` (MongoDB) where `<module>` matches the lib's `WithModule(...)` option (default `"systemplane"`). The middleware populates the request context; the lib calls `tmcore.GetPGContext` / `tmcore.GetMBContext` to resolve the tenant database and runs the read/write against that handle. | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Scope the MT read-path statement to the non-Manager path. Line 29 currently reads as absolute behavior for all MT usage. Please qualify it (e.g., “without a bound Manager”) so docs stay accurate for Manager-backed MT deployments. Based on learnings: "Keep docs factual and code-backed; avoid speculative roadmap text." 🤖 Prompt for AI Agents |
||
|
|
||
| > **Provisioning (Postgres).** The library no longer creates its schema or seeds defaults at runtime. Provision `systemplane_entries` (plus the `systemplane_notify_v3()` trigger function and the NOTIFY triggers) and any defaults externally — e.g. via your migration pipeline — using the DDL published by [`SchemaSQL()` / `DefaultSeedSQL()`](#schema-provisioning). The runtime database role only needs DML (`SELECT`/`INSERT`/`UPDATE`/`DELETE`) + `LISTEN`; it does NOT need `CREATE` on the schema. | ||
|
|
||
| ## Schema provisioning | ||
|
|
||
| `lib-systemplane` does not run any DDL or defaults seed at runtime (single- or multi-tenant). The Postgres schema and defaults are published as importable artifacts so consumers can fold them into their own migration pipeline: | ||
|
|
||
| ```go | ||
| // systemplane.SchemaSQL() returns the full idempotent DDL: | ||
| // - CREATE TABLE IF NOT EXISTS systemplane_entries (...) | ||
| // - CREATE OR REPLACE FUNCTION systemplane_notify_v3() ... | ||
| // - the INSERT/DELETE and UPDATE NOTIFY triggers on the | ||
| // systemplane_changes channel | ||
| ddl := systemplane.SchemaSQL() | ||
|
|
||
| // systemplane.DefaultSeedSQL() returns neutral runtime_config defaults | ||
| // inserted with ON CONFLICT (namespace, "key") DO NOTHING. | ||
| seed := systemplane.DefaultSeedSQL() | ||
| ``` | ||
|
|
||
| Run both through a privileged role during provisioning (e.g. as a migration executed by your migrate-up step or by the tenant-manager during per-tenant database provisioning). At runtime the library only reads, writes values (DML), and — in single-tenant mode — runs `LISTEN`, so the runtime role can be least-privilege with no `CREATE` on the schema. If the table has not been provisioned yet when a multi-tenant `Manager` activates a tenant, warm-load logs a warning and proceeds with an empty cache rather than failing; the cache refreshes via LISTEN/poll once the migration creates the table. | ||
|
|
||
| ## Single-tenant Quickstart — PostgreSQL | ||
|
|
||
|
|
@@ -275,7 +296,7 @@ func run() error { | |
| } | ||
| ``` | ||
|
|
||
| In multi-tenant mode the lib lazily runs `CREATE TABLE IF NOT EXISTS` (Postgres) or its MongoDB equivalent once per tenant database, the first time a request touches that database. Calling `OnChange` returns `ErrNotSupportedInMultiTenant`. | ||
| In multi-tenant mode the lib does NOT provision schema at runtime for Postgres — the `systemplane_entries` table and its NOTIFY triggers must be created externally (see [Schema provisioning](#schema-provisioning)) before the lib reads or writes. Calling `OnChange` returns `ErrNotSupportedInMultiTenant`. | ||
|
|
||
| ## Admin HTTP routes | ||
|
|
||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove speculative consumer-impact wording.
Lines 20-21 assert adoption impact that is not verifiable from code/changelog evidence. Prefer factual release-note language only.
Based on learnings: "Keep docs factual and code-backed; avoid speculative roadmap text."
🤖 Prompt for AI Agents