Litestream をさくらのオブジェクトストレージで試してみた

## はじめに

[Litestream](https://litestream.io/) は Ben Johnson さんが開発されている SQLite のデータベースをストリーミングするソフトウェアです。 

[I'm All-In on Server-Side SQLite ・ Fly](https://fly.io/blog/all-in-on-sqlite-litestream/) によると Ben Johnson さんは Fly.io にジョインしてフルタイムで Litestream の開発をすることになったそうです。良い話ですねー。

Litestream では AWS S3 互換のオブジェクトストレージが利用可能ということで、[さくらインターネットのオブジェクトストレージ](https://cloud.sakura.ad.jp/specification/object-storage/) でも使えるように[プルリクエスト](https://github.com/benbjohnson/litestream/pull/374)を送ったところマージしていただけました。

さて、この記事では [Getting Started - Litestream](https://litestream.io/getting-started/) をさくらのオブジェクトストレージでも試してみましたので以下で手順を説明します。今回試した環境は Ubuntu 22.04 LTS です。

## Getting Started のチュートリアルの事前準備

### Litestream をソースからインストールする
[Releases ・ benbjohnson/litestream](https://github.com/benbjohnson/litestream/releases) には上記の [プルリクエスト](https://github.com/benbjohnson/litestream/pull/374) を取り込んだバージョンはリリースされていないため、ソースからインストールする必要があります。

そのため Go のインストールが必要です。 https://go.dev/ の Download ボタンから 
[Download and install - The Go Programming Language](https://go.dev/doc/install) の手順に従ってダウンロード、インストールしてください。

また Litestream が依存している [mattn/go-sqlite3: sqlite3 driver for go using database/sql](https://github.com/mattn/go-sqlite3) で cgo を使用しているため C コンパイラも必要です。以下のコマンドでインストールしてください。

```
sudo apt-get update && sudo apt-get install -y build-essential
```

あとは以下のコマンドを実行すると Litestream の最新版をソースからインストールできます。

```
go install github.com/benbjohnson/litestream/cmd/litestream@latest
```

### オブジェクトストレージの API キーの発行

オブジェクトストレージのサイトとバケットは既に作成済みとします。もしまだの場合は [ご利用手順](https://manual.sakura.ad.jp/cloud/objectstorage/about.html#id8) に従って作成してください。

次にAPIキーの発行ですが、バケットごとに発行できる [パーミッション設定によるアクセスキーの発行](https://manual.sakura.ad.jp/cloud/objectstorage/about.html#objectstrage-about-permission) の手順で発行するのがお勧めです。パーミションは READ/WRITE (読み込みおよび書き込みを許可) を設定してください。

### sqlite3 パッケージのインストール

sqlite のデータベースを開いて操作するために CLI をインストールします。
以下のコマンドを実行します。

```
sudo apt-get update && sudo apt-get install -y sqlite3
```

## Getting Started のチュートリアルの実行手順
### 実験用データベース作成

作業用のディレクトリを適宜作成してそこに移動して以下のコマンドを実行し、新規にデータベースを作成します。

```
sqlite3 fruits.db
```

`sqlite>` のプロンプトで以下の SQL を実行して `fruits` テーブルを作成します。

```
CREATE TABLE fruits (name TEXT, color TEXT);
```

引き続き、`sqlite>` のプロンプトで以下の SQL を実行して `fruits` テーブルに2行レコードを登録します。

```
INSERT INTO fruits (name, color) VALUES ('apple', 'red');
INSERT INTO fruits (name, color) VALUES ('banana', 'yellow');
```

この後さらに SQL を実行するので `sqlite>` のプロンプトを抜けずに残しておいてください。

### ローカルのデータベースをオブジェクトストレージにレプリケーション

上記の sqlite3 を実行しているのとは別に端末を開きます。

bash のプロンプトで以下のコマンドを実行して `LITESTREAM_ACCESS_KEY_ID` と `LITESTREAM_SECRET_ACCESS_KEY` の環境変数を設定します。

```
export LITESTREAM_ACCESS_KEY_ID=発行したアクセスキーID
export LITESTREAM_SECRET_ACCESS_KEY=発行したシークレットアクセスキー
export LITESTREAM_ENDPOINT=s3.isk01.sakurastorage.jp
export LITESTREAM_REGION=jp-north-1
```

次に Litestream の reprecate コマンドを使ってリプリケーションを開始します。
以下のコマンド例の `my_bucket_name` の箇所は実際のバケット名に置き換えて実行してください。

```
litestream replicate fruits.db s3://my_bucket_name/fruits.db
```

すると以下のようなメッセージが litestream から出力されます。

```
…(略)…
initialized db: 作業ディレクトリのパス/fruits.db
replicating to: name="s3" type="s3" bucket="my_bucket_name" path="fruits.db" region="jp-north-1" endpoint="https://s3.isk01.sakurastorage.jp" sync-interval=1s
…(略)…
```

### オブジェクトストレージ上のデータベースをローカルにリストア

さらに別の端末で bash を起動して、`LITESTREAM_ACCESS_KEY_ID` と `LITESTREAM_SECRET_ACCESS_KEY` の環境変数を設定します。

```
export LITESTREAM_ACCESS_KEY_ID=発行したアクセスキーID
export LITESTREAM_SECRET_ACCESS_KEY=発行したシークレットアクセスキー
export LITESTREAM_ENDPOINT=s3.isk01.sakurastorage.jp
export LITESTREAM_REGION=jp-north-1
```

そして以下のように実行してオブジェクトストレージ上のデータベースをローカルの `fruits2.db` という別のファイルにリストアします。`my_bucket_name` は実際のバケット名に置き換えて実行してください。

```
litestream restore -o fruits2.db s3://my_bucket_name/fruits.db
```

すると作業ディレクトリに `fruits2.db` というデータベースファイルが作成されます。
sqlite3 コマンドで開いて確認します。

```
sqlite3 fruits2.db
```

`sqlite>` プロンプトで以下の SQL を実行して `fruits` テーブルの中身を確認します。

```
select * from fruits;
```

すると以下のように2行の結果が表示されます。

```
apple|red
banana|yellow
```

こちらは `.quit` を入力して抜けてください。

### 継続的なレプリケーション

ここで *最初の (ローカルでデータベースを作成した) 端末* に戻って、以下のSQLを実行してレコードを1行追加します。

```
INSERT INTO fruits (name, color) VALUES ('grape', 'purple');
```

次に *3番目の (データベースをリストアした) 端末* で以下のコマンドを実行して、別のデータベースファイル `fruits3.db` にリストアします。

```
litestream restore -o fruits3.db s3://my_bucket_name/fruits.db
```

以下のコマンドで `fruits3.db` を開きます。

```
sqlite3 fruits3.db
```

`sqlite>` プロンプトで以下の SQL を実行して `fruits` テーブルの中身を確認します。

```
select * from fruits;
```

すると以下のように、上で1行追加した3行の結果が表示されます。

```
apple|red
banana|yellow
grape|purple
```

## おわりに

Getting Started のチュートリアルではSQLite の DB ファイルからオブジェクトストレージへの継続レプリケーションでしたが、[I'm All-In on Server-Side SQLite ・ Fly](https://fly.io/blog/all-in-on-sqlite-litestream/#enter-litestream) を読むと、今後のリリースではオブジェクトストレージ無しで 2つの SQLite のデータベースファイル間での継続レプリケーションも計画されているようです。こちらも楽しみですね！