james_cowling
If you were to attempt to fetch all items from an empty shopping cart, that query will automatically be invalidated if any item is added to the cart, even though there were no documents returned from the original query. Query intersection always works.
The aggregate example isn't a great one in Convex since it doesn't currently support built-in aggregates - a full-table aggregate involves a table scan and therefore the readset would be the entire index. We may add built-in aggregates that are incrementally computed if there's enough demand.
After 2 years the code becomes Apache 2.0.
Note that if you're running the open source release you're on the hook for managing and scaling your own reliable infrastructure, as described in the open source readme.
I talk more about the philosophy re open sourcing convex in https://softwareengineeringdaily.com/2024/03/20/going-open-s...
Convex is in a very comfortable financial position but it is also open source: https://github.com/get-convex/convex-backend
Internally the very bottom of the stack on the hosted product is actually just a write ahead log on RDS and in the open source product it's just sqlite. We'll likely eventually build our own durable write ahead log, and have plenty of experience doing so, but this hasn't been needed so far.
So far our answer for folks who want alternative storage/query engines is to use our streaming Airbyte source connector or write directly to Pinecone, Snowflake, etc. This should work great for most devs.
There are likely always going to be some developers who want to use a particular third party database in addition to Convex, but we plan to expand built-in support for most workloads over time so that Convex is a truly full-stack backend replacement.
(a bunch of the early MP folks work at Convex now)
Should we trust these numbers though? Of course not, because the secret truth is that adherence to theoretical durability estimates is missing the point. They tell you how likely you are to lose data due to routine disk failure, but routine disk failure is easy to model for and protect against. If you lose data due to routine disk failure you’re probably doing something wrong."
https://medium.com/@jamesacowling/how-many-nines-is-my-stora...
MP was designed for multiple exabytes across many data centers and geographic regions. A different system design (or just using S3) would be more appropriate for use at smaller scales.
As far as I know Magic Pocket has had 100% durability, but that's obviously beside the point.
Convex in particular is designed far more for end-to-end consistency rather than just a database to talk to: https://docs.convex.dev/understanding/convex-vs-firebase
I talk more about how we perform this comparison in this (now rather outdated) talk: https://youtu.be/iizcidmSwJ4?t=1218
Note that there are false-positives here, i.e., it's possible for the inputs to a query function to change without the outputs actually changing, but this is not a significant issue in practice.
The specific concern with regards to multiple round trips is scenarios where the client needs to send multiple serial requests to render a web view, e.g., having to fetch a list of posts first before knowing which post ids to fetch comments for. These request waterfalls can lead to high page load times for many web apps.
Consistency-wise Convex supports full serializability. The main point this article is making with regards to consistency however is providing tools to ensure not only that the database is consistent but that the data rendered to the end user is also a consistent view. I talk a little more about that here: https://youtu.be/B9aeddqwVas?t=446
Btw we are huge fans of Firebase and think it did a ton to advance the industry forward. Thanks for your work on it!
Another key distinction is that "query" here doesn't refer to a database read, it could be a complex function containing multiple reads, relatively-arbitrary compute, etc.
This is with regards to long-term productivity however. Generally it shows up in building things that work right the first time around, being able to refactor confidently, etc. Personally I still find myself faster in Go or Python if I have to throw a short-term project together very quickly but other members of the team still profess to being more efficient with Rust for these types of projects too.
On/off-boarding of funds to local currencies is always a challenge in the crypto space but the app supports deposits from a debit card at least. Looking forward to exchange with fiat currencies getting more streamlined over time.
> Would you be able to give more details as to how you use disks as raw block devices, bypassing the filesystem? For example, the kind of abstraction layers you use, their interfaces and methods, disk layout of extents, metadata layout, consistency issues, fsync issues, and the various performance advantages from bypassing the filesystem?
We can definitely go into details on the raw block device. Will probably leave that to @jamwt to write a blog post on since there's quite a lot of content there. Most of the motivation for doing so was actually so we could exploit the write-zone layout of SMR disk drives, although we get some other minor benefits like a bit more "formatted" disk space and no filesystem bugs.
> I was also wondering, I know you have two functional zones, but do you also keep an isolated timelapsed append-only backup of all data, in case of ultimate software error (something bad gets past the verifiers, or a delayed bug kicks in)?
Quick summary is that we write typically only data into at least two zones (regions), obviously replicated within these zones, and then we keep "trash" around in case there's a software bug that causes deletion. We also have a delete grace period where we keep data within the storage system for longer in case an application issued an erroneous delete.
The Dropbox filesystem layer actually checks that a file was added to MP before committing it to the metadata layer of the filesystem, so there's an extra check there that the write didn't just disappear.
We also log metadata about each put and every internal data transformation to a separate system so we can retrace our steps if there was ever an issue. This logging system is actually running on HDFS, so MP isn't the only storage system running at Dropbox, but it's by far the biggest one.
We do a number of things here, like taking down a sufficient number of storage nodes in a single region to make blocks appear "missing" in the region and force an automatic failover to the other region (this is transparent to users, apart from slightly more latency), or more direct/risky checks in our Staging cluster (we don't ever mess with data in our main production cluster).
In reality a large system like this regularly encounters timeouts or failures of sub-components which are masked by our multi-region redundancy but show up as spikes in the verifiers. These remind us that everything is working, in between more explicit DRT (Disaster Recovery Training) tests.
We've very strong believers that an effective replication strategy is just table stakes and that from there the real risks to durability are the "black swan" events that are much harder to model.
I gave at talk at Data@Scale recently where the main premise is about "Durability Theater" and how to combat it in a production storage system. In case you're interested: https://code.facebook.com/posts/253562281667886/data-scale-j...
We haven't shared drive reliability stats before but I don't think we have any objection to doing so. Don't expect these in the next month or two but we'll likely get to this in the not-too-distant future.
A lot of other factors go into an end-to-end durability numbers, including the possibility of software bugs, correlated failure, etc. Magic Pocket has an external durability claim of 12 9s which gives us a lot of headroom between the external figure and the internal model.
Fortunately both 11 or 12 nines is close enough to infinite as far as clients are concerned.
It's always a bit of a tradeoff. A 512MB bucket size would double the size of the Replication Table and we want make sure we keep the entire working set in the buffer cache. 1GB also has the advantage of filling up slower, which gives us more time to close full buckets and create new ones as needed.
That said, 1GB is a bit of an arbitrary number which just happens to work pretty well. 512MB might work just fine. The exact number isn't as significant since typically we lose a whole disk at a time and thus needs to recover a certain number of TB regardless of the bucket size.
I'll give a slightly lazy answer here however and point you to a talk I gave about building durable systems, which covers a lot of this material: https://www.oreilly.com/events/velocity/devops-web-performan...
We just benefit from our very-large scale and close alignment between hardware and software to tailor our infrastructure to our workloads.