Lab Notes #023 Cambrian Migrations
A major implication not often included in discussions of local-first software is how to deal with migrations. The degree of the problem depends largely on how you fence the boundaries of your system. If your local-first system allows any number of replicas to come in and out of the network and sync, that's the equivalent of allowing a replica to be offline for an indefinite amount of time. This is the most flexible case, but also the most complex one.
Many systems mitigate the issue by constraining the system in one way or another. One way is to require all replicas to be at the same schema before they are allowed to sync. Another way is to limit how long a replica can remain offline and still sync when it comes back online.
I wanted to explore the implications of the most flexible case, so I'd been using Cambria as a solution for managing migrations in a local-first app. The experience is not as polished as I was expecting, due to the polish of the blog post, but the repo did warn it wasn't production-ready.
As a brief explainer, Cambria models migrations as a graph of lenses that transforms data. The nodes of the graph are schema validations of data, and the edges are the lens transformations. We declare a sequence of transformations as a lens in YAML, and the rest get generated. So when two replicas with different schema versions need to trade data, they can use this graph of transformations loaded in memory to translate the data from the incoming version to the version they understand.
I thought this model was quite interesting and wanted to see what the experience was on the ground. The following are the roadblocks I ran into since Cambria isn't production-ready software.
- The documentation is sparse, and I mostly had to reference the code to see what the ops and their fields were.
- I couldn't figure out what the flags for the CLI do.
- I also couldn't find yarn commands as stated in the blog post to do migrations.
- To run it on the browser, I had to write code to load lenses as files and insert it into the lens graph. I was hoping to have partial loads, but it assumes loading from 'mu' (root) onwards. I didn't look too much into bypassing it.
- I wasn't clear on how to generate the JSON schemas until I looked at the code and realized each node in the lens graph was a schema.
- To validate data, I had to run the schema through a validator like Ajv. Ajv's popular, but the compile time for a schema is about 100 ms. Loading these right now contributes to startup times.
- I like the idea of a lens graph that you can start from anywhere and get to anywhere else. But you can do this migration on read, on write, on migration, as a design choice, but that also means there's a tax on every place you do this migration. I wasn't sure how big this tax was.
- In practice, because I'm the only one issuing new migrations, the graph of migrations turns out to be linear. In this use case, I didn't need the entire graph traversal mechanism.
- I also wanted to be able to express validations like "format" for date-times, and dynamic defaults like "$currentDateTime", and there was no way to do that.
That said, the idea of a shared graph of migrations is compelling if you've ever seen talks on how to manage schema migrations in event-sourced systems [^1], or read this book on versioning in event-sourced systems. I think it's worth considering if the practical computational tax could be mitigated.
But the graph nature of the migration points to a future where the data schema isn't owned by a single entity, but is agreed upon by the marketplace of which parties want to talk to each other. Instead of federating apps, it's the data that's federated and open for local applications to read. That's certainly a vague future in focus for the non-monetary, decentralized builders in the cryptocurrency space. I've seen hints of it here and there.
I think we'll see more of that idea spread when it's apparent to builders of greenfield projects that they can leverage storage infrastructure as a public good and it's more cost-efficient than renting servers on AWS.
[^1] Lots of people burned by event-sourcing. It's valuable, but you need to go into it with clear eyes.
- Don't Let the Internet Dupe You, Event Sourcing is Hard
- Things I wish I knew before I started with event-sourcing
- A Whole System Based on Event Sourcing is an Anti-Pattern
- What they don't tell you about event-sourcing
- Mistakes we made adopting event sourcing