LitestreamでSQLiteのレプリケーションをやってみる

# Litestreamとは

- SQLiteでレプリケーションを簡単に実現するOSS
- SQLiteのレプリケーションは同じくOSSの[rqlite](https://github.com/rqlite/rqlite)でも実現可能だが、サーバを3台用意する必要がある等の制限があり、あまり気軽に構築できなかった
- Litestreamでは、S3等のオブジェクトストレージを使用することでSQLiteのレプリケーションを実現している
- GitHub: https://github.com/benbjohnson/litestream
- ちなみにGo製のKVSである[Bolt](https://github.com/boltdb/bolt)の開発者でもあるベン・ジョンソン氏が開発した

# MinIOの構築

- 今回はローカル開発環境で検証したいので、S3の代わりにS3互換のオブジェクトストレージのOSSである[MinIO](https://github.com/minio/minio)を使用する
- MinIOの詳細については私の過去記事の[Docker Composeで立てたMinIOにmc/AWS CLI/Boto3で接続してみる](https://qiita.com/h_tyokinuhata/items/c2b76b39b99728d8246a)を参照

## Docker Composeの設定

```yml:compose.yml
services:
  minio:
    # イメージは現時点で最新のものを指定している
    image: quay.io/minio/minio:RELEASE.2023-03-13T19-46-17Z
    container_name: minio
    ports:
      - 9000:9000
      - 9001:9001
    environment:
      - MINIO_ROOT_USER=root
      - MINIO_ROOT_PASSWORD=password
    command: server /data --console-address ":9001"
    volumes:
      - ./docker/minio/data:/data
```

## コンテナの起動

```bash
# コンテナの起動
$ docker compose up -d

# コンテナが正常に起動していることを確認
$ docker compose ps
NAME                COMMAND                  SERVICE             STATUS              PORTS
minio               "/usr/bin/docker-ent…"   minio               running             0.0.0.0:9000-9001->9000-9001/tcp
```

## Web UIにログイン

- Webブラウザ上で`http://localhost:9001`を開くとMinIOのWeb UIにアクセスできる
- 「Username」と「Password」には`compose.yml`に設定した`MINIO_ROOT_USER`と`MINIO_ROOT_PASSWORD`をそれぞれ入力する

![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/78249/40cb40e3-ab43-2e61-ae5d-8687b4c3ca5a.png)

## バケットの作成

- Buckets > Create Bucket を押下

![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/78249/d5aa59cf-cbb7-3369-ec8c-e05e702cd6b0.png)

- 「Bucket Name」を適当に設定して「Create Bucket」を押下

![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/78249/298d7afa-5152-ea0a-2cca-44a5bd691f78.png)

- バケットが作成された

![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/78249/3bd5068e-f810-b180-19e6-d5201159d0ba.png)

# DBの構築

## DBの作成

```bash
# DBの作成
$ touch database.sqlite3
```

## テーブルの作成とレコードの追加

- `users`テーブルを作成し、レコードを1つ追加するSQLを用意する

```sql:users.sql
DROP TABLE IF EXISTS users;

CREATE TABLE users (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    name TEXT NOT NULL,
    age INTEGER NOT NULL
);

INSERT INTO Users (name, age) VALUES ("Takashi", 25);
```

- SQLの実行

```bash
# SQLの実行
$ sqlite3 database.sqlite3 < users.sql

# DBに接続
$ sqlite3 database.sqlite3 
SQLite version 3.39.5 2022-10-14 20:58:05
Enter ".help" for usage hints.
sqlite> 

# テーブルのスキーマを確認
sqlite> .schema users
CREATE TABLE users (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    name TEXT NOT NULL,
    age INTEGER NOT NULL
);

# レコードの確認
sqlite> SELECT * FROM users;
1|Takashi|25
```

# Litestreamでレプリケーション

## Litestreamのインストール

- macOS以外の環境については[Install](https://litestream.io/install/)を参照

```bash
$ brew install benbjohnson/litestream/litestream
```

## レプリケーション

- Litestreamがバケットにアクセスできるよう環境変数を設定
    - ここで設定する値は`compose.yml`の`MINIO_ROOT_USER`と`MINIO_ROOT_PASSWORD`の値

```bash
$ export LITESTREAM_ACCESS_KEY_ID=root
$ export LITESTREAM_SECRET_ACCESS_KEY=password
```

- レプリケーションの実行

```bash
$ litestream replicate database.sqlite3 s3://litestream-example.localhost:9000/database.sqlite3
litestream v0.3.9
initialized db: /Users/foo/litestream-example/database.sqlite3
replicating to: name="s3" type="s3" bucket="litestream-example" path="database.sqlite3" region="us-east-1" endpoint="http://localhost:9000" sync-interval=1s
/Users/foo/litestream-example/database.sqlite3(s3): snapshot written c31dd8bb83686ba2/00000000
```

## リストアして確認

- シェルをもう１つ開く
- Litestreamがバケットにアクセスできるよう環境変数を設定
    - ここで設定する値は`compose.yml`の`MINIO_ROOT_USER`と`MINIO_ROOT_PASSWORD`の値

```bash
$ export LITESTREAM_ACCESS_KEY_ID=root
$ export LITESTREAM_SECRET_ACCESS_KEY=password
```

- リストアの実行

```bash
$ litestream restore -o database2.sqlite3 s3://litestream-example.localhost:9000/database.sqlite3
```

- MinIO上にレプリケーションしたDBをリストアしたDBに`users`テーブルや意図したレコードが作成されていることから、レプリケーションされていることが分かる

```bash
# DBに接続
$ sqlite3 database2.sqlite3 
SQLite version 3.39.5 2022-10-14 20:58:05
Enter ".help" for usage hints.
sqlite> 

# テーブルのスキーマを確認
sqlite> .schema users
CREATE TABLE users (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    name TEXT NOT NULL,
    age INTEGER NOT NULL
);

# レコードの確認
sqlite> SELECT * FROM users;
1|Takashi|25
```