Files
synapse/develop/development/synapse_architecture/faster_joins.html
2026-01-07 18:48:31 +00:00

604 lines
45 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE HTML>
<html lang="en" class="light sidebar-visible" dir="ltr">
<head>
<!-- Book generated using mdBook -->
<meta charset="UTF-8">
<title>Faster remote joins - Synapse</title>
<!-- Custom HTML head -->
<meta name="description" content="">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="theme-color" content="#ffffff">
<link rel="icon" href="../../favicon-de23e50b.svg">
<link rel="shortcut icon" href="../../favicon-8114d1fc.png">
<link rel="stylesheet" href="../../css/variables-8adf115d.css">
<link rel="stylesheet" href="../../css/general-2459343d.css">
<link rel="stylesheet" href="../../css/chrome-ae938929.css">
<link rel="stylesheet" href="../../css/print-9e4910d8.css" media="print">
<!-- Fonts -->
<link rel="stylesheet" href="../../fonts/fonts-9644e21d.css">
<!-- Highlight.js Stylesheets -->
<link rel="stylesheet" id="mdbook-highlight-css" href="../../highlight-493f70e1.css">
<link rel="stylesheet" id="mdbook-tomorrow-night-css" href="../../tomorrow-night-4c0ae647.css">
<link rel="stylesheet" id="mdbook-ayu-highlight-css" href="../../ayu-highlight-3fdfc3ac.css">
<!-- Custom theme stylesheets -->
<link rel="stylesheet" href="../../docs/website_files/indent-section-headers-3390f960.css">
<link rel="stylesheet" href="../../docs/website_files/version-picker-ddc5216e.css">
<!-- Provide site root and default themes to javascript -->
<script>
const path_to_root = "../../";
const default_light_theme = "light";
const default_dark_theme = "navy";
window.path_to_searchindex_js = "../../searchindex-ad6b5e04.js";
</script>
<!-- Start loading toc.js asap -->
<script src="../../toc-4dc10a98.js"></script>
</head>
<body>
<div id="mdbook-help-container">
<div id="mdbook-help-popup">
<h2 class="mdbook-help-title">Keyboard shortcuts</h2>
<div>
<p>Press <kbd></kbd> or <kbd></kbd> to navigate between chapters</p>
<p>Press <kbd>S</kbd> or <kbd>/</kbd> to search in the book</p>
<p>Press <kbd>?</kbd> to show this help</p>
<p>Press <kbd>Esc</kbd> to hide this help</p>
</div>
</div>
</div>
<div id="mdbook-body-container">
<!-- Work around some values being stored in localStorage wrapped in quotes -->
<script>
try {
let theme = localStorage.getItem('mdbook-theme');
let sidebar = localStorage.getItem('mdbook-sidebar');
if (theme.startsWith('"') && theme.endsWith('"')) {
localStorage.setItem('mdbook-theme', theme.slice(1, theme.length - 1));
}
if (sidebar.startsWith('"') && sidebar.endsWith('"')) {
localStorage.setItem('mdbook-sidebar', sidebar.slice(1, sidebar.length - 1));
}
} catch (e) { }
</script>
<!-- Set the theme before any content is loaded, prevents flash -->
<script>
const default_theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? default_dark_theme : default_light_theme;
let theme;
try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
if (theme === null || theme === undefined) { theme = default_theme; }
const html = document.documentElement;
html.classList.remove('light')
html.classList.add(theme);
html.classList.add("js");
</script>
<input type="checkbox" id="mdbook-sidebar-toggle-anchor" class="hidden">
<!-- Hide / unhide sidebar before it is displayed -->
<script>
let sidebar = null;
const sidebar_toggle = document.getElementById("mdbook-sidebar-toggle-anchor");
if (document.body.clientWidth >= 1080) {
try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { }
sidebar = sidebar || 'visible';
} else {
sidebar = 'hidden';
sidebar_toggle.checked = false;
}
if (sidebar === 'visible') {
sidebar_toggle.checked = true;
} else {
html.classList.remove('sidebar-visible');
}
</script>
<nav id="mdbook-sidebar" class="sidebar" aria-label="Table of contents">
<!-- populated by js -->
<mdbook-sidebar-scrollbox class="sidebar-scrollbox"></mdbook-sidebar-scrollbox>
<noscript>
<iframe class="sidebar-iframe-outer" src="../../toc.html"></iframe>
</noscript>
<div id="mdbook-sidebar-resize-handle" class="sidebar-resize-handle">
<div class="sidebar-resize-indicator"></div>
</div>
</nav>
<div id="mdbook-page-wrapper" class="page-wrapper">
<div class="page">
<div id="mdbook-menu-bar-hover-placeholder"></div>
<div id="mdbook-menu-bar" class="menu-bar sticky">
<div class="left-buttons">
<label id="mdbook-sidebar-toggle" class="icon-button" for="mdbook-sidebar-toggle-anchor" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="mdbook-sidebar">
<span class=fa-svg><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--! Font Awesome Free 6.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2022 Fonticons, Inc. --><path d="M0 96C0 78.3 14.3 64 32 64H416c17.7 0 32 14.3 32 32s-14.3 32-32 32H32C14.3 128 0 113.7 0 96zM0 256c0-17.7 14.3-32 32-32H416c17.7 0 32 14.3 32 32s-14.3 32-32 32H32c-17.7 0-32-14.3-32-32zM448 416c0 17.7-14.3 32-32 32H32c-17.7 0-32-14.3-32-32s14.3-32 32-32H416c17.7 0 32 14.3 32 32z"/></svg></span>
</label>
<button id="mdbook-theme-toggle" class="icon-button" type="button" title="Change theme" aria-label="Change theme" aria-haspopup="true" aria-expanded="false" aria-controls="mdbook-theme-list">
<span class=fa-svg><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512"><!--! Font Awesome Free 6.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2022 Fonticons, Inc. --><path d="M371.3 367.1c27.3-3.9 51.9-19.4 67.2-42.9L600.2 74.1c12.6-19.5 9.4-45.3-7.6-61.2S549.7-4.4 531.1 9.6L294.4 187.2c-24 18-38.2 46.1-38.4 76.1L371.3 367.1zm-19.6 25.4l-116-104.4C175.9 290.3 128 339.6 128 400c0 3.9 .2 7.8 .6 11.6c1.8 17.5-10.2 36.4-27.8 36.4H96c-17.7 0-32 14.3-32 32s14.3 32 32 32H240c61.9 0 112-50.1 112-112c0-2.5-.1-5-.2-7.5z"/></svg></span>
</button>
<ul id="mdbook-theme-list" class="theme-popup" aria-label="Themes" role="menu">
<li role="none"><button role="menuitem" class="theme" id="mdbook-theme-default_theme">Auto</button></li>
<li role="none"><button role="menuitem" class="theme" id="mdbook-theme-light">Light</button></li>
<li role="none"><button role="menuitem" class="theme" id="mdbook-theme-rust">Rust</button></li>
<li role="none"><button role="menuitem" class="theme" id="mdbook-theme-coal">Coal</button></li>
<li role="none"><button role="menuitem" class="theme" id="mdbook-theme-navy">Navy</button></li>
<li role="none"><button role="menuitem" class="theme" id="mdbook-theme-ayu">Ayu</button></li>
</ul>
<button id="mdbook-search-toggle" class="icon-button" type="button" title="Search (`/`)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="/ s" aria-controls="mdbook-searchbar">
<span class=fa-svg><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--! Font Awesome Free 6.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2022 Fonticons, Inc. --><path d="M416 208c0 45.9-14.9 88.3-40 122.7L502.6 457.4c12.5 12.5 12.5 32.8 0 45.3s-32.8 12.5-45.3 0L330.7 376c-34.4 25.2-76.8 40-122.7 40C93.1 416 0 322.9 0 208S93.1 0 208 0S416 93.1 416 208zM208 352c79.5 0 144-64.5 144-144s-64.5-144-144-144S64 128.5 64 208s64.5 144 144 144z"/></svg></span>
</button>
</div>
<!-- BEGIN CUSTOM SYNAPSE MODIFICATIONS -->
<div class="version-picker">
<div class="dropdown">
<div class="select">
<span></span>
<i class="fa fa-chevron-down"></i>
</div>
<input type="hidden" name="version">
<ul class="dropdown-menu">
<!-- Versions will be added dynamically in version-picker.js -->
</ul>
</div>
</div>
<!-- END CUSTOM SYNAPSE MODIFICATIONS -->
<h1 class="menu-title">Synapse</h1>
<div class="right-buttons">
<a href="../../print.html" title="Print this book" aria-label="Print this book">
<span class=fa-svg id="print-button"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--! Font Awesome Free 6.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2022 Fonticons, Inc. --><path d="M128 0C92.7 0 64 28.7 64 64v96h64V64H354.7L384 93.3V160h64V93.3c0-17-6.7-33.3-18.7-45.3L400 18.7C388 6.7 371.7 0 354.7 0H128zM384 352v32 64H128V384 368 352H384zm64 32h32c17.7 0 32-14.3 32-32V256c0-35.3-28.7-64-64-64H64c-35.3 0-64 28.7-64 64v96c0 17.7 14.3 32 32 32H64v64c0 35.3 28.7 64 64 64H384c35.3 0 64-28.7 64-64V384zm-16-88c-13.3 0-24-10.7-24-24s10.7-24 24-24s24 10.7 24 24s-10.7 24-24 24z"/></svg></span>
</a>
<a href="https://github.com/element-hq/synapse" title="Git repository" aria-label="Git repository">
<span class=fa-svg><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 496 512"><!--! Font Awesome Free 6.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2022 Fonticons, Inc. --><path d="M165.9 397.4c0 2-2.3 3.6-5.2 3.6-3.3.3-5.6-1.3-5.6-3.6 0-2 2.3-3.6 5.2-3.6 3-.3 5.6 1.3 5.6 3.6zm-31.1-4.5c-.7 2 1.3 4.3 4.3 4.9 2.6 1 5.6 0 6.2-2s-1.3-4.3-4.3-5.2c-2.6-.7-5.5.3-6.2 2.3zm44.2-1.7c-2.9.7-4.9 2.6-4.6 4.9.3 2 2.9 3.3 5.9 2.6 2.9-.7 4.9-2.6 4.6-4.6-.3-1.9-3-3.2-5.9-2.9zM244.8 8C106.1 8 0 113.3 0 252c0 110.9 69.8 205.8 169.5 239.2 12.8 2.3 17.3-5.6 17.3-12.1 0-6.2-.3-40.4-.3-61.4 0 0-70 15-84.7-29.8 0 0-11.4-29.1-27.8-36.6 0 0-22.9-15.7 1.6-15.4 0 0 24.9 2 38.6 25.8 21.9 38.6 58.6 27.5 72.9 20.9 2.3-16 8.8-27.1 16-33.7-55.9-6.2-112.3-14.3-112.3-110.5 0-27.5 7.6-41.3 23.6-58.9-2.6-6.5-11.1-33.3 2.6-67.9 20.9-6.5 69 27 69 27 20-5.6 41.5-8.5 62.8-8.5s42.8 2.9 62.8 8.5c0 0 48.1-33.6 69-27 13.7 34.7 5.2 61.4 2.6 67.9 16 17.7 25.8 31.5 25.8 58.9 0 96.5-58.9 104.2-114.8 110.5 9.2 7.9 17 22.9 17 46.4 0 33.7-.3 75.4-.3 83.6 0 6.5 4.6 14.4 17.3 12.1C428.2 457.8 496 362.9 496 252 496 113.3 383.5 8 244.8 8zM97.2 352.9c-1.3 1-1 3.3.7 5.2 1.6 1.6 3.9 2.3 5.2 1 1.3-1 1-3.3-.7-5.2-1.6-1.6-3.9-2.3-5.2-1zm-10.8-8.1c-.7 1.3.3 2.9 2.3 3.9 1.6 1 3.6.7 4.3-.7.7-1.3-.3-2.9-2.3-3.9-2-.6-3.6-.3-4.3.7zm32.4 35.6c-1.6 1.3-1 4.3 1.3 6.2 2.3 2.3 5.2 2.6 6.5 1 1.3-1.3.7-4.3-1.3-6.2-2.2-2.3-5.2-2.6-6.5-1zm-11.4-14.7c-1.6 1-1.6 3.6 0 5.9 1.6 2.3 4.3 3.3 5.6 2.3 1.6-1.3 1.6-3.9 0-6.2-1.4-2.3-4-3.3-5.6-2z"/></svg></span>
</a>
<a href="https://github.com/element-hq/synapse/edit/develop/docs/development/synapse_architecture/faster_joins.md" title="Suggest an edit" aria-label="Suggest an edit" rel="edit">
<span class=fa-svg id="git-edit-button"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--! Font Awesome Free 6.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2022 Fonticons, Inc. --><path d="M421.7 220.3l-11.3 11.3-22.6 22.6-205 205c-6.6 6.6-14.8 11.5-23.8 14.1L30.8 511c-8.4 2.5-17.5 .2-23.7-6.1S-1.5 489.7 1 481.2L38.7 353.1c2.6-9 7.5-17.2 14.1-23.8l205-205 22.6-22.6 11.3-11.3 33.9 33.9 62.1 62.1 33.9 33.9zM96 353.9l-9.3 9.3c-.9 .9-1.6 2.1-2 3.4l-25.3 86 86-25.3c1.3-.4 2.5-1.1 3.4-2l9.3-9.3H112c-8.8 0-16-7.2-16-16V353.9zM453.3 19.3l39.4 39.4c25 25 25 65.5 0 90.5l-14.5 14.5-22.6 22.6-11.3 11.3-33.9-33.9-62.1-62.1L314.3 67.7l11.3-11.3 22.6-22.6 14.5-14.5c25-25 65.5-25 90.5 0z"/></svg></span>
</a>
</div>
</div>
<div id="mdbook-search-wrapper" class="hidden">
<form id="mdbook-searchbar-outer" class="searchbar-outer">
<div class="search-wrapper">
<input type="search" id="mdbook-searchbar" name="searchbar" placeholder="Search this book ..." aria-controls="mdbook-searchresults-outer" aria-describedby="searchresults-header">
<div class="spinner-wrapper">
<span class=fa-svg id="fa-spin"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--! Font Awesome Free 6.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2022 Fonticons, Inc. --><path d="M304 48c0-26.5-21.5-48-48-48s-48 21.5-48 48s21.5 48 48 48s48-21.5 48-48zm0 416c0-26.5-21.5-48-48-48s-48 21.5-48 48s21.5 48 48 48s48-21.5 48-48zM48 304c26.5 0 48-21.5 48-48s-21.5-48-48-48s-48 21.5-48 48s21.5 48 48 48zm464-48c0-26.5-21.5-48-48-48s-48 21.5-48 48s21.5 48 48 48s48-21.5 48-48zM142.9 437c18.7-18.7 18.7-49.1 0-67.9s-49.1-18.7-67.9 0s-18.7 49.1 0 67.9s49.1 18.7 67.9 0zm0-294.2c18.7-18.7 18.7-49.1 0-67.9S93.7 56.2 75 75s-18.7 49.1 0 67.9s49.1 18.7 67.9 0zM369.1 437c18.7 18.7 49.1 18.7 67.9 0s18.7-49.1 0-67.9s-49.1-18.7-67.9 0s-18.7 49.1 0 67.9z"/></svg></span>
</div>
</div>
</form>
<div id="mdbook-searchresults-outer" class="searchresults-outer hidden">
<div id="mdbook-searchresults-header" class="searchresults-header"></div>
<ul id="mdbook-searchresults">
</ul>
</div>
</div>
<!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
<script>
document.getElementById('mdbook-sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
document.getElementById('mdbook-sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
Array.from(document.querySelectorAll('#mdbook-sidebar a')).forEach(function(link) {
link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
});
</script>
<div id="mdbook-content" class="content">
<main>
<h1 id="how-do-faster-joins-work"><a class="header" href="#how-do-faster-joins-work">How do faster joins work?</a></h1>
<p>This is a work-in-progress set of notes with two goals:</p>
<ul>
<li>act as a reference, explaining how Synapse implements faster joins; and</li>
<li>record the rationale behind our choices.</li>
</ul>
<p>See also <a href="https://github.com/matrix-org/matrix-spec-proposals/pull/3902">MSC3902</a>.</p>
<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
request a lightweight response to the federation <code>/send_join</code> endpoint.
This is called a <strong>faster join</strong>, also known as a <strong>partial join</strong>. In these
notes well usually use the word “partial” as it matches the database schema.</p>
<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>
<p>The response to a partial join consists of</p>
<ul>
<li>the requested join event <code>J</code>,</li>
<li>a list of the servers in the room (according to the state before <code>J</code>),</li>
<li>a subset of the state of the room before <code>J</code>,</li>
<li>the full auth chain of that state subset.</li>
</ul>
<p>Synapse marks the room as partially joined by adding a row to the database table
<code>partial_state_rooms</code>. It also marks the join event <code>J</code> as “partially stated”,
meaning that we have neither received nor computed the full state before/after
<code>J</code>. This is done by adding a row to <code>partial_state_events</code>.</p>
<details><summary>DB schema</summary>
<pre><code>matrix=&gt; \d partial_state_events
Table "matrix.partial_state_events"
Column │ Type │ Collation │ Nullable │ Default
══════════╪══════╪═══════════╪══════════╪═════════
room_id │ text │ │ not null │
event_id │ text │ │ not null │
matrix=&gt; \d partial_state_rooms
Table "matrix.partial_state_rooms"
Column │ Type │ Collation │ Nullable │ Default
════════════════════════╪════════╪═══════════╪══════════╪═════════
room_id │ text │ │ not null │
device_lists_stream_id │ bigint │ │ not null │ 0
join_event_id │ text │ │ │
joined_via │ text │ │ │
matrix=&gt; \d partial_state_rooms_servers
Table "matrix.partial_state_rooms_servers"
Column │ Type │ Collation │ Nullable │ Default
═════════════╪══════╪═══════════╪══════════╪═════════
room_id │ text │ │ not null │
server_name │ text │ │ not null │
</code></pre>
<p>Indices, foreign-keys and check constraints are omitted for brevity.</p>
</details>
<p>While partially joined to a room, Synapse receives events <code>E</code> from remote
homeservers as normal, and can create events at the request of its local users.
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>
<blockquote>
<ol>
<li>Is a valid event, otherwise it is dropped. For an event to be valid, it
must contain a room_id, and it must comply with the event format of that
room version.</li>
<li>Passes signature checks, otherwise it is dropped.</li>
<li>Passes hash checks, otherwise it is redacted before being processed further.</li>
<li>Passes authorization rules based on the events auth events, otherwise it
is rejected.</li>
<li><strong>Passes authorization rules based on the state before the event, otherwise
it is rejected.</strong></li>
<li><strong>Passes authorization rules based on the current state of the room,
otherwise it is “soft failed”.</strong></li>
</ol>
</blockquote>
<p>We can enforce checks 14 without any problems.
But we cannot enforce checks 5 or 6 with complete certainty, since Synapse does
not know the full state before <code>E</code>, nor that of the room.</p>
<h3 id="partial-state"><a class="header" href="#partial-state">Partial state</a></h3>
<p>Instead, we make a best-effort approximation.
While the room is considered partially joined, Synapse tracks the “partial
state” before events.
This works in a similar way as regular state:</p>
<ul>
<li>The partial state before <code>J</code> is that given to us by the partial join response.</li>
<li>The partial state before an event <code>E</code> is the resolution of the partial states
after each of <code>E</code>s <code>prev_event</code>s.</li>
<li>If <code>E</code> is rejected or a message event, the partial state after <code>E</code> is the
partial state before <code>E</code>.</li>
<li>Otherwise, the partial state after <code>E</code> is the partial state before <code>E</code>, plus
<code>E</code> itself.</li>
</ul>
<p>More concisely, partial state propagates just like full state; the only
difference is that we “seed” it with an incomplete initial state.
Synapse records that we have only calculated partial state for this event with
a row in <code>partial_state_events</code>.</p>
<p>While the room remains partially stated, check 5 on incoming events to that
room becomes:</p>
<blockquote>
<ol start="5">
<li>Passes authorization rules based on <strong>the resolution between the partial
state before <code>E</code> and <code>E</code>s auth events.</strong> If the event fails to pass
authorization rules, it is rejected.</li>
</ol>
</blockquote>
<p>Additionally, check 6 is deleted: no soft-failures are enforced.</p>
<p>While partially joined, the current partial state of the room is defined as the
resolution across the partial states after all forward extremities in the room.</p>
<p><em>Remark.</em> Events with partial state are <em>not</em> considered
<a href="../room-dag-concepts.html#outliers">outliers</a>.</p>
<h3 id="approximation-error"><a class="header" href="#approximation-error">Approximation error</a></h3>
<p>Using partial state means the auth checks can fail in a few different ways<sup class="footnote-reference" id="fr-2-1"><a href="#footnote-2">1</a></sup>.</p>
<ul>
<li>We may erroneously accept an incoming event in check 5 based on partial state
when it would have been rejected based on full state, or vice versa.</li>
<li>This means that an event could erroneously be added to the current partial
state of the room when it would not be present in the full state of the room,
or vice versa.</li>
<li>Additionally, we may have skipped soft-failing an event that would have been
soft-failed based on full state.</li>
</ul>
<p>(Note that the discrepancies described in the last two bullets are user-visible.)</p>
<p>This means that we have to be very careful when we want to lookup pieces of room
state in a partially-joined room. Our approximation of the state may be
incorrect or missing. But we can make some educated guesses. If</p>
<ul>
<li>our partial state is likely to be correct, or</li>
<li>the consequences of our partial state being incorrect are minor,</li>
</ul>
<p>then we proceed as normal, and let the resync process fix up any mistakes (see
below).</p>
<p>When is our partial state likely to be correct?</p>
<ul>
<li>Its more accurate the closer we are to the partial join event. (So we should
ideally complete the resync as soon as possible.)</li>
<li>Non-member events: we will have received them as part of the partial join
response, if they were part of the room state at that point. We may
incorrectly accept or reject updates to that state (at first because we lack
remote membership information; later because of compounding errors), so these
can become incorrect over time.</li>
<li>Local members memberships: we are the only ones who can create join and
knock events for our users. We cant be completely confident in the
correctness of bans, invites and kicks from other homeservers, but the resync
process should correct any mistakes.</li>
<li>Remote members memberships: we did not receive these in the /send_join
response, so we have essentially no idea if these are correct or not.</li>
</ul>
<p>In short, we deem it acceptable to trust the partial state for non-membership
and local membership events. For remote membership events, we wait for the
resync to complete, at which point we have the full state of the room and can
proceed as normal.</p>
<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>
<p>The partial-state approximation is only a temporary affair. In the background,
synapse beings a “resync” process. This is a continuous loop, starting at the
partial join event and proceeding downwards through the event graph. For each
<code>E</code> seen in the room since partial join, Synapse will fetch</p>
<ul>
<li>the event ids in the state of the room before <code>E</code>, via
<a href="https://spec.matrix.org/v1.5/server-server-api/#get_matrixfederationv1state_idsroomid"><code>/state_ids</code></a>;</li>
<li>the event ids in the full auth chain of <code>E</code>, included in the <code>/state_ids</code>
response; and</li>
<li>any events from the previous two bullets that Synapse hasnt persisted, via
<a href="https://spec.matrix.org/v1.5/server-server-api/#get_matrixfederationv1stateroomid">`/state</a>.</li>
</ul>
<p>This means Synapse has (or can compute) the full state before <code>E</code>, which allows
Synapse to properly authorise or reject <code>E</code>. At this point ,the event
is considered to have “full state” rather than “partial state”. We record this
by removing <code>E</code> from the <code>partial_state_events</code> table.</p>
<p>[<strong>TODO:</strong> Does Synapse persist a new state group for the full state
before <code>E</code>, or do we alter the (partial-)state group in-place? Are state groups
ever marked as partially-stated? ]</p>
<p>This scheme means it is possible for us to have accepted and sent an event to
clients, only to reject it during the resync. From a clients perspective, the
effect is similar to a retroactive
state change due to state resolution—i.e. a “state reset”.<sup class="footnote-reference" id="fr-3-1"><a href="#footnote-3">2</a></sup></p>
<p>When all events since the join <code>J</code> have been fully-stated, the room resync
process is complete. We record this by removing the room from
<code>partial_state_rooms</code>.</p>
<h2 id="faster-joins-on-workers"><a class="header" href="#faster-joins-on-workers">Faster joins on workers</a></h2>
<p>For the time being, the resync process happens on the master worker.
A new replication stream <code>un_partial_stated_room</code> is added. Whenever a resync
completes and a partial-state room becomes fully stated, a new message is sent
into that stream containing the room ID.</p>
<h2 id="notes-on-specific-cases"><a class="header" href="#notes-on-specific-cases">Notes on specific cases</a></h2>
<blockquote>
<p><strong>NB.</strong> The notes below are rough. Some of them are hidden under <code>&lt;details&gt;</code>
disclosures because they have yet to be implemented in mainline Synapse.</p>
</blockquote>
<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>
<p>When sending out messages during a partial join, we assume our partial state is
accurate and proceed as normal. For this to have any hope of succeeding at all,
our partial state must contain an entry for each of the (type, state key) pairs
<a href="https://spec.matrix.org/v1.3/rooms/v10/#authorization-rules">specified by the auth rules</a>:</p>
<ul>
<li><code>m.room.create</code></li>
<li><code>m.room.join_rules</code></li>
<li><code>m.room.power_levels</code></li>
<li><code>m.room.third_party_invite</code></li>
<li><code>m.room.member</code></li>
</ul>
<p>The first four of these should be present in the state before <code>J</code> that is given
to us in the partial join response; only membership events are omitted. In order
for us to consider the user joined, we must have their membership event. That
means the only possible omission is the targets membership in an invite, kick
or ban.</p>
<p>The worst possibility is that we locally invite someone who is banned according to
the full state, because we lack their ban in our current partial state. The rest
of the federation—at least, those who are fully joined—should correctly
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
homeservers and resolved by the resync for partially-joined homeservers.</p>
<p>In more generality, there are two problems were worrying about here:</p>
<ul>
<li>We might create an event that is valid under our partial state, only to later
find out that is actually invalid according to the full state.</li>
<li>Or: we might refuse to create an event that is invalid under our partial
state, even though it would be perfectly valid under the full state.</li>
</ul>
<p>However we expect such problems to be unlikely in practise, because</p>
<ul>
<li>We trust that the room has sensible power levels, e.g. that bad actors with
high power levels are demoted before their ban.</li>
<li>We trust that the resident server provides us up-to-date power levels, join
rules, etc.</li>
<li>State changes in rooms are relatively infrequent, and the resync period is
relatively quick.</li>
</ul>
<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>
<p><strong>TODO:</strong> needs prose fleshing out.</p>
<p>Normally: send out in a fed txn to all HSes in the room.
We only know that some HSes were in the room at some point. Wat do.
Send it out to the list of servers from the first join.
<strong>TODO</strong> what do we do here if we have full state?
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…)
But dont want to send out sensitive data in other HSs events in this way.</p>
<p>Suppose we discover after resync that we shouldnt have sent out one our events (not a prev_event) to a target HS. Not much we can do.
What about if we didnt send them an event but shouldntve?
E.g. what if someone joined from a new HS shortly after you did? We wouldnt talk to them.
Could imagine sending out the “Missed” events after the resync but… painful to work out what they should have seen if they joined/left.
Instead, just send them the latest event (if theyre still in the room after resync) and let them backfill.(?)</p>
<ul>
<li>Dont do this currently.</li>
<li>If anyone who has received our messages sends a message to a HS we missed, they can backfill our messages</li>
<li>Gap: rooms which are infrequently used and take a long time to resync.</li>
</ul>
<h3 id="joining-after-a-partial-join"><a class="header" href="#joining-after-a-partial-join">Joining after a partial join</a></h3>
<p><strong>NB.</strong> Not yet implemented.</p>
<details>
<p><strong>TODO:</strong> needs prose fleshing out. Liase with Matthieu. Explain why /send_join
(Rich was surprised we didnt just create it locally. Answer: to try and avoid
a join which then gets rejected after resync.)</p>
<p>We dont know for sure that any join we create would be accepted.
E.g. the joined user might have been banned; the join rules might have changed in a way that we didnt realise… some way in which the partial state was mistaken.
Instead, do another partial make-join/send-join handshake to confirm that the join works.</p>
<ul>
<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>
<li>What if the second send_join response includes a different list of reisdent HSes? Could ignore it.
<ul>
<li>Could even have a special flag that says “just make me a join”, i.e. dont bother giving me state or servers in room. Deffo want the auth chain tho.</li>
</ul>
</li>
<li>SQ: wrt device lists its a lot safer to ignore it!!!!!</li>
<li>What if the state at the second join is inconsistent with what we have? Ignore it?</li>
</ul>
</details>
<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>
<p><strong>NB.</strong> Not yet implemented.</p>
<details>
<p>When youre fully joined to a room, to have <code>U</code> leave a room their homeserver
needs to</p>
<ul>
<li>create a new leave event for <code>U</code> which will be accepted by other homeservers,
and</li>
<li>send that event <code>U</code> out to the homeservers in the federation.</li>
</ul>
<p>When is a leave event accepted? See
<a href="https://spec.matrix.org/v1.5/rooms/v10/#authorization-rules">v10 auth rules</a>:</p>
<blockquote>
<ol start="4">
<li>If type is m.room.member: […]
&gt;
&gt; 5. If membership is leave:
&gt;
&gt; 1. If the sender matches state_key, allow if and only if that users current membership state is invite, join, or knock.
2. […]</li>
</ol>
</blockquote>
<p>I think this means that (well-formed!) self-leaves are governed entirely by
4.5.1. This means that if we correctly calculate state which says that <code>U</code> is
invited, joined or knocked and include it in the leaves auth events, our event
is accepted by checks 4 and 5 on incoming events.</p>
<blockquote>
<ol start="4">
<li>Passes authorization rules based on the events auth events, otherwise
&gt; it is rejected.</li>
<li>Passes authorization rules based on the state before the event, otherwise
&gt; it is rejected.</li>
</ol>
</blockquote>
<p>The only way to fail check 6 is if the receiving servers current state of the
room says that <code>U</code> is banned, has left, or has no membership event. But this is
fine: the receiving server already thinks that <code>U</code> isnt in the room.</p>
<blockquote>
<ol start="6">
<li>Passes authorization rules based on the current state of the room,
&gt; otherwise it is “soft failed”.</li>
</ol>
</blockquote>
<p>For the second point (publishing the leave event), the best thing we can do is
to is publish to all HSes we know to be currently in the room. If they miss that
event, they might send us traffic in the room that we dont care about. This is
a problem with leaving after a “full” join; we dont seek to fix this with
partial joins.</p>
<p>(With that said: theres nothing machine-readable in the /send response. I dont
think we can deduce “destination has left the room” from a failure to /send an
event into that room?)</p>
<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>
<p>We can create leave events and can choose what gets included in our auth events,
so we can be sure that we pass check 4 on incoming events. For check 5, we might
have an incorrect view of the state before an event.
The only way we might erroneously think a leave is valid is if</p>
<ul>
<li>the partial state before the leave has <code>U</code> joined, invited or knocked, but</li>
<li>the full state before the leave has <code>U</code> banned, left or not present,</li>
</ul>
<p>in which case the leave doesnt make anything worse: other HSes already consider
us as not in the room, and will continue to do so after seeing the leave.</p>
<p>The remaining obstacle is then: can we safely broadcast the leave event? We may
miss servers or incorrectly think that a server is in the room. Or the
destination server may be offline and miss the transaction containing our leave
event.This should self-heal when they see an event whose <code>prev_events</code> descends
from our leave.</p>
<p>Another option we considered was to use federation <code>/send_leave</code> to ask a
fully-joined server to send out the event on our behalf. But that introduces
complexity without much benefit. Besides, as Rich put it,</p>
<blockquote>
<p>sending out leaves is pretty best-effort currently</p>
</blockquote>
<p>so this is probably good enough as-is.</p>
<h4 id="cleanup-after-the-last-leave"><a class="header" href="#cleanup-after-the-last-leave">Cleanup after the last leave</a></h4>
<p><strong>TODO</strong>: what cleanup is necessary? Is it all just nice-to-have to save unused
work?</p>
</details>
<hr>
<ol class="footnote-definition">
<li id="footnote-2">
<p>Is this exhaustive? <a href="#fr-2-1"></a></p>
</li>
<li id="footnote-3">
<p>Clients should refresh caches to detect such a change. Rumour has it that
sliding sync will fix this. <a href="#fr-3-1"></a></p>
</li>
</ol>
</main>
<nav class="nav-wrapper" aria-label="Page navigation">
<!-- Mobile navigation buttons -->
<a rel="prev" href="../../tcp_replication.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
<span class=fa-svg><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 320 512"><!--! Font Awesome Free 6.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2022 Fonticons, Inc. --><path d="M41.4 233.4c-12.5 12.5-12.5 32.8 0 45.3l160 160c12.5 12.5 32.8 12.5 45.3 0s12.5-32.8 0-45.3L109.3 256 246.6 118.6c12.5-12.5 12.5-32.8 0-45.3s-32.8-12.5-45.3 0l-160 160z"/></svg></span>
</a>
<a rel="next prefetch" href="../../development/internal_documentation/index.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
<span class=fa-svg><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 320 512"><!--! Font Awesome Free 6.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2022 Fonticons, Inc. --><path d="M278.6 233.4c12.5 12.5 12.5 32.8 0 45.3l-160 160c-12.5 12.5-32.8 12.5-45.3 0s-12.5-32.8 0-45.3L210.7 256 73.4 118.6c-12.5-12.5-12.5-32.8 0-45.3s32.8-12.5 45.3 0l160 160z"/></svg></span>
</a>
<div style="clear: both"></div>
</nav>
</div>
</div>
<nav class="nav-wide-wrapper" aria-label="Page navigation">
<a rel="prev" href="../../tcp_replication.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
<span class=fa-svg><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 320 512"><!--! Font Awesome Free 6.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2022 Fonticons, Inc. --><path d="M41.4 233.4c-12.5 12.5-12.5 32.8 0 45.3l160 160c12.5 12.5 32.8 12.5 45.3 0s12.5-32.8 0-45.3L109.3 256 246.6 118.6c12.5-12.5 12.5-32.8 0-45.3s-32.8-12.5-45.3 0l-160 160z"/></svg></span>
</a>
<a rel="next prefetch" href="../../development/internal_documentation/index.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
<span class=fa-svg><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 320 512"><!--! Font Awesome Free 6.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2022 Fonticons, Inc. --><path d="M278.6 233.4c12.5 12.5 12.5 32.8 0 45.3l-160 160c-12.5 12.5-32.8 12.5-45.3 0s-12.5-32.8 0-45.3L210.7 256 73.4 118.6c-12.5-12.5-12.5-32.8 0-45.3s32.8-12.5 45.3 0l160 160z"/></svg></span>
</a>
</nav>
</div>
<template id=fa-eye><span class=fa-svg><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512"><!--! Font Awesome Free 6.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2022 Fonticons, Inc. --><path d="M288 32c-80.8 0-145.5 36.8-192.6 80.6C48.6 156 17.3 208 2.5 243.7c-3.3 7.9-3.3 16.7 0 24.6C17.3 304 48.6 356 95.4 399.4C142.5 443.2 207.2 480 288 480s145.5-36.8 192.6-80.6c46.8-43.5 78.1-95.4 93-131.1c3.3-7.9 3.3-16.7 0-24.6c-14.9-35.7-46.2-87.7-93-131.1C433.5 68.8 368.8 32 288 32zM432 256c0 79.5-64.5 144-144 144s-144-64.5-144-144s64.5-144 144-144s144 64.5 144 144zM288 192c0 35.3-28.7 64-64 64c-11.5 0-22.3-3-31.6-8.4c-.2 2.8-.4 5.5-.4 8.4c0 53 43 96 96 96s96-43 96-96s-43-96-96-96c-2.8 0-5.6 .1-8.4 .4c5.3 9.3 8.4 20.1 8.4 31.6z"/></svg></span></template>
<template id=fa-eye-slash><span class=fa-svg><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 512"><!--! Font Awesome Free 6.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2022 Fonticons, Inc. --><path d="M38.8 5.1C28.4-3.1 13.3-1.2 5.1 9.2S-1.2 34.7 9.2 42.9l592 464c10.4 8.2 25.5 6.3 33.7-4.1s6.3-25.5-4.1-33.7L525.6 386.7c39.6-40.6 66.4-86.1 79.9-118.4c3.3-7.9 3.3-16.7 0-24.6c-14.9-35.7-46.2-87.7-93-131.1C465.5 68.8 400.8 32 320 32c-68.2 0-125 26.3-169.3 60.8L38.8 5.1zM223.1 149.5C248.6 126.2 282.7 112 320 112c79.5 0 144 64.5 144 144c0 24.9-6.3 48.3-17.4 68.7L408 294.5c5.2-11.8 8-24.8 8-38.5c0-53-43-96-96-96c-2.8 0-5.6 .1-8.4 .4c5.3 9.3 8.4 20.1 8.4 31.6c0 10.2-2.4 19.8-6.6 28.3l-90.3-70.8zm223.1 298L373 389.9c-16.4 6.5-34.3 10.1-53 10.1c-79.5 0-144-64.5-144-144c0-6.9 .5-13.6 1.4-20.2L83.1 161.5C60.3 191.2 44 220.8 34.5 243.7c-3.3 7.9-3.3 16.7 0 24.6c14.9 35.7 46.2 87.7 93 131.1C174.5 443.2 239.2 480 320 480c47.8 0 89.9-12.9 126.2-32.5z"/></svg></span></template>
<template id=fa-copy><span class=fa-svg><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--! Font Awesome Free 6.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2022 Fonticons, Inc. --><path d="M502.6 70.63l-61.25-61.25C435.4 3.371 427.2 0 418.7 0H255.1c-35.35 0-64 28.66-64 64l.0195 256C192 355.4 220.7 384 256 384h192c35.2 0 64-28.8 64-64V93.25C512 84.77 508.6 76.63 502.6 70.63zM464 320c0 8.836-7.164 16-16 16H255.1c-8.838 0-16-7.164-16-16L239.1 64.13c0-8.836 7.164-16 16-16h128L384 96c0 17.67 14.33 32 32 32h47.1V320zM272 448c0 8.836-7.164 16-16 16H63.1c-8.838 0-16-7.164-16-16L47.98 192.1c0-8.836 7.164-16 16-16H160V128H63.99c-35.35 0-64 28.65-64 64l.0098 256C.002 483.3 28.66 512 64 512h192c35.2 0 64-28.8 64-64v-32h-47.1L272 448z"/></svg></span></template>
<template id=fa-play><span class=fa-svg><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 384 512"><!--! Font Awesome Free 6.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2022 Fonticons, Inc. --><path d="M73 39c-14.8-9.1-33.4-9.4-48.5-.9S0 62.6 0 80V432c0 17.4 9.4 33.4 24.5 41.9s33.7 8.1 48.5-.9L361 297c14.3-8.7 23-24.2 23-41s-8.7-32.2-23-41L73 39z"/></svg></span></template>
<template id=fa-clock-rotate-left><span class=fa-svg><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--! Font Awesome Free 6.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2022 Fonticons, Inc. --><path d="M75 75L41 41C25.9 25.9 0 36.6 0 57.9V168c0 13.3 10.7 24 24 24H134.1c21.4 0 32.1-25.9 17-41l-30.8-30.8C155 85.5 203 64 256 64c106 0 192 86 192 192s-86 192-192 192c-40.8 0-78.6-12.7-109.7-34.4c-14.5-10.1-34.4-6.6-44.6 7.9s-6.6 34.4 7.9 44.6C151.2 495 201.7 512 256 512c141.4 0 256-114.6 256-256S397.4 0 256 0C185.3 0 121.3 28.7 75 75zm181 53c-13.3 0-24 10.7-24 24V256c0 6.4 2.5 12.5 7 17l72 72c9.4 9.4 24.6 9.4 33.9 0s9.4-24.6 0-33.9l-65-65V152c0-13.3-10.7-24-24-24z"/></svg></span></template>
<script>
window.playground_copyable = true;
</script>
<script src="../../elasticlunr-ef4e11c1.min.js"></script>
<script src="../../mark-09e88c2c.min.js"></script>
<script src="../../searcher-c2a407aa.js"></script>
<script src="../../clipboard-1626706a.min.js"></script>
<script src="../../highlight-abc7f01d.js"></script>
<script src="../../book-a0b12cfe.js"></script>
<!-- Custom JS scripts -->
<script src="../../docs/website_files/version-picker-a1e40922.js"></script>
<script src="../../docs/website_files/version-d2c738f4.js"></script>
</div>
</body>
</html>