Sure.
This is what a get + update looks like when interacting at the Riak DB API level (using CRDT maps for conflict resolution):
iex(1)> {:ok, obj} = Riax.get(pid, "app", "users", "user_1") # Fetch from the KV store
{:ok,
%Riax.Object{
context: "g2wAAAABaAJtAAAADCMJ/vlMu3fTAABOIWEBag==",
value: %{{"name", :register} => "Old Name"},
updates: %{},
removes: MapSet.new([])
}}
iex(2)> obj = obj |> Riax.Object.update({"name", :register}, "New Name")
%Riax.Object{
context: "g2wAAAABaAJtAAAADCMJ/vlMu3fTAABOIWEBag==",
value: %{{"name", :register} => "Old Name"},
updates: %{{"name", :register} => "New Name"},
removes: MapSet.new([])
}
iex(3)> Riax.update(pid, "app", "users", "user_1", obj) # Update the KV store
:ok
Since Ecto.Changeset is such a nice abstraction, I’m trying to get a minimal repo to allow:
iex(1)> user = App.Repo.get(App.User, "user_1")
%AppUser{
__meta__: #Ecto.Schema.Metadata<:loaded, "users", "g2wAAAABaAJtAAAADCMJ/vlMu3fTAABOIWEBag==">,
id: "user_1",
name: "Old Name"
}
iex(2)> changeset = Ecto.Changeset.change(user, name: "New Name")
#Ecto.Changeset<
action: nil,
changes: %{name: "New Name"},
errors: [],
data: #App.User<>,
valid?: true
>
iex(3)> App.Repo.update(changeset)
{:ok,
%App.User{
__meta__: #Ecto.Schema.Metadata<:loaded, "users", "g2wAAAABaAJtAAAADCMJ/vlMu3fTAABOIWEBag==">,
id: "user_1",
name: "New Name"
}}
The CRDT vclock “g2wAAAABaAJtAAAADCMJ/vlMu3fTAABOIWEBag==” needs to be carried from the get to the update so that in case of concurrent updates the DB can merge correctly using the CRDT logic.
Storing this in the “context” of the schema metadata seemed appropriate, but in Ecto 3 I couldn’t find a way to set it inside the adapter implementation module.