spacebat
Hi, we are writing an application that will have its own single/static database, as well as needing to manage connections to an arbitrary number of other databases. Ecto supports this and I wonder if there are any Ash-specific twists to this approach?
If it simplifies things, I think there would be some resources that are always from a preconfigured database, while some other resources would always come from dynamic repos.
Trending in Questions
I’m working on a project that simulates the bumbl example in the programming phoenix book. It acts almost like an email client. We have a...
New
Hello,
I know there is an approach for handling lists that allows for optimized traversal, but I can’t recall the specific method (somet...
New
I’m seeing that a list inside a Kino.DataTable will be interpreted as a charlist, even if the Kino.configure() is set to charlists: :as_l...
New
So my question is quite simple and i have found no conclusive answer on forum, google or AI.
Should we use :erlang.float for Integer to ...
New
Documentation
While reading the Scoped Routes section, I noticed that the documentation currently refers to a problem without explainin...
New
Hi, I’ve just set up an application with ash_authentication. There is only magic link strategy for now, so there is no confirmation add o...
New
If a change or preparation module uses Ash.Changeset.get_argument/2 or Ash.Query.get_argument/2 (or any of the other get_argument functio...
New
Other Trending Topics
I am happy to introduce the very α version of the new programming language compiled to BEAM.
Welcome Cure.
It has literally three kille...
New
Hi there! We created Gust: A task orchestrator inspired by Airflow.
For those who have never heard about Aiflow, it’s a Python-based wor...
New
Beam Bots (or just BB for short) is a framework for building fault-tolerant robotics applications in Elixir using familiar OTP patterns. ...
New
Xamal is a deployment tool for Elixir apps that deploys native releases to bare metal servers over SSH. It’s a port of GitHub - basecamp/...
New
Corex is an accessible, unstyled UI component library for Phoenix that integrates Zag.js state machines using Vanilla JavaScript and Live...
New
With AI doing more of the implementation work, I’ve been wondering how much coding I should deliberately keep doing myself.
My main conc...
New
Categories:
Sub Categories:
Forums
Popular Tags
- #ecto
- #liveview
- #troubleshooting
- #learning-elixir
- #library
- #deployment
- #erlang
- #testing
- #genserver
- #mix
- #absinthe
- #remote-other
- #otp
- #plug
- #how-to-question
- #macros
- #postgres
- #elixirconf
- #channels
- #exunit
- #discussion
- #code-sync
- #podcasts
- #javascript
- #onsite
- #dialyzer
- #docker
- #authentication
- #umbrella
- #full-time-contract
- #podcasts-by-brainlid
- #ecto-query
- #ai
- #elixirconf-us
- #blog-post
- #elixir-ls
- #phoenix_html
- #iex
- #graphql
- #genstage
- #websockets
- #supervisor
- #advent-of-code
- #distillery
- #processes
- #api
- #forms
- #hex
- #security
- #metaprogramming










Showing Posts 1 to 10- Show Best Posts
- Show All (oldest first)
- Show All (newest first)
zachdaniel
To do that with Ash the way it generally works is that you can set a context in your action to set (or override the default) repo. For example:
You can abstract this using a module-backed preparation, and attach it to a resource using the global preparations block in the resource, to trigger it on all queries:
Not necessarily a full breakdown, but that should give you a starting point
spacebat
It seems that preparations are for read actions only. Is there something more generic that would allow the correct repo to be used for actions that write to the database?
In our preparation we have tried to use the process-scoped setting
Ecto.Repo.put_dynamic_repo/1(this works from IEx such thatMyApp.DynamicRepo.query!(some_sql)is sent to the correct database) however queries generated by Ash give a permission denied error. It looks like Ash is running the query in a different process to that which ran the preparation withput_dynamic_repo.Is there some way to intercept a query in the process that runs it, in a way that applies to all action types, not just read? If I could
put_dynamic_repothere I think it would work.zachdaniel
So, there are two relevant answers here.
Changes are the equivalent of preparations for other action types
Preparations are for read actions only, but changes can do the same thing for all other action types.
Keeping process context
For this, you need to add an
Ash.Tracer.Ash.Tracercan “move” process context from one process to any process that Ash starts.And then you can configure the tracer statically:
YMMV on using
put_dynamic_repo, but will be interested to hear how it goes if you go that route.spacebat
Thanks for the advice @zachdaniel, it didn’t pan out that way but we did find something that works.
Ash.Tracerhasget_span_context/0andset_span_context/1however the problem remained of finding a nice way to execute custom code to fetch the desired repo from the span context in the querying process.I also looked into supplying a function instead of a module name to the repo declaration in the postgres block of our resources, however that function only receives the resource and the operation type, not the queryable which is what we need to decide which repo to use.
My colleague wrote a module that implements various functions from using Ecto.Repo, and in each one checks for the data emplaced by our preparation, uses that to get the right repo and passes it on to
Ecto.Repo.Queryable.I wasn’t comfortable with that approach but unable to find another way, settled on something similar. I wrote a wrapper module that walks the
module_info(:exports)for the target module in the__using__macro, wrapping every user function in the target module sot that if the first argument has the repo identifier within it, the repo is obtained andput_dynamic_repo/1called with it. Then the original function is called unconditionally.This late binding of the indicated repo to the dictionary of the process calling the repo functions is the only thing we’ve found that works.
zachdaniel
This definitely sounds unideal and I feel confident we could find a way to do this better. Would it be at all possible for you to make a small example repo that has one or two resources and has the same dynamic repo set up?
spacebat
I’ll see what I can do, maybe on the weekend. For multi-tenancy in Ash there is the postgresql schema approach, but what we’re trying for is a different database per tenant (and so, each requires a different connection/process and the whole
put_dynamic_repo/1rabbit hole). I know database-per-tenant has its own trade-offs but we’re integrating with a pre-existing system so we’re stuck with it, for some resources, at least for now.Thanks for your input and more importantly thanks for Ash!
zachdaniel
Totally, makes sense. We ultimately want Ash to be able to support all of those models too, even though naturally database per tenant will be a bit more challenging than the alternatives. I could even see a case for building database-per-tenant multi tenancy into
ash_postgresas a first class thing at some point.spacebat
Tragically the fix that I found was working in Ash v2.18.2 and Ash Postgres v1.4.0, but upgrading to v2.19.x and v1.5.x respectively broke it.
It seems that the refactoring done in Ash to improve performance and run queries async has lead to calls to
in_transaction?being made when no arguments are present indicating which database to use.I’ve created a git repository that doesn’t attempt this macrology to wrap the repo, and instead just tries calling
put_dynamic_repo/1from a preparation, which of course does not work:zachdaniel
Perfect, I will look at this this week, thanks.
spacebat
BTW we’ve tried this in a preparation:
Ash.Query.set_context(query, %{data_layer: %{repo: dynamic_repo_pid}})However it leads to
ArgumentError 1st argument: not an atomfromAshPostgres.DataLayer.run_query/2. I believe that this is because repo is expected to be an atom indicating a repo module, not a pid corresponding to a connection using a repo module.[edit: that’s a non-starter but led on to a possible solution…]