Skip to content
This repository has been archived by the owner on Apr 26, 2024. It is now read-only.

Rewrite get push actions queries #13597

Merged
merged 6 commits into from
Aug 24, 2022

Conversation

Fizzadar
Copy link
Contributor

@Fizzadar Fizzadar commented Aug 23, 2022

Previously two large queries were used to fetch push actions for a user, each with a nested query to the receipts table.

This simplifies things by hoisting out the receipts query and then uses a single simplified query to get the push actions and combines the results to get the same data. Two complex queries replaced with two simpler ones.

Part of #13448

Signed off by Nick @ Beeper (@Fizzadar).

Pull Request Checklist

Comparison

Before/current queries:

-- 1st query:

synapse=> EXPLAIN SELECT ep.event_id, ep.room_id, ep.stream_ordering, ep.actions,
   ep.highlight
 FROM (
   SELECT room_id,
       MAX(stream_ordering) as stream_ordering
   FROM events
   INNER JOIN receipts_linearized USING (room_id, event_id)
   WHERE receipt_type IN ('m.read', 'm.read.private') AND user_id = '@fizzadar:beeper.com'
   GROUP BY room_id
) AS rl,
 event_push_actions AS ep
 WHERE
   ep.room_id = rl.room_id
   AND ep.stream_ordering > rl.stream_ordering
    AND ep.stream_ordering > 452600000
    AND ep.stream_ordering <= 4527000000
   AND ep.user_id = '@fizzadar:beeper.com'
   AND ep.notif = 1
 ORDER BY ep.stream_ordering ASC;


-- 2nd query:

EXPLAIN SELECT ep.event_id, ep.room_id, ep.stream_ordering, ep.actions,
    ep.highlight
FROM event_push_actions AS ep
INNER JOIN events AS e USING (room_id, event_id)
WHERE
    ep.room_id NOT IN (
        SELECT room_id FROM receipts_linearized
        WHERE receipt_type IN ('m.read', 'm.read.private') AND user_id = '@fizzadar:beeper.com'
        GROUP BY room_id
    )
    AND ep.user_id = '@fizzadar:beeper.com'
    AND ep.stream_ordering > 452600000
    AND ep.stream_ordering <= 4527000000
    AND ep.notif = 1
ORDER BY ep.stream_ordering ASC;
                                                                                QUERY PLAN
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------
 Sort  (cost=181.27..181.28 rows=1 width=97)
   Sort Key: ep.stream_ordering
   ->  Nested Loop  (cost=1.68..181.26 rows=1 width=97)
         ->  GroupAggregate  (cost=1.25..177.63 rows=1 width=39)
               Group Key: events.room_id
               ->  Nested Loop  (cost=1.25..177.61 rows=1 width=39)
                     ->  Index Scan using nick_receipts_linearized_user_room_type on receipts_linearized  (cost=0.56..53.21 rows=32 width=75)
                           Index Cond: (user_id = '@fizzadar:beeper.com'::text)
                           Filter: (receipt_type = ANY ('{m.read,m.read.private}'::text[]))
                     ->  Index Scan using events_event_id_key on events  (cost=0.70..3.88 rows=1 width=83)
                           Index Cond: (event_id = receipts_linearized.event_id)
                           Filter: (receipts_linearized.room_id = room_id)
         ->  Index Scan using event_push_actions_room_id_user_id on event_push_actions ep  (cost=0.42..3.62 rows=1 width=97)
               Index Cond: ((room_id = events.room_id) AND (user_id = '@fizzadar:beeper.com'::text))
               Filter: ((stream_ordering > 452600000) AND (stream_ordering <= '4527000000'::bigint) AND (notif = 1) AND (stream_ordering > (max(events.stream_ordering))))
(15 rows)

                                                                   QUERY PLAN
------------------------------------------------------------------------------------------------------------------------------------------------
 Nested Loop  (cost=32.55..244.71 rows=1 width=97)
   ->  Index Scan using event_push_actions_u_highlight on event_push_actions ep  (cost=31.85..174.73 rows=18 width=97)
         Index Cond: ((user_id = '@fizzadar:beeper.com'::text) AND (stream_ordering > 452600000) AND (stream_ordering <= '4527000000'::bigint))
         Filter: ((NOT (hashed SubPlan 1)) AND (notif = 1))
         SubPlan 1
           ->  Group  (cost=0.56..31.22 rows=32 width=31)
                 Group Key: receipts_linearized.room_id
                 ->  Index Only Scan using nick_receipts_linearized_user_room_type on receipts_linearized  (cost=0.56..31.14 rows=32 width=31)
                       Index Cond: (user_id = '@fizzadar:beeper.com'::text)
                       Filter: (receipt_type = ANY ('{m.read,m.read.private}'::text[]))
   ->  Index Scan using events_event_id_key on events e  (cost=0.70..3.88 rows=1 width=75)
         Index Cond: (event_id = ep.event_id)
         Filter: (ep.room_id = room_id)
(13 rows)

Then after this change:

-- 1st query:

synapse=> EXPLAIN SELECT room_id, MAX(stream_ordering)
FROM receipts_linearized
INNER JOIN events USING (room_id, event_id)
WHERE receipt_type IN ('m.read', 'm.read.private')
AND user_id = '@fizzadar:beeper.com'
GROUP BY room_id;


-- 2nd query:

EXPLAIN SELECT ep.event_id, ep.room_id, ep.stream_ordering, ep.actions, ep.highlight
FROM event_push_actions AS ep
WHERE
    ep.user_id = '@fizzadar:beeper.com'
    AND ep.stream_ordering > 452600000
    AND ep.stream_ordering <= 4527000000
    AND ep.notif = 1
ORDER BY ep.stream_ordering ASC;
                                                            QUERY PLAN
----------------------------------------------------------------------------------------------------------------------------------
 GroupAggregate  (cost=1.25..177.63 rows=1 width=39)
   Group Key: receipts_linearized.room_id
   ->  Nested Loop  (cost=1.25..177.61 rows=1 width=39)
         ->  Index Scan using nick_receipts_linearized_user_room_type on receipts_linearized  (cost=0.56..53.21 rows=32 width=75)
               Index Cond: (user_id = '@fizzadar:beeper.com'::text)
               Filter: (receipt_type = ANY ('{m.read,m.read.private}'::text[]))
         ->  Index Scan using events_event_id_key on events  (cost=0.70..3.88 rows=1 width=83)
               Index Cond: (event_id = receipts_linearized.event_id)
               Filter: (receipts_linearized.room_id = room_id)
(9 rows)

                                                                QUERY PLAN
------------------------------------------------------------------------------------------------------------------------------------------
 Index Scan using event_push_actions_u_highlight on event_push_actions ep  (cost=0.55..143.22 rows=37 width=97)
   Index Cond: ((user_id = '@fizzadar:beeper.com'::text) AND (stream_ordering > 452600000) AND (stream_ordering <= '4527000000'::bigint))
   Filter: (notif = 1)
(3 rows)

@Fizzadar Fizzadar marked this pull request as ready for review August 23, 2022 11:08
@Fizzadar Fizzadar requested a review from a team as a code owner August 23, 2022 11:08
@@ -0,0 +1 @@
Optimise push action fetching queries. Contributed by Nick @ Beeper (@fizzadar).
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would get_unread_push_actions_for_user_in_range_for_email benefit from these changes too? I think the queries are identical except for columns returned.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes! Not sure how I missed that... 34be471

@clokep clokep requested a review from a team August 23, 2022 11:27
@clokep
Copy link
Member

clokep commented Aug 23, 2022

I didn't do a full review, just an observation!

@reivilibre
Copy link
Contributor

Sorry, struggling to follow where the optimisation is coming from on this one. (maybe separate commits would have been easier to keep track of, at least for this time of day there are quite a lot of things happening in one diff!) :).

I can see and read from your description that you've unified two queries into one, but it still seems like 2 calls are being made to that query? (and the query doesn't seem to be cached as far as I can see)

What am I missing? :)

@Fizzadar
Copy link
Contributor Author

Sorry, struggling to follow where the optimisation is coming from on this one. (maybe separate commits would have been easier to keep track of, at least for this time of day there are quite a lot of things happening in one diff!) :).

Totally agree, I struggled to make this into nice commits. Have re-attempted this here: develop...Fizzadar:synapse:optimise-push-actions-queries-rewrite-history - I think this is better?

I can see and read from your description that you've unified two queries into one, but it still seems like 2 calls are being made to that query? (and the query doesn't seem to be cached as far as I can see)

What am I missing? :)

Still two queries, before it was two large queries with a near-identical nested query on the receipts table. This change hoists the receipt query out and executes it first, then a simplified query to get the push actions. Hopefully the new history above will make more sense!

Can overwrite this history with that one if that's OK!

@reivilibre
Copy link
Contributor

yeah, feel free to overwrite the history

@Fizzadar Fizzadar force-pushed the optimise-push-actions-queries branch from 34be471 to 5c6dce6 Compare August 23, 2022 16:11
Copy link
Contributor

@reivilibre reivilibre left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks plausible, couple of small things. Thanks for taking the time to split the commits out for me :)

@@ -516,6 +516,26 @@ async def get_unread_push_actions_for_user_in_range_for_http(
),
)

def get_push_actions(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

just to be customary, this name should include the _txn suffix because it takes a Transaction.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@@ -615,7 +635,10 @@ def get_no_receipt(
stream_ordering=row[2],
actions=_deserialize_action(row[3], row[4]),
)
for row in after_read_receipt + no_read_receipt
for row in push_actions
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

to help with readability, especially since you're adding a condition, I'd be tempted to destructure this tuple and then refer to everything by name:

Suggested change
for row in push_actions
for event_id, room_id, stream_ordering, actions, highlight in push_actions

Copy link
Contributor Author

@Fizzadar Fizzadar Aug 23, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Much cleaner! b7a66e3

@@ -758,7 +803,10 @@ def get_no_receipt(
actions=_deserialize_action(row[3], row[4]),
received_ts=row[5],
)
for row in after_read_receipt + no_read_receipt
for row in push_actions
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ditto for destructuring here

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Fizzadar Fizzadar changed the title Rewrite get HTTP push actions queries Rewrite get push actions queries Aug 23, 2022
@reivilibre reivilibre merged commit b687010 into matrix-org:develop Aug 24, 2022
@Fizzadar Fizzadar deleted the optimise-push-actions-queries branch August 24, 2022 09:16
Fizzadar added a commit to beeper/synapse-legacy-fork that referenced this pull request Aug 25, 2022
Fizzadar added a commit to beeper/synapse-legacy-fork that referenced this pull request Sep 15, 2022
Synapse 1.67.0 (2022-09-13)
===========================

This release removes using the deprecated direct TCP replication configuration
for workers. Server admins should use Redis instead. See the [upgrade
notes](https://matrix-org.github.io/synapse/v1.67/upgrade.html#upgrading-to-v1670).

The minimum version of `poetry` supported for managing source checkouts is now
1.2.0.

**Notice:** from the next major release (1.68.0) installing Synapse from a source
checkout will require a recent Rust compiler. Those using packages or
`pip install matrix-synapse` will not be affected. See the [upgrade
notes](https://matrix-org.github.io/synapse/v1.67/upgrade.html#upgrading-to-v1670).

**Notice:** from the next major release (1.68.0), running Synapse with a SQLite
database will require SQLite version 3.27.0 or higher. (The [current minimum
 version is SQLite 3.22.0](https://github.com/matrix-org/synapse/blob/release-v1.67/synapse/storage/engines/sqlite.py#L69-L78).)
See [matrix-org#12983](matrix-org#12983) and the [upgrade notes](https://matrix-org.github.io/synapse/v1.67/upgrade.html#upgrading-to-v1670) for more details.

No significant changes since 1.67.0rc1.

Synapse 1.67.0rc1 (2022-09-06)
==============================

Features
--------

- Support setting the registration shared secret in a file, via a new `registration_shared_secret_path` configuration option. ([\matrix-org#13614](matrix-org#13614))
- Change the default startup behaviour so that any missing "additional" configuration files (signing key, etc) are generated automatically. ([\matrix-org#13615](matrix-org#13615))
- Improve performance of sending messages in rooms with thousands of local users. ([\matrix-org#13634](matrix-org#13634))

Bugfixes
--------

- Fix a bug introduced in Synapse 1.13 where the [List Rooms admin API](https://matrix-org.github.io/synapse/develop/admin_api/rooms.html#list-room-api) would return integers instead of booleans for the `federatable` and `public` fields when using a Sqlite database. ([\matrix-org#13509](matrix-org#13509))
- Fix bug that user cannot `/forget` rooms after the last member has left the room. ([\matrix-org#13546](matrix-org#13546))
- Faster Room Joins: fix `/make_knock` blocking indefinitely when the room in question is a partial-stated room. ([\matrix-org#13583](matrix-org#13583))
- Fix loading the current stream position behind the actual position. ([\matrix-org#13585](matrix-org#13585))
- Fix a longstanding bug in `register_new_matrix_user` which meant it was always necessary to explicitly give a server URL. ([\matrix-org#13616](matrix-org#13616))
- Fix the running of [MSC1763](matrix-org/matrix-spec-proposals#1763) retention purge_jobs in deployments with background jobs running on a worker by forcing them back onto the main worker. Contributed by Brad @ Beeper. ([\matrix-org#13632](matrix-org#13632))
- Fix a long-standing bug that downloaded media for URL previews was not deleted while database background updates were running. ([\matrix-org#13657](matrix-org#13657))
- Fix [MSC3030](matrix-org/matrix-spec-proposals#3030) `/timestamp_to_event` endpoint to return the correct next event when the events have the same timestamp. ([\matrix-org#13658](matrix-org#13658))
- Fix bug where we wedge media plugins if clients disconnect early. Introduced in v1.22.0. ([\matrix-org#13660](matrix-org#13660))
- Fix a long-standing bug which meant that keys for unwhitelisted servers were not returned by `/_matrix/key/v2/query`. ([\matrix-org#13683](matrix-org#13683))
- Fix a bug introduced in Synapse v1.20.0 that would cause the unstable unread counts from [MSC2654](matrix-org/matrix-spec-proposals#2654) to be calculated even if the feature is disabled. ([\matrix-org#13694](matrix-org#13694))

Updates to the Docker image
---------------------------

- Update docker image to use a stable version of poetry. ([\matrix-org#13688](matrix-org#13688))

Improved Documentation
----------------------

- Improve the description of the ["chain cover index"](https://matrix-org.github.io/synapse/latest/auth_chain_difference_algorithm.html) used internally by Synapse. ([\matrix-org#13602](matrix-org#13602))
- Document how ["monthly active users"](https://matrix-org.github.io/synapse/latest/usage/administration/monthly_active_users.html) is calculated and used. ([\matrix-org#13617](matrix-org#13617))
- Improve documentation around user registration. ([\matrix-org#13640](matrix-org#13640))
- Remove documentation of legacy `frontend_proxy` worker app. ([\matrix-org#13645](matrix-org#13645))
- Clarify documentation that HTTP replication traffic can be protected with a shared secret. ([\matrix-org#13656](matrix-org#13656))
- Remove unintentional colons from [config manual](https://matrix-org.github.io/synapse/latest/usage/configuration/config_documentation.html) headers. ([\matrix-org#13665](matrix-org#13665))
- Update docs to make enabling metrics more clear. ([\matrix-org#13678](matrix-org#13678))
- Clarify `(room_id, event_id)` global uniqueness and how we should scope our database schemas. ([\matrix-org#13701](matrix-org#13701))

Deprecations and Removals
-------------------------

- Drop support for calling `/_matrix/client/v3/rooms/{roomId}/invite` without an `id_access_token`, which was not permitted by the spec. Contributed by @Vetchu. ([\matrix-org#13241](matrix-org#13241))
- Remove redundant `_get_joined_users_from_context` cache. Contributed by Nick @ Beeper (@Fizzadar). ([\matrix-org#13569](matrix-org#13569))
- Remove the ability to use direct TCP replication with workers. Direct TCP replication was deprecated in Synapse v1.18.0. Workers now require using Redis. ([\matrix-org#13647](matrix-org#13647))
- Remove support for unstable [private read receipts](matrix-org/matrix-spec-proposals#2285). ([\matrix-org#13653](matrix-org#13653), [\matrix-org#13692](matrix-org#13692))

Internal Changes
----------------

- Extend the release script to wait for GitHub Actions to finish and to be usable as a guide for the whole process. ([\matrix-org#13483](matrix-org#13483))
- Add experimental configuration option to allow disabling legacy Prometheus metric names. ([\matrix-org#13540](matrix-org#13540))
- Cache user IDs instead of profiles to reduce cache memory usage. Contributed by Nick @ Beeper (@Fizzadar). ([\matrix-org#13573](matrix-org#13573), [\matrix-org#13600](matrix-org#13600))
- Optimize how Synapse calculates domains to fetch from during backfill. ([\matrix-org#13575](matrix-org#13575))
- Comment about a better future where we can get the state diff between two events. ([\matrix-org#13586](matrix-org#13586))
- Instrument `_check_sigs_and_hash_and_fetch` to trace time spent in child concurrent calls for understandable traces in Jaeger. ([\matrix-org#13588](matrix-org#13588))
- Improve performance of `@cachedList`. ([\matrix-org#13591](matrix-org#13591))
- Minor speed up of fetching large numbers of push rules. ([\matrix-org#13592](matrix-org#13592))
- Optimise push action fetching queries. Contributed by Nick @ Beeper (@Fizzadar). ([\matrix-org#13597](matrix-org#13597))
- Rename `event_map` to `unpersisted_events` when computing the auth differences. ([\matrix-org#13603](matrix-org#13603))
- Refactor `get_users_in_room(room_id)` mis-use with dedicated `get_current_hosts_in_room(room_id)` function. ([\matrix-org#13605](matrix-org#13605))
- Use dedicated `get_local_users_in_room(room_id)` function to find local users when calculating `join_authorised_via_users_server` of a `/make_join` request. ([\matrix-org#13606](matrix-org#13606))
- Refactor `get_users_in_room(room_id)` mis-use to lookup single local user with dedicated `check_local_user_in_room(...)` function. ([\matrix-org#13608](matrix-org#13608))
- Drop unused column `application_services_state.last_txn`. ([\matrix-org#13627](matrix-org#13627))
- Improve readability of Complement CI logs by printing failure results last. ([\matrix-org#13639](matrix-org#13639))
- Generalise the `@cancellable` annotation so it can be used on functions other than just servlet methods. ([\matrix-org#13662](matrix-org#13662))
- Introduce a `CommonUsageMetrics` class to share some usage metrics between the Prometheus exporter and the phone home stats. ([\matrix-org#13671](matrix-org#13671))
- Add some logging to help track down matrix-org#13444. ([\matrix-org#13679](matrix-org#13679))
- Update poetry lock file for v1.2.0. ([\matrix-org#13689](matrix-org#13689))
- Add cache to `is_partial_state_room`. ([\matrix-org#13693](matrix-org#13693))
- Update the Grafana dashboard that is included with Synapse in the `contrib` directory. ([\matrix-org#13697](matrix-org#13697))
- Only run trial CI on all python versions on non-PRs. ([\matrix-org#13698](matrix-org#13698))
- Fix typechecking with latest types-jsonschema. ([\matrix-org#13712](matrix-org#13712))
- Reduce number of CI checks we run for PRs. ([\matrix-org#13713](matrix-org#13713))

# -----BEGIN PGP SIGNATURE-----
#
# iQFEBAABCgAuFiEEBTGR3/RnAzBGUif3pULk7RsPrAkFAmMgR2QQHGVyaWtAbWF0
# cml4Lm9yZwAKCRClQuTtGw+sCfG7B/94PwW1ChsaI8hkz/3e+93PEl/mNJ6YFaEB
# 5pP4Dh/0dipP/iKbpgNuj5xz/JFnIi8D49A8sKNnku3jk0/8AZHgqDiBgOkrN76z
# Y3awo5Q9ag4xww/105V3bhdnX1NrX8Avf6F2jchDv6/9q8wQHGBPg6DMgfZ/m/BL
# SB4dypbbNpgLykuwtWxx6YMUYH+trsXJOn/MoAqld3QcZsqkDR25wXCt9+Dr+6AT
# dPd/czi8kV8ruU59tf2K5HB7XKzBW9S3Qb3dJJmGOTTJ7ccUkN/XuTwqnII950Mo
# bSlMXjY2hqk8rKUNhGZpi9bqUkwNhMgOkZl9A0Y1XtsXx6yjy0T/
# =zSGi
# -----END PGP SIGNATURE-----
# gpg: Signature made Tue Sep 13 10:03:32 2022 BST
# gpg:                using RSA key 053191DFF4670330465227F7A542E4ED1B0FAC09
# gpg:                issuer "erik@matrix.org"
# gpg: Can't check signature: No public key

# Conflicts:
#	synapse/config/experimental.py
#	synapse/push/bulk_push_rule_evaluator.py
#	synapse/storage/databases/main/event_push_actions.py
#	synapse/util/caches/descriptors.py
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants