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,2581 @@
1
+ #
2
+ # This file is licensed under the Affero General Public License (AGPL) version 3.
3
+ #
4
+ # Copyright 2019,2020 The Matrix.org Foundation C.I.C.
5
+ # Copyright 2016 OpenMarket Ltd
6
+ # Copyright (C) 2023 New Vector, Ltd
7
+ #
8
+ # This program is free software: you can redistribute it and/or modify
9
+ # it under the terms of the GNU Affero General Public License as
10
+ # published by the Free Software Foundation, either version 3 of the
11
+ # License, or (at your option) any later version.
12
+ #
13
+ # See the GNU Affero General Public License for more details:
14
+ # <https://www.gnu.org/licenses/agpl-3.0.html>.
15
+ #
16
+ # Originally licensed under the Apache License, Version 2.0:
17
+ # <http://www.apache.org/licenses/LICENSE-2.0>.
18
+ #
19
+ # [This file includes modifications made by New Vector Limited]
20
+ #
21
+ #
22
+ import logging
23
+ from typing import (
24
+ TYPE_CHECKING,
25
+ Any,
26
+ Collection,
27
+ Iterable,
28
+ Mapping,
29
+ Optional,
30
+ cast,
31
+ )
32
+
33
+ from canonicaljson import encode_canonical_json
34
+
35
+ from synapse.api.constants import EduTypes
36
+ from synapse.api.errors import Codes, StoreError
37
+ from synapse.logging.opentracing import (
38
+ get_active_span_text_map,
39
+ set_tag,
40
+ trace,
41
+ whitelisted_homeserver,
42
+ )
43
+ from synapse.metrics.background_process_metrics import wrap_as_background_process
44
+ from synapse.replication.tcp.streams._base import DeviceListsStream
45
+ from synapse.storage._base import SQLBaseStore, db_to_json, make_in_list_sql_clause
46
+ from synapse.storage.database import (
47
+ DatabasePool,
48
+ LoggingDatabaseConnection,
49
+ LoggingTransaction,
50
+ make_tuple_comparison_clause,
51
+ )
52
+ from synapse.storage.databases.main.end_to_end_keys import EndToEndKeyWorkerStore
53
+ from synapse.storage.databases.main.roommember import RoomMemberWorkerStore
54
+ from synapse.storage.types import Cursor
55
+ from synapse.storage.util.id_generators import MultiWriterIdGenerator
56
+ from synapse.types import (
57
+ JsonDict,
58
+ JsonMapping,
59
+ MultiWriterStreamToken,
60
+ StrCollection,
61
+ get_verify_key_from_cross_signing_key,
62
+ )
63
+ from synapse.util.caches.descriptors import cached, cachedList
64
+ from synapse.util.caches.stream_change_cache import StreamChangeCache
65
+ from synapse.util.cancellation import cancellable
66
+ from synapse.util.iterutils import batch_iter
67
+ from synapse.util.json import json_decoder, json_encoder
68
+ from synapse.util.stringutils import shortstr
69
+
70
+ if TYPE_CHECKING:
71
+ from synapse.server import HomeServer
72
+
73
+ logger = logging.getLogger(__name__)
74
+ issue_8631_logger = logging.getLogger("synapse.8631_debug")
75
+
76
+ DROP_DEVICE_LIST_STREAMS_NON_UNIQUE_INDEXES = (
77
+ "drop_device_list_streams_non_unique_indexes"
78
+ )
79
+
80
+ BG_UPDATE_REMOVE_DUP_OUTBOUND_POKES = "remove_dup_outbound_pokes"
81
+
82
+
83
+ class DeviceWorkerStore(RoomMemberWorkerStore, EndToEndKeyWorkerStore):
84
+ _device_list_id_gen: MultiWriterIdGenerator
85
+ _instance_name: str
86
+
87
+ def __init__(
88
+ self,
89
+ database: DatabasePool,
90
+ db_conn: LoggingDatabaseConnection,
91
+ hs: "HomeServer",
92
+ ):
93
+ super().__init__(database, db_conn, hs)
94
+
95
+ # In the worker store this is an ID tracker which we overwrite in the non-worker
96
+ # class below that is used on the main process.
97
+ self._device_list_id_gen = MultiWriterIdGenerator(
98
+ db_conn=db_conn,
99
+ db=database,
100
+ notifier=hs.get_replication_notifier(),
101
+ stream_name="device_lists_stream",
102
+ server_name=self.server_name,
103
+ instance_name=self._instance_name,
104
+ tables=[
105
+ ("device_lists_stream", "instance_name", "stream_id"),
106
+ ("user_signature_stream", "instance_name", "stream_id"),
107
+ ("device_lists_outbound_pokes", "instance_name", "stream_id"),
108
+ ("device_lists_changes_in_room", "instance_name", "stream_id"),
109
+ ("device_lists_remote_pending", "instance_name", "stream_id"),
110
+ (
111
+ "device_lists_changes_converted_stream_position",
112
+ "instance_name",
113
+ "stream_id",
114
+ ),
115
+ ],
116
+ sequence_name="device_lists_sequence",
117
+ writers=hs.config.worker.writers.device_lists,
118
+ )
119
+
120
+ self._is_device_list_writer = (
121
+ self._instance_name in hs.config.worker.writers.device_lists
122
+ )
123
+
124
+ device_list_max = self._device_list_id_gen.get_current_token()
125
+ device_list_prefill, min_device_list_id = self.db_pool.get_cache_dict(
126
+ db_conn,
127
+ "device_lists_stream",
128
+ entity_column="user_id",
129
+ stream_column="stream_id",
130
+ max_value=device_list_max,
131
+ limit=10000,
132
+ )
133
+ self._device_list_stream_cache = StreamChangeCache(
134
+ name="DeviceListStreamChangeCache",
135
+ server_name=self.server_name,
136
+ current_stream_pos=min_device_list_id,
137
+ prefilled_cache=device_list_prefill,
138
+ )
139
+
140
+ device_list_room_prefill, min_device_list_room_id = self.db_pool.get_cache_dict(
141
+ db_conn,
142
+ "device_lists_changes_in_room",
143
+ entity_column="room_id",
144
+ stream_column="stream_id",
145
+ max_value=device_list_max,
146
+ limit=10000,
147
+ )
148
+ self._device_list_room_stream_cache = StreamChangeCache(
149
+ name="DeviceListRoomStreamChangeCache",
150
+ server_name=self.server_name,
151
+ current_stream_pos=min_device_list_room_id,
152
+ prefilled_cache=device_list_room_prefill,
153
+ )
154
+
155
+ (
156
+ user_signature_stream_prefill,
157
+ user_signature_stream_list_id,
158
+ ) = self.db_pool.get_cache_dict(
159
+ db_conn,
160
+ "user_signature_stream",
161
+ entity_column="from_user_id",
162
+ stream_column="stream_id",
163
+ max_value=device_list_max,
164
+ limit=1000,
165
+ )
166
+ self._user_signature_stream_cache = StreamChangeCache(
167
+ name="UserSignatureStreamChangeCache",
168
+ server_name=self.server_name,
169
+ current_stream_pos=user_signature_stream_list_id,
170
+ prefilled_cache=user_signature_stream_prefill,
171
+ )
172
+
173
+ self._device_list_federation_stream_cache = None
174
+ if hs.should_send_federation():
175
+ (
176
+ device_list_federation_prefill,
177
+ device_list_federation_list_id,
178
+ ) = self.db_pool.get_cache_dict(
179
+ db_conn,
180
+ "device_lists_outbound_pokes",
181
+ entity_column="destination",
182
+ stream_column="stream_id",
183
+ max_value=device_list_max,
184
+ limit=10000,
185
+ )
186
+ self._device_list_federation_stream_cache = StreamChangeCache(
187
+ name="DeviceListFederationStreamChangeCache",
188
+ server_name=self.server_name,
189
+ current_stream_pos=device_list_federation_list_id,
190
+ prefilled_cache=device_list_federation_prefill,
191
+ )
192
+
193
+ if hs.config.worker.run_background_tasks:
194
+ self.clock.looping_call(
195
+ self._prune_old_outbound_device_pokes, 60 * 60 * 1000
196
+ )
197
+
198
+ def process_replication_rows(
199
+ self, stream_name: str, instance_name: str, token: int, rows: Iterable[Any]
200
+ ) -> None:
201
+ if stream_name == DeviceListsStream.NAME:
202
+ self._invalidate_caches_for_devices(token, rows)
203
+
204
+ return super().process_replication_rows(stream_name, instance_name, token, rows)
205
+
206
+ def process_replication_position(
207
+ self, stream_name: str, instance_name: str, token: int
208
+ ) -> None:
209
+ if stream_name == DeviceListsStream.NAME:
210
+ self._device_list_id_gen.advance(instance_name, token)
211
+
212
+ super().process_replication_position(stream_name, instance_name, token)
213
+
214
+ def _invalidate_caches_for_devices(
215
+ self, token: int, rows: Iterable[DeviceListsStream.DeviceListsStreamRow]
216
+ ) -> None:
217
+ for row in rows:
218
+ if row.is_signature:
219
+ self._user_signature_stream_cache.entity_has_changed(row.user_id, token)
220
+ continue
221
+
222
+ # The entities are either user IDs (starting with '@') whose devices
223
+ # have changed, or remote servers that we need to tell about
224
+ # changes.
225
+ if not row.hosts_calculated:
226
+ self._device_list_stream_cache.entity_has_changed(row.user_id, token)
227
+ self.get_cached_devices_for_user.invalidate((row.user_id,))
228
+ self._get_cached_user_device.invalidate((row.user_id,))
229
+ self.get_device_list_last_stream_id_for_remote.invalidate(
230
+ (row.user_id,)
231
+ )
232
+
233
+ def device_lists_outbound_pokes_have_changed(
234
+ self, destinations: StrCollection, token: int
235
+ ) -> None:
236
+ assert self._device_list_federation_stream_cache is not None
237
+
238
+ for destination in destinations:
239
+ self._device_list_federation_stream_cache.entity_has_changed(
240
+ destination, token
241
+ )
242
+
243
+ def device_lists_in_rooms_have_changed(
244
+ self, room_ids: StrCollection, token: int
245
+ ) -> None:
246
+ "Record that device lists have changed in rooms"
247
+ for room_id in room_ids:
248
+ self._device_list_room_stream_cache.entity_has_changed(room_id, token)
249
+
250
+ def get_device_stream_token(self) -> MultiWriterStreamToken:
251
+ return MultiWriterStreamToken.from_generator(self._device_list_id_gen)
252
+
253
+ def get_device_stream_id_generator(self) -> MultiWriterIdGenerator:
254
+ return self._device_list_id_gen
255
+
256
+ async def count_devices_by_users(
257
+ self, user_ids: Optional[Collection[str]] = None
258
+ ) -> int:
259
+ """Retrieve number of all devices of given users.
260
+ Only returns number of devices that are not marked as hidden.
261
+
262
+ Args:
263
+ user_ids: The IDs of the users which owns devices
264
+ Returns:
265
+ Number of devices of this users.
266
+ """
267
+
268
+ def count_devices_by_users_txn(
269
+ txn: LoggingTransaction, user_ids: Collection[str]
270
+ ) -> int:
271
+ sql = """
272
+ SELECT count(*)
273
+ FROM devices
274
+ WHERE
275
+ hidden = '0' AND
276
+ """
277
+
278
+ clause, args = make_in_list_sql_clause(
279
+ txn.database_engine, "user_id", user_ids
280
+ )
281
+
282
+ txn.execute(sql + clause, args)
283
+ return cast(tuple[int], txn.fetchone())[0]
284
+
285
+ if not user_ids:
286
+ return 0
287
+
288
+ return await self.db_pool.runInteraction(
289
+ "count_devices_by_users", count_devices_by_users_txn, user_ids
290
+ )
291
+
292
+ async def store_device(
293
+ self,
294
+ user_id: str,
295
+ device_id: str,
296
+ initial_device_display_name: Optional[str],
297
+ auth_provider_id: Optional[str] = None,
298
+ auth_provider_session_id: Optional[str] = None,
299
+ ) -> bool:
300
+ """Ensure the given device is known; add it to the store if not
301
+
302
+ Args:
303
+ user_id: id of user associated with the device
304
+ device_id: id of device
305
+ initial_device_display_name: initial displayname of the device.
306
+ Ignored if device exists.
307
+ auth_provider_id: The SSO IdP the user used, if any.
308
+ auth_provider_session_id: The session ID (sid) got from a OIDC login.
309
+
310
+ Returns:
311
+ Whether the device was inserted or an existing device existed with that ID.
312
+
313
+ Raises:
314
+ StoreError: if the device is already in use
315
+ """
316
+ try:
317
+ inserted = await self.db_pool.simple_upsert(
318
+ "devices",
319
+ keyvalues={
320
+ "user_id": user_id,
321
+ "device_id": device_id,
322
+ },
323
+ values={},
324
+ insertion_values={
325
+ "display_name": initial_device_display_name,
326
+ "hidden": False,
327
+ },
328
+ desc="store_device",
329
+ )
330
+ await self.invalidate_cache_and_stream("get_device", (user_id, device_id))
331
+
332
+ if not inserted:
333
+ # if the device already exists, check if it's a real device, or
334
+ # if the device ID is reserved by something else
335
+ hidden = await self.db_pool.simple_select_one_onecol(
336
+ "devices",
337
+ keyvalues={"user_id": user_id, "device_id": device_id},
338
+ retcol="hidden",
339
+ )
340
+ if hidden:
341
+ raise StoreError(400, "The device ID is in use", Codes.FORBIDDEN)
342
+
343
+ if auth_provider_id and auth_provider_session_id:
344
+ await self.db_pool.simple_insert(
345
+ "device_auth_providers",
346
+ values={
347
+ "user_id": user_id,
348
+ "device_id": device_id,
349
+ "auth_provider_id": auth_provider_id,
350
+ "auth_provider_session_id": auth_provider_session_id,
351
+ },
352
+ desc="store_device_auth_provider",
353
+ )
354
+
355
+ return inserted
356
+ except StoreError:
357
+ raise
358
+ except Exception as e:
359
+ logger.error(
360
+ "store_device with device_id=%s(%r) user_id=%s(%r)"
361
+ " display_name=%s(%r) failed: %s",
362
+ type(device_id).__name__,
363
+ device_id,
364
+ type(user_id).__name__,
365
+ user_id,
366
+ type(initial_device_display_name).__name__,
367
+ initial_device_display_name,
368
+ e,
369
+ )
370
+ raise StoreError(500, "Problem storing device.")
371
+
372
+ async def delete_devices(self, user_id: str, device_ids: StrCollection) -> None:
373
+ """Deletes several devices.
374
+
375
+ Args:
376
+ user_id: The ID of the user which owns the devices
377
+ device_ids: The IDs of the devices to delete
378
+ """
379
+
380
+ def _delete_devices_txn(txn: LoggingTransaction, device_ids: list[str]) -> None:
381
+ self.db_pool.simple_delete_many_txn(
382
+ txn,
383
+ table="devices",
384
+ column="device_id",
385
+ values=device_ids,
386
+ keyvalues={"user_id": user_id, "hidden": False},
387
+ )
388
+
389
+ self.db_pool.simple_delete_many_txn(
390
+ txn,
391
+ table="device_auth_providers",
392
+ column="device_id",
393
+ values=device_ids,
394
+ keyvalues={"user_id": user_id},
395
+ )
396
+
397
+ # Also delete associated e2e keys.
398
+ self.db_pool.simple_delete_many_txn(
399
+ txn,
400
+ table="e2e_device_keys_json",
401
+ keyvalues={"user_id": user_id},
402
+ column="device_id",
403
+ values=device_ids,
404
+ )
405
+ self.db_pool.simple_delete_many_txn(
406
+ txn,
407
+ table="e2e_one_time_keys_json",
408
+ keyvalues={"user_id": user_id},
409
+ column="device_id",
410
+ values=device_ids,
411
+ )
412
+ self.db_pool.simple_delete_many_txn(
413
+ txn,
414
+ table="dehydrated_devices",
415
+ keyvalues={"user_id": user_id},
416
+ column="device_id",
417
+ values=device_ids,
418
+ )
419
+ self.db_pool.simple_delete_many_txn(
420
+ txn,
421
+ table="e2e_fallback_keys_json",
422
+ keyvalues={"user_id": user_id},
423
+ column="device_id",
424
+ values=device_ids,
425
+ )
426
+
427
+ # We're bulk deleting potentially many devices at once, so
428
+ # let's not invalidate the cache for each device individually.
429
+ # Instead, we will invalidate the cache for the user as a whole.
430
+ self._invalidate_cache_and_stream(txn, self.get_device, (user_id,))
431
+ self._invalidate_cache_and_stream(
432
+ txn, self.count_e2e_one_time_keys, (user_id,)
433
+ )
434
+ self._invalidate_cache_and_stream(
435
+ txn, self.get_e2e_unused_fallback_key_types, (user_id,)
436
+ )
437
+
438
+ for batch in batch_iter(device_ids, 1000):
439
+ await self.db_pool.runInteraction(
440
+ "delete_devices", _delete_devices_txn, batch
441
+ )
442
+
443
+ async def update_device(
444
+ self, user_id: str, device_id: str, new_display_name: Optional[str] = None
445
+ ) -> None:
446
+ """Update a device. Only updates the device if it is not marked as
447
+ hidden.
448
+
449
+ Args:
450
+ user_id: The ID of the user which owns the device
451
+ device_id: The ID of the device to update
452
+ new_display_name: new displayname for device; None to leave unchanged
453
+ Raises:
454
+ StoreError: if the device is not found
455
+ """
456
+ updates = {}
457
+ if new_display_name is not None:
458
+ updates["display_name"] = new_display_name
459
+ if not updates:
460
+ return None
461
+ await self.db_pool.simple_update_one(
462
+ table="devices",
463
+ keyvalues={"user_id": user_id, "device_id": device_id, "hidden": False},
464
+ updatevalues=updates,
465
+ desc="update_device",
466
+ )
467
+ await self.invalidate_cache_and_stream("get_device", (user_id, device_id))
468
+
469
+ @cached(tree=True)
470
+ async def get_device(
471
+ self, user_id: str, device_id: str
472
+ ) -> Optional[Mapping[str, Any]]:
473
+ """Retrieve a device. Only returns devices that are not marked as
474
+ hidden.
475
+
476
+ Args:
477
+ user_id: The ID of the user which owns the device
478
+ device_id: The ID of the device to retrieve
479
+ Returns:
480
+ A dict containing the device information, or `None` if the device does not
481
+ exist.
482
+ """
483
+ row = await self.db_pool.simple_select_one(
484
+ table="devices",
485
+ keyvalues={"user_id": user_id, "device_id": device_id, "hidden": False},
486
+ retcols=("user_id", "device_id", "display_name"),
487
+ desc="get_device",
488
+ allow_none=True,
489
+ )
490
+ if row is None:
491
+ return None
492
+ return {"user_id": row[0], "device_id": row[1], "display_name": row[2]}
493
+
494
+ async def get_devices_by_user(
495
+ self, user_id: str
496
+ ) -> dict[str, dict[str, Optional[str]]]:
497
+ """Retrieve all of a user's registered devices. Only returns devices
498
+ that are not marked as hidden.
499
+
500
+ Args:
501
+ user_id:
502
+ Returns:
503
+ A mapping from device_id to a dict containing "device_id", "user_id"
504
+ and "display_name" for each device. Display name may be null.
505
+ """
506
+ devices = cast(
507
+ list[tuple[str, str, Optional[str]]],
508
+ await self.db_pool.simple_select_list(
509
+ table="devices",
510
+ keyvalues={"user_id": user_id, "hidden": False},
511
+ retcols=("user_id", "device_id", "display_name"),
512
+ desc="get_devices_by_user",
513
+ ),
514
+ )
515
+
516
+ return {
517
+ d[1]: {"user_id": d[0], "device_id": d[1], "display_name": d[2]}
518
+ for d in devices
519
+ }
520
+
521
+ async def get_devices_by_auth_provider_session_id(
522
+ self, auth_provider_id: str, auth_provider_session_id: str
523
+ ) -> list[tuple[str, str]]:
524
+ """Retrieve the list of devices associated with a SSO IdP session ID.
525
+
526
+ Args:
527
+ auth_provider_id: The SSO IdP ID as defined in the server config
528
+ auth_provider_session_id: The session ID within the IdP
529
+ Returns:
530
+ A list of dicts containing the device_id and the user_id of each device
531
+ """
532
+ return cast(
533
+ list[tuple[str, str]],
534
+ await self.db_pool.simple_select_list(
535
+ table="device_auth_providers",
536
+ keyvalues={
537
+ "auth_provider_id": auth_provider_id,
538
+ "auth_provider_session_id": auth_provider_session_id,
539
+ },
540
+ retcols=("user_id", "device_id"),
541
+ desc="get_devices_by_auth_provider_session_id",
542
+ ),
543
+ )
544
+
545
+ @trace
546
+ async def get_device_updates_by_remote(
547
+ self, destination: str, from_stream_id: int, limit: int
548
+ ) -> tuple[int, list[tuple[str, JsonDict]]]:
549
+ """Get a stream of device updates to send to the given remote server.
550
+
551
+ Args:
552
+ destination: The host the device updates are intended for
553
+ from_stream_id: The minimum stream_id to filter updates by, exclusive
554
+ limit: Maximum number of device updates to return
555
+
556
+ Returns:
557
+ - The current stream id (i.e. the stream id of the last update included
558
+ in the response); and
559
+ - The list of updates, where each update is a pair of EDU type and
560
+ EDU contents.
561
+ """
562
+ # Here, we don't use the individual instances positions, as we only
563
+ # record the last stream position we've sent to a destination. This
564
+ # means we have to wait for all the writers to catch up before sending
565
+ # device list updates, which is fine.
566
+ now_stream_id = self.get_device_stream_token().stream
567
+ if from_stream_id == now_stream_id:
568
+ return now_stream_id, []
569
+
570
+ if self._device_list_federation_stream_cache is None:
571
+ raise Exception("Func can only be used on federation senders")
572
+
573
+ has_changed = self._device_list_federation_stream_cache.has_entity_changed(
574
+ destination, int(from_stream_id)
575
+ )
576
+ if not has_changed:
577
+ # debugging for https://github.com/matrix-org/synapse/issues/14251
578
+ issue_8631_logger.debug(
579
+ "%s: no change between %i and %i",
580
+ destination,
581
+ from_stream_id,
582
+ now_stream_id,
583
+ )
584
+ return now_stream_id, []
585
+
586
+ updates = await self.db_pool.runInteraction(
587
+ "get_device_updates_by_remote",
588
+ self._get_device_updates_by_remote_txn,
589
+ destination,
590
+ from_stream_id,
591
+ now_stream_id,
592
+ limit,
593
+ )
594
+
595
+ # We need to ensure `updates` doesn't grow too big.
596
+ # Currently: `len(updates) <= limit`.
597
+
598
+ # Return an empty list if there are no updates
599
+ if not updates:
600
+ return now_stream_id, []
601
+
602
+ if issue_8631_logger.isEnabledFor(logging.DEBUG):
603
+ data = {(user, device): stream_id for user, device, stream_id, _ in updates}
604
+ issue_8631_logger.debug(
605
+ "device updates need to be sent to %s: %s", destination, data
606
+ )
607
+
608
+ # get the cross-signing keys of the users in the list, so that we can
609
+ # determine which of the device changes were cross-signing keys
610
+ users = {r[0] for r in updates}
611
+ master_key_by_user = {}
612
+ self_signing_key_by_user = {}
613
+ for user in users:
614
+ cross_signing_key = await self.get_e2e_cross_signing_key(user, "master")
615
+ if cross_signing_key:
616
+ key_id, verify_key = get_verify_key_from_cross_signing_key(
617
+ cross_signing_key
618
+ )
619
+ # verify_key is a VerifyKey from signedjson, which uses
620
+ # .version to denote the portion of the key ID after the
621
+ # algorithm and colon, which is the device ID
622
+ master_key_by_user[user] = {
623
+ "key_info": cross_signing_key,
624
+ "device_id": verify_key.version,
625
+ }
626
+
627
+ cross_signing_key = await self.get_e2e_cross_signing_key(
628
+ user, "self_signing"
629
+ )
630
+ if cross_signing_key:
631
+ key_id, verify_key = get_verify_key_from_cross_signing_key(
632
+ cross_signing_key
633
+ )
634
+ self_signing_key_by_user[user] = {
635
+ "key_info": cross_signing_key,
636
+ "device_id": verify_key.version,
637
+ }
638
+
639
+ # Perform the equivalent of a GROUP BY
640
+ #
641
+ # Iterate through the updates list and copy non-duplicate
642
+ # (user_id, device_id) entries into a map, with the value being
643
+ # the max stream_id across each set of duplicate entries
644
+ #
645
+ # maps (user_id, device_id) -> (stream_id, opentracing_context)
646
+ #
647
+ # opentracing_context contains the opentracing metadata for the request
648
+ # that created the poke
649
+ #
650
+ # The most recent request's opentracing_context is used as the
651
+ # context which created the Edu.
652
+
653
+ # This is the stream ID that we will return for the consumer to resume
654
+ # following this stream later.
655
+ last_processed_stream_id = from_stream_id
656
+
657
+ # A map of (user ID, device ID) to (stream ID, context).
658
+ query_map: dict[tuple[str, str], tuple[int, Optional[str]]] = {}
659
+ cross_signing_keys_by_user: dict[str, dict[str, object]] = {}
660
+ for user_id, device_id, update_stream_id, update_context in updates:
661
+ # Calculate the remaining length budget.
662
+ # Note that, for now, each entry in `cross_signing_keys_by_user`
663
+ # gives rise to two device updates in the result, so those cost twice
664
+ # as much (and are the whole reason we need to separately calculate
665
+ # the budget; we know len(updates) <= limit otherwise!)
666
+ # N.B. len() on dicts is cheap since they store their size.
667
+ remaining_length_budget = limit - (
668
+ len(query_map) + 2 * len(cross_signing_keys_by_user)
669
+ )
670
+ assert remaining_length_budget >= 0
671
+
672
+ is_master_key_update = (
673
+ user_id in master_key_by_user
674
+ and device_id == master_key_by_user[user_id]["device_id"]
675
+ )
676
+ is_self_signing_key_update = (
677
+ user_id in self_signing_key_by_user
678
+ and device_id == self_signing_key_by_user[user_id]["device_id"]
679
+ )
680
+
681
+ is_cross_signing_key_update = (
682
+ is_master_key_update or is_self_signing_key_update
683
+ )
684
+
685
+ if (
686
+ is_cross_signing_key_update
687
+ and user_id not in cross_signing_keys_by_user
688
+ ):
689
+ # This will give rise to 2 device updates.
690
+ # If we don't have the budget, stop here!
691
+ if remaining_length_budget < 2:
692
+ break
693
+
694
+ if is_master_key_update:
695
+ result = cross_signing_keys_by_user.setdefault(user_id, {})
696
+ result["master_key"] = master_key_by_user[user_id]["key_info"]
697
+ elif is_self_signing_key_update:
698
+ result = cross_signing_keys_by_user.setdefault(user_id, {})
699
+ result["self_signing_key"] = self_signing_key_by_user[user_id][
700
+ "key_info"
701
+ ]
702
+ else:
703
+ key = (user_id, device_id)
704
+
705
+ if key not in query_map and remaining_length_budget < 1:
706
+ # We don't have space for a new entry
707
+ break
708
+
709
+ previous_update_stream_id, _ = query_map.get(key, (0, None))
710
+
711
+ if update_stream_id > previous_update_stream_id:
712
+ # FIXME If this overwrites an older update, this discards the
713
+ # previous OpenTracing context.
714
+ # It might make it harder to track down issues using OpenTracing.
715
+ # If there's a good reason why it doesn't matter, a comment here
716
+ # about that would not hurt.
717
+ query_map[key] = (update_stream_id, update_context)
718
+
719
+ # As this update has been added to the response, advance the stream
720
+ # position.
721
+ last_processed_stream_id = update_stream_id
722
+
723
+ # In the worst case scenario, each update is for a distinct user and is
724
+ # added either to the query_map or to cross_signing_keys_by_user,
725
+ # but not both:
726
+ # len(query_map) + len(cross_signing_keys_by_user) <= len(updates) here,
727
+ # so len(query_map) + len(cross_signing_keys_by_user) <= limit.
728
+
729
+ results = await self._get_device_update_edus_by_remote(
730
+ destination, from_stream_id, query_map
731
+ )
732
+
733
+ # len(results) <= len(query_map) here,
734
+ # so len(results) + len(cross_signing_keys_by_user) <= limit.
735
+
736
+ # Add the updated cross-signing keys to the results list
737
+ for user_id, result in cross_signing_keys_by_user.items():
738
+ result["user_id"] = user_id
739
+ results.append((EduTypes.SIGNING_KEY_UPDATE, result))
740
+ # also send the unstable version
741
+ # FIXME: remove this when enough servers have upgraded
742
+ # and remove the length budgeting above.
743
+ results.append(("org.matrix.signing_key_update", result))
744
+
745
+ if issue_8631_logger.isEnabledFor(logging.DEBUG):
746
+ for user_id, edu in results:
747
+ issue_8631_logger.debug(
748
+ "device update to %s for %s from %s to %s: %s",
749
+ destination,
750
+ user_id,
751
+ from_stream_id,
752
+ last_processed_stream_id,
753
+ edu,
754
+ )
755
+
756
+ return last_processed_stream_id, results
757
+
758
+ def _get_device_updates_by_remote_txn(
759
+ self,
760
+ txn: LoggingTransaction,
761
+ destination: str,
762
+ from_stream_id: int,
763
+ now_stream_id: int,
764
+ limit: int,
765
+ ) -> list[tuple[str, str, int, Optional[str]]]:
766
+ """Return device update information for a given remote destination
767
+
768
+ Args:
769
+ txn: The transaction to execute
770
+ destination: The host the device updates are intended for
771
+ from_stream_id: The minimum stream_id to filter updates by, exclusive
772
+ now_stream_id: The maximum stream_id to filter updates by, inclusive
773
+ limit: Maximum number of device updates to return
774
+
775
+ Returns:
776
+ List of device update tuples:
777
+ - user_id
778
+ - device_id
779
+ - stream_id
780
+ - opentracing_context
781
+ """
782
+ # get the list of device updates that need to be sent
783
+ sql = """
784
+ SELECT user_id, device_id, stream_id, opentracing_context FROM device_lists_outbound_pokes
785
+ WHERE destination = ? AND ? < stream_id AND stream_id <= ?
786
+ ORDER BY stream_id
787
+ LIMIT ?
788
+ """
789
+ txn.execute(sql, (destination, from_stream_id, now_stream_id, limit))
790
+
791
+ return cast(list[tuple[str, str, int, Optional[str]]], txn.fetchall())
792
+
793
+ async def _get_device_update_edus_by_remote(
794
+ self,
795
+ destination: str,
796
+ from_stream_id: int,
797
+ query_map: dict[tuple[str, str], tuple[int, Optional[str]]],
798
+ ) -> list[tuple[str, dict]]:
799
+ """Returns a list of device update EDUs as well as E2EE keys
800
+
801
+ Args:
802
+ destination: The host the device updates are intended for
803
+ from_stream_id: The minimum stream_id to filter updates by, exclusive
804
+ query_map: Dictionary mapping (user_id, device_id) to
805
+ (update stream_id, the relevant json-encoded opentracing context)
806
+
807
+ Returns:
808
+ List of objects representing a device update EDU.
809
+
810
+ Postconditions:
811
+ The returned list has a length not exceeding that of the query_map:
812
+ len(result) <= len(query_map)
813
+ """
814
+ devices = (
815
+ await self.get_e2e_device_keys_and_signatures(
816
+ # Because these are (user_id, device_id) tuples with all
817
+ # device_ids not being None, the returned list's length will not
818
+ # exceed that of query_map.
819
+ query_map.keys(),
820
+ include_all_devices=True,
821
+ include_deleted_devices=True,
822
+ )
823
+ if query_map
824
+ else {}
825
+ )
826
+
827
+ results = []
828
+ for user_id, user_devices in devices.items():
829
+ # The prev_id for the first row is always the last row before
830
+ # `from_stream_id`
831
+ prev_id = await self._get_last_device_update_for_remote_user(
832
+ destination, user_id, from_stream_id
833
+ )
834
+
835
+ # make sure we go through the devices in stream order
836
+ device_ids = sorted(
837
+ user_devices.keys(),
838
+ key=lambda i: query_map[(user_id, i)][0],
839
+ )
840
+
841
+ for device_id in device_ids:
842
+ device = user_devices[device_id]
843
+ stream_id, opentracing_context = query_map[(user_id, device_id)]
844
+ result = {
845
+ "user_id": user_id,
846
+ "device_id": device_id,
847
+ "prev_id": [prev_id] if prev_id else [],
848
+ "stream_id": stream_id,
849
+ }
850
+
851
+ if opentracing_context != "{}":
852
+ result["org.matrix.opentracing_context"] = opentracing_context
853
+
854
+ prev_id = stream_id
855
+
856
+ if device is not None:
857
+ keys = device.keys
858
+ if keys:
859
+ result["keys"] = keys
860
+
861
+ device_display_name = None
862
+ if self.hs.config.federation.allow_device_name_lookup_over_federation:
863
+ device_display_name = device.display_name
864
+ if device_display_name:
865
+ result["device_display_name"] = device_display_name
866
+ else:
867
+ result["deleted"] = True
868
+
869
+ results.append((EduTypes.DEVICE_LIST_UPDATE, result))
870
+
871
+ return results
872
+
873
+ async def _get_last_device_update_for_remote_user(
874
+ self, destination: str, user_id: str, from_stream_id: int
875
+ ) -> int:
876
+ def f(txn: LoggingTransaction) -> int:
877
+ prev_sent_id_sql = """
878
+ SELECT coalesce(max(stream_id), 0) as stream_id
879
+ FROM device_lists_outbound_last_success
880
+ WHERE destination = ? AND user_id = ? AND stream_id <= ?
881
+ """
882
+ txn.execute(prev_sent_id_sql, (destination, user_id, from_stream_id))
883
+ rows = txn.fetchall()
884
+ return rows[0][0]
885
+
886
+ return await self.db_pool.runInteraction(
887
+ "get_last_device_update_for_remote_user", f
888
+ )
889
+
890
+ async def mark_as_sent_devices_by_remote(
891
+ self, destination: str, stream_id: int
892
+ ) -> None:
893
+ """Mark that updates have successfully been sent to the destination."""
894
+ await self.db_pool.runInteraction(
895
+ "mark_as_sent_devices_by_remote",
896
+ self._mark_as_sent_devices_by_remote_txn,
897
+ destination,
898
+ stream_id,
899
+ )
900
+
901
+ def _mark_as_sent_devices_by_remote_txn(
902
+ self, txn: LoggingTransaction, destination: str, stream_id: int
903
+ ) -> None:
904
+ # We update the device_lists_outbound_last_success with the successfully
905
+ # poked users.
906
+ sql = """
907
+ SELECT user_id, coalesce(max(o.stream_id), 0)
908
+ FROM device_lists_outbound_pokes as o
909
+ WHERE destination = ? AND o.stream_id <= ?
910
+ GROUP BY user_id
911
+ """
912
+ txn.execute(sql, (destination, stream_id))
913
+ rows = txn.fetchall()
914
+
915
+ self.db_pool.simple_upsert_many_txn(
916
+ txn=txn,
917
+ table="device_lists_outbound_last_success",
918
+ key_names=("destination", "user_id"),
919
+ key_values=[(destination, user_id) for user_id, _ in rows],
920
+ value_names=("stream_id",),
921
+ value_values=[(stream_id,) for _, stream_id in rows],
922
+ )
923
+
924
+ # Delete all sent outbound pokes
925
+ sql = """
926
+ DELETE FROM device_lists_outbound_pokes
927
+ WHERE destination = ? AND stream_id <= ?
928
+ """
929
+ txn.execute(sql, (destination, stream_id))
930
+
931
+ async def add_user_signature_change_to_streams(
932
+ self, from_user_id: str, user_ids: list[str]
933
+ ) -> int:
934
+ """Persist that a user has made new signatures
935
+
936
+ Args:
937
+ from_user_id: the user who made the signatures
938
+ user_ids: the users who were signed
939
+
940
+ Returns:
941
+ The new stream ID.
942
+ """
943
+ # This generates new stream IDs and therefore must be called on a writer.
944
+ if not self._is_device_list_writer:
945
+ raise Exception("Can only be called on device list writers")
946
+
947
+ async with self._device_list_id_gen.get_next() as stream_id:
948
+ await self.db_pool.runInteraction(
949
+ "add_user_sig_change_to_streams",
950
+ self._add_user_signature_change_txn,
951
+ from_user_id,
952
+ user_ids,
953
+ stream_id,
954
+ )
955
+ return stream_id
956
+
957
+ def _add_user_signature_change_txn(
958
+ self,
959
+ txn: LoggingTransaction,
960
+ from_user_id: str,
961
+ user_ids: list[str],
962
+ stream_id: int,
963
+ ) -> None:
964
+ txn.call_after(
965
+ self._user_signature_stream_cache.entity_has_changed,
966
+ from_user_id,
967
+ stream_id,
968
+ )
969
+ self.db_pool.simple_insert_txn(
970
+ txn,
971
+ "user_signature_stream",
972
+ values={
973
+ "stream_id": stream_id,
974
+ "from_user_id": from_user_id,
975
+ "user_ids": json_encoder.encode(user_ids),
976
+ "instance_name": self._instance_name,
977
+ },
978
+ )
979
+
980
+ @trace
981
+ @cancellable
982
+ async def get_user_devices_from_cache(
983
+ self, user_ids: set[str], user_and_device_ids: list[tuple[str, str]]
984
+ ) -> tuple[set[str], dict[str, Mapping[str, JsonMapping]]]:
985
+ """Get the devices (and keys if any) for remote users from the cache.
986
+
987
+ Args:
988
+ user_ids: users which should have all device IDs returned
989
+ user_and_device_ids: List of (user_id, device_ids)
990
+
991
+ Returns:
992
+ A tuple of (user_ids_not_in_cache, results_map), where
993
+ user_ids_not_in_cache is a set of user_ids and results_map is a
994
+ mapping of user_id -> device_id -> device_info.
995
+ """
996
+ unique_user_ids = user_ids | {user_id for user_id, _ in user_and_device_ids}
997
+
998
+ user_ids_in_cache = await self.get_users_whose_devices_are_cached(
999
+ unique_user_ids
1000
+ )
1001
+ user_ids_not_in_cache = unique_user_ids - user_ids_in_cache
1002
+
1003
+ # First fetch all the users which all devices are to be returned.
1004
+ results: dict[str, Mapping[str, JsonMapping]] = {}
1005
+ for user_id in user_ids:
1006
+ if user_id in user_ids_in_cache:
1007
+ results[user_id] = await self.get_cached_devices_for_user(user_id)
1008
+ # Then fetch all device-specific requests, but skip users we've already
1009
+ # fetched all devices for.
1010
+ device_specific_results: dict[str, dict[str, JsonMapping]] = {}
1011
+ for user_id, device_id in user_and_device_ids:
1012
+ if user_id in user_ids_in_cache and user_id not in user_ids:
1013
+ device = await self._get_cached_user_device(user_id, device_id)
1014
+ device_specific_results.setdefault(user_id, {})[device_id] = device
1015
+ results.update(device_specific_results)
1016
+
1017
+ set_tag("in_cache", str(results))
1018
+ set_tag("not_in_cache", str(user_ids_not_in_cache))
1019
+
1020
+ return user_ids_not_in_cache, results
1021
+
1022
+ async def get_users_whose_devices_are_cached(
1023
+ self, user_ids: StrCollection
1024
+ ) -> set[str]:
1025
+ """Checks which of the given users we have cached the devices for."""
1026
+ user_map = await self.get_device_list_last_stream_id_for_remotes(user_ids)
1027
+
1028
+ # We go and check if any of the users need to have their device lists
1029
+ # resynced. If they do then we remove them from the cached list.
1030
+ users_needing_resync = await self.get_user_ids_requiring_device_list_resync(
1031
+ user_ids
1032
+ )
1033
+ user_ids_in_cache = {
1034
+ user_id for user_id, stream_id in user_map.items() if stream_id
1035
+ } - users_needing_resync
1036
+ return user_ids_in_cache
1037
+
1038
+ @cached(num_args=2, tree=True)
1039
+ async def _get_cached_user_device(
1040
+ self, user_id: str, device_id: str
1041
+ ) -> JsonMapping:
1042
+ content = await self.db_pool.simple_select_one_onecol(
1043
+ table="device_lists_remote_cache",
1044
+ keyvalues={"user_id": user_id, "device_id": device_id},
1045
+ retcol="content",
1046
+ desc="_get_cached_user_device",
1047
+ )
1048
+ return db_to_json(content)
1049
+
1050
+ @cached()
1051
+ async def get_cached_devices_for_user(
1052
+ self, user_id: str
1053
+ ) -> Mapping[str, JsonMapping]:
1054
+ devices = cast(
1055
+ list[tuple[str, str]],
1056
+ await self.db_pool.simple_select_list(
1057
+ table="device_lists_remote_cache",
1058
+ keyvalues={"user_id": user_id},
1059
+ retcols=("device_id", "content"),
1060
+ desc="get_cached_devices_for_user",
1061
+ ),
1062
+ )
1063
+ return {device[0]: db_to_json(device[1]) for device in devices}
1064
+
1065
+ @cancellable
1066
+ async def get_all_devices_changed(
1067
+ self,
1068
+ from_key: MultiWriterStreamToken,
1069
+ to_key: MultiWriterStreamToken,
1070
+ ) -> set[str]:
1071
+ """Get all users whose devices have changed in the given range.
1072
+
1073
+ Args:
1074
+ from_key: The minimum device lists stream token to query device list
1075
+ changes for, exclusive.
1076
+ to_key: The maximum device lists stream token to query device list
1077
+ changes for, inclusive.
1078
+
1079
+ Returns:
1080
+ The set of user_ids whose devices have changed since `from_key`
1081
+ (exclusive) until `to_key` (inclusive).
1082
+ """
1083
+
1084
+ result = self._device_list_stream_cache.get_all_entities_changed(
1085
+ from_key.stream
1086
+ )
1087
+
1088
+ if result.hit:
1089
+ # We know which users might have changed devices.
1090
+ if not result.entities:
1091
+ # If no users then we can return early.
1092
+ return set()
1093
+
1094
+ # Otherwise we need to filter down the list
1095
+ return await self.get_users_whose_devices_changed(
1096
+ from_key, result.entities, to_key
1097
+ )
1098
+
1099
+ # If the cache didn't tell us anything, we just need to query the full
1100
+ # range.
1101
+ sql = """
1102
+ SELECT user_id, stream_id, instance_name
1103
+ FROM device_lists_stream
1104
+ WHERE ? < stream_id AND stream_id <= ?
1105
+ """
1106
+
1107
+ rows = await self.db_pool.execute(
1108
+ "get_all_devices_changed",
1109
+ sql,
1110
+ from_key.stream,
1111
+ to_key.get_max_stream_pos(),
1112
+ )
1113
+ return {
1114
+ user_id
1115
+ for (user_id, stream_id, instance_name) in rows
1116
+ if MultiWriterStreamToken.is_stream_position_in_range(
1117
+ low=from_key,
1118
+ high=to_key,
1119
+ instance_name=instance_name,
1120
+ pos=stream_id,
1121
+ )
1122
+ }
1123
+
1124
+ @cancellable
1125
+ async def get_users_whose_devices_changed(
1126
+ self,
1127
+ from_key: MultiWriterStreamToken,
1128
+ user_ids: Collection[str],
1129
+ to_key: Optional[MultiWriterStreamToken] = None,
1130
+ ) -> set[str]:
1131
+ """Get set of users whose devices have changed since `from_key` that
1132
+ are in the given list of user_ids.
1133
+
1134
+ Args:
1135
+ from_key: The minimum device lists stream token to query device list changes for,
1136
+ exclusive.
1137
+ user_ids: If provided, only check if these users have changed their device lists.
1138
+ Otherwise changes from all users are returned.
1139
+ to_key: The maximum device lists stream token to query device list changes for,
1140
+ inclusive.
1141
+
1142
+ Returns:
1143
+ The set of user_ids whose devices have changed since `from_key` (exclusive)
1144
+ until `to_key` (inclusive).
1145
+ """
1146
+ # Get set of users who *may* have changed. Users not in the returned
1147
+ # list have definitely not changed.
1148
+ user_ids_to_check = self._device_list_stream_cache.get_entities_changed(
1149
+ user_ids, from_key.stream
1150
+ )
1151
+
1152
+ # If an empty set was returned, there's nothing to do.
1153
+ if not user_ids_to_check:
1154
+ return set()
1155
+
1156
+ if to_key is None:
1157
+ to_key = self.get_device_stream_token()
1158
+
1159
+ def _get_users_whose_devices_changed_txn(
1160
+ txn: LoggingTransaction,
1161
+ from_key: MultiWriterStreamToken,
1162
+ to_key: MultiWriterStreamToken,
1163
+ ) -> set[str]:
1164
+ sql = """
1165
+ SELECT user_id, stream_id, instance_name
1166
+ FROM device_lists_stream
1167
+ WHERE ? < stream_id AND stream_id <= ? AND %s
1168
+ """
1169
+
1170
+ changes: set[str] = set()
1171
+
1172
+ # Query device changes with a batch of users at a time
1173
+ for chunk in batch_iter(user_ids_to_check, 100):
1174
+ clause, args = make_in_list_sql_clause(
1175
+ txn.database_engine, "user_id", chunk
1176
+ )
1177
+ txn.execute(
1178
+ sql % (clause,),
1179
+ [from_key.stream, to_key.get_max_stream_pos()] + args,
1180
+ )
1181
+ changes.update(
1182
+ user_id
1183
+ for (user_id, stream_id, instance_name) in txn
1184
+ if MultiWriterStreamToken.is_stream_position_in_range(
1185
+ low=from_key,
1186
+ high=to_key,
1187
+ instance_name=instance_name,
1188
+ pos=stream_id,
1189
+ )
1190
+ )
1191
+
1192
+ return changes
1193
+
1194
+ return await self.db_pool.runInteraction(
1195
+ "get_users_whose_devices_changed",
1196
+ _get_users_whose_devices_changed_txn,
1197
+ from_key,
1198
+ to_key,
1199
+ )
1200
+
1201
+ async def get_users_whose_signatures_changed(
1202
+ self, user_id: str, from_key: MultiWriterStreamToken
1203
+ ) -> set[str]:
1204
+ """Get the users who have new cross-signing signatures made by `user_id` since
1205
+ `from_key`.
1206
+
1207
+ Args:
1208
+ user_id: the user who made the signatures
1209
+ from_key: The device lists stream token
1210
+
1211
+ Returns:
1212
+ A set of user IDs with updated signatures.
1213
+ """
1214
+
1215
+ if not self._user_signature_stream_cache.has_entity_changed(
1216
+ user_id, from_key.stream
1217
+ ):
1218
+ return set()
1219
+
1220
+ sql = """
1221
+ SELECT user_ids, stream_id, instance_name
1222
+ FROM user_signature_stream
1223
+ WHERE from_user_id = ? AND stream_id > ?
1224
+ """
1225
+ rows = await self.db_pool.execute(
1226
+ "get_users_whose_signatures_changed", sql, user_id, from_key.stream
1227
+ )
1228
+ return {
1229
+ user
1230
+ for (user_ids, stream_id, instance_name) in rows
1231
+ if MultiWriterStreamToken.is_stream_position_in_range(
1232
+ low=from_key,
1233
+ high=None,
1234
+ instance_name=instance_name,
1235
+ pos=stream_id,
1236
+ )
1237
+ for user in db_to_json(user_ids)
1238
+ }
1239
+
1240
+ async def get_all_device_list_changes_for_remotes(
1241
+ self, instance_name: str, last_id: int, current_id: int, limit: int
1242
+ ) -> tuple[list[tuple[int, tuple]], int, bool]:
1243
+ """Get updates for device lists replication stream.
1244
+
1245
+ Args:
1246
+ instance_name: The writer we want to fetch updates from. Unused
1247
+ here since there is only ever one writer.
1248
+ last_id: The token to fetch updates from. Exclusive.
1249
+ current_id: The token to fetch updates up to. Inclusive.
1250
+ limit: The requested limit for the number of rows to return. The
1251
+ function may return more or fewer rows.
1252
+
1253
+ Returns:
1254
+ A tuple consisting of: the updates, a token to use to fetch
1255
+ subsequent updates, and whether we returned fewer rows than exists
1256
+ between the requested tokens due to the limit.
1257
+
1258
+ The token returned can be used in a subsequent call to this
1259
+ function to get further updates.
1260
+
1261
+ The updates are a list of 2-tuples of stream ID and the row data
1262
+ """
1263
+
1264
+ if last_id == current_id:
1265
+ return [], current_id, False
1266
+
1267
+ def _get_all_device_list_changes_for_remotes(
1268
+ txn: Cursor,
1269
+ ) -> tuple[list[tuple[int, tuple]], int, bool]:
1270
+ # This query Does The Right Thing where it'll correctly apply the
1271
+ # bounds to the inner queries.
1272
+ sql = """
1273
+ SELECT stream_id, user_id, hosts FROM (
1274
+ SELECT stream_id, user_id, false AS hosts FROM device_lists_stream
1275
+ UNION ALL
1276
+ SELECT DISTINCT stream_id, user_id, true AS hosts FROM device_lists_outbound_pokes
1277
+ ) AS e
1278
+ WHERE ? < stream_id AND stream_id <= ?
1279
+ ORDER BY stream_id ASC
1280
+ LIMIT ?
1281
+ """
1282
+
1283
+ txn.execute(sql, (last_id, current_id, limit))
1284
+ updates = [(row[0], row[1:]) for row in txn]
1285
+ limited = False
1286
+ upto_token = current_id
1287
+ if len(updates) >= limit:
1288
+ upto_token = updates[-1][0]
1289
+ limited = True
1290
+
1291
+ return updates, upto_token, limited
1292
+
1293
+ return await self.db_pool.runInteraction(
1294
+ "get_all_device_list_changes_for_remotes",
1295
+ _get_all_device_list_changes_for_remotes,
1296
+ )
1297
+
1298
+ @cached(max_entries=10000)
1299
+ async def get_device_list_last_stream_id_for_remote(
1300
+ self, user_id: str
1301
+ ) -> Optional[str]:
1302
+ """Get the last stream_id we got for a user. May be None if we haven't
1303
+ got any information for them.
1304
+ """
1305
+ return await self.db_pool.simple_select_one_onecol(
1306
+ table="device_lists_remote_extremeties",
1307
+ keyvalues={"user_id": user_id},
1308
+ retcol="stream_id",
1309
+ desc="get_device_list_last_stream_id_for_remote",
1310
+ allow_none=True,
1311
+ )
1312
+
1313
+ @cachedList(
1314
+ cached_method_name="get_device_list_last_stream_id_for_remote",
1315
+ list_name="user_ids",
1316
+ )
1317
+ async def get_device_list_last_stream_id_for_remotes(
1318
+ self, user_ids: Iterable[str]
1319
+ ) -> Mapping[str, Optional[str]]:
1320
+ rows = cast(
1321
+ list[tuple[str, str]],
1322
+ await self.db_pool.simple_select_many_batch(
1323
+ table="device_lists_remote_extremeties",
1324
+ column="user_id",
1325
+ iterable=user_ids,
1326
+ retcols=("user_id", "stream_id"),
1327
+ desc="get_device_list_last_stream_id_for_remotes",
1328
+ ),
1329
+ )
1330
+
1331
+ results: dict[str, Optional[str]] = dict.fromkeys(user_ids)
1332
+ results.update(rows)
1333
+
1334
+ return results
1335
+
1336
+ async def get_user_ids_requiring_device_list_resync(
1337
+ self,
1338
+ user_ids: Optional[Collection[str]] = None,
1339
+ ) -> set[str]:
1340
+ """Given a list of remote users return the list of users that we
1341
+ should resync the device lists for. If None is given instead of a list,
1342
+ return every user that we should resync the device lists for.
1343
+
1344
+ Returns:
1345
+ The IDs of users whose device lists need resync.
1346
+ """
1347
+ if user_ids:
1348
+ rows = cast(
1349
+ list[tuple[str]],
1350
+ await self.db_pool.simple_select_many_batch(
1351
+ table="device_lists_remote_resync",
1352
+ column="user_id",
1353
+ iterable=user_ids,
1354
+ retcols=("user_id",),
1355
+ desc="get_user_ids_requiring_device_list_resync_with_iterable",
1356
+ ),
1357
+ )
1358
+ else:
1359
+ rows = cast(
1360
+ list[tuple[str]],
1361
+ await self.db_pool.simple_select_list(
1362
+ table="device_lists_remote_resync",
1363
+ keyvalues=None,
1364
+ retcols=("user_id",),
1365
+ desc="get_user_ids_requiring_device_list_resync",
1366
+ ),
1367
+ )
1368
+
1369
+ return {row[0] for row in rows}
1370
+
1371
+ async def mark_remote_users_device_caches_as_stale(
1372
+ self, user_ids: StrCollection
1373
+ ) -> None:
1374
+ """Records that the server has reason to believe the cache of the devices
1375
+ for the remote users is out of date.
1376
+ """
1377
+
1378
+ def _mark_remote_users_device_caches_as_stale_txn(
1379
+ txn: LoggingTransaction,
1380
+ ) -> None:
1381
+ # TODO add insertion_values support to simple_upsert_many and use
1382
+ # that!
1383
+ for user_id in user_ids:
1384
+ self.db_pool.simple_upsert_txn(
1385
+ txn,
1386
+ table="device_lists_remote_resync",
1387
+ keyvalues={"user_id": user_id},
1388
+ values={},
1389
+ insertion_values={"added_ts": self.clock.time_msec()},
1390
+ )
1391
+
1392
+ await self.db_pool.runInteraction(
1393
+ "mark_remote_users_device_caches_as_stale",
1394
+ _mark_remote_users_device_caches_as_stale_txn,
1395
+ )
1396
+
1397
+ async def mark_remote_user_device_cache_as_valid(self, user_id: str) -> None:
1398
+ # Remove the database entry that says we need to resync devices, after a resync
1399
+ await self.db_pool.simple_delete(
1400
+ table="device_lists_remote_resync",
1401
+ keyvalues={"user_id": user_id},
1402
+ desc="mark_remote_user_device_cache_as_valid",
1403
+ )
1404
+
1405
+ async def handle_potentially_left_users(self, user_ids: set[str]) -> None:
1406
+ """Given a set of remote users check if the server still shares a room with
1407
+ them. If not then mark those users' device cache as stale.
1408
+ """
1409
+
1410
+ if not user_ids:
1411
+ return
1412
+
1413
+ await self.db_pool.runInteraction(
1414
+ "_handle_potentially_left_users",
1415
+ self.handle_potentially_left_users_txn,
1416
+ user_ids,
1417
+ )
1418
+
1419
+ def handle_potentially_left_users_txn(
1420
+ self,
1421
+ txn: LoggingTransaction,
1422
+ user_ids: set[str],
1423
+ ) -> None:
1424
+ """Given a set of remote users check if the server still shares a room with
1425
+ them. If not then mark those users' device cache as stale.
1426
+ """
1427
+
1428
+ if not user_ids:
1429
+ return
1430
+
1431
+ joined_users = self.get_users_server_still_shares_room_with_txn(txn, user_ids)
1432
+ left_users = user_ids - joined_users
1433
+
1434
+ for user_id in left_users:
1435
+ self.mark_remote_user_device_list_as_unsubscribed_txn(txn, user_id)
1436
+
1437
+ async def mark_remote_user_device_list_as_unsubscribed(self, user_id: str) -> None:
1438
+ """Mark that we no longer track device lists for remote user."""
1439
+
1440
+ await self.db_pool.runInteraction(
1441
+ "mark_remote_user_device_list_as_unsubscribed",
1442
+ self.mark_remote_user_device_list_as_unsubscribed_txn,
1443
+ user_id,
1444
+ )
1445
+
1446
+ def mark_remote_user_device_list_as_unsubscribed_txn(
1447
+ self,
1448
+ txn: LoggingTransaction,
1449
+ user_id: str,
1450
+ ) -> None:
1451
+ self.db_pool.simple_delete_txn(
1452
+ txn,
1453
+ table="device_lists_remote_extremeties",
1454
+ keyvalues={"user_id": user_id},
1455
+ )
1456
+ self._invalidate_cache_and_stream(
1457
+ txn, self.get_device_list_last_stream_id_for_remote, (user_id,)
1458
+ )
1459
+
1460
+ async def get_dehydrated_device(
1461
+ self, user_id: str
1462
+ ) -> Optional[tuple[str, JsonDict]]:
1463
+ """Retrieve the information for a dehydrated device.
1464
+
1465
+ Args:
1466
+ user_id: the user whose dehydrated device we are looking for
1467
+ Returns:
1468
+ a tuple whose first item is the device ID, and the second item is
1469
+ the dehydrated device information
1470
+ """
1471
+ # FIXME: make sure device ID still exists in devices table
1472
+ row = await self.db_pool.simple_select_one(
1473
+ table="dehydrated_devices",
1474
+ keyvalues={"user_id": user_id},
1475
+ retcols=["device_id", "device_data"],
1476
+ allow_none=True,
1477
+ )
1478
+ return (row[0], json_decoder.decode(row[1])) if row else None
1479
+
1480
+ def _store_dehydrated_device_txn(
1481
+ self,
1482
+ txn: LoggingTransaction,
1483
+ user_id: str,
1484
+ device_id: str,
1485
+ device_data: str,
1486
+ time: int,
1487
+ keys: Optional[JsonDict] = None,
1488
+ ) -> Optional[str]:
1489
+ # TODO: make keys non-optional once support for msc2697 is dropped
1490
+ if keys:
1491
+ device_keys = keys.get("device_keys", None)
1492
+ if device_keys:
1493
+ self._set_e2e_device_keys_txn(
1494
+ txn, user_id, device_id, time, device_keys
1495
+ )
1496
+
1497
+ one_time_keys = keys.get("one_time_keys", None)
1498
+ if one_time_keys:
1499
+ key_list = []
1500
+ for key_id, key_obj in one_time_keys.items():
1501
+ algorithm, key_id = key_id.split(":")
1502
+ key_list.append(
1503
+ (
1504
+ algorithm,
1505
+ key_id,
1506
+ encode_canonical_json(key_obj).decode("ascii"),
1507
+ )
1508
+ )
1509
+ self._add_e2e_one_time_keys_txn(txn, user_id, device_id, time, key_list)
1510
+
1511
+ fallback_keys = keys.get("fallback_keys", None)
1512
+ if fallback_keys:
1513
+ self._set_e2e_fallback_keys_txn(txn, user_id, device_id, fallback_keys)
1514
+
1515
+ old_device_id = self.db_pool.simple_select_one_onecol_txn(
1516
+ txn,
1517
+ table="dehydrated_devices",
1518
+ keyvalues={"user_id": user_id},
1519
+ retcol="device_id",
1520
+ allow_none=True,
1521
+ )
1522
+ self.db_pool.simple_upsert_txn(
1523
+ txn,
1524
+ table="dehydrated_devices",
1525
+ keyvalues={"user_id": user_id},
1526
+ values={"device_id": device_id, "device_data": device_data},
1527
+ )
1528
+
1529
+ return old_device_id
1530
+
1531
+ async def store_dehydrated_device(
1532
+ self,
1533
+ user_id: str,
1534
+ device_id: str,
1535
+ device_data: JsonDict,
1536
+ time_now: int,
1537
+ keys: Optional[dict] = None,
1538
+ ) -> Optional[str]:
1539
+ """Store a dehydrated device for a user.
1540
+
1541
+ Args:
1542
+ user_id: the user that we are storing the device for
1543
+ device_id: the ID of the dehydrated device
1544
+ device_data: the dehydrated device information
1545
+ time_now: current time at the request in milliseconds
1546
+ keys: keys for the dehydrated device
1547
+
1548
+ Returns:
1549
+ device id of the user's previous dehydrated device, if any
1550
+ """
1551
+
1552
+ return await self.db_pool.runInteraction(
1553
+ "store_dehydrated_device_txn",
1554
+ self._store_dehydrated_device_txn,
1555
+ user_id,
1556
+ device_id,
1557
+ json_encoder.encode(device_data),
1558
+ time_now,
1559
+ keys,
1560
+ )
1561
+
1562
+ async def remove_dehydrated_device(self, user_id: str, device_id: str) -> bool:
1563
+ """Remove a dehydrated device.
1564
+
1565
+ Args:
1566
+ user_id: the user that the dehydrated device belongs to
1567
+ device_id: the ID of the dehydrated device
1568
+ """
1569
+ count = await self.db_pool.simple_delete(
1570
+ "dehydrated_devices",
1571
+ {"user_id": user_id, "device_id": device_id},
1572
+ desc="remove_dehydrated_device",
1573
+ )
1574
+ return count >= 1
1575
+
1576
+ @wrap_as_background_process("prune_old_outbound_device_pokes")
1577
+ async def _prune_old_outbound_device_pokes(
1578
+ self, prune_age: int = 24 * 60 * 60 * 1000
1579
+ ) -> None:
1580
+ """Delete old entries out of the device_lists_outbound_pokes to ensure
1581
+ that we don't fill up due to dead servers.
1582
+
1583
+ Normally, we try to send device updates as a delta since a previous known point:
1584
+ this is done by setting the prev_id in the m.device_list_update EDU. However,
1585
+ for that to work, we have to have a complete record of each change to
1586
+ each device, which can add up to quite a lot of data.
1587
+
1588
+ An alternative mechanism is that, if the remote server sees that it has missed
1589
+ an entry in the stream_id sequence for a given user, it will request a full
1590
+ list of that user's devices. Hence, we can reduce the amount of data we have to
1591
+ store (and transmit in some future transaction), by clearing almost everything
1592
+ for a given destination out of the database, and having the remote server
1593
+ resync.
1594
+
1595
+ All we need to do is make sure we keep at least one row for each
1596
+ (user, destination) pair, to remind us to send a m.device_list_update EDU for
1597
+ that user when the destination comes back. It doesn't matter which device
1598
+ we keep.
1599
+ """
1600
+ yesterday = self.clock.time_msec() - prune_age
1601
+
1602
+ def _prune_txn(txn: LoggingTransaction) -> None:
1603
+ # look for (user, destination) pairs which have an update older than
1604
+ # the cutoff.
1605
+ #
1606
+ # For each pair, we also need to know the most recent stream_id, and
1607
+ # an arbitrary device_id at that stream_id.
1608
+ select_sql = """
1609
+ SELECT
1610
+ dlop1.destination,
1611
+ dlop1.user_id,
1612
+ MAX(dlop1.stream_id) AS stream_id,
1613
+ (SELECT MIN(dlop2.device_id) AS device_id FROM
1614
+ device_lists_outbound_pokes dlop2
1615
+ WHERE dlop2.destination = dlop1.destination AND
1616
+ dlop2.user_id=dlop1.user_id AND
1617
+ dlop2.stream_id=MAX(dlop1.stream_id)
1618
+ )
1619
+ FROM device_lists_outbound_pokes dlop1
1620
+ GROUP BY destination, user_id
1621
+ HAVING min(ts) < ? AND count(*) > 1
1622
+ """
1623
+
1624
+ txn.execute(select_sql, (yesterday,))
1625
+ rows = txn.fetchall()
1626
+
1627
+ if not rows:
1628
+ return
1629
+
1630
+ logger.info(
1631
+ "Pruning old outbound device list updates for %i users/destinations: %s",
1632
+ len(rows),
1633
+ shortstr((row[0], row[1]) for row in rows),
1634
+ )
1635
+
1636
+ # we want to keep the update with the highest stream_id for each user.
1637
+ #
1638
+ # there might be more than one update (with different device_ids) with the
1639
+ # same stream_id, so we also delete all but one rows with the max stream id.
1640
+ delete_sql = """
1641
+ DELETE FROM device_lists_outbound_pokes
1642
+ WHERE destination = ? AND user_id = ? AND (
1643
+ stream_id < ? OR
1644
+ (stream_id = ? AND device_id != ?)
1645
+ )
1646
+ """
1647
+ count = 0
1648
+ for destination, user_id, stream_id, device_id in rows:
1649
+ txn.execute(
1650
+ delete_sql, (destination, user_id, stream_id, stream_id, device_id)
1651
+ )
1652
+ count += txn.rowcount
1653
+
1654
+ # Since we've deleted unsent deltas, we need to remove the entry
1655
+ # of last successful sent so that the prev_ids are correctly set.
1656
+ sql = """
1657
+ DELETE FROM device_lists_outbound_last_success
1658
+ WHERE destination = ? AND user_id = ?
1659
+ """
1660
+ txn.execute_batch(sql, [(row[0], row[1]) for row in rows])
1661
+
1662
+ logger.info("Pruned %d device list outbound pokes", count)
1663
+
1664
+ await self.db_pool.runInteraction(
1665
+ "_prune_old_outbound_device_pokes",
1666
+ _prune_txn,
1667
+ )
1668
+
1669
+ async def get_local_devices_not_accessed_since(
1670
+ self, since_ms: int
1671
+ ) -> dict[str, list[str]]:
1672
+ """Retrieves local devices that haven't been accessed since a given date.
1673
+
1674
+ Args:
1675
+ since_ms: the timestamp to select on, every device with a last access date
1676
+ from before that time is returned.
1677
+
1678
+ Returns:
1679
+ A dictionary with an entry for each user with at least one device matching
1680
+ the request, which value is a list of the device ID(s) for the corresponding
1681
+ device(s).
1682
+ """
1683
+
1684
+ def get_devices_not_accessed_since_txn(
1685
+ txn: LoggingTransaction,
1686
+ ) -> list[tuple[str, str]]:
1687
+ sql = """
1688
+ SELECT user_id, device_id
1689
+ FROM devices WHERE last_seen < ? AND hidden = FALSE
1690
+ """
1691
+ txn.execute(sql, (since_ms,))
1692
+ return cast(list[tuple[str, str]], txn.fetchall())
1693
+
1694
+ rows = await self.db_pool.runInteraction(
1695
+ "get_devices_not_accessed_since",
1696
+ get_devices_not_accessed_since_txn,
1697
+ )
1698
+
1699
+ devices: dict[str, list[str]] = {}
1700
+ for user_id, device_id in rows:
1701
+ # Remote devices are never stale from our point of view.
1702
+ if self.hs.is_mine_id(user_id):
1703
+ user_devices = devices.setdefault(user_id, [])
1704
+ user_devices.append(device_id)
1705
+
1706
+ return devices
1707
+
1708
+ @cached()
1709
+ async def _get_min_device_lists_changes_in_room(self) -> int:
1710
+ """Returns the minimum stream ID that we have entries for
1711
+ `device_lists_changes_in_room`
1712
+ """
1713
+
1714
+ return await self.db_pool.simple_select_one_onecol(
1715
+ table="device_lists_changes_in_room",
1716
+ keyvalues={},
1717
+ retcol="COALESCE(MIN(stream_id), 0)",
1718
+ desc="get_min_device_lists_changes_in_room",
1719
+ )
1720
+
1721
+ @cancellable
1722
+ async def get_device_list_changes_in_rooms(
1723
+ self,
1724
+ room_ids: Collection[str],
1725
+ from_token: MultiWriterStreamToken,
1726
+ to_token: MultiWriterStreamToken,
1727
+ ) -> Optional[set[str]]:
1728
+ """Return the set of users whose devices have changed in the given rooms
1729
+ since the given stream ID.
1730
+
1731
+ Returns None if the given stream ID is too old.
1732
+ """
1733
+
1734
+ if not room_ids:
1735
+ return set()
1736
+
1737
+ min_stream_id = await self._get_min_device_lists_changes_in_room()
1738
+
1739
+ # Return early if there are no rows to process in device_lists_changes_in_room
1740
+ if min_stream_id > from_token.stream:
1741
+ return None
1742
+
1743
+ changed_room_ids = self._device_list_room_stream_cache.get_entities_changed(
1744
+ room_ids, from_token.stream
1745
+ )
1746
+ if not changed_room_ids:
1747
+ return set()
1748
+
1749
+ sql = """
1750
+ SELECT user_id, stream_id, instance_name
1751
+ FROM device_lists_changes_in_room
1752
+ WHERE {clause} AND stream_id > ? AND stream_id <= ?
1753
+ """
1754
+
1755
+ def _get_device_list_changes_in_rooms_txn(
1756
+ txn: LoggingTransaction,
1757
+ chunk: list[str],
1758
+ ) -> set[str]:
1759
+ clause, args = make_in_list_sql_clause(
1760
+ self.database_engine, "room_id", chunk
1761
+ )
1762
+ args.append(from_token.stream)
1763
+ args.append(to_token.get_max_stream_pos())
1764
+
1765
+ txn.execute(sql.format(clause=clause), args)
1766
+ return {
1767
+ user_id
1768
+ for (user_id, stream_id, instance_name) in txn
1769
+ if MultiWriterStreamToken.is_stream_position_in_range(
1770
+ low=from_token,
1771
+ high=to_token,
1772
+ instance_name=instance_name,
1773
+ pos=stream_id,
1774
+ )
1775
+ }
1776
+
1777
+ changes = set()
1778
+ for chunk in batch_iter(changed_room_ids, 1000):
1779
+ changes |= await self.db_pool.runInteraction(
1780
+ "get_device_list_changes_in_rooms",
1781
+ _get_device_list_changes_in_rooms_txn,
1782
+ chunk,
1783
+ )
1784
+
1785
+ return changes
1786
+
1787
+ async def get_all_device_list_changes(self, from_id: int, to_id: int) -> set[str]:
1788
+ """Return the set of rooms where devices have changed since the given
1789
+ stream ID.
1790
+
1791
+ Will raise an exception if the given stream ID is too old.
1792
+ """
1793
+
1794
+ min_stream_id = await self._get_min_device_lists_changes_in_room()
1795
+
1796
+ if min_stream_id > from_id:
1797
+ raise Exception("stream ID is too old")
1798
+
1799
+ sql = """
1800
+ SELECT DISTINCT room_id FROM device_lists_changes_in_room
1801
+ WHERE stream_id > ? AND stream_id <= ?
1802
+ """
1803
+
1804
+ def _get_all_device_list_changes_txn(
1805
+ txn: LoggingTransaction,
1806
+ ) -> set[str]:
1807
+ txn.execute(sql, (from_id, to_id))
1808
+ return {room_id for (room_id,) in txn}
1809
+
1810
+ return await self.db_pool.runInteraction(
1811
+ "get_all_device_list_changes",
1812
+ _get_all_device_list_changes_txn,
1813
+ )
1814
+
1815
+ async def get_device_list_changes_in_room(
1816
+ self, room_id: str, min_stream_id: int
1817
+ ) -> Collection[tuple[str, str]]:
1818
+ """Get all device list changes that happened in the room since the given
1819
+ stream ID.
1820
+
1821
+ Returns:
1822
+ Collection of user ID/device ID tuples of all devices that have
1823
+ changed
1824
+ """
1825
+
1826
+ sql = """
1827
+ SELECT DISTINCT user_id, device_id FROM device_lists_changes_in_room
1828
+ WHERE room_id = ? AND stream_id > ?
1829
+ """
1830
+
1831
+ def get_device_list_changes_in_room_txn(
1832
+ txn: LoggingTransaction,
1833
+ ) -> Collection[tuple[str, str]]:
1834
+ txn.execute(sql, (room_id, min_stream_id))
1835
+ return cast(Collection[tuple[str, str]], txn.fetchall())
1836
+
1837
+ return await self.db_pool.runInteraction(
1838
+ "get_device_list_changes_in_room",
1839
+ get_device_list_changes_in_room_txn,
1840
+ )
1841
+
1842
+ async def get_destinations_for_device(self, stream_id: int) -> StrCollection:
1843
+ return await self.db_pool.simple_select_onecol(
1844
+ table="device_lists_outbound_pokes",
1845
+ keyvalues={"stream_id": stream_id},
1846
+ retcol="destination",
1847
+ desc="get_destinations_for_device",
1848
+ )
1849
+
1850
+ async def update_remote_device_list_cache_entry(
1851
+ self, user_id: str, device_id: str, content: JsonDict, stream_id: str
1852
+ ) -> None:
1853
+ """Updates a single device in the cache of a remote user's devicelist.
1854
+
1855
+ Note: assumes that we are the only thread that can be updating this user's
1856
+ device list.
1857
+
1858
+ Args:
1859
+ user_id: User to update device list for
1860
+ device_id: ID of decivice being updated
1861
+ content: new data on this device
1862
+ stream_id: the version of the device list
1863
+ """
1864
+ await self.db_pool.runInteraction(
1865
+ "update_remote_device_list_cache_entry",
1866
+ self._update_remote_device_list_cache_entry_txn,
1867
+ user_id,
1868
+ device_id,
1869
+ content,
1870
+ stream_id,
1871
+ )
1872
+
1873
+ def _update_remote_device_list_cache_entry_txn(
1874
+ self,
1875
+ txn: LoggingTransaction,
1876
+ user_id: str,
1877
+ device_id: str,
1878
+ content: JsonDict,
1879
+ stream_id: str,
1880
+ ) -> None:
1881
+ """Delete, update or insert a cache entry for this (user, device) pair."""
1882
+ if content.get("deleted"):
1883
+ self.db_pool.simple_delete_txn(
1884
+ txn,
1885
+ table="device_lists_remote_cache",
1886
+ keyvalues={"user_id": user_id, "device_id": device_id},
1887
+ )
1888
+ else:
1889
+ self.db_pool.simple_upsert_txn(
1890
+ txn,
1891
+ table="device_lists_remote_cache",
1892
+ keyvalues={"user_id": user_id, "device_id": device_id},
1893
+ values={"content": json_encoder.encode(content)},
1894
+ )
1895
+
1896
+ txn.call_after(self._get_cached_user_device.invalidate, (user_id, device_id))
1897
+ txn.call_after(self.get_cached_devices_for_user.invalidate, (user_id,))
1898
+ txn.call_after(
1899
+ self.get_device_list_last_stream_id_for_remote.invalidate, (user_id,)
1900
+ )
1901
+
1902
+ self.db_pool.simple_upsert_txn(
1903
+ txn,
1904
+ table="device_lists_remote_extremeties",
1905
+ keyvalues={"user_id": user_id},
1906
+ values={"stream_id": stream_id},
1907
+ )
1908
+
1909
+ async def update_remote_device_list_cache(
1910
+ self, user_id: str, devices: list[dict], stream_id: int
1911
+ ) -> None:
1912
+ """Replace the entire cache of the remote user's devices.
1913
+
1914
+ Note: assumes that we are the only thread that can be updating this user's
1915
+ device list.
1916
+
1917
+ Args:
1918
+ user_id: User to update device list for
1919
+ devices: list of device objects supplied over federation
1920
+ stream_id: the version of the device list
1921
+ """
1922
+ await self.db_pool.runInteraction(
1923
+ "update_remote_device_list_cache",
1924
+ self._update_remote_device_list_cache_txn,
1925
+ user_id,
1926
+ devices,
1927
+ stream_id,
1928
+ )
1929
+
1930
+ def _update_remote_device_list_cache_txn(
1931
+ self, txn: LoggingTransaction, user_id: str, devices: list[dict], stream_id: int
1932
+ ) -> None:
1933
+ """Replace the list of cached devices for this user with the given list."""
1934
+ self.db_pool.simple_delete_txn(
1935
+ txn, table="device_lists_remote_cache", keyvalues={"user_id": user_id}
1936
+ )
1937
+
1938
+ self.db_pool.simple_insert_many_txn(
1939
+ txn,
1940
+ table="device_lists_remote_cache",
1941
+ keys=("user_id", "device_id", "content"),
1942
+ values=[
1943
+ (user_id, content["device_id"], json_encoder.encode(content))
1944
+ for content in devices
1945
+ ],
1946
+ )
1947
+
1948
+ txn.call_after(self.get_cached_devices_for_user.invalidate, (user_id,))
1949
+ txn.call_after(self._get_cached_user_device.invalidate, (user_id,))
1950
+ txn.call_after(
1951
+ self.get_device_list_last_stream_id_for_remote.invalidate, (user_id,)
1952
+ )
1953
+
1954
+ self.db_pool.simple_upsert_txn(
1955
+ txn,
1956
+ table="device_lists_remote_extremeties",
1957
+ keyvalues={"user_id": user_id},
1958
+ values={"stream_id": stream_id},
1959
+ )
1960
+
1961
+ async def add_device_change_to_streams(
1962
+ self,
1963
+ user_id: str,
1964
+ device_ids: StrCollection,
1965
+ room_ids: StrCollection,
1966
+ ) -> Optional[int]:
1967
+ """Persist that a user's devices have been updated, and which hosts
1968
+ (if any) should be poked.
1969
+
1970
+ Args:
1971
+ user_id: The ID of the user whose device changed.
1972
+ device_ids: The IDs of any changed devices. If empty, this function will
1973
+ return None.
1974
+ room_ids: The rooms that the user is in
1975
+
1976
+ Returns:
1977
+ The maximum stream ID of device list updates that were added to the database, or
1978
+ None if no updates were added.
1979
+ """
1980
+ # This generates new stream IDs and therefore must be called on a writer.
1981
+ if not self._is_device_list_writer:
1982
+ raise Exception("Can only be called on device list writers")
1983
+
1984
+ if not device_ids:
1985
+ return None
1986
+
1987
+ context = get_active_span_text_map()
1988
+
1989
+ def add_device_changes_txn(
1990
+ txn: LoggingTransaction,
1991
+ batch_device_ids: StrCollection,
1992
+ ) -> int:
1993
+ stream_ids = self._device_list_id_gen.get_next_mult_txn(
1994
+ txn, len(device_ids)
1995
+ )
1996
+
1997
+ self._add_device_change_to_stream_txn(
1998
+ txn,
1999
+ user_id,
2000
+ batch_device_ids,
2001
+ stream_ids,
2002
+ )
2003
+
2004
+ self._add_device_outbound_room_poke_txn(
2005
+ txn,
2006
+ user_id,
2007
+ batch_device_ids,
2008
+ room_ids,
2009
+ stream_ids,
2010
+ context,
2011
+ )
2012
+
2013
+ return stream_ids[-1]
2014
+
2015
+ last_stream_id: Optional[int] = None
2016
+ for batch_device_ids in batch_iter(device_ids, 1000):
2017
+ last_stream_id = await self.db_pool.runInteraction(
2018
+ "add_device_change_to_stream",
2019
+ add_device_changes_txn,
2020
+ batch_device_ids,
2021
+ )
2022
+
2023
+ return last_stream_id
2024
+
2025
+ def _add_device_change_to_stream_txn(
2026
+ self,
2027
+ txn: LoggingTransaction,
2028
+ user_id: str,
2029
+ device_ids: Collection[str],
2030
+ stream_ids: list[int],
2031
+ ) -> None:
2032
+ txn.call_after(
2033
+ self._device_list_stream_cache.entity_has_changed,
2034
+ user_id,
2035
+ stream_ids[-1],
2036
+ )
2037
+ txn.call_after(
2038
+ self._get_e2e_device_keys_for_federation_query_inner.invalidate,
2039
+ (user_id,),
2040
+ )
2041
+
2042
+ min_stream_id = stream_ids[0]
2043
+
2044
+ # Delete older entries in the table, as we really only care about
2045
+ # when the latest change happened.
2046
+ cleanup_obsolete_stmt = """
2047
+ DELETE FROM device_lists_stream
2048
+ WHERE user_id = ? AND stream_id < ? AND %s
2049
+ """
2050
+ device_ids_clause, device_ids_args = make_in_list_sql_clause(
2051
+ txn.database_engine, "device_id", device_ids
2052
+ )
2053
+ txn.execute(
2054
+ cleanup_obsolete_stmt % (device_ids_clause,),
2055
+ [user_id, min_stream_id] + device_ids_args,
2056
+ )
2057
+
2058
+ self.db_pool.simple_insert_many_txn(
2059
+ txn,
2060
+ table="device_lists_stream",
2061
+ keys=("instance_name", "stream_id", "user_id", "device_id"),
2062
+ values=[
2063
+ (self._instance_name, stream_id, user_id, device_id)
2064
+ for stream_id, device_id in zip(stream_ids, device_ids)
2065
+ ],
2066
+ )
2067
+
2068
+ def _add_device_outbound_poke_to_stream_txn(
2069
+ self,
2070
+ txn: LoggingTransaction,
2071
+ user_id: str,
2072
+ device_id: str,
2073
+ hosts: Collection[str],
2074
+ stream_id: int,
2075
+ context: Optional[dict[str, str]],
2076
+ ) -> None:
2077
+ if self._device_list_federation_stream_cache:
2078
+ for host in hosts:
2079
+ txn.call_after(
2080
+ self._device_list_federation_stream_cache.entity_has_changed,
2081
+ host,
2082
+ stream_id,
2083
+ )
2084
+
2085
+ now = self.clock.time_msec()
2086
+
2087
+ encoded_context = json_encoder.encode(context)
2088
+ mark_sent = not self.hs.is_mine_id(user_id)
2089
+
2090
+ values = [
2091
+ (
2092
+ destination,
2093
+ self._instance_name,
2094
+ stream_id,
2095
+ user_id,
2096
+ device_id,
2097
+ mark_sent,
2098
+ now,
2099
+ encoded_context if whitelisted_homeserver(destination) else "{}",
2100
+ )
2101
+ for destination in hosts
2102
+ ]
2103
+
2104
+ self.db_pool.simple_insert_many_txn(
2105
+ txn,
2106
+ table="device_lists_outbound_pokes",
2107
+ keys=(
2108
+ "destination",
2109
+ "instance_name",
2110
+ "stream_id",
2111
+ "user_id",
2112
+ "device_id",
2113
+ "sent",
2114
+ "ts",
2115
+ "opentracing_context",
2116
+ ),
2117
+ values=values,
2118
+ )
2119
+
2120
+ # debugging for https://github.com/matrix-org/synapse/issues/14251
2121
+ if issue_8631_logger.isEnabledFor(logging.DEBUG):
2122
+ issue_8631_logger.debug(
2123
+ "Recorded outbound pokes for %s:%s with device stream ids %s",
2124
+ user_id,
2125
+ device_id,
2126
+ {
2127
+ stream_id: destination
2128
+ for (destination, _, stream_id, _, _, _, _, _) in values
2129
+ },
2130
+ )
2131
+
2132
+ async def mark_redundant_device_lists_pokes(
2133
+ self,
2134
+ user_id: str,
2135
+ device_id: str,
2136
+ room_id: str,
2137
+ converted_upto_stream_id: int,
2138
+ ) -> None:
2139
+ """If we've calculated the outbound pokes for a given room/device list
2140
+ update, mark any subsequent changes as already converted"""
2141
+
2142
+ sql = """
2143
+ UPDATE device_lists_changes_in_room
2144
+ SET converted_to_destinations = true
2145
+ WHERE stream_id > ? AND user_id = ? AND device_id = ?
2146
+ AND room_id = ? AND NOT converted_to_destinations
2147
+ """
2148
+
2149
+ def mark_redundant_device_lists_pokes_txn(txn: LoggingTransaction) -> None:
2150
+ txn.execute(sql, (converted_upto_stream_id, user_id, device_id, room_id))
2151
+
2152
+ return await self.db_pool.runInteraction(
2153
+ "mark_redundant_device_lists_pokes", mark_redundant_device_lists_pokes_txn
2154
+ )
2155
+
2156
+ def _add_device_outbound_room_poke_txn(
2157
+ self,
2158
+ txn: LoggingTransaction,
2159
+ user_id: str,
2160
+ device_ids: StrCollection,
2161
+ room_ids: StrCollection,
2162
+ stream_ids: list[int],
2163
+ context: dict[str, str],
2164
+ ) -> None:
2165
+ """Record the user in the room has updated their device."""
2166
+
2167
+ encoded_context = json_encoder.encode(context)
2168
+
2169
+ # The `device_lists_changes_in_room.stream_id` column matches the
2170
+ # corresponding `stream_id` of the update in the `device_lists_stream`
2171
+ # table, i.e. all rows persisted for the same device update will have
2172
+ # the same `stream_id` (but different room IDs).
2173
+ self.db_pool.simple_insert_many_txn(
2174
+ txn,
2175
+ table="device_lists_changes_in_room",
2176
+ keys=(
2177
+ "user_id",
2178
+ "device_id",
2179
+ "room_id",
2180
+ "stream_id",
2181
+ "instance_name",
2182
+ "converted_to_destinations",
2183
+ "opentracing_context",
2184
+ ),
2185
+ values=[
2186
+ (
2187
+ user_id,
2188
+ device_id,
2189
+ room_id,
2190
+ stream_id,
2191
+ self._instance_name,
2192
+ # We only need to calculate outbound pokes for local users
2193
+ not self.hs.is_mine_id(user_id),
2194
+ encoded_context,
2195
+ )
2196
+ for room_id in room_ids
2197
+ for device_id, stream_id in zip(device_ids, stream_ids)
2198
+ ],
2199
+ )
2200
+
2201
+ txn.call_after(
2202
+ self.device_lists_in_rooms_have_changed, room_ids, max(stream_ids)
2203
+ )
2204
+
2205
+ async def get_uncoverted_outbound_room_pokes(
2206
+ self, start_stream_id: int, start_room_id: str, limit: int = 10
2207
+ ) -> list[tuple[str, str, str, int, Optional[dict[str, str]]]]:
2208
+ """Get device list changes by room that have not yet been handled and
2209
+ written to `device_lists_outbound_pokes`.
2210
+
2211
+ Args:
2212
+ start_stream_id: Together with `start_room_id`, indicates the position after
2213
+ which to return device list changes.
2214
+ start_room_id: Together with `start_stream_id`, indicates the position after
2215
+ which to return device list changes.
2216
+ limit: The maximum number of device list changes to return.
2217
+
2218
+ Returns:
2219
+ A list of user ID, device ID, room ID, stream ID and optional opentracing
2220
+ context, in order of ascending (stream ID, room ID).
2221
+ """
2222
+ sql = """
2223
+ SELECT user_id, device_id, room_id, stream_id, opentracing_context
2224
+ FROM device_lists_changes_in_room
2225
+ WHERE
2226
+ (stream_id, room_id) > (?, ?) AND
2227
+ stream_id <= ? AND
2228
+ NOT converted_to_destinations
2229
+ ORDER BY stream_id ASC, room_id ASC
2230
+ LIMIT ?
2231
+ """
2232
+
2233
+ def get_uncoverted_outbound_room_pokes_txn(
2234
+ txn: LoggingTransaction,
2235
+ ) -> list[tuple[str, str, str, int, Optional[dict[str, str]]]]:
2236
+ txn.execute(
2237
+ sql,
2238
+ (
2239
+ start_stream_id,
2240
+ start_room_id,
2241
+ # Avoid returning rows if there may be uncommitted device list
2242
+ # changes with smaller stream IDs.
2243
+ self._device_list_id_gen.get_current_token(),
2244
+ limit,
2245
+ ),
2246
+ )
2247
+
2248
+ return [
2249
+ (
2250
+ user_id,
2251
+ device_id,
2252
+ room_id,
2253
+ stream_id,
2254
+ db_to_json(opentracing_context),
2255
+ )
2256
+ for user_id, device_id, room_id, stream_id, opentracing_context in txn
2257
+ ]
2258
+
2259
+ return await self.db_pool.runInteraction(
2260
+ "get_uncoverted_outbound_room_pokes", get_uncoverted_outbound_room_pokes_txn
2261
+ )
2262
+
2263
+ async def add_device_list_outbound_pokes(
2264
+ self,
2265
+ user_id: str,
2266
+ device_id: str,
2267
+ room_id: str,
2268
+ hosts: Collection[str],
2269
+ context: Optional[dict[str, str]],
2270
+ ) -> None:
2271
+ """Queue the device update to be sent to the given set of hosts,
2272
+ calculated from the room ID.
2273
+ """
2274
+ # This generates new stream IDs and therefore must be called on a writer.
2275
+ if not self._is_device_list_writer:
2276
+ raise Exception("Can only be called on device list writers")
2277
+
2278
+ if not hosts:
2279
+ return
2280
+
2281
+ def add_device_list_outbound_pokes_txn(
2282
+ txn: LoggingTransaction, stream_id: int
2283
+ ) -> None:
2284
+ self._add_device_outbound_poke_to_stream_txn(
2285
+ txn,
2286
+ user_id=user_id,
2287
+ device_id=device_id,
2288
+ hosts=hosts,
2289
+ stream_id=stream_id,
2290
+ context=context,
2291
+ )
2292
+
2293
+ async with self._device_list_id_gen.get_next() as stream_id:
2294
+ return await self.db_pool.runInteraction(
2295
+ "add_device_list_outbound_pokes",
2296
+ add_device_list_outbound_pokes_txn,
2297
+ stream_id,
2298
+ )
2299
+
2300
+ async def add_remote_device_list_to_pending(
2301
+ self, user_id: str, device_id: str
2302
+ ) -> None:
2303
+ """Add a device list update to the table tracking remote device list
2304
+ updates during partial joins.
2305
+ """
2306
+ # This generates new stream IDs and therefore must be called on a writer.
2307
+ if not self._is_device_list_writer:
2308
+ raise Exception("Can only be called on device list writers")
2309
+
2310
+ async with self._device_list_id_gen.get_next() as stream_id:
2311
+ await self.db_pool.simple_upsert(
2312
+ table="device_lists_remote_pending",
2313
+ keyvalues={
2314
+ "user_id": user_id,
2315
+ "device_id": device_id,
2316
+ },
2317
+ values={
2318
+ "stream_id": stream_id,
2319
+ "instance_name": self._instance_name,
2320
+ },
2321
+ desc="add_remote_device_list_to_pending",
2322
+ )
2323
+
2324
+ async def get_pending_remote_device_list_updates_for_room(
2325
+ self, room_id: str
2326
+ ) -> Collection[tuple[str, str]]:
2327
+ """Get the set of remote device list updates from the pending table for
2328
+ the room.
2329
+ """
2330
+
2331
+ # The device list stream is a multi-writer stream, but when we partially
2332
+ # join a room, we only record the minimum stream ID. This means that we
2333
+ # may be returning a device update that was already sent through
2334
+ # federation here in case of concurrent writes. This is absolutely fine,
2335
+ # sending a device update multiple times through federation is safe
2336
+
2337
+ # FIXME: record the full multi-writer stream token with individual
2338
+ # writer positions at the time of the join to avoid this
2339
+ min_device_stream_id = await self.db_pool.simple_select_one_onecol(
2340
+ table="partial_state_rooms",
2341
+ keyvalues={
2342
+ "room_id": room_id,
2343
+ },
2344
+ retcol="device_lists_stream_id",
2345
+ desc="get_pending_remote_device_list_updates_for_room_device",
2346
+ )
2347
+
2348
+ sql = """
2349
+ SELECT user_id, device_id FROM device_lists_remote_pending AS d
2350
+ INNER JOIN current_state_events AS c ON
2351
+ type = 'm.room.member'
2352
+ AND state_key = user_id
2353
+ AND membership = 'join'
2354
+ WHERE
2355
+ room_id = ? AND stream_id > ?
2356
+ """
2357
+
2358
+ def get_pending_remote_device_list_updates_for_room_txn(
2359
+ txn: LoggingTransaction,
2360
+ ) -> Collection[tuple[str, str]]:
2361
+ txn.execute(sql, (room_id, min_device_stream_id))
2362
+ return cast(Collection[tuple[str, str]], txn.fetchall())
2363
+
2364
+ return await self.db_pool.runInteraction(
2365
+ "get_pending_remote_device_list_updates_for_room",
2366
+ get_pending_remote_device_list_updates_for_room_txn,
2367
+ )
2368
+
2369
+ async def get_device_change_last_converted_pos(self) -> tuple[int, str]:
2370
+ """
2371
+ Get the position of the last row in `device_list_changes_in_room` that has been
2372
+ converted to `device_lists_outbound_pokes`.
2373
+
2374
+ Rows with a strictly greater position where `converted_to_destinations` is
2375
+ `FALSE` have not been converted.
2376
+ """
2377
+
2378
+ # There should be only one row in this table, though we want to
2379
+ # future-proof ourselves for when we have multiple rows (one for each
2380
+ # instance). So to handle that case we take the minimum of all rows.
2381
+ rows = await self.db_pool.simple_select_list(
2382
+ table="device_lists_changes_converted_stream_position",
2383
+ keyvalues={},
2384
+ retcols=["stream_id", "room_id"],
2385
+ desc="get_device_change_last_converted_pos",
2386
+ )
2387
+ return cast(tuple[int, str], min(rows))
2388
+
2389
+ async def set_device_change_last_converted_pos(
2390
+ self,
2391
+ stream_id: int,
2392
+ room_id: str,
2393
+ ) -> None:
2394
+ """
2395
+ Set the position of the last row in `device_list_changes_in_room` that has been
2396
+ converted to `device_lists_outbound_pokes`.
2397
+ """
2398
+
2399
+ await self.db_pool.simple_update_one(
2400
+ table="device_lists_changes_converted_stream_position",
2401
+ keyvalues={},
2402
+ updatevalues={
2403
+ "stream_id": stream_id,
2404
+ "instance_name": self._instance_name,
2405
+ "room_id": room_id,
2406
+ },
2407
+ desc="set_device_change_last_converted_pos",
2408
+ )
2409
+
2410
+
2411
+ class DeviceBackgroundUpdateStore(SQLBaseStore):
2412
+ _instance_name: str
2413
+
2414
+ def __init__(
2415
+ self,
2416
+ database: DatabasePool,
2417
+ db_conn: LoggingDatabaseConnection,
2418
+ hs: "HomeServer",
2419
+ ):
2420
+ super().__init__(database, db_conn, hs)
2421
+
2422
+ self._instance_name = hs.get_instance_name()
2423
+
2424
+ self.db_pool.updates.register_background_index_update(
2425
+ "device_lists_stream_idx",
2426
+ index_name="device_lists_stream_user_id",
2427
+ table="device_lists_stream",
2428
+ columns=["user_id", "device_id"],
2429
+ )
2430
+
2431
+ # create a unique index on device_lists_remote_cache
2432
+ self.db_pool.updates.register_background_index_update(
2433
+ "device_lists_remote_cache_unique_idx",
2434
+ index_name="device_lists_remote_cache_unique_id",
2435
+ table="device_lists_remote_cache",
2436
+ columns=["user_id", "device_id"],
2437
+ unique=True,
2438
+ )
2439
+
2440
+ # And one on device_lists_remote_extremeties
2441
+ self.db_pool.updates.register_background_index_update(
2442
+ "device_lists_remote_extremeties_unique_idx",
2443
+ index_name="device_lists_remote_extremeties_unique_idx",
2444
+ table="device_lists_remote_extremeties",
2445
+ columns=["user_id"],
2446
+ unique=True,
2447
+ )
2448
+
2449
+ # once they complete, we can remove the old non-unique indexes.
2450
+ self.db_pool.updates.register_background_update_handler(
2451
+ DROP_DEVICE_LIST_STREAMS_NON_UNIQUE_INDEXES,
2452
+ self._drop_device_list_streams_non_unique_indexes,
2453
+ )
2454
+
2455
+ # clear out duplicate device list outbound pokes
2456
+ self.db_pool.updates.register_background_update_handler(
2457
+ BG_UPDATE_REMOVE_DUP_OUTBOUND_POKES,
2458
+ self._remove_duplicate_outbound_pokes,
2459
+ )
2460
+
2461
+ self.db_pool.updates.register_background_index_update(
2462
+ "device_lists_changes_in_room_by_room_index",
2463
+ index_name="device_lists_changes_in_room_by_room_idx",
2464
+ table="device_lists_changes_in_room",
2465
+ columns=["room_id", "stream_id"],
2466
+ )
2467
+
2468
+ async def _drop_device_list_streams_non_unique_indexes(
2469
+ self, progress: JsonDict, batch_size: int
2470
+ ) -> int:
2471
+ def f(conn: LoggingDatabaseConnection) -> None:
2472
+ txn = conn.cursor()
2473
+ txn.execute("DROP INDEX IF EXISTS device_lists_remote_cache_id")
2474
+ txn.execute("DROP INDEX IF EXISTS device_lists_remote_extremeties_id")
2475
+ txn.close()
2476
+
2477
+ await self.db_pool.runWithConnection(f)
2478
+ await self.db_pool.updates._end_background_update(
2479
+ DROP_DEVICE_LIST_STREAMS_NON_UNIQUE_INDEXES
2480
+ )
2481
+ return 1
2482
+
2483
+ async def _remove_duplicate_outbound_pokes(
2484
+ self, progress: JsonDict, batch_size: int
2485
+ ) -> int:
2486
+ # for some reason, we have accumulated duplicate entries in
2487
+ # device_lists_outbound_pokes, which makes prune_outbound_device_list_pokes less
2488
+ # efficient.
2489
+ #
2490
+ # For each duplicate, we delete all the existing rows and put one back.
2491
+
2492
+ last_row = progress.get(
2493
+ "last_row",
2494
+ {"stream_id": 0, "destination": "", "user_id": "", "device_id": ""},
2495
+ )
2496
+
2497
+ def _txn(txn: LoggingTransaction) -> int:
2498
+ clause, args = make_tuple_comparison_clause(
2499
+ [
2500
+ ("stream_id", last_row["stream_id"]),
2501
+ ("destination", last_row["destination"]),
2502
+ ("user_id", last_row["user_id"]),
2503
+ ("device_id", last_row["device_id"]),
2504
+ ]
2505
+ )
2506
+ sql = f"""
2507
+ SELECT stream_id, destination, user_id, device_id, MAX(ts) AS ts
2508
+ FROM device_lists_outbound_pokes
2509
+ WHERE {clause}
2510
+ GROUP BY stream_id, destination, user_id, device_id
2511
+ HAVING count(*) > 1
2512
+ ORDER BY stream_id, destination, user_id, device_id
2513
+ LIMIT ?
2514
+ """
2515
+ txn.execute(sql, args + [batch_size])
2516
+ rows = txn.fetchall()
2517
+
2518
+ stream_id, destination, user_id, device_id = None, None, None, None
2519
+ for stream_id, destination, user_id, device_id, _ in rows:
2520
+ self.db_pool.simple_delete_txn(
2521
+ txn,
2522
+ "device_lists_outbound_pokes",
2523
+ {
2524
+ "stream_id": stream_id,
2525
+ "destination": destination,
2526
+ "user_id": user_id,
2527
+ "device_id": device_id,
2528
+ },
2529
+ )
2530
+
2531
+ self.db_pool.simple_insert_txn(
2532
+ txn,
2533
+ "device_lists_outbound_pokes",
2534
+ {
2535
+ "stream_id": stream_id,
2536
+ "instance_name": self._instance_name,
2537
+ "destination": destination,
2538
+ "user_id": user_id,
2539
+ "device_id": device_id,
2540
+ "sent": False,
2541
+ },
2542
+ )
2543
+
2544
+ if rows:
2545
+ self.db_pool.updates._background_update_progress_txn(
2546
+ txn,
2547
+ BG_UPDATE_REMOVE_DUP_OUTBOUND_POKES,
2548
+ {
2549
+ "last_row": {
2550
+ "stream_id": stream_id,
2551
+ "destination": destination,
2552
+ "user_id": user_id,
2553
+ "device_id": device_id,
2554
+ }
2555
+ },
2556
+ )
2557
+
2558
+ return len(rows)
2559
+
2560
+ rows = await self.db_pool.runInteraction(
2561
+ BG_UPDATE_REMOVE_DUP_OUTBOUND_POKES, _txn
2562
+ )
2563
+
2564
+ if not rows:
2565
+ await self.db_pool.updates._end_background_update(
2566
+ BG_UPDATE_REMOVE_DUP_OUTBOUND_POKES
2567
+ )
2568
+
2569
+ return rows
2570
+
2571
+
2572
+ class DeviceStore(DeviceWorkerStore, DeviceBackgroundUpdateStore):
2573
+ _instance_name: str
2574
+
2575
+ def __init__(
2576
+ self,
2577
+ database: DatabasePool,
2578
+ db_conn: LoggingDatabaseConnection,
2579
+ hs: "HomeServer",
2580
+ ):
2581
+ super().__init__(database, db_conn, hs)