Skip to content

feat: add tenant_schedule database table for manifest-driven scheduling #1015

feat: add tenant_schedule database table for manifest-driven scheduling

feat: add tenant_schedule database table for manifest-driven scheduling #1015

Workflow file for this run

name: Database Migrations
on:
push:
branches: [develop, main]
paths:
- 'services/*/migrations/**'
- 'services/*/atlas/**'
- 'shared/atlas/**'
- 'utilities/atlas-loader/**'
- 'shared/domain/models/**'
- 'scripts/migrate-all-orgs.sh'
pull_request:
branches: [develop, main]
paths:
- 'services/*/migrations/**'
- 'services/*/atlas/**'
- 'shared/atlas/**'
- 'utilities/atlas-loader/**'
- 'shared/domain/models/**'
- 'scripts/migrate-all-orgs.sh'
permissions:
contents: read
jobs:
verify-migrations:
name: Verify Migration Checksums
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- name: Checkout code
uses: actions/checkout@v6
- name: Set up Go
uses: actions/setup-go@v6
with:
go-version: '1.26.1'
cache: true
- name: Install Atlas CLI
run: |
curl -sSf https://atlasgo.sh | sh -s -- --yes
atlas version
timeout-minutes: 5
- name: Verify migration checksums
run: |
echo "Verifying migration checksums..."
atlas migrate hash --env ci --config file://services/current-account/atlas/atlas.hcl
atlas migrate hash --env ci --config file://services/position-keeping/atlas/atlas.hcl
atlas migrate hash --env ci --config file://services/financial-accounting/atlas/atlas.hcl
atlas migrate hash --env ci --config file://services/payment-order/atlas/atlas.hcl
atlas migrate hash --env ci --config file://services/party/atlas/atlas.hcl
atlas migrate hash --env ci --config file://services/tenant/atlas/atlas.hcl
echo "✓ All migration checksums verified"
test-multi-org-migrations:
name: Test Multi-Organization Migrations
runs-on: ubuntu-latest
timeout-minutes: 15
services:
postgres:
image: postgres:16
env:
POSTGRES_PASSWORD: postgres
POSTGRES_DB: meridian_test
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
ports:
- 5432:5432
steps:
- name: Checkout code
uses: actions/checkout@v6
- name: Set up Go
uses: actions/setup-go@v6
with:
go-version: '1.26.1'
cache: true
- name: Install Atlas CLI
run: |
curl -sSf https://atlasgo.sh | sh -s -- --yes
atlas version
timeout-minutes: 5
- name: Create test organization schemas
env:
DATABASE_URL: postgres://postgres:postgres@localhost:5432/meridian_test?sslmode=disable
run: |
echo "Creating test organization schemas..."
# In production: database-per-service + schema-per-org
# Each service has its own database, within which each org gets a schema
# For CI: single database, test schema-per-org isolation
psql "$DATABASE_URL" -c "CREATE SCHEMA IF NOT EXISTS org_acme_bank;"
psql "$DATABASE_URL" -c "CREATE SCHEMA IF NOT EXISTS org_test_org;"
# Create service-specific schemas for Atlas revision tracking
# This prevents cross-service version confusion when sharing org schemas
for SERVICE in current_account financial_accounting party payment_order position_keeping; do
psql "$DATABASE_URL" -c "CREATE SCHEMA IF NOT EXISTS atlas_${SERVICE}_revisions;"
done
echo "✓ Test organization schemas created"
- name: Apply migrations to test organizations
env:
DATABASE_URL: postgres://postgres:postgres@localhost:5432/meridian_test?sslmode=disable
run: |
echo "Applying migrations to test organizations..."
echo "Architecture: database-per-service + schema-per-org"
echo "- In production: each service has its own database"
echo "- Within each database: each org gets its own schema"
echo "- Tables use singular, unqualified names (search_path routing)"
echo "- In CI: services get separate schemas to mirror production separation"
echo ""
FAILED=0
for ORG in acme_bank test_org; do
echo "Migrating organization: $ORG..."
for CONFIG in services/current-account/atlas/atlas.hcl \
services/financial-accounting/atlas/atlas.hcl \
services/party/atlas/atlas.hcl \
services/payment-order/atlas/atlas.hcl \
services/position-keeping/atlas/atlas.hcl; do
SERVICE=$(basename $(dirname $(dirname "$CONFIG")))
SERVICE_UNDERSCORE=$(echo "$SERVICE" | tr '-' '_')
# Each service gets its own schema per org (mirrors production database-per-service)
ORG_SCHEMA="${SERVICE_UNDERSCORE}_org_${ORG}"
REVISION_SCHEMA="atlas_${SERVICE_UNDERSCORE}_revisions"
echo " Service: $SERVICE (schema: $ORG_SCHEMA, revisions: $REVISION_SCHEMA)"
# Create service-specific org schema if it doesn't exist
psql "$DATABASE_URL" -c "CREATE SCHEMA IF NOT EXISTS ${ORG_SCHEMA};" 2>&1 | grep -v "CREATE SCHEMA" || true
# --revisions-schema isolates each service's revision tracking
# In production, each service has its own database
if ! atlas migrate apply \
--env ci \
--config "file://$CONFIG" \
--url "${DATABASE_URL}&search_path=${ORG_SCHEMA}" \
--revisions-schema "$REVISION_SCHEMA" \
--tx-mode none; then
echo " ✗ Migration failed for $SERVICE in $ORG_SCHEMA"
FAILED=1
fi
done
done
if [ "$FAILED" -eq 1 ]; then
echo "❌ Some migrations failed"
exit 1
fi
echo ""
echo "✓ Migrations applied successfully"
- name: Verify organization isolation and table naming
env:
DATABASE_URL: postgres://postgres:postgres@localhost:5432/meridian_test?sslmode=disable
run: |
echo "Verifying service-per-org schemas and table naming..."
echo "Each service gets its own schema per org (mirrors production database-per-service)"
# Define expected tables per service (all use unqualified/singular names)
declare -A SERVICE_TABLES
SERVICE_TABLES[current_account]="account lien audit_log audit_outbox"
SERVICE_TABLES[financial_accounting]="financial_booking_log ledger_posting audit_log audit_outbox"
SERVICE_TABLES[party]="party party_association party_demographic party_reference party_bank_relation audit_log audit_outbox"
SERVICE_TABLES[payment_order]="payment_order audit_log audit_outbox"
SERVICE_TABLES[position_keeping]="financial_position_log audit_trail_entry transaction_lineage transaction_log_entry audit_log audit_outbox"
for ORG in acme_bank test_org; do
echo ""
echo "Verifying organization: $ORG"
for SERVICE in current_account financial_accounting party payment_order position_keeping; do
SCHEMA="${SERVICE}_org_${ORG}"
echo " Schema: $SCHEMA"
# Verify schema exists
SCHEMA_EXISTS=$(psql "$DATABASE_URL" -t -c "
SELECT COUNT(*) FROM information_schema.schemata
WHERE schema_name = '$SCHEMA';
" | xargs)
if [ "$SCHEMA_EXISTS" -eq 0 ]; then
echo " ✗ Schema does not exist!"
continue
fi
# Count tables
TABLE_COUNT=$(psql "$DATABASE_URL" -t -c "
SELECT COUNT(*) FROM information_schema.tables
WHERE table_schema = '$SCHEMA' AND table_type = 'BASE TABLE';
" | xargs)
echo " Tables: $TABLE_COUNT"
# Verify expected tables exist
for TABLE in ${SERVICE_TABLES[$SERVICE]}; do
EXISTS=$(psql "$DATABASE_URL" -t -c "
SELECT COUNT(*) FROM information_schema.tables
WHERE table_schema = '$SCHEMA' AND table_name = '$TABLE';
" | xargs)
if [ "$EXISTS" -gt 0 ]; then
echo " ✓ $TABLE"
fi
done
done
done
# Verify NO old schema-qualified patterns exist
echo ""
echo "Verifying no old schema-qualified tables..."
OLD_SCHEMAS="current_account financial_accounting payment_order position_keeping platform"
for SCHEMA in $OLD_SCHEMAS; do
EXISTS=$(psql "$DATABASE_URL" -t -c "
SELECT COUNT(*) FROM information_schema.schemata
WHERE schema_name = '$SCHEMA';
" | xargs)
if [ "$EXISTS" -gt 0 ]; then
TABLES=$(psql "$DATABASE_URL" -t -c "
SELECT COUNT(*) FROM information_schema.tables
WHERE table_schema = '$SCHEMA';
" | xargs)
if [ "$TABLES" -gt 0 ]; then
echo " ❌ Found $TABLES tables in old schema '$SCHEMA'"
exit 1
fi
fi
done
echo " ✓ No old schema-qualified tables found"
echo ""
echo "✓ Organization isolation and table naming verified"