We hit a major milestone today on Dendrite, our next-generation golang homeserver: Dendrite received its first messages!!
Before you get too excited, please understand that Dendrite is still a pre-alpha work in progress – whilst we successfully created some rooms on an instance and sent a bunch of messages into them via the Client-Server API, most other functionality (e.g. receiving messages via /sync, logging in, registering, federation etc) is yet to exist. It cannot yet be used as a homeserver. However, this is still a huge step in the right direction, as it demonstrates the core DAG functionality of Matrix is intact, and the beginnings of a usable Client-Server API are hooked up.
The architecture of Dendrite is genuinely interesting – please check out the wiring diagram if you haven’t already. The idea is that the server is broken down into a series of components which process streams of data stored in Kafka-style append-only logs. Each component scales horizontally (you can have as many as required to handle load), which is an enormous win over Synapse’s monolithic design. Each component is also decoupled from each other by the logs, letting them run on entirely different machines as required. Please note that whilst the initial implementation is using Kafka for convenience, the actual append-only log mechanism is abstracted away – in future we expect to see configurations of Dendrite which operate entirely from within a single go executable (using go channels as the log mechanism), as well as alternatives to Kafka for distributed solutions.
The components which have taken form so far are the central roomserver service, which is responsible (as the name suggests) for maintaining the state and integrity of one or more rooms – authorizing events into the room DAG; storing them in postgres, tracking the auth chain of events where needed; etc. Much of the core matrix DAG logic of the roomserver is provided by gomatrixserverlib. The roomserver receives events sent by users via the ‘client room send’ component (and ‘federation backfill’ component, when that exists). The ‘client room send’ component (and in future also ‘client sync’) is provided by the clientapi service – which is what as of today is successfully creating rooms and events and relaying them to the roomserver!
The actual events we’ve been testing with are the history of the Matrix Core room: around 10k events. Right now the roomserver (and the postgres DB that backs it) are the main bottleneck in the pipeline rather than clientapi, so it’s been interesting to see how rapidly the roomserver can consume its log of events. As of today’s benchmark, on a generic dev workstation and an entirely unoptimised roomserver (i.e. no caching whatsoever) running on a single core, we’re seeing it ingest the room history at over 350 events per second. The vast majority of this work is going into encoding/decoding JSON or waiting for postgres: with a simple event cache to avoid repeatedly hitting the DB for every auth and state event, we expect this to increase significantly. And then as we increase the number of cores, kafka partitions and roomserver instances it should scale fairly arbitrarily(!)
For context, the main synapse process for Matrix.org currently maxes out persisting events at around 15 and 20 per second (although it is also spending a bunch of time relaying events to the various worker processes, and other miscellanies). As such, an initial benchmark for Dendrite of 350 msgs/s really does look incredibly promising.
You may be wondering where this leaves Synapse? Well, a major driver for implementing Dendrite has been to support the growth of the main matrix.org server, which currently persists around 10 events/s (whilst emitting around 1500 events/s). We have exhausted most of the low-hanging fruit for optimising Synapse, and have got to point where the architectural fixes required are of similar shape and size to the work going into Dendrite. So, whilst Synapse is going to be around for a while yet, we’re putting the majority of our long-term plans into Dendrite, with a distinct degree of urgency as we race against the ever-increasing traffic levels on the Matrix.org server!
Finally, you may be wondering what happened to Dendron, our original experiment in Golang servers. Well: Dendron was an attempt at a strangler pattern rewrite of Synapse, acting as an shim in front of Synapse which could gradually swap out endpoints with their golang implementations. In practice, the operational complexity it introduced, as well as the amount of room for improvement (at the time) we had in Synapse, and the relatively tight coupling to Synapse’s existing architecture, storage & schema, meant that it was far from a clear win – and effectively served as an excuse to learn Go. As such, we’ve finally formally killed it off as of last week – Matrix.org is now running behind a normal haproxy, and Dendron is dead. Meanwhile, Dendrite (aka Dendron done Right ;) is very much alive, progressing fast, free from the shackles of Synapse.
We’ll try to keep the blog updated with progress on Dendrite as it continues to grow!
Bridges come in many flavours, and we need consistent terminology within the Matrix community to ensure everyone (users, developers, core team) is on the same page. This post is primarily intended for bridge developers to refer to when building bridges.
The most recent version of this document is here (source) but we’re also posting it as a blog post for visibility.
Types of rooms
Bridges can register themselves as controlling chunks of room aliases namespace, letting Matrix users join remote rooms transparently if they /join #freenode_#wherever:matrix.org or similar. The resulting Matrix room is typically automatically bridged to the single target remote room. Access control for Matrix users is typically managed by the remote network’s side of the room. This is called a portal room, and is useful for jumping into remote rooms without any configuration needed whatsoever – using Matrix as a ‘bouncer’ for the remote network.
Alternatively, an existing Matrix room can be can plumbed into one or more specific remote rooms by configuring a bridge (which can be run by anyone). For instance, #matrix:matrix.org is plumbed into #matrix on Freenode, matrixdotorg/#matrix on Slack, etc. Access control for Matrix users is necessarily managed by the Matrix side of the room. This is useful for using Matrix to link together different communities.
Migrating rooms between a portal & plumbed room is currently a bit of a mess, as there’s not yet a way for users to remove portal rooms once they’re created, so you can end up with a mix of portal & plumbed users bridged into a room, which looks weird from both the Matrix and non-Matrix viewpoints. https://github.com/matrix-org/matrix-appservice-irc/issues/387 tracks this.
Types of bridges (simplest first):
The simplest way to exchange messages with a remote network is to have the bridge log into the network using one or more predefined users called bridge bots – typically called MatrixBridge or MatrixBridge etc. These relay traffic on behalf of the users on the other side, but it’s a terrible experience as all the metadata about the messages and senders is lost. This is how the telematrix matrix<->telegram bridge currently works.
Bot-API (aka Virtual user) based bridges
Some remote systems support the idea of injecting messages from ‘fake’ or ‘virtual’ users, which can be used to represent the Matrix-side users as unique entities in the remote network. For instance, Slack’s inbound webhooks lets remote bots be created on demand, letting Matrix users be shown cosmetically correctly in the timeline as virtual users. However, the resulting virtual users aren’t real users on the remote system, so don’t have presence/profile and can’t be tab-completed or direct-messaged etc. They also have no way to receive typing notifs or other richer info which may not be available via bot APIs. This is how the current matrix-appservice-slack bridge works.
Simple puppeted bridge
This is a richer form of bridging, where the bridge logs into the remote service as if it were a real 3rd party client for that service. As a result, the Matrix user has to already have a valid account on the remote system. In exchange, the Matrix user ‘puppets’ their remote user, such that other users on the remote system aren’t even aware they are speaking to a user via Matrix. The full semantics of the remote system are available to the bridge to expose into Matrix. However, the bridge has to handle the authentication process to log the user into the remote bridge.
This is essentially how the current matrix-appservice-irc bridge works (if you configure it to log into the remote IRC network as your ‘real’ IRC nickname). matrix-appservice-gitter is being extended to support both puppeted and bridgebot-based operation. It’s how the experimental matrix-appservice-tg bridge works.
Going forwards we’re aiming for all bridges to be at least simple puppeted, if not double-puppeted.
A simple ‘puppeted bridge’ allows the Matrix user to control their account on their remote network. However, ideally this puppeting should work in both directions, so if the user logs into (say) their native telegram client and starts conversations, sends messages etc, these should be reflected back into Matrix as if the user had done them there. This requires the bridge to be able to puppet the Matrix side of the bridge on behalf of the user.
This is the holy-grail of bridging; matrix-puppet-bridge is a community project that tries to facilitate development of double puppeted bridges, having done so for several networks. The main obstacle is working out an elegant way of having the bridge auth with Matrix as the matrix user (which requires some kind of scoped access_token delegation).
Some remote protocols (IRC, XMPP, SIP, SMTP, NNTP, GnuSocial etc) support federation – either open or closed. The most elegant way of bridging to these protocols would be to have the bridge participate in the federation as a server, directly bridging the entire namespace into Matrix.
We’re not aware of anyone who’s done this yet.
Finally: the types of bridging described above assume that you are synchronising the conversation history of the remote system into Matrix, so it may be decentralised and exposed to multiple users within the wider Matrix network.
This can cause problems where the remote system may have arbitrarily complicated permissions (ACLs) controlling access to the history, which will then need to be correctly synchronised with Matrix’s ACL model, without introducing security issues such as races. We already see some problems with this on the IRC bridge, where history visibility for +i and +k channels have to be carefully synchronised with the Matrix rooms.
You can also hit problems with other network-specific features not yet having equivalent representation in the Matrix protocol (e.g. ephemeral messages, or op-only messages – although arguably that’s a type of ACL).
One solution could be to support an entirely different architecture of bridging, where the Matrix client-server API is mapped directly to the remote service, meaning that ACL decisions are delegated to the remote service, and conversations are not exposed into the wider Matrix. This is effectively using the bridge purely as a 3rd party client for the network (similar to Bitlbee). The bridge is only available to a single user, and conversations cannot be shared with other Matrix users as they aren’t actually Matrix rooms. (Another solution could be to use Active Policy Servers at last as a way of centralising and delegating ACLs for a room)
This is essentially an entirely different product to the rest of Matrix, and whilst it could be a solution for some particularly painful ACL problems, we’re focusing on non-sidecar bridges for now.
We’re happy to announce the release of Synapse 0.19.0 (same as 0.19.0-rc4) today, just in time for anyone discovering Matrix for the first time at FOSDEM 2017! In fact, here’s Erik doing the release right now (with moral support from Luke):
This is a pretty big release, with a bunch of new features and lots and lots of debugging and optimisation work following on some of the dramas that we had with 0.18 over the Christmas break. The biggest things are:
- IPv6 Support (unless you have an IPv6 only resolver), thanks to contributions from Glyph from Twisted and Kyrias!
- A new API for tracking the E2E devices present in a room (required for fixing many of the remaining E2E bugs…)
- Rewrite the ‘state resolution’ algorithm to be orders of magnitude more performant
- Lots of tuning to the caching logic.
If you’re already running a server, please upgrade! And if you’re not, go grab yourself a brand new Synapse from Github. Debian packages will follow shortly (as soon as Erik can figure out the necessary backporting required for Twisted 16.6.0)
And here’s the full changelog…
No changes since RC 4.
Changes in synapse v0.19.0-rc4 (2017-02-02)
- Bump cache sizes for common membership queries (PR #1879)
Changes in synapse v0.19.0-rc3 (2017-02-02)
- Fix email push in pusher worker (PR #1875)
- Make presence.get_new_events a bit faster (PR #1876)
- Make /keys/changes a bit more performant (PR #1877)
Changes in synapse v0.19.0-rc2 (2017-02-02)
- Include newly joined users in /keys/changes API (PR #1872)
Changes in synapse v0.19.0-rc1 (2017-02-02)
- Add support for specifying multiple bind addresses (PR #1709, #1712, #1795, #1835). Thanks to @kyrias!
- Add /account/3pid/delete endpoint (PR #1714)
- Add config option to configure the Riot URL used in notification emails (PR #1811). Thanks to @aperezdc!
- Add username and password config options for turn server (PR #1832). Thanks to @xsteadfastx!
- Implement device lists updates over federation (PR #1857, #1861, #1864)
- Implement /keys/changes (PR #1869, #1872)
- Improve IPv6 support (PR #1696). Thanks to @kyrias and @glyph!
- Log which files we saved attachments to in the media_repository (PR #1791)
- Linearize updates to membership via PUT /state/ to better handle multiple joins (PR #1787)
- Limit number of entries to prefill from cache on startup (PR #1792)
- Remove full_twisted_stacktraces option (PR #1802)
- Measure size of some caches by sum of the size of cached values (PR #1815)
- Measure metrics of string_cache (PR #1821)
- Reduce logging verbosity (PR #1822, #1823, #1824)
- Don’t clobber a displayname or avatar_url if provided by an m.room.member event (PR #1852)
- Better handle 401/404 response for federation /send/ (PR #1866, #1871)
- Fix ability to change password to a non-ascii one (PR #1711)
- Fix push getting stuck due to looking at the wrong view of state (PR #1820)
- Fix email address comparison to be case insensitive (PR #1827)
- Fix occasional inconsistencies of room membership (PR #1836, #1840)
- Don’t block messages sending on bumping presence (PR #1789)
- Change device_inbox stream index to include user (PR #1793)
- Optimise state resolution (PR #1818)
- Use DB cache of joined users for presence (PR #1862)
- Add an index to make membership queries faster (PR #1867)
As many will have noticed there was a major outage on the Matrix homeserver for matrix.org last night (UK-time). This impacted anyone with an account on the matrix.org server, as well as anyone using matrix.org-hosted bots & bridges. As Matrix rooms are shared over all participants, rooms with participants on other servers were unaffected (for users on those servers). Here’s a quick explanation of what went wrong (times are UTC):
- 2017-01-24 16:00 – We notice that we’re badly running out of diskspace on the matrix.org backup postgres replica. (Turns out the backup box, whilst identical hardware to the master, had been built out as RAID-10 rather than RAID-5 and so has less disk space).
- 2017-01-24 17:00 – We decide to drop a large DB index:
event_push_actions(room_id, event_id, user_id, profile_tag), which was taking up a disproportionate amount of disk space, on the basis that it didn’t appear to be being used according to the postgres stats. All seems good.
- 2017-01-24 ~23:00 – The core matrix.org team go to bed.
- 2017-01-24 23:33 – Someone redacts an event in a very active room (probably #matrix:matrix.org) which necessitates redacting the associated push notification from the event_push_actions table. This takes out a lock within persist_event, which is then blocked on deleting the push notification. It turns out that this deletion requires the missing DB constraint, causing the query to run for hours whilst holding the transaction lock. The symptoms are that anything reading events from the DB was blocked on the transaction, causing messages not to be relayed to other clients or servers despite appearing to send correctly. Meanwhile, the fact that events are being received by the server fine (including over federation) makes the monitoring graphs look largely healthy.
- 2017-01-24 23:35 – End-to-end monitoring detects problems, and sends alerts into pagerduty and various Matrix rooms. Unfortunately we’d failed to upgrade the pageduty trial into a paid account a few months ago, however, so the alerts are lost.
- 2017-01-25 08:00 – Matrix team starts to wake up and spot problems, but confusion over the right escalation process (especially with Matthew on holiday) means folks assume that other members of the team must already be investigating.
- 2017-01-25 09:00 – Server gets restarted, service starts to resume, although box suffers from load problems as traffic tries to catch up.
- 2017-01-25 09:45 – Normal service on the homeserver itself is largely resumed (other than bridges; see below)
- 2017-01-25 10:41 – Root cause established and the redaction path is patched on matrix.org to stop a recurrence.
- 2017-01-25 11:15 – Bridges are seen to be lagging and taking much longer to recover than expected. Decision made to let them continue to catch up normally rather than risk further disruption (e.g. IRC join/part spam) by restarting them.
- 2017-01-25 13:00 – All hosted bridges returned to normal.
Obviously this is rather embarrassing, and a huge pain for everyone using the matrix.org homeserver – many apologies indeed for the outage. On the plus side, all the other Matrix homeservers out there carried on without noticing any problems (which actually complicated spotting that things had broken, given many of the core team primarily use their personal homeservers).
In some ways the root cause here is that the core team has been focusing all its energy recently on improving the overall Matrix codebase rather than operational issues on matrix.org itself, and as a result our ops practices have fallen behind (especially as the health of the Matrix ecosystem as a whole is arguably more important than the health of a single homeserver deployment). However, we clearly need to improve things here given the number of people (>750K at the last count) dependent on the Matrix.org homeserver and its bridges & bots.
Lessons learnt on our side are:
- Make sure that even though we had monitoring graphs & thresholds set up on all the right things… monitoring alerts actually have to be routed somewhere useful – i.e. phone calls to the team’s phones. Pagerduty is now set up and running properly to this end.
- Make sure that people know to wake up the right people anyway if the monitoring alerting system fails.
- To be even more paranoid about hotfixes to production at 5pm, especially if they can wait ’til the next day (as this one could have).
- To investigate ways to rapidly recover bridges without causing unnecessary disruption.
Apologies again to everyone who was bitten by this – we’re doing everything we can to ensure it doesn’t happen again.
Matthew & the team.
TL;DR: Please upgrade to Synapse 0.18.6, especially if you are on 0.18.5 which is a bad release.
TL;DR: Please upgrade to Synapse 0.18.7 – especially if you are on 0.18.5 or 0.18.6 which both have serious federation bugs.
Synapse 0.18.5 contained a really nasty regression in the federation code which causes servers to echo transactions that they receive back out to the other servers participating in a room. This has effectively resulted in a gradual amplification of federation traffic as more people have installed 0.18.5, causing every transaction to be received N times over where N is the number of servers in the room.
We’ll do a full write-up once we’re happy we’ve tracked down all the root problems here, but the short story is that this hit critical mass around Dec 26, where typical Synapses started to fail to keep up with the traffic – especially when requests hit some of the more inefficient or buggy codepaths in Synapse. As servers started to overload with inbound connections, this in turn started to slow down and consume resources on the connecting servers – especially due to an architectural mistake in Synapse which blocks inbound connections until the request has been fully processed (which could require the receiving server in turn to make outbound connections), rather than releasing the inbound connection asap. This hit the point that servers were running out of file descriptors due to all the outbound and inbound connections, at which point they started to entirely tarpit inbound connections, resulting in a slow feedback loop making the whole situation even worse.
We’ve spent the last two weeks hunting all the individual inefficient requests which were mysteriously starting to cause more problems than they ever had before; then trying to understand the feedback misbehaviour; before finally discovering the regression in 0.18.5 as the plausible root cause of the problem. Troubleshooting has been complicated by most of the team having unplugged for the holidays, and because this is the first (and hopefully last!) failure mode to be distributed across the whole network, making debugging something of a nightmare – especially when the overloading was triggering a plethora of different exotic failure modes. Huge thanks to everyone who has shared their server logs with the team to help debug this.
Some of these failure modes are still happening (and we’re working on fixing them), but we believe that if everyone upgrades away from the bad 0.18.5 release most of the symptoms will go away, or at least go back to being as bad as they were before. Meanwhile, if you find your server suddenly grinding to a halt after upgrading to
0.18.6 0.18.7 please come tell us in #matrix-dev:matrix.org.
We’re enormously sorry if you’ve been bitten by the federation instability this has caused – and many many thanks for your patience whilst we’ve hunted it down. On the plus side, it’s given us a lot of *very* useful insight into how to implement federation in future homeservers to not suffer from any of these failure modes. It’s also revealed the root cause of why Synapse’s RAM usage is quite so bad – it turns out that it actually idles at around 200MB with default caching, but there’s a particular codepath which causes it to spike temporarily by 1GB or so – and that RAM is then not released back to the OS. We’re working on a fix for this too, but it’ll come after 0.18.7.
Unfortunately the original release of 0.18.6 still exhibits the root bug, but 0.18.7 (originally released as 0.18.7-rc2) should finally fix this. Sorry for all the upgrades :(
So please upgrade as soon as possible to 0.18.7. Debian packages are available as normal.
Changes in synapse v0.18.7 (2017-01-09)
- No changes from v0.18.7-rc2
Changes in synapse v0.18.7-rc2 (2017-01-07)
- Fix error in rc1’s discarding invalid inbound traffic logic that was incorrectly discarding missing events
Changes in synapse v0.18.7-rc1 (2017-01-06)
- Fix error in #PR 1764 to actually fix the nightmare #1753 bug.
- Improve deadlock logging further
- Discard inbound federation traffic from invalid domains, to immunise against #1753
Changes in synapse v0.18.6 (2017-01-06)
- Fix bug when checking if a guest user is allowed to join a room – thanks to Patrik Oldsberg (PR #1772)
Changes in synapse v0.18.6-rc3 (2017-01-05)
- Fix bug where we failed to send ban events to the banned server (PR #1758)
- Fix bug where we sent event that didn’t originate on this server to other servers (PR #1764)
- Fix bug where processing an event from a remote server took a long time because we were making long HTTP requests (PR #1765, PR #1744)
- Improve logging for debugging deadlocks (PR #1766, PR #1767)
Changes in synapse v0.18.6-rc2 (2016-12-30)
- Fix memory leak in twisted by initialising logging correctly (PR #1731)
- Fix bug where fetching missing events took an unacceptable amount of time in large rooms (PR #1734)
Changes in synapse v0.18.6-rc1 (2016-12-29)
- Make sure that outbound connections are closed (PR #1725)
Also, we’ve just released a major update to the IRC bridge codebase after trialling it on the matrix.org-hosted bridges for the last few days.
The big news is:
- The bridge uses Synapse 0.18.5’s new APIs for managing the public room list (improving performance a bunch)
- Much faster startup using the new /joined_rooms and /joined_members APIs in Synapse 0.18.5
- The bridge will now remember your NickServ password (encrypted at rest) if you want it to via the
- You can now set arbitrary user modes for IRC clients on connection (to mitigate PM spam etc)
- After a split, the bridge will drop Matrix->IRC messages older than N seconds, rather than try to catch the IRC room up on everything they missed on Matrix :S
- Operational metrics are now implemented using Prometheus rather than statsd
!quit command to nuke your user from the remote IRC network
- Membership list syncing for IRC->Matrix is enormously improved, and enabled for all matrix.org-hosted bridges apart from Freenode. <b>At last, membership lists should be in sync betwen IRC and Matrix; please let us know if they’re not</b>.
- Better error logging
For full details, please see the changelog.
With things like NickServ-pass storing, !quit support and full bi-directional membership list syncing, it’s never been a better time to run your own IRC bridge. Please install or upgrade today from https://github.com/matrix-org/matrix-appservice-irc!