Skip to content
This repository was archived by the owner on Nov 13, 2025. It is now read-only.
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 37 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,17 +13,51 @@ Add `redshift_ecto` to your list of dependencies in `mix.exs`:
```elixir
def deps do
[
{:redshift_ecto, "~> 0.1.0"}
{:redshift_ecto, "~> 0.2.0"}
]
end
```

### Example configuration
## Connecting

`RedshiftEcto` communicates with Redshift through the
[Redshift Data API](https://docs.aws.amazon.com/redshift/latest/mgmt/data-api.html)
via the [`AWS.RedshiftData`](https://hexdocs.pm/aws/AWS.RedshiftData.html)
module. The repository configuration accepts the connection options required
by the AWS client.

### Production cluster

Provide your AWS credentials through the environment or explicitly in the
configuration. Specify the cluster identifier, database name and either a
database user or a Secrets Manager ARN:

```elixir
config :my_app, MyApp.Repo,
adapter: RedshiftEcto,
region: "us-east-1",
cluster_identifier: "my-redshift-cluster",
database: "dev",
db_user: "awsuser"
# or: secret_arn: "arn:aws:secretsmanager:..."
```

### Localstack

When developing locally you can point the adapter at a Localstack instance
that provides the Redshift Data API. Use the Localstack endpoint and dummy
credentials:

```elixir
config :my_app, MyApp.Repo,
adapter: RedshiftEcto,
url: "ecto://user:pass@data-warehouse.abc123.us-east-1.redshift.amazonaws.com:5439/db"
region: "us-east-1",
cluster_identifier: "local",
database: "dev",
db_user: "test",
access_key_id: "test",
secret_access_key: "test",
endpoint: "http://localhost:4566"
```

## Testing
Expand Down
4 changes: 2 additions & 2 deletions config/config.exs
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
use Mix.Config
import Config

config :postgrex, :json_library, Jason
config :aws, :json_library, Jason
49 changes: 8 additions & 41 deletions lib/redshift_ecto.ex
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@ defmodule RedshiftEcto do
@moduledoc """
Ecto adapter for [AWS Redshift](https://aws.amazon.com/redshift/).

It uses `Postgrex` for communicating to the database and a connection pool,
such as `DBConnection.Poolboy`.
It communicates with Redshift through the AWS Redshift Data API via the
[`AWS.RedshiftData`](https://hexdocs.pm/aws/AWS.RedshiftData.html) module.
Connections are managed using `DBConnection`.

This adapter is based on Ecto's builtin `Ecto.Adapters.Postgres` adapter. It
delegates some functions to it but changes the implementation of most that
Expand Down Expand Up @@ -95,7 +96,7 @@ defmodule RedshiftEcto do
"""

# Inherit all behaviour from Ecto.Adapters.SQL
use Ecto.Adapters.SQL, driver: :postgrex, migration_lock: nil
use Ecto.Adapters.SQL, driver: :aws, migration_lock: nil

alias Ecto.Adapters.Postgres

Expand Down Expand Up @@ -203,43 +204,9 @@ defmodule RedshiftEcto do
## Helpers

defp run_query(sql, opts) do
{:ok, _} = Application.ensure_all_started(:postgrex)

opts =
opts
|> Keyword.drop([:name, :log])
|> Keyword.put(:pool, DBConnection.Connection)
|> 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 = RedshiftEcto.Connection.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
{:ok, conn} = RedshiftEcto.DataAPI.start_link(opts)
result = RedshiftEcto.DataAPI.execute(conn, sql, [])
GenServer.stop(conn)
result
end
end
Loading