Späť na blog

Logical Replication Slot WAL Bloat: Keď Subscribery Odídu Offline

Replication sloty sa lahko zabudnu a draho ignoruju. “pg_wal adresár má 500GB a rastie.” Príčina: logical replication subscriber sa odpojil pred 3 dňami a PostgreSQL drží všetky WAL súbory odvtedy čakajúc na jeho reconnect.

Prostredie: PostgreSQL 14+ s logical replikáciou, CDC pipelines (Debezium, atď.), viacerí subscriberi

Problém

Mizajúce Miesto na Disku

# Disk alert sa spustí
df -h /var/lib/postgresql
# /dev/sda1  1000G  950G  50G  95% /var/lib/postgresql

# Kde je miesto?
du -sh /var/lib/postgresql/14/main/*
# 450G pg_wal  <- WAL súbory žerú disk!

# Koľko WAL súborov?
ls -la /var/lib/postgresql/14/main/pg_wal/*.gz | wc -l
# 28,521 súborov

# Skontroluj najstarší WAL súbor
ls -lt /var/lib/postgresql/14/main/pg_wal/ | tail -5
# -rw------- 1 postgres postgres 16777216 Dec 26 10:00 0000000100000ABC00000001
# To je 3 dni staré - prečo je stále tu?

Nájdenie Vinníka

-- Skontroluj replication sloty
SELECT
    slot_name,
    slot_type,
    active,
    restart_lsn,
    confirmed_flush_lsn,
    pg_size_pretty(pg_wal_lsn_diff(pg_current_wal_lsn(), restart_lsn)) AS retained_wal,
    age(now(), COALESCE(
        (SELECT backend_start FROM pg_stat_activity
         WHERE pid = active_pid), '2000-01-01'
    )) AS last_active
FROM pg_replication_slots
ORDER BY pg_wal_lsn_diff(pg_current_wal_lsn(), restart_lsn) DESC;

-- Výstup:
-- slot_name           | active | retained_wal | last_active
-- debezium_cdc_slot   | f      | 450 GB       | 3 days 02:15:33
-- analytics_sub       | t      | 15 MB        | 00:00:05
-- audit_log_slot      | t      | 8 MB         | 00:00:12

-- debezium_cdc_slot je neaktívny a drží 450GB WAL!

Príčina

Ako Logical Replication Sloty Fungujú

Normálna prevádzka:
┌─────────────────────────────────────────────────────────────┐
│ PostgreSQL Primary                                          │
│                                                             │
│ WAL Generácia: [001][002][003][004][005][006]...           │
│                                                             │
│ Slot A (aktívny):  confirmed_lsn = 005                     │
│ Slot B (aktívny):  confirmed_lsn = 004                     │
│                                                             │
│ Môže zmazať: [001][002][003] (pred všetkými slotmi)        │
│ Musí držať:  [004][005][006]... (potrebný nejakým slotom)  │
└─────────────────────────────────────────────────────────────┘

Keď subscriber odíde offline:
┌─────────────────────────────────────────────────────────────┐
│ PostgreSQL Primary                                          │
│                                                             │
│ WAL Generácia: [001][002]...[1000][1001][1002]...          │
│                                                             │
│ Slot A (OFFLINE): confirmed_lsn = 002 (spred 3 dní!)       │
│ Slot B (aktívny): confirmed_lsn = 1002                     │
│                                                             │
│ Môže zmazať: [001] (pred VŠETKÝMI slotmi vrátane offline)  │
│ Musí držať:  [002]...[1002] (Slot A sa možno pripojí)      │
│                                                             │
│ 1000 × 16MB = 16GB minimum (často oveľa viac)              │
└─────────────────────────────────────────────────────────────┘

Prečo PostgreSQL Drží WAL

-- Logical sloty sú PERZISTENTNÉ
-- Prežijú reštarty a čakajú neobmedzene na consumera

-- Slot kontrakt:
-- "Sľubujem mať všetky zmeny od restart_lsn dostupné
--  aby subscriber mohol pokračovať odkiaľ skončil"

-- Toto je správne správanie, ale neobmedzené:
-- - Žiadny automatický cleanup neaktívnych slotov
-- - Žiadny timeout pre offline consumerov
-- - Žiadny size limit na retained WAL

Diagnostika

Monitoruj Slot Lag

-- Vytvor monitoring query
CREATE OR REPLACE VIEW replication_slot_lag AS
SELECT
    slot_name,
    slot_type,
    active,
    pg_size_pretty(pg_wal_lsn_diff(pg_current_wal_lsn(), restart_lsn)) AS wal_retained,
    pg_wal_lsn_diff(pg_current_wal_lsn(), restart_lsn) / 1024 / 1024 AS mb_retained,
    CASE
        WHEN active THEN
            (SELECT now() - backend_start FROM pg_stat_activity
             WHERE pid = active_pid)
        ELSE
            'inactive'::interval
    END AS connection_age
FROM pg_replication_slots;

-- Kontroluj pravidelne
SELECT * FROM replication_slot_lag WHERE mb_retained > 1000;

Skontroluj WAL Retention Nastavenia

-- Tieto nastavenia ovplyvňujú WAL retention ale NELIMITUJú slot retention
SHOW wal_keep_size;        -- Minimum WAL na držanie (nie max!)
SHOW max_slot_wal_keep_size; -- PostgreSQL 13+: Max WAL per slot!

-- max_slot_wal_keep_size je kľúčové nastavenie
-- Default: -1 (neobmedzené - nebezpečné!)
-- Nastav limit: '100GB'

Riešenie

Možnosť 1: Dropni Neaktívny Slot (Ak Consumer Je Preč)

-- NEBEZPEČENSTVO: Toto stratí všetky zmeny od kedy slot odišiel offline!
-- Rob len ak consumer je naozaj preč alebo urobí full-resync

-- Skontroluj čo dropuješ
SELECT slot_name, pg_size_pretty(pg_wal_lsn_diff(pg_current_wal_lsn(), restart_lsn))
FROM pg_replication_slots WHERE slot_name = 'debezium_cdc_slot';

-- Dropni slot
SELECT pg_drop_replication_slot('debezium_cdc_slot');

-- WAL súbory sa vyčistia pri ďalšom checkpointe
CHECKPOINT;

Možnosť 2: Nastav max_slot_wal_keep_size (Prevencia)

-- PostgreSQL 13+: Limituj WAL retained per slot
ALTER SYSTEM SET max_slot_wal_keep_size = '100GB';
SELECT pg_reload_conf();

-- Ak slot prekročí toto, stane sa "invalid"
-- Subscriber musí urobiť full resync, ale disk je chránený

-- Skontroluj invalidované sloty
SELECT slot_name, wal_status FROM pg_replication_slots;
-- wal_status = 'lost' znamená slot je invalid (potrebuje resync)

Možnosť 3: Proaktívny Slot Management

-- Vytvor funkciu na drop stale slotov
CREATE OR REPLACE FUNCTION cleanup_stale_slots(max_age interval, max_wal_gb int)
RETURNS TABLE (slot_name text, action text) AS $$
DECLARE
    slot RECORD;
BEGIN
    FOR slot IN
        SELECT
            s.slot_name,
            s.active,
            pg_wal_lsn_diff(pg_current_wal_lsn(), s.restart_lsn) / 1024 / 1024 / 1024 AS gb_retained
        FROM pg_replication_slots s
        WHERE s.slot_type = 'logical'
        AND s.active = false
        AND pg_wal_lsn_diff(pg_current_wal_lsn(), s.restart_lsn) > max_wal_gb * 1024 * 1024 * 1024
    LOOP
        PERFORM pg_drop_replication_slot(slot.slot_name);
        RETURN QUERY SELECT slot.slot_name, 'dropped'::text;
    END LOOP;
END;
$$ LANGUAGE plpgsql;

-- Spúšťaj periodicky (napr. cez pg_cron)
SELECT * FROM cleanup_stale_slots('1 day', 50);

Checklist

## Logical Replication Slot WAL Bloat

### Symptómy
- [ ] pg_wal adresár neobmedzene rastie
- [ ] Disk space alerty na PostgreSQL serveri
- [ ] WAL súbory staršie než očakávaná retencia
- [ ] Neaktívne sloty v pg_replication_slots

### Diagnostika
- [ ] Skontroluj pg_replication_slots pre neaktívne sloty
- [ ] Identifikuj retained WAL size per slot
- [ ] Nájdi vlastníka slotu (aplikácia/subscriber)
- [ ] Skontroluj max_slot_wal_keep_size nastavenie

### Riešenia
- [ ] Dropni skutočne opustené sloty
- [ ] Nastav max_slot_wal_keep_size pre ochranu
- [ ] Reconnectni/reštartuj offline subscribera
- [ ] Implementuj proaktívny slot cleanup

### Prevencia
- [ ] Monitoruj slot lag metriky
- [ ] Alertuj na neaktívne sloty
- [ ] Nastav max_slot_wal_keep_size
- [ ] Dokumentuj vlastníkov slotov a recovery procedúry

Záver

Lekcia: logical replication sloty sú sľuby ktoré PostgreSQL berie vážne. Zabudnutý slot bude držať WAL navždy. Nastav max_slot_wal_keep_size a monitoruj zdravie slotov aby si sa vyhol disk-full núdzovým situáciám.

Kľúčové body:

  1. Neaktívne sloty držia WAL neobmedzene by design
  2. max_slot_wal_keep_size je tvoja záchranná sieť (PostgreSQL 13+)
  3. Monitoruj a alertuj na slot lag predtým než disk sa naplní
  4. Dokumentuj vlastníctvo slotov aby si vedel čo sa dá dropnúť

Súvisiace články

Súvisiace články

Citujte tento článok

Ak na článok odkazujete, pridajte pôvodnú URL a uveďte autora.

Michal Drozd. "Logical Replication Slot WAL Bloat: Keď Subscribery Odídu Offline". https://www.michal-drozd.com/sk/blog/logical-replication-slot-wal-retention/ (Publikované 9. septembra 2025).