Replace Azure SQL Server with PostgreSQL Flexible Server (breaking change)#860
Merged
Replace Azure SQL Server with PostgreSQL Flexible Server (breaking change)#860
Conversation
acbdef2 to
989648f
Compare
68f0235 to
6bfd8e5
Compare
|
6bfd8e5 to
78b6c40
Compare
53c36e9 to
bdc0805
Compare
c729e33 to
63069a4
Compare
…er using Entra ID authentication
…alidation to PostgreSQL infrastructure
… advisory lock, and per-migration timeout
…ention to SQLite test configuration
…at-if issue #157 is resolved
…ion profile and subnet delegation
…trengthen PK lookup semantics
63069a4 to
525016d
Compare
Approve Database Migration
|
Approve Database Migration
|
|
Approve Database Migration
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.



Summary & Motivation
Migrate the entire database layer from Azure SQL Server to PostgreSQL Flexible Server. PostgreSQL provides a lighter local development experience (smaller Docker image, faster startup), lower Azure hosting costs, and better alignment with the open-source ecosystem.
This is a clean break with no migration path from SQL Server. All existing migrations are deleted and replaced with fresh PostgreSQL-native migrations using snake_case conventions throughout.
Application changes:
EFCore.NamingConventionswith explicitMigrationsHistoryTable("__ef_migrations_history")since the naming convention does not apply to the history tabletext,timestamptz,boolean,integer,jsonb) with snake_case naming, including__data_migrations_historytable andfk_subscriptions_tenants_tenant_idforeign keyExternalIdentities,PaymentTransactions,BillingInfo,Payload) asjsonbwith.HasColumnType("jsonb")alongsideHasConversionDataMigrationRunnerwith PostgreSQL advisory locks using a deterministic SHA256-based lock key, an independentNpgsqlDataSourceconnection, a required per-migrationTimeout(max 20 minutes),ManagesOwnTransactionsopt-out for batch migrations outside execution strategy retry, and error handling on lock releaseFOR UPDATEinSubscriptionRepository, LINQ queries inUserRepository.GetUserSummaryAsync(with SQLite raw SQL fallback forDateTimeOffsetcomparison), and a single snake_case SQL statement inSessionRepository.TryRefreshAsync.OrderBy(u => u.Id)toGetUsersByEmailUnfilteredAsyncsince PostgreSQL does not guarantee row ordering withoutORDER BY, and useSingleOrDefaultAsyncfor primary key lookupsDefaultAzureCredentialwithManagedIdentityClientIdfor the database token provider viaUsePeriodicPasswordProviderUseSnakeCaseNamingConvention()to SQLite test configuration and update all raw SQL across 55 test files to snake_casePlanproperty toTenantto denormalize the subscription plan, restrictGetPaymentHistoryto owners only[Collection("StripeTests")]Infrastructure changes:
private-endpointssubnet, andSsl Mode=VerifyFullfor certificate verificationcontainer-apps(delegated toMicrosoft.App/environments) andprivate-endpoints, since delegated subnets cannot host private endpointspg_stat_statementsfor query monitoring,log_statement=modfor audit logging with diagnostic logs routed to a storage account with 90-day lifecycle retention, andwal_level=logicalfor logical replication supportdependsOnwith module output references for proper implicit dependencies and--what-ifvisibilitypasswordAuth: 'Disabled'in a single Bicep deploymentCI/CD changes:
psqlwithsslmode=verify-full sslrootcert=systemand-v ON_ERROR_STOP=1(without--single-transactionto supportCREATE INDEX CONCURRENTLY) for migration apply, andSsl Mode=VerifyFullfordotnet efconnectionsset -eto shell scripts and IP address validation infirewall.shDocumentation changes:
api-tests.md,domain-modeling.md,database-migrations.md,backend.md,repositories.md) to reflect PostgreSQL conventions including snake_case examples,HasColumnType("jsonb")guidance,CREATE INDEX CONCURRENTLYsupport, and PostgreSQL ordering behaviorDownstream projects
This is a breaking change. The database provider changed from SQL Server to PostgreSQL. Delete existing SQL Server migrations and create a new initial migration using PostgreSQL conventions. There is no migration path to move data in a production system.
See the updated database-migrations rule file and the new account initial migration for reference.
Rename the Entra ID security groups in Azure from "SQL Admins" to "PostgreSQL Admins" (e.g.,
SQL Admins - Staging - GitHubOrganisation/GitHubRepositorybecomesPostgreSQL Admins - Staging - GitHubOrganisation/GitHubRepository). Then rename the following GitHub Actions variables:STAGING_SQL_ADMIN_OBJECT_IDtoSTAGING_POSTGRES_ADMIN_OBJECT_IDPRODUCTION_SQL_ADMIN_OBJECT_IDtoPRODUCTION_POSTGRES_ADMIN_OBJECT_IDOptional: Enable Microsoft Defender for PostgreSQL
For vulnerability assessments and security alerts, enable Microsoft Defender for Open-Source Relational Databases in your Azure subscription. This provides brute force detection, anomalous access alerts, and vulnerability scanning for PostgreSQL Flexible Server (~$15/month per server). Enable it in the Azure Portal under Microsoft Defender for Cloud > Environment settings > Defender plans, or via Azure CLI:
Checklist