test_cleanup_extrems.py 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265
  1. # -*- coding: utf-8 -*-
  2. # Copyright 2019 The Matrix.org Foundation C.I.C.
  3. #
  4. # Licensed under the Apache License, Version 2.0 (the "License");
  5. # you may not use this file except in compliance with the License.
  6. # You may obtain a copy of the License at
  7. #
  8. # http://www.apache.org/licenses/LICENSE-2.0
  9. #
  10. # Unless required by applicable law or agreed to in writing, software
  11. # distributed under the License is distributed on an "AS IS" BASIS,
  12. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. # See the License for the specific language governing permissions and
  14. # limitations under the License.
  15. import os.path
  16. from synapse.storage import prepare_database
  17. from synapse.types import Requester, UserID
  18. from tests.unittest import HomeserverTestCase
  19. class CleanupExtremBackgroundUpdateStoreTestCase(HomeserverTestCase):
  20. """
  21. Test the background update to clean forward extremities table.
  22. """
  23. def prepare(self, reactor, clock, homeserver):
  24. self.store = homeserver.get_datastore()
  25. self.room_creator = homeserver.get_room_creation_handler()
  26. # Create a test user and room
  27. self.user = UserID("alice", "test")
  28. self.requester = Requester(self.user, None, False, None, None)
  29. info = self.get_success(self.room_creator.create_room(self.requester, {}))
  30. self.room_id = info["room_id"]
  31. def run_background_update(self):
  32. """Re run the background update to clean up the extremities.
  33. """
  34. # Make sure we don't clash with in progress updates.
  35. self.assertTrue(self.store._all_done, "Background updates are still ongoing")
  36. schema_path = os.path.join(
  37. prepare_database.dir_path,
  38. "schema",
  39. "delta",
  40. "54",
  41. "delete_forward_extremities.sql",
  42. )
  43. def run_delta_file(txn):
  44. prepare_database.executescript(txn, schema_path)
  45. self.get_success(
  46. self.store.runInteraction("test_delete_forward_extremities", run_delta_file)
  47. )
  48. # Ugh, have to reset this flag
  49. self.store._all_done = False
  50. while not self.get_success(self.store.has_completed_background_updates()):
  51. self.get_success(self.store.do_next_background_update(100), by=0.1)
  52. def test_soft_failed_extremities_handled_correctly(self):
  53. """Test that extremities are correctly calculated in the presence of
  54. soft failed events.
  55. Tests a graph like:
  56. A <- SF1 <- SF2 <- B
  57. Where SF* are soft failed.
  58. """
  59. # Create the room graph
  60. event_id_1 = self.create_and_send_event(self.room_id, self.user)
  61. event_id_2 = self.create_and_send_event(
  62. self.room_id, self.user, True, [event_id_1]
  63. )
  64. event_id_3 = self.create_and_send_event(
  65. self.room_id, self.user, True, [event_id_2]
  66. )
  67. event_id_4 = self.create_and_send_event(
  68. self.room_id, self.user, False, [event_id_3]
  69. )
  70. # Check the latest events are as expected
  71. latest_event_ids = self.get_success(
  72. self.store.get_latest_event_ids_in_room(self.room_id)
  73. )
  74. self.assertEqual(latest_event_ids, [event_id_4])
  75. def test_basic_cleanup(self):
  76. """Test that extremities are correctly calculated in the presence of
  77. soft failed events.
  78. Tests a graph like:
  79. A <- SF1 <- B
  80. Where SF* are soft failed, and with extremities of A and B
  81. """
  82. # Create the room graph
  83. event_id_a = self.create_and_send_event(self.room_id, self.user)
  84. event_id_sf1 = self.create_and_send_event(
  85. self.room_id, self.user, True, [event_id_a]
  86. )
  87. event_id_b = self.create_and_send_event(
  88. self.room_id, self.user, False, [event_id_sf1]
  89. )
  90. # Add the new extremity and check the latest events are as expected
  91. self.add_extremity(self.room_id, event_id_a)
  92. latest_event_ids = self.get_success(
  93. self.store.get_latest_event_ids_in_room(self.room_id)
  94. )
  95. self.assertEqual(set(latest_event_ids), set((event_id_a, event_id_b)))
  96. # Run the background update and check it did the right thing
  97. self.run_background_update()
  98. latest_event_ids = self.get_success(
  99. self.store.get_latest_event_ids_in_room(self.room_id)
  100. )
  101. self.assertEqual(latest_event_ids, [event_id_b])
  102. def test_chain_of_fail_cleanup(self):
  103. """Test that extremities are correctly calculated in the presence of
  104. soft failed events.
  105. Tests a graph like:
  106. A <- SF1 <- SF2 <- B
  107. Where SF* are soft failed, and with extremities of A and B
  108. """
  109. # Create the room graph
  110. event_id_a = self.create_and_send_event(self.room_id, self.user)
  111. event_id_sf1 = self.create_and_send_event(
  112. self.room_id, self.user, True, [event_id_a]
  113. )
  114. event_id_sf2 = self.create_and_send_event(
  115. self.room_id, self.user, True, [event_id_sf1]
  116. )
  117. event_id_b = self.create_and_send_event(
  118. self.room_id, self.user, False, [event_id_sf2]
  119. )
  120. # Add the new extremity and check the latest events are as expected
  121. self.add_extremity(self.room_id, event_id_a)
  122. latest_event_ids = self.get_success(
  123. self.store.get_latest_event_ids_in_room(self.room_id)
  124. )
  125. self.assertEqual(set(latest_event_ids), set((event_id_a, event_id_b)))
  126. # Run the background update and check it did the right thing
  127. self.run_background_update()
  128. latest_event_ids = self.get_success(
  129. self.store.get_latest_event_ids_in_room(self.room_id)
  130. )
  131. self.assertEqual(latest_event_ids, [event_id_b])
  132. def test_forked_graph_cleanup(self):
  133. r"""Test that extremities are correctly calculated in the presence of
  134. soft failed events.
  135. Tests a graph like, where time flows down the page:
  136. A B
  137. / \ /
  138. / \ /
  139. SF1 SF2
  140. | |
  141. SF3 |
  142. / \ |
  143. | \ |
  144. C SF4
  145. Where SF* are soft failed, and with them A, B and C marked as
  146. extremities. This should resolve to B and C being marked as extremity.
  147. """
  148. # Create the room graph
  149. event_id_a = self.create_and_send_event(self.room_id, self.user)
  150. event_id_b = self.create_and_send_event(self.room_id, self.user)
  151. event_id_sf1 = self.create_and_send_event(
  152. self.room_id, self.user, True, [event_id_a]
  153. )
  154. event_id_sf2 = self.create_and_send_event(
  155. self.room_id, self.user, True, [event_id_a, event_id_b]
  156. )
  157. event_id_sf3 = self.create_and_send_event(
  158. self.room_id, self.user, True, [event_id_sf1]
  159. )
  160. self.create_and_send_event(
  161. self.room_id, self.user, True, [event_id_sf2, event_id_sf3]
  162. ) # SF4
  163. event_id_c = self.create_and_send_event(
  164. self.room_id, self.user, False, [event_id_sf3]
  165. )
  166. # Add the new extremity and check the latest events are as expected
  167. self.add_extremity(self.room_id, event_id_a)
  168. latest_event_ids = self.get_success(
  169. self.store.get_latest_event_ids_in_room(self.room_id)
  170. )
  171. self.assertEqual(
  172. set(latest_event_ids), set((event_id_a, event_id_b, event_id_c))
  173. )
  174. # Run the background update and check it did the right thing
  175. self.run_background_update()
  176. latest_event_ids = self.get_success(
  177. self.store.get_latest_event_ids_in_room(self.room_id)
  178. )
  179. self.assertEqual(set(latest_event_ids), set([event_id_b, event_id_c]))
  180. class CleanupExtremDummyEventsTestCase(HomeserverTestCase):
  181. def make_homeserver(self, reactor, clock):
  182. config = self.default_config()
  183. config["cleanup_extremities_with_dummy_events"] = True
  184. return self.setup_test_homeserver(config=config)
  185. def prepare(self, reactor, clock, homeserver):
  186. self.store = homeserver.get_datastore()
  187. self.room_creator = homeserver.get_room_creation_handler()
  188. # Create a test user and room
  189. self.user = UserID("alice", "test")
  190. self.requester = Requester(self.user, None, False, None, None)
  191. info = self.get_success(self.room_creator.create_room(self.requester, {}))
  192. self.room_id = info["room_id"]
  193. def test_send_dummy_event(self):
  194. # Create a bushy graph with 50 extremities.
  195. event_id_start = self.create_and_send_event(self.room_id, self.user)
  196. for _ in range(50):
  197. self.create_and_send_event(
  198. self.room_id, self.user, prev_event_ids=[event_id_start]
  199. )
  200. latest_event_ids = self.get_success(
  201. self.store.get_latest_event_ids_in_room(self.room_id)
  202. )
  203. self.assertEqual(len(latest_event_ids), 50)
  204. # Pump the reactor repeatedly so that the background updates have a
  205. # chance to run.
  206. self.pump(10 * 60)
  207. latest_event_ids = self.get_success(
  208. self.store.get_latest_event_ids_in_room(self.room_id)
  209. )
  210. self.assertTrue(len(latest_event_ids) < 10, len(latest_event_ids))