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

Add an approximate difference method to StateFilters #10825

Merged
merged 35 commits into from
Oct 12, 2021
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
7dad902
Add an approximate difference method to StateFilters
reivilibre Sep 15, 2021
1fe75e6
Add tests for the approximate difference of StateFilters
reivilibre Sep 15, 2021
a05692c
Newsfile
reivilibre Sep 15, 2021
10a7071
Try to clarify docstring for `approx_difference`
reivilibre Sep 17, 2021
d0e14d5
Process all the keys to return a narrower state filter
reivilibre Sep 17, 2021
a5fdd46
Add more test cases
reivilibre Sep 17, 2021
0e0085c
STASH
reivilibre Sep 17, 2021
9d50f05
Tighten up the postconditions of `approx_difference`
reivilibre Sep 20, 2021
ace3316
Merge remote-tracking branch 'origin/develop' into rei/sf_diff
reivilibre Sep 20, 2021
c72c436
More wordsmithing — thanks David
reivilibre Sep 20, 2021
bacd394
Revert "STASH"
reivilibre Sep 20, 2021
cd1de9b
Remove needless set construction
reivilibre Sep 22, 2021
6bedcba
Simplify logic a bit, since this isn't operating in-place anyway
reivilibre Sep 22, 2021
42617db
Attempt to clean up `approx_difference` with improved comments and names
reivilibre Sep 22, 2021
0c8e930
Split out tests into own TestCase class
reivilibre Sep 22, 2021
b6274d6
Add extensive tests for all 4 combinations of include_others
reivilibre Sep 22, 2021
f6b4dc5
Merge a test
reivilibre Sep 22, 2021
0d1c3d8
Split out some very simple tests
reivilibre Sep 22, 2021
18714d7
Deduplicate the old tests into the systematic style tests
reivilibre Sep 22, 2021
770afea
Add function to decompose a StateFilter into four parts
reivilibre Sep 22, 2021
e119af9
Add `StateFilter.freeze` convenience constructor
reivilibre Sep 22, 2021
70f646a
Add `recompose_from_four_parts` method as inverse
reivilibre Sep 22, 2021
c9bb226
Use a shorter version of `decompose_into_four_parts`.
reivilibre Sep 24, 2021
093f670
Use a shorter version of `recompose_from_four_parts`
reivilibre Sep 24, 2021
a187c24
Use a step-by-step implementation of `approx_difference` rather than …
reivilibre Sep 24, 2021
4bbe3d1
Nest the ifs for clarity; no functional change
reivilibre Sep 24, 2021
20bc299
Prefix decompose/recompose method names with underscores
reivilibre Sep 24, 2021
27c3a7a
Use self_excludes as it's equivalent with this definition of decompose
reivilibre Sep 24, 2021
54d77c9
Rename `subtrahend` to `other` to follow convention
reivilibre Sep 28, 2021
538f99e
Try 'included' rather than 'admitted' to describe state filters
reivilibre Sep 28, 2021
bf202bc
Rename derived variables from sub(trahend) to other.
reivilibre Sep 28, 2021
9169d38
Add a little bit of context as to why this is useful
reivilibre Sep 28, 2021
1f3008b
Use 'returned' instead of 'resultant' as that may be clearer
reivilibre Sep 28, 2021
3552bc1
Remove formal definition to focus on one way of explaining
reivilibre Sep 28, 2021
4eaf980
Use `StateFilter.freeze` in tests to improve readability
reivilibre Sep 29, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 45 additions & 0 deletions synapse/storage/databases/state/store.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
from synapse.storage.types import Cursor
from synapse.storage.util.sequence import build_sequence_generator
from synapse.types import MutableStateMap, StateMap
from synapse.util.async_helpers import ObservableDeferred
from synapse.util.caches.descriptors import cached
from synapse.util.caches.dictionary_cache import DictionaryCache

Expand Down Expand Up @@ -91,6 +92,12 @@ def __init__(self, database: DatabasePool, db_conn, hs):
500000,
)

# Current ongoing get_state_for_groups in-flight requests
# {group ID -> {StateFilter -> ObservableDeferred}}
self._state_group_inflight_requests: Dict[
int, Dict[StateFilter, ObservableDeferred[StateMap[str]]]
] = {}

def get_max_state_group_txn(txn: Cursor):
txn.execute("SELECT COALESCE(max(id), 0) FROM state_groups")
return txn.fetchone()[0]
Expand Down Expand Up @@ -249,6 +256,44 @@ async def _get_state_for_groups(
if not incomplete_groups:
return state

# try and rely on in-flight requests to complete this request without
# needing to spawn additional queries.

# (group ID, ObservableDeferred of request result)
reusable_inflight_requests: List[
Tuple[int, ObservableDeferred[StateMap[str]]]
] = []
# group ID -> left over StateFilter to request
requests_to_spawn: Dict[int, StateFilter] = {}

for group in incomplete_groups:
requests_in_flight_for_group = self._state_group_inflight_requests.get(
group
)
if requests_in_flight_for_group is None:
continue

state_filter_left_over = state_filter
for (
request_state_filter,
request_deferred,
) in requests_in_flight_for_group.items():
new_state_filter_left_over = state_filter_left_over.approx_difference(
request_state_filter
)
if new_state_filter_left_over != state_filter_left_over:
# reusing this request narrows our StateFilter down a bit.
reusable_inflight_requests.append((group, request_deferred))
state_filter_left_over = new_state_filter_left_over
if state_filter_left_over == StateFilter.none():
# we have managed to collect enough of the in-flight requests
# to cover our StateFilter and give us the state we need.
break
else:
# we have some of the state filter left over, so need to spawn
# a request
requests_to_spawn[group] = state_filter_left_over

cache_sequence_nm = self._state_group_cache.sequence
cache_sequence_m = self._state_group_members_cache.sequence

Expand Down
10 changes: 6 additions & 4 deletions synapse/storage/state.py
Original file line number Diff line number Diff line change
Expand Up @@ -361,18 +361,20 @@ def approx_difference(self, subtrahend: "StateFilter") -> "StateFilter":
Returns a state filter which represents `self - subtrahend`.

The resultant state filter MUST admit all state events that are admitted
reivilibre marked this conversation as resolved.
Show resolved Hide resolved
by only this filter (`self`) and not `subtrahend`. (1)
by only this filter (`self`) and not `subtrahend`.
The resultant filter MAY be an over-approximation: the resultant state
filter MAY additionally admit other state events.
filter MAY additionally admit some state events from `subtrahend`.


Formally, if the set of state events admitted by a state filter F are
written as E(F), then the resultant state filter bears this property:

E(difference(self, subtrahend)) ⊇ E(self) ∖ E(subtrahend)
E(self) ∖ E(subtrahend)
⊆ E(approx_difference(self, subtrahend))
⊆ E(self)


This function attempts to return the narrowest such state filter.
This implementation attempts to return the narrowest such state filter.
In the case that `self` contains wildcards for state types where
`subtrahend` contains specific state keys, an approximation must be made:
the resultant state filter keeps the wildcard, as state filters are not
Expand Down