-
-
Notifications
You must be signed in to change notification settings - Fork 2.1k
The state_group_edges
table doesn't have a unique index; can severely impact WITH RECURSIVE state(state_group) AS ...
query performance if duplicates are somehow introduced
#11779
Comments
This was #5064 (comment). Suppose you've got 4 state groups (A,B,C,D), each linked with edges, and each with (say) one state event. Correct behaviour says you should get 4 events back. If you double the edges, then you end up checking SG A once, B twice, C four times, D 8 times, for a total of 15 state groups (and hence 15 events). Hence, square. |
see also #7772 for my first discovery of this issue. |
That sounds more exponential (2^n - 1) to me (i.e. worse) — if you add an E in the same configuration you'll check it 16 times for this (perhaps artificial) example. |
errr good point. |
(and given state group chains often run into a hundred links... that's a lot of rows) |
This has severely impacted my performance fwiw. Just from tailing logs in postgres:
|
@Half-Shot : to confirm it's caused by duplicate rows, would you mind running this?: SELECT COUNT(1) from ( SELECT state_group, prev_state_group FROM state_group_edges GROUP BY state_group, prev_state_group HAVING COUNT(ctid) > 1 ) AS dupes; This ran fairly quickly for me (less than a second for 295k rows in the table) but I can't guarantee whether it will or not for you, but it would be nice to confirm that this is the cause if it works easily. |
Bah, looks like I don't have dupes, just regular slowness. |
A community member in the Synapse Admins room today came with an issue where this query was being slow. Luckily I knew of this issue (and the query I posted two messages above came back with a non-zero result to confirm my hypothesis!). What went wrong is that they cancelled (^C?) the import of their backup half-way through and later did it again. It's not that difficult a mistake to make. 'Luckily' their Synapse was so completely borked by the slow queries that they came looking for help and the suggested solution was to delete the database and freshly import it again. However in general it would be nice to prevent others from falling into this trap. As evidenced today: it does happen! |
Can we (do we not?) wrap the import in a transaction to prevent this kind of thing? (That said, I'd still rather have uniqueness requirements enforced by the schema) |
That depends on what |
Looks like we'd have to opt-in to it with |
fc17: That was me… and I used |
Maybe using workers will help this? |
@chagai95 this issue is about performance problems caused by duplicate entries. As your query shows, you don't have any. Workers are not the right solution to duplicate rows. |
Ok thx, so I'll create s new ticket, thx! |
I think the work here is to convert the existing index on (A technically preferable solution might be to replace the This should be a fairly simple thing to do via background updates (the |
Should this be in state store instead...?
Useful QueriesPostgresDiagnosing the conditionSELECT COUNT(1) from ( SELECT state_group, prev_state_group FROM state_group_edges GROUP BY state_group, prev_state_group HAVING COUNT(ctid) > 1 ) AS dupes; This will return Solving the conditionBEGIN;
DELETE FROM state_group_edges WHERE (ctid, state_group, prev_state_group) IN (
SELECT row_id, state_group, prev_state_group
FROM (
SELECT
ctid AS row_id,
MIN(ctid) OVER (PARTITION BY state_group, prev_state_group) AS min_row_id,
state_group,
prev_state_group
FROM state_group_edges
) AS t1
WHERE row_id <> min_row_id
);
COMMIT; If you want some peace of mind, check that SQLiteDiagnosing the conditionSELECT COUNT(1) from ( SELECT state_group, prev_state_group FROM state_group_edges GROUP BY state_group, prev_state_group HAVING COUNT(rowid) > 1 ) AS dupes; This will return Solving the conditionUsing the BEGIN;
DELETE FROM state_group_edges WHERE (rowid, state_group, prev_state_group) IN (
SELECT row_id, state_group, prev_state_group
FROM (
SELECT
rowid AS row_id,
MIN(rowid) OVER (PARTITION BY state_group, prev_state_group) AS min_row_id,
state_group,
prev_state_group
FROM state_group_edges
)
WHERE row_id <> min_row_id
);
COMMIT; If you want some peace of mind, enter |
This is similar to, but not the same as, #10301.
The
state_group_edges
table doesn't have a unique index, so it's 'easy' to introduce duplicate rows e.g. by restoring a backup twice (or potentially even due to pure bugs in Synapse).This query as part of
_get_state_groups_from_groups
:would be seriously slowed down by the presence of duplicate rows (I seem to think someone mentioned it would return the square of the number of rows it would usually, but I haven't verified that and I can't find that again to reference it).
It may be worth adding a unique index or perhaps having an SQL snippet that administrators can run to recover from this (though I would favour the former — make it hard to do in the first place).
As an aside:
state_group_edges
can potentially contain loops but that shouldn't arise from recovering backups (as far as I know) and it's not so easy to do something about — but worth bearing in mind that it would cause the query to go forever.The text was updated successfully, but these errors were encountered: