michaelfich

michaelfich

Hey all,

I’ve been working on a project where I need to move an existing database into a multi-tenant configuration.

We have some migrations that we want to run outside of individual tenants (creating enums, etc) to be run from priv/repo/migration, and also a set of migrations we want to run to generate tables for each individual tenant from priv/repo/tenant_migrations.

Ideally I would want the migrations to run for all of these sequentially whenever I run mix ecto.migrate on the single database, in the following order:

  1. public
  2. tenant_1
  3. tenant_2
  4. tenant_3
  5. etc…

Additionally I do not wish maintain multiple pools of database connections to the same database, if possible, though it would be nice to be able to have separate Ecto Repo’s for each tenant, to make queries such as:

MyApp.Repo.Tenant1.all(query)

# -- or --

MyApp.Repo.all(query, prefix: "tenant_1")

I’ve been able to run these migrations properly from the command line through multiple commands, by doing the following:

mix ecto.migrate

mix ecto.migrate --prefix=tenant_1 --migrations-path=priv/repo/tenant_migrations/

mix ecto.migrate --prefix=tenant_2 --migrations-path=priv/repo/tenant_migrations/

mix ecto.migrate --prefix=tenant_3 --migrations-path=priv/repo/tenant_migrations/

If possible, it would be nice to configure each Repo with a shared connection pool and then have their own set of migrations. One of the issues I encountered while trying to define a separate Repo for each tenant was that running mix ecto.create or mix ecto.drop, it tries to do create or drop the database multiple times; working the first and failing the rest of the times.

I’ve scoured the Ecto and EctoSQL libraries for documentation on this and tried multiple solutions I’ve come across, but it’s not yet produced a solution that works yet aside from running multiple ecto.migrate mix tasks as demonstrated above.

Has anyone come across this use-case and able to point me in the right direction? Any assistance would be greatly appreciated.

Showing Posts 1 to 9

l00ker

l00ker

Have you looked at Triplex?

https://github.com/ateliware/triplex

l00ker

l00ker

I know this is a bit of a stretch, but… I just started playing with Ash Framework and I noticed that it has multi-tenancy built in.

I say a bit of a stretch because it might be too much all at once. I decided to try Ash with a section of an app I’m working on and so far so good, but it is quite a lot to ingest.

Just another option to consider.

al2o3cr

al2o3cr

Like @l00ker said, take a look at triplex; the process you described that you want is so similar to how that package works I thought you might be using it already.

michaelfich

michaelfich OP

Hey guys, thanks for the responses!

I’ve taken a look at Triplex and it looks promising but one of the internal concerns we have is that it hasn’t had a new release in nearly 4 years so I’m not sure if we want to use such a dependency on something that’s important to our project.

I’m hoping to be able to complete this with just Ecto/EctoSQL alone if possible to migrate an existing project with several hundred migrations into a multi-tenant DB

al2o3cr

al2o3cr

I can understand this concern for some libraries, but take the time to read through the Triplex code: there’s not much of it, and what’s there is mostly gluing together things that already exist. All the actual “tenent enforcement” is delegated to Ecto’s prefix machinery.

cjbottaro

cjbottaro

I start a separate Ecto.Repo per Postgres server. Each server holds many tenants (as Postgres schemas).

Then you use these callbacks…

put_dynamic_repo/1
get_dynamic_repo/0
default_options/1

to determine which Ecto.Repo to use and also set the prefix (schema).

Then I wrote my own mix ecto.tenants.migrate which uses Task.async_stream to migrate all tenants, delegating out to the existing migration functions, kinda looking at how mix ecto.migrate does it.

Ecto is close to having multi-tenancy out of the box… it’s just missing like one callback (something to list all the tenants) and a migration task that basically iterates over all tenants, calling the normal migration code.

michaelfich

michaelfich OP

That makes a lot of sense when the use-cases is separate databases.

I was curious if anyone could point me in the right direction for different schemas within the same database as well.

joddm

joddm

You can specify prefixes inside the migration code:
Multi tenancy with query prefixes — Ecto v3.9.4 (hexdocs.pm)

The other approach is by changing each desired migration to run across multiple prefixes.

cjbottaro

cjbottaro

It’s even easier because you just have one repo. Then you just use the default_options/1 callback to automatically set the prefix.

defmodule MyApp.Repo do
  use Ecto.Repo, otp_app: :my_app
  
  def default_options(_operation) do
    prefix = Tenant.get()
    |> Tenant.to_schema()
    
    [prefix: prefix]
  end
  
end

Then your migration code is something like…

def run(_args) do
  Tenant.all()
  |> Enum.each(fn tenant ->
    opts = [
      prefix: Tenant.to_schema(tenant),
      migration_lock: false
    ]
    
    Ecto.Migrator.run(MyApp.Repo, migrations, dir, opts)
  end)
end

Note the migration_lock: false. Last I checked, the code that does migration locking does not honor the :prefix option. Should probably open a GitHub issue about that.

— All posts loaded —

Where Next? Top

Trending in Questions Top

stjefim
Hello! Suppose you are building workflow (order / task / payment) processing system with the following requirements: Each workflow con...
New
jonnycharles
I’m in search of an Elixir library that offers PDF generation capabilities similar to Ruby’s Prawn. While there have been discussions abo...
New
spammy
I’m looking to build a personal workflow to quickly deploy web applications written in elixir/phoenix, for local consumption (ie not on t...
New
dli
Before I dive in myself, did anyone successfully sprinkle Hologram into their existing LiveView app? Looking for hints regarding: Addi...
New
roeland
Kia ora, We have been using elixir-google-api to connect to Google Drive. However, with the updates to Tesla due to CVEs this is now bro...
New
bottlenecked
Hi all, I wanted to ask how the community is dealing with post-release steps. Today we have Ecto migrations, which make sure that the db...
New
rahultumpala
Hello, I have an Elixir backend that implements a custom protocol over TCP. I want to load test the backend and assess the performance o...
New

Other Trending Topics Top

JesseHerrick
Hey, I’m Jesse and I’m the main contributor behind Dexter, a full-featured, lightning-fast Elixir LSP optimized for large codebases. It s...
New
jimsynz
Beam Bots (or just BB for short) is a framework for building fault-tolerant robotics applications in Elixir using familiar OTP patterns. ...
New
mcass19
ExRatatui lets you cook up rich terminal UIs in Elixir, powered by Rust’s ratatui via Rustler NIFs. Build interactive terminal applicatio...
New
Damirados
Hello everyone. After busy few months I am happy to announce v0.1.0 of Emerge & Solve. They are GUI (Emerge) and State management (S...
New
netoum
Corex is an accessible, unstyled UI component library for Phoenix that integrates Zag.js state machines using Vanilla JavaScript and Live...
New
ausimian
Emily is an Elixir library that runs Nx computations on Apple’s MLX. Install it as the default Nx backend and Nx, defn, Axon, Nx.Serving,...
New

We're in Beta

About us Mission Statement

Options

Thread Display Mode




Thread Preview

Skip Thread Previews