The growing pains of database architecture
figma.com
figma.com
I would think that most documents tend to have a small-ish set of users.
> It’s worth noting that we only use multiplayer for syncing changes to Figma documents. We also sync changes to a lot of other data (comments, users, teams, projects, etc.) but that is stored in Postgres, not our multiplayer system, and is synced with clients using a completely separate system that won’t be discussed in this article. Although these two systems are similar, they have separate implementations because of different tradeoffs around certain properties such as performance, offline availability, and security.
This post[2] goes into more detail on how they spin up backend processes to serve as the source of truth while documents are open (we took heavy inspiration from it when building https://plane.dev, which aims to be an open-source implementation of that architecture.)
[1] https://www.figma.com/blog/how-figmas-multiplayer-technology... [2] https://www.figma.com/blog/rust-in-production-at-figma/
I was surprised to see that they had to cancel those ~10 queries that were in flight in the moment when they needed to switch over the query traffic. When doing this with ProxySQL, there was an option to: pause all connections such that they can’t create new transactions and queries, while not cancelling running txs/queries, and then wait for all ongoing txs/queries to finish, and then do the switch and unpause.
Caching and optimization weren't mentioned at all, but I guess they already maxed out that path
Just because you can wait for them to finish, doesn't mean it's better to when you look at the cutover as a whole.
This kind of thing there's a lot that needs to be kept in mind, I've never done a live database migration before to this scale. I'm a beginner with distributed systems but I find them highly interesting.
I feel that along of the work that was done in this article should have been the responsibility of a Kubernetes operator.
My dream is multimaster databases that mean we don't need to have so much pain in doing database migrations. I wrote an eventually consistent Python socket server that asynchonously streams messages to its replicas. I use a client side timestamp to provide global ordering. In theory if two servers had differing system clocks, they could cause the causality of messages to be the wrong way round in a strict sense. But ALL replicas shall agree on the ordering, server agreement among replicas that are caught up is deterministic. If the servers are not caught up, a server shall serve a stale value or a value that only that replica knows about.
The database essentially uses event sourcing and takes the last write to determine victory.
Here's a diagram of a log where the cluster bounced between values, it's a very big diagram you'll have to zoom in. You can see states that were forks of the true cluster agreed value. https://github.com/samsquire/eventually-consistent-mesh/blob...
I wrote a Jepsen test that because it is not linerarizable, it fails.
My script abandons the C(onsistency) of CAP and maintains (A)vailability and (P)artition tolerance. It assumes the network shall heal and the replication shall resume. In PACELC theory in During normal operation it prefers Availability during Partitions and Else Latency in normal operations.
How do you build a system around a database where values can no longer be valid at any time? If I kick off a process in the database with a value that was only reported by one replica that was partitioned away from the majority of the network, what do I do?
My immediate thoughts are to create dependency relationships to values that are on the agreed path, data that is based on a failed branch is not valid.
This is a difficult problem, and there aren't nice general solutions. If you read the original Amazon Dynamo paper, for example, there's a good discussion of why eventual consistency is OK for their applications (https://www.allthingsdistributed.com/files/amazon-dynamo-sos...), and more in Werner Vogel's CACM article from two years later (https://dl.acm.org/doi/10.1145/1435417.1435432).
There are conceptual frameworks (like CALM https://arxiv.org/pdf/1901.01930.pdf) that provide a way to reason through the behavior of systems under eventual consistency (and provide a path to data structures with behavior that's easier to reason about than last-writer-wins).
> My script abandons the C(onsistency) of CAP and maintains (A)vailability and (P)artition tolerance.
I'd recommend you check out "Rethinking Eventual Consistency" (https://www.microsoft.com/en-us/research/publication/rethink...). PACELC is a better tool than CAP for thinking about this stuff, but Figure 1 in that paper is even better. Specifically, there is no need to abandon either availability or consistency in the majority partition.
The confusion here comes from CAP's unhelpful use of "total availability", which isn't a system property that most datacenter applications care about. Mobile, IoT, and other frequently-disconnected applications do care about it.
> For Postgres-compatible NewSQL, we would’ve had one of the largest single-cluster footprints for cloud-managed distributed Postgres. We didn’t want to bear the burden of being the first customer to hit certain scaling issues
I find their claim a bit hard to believe.
It's smart to be cautious.
CockroachDB you have two options both equally horrible. Pay for a hosted solution or run your own. I would pick hosting my own, but that would be some work.
Your comment is pretty unhelpful either way though.
Atleast that is what I guessed from some brief reading - I may be totally wrong.
For most businesses, one customer is only interested in their own data. So that's where you can scale out - put each customer on their own virtual instance.
If there is a relation, like who is friend with who, and not much else, you can lift out that into it's on service and scale it individually.
This way you get all the benefits of the relational model (you can use foreign keys, transactions consisting of multiple tables, etc.) and the performance benefits of additional machines that are not just read-replicas.
Centralized shared tables can be in a separate database which can also hold the mapping between customer-ids and database instances.
Only drawback is that management is more difficult -- backup, migrations, etc. Specifically, you need to handle the case where some customers have migrated their database and others had not yet.
[1] https://techcrunch.com/2022/06/08/figmas-dylan-field-will-di...
Interesting, in other programming envs. Active Record is considered as "antipattern"
e.g EF Core (C# ORM) doesnt
In this case, we'd start with the test. From Wikipedia, "An anti-pattern in software engineering, project management, and business processes is a common response to a recurring problem that is usually ineffective and risks being highly counterproductive."
Then we look at the top answer on Stack Overflow (2)
=== Exhibit A ===
But ORM can be a pain:
You have to learn it, and ORM libraries are not lightweight tools;
You have to set it up. Same problem.
Performance is OK for usual queries, but a SQL master will always do better with his own SQL for big projects.
It abstracts the DB. While it's OK if you know what's happening behind the scene, it's a trap for new programmers that can write very greedy statements, like a heavy hit in a for loop.
=== Exhibit B === Do not try to write your own ORM, unless you are trying to learn something. This is a gigantic piece of work, and the old ones took a lot of time and work before they became reliable.
So, I have presented some initial evidence that tends to support the idea that an ORM is an anti-pattern. If you disagree, you are welcome to cite additional evidence to the contrary. But keep in mind the test elements.Setting something up is similar. It depends on complexity of toolings.
Performance is also dependant on the quality of the generated native query.
It abstracts the DB, which i consider a good thing in long term, and of course, some ORM gives you enough escape hatches in a good way.
Overall, i love working with a "good" ORM rather than working directly with SQL (for example).
But the real world libraries is not good enough for the ORM ecosystem to have good faith on DX side.
ORM allows you to focus more on modeling domain model instead of db model
ORM speeds up writing boring queries like insert or update
ORM can detect changes and update stuff accordingly
ORM prevents people from SQL Injections
ORM can manage migrations and db schema versioning
ORM allows you to change DBs to some extent easier
ORM still allows you to write raw query when you need it
Try C#s EF Core with LINQ.
ORMs become an antipattern when they try to abstract away the database completely by masking write operations and paging lists implicitly. Developers need to know precisely when these operations happen to manage performance.
JOOQ doesn't do that. Active record does. Hibernate does (but it's configurable)