What does HackerNews think of statebox?
Erlang state "monad" with merge/conflict-resolution capabilities. Useful for Riak.
If I'm understanding correctly, it requires the mutations to be deterministic in order for the nodes to converge.
Replicache (replicache.dev - my thing) takes a similar approach except it does not requires the mutations to be deterministic, which is very useful because it enables the mutations to reach out to other services, be authenticated, etc.
Both the idea here and Replicache's approach are closely related to game networking. If you are interested in these ideas, a really excellent set of content is: https://www.gabrielgambetta.com/client-server-game-architect....
If you are interested in how Replicache's sync protocol works and achieves transactional changes with server authority, it is described here: https://doc.replicache.dev/concepts/how-it-works.
Yes, I need convergance, but I also need to ensure application invariants are maintained.
I agree with parent that a CRDT isn't that useful for local-first software unless you can encode arbitrarily complex programmatic logic in the transitions. That way I as a developer have control over how merges happen globally and all invariants. It's too complex to reason about otherwise.
It's not clear to me if this paper gives me what I'm looking for -- I'm looking forward to digging into it.
There are other approaches to CRDTs that I'm more excited about: Statebox (https://github.com/mochi/statebox) from years ago is a CRDT that encodes arbitrary transitions and converges by rewinding and replaying in a consistent order. This is basically the same trick blockchains typically do to merge forks. Radicle (https://radicle.xyz/) is another related approach.
I think things like this are the better path forward for local-first software, personally.
Basically, your storage has container types ("T"). A list, a set, a dictionary, etc. Container types can be split and added together in a distributed fashion ("R" and "D").
The "C" in CRDT stands for "Convergent and Commutative" to imply your distributed operations can obtain the same value when merged.
Quick example: \nIf you have a node with a key pointing to value (set) [a, b, c] and another node with the same key but different value [c, e, f], then when the nodes communicate, they can do a set union for the actual result of [a, b, c, e, f]. Keys can keep a running log of recent operations to clean up the global result too (like: [c, e, (recently deleted f)], so on merge, if the other list has f, it would be deleted instead of re-added).
Before CRDTs were a thing, Bob made state box and it's very easy to understand. Give the README a read to understand more basics: https://github.com/mochi/statebox
One way is to write domain-specific logic that knows how to resolve your values. For example, your models might have some state that only happen-after another state, so conflicts of this nature resolve to the 'later' state.
Another approach is to use data-structures or a library designed for this, like CRDTs. Some resources below:
A comprehensive study of Convergent and Commutative Replicated Data Types http://hal.archives-ouvertes.fr/inria-00555588/
https://github.com/reiddraper/knockbox https://github.com/aphyr/meangirls https://github.com/ericmoritz/crdt https://github.com/mochi/statebox
The riak-java-client[2] provides a simple way to hook conflict resolution into your code (thanks to the ideas and solutions in that talk.) There is also the excellent Statebox from Mochi, which automates conflict resolution.
[1] Riak and Scala at Yammer - http://blog.basho.com/2011/03/28/Riak-and-Scala-at-Yammer/ [2] Riak-java-client - https://github.com/basho/riak-java-client/ [3] Statebox - https://github.com/mochi/statebox