README.md

# ExWal

<!-- MDOC !-->

**ExWal is a project that aims to provide a solution for managing write-ahead log (WAL) in Elixir.**

## Installation

The package can be installed by adding `ex_wal` to your list of dependencies in `mix.exs`:

```elixir
def deps do
  [
    {:ex_wal, "~> 0.2"}
  ]
end
```

## Design
The design of this project borrows from the implementation of WAL in the [Pebble](https://github.com/cockroachdb/pebble) project.


## Usage

1. Prepare a instance
```Elixir
defmodule MyApp do
  use ExWal, otp_app: :my_app
end
```

2. Choose your file system implementation
ExWal provides 2 file system implementations:
- `ExWal.FS.Default`
- `ExWal.FS.Syncing`

`ExWal.FS.Syncing` provider better write performance. You can use `MyApp.syncing_fs/0` to get a `ExWal.FS.Syncing` instance.


3. Add it to supervised tree
```elixir
Supervisor.start_link(
  [
    MyApp
  ],
  strategy: :one_for_one
)
```

4. Get a manager

ExWal provides 2 manager implementations:
- `ExWal.Manager.Standalone`
- `ExWal.Manager.Failover`

You can use `MyApp.manager/3` to get a `ExWal.Manager` instance.

```Elixir
# get a standalone manager
{:ok, m} =
      MyApp.manager(:standalone, "my-manager", %ExWal.Manager.Options{
        primary: [
          fs: MyApp.syncing_fs(),
          dir: "my-primary-dir-path"
        ]
      })

# get a failover manager
{:ok, m} =
      MyApp.manager(:failover, "my-manager", %ExWal.Manager.Options{
        primary: [
          fs: MyApp.syncing_fs(),
          dir: "my-primary-dir-path"
        ],
        secondary: [
          fs: MyApp.syncing_fs(),
          dir: "my-secondary-dir-path"
        ]
      })
```

5. Create a WAL writer

Manager provides a `create/2` function to create a WAL writer. Writer is a instance which implements `ExWal.LogWriter` protocol.

```Elixir
{:ok, m} =
      MyApp.manager(:standalone, "my-manager", %ExWal.Manager.Options{
        primary: [
          fs: MyApp.syncing_fs(),
          dir: "my-primary-dir-path"
        ]
      })

{:ok, writer} = ExWal.Manager.create(m, 1)

1..50
|> Enum.map(fn x ->
  s =
    x
    |> Integer.to_string()
    |> String.pad_leading(4, "0")

  "Hello Elixir! I am a developer. I love Elixir #{s}."
end)
|> Enum.each(fn data -> LogWriter.write_record(writer, data) end)
```

6. Create a WAL reader

Manager provides a `list/2` function to list all WAL files. You can use `MyApp.open_for_read/1` function to create a Reader.  Reader is a instance which implements `ExWal.LogReader` protocol.

```Elixir
{:ok, m} =
      MyApp.manager(:standalone, "my-manager", %ExWal.Manager.Options{
        primary: [
          fs: MyApp.syncing_fs(),
          dir: "my-primary-dir-path"
        ]
      })

{:ok, [log | _]} = ExWal.Manager.list(m)
{:ok, reader} = MyApp.open_for_read(log)

reader
|> Enum.reduce_while(fn reader ->
  case LogReader.next(reader) do
    :eof ->
      {:halt, :ok}

    {:error, _reason} ->
      LogReader.recovery(reader)
      {:cont, reader}

    # raise ExWal.Exception, message: "read failed: #{inspect(reason)}"

    bin ->
      IO.puts(bin)
      {:cont, reader}
  end
end)
```


## Benchmark

See [benchmarks/report.md](benchmarks/report.md) for more details.

-----
Documentation can be generated with [ExDoc](https://github.com/elixir-lang/ex_doc)
and published on [HexDocs](https://hexdocs.pm). Once published, the docs can
be found at <https://hexdocs.pm/ex_wal>.