Skip to content

feat: add table-existence verification to provisioning completeness#2123

Merged
bjcoombs merged 2 commits intodevelopfrom
demo-provisioner-fix--7--provisioning-completeness
Apr 4, 2026
Merged

feat: add table-existence verification to provisioning completeness#2123
bjcoombs merged 2 commits intodevelopfrom
demo-provisioner-fix--7--provisioning-completeness

Conversation

@bjcoombs
Copy link
Copy Markdown
Collaborator

@bjcoombs bjcoombs commented Apr 4, 2026

Summary

  • Adds post-migration verification that checks expected tables exist in tenant schema before transitioning to active state
  • Introduces SentinelTable field on ServiceConfig - each service declares its primary domain table as a sentinel
  • Default service configs now include sentinel tables: party, account, financial_position_log, financial_booking_log, payment_order, data_source, instrument_definition
  • Services without migrations (e.g., internal-account, reconciliation) have no sentinel and pass verification automatically
  • If verification fails, tenant remains in failed state with detailed error message listing which services/tables are missing

Test plan

  • Test: sentinel table present after migration - provisioning succeeds
  • Test: sentinel table missing (wrong table name) - provisioning fails with ErrSchemaVerificationFailed
  • Test: no sentinel configured with tables present - provisioning succeeds
  • Test: empty schema with no sentinel (no-migration service) - provisioning succeeds
  • CI: existing provisioner tests still pass

…heck

After schema migrations run, verify expected tables exist in tenant schema
before transitioning to active state. This closes the partial-provisioning
gap where the schema namespace exists but migrations failed silently.

Each service can define a SentinelTable (the primary domain table created by
its first migration). If set, that specific table is checked. If not set,
the service is assumed to have no required tables (e.g., internal-account).
Default service configs now include sentinel tables for all services with
migrations.
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 4, 2026

📝 Walkthrough

Walkthrough

Adds post-migration sentinel-table verification to tenant provisioning: a new sentinel error, a verification helper that checks for expected tables in the tenant schema, and integration of that check into the ProvisionSchemas flow with updated service config to specify sentinel tables.

Changes

Cohort / File(s) Summary
Sentinel Error
services/tenant/provisioner/errors.go
Added exported sentinel error ErrSchemaVerificationFailed.
Verification Helper
services/tenant/provisioner/provisioner_helpers.go
Added verifySchemaProvisioned which queries information_schema.tables for each service's SentinelTable, aggregates failures, and returns a wrapped ErrSchemaVerificationFailed on any mismatch.
Provisioning Integration
services/tenant/provisioner/postgres_provisioner.go
After migrations, ProvisionSchemas now calls verifySchemaProvisioned; on verification failure it logs and marks provisioning as failed and returns the error.
Config Changes
services/tenant/provisioner/provisioner.go
Added SentinelTable string to ServiceConfig; replaced default service name list with default service definitions including SentinelTable; updated builder to populate sentinel values.
Tests
services/tenant/provisioner/postgres_provisioner_test.go
Added four tests covering sentinel verification: success when table exists, failure when missing (asserting ErrSchemaVerificationFailed and StateFailed), success when sentinel empty, and success for services without migrations.

Sequence Diagram(s)

sequenceDiagram
  participant Provisioner as Provisioner
  participant Migrator as Migration Engine
  participant DB as Postgres
  participant Store as Provisioning Store
  participant Logger as Logger

  Provisioner->>Migrator: createAndMigrateSchemas(schema)
  Migrator->>DB: apply migrations in tenant schema
  DB-->>Migrator: migration results
  Migrator-->>Provisioner: migrations succeeded

  Provisioner->>DB: SELECT EXISTS(...) for each SentinelTable
  DB-->>Provisioner: table exists / not exists or error

  alt any verification failure
    Provisioner->>Logger: log verification error
    Provisioner->>Store: markProvisioningFailed(schema, reason)
    Store-->>Provisioner: state=failed
    Provisioner-->>Logger: return error (ErrSchemaVerificationFailed)
  else all verifications passed or skipped
    Provisioner->>Store: markProvisioningActive(schema)
    Store-->>Provisioner: state=active
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 25.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly describes the main feature: adding table-existence verification to provisioning completeness.
Description check ✅ Passed The description is detailed and directly related to the changeset, covering the verification logic, sentinel tables, default configs, and test coverage.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch demo-provisioner-fix--7--provisioning-completeness

Comment @coderabbitai help to get the list of available commands and usage tips.

@claude
Copy link
Copy Markdown

claude Bot commented Apr 4, 2026

Claude Code Review

Commit: 552cd38 | CI: running

Summary

Clean, well-scoped PR that closes a real gap: detecting partial provisioning where the schema namespace exists but migrations failed silently. The sentinel table pattern is a good fit -- lightweight verification that each service's primary domain table landed. Tests cover the four key scenarios (present, missing, no-sentinel-with-tables, empty-schema-no-sentinel). The refactor from defaultServiceNames to defaultServiceDefs is a natural evolution.

Second commit correctly aligns the SentinelTable doc comment with implementation and removes the dead COUNT query.

Risk Assessment

Area Level Detail
Blast radius Low Only affects provisioning flow; existing active tenants unaffected
Rollback Safe Removing the verification call is a one-line revert; no schema changes
Scale Low One information_schema query per service per provisioning -- negligible
Cross-system Low Contained within tenant provisioner package
Migration N/A No SQL migration files changed

Findings

Severity Location Description Status
-- -- No open findings --

Previously Flagged

Severity Location Description Status
Improvement provisioner.go:300-303 SentinelTable doc said checks at least one table exists but implementation skipped verification -- fixed in 552cd38 Resolved

Bot Review Notes

  • CodeRabbit: Still processing at time of review. No unresolved threads.

Copy link
Copy Markdown

@claude claude Bot left a comment

Choose a reason for hiding this comment

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

Clean implementation. One doc-vs-code inconsistency noted in summary comment.

Comment thread services/tenant/provisioner/provisioner.go
coderabbitai[bot]
coderabbitai Bot previously requested changes Apr 4, 2026
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@services/tenant/provisioner/provisioner_helpers.go`:
- Around line 164-178: The current "no sentinel" branch queries tableCount but
never fails when tableCount == 0; update the else branch that runs
serviceDB.WithContext(bypassCtx(ctx)).Raw(...).Scan(&tableCount).Error to treat
an empty schema as a verification failure when the service has no configured
sentinel: after the query (and after handling err), if tableCount == 0 and
svc.Config.SentinelTable == "" then logger.Error with context ("service",
svc.Name, "table_count", tableCount) and append failedServices with a
descriptive message (e.g. fmt.Sprintf("%s (no tables found, missing sentinel
config)", svc.Name)) so missed/empty SentinelTable no longer silently passes
verification.

In `@services/tenant/provisioner/provisioner.go`:
- Around line 301-305: The comment for SentinelTable says an empty value should
trigger a "at least one table exists" check but the post-migration verification
logic currently allows zero tables; reconcile them by either updating the
comment to reflect that zero tables are permitted or changing the verification
code to enforce tablesCount > 0 when SentinelTable == "". Locate the
post-migration verification routine (the code that inspects schema table count
after migrations) and: if you choose behavioral fix, add a conditional that when
SentinelTable == "" validates that the discovered table count is >= 1 and fails
otherwise; if you choose documentation fix, update the comment on SentinelTable
to accurately describe the current verification behavior. Ensure the change
references SentinelTable so future readers see the intended contract.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 8505057e-d810-41ab-ba3f-a68ad4ec492d

📥 Commits

Reviewing files that changed from the base of the PR and between 21aabba and f4609bd.

📒 Files selected for processing (5)
  • services/tenant/provisioner/errors.go
  • services/tenant/provisioner/postgres_provisioner.go
  • services/tenant/provisioner/postgres_provisioner_test.go
  • services/tenant/provisioner/provisioner.go
  • services/tenant/provisioner/provisioner_helpers.go

Comment thread services/tenant/provisioner/provisioner_helpers.go
Comment thread services/tenant/provisioner/provisioner.go
@codecov
Copy link
Copy Markdown

codecov Bot commented Apr 4, 2026

Codecov Report

❌ Patch coverage is 78.57143% with 9 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
services/tenant/provisioner/provisioner_helpers.go 71.87% 7 Missing and 2 partials ⚠️

📢 Thoughts on this report? Let us know!

…uery

The doc comment promised "at least one table exists" verification when
SentinelTable is empty, but the code intentionally allowed zero tables
for services without migrations. Fix the doc to match the behavior and
remove the unused COUNT query.
@bjcoombs bjcoombs dismissed coderabbitai[bot]’s stale review April 4, 2026 08:05

Stale bot review - findings addressed in 552cd38

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
services/tenant/provisioner/provisioner_helpers.go (1)

133-137: Treat missing service DB handles as verification failures instead of silently skipping.

If p.serviceDbs ever drifts from p.config.Services, this path can incorrectly pass verification and allow activation despite an unverified service.

Proposed change
 	for _, svc := range p.config.Services {
 		serviceDB, ok := p.serviceDbs[svc.Name]
 		if !ok {
-			continue // Already caught by provisionSingleService
+			logger.Error("service database handle missing during schema verification",
+				"service", svc.Name,
+				"schema", schemaName)
+			failedServices = append(failedServices, fmt.Sprintf("%s (service DB unavailable)", svc.Name))
+			continue
 		}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@services/tenant/provisioner/provisioner_helpers.go` around lines 133 - 137,
The loop over p.config.Services silently skips when a service DB handle is
missing (serviceDB, ok := p.serviceDbs[svc.Name] ... continue), which can hide
verification failures; change this to treat a missing entry as a verification
failure by returning an error (or a failed verification result) instead of
continuing. Update the verification path in the function containing that loop
(referencing p.config.Services, p.serviceDbs and provisionSingleService) so that
when ok is false you log/return a clear error indicating the missing service DB
handle and fail the overall verification/activation flow. Ensure callers of this
function handle the error consistently.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@services/tenant/provisioner/provisioner_helpers.go`:
- Around line 133-137: The loop over p.config.Services silently skips when a
service DB handle is missing (serviceDB, ok := p.serviceDbs[svc.Name] ...
continue), which can hide verification failures; change this to treat a missing
entry as a verification failure by returning an error (or a failed verification
result) instead of continuing. Update the verification path in the function
containing that loop (referencing p.config.Services, p.serviceDbs and
provisionSingleService) so that when ok is false you log/return a clear error
indicating the missing service DB handle and fail the overall
verification/activation flow. Ensure callers of this function handle the error
consistently.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 42d93a7c-91f2-4621-9d46-140f7103af72

📥 Commits

Reviewing files that changed from the base of the PR and between f4609bd and 552cd38.

📒 Files selected for processing (2)
  • services/tenant/provisioner/provisioner.go
  • services/tenant/provisioner/provisioner_helpers.go

Copy link
Copy Markdown

@claude claude Bot left a comment

Choose a reason for hiding this comment

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

Previous finding resolved in second commit. No new concerns. See summary comment for details.

@bjcoombs bjcoombs merged commit 0264b80 into develop Apr 4, 2026
40 checks passed
@bjcoombs bjcoombs deleted the demo-provisioner-fix--7--provisioning-completeness branch April 4, 2026 08:34
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.

1 participant