Setting Ecto.Schema.Metadata's context in the adapter

Hello,

In ecto 3.0, is it possible for the adapter to set the context (in Ecto.Schema.Metadata of the schema) of the results of a query?

Looking at what the implementation of execute in Ecto.Adapter.Queryable is supposed to return, the adapter seems to only be able to include the field values. I couldn’t find a way to plug any postprocessing logic at the adapter level. Is it not available or did I miss it?

The database I’m trying to interact via a custom ecto adapter returns metadata on a read that must be passed back in the update following it. It’s used for conflict detection/resolution of concurrent updates by the DB. The idea was to store this in meta.context.

Thanks.

Paulo

Can you illustrate your need with an example?

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.

1 Like