@@ -93,13 +93,6 @@ def _get_state_groups_from_groups_txn(
9393
9494 results : Dict [int , MutableStateMap [str ]] = {group : {} for group in groups }
9595
96- where_clause , where_args = state_filter .make_sql_filter_clause ()
97-
98- # Unless the filter clause is empty, we're going to append it after an
99- # existing where clause
100- if where_clause :
101- where_clause = " AND (%s)" % (where_clause ,)
102-
10396 if isinstance (self .database_engine , PostgresEngine ):
10497 # Temporarily disable sequential scans in this transaction. This is
10598 # a temporary hack until we can add the right indices in
@@ -110,38 +103,104 @@ def _get_state_groups_from_groups_txn(
110103 # against `state_groups_state` to fetch the latest state.
111104 # It assumes that previous state groups are always numerically
112105 # lesser.
113- # The PARTITION is used to get the event_id in the greatest state
114- # group for the given type, state_key.
115106 # This may return multiple rows per (type, state_key), but last_value
116107 # should be the same.
117108 sql = """
118- WITH RECURSIVE state (state_group) AS (
109+ WITH RECURSIVE sgs (state_group) AS (
119110 VALUES(?::bigint)
120111 UNION ALL
121- SELECT prev_state_group FROM state_group_edges e, state s
112+ SELECT prev_state_group FROM state_group_edges e, sgs s
122113 WHERE s.state_group = e.state_group
123114 )
124- SELECT DISTINCT ON (type, state_key)
125- type, state_key, event_id
126- FROM state_groups_state
127- WHERE state_group IN (
128- SELECT state_group FROM state
129- ) %s
130- ORDER BY type, state_key, state_group DESC
115+ %s
131116 """
132117
118+ overall_select_query_args : List [Union [int , str ]] = []
119+
120+ # This is an optimization to create a select clause per-condition. This
121+ # makes the query planner a lot smarter on what rows should pull out in the
122+ # first place and we end up with something that takes 10x less time to get a
123+ # result.
124+ use_condition_optimization = (
125+ not state_filter .include_others and not state_filter .is_full ()
126+ )
127+ state_filter_condition_combos : List [Tuple [str , Optional [str ]]] = []
128+ # We don't need to caclculate this list if we're not using the condition
129+ # optimization
130+ if use_condition_optimization :
131+ for etype , state_keys in state_filter .types .items ():
132+ if state_keys is None :
133+ state_filter_condition_combos .append ((etype , None ))
134+ else :
135+ for state_key in state_keys :
136+ state_filter_condition_combos .append ((etype , state_key ))
137+ # And here is the optimization itself. We don't want to do the optimization
138+ # if there are too many individual conditions. 10 is an arbitrary number
139+ # with no testing behind it but we do know that we specifically made this
140+ # optimization for when we grab the necessary state out for
141+ # `filter_events_for_client` which just uses 2 conditions
142+ # (`EventTypes.RoomHistoryVisibility` and `EventTypes.Member`).
143+ if use_condition_optimization and len (state_filter_condition_combos ) < 10 :
144+ select_clause_list : List [str ] = []
145+ for etype , skey in state_filter_condition_combos :
146+ if skey is None :
147+ where_clause = "(type = ?)"
148+ overall_select_query_args .extend ([etype ])
149+ else :
150+ where_clause = "(type = ? AND state_key = ?)"
151+ overall_select_query_args .extend ([etype , skey ])
152+
153+ select_clause_list .append (
154+ f"""
155+ (
156+ SELECT DISTINCT ON (type, state_key)
157+ type, state_key, event_id
158+ FROM state_groups_state
159+ INNER JOIN sgs USING (state_group)
160+ WHERE { where_clause }
161+ ORDER BY type, state_key, state_group DESC
162+ )
163+ """
164+ )
165+
166+ overall_select_clause = " UNION " .join (select_clause_list )
167+ else :
168+ where_clause , where_args = state_filter .make_sql_filter_clause ()
169+ # Unless the filter clause is empty, we're going to append it after an
170+ # existing where clause
171+ if where_clause :
172+ where_clause = " AND (%s)" % (where_clause ,)
173+
174+ overall_select_query_args .extend (where_args )
175+
176+ overall_select_clause = f"""
177+ SELECT DISTINCT ON (type, state_key)
178+ type, state_key, event_id
179+ FROM state_groups_state
180+ WHERE state_group IN (
181+ SELECT state_group FROM sgs
182+ ) { where_clause }
183+ ORDER BY type, state_key, state_group DESC
184+ """
185+
133186 for group in groups :
134187 args : List [Union [int , str ]] = [group ]
135- args .extend (where_args )
188+ args .extend (overall_select_query_args )
136189
137- txn .execute (sql % (where_clause ,), args )
190+ txn .execute (sql % (overall_select_clause ,), args )
138191 for row in txn :
139192 typ , state_key , event_id = row
140193 key = (intern_string (typ ), intern_string (state_key ))
141194 results [group ][key ] = event_id
142195 else :
143196 max_entries_returned = state_filter .max_entries_returned ()
144197
198+ where_clause , where_args = state_filter .make_sql_filter_clause ()
199+ # Unless the filter clause is empty, we're going to append it after an
200+ # existing where clause
201+ if where_clause :
202+ where_clause = " AND (%s)" % (where_clause ,)
203+
145204 # We don't use WITH RECURSIVE on sqlite3 as there are distributions
146205 # that ship with an sqlite3 version that doesn't support it (e.g. wheezy)
147206 for group in groups :
0 commit comments