Polycrate API 0.14.15
Polycrate API 0.14.15
Release-Datum: 19. März 2026
Typ: Hotfix
Highlights
- FTS DB-Bloat Hotfix – Der Reconciliation Loop triggerte nach dem FTS-Rollout (0.14.13) ~750 Tuple-Updates pro 5 Minuten mit vollständigen GIN-Index-Rebuilds; DB-Wachstum ~3,5 GB/Tag, Block I/O verdreifacht
- Signal-Guard Fix –
set_state()undadd_condition()nutzen jetztsave(update_fields=...)damit der FTS-Signal-Guard state-only Saves korrekt herausfiltert - Suche: Infrastruktur-Namen mit Bindestrich –
search_type="websearch"interpretierte-als NOT-Operator; jetzt"plain"für korrekte Ergebnisse bei Namen wieprod-k8s-01,aycloud-fra1
Artefakte
Docker Image
docker pull cargo.ayedo.cloud/polycrate/polycrate-api:0.14.15
Block
polycrate pull cargo.ayedo.cloud/ayedo/k8s/polycrate-api:0.9.12
polycrate run polycrate-api install
Fixes
FTS GIN-Reindex-Storm: state und kind aus Signal-Trigger entfernt
Ursache: Nach dem FTS-Rollout (0.14.13) enthielt _SEARCH_BASE_FIELDS die Felder state und kind. Der Reconciliation Loop ruft set_state() bei jedem Durchlauf auf – damit wurde bei jeder Zustandsänderung ein vollständiger GIN-Index-Rebuild auf der SearchIndex-Tabelle ausgelöst.
Effekt:
- ~750 Tuple-Updates / 5 Minuten auf SearchIndex
- GIN-Index fastupdate-Pending-List wurde permanent geleert und neu befüllt → I/O-Bursts
- MVCC-Bloat: Autovacuum kam nicht nach → DB-Wachstum ~3,5 GB/Tag (25 GB → 45 GB in 6 Tagen)
- Block I/O verdreifacht (1000 → 3000 Blöcke/5min), WAL-Aktivität stark erhöht
- Bis zu 45 blockierte Queries, eine Transaktion mit 33 Minuten Laufzeit
Fix: state und kind aus _SEARCH_BASE_FIELDS entfernt. Diese Felder sind Filter-only auf SearchIndex — ihr Wert beeinflusst nicht, was ein User in die Suchmaske tippt.
# signals.py
_SEARCH_BASE_FIELDS = frozenset([
"name", "display_name", "description",
"archived", "labels",
"organization_id", "organization",
"workspace_id", "workspace",
])
Signal-Guard: set_state() und add_condition() nutzen update_fields
Ursache: Der FTS-Signal post_save enthält einen Guard, der Re-Indexing überspringt wenn update_fields gesetzt ist und keine FTS-relevanten Felder betroffen sind. set_state() rief self.save() ohne update_fields auf — der Guard griff daher nie.
Zusätzlich: Im WARNING-Branch von set_state() existierte ein bare save() das den State nicht veränderte, aber dennoch den Signal-Guard triggerte.
Fix:
# models.py — set_state()
self.save(update_fields=["state", "last_state", "last_state_change"])
# models.py — add_condition()
self.save(update_fields=["conditions"])
Der no-op-save() im WARNING-Branch wurde entfernt.
Suche: search_type="plain" statt "websearch"
Ursache: SearchQuery verwendete search_type="websearch". PostgreSQL's WebSearch-Parser interpretiert - als NOT-Operator. Suchanfragen wie prod-k8s-01 wurden intern zu prod AND NOT k8s AND NOT 01 — valide Objekte blieben unsichtbar.
Fix:
# search.py
search_query = SearchQuery(query, config="simple", search_type="plain")
Nach dem Deployment
Nach dem Deployment empfiehlt sich ein manuelles VACUUM ANALYZE auf der SearchIndex-Tabelle um den aufgelaufenen MVCC-Bloat sofort zu bereinigen und die Autovacuum-Aggressivität anzupassen:
-- Autovacuum für SearchIndex aggressiver konfigurieren
ALTER TABLE polycrate_api_searchindex SET (
autovacuum_vacuum_scale_factor = 0.02,
autovacuum_analyze_scale_factor = 0.01,
autovacuum_vacuum_cost_delay = 2
);
-- Aufgelaufenen Bloat sofort bereinigen
VACUUM ANALYZE polycrate_api_searchindex;