View Source Schema Based Multitenancy

Multitenancy in AshPostgres is implemented via postgres schemas. For more information on schemas, see postgres' schema documentation

Implementing multitenancy via schema's involves tracking "tenant migrations" separately from migrations for your public schema. You can see what this looks like by simply creating a multitenant resource, and using the migration generator mix ash_postgres.generate_migrations --apis My.Api. It will put schema specific migrations in priv/repo/tenant_migrations. When you generate migrations, you'll want to be sure to audit migrations in both directories. Additionally, when you deploy, you'll want to run your migrations, as well as running them with the migrations path priv/repo/tenant_migrations.

Generated migrations

The generated migrations include a lot of niceties around multitenancy. Specifically, foreign keys will point at tables in the correct schema, and foreign keys to non-multitenant resources will point to the correct table. If you are using attribute multitenancy, foreign keys to tables also using attribute multitenancy will be composite foreign keys, including the tenant attribute as well as the referencing field.

Migrations in the tenant directory will call repo().all_tenants(), which is a callback you will need to implement in your repo that should return a list of all schemas that need to be migrated.

Automatically managing tenants

By setting the template configuration, in the manage_tenant section, you can cause the creation/updating of a given resource to create/rename tenants. For example:

defmodule MyApp.Organization do
  use Ash.Resource,
    ...

  postgres do
    ...

    manage_tenant do
      template ["org_", :id]
    end
  end
end

With this configuration, if you create an organization, it will create a corresponding schema, e.g. org_10 in the database. Then it will run your tenant migrations on that schema. To override the tenant_migrations path, implement the AshPostgres.Repo.tenant_migrations_path/0 callback.

Notice that manage_tenant is nested inside the postgres block. This is because the method of managing tenants is specific to postgres, and if another data layer supported multitenancy they may or may not support managing tenants in the same way.