fix: deduplicate prosody table before creating unique index
The pre-upgrade hook failed with "could not create unique index prosody_unique_index" because the prosody table contained duplicate rows (same host, user, store, key). Without the unique index, mod_storage_sql falls back to SELECT-then-INSERT instead of ON CONFLICT upsert, which races under concurrent writes and inserts duplicates — most commonly in the fcmtoken map store. Add a DELETE that removes duplicate rows (keeping the last-written row per group via ctid ordering) immediately before the CREATE UNIQUE INDEX in both prosody-13-new-deployment.sql and prosody-13-migration-once.sql. The DELETE is a no-op when no duplicates exist, preserving idempotency. Part-of: <http://gitlab.vnc.biz/uxf/vnctalk-prosody/-/merge_requests/8>
This commit is contained in:
@@ -44,8 +44,30 @@ end $$;
|
||||
-- exist") and sets has_upsert_index=false — disabling ON CONFLICT upserts and
|
||||
-- falling back to slower SELECT-then-INSERT/UPDATE. Create it here so the
|
||||
-- index exists after migration and the warning is gone on next startup.
|
||||
--
|
||||
-- Without the unique index, mod_storage_sql falls back to SELECT-then-INSERT
|
||||
-- instead of ON CONFLICT upsert, which can create duplicate rows under
|
||||
-- concurrent writes (common in the fcmtoken map store). Deduplicate before
|
||||
-- creating the index so it does not fail on pre-existing duplicates.
|
||||
-- Idempotent: safe to re-run.
|
||||
-- ---------------------------------------------------------------------------
|
||||
|
||||
-- Remove duplicate rows, keeping only the last-written row per
|
||||
-- (host, user, store, key). ctid is the physical row identifier; the
|
||||
-- highest ctid is the most recently inserted row. No-op if no duplicates.
|
||||
delete from prosody
|
||||
where ctid in (
|
||||
select ctid from (
|
||||
select ctid,
|
||||
row_number() over (
|
||||
partition by host, "user", store, key
|
||||
order by ctid desc
|
||||
) as rn
|
||||
from prosody
|
||||
) t
|
||||
where rn > 1
|
||||
);
|
||||
|
||||
create unique index if not exists prosody_unique_index
|
||||
on prosody ("host", "user", "store", "key");
|
||||
|
||||
|
||||
@@ -50,7 +50,29 @@ create extension if not exists pgcrypto;
|
||||
-- creates this itself via create_table(); re-creating it here with IF NOT
|
||||
-- EXISTS is a harmless no-op in that case and fixes deployments where the
|
||||
-- table was created by an older Prosody version without the unique index.
|
||||
--
|
||||
-- Without the unique index, mod_storage_sql falls back to SELECT-then-INSERT
|
||||
-- instead of ON CONFLICT upsert, which can create duplicate rows under
|
||||
-- concurrent writes (common in the fcmtoken map store). Deduplicate before
|
||||
-- creating the index so it does not fail on pre-existing duplicates.
|
||||
-- ---------------------------------------------------------------------------
|
||||
|
||||
-- Remove duplicate rows, keeping only the last-written row per
|
||||
-- (host, user, store, key). ctid is the physical row identifier; the
|
||||
-- highest ctid is the most recently inserted row. No-op if no duplicates.
|
||||
delete from prosody
|
||||
where ctid in (
|
||||
select ctid from (
|
||||
select ctid,
|
||||
row_number() over (
|
||||
partition by host, "user", store, key
|
||||
order by ctid desc
|
||||
) as rn
|
||||
from prosody
|
||||
) t
|
||||
where rn > 1
|
||||
);
|
||||
|
||||
create unique index if not exists prosody_unique_index
|
||||
on prosody ("host", "user", "store", "key");
|
||||
|
||||
|
||||
Reference in New Issue
Block a user