How to set up the Ecto adapter

Copy Markdown View Source

This guide shows you how to put the Postgres-backed storage adapter under your own Ecto repo: configure a persistence module, migrate, and build the guarded store the rest of the package works through. The store it builds is the one How to persist an execution and resume it in another process uses with the in-memory adapter, so a host that has followed that guide swaps one new/2 call.

Before you start

You need ecto_sql in your dependencies (it is optional for this package, see the README's Installation section) and an Ecto repo, called MyApp.Repo below. A database that an older release of this package already migrated, before 0.13, needs the steps in Upgrading a host from 0.13 to 0.25.0 first.

Configure a persistence module

Define a module that says which repo holds the tables:

defmodule MyApp.Persistence do
  use StatifierPersistence.Ecto, repo: MyApp.Repo
end

Migrate

Add one migration that calls the versioned helper:

defmodule MyApp.Repo.Migrations.AddStatifierPersistence do
  use Ecto.Migration
  def up, do: StatifierPersistence.Ecto.Migrations.up(for: MyApp.Persistence)
  def down, do: StatifierPersistence.Ecto.Migrations.down(for: MyApp.Persistence)
end

One migration covers every version of the package DDL on a fresh database.

Pick up later versions on a database you already migrated

If you already ran that migration when this package shipped only V01, pick the later versions up with a second ordinary migration rather than re-running the first:

defmodule MyApp.Repo.Migrations.AddStatifierPersistenceExecutionMetadata do
  use Ecto.Migration
  def up, do: StatifierPersistence.Ecto.Migrations.up(for: MyApp.Persistence, from: 2)
  def down, do: StatifierPersistence.Ecto.Migrations.down(for: MyApp.Persistence, version: 2)
end

from: says where a call starts and version: where it ends, in both directions, so a migration's two calls always cover the same span: up from from: (default V01) up to version: (default the newest), down from from: (default the newest) back to version: (default V01). A migration that caps one end caps the other to match - Upgrading a host from 0.13 to 0.25.0 shows the case that bites.

Build the guarded store

Hand the persistence module to StatifierPersistence.Storage.new/2:

{:ok, store} =
  StatifierPersistence.Storage.new(
    StatifierPersistence.Storage.Ecto,
    persistence: MyApp.Persistence
  )

The adapter passes the same conformance suite the in-memory reference does (StatifierPersistence.Testing.StorageConformance - point it at your own adapter to hold it to the identical bar), stores engine identities verbatim, and implements the optional per-execution lock_execution/3 as a transaction-scoped advisory-plus-row lock (ADR-0004 as amended).

Isolate each test

In your test suite, pass sandbox: true so each test runs in its own Ecto.Adapters.SQL.Sandbox checkout via the adapter's isolate/1.

Run on a backend that is not Postgres

The adapter is written against Postgres, and only three of its callbacks are Postgres SQL: lock_execution/3 and the two metadata listings. A host on SQLite or another backend declines the lock callback by passing its own serialization: {module, config} strategy, and must not leave the default in place, which reaches the Postgres-only lock_execution/3 and raises mid-execution. Run the shipped suite with mix test --exclude postgres to skip the five conformance cases those callbacks generate.

How to run the Ecto adapter on a backend that is not Postgres is the full guide: what is Postgres-only and why, how to write the strategy, what declining costs, and how to verify your own setup.