superfly / fly_postgres_elixir

Library for working with local read-replica postgres databases and performing writes through RPC calls to other nodes in the primary Fly.io region.
https://hex.pm/packages/fly_postgres
Apache License 2.0
104 stars 10 forks source link

Feature request: Repo.transaction_on_primary() #35

Open Qqwy opened 1 year ago

Qqwy commented 1 year ago

Currently, it is recommended that you change your 'non-distributed' code:

def update(some, params) do
  Myapp.Repo.transaction(fn ->
    # ... complicated work involving DB manipulation here.
  end)
end

into

def update(some, params) do
  Fly.Postgres.rpc_and_wait(__MODULE__, :do_update, [some, params])
end

def do_update(some, params) do
  Myapp.Repo.transaction(fn ->
    # ... complicated work involving DB manipulation here.
  end)
end

However, the following alternative would require much less boilerplate and therefore fit the library's goal as 'easy to introduce, easy to rip out':

def update(some, params) do
  Myapp.Repo.transaction_on_primary(fn ->
    # ... complicated work involving DB manipulation here.
  end)
end

The main required change, would be to amend the fly_rpc library to allow passing an anonymous function (which would desugar to calling e.g. Fly.RPC.rpc(node, :erlang, :apply, [fun, []]).

The one drawback of this change, is that logging an anonymous function is not as insightful as logging a MFA-tuple. However, inspecting an anonymous function like the one constructed above will still look like:

#Function<3.103361360/1 in YourModule.update/2>

In my opinion, that should be good enough if you need to debug the RPC.

Qqwy commented 1 year ago

For now I have added the following function to the overridden MyApp.Repo module, which also seems to work:

  def transaction_on_primary(tx_fun) do
    Fly.Postgres.rpc_and_wait(__MODULE__, :transaction, [tx_fun])
  end