mirror of
https://github.com/element-hq/synapse.git
synced 2026-10-23 15:13:31 +00:00
deploy: 9d3713d6d5
This commit is contained in:
1 parent
730a04d8ff
commit
47cd8139ed
102 files changed
+548
-105
No files matched your search
+128
-1
@@ -77,7 +77,7 @@
|
||||
|
||||
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
|
||||
<div class="sidebar-scrollbox">
|
||||
<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 expLine truncated
|
||||
<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 expLine truncated
|
||||
</div>
|
||||
<div id="sidebar-resize-handle" class="sidebar-resize-handle"></div>
|
||||
</nav>
|
||||
@@ -17927,6 +17927,133 @@ minimal.</p>
|
||||
<p>Information about how the tcp replication module is structured, including how
|
||||
the classes interact, can be found in
|
||||
<code>synapse/replication/tcp/__init__.py</code></p>
|
||||
<div style="break-before: page; page-break-before: always;"></div><h2 id="streams"><a class="header" href="#streams">Streams</a></h2>
|
||||
<p>Synapse has a concept of "streams", which are roughly described in <a href="https://github.com/matrix-org/synapse/blob/develop/synapse/storage/util/id_generators.py"><code>id_generators.py</code></a>.
|
||||
Generally speaking, streams are a series of notifications that something in Synapse's database has changed that the application might need to respond to.
|
||||
For example:</p>
|
||||
<ul>
|
||||
<li>The events stream reports new events (PDUs) that Synapse creates, or that Synapse accepts from another homeserver.</li>
|
||||
<li>The account data stream reports changes to users' <a href="https://spec.matrix.org/v1.7/client-server-api/#client-config">account data</a>.</li>
|
||||
<li>The to-device stream reports when a device has a new <a href="https://spec.matrix.org/v1.7/client-server-api/#send-to-device-messaging">to-device message</a>.</li>
|
||||
</ul>
|
||||
<p>See <a href="https://github.com/matrix-org/synapse/blob/develop/synapse/replication/tcp/streams/__init__.py"><code>synapse.replication.tcp.streams</code></a> for the full list of streams.</p>
|
||||
<p>It is very helpful to understand the streams mechanism when working on any part of Synapse that needs to respond to changes—especially if those changes are made by different workers.
|
||||
To that end, let's describe streams formally, paraphrasing from the docstring of <a href="https://github.com/matrix-org/synapse/blob/a719b703d9bd0dade2565ddcad0e2f3a7a9d4c37/synapse/storage/util/id_generators.py#L96"><code>AbstractStreamIdGenerator</code></a>.</p>
|
||||
<h3 id="definition"><a class="header" href="#definition">Definition</a></h3>
|
||||
<p>A stream is an append-only log <code>T1, T2, ..., Tn, ...</code> of facts<sup class="footnote-reference"><a href="#1">1</a></sup> which grows over time.
|
||||
Only "writers" can add facts to a stream, and there may be multiple writers.</p>
|
||||
<p>Each fact has an ID, called its "stream ID".
|
||||
Readers should only process facts in ascending stream ID order.</p>
|
||||
<p>Roughly speaking, each stream is backed by a database table.
|
||||
It should have a <code>stream_id</code> (or similar) bigint column holding stream IDs, plus additional columns as necessary to describe the fact.
|
||||
Typically, a fact is expressed with a single row in its backing table.<sup class="footnote-reference"><a href="#2">2</a></sup>
|
||||
Within a stream, no two facts may have the same stream_id.</p>
|
||||
<blockquote>
|
||||
<p><em>Aside</em>. Some additional notes on streams' backing tables.</p>
|
||||
<ol>
|
||||
<li>Rich would like to <a href="https://github.com/matrix-org/synapse/issues/13456">ditch the backing tables</a>.</li>
|
||||
<li>The backing tables may have other uses.
|
||||
> For example, the events table serves backs the events stream, and is read when processing new events.
|
||||
> But old rows are read from the table all the time, whenever Synapse needs to lookup some facts about an event.</li>
|
||||
<li>Rich suspects that sometimes the stream is backed by multiple tables, so the stream proper is the union of those tables.</li>
|
||||
</ol>
|
||||
</blockquote>
|
||||
<p>Stream writers can "reserve" a stream ID, and then later mark it as having being completed.
|
||||
Stream writers need to track the completion of each stream fact.
|
||||
In the happy case, completion means a fact has been written to the stream table.
|
||||
But unhappy cases (e.g. transaction rollback due to an error) also count as completion.
|
||||
Once completed, the rows written with that stream ID are fixed, and no new rows
|
||||
will be inserted with that ID.</p>
|
||||
<h3 id="current-stream-id"><a class="header" href="#current-stream-id">Current stream ID</a></h3>
|
||||
<p>For any given stream reader (including writers themselves), we may define a per-writer current stream ID:</p>
|
||||
<blockquote>
|
||||
<p>The current stream ID <em>for a writer W</em> is the largest stream ID such that
|
||||
all transactions added by W with equal or smaller ID have completed.</p>
|
||||
</blockquote>
|
||||
<p>Similarly, there is a "linear" notion of current stream ID:</p>
|
||||
<blockquote>
|
||||
<p>The "linear" current stream ID is the largest stream ID such that
|
||||
all facts (added by any writer) with equal or smaller ID have completed.</p>
|
||||
</blockquote>
|
||||
<p>Because different stream readers A and B learn about new facts at different times, A and B may disagree about current stream IDs.
|
||||
Put differently: we should think of stream readers as being independent of each other, proceeding through a stream of facts at different rates.</p>
|
||||
<p><strong>NB.</strong> For both senses of "current", that if a writer opens a transaction that never completes, the current stream ID will never advance beyond that writer's last written stream ID.</p>
|
||||
<p>For single-writer streams, the per-writer current ID and the linear current ID are the same.
|
||||
Both senses of current ID are monotonic, but they may "skip" or jump over IDs because facts complete out of order.</p>
|
||||
<p><em>Example</em>.
|
||||
Consider a single-writer stream which is initially at ID 1.</p>
|
||||
<table><thead><tr><th>Action</th><th>Current stream ID</th><th>Notes</th></tr></thead><tbody>
|
||||
<tr><td></td><td>1</td><td></td></tr>
|
||||
<tr><td>Reserve 2</td><td>1</td><td></td></tr>
|
||||
<tr><td>Reserve 3</td><td>1</td><td></td></tr>
|
||||
<tr><td>Complete 3</td><td>1</td><td>current ID unchanged, waiting for 2 to complete</td></tr>
|
||||
<tr><td>Complete 2</td><td>3</td><td>current ID jumps from 1 -> 3</td></tr>
|
||||
<tr><td>Reserve 4</td><td>3</td><td></td></tr>
|
||||
<tr><td>Reserve 5</td><td>3</td><td></td></tr>
|
||||
<tr><td>Reserve 6</td><td>3</td><td></td></tr>
|
||||
<tr><td>Complete 5</td><td>3</td><td></td></tr>
|
||||
<tr><td>Complete 4</td><td>5</td><td>current ID jumps 3->5, even though 6 is pending</td></tr>
|
||||
<tr><td>Complete 6</td><td>6</td><td></td></tr>
|
||||
</tbody></table>
|
||||
<h3 id="multi-writer-streams"><a class="header" href="#multi-writer-streams">Multi-writer streams</a></h3>
|
||||
<p>There are two ways to view a multi-writer stream.</p>
|
||||
<ol>
|
||||
<li>Treat it as a collection of distinct single-writer streams, one
|
||||
for each writer.</li>
|
||||
<li>Treat it as a single stream.</li>
|
||||
</ol>
|
||||
<p>The single stream (option 2) is conceptually simpler, and easier to represent (a single stream id).
|
||||
However, it requires each reader to know about the entire set of writers, to ensures that readers don't erroneously advance their current stream position too early and miss a fact from an unknown writer.
|
||||
In contrast, multiple parallel streams (option 1) are more complex, requiring more state to represent (map from writer to stream id).
|
||||
The payoff for doing so is that readers can "peek" ahead to facts that completed on one writer no matter the state of the others, reducing latency.</p>
|
||||
<p>Note that a multi-writer stream can be viewed in both ways.
|
||||
For example, the events stream is treated as multiple single-writer streams (option 1) by the sync handler, so that events are sent to clients as soon as possible.
|
||||
But the background process that works through events treats them as a single linear stream.</p>
|
||||
<p>Another useful example is the cache invalidation stream.
|
||||
The facts this stream holds are instructions to "you should now invalidate these cache entries".
|
||||
We only ever treat this as a multiple single-writer streams as there is no important ordering between cache invalidations.
|
||||
(Invalidations are self-contained facts; and the invalidations commute/are idempotent).</p>
|
||||
<h3 id="writing-to-streams"><a class="header" href="#writing-to-streams">Writing to streams</a></h3>
|
||||
<p>Writers need to track:</p>
|
||||
<ul>
|
||||
<li>track their current position (i.e. its own per-writer stream ID).</li>
|
||||
<li>their facts currently awaiting completion.</li>
|
||||
</ul>
|
||||
<p>At startup, </p>
|
||||
<ul>
|
||||
<li>the current position of that writer can be found by querying the database (which suggests that facts need to be written to the database atomically, in a transaction); and</li>
|
||||
<li>there are no facts awaiting completion.</li>
|
||||
</ul>
|
||||
<p>To reserve a stream ID, call <a href="https://www.postgresql.org/docs/current/functions-sequence.html"><code>nextval</code></a> on the appropriate postgres sequence.</p>
|
||||
<p>To write a fact to the stream: insert the appropriate rows to the appropriate backing table.</p>
|
||||
<p>To complete a fact, first remove it from your map of facts currently awaiting completion.
|
||||
Then, if no earlier fact is awaiting completion, the writer can advance its current position in that stream.
|
||||
Upon doing so it should emit an <code>RDATA</code> message<sup class="footnote-reference"><a href="#3">3</a></sup>, once for every fact between the old and the new stream ID.</p>
|
||||
<h3 id="subscribing-to-streams"><a class="header" href="#subscribing-to-streams">Subscribing to streams</a></h3>
|
||||
<p>Readers need to track the current position of every writer.</p>
|
||||
<p>At startup, they can find this by contacting each writer with a <code>REPLICATE</code> message,
|
||||
requesting that all writers reply describing their current position in their streams.
|
||||
Writers reply with a <code>POSITION</code> message.</p>
|
||||
<p>To learn about new facts, readers should listen for <code>RDATA</code> messages and process them to respond to the new fact.
|
||||
The <code>RDATA</code> itself is not a self-contained representation of the fact;
|
||||
readers will have to query the stream tables for the full details.
|
||||
Readers must also advance their record of the writer's current position for that stream.</p>
|
||||
<h1 id="summary"><a class="header" href="#summary">Summary</a></h1>
|
||||
<p>In a nutshell: we have an append-only log with a "buffer/scratchpad" at the end where we have to wait for the sequence to be linear and contiguous.</p>
|
||||
<hr />
|
||||
<div class="footnote-definition" id="1"><sup class="footnote-definition-label">1</sup>
|
||||
<p>we use the word <em>fact</em> here for two reasons.
|
||||
Firstly, the word "event" is already heavily overloaded (PDUs, EDUs, account data, ...) and we don't need to make that worse.
|
||||
Secondly, "fact" emphasises that the things we append to a stream cannot change after the fact.</p>
|
||||
</div>
|
||||
<div class="footnote-definition" id="2"><sup class="footnote-definition-label">2</sup>
|
||||
<p>A fact might be expressed with 0 rows, e.g. if we opened a transaction to persist an event, but failed and rolled the transaction back before marking the fact as completed.
|
||||
In principle a fact might be expressed with 2 or more rows; if so, each of those rows should share the fact's stream ID.</p>
|
||||
</div>
|
||||
<div class="footnote-definition" id="3"><sup class="footnote-definition-label">3</sup>
|
||||
<p>This communication used to happen directly with the writers <a href="development/synapse_architecture/../../tcp_replication.html">over TCP</a>;
|
||||
nowadays it's done via Redis's Pubsub.</p>
|
||||
</div>
|
||||
<div style="break-before: page; page-break-before: always;"></div><h1 id="tcp-replication"><a class="header" href="#tcp-replication">TCP Replication</a></h1>
|
||||
<h2 id="motivation-1"><a class="header" href="#motivation-1">Motivation</a></h2>
|
||||
<p>Previously the workers used an HTTP long poll mechanism to get updates
|
||||
|
||||
Reference in new issue
Block a user