Skip to content

Commit bfc5281

Browse files
authored
fix: force re-provision master tenant schemas on every bootstrap (#2044)
* fix: force re-provision master tenant schemas on every bootstrap The provisioner's idempotency gate trusts the stored provisioning status, but this can be stale after partial provisioning or DB reset. The bootstrap now resets the status to 'pending' before invoking the provisioner, ensuring schemas are always created in all service databases. CREATE SCHEMA IF NOT EXISTS makes this safe. This fixes intermittent "schema does not exist" and "column does not exist" errors in the demo deploy bootstrap step. * fix: fail fast on provisioning status reset error If the reset fails due to a DB error, the state remains active and provisioning is silently skipped - defeating the purpose of the fix. Propagate the error instead of swallowing it. --------- Co-authored-by: Ben Coombs <bjcoombs@users.noreply.github.com>
1 parent 869a7fc commit bfc5281

1 file changed

Lines changed: 30 additions & 10 deletions

File tree

internal/bootstrap/master_tenant.go

Lines changed: 30 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,11 @@ func Run(ctx context.Context, cfg Config) error {
9191
}
9292

9393
// provisionSchemas creates org_meridian_master schemas in all service databases.
94+
//
95+
// The provisioner's idempotency gate trusts the stored provisioning status,
96+
// which can be stale if schemas were partially created or if the database was
97+
// reset. To guarantee all schemas exist, we reset the status to pending before
98+
// invoking the provisioner. CREATE SCHEMA IF NOT EXISTS makes this safe.
9499
func provisionSchemas(ctx context.Context, cfg Config, logger *slog.Logger) error {
95100
logger.Info("provisioning master tenant schemas")
96101

@@ -106,16 +111,12 @@ func provisionSchemas(ctx context.Context, cfg Config, logger *slog.Logger) erro
106111

107112
tenantID := tenant.TenantID(MasterTenantID)
108113

109-
// Check if already provisioned
110-
status, err := prov.GetProvisioningStatus(ctx, tenantID)
111-
if err == nil && status.State == provisioner.StateActive {
112-
logger.Info("master tenant schemas already provisioned")
113-
return nil
114-
}
115-
116-
// Initialize provisioning status if needed
117-
if err := prov.InitializeProvisioningStatus(ctx, tenantID); err != nil {
118-
logger.Warn("failed to initialize provisioning status (may already exist)", "error", err)
114+
// Reset provisioning status to pending so the provisioner always runs.
115+
// The provisioner's idempotency gate trusts the stored status, which can
116+
// be stale if schemas were partially created or if the database was reset.
117+
// CREATE SCHEMA IF NOT EXISTS and idempotent migrations make this safe.
118+
if err := resetProvisioningToPending(ctx, cfg.PlatformDB, tenantID, logger); err != nil {
119+
return fmt.Errorf("reset provisioning status: %w", err)
119120
}
120121

121122
// Provision schemas
@@ -204,6 +205,25 @@ func seedPlatformManifest(ctx context.Context, db *gorm.DB, logger *slog.Logger)
204205
return nil
205206
}
206207

208+
// resetProvisioningToPending updates the provisioning status to "pending" so the
209+
// provisioner re-runs schema creation and migrations. This is necessary because
210+
// the provisioner skips tenants with "active" status, but the actual schemas may
211+
// be missing (partial provisioning, DB reset, or new service added).
212+
func resetProvisioningToPending(ctx context.Context, db *gorm.DB, tenantID tenant.TenantID, logger *slog.Logger) error {
213+
result := db.WithContext(ctx).Exec(
214+
`UPDATE tenant_provisioning SET state = 'pending', updated_at = NOW() WHERE tenant_id = ?`,
215+
tenantID.String(),
216+
)
217+
if result.Error != nil {
218+
return result.Error
219+
}
220+
if result.RowsAffected > 0 {
221+
logger.Info("reset provisioning status to pending for re-provisioning",
222+
"tenant_id", tenantID.String())
223+
}
224+
return nil
225+
}
226+
207227
// LoadPlatformManifest loads the embedded platform economy manifest as a proto Manifest.
208228
func LoadPlatformManifest() (*controlplanev1.Manifest, error) {
209229
// First verify it's valid JSON

0 commit comments

Comments
 (0)