Prototype of an Ecto adapter for libSQL servers (Turso, Bunny Database, sqld), over the Hrana protocol on HTTP, in pure Elixir.

libSQL speaks SQLite's dialect, so the SQL generation, the migrations, the loaders/dumpers and the constraint errors are ecto_sqlite3's: only the driver (EctoHrana.Connection) is new.

defmodule MyApp.Repo do
  use Ecto.Repo, otp_app: :my_app, adapter: Ecto.Adapters.Hrana
end

config :my_app, MyApp.Repo,
  uri: "libsql://my-db.turso.io",
  auth_token: System.fetch_env!("LIBSQL_AUTH_TOKEN")

Motivations

  • Pure Elixir, no native code. ecto_libsql wraps libSQL's Rust library in a NIF: it needs a precompiled binary for the target (or a Rust toolchain), and a crash in native code takes the whole VM down. Against a remote server, the native library is not needed: Hrana is HTTP and JSON, which Req and Jason speak. This adapter builds and runs wherever the BEAM does, and a failing connection is an ordinary process crash, restarted by DBConnection.
  • Reuse rather than rewrite. libSQL speaks SQLite's dialect, and ecto_sqlite3 already generates it, runs the migrations, loads and dumps the types and maps the constraint errors. Only the transport is new, so the behaviour is the one SQLite users already know, ash_sqlite included.
  • Hosted libSQL, from Elixir. Turso and Bunny Database expose libSQL over Hrana: this lets an Ecto (or Ash) application use them like any other database, with transactions and savepoints.

Limitations

  • A prototype: it passes its tests against sqld and a full Ash application test suite, but has not been used against a remote server in production.
  • Each statement is an HTTP round trip to the server: against a remote server, the network latency dominates. Keep the application close to the database and avoid N+1 queries.
  • Streams are emulated: Repo.stream/2 fetches all the rows at once.
  • No embedded replicas (a local copy synchronised with the server), which need libSQL's native library.
  • The SQL generation comes from ecto_sqlite3's internal modules, so the ecto_sqlite3 versions this library works with are pinned.

As with ecto_sqlite3, query_many/4 is not supported: run the statements one by one.

Development

The tests and bench/compare.exs need a libSQL server:

docker run -p 127.0.0.1:8080:8080 ghcr.io/tursodatabase/libsql-server:latest

License

MIT, see LICENSE.