0
votes

Goal: Have a phoenix app (#1), that has empty (no tables) Postgres and on first boot waits to be further setup. Once it receives migration and model/schema files via http from other phoenix app (#2), it runs migrations, starts a supervisor(App1.Repo, []), and all received models/schemas from app #2 puts in state in worker(App1.Models, []), so that app #1 can process queries like Repo.all(User). Then, app #1 listens for requests from the app #2 and performs changes in Postgres.

Reason: Idea is to isolate DB completely from app #2, so that when it needs something from db, it just sends Ecto queries to app #1, and app #1 is able to perform that query and return a result. However, app #1 copy can be spawned for different apps, i.e. app #2, #3, #4, which all have different models/schemas, and on first connection provide their db settings (models/migrations) to app #1, which prepares its Postgres with those settings, and then just accept db queries.


Example:

Step 1: App #1 receives User model and respective migration file from app #2.

Step 2: App #1 runs migration file to prepare Postgres.

Step 3: App #1 starts a GenServer (process?) with Ecto, Repo and newly received User model from app #2 and listens for incoming requests.

Step 4: App #1 receives request, runs query, and returns list of all users in its db.

%{
  query: 'User',
  command: 'all'
 } 
 # Should run by app #1 as Repo.all(User)

Problems:

  1. First of all, is it even possible to do something like this? Or there is some fundamental issue in the idea?
  2. How can app #1 run migrations received dynamically, without start/stopping its server? Can we place migrations files (received from app #2) in some directory and then run something like Mix.Tasks.Ecto.Migrate.run() to run all migrations?
  3. Solved Can App #1 compile models/schemas (received from app #2), start a GenServer with those models and make them available to run commands, like Repo.all(User)? Or can we use received models/schemas to dynamically create modules, and pass those modules to App1.Repo, in order to perform queries like Repo.all(User), where User is a dynamically generated module? Solution: Using logic from here, I was able to dynamically create modules, and then use them to perform queries.

I might have confused some things, but I believe solving these problems will help achieve the goal...? Any help appreciated!

1

1 Answers

2
votes
  1. Although I could hardly imagine a benefit of this architecture this is surely possible.

  2. Sure, why not?

Use Ecto.Migrator:

def migrate! do
  path = Application.app_dir(:my_app, "priv/repo/migrations")
  Ecto.Migrator.run(MaApp.Repo, path, :up, all: true)
end
  1. I would take an advantage of using ErlangVM, running the remote functions on different nodes with Node.spawn/* instead of re-inventing a wheel.

That way one might use the the code from App2 from App1 to deal with models/schemas. Maybe I misunderstood the goal here, though, but since it’s already marked a solved, I would live my comment as an additional suggestion.