Elixir Litefs

When using Sqlite and Litefs with multiple servers, this forwards all write requests to the primary node.

This library is based off of the packages fly_rpc and fly_postgres_elixir.

Installation

{:litefs, git: "https://git.sr.ht/~sheertj/elixir_litefs" }

Configuration

To use it, rename your existing repo module and add a new module with the same name as your original repo like this.

Original code:

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

Changes to:

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

  defmodule MyApp.Repo do
    use Litefs.Repo, local_repo: MyApp.Repo.Local
  end

The Litefs.Repo performs all read operations like all, one, and get_by directly on the local replica. Other modifying functions like insert, update, and delete are performed on the primary database through proxy calls to a node in your Elixir cluster as identified by litefs.

Migration Files

After changing your repo name, generating migrations can end up in the wrong place, or at least not where you want them.

You can override the inferred location in your config:

config :my_app, MyApp.Repo.Local,
  priv: "priv/repo"

Repo References

The goal with using this repo wrapper, is to leave the majority of your application code and business logic unchanged. However, there are a few places that need to be updated to make it work smoothly.

The following examples are places in your project code that need reference your actual Ecto.Repo. Following the above example, it should point to MyApp.Repo.Local.

With these project plumbing changes, you application code can stay largely untouched!

Application

There are several changes to your application supervision tree.

defmodule MyApp.Application do
  use Application

  def start(_type, _args) do
    # ...
    topologies = Application.get_env(:libcluster, :topologies) || []
    children = [
      # Start litefs genserver and pass the local repo configuration
      {Litefs, Application.get_env(:my_app, MyApp.Repo.Local)},
      # Start the Ecto repository
      MyApp.Repo.Local,
      # setup libcluster
      {Cluster.Supervisor, [topologies, [name: MyApp.ClusterSupervisor]]},
      #...
    ]

    # ...
  end
end

Configure the local sqlite repo.


config :my_app, MyApp.Repo.Local,
  database: "/mnt/litefs1/test.db",
  journal_mode: :delete,
  pool_size: 5,
  stacktrace: true

Libcluster needs to be configured appropriately for dev.exs / runtime.exs.

config :libcluster,
  topologies: [
    local_epmd_example: [
      strategy: Elixir.Cluster.Strategy.LocalEpmd]]