ICU Collation Version Drift: Keď Upgrade Databázy Rozbije Tvoje Indexy
Bugy v sortovani su najhorsie, lebo sa skryvaju na prvy pohlad. “Rovnaký query, rozdielne výsledky po upgrade z Ubuntu 20.04 na 22.04.” Príčina: ICU library verzia sa zmenila, pravidlá porovnávania stringov sa mierne posunuli a tvoje btree indexy sú teraz corrupt (nesprávne zoradené) pre novú collation verziu.
Prostredie: PostgreSQL 14+, ICU collation (nie libc), OS alebo container base image upgrade
Problém
Tichá Korupcia Dát
-- Pred OS upgrade (ICU 67):
SELECT name FROM users WHERE name > 'Müller' ORDER BY name LIMIT 5;
-- Vracia: Muñoz, Murphy, Murray, Myers, ...
-- Po OS upgrade (ICU 72):
SELECT name FROM users WHERE name > 'Müller' ORDER BY name LIMIT 5;
-- Vracia: Murphy, Murray, Muñoz, Myers, ... -- Iné poradie!
-- Alebo horšie, index scan vracia nesprávne výsledky:
SELECT * FROM users WHERE name = 'Müller';
-- Vracia 0 riadkov! (ale dáta TAM SÚ)
-- Seq scan to nájde:
SET enable_indexscan = off;
SELECT * FROM users WHERE name = 'Müller';
-- Vracia očakávaný riadok
Čo Sa Zmenilo?
ICU collation určuje poradie triedenia stringov.
Rôzne ICU verzie majú rôzne pravidlá:
ICU 67 (Ubuntu 20.04):
'ü' sa triedi ako: ue ekvivalent
'Müller' < 'Muñoz'
ICU 72 (Ubuntu 22.04):
'ü' sa triedi ako: u + kombinujúci umlaut
'Muñoz' < 'Müller'
Tvoj btree index bol postavený s ICU 67 poradím triedenia.
Po upgrade PostgreSQL používa ICU 72 pre porovnania.
Index binárne vyhľadávanie používa nové pravidlá na dátach
zoradených starými pravidlami.
Výsledok: Index vracia nesprávne riadky alebo minie platné zhody!
Príčina
Ako Btree Indexy Fungujú
Btree index na 'name' stĺpci (zoradený podľa ICU 67):
[Mueller]
/ \
[Muller] [Murphy]
/ \ \
[Miller][Müller][Murray]
Binárne vyhľadávanie s ICU 72:
Hľadám 'Müller' > 'Muñoz'?
- ICU 72 hovorí: Müller > Muñoz (rozdielne od ICU 67!)
- Ide nesprávnou vetvou v strome
- Minie skutočný 'Müller' záznam
Index je štrukturálne validný ale sémanticky corrupt.
PostgreSQL Collation Versioning
-- PostgreSQL 14+ sleduje collation verzie
SELECT collname, collversion, collprovider
FROM pg_collation WHERE collname = 'en-US-x-icu';
-- collname | collversion | collprovider
-- en-US-x-icu| 67.1 | i
-- Po ICU upgrade sa detekuje mismatch:
-- WARNING: collation "en-US-x-icu" has version mismatch
-- DETAIL: The collation in the database was created using version 67.1,
-- but the operating system provides version 72.1.
-- HINT: Rebuild affected indexes with REINDEX.
-- Ale warning sa objaví len keď sa collation POUŽÍVA
-- Nie proaktívne pri štarte!
Diagnostika
Skontroluj Collation Version Mismatche
-- Nájdi všetky collations s version mismatchmi
SELECT
c.collname,
c.collversion as db_version,
pg_collation_actual_version(c.oid) as os_version,
CASE
WHEN c.collversion != pg_collation_actual_version(c.oid)
THEN 'MISMATCH - TREBA REINDEX'
ELSE 'OK'
END as status
FROM pg_collation c
WHERE c.collprovider = 'i' -- Len ICU collations
AND c.collversion IS NOT NULL;
Nájdi Ovplyvnené Indexy
-- Nájdi indexy používajúce ICU collations (možno treba rebuild)
SELECT
n.nspname as schema,
c.relname as index_name,
t.relname as table_name,
a.attname as column_name,
coll.collname as collation,
coll.collversion as indexed_version,
pg_collation_actual_version(coll.oid) as current_version
FROM pg_index i
JOIN pg_class c ON c.oid = i.indexrelid
JOIN pg_class t ON t.oid = i.indrelid
JOIN pg_namespace n ON n.oid = c.relnamespace
JOIN pg_attribute a ON a.attrelid = t.oid
JOIN pg_collation coll ON coll.oid = a.attcollation
WHERE coll.collprovider = 'i'
AND coll.collversion != pg_collation_actual_version(coll.oid);
Over Index Korupciu
-- Porovnaj index scan vs seq scan výsledky
SET enable_indexscan = on;
SET enable_seqscan = off;
SELECT count(*) FROM users WHERE name LIKE 'M%'; -- Používa index
-- Vracia: 1523
SET enable_indexscan = off;
SET enable_seqscan = on;
SELECT count(*) FROM users WHERE name LIKE 'M%'; -- Full scan
-- Vracia: 1547 -- Rozdielne! Index je corrupt
-- Skontroluj validitu indexu s amcheck
CREATE EXTENSION IF NOT EXISTS amcheck;
SELECT bt_index_check('users_name_idx');
-- Možno prejde! Index je štrukturálne validný, len sémanticky nesprávny
Riešenie
Možnosť 1: Reindexuj Ovplyvnené Indexy
-- Reindexuj konkrétny index
REINDEX INDEX CONCURRENTLY users_name_idx;
-- Reindexuj všetky indexy na tabuľke
REINDEX TABLE CONCURRENTLY users;
-- Reindexuj celú databázu (opatrne - trvá dlho!)
REINDEX DATABASE CONCURRENTLY mydb;
-- Po reindexe updatni uloženú collation verziu
ALTER COLLATION "en-US-x-icu" REFRESH VERSION;
Možnosť 2: Naplánovaný Reindex Po Upgrades
#!/bin/bash
# Spusti po OS upgrades ktoré menia ICU
# Skontroluj mismatche
psql -c "
SELECT collname FROM pg_collation
WHERE collprovider = 'i'
AND collversion != pg_collation_actual_version(oid);
" | grep -q 'en-US-x-icu' && {
echo "Detekovaný ICU version mismatch, reindexujem..."
psql -c "REINDEX DATABASE CONCURRENTLY mydb;"
psql -c "ALTER COLLATION \"en-US-x-icu\" REFRESH VERSION;"
}
Možnosť 3: Pripni ICU Verziu v Containeri
# Dockerfile: Pripni presnú ICU verziu
FROM postgres:15
# Nainštaluj konkrétnu ICU verziu
RUN apt-get update && apt-get install -y \
libicu67 \
&& rm -rf /var/lib/apt/lists/*
# Alebo použi distroless/immutable base ktorý sa nezmení
Možnosť 4: Použi Deterministickú Collation
-- Vytvor deterministickú collation (žiadne locale-špecifické pravidlá)
CREATE COLLATION "en_US_posix" (provider = libc, locale = 'POSIX');
-- Alebo použi C collation (byte-order porovnanie)
CREATE INDEX users_name_c_idx ON users (name COLLATE "C");
-- Nevýhoda: Menej lingvisticky korektné triedenie
-- Výhoda: Nikdy sa nemení medzi verziami
Prevencia
Pre-Upgrade Checklist
#!/bin/bash
# Spusti PRED OS upgrade
echo "=== ICU Collation Verzie Pred Upgrade ==="
psql -c "
SELECT collname, collversion, pg_collation_actual_version(oid)
FROM pg_collation WHERE collprovider = 'i';"
echo "=== Indexy Používajúce ICU Collations ==="
psql -c "
SELECT c.relname as index_name, t.relname as table_name
FROM pg_index i
JOIN pg_class c ON c.oid = i.indexrelid
JOIN pg_class t ON t.oid = i.indrelid
JOIN pg_attribute a ON a.attrelid = t.oid
JOIN pg_collation coll ON coll.oid = a.attcollation
WHERE coll.collprovider = 'i';"
echo "=== Ulož pre post-upgrade verifikáciu ==="
Checklist
## ICU Collation Version Drift
### Symptómy
- [ ] Queries vracajú rozdielne výsledky po OS upgrade
- [ ] Index scany minia riadky ktoré seq scany nájdu
- [ ] LIKE/comparison queries sa správajú neočakávane
- [ ] "collation version mismatch" warningy v logoch
### Diagnostika
- [ ] Skontroluj pg_collation pre version mismatche
- [ ] Identifikuj indexy používajúce ICU collations
- [ ] Porovnaj index scan vs seq scan výsledky
- [ ] Over zmenu ICU library verzie
### Riešenia
- [ ] REINDEX ovplyvnené indexy
- [ ] ALTER COLLATION ... REFRESH VERSION
- [ ] Zváž C alebo POSIX collation pre stabilitu
- [ ] Pripni ICU verziu v container images
### Prevencia
- [ ] Dokumentuj ICU verzie pred upgrades
- [ ] Zahrň reindex do OS upgrade runbooku
- [ ] Monitoruj collation version warningy
- [ ] Testuj queries po upgrades
Záver
Lekcia: Unicode je komplikovaný a “upgrade OS” môže ticho poškodiť tvoje indexy. PostgreSQL 14+ na toto upozorňuje, ale len keď použiješ collation - nie proaktívne.
Kľúčové princípy:
- Kontroluj collation verzie pred a po OS upgrades
- REINDEX po ICU library updates
- Použi deterministickú collation pre version-stabilné indexy
- Pripni závislosti v kontajneroch aby si sa vyhol prekvapivým zmenám
Súvisiace články
- PostgreSQL HOT Updates Index Trap - Ďalší index problém
- pg_waldump WAL Forensics - Debugging PostgreSQL problémov
Súvisiace články
PostgreSQL Partial Index: Plánovač Ignoruje Tvoj Index
Query skenuje celú tabuľku napriek perfektnému partial indexu. Príčina: WHERE klauzula query sa presne nezhoduje s predikátom indexu, alebo štatistiky zavádzajú plánovač.
GIN Index Pending List Overflow: Rýchle Zápisy, Pomalé Vyhľadávanie
Full-text search bol rýchly, teraz je pomalý. Príčina: GIN index pending list narástol obrovský počas bulk insertov a každé vyhľadávanie musí teraz skenovať nezoradené pending záznamy.
Dvojité Účtovanie z Idempotency Keys: Pasca Replica Lag
Perfektná idempotency logika, ale zákazníci sú stále účtovaní dvakrát. Príčina: kontrola idempotency keys voči read replice ktorá je sekundy za primary počas špičiek.
PostgreSQL Read Replica Konflikty: Prečo sa vaše dotazy rušia
Dotazy na read replikách zlyhávajú s 'canceling statement due to conflict with recovery'. Riešenie závisí od toho, ktorý z 5 typov konfliktov máte - tu je návod ako diagnostikovať a vyriešiť každý z nich.
Citujte tento článok
Ak na článok odkazujete, pridajte pôvodnú URL a uveďte autora.