LoginSignup
8
4

More than 3 years have passed since last update.

ElixirのDB設定をURLで指定(Gigalixirでの指定も)

Last updated at Posted at 2019-05-04

fukuoka.ex代表のpiacereです
ご覧いただいて、ありがとうございます :bow:

ElixirのDBラッパーである「Ecto.Repo」は、Rails同様、DB設定をURL形式で記述できます

これを応用することで、環境変数でもDB設定が行え、本番環境での設定はもちろん、テスト環境やステージング環境でのDB設定切替が容易になります(ただし、configで設定するため、アプリ再起動が必要です)

内容が、面白かったり、役に立ったら、「いいね」よろしくお願いします :wink:

configの「url」でDB設定を書く

通常の設定(Phoenixで自動生成された場合も含む)は、以下のように記述されます

(なお、「adapter」が未記載ですが、Phoenix 1.4からは、configからRepoモジュール内に移動しています)

config/dev.exs

# Configure your database
config :sample_db, SampleDb.Repo,
    username: "postgres",
    password: "password",
    database: "sample_db_dev",
    hostname: "localhost",
    port: 5432,
    timeout: 1000,
    pool_size: 10

上記と同じ設定を「url」で書くと、以下のようになります

config/dev.exs

# Configure your database
config :sample_db, SampleDb.Repo,
    url: "ecto://postgres:password@localhost:5432/sample_db_dev?timeout=1000&pool_size=10"

書式は、以下の通りです

ecto://【ID】:【パスワード】@【ホスト名】:【ポート番号】/【DB名】?【オプション1】&【オプション2】&…

SSL設定等のオプションも、上記同様、URLパラメータとして記述できます

環境変数でのDB設定

URL形式での指定を応用して、環境変数にDB設定のURLを書いておき、configで読み込めます

これを使うと、本番環境のDBパスワード等を秘匿するのに便利です

prod.secret.exsは、レポジトリにデフォルトで登録されず不便なため、環境変数の方が使いやすいと思います(Gigalixirでもprod.secret.exsは削除が推奨されています

また、DB設定がコード書換無で可能となる(ただし、アプリ再起動は必要)ので、テスト環境やステージング環境でのDB設定切替の運用を安全に行えます

windows
set DATABASE_URL=ecto://postgres:password@localhost:5432/sample_db_dev?timeout=1000&pool_size=10
windows以外
export DATABASE_URL=ecto://postgres:password@localhost:5432/sample_db_dev?timeout=1000&pool_size=10

configでは、System.get_env()を使って、環境変数を読み込みます

config/dev.exs

# Configure your database
config :sample_db, SampleDb.Repo,
    url: System.get_env( "DATABASE_URL" ), 
    

Gigalixirでの設定の書き方

Gigalixirでは、以下のように設定を書きます

config/prod.exs

# Configure your database
config :sample_db, SampleDb.Repo,
    url: "${DATABASE_URL}",
    

${DATABASE_URL}に、どんな設定がされているかは、以下コマンドで確認できます

gigalixir config
{
  "DATABASE_URL": "ecto://XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX-user:pw-XXXXXXXXXXXXXXXX@postgres-XXXXXXXX.gigalixir.com:5432/XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX"
}

設定を変更する場合は、以下コマンドで行えます

gigalixir config:set DATABASE_URL="ecto://XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX-user:pw-XXXXXXXXXXXXXXXX@postgres-XXXXXXXX.gigalixir.com:5432/XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX"

オマケ:DB設定のパース

Ecto.Repo.Supervisor.parse_url()でURLがパースされています

/ecto/lib/ecto/repo/supervisor.ex
defmodule Ecto.Repo.Supervisor do
  
  @doc """
  Parses an Ecto URL allowed in configuration.

  The format must be:

      "ecto://username:password@hostname:port/database?ssl=true&timeout=1000"

  """
  def parse_url(""), do: []

  def parse_url(url) when is_binary(url) do
    info = URI.parse(url)

    if is_nil(info.host) do
      raise Ecto.InvalidURLError, url: url, message: "host is not present"
    end

    if is_nil(info.path) or not (info.path =~ ~r"^/([^/])+$") do
      raise Ecto.InvalidURLError, url: url, message: "path should be a database name"
    end

    destructure [username, password], info.userinfo && String.split(info.userinfo, ":")
    "/" <> database = info.path

    url_opts = [username: username,
                password: password,
                database: database,
                hostname: info.host,
                port:     info.port]

    query_opts = parse_uri_query(info)

    for {k, v} <- url_opts ++ query_opts,
        not is_nil(v),
        do: {k, if(is_binary(v), do: URI.decode(v), else: v)}
  end

  defp parse_uri_query(%URI{query: nil}),
    do: []
  defp parse_uri_query(%URI{query: query} = url) do
    query
    |> URI.query_decoder()
    |> Enum.reduce([], fn
      {"ssl", "true"}, acc ->
        [{:ssl, true}] ++ acc

      {"ssl", "false"}, acc ->
        [{:ssl, false}] ++ acc

      {key, value}, acc when key in @integer_url_query_params ->
        [{String.to_atom(key), parse_integer!(key, value, url)}] ++ acc

      {key, value}, acc ->
        [{String.to_atom(key), value}] ++ acc
    end)
  end

  defp parse_integer!(key, value, url) do
    case Integer.parse(value) do
      {int, ""} ->
        int

      _ ->
        raise Ecto.InvalidURLError,
              url: url,
              message: "can not parse value `#{value}` for parameter `#{key}` as an integer"
    end
  end

p.s.「いいね」よろしくお願いします

ページ左上の image.pngimage.png のクリックを、どうぞよろしくお願いします:bow:
ここの数字が増えると、書き手としては「ウケている」という感覚が得られ、連載を更に進化させていくモチベーションになりますので、もっとElixirネタを見たいというあなた、私達と一緒に盛り上げてください!:tada:

8
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
4