Database multitenancy for Elixir applications!
Triplex.create/1,2
now rolls back the prefix creation if the func
fails with error tupleIt's not our fault, but there is a breaking change if you upgrade it because migration on Ecto 3 are ran on a different process.
The problem you may find is basically with this kind of code:
Repo.transaction(fn ->
{:ok, _} = Triplex.create("tenant")
User.insert!(%{name: "Demo user 1"})
User.insert!(%{name: "Demo user 2"})
end)
As Triplex.create/1
runs the tenant migrations, and they will run on different processes,
you will get an error from your db saying that the given tenant prefix (schema or database
depending on the db) does not exist.
That occurs because the new processes will checkout a new connection to db, making them not aware of the current transaction, since it is on another db connection. But don't panic, we have a solution for you!
Here is how you could achieve the same results on success or fail:
Triplex.create_schema("tenant", Repo, fn(tenant, repo) ->
Repo.transaction(fn ->
{:ok, _} = Triplex.migrate(tenant, repo)
User.insert!(%{name: "Demo user 1"})
User.insert!(%{name: "Demo user 2"})
tenant
end)
end)
For more details about these function check the online documentation for Triplex.create/1,2
and Triplex.create_schema/1,2,3
.
Triplex.create/1,2
now rolls back the prefix creation if the func
fails with error tupleIt's not our fault, but there is a breaking change if you upgrade it because migration on Ecto 3 are ran on a different process.
The problem you may find is basically with this kind of code:
Repo.transaction(fn ->
{:ok, _} = Triplex.create("tenant")
User.insert!(%{name: "Demo user 1"})
User.insert!(%{name: "Demo user 2"})
end)
As Triplex.create/1
runs the tenant migrations, and they will run on different processes,
you will get an error from your db saying that the given tenant prefix (schema or database
depending on the db) does not exist.
That occurs because the new processes will checkout a new connection to db, making them not aware of the current transaction, since it is on another db connection. But don't panic, we have a solution for you!
Here is how you could achieve the same results on success or fail:
Triplex.create_schema("tenant", Repo, fn(tenant, repo) ->
Repo.transaction(fn ->
{:ok, _} = Triplex.migrate(tenant, repo)
User.insert!(%{name: "Demo user 1"})
User.insert!(%{name: "Demo user 2"})
tenant
end)
end)
For more details about these function check the online documentation for Triplex.create/1,2
and Triplex.create_schema/1,2,3
.
This is the official release of the candidate 1.2.0-rc.0
For more details check the release canditate.
This is the release candidate for triplex new version!
Hope to get an official review in a week or two!
In short, this release has this three main changes:
1.1
patch release, please let me know.The PR's in this release are:
Triplex
(#42) @kelvinstThanks for all your work @dustinfarris!
For those who wonder, there were not big changes, but here is the list of what can break on your code.
If you use any of the second element of the return on the following methods, be sure to check if they still work:
Triplex.create_schema/3
- this method was returning either the given func
result (the 3rd param), which could be pretty much anything, or the result of a Ecto.Adapters.SQL.query/4
call, which also can change depending on ecto's version. Now it returns {:ok, tenant}
, where tenant
is the name of the tenant.If you need your func
result, you can change your code as follows:
# This
case Triplex.create_schema("test", Repo, your_func) do
{:ok, func_return} -> func_return
{:error, reason} -> raise reason
end
# Becomes this
with {:ok, _} <- Triplex.create_schema("test", Repo),
{:ok, func_return} <- your_func.("test", Repo) do
func_return
else
{:error, reason} -> raise reason
end
Triplex.create/2
- this method was returning {:ok, migrations}
, where migrations
was the list of migrations ran (which could be even an empty list). This changed because this method actually just delegates to Triplex.create_schema/3
sending Triplex.migrate/2
as the fun, so now it returns {:ok, tenant}
as well.So if you need the migrations ran, here is how you should do:
# This
case Triplex.create("test", Repo) do
{:ok, migrations} -> migrations
{:error, reason} -> raise reason
end
# Becomes this
with {:ok, _} <- Triplex.create_schema("test", Repo),
{:ok, migrations} <- Triplex.migrate("test", Repo) do
migrations
else
{:error, reason} -> raise reason
end
Triplex.PlugConfig
on #43This was a restructuring on the plugs configuration, which was only one configuration for all the plugs and now we changed to specific configs for each plug type.
This only affects you if you were using the module Triplex.PlugConfig
(which was completely removed) explicitly, otherwise you should be ok!