faster_joins.html 44 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545
  1. <!DOCTYPE HTML>
  2. <html lang="en" class="sidebar-visible no-js light">
  3. <head>
  4. <!-- Book generated using mdBook -->
  5. <meta charset="UTF-8">
  6. <title>Faster remote joins - Synapse</title>
  7. <!-- Custom HTML head -->
  8. <meta content="text/html; charset=utf-8" http-equiv="Content-Type">
  9. <meta name="description" content="">
  10. <meta name="viewport" content="width=device-width, initial-scale=1">
  11. <meta name="theme-color" content="#ffffff" />
  12. <link rel="icon" href="../../favicon.svg">
  13. <link rel="shortcut icon" href="../../favicon.png">
  14. <link rel="stylesheet" href="../../css/variables.css">
  15. <link rel="stylesheet" href="../../css/general.css">
  16. <link rel="stylesheet" href="../../css/chrome.css">
  17. <link rel="stylesheet" href="../../css/print.css" media="print">
  18. <!-- Fonts -->
  19. <link rel="stylesheet" href="../../FontAwesome/css/font-awesome.css">
  20. <link rel="stylesheet" href="../../fonts/fonts.css">
  21. <!-- Highlight.js Stylesheets -->
  22. <link rel="stylesheet" href="../../highlight.css">
  23. <link rel="stylesheet" href="../../tomorrow-night.css">
  24. <link rel="stylesheet" href="../../ayu-highlight.css">
  25. <!-- Custom theme stylesheets -->
  26. <link rel="stylesheet" href="../../docs/website_files/table-of-contents.css">
  27. <link rel="stylesheet" href="../../docs/website_files/remove-nav-buttons.css">
  28. <link rel="stylesheet" href="../../docs/website_files/indent-section-headers.css">
  29. <link rel="stylesheet" href="../../docs/website_files/version-picker.css">
  30. </head>
  31. <body>
  32. <!-- Provide site root to javascript -->
  33. <script type="text/javascript">
  34. var path_to_root = "../../";
  35. var default_theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? "navy" : "light";
  36. </script>
  37. <!-- Work around some values being stored in localStorage wrapped in quotes -->
  38. <script type="text/javascript">
  39. try {
  40. var theme = localStorage.getItem('mdbook-theme');
  41. var sidebar = localStorage.getItem('mdbook-sidebar');
  42. if (theme.startsWith('"') && theme.endsWith('"')) {
  43. localStorage.setItem('mdbook-theme', theme.slice(1, theme.length - 1));
  44. }
  45. if (sidebar.startsWith('"') && sidebar.endsWith('"')) {
  46. localStorage.setItem('mdbook-sidebar', sidebar.slice(1, sidebar.length - 1));
  47. }
  48. } catch (e) { }
  49. </script>
  50. <!-- Set the theme before any content is loaded, prevents flash -->
  51. <script type="text/javascript">
  52. var theme;
  53. try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
  54. if (theme === null || theme === undefined) { theme = default_theme; }
  55. var html = document.querySelector('html');
  56. html.classList.remove('no-js')
  57. html.classList.remove('light')
  58. html.classList.add(theme);
  59. html.classList.add('js');
  60. </script>
  61. <!-- Hide / unhide sidebar before it is displayed -->
  62. <script type="text/javascript">
  63. var html = document.querySelector('html');
  64. var sidebar = 'hidden';
  65. if (document.body.clientWidth >= 1080) {
  66. try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { }
  67. sidebar = sidebar || 'visible';
  68. }
  69. html.classList.remove('sidebar-visible');
  70. html.classList.add("sidebar-" + sidebar);
  71. </script>
  72. <nav id="sidebar" class="sidebar" aria-label="Table of contents">
  73. <div class="sidebar-scrollbox">
  74. <ol class="chapter"><li class="chapter-item expanded affix "><li class="part-title">Introduction</li><li class="chapter-item expanded "><a href="../../welcome_and_overview.html">Welcome and Overview</a></li><li class="chapter-item expanded affix "><li class="part-title">Setup</li><li class="chapter-item expanded "><a href="../../setup/installation.html">Installation</a></li><li class="chapter-item expanded "><a href="../../postgres.html">Using Postgres</a></li><li class="chapter-item expanded "><a href="../../reverse_proxy.html">Configuring a Reverse Proxy</a></li><li class="chapter-item expanded "><a href="../../setup/forward_proxy.html">Configuring a Forward/Outbound Proxy</a></li><li class="chapter-item expanded "><a href="../../turn-howto.html">Configuring a Turn Server</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="../../setup/turn/coturn.html">coturn TURN server</a></li><li class="chapter-item expanded "><a href="../../setup/turn/eturnal.html">eturnal TURN server</a></li></ol></li><li class="chapter-item expanded "><a href="../../delegate.html">Delegation</a></li><li class="chapter-item expanded affix "><li class="part-title">Upgrading</li><li class="chapter-item expanded "><a href="../../upgrade.html">Upgrading between Synapse Versions</a></li><li class="chapter-item expanded affix "><li class="part-title">Usage</li><li class="chapter-item expanded "><a href="../../federate.html">Federation</a></li><li class="chapter-item expanded "><a href="../../usage/configuration/index.html">Configuration</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="../../usage/configuration/config_documentation.html">Configuration Manual</a></li><li class="chapter-item expanded "><a href="../../usage/configuration/homeserver_sample_config.html">Homeserver Sample Config File</a></li><li class="chapter-item expanded "><a href="../../usage/configuration/logging_sample_config.html">Logging Sample Config File</a></li><li class="chapter-item expanded "><a href="../../structured_logging.html">Structured Logging</a></li><li class="chapter-item expanded "><a href="../../templates.html">Templates</a></li><li class="chapter-item expanded "><a href="../../usage/configuration/user_authentication/index.html">User Authentication</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="../../usage/configuration/user_authentication/single_sign_on/index.html">Single-Sign On</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="../../openid.html">OpenID Connect</a></li><li class="chapter-item expanded "><a href="../../usage/configuration/user_authentication/single_sign_on/saml.html">SAML</a></li><li class="chapter-item expanded "><a href="../../usage/configuration/user_authentication/single_sign_on/cas.html">CAS</a></li><li class="chapter-item expanded "><a href="../../sso_mapping_providers.html">SSO Mapping Providers</a></li></ol></li><li class="chapter-item expanded "><a href="../../password_auth_providers.html">Password Auth Providers</a></li><li class="chapter-item expanded "><a href="../../jwt.html">JSON Web Tokens</a></li><li class="chapter-item expanded "><a href="../../usage/configuration/user_authentication/refresh_tokens.html">Refresh Tokens</a></li></ol></li><li class="chapter-item expanded "><a href="../../CAPTCHA_SETUP.html">Registration Captcha</a></li><li class="chapter-item expanded "><a href="../../application_services.html">Application Services</a></li><li class="chapter-item expanded "><a href="../../server_notices.html">Server Notices</a></li><li class="chapter-item expanded "><a href="../../consent_tracking.html">Consent Tracking</a></li><li class="chapter-item expanded "><a href="../../user_directory.html">User Directory</a></li><li class="chapter-item expanded "><a href="../../message_retention_policies.html">Message Retention Policies</a></li><li class="chapter-item expanded "><a href="../../modules/index.html">Pluggable Modules</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="../../modules/writing_a_module.html">Writing a module</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="../../modules/spam_checker_callbacks.html">Spam checker callbacks</a></li><li class="chapter-item expanded "><a href="../../modules/third_party_rules_callbacks.html">Third-party rules callbacks</a></li><li class="chapter-item expanded "><a href="../../modules/presence_router_callbacks.html">Presence router callbacks</a></li><li class="chapter-item expanded "><a href="../../modules/account_validity_callbacks.html">Account validity callbacks</a></li><li class="chapter-item expanded "><a href="../../modules/password_auth_provider_callbacks.html">Password auth provider callbacks</a></li><li class="chapter-item expanded "><a href="../../modules/background_update_controller_callbacks.html">Background update controller callbacks</a></li><li class="chapter-item expanded "><a href="../../modules/account_data_callbacks.html">Account data callbacks</a></li><li class="chapter-item expanded "><a href="../../modules/porting_legacy_module.html">Porting a legacy module to the new interface</a></li></ol></li></ol></li><li class="chapter-item expanded "><a href="../../workers.html">Workers</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="../../synctl_workers.html">Using synctl with Workers</a></li><li class="chapter-item expanded "><a href="../../systemd-with-workers/index.html">Systemd</a></li></ol></li></ol></li><li class="chapter-item expanded "><a href="../../usage/administration/index.html">Administration</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="../../usage/administration/admin_api/index.html">Admin API</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="../../admin_api/account_validity.html">Account Validity</a></li><li class="chapter-item expanded "><a href="../../usage/administration/admin_api/background_updates.html">Background Updates</a></li><li class="chapter-item expanded "><a href="../../admin_api/event_reports.html">Event Reports</a></li><li class="chapter-item expanded "><a href="../../admin_api/experimental_features.html">Experimental Features</a></li><li class="chapter-item expanded "><a href="../../admin_api/media_admin_api.html">Media</a></li><li class="chapter-item expanded "><a href="../../admin_api/purge_history_api.html">Purge History</a></li><li class="chapter-item expanded "><a href="../../admin_api/register_api.html">Register Users</a></li><li class="chapter-item expanded "><a href="../../usage/administration/admin_api/registration_tokens.html">Registration Tokens</a></li><li class="chapter-item expanded "><a href="../../admin_api/room_membership.html">Manipulate Room Membership</a></li><li class="chapter-item expanded "><a href="../../admin_api/rooms.html">Rooms</a></li><li class="chapter-item expanded "><a href="../../admin_api/server_notices.html">Server Notices</a></li><li class="chapter-item expanded "><a href="../../admin_api/statistics.html">Statistics</a></li><li class="chapter-item expanded "><a href="../../admin_api/user_admin_api.html">Users</a></li><li class="chapter-item expanded "><a href="../../admin_api/version_api.html">Server Version</a></li><li class="chapter-item expanded "><a href="../../usage/administration/admin_api/federation.html">Federation</a></li></ol></li><li class="chapter-item expanded "><a href="../../manhole.html">Manhole</a></li><li class="chapter-item expanded "><a href="../../metrics-howto.html">Monitoring</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="../../usage/administration/monitoring/reporting_homeserver_usage_statistics.html">Reporting Homeserver Usage Statistics</a></li></ol></li><li class="chapter-item expanded "><a href="../../usage/administration/monthly_active_users.html">Monthly Active Users</a></li><li class="chapter-item expanded "><a href="../../usage/administration/understanding_synapse_through_grafana_graphs.html">Understanding Synapse Through Grafana Graphs</a></li><li class="chapter-item expanded "><a href="../../usage/administration/useful_sql_for_admins.html">Useful SQL for Admins</a></li><li class="chapter-item expanded "><a href="../../usage/administration/database_maintenance_tools.html">Database Maintenance Tools</a></li><li class="chapter-item expanded "><a href="../../usage/administration/state_groups.html">State Groups</a></li><li class="chapter-item expanded "><a href="../../usage/administration/request_log.html">Request log format</a></li><li class="chapter-item expanded "><a href="../../usage/administration/admin_faq.html">Admin FAQ</a></li><li class="chapter-item expanded "><div>Scripts</div></li></ol></li><li class="chapter-item expanded "><li class="part-title">Development</li><li class="chapter-item expanded "><a href="../../development/contributing_guide.html">Contributing Guide</a></li><li class="chapter-item expanded "><a href="../../code_style.html">Code Style</a></li><li class="chapter-item expanded "><a href="../../development/reviews.html">Reviewing Code</a></li><li class="chapter-item expanded "><a href="../../development/releases.html">Release Cycle</a></li><li class="chapter-item expanded "><a href="../../development/git.html">Git Usage</a></li><li class="chapter-item expanded "><div>Testing</div></li><li><ol class="section"><li class="chapter-item expanded "><a href="../../development/demo.html">Demo scripts</a></li></ol></li><li class="chapter-item expanded "><a href="../../opentracing.html">OpenTracing</a></li><li class="chapter-item expanded "><a href="../../development/database_schema.html">Database Schemas</a></li><li class="chapter-item expanded "><a href="../../development/experimental_features.html">Experimental features</a></li><li class="chapter-item expanded "><a href="../../development/dependencies.html">Dependency management</a></li><li class="chapter-item expanded "><div>Synapse Architecture</div></li><li><ol class="section"><li class="chapter-item expanded "><a href="../../development/synapse_architecture/cancellation.html">Cancellation</a></li><li class="chapter-item expanded "><a href="../../log_contexts.html">Log Contexts</a></li><li class="chapter-item expanded "><a href="../../replication.html">Replication</a></li><li class="chapter-item expanded "><a href="../../development/synapse_architecture/streams.html">Streams</a></li><li class="chapter-item expanded "><a href="../../tcp_replication.html">TCP Replication</a></li><li class="chapter-item expanded "><a href="../../development/synapse_architecture/faster_joins.html" class="active">Faster remote joins</a></li></ol></li><li class="chapter-item expanded "><a href="../../development/internal_documentation/index.html">Internal Documentation</a></li><li><ol class="section"><li class="chapter-item expanded "><div>Single Sign-On</div></li><li><ol class="section"><li class="chapter-item expanded "><a href="../../development/saml.html">SAML</a></li><li class="chapter-item expanded "><a href="../../development/cas.html">CAS</a></li></ol></li><li class="chapter-item expanded "><a href="../../development/room-dag-concepts.html">Room DAG concepts</a></li><li class="chapter-item expanded "><div>State Resolution</div></li><li><ol class="section"><li class="chapter-item expanded "><a href="../../auth_chain_difference_algorithm.html">The Auth Chain Difference Algorithm</a></li></ol></li><li class="chapter-item expanded "><a href="../../media_repository.html">Media Repository</a></li><li class="chapter-item expanded "><a href="../../room_and_user_statistics.html">Room and User Statistics</a></li></ol></li><li class="chapter-item expanded "><div>Scripts</div></li><li class="chapter-item expanded affix "><li class="part-title">Other</li><li class="chapter-item expanded "><a href="../../deprecation_policy.html">Dependency Deprecation Policy</a></li><li class="chapter-item expanded "><a href="../../other/running_synapse_on_single_board_computers.html">Running Synapse on a Single-Board Computer</a></li></ol>
  75. </div>
  76. <div id="sidebar-resize-handle" class="sidebar-resize-handle"></div>
  77. </nav>
  78. <div id="page-wrapper" class="page-wrapper">
  79. <div class="page">
  80. <div id="menu-bar-hover-placeholder"></div>
  81. <div id="menu-bar" class="menu-bar sticky bordered">
  82. <div class="left-buttons">
  83. <button id="sidebar-toggle" class="icon-button" type="button" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="sidebar">
  84. <i class="fa fa-bars"></i>
  85. </button>
  86. <button id="theme-toggle" class="icon-button" type="button" title="Change theme" aria-label="Change theme" aria-haspopup="true" aria-expanded="false" aria-controls="theme-list">
  87. <i class="fa fa-paint-brush"></i>
  88. </button>
  89. <ul id="theme-list" class="theme-popup" aria-label="Themes" role="menu">
  90. <li role="none"><button role="menuitem" class="theme" id="light">Light (default)</button></li>
  91. <li role="none"><button role="menuitem" class="theme" id="rust">Rust</button></li>
  92. <li role="none"><button role="menuitem" class="theme" id="coal">Coal</button></li>
  93. <li role="none"><button role="menuitem" class="theme" id="navy">Navy</button></li>
  94. <li role="none"><button role="menuitem" class="theme" id="ayu">Ayu</button></li>
  95. </ul>
  96. <button id="search-toggle" class="icon-button" type="button" title="Search. (Shortkey: s)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="S" aria-controls="searchbar">
  97. <i class="fa fa-search"></i>
  98. </button>
  99. <div class="version-picker">
  100. <div class="dropdown">
  101. <div class="select">
  102. <span></span>
  103. <i class="fa fa-chevron-down"></i>
  104. </div>
  105. <input type="hidden" name="version">
  106. <ul class="dropdown-menu">
  107. <!-- Versions will be added dynamically in version-picker.js -->
  108. </ul>
  109. </div>
  110. </div>
  111. </div>
  112. <h1 class="menu-title">Synapse</h1>
  113. <div class="right-buttons">
  114. <a href="../../print.html" title="Print this book" aria-label="Print this book">
  115. <i id="print-button" class="fa fa-print"></i>
  116. </a>
  117. <a href="https://github.com/matrix-org/synapse" title="Git repository" aria-label="Git repository">
  118. <i id="git-repository-button" class="fa fa-github"></i>
  119. </a>
  120. <a href="https://github.com/matrix-org/synapse/edit/develop/docs/development/synapse_architecture/faster_joins.md" title="Suggest an edit" aria-label="Suggest an edit">
  121. <i id="git-edit-button" class="fa fa-edit"></i>
  122. </a>
  123. </div>
  124. </div>
  125. <div id="search-wrapper" class="hidden">
  126. <form id="searchbar-outer" class="searchbar-outer">
  127. <input type="search" id="searchbar" name="searchbar" placeholder="Search this book ..." aria-controls="searchresults-outer" aria-describedby="searchresults-header">
  128. </form>
  129. <div id="searchresults-outer" class="searchresults-outer hidden">
  130. <div id="searchresults-header" class="searchresults-header"></div>
  131. <ul id="searchresults">
  132. </ul>
  133. </div>
  134. </div>
  135. <!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
  136. <script type="text/javascript">
  137. document.getElementById('sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
  138. document.getElementById('sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
  139. Array.from(document.querySelectorAll('#sidebar a')).forEach(function(link) {
  140. link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
  141. });
  142. </script>
  143. <div id="content" class="content">
  144. <main>
  145. <!-- Page table of contents -->
  146. <div class="sidetoc">
  147. <nav class="pagetoc"></nav>
  148. </div>
  149. <h1 id="how-do-faster-joins-work"><a class="header" href="#how-do-faster-joins-work">How do faster joins work?</a></h1>
  150. <p>This is a work-in-progress set of notes with two goals:</p>
  151. <ul>
  152. <li>act as a reference, explaining how Synapse implements faster joins; and</li>
  153. <li>record the rationale behind our choices.</li>
  154. </ul>
  155. <p>See also <a href="https://github.com/matrix-org/matrix-spec-proposals/pull/3902">MSC3902</a>.</p>
  156. <p>The key idea is described by <a href="https://github.com/matrix-org/matrix-spec-proposals/pull/3706">MSC3706</a>. This allows servers to
  157. request a lightweight response to the federation <code>/send_join</code> endpoint.
  158. This is called a <strong>faster join</strong>, also known as a <strong>partial join</strong>. In these
  159. notes we'll usually use the word &quot;partial&quot; as it matches the database schema.</p>
  160. <h2 id="overview-processing-events-in-a-partially-joined-room"><a class="header" href="#overview-processing-events-in-a-partially-joined-room">Overview: processing events in a partially-joined room</a></h2>
  161. <p>The response to a partial join consists of</p>
  162. <ul>
  163. <li>the requested join event <code>J</code>,</li>
  164. <li>a list of the servers in the room (according to the state before <code>J</code>),</li>
  165. <li>a subset of the state of the room before <code>J</code>,</li>
  166. <li>the full auth chain of that state subset.</li>
  167. </ul>
  168. <p>Synapse marks the room as partially joined by adding a row to the database table
  169. <code>partial_state_rooms</code>. It also marks the join event <code>J</code> as &quot;partially stated&quot;,
  170. meaning that we have neither received nor computed the full state before/after
  171. <code>J</code>. This is done by adding a row to <code>partial_state_events</code>.</p>
  172. <details><summary>DB schema</summary>
  173. <pre><code>matrix=&gt; \d partial_state_events
  174. Table &quot;matrix.partial_state_events&quot;
  175. Column │ Type │ Collation │ Nullable │ Default
  176. ══════════╪══════╪═══════════╪══════════╪═════════
  177. room_id │ text │ │ not null │
  178. event_id │ text │ │ not null │
  179. matrix=&gt; \d partial_state_rooms
  180. Table &quot;matrix.partial_state_rooms&quot;
  181. Column │ Type │ Collation │ Nullable │ Default
  182. ════════════════════════╪════════╪═══════════╪══════════╪═════════
  183. room_id │ text │ │ not null │
  184. device_lists_stream_id │ bigint │ │ not null │ 0
  185. join_event_id │ text │ │ │
  186. joined_via │ text │ │ │
  187. matrix=&gt; \d partial_state_rooms_servers
  188. Table &quot;matrix.partial_state_rooms_servers&quot;
  189. Column │ Type │ Collation │ Nullable │ Default
  190. ═════════════╪══════╪═══════════╪══════════╪═════════
  191. room_id │ text │ │ not null │
  192. server_name │ text │ │ not null │
  193. </code></pre>
  194. <p>Indices, foreign-keys and check constraints are omitted for brevity.</p>
  195. </details>
  196. <p>While partially joined to a room, Synapse receives events <code>E</code> from remote
  197. homeservers as normal, and can create events at the request of its local users.
  198. However, we run into trouble when we enforce the <a href="https://spec.matrix.org/v1.5/server-server-api/#checks-performed-on-receipt-of-a-pdu">checks on an event</a>.</p>
  199. <blockquote>
  200. <ol>
  201. <li>Is a valid event, otherwise it is dropped. For an event to be valid, it
  202. must contain a room_id, and it must comply with the event format of that
  203. room version.</li>
  204. <li>Passes signature checks, otherwise it is dropped.</li>
  205. <li>Passes hash checks, otherwise it is redacted before being processed further.</li>
  206. <li>Passes authorization rules based on the event’s auth events, otherwise it
  207. is rejected.</li>
  208. <li><strong>Passes authorization rules based on the state before the event, otherwise
  209. it is rejected.</strong></li>
  210. <li><strong>Passes authorization rules based on the current state of the room,
  211. otherwise it is “soft failed”.</strong></li>
  212. </ol>
  213. </blockquote>
  214. <p>We can enforce checks 1--4 without any problems.
  215. But we cannot enforce checks 5 or 6 with complete certainty, since Synapse does
  216. not know the full state before <code>E</code>, nor that of the room.</p>
  217. <h3 id="partial-state"><a class="header" href="#partial-state">Partial state</a></h3>
  218. <p>Instead, we make a best-effort approximation.
  219. While the room is considered partially joined, Synapse tracks the &quot;partial
  220. state&quot; before events.
  221. This works in a similar way as regular state:</p>
  222. <ul>
  223. <li>The partial state before <code>J</code> is that given to us by the partial join response.</li>
  224. <li>The partial state before an event <code>E</code> is the resolution of the partial states
  225. after each of <code>E</code>'s <code>prev_event</code>s.</li>
  226. <li>If <code>E</code> is rejected or a message event, the partial state after <code>E</code> is the
  227. partial state before <code>E</code>.</li>
  228. <li>Otherwise, the partial state after <code>E</code> is the partial state before <code>E</code>, plus
  229. <code>E</code> itself.</li>
  230. </ul>
  231. <p>More concisely, partial state propagates just like full state; the only
  232. difference is that we &quot;seed&quot; it with an incomplete initial state.
  233. Synapse records that we have only calculated partial state for this event with
  234. a row in <code>partial_state_events</code>.</p>
  235. <p>While the room remains partially stated, check 5 on incoming events to that
  236. room becomes:</p>
  237. <blockquote>
  238. <ol start="5">
  239. <li>Passes authorization rules based on <strong>the resolution between the partial
  240. state before <code>E</code> and <code>E</code>'s auth events.</strong> If the event fails to pass
  241. authorization rules, it is rejected.</li>
  242. </ol>
  243. </blockquote>
  244. <p>Additionally, check 6 is deleted: no soft-failures are enforced.</p>
  245. <p>While partially joined, the current partial state of the room is defined as the
  246. resolution across the partial states after all forward extremities in the room.</p>
  247. <p><em>Remark.</em> Events with partial state are <em>not</em> considered
  248. <a href="../room-dag-concepts.html#outliers">outliers</a>.</p>
  249. <h3 id="approximation-error"><a class="header" href="#approximation-error">Approximation error</a></h3>
  250. <p>Using partial state means the auth checks can fail in a few different ways<sup class="footnote-reference"><a href="#2">1</a></sup>.</p>
  251. <div class="footnote-definition" id="2"><sup class="footnote-definition-label">1</sup>
  252. <p>Is this exhaustive?</p>
  253. </div>
  254. <ul>
  255. <li>We may erroneously accept an incoming event in check 5 based on partial state
  256. when it would have been rejected based on full state, or vice versa.</li>
  257. <li>This means that an event could erroneously be added to the current partial
  258. state of the room when it would not be present in the full state of the room,
  259. or vice versa.</li>
  260. <li>Additionally, we may have skipped soft-failing an event that would have been
  261. soft-failed based on full state.</li>
  262. </ul>
  263. <p>(Note that the discrepancies described in the last two bullets are user-visible.)</p>
  264. <p>This means that we have to be very careful when we want to lookup pieces of room
  265. state in a partially-joined room. Our approximation of the state may be
  266. incorrect or missing. But we can make some educated guesses. If</p>
  267. <ul>
  268. <li>our partial state is likely to be correct, or</li>
  269. <li>the consequences of our partial state being incorrect are minor,</li>
  270. </ul>
  271. <p>then we proceed as normal, and let the resync process fix up any mistakes (see
  272. below).</p>
  273. <p>When is our partial state likely to be correct?</p>
  274. <ul>
  275. <li>It's more accurate the closer we are to the partial join event. (So we should
  276. ideally complete the resync as soon as possible.)</li>
  277. <li>Non-member events: we will have received them as part of the partial join
  278. response, if they were part of the room state at that point. We may
  279. incorrectly accept or reject updates to that state (at first because we lack
  280. remote membership information; later because of compounding errors), so these
  281. can become incorrect over time.</li>
  282. <li>Local members' memberships: we are the only ones who can create join and
  283. knock events for our users. We can't be completely confident in the
  284. correctness of bans, invites and kicks from other homeservers, but the resync
  285. process should correct any mistakes.</li>
  286. <li>Remote members' memberships: we did not receive these in the /send_join
  287. response, so we have essentially no idea if these are correct or not.</li>
  288. </ul>
  289. <p>In short, we deem it acceptable to trust the partial state for non-membership
  290. and local membership events. For remote membership events, we wait for the
  291. resync to complete, at which point we have the full state of the room and can
  292. proceed as normal.</p>
  293. <h3 id="fixing-the-approximation-with-a-resync"><a class="header" href="#fixing-the-approximation-with-a-resync">Fixing the approximation with a resync</a></h3>
  294. <p>The partial-state approximation is only a temporary affair. In the background,
  295. synapse beings a &quot;resync&quot; process. This is a continuous loop, starting at the
  296. partial join event and proceeding downwards through the event graph. For each
  297. <code>E</code> seen in the room since partial join, Synapse will fetch </p>
  298. <ul>
  299. <li>the event ids in the state of the room before <code>E</code>, via
  300. <a href="https://spec.matrix.org/v1.5/server-server-api/#get_matrixfederationv1state_idsroomid"><code>/state_ids</code></a>;</li>
  301. <li>the event ids in the full auth chain of <code>E</code>, included in the <code>/state_ids</code>
  302. response; and</li>
  303. <li>any events from the previous two bullets that Synapse hasn't persisted, via
  304. <a href="https://spec.matrix.org/v1.5/server-server-api/#get_matrixfederationv1stateroomid">`/state</a>.</li>
  305. </ul>
  306. <p>This means Synapse has (or can compute) the full state before <code>E</code>, which allows
  307. Synapse to properly authorise or reject <code>E</code>. At this point ,the event
  308. is considered to have &quot;full state&quot; rather than &quot;partial state&quot;. We record this
  309. by removing <code>E</code> from the <code>partial_state_events</code> table.</p>
  310. <p>[<strong>TODO:</strong> Does Synapse persist a new state group for the full state
  311. before <code>E</code>, or do we alter the (partial-)state group in-place? Are state groups
  312. ever marked as partially-stated? ]</p>
  313. <p>This scheme means it is possible for us to have accepted and sent an event to
  314. clients, only to reject it during the resync. From a client's perspective, the
  315. effect is similar to a retroactive
  316. state change due to state resolution---i.e. a &quot;state reset&quot;.<sup class="footnote-reference"><a href="#3">2</a></sup></p>
  317. <div class="footnote-definition" id="3"><sup class="footnote-definition-label">2</sup>
  318. <p>Clients should refresh caches to detect such a change. Rumour has it that
  319. sliding sync will fix this.</p>
  320. </div>
  321. <p>When all events since the join <code>J</code> have been fully-stated, the room resync
  322. process is complete. We record this by removing the room from
  323. <code>partial_state_rooms</code>.</p>
  324. <h2 id="faster-joins-on-workers"><a class="header" href="#faster-joins-on-workers">Faster joins on workers</a></h2>
  325. <p>For the time being, the resync process happens on the master worker.
  326. A new replication stream <code>un_partial_stated_room</code> is added. Whenever a resync
  327. completes and a partial-state room becomes fully stated, a new message is sent
  328. into that stream containing the room ID.</p>
  329. <h2 id="notes-on-specific-cases"><a class="header" href="#notes-on-specific-cases">Notes on specific cases</a></h2>
  330. <blockquote>
  331. <p><strong>NB.</strong> The notes below are rough. Some of them are hidden under <code>&lt;details&gt;</code>
  332. disclosures because they have yet to be implemented in mainline Synapse.</p>
  333. </blockquote>
  334. <h3 id="creating-events-during-a-partial-join"><a class="header" href="#creating-events-during-a-partial-join">Creating events during a partial join</a></h3>
  335. <p>When sending out messages during a partial join, we assume our partial state is
  336. accurate and proceed as normal. For this to have any hope of succeeding at all,
  337. our partial state must contain an entry for each of the (type, state key) pairs
  338. <a href="https://spec.matrix.org/v1.3/rooms/v10/#authorization-rules">specified by the auth rules</a>:</p>
  339. <ul>
  340. <li><code>m.room.create</code></li>
  341. <li><code>m.room.join_rules</code></li>
  342. <li><code>m.room.power_levels</code></li>
  343. <li><code>m.room.third_party_invite</code></li>
  344. <li><code>m.room.member</code></li>
  345. </ul>
  346. <p>The first four of these should be present in the state before <code>J</code> that is given
  347. to us in the partial join response; only membership events are omitted. In order
  348. for us to consider the user joined, we must have their membership event. That
  349. means the only possible omission is the target's membership in an invite, kick
  350. or ban.</p>
  351. <p>The worst possibility is that we locally invite someone who is banned according to
  352. the full state, because we lack their ban in our current partial state. The rest
  353. of the federation---at least, those who are fully joined---should correctly
  354. enforce the <a href="https://spec.matrix.org/v1.3/client-server-api/#room-membership">membership transition constraints</a>. So any the erroneous invite should be ignored by fully-joined
  355. homeservers and resolved by the resync for partially-joined homeservers.</p>
  356. <p>In more generality, there are two problems we're worrying about here:</p>
  357. <ul>
  358. <li>We might create an event that is valid under our partial state, only to later
  359. find out that is actually invalid according to the full state.</li>
  360. <li>Or: we might refuse to create an event that is invalid under our partial
  361. state, even though it would be perfectly valid under the full state.</li>
  362. </ul>
  363. <p>However we expect such problems to be unlikely in practise, because</p>
  364. <ul>
  365. <li>We trust that the room has sensible power levels, e.g. that bad actors with
  366. high power levels are demoted before their ban.</li>
  367. <li>We trust that the resident server provides us up-to-date power levels, join
  368. rules, etc.</li>
  369. <li>State changes in rooms are relatively infrequent, and the resync period is
  370. relatively quick.</li>
  371. </ul>
  372. <h4 id="sending-out-the-event-over-federation"><a class="header" href="#sending-out-the-event-over-federation">Sending out the event over federation</a></h4>
  373. <p><strong>TODO:</strong> needs prose fleshing out.</p>
  374. <p>Normally: send out in a fed txn to all HSes in the room.
  375. We only know that some HSes were in the room at some point. Wat do.
  376. Send it out to the list of servers from the first join.
  377. <strong>TODO</strong> what do we do here if we have full state?
  378. If the prev event was created by us, we can risk sending it to the wrong HS. (Motivation: privacy concern of the content. Not such a big deal for a public room or an encrypted room. But non-encrypted invite-only...)
  379. But don't want to send out sensitive data in other HS's events in this way.</p>
  380. <p>Suppose we discover after resync that we shouldn't have sent out one our events (not a prev_event) to a target HS. Not much we can do.
  381. What about if we didn't send them an event but shouldn't've?
  382. E.g. what if someone joined from a new HS shortly after you did? We wouldn't talk to them.
  383. Could imagine sending out the &quot;Missed&quot; events after the resync but... painful to work out what they shuld have seen if they joined/left.
  384. Instead, just send them the latest event (if they're still in the room after resync) and let them backfill.(?)</p>
  385. <ul>
  386. <li>Don't do this currently.</li>
  387. <li>If anyone who has received our messages sends a message to a HS we missed, they can backfill our messages</li>
  388. <li>Gap: rooms which are infrequently used and take a long time to resync.</li>
  389. </ul>
  390. <h3 id="joining-after-a-partial-join"><a class="header" href="#joining-after-a-partial-join">Joining after a partial join</a></h3>
  391. <p><strong>NB.</strong> Not yet implemented.</p>
  392. <details>
  393. <p><strong>TODO:</strong> needs prose fleshing out. Liase with Matthieu. Explain why /send_join
  394. (Rich was surprised we didn't just create it locally. Answer: to try and avoid
  395. a join which then gets rejected after resync.)</p>
  396. <p>We don't know for sure that any join we create would be accepted.
  397. E.g. the joined user might have been banned; the join rules might have changed in a way that we didn't realise... some way in which the partial state was mistaken.
  398. Instead, do another partial make-join/send-join handshake to confirm that the join works.</p>
  399. <ul>
  400. <li>Probably going to get a bunch of duplicate state events and auth events.... but the point of partial joins is that these should be small. Many are already persisted = good.</li>
  401. <li>What if the second send_join response includes a different list of reisdent HSes? Could ignore it.
  402. <ul>
  403. <li>Could even have a special flag that says &quot;just make me a join&quot;, i.e. don't bother giving me state or servers in room. Deffo want the auth chain tho.</li>
  404. </ul>
  405. </li>
  406. <li>SQ: wrt device lists it's a lot safer to ignore it!!!!!</li>
  407. <li>What if the state at the second join is inconsistent with what we have? Ignore it?</li>
  408. </ul>
  409. </details>
  410. <h3 id="leaving-and-kicks-and-bans-after-a-partial-join"><a class="header" href="#leaving-and-kicks-and-bans-after-a-partial-join">Leaving (and kicks and bans) after a partial join</a></h3>
  411. <p><strong>NB.</strong> Not yet implemented.</p>
  412. <details>
  413. <p>When you're fully joined to a room, to have <code>U</code> leave a room their homeserver
  414. needs to</p>
  415. <ul>
  416. <li>create a new leave event for <code>U</code> which will be accepted by other homeservers,
  417. and</li>
  418. <li>send that event <code>U</code> out to the homeservers in the federation.</li>
  419. </ul>
  420. <p>When is a leave event accepted? See
  421. <a href="https://spec.matrix.org/v1.5/rooms/v10/#authorization-rules">v10 auth rules</a>:</p>
  422. <blockquote>
  423. <ol start="4">
  424. <li>If type is m.room.member: [...]
  425. &gt;
  426. &gt; 5. If membership is leave:
  427. &gt;
  428. &gt; 1. If the sender matches state_key, allow if and only if that user’s current membership state is invite, join, or knock.
  429. 2. [...]</li>
  430. </ol>
  431. </blockquote>
  432. <p>I think this means that (well-formed!) self-leaves are governed entirely by
  433. 4.5.1. This means that if we correctly calculate state which says that <code>U</code> is
  434. invited, joined or knocked and include it in the leave's auth events, our event
  435. is accepted by checks 4 and 5 on incoming events.</p>
  436. <blockquote>
  437. <ol start="4">
  438. <li>Passes authorization rules based on the event’s auth events, otherwise
  439. &gt; it is rejected.</li>
  440. <li>Passes authorization rules based on the state before the event, otherwise
  441. &gt; it is rejected.</li>
  442. </ol>
  443. </blockquote>
  444. <p>The only way to fail check 6 is if the receiving server's current state of the
  445. room says that <code>U</code> is banned, has left, or has no membership event. But this is
  446. fine: the receiving server already thinks that <code>U</code> isn't in the room.</p>
  447. <blockquote>
  448. <ol start="6">
  449. <li>Passes authorization rules based on the current state of the room,
  450. &gt; otherwise it is “soft failed”.</li>
  451. </ol>
  452. </blockquote>
  453. <p>For the second point (publishing the leave event), the best thing we can do is
  454. to is publish to all HSes we know to be currently in the room. If they miss that
  455. event, they might send us traffic in the room that we don't care about. This is
  456. a problem with leaving after a &quot;full&quot; join; we don't seek to fix this with
  457. partial joins.</p>
  458. <p>(With that said: there's nothing machine-readable in the /send response. I don't
  459. think we can deduce &quot;destination has left the room&quot; from a failure to /send an
  460. event into that room?)</p>
  461. <h4 id="can-we-still-do-this-during-a-partial-join"><a class="header" href="#can-we-still-do-this-during-a-partial-join">Can we still do this during a partial join?</a></h4>
  462. <p>We can create leave events and can choose what gets included in our auth events,
  463. so we can be sure that we pass check 4 on incoming events. For check 5, we might
  464. have an incorrect view of the state before an event.
  465. The only way we might erroneously think a leave is valid is if</p>
  466. <ul>
  467. <li>the partial state before the leave has <code>U</code> joined, invited or knocked, but</li>
  468. <li>the full state before the leave has <code>U</code> banned, left or not present,</li>
  469. </ul>
  470. <p>in which case the leave doesn't make anything worse: other HSes already consider
  471. us as not in the room, and will continue to do so after seeing the leave.</p>
  472. <p>The remaining obstacle is then: can we safely broadcast the leave event? We may
  473. miss servers or incorrectly think that a server is in the room. Or the
  474. destination server may be offline and miss the transaction containing our leave
  475. event.This should self-heal when they see an event whose <code>prev_events</code> descends
  476. from our leave.</p>
  477. <p>Another option we considered was to use federation <code>/send_leave</code> to ask a
  478. fully-joined server to send out the event on our behalf. But that introduces
  479. complexity without much benefit. Besides, as Rich put it,</p>
  480. <blockquote>
  481. <p>sending out leaves is pretty best-effort currently</p>
  482. </blockquote>
  483. <p>so this is probably good enough as-is.</p>
  484. <h4 id="cleanup-after-the-last-leave"><a class="header" href="#cleanup-after-the-last-leave">Cleanup after the last leave</a></h4>
  485. <p><strong>TODO</strong>: what cleanup is necessary? Is it all just nice-to-have to save unused
  486. work?</p>
  487. </details>
  488. </main>
  489. <nav class="nav-wrapper" aria-label="Page navigation">
  490. <!-- Mobile navigation buttons -->
  491. <a rel="prev" href="../../tcp_replication.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
  492. <i class="fa fa-angle-left"></i>
  493. </a>
  494. <a rel="next" href="../../development/internal_documentation/index.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
  495. <i class="fa fa-angle-right"></i>
  496. </a>
  497. <div style="clear: both"></div>
  498. </nav>
  499. </div>
  500. </div>
  501. <nav class="nav-wide-wrapper" aria-label="Page navigation">
  502. <a rel="prev" href="../../tcp_replication.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
  503. <i class="fa fa-angle-left"></i>
  504. </a>
  505. <a rel="next" href="../../development/internal_documentation/index.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
  506. <i class="fa fa-angle-right"></i>
  507. </a>
  508. </nav>
  509. </div>
  510. <script type="text/javascript">
  511. window.playground_copyable = true;
  512. </script>
  513. <script src="../../elasticlunr.min.js" type="text/javascript" charset="utf-8"></script>
  514. <script src="../../mark.min.js" type="text/javascript" charset="utf-8"></script>
  515. <script src="../../searcher.js" type="text/javascript" charset="utf-8"></script>
  516. <script src="../../clipboard.min.js" type="text/javascript" charset="utf-8"></script>
  517. <script src="../../highlight.js" type="text/javascript" charset="utf-8"></script>
  518. <script src="../../book.js" type="text/javascript" charset="utf-8"></script>
  519. <!-- Custom JS scripts -->
  520. <script type="text/javascript" src="../../docs/website_files/table-of-contents.js"></script>
  521. <script type="text/javascript" src="../../docs/website_files/version-picker.js"></script>
  522. <script type="text/javascript" src="../../docs/website_files/version.js"></script>
  523. </body>
  524. </html>