todos.
Предварительные требования
- Elixir 1.15 или новее и Erlang/OTP. Это руководство проверено на Elixir 1.20.4, Erlang/OTP 29, Phoenix 1.8.15,
ecto_sql3.14.0 и Postgrex 0.22.4. - Аккаунт ClickHouse Cloud
Создание сервиса ClickHouse Managed Postgres
В консоли ClickHouse Cloud нажмите New service и выберите Postgres. Инстанс будет готов через несколько минут. Пошаговые инструкции см. в руководстве по быстрому старту.Получение сведений о подключении
Откройте свой сервис и нажмите Подключение на левой боковой панели. В открывшемся модальном окне отображаются имя пользователя, пароль, сервер и порт, а также переключатель Directly / via PgBouncer. Оставьте выбранным вариант Directly и включите Use SSL. Скопируйте пароль и имя хоста сервера, затем нажмите Download CA certificate. CA‑сертификат уникален для каждого сервиса. Его также можно скачать позже в разделе Settings → CA Certificate. Прямое подключение или PgBouncer? Приложение Phoenix работает как долгоживущий процесс, а Ecto поддерживает собственный пул соединений (pool_size, по умолчанию — 10). В этом руководстве используется прямое подключение к Postgres через порт 5432. Если суммарное число соединений от множества инстансов приложения приближается к лимиту Postgres, см. раздел Использование PgBouncer.
Создание приложения Phoenix
Установите генератор проектов Phoenix и создайте приложение. По умолчанию в качестве базы данных используется Postgres:Настройка соединения с базой данных
Переместите загруженный CA‑сертификат в каталогpriv приложения и переименуйте его в ca-certificate.pem. Загруженный файл называется по имени вашего сервиса, например your-service-ca-certificate.pem:
priv также включаются в релизы.
В файле config/dev.exs замените строки username, password, hostname и database в конфигурации Hello.Repo на url и ssl:
config/dev.exs
ssl задан в виде keyword list, Postgrex включает проверку сертификата (verify: :verify_peer) и проверку имени хоста, а также передаёт имя хоста из URL в качестве имени TLS‑сервера. Достаточно передать только CA‑сертификат. Вариант ssl: true не подойдёт: в этом случае сертификат сервера проверяется по хранилищу CA вашей операционной системы, а CA вашего сервиса в нём нет.
Укажите в DATABASE_URL адрес вашего сервиса с новым именем базы данных. В этом руководстве используется guide_phoenix:
mix ecto.create подключается к стандартной базе данных postgres с теми же настройками TLS, чтобы выполнить CREATE DATABASE.
Генерация JSON-ресурса и запуск миграции
Сгенерируйте контекст, схему, миграцию и JSON-контроллер для задач (todos):lib/hello_web/router.ex раскомментируйте блок scope для /api и добавьте в него ресурс:
lib/hello_web/router.ex
schema_migrations и при последующих запусках выполняет только новые.
Запуск и проверка
Убедитесь, что соединение зашифровано. Запрос читаетpg_stat_ssl для текущего соединения:
3116:
PATCH возвращает обновлённую задачу:
http://localhost:3116/api/todos в браузере, чтобы увидеть список оставшихся задач:
guide_phoenix, затем public и нажмите на таблицу todos. В схеме public также находится таблица schema_migrations:
Настройка для продакшн-среды
В продакшн-среде Phoenix считывает настройки базы данных изconfig/runtime.exs. В конфигурации Hello.Repo внутри блока if config_env() == :prod do замените строку # ssl: true, путём к CA‑сертификату:
config/runtime.exs
Application.app_dir вычисляет путь внутри вашей сборки или релиза, поэтому настройка работает независимо от того, из какого каталога запускается приложение. Задайте DATABASE_URL в среде развертывания — и снова без строки запроса.
Если изначально ваше приложение работало с SQLite, config/runtime.exs вместо этого считывает DATABASE_PATH. Замените переменную database_path и связанный с ней вызов config :hello, Hello.Repo следующим кодом:
config/runtime.exs
--database sqlite3, также применяют ещё не выполненные миграции при запуске релиза — с помощью дочернего процесса Ecto.Migrator в lib/<your_app>/application.ex. С Postgres этот механизм работает так же.
Использование PgBouncer
Если инстансы вашего приложения в совокупности открывают слишком много прямых соединений, подключайтесь через встроенный PgBouncer. Выберите via PgBouncer в модальном окне Подключение, укажите порт6432 в DATABASE_URL и добавьте prepare: :unnamed в конфигурацию репозитория — как в config/dev.exs, так и в config/runtime.exs:
config/dev.exs
-
Подготовленные операторы: по умолчанию Ecto выполняет каждый запрос как именованный подготовленный оператор, а встроенный PgBouncer поддерживает именованные операторы. Postgrex освобождает операторы с помощью сообщения
Closeна уровне протокола, а не SQL-командойDEALLOCATE. Тем не менее при значении по умолчаниюprepare: :namedпри работе через PgBouncer возникают две проблемы:- Запрос, SQL-текст которого занимает около 4 КБ, может зависнуть до истечения тайм-аута. Примерно через минуту вызывающий код получает
DBConnection.ConnectionError. - После того как миграция изменяет тип столбца, запросы, читающие этот столбец, продолжают завершаться ошибкой
cached plan must not change result typeдаже после перезапуска приложения, поскольку PgBouncer повторно использует свой подготовленный оператор на сервере.
prepare: :unnamedPostgrex заново подготавливает запрос при каждом выполнении, и обе проблемы исчезают. Цена — один дополнительный сетевой обмен на каждый запрос. - Запрос, SQL-текст которого занимает около 4 КБ, может зависнуть до истечения тайм-аута. Примерно через минуту вызывающий код получает
-
Миграции:
mix ecto.migrateработает через PgBouncer со стандартной блокировкой миграций Ecto, которая блокирует таблицуschema_migrationsвнутри транзакции. Не задавайтеmigration_lock: :pg_advisory_lockдля соединения через PgBouncer. Рекомендательная блокировка уровня сеанса может освобождаться через другое соединение Postgres: в этом случае возникает ошибкаfailed to release advisory lock, блокировка остаётся захваченной, и последующие запуски, в том числе через порт5432, ожидают её снятия. Чтобы выполнять миграции через прямое соединение, задайте для командыmix ecto.migrateпеременнуюDATABASE_URLс портом5432. -
Состояние сеанса: операторы
SET, напримерSET search_pathв callback-функцииafter_connect, действуют только для соединения Postgres текущей транзакции. Указаниеsearch_pathв параметреparametersPostgrex приводит к ошибкеunsupported startup parameter: search_path. ДляLISTENиNOTIFY, которые используетPostgrex.Notifications, требуется прямое соединение.
Устранение неполадок
CLIENT ALERT: Fatal - Unknown CA:cacertfileне указывает на CA‑сертификат вашего сервиса, либо вы задалиssl: true. Скачайте сертификат заново в разделе Settings → CA Certificate. У каждого сервиса собственный CA.CLIENT ALERT: Fatal - Bad Certificateсhostname_check_failed: хост вDATABASE_URLне совпадает с указанным в сертификате сервера — например, если вы использовали IP-адрес. Укажите имя хоста из модального окна Подключение.Invalid CA certificate file ... no such file or directory: неверно указан путь вcacertfile. Убедитесь, что файлpriv/ca-certificate.pemсуществует.
Дальнейшие шаги
- Подключение: строки подключения, PgBouncer и TLS
- Настройки: параметры Postgres и PgBouncer, например
max_connections - Реплики для чтения: перенаправление запросов на чтение на реплику с помощью репозиториев реплик Ecto
- Синхронизация с ClickHouse: репликация таблиц Phoenix в ClickHouse для аналитики