This specification is maintained by the AgentRules Working Group. Have enhancements or want to claim maintainership?Submit Specification →
backendcritical PriorityID: postgres-17-logical-decoding-spill-memory-ceiling

Postgres 17 Logical Replication Memory Ceiling & Parallel Spill-to-Disk Control

Enforces strict memory ceilings and streaming configurations on Postgres 17 logical replication engines during large transactional workloads. Prevents wal_sender and parallel apply worker out-of-memory (OOM) crashes by mandating bounded logical_decoding_work_mem limits, streaming=parallel subscription parameters, and batch-bounded application transactions.

Prohibited Anti-Pattern: Unbounded transaction sizes and default replication streaming triggering wal_sender heap exhaustion and worker OOM crashes

Export Ready-to-Drop Configuration

Select format below to copy code or download file directly.

---
description: Postgres 17 Logical Replication Memory Ceiling & Parallel Spill-to-Disk Control - Enforces strict memory ceilings and streaming configurations on Postgres 17 logical replication engines during large transactional workloads. Prevents wal_sender and parallel apply worker out-of-memory (OOM) crashes by mandating bounded logical_decoding_work_mem limits, streaming=parallel subscription parameters, and batch-bounded application transactions.
globs: src/**/*.ts, migrations/**/*.sql, infra/**/*.tf, infra/**/*.sql
alwaysApply: true
verified_by: "agentrules.org"
license: "CC-BY-4.0"
source: "https://agentrules.org/rules/postgres-17-logical-decoding-spill-memory-ceiling"
---

# Postgres 17 Logical Replication Memory Ceiling & Parallel Spill-to-Disk Control

> **Severity**: CRITICAL | **Category**: backend
> **Anti-Pattern Warning**: Unbounded transaction sizes and default replication streaming triggering wal_sender heap exhaustion and worker OOM crashes

## System Constraints
You are a Principal Software Architect enforcing Postgres 17 parallel logical streaming and memory boundary standards. You must ensure that bulk data mutations, database schema migrations, and replication orchestrations never exhaust server memory buffers. Mandate strict logical_decoding_work_mem ceilings, enforce parallel streaming replication parameters (streaming = 'parallel'), require transactional chunking on all data modifications exceeding 50,000 tuples, and monitor spill metrics via pg_stat_replication_slots to prevent database worker terminations.

## Strict Guidelines
- Engineers MUST ALWAYS enforce explicit bounded logical_decoding_work_mem (recommended 64MB to 256MB per connection) rather than allowing unbounded memory consumption in replication sessions.
- Engineers MUST ALWAYS configure Postgres 17 subscriptions consuming heavy write workloads with 'streaming = parallel' to leverage parallel apply workers and disk-backed streaming.
- Engineers MUST NOT execute bulk data mutations (UPDATE/DELETE/INSERT) exceeding 50,000 records within a single monolithic transaction; all write pipelines must use keyset-paginated chunked transactions.
- Code SHOULD inspect and monitor pg_stat_replication_slots metrics (spill_txns, spill_count, spill_bytes) in health checks to verify that spool-to-disk thresholds are operating within provisioned IOPS capacity.
- Services MUST verify that publisher and subscriber ephemeral storage (pgsql_tmp directory volume) has sufficient IOPS and capacity to sustain parallel spilled transaction files without disk-full cascade failures.

## Bad vs Good Implementations
### Bulk Mutation Pipeline with Memory-Safe Transaction Chunking and Spill Validation

#### Bad Antipattern (AI Common Hallucination)
> **Why it fails**: Executing an unchunked update on millions of rows forces the logical decoding engine to accumulate WAL records. If logical_decoding_work_mem is breached without appropriate streaming configurations or if spooling IO blocks, wal_sender or subscriber apply workers encounter severe memory pressure or OOM termination, stalling all downstream replicas.
```typescript
import { Pool } from 'pg';

export async function migrateTenantRecords(pool: Pool, tenantId: string): Promise<void> {
  const client = await pool.connect();
  try {
    // ANTI-PATTERN: Monolithic transaction modifying millions of rows in a single commit.
    // In Postgres logical replication, wal_sender decodes this entire transaction into memory,
    // exceeding logical_decoding_work_mem or blowing process heap, causing OOM terminating workers.
    await client.query('BEGIN');
    await client.query(
      `UPDATE audit_logs 
       SET status = 'archived', updated_at = NOW() 
       WHERE tenant_id = $1 AND status = 'pending'`, 
      [tenantId]
    );
    await client.query('COMMIT');
  } catch (error) {
    await client.query('ROLLBACK');
    throw error;
  } finally {
    client.release();
  }
}
```

#### Production Standard (Correct)
> **Standard**: The good pattern bounds transactional mutation volume using keyset-paginated micro-batches (10,000 items). Each batch commits independently, generating bounded WAL deltas that fit within default logical_decoding_work_mem limits (avoiding worker memory spikes) and queries pg_stat_replication_slots to verify system disk spool health.
```typescript
import { Pool } from 'pg';

interface ChunkMigrationResult {
  totalProcessed: number;
  spillBytesEncountered: bigint;
}

export async function migrateTenantRecords(
  pool: Pool,
  tenantId: string,
  chunkSize = 10_000
): Promise<ChunkMigrationResult> {
  let totalProcessed = 0;
  let lastId: string | null = null;
  let hasMore = true;

  while (hasMore) {
    const client = await pool.connect();
    try {
      await client.query('BEGIN');

      // Safe: Keyset pagination limits single WAL decoding footprint to well below logical_decoding_work_mem
      const selectQuery = lastId === null
        ? 'SELECT id FROM audit_logs WHERE tenant_id = $1 AND status = $2 ORDER BY id ASC LIMIT $3'
        : 'SELECT id FROM audit_logs WHERE tenant_id = $1 AND status = $2 AND id > $3 ORDER BY id ASC LIMIT $4';
      
      const selectParams = lastId === null 
        ? [tenantId, 'pending', chunkSize]
        : [tenantId, 'pending', lastId, chunkSize];

      const { rows } = await client.query<{ id: string }>(selectQuery, selectParams);

      if (rows.length === 0) {
        hasMore = false;
        await client.query('COMMIT');
        break;
      }

      const ids = rows.map((r) => r.id);
      await client.query(
        'UPDATE audit_logs SET status = $1, updated_at = NOW() WHERE id = ANY($2::uuid[])',
        ['archived', ids]
      );

      await client.query('COMMIT');
      totalProcessed += rows.length;
      lastId = rows[rows.length - 1].id;

      if (rows.length < chunkSize) {
        hasMore = false;
      }
    } catch (err) {
      await client.query('ROLLBACK');
      throw err;
    } finally {
      client.release();
    }
  }

  // Verify replication health and spill state
  const statsResult = await pool.query<{ spill_bytes: string }>(
    `SELECT COALESCE(SUM(spill_bytes), 0) AS spill_bytes 
     FROM pg_stat_replication_slots`
  );

  return {
    totalProcessed,
    spillBytesEncountered: BigInt(statsResult.rows[0]?.spill_bytes || '0')
  };
}
```

Architectural Specification & Invariant Rationale

Architectural Invariant Design: Postgres 17 Logical Replication Memory Ceiling & Parallel Spill-to-Disk Control

Postgres 17 Logical Replication Memory Ceiling & Parallel Spill-to-Disk Control defines an industrial-grade behavioral standard for backend engineering. Enforces strict memory ceilings and streaming configurations on Postgres 17 logical replication engines during large transactional workloads. Prevents wal_sender and parallel apply worker out-of-memory (OOM) crashes by mandating bounded logical_decoding_work_mem limits, streaming=parallel subscription parameters, and batch-bounded application transactions. Modern software architectures require strict adherence to deterministic patterns to guarantee high availability, maintainability, and zero security vulnerabilities.

Framework Paradigm Shift & Drift MechanicsLLMs are trained on millions of open-source repositories containing outdated code, legacy idioms, and unpatched antipatterns. When coding assistants generate implementations for this stack, they default to common statistical tokens: Unbounded transaction sizes and default replication streaming triggering wal_sender heap exhaustion and worker OOM crashes Without an explicit system instruction file, developers spend hours debugging silent failures.
Failure Modes & Production Blast Radius

Hazard Analysis: What Breaks Without This Rule

The following failure modes have been cataloged from hundreds of hours of production code reviews where AI assistants produced non-compliant implementations:

Failure ModeAI Symptom (The Anti-Pattern)SeverityProduction Impact & Remedy
AI Hallucination & Deprecation DriftUnbounded transaction sizes and default replication streaming triggering wal_sender heap exhaustion and worker OOM crashesCritical
Violating this industrial standard introduces regression bugs, security risks, and unexpected runtime failures in production.
Enforce explicit behavioral system directives across all AI coding assistants in your repository.
Silent Architecture InconsistencyAI assistants produce mixed paradigms across files, creating fragmented codebase conventions.Medium
Increases cognitive load during code reviews and leads to maintenance debt across engineering teams.
Pin project conventions in .cursor/rules, CLAUDE.md, and AGENTS.md so all team agents follow identical guardrails.
Architectural Directives in Detail

Step-by-Step Production Directives

Each rule constraint below is injected into the AI agent's system prompt to establish deterministic boundaries:

1

Engineers MUST ALWAYS enforce explicit bounded logical_decoding_work_mem (recommended 64MB to 256MB per connection) rather than allowing unbounded memory consumption in replication sessions.

Engineers MUST ALWAYS enforce explicit bounded logical_decoding_work_mem (recommended 64MB to 256MB per connection) rather than allowing unbounded memory consumption in replication sessions.

Rationale:AI models frequently generate unconstrained code here because statistical language distributions lack project-specific architectural context. Mandating this directive ensures that generated code adheres to verified industrial production requirements.
Enforcement:Integrated into the Agent Pack rule file. When Cursor, Claude Code, or Codex processes this prompt, it applies this constraint before emitting syntax.
2

Engineers MUST ALWAYS configure Postgres 17 subscriptions consuming heavy write workloads with 'streaming = parallel' to leverage parallel apply workers and disk-backed streaming.

Engineers MUST ALWAYS configure Postgres 17 subscriptions consuming heavy write workloads with 'streaming = parallel' to leverage parallel apply workers and disk-backed streaming.

Rationale:AI models frequently generate unconstrained code here because statistical language distributions lack project-specific architectural context. Mandating this directive ensures that generated code adheres to verified industrial production requirements.
Enforcement:Integrated into the Agent Pack rule file. When Cursor, Claude Code, or Codex processes this prompt, it applies this constraint before emitting syntax.
3

Engineers MUST NOT execute bulk data mutations (UPDATE/DELETE/INSERT) exceeding 50,000 records within a single monolithic transaction

Engineers MUST NOT execute bulk data mutations (UPDATE/DELETE/INSERT) exceeding 50,000 records within a single monolithic transaction; all write pipelines must use keyset-paginated chunked transactions.

Rationale:AI models frequently generate unconstrained code here because statistical language distributions lack project-specific architectural context. Mandating this directive ensures that generated code adheres to verified industrial production requirements.
Enforcement:Integrated into the Agent Pack rule file. When Cursor, Claude Code, or Codex processes this prompt, it applies this constraint before emitting syntax.
4

Code SHOULD inspect and monitor pg_stat_replication_slots metrics (spill_txns, spill_count, spill_bytes) in health checks to verify that spool-to-disk thresholds are operating within provisioned IOPS capacity.

Code SHOULD inspect and monitor pg_stat_replication_slots metrics (spill_txns, spill_count, spill_bytes) in health checks to verify that spool-to-disk thresholds are operating within provisioned IOPS capacity.

Rationale:AI models frequently generate unconstrained code here because statistical language distributions lack project-specific architectural context. Mandating this directive ensures that generated code adheres to verified industrial production requirements.
Enforcement:Integrated into the Agent Pack rule file. When Cursor, Claude Code, or Codex processes this prompt, it applies this constraint before emitting syntax.
5

Services MUST verify that publisher and subscriber ephemeral storage (pgsql_tmp directory volume) has sufficient IOPS and capacity to sustain parallel spilled transaction files without disk-full cascade failures.

Services MUST verify that publisher and subscriber ephemeral storage (pgsql_tmp directory volume) has sufficient IOPS and capacity to sustain parallel spilled transaction files without disk-full cascade failures.

Rationale:AI models frequently generate unconstrained code here because statistical language distributions lack project-specific architectural context. Mandating this directive ensures that generated code adheres to verified industrial production requirements.
Enforcement:Integrated into the Agent Pack rule file. When Cursor, Claude Code, or Codex processes this prompt, it applies this constraint before emitting syntax.
Technical Questions & Answers

Frequently Asked Technical Questions

Common questions developers face when configuring AI coding assistants for this stack:

Q:Why is Postgres 17 Logical Replication Memory Ceiling & Parallel Spill-to-Disk Control critical for AI coding assistants?

Enforces strict memory ceilings and streaming configurations on Postgres 17 logical replication engines during large transactional workloads. Prevents wal_sender and parallel apply worker out-of-memory (OOM) crashes by mandating bounded logical_decoding_work_mem limits, streaming=parallel subscription parameters, and batch-bounded application transactions. Without explicit behavioral boundaries, AI coding assistants (Cursor, Claude Code, Windsurf, Codex) rely on training data averages, which frequently produce deprecated, inefficient, or vulnerable code patterns.

Q:How does adopting this rule prevent AI hallucinations?

This rule explicitly declares what the AI must avoid (Unbounded transaction sizes and default replication streaming triggering wal_sender heap exhaustion and worker OOM crashes) and prescribes verified positive examples. Grounding the LLM with negative and positive pairs eliminates guesswork.

Q:Which AI coding environments are supported?

This specification is multi-platform compatible: Cursor (.cursor/rules/*.mdc & .cursorrules), Anthropic Claude Code (CLAUDE.md), Windsurf (.windsurfrules), Codex / Cline (AGENTS.md), and GitHub Copilot (.github/copilot-instructions.md).

Q:Can I combine this rule with other technology stacks?

Yes. Use the Interactive Stack Builder or download an Agent Pack to merge this rule with other frontend, backend, database, security, and DevOps rules without syntax collision.

Open Specification · CC BY 4.0·Free for Personal, Team & Commercial In-Repo Use

Citation & Open Source Attribution Guidelines

This specification is licensed under Creative Commons Attribution 4.0 International (CC BY 4.0). You are completely free to adapt and share this rule in your workspace. If citing, including in public AI benchmark datasets, or redistributing, attribution to AgentRules.org is required.

1 Rule in Stack

Postgres