Packages
eventstore
1.0.1
1.4.8
1.4.7
1.4.6
1.4.4
1.4.3
1.4.2
1.4.1
1.4.0
1.3.2
1.3.1
1.3.0
1.2.3
1.2.2
1.2.1
1.2.0
1.1.0
1.0.3
1.0.2
1.0.1
1.0.0
1.0.0-rc.0
0.17.0
0.16.2
0.16.1
0.16.0
0.15.1
0.15.0
0.14.0
0.14.0-rc.0
0.13.2
0.13.1
0.13.0
0.12.1
0.12.0
0.11.0
0.11.0-rc.0
0.10.1
0.10.0
0.9.0
0.8.1
0.8.0
0.7.4
0.7.3
0.7.2
0.7.1
0.7.0
0.6.2
0.6.1
0.6.0
0.5.2
0.5.1
0.5.0
0.4.3
0.4.2
0.4.1
0.4.0
0.3.0
0.2.1
0.2.0
0.1.0
0.0.5
0.0.4
0.0.3
0.0.2
0.0.1
Event store using PostgreSQL for persistence.
Current section
Files
Jump to
Current section
Files
lib/event_store/storage/database.ex
defmodule EventStore.Storage.Database do
@moduledoc false
require Logger
def create(config), do: storage_up(config)
def drop(config), do: storage_down(config)
def migrate(opts, migration) do
opts = Keyword.put(opts, :timeout, :infinity)
case run_query(migration, opts) do
{:ok, _} ->
:ok
{:error, error} ->
{:error, Exception.message(error)}
end
end
def dump(config, target_path) do
database = Keyword.fetch!(config, :database)
unless System.find_executable("pg_dump") do
raise "could not find executable `pg_dump` in path, " <>
"please guarantee it is available before running event_store mix commands"
end
args = [database | parse_default_args(config)]
env = parse_env(config)
System.cmd("pg_dump", args, env: env, into: File.stream!(target_path))
end
defp parse_env(config) do
env =
if password = config[:password] do
[{"PGPASSWORD", password}]
else
[]
end
[{"PGCONNECT_TIMEOUT", "10"} | env]
end
defp parse_default_args(config) do
args = []
args =
case config[:username] do
nil -> args
username -> ["-U", username | args]
end
args =
case config[:port] do
nil -> args
port -> ["-p", to_string(port) | args]
end
host = config[:socket_dir] || config[:hostname] || System.get_env("PGHOST") || "localhost"
args ++
[
"--host",
host,
"--no-password"
]
end
# Taken from ecto/adapters/postgres.ex
defp storage_up(opts) do
database =
Keyword.fetch!(opts, :database) || raise ":database is nil in repository configuration"
encoding = opts[:encoding] || "UTF8"
opts = Keyword.put(opts, :database, "postgres")
command =
~s(CREATE DATABASE "#{database}" ENCODING '#{encoding}')
|> concat_if(opts[:template], &"TEMPLATE=#{&1}")
|> concat_if(opts[:lc_ctype], &"LC_CTYPE='#{&1}'")
|> concat_if(opts[:lc_collate], &"LC_COLLATE='#{&1}'")
case run_query(command, opts) do
{:ok, _} ->
:ok
{:error, %{postgres: %{code: :duplicate_database}}} ->
{:error, :already_up}
{:error, error} ->
{:error, Exception.message(error)}
end
end
defp concat_if(content, nil, _fun), do: content
defp concat_if(content, value, fun), do: content <> " " <> fun.(value)
defp storage_down(opts) do
database =
Keyword.fetch!(opts, :database) || raise ":database is nil in repository configuration"
command = "DROP DATABASE \"#{database}\""
opts = Keyword.put(opts, :database, "postgres")
case run_query(command, opts) do
{:ok, _} ->
:ok
{:error, %{postgres: %{code: :invalid_catalog_name}}} ->
{:error, :already_down}
{:error, error} ->
{:error, Exception.message(error)}
end
end
defp run_query(sql, opts) do
{:ok, _} = Application.ensure_all_started(:postgrex)
opts =
opts
|> Keyword.drop([:name, :log])
|> Keyword.put(:pool, EventStore.Config.get_pool())
|> Keyword.put(:backoff_type, :stop)
{:ok, pid} = Task.Supervisor.start_link()
task =
Task.Supervisor.async_nolink(pid, fn ->
{:ok, conn} = Postgrex.start_link(opts)
value = execute(conn, sql, [], opts)
GenServer.stop(conn)
value
end)
timeout = Keyword.get(opts, :timeout, 15_000)
case Task.yield(task, timeout) || Task.shutdown(task) do
{:ok, {:ok, result}} ->
{:ok, result}
{:ok, {:error, error}} ->
{:error, error}
{:exit, {%{__struct__: struct} = error, _}}
when struct in [Postgrex.Error, DBConnection.Error] ->
{:error, error}
{:exit, reason} ->
{:error, RuntimeError.exception(Exception.format_exit(reason))}
nil ->
{:error, RuntimeError.exception("command timed out")}
end
end
# Taken from ecto/adapters/postgres/connection.ex
defp execute(conn, sql, params, opts) when is_binary(sql) or is_list(sql) do
query = %Postgrex.Query{name: "", statement: sql}
opts = [function: :prepare_execute] ++ opts
case DBConnection.prepare_execute(conn, query, params, opts) do
{:ok, _, result} ->
{:ok, result}
{:error, %Postgrex.Error{}} = error ->
error
{:error, err} ->
raise err
end
end
defp execute(conn, %{} = query, params, opts) do
opts = [function: :execute] ++ opts
case DBConnection.execute(conn, query, params, opts) do
{:ok, _, _} = ok ->
ok
{:error, %ArgumentError{} = err} ->
{:reset, err}
{:error, %Postgrex.Error{postgres: %{code: :feature_not_supported}} = err} ->
{:reset, err}
{:error, %Postgrex.Error{}} = error ->
error
{:error, err} ->
raise err
end
end
end