matrix-synapse 1.142.0rc3__cp314-abi3-musllinux_1_2_aarch64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of matrix-synapse might be problematic. Click here for more details.

Files changed (1057) hide show
  1. matrix_synapse-1.142.0rc3.dist-info/AUTHORS.rst +51 -0
  2. matrix_synapse-1.142.0rc3.dist-info/LICENSE-AGPL-3.0 +661 -0
  3. matrix_synapse-1.142.0rc3.dist-info/LICENSE-COMMERCIAL +6 -0
  4. matrix_synapse-1.142.0rc3.dist-info/METADATA +375 -0
  5. matrix_synapse-1.142.0rc3.dist-info/RECORD +1057 -0
  6. matrix_synapse-1.142.0rc3.dist-info/WHEEL +4 -0
  7. matrix_synapse-1.142.0rc3.dist-info/entry_points.txt +14 -0
  8. matrix_synapse.libs/libgcc_s-2d945d6c.so.1 +0 -0
  9. synapse/__init__.py +97 -0
  10. synapse/_scripts/__init__.py +0 -0
  11. synapse/_scripts/export_signing_key.py +109 -0
  12. synapse/_scripts/generate_config.py +83 -0
  13. synapse/_scripts/generate_log_config.py +56 -0
  14. synapse/_scripts/generate_signing_key.py +55 -0
  15. synapse/_scripts/generate_workers_map.py +318 -0
  16. synapse/_scripts/hash_password.py +95 -0
  17. synapse/_scripts/move_remote_media_to_new_store.py +128 -0
  18. synapse/_scripts/register_new_matrix_user.py +374 -0
  19. synapse/_scripts/review_recent_signups.py +212 -0
  20. synapse/_scripts/synapse_port_db.py +1603 -0
  21. synapse/_scripts/synctl.py +365 -0
  22. synapse/_scripts/update_synapse_database.py +130 -0
  23. synapse/api/__init__.py +20 -0
  24. synapse/api/auth/__init__.py +207 -0
  25. synapse/api/auth/base.py +406 -0
  26. synapse/api/auth/internal.py +299 -0
  27. synapse/api/auth/mas.py +457 -0
  28. synapse/api/auth/msc3861_delegated.py +617 -0
  29. synapse/api/auth_blocking.py +144 -0
  30. synapse/api/constants.py +362 -0
  31. synapse/api/errors.py +907 -0
  32. synapse/api/filtering.py +539 -0
  33. synapse/api/presence.py +104 -0
  34. synapse/api/ratelimiting.py +482 -0
  35. synapse/api/room_versions.py +535 -0
  36. synapse/api/urls.py +119 -0
  37. synapse/app/__init__.py +60 -0
  38. synapse/app/_base.py +866 -0
  39. synapse/app/admin_cmd.py +388 -0
  40. synapse/app/appservice.py +30 -0
  41. synapse/app/client_reader.py +30 -0
  42. synapse/app/complement_fork_starter.py +206 -0
  43. synapse/app/event_creator.py +29 -0
  44. synapse/app/federation_reader.py +30 -0
  45. synapse/app/federation_sender.py +30 -0
  46. synapse/app/frontend_proxy.py +30 -0
  47. synapse/app/generic_worker.py +475 -0
  48. synapse/app/homeserver.py +504 -0
  49. synapse/app/media_repository.py +30 -0
  50. synapse/app/phone_stats_home.py +296 -0
  51. synapse/app/pusher.py +30 -0
  52. synapse/app/synchrotron.py +30 -0
  53. synapse/app/user_dir.py +31 -0
  54. synapse/appservice/__init__.py +461 -0
  55. synapse/appservice/api.py +569 -0
  56. synapse/appservice/scheduler.py +567 -0
  57. synapse/config/__init__.py +27 -0
  58. synapse/config/__main__.py +62 -0
  59. synapse/config/_base.py +1108 -0
  60. synapse/config/_base.pyi +217 -0
  61. synapse/config/_util.py +99 -0
  62. synapse/config/account_validity.py +116 -0
  63. synapse/config/api.py +141 -0
  64. synapse/config/appservice.py +210 -0
  65. synapse/config/auth.py +80 -0
  66. synapse/config/auto_accept_invites.py +43 -0
  67. synapse/config/background_updates.py +44 -0
  68. synapse/config/cache.py +231 -0
  69. synapse/config/captcha.py +90 -0
  70. synapse/config/cas.py +116 -0
  71. synapse/config/consent.py +73 -0
  72. synapse/config/database.py +184 -0
  73. synapse/config/emailconfig.py +367 -0
  74. synapse/config/experimental.py +595 -0
  75. synapse/config/federation.py +114 -0
  76. synapse/config/homeserver.py +141 -0
  77. synapse/config/jwt.py +55 -0
  78. synapse/config/key.py +447 -0
  79. synapse/config/logger.py +390 -0
  80. synapse/config/mas.py +191 -0
  81. synapse/config/matrixrtc.py +66 -0
  82. synapse/config/metrics.py +84 -0
  83. synapse/config/modules.py +40 -0
  84. synapse/config/oembed.py +185 -0
  85. synapse/config/oidc.py +509 -0
  86. synapse/config/password_auth_providers.py +82 -0
  87. synapse/config/push.py +64 -0
  88. synapse/config/ratelimiting.py +254 -0
  89. synapse/config/redis.py +74 -0
  90. synapse/config/registration.py +296 -0
  91. synapse/config/repository.py +311 -0
  92. synapse/config/retention.py +162 -0
  93. synapse/config/room.py +88 -0
  94. synapse/config/room_directory.py +165 -0
  95. synapse/config/saml2.py +251 -0
  96. synapse/config/server.py +1170 -0
  97. synapse/config/server_notices.py +84 -0
  98. synapse/config/spam_checker.py +66 -0
  99. synapse/config/sso.py +121 -0
  100. synapse/config/stats.py +54 -0
  101. synapse/config/third_party_event_rules.py +40 -0
  102. synapse/config/tls.py +192 -0
  103. synapse/config/tracer.py +71 -0
  104. synapse/config/user_directory.py +47 -0
  105. synapse/config/user_types.py +44 -0
  106. synapse/config/voip.py +59 -0
  107. synapse/config/workers.py +642 -0
  108. synapse/crypto/__init__.py +20 -0
  109. synapse/crypto/context_factory.py +278 -0
  110. synapse/crypto/event_signing.py +194 -0
  111. synapse/crypto/keyring.py +931 -0
  112. synapse/event_auth.py +1266 -0
  113. synapse/events/__init__.py +668 -0
  114. synapse/events/auto_accept_invites.py +216 -0
  115. synapse/events/builder.py +387 -0
  116. synapse/events/presence_router.py +245 -0
  117. synapse/events/snapshot.py +559 -0
  118. synapse/events/utils.py +928 -0
  119. synapse/events/validator.py +305 -0
  120. synapse/federation/__init__.py +22 -0
  121. synapse/federation/federation_base.py +383 -0
  122. synapse/federation/federation_client.py +2134 -0
  123. synapse/federation/federation_server.py +1544 -0
  124. synapse/federation/persistence.py +71 -0
  125. synapse/federation/send_queue.py +532 -0
  126. synapse/federation/sender/__init__.py +1165 -0
  127. synapse/federation/sender/per_destination_queue.py +884 -0
  128. synapse/federation/sender/transaction_manager.py +210 -0
  129. synapse/federation/transport/__init__.py +28 -0
  130. synapse/federation/transport/client.py +1201 -0
  131. synapse/federation/transport/server/__init__.py +334 -0
  132. synapse/federation/transport/server/_base.py +429 -0
  133. synapse/federation/transport/server/federation.py +912 -0
  134. synapse/federation/units.py +133 -0
  135. synapse/handlers/__init__.py +20 -0
  136. synapse/handlers/account.py +162 -0
  137. synapse/handlers/account_data.py +362 -0
  138. synapse/handlers/account_validity.py +361 -0
  139. synapse/handlers/admin.py +618 -0
  140. synapse/handlers/appservice.py +991 -0
  141. synapse/handlers/auth.py +2494 -0
  142. synapse/handlers/cas.py +413 -0
  143. synapse/handlers/deactivate_account.py +363 -0
  144. synapse/handlers/delayed_events.py +635 -0
  145. synapse/handlers/device.py +1873 -0
  146. synapse/handlers/devicemessage.py +399 -0
  147. synapse/handlers/directory.py +554 -0
  148. synapse/handlers/e2e_keys.py +1834 -0
  149. synapse/handlers/e2e_room_keys.py +455 -0
  150. synapse/handlers/event_auth.py +390 -0
  151. synapse/handlers/events.py +201 -0
  152. synapse/handlers/federation.py +2043 -0
  153. synapse/handlers/federation_event.py +2420 -0
  154. synapse/handlers/identity.py +812 -0
  155. synapse/handlers/initial_sync.py +528 -0
  156. synapse/handlers/jwt.py +120 -0
  157. synapse/handlers/message.py +2347 -0
  158. synapse/handlers/oidc.py +1803 -0
  159. synapse/handlers/pagination.py +768 -0
  160. synapse/handlers/password_policy.py +102 -0
  161. synapse/handlers/presence.py +2638 -0
  162. synapse/handlers/profile.py +655 -0
  163. synapse/handlers/push_rules.py +164 -0
  164. synapse/handlers/read_marker.py +79 -0
  165. synapse/handlers/receipts.py +351 -0
  166. synapse/handlers/register.py +1060 -0
  167. synapse/handlers/relations.py +624 -0
  168. synapse/handlers/reports.py +98 -0
  169. synapse/handlers/room.py +2447 -0
  170. synapse/handlers/room_list.py +632 -0
  171. synapse/handlers/room_member.py +2365 -0
  172. synapse/handlers/room_member_worker.py +146 -0
  173. synapse/handlers/room_policy.py +186 -0
  174. synapse/handlers/room_summary.py +1057 -0
  175. synapse/handlers/saml.py +524 -0
  176. synapse/handlers/search.py +723 -0
  177. synapse/handlers/send_email.py +209 -0
  178. synapse/handlers/set_password.py +71 -0
  179. synapse/handlers/sliding_sync/__init__.py +1701 -0
  180. synapse/handlers/sliding_sync/extensions.py +970 -0
  181. synapse/handlers/sliding_sync/room_lists.py +2266 -0
  182. synapse/handlers/sliding_sync/store.py +128 -0
  183. synapse/handlers/sso.py +1292 -0
  184. synapse/handlers/state_deltas.py +82 -0
  185. synapse/handlers/stats.py +322 -0
  186. synapse/handlers/sync.py +3109 -0
  187. synapse/handlers/thread_subscriptions.py +190 -0
  188. synapse/handlers/typing.py +606 -0
  189. synapse/handlers/ui_auth/__init__.py +48 -0
  190. synapse/handlers/ui_auth/checkers.py +332 -0
  191. synapse/handlers/user_directory.py +783 -0
  192. synapse/handlers/worker_lock.py +365 -0
  193. synapse/http/__init__.py +106 -0
  194. synapse/http/additional_resource.py +62 -0
  195. synapse/http/client.py +1360 -0
  196. synapse/http/connectproxyclient.py +309 -0
  197. synapse/http/federation/__init__.py +19 -0
  198. synapse/http/federation/matrix_federation_agent.py +490 -0
  199. synapse/http/federation/srv_resolver.py +196 -0
  200. synapse/http/federation/well_known_resolver.py +367 -0
  201. synapse/http/matrixfederationclient.py +1875 -0
  202. synapse/http/proxy.py +290 -0
  203. synapse/http/proxyagent.py +497 -0
  204. synapse/http/replicationagent.py +203 -0
  205. synapse/http/request_metrics.py +309 -0
  206. synapse/http/server.py +1114 -0
  207. synapse/http/servlet.py +1019 -0
  208. synapse/http/site.py +825 -0
  209. synapse/http/types.py +27 -0
  210. synapse/logging/__init__.py +31 -0
  211. synapse/logging/_remote.py +261 -0
  212. synapse/logging/_terse_json.py +95 -0
  213. synapse/logging/context.py +1211 -0
  214. synapse/logging/formatter.py +63 -0
  215. synapse/logging/handlers.py +99 -0
  216. synapse/logging/loggers.py +25 -0
  217. synapse/logging/opentracing.py +1132 -0
  218. synapse/logging/scopecontextmanager.py +161 -0
  219. synapse/media/_base.py +827 -0
  220. synapse/media/filepath.py +417 -0
  221. synapse/media/media_repository.py +1580 -0
  222. synapse/media/media_storage.py +704 -0
  223. synapse/media/oembed.py +277 -0
  224. synapse/media/preview_html.py +559 -0
  225. synapse/media/storage_provider.py +195 -0
  226. synapse/media/thumbnailer.py +833 -0
  227. synapse/media/url_previewer.py +875 -0
  228. synapse/metrics/__init__.py +754 -0
  229. synapse/metrics/_gc.py +219 -0
  230. synapse/metrics/_reactor_metrics.py +171 -0
  231. synapse/metrics/_types.py +38 -0
  232. synapse/metrics/background_process_metrics.py +556 -0
  233. synapse/metrics/common_usage_metrics.py +94 -0
  234. synapse/metrics/jemalloc.py +248 -0
  235. synapse/module_api/__init__.py +2154 -0
  236. synapse/module_api/callbacks/__init__.py +50 -0
  237. synapse/module_api/callbacks/account_validity_callbacks.py +106 -0
  238. synapse/module_api/callbacks/media_repository_callbacks.py +160 -0
  239. synapse/module_api/callbacks/ratelimit_callbacks.py +79 -0
  240. synapse/module_api/callbacks/spamchecker_callbacks.py +1113 -0
  241. synapse/module_api/callbacks/third_party_event_rules_callbacks.py +599 -0
  242. synapse/module_api/errors.py +42 -0
  243. synapse/notifier.py +972 -0
  244. synapse/push/__init__.py +212 -0
  245. synapse/push/bulk_push_rule_evaluator.py +637 -0
  246. synapse/push/clientformat.py +126 -0
  247. synapse/push/emailpusher.py +333 -0
  248. synapse/push/httppusher.py +564 -0
  249. synapse/push/mailer.py +1012 -0
  250. synapse/push/presentable_names.py +216 -0
  251. synapse/push/push_tools.py +114 -0
  252. synapse/push/push_types.py +141 -0
  253. synapse/push/pusher.py +87 -0
  254. synapse/push/pusherpool.py +501 -0
  255. synapse/push/rulekinds.py +33 -0
  256. synapse/py.typed +0 -0
  257. synapse/replication/__init__.py +20 -0
  258. synapse/replication/http/__init__.py +68 -0
  259. synapse/replication/http/_base.py +468 -0
  260. synapse/replication/http/account_data.py +297 -0
  261. synapse/replication/http/deactivate_account.py +81 -0
  262. synapse/replication/http/delayed_events.py +62 -0
  263. synapse/replication/http/devices.py +254 -0
  264. synapse/replication/http/federation.py +334 -0
  265. synapse/replication/http/login.py +106 -0
  266. synapse/replication/http/membership.py +364 -0
  267. synapse/replication/http/presence.py +133 -0
  268. synapse/replication/http/push.py +156 -0
  269. synapse/replication/http/register.py +172 -0
  270. synapse/replication/http/send_events.py +182 -0
  271. synapse/replication/http/state.py +82 -0
  272. synapse/replication/http/streams.py +101 -0
  273. synapse/replication/tcp/__init__.py +56 -0
  274. synapse/replication/tcp/client.py +552 -0
  275. synapse/replication/tcp/commands.py +569 -0
  276. synapse/replication/tcp/context.py +41 -0
  277. synapse/replication/tcp/external_cache.py +156 -0
  278. synapse/replication/tcp/handler.py +942 -0
  279. synapse/replication/tcp/protocol.py +608 -0
  280. synapse/replication/tcp/redis.py +509 -0
  281. synapse/replication/tcp/resource.py +348 -0
  282. synapse/replication/tcp/streams/__init__.py +96 -0
  283. synapse/replication/tcp/streams/_base.py +766 -0
  284. synapse/replication/tcp/streams/events.py +287 -0
  285. synapse/replication/tcp/streams/federation.py +92 -0
  286. synapse/replication/tcp/streams/partial_state.py +80 -0
  287. synapse/res/providers.json +29 -0
  288. synapse/res/templates/_base.html +29 -0
  289. synapse/res/templates/account_previously_renewed.html +6 -0
  290. synapse/res/templates/account_renewed.html +6 -0
  291. synapse/res/templates/add_threepid.html +8 -0
  292. synapse/res/templates/add_threepid.txt +6 -0
  293. synapse/res/templates/add_threepid_failure.html +7 -0
  294. synapse/res/templates/add_threepid_success.html +6 -0
  295. synapse/res/templates/already_in_use.html +12 -0
  296. synapse/res/templates/already_in_use.txt +10 -0
  297. synapse/res/templates/auth_success.html +21 -0
  298. synapse/res/templates/invalid_token.html +6 -0
  299. synapse/res/templates/mail-Element.css +7 -0
  300. synapse/res/templates/mail-Vector.css +7 -0
  301. synapse/res/templates/mail-expiry.css +4 -0
  302. synapse/res/templates/mail.css +156 -0
  303. synapse/res/templates/notice_expiry.html +46 -0
  304. synapse/res/templates/notice_expiry.txt +7 -0
  305. synapse/res/templates/notif.html +51 -0
  306. synapse/res/templates/notif.txt +22 -0
  307. synapse/res/templates/notif_mail.html +59 -0
  308. synapse/res/templates/notif_mail.txt +10 -0
  309. synapse/res/templates/password_reset.html +10 -0
  310. synapse/res/templates/password_reset.txt +7 -0
  311. synapse/res/templates/password_reset_confirmation.html +15 -0
  312. synapse/res/templates/password_reset_failure.html +7 -0
  313. synapse/res/templates/password_reset_success.html +6 -0
  314. synapse/res/templates/recaptcha.html +42 -0
  315. synapse/res/templates/registration.html +12 -0
  316. synapse/res/templates/registration.txt +10 -0
  317. synapse/res/templates/registration_failure.html +6 -0
  318. synapse/res/templates/registration_success.html +6 -0
  319. synapse/res/templates/registration_token.html +18 -0
  320. synapse/res/templates/room.html +33 -0
  321. synapse/res/templates/room.txt +9 -0
  322. synapse/res/templates/sso.css +129 -0
  323. synapse/res/templates/sso_account_deactivated.html +25 -0
  324. synapse/res/templates/sso_auth_account_details.html +186 -0
  325. synapse/res/templates/sso_auth_account_details.js +116 -0
  326. synapse/res/templates/sso_auth_bad_user.html +26 -0
  327. synapse/res/templates/sso_auth_confirm.html +27 -0
  328. synapse/res/templates/sso_auth_success.html +26 -0
  329. synapse/res/templates/sso_error.html +71 -0
  330. synapse/res/templates/sso_footer.html +19 -0
  331. synapse/res/templates/sso_login_idp_picker.html +60 -0
  332. synapse/res/templates/sso_new_user_consent.html +30 -0
  333. synapse/res/templates/sso_partial_profile.html +19 -0
  334. synapse/res/templates/sso_redirect_confirm.html +39 -0
  335. synapse/res/templates/style.css +33 -0
  336. synapse/res/templates/terms.html +27 -0
  337. synapse/rest/__init__.py +197 -0
  338. synapse/rest/admin/__init__.py +390 -0
  339. synapse/rest/admin/_base.py +72 -0
  340. synapse/rest/admin/background_updates.py +171 -0
  341. synapse/rest/admin/devices.py +221 -0
  342. synapse/rest/admin/event_reports.py +173 -0
  343. synapse/rest/admin/events.py +69 -0
  344. synapse/rest/admin/experimental_features.py +137 -0
  345. synapse/rest/admin/federation.py +243 -0
  346. synapse/rest/admin/media.py +540 -0
  347. synapse/rest/admin/registration_tokens.py +358 -0
  348. synapse/rest/admin/rooms.py +1061 -0
  349. synapse/rest/admin/scheduled_tasks.py +70 -0
  350. synapse/rest/admin/server_notice_servlet.py +132 -0
  351. synapse/rest/admin/statistics.py +132 -0
  352. synapse/rest/admin/username_available.py +58 -0
  353. synapse/rest/admin/users.py +1608 -0
  354. synapse/rest/client/__init__.py +20 -0
  355. synapse/rest/client/_base.py +113 -0
  356. synapse/rest/client/account.py +930 -0
  357. synapse/rest/client/account_data.py +319 -0
  358. synapse/rest/client/account_validity.py +103 -0
  359. synapse/rest/client/appservice_ping.py +125 -0
  360. synapse/rest/client/auth.py +218 -0
  361. synapse/rest/client/auth_metadata.py +122 -0
  362. synapse/rest/client/capabilities.py +121 -0
  363. synapse/rest/client/delayed_events.py +111 -0
  364. synapse/rest/client/devices.py +587 -0
  365. synapse/rest/client/directory.py +211 -0
  366. synapse/rest/client/events.py +116 -0
  367. synapse/rest/client/filter.py +112 -0
  368. synapse/rest/client/initial_sync.py +65 -0
  369. synapse/rest/client/keys.py +678 -0
  370. synapse/rest/client/knock.py +104 -0
  371. synapse/rest/client/login.py +754 -0
  372. synapse/rest/client/login_token_request.py +127 -0
  373. synapse/rest/client/logout.py +93 -0
  374. synapse/rest/client/matrixrtc.py +52 -0
  375. synapse/rest/client/media.py +286 -0
  376. synapse/rest/client/mutual_rooms.py +93 -0
  377. synapse/rest/client/notifications.py +137 -0
  378. synapse/rest/client/openid.py +109 -0
  379. synapse/rest/client/password_policy.py +69 -0
  380. synapse/rest/client/presence.py +131 -0
  381. synapse/rest/client/profile.py +291 -0
  382. synapse/rest/client/push_rule.py +331 -0
  383. synapse/rest/client/pusher.py +181 -0
  384. synapse/rest/client/read_marker.py +104 -0
  385. synapse/rest/client/receipts.py +165 -0
  386. synapse/rest/client/register.py +1067 -0
  387. synapse/rest/client/relations.py +138 -0
  388. synapse/rest/client/rendezvous.py +76 -0
  389. synapse/rest/client/reporting.py +207 -0
  390. synapse/rest/client/room.py +1669 -0
  391. synapse/rest/client/room_keys.py +426 -0
  392. synapse/rest/client/room_upgrade_rest_servlet.py +112 -0
  393. synapse/rest/client/sendtodevice.py +85 -0
  394. synapse/rest/client/sync.py +1131 -0
  395. synapse/rest/client/tags.py +129 -0
  396. synapse/rest/client/thirdparty.py +130 -0
  397. synapse/rest/client/thread_subscriptions.py +247 -0
  398. synapse/rest/client/tokenrefresh.py +52 -0
  399. synapse/rest/client/transactions.py +149 -0
  400. synapse/rest/client/user_directory.py +90 -0
  401. synapse/rest/client/versions.py +191 -0
  402. synapse/rest/client/voip.py +88 -0
  403. synapse/rest/consent/__init__.py +0 -0
  404. synapse/rest/consent/consent_resource.py +210 -0
  405. synapse/rest/health.py +38 -0
  406. synapse/rest/key/__init__.py +20 -0
  407. synapse/rest/key/v2/__init__.py +40 -0
  408. synapse/rest/key/v2/local_key_resource.py +125 -0
  409. synapse/rest/key/v2/remote_key_resource.py +302 -0
  410. synapse/rest/media/__init__.py +0 -0
  411. synapse/rest/media/config_resource.py +53 -0
  412. synapse/rest/media/create_resource.py +90 -0
  413. synapse/rest/media/download_resource.py +110 -0
  414. synapse/rest/media/media_repository_resource.py +113 -0
  415. synapse/rest/media/preview_url_resource.py +77 -0
  416. synapse/rest/media/thumbnail_resource.py +142 -0
  417. synapse/rest/media/upload_resource.py +187 -0
  418. synapse/rest/media/v1/__init__.py +39 -0
  419. synapse/rest/media/v1/_base.py +23 -0
  420. synapse/rest/media/v1/media_storage.py +23 -0
  421. synapse/rest/media/v1/storage_provider.py +23 -0
  422. synapse/rest/synapse/__init__.py +20 -0
  423. synapse/rest/synapse/client/__init__.py +93 -0
  424. synapse/rest/synapse/client/federation_whitelist.py +66 -0
  425. synapse/rest/synapse/client/jwks.py +77 -0
  426. synapse/rest/synapse/client/new_user_consent.py +115 -0
  427. synapse/rest/synapse/client/oidc/__init__.py +45 -0
  428. synapse/rest/synapse/client/oidc/backchannel_logout_resource.py +42 -0
  429. synapse/rest/synapse/client/oidc/callback_resource.py +48 -0
  430. synapse/rest/synapse/client/password_reset.py +129 -0
  431. synapse/rest/synapse/client/pick_idp.py +107 -0
  432. synapse/rest/synapse/client/pick_username.py +153 -0
  433. synapse/rest/synapse/client/rendezvous.py +58 -0
  434. synapse/rest/synapse/client/saml2/__init__.py +42 -0
  435. synapse/rest/synapse/client/saml2/metadata_resource.py +46 -0
  436. synapse/rest/synapse/client/saml2/response_resource.py +52 -0
  437. synapse/rest/synapse/client/sso_register.py +56 -0
  438. synapse/rest/synapse/client/unsubscribe.py +88 -0
  439. synapse/rest/synapse/mas/__init__.py +71 -0
  440. synapse/rest/synapse/mas/_base.py +55 -0
  441. synapse/rest/synapse/mas/devices.py +239 -0
  442. synapse/rest/synapse/mas/users.py +469 -0
  443. synapse/rest/well_known.py +148 -0
  444. synapse/server.py +1258 -0
  445. synapse/server_notices/__init__.py +0 -0
  446. synapse/server_notices/consent_server_notices.py +136 -0
  447. synapse/server_notices/resource_limits_server_notices.py +215 -0
  448. synapse/server_notices/server_notices_manager.py +388 -0
  449. synapse/server_notices/server_notices_sender.py +67 -0
  450. synapse/server_notices/worker_server_notices_sender.py +46 -0
  451. synapse/spam_checker_api/__init__.py +31 -0
  452. synapse/state/__init__.py +1022 -0
  453. synapse/state/v1.py +370 -0
  454. synapse/state/v2.py +985 -0
  455. synapse/static/client/login/index.html +47 -0
  456. synapse/static/client/login/js/jquery-3.4.1.min.js +2 -0
  457. synapse/static/client/login/js/login.js +291 -0
  458. synapse/static/client/login/spinner.gif +0 -0
  459. synapse/static/client/login/style.css +79 -0
  460. synapse/static/index.html +63 -0
  461. synapse/storage/__init__.py +43 -0
  462. synapse/storage/_base.py +245 -0
  463. synapse/storage/admin_client_config.py +26 -0
  464. synapse/storage/background_updates.py +1189 -0
  465. synapse/storage/controllers/__init__.py +57 -0
  466. synapse/storage/controllers/persist_events.py +1239 -0
  467. synapse/storage/controllers/purge_events.py +456 -0
  468. synapse/storage/controllers/state.py +954 -0
  469. synapse/storage/controllers/stats.py +119 -0
  470. synapse/storage/database.py +2720 -0
  471. synapse/storage/databases/__init__.py +175 -0
  472. synapse/storage/databases/main/__init__.py +424 -0
  473. synapse/storage/databases/main/account_data.py +1060 -0
  474. synapse/storage/databases/main/appservice.py +473 -0
  475. synapse/storage/databases/main/cache.py +911 -0
  476. synapse/storage/databases/main/censor_events.py +225 -0
  477. synapse/storage/databases/main/client_ips.py +817 -0
  478. synapse/storage/databases/main/delayed_events.py +560 -0
  479. synapse/storage/databases/main/deviceinbox.py +1272 -0
  480. synapse/storage/databases/main/devices.py +2581 -0
  481. synapse/storage/databases/main/directory.py +212 -0
  482. synapse/storage/databases/main/e2e_room_keys.py +690 -0
  483. synapse/storage/databases/main/end_to_end_keys.py +1896 -0
  484. synapse/storage/databases/main/event_federation.py +2509 -0
  485. synapse/storage/databases/main/event_push_actions.py +1937 -0
  486. synapse/storage/databases/main/events.py +3746 -0
  487. synapse/storage/databases/main/events_bg_updates.py +2910 -0
  488. synapse/storage/databases/main/events_forward_extremities.py +126 -0
  489. synapse/storage/databases/main/events_worker.py +2784 -0
  490. synapse/storage/databases/main/experimental_features.py +130 -0
  491. synapse/storage/databases/main/filtering.py +231 -0
  492. synapse/storage/databases/main/keys.py +291 -0
  493. synapse/storage/databases/main/lock.py +553 -0
  494. synapse/storage/databases/main/media_repository.py +1070 -0
  495. synapse/storage/databases/main/metrics.py +460 -0
  496. synapse/storage/databases/main/monthly_active_users.py +443 -0
  497. synapse/storage/databases/main/openid.py +61 -0
  498. synapse/storage/databases/main/presence.py +511 -0
  499. synapse/storage/databases/main/profile.py +541 -0
  500. synapse/storage/databases/main/purge_events.py +511 -0
  501. synapse/storage/databases/main/push_rule.py +972 -0
  502. synapse/storage/databases/main/pusher.py +794 -0
  503. synapse/storage/databases/main/receipts.py +1342 -0
  504. synapse/storage/databases/main/registration.py +3076 -0
  505. synapse/storage/databases/main/rejections.py +38 -0
  506. synapse/storage/databases/main/relations.py +1118 -0
  507. synapse/storage/databases/main/room.py +2781 -0
  508. synapse/storage/databases/main/roommember.py +2112 -0
  509. synapse/storage/databases/main/search.py +941 -0
  510. synapse/storage/databases/main/session.py +151 -0
  511. synapse/storage/databases/main/signatures.py +94 -0
  512. synapse/storage/databases/main/sliding_sync.py +603 -0
  513. synapse/storage/databases/main/state.py +1006 -0
  514. synapse/storage/databases/main/state_deltas.py +329 -0
  515. synapse/storage/databases/main/stats.py +791 -0
  516. synapse/storage/databases/main/stream.py +2580 -0
  517. synapse/storage/databases/main/tags.py +360 -0
  518. synapse/storage/databases/main/task_scheduler.py +225 -0
  519. synapse/storage/databases/main/thread_subscriptions.py +591 -0
  520. synapse/storage/databases/main/transactions.py +681 -0
  521. synapse/storage/databases/main/ui_auth.py +420 -0
  522. synapse/storage/databases/main/user_directory.py +1331 -0
  523. synapse/storage/databases/main/user_erasure_store.py +117 -0
  524. synapse/storage/databases/state/__init__.py +22 -0
  525. synapse/storage/databases/state/bg_updates.py +499 -0
  526. synapse/storage/databases/state/deletion.py +558 -0
  527. synapse/storage/databases/state/store.py +949 -0
  528. synapse/storage/engines/__init__.py +70 -0
  529. synapse/storage/engines/_base.py +154 -0
  530. synapse/storage/engines/postgres.py +261 -0
  531. synapse/storage/engines/sqlite.py +199 -0
  532. synapse/storage/invite_rule.py +112 -0
  533. synapse/storage/keys.py +40 -0
  534. synapse/storage/prepare_database.py +731 -0
  535. synapse/storage/push_rule.py +28 -0
  536. synapse/storage/roommember.py +89 -0
  537. synapse/storage/schema/README.md +4 -0
  538. synapse/storage/schema/__init__.py +182 -0
  539. synapse/storage/schema/common/delta/25/00background_updates.sql +40 -0
  540. synapse/storage/schema/common/delta/35/00background_updates_add_col.sql +36 -0
  541. synapse/storage/schema/common/delta/58/00background_update_ordering.sql +38 -0
  542. synapse/storage/schema/common/full_schemas/72/full.sql.postgres +8 -0
  543. synapse/storage/schema/common/full_schemas/72/full.sql.sqlite +6 -0
  544. synapse/storage/schema/common/schema_version.sql +60 -0
  545. synapse/storage/schema/main/delta/12/v12.sql +82 -0
  546. synapse/storage/schema/main/delta/13/v13.sql +38 -0
  547. synapse/storage/schema/main/delta/14/v14.sql +42 -0
  548. synapse/storage/schema/main/delta/15/appservice_txns.sql +50 -0
  549. synapse/storage/schema/main/delta/15/presence_indices.sql +2 -0
  550. synapse/storage/schema/main/delta/15/v15.sql +24 -0
  551. synapse/storage/schema/main/delta/16/events_order_index.sql +4 -0
  552. synapse/storage/schema/main/delta/16/remote_media_cache_index.sql +2 -0
  553. synapse/storage/schema/main/delta/16/remove_duplicates.sql +9 -0
  554. synapse/storage/schema/main/delta/16/room_alias_index.sql +3 -0
  555. synapse/storage/schema/main/delta/16/unique_constraints.sql +72 -0
  556. synapse/storage/schema/main/delta/16/users.sql +56 -0
  557. synapse/storage/schema/main/delta/17/drop_indexes.sql +37 -0
  558. synapse/storage/schema/main/delta/17/server_keys.sql +43 -0
  559. synapse/storage/schema/main/delta/17/user_threepids.sql +9 -0
  560. synapse/storage/schema/main/delta/18/server_keys_bigger_ints.sql +51 -0
  561. synapse/storage/schema/main/delta/19/event_index.sql +38 -0
  562. synapse/storage/schema/main/delta/20/dummy.sql +1 -0
  563. synapse/storage/schema/main/delta/20/pushers.py +93 -0
  564. synapse/storage/schema/main/delta/21/end_to_end_keys.sql +53 -0
  565. synapse/storage/schema/main/delta/21/receipts.sql +57 -0
  566. synapse/storage/schema/main/delta/22/receipts_index.sql +41 -0
  567. synapse/storage/schema/main/delta/22/user_threepids_unique.sql +19 -0
  568. synapse/storage/schema/main/delta/24/stats_reporting.sql +37 -0
  569. synapse/storage/schema/main/delta/25/fts.py +81 -0
  570. synapse/storage/schema/main/delta/25/guest_access.sql +44 -0
  571. synapse/storage/schema/main/delta/25/history_visibility.sql +44 -0
  572. synapse/storage/schema/main/delta/25/tags.sql +57 -0
  573. synapse/storage/schema/main/delta/26/account_data.sql +36 -0
  574. synapse/storage/schema/main/delta/27/account_data.sql +55 -0
  575. synapse/storage/schema/main/delta/27/forgotten_memberships.sql +45 -0
  576. synapse/storage/schema/main/delta/27/ts.py +61 -0
  577. synapse/storage/schema/main/delta/28/event_push_actions.sql +46 -0
  578. synapse/storage/schema/main/delta/28/events_room_stream.sql +39 -0
  579. synapse/storage/schema/main/delta/28/public_roms_index.sql +39 -0
  580. synapse/storage/schema/main/delta/28/receipts_user_id_index.sql +41 -0
  581. synapse/storage/schema/main/delta/28/upgrade_times.sql +40 -0
  582. synapse/storage/schema/main/delta/28/users_is_guest.sql +41 -0
  583. synapse/storage/schema/main/delta/29/push_actions.sql +54 -0
  584. synapse/storage/schema/main/delta/30/alias_creator.sql +35 -0
  585. synapse/storage/schema/main/delta/30/as_users.py +82 -0
  586. synapse/storage/schema/main/delta/30/deleted_pushers.sql +44 -0
  587. synapse/storage/schema/main/delta/30/presence_stream.sql +49 -0
  588. synapse/storage/schema/main/delta/30/public_rooms.sql +42 -0
  589. synapse/storage/schema/main/delta/30/push_rule_stream.sql +57 -0
  590. synapse/storage/schema/main/delta/30/threepid_guest_access_tokens.sql +43 -0
  591. synapse/storage/schema/main/delta/31/invites.sql +61 -0
  592. synapse/storage/schema/main/delta/31/local_media_repository_url_cache.sql +46 -0
  593. synapse/storage/schema/main/delta/31/pushers_0.py +92 -0
  594. synapse/storage/schema/main/delta/31/pushers_index.sql +41 -0
  595. synapse/storage/schema/main/delta/31/search_update.py +65 -0
  596. synapse/storage/schema/main/delta/32/events.sql +35 -0
  597. synapse/storage/schema/main/delta/32/openid.sql +9 -0
  598. synapse/storage/schema/main/delta/32/pusher_throttle.sql +42 -0
  599. synapse/storage/schema/main/delta/32/remove_indices.sql +52 -0
  600. synapse/storage/schema/main/delta/32/reports.sql +44 -0
  601. synapse/storage/schema/main/delta/33/access_tokens_device_index.sql +36 -0
  602. synapse/storage/schema/main/delta/33/devices.sql +40 -0
  603. synapse/storage/schema/main/delta/33/devices_for_e2e_keys.sql +38 -0
  604. synapse/storage/schema/main/delta/33/devices_for_e2e_keys_clear_unknown_device.sql +39 -0
  605. synapse/storage/schema/main/delta/33/event_fields.py +61 -0
  606. synapse/storage/schema/main/delta/33/remote_media_ts.py +43 -0
  607. synapse/storage/schema/main/delta/33/user_ips_index.sql +36 -0
  608. synapse/storage/schema/main/delta/34/appservice_stream.sql +42 -0
  609. synapse/storage/schema/main/delta/34/cache_stream.py +50 -0
  610. synapse/storage/schema/main/delta/34/device_inbox.sql +43 -0
  611. synapse/storage/schema/main/delta/34/push_display_name_rename.sql +39 -0
  612. synapse/storage/schema/main/delta/34/received_txn_purge.py +36 -0
  613. synapse/storage/schema/main/delta/35/contains_url.sql +36 -0
  614. synapse/storage/schema/main/delta/35/device_outbox.sql +58 -0
  615. synapse/storage/schema/main/delta/35/device_stream_id.sql +40 -0
  616. synapse/storage/schema/main/delta/35/event_push_actions_index.sql +36 -0
  617. synapse/storage/schema/main/delta/35/public_room_list_change_stream.sql +52 -0
  618. synapse/storage/schema/main/delta/35/stream_order_to_extrem.sql +56 -0
  619. synapse/storage/schema/main/delta/36/readd_public_rooms.sql +45 -0
  620. synapse/storage/schema/main/delta/37/remove_auth_idx.py +89 -0
  621. synapse/storage/schema/main/delta/37/user_threepids.sql +71 -0
  622. synapse/storage/schema/main/delta/38/postgres_fts_gist.sql +38 -0
  623. synapse/storage/schema/main/delta/39/appservice_room_list.sql +48 -0
  624. synapse/storage/schema/main/delta/39/device_federation_stream_idx.sql +35 -0
  625. synapse/storage/schema/main/delta/39/event_push_index.sql +36 -0
  626. synapse/storage/schema/main/delta/39/federation_out_position.sql +41 -0
  627. synapse/storage/schema/main/delta/39/membership_profile.sql +39 -0
  628. synapse/storage/schema/main/delta/40/current_state_idx.sql +36 -0
  629. synapse/storage/schema/main/delta/40/device_inbox.sql +40 -0
  630. synapse/storage/schema/main/delta/40/device_list_streams.sql +79 -0
  631. synapse/storage/schema/main/delta/40/event_push_summary.sql +57 -0
  632. synapse/storage/schema/main/delta/40/pushers.sql +58 -0
  633. synapse/storage/schema/main/delta/41/device_list_stream_idx.sql +36 -0
  634. synapse/storage/schema/main/delta/41/device_outbound_index.sql +35 -0
  635. synapse/storage/schema/main/delta/41/event_search_event_id_idx.sql +36 -0
  636. synapse/storage/schema/main/delta/41/ratelimit.sql +41 -0
  637. synapse/storage/schema/main/delta/42/current_state_delta.sql +48 -0
  638. synapse/storage/schema/main/delta/42/device_list_last_id.sql +52 -0
  639. synapse/storage/schema/main/delta/42/event_auth_state_only.sql +36 -0
  640. synapse/storage/schema/main/delta/42/user_dir.py +88 -0
  641. synapse/storage/schema/main/delta/43/blocked_rooms.sql +40 -0
  642. synapse/storage/schema/main/delta/43/quarantine_media.sql +36 -0
  643. synapse/storage/schema/main/delta/43/url_cache.sql +35 -0
  644. synapse/storage/schema/main/delta/43/user_share.sql +52 -0
  645. synapse/storage/schema/main/delta/44/expire_url_cache.sql +60 -0
  646. synapse/storage/schema/main/delta/45/group_server.sql +186 -0
  647. synapse/storage/schema/main/delta/45/profile_cache.sql +47 -0
  648. synapse/storage/schema/main/delta/46/drop_refresh_tokens.sql +36 -0
  649. synapse/storage/schema/main/delta/46/drop_unique_deleted_pushers.sql +54 -0
  650. synapse/storage/schema/main/delta/46/group_server.sql +51 -0
  651. synapse/storage/schema/main/delta/46/local_media_repository_url_idx.sql +43 -0
  652. synapse/storage/schema/main/delta/46/user_dir_null_room_ids.sql +54 -0
  653. synapse/storage/schema/main/delta/46/user_dir_typos.sql +43 -0
  654. synapse/storage/schema/main/delta/47/last_access_media.sql +35 -0
  655. synapse/storage/schema/main/delta/47/postgres_fts_gin.sql +36 -0
  656. synapse/storage/schema/main/delta/47/push_actions_staging.sql +47 -0
  657. synapse/storage/schema/main/delta/48/add_user_consent.sql +37 -0
  658. synapse/storage/schema/main/delta/48/add_user_ips_last_seen_index.sql +36 -0
  659. synapse/storage/schema/main/delta/48/deactivated_users.sql +44 -0
  660. synapse/storage/schema/main/delta/48/group_unique_indexes.py +67 -0
  661. synapse/storage/schema/main/delta/48/groups_joinable.sql +41 -0
  662. synapse/storage/schema/main/delta/49/add_user_consent_server_notice_sent.sql +39 -0
  663. synapse/storage/schema/main/delta/49/add_user_daily_visits.sql +40 -0
  664. synapse/storage/schema/main/delta/49/add_user_ips_last_seen_only_index.sql +36 -0
  665. synapse/storage/schema/main/delta/50/add_creation_ts_users_index.sql +38 -0
  666. synapse/storage/schema/main/delta/50/erasure_store.sql +40 -0
  667. synapse/storage/schema/main/delta/50/make_event_content_nullable.py +102 -0
  668. synapse/storage/schema/main/delta/51/e2e_room_keys.sql +58 -0
  669. synapse/storage/schema/main/delta/51/monthly_active_users.sql +46 -0
  670. synapse/storage/schema/main/delta/52/add_event_to_state_group_index.sql +38 -0
  671. synapse/storage/schema/main/delta/52/device_list_streams_unique_idx.sql +55 -0
  672. synapse/storage/schema/main/delta/52/e2e_room_keys.sql +72 -0
  673. synapse/storage/schema/main/delta/53/add_user_type_to_users.sql +38 -0
  674. synapse/storage/schema/main/delta/53/drop_sent_transactions.sql +35 -0
  675. synapse/storage/schema/main/delta/53/event_format_version.sql +35 -0
  676. synapse/storage/schema/main/delta/53/user_dir_populate.sql +49 -0
  677. synapse/storage/schema/main/delta/53/user_ips_index.sql +49 -0
  678. synapse/storage/schema/main/delta/53/user_share.sql +63 -0
  679. synapse/storage/schema/main/delta/53/user_threepid_id.sql +48 -0
  680. synapse/storage/schema/main/delta/53/users_in_public_rooms.sql +47 -0
  681. synapse/storage/schema/main/delta/54/account_validity_with_renewal.sql +49 -0
  682. synapse/storage/schema/main/delta/54/add_validity_to_server_keys.sql +42 -0
  683. synapse/storage/schema/main/delta/54/delete_forward_extremities.sql +42 -0
  684. synapse/storage/schema/main/delta/54/drop_legacy_tables.sql +49 -0
  685. synapse/storage/schema/main/delta/54/drop_presence_list.sql +35 -0
  686. synapse/storage/schema/main/delta/54/relations.sql +46 -0
  687. synapse/storage/schema/main/delta/54/stats.sql +99 -0
  688. synapse/storage/schema/main/delta/54/stats2.sql +47 -0
  689. synapse/storage/schema/main/delta/55/access_token_expiry.sql +37 -0
  690. synapse/storage/schema/main/delta/55/track_threepid_validations.sql +50 -0
  691. synapse/storage/schema/main/delta/55/users_alter_deactivated.sql +38 -0
  692. synapse/storage/schema/main/delta/56/add_spans_to_device_lists.sql +39 -0
  693. synapse/storage/schema/main/delta/56/current_state_events_membership.sql +41 -0
  694. synapse/storage/schema/main/delta/56/current_state_events_membership_mk2.sql +43 -0
  695. synapse/storage/schema/main/delta/56/delete_keys_from_deleted_backups.sql +44 -0
  696. synapse/storage/schema/main/delta/56/destinations_failure_ts.sql +44 -0
  697. synapse/storage/schema/main/delta/56/destinations_retry_interval_type.sql.postgres +18 -0
  698. synapse/storage/schema/main/delta/56/device_stream_id_insert.sql +39 -0
  699. synapse/storage/schema/main/delta/56/devices_last_seen.sql +43 -0
  700. synapse/storage/schema/main/delta/56/drop_unused_event_tables.sql +39 -0
  701. synapse/storage/schema/main/delta/56/event_expiry.sql +40 -0
  702. synapse/storage/schema/main/delta/56/event_labels.sql +49 -0
  703. synapse/storage/schema/main/delta/56/event_labels_background_update.sql +36 -0
  704. synapse/storage/schema/main/delta/56/fix_room_keys_index.sql +37 -0
  705. synapse/storage/schema/main/delta/56/hidden_devices.sql +37 -0
  706. synapse/storage/schema/main/delta/56/hidden_devices_fix.sql.sqlite +42 -0
  707. synapse/storage/schema/main/delta/56/nuke_empty_communities_from_db.sql +48 -0
  708. synapse/storage/schema/main/delta/56/public_room_list_idx.sql +35 -0
  709. synapse/storage/schema/main/delta/56/redaction_censor.sql +35 -0
  710. synapse/storage/schema/main/delta/56/redaction_censor2.sql +41 -0
  711. synapse/storage/schema/main/delta/56/redaction_censor3_fix_update.sql.postgres +25 -0
  712. synapse/storage/schema/main/delta/56/redaction_censor4.sql +35 -0
  713. synapse/storage/schema/main/delta/56/remove_tombstoned_rooms_from_directory.sql +38 -0
  714. synapse/storage/schema/main/delta/56/room_key_etag.sql +36 -0
  715. synapse/storage/schema/main/delta/56/room_membership_idx.sql +37 -0
  716. synapse/storage/schema/main/delta/56/room_retention.sql +52 -0
  717. synapse/storage/schema/main/delta/56/signing_keys.sql +75 -0
  718. synapse/storage/schema/main/delta/56/signing_keys_nonunique_signatures.sql +41 -0
  719. synapse/storage/schema/main/delta/56/stats_separated.sql +175 -0
  720. synapse/storage/schema/main/delta/56/unique_user_filter_index.py +46 -0
  721. synapse/storage/schema/main/delta/56/user_external_ids.sql +43 -0
  722. synapse/storage/schema/main/delta/56/users_in_public_rooms_idx.sql +36 -0
  723. synapse/storage/schema/main/delta/57/delete_old_current_state_events.sql +41 -0
  724. synapse/storage/schema/main/delta/57/device_list_remote_cache_stale.sql +44 -0
  725. synapse/storage/schema/main/delta/57/local_current_membership.py +111 -0
  726. synapse/storage/schema/main/delta/57/remove_sent_outbound_pokes.sql +40 -0
  727. synapse/storage/schema/main/delta/57/rooms_version_column.sql +43 -0
  728. synapse/storage/schema/main/delta/57/rooms_version_column_2.sql.postgres +35 -0
  729. synapse/storage/schema/main/delta/57/rooms_version_column_2.sql.sqlite +22 -0
  730. synapse/storage/schema/main/delta/57/rooms_version_column_3.sql.postgres +39 -0
  731. synapse/storage/schema/main/delta/57/rooms_version_column_3.sql.sqlite +23 -0
  732. synapse/storage/schema/main/delta/58/02remove_dup_outbound_pokes.sql +41 -0
  733. synapse/storage/schema/main/delta/58/03persist_ui_auth.sql +55 -0
  734. synapse/storage/schema/main/delta/58/05cache_instance.sql.postgres +30 -0
  735. synapse/storage/schema/main/delta/58/06dlols_unique_idx.py +83 -0
  736. synapse/storage/schema/main/delta/58/07add_method_to_thumbnail_constraint.sql.postgres +33 -0
  737. synapse/storage/schema/main/delta/58/07add_method_to_thumbnail_constraint.sql.sqlite +44 -0
  738. synapse/storage/schema/main/delta/58/07persist_ui_auth_ips.sql +44 -0
  739. synapse/storage/schema/main/delta/58/08_media_safe_from_quarantine.sql.postgres +18 -0
  740. synapse/storage/schema/main/delta/58/08_media_safe_from_quarantine.sql.sqlite +18 -0
  741. synapse/storage/schema/main/delta/58/09shadow_ban.sql +37 -0
  742. synapse/storage/schema/main/delta/58/10_pushrules_enabled_delete_obsolete.sql +47 -0
  743. synapse/storage/schema/main/delta/58/10drop_local_rejections_stream.sql +41 -0
  744. synapse/storage/schema/main/delta/58/10federation_pos_instance_name.sql +41 -0
  745. synapse/storage/schema/main/delta/58/11dehydration.sql +39 -0
  746. synapse/storage/schema/main/delta/58/11fallback.sql +43 -0
  747. synapse/storage/schema/main/delta/58/11user_id_seq.py +38 -0
  748. synapse/storage/schema/main/delta/58/12room_stats.sql +51 -0
  749. synapse/storage/schema/main/delta/58/13remove_presence_allow_inbound.sql +36 -0
  750. synapse/storage/schema/main/delta/58/14events_instance_name.sql +35 -0
  751. synapse/storage/schema/main/delta/58/14events_instance_name.sql.postgres +28 -0
  752. synapse/storage/schema/main/delta/58/15_catchup_destination_rooms.sql +61 -0
  753. synapse/storage/schema/main/delta/58/15unread_count.sql +45 -0
  754. synapse/storage/schema/main/delta/58/16populate_stats_process_rooms_fix.sql +41 -0
  755. synapse/storage/schema/main/delta/58/17_catchup_last_successful.sql +40 -0
  756. synapse/storage/schema/main/delta/58/18stream_positions.sql +41 -0
  757. synapse/storage/schema/main/delta/58/19instance_map.sql.postgres +25 -0
  758. synapse/storage/schema/main/delta/58/19txn_id.sql +59 -0
  759. synapse/storage/schema/main/delta/58/20instance_name_event_tables.sql +36 -0
  760. synapse/storage/schema/main/delta/58/20user_daily_visits.sql +37 -0
  761. synapse/storage/schema/main/delta/58/21as_device_stream.sql +36 -0
  762. synapse/storage/schema/main/delta/58/21drop_device_max_stream_id.sql +1 -0
  763. synapse/storage/schema/main/delta/58/22puppet_token.sql +36 -0
  764. synapse/storage/schema/main/delta/58/22users_have_local_media.sql +2 -0
  765. synapse/storage/schema/main/delta/58/23e2e_cross_signing_keys_idx.sql +36 -0
  766. synapse/storage/schema/main/delta/58/24drop_event_json_index.sql +38 -0
  767. synapse/storage/schema/main/delta/58/25user_external_ids_user_id_idx.sql +36 -0
  768. synapse/storage/schema/main/delta/58/26access_token_last_validated.sql +37 -0
  769. synapse/storage/schema/main/delta/58/27local_invites.sql +37 -0
  770. synapse/storage/schema/main/delta/58/28drop_last_used_column.sql.postgres +16 -0
  771. synapse/storage/schema/main/delta/58/28drop_last_used_column.sql.sqlite +62 -0
  772. synapse/storage/schema/main/delta/59/01ignored_user.py +85 -0
  773. synapse/storage/schema/main/delta/59/02shard_send_to_device.sql +37 -0
  774. synapse/storage/schema/main/delta/59/03shard_send_to_device_sequence.sql.postgres +25 -0
  775. synapse/storage/schema/main/delta/59/04_event_auth_chains.sql +71 -0
  776. synapse/storage/schema/main/delta/59/04_event_auth_chains.sql.postgres +16 -0
  777. synapse/storage/schema/main/delta/59/04drop_account_data.sql +36 -0
  778. synapse/storage/schema/main/delta/59/05cache_invalidation.sql +36 -0
  779. synapse/storage/schema/main/delta/59/06chain_cover_index.sql +36 -0
  780. synapse/storage/schema/main/delta/59/06shard_account_data.sql +39 -0
  781. synapse/storage/schema/main/delta/59/06shard_account_data.sql.postgres +32 -0
  782. synapse/storage/schema/main/delta/59/07shard_account_data_fix.sql +37 -0
  783. synapse/storage/schema/main/delta/59/08delete_pushers_for_deactivated_accounts.sql +39 -0
  784. synapse/storage/schema/main/delta/59/08delete_stale_pushers.sql +39 -0
  785. synapse/storage/schema/main/delta/59/09rejected_events_metadata.sql +45 -0
  786. synapse/storage/schema/main/delta/59/10delete_purged_chain_cover.sql +36 -0
  787. synapse/storage/schema/main/delta/59/11add_knock_members_to_stats.sql +39 -0
  788. synapse/storage/schema/main/delta/59/11drop_thumbnail_constraint.sql.postgres +22 -0
  789. synapse/storage/schema/main/delta/59/12account_validity_token_used_ts_ms.sql +37 -0
  790. synapse/storage/schema/main/delta/59/12presence_stream_instance.sql +37 -0
  791. synapse/storage/schema/main/delta/59/12presence_stream_instance_seq.sql.postgres +20 -0
  792. synapse/storage/schema/main/delta/59/13users_to_send_full_presence_to.sql +53 -0
  793. synapse/storage/schema/main/delta/59/14refresh_tokens.sql +53 -0
  794. synapse/storage/schema/main/delta/59/15locks.sql +56 -0
  795. synapse/storage/schema/main/delta/59/16federation_inbound_staging.sql +51 -0
  796. synapse/storage/schema/main/delta/60/01recreate_stream_ordering.sql.postgres +45 -0
  797. synapse/storage/schema/main/delta/60/02change_stream_ordering_columns.sql.postgres +30 -0
  798. synapse/storage/schema/main/delta/61/01change_appservices_txns.sql.postgres +23 -0
  799. synapse/storage/schema/main/delta/61/01insertion_event_lookups.sql +68 -0
  800. synapse/storage/schema/main/delta/61/02drop_redundant_room_depth_index.sql +37 -0
  801. synapse/storage/schema/main/delta/61/03recreate_min_depth.py +74 -0
  802. synapse/storage/schema/main/delta/62/01insertion_event_extremities.sql +43 -0
  803. synapse/storage/schema/main/delta/63/01create_registration_tokens.sql +42 -0
  804. synapse/storage/schema/main/delta/63/02delete_unlinked_email_pushers.sql +39 -0
  805. synapse/storage/schema/main/delta/63/02populate-rooms-creator.sql +36 -0
  806. synapse/storage/schema/main/delta/63/03session_store.sql +42 -0
  807. synapse/storage/schema/main/delta/63/04add_presence_stream_not_offline_index.sql +37 -0
  808. synapse/storage/schema/main/delta/64/01msc2716_chunk_to_batch_rename.sql.postgres +23 -0
  809. synapse/storage/schema/main/delta/64/01msc2716_chunk_to_batch_rename.sql.sqlite +37 -0
  810. synapse/storage/schema/main/delta/65/01msc2716_insertion_event_edges.sql +38 -0
  811. synapse/storage/schema/main/delta/65/03remove_hidden_devices_from_device_inbox.sql +41 -0
  812. synapse/storage/schema/main/delta/65/04_local_group_updates.sql +37 -0
  813. synapse/storage/schema/main/delta/65/05_remove_room_stats_historical_and_user_stats_historical.sql +38 -0
  814. synapse/storage/schema/main/delta/65/06remove_deleted_devices_from_device_inbox.sql +53 -0
  815. synapse/storage/schema/main/delta/65/07_arbitrary_relations.sql +37 -0
  816. synapse/storage/schema/main/delta/65/08_device_inbox_background_updates.sql +37 -0
  817. synapse/storage/schema/main/delta/65/10_expirable_refresh_tokens.sql +47 -0
  818. synapse/storage/schema/main/delta/65/11_devices_auth_provider_session.sql +46 -0
  819. synapse/storage/schema/main/delta/67/01drop_public_room_list_stream.sql +37 -0
  820. synapse/storage/schema/main/delta/68/01event_columns.sql +45 -0
  821. synapse/storage/schema/main/delta/68/02_msc2409_add_device_id_appservice_stream_type.sql +40 -0
  822. synapse/storage/schema/main/delta/68/03_delete_account_data_for_deactivated_accounts.sql +39 -0
  823. synapse/storage/schema/main/delta/68/04_refresh_tokens_index_next_token_id.sql +47 -0
  824. synapse/storage/schema/main/delta/68/04partial_state_rooms.sql +60 -0
  825. synapse/storage/schema/main/delta/68/05_delete_non_strings_from_event_search.sql.sqlite +22 -0
  826. synapse/storage/schema/main/delta/68/05partial_state_rooms_triggers.py +80 -0
  827. synapse/storage/schema/main/delta/68/06_msc3202_add_device_list_appservice_stream_type.sql +42 -0
  828. synapse/storage/schema/main/delta/69/01as_txn_seq.py +54 -0
  829. synapse/storage/schema/main/delta/69/01device_list_oubound_by_room.sql +57 -0
  830. synapse/storage/schema/main/delta/69/02cache_invalidation_index.sql +37 -0
  831. synapse/storage/schema/main/delta/70/01clean_table_purged_rooms.sql +39 -0
  832. synapse/storage/schema/main/delta/71/01rebuild_event_edges.sql.postgres +43 -0
  833. synapse/storage/schema/main/delta/71/01rebuild_event_edges.sql.sqlite +47 -0
  834. synapse/storage/schema/main/delta/71/01remove_noop_background_updates.sql +80 -0
  835. synapse/storage/schema/main/delta/71/02event_push_summary_unique.sql +37 -0
  836. synapse/storage/schema/main/delta/72/01add_room_type_to_state_stats.sql +38 -0
  837. synapse/storage/schema/main/delta/72/01event_push_summary_receipt.sql +54 -0
  838. synapse/storage/schema/main/delta/72/02event_push_actions_index.sql +38 -0
  839. synapse/storage/schema/main/delta/72/03bg_populate_events_columns.py +57 -0
  840. synapse/storage/schema/main/delta/72/03drop_event_reference_hashes.sql +36 -0
  841. synapse/storage/schema/main/delta/72/03remove_groups.sql +50 -0
  842. synapse/storage/schema/main/delta/72/04drop_column_application_services_state_last_txn.sql.postgres +17 -0
  843. synapse/storage/schema/main/delta/72/04drop_column_application_services_state_last_txn.sql.sqlite +40 -0
  844. synapse/storage/schema/main/delta/72/05receipts_event_stream_ordering.sql +38 -0
  845. synapse/storage/schema/main/delta/72/05remove_unstable_private_read_receipts.sql +38 -0
  846. synapse/storage/schema/main/delta/72/06add_consent_ts_to_users.sql +35 -0
  847. synapse/storage/schema/main/delta/72/06thread_notifications.sql +49 -0
  848. synapse/storage/schema/main/delta/72/07force_update_current_state_events_membership.py +67 -0
  849. synapse/storage/schema/main/delta/72/07thread_receipts.sql.postgres +30 -0
  850. synapse/storage/schema/main/delta/72/07thread_receipts.sql.sqlite +70 -0
  851. synapse/storage/schema/main/delta/72/08begin_cache_invalidation_seq_at_2.sql.postgres +23 -0
  852. synapse/storage/schema/main/delta/72/08thread_receipts.sql +39 -0
  853. synapse/storage/schema/main/delta/72/09partial_indices.sql.sqlite +56 -0
  854. synapse/storage/schema/main/delta/73/01event_failed_pull_attempts.sql +48 -0
  855. synapse/storage/schema/main/delta/73/02add_pusher_enabled.sql +35 -0
  856. synapse/storage/schema/main/delta/73/02room_id_indexes_for_purging.sql +41 -0
  857. synapse/storage/schema/main/delta/73/03pusher_device_id.sql +39 -0
  858. synapse/storage/schema/main/delta/73/03users_approved_column.sql +39 -0
  859. synapse/storage/schema/main/delta/73/04partial_join_details.sql +42 -0
  860. synapse/storage/schema/main/delta/73/04pending_device_list_updates.sql +47 -0
  861. synapse/storage/schema/main/delta/73/05old_push_actions.sql.postgres +22 -0
  862. synapse/storage/schema/main/delta/73/05old_push_actions.sql.sqlite +24 -0
  863. synapse/storage/schema/main/delta/73/06thread_notifications_thread_id_idx.sql +42 -0
  864. synapse/storage/schema/main/delta/73/08thread_receipts_non_null.sql.postgres +23 -0
  865. synapse/storage/schema/main/delta/73/08thread_receipts_non_null.sql.sqlite +76 -0
  866. synapse/storage/schema/main/delta/73/09partial_joined_via_destination.sql +37 -0
  867. synapse/storage/schema/main/delta/73/09threads_table.sql +49 -0
  868. synapse/storage/schema/main/delta/73/10_update_sqlite_fts4_tokenizer.py +71 -0
  869. synapse/storage/schema/main/delta/73/10login_tokens.sql +54 -0
  870. synapse/storage/schema/main/delta/73/11event_search_room_id_n_distinct.sql.postgres +33 -0
  871. synapse/storage/schema/main/delta/73/12refactor_device_list_outbound_pokes.sql +72 -0
  872. synapse/storage/schema/main/delta/73/13add_device_lists_index.sql +39 -0
  873. synapse/storage/schema/main/delta/73/20_un_partial_stated_room_stream.sql +51 -0
  874. synapse/storage/schema/main/delta/73/21_un_partial_stated_room_stream_seq.sql.postgres +20 -0
  875. synapse/storage/schema/main/delta/73/22_rebuild_user_dir_stats.sql +48 -0
  876. synapse/storage/schema/main/delta/73/22_un_partial_stated_event_stream.sql +53 -0
  877. synapse/storage/schema/main/delta/73/23_fix_thread_index.sql +52 -0
  878. synapse/storage/schema/main/delta/73/23_un_partial_stated_room_stream_seq.sql.postgres +20 -0
  879. synapse/storage/schema/main/delta/73/24_events_jump_to_date_index.sql +36 -0
  880. synapse/storage/schema/main/delta/73/25drop_presence.sql +36 -0
  881. synapse/storage/schema/main/delta/74/01_user_directory_stale_remote_users.sql +58 -0
  882. synapse/storage/schema/main/delta/74/02_set_device_id_for_pushers_bg_update.sql +38 -0
  883. synapse/storage/schema/main/delta/74/03_membership_tables_event_stream_ordering.sql.postgres +29 -0
  884. synapse/storage/schema/main/delta/74/03_membership_tables_event_stream_ordering.sql.sqlite +23 -0
  885. synapse/storage/schema/main/delta/74/03_room_membership_index.sql +38 -0
  886. synapse/storage/schema/main/delta/74/04_delete_e2e_backup_keys_for_deactivated_users.sql +36 -0
  887. synapse/storage/schema/main/delta/74/04_membership_tables_event_stream_ordering_triggers.py +87 -0
  888. synapse/storage/schema/main/delta/74/05_events_txn_id_device_id.sql +72 -0
  889. synapse/storage/schema/main/delta/74/90COMMENTS_destinations.sql.postgres +52 -0
  890. synapse/storage/schema/main/delta/76/01_add_profiles_full_user_id_column.sql +39 -0
  891. synapse/storage/schema/main/delta/76/02_add_user_filters_full_user_id_column.sql +39 -0
  892. synapse/storage/schema/main/delta/76/03_per_user_experimental_features.sql +46 -0
  893. synapse/storage/schema/main/delta/76/04_add_room_forgetter.sql +43 -0
  894. synapse/storage/schema/main/delta/77/01_add_profiles_not_valid_check.sql.postgres +16 -0
  895. synapse/storage/schema/main/delta/77/02_add_user_filters_not_valid_check.sql.postgres +16 -0
  896. synapse/storage/schema/main/delta/77/03bg_populate_full_user_id_profiles.sql +35 -0
  897. synapse/storage/schema/main/delta/77/04bg_populate_full_user_id_user_filters.sql +35 -0
  898. synapse/storage/schema/main/delta/77/05thread_notifications_backfill.sql +67 -0
  899. synapse/storage/schema/main/delta/77/06thread_notifications_not_null.sql.sqlite +102 -0
  900. synapse/storage/schema/main/delta/77/06thread_notifications_not_null_event_push_actions.sql.postgres +27 -0
  901. synapse/storage/schema/main/delta/77/06thread_notifications_not_null_event_push_actions_staging.sql.postgres +27 -0
  902. synapse/storage/schema/main/delta/77/06thread_notifications_not_null_event_push_summary.sql.postgres +29 -0
  903. synapse/storage/schema/main/delta/77/14bg_indices_event_stream_ordering.sql +39 -0
  904. synapse/storage/schema/main/delta/78/01_validate_and_update_profiles.py +99 -0
  905. synapse/storage/schema/main/delta/78/02_validate_and_update_user_filters.py +100 -0
  906. synapse/storage/schema/main/delta/78/03_remove_unused_indexes_user_filters.py +72 -0
  907. synapse/storage/schema/main/delta/78/03event_extremities_constraints.py +65 -0
  908. synapse/storage/schema/main/delta/78/04_add_full_user_id_index_user_filters.py +32 -0
  909. synapse/storage/schema/main/delta/79/03_read_write_locks_triggers.sql.postgres +102 -0
  910. synapse/storage/schema/main/delta/79/03_read_write_locks_triggers.sql.sqlite +72 -0
  911. synapse/storage/schema/main/delta/79/04_mitigate_stream_ordering_update_race.py +70 -0
  912. synapse/storage/schema/main/delta/79/05_read_write_locks_triggers.sql.postgres +69 -0
  913. synapse/storage/schema/main/delta/79/05_read_write_locks_triggers.sql.sqlite +65 -0
  914. synapse/storage/schema/main/delta/80/01_users_alter_locked.sql +35 -0
  915. synapse/storage/schema/main/delta/80/02_read_write_locks_unlogged.sql.postgres +30 -0
  916. synapse/storage/schema/main/delta/80/02_scheduled_tasks.sql +47 -0
  917. synapse/storage/schema/main/delta/80/03_read_write_locks_triggers.sql.postgres +37 -0
  918. synapse/storage/schema/main/delta/80/04_read_write_locks_deadlock.sql.postgres +71 -0
  919. synapse/storage/schema/main/delta/82/02_scheduled_tasks_index.sql +35 -0
  920. synapse/storage/schema/main/delta/82/04_add_indices_for_purging_rooms.sql +39 -0
  921. synapse/storage/schema/main/delta/82/05gaps.sql +44 -0
  922. synapse/storage/schema/main/delta/83/01_drop_old_tables.sql +43 -0
  923. synapse/storage/schema/main/delta/83/03_instance_name_receipts.sql.sqlite +17 -0
  924. synapse/storage/schema/main/delta/83/05_cross_signing_key_update_grant.sql +34 -0
  925. synapse/storage/schema/main/delta/83/06_event_push_summary_room.sql +36 -0
  926. synapse/storage/schema/main/delta/84/01_auth_links_stats.sql.postgres +20 -0
  927. synapse/storage/schema/main/delta/84/02_auth_links_index.sql +16 -0
  928. synapse/storage/schema/main/delta/84/03_auth_links_analyze.sql.postgres +16 -0
  929. synapse/storage/schema/main/delta/84/04_access_token_index.sql +15 -0
  930. synapse/storage/schema/main/delta/85/01_add_suspended.sql +14 -0
  931. synapse/storage/schema/main/delta/85/02_add_instance_names.sql +27 -0
  932. synapse/storage/schema/main/delta/85/03_new_sequences.sql.postgres +54 -0
  933. synapse/storage/schema/main/delta/85/04_cleanup_device_federation_outbox.sql +15 -0
  934. synapse/storage/schema/main/delta/85/05_add_instance_names_converted_pos.sql +16 -0
  935. synapse/storage/schema/main/delta/85/06_add_room_reports.sql +20 -0
  936. synapse/storage/schema/main/delta/86/01_authenticate_media.sql +15 -0
  937. synapse/storage/schema/main/delta/86/02_receipts_event_id_index.sql +15 -0
  938. synapse/storage/schema/main/delta/87/01_sliding_sync_memberships.sql +169 -0
  939. synapse/storage/schema/main/delta/87/02_per_connection_state.sql +81 -0
  940. synapse/storage/schema/main/delta/87/03_current_state_index.sql +19 -0
  941. synapse/storage/schema/main/delta/88/01_add_delayed_events.sql +43 -0
  942. synapse/storage/schema/main/delta/88/01_custom_profile_fields.sql +15 -0
  943. synapse/storage/schema/main/delta/88/02_fix_sliding_sync_membership_snapshots_forgotten_column.sql +21 -0
  944. synapse/storage/schema/main/delta/88/03_add_otk_ts_added_index.sql +18 -0
  945. synapse/storage/schema/main/delta/88/04_current_state_delta_index.sql +18 -0
  946. synapse/storage/schema/main/delta/88/05_drop_old_otks.sql.postgres +19 -0
  947. synapse/storage/schema/main/delta/88/05_drop_old_otks.sql.sqlite +19 -0
  948. synapse/storage/schema/main/delta/88/05_sliding_sync_room_config_index.sql +20 -0
  949. synapse/storage/schema/main/delta/88/06_events_received_ts_index.sql +17 -0
  950. synapse/storage/schema/main/delta/89/01_sliding_sync_membership_snapshot_index.sql +15 -0
  951. synapse/storage/schema/main/delta/90/01_add_column_participant_room_memberships_table.sql +16 -0
  952. synapse/storage/schema/main/delta/91/01_media_hash.sql +28 -0
  953. synapse/storage/schema/main/delta/92/01_remove_trigger.sql.postgres +16 -0
  954. synapse/storage/schema/main/delta/92/01_remove_trigger.sql.sqlite +16 -0
  955. synapse/storage/schema/main/delta/92/02_remove_populate_participant_bg_update.sql +17 -0
  956. synapse/storage/schema/main/delta/92/04_ss_membership_snapshot_idx.sql +16 -0
  957. synapse/storage/schema/main/delta/92/04_thread_subscriptions.sql +59 -0
  958. synapse/storage/schema/main/delta/92/04_thread_subscriptions_seq.sql.postgres +19 -0
  959. synapse/storage/schema/main/delta/92/05_fixup_max_depth_cap.sql +17 -0
  960. synapse/storage/schema/main/delta/92/05_thread_subscriptions_comments.sql.postgres +18 -0
  961. synapse/storage/schema/main/delta/92/06_device_federation_inbox_index.sql +16 -0
  962. synapse/storage/schema/main/delta/92/06_threads_last_sent_stream_ordering_comments.sql.postgres +24 -0
  963. synapse/storage/schema/main/delta/92/07_add_user_reports.sql +22 -0
  964. synapse/storage/schema/main/delta/92/07_event_txn_id_device_id_txn_id2.sql +15 -0
  965. synapse/storage/schema/main/delta/92/08_room_ban_redactions.sql +21 -0
  966. synapse/storage/schema/main/delta/92/08_thread_subscriptions_seq_fixup.sql.postgres +19 -0
  967. synapse/storage/schema/main/delta/92/09_thread_subscriptions_update.sql +20 -0
  968. synapse/storage/schema/main/delta/92/09_thread_subscriptions_update.sql.postgres +18 -0
  969. synapse/storage/schema/main/full_schemas/72/full.sql.postgres +1344 -0
  970. synapse/storage/schema/main/full_schemas/72/full.sql.sqlite +646 -0
  971. synapse/storage/schema/state/delta/23/drop_state_index.sql +35 -0
  972. synapse/storage/schema/state/delta/32/remove_state_indices.sql +38 -0
  973. synapse/storage/schema/state/delta/35/add_state_index.sql +36 -0
  974. synapse/storage/schema/state/delta/35/state.sql +41 -0
  975. synapse/storage/schema/state/delta/35/state_dedupe.sql +36 -0
  976. synapse/storage/schema/state/delta/47/state_group_seq.py +38 -0
  977. synapse/storage/schema/state/delta/56/state_group_room_idx.sql +36 -0
  978. synapse/storage/schema/state/delta/61/02state_groups_state_n_distinct.sql.postgres +34 -0
  979. synapse/storage/schema/state/delta/70/08_state_group_edges_unique.sql +36 -0
  980. synapse/storage/schema/state/delta/89/01_state_groups_deletion.sql +39 -0
  981. synapse/storage/schema/state/delta/90/02_delete_unreferenced_state_groups.sql +16 -0
  982. synapse/storage/schema/state/delta/90/03_remove_old_deletion_bg_update.sql +15 -0
  983. synapse/storage/schema/state/full_schemas/72/full.sql.postgres +30 -0
  984. synapse/storage/schema/state/full_schemas/72/full.sql.sqlite +20 -0
  985. synapse/storage/types.py +185 -0
  986. synapse/storage/util/__init__.py +20 -0
  987. synapse/storage/util/id_generators.py +909 -0
  988. synapse/storage/util/partial_state_events_tracker.py +194 -0
  989. synapse/storage/util/sequence.py +315 -0
  990. synapse/streams/__init__.py +43 -0
  991. synapse/streams/config.py +92 -0
  992. synapse/streams/events.py +203 -0
  993. synapse/synapse_rust/__init__.pyi +3 -0
  994. synapse/synapse_rust/acl.pyi +20 -0
  995. synapse/synapse_rust/events.pyi +136 -0
  996. synapse/synapse_rust/http_client.pyi +32 -0
  997. synapse/synapse_rust/push.pyi +86 -0
  998. synapse/synapse_rust/rendezvous.pyi +30 -0
  999. synapse/synapse_rust/segmenter.pyi +1 -0
  1000. synapse/synapse_rust.abi3.so +0 -0
  1001. synapse/types/__init__.py +1600 -0
  1002. synapse/types/handlers/__init__.py +93 -0
  1003. synapse/types/handlers/policy_server.py +16 -0
  1004. synapse/types/handlers/sliding_sync.py +909 -0
  1005. synapse/types/rest/__init__.py +25 -0
  1006. synapse/types/rest/client/__init__.py +415 -0
  1007. synapse/types/state.py +635 -0
  1008. synapse/types/storage/__init__.py +66 -0
  1009. synapse/util/__init__.py +170 -0
  1010. synapse/util/async_helpers.py +1067 -0
  1011. synapse/util/batching_queue.py +202 -0
  1012. synapse/util/caches/__init__.py +300 -0
  1013. synapse/util/caches/cached_call.py +143 -0
  1014. synapse/util/caches/deferred_cache.py +530 -0
  1015. synapse/util/caches/descriptors.py +694 -0
  1016. synapse/util/caches/dictionary_cache.py +350 -0
  1017. synapse/util/caches/expiringcache.py +251 -0
  1018. synapse/util/caches/lrucache.py +977 -0
  1019. synapse/util/caches/response_cache.py +323 -0
  1020. synapse/util/caches/stream_change_cache.py +370 -0
  1021. synapse/util/caches/treecache.py +189 -0
  1022. synapse/util/caches/ttlcache.py +197 -0
  1023. synapse/util/cancellation.py +63 -0
  1024. synapse/util/check_dependencies.py +335 -0
  1025. synapse/util/clock.py +500 -0
  1026. synapse/util/constants.py +22 -0
  1027. synapse/util/daemonize.py +165 -0
  1028. synapse/util/distributor.py +159 -0
  1029. synapse/util/events.py +134 -0
  1030. synapse/util/file_consumer.py +164 -0
  1031. synapse/util/frozenutils.py +57 -0
  1032. synapse/util/gai_resolver.py +180 -0
  1033. synapse/util/hash.py +38 -0
  1034. synapse/util/httpresourcetree.py +108 -0
  1035. synapse/util/iterutils.py +189 -0
  1036. synapse/util/json.py +56 -0
  1037. synapse/util/linked_list.py +156 -0
  1038. synapse/util/logcontext.py +46 -0
  1039. synapse/util/logformatter.py +28 -0
  1040. synapse/util/macaroons.py +325 -0
  1041. synapse/util/manhole.py +191 -0
  1042. synapse/util/metrics.py +340 -0
  1043. synapse/util/module_loader.py +116 -0
  1044. synapse/util/msisdn.py +51 -0
  1045. synapse/util/patch_inline_callbacks.py +250 -0
  1046. synapse/util/pydantic_models.py +56 -0
  1047. synapse/util/ratelimitutils.py +420 -0
  1048. synapse/util/retryutils.py +339 -0
  1049. synapse/util/rlimit.py +42 -0
  1050. synapse/util/rust.py +134 -0
  1051. synapse/util/sentinel.py +21 -0
  1052. synapse/util/stringutils.py +293 -0
  1053. synapse/util/task_scheduler.py +493 -0
  1054. synapse/util/templates.py +126 -0
  1055. synapse/util/threepids.py +123 -0
  1056. synapse/util/wheel_timer.py +112 -0
  1057. synapse/visibility.py +836 -0
@@ -0,0 +1,2509 @@
1
+ #
2
+ # This file is licensed under the Affero General Public License (AGPL) version 3.
3
+ #
4
+ # Copyright 2014-2016 OpenMarket Ltd
5
+ # Copyright (C) 2023 New Vector, Ltd
6
+ #
7
+ # This program is free software: you can redistribute it and/or modify
8
+ # it under the terms of the GNU Affero General Public License as
9
+ # published by the Free Software Foundation, either version 3 of the
10
+ # License, or (at your option) any later version.
11
+ #
12
+ # See the GNU Affero General Public License for more details:
13
+ # <https://www.gnu.org/licenses/agpl-3.0.html>.
14
+ #
15
+ # Originally licensed under the Apache License, Version 2.0:
16
+ # <http://www.apache.org/licenses/LICENSE-2.0>.
17
+ #
18
+ # [This file includes modifications made by New Vector Limited]
19
+ #
20
+ #
21
+ import datetime
22
+ import itertools
23
+ import logging
24
+ from queue import Empty, PriorityQueue
25
+ from typing import (
26
+ TYPE_CHECKING,
27
+ Collection,
28
+ Generator,
29
+ Iterable,
30
+ Optional,
31
+ Sequence,
32
+ cast,
33
+ )
34
+
35
+ import attr
36
+ from prometheus_client import Counter, Gauge
37
+
38
+ from synapse.api.constants import MAX_DEPTH
39
+ from synapse.api.errors import StoreError
40
+ from synapse.api.room_versions import EventFormatVersions, RoomVersion
41
+ from synapse.events import EventBase, make_event_from_dict
42
+ from synapse.logging.opentracing import tag_args, trace
43
+ from synapse.metrics import SERVER_NAME_LABEL
44
+ from synapse.metrics.background_process_metrics import wrap_as_background_process
45
+ from synapse.storage._base import db_to_json, make_in_list_sql_clause
46
+ from synapse.storage.background_updates import ForeignKeyConstraint
47
+ from synapse.storage.database import (
48
+ DatabasePool,
49
+ LoggingDatabaseConnection,
50
+ LoggingTransaction,
51
+ )
52
+ from synapse.storage.databases.main.cache import CacheInvalidationWorkerStore
53
+ from synapse.storage.databases.main.events_worker import EventsWorkerStore
54
+ from synapse.storage.databases.main.signatures import SignatureWorkerStore
55
+ from synapse.storage.engines import PostgresEngine, Sqlite3Engine
56
+ from synapse.types import JsonDict, StrCollection
57
+ from synapse.util.caches.descriptors import cached
58
+ from synapse.util.caches.lrucache import LruCache
59
+ from synapse.util.cancellation import cancellable
60
+ from synapse.util.iterutils import batch_iter
61
+ from synapse.util.json import json_encoder
62
+
63
+ if TYPE_CHECKING:
64
+ from synapse.server import HomeServer
65
+
66
+ oldest_pdu_in_federation_staging = Gauge(
67
+ "synapse_federation_server_oldest_inbound_pdu_in_staging",
68
+ "The age in seconds since we received the oldest pdu in the federation staging area",
69
+ labelnames=[SERVER_NAME_LABEL],
70
+ )
71
+
72
+ number_pdus_in_federation_queue = Gauge(
73
+ "synapse_federation_server_number_inbound_pdu_in_staging",
74
+ "The total number of events in the inbound federation staging",
75
+ labelnames=[SERVER_NAME_LABEL],
76
+ )
77
+
78
+ pdus_pruned_from_federation_queue = Counter(
79
+ "synapse_federation_server_number_inbound_pdu_pruned",
80
+ "The number of events in the inbound federation staging that have been "
81
+ "pruned due to the queue getting too long",
82
+ labelnames=[SERVER_NAME_LABEL],
83
+ )
84
+
85
+ logger = logging.getLogger(__name__)
86
+
87
+ # Parameters controlling exponential backoff between backfill failures.
88
+ # After the first failure to backfill, we wait 2 hours before trying again. If the
89
+ # second attempt fails, we wait 4 hours before trying again. If the third attempt fails,
90
+ # we wait 8 hours before trying again, ... and so on.
91
+ #
92
+ # Each successive backoff period is twice as long as the last. However we cap this
93
+ # period at a maximum of 2^8 = 256 hours: a little over 10 days. (This is the smallest
94
+ # power of 2 which yields a maximum backoff period of at least 7 days---which was the
95
+ # original maximum backoff period.) Even when we hit this cap, we will continue to
96
+ # make backfill attempts once every 10 days.
97
+ BACKFILL_EVENT_EXPONENTIAL_BACKOFF_MAXIMUM_DOUBLING_STEPS = 8
98
+ BACKFILL_EVENT_EXPONENTIAL_BACKOFF_STEP_MILLISECONDS = int(
99
+ datetime.timedelta(hours=1).total_seconds() * 1000
100
+ )
101
+
102
+ # We need a cap on the power of 2 or else the backoff period
103
+ # 2^N * (milliseconds per hour)
104
+ # will overflow when calcuated within the database. We ensure overflow does not occur
105
+ # by checking that the largest backoff period fits in a 32-bit signed integer.
106
+ _LONGEST_BACKOFF_PERIOD_MILLISECONDS = (
107
+ 2**BACKFILL_EVENT_EXPONENTIAL_BACKOFF_MAXIMUM_DOUBLING_STEPS
108
+ ) * BACKFILL_EVENT_EXPONENTIAL_BACKOFF_STEP_MILLISECONDS
109
+ assert 0 < _LONGEST_BACKOFF_PERIOD_MILLISECONDS <= ((2**31) - 1)
110
+
111
+
112
+ # We use 2^53-1 as a "very large number", it has no particular
113
+ # importance other than knowing synapse can support it (given canonical json
114
+ # requires it).
115
+ MAX_CHAIN_LENGTH = (2**53) - 1
116
+
117
+
118
+ # All the info we need while iterating the DAG while backfilling
119
+ @attr.s(frozen=True, slots=True, auto_attribs=True)
120
+ class BackfillQueueNavigationItem:
121
+ depth: int
122
+ stream_ordering: int
123
+ event_id: str
124
+ type: str
125
+
126
+
127
+ @attr.s(frozen=True, slots=True, auto_attribs=True)
128
+ class StateDifference:
129
+ # The event IDs in the auth difference.
130
+ auth_difference: set[str]
131
+ # The event IDs in the conflicted state subgraph. Used in v2.1 only.
132
+ conflicted_subgraph: Optional[set[str]]
133
+
134
+
135
+ class _NoChainCoverIndex(Exception):
136
+ def __init__(self, room_id: str):
137
+ super().__init__("Unexpectedly no chain cover for events in %s" % (room_id,))
138
+
139
+
140
+ class EventFederationWorkerStore(
141
+ SignatureWorkerStore, EventsWorkerStore, CacheInvalidationWorkerStore
142
+ ):
143
+ # TODO: this attribute comes from EventPushActionWorkerStore. Should we inherit from
144
+ # that store so that mypy can deduce this for itself?
145
+ stream_ordering_month_ago: Optional[int]
146
+
147
+ def __init__(
148
+ self,
149
+ database: DatabasePool,
150
+ db_conn: LoggingDatabaseConnection,
151
+ hs: "HomeServer",
152
+ ):
153
+ super().__init__(database, db_conn, hs)
154
+
155
+ self.hs = hs
156
+
157
+ if hs.config.worker.run_background_tasks:
158
+ hs.get_clock().looping_call(
159
+ self._delete_old_forward_extrem_cache, 60 * 60 * 1000
160
+ )
161
+
162
+ # Cache of event ID to list of auth event IDs and their depths.
163
+ self._event_auth_cache: LruCache[str, list[tuple[str, int]]] = LruCache(
164
+ max_size=500000,
165
+ clock=self.hs.get_clock(),
166
+ server_name=self.server_name,
167
+ cache_name="_event_auth_cache",
168
+ size_callback=len,
169
+ )
170
+
171
+ # Flag used by unit tests to disable fallback when there is no chain cover
172
+ # index.
173
+ self.tests_allow_no_chain_cover_index = True
174
+
175
+ self.clock.looping_call(self._get_stats_for_federation_staging, 30 * 1000)
176
+
177
+ if isinstance(self.database_engine, PostgresEngine):
178
+ self.db_pool.updates.register_background_validate_constraint_and_delete_rows(
179
+ update_name="event_forward_extremities_event_id_foreign_key_constraint_update",
180
+ table="event_forward_extremities",
181
+ constraint_name="event_forward_extremities_event_id",
182
+ constraint=ForeignKeyConstraint(
183
+ "events", [("event_id", "event_id")], deferred=True
184
+ ),
185
+ unique_columns=("event_id", "room_id"),
186
+ )
187
+
188
+ self.db_pool.updates.register_background_index_update(
189
+ update_name="event_auth_chain_links_origin_index",
190
+ index_name="event_auth_chain_links_origin_index",
191
+ table="event_auth_chain_links",
192
+ columns=("origin_chain_id", "origin_sequence_number"),
193
+ )
194
+
195
+ async def get_auth_chain(
196
+ self, room_id: str, event_ids: Collection[str], include_given: bool = False
197
+ ) -> list[EventBase]:
198
+ """Get auth events for given event_ids. The events *must* be state events.
199
+
200
+ Args:
201
+ room_id: The room the event is in.
202
+ event_ids: state events
203
+ include_given: include the given events in result
204
+
205
+ Returns:
206
+ list of events
207
+ """
208
+ event_ids = await self.get_auth_chain_ids(
209
+ room_id, event_ids, include_given=include_given
210
+ )
211
+ return await self.get_events_as_list(event_ids)
212
+
213
+ @trace
214
+ @tag_args
215
+ async def get_auth_chain_ids(
216
+ self,
217
+ room_id: str,
218
+ event_ids: Collection[str],
219
+ include_given: bool = False,
220
+ ) -> set[str]:
221
+ """Get auth events for given event_ids. The events *must* be state events.
222
+
223
+ Args:
224
+ room_id: The room the event is in.
225
+ event_ids: state events
226
+ include_given: include the given events in result
227
+
228
+ Returns:
229
+ set of event_ids
230
+ """
231
+
232
+ # Check if we have indexed the room so we can use the chain cover
233
+ # algorithm.
234
+ room = await self.get_room(room_id) # type: ignore[attr-defined]
235
+ # If the room has an auth chain index.
236
+ if room[1]:
237
+ try:
238
+ return await self.db_pool.runInteraction(
239
+ "get_auth_chain_ids_chains",
240
+ self._get_auth_chain_ids_using_cover_index_txn,
241
+ room_id,
242
+ event_ids,
243
+ include_given,
244
+ )
245
+ except _NoChainCoverIndex:
246
+ # For whatever reason we don't actually have a chain cover index
247
+ # for the events in question, so we fall back to the old method
248
+ # (except in tests)
249
+ if not self.tests_allow_no_chain_cover_index:
250
+ raise
251
+
252
+ return await self.db_pool.runInteraction(
253
+ "get_auth_chain_ids",
254
+ self._get_auth_chain_ids_txn,
255
+ event_ids,
256
+ include_given,
257
+ )
258
+
259
+ def _get_auth_chain_ids_using_cover_index_txn(
260
+ self,
261
+ txn: LoggingTransaction,
262
+ room_id: str,
263
+ event_ids: Collection[str],
264
+ include_given: bool,
265
+ ) -> set[str]:
266
+ """Calculates the auth chain IDs using the chain index."""
267
+
268
+ # First we look up the chain ID/sequence numbers for the given events.
269
+
270
+ initial_events = set(event_ids)
271
+
272
+ # All the events that we've found that are reachable from the events.
273
+ seen_events: set[str] = set()
274
+
275
+ # A map from chain ID to max sequence number of the given events.
276
+ event_chains: dict[int, int] = {}
277
+
278
+ sql = """
279
+ SELECT event_id, chain_id, sequence_number
280
+ FROM event_auth_chains
281
+ WHERE %s
282
+ """
283
+ for batch in batch_iter(initial_events, 1000):
284
+ clause, args = make_in_list_sql_clause(
285
+ txn.database_engine, "event_id", batch
286
+ )
287
+ txn.execute(sql % (clause,), args)
288
+
289
+ for event_id, chain_id, sequence_number in txn:
290
+ seen_events.add(event_id)
291
+ event_chains[chain_id] = max(
292
+ sequence_number, event_chains.get(chain_id, 0)
293
+ )
294
+
295
+ # Check that we actually have a chain ID for all the events.
296
+ events_missing_chain_info = initial_events.difference(seen_events)
297
+ if events_missing_chain_info:
298
+ # This can happen due to e.g. downgrade/upgrade of the server. We
299
+ # raise an exception and fall back to the previous algorithm.
300
+ logger.error(
301
+ "Unexpectedly found that events don't have chain IDs in room %s: %s",
302
+ room_id,
303
+ events_missing_chain_info,
304
+ )
305
+ raise _NoChainCoverIndex(room_id)
306
+
307
+ # Now we look up all links for the chains we have, adding chains that
308
+ # are reachable from any event.
309
+
310
+ # A map from chain ID to max sequence number *reachable* from any event ID.
311
+ chains: dict[int, int] = {}
312
+ for links in self._get_chain_links(txn, set(event_chains.keys())):
313
+ for chain_id in links:
314
+ if chain_id not in event_chains:
315
+ continue
316
+
317
+ _materialize(chain_id, event_chains[chain_id], links, chains)
318
+
319
+ # Add the initial set of chains, excluding the sequence corresponding to
320
+ # initial event.
321
+ for chain_id, seq_no in event_chains.items():
322
+ # Check if the initial event is the first item in the chain. If so, then
323
+ # there is nothing new to add from this chain.
324
+ if seq_no == 1:
325
+ continue
326
+
327
+ chains[chain_id] = max(seq_no - 1, chains.get(chain_id, 0))
328
+
329
+ # Now for each chain we figure out the maximum sequence number reachable
330
+ # from *any* event ID. Events with a sequence less than that are in the
331
+ # auth chain.
332
+ if include_given:
333
+ results = initial_events
334
+ else:
335
+ results = set()
336
+
337
+ if isinstance(self.database_engine, PostgresEngine):
338
+ # We can use `execute_values` to efficiently fetch the gaps when
339
+ # using postgres.
340
+ sql = """
341
+ SELECT event_id
342
+ FROM event_auth_chains AS c, (VALUES ?) AS l(chain_id, max_seq)
343
+ WHERE
344
+ c.chain_id = l.chain_id
345
+ AND sequence_number <= max_seq
346
+ """
347
+
348
+ rows = txn.execute_values(sql, chains.items())
349
+ results.update(r for (r,) in rows)
350
+ else:
351
+ # For SQLite we just fall back to doing a noddy for loop.
352
+ sql = """
353
+ SELECT event_id FROM event_auth_chains
354
+ WHERE chain_id = ? AND sequence_number <= ?
355
+ """
356
+ for chain_id, max_no in chains.items():
357
+ txn.execute(sql, (chain_id, max_no))
358
+ results.update(r for (r,) in txn)
359
+
360
+ return results
361
+
362
+ @classmethod
363
+ def _get_chain_links(
364
+ cls, txn: LoggingTransaction, chains_to_fetch: set[int]
365
+ ) -> Generator[dict[int, list[tuple[int, int, int]]], None, None]:
366
+ """Fetch all auth chain links from the given set of chains, and all
367
+ links from those chains, recursively.
368
+
369
+ Note: This may return links that are not reachable from the given
370
+ chains.
371
+
372
+ Returns a generator that produces dicts from origin chain ID to 3-tuple
373
+ of origin sequence number, target chain ID and target sequence number.
374
+ """
375
+
376
+ # This query is structured to first get all chain IDs reachable, and
377
+ # then pull out all links from those chains. This does pull out more
378
+ # rows than is strictly necessary, however there isn't a way of
379
+ # structuring the recursive part of query to pull out the links without
380
+ # also returning large quantities of redundant data (which can make it a
381
+ # lot slower).
382
+ sql = """
383
+ WITH RECURSIVE links(chain_id) AS (
384
+ SELECT
385
+ DISTINCT origin_chain_id
386
+ FROM event_auth_chain_links WHERE %s
387
+ UNION
388
+ SELECT
389
+ target_chain_id
390
+ FROM event_auth_chain_links
391
+ INNER JOIN links ON (chain_id = origin_chain_id)
392
+ )
393
+ SELECT
394
+ origin_chain_id, origin_sequence_number,
395
+ target_chain_id, target_sequence_number
396
+ FROM links
397
+ INNER JOIN event_auth_chain_links ON (chain_id = origin_chain_id)
398
+ """
399
+
400
+ while chains_to_fetch:
401
+ batch2 = tuple(itertools.islice(chains_to_fetch, 1000))
402
+ chains_to_fetch.difference_update(batch2)
403
+ clause, args = make_in_list_sql_clause(
404
+ txn.database_engine, "origin_chain_id", batch2
405
+ )
406
+ txn.execute(sql % (clause,), args)
407
+
408
+ links: dict[int, list[tuple[int, int, int]]] = {}
409
+
410
+ for (
411
+ origin_chain_id,
412
+ origin_sequence_number,
413
+ target_chain_id,
414
+ target_sequence_number,
415
+ ) in txn:
416
+ links.setdefault(origin_chain_id, []).append(
417
+ (origin_sequence_number, target_chain_id, target_sequence_number)
418
+ )
419
+
420
+ chains_to_fetch.difference_update(links)
421
+
422
+ yield links
423
+
424
+ def _get_auth_chain_ids_txn(
425
+ self, txn: LoggingTransaction, event_ids: Collection[str], include_given: bool
426
+ ) -> set[str]:
427
+ """Calculates the auth chain IDs.
428
+
429
+ This is used when we don't have a cover index for the room.
430
+ """
431
+ if include_given:
432
+ results = set(event_ids)
433
+ else:
434
+ results = set()
435
+
436
+ # We pull out the depth simply so that we can populate the
437
+ # `_event_auth_cache` cache.
438
+ base_sql = """
439
+ SELECT a.event_id, auth_id, depth
440
+ FROM event_auth AS a
441
+ INNER JOIN events AS e ON (e.event_id = a.auth_id)
442
+ WHERE
443
+ """
444
+
445
+ front = set(event_ids)
446
+ while front:
447
+ new_front: set[str] = set()
448
+ for chunk in batch_iter(front, 100):
449
+ # Pull the auth events either from the cache or DB.
450
+ to_fetch: list[str] = [] # Event IDs to fetch from DB
451
+ for event_id in chunk:
452
+ res = self._event_auth_cache.get(event_id)
453
+ if res is None:
454
+ to_fetch.append(event_id)
455
+ else:
456
+ new_front.update(auth_id for auth_id, depth in res)
457
+
458
+ if to_fetch:
459
+ clause, args = make_in_list_sql_clause(
460
+ txn.database_engine, "a.event_id", to_fetch
461
+ )
462
+ txn.execute(base_sql + clause, args)
463
+
464
+ # Note we need to batch up the results by event ID before
465
+ # adding to the cache.
466
+ to_cache: dict[str, list[tuple[str, int]]] = {}
467
+ for event_id, auth_event_id, auth_event_depth in txn:
468
+ to_cache.setdefault(event_id, []).append(
469
+ (auth_event_id, auth_event_depth)
470
+ )
471
+ new_front.add(auth_event_id)
472
+
473
+ for event_id, auth_events in to_cache.items():
474
+ self._event_auth_cache.set(event_id, auth_events)
475
+
476
+ new_front -= results
477
+
478
+ front = new_front
479
+ results.update(front)
480
+
481
+ return results
482
+
483
+ async def get_auth_chain_difference(
484
+ self,
485
+ room_id: str,
486
+ state_sets: list[set[str]],
487
+ ) -> set[str]:
488
+ state_diff = await self.get_auth_chain_difference_extended(
489
+ room_id, state_sets, None, None
490
+ )
491
+ return state_diff.auth_difference
492
+
493
+ async def get_auth_chain_difference_extended(
494
+ self,
495
+ room_id: str,
496
+ state_sets: list[set[str]],
497
+ conflicted_set: Optional[set[str]],
498
+ additional_backwards_reachable_conflicted_events: Optional[set[str]],
499
+ ) -> StateDifference:
500
+ """ "Given sets of state events figure out the auth chain difference (as
501
+ per state res v2 algorithm).
502
+
503
+ This is equivalent to fetching the full auth chain for each set of state
504
+ and returning the events that don't appear in each and every auth
505
+ chain.
506
+
507
+ If conflicted_set is not None, calculate and return the conflicted sub-graph as per
508
+ state res v2.1. The event IDs in the conflicted set MUST be a subset of the event IDs in
509
+ state_sets.
510
+
511
+ If additional_backwards_reachable_conflicted_events is set, the provided events are included
512
+ when calculating the conflicted subgraph. This is primarily useful for calculating the
513
+ subgraph across a combination of persisted and unpersisted events. The event IDs in this set
514
+ MUST be a subset of the event IDs in state_sets.
515
+
516
+ Returns:
517
+ information on the auth chain difference, and also the conflicted subgraph if
518
+ conflicted_set is not None
519
+ """
520
+
521
+ # Check if we have indexed the room so we can use the chain cover
522
+ # algorithm.
523
+ room = await self.get_room(room_id) # type: ignore[attr-defined]
524
+ # If the room has an auth chain index.
525
+ if room[1]:
526
+ try:
527
+ return await self.db_pool.runInteraction(
528
+ "get_auth_chain_difference_chains",
529
+ self._get_auth_chain_difference_using_cover_index_txn,
530
+ room_id,
531
+ state_sets,
532
+ conflicted_set,
533
+ additional_backwards_reachable_conflicted_events,
534
+ )
535
+ except _NoChainCoverIndex:
536
+ # For whatever reason we don't actually have a chain cover index
537
+ # for the events in question, so we fall back to the old method
538
+ # (except in tests)
539
+ if not self.tests_allow_no_chain_cover_index:
540
+ raise
541
+
542
+ # It's been 4 years since we added chain cover, so we expect all rooms to have it.
543
+ # If they don't, we will error out when trying to do state res v2.1
544
+ if conflicted_set is not None:
545
+ raise _NoChainCoverIndex(room_id)
546
+
547
+ auth_diff = await self.db_pool.runInteraction(
548
+ "get_auth_chain_difference",
549
+ self._get_auth_chain_difference_txn,
550
+ state_sets,
551
+ )
552
+ return StateDifference(auth_difference=auth_diff, conflicted_subgraph=None)
553
+
554
+ def _get_auth_chain_difference_using_cover_index_txn(
555
+ self,
556
+ txn: LoggingTransaction,
557
+ room_id: str,
558
+ state_sets: list[set[str]],
559
+ conflicted_set: Optional[set[str]] = None,
560
+ additional_backwards_reachable_conflicted_events: Optional[set[str]] = None,
561
+ ) -> StateDifference:
562
+ """Calculates the auth chain difference using the chain index.
563
+
564
+ See docs/auth_chain_difference_algorithm.md for details
565
+ """
566
+ is_state_res_v21 = conflicted_set is not None
567
+
568
+ # First we look up the chain ID/sequence numbers for all the events, and
569
+ # work out the chain/sequence numbers reachable from each state set.
570
+
571
+ initial_events = set(state_sets[0]).union(*state_sets[1:])
572
+
573
+ if is_state_res_v21:
574
+ # Sanity check v2.1 fields
575
+ assert conflicted_set is not None
576
+ assert conflicted_set.issubset(initial_events)
577
+ # It's possible for the conflicted_set to be empty if all the conflicts are in
578
+ # unpersisted events, so we don't assert that conflicted_set has len > 0
579
+ if additional_backwards_reachable_conflicted_events:
580
+ assert additional_backwards_reachable_conflicted_events.issubset(
581
+ initial_events
582
+ )
583
+
584
+ # Map from event_id -> (chain ID, seq no)
585
+ chain_info: dict[str, tuple[int, int]] = {}
586
+
587
+ # Map from chain ID -> seq no -> event Id
588
+ chain_to_event: dict[int, dict[int, str]] = {}
589
+
590
+ # All the chains that we've found that are reachable from the state
591
+ # sets.
592
+ seen_chains: set[int] = set()
593
+
594
+ # Fetch the chain cover index for the initial set of events we're
595
+ # considering.
596
+ def fetch_chain_info(events_to_fetch: Collection[str]) -> None:
597
+ sql = """
598
+ SELECT event_id, chain_id, sequence_number
599
+ FROM event_auth_chains
600
+ WHERE %s
601
+ """
602
+ for batch in batch_iter(events_to_fetch, 1000):
603
+ clause, args = make_in_list_sql_clause(
604
+ txn.database_engine, "event_id", batch
605
+ )
606
+ txn.execute(sql % (clause,), args)
607
+
608
+ for event_id, chain_id, sequence_number in txn:
609
+ chain_info[event_id] = (chain_id, sequence_number)
610
+ seen_chains.add(chain_id)
611
+ chain_to_event.setdefault(chain_id, {})[sequence_number] = event_id
612
+
613
+ fetch_chain_info(initial_events)
614
+
615
+ # Check that we actually have a chain ID for all the events.
616
+ events_missing_chain_info = initial_events.difference(chain_info)
617
+
618
+ # The result set to return, i.e. the auth chain difference.
619
+ auth_difference_result: set[str] = set()
620
+
621
+ if events_missing_chain_info:
622
+ # For some reason we have events we haven't calculated the chain
623
+ # index for, so we need to handle those separately. This should only
624
+ # happen for older rooms where the server doesn't have all the auth
625
+ # events.
626
+ auth_difference_result = self._fixup_auth_chain_difference_sets(
627
+ txn,
628
+ room_id,
629
+ state_sets=state_sets,
630
+ events_missing_chain_info=events_missing_chain_info,
631
+ events_that_have_chain_index=chain_info,
632
+ )
633
+
634
+ # We now need to refetch any events that we have added to the state
635
+ # sets.
636
+ new_events_to_fetch = {
637
+ event_id
638
+ for state_set in state_sets
639
+ for event_id in state_set
640
+ if event_id not in initial_events
641
+ }
642
+
643
+ fetch_chain_info(new_events_to_fetch)
644
+
645
+ # State Res v2.1 needs extra data structures to calculate the conflicted subgraph which
646
+ # are outlined below.
647
+
648
+ # A subset of chain_info for conflicted events only, as we need to
649
+ # loop all conflicted chain positions. Map from event_id -> (chain ID, seq no)
650
+ conflicted_chain_positions: dict[str, tuple[int, int]] = {}
651
+ # For each chain, remember the positions where conflicted events are.
652
+ # We need this for calculating the forward reachable events.
653
+ conflicted_chain_to_seq: dict[int, set[int]] = {} # chain_id => {seq_num}
654
+ # A subset of chain_info for additional backwards reachable events only, as we need to
655
+ # loop all additional backwards reachable events for calculating backwards reachable events.
656
+ additional_backwards_reachable_positions: dict[
657
+ str, tuple[int, int]
658
+ ] = {} # event_id => (chain_id, seq_num)
659
+ # These next two fields are critical as the intersection of them is the conflicted subgraph.
660
+ # We'll populate them when we walk the chain links.
661
+ # chain_id => max(seq_num) backwards reachable (e.g 4 means 1,2,3,4 are backwards reachable)
662
+ conflicted_backwards_reachable: dict[int, int] = {}
663
+ # chain_id => min(seq_num) forwards reachable (e.g 4 means 4,5,6..n are forwards reachable)
664
+ conflicted_forwards_reachable: dict[int, int] = {}
665
+
666
+ # populate the v2.1 data structures
667
+ if is_state_res_v21:
668
+ assert conflicted_set is not None
669
+ # provide chain positions for each conflicted event
670
+ for conflicted_event_id in conflicted_set:
671
+ (chain_id, seq_num) = chain_info[conflicted_event_id]
672
+ conflicted_chain_positions[conflicted_event_id] = (chain_id, seq_num)
673
+ conflicted_chain_to_seq.setdefault(chain_id, set()).add(seq_num)
674
+ if additional_backwards_reachable_conflicted_events:
675
+ for (
676
+ additional_event_id
677
+ ) in additional_backwards_reachable_conflicted_events:
678
+ (chain_id, seq_num) = chain_info[additional_event_id]
679
+ additional_backwards_reachable_positions[additional_event_id] = (
680
+ chain_id,
681
+ seq_num,
682
+ )
683
+
684
+ # Corresponds to `state_sets`, except as a map from chain ID to max
685
+ # sequence number reachable from the state set.
686
+ set_to_chain: list[dict[int, int]] = []
687
+ for state_set in state_sets:
688
+ chains: dict[int, int] = {}
689
+ set_to_chain.append(chains)
690
+
691
+ for state_id in state_set:
692
+ chain_id, seq_no = chain_info[state_id]
693
+
694
+ chains[chain_id] = max(seq_no, chains.get(chain_id, 0))
695
+
696
+ # Now we look up all links for the chains we have, adding chains that
697
+ # are reachable from any event.
698
+
699
+ # (We need to take a copy of `seen_chains` as the function mutates it)
700
+ for links in self._get_chain_links(txn, set(seen_chains)):
701
+ # `links` encodes the backwards reachable events _from a single chain_ all the way to
702
+ # the root of the graph.
703
+ for chains in set_to_chain:
704
+ for chain_id in links:
705
+ if chain_id not in chains:
706
+ continue
707
+
708
+ _materialize(chain_id, chains[chain_id], links, chains)
709
+
710
+ seen_chains.update(chains)
711
+ if is_state_res_v21:
712
+ # Apply v2.1 conflicted event reachability checks.
713
+ #
714
+ # A <-- B <-- C <-- D <-- E
715
+ #
716
+ # Backwards reachable from C = {A,B}
717
+ # Forwards reachable from C = {D,E}
718
+
719
+ # this handles calculating forwards reachable information and updates
720
+ # conflicted_forwards_reachable.
721
+ accumulate_forwards_reachable_events(
722
+ conflicted_forwards_reachable,
723
+ links,
724
+ conflicted_chain_positions,
725
+ )
726
+
727
+ # handle backwards reachable information
728
+ for (
729
+ conflicted_chain_id,
730
+ conflicted_chain_seq,
731
+ ) in conflicted_chain_positions.values():
732
+ if conflicted_chain_id not in links:
733
+ # This conflicted event does not lie on the path to the root.
734
+ continue
735
+
736
+ # The conflicted chain position itself encodes reachability information
737
+ # _within_ the chain. Set it now before walking to other links.
738
+ conflicted_backwards_reachable[conflicted_chain_id] = max(
739
+ conflicted_chain_seq,
740
+ conflicted_backwards_reachable.get(conflicted_chain_id, 0),
741
+ )
742
+
743
+ # Build backwards reachability paths. This is the same as what the auth difference
744
+ # code does. We find which chain the conflicted event
745
+ # belongs to then walk it backwards to the root. We store reachability info
746
+ # for all conflicted events in the same map 'conflicted_backwards_reachable'
747
+ # as we don't care about the paths themselves.
748
+ _materialize(
749
+ conflicted_chain_id,
750
+ conflicted_chain_seq,
751
+ links,
752
+ conflicted_backwards_reachable,
753
+ )
754
+ # Mark some extra events as backwards reachable. This is used when we have some
755
+ # unpersisted events and want to know the subgraph across the persisted/unpersisted
756
+ # boundary:
757
+ # |
758
+ # A <-- B <-- C <-|- D <-- E <-- F
759
+ # persisted | unpersisted
760
+ #
761
+ # Assume {B,E} are conflicted, we want to return {B,C,D,E}
762
+ #
763
+ # The unpersisted code ensures it passes C as an additional backwards reachable
764
+ # event. C is NOT a conflicted event, but we do need to consider it as part of
765
+ # the backwards reachable set. When we then calculate the forwards reachable set
766
+ # from B, C will be in both the backwards and forwards reachable sets and hence
767
+ # will be included in the conflicted subgraph.
768
+ for (
769
+ additional_chain_id,
770
+ additional_chain_seq,
771
+ ) in additional_backwards_reachable_positions.values():
772
+ if additional_chain_id not in links:
773
+ # The additional backwards reachable event does not lie on the path to the root.
774
+ continue
775
+
776
+ # the additional event chain position itself encodes reachability information.
777
+ # It means that position and all positions earlier in that chain are backwards reachable
778
+ # by some unpersisted conflicted event.
779
+ conflicted_backwards_reachable[additional_chain_id] = max(
780
+ additional_chain_seq,
781
+ conflicted_backwards_reachable.get(additional_chain_id, 0),
782
+ )
783
+
784
+ # Now walk the chains back, marking backwards reachable events.
785
+ # This is the same thing we do for auth difference / conflicted events.
786
+ _materialize(
787
+ additional_chain_id, # walk all links back, marking them as backwards reachable
788
+ additional_chain_seq,
789
+ links,
790
+ conflicted_backwards_reachable,
791
+ )
792
+
793
+ # Now for each chain we figure out the maximum sequence number reachable
794
+ # from *any* state set and the minimum sequence number reachable from
795
+ # *all* state sets. Events in that range are in the auth chain
796
+ # difference.
797
+
798
+ # Mapping from chain ID to the range of sequence numbers that should be
799
+ # pulled from the database.
800
+ auth_diff_chain_to_gap: dict[int, tuple[int, int]] = {}
801
+
802
+ for chain_id in seen_chains:
803
+ min_seq_no = min(chains.get(chain_id, 0) for chains in set_to_chain)
804
+ max_seq_no = max(chains.get(chain_id, 0) for chains in set_to_chain)
805
+
806
+ if min_seq_no < max_seq_no:
807
+ # We have a non empty gap, try and fill it from the events that
808
+ # we have, otherwise add them to the list of gaps to pull out
809
+ # from the DB.
810
+ for seq_no in range(min_seq_no + 1, max_seq_no + 1):
811
+ event_id = chain_to_event.get(chain_id, {}).get(seq_no)
812
+ if event_id:
813
+ auth_difference_result.add(event_id)
814
+ else:
815
+ auth_diff_chain_to_gap[chain_id] = (min_seq_no, max_seq_no)
816
+ break
817
+
818
+ conflicted_subgraph_result: set[str] = set()
819
+ # Mapping from chain ID to the range of sequence numbers that should be
820
+ # pulled from the database.
821
+ conflicted_subgraph_chain_to_gap: dict[int, tuple[int, int]] = {}
822
+ if is_state_res_v21:
823
+ # also include the conflicted subgraph using backward/forward reachability info from all
824
+ # the conflicted events. To calculate this, we want to extract the intersection between
825
+ # the backwards and forwards reachability sets, e.g:
826
+ # A <- B <- C <- D <- E
827
+ # Assume B and D are conflicted so we want {C} as the conflicted subgraph.
828
+ # B_backwards={A}, B_forwards={C,D,E}
829
+ # D_backwards={A,B,C} D_forwards={E}
830
+ # ALL_backwards={A,B,C} ALL_forwards={C,D,E}
831
+ # Intersection(ALL_backwards, ALL_forwards) = {C}
832
+ #
833
+ # It's worth noting that once we have the ALL_ sets, we no longer care about the paths.
834
+ # We're dealing with chains and not singular events, but we've already got the ALL_ sets.
835
+ # As such, we can inspect each chain in isolation and check for overlapping sequence
836
+ # numbers:
837
+ # 1,2,3,4,5 Seq Num
838
+ # Chain N [A,B,C,D,E]
839
+ #
840
+ # if (N,4) is in the backwards set and (N,2) is in the forwards set, then the
841
+ # intersection is events between 2 < 4. We will include the conflicted events themselves
842
+ # in the subgraph, but they will already be, hence the full set of events is {B,C,D}.
843
+ for chain_id, backwards_seq_num in conflicted_backwards_reachable.items():
844
+ forwards_seq_num = conflicted_forwards_reachable.get(chain_id)
845
+ if forwards_seq_num is None:
846
+ continue # this chain isn't in both sets so can't intersect
847
+ if forwards_seq_num > backwards_seq_num:
848
+ continue # this chain is in both sets but they don't overap
849
+ for seq_no in range(
850
+ forwards_seq_num, backwards_seq_num + 1
851
+ ): # inclusive of both
852
+ event_id = chain_to_event.get(chain_id, {}).get(seq_no)
853
+ if event_id:
854
+ conflicted_subgraph_result.add(event_id)
855
+ else:
856
+ conflicted_subgraph_chain_to_gap[chain_id] = (
857
+ # _fetch_event_ids_from_chains_txn is exclusive of the min value
858
+ forwards_seq_num - 1,
859
+ backwards_seq_num,
860
+ )
861
+ break
862
+
863
+ if auth_diff_chain_to_gap:
864
+ auth_difference_result.update(
865
+ self._fetch_event_ids_from_chains_txn(txn, auth_diff_chain_to_gap)
866
+ )
867
+ if conflicted_subgraph_chain_to_gap:
868
+ conflicted_subgraph_result.update(
869
+ self._fetch_event_ids_from_chains_txn(
870
+ txn, conflicted_subgraph_chain_to_gap
871
+ )
872
+ )
873
+
874
+ return StateDifference(
875
+ auth_difference=auth_difference_result,
876
+ conflicted_subgraph=conflicted_subgraph_result,
877
+ )
878
+
879
+ def _fetch_event_ids_from_chains_txn(
880
+ self, txn: LoggingTransaction, chains: dict[int, tuple[int, int]]
881
+ ) -> set[str]:
882
+ result: set[str] = set()
883
+ if isinstance(self.database_engine, PostgresEngine):
884
+ # We can use `execute_values` to efficiently fetch the gaps when
885
+ # using postgres.
886
+ sql = """
887
+ SELECT event_id
888
+ FROM event_auth_chains AS c, (VALUES ?) AS l(chain_id, min_seq, max_seq)
889
+ WHERE
890
+ c.chain_id = l.chain_id
891
+ AND min_seq < sequence_number AND sequence_number <= max_seq
892
+ """
893
+
894
+ args = [
895
+ (chain_id, min_no, max_no)
896
+ for chain_id, (min_no, max_no) in chains.items()
897
+ ]
898
+
899
+ rows = txn.execute_values(sql, args)
900
+ result.update(r for (r,) in rows)
901
+ else:
902
+ # For SQLite we just fall back to doing a noddy for loop.
903
+ sql = """
904
+ SELECT event_id FROM event_auth_chains
905
+ WHERE chain_id = ? AND ? < sequence_number AND sequence_number <= ?
906
+ """
907
+ for chain_id, (min_no, max_no) in chains.items():
908
+ txn.execute(sql, (chain_id, min_no, max_no))
909
+ result.update(r for (r,) in txn)
910
+ return result
911
+
912
+ def _fixup_auth_chain_difference_sets(
913
+ self,
914
+ txn: LoggingTransaction,
915
+ room_id: str,
916
+ state_sets: list[set[str]],
917
+ events_missing_chain_info: set[str],
918
+ events_that_have_chain_index: Collection[str],
919
+ ) -> set[str]:
920
+ """Helper for `_get_auth_chain_difference_using_cover_index_txn` to
921
+ handle the case where we haven't calculated the chain cover index for
922
+ all events.
923
+
924
+ This modifies `state_sets` so that they only include events that have a
925
+ chain cover index, and returns a set of event IDs that are part of the
926
+ auth difference.
927
+ """
928
+
929
+ # This works similarly to the handling of unpersisted events in
930
+ # `synapse.state.v2_get_auth_chain_difference`. We uses the observation
931
+ # that if you can split the set of events into two classes X and Y,
932
+ # where no events in Y have events in X in their auth chain, then we can
933
+ # calculate the auth difference by considering X and Y separately.
934
+ #
935
+ # We do this in three steps:
936
+ # 1. Compute the set of events without chain cover index belonging to
937
+ # the auth difference.
938
+ # 2. Replacing the un-indexed events in the state_sets with their auth
939
+ # events, recursively, until the state_sets contain only indexed
940
+ # events. We can then calculate the auth difference of those state
941
+ # sets using the chain cover index.
942
+ # 3. Add the results of 1 and 2 together.
943
+
944
+ # By construction we know that all events that we haven't persisted the
945
+ # chain cover index for are contained in
946
+ # `event_auth_chain_to_calculate`, so we pull out the events from those
947
+ # rather than doing recursive queries to walk the auth chain.
948
+ #
949
+ # We pull out those events with their auth events, which gives us enough
950
+ # information to construct the auth chain of an event up to auth events
951
+ # that have the chain cover index.
952
+ sql = """
953
+ SELECT tc.event_id, ea.auth_id, eac.chain_id IS NOT NULL
954
+ FROM event_auth_chain_to_calculate AS tc
955
+ LEFT JOIN event_auth AS ea USING (event_id)
956
+ LEFT JOIN event_auth_chains AS eac ON (ea.auth_id = eac.event_id)
957
+ WHERE tc.room_id = ?
958
+ """
959
+ txn.execute(sql, (room_id,))
960
+ event_to_auth_ids: dict[str, set[str]] = {}
961
+ events_that_have_chain_index = set(events_that_have_chain_index)
962
+ for event_id, auth_id, auth_id_has_chain in txn:
963
+ s = event_to_auth_ids.setdefault(event_id, set())
964
+ if auth_id is not None:
965
+ s.add(auth_id)
966
+ if auth_id_has_chain:
967
+ events_that_have_chain_index.add(auth_id)
968
+
969
+ if events_missing_chain_info - event_to_auth_ids.keys():
970
+ # Uh oh, we somehow haven't correctly done the chain cover index,
971
+ # bail and fall back to the old method.
972
+ logger.error(
973
+ "Unexpectedly found that events don't have chain IDs in room %s: %s",
974
+ room_id,
975
+ events_missing_chain_info - event_to_auth_ids.keys(),
976
+ )
977
+ raise _NoChainCoverIndex(room_id)
978
+
979
+ # Create a map from event IDs we care about to their partial auth chain.
980
+ event_id_to_partial_auth_chain: dict[str, set[str]] = {}
981
+ for event_id, auth_ids in event_to_auth_ids.items():
982
+ if not any(event_id in state_set for state_set in state_sets):
983
+ continue
984
+
985
+ processing = set(auth_ids)
986
+ to_add = set()
987
+ while processing:
988
+ auth_id = processing.pop()
989
+ to_add.add(auth_id)
990
+
991
+ sub_auth_ids = event_to_auth_ids.get(auth_id)
992
+ if sub_auth_ids is None:
993
+ continue
994
+
995
+ processing.update(sub_auth_ids - to_add)
996
+
997
+ event_id_to_partial_auth_chain[event_id] = to_add
998
+
999
+ # Now we do two things:
1000
+ # 1. Update the state sets to only include indexed events; and
1001
+ # 2. Create a new list containing the auth chains of the un-indexed
1002
+ # events
1003
+ unindexed_state_sets: list[set[str]] = []
1004
+ for state_set in state_sets:
1005
+ unindexed_state_set = set()
1006
+ for event_id, auth_chain in event_id_to_partial_auth_chain.items():
1007
+ if event_id not in state_set:
1008
+ continue
1009
+
1010
+ unindexed_state_set.add(event_id)
1011
+
1012
+ state_set.discard(event_id)
1013
+ state_set.difference_update(auth_chain)
1014
+ for auth_id in auth_chain:
1015
+ if auth_id in events_that_have_chain_index:
1016
+ state_set.add(auth_id)
1017
+ else:
1018
+ unindexed_state_set.add(auth_id)
1019
+
1020
+ unindexed_state_sets.append(unindexed_state_set)
1021
+
1022
+ # Calculate and return the auth difference of the un-indexed events.
1023
+ union = unindexed_state_sets[0].union(*unindexed_state_sets[1:])
1024
+ intersection = unindexed_state_sets[0].intersection(*unindexed_state_sets[1:])
1025
+
1026
+ return union - intersection
1027
+
1028
+ def _get_auth_chain_difference_txn(
1029
+ self, txn: LoggingTransaction, state_sets: list[set[str]]
1030
+ ) -> set[str]:
1031
+ """Calculates the auth chain difference using a breadth first search.
1032
+
1033
+ This is used when we don't have a cover index for the room.
1034
+ """
1035
+
1036
+ # Algorithm Description
1037
+ # ~~~~~~~~~~~~~~~~~~~~~
1038
+ #
1039
+ # The idea here is to basically walk the auth graph of each state set in
1040
+ # tandem, keeping track of which auth events are reachable by each state
1041
+ # set. If we reach an auth event we've already visited (via a different
1042
+ # state set) then we mark that auth event and all ancestors as reachable
1043
+ # by the state set. This requires that we keep track of the auth chains
1044
+ # in memory.
1045
+ #
1046
+ # Doing it in a such a way means that we can stop early if all auth
1047
+ # events we're currently walking are reachable by all state sets.
1048
+ #
1049
+ # *Note*: We can't stop walking an event's auth chain if it is reachable
1050
+ # by all state sets. This is because other auth chains we're walking
1051
+ # might be reachable only via the original auth chain. For example,
1052
+ # given the following auth chain:
1053
+ #
1054
+ # A -> C -> D -> E
1055
+ # / /
1056
+ # B -´---------´
1057
+ #
1058
+ # and state sets {A} and {B} then walking the auth chains of A and B
1059
+ # would immediately show that C is reachable by both. However, if we
1060
+ # stopped at C then we'd only reach E via the auth chain of B and so E
1061
+ # would erroneously get included in the returned difference.
1062
+ #
1063
+ # The other thing that we do is limit the number of auth chains we walk
1064
+ # at once, due to practical limits (i.e. we can only query the database
1065
+ # with a limited set of parameters). We pick the auth chains we walk
1066
+ # each iteration based on their depth, in the hope that events with a
1067
+ # lower depth are likely reachable by those with higher depths.
1068
+ #
1069
+ # We could use any ordering that we believe would give a rough
1070
+ # topological ordering, e.g. origin server timestamp. If the ordering
1071
+ # chosen is not topological then the algorithm still produces the right
1072
+ # result, but perhaps a bit more inefficiently. This is why it is safe
1073
+ # to use "depth" here.
1074
+
1075
+ initial_events = set(state_sets[0]).union(*state_sets[1:])
1076
+
1077
+ # Dict from events in auth chains to which sets *cannot* reach them.
1078
+ # I.e. if the set is empty then all sets can reach the event.
1079
+ event_to_missing_sets = {
1080
+ event_id: {i for i, a in enumerate(state_sets) if event_id not in a}
1081
+ for event_id in initial_events
1082
+ }
1083
+
1084
+ # The sorted list of events whose auth chains we should walk.
1085
+ search: list[tuple[int, str]] = []
1086
+
1087
+ # We need to get the depth of the initial events for sorting purposes.
1088
+ sql = """
1089
+ SELECT depth, event_id FROM events
1090
+ WHERE %s
1091
+ """
1092
+ # the list can be huge, so let's avoid looking them all up in one massive
1093
+ # query.
1094
+ for batch in batch_iter(initial_events, 1000):
1095
+ clause, args = make_in_list_sql_clause(
1096
+ txn.database_engine, "event_id", batch
1097
+ )
1098
+ txn.execute(sql % (clause,), args)
1099
+
1100
+ # I think building a temporary list with fetchall is more efficient than
1101
+ # just `search.extend(txn)`, but this is unconfirmed
1102
+ search.extend(cast(list[tuple[int, str]], txn.fetchall()))
1103
+
1104
+ # sort by depth
1105
+ search.sort()
1106
+
1107
+ # Map from event to its auth events
1108
+ event_to_auth_events: dict[str, set[str]] = {}
1109
+
1110
+ base_sql = """
1111
+ SELECT a.event_id, auth_id, depth
1112
+ FROM event_auth AS a
1113
+ INNER JOIN events AS e ON (e.event_id = a.auth_id)
1114
+ WHERE
1115
+ """
1116
+
1117
+ while search:
1118
+ # Check whether all our current walks are reachable by all state
1119
+ # sets. If so we can bail.
1120
+ if all(not event_to_missing_sets[eid] for _, eid in search):
1121
+ break
1122
+
1123
+ # Fetch the auth events and their depths of the N last events we're
1124
+ # currently walking, either from cache or DB.
1125
+ search, chunk = search[:-100], search[-100:]
1126
+
1127
+ found: list[tuple[str, str, int]] = [] # Results found
1128
+ to_fetch: list[str] = [] # Event IDs to fetch from DB
1129
+ for _, event_id in chunk:
1130
+ res = self._event_auth_cache.get(event_id)
1131
+ if res is None:
1132
+ to_fetch.append(event_id)
1133
+ else:
1134
+ found.extend((event_id, auth_id, depth) for auth_id, depth in res)
1135
+
1136
+ if to_fetch:
1137
+ clause, args = make_in_list_sql_clause(
1138
+ txn.database_engine, "a.event_id", to_fetch
1139
+ )
1140
+ txn.execute(base_sql + clause, args)
1141
+
1142
+ # We parse the results and add the to the `found` set and the
1143
+ # cache (note we need to batch up the results by event ID before
1144
+ # adding to the cache).
1145
+ to_cache: dict[str, list[tuple[str, int]]] = {}
1146
+ for event_id, auth_event_id, auth_event_depth in txn:
1147
+ to_cache.setdefault(event_id, []).append(
1148
+ (auth_event_id, auth_event_depth)
1149
+ )
1150
+ found.append((event_id, auth_event_id, auth_event_depth))
1151
+
1152
+ for event_id, auth_events in to_cache.items():
1153
+ self._event_auth_cache.set(event_id, auth_events)
1154
+
1155
+ for event_id, auth_event_id, auth_event_depth in found:
1156
+ event_to_auth_events.setdefault(event_id, set()).add(auth_event_id)
1157
+
1158
+ sets = event_to_missing_sets.get(auth_event_id)
1159
+ if sets is None:
1160
+ # First time we're seeing this event, so we add it to the
1161
+ # queue of things to fetch.
1162
+ search.append((auth_event_depth, auth_event_id))
1163
+
1164
+ # Assume that this event is unreachable from any of the
1165
+ # state sets until proven otherwise
1166
+ sets = event_to_missing_sets[auth_event_id] = set(
1167
+ range(len(state_sets))
1168
+ )
1169
+ else:
1170
+ # We've previously seen this event, so look up its auth
1171
+ # events and recursively mark all ancestors as reachable
1172
+ # by the current event's state set.
1173
+ a_ids = event_to_auth_events.get(auth_event_id)
1174
+ while a_ids:
1175
+ new_aids = set()
1176
+ for a_id in a_ids:
1177
+ event_to_missing_sets[a_id].intersection_update(
1178
+ event_to_missing_sets[event_id]
1179
+ )
1180
+
1181
+ b = event_to_auth_events.get(a_id)
1182
+ if b:
1183
+ new_aids.update(b)
1184
+
1185
+ a_ids = new_aids
1186
+
1187
+ # Mark that the auth event is reachable by the appropriate sets.
1188
+ sets.intersection_update(event_to_missing_sets[event_id])
1189
+
1190
+ search.sort()
1191
+
1192
+ # Return all events where not all sets can reach them.
1193
+ return {eid for eid, n in event_to_missing_sets.items() if n}
1194
+
1195
+ @trace
1196
+ @tag_args
1197
+ async def get_backfill_points_in_room(
1198
+ self,
1199
+ room_id: str,
1200
+ current_depth: int,
1201
+ limit: int,
1202
+ ) -> list[tuple[str, int]]:
1203
+ """
1204
+ Get the backward extremities to backfill from in the room along with the
1205
+ approximate depth.
1206
+
1207
+ Only returns events that are at a depth lower than or
1208
+ equal to the `current_depth`. Sorted by depth, highest to lowest (descending)
1209
+ so the closest events to the `current_depth` are first in the list.
1210
+
1211
+ We ignore extremities that are newer than the user's current scroll position
1212
+ (ie, those with depth greater than `current_depth`) as:
1213
+ 1. we don't really care about getting events that have happened
1214
+ after our current position; and
1215
+ 2. by the nature of paginating and scrolling back, we have likely
1216
+ previously tried and failed to backfill from that extremity, so
1217
+ to avoid getting "stuck" requesting the same backfill repeatedly
1218
+ we drop those extremities.
1219
+
1220
+ Args:
1221
+ room_id: Room where we want to find the oldest events
1222
+ current_depth: The depth at the user's current scrollback position
1223
+ limit: The max number of backfill points to return
1224
+
1225
+ Returns:
1226
+ List of (event_id, depth) tuples. Sorted by depth, highest to lowest
1227
+ (descending) so the closest events to the `current_depth` are first
1228
+ in the list.
1229
+ """
1230
+
1231
+ def get_backfill_points_in_room_txn(
1232
+ txn: LoggingTransaction, room_id: str
1233
+ ) -> list[tuple[str, int]]:
1234
+ # Assemble a tuple lookup of event_id -> depth for the oldest events
1235
+ # we know of in the room. Backwards extremeties are the oldest
1236
+ # events we know of in the room but we only know of them because
1237
+ # some other event referenced them by prev_event and aren't
1238
+ # persisted in our database yet (meaning we don't know their depth
1239
+ # specifically). So we need to look for the approximate depth from
1240
+ # the events connected to the current backwards extremeties.
1241
+
1242
+ if isinstance(self.database_engine, PostgresEngine):
1243
+ least_function = "LEAST"
1244
+ elif isinstance(self.database_engine, Sqlite3Engine):
1245
+ least_function = "MIN"
1246
+ else:
1247
+ raise RuntimeError("Unknown database engine")
1248
+
1249
+ sql = f"""
1250
+ SELECT backward_extrem.event_id, event.depth FROM events AS event
1251
+ /**
1252
+ * Get the edge connections from the event_edges table
1253
+ * so we can see whether this event's prev_events points
1254
+ * to a backward extremity in the next join.
1255
+ */
1256
+ INNER JOIN event_edges AS edge
1257
+ ON edge.event_id = event.event_id
1258
+ /**
1259
+ * We find the "oldest" events in the room by looking for
1260
+ * events connected to backwards extremeties (oldest events
1261
+ * in the room that we know of so far).
1262
+ */
1263
+ INNER JOIN event_backward_extremities AS backward_extrem
1264
+ ON edge.prev_event_id = backward_extrem.event_id
1265
+ /**
1266
+ * We use this info to make sure we don't retry to use a backfill point
1267
+ * if we've already attempted to backfill from it recently.
1268
+ */
1269
+ LEFT JOIN event_failed_pull_attempts AS failed_backfill_attempt_info
1270
+ ON
1271
+ failed_backfill_attempt_info.room_id = backward_extrem.room_id
1272
+ AND failed_backfill_attempt_info.event_id = backward_extrem.event_id
1273
+ WHERE
1274
+ backward_extrem.room_id = ?
1275
+ /* We only care about non-state edges because we used to use
1276
+ * `event_edges` for two different sorts of "edges" (the current
1277
+ * event DAG, but also a link to the previous state, for state
1278
+ * events). These legacy state event edges can be distinguished by
1279
+ * `is_state` and are removed from the codebase and schema but
1280
+ * because the schema change is in a background update, it's not
1281
+ * necessarily safe to assume that it will have been completed.
1282
+ */
1283
+ AND edge.is_state is FALSE
1284
+ /**
1285
+ * We only want backwards extremities that are older than or at
1286
+ * the same position of the given `current_depth` (where older
1287
+ * means less than the given depth) because we're looking backwards
1288
+ * from the `current_depth` when backfilling.
1289
+ *
1290
+ * current_depth (ignore events that come after this, ignore 2-4)
1291
+ * |
1292
+ * ▼
1293
+ * <oldest-in-time> [0]<--[1]<--[2]<--[3]<--[4] <newest-in-time>
1294
+ */
1295
+ AND event.depth <= ? /* current_depth */
1296
+ /**
1297
+ * Exponential back-off (up to the upper bound) so we don't retry the
1298
+ * same backfill point over and over. ex. 2hr, 4hr, 8hr, 16hr, etc.
1299
+ *
1300
+ * We use `1 << n` as a power of 2 equivalent for compatibility
1301
+ * with older SQLites. The left shift equivalent only works with
1302
+ * powers of 2 because left shift is a binary operation (base-2).
1303
+ * Otherwise, we would use `power(2, n)` or the power operator, `2^n`.
1304
+ */
1305
+ AND (
1306
+ failed_backfill_attempt_info.event_id IS NULL
1307
+ OR ? /* current_time */ >= failed_backfill_attempt_info.last_attempt_ts + (
1308
+ (1 << {least_function}(failed_backfill_attempt_info.num_attempts, ? /* max doubling steps */))
1309
+ * ? /* step */
1310
+ )
1311
+ )
1312
+ /**
1313
+ * Sort from highest (closest to the `current_depth`) to the lowest depth
1314
+ * because the closest are most relevant to backfill from first.
1315
+ * Then tie-break on alphabetical order of the event_ids so we get a
1316
+ * consistent ordering which is nice when asserting things in tests.
1317
+ */
1318
+ ORDER BY event.depth DESC, backward_extrem.event_id DESC
1319
+ LIMIT ?
1320
+ """
1321
+
1322
+ txn.execute(
1323
+ sql,
1324
+ (
1325
+ room_id,
1326
+ current_depth,
1327
+ self.clock.time_msec(),
1328
+ BACKFILL_EVENT_EXPONENTIAL_BACKOFF_MAXIMUM_DOUBLING_STEPS,
1329
+ BACKFILL_EVENT_EXPONENTIAL_BACKOFF_STEP_MILLISECONDS,
1330
+ limit,
1331
+ ),
1332
+ )
1333
+
1334
+ return cast(list[tuple[str, int]], txn.fetchall())
1335
+
1336
+ return await self.db_pool.runInteraction(
1337
+ "get_backfill_points_in_room",
1338
+ get_backfill_points_in_room_txn,
1339
+ room_id,
1340
+ )
1341
+
1342
+ async def get_max_depth_of(
1343
+ self, event_ids: Collection[str]
1344
+ ) -> tuple[Optional[str], int]:
1345
+ """Returns the event ID and depth for the event that has the max depth from a set of event IDs
1346
+
1347
+ Args:
1348
+ event_ids: The event IDs to calculate the max depth of.
1349
+ """
1350
+ rows = cast(
1351
+ list[tuple[str, int]],
1352
+ await self.db_pool.simple_select_many_batch(
1353
+ table="events",
1354
+ column="event_id",
1355
+ iterable=event_ids,
1356
+ retcols=(
1357
+ "event_id",
1358
+ "depth",
1359
+ ),
1360
+ desc="get_max_depth_of",
1361
+ ),
1362
+ )
1363
+
1364
+ if not rows:
1365
+ return None, 0
1366
+ else:
1367
+ max_depth_event_id = ""
1368
+ current_max_depth = 0
1369
+ for event_id, depth in rows:
1370
+ if depth > current_max_depth:
1371
+ max_depth_event_id = event_id
1372
+ current_max_depth = depth
1373
+
1374
+ return max_depth_event_id, current_max_depth
1375
+
1376
+ async def get_min_depth_of(self, event_ids: list[str]) -> tuple[Optional[str], int]:
1377
+ """Returns the event ID and depth for the event that has the min depth from a set of event IDs
1378
+
1379
+ Args:
1380
+ event_ids: The event IDs to calculate the max depth of.
1381
+ """
1382
+ rows = cast(
1383
+ list[tuple[str, int]],
1384
+ await self.db_pool.simple_select_many_batch(
1385
+ table="events",
1386
+ column="event_id",
1387
+ iterable=event_ids,
1388
+ retcols=(
1389
+ "event_id",
1390
+ "depth",
1391
+ ),
1392
+ desc="get_min_depth_of",
1393
+ ),
1394
+ )
1395
+
1396
+ if not rows:
1397
+ return None, 0
1398
+ else:
1399
+ min_depth_event_id = ""
1400
+ current_min_depth = MAX_DEPTH
1401
+ for event_id, depth in rows:
1402
+ if depth < current_min_depth:
1403
+ min_depth_event_id = event_id
1404
+ current_min_depth = depth
1405
+
1406
+ return min_depth_event_id, current_min_depth
1407
+
1408
+ async def get_prev_events_for_room(self, room_id: str) -> list[str]:
1409
+ """
1410
+ Gets a subset of the current forward extremities in the given room.
1411
+
1412
+ Limits the result to 10 extremities, so that we can avoid creating
1413
+ events which refer to hundreds of prev_events.
1414
+
1415
+ Args:
1416
+ room_id: room_id
1417
+
1418
+ Returns:
1419
+ The event ids of the forward extremities.
1420
+
1421
+ """
1422
+
1423
+ return await self.db_pool.runInteraction(
1424
+ "get_prev_events_for_room", self._get_prev_events_for_room_txn, room_id
1425
+ )
1426
+
1427
+ def _get_prev_events_for_room_txn(
1428
+ self, txn: LoggingTransaction, room_id: str
1429
+ ) -> list[str]:
1430
+ # we just use the 10 newest events. Older events will become
1431
+ # prev_events of future events.
1432
+
1433
+ sql = """
1434
+ SELECT e.event_id FROM event_forward_extremities AS f
1435
+ INNER JOIN events AS e USING (event_id)
1436
+ WHERE f.room_id = ?
1437
+ ORDER BY e.depth DESC
1438
+ LIMIT 10
1439
+ """
1440
+
1441
+ txn.execute(sql, (room_id,))
1442
+
1443
+ return [row[0] for row in txn]
1444
+
1445
+ async def get_rooms_with_many_extremities(
1446
+ self, min_count: int, limit: int, room_id_filter: Iterable[str]
1447
+ ) -> list[str]:
1448
+ """Get the top rooms with at least N extremities.
1449
+
1450
+ Args:
1451
+ min_count: The minimum number of extremities
1452
+ limit: The maximum number of rooms to return.
1453
+ room_id_filter: room_ids to exclude from the results
1454
+
1455
+ Returns:
1456
+ At most `limit` room IDs that have at least `min_count` extremities,
1457
+ sorted by extremity count.
1458
+ """
1459
+
1460
+ def _get_rooms_with_many_extremities_txn(txn: LoggingTransaction) -> list[str]:
1461
+ where_clause = "1=1"
1462
+ if room_id_filter:
1463
+ where_clause = "room_id NOT IN (%s)" % (
1464
+ ",".join("?" for _ in room_id_filter),
1465
+ )
1466
+
1467
+ sql = """
1468
+ SELECT room_id FROM event_forward_extremities
1469
+ WHERE %s
1470
+ GROUP BY room_id
1471
+ HAVING count(*) > ?
1472
+ ORDER BY count(*) DESC
1473
+ LIMIT ?
1474
+ """ % (where_clause,)
1475
+
1476
+ query_args = list(itertools.chain(room_id_filter, [min_count, limit]))
1477
+ txn.execute(sql, query_args)
1478
+ return [room_id for (room_id,) in txn]
1479
+
1480
+ return await self.db_pool.runInteraction(
1481
+ "get_rooms_with_many_extremities", _get_rooms_with_many_extremities_txn
1482
+ )
1483
+
1484
+ @cached(max_entries=5000, iterable=True)
1485
+ async def get_latest_event_ids_in_room(self, room_id: str) -> frozenset[str]:
1486
+ event_ids = await self.db_pool.simple_select_onecol(
1487
+ table="event_forward_extremities",
1488
+ keyvalues={"room_id": room_id},
1489
+ retcol="event_id",
1490
+ desc="get_latest_event_ids_in_room",
1491
+ )
1492
+ return frozenset(event_ids)
1493
+
1494
+ async def get_min_depth(self, room_id: str) -> Optional[int]:
1495
+ """For the given room, get the minimum depth we have seen for it."""
1496
+ return await self.db_pool.runInteraction(
1497
+ "get_min_depth", self._get_min_depth_interaction, room_id
1498
+ )
1499
+
1500
+ def _get_min_depth_interaction(
1501
+ self, txn: LoggingTransaction, room_id: str
1502
+ ) -> Optional[int]:
1503
+ min_depth = self.db_pool.simple_select_one_onecol_txn(
1504
+ txn,
1505
+ table="room_depth",
1506
+ keyvalues={"room_id": room_id},
1507
+ retcol="min_depth",
1508
+ allow_none=True,
1509
+ )
1510
+
1511
+ return int(min_depth) if min_depth is not None else None
1512
+
1513
+ async def have_room_forward_extremities_changed_since(
1514
+ self,
1515
+ room_id: str,
1516
+ stream_ordering: int,
1517
+ ) -> bool:
1518
+ """Check if the forward extremities in a room have changed since the
1519
+ given stream ordering
1520
+
1521
+ Throws a StoreError if we have since purged the index for
1522
+ stream_orderings from that point.
1523
+ """
1524
+ assert self.stream_ordering_month_ago is not None
1525
+ if stream_ordering <= self.stream_ordering_month_ago:
1526
+ raise StoreError(400, f"stream_ordering too old {stream_ordering}")
1527
+
1528
+ sql = """
1529
+ SELECT 1 FROM stream_ordering_to_exterm
1530
+ WHERE stream_ordering > ? AND room_id = ?
1531
+ LIMIT 1
1532
+ """
1533
+
1534
+ def have_room_forward_extremities_changed_since_txn(
1535
+ txn: LoggingTransaction,
1536
+ ) -> bool:
1537
+ txn.execute(sql, (stream_ordering, room_id))
1538
+ return txn.fetchone() is not None
1539
+
1540
+ return await self.db_pool.runInteraction(
1541
+ "have_room_forward_extremities_changed_since",
1542
+ have_room_forward_extremities_changed_since_txn,
1543
+ )
1544
+
1545
+ @cancellable
1546
+ async def get_forward_extremities_for_room_at_stream_ordering(
1547
+ self, room_id: str, stream_ordering: int
1548
+ ) -> Sequence[str]:
1549
+ """For a given room_id and stream_ordering, return the forward
1550
+ extremeties of the room at that point in "time".
1551
+
1552
+ Throws a StoreError if we have since purged the index for
1553
+ stream_orderings from that point.
1554
+
1555
+ Args:
1556
+ room_id:
1557
+ stream_ordering:
1558
+
1559
+ Returns:
1560
+ A list of event_ids
1561
+ """
1562
+ # We want to make the cache more effective, so we clamp to the last
1563
+ # change before the given ordering.
1564
+ last_change = self._events_stream_cache.get_max_pos_of_last_change(room_id) # type: ignore[attr-defined]
1565
+ if last_change is None:
1566
+ # If the room isn't in the cache we know that the last change was
1567
+ # somewhere before the earliest known position of the cache, so we
1568
+ # can clamp to that.
1569
+ last_change = self._events_stream_cache.get_earliest_known_position() # type: ignore[attr-defined]
1570
+
1571
+ # We don't always have a full stream_to_exterm_id table, e.g. after
1572
+ # the upgrade that introduced it, so we make sure we never ask for a
1573
+ # stream_ordering from before a restart
1574
+ last_change = max(self._stream_order_on_start, last_change) # type: ignore[attr-defined]
1575
+
1576
+ # provided the last_change is recent enough, we now clamp the requested
1577
+ # stream_ordering to it.
1578
+ assert self.stream_ordering_month_ago is not None
1579
+ if last_change > self.stream_ordering_month_ago:
1580
+ stream_ordering = min(last_change, stream_ordering)
1581
+
1582
+ return await self._get_forward_extremeties_for_room(room_id, stream_ordering)
1583
+
1584
+ @cached(max_entries=5000, num_args=2)
1585
+ async def _get_forward_extremeties_for_room(
1586
+ self, room_id: str, stream_ordering: int
1587
+ ) -> Sequence[str]:
1588
+ """For a given room_id and stream_ordering, return the forward
1589
+ extremeties of the room at that point in "time".
1590
+
1591
+ Throws a StoreError if we have since purged the index for
1592
+ stream_orderings from that point.
1593
+ """
1594
+ assert self.stream_ordering_month_ago is not None
1595
+ if stream_ordering <= self.stream_ordering_month_ago:
1596
+ raise StoreError(400, "stream_ordering too old %s" % (stream_ordering,))
1597
+
1598
+ sql = """
1599
+ SELECT event_id FROM stream_ordering_to_exterm
1600
+ INNER JOIN (
1601
+ SELECT room_id, MAX(stream_ordering) AS stream_ordering
1602
+ FROM stream_ordering_to_exterm
1603
+ WHERE stream_ordering <= ? GROUP BY room_id
1604
+ ) AS rms USING (room_id, stream_ordering)
1605
+ WHERE room_id = ?
1606
+ """
1607
+
1608
+ def get_forward_extremeties_for_room_txn(txn: LoggingTransaction) -> list[str]:
1609
+ txn.execute(sql, (stream_ordering, room_id))
1610
+ return [event_id for (event_id,) in txn]
1611
+
1612
+ event_ids = await self.db_pool.runInteraction(
1613
+ "get_forward_extremeties_for_room", get_forward_extremeties_for_room_txn
1614
+ )
1615
+
1616
+ # If we didn't find any IDs, then we must have cleared out the
1617
+ # associated `stream_ordering_to_exterm`.
1618
+ if not event_ids:
1619
+ raise StoreError(400, "stream_ordering too old %s" % (stream_ordering,))
1620
+
1621
+ return event_ids
1622
+
1623
+ def _get_connected_prev_event_backfill_results_txn(
1624
+ self, txn: LoggingTransaction, event_id: str, limit: int
1625
+ ) -> list[BackfillQueueNavigationItem]:
1626
+ """
1627
+ Find any events connected by prev_event the specified event_id.
1628
+
1629
+ Args:
1630
+ txn: The database transaction to use
1631
+ event_id: The event ID to navigate from
1632
+ limit: Max number of event ID's to query for and return
1633
+
1634
+ Returns:
1635
+ List of prev events that the backfill queue can process
1636
+ """
1637
+ # Look for the prev_event_id connected to the given event_id
1638
+ connected_prev_event_query = """
1639
+ SELECT depth, stream_ordering, prev_event_id, events.type FROM event_edges
1640
+ /* Get the depth and stream_ordering of the prev_event_id from the events table */
1641
+ INNER JOIN events
1642
+ ON prev_event_id = events.event_id
1643
+
1644
+ /* exclude outliers from the results (we don't have the state, so cannot
1645
+ * verify if the requesting server can see them).
1646
+ */
1647
+ WHERE NOT events.outlier
1648
+
1649
+ /* Look for an edge which matches the given event_id */
1650
+ AND event_edges.event_id = ? AND NOT event_edges.is_state
1651
+
1652
+ /* Because we can have many events at the same depth,
1653
+ * we want to also tie-break and sort on stream_ordering */
1654
+ ORDER BY depth DESC, stream_ordering DESC
1655
+ LIMIT ?
1656
+ """
1657
+
1658
+ txn.execute(
1659
+ connected_prev_event_query,
1660
+ (event_id, limit),
1661
+ )
1662
+ return [
1663
+ BackfillQueueNavigationItem(
1664
+ depth=row[0],
1665
+ stream_ordering=row[1],
1666
+ event_id=row[2],
1667
+ type=row[3],
1668
+ )
1669
+ for row in txn
1670
+ ]
1671
+
1672
+ async def get_backfill_events(
1673
+ self, room_id: str, seed_event_id_list: list[str], limit: int
1674
+ ) -> list[EventBase]:
1675
+ """Get a list of Events for a given topic that occurred before (and
1676
+ including) the events in seed_event_id_list. Return a list of max size `limit`
1677
+
1678
+ Args:
1679
+ room_id
1680
+ seed_event_id_list
1681
+ limit
1682
+ """
1683
+ event_ids = await self.db_pool.runInteraction(
1684
+ "get_backfill_events",
1685
+ self._get_backfill_events,
1686
+ room_id,
1687
+ seed_event_id_list,
1688
+ limit,
1689
+ )
1690
+ events = await self.get_events_as_list(event_ids)
1691
+ return sorted(
1692
+ # type-ignore: mypy doesn't like negating the Optional[int] stream_ordering.
1693
+ # But it's never None, because these events were previously persisted to the DB.
1694
+ events,
1695
+ key=lambda e: (-e.depth, -e.internal_metadata.stream_ordering), # type: ignore[operator]
1696
+ )
1697
+
1698
+ def _get_backfill_events(
1699
+ self,
1700
+ txn: LoggingTransaction,
1701
+ room_id: str,
1702
+ seed_event_id_list: list[str],
1703
+ limit: int,
1704
+ ) -> set[str]:
1705
+ """
1706
+ We want to make sure that we do a breadth-first, "depth" ordered search.
1707
+ We also handle navigating historical branches of history connected by
1708
+ insertion and batch events.
1709
+ """
1710
+ logger.debug(
1711
+ "_get_backfill_events(room_id=%s): seeding backfill with seed_event_id_list=%s limit=%s",
1712
+ room_id,
1713
+ seed_event_id_list,
1714
+ limit,
1715
+ )
1716
+
1717
+ event_id_results: set[str] = set()
1718
+
1719
+ # In a PriorityQueue, the lowest valued entries are retrieved first.
1720
+ # We're using depth as the priority in the queue and tie-break based on
1721
+ # stream_ordering. Depth is lowest at the oldest-in-time message and
1722
+ # highest and newest-in-time message. We add events to the queue with a
1723
+ # negative depth so that we process the newest-in-time messages first
1724
+ # going backwards in time. stream_ordering follows the same pattern.
1725
+ queue: "PriorityQueue[tuple[int, int, str, str]]" = PriorityQueue()
1726
+
1727
+ for seed_event_id in seed_event_id_list:
1728
+ event_lookup_result = self.db_pool.simple_select_one_txn(
1729
+ txn,
1730
+ table="events",
1731
+ keyvalues={"event_id": seed_event_id, "room_id": room_id},
1732
+ retcols=(
1733
+ "type",
1734
+ "depth",
1735
+ "stream_ordering",
1736
+ ),
1737
+ allow_none=True,
1738
+ )
1739
+
1740
+ if event_lookup_result is not None:
1741
+ event_type, depth, stream_ordering = event_lookup_result
1742
+ logger.debug(
1743
+ "_get_backfill_events(room_id=%s): seed_event_id=%s depth=%s stream_ordering=%s type=%s",
1744
+ room_id,
1745
+ seed_event_id,
1746
+ depth,
1747
+ stream_ordering,
1748
+ event_type,
1749
+ )
1750
+
1751
+ if depth:
1752
+ queue.put((-depth, -stream_ordering, seed_event_id, event_type))
1753
+
1754
+ while not queue.empty() and len(event_id_results) < limit:
1755
+ try:
1756
+ _, _, event_id, event_type = queue.get_nowait()
1757
+ except Empty:
1758
+ break
1759
+
1760
+ if event_id in event_id_results:
1761
+ continue
1762
+
1763
+ event_id_results.add(event_id)
1764
+
1765
+ # Now we just look up the DAG by prev_events as normal
1766
+ connected_prev_event_backfill_results = (
1767
+ self._get_connected_prev_event_backfill_results_txn(
1768
+ txn, event_id, limit - len(event_id_results)
1769
+ )
1770
+ )
1771
+ logger.debug(
1772
+ "_get_backfill_events(room_id=%s): connected_prev_event_backfill_results=%s",
1773
+ room_id,
1774
+ connected_prev_event_backfill_results,
1775
+ )
1776
+ for (
1777
+ connected_prev_event_backfill_item
1778
+ ) in connected_prev_event_backfill_results:
1779
+ if connected_prev_event_backfill_item.event_id not in event_id_results:
1780
+ queue.put(
1781
+ (
1782
+ -connected_prev_event_backfill_item.depth,
1783
+ -connected_prev_event_backfill_item.stream_ordering,
1784
+ connected_prev_event_backfill_item.event_id,
1785
+ connected_prev_event_backfill_item.type,
1786
+ )
1787
+ )
1788
+
1789
+ return event_id_results
1790
+
1791
+ @trace
1792
+ async def record_event_failed_pull_attempt(
1793
+ self, room_id: str, event_id: str, cause: str
1794
+ ) -> None:
1795
+ """
1796
+ Record when we fail to pull an event over federation.
1797
+
1798
+ This information allows us to be more intelligent when we decide to
1799
+ retry (we don't need to fail over and over) and we can process that
1800
+ event in the background so we don't block on it each time.
1801
+
1802
+ Args:
1803
+ room_id: The room where the event failed to pull from
1804
+ event_id: The event that failed to be fetched or processed
1805
+ cause: The error message or reason that we failed to pull the event
1806
+ """
1807
+ logger.debug(
1808
+ "record_event_failed_pull_attempt room_id=%s, event_id=%s, cause=%s",
1809
+ room_id,
1810
+ event_id,
1811
+ cause,
1812
+ )
1813
+ await self.db_pool.runInteraction(
1814
+ "record_event_failed_pull_attempt",
1815
+ self._record_event_failed_pull_attempt_upsert_txn,
1816
+ room_id,
1817
+ event_id,
1818
+ cause,
1819
+ db_autocommit=True, # Safe as it's a single upsert
1820
+ )
1821
+
1822
+ def _record_event_failed_pull_attempt_upsert_txn(
1823
+ self,
1824
+ txn: LoggingTransaction,
1825
+ room_id: str,
1826
+ event_id: str,
1827
+ cause: str,
1828
+ ) -> None:
1829
+ sql = """
1830
+ INSERT INTO event_failed_pull_attempts (
1831
+ room_id, event_id, num_attempts, last_attempt_ts, last_cause
1832
+ )
1833
+ VALUES (?, ?, ?, ?, ?)
1834
+ ON CONFLICT (room_id, event_id) DO UPDATE SET
1835
+ num_attempts=event_failed_pull_attempts.num_attempts + 1,
1836
+ last_attempt_ts=EXCLUDED.last_attempt_ts,
1837
+ last_cause=EXCLUDED.last_cause;
1838
+ """
1839
+
1840
+ txn.execute(sql, (room_id, event_id, 1, self.clock.time_msec(), cause))
1841
+
1842
+ @trace
1843
+ async def get_event_ids_with_failed_pull_attempts(
1844
+ self, event_ids: StrCollection
1845
+ ) -> set[str]:
1846
+ """
1847
+ Filter the given list of `event_ids` and return events which have any failed
1848
+ pull attempts.
1849
+
1850
+ Args:
1851
+ event_ids: A list of events to filter down.
1852
+
1853
+ Returns:
1854
+ A filtered down list of `event_ids` that have previous failed pull attempts.
1855
+ """
1856
+
1857
+ rows = cast(
1858
+ list[tuple[str]],
1859
+ await self.db_pool.simple_select_many_batch(
1860
+ table="event_failed_pull_attempts",
1861
+ column="event_id",
1862
+ iterable=event_ids,
1863
+ keyvalues={},
1864
+ retcols=("event_id",),
1865
+ desc="get_event_ids_with_failed_pull_attempts",
1866
+ ),
1867
+ )
1868
+ return {row[0] for row in rows}
1869
+
1870
+ @trace
1871
+ async def get_event_ids_to_not_pull_from_backoff(
1872
+ self,
1873
+ room_id: str,
1874
+ event_ids: Collection[str],
1875
+ ) -> dict[str, int]:
1876
+ """
1877
+ Filter down the events to ones that we've failed to pull before recently. Uses
1878
+ exponential backoff.
1879
+
1880
+ Args:
1881
+ room_id: The room that the events belong to
1882
+ event_ids: A list of events to filter down
1883
+
1884
+ Returns:
1885
+ A dictionary of event_ids that should not be attempted to be pulled and the
1886
+ next timestamp at which we may try pulling them again.
1887
+ """
1888
+ event_failed_pull_attempts = cast(
1889
+ list[tuple[str, int, int]],
1890
+ await self.db_pool.simple_select_many_batch(
1891
+ table="event_failed_pull_attempts",
1892
+ column="event_id",
1893
+ iterable=event_ids,
1894
+ keyvalues={},
1895
+ retcols=(
1896
+ "event_id",
1897
+ "last_attempt_ts",
1898
+ "num_attempts",
1899
+ ),
1900
+ desc="get_event_ids_to_not_pull_from_backoff",
1901
+ ),
1902
+ )
1903
+
1904
+ current_time = self.clock.time_msec()
1905
+
1906
+ event_ids_with_backoff = {}
1907
+ for event_id, last_attempt_ts, num_attempts in event_failed_pull_attempts:
1908
+ # Exponential back-off (up to the upper bound) so we don't try to
1909
+ # pull the same event over and over. ex. 2hr, 4hr, 8hr, 16hr, etc.
1910
+ backoff_end_time = (
1911
+ last_attempt_ts
1912
+ + (
1913
+ 2
1914
+ ** min(
1915
+ num_attempts,
1916
+ BACKFILL_EVENT_EXPONENTIAL_BACKOFF_MAXIMUM_DOUBLING_STEPS,
1917
+ )
1918
+ )
1919
+ * BACKFILL_EVENT_EXPONENTIAL_BACKOFF_STEP_MILLISECONDS
1920
+ )
1921
+
1922
+ if current_time < backoff_end_time: # `backoff_end_time` is exclusive
1923
+ event_ids_with_backoff[event_id] = backoff_end_time
1924
+
1925
+ return event_ids_with_backoff
1926
+
1927
+ async def get_missing_events(
1928
+ self,
1929
+ room_id: str,
1930
+ earliest_events: list[str],
1931
+ latest_events: list[str],
1932
+ limit: int,
1933
+ ) -> list[EventBase]:
1934
+ ids = await self.db_pool.runInteraction(
1935
+ "get_missing_events",
1936
+ self._get_missing_events,
1937
+ room_id,
1938
+ earliest_events,
1939
+ latest_events,
1940
+ limit,
1941
+ )
1942
+ return await self.get_events_as_list(ids)
1943
+
1944
+ def _get_missing_events(
1945
+ self,
1946
+ txn: LoggingTransaction,
1947
+ room_id: str,
1948
+ earliest_events: list[str],
1949
+ latest_events: list[str],
1950
+ limit: int,
1951
+ ) -> list[str]:
1952
+ seen_events = set(earliest_events)
1953
+ front = set(latest_events) - seen_events
1954
+ event_results: list[str] = []
1955
+
1956
+ query = (
1957
+ "SELECT prev_event_id FROM event_edges "
1958
+ "WHERE event_id = ? AND NOT is_state "
1959
+ "LIMIT ?"
1960
+ )
1961
+
1962
+ while front and len(event_results) < limit:
1963
+ new_front = set()
1964
+ for event_id in front:
1965
+ txn.execute(query, (event_id, limit - len(event_results)))
1966
+ new_results = {t[0] for t in txn} - seen_events
1967
+
1968
+ new_front |= new_results
1969
+ seen_events |= new_results
1970
+ event_results.extend(new_results)
1971
+
1972
+ front = new_front
1973
+
1974
+ # we built the list working backwards from latest_events; we now need to
1975
+ # reverse it so that the events are approximately chronological.
1976
+ event_results.reverse()
1977
+ return event_results
1978
+
1979
+ @trace
1980
+ @tag_args
1981
+ async def get_successor_events(self, event_id: str) -> list[str]:
1982
+ """Fetch all events that have the given event as a prev event
1983
+
1984
+ Args:
1985
+ event_id: The event to search for as a prev_event.
1986
+ """
1987
+ return await self.db_pool.simple_select_onecol(
1988
+ table="event_edges",
1989
+ keyvalues={"prev_event_id": event_id},
1990
+ retcol="event_id",
1991
+ desc="get_successor_events",
1992
+ )
1993
+
1994
+ @wrap_as_background_process("delete_old_forward_extrem_cache")
1995
+ async def _delete_old_forward_extrem_cache(self) -> None:
1996
+ def _delete_old_forward_extrem_cache_txn(txn: LoggingTransaction) -> None:
1997
+ sql = """
1998
+ DELETE FROM stream_ordering_to_exterm
1999
+ WHERE stream_ordering < ?
2000
+ """
2001
+ txn.execute(sql, (self.stream_ordering_month_ago,))
2002
+
2003
+ await self.db_pool.runInteraction(
2004
+ "_delete_old_forward_extrem_cache",
2005
+ _delete_old_forward_extrem_cache_txn,
2006
+ )
2007
+
2008
+ async def insert_received_event_to_staging(
2009
+ self, origin: str, event: EventBase
2010
+ ) -> None:
2011
+ """Insert a newly received event from federation into the staging area."""
2012
+
2013
+ # We use an upsert here to handle the case where we see the same event
2014
+ # from the same server multiple times.
2015
+ await self.db_pool.simple_upsert(
2016
+ table="federation_inbound_events_staging",
2017
+ keyvalues={
2018
+ "origin": origin,
2019
+ "event_id": event.event_id,
2020
+ },
2021
+ values={},
2022
+ insertion_values={
2023
+ "room_id": event.room_id,
2024
+ "received_ts": self.clock.time_msec(),
2025
+ "event_json": json_encoder.encode(event.get_dict()),
2026
+ "internal_metadata": json_encoder.encode(
2027
+ event.internal_metadata.get_dict()
2028
+ ),
2029
+ },
2030
+ desc="insert_received_event_to_staging",
2031
+ )
2032
+
2033
+ async def remove_received_event_from_staging(
2034
+ self,
2035
+ origin: str,
2036
+ event_id: str,
2037
+ ) -> Optional[int]:
2038
+ """Remove the given event from the staging area.
2039
+
2040
+ Returns:
2041
+ The received_ts of the row that was deleted, if any.
2042
+ """
2043
+
2044
+ def _remove_received_event_from_staging_txn(
2045
+ txn: LoggingTransaction,
2046
+ ) -> Optional[int]:
2047
+ sql = """
2048
+ DELETE FROM federation_inbound_events_staging
2049
+ WHERE origin = ? AND event_id = ?
2050
+ RETURNING received_ts
2051
+ """
2052
+
2053
+ txn.execute(sql, (origin, event_id))
2054
+ row = cast(Optional[tuple[int]], txn.fetchone())
2055
+
2056
+ if row is None:
2057
+ return None
2058
+
2059
+ return row[0]
2060
+
2061
+ return await self.db_pool.runInteraction(
2062
+ "remove_received_event_from_staging",
2063
+ _remove_received_event_from_staging_txn,
2064
+ db_autocommit=True,
2065
+ )
2066
+
2067
+ async def get_next_staged_event_id_for_room(
2068
+ self,
2069
+ room_id: str,
2070
+ ) -> Optional[tuple[str, str]]:
2071
+ """
2072
+ Get the next event ID in the staging area for the given room.
2073
+
2074
+ Returns:
2075
+ Tuple of the `origin` and `event_id`
2076
+ """
2077
+
2078
+ def _get_next_staged_event_id_for_room_txn(
2079
+ txn: LoggingTransaction,
2080
+ ) -> Optional[tuple[str, str]]:
2081
+ sql = """
2082
+ SELECT origin, event_id
2083
+ FROM federation_inbound_events_staging
2084
+ WHERE room_id = ?
2085
+ ORDER BY received_ts ASC
2086
+ LIMIT 1
2087
+ """
2088
+
2089
+ txn.execute(sql, (room_id,))
2090
+
2091
+ return cast(Optional[tuple[str, str]], txn.fetchone())
2092
+
2093
+ return await self.db_pool.runInteraction(
2094
+ "get_next_staged_event_id_for_room", _get_next_staged_event_id_for_room_txn
2095
+ )
2096
+
2097
+ async def get_next_staged_event_for_room(
2098
+ self,
2099
+ room_id: str,
2100
+ room_version: RoomVersion,
2101
+ ) -> Optional[tuple[str, EventBase]]:
2102
+ """Get the next event in the staging area for the given room."""
2103
+
2104
+ def _get_next_staged_event_for_room_txn(
2105
+ txn: LoggingTransaction,
2106
+ ) -> Optional[tuple[str, str, str]]:
2107
+ sql = """
2108
+ SELECT event_json, internal_metadata, origin
2109
+ FROM federation_inbound_events_staging
2110
+ WHERE room_id = ?
2111
+ ORDER BY received_ts ASC
2112
+ LIMIT 1
2113
+ """
2114
+ txn.execute(sql, (room_id,))
2115
+
2116
+ return cast(Optional[tuple[str, str, str]], txn.fetchone())
2117
+
2118
+ row = await self.db_pool.runInteraction(
2119
+ "get_next_staged_event_for_room", _get_next_staged_event_for_room_txn
2120
+ )
2121
+
2122
+ if not row:
2123
+ return None
2124
+
2125
+ event_d = db_to_json(row[0])
2126
+ internal_metadata_d = db_to_json(row[1])
2127
+ origin = row[2]
2128
+
2129
+ event = make_event_from_dict(
2130
+ event_dict=event_d,
2131
+ room_version=room_version,
2132
+ internal_metadata_dict=internal_metadata_d,
2133
+ )
2134
+
2135
+ return origin, event
2136
+
2137
+ async def prune_staged_events_in_room(
2138
+ self,
2139
+ room_id: str,
2140
+ room_version: RoomVersion,
2141
+ ) -> bool:
2142
+ """Checks if there are lots of staged events for the room, and if so
2143
+ prune them down.
2144
+
2145
+ Returns:
2146
+ Whether any events were pruned
2147
+ """
2148
+
2149
+ # First check the size of the queue.
2150
+ count = await self.db_pool.simple_select_one_onecol(
2151
+ table="federation_inbound_events_staging",
2152
+ keyvalues={"room_id": room_id},
2153
+ retcol="COUNT(*)",
2154
+ desc="prune_staged_events_in_room_count",
2155
+ )
2156
+
2157
+ if count < 100:
2158
+ return False
2159
+
2160
+ # If the queue is too large, then we want clear the entire queue,
2161
+ # keeping only the forward extremities (i.e. the events not referenced
2162
+ # by other events in the queue). We do this so that we can always
2163
+ # backpaginate in all the events we have dropped.
2164
+ rows = cast(
2165
+ list[tuple[str, str]],
2166
+ await self.db_pool.simple_select_list(
2167
+ table="federation_inbound_events_staging",
2168
+ keyvalues={"room_id": room_id},
2169
+ retcols=("event_id", "event_json"),
2170
+ desc="prune_staged_events_in_room_fetch",
2171
+ ),
2172
+ )
2173
+
2174
+ # Find the set of events referenced by those in the queue, as well as
2175
+ # collecting all the event IDs in the queue.
2176
+ referenced_events: set[str] = set()
2177
+ seen_events: set[str] = set()
2178
+ for event_id, event_json in rows:
2179
+ seen_events.add(event_id)
2180
+ event_d = db_to_json(event_json)
2181
+
2182
+ # We don't bother parsing the dicts into full blown event objects,
2183
+ # as that is needlessly expensive.
2184
+
2185
+ # We haven't checked that the `prev_events` have the right format
2186
+ # yet, so we check as we go.
2187
+ prev_events = event_d.get("prev_events", [])
2188
+ if not isinstance(prev_events, list):
2189
+ logger.info("Invalid prev_events for %s", event_id)
2190
+ continue
2191
+
2192
+ if room_version.event_format == EventFormatVersions.ROOM_V1_V2:
2193
+ for prev_event_tuple in prev_events:
2194
+ if (
2195
+ not isinstance(prev_event_tuple, list)
2196
+ or len(prev_event_tuple) != 2
2197
+ ):
2198
+ logger.info("Invalid prev_events for %s", event_id)
2199
+ break
2200
+
2201
+ prev_event_id = prev_event_tuple[0]
2202
+ if not isinstance(prev_event_id, str):
2203
+ logger.info("Invalid prev_events for %s", event_id)
2204
+ break
2205
+
2206
+ referenced_events.add(prev_event_id)
2207
+ else:
2208
+ for prev_event_id in prev_events:
2209
+ if not isinstance(prev_event_id, str):
2210
+ logger.info("Invalid prev_events for %s", event_id)
2211
+ break
2212
+
2213
+ referenced_events.add(prev_event_id)
2214
+
2215
+ to_delete = referenced_events & seen_events
2216
+ if not to_delete:
2217
+ return False
2218
+
2219
+ pdus_pruned_from_federation_queue.labels(
2220
+ **{SERVER_NAME_LABEL: self.server_name}
2221
+ ).inc(len(to_delete))
2222
+ logger.info(
2223
+ "Pruning %d events in room %s from federation queue",
2224
+ len(to_delete),
2225
+ room_id,
2226
+ )
2227
+
2228
+ await self.db_pool.simple_delete_many(
2229
+ table="federation_inbound_events_staging",
2230
+ keyvalues={"room_id": room_id},
2231
+ iterable=to_delete,
2232
+ column="event_id",
2233
+ desc="prune_staged_events_in_room_delete",
2234
+ )
2235
+
2236
+ return True
2237
+
2238
+ async def get_all_rooms_with_staged_incoming_events(self) -> list[str]:
2239
+ """Get the room IDs of all events currently staged."""
2240
+ return await self.db_pool.simple_select_onecol(
2241
+ table="federation_inbound_events_staging",
2242
+ keyvalues={},
2243
+ retcol="DISTINCT room_id",
2244
+ desc="get_all_rooms_with_staged_incoming_events",
2245
+ )
2246
+
2247
+ @wrap_as_background_process("_get_stats_for_federation_staging")
2248
+ async def _get_stats_for_federation_staging(self) -> None:
2249
+ """Update the prometheus metrics for the inbound federation staging area."""
2250
+
2251
+ def _get_stats_for_federation_staging_txn(
2252
+ txn: LoggingTransaction,
2253
+ ) -> tuple[int, int]:
2254
+ txn.execute("SELECT count(*) FROM federation_inbound_events_staging")
2255
+ (count,) = cast(tuple[int], txn.fetchone())
2256
+
2257
+ txn.execute(
2258
+ "SELECT min(received_ts) FROM federation_inbound_events_staging"
2259
+ )
2260
+
2261
+ (received_ts,) = cast(tuple[Optional[int]], txn.fetchone())
2262
+
2263
+ # If there is nothing in the staging area default it to 0.
2264
+ age = 0
2265
+ if received_ts is not None:
2266
+ age = self.clock.time_msec() - received_ts
2267
+
2268
+ return count, age
2269
+
2270
+ count, age = await self.db_pool.runInteraction(
2271
+ "_get_stats_for_federation_staging", _get_stats_for_federation_staging_txn
2272
+ )
2273
+
2274
+ number_pdus_in_federation_queue.labels(
2275
+ **{SERVER_NAME_LABEL: self.server_name}
2276
+ ).set(count)
2277
+ oldest_pdu_in_federation_staging.labels(
2278
+ **{SERVER_NAME_LABEL: self.server_name}
2279
+ ).set(age)
2280
+
2281
+ async def clean_room_for_join(self, room_id: str) -> None:
2282
+ await self.db_pool.runInteraction(
2283
+ "clean_room_for_join", self._clean_room_for_join_txn, room_id
2284
+ )
2285
+
2286
+ def _clean_room_for_join_txn(self, txn: LoggingTransaction, room_id: str) -> None:
2287
+ query = "DELETE FROM event_forward_extremities WHERE room_id = ?"
2288
+
2289
+ txn.execute(query, (room_id,))
2290
+ self._invalidate_cache_and_stream(
2291
+ txn, self.get_latest_event_ids_in_room, (room_id,)
2292
+ )
2293
+
2294
+
2295
+ class EventFederationStore(EventFederationWorkerStore):
2296
+ """Responsible for storing and serving up the various graphs associated
2297
+ with an event. Including the main event graph and the auth chains for an
2298
+ event.
2299
+
2300
+ Also has methods for getting the front (latest) and back (oldest) edges
2301
+ of the event graphs. These are used to generate the parents for new events
2302
+ and backfilling from another server respectively.
2303
+ """
2304
+
2305
+ EVENT_AUTH_STATE_ONLY = "event_auth_state_only"
2306
+
2307
+ def __init__(
2308
+ self,
2309
+ database: DatabasePool,
2310
+ db_conn: LoggingDatabaseConnection,
2311
+ hs: "HomeServer",
2312
+ ):
2313
+ super().__init__(database, db_conn, hs)
2314
+
2315
+ self.db_pool.updates.register_background_update_handler(
2316
+ self.EVENT_AUTH_STATE_ONLY, self._background_delete_non_state_event_auth
2317
+ )
2318
+
2319
+ async def _background_delete_non_state_event_auth(
2320
+ self, progress: JsonDict, batch_size: int
2321
+ ) -> int:
2322
+ def delete_event_auth(txn: LoggingTransaction) -> bool:
2323
+ target_min_stream_id = progress.get("target_min_stream_id_inclusive")
2324
+ max_stream_id = progress.get("max_stream_id_exclusive")
2325
+
2326
+ if not target_min_stream_id or not max_stream_id:
2327
+ txn.execute("SELECT COALESCE(MIN(stream_ordering), 0) FROM events")
2328
+ rows = txn.fetchall()
2329
+ target_min_stream_id = rows[0][0]
2330
+
2331
+ txn.execute("SELECT COALESCE(MAX(stream_ordering), 0) FROM events")
2332
+ rows = txn.fetchall()
2333
+ max_stream_id = rows[0][0]
2334
+
2335
+ min_stream_id = max_stream_id - batch_size
2336
+
2337
+ sql = """
2338
+ DELETE FROM event_auth
2339
+ WHERE event_id IN (
2340
+ SELECT event_id FROM events
2341
+ LEFT JOIN state_events AS se USING (room_id, event_id)
2342
+ WHERE ? <= stream_ordering AND stream_ordering < ?
2343
+ AND se.state_key IS null
2344
+ )
2345
+ """
2346
+
2347
+ txn.execute(sql, (min_stream_id, max_stream_id))
2348
+
2349
+ new_progress = {
2350
+ "target_min_stream_id_inclusive": target_min_stream_id,
2351
+ "max_stream_id_exclusive": min_stream_id,
2352
+ }
2353
+
2354
+ self.db_pool.updates._background_update_progress_txn(
2355
+ txn, self.EVENT_AUTH_STATE_ONLY, new_progress
2356
+ )
2357
+
2358
+ return min_stream_id >= target_min_stream_id
2359
+
2360
+ result = await self.db_pool.runInteraction(
2361
+ self.EVENT_AUTH_STATE_ONLY, delete_event_auth
2362
+ )
2363
+
2364
+ if not result:
2365
+ await self.db_pool.updates._end_background_update(
2366
+ self.EVENT_AUTH_STATE_ONLY
2367
+ )
2368
+
2369
+ return batch_size
2370
+
2371
+
2372
+ def _materialize(
2373
+ origin_chain_id: int,
2374
+ origin_sequence_number: int,
2375
+ links: dict[int, list[tuple[int, int, int]]],
2376
+ materialized: dict[int, int],
2377
+ backwards: bool = True,
2378
+ ) -> None:
2379
+ """Helper function for fetching auth chain links. For a given origin chain
2380
+ ID / sequence number and a dictionary of links, updates the materialized
2381
+ dict with the reachable chains.
2382
+
2383
+ To get a dict of all chains reachable from a set of chains this function can
2384
+ be called in a loop, once per origin chain with the same links and
2385
+ materialized args. The materialized dict will the result.
2386
+
2387
+ Args:
2388
+ origin_chain_id, origin_sequence_number
2389
+ links: map of the links between chains as a dict from origin chain ID
2390
+ to list of 3-tuples of origin sequence number, target chain ID and
2391
+ target sequence number.
2392
+ materialized: dict to update with new reachability information, as a
2393
+ map from chain ID to max sequence number reachable.
2394
+ backwards: If True, walks backwards down the chains. If False, walks forwards from the chains.
2395
+ """
2396
+
2397
+ # Do a standard graph traversal.
2398
+ stack = [(origin_chain_id, origin_sequence_number)]
2399
+
2400
+ while stack:
2401
+ c, s = stack.pop()
2402
+
2403
+ chain_links = links.get(c, [])
2404
+ for (
2405
+ sequence_number,
2406
+ target_chain_id,
2407
+ target_sequence_number,
2408
+ ) in chain_links:
2409
+ if backwards:
2410
+ # Ignore any links that are higher up the chain
2411
+ if sequence_number > s:
2412
+ continue
2413
+
2414
+ # Check if we have already visited the target chain before, if so we
2415
+ # can skip it.
2416
+ if materialized.get(target_chain_id, 0) < target_sequence_number:
2417
+ stack.append((target_chain_id, target_sequence_number))
2418
+ materialized[target_chain_id] = target_sequence_number
2419
+ else:
2420
+ # Ignore any links that are lower down the chain.
2421
+ if sequence_number < s:
2422
+ continue
2423
+ # Check if we have already visited the target chain before, if so we
2424
+ # can skip it.
2425
+ if (
2426
+ materialized.get(target_chain_id, MAX_CHAIN_LENGTH)
2427
+ > target_sequence_number
2428
+ ):
2429
+ stack.append((target_chain_id, target_sequence_number))
2430
+ materialized[target_chain_id] = target_sequence_number
2431
+
2432
+
2433
+ def _generate_forward_links(
2434
+ links: dict[int, list[tuple[int, int, int]]],
2435
+ ) -> dict[int, list[tuple[int, int, int]]]:
2436
+ """Reverse the input links from the given backwards links"""
2437
+ new_links: dict[int, list[tuple[int, int, int]]] = {}
2438
+ for origin_chain_id, chain_links in links.items():
2439
+ for origin_seq_num, target_chain_id, target_seq_num in chain_links:
2440
+ new_links.setdefault(target_chain_id, []).append(
2441
+ (target_seq_num, origin_chain_id, origin_seq_num)
2442
+ )
2443
+ return new_links
2444
+
2445
+
2446
+ def accumulate_forwards_reachable_events(
2447
+ conflicted_forwards_reachable: dict[int, int],
2448
+ back_links: dict[int, list[tuple[int, int, int]]],
2449
+ conflicted_chain_positions: dict[str, tuple[int, int]],
2450
+ ) -> None:
2451
+ """Accumulate new forwards reachable events using the back_links provided.
2452
+
2453
+ Accumulating forwards reachable information is quite different from backwards reachable information
2454
+ because _get_chain_links returns the entire linkage information for backwards reachable events,
2455
+ but not _forwards_ reachable events. We are only interested in the forwards reachable information
2456
+ that is encoded in the backwards reachable links, so we can just invert all the operations we do
2457
+ for backwards reachable events to calculate a subset of forwards reachable information. The
2458
+ caveat with this approach is that it is a _subset_. This means new back_links may encode new
2459
+ forwards reachable information which we also need. Consider this scenario:
2460
+
2461
+ A <-- B <-- C <--- D <-- E <-- F Chain 1
2462
+ |
2463
+ `----- G <-- H <-- I Chain 2
2464
+ |
2465
+ `---- J <-- K Chain 3
2466
+
2467
+ Now consider what happens when B is a conflicted event. _get_chain_links returns the conflicted
2468
+ chain and ALL links heading towards the root of the graph. This means we will know the
2469
+ Chain 1 to Chain 2 link via C (as all links for the chain are returned, not strictly ones with
2470
+ a lower sequence number), but we will NOT know the Chain 2 to Chain 3 link via H. We can be
2471
+ blissfully unaware of Chain 3 entirely, if and only if there isn't some other conflicted event
2472
+ on that chain. Consider what happens when K is /also/ conflicted. _get_chain_links will generate
2473
+ two iterations: one for B and one for K. It's important that we re-evaluate the forwards reachable
2474
+ information for B to include Chain 3 when we process the K iteration, hence we are "accumulating"
2475
+ forwards reachability information.
2476
+
2477
+ NB: We don't consider 'additional backwards reachable events' here because they have no effect
2478
+ on forwards reachability calculations, only backwards.
2479
+
2480
+ Args:
2481
+ conflicted_forwards_reachable: The materialised dict of forwards reachable information.
2482
+ The output to this function are stored here.
2483
+ back_links: One iteration of _get_chain_links which encodes backwards reachable information.
2484
+ conflicted_chain_positions: The conflicted events.
2485
+ """
2486
+ # links go backwards but we want them to go forwards as well for v2.1
2487
+ fwd_links = _generate_forward_links(back_links)
2488
+
2489
+ # for each conflicted event, accumulate forwards reachability information
2490
+ for (
2491
+ conflicted_chain_id,
2492
+ conflicted_chain_seq,
2493
+ ) in conflicted_chain_positions.values():
2494
+ # the conflicted event itself encodes reachability information
2495
+ # e.g if D was conflicted, it encodes E,F as forwards reachable.
2496
+ conflicted_forwards_reachable[conflicted_chain_id] = min(
2497
+ conflicted_chain_seq,
2498
+ conflicted_forwards_reachable.get(conflicted_chain_id, MAX_CHAIN_LENGTH),
2499
+ )
2500
+ # Walk from the conflicted event forwards to explore the links.
2501
+ # This function checks if we've visited the chain before and skips reprocessing, so this
2502
+ # does not repeatedly traverse the graph.
2503
+ _materialize(
2504
+ conflicted_chain_id,
2505
+ conflicted_chain_seq,
2506
+ fwd_links,
2507
+ conflicted_forwards_reachable,
2508
+ backwards=False,
2509
+ )