PostgreSQLAzureInfrastructureIncident Response

The Day Azure Put Our Database in Read-Only Mode

When Sentry lit up with read-only transaction errors across multiple services, the culprit was not Prisma or credentials — Azure had locked the database after storage ran out.

Jun 23, 20267 min read

It started with Sentry alerts.

Multiple services failing at the same time:

cannot execute INSERT in a read-only transaction
cannot execute DELETE in a read-only transaction
cannot execute SELECT FOR UPDATE in a read-only transaction

The decoder worker was crashing. Redis health checks failing. Background workers restarting. One pod had restarted over 100 times.

My first thought: database credentials issue. Bad connection string. Wrong replica. Prisma bug.

Something in the application code was broken.

The Debugging

I checked what databases each service was using:

kubectl exec deployment/backend-api -- printenv DATABASE_URL
kubectl exec deployment/decoder-worker -- printenv DATABASE_URL

Main API connected to Database A. Decoder worker connected to Database B. Completely different databases.

Yet both were failing with read-only errors.

That's when I stopped blaming the application.

I SSH'd into a running container and ran:

SHOW transaction_read_only;

Result: on

Then:

SELECT current_database();
SELECT current_user();
SELECT pg_is_in_recovery();

All normal. The right database, right user, primary node (not a replica). Everything looked correct except the database was stubbornly read-only.

The Breakthrough

I checked Azure Resource Health.

There it was:

Azure Database for PostgreSQL Flexible Server
Status: Degraded
Cause: PlatformInitiated
The Azure Database for PostgreSQL Flexible Server is set to read-only state.

Azure itself had forced the database into read-only mode.

I verified where this setting came from:

SHOW default_transaction_read_only;

Source: /datadrive/pg/data/postgresql.auto.conf

This proved the application didn't enable read-only. Azure did.

Why Azure Did This

I checked storage metrics:

Provisioned: 32 GB
Actual usage:

sensor_ppg_raw:  ~15 GB
sensor_data:     ~6 GB
sensor_bioz_raw: ~1.7 GB
sensor_eda_raw:  ~1 GB
sensor_mic_raw:  ~466 MB

Total: ~25+ GB

The database was running out of space. Azure's platform automatically forced read-only mode as a protective measure.

Storage pressure + infrastructure conditions = database lockdown.

The Fix

How we diagnosed and fixed an Azure PostgreSQL database forced into read-only mode

I scaled resources:

  1. Storage — Enabled autogrow and increased capacity
  2. Compute — Scaled from 2 vCPU/8GB RAM to a larger tier
  3. High Availability — Enabled for better resilience

After Azure completed the scaling operation:

SHOW transaction_read_only;

Result: off

Within minutes everything recovered:

  • Decoder workers stopped crashing
  • Health checks returned green
  • Job queues resumed
  • Sentry went quiet

The database write just needed more breathing room.

What I Actually Learned

1. Read-only errors aren't always application bugs

When multiple unrelated services suddenly fail with cannot execute INSERT, check infrastructure first.

2. Check resource health early

Azure Resource Health had the answer before I did. Should've looked there immediately instead of hunting through Prisma code.

3. Verify the source of PostgreSQL settings

This query saved hours:

SHOW default_transaction_read_only;

Combined with checking where the setting came from (postgresql.auto.conf), it proved the platform—not the app—enabled read-only mode.

4. Monitor storage growth proactively

Raw sensor ingestion tables consumed the vast majority of storage. A simple alert on table growth would've prevented this entirely.

5. Sometimes the fix isn't code

I spent hours expecting to deploy a fix. The actual fix was:

  • More disk
  • More CPU
  • Enable HA

No pull request. No code review. Just scale the infrastructure.

The Debugging Path

Wrong → Right:

Application bug (assumption)
  ↓
Database credentials (checked)
  ↓
Prisma transaction logic (checked)
  ↓
PostgreSQL configuration (checked)
  ↓
Azure Resource Health (found it)
  ↓
Platform-level read-only enforcement (actual cause)

This is why production debugging should move application → database → platform, not assume the code is always guilty.

In this case, every application symptom traced back to the platform itself.

That incident taught me to look at infrastructure health first when multiple services fail at once. The code is probably fine. The platform is telling you something.

Have a similar problem?

Let's talk through your architecture or scaling challenge.