mirror of
https://git.anonymousland.org/anonymousland/synapse.git
synced 2025-08-09 09:42:14 -04:00
Use a chain cover index to efficiently calculate auth chain difference (#8868)
This commit is contained in:
parent
671138f658
commit
1315a2e8be
14 changed files with 1777 additions and 56 deletions
|
@ -13,6 +13,11 @@
|
|||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import attr
|
||||
from parameterized import parameterized
|
||||
|
||||
from synapse.events import _EventInternalMetadata
|
||||
|
||||
import tests.unittest
|
||||
import tests.utils
|
||||
|
||||
|
@ -113,7 +118,154 @@ class EventFederationWorkerStoreTestCase(tests.unittest.HomeserverTestCase):
|
|||
r = self.get_success(self.store.get_rooms_with_many_extremities(5, 1, [room1]))
|
||||
self.assertTrue(r == [room2] or r == [room3])
|
||||
|
||||
def test_auth_difference(self):
|
||||
@parameterized.expand([(True,), (False,)])
|
||||
def test_auth_difference(self, use_chain_cover_index: bool):
|
||||
room_id = "@ROOM:local"
|
||||
|
||||
# The silly auth graph we use to test the auth difference algorithm,
|
||||
# where the top are the most recent events.
|
||||
#
|
||||
# A B
|
||||
# \ /
|
||||
# D E
|
||||
# \ |
|
||||
# ` F C
|
||||
# | /|
|
||||
# G ´ |
|
||||
# | \ |
|
||||
# H I
|
||||
# | |
|
||||
# K J
|
||||
|
||||
auth_graph = {
|
||||
"a": ["e"],
|
||||
"b": ["e"],
|
||||
"c": ["g", "i"],
|
||||
"d": ["f"],
|
||||
"e": ["f"],
|
||||
"f": ["g"],
|
||||
"g": ["h", "i"],
|
||||
"h": ["k"],
|
||||
"i": ["j"],
|
||||
"k": [],
|
||||
"j": [],
|
||||
}
|
||||
|
||||
depth_map = {
|
||||
"a": 7,
|
||||
"b": 7,
|
||||
"c": 4,
|
||||
"d": 6,
|
||||
"e": 6,
|
||||
"f": 5,
|
||||
"g": 3,
|
||||
"h": 2,
|
||||
"i": 2,
|
||||
"k": 1,
|
||||
"j": 1,
|
||||
}
|
||||
|
||||
# Mark the room as not having a cover index
|
||||
|
||||
def store_room(txn):
|
||||
self.store.db_pool.simple_insert_txn(
|
||||
txn,
|
||||
"rooms",
|
||||
{
|
||||
"room_id": room_id,
|
||||
"creator": "room_creator_user_id",
|
||||
"is_public": True,
|
||||
"room_version": "6",
|
||||
"has_auth_chain_index": use_chain_cover_index,
|
||||
},
|
||||
)
|
||||
|
||||
self.get_success(self.store.db_pool.runInteraction("store_room", store_room))
|
||||
|
||||
# We rudely fiddle with the appropriate tables directly, as that's much
|
||||
# easier than constructing events properly.
|
||||
|
||||
def insert_event(txn):
|
||||
stream_ordering = 0
|
||||
|
||||
for event_id in auth_graph:
|
||||
stream_ordering += 1
|
||||
depth = depth_map[event_id]
|
||||
|
||||
self.store.db_pool.simple_insert_txn(
|
||||
txn,
|
||||
table="events",
|
||||
values={
|
||||
"event_id": event_id,
|
||||
"room_id": room_id,
|
||||
"depth": depth,
|
||||
"topological_ordering": depth,
|
||||
"type": "m.test",
|
||||
"processed": True,
|
||||
"outlier": False,
|
||||
"stream_ordering": stream_ordering,
|
||||
},
|
||||
)
|
||||
|
||||
self.hs.datastores.persist_events._persist_event_auth_chain_txn(
|
||||
txn,
|
||||
[
|
||||
FakeEvent(event_id, room_id, auth_graph[event_id])
|
||||
for event_id in auth_graph
|
||||
],
|
||||
)
|
||||
|
||||
self.get_success(self.store.db_pool.runInteraction("insert", insert_event,))
|
||||
|
||||
# Now actually test that various combinations give the right result:
|
||||
|
||||
difference = self.get_success(
|
||||
self.store.get_auth_chain_difference(room_id, [{"a"}, {"b"}])
|
||||
)
|
||||
self.assertSetEqual(difference, {"a", "b"})
|
||||
|
||||
difference = self.get_success(
|
||||
self.store.get_auth_chain_difference(room_id, [{"a"}, {"b"}, {"c"}])
|
||||
)
|
||||
self.assertSetEqual(difference, {"a", "b", "c", "e", "f"})
|
||||
|
||||
difference = self.get_success(
|
||||
self.store.get_auth_chain_difference(room_id, [{"a", "c"}, {"b"}])
|
||||
)
|
||||
self.assertSetEqual(difference, {"a", "b", "c"})
|
||||
|
||||
difference = self.get_success(
|
||||
self.store.get_auth_chain_difference(room_id, [{"a", "c"}, {"b", "c"}])
|
||||
)
|
||||
self.assertSetEqual(difference, {"a", "b"})
|
||||
|
||||
difference = self.get_success(
|
||||
self.store.get_auth_chain_difference(room_id, [{"a"}, {"b"}, {"d"}])
|
||||
)
|
||||
self.assertSetEqual(difference, {"a", "b", "d", "e"})
|
||||
|
||||
difference = self.get_success(
|
||||
self.store.get_auth_chain_difference(room_id, [{"a"}, {"b"}, {"c"}, {"d"}])
|
||||
)
|
||||
self.assertSetEqual(difference, {"a", "b", "c", "d", "e", "f"})
|
||||
|
||||
difference = self.get_success(
|
||||
self.store.get_auth_chain_difference(room_id, [{"a"}, {"b"}, {"e"}])
|
||||
)
|
||||
self.assertSetEqual(difference, {"a", "b"})
|
||||
|
||||
difference = self.get_success(
|
||||
self.store.get_auth_chain_difference(room_id, [{"a"}])
|
||||
)
|
||||
self.assertSetEqual(difference, set())
|
||||
|
||||
def test_auth_difference_partial_cover(self):
|
||||
"""Test that we correctly handle rooms where not all events have a chain
|
||||
cover calculated. This can happen in some obscure edge cases, including
|
||||
during the background update that calculates the chain cover for old
|
||||
rooms.
|
||||
"""
|
||||
|
||||
room_id = "@ROOM:local"
|
||||
|
||||
# The silly auth graph we use to test the auth difference algorithm,
|
||||
|
@ -162,43 +314,74 @@ class EventFederationWorkerStoreTestCase(tests.unittest.HomeserverTestCase):
|
|||
# We rudely fiddle with the appropriate tables directly, as that's much
|
||||
# easier than constructing events properly.
|
||||
|
||||
def insert_event(txn, event_id, stream_ordering):
|
||||
|
||||
depth = depth_map[event_id]
|
||||
|
||||
def insert_event(txn):
|
||||
# First insert the room and mark it as having a chain cover.
|
||||
self.store.db_pool.simple_insert_txn(
|
||||
txn,
|
||||
table="events",
|
||||
values={
|
||||
"event_id": event_id,
|
||||
"rooms",
|
||||
{
|
||||
"room_id": room_id,
|
||||
"depth": depth,
|
||||
"topological_ordering": depth,
|
||||
"type": "m.test",
|
||||
"processed": True,
|
||||
"outlier": False,
|
||||
"stream_ordering": stream_ordering,
|
||||
"creator": "room_creator_user_id",
|
||||
"is_public": True,
|
||||
"room_version": "6",
|
||||
"has_auth_chain_index": True,
|
||||
},
|
||||
)
|
||||
|
||||
self.store.db_pool.simple_insert_many_txn(
|
||||
stream_ordering = 0
|
||||
|
||||
for event_id in auth_graph:
|
||||
stream_ordering += 1
|
||||
depth = depth_map[event_id]
|
||||
|
||||
self.store.db_pool.simple_insert_txn(
|
||||
txn,
|
||||
table="events",
|
||||
values={
|
||||
"event_id": event_id,
|
||||
"room_id": room_id,
|
||||
"depth": depth,
|
||||
"topological_ordering": depth,
|
||||
"type": "m.test",
|
||||
"processed": True,
|
||||
"outlier": False,
|
||||
"stream_ordering": stream_ordering,
|
||||
},
|
||||
)
|
||||
|
||||
# Insert all events apart from 'B'
|
||||
self.hs.datastores.persist_events._persist_event_auth_chain_txn(
|
||||
txn,
|
||||
table="event_auth",
|
||||
values=[
|
||||
{"event_id": event_id, "room_id": room_id, "auth_id": a}
|
||||
for a in auth_graph[event_id]
|
||||
[
|
||||
FakeEvent(event_id, room_id, auth_graph[event_id])
|
||||
for event_id in auth_graph
|
||||
if event_id != "b"
|
||||
],
|
||||
)
|
||||
|
||||
next_stream_ordering = 0
|
||||
for event_id in auth_graph:
|
||||
next_stream_ordering += 1
|
||||
self.get_success(
|
||||
self.store.db_pool.runInteraction(
|
||||
"insert", insert_event, event_id, next_stream_ordering
|
||||
)
|
||||
# Now we insert the event 'B' without a chain cover, by temporarily
|
||||
# pretending the room doesn't have a chain cover.
|
||||
|
||||
self.store.db_pool.simple_update_txn(
|
||||
txn,
|
||||
table="rooms",
|
||||
keyvalues={"room_id": room_id},
|
||||
updatevalues={"has_auth_chain_index": False},
|
||||
)
|
||||
|
||||
self.hs.datastores.persist_events._persist_event_auth_chain_txn(
|
||||
txn, [FakeEvent("b", room_id, auth_graph["b"])],
|
||||
)
|
||||
|
||||
self.store.db_pool.simple_update_txn(
|
||||
txn,
|
||||
table="rooms",
|
||||
keyvalues={"room_id": room_id},
|
||||
updatevalues={"has_auth_chain_index": True},
|
||||
)
|
||||
|
||||
self.get_success(self.store.db_pool.runInteraction("insert", insert_event,))
|
||||
|
||||
# Now actually test that various combinations give the right result:
|
||||
|
||||
difference = self.get_success(
|
||||
|
@ -240,3 +423,21 @@ class EventFederationWorkerStoreTestCase(tests.unittest.HomeserverTestCase):
|
|||
self.store.get_auth_chain_difference(room_id, [{"a"}])
|
||||
)
|
||||
self.assertSetEqual(difference, set())
|
||||
|
||||
|
||||
@attr.s
|
||||
class FakeEvent:
|
||||
event_id = attr.ib()
|
||||
room_id = attr.ib()
|
||||
auth_events = attr.ib()
|
||||
|
||||
type = "foo"
|
||||
state_key = "foo"
|
||||
|
||||
internal_metadata = _EventInternalMetadata({})
|
||||
|
||||
def auth_event_ids(self):
|
||||
return self.auth_events
|
||||
|
||||
def is_state(self):
|
||||
return True
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue