ストレージと再起動
Calendarpit がデータを保存する場所、SQLite とインメモリモードの選び方、再起動やリセットの後に残るデータについて説明します。
Calendarpit は、デフォルトでワークスペースの状態をローカルの SQLite ファイルに保存します。使い捨てのテストセッションでは、インメモリに保存することもできます。
ストレージモードの選び方
Homebrew や単体実行ファイルでは、デフォルトで起動したディレクトリ内の .calendarpit/calendarpit.sqlite に保存します。プロジェクトのディレクトリから起動すると、プロジェクトごとにデータを保持できます。
保存先や :memory: は --database <path> または CALENDARPIT_DATABASE で指定します。CLI フラグは環境変数より優先されます。必要な環境と公開状況はインストールで確認してください。
# Temporary E2E session; no database files are written
calendarpit serve --database :memory: --port 0
# Choose a persistent workspace
calendarpit serve --database ./.calendarpit/manual.sqliteモード
- SQLite ファイル(デフォルト): 状態を変更するリクエストが終わるたびに、変更を保存します。Webhook の配信が終わったときにも保存します。相対パスは作業ディレクトリを基準に解決されます。
- インメモリ(
:memory:): データベースファイルもロックファイルも書き込みません。プロセスが終了すると、すべての状態が破棄されます。 - Docker: イメージでは
CALENDARPIT_DATABASE=/data/calendarpit.sqliteが設定されています。- コンテナを削除して作り直してもデータを残すには、
-v calendarpit-data:/dataで名前付きボリュームをマウントします。 - 一時的なセッションには
-e CALENDARPIT_DATABASE=:memory:を渡します。 - Compose の例も名前付きボリュームを使います。
docker compose downではデータが残り、docker compose down -vでは削除されます。
- コンテナを削除して作り直してもデータを残すには、
使用中のモードを確認する
使用中のストレージモードとパスは、次の 3 か所で確認できます。
- 起動ログ: CLI は
Data <absolute-path>(またはData :memory:)を出力します。サーバーのエントリが読み込まれた時点でCalendarpit data: <path or :memory:>も出力します。 - 管理画面: サイドバーのフッターに
SQLiteまたはIn-memoryが表示されます。Settings → Test data には、モードの説明とパス(または:memory:)が表示されます。 - 管理 API:
GET /__calendarpit/healthはstorage: "sqlite" | "memory"を返します。GET /__calendarpit/stateはstorage: { mode, path? }を返します。
SQLite の再起動後に残るデータ
既存の SQLite ファイルを開き直すと、保存済みのデータが復元されます。短時間しか使わないフローの状態は破棄されます。
SIGINT または SIGTERM を受け取ると、Calendarpit は次の順に処理します。
- 処理中の Webhook 配信を中断します。
- 状態を保存します。
- データベースのロックを解放します。
再起動後の扱いは、状態の種類ごとに次のとおりです。
| 状態 | SQLite の再起動後の扱い |
|---|---|
| ユーザー、カレンダー、イベント、OAuth クライアント | 復元されます。イベント ID、タイムスタンプ、etag もそのまま残ります |
| カレンダーリストの設定 | 復元されます。リセットまたは Fixture の読み込みで消去されます |
| リセット基準(起動時 Fixture) | 最初の初期データが保持されます |
| OAuth 接続(grant)、アクセストークン、リフレッシュトークン | 復元されます |
| watch チャネル | 下記の条件を満たす場合だけ復元されます |
| リクエスト履歴、配信ログ、通知 | それぞれ最新の 200 件が復元されます |
| キュー内または処理中の Webhook 配信 | cancelled になります。自動で再送されることはありません |
| 同期トークン、カレンダーの削除済みレコード | 復元されます。リセットまたは Fixture の読み込みで消去されます |
| 保留中の同意、認可コード、ページトークン | 破棄されます。フローまたは一覧の取得をやり直してください |
一部の行の補足です。
- カレンダーリストの設定には、ユーザーごとの上書き・色・非表示/選択状態・リマインダー・削除したエントリが含まれます。
- リセット基準:
--configまたはCALENDARPIT_CONFIGのファイルの内容は無視されます。 - トークンと接続:
- アクセストークンは元の有効期限のままです。
- リフレッシュトークンは期限切れになりません。
- 取り消した接続は取り消されたままです。
- watch チャネルは、次のすべてを満たす場合に復元されます。復元されたチャネルはメッセージ番号も引き継ぎます。
- 有効期限内である。
- 接続が有効である。
- コールバック先のホストが引き続き許可されている。
- ページトークンは
calendarListとeventsのカーソルです。
再起動後は、アプリ側でカレンダーの状態を再取得してください。停止によって中断されたwatch 通知の差分を取り込めます。
初期データ、Fixture、リセット
起動時Fixtureがデータを投入するのは、新しいデータベースのときだけです。起動時 Fixture は、--config、CALENDARPIT_CONFIG、または組み込みのデフォルトから読み込まれます。
- 既存の SQLite ファイル: 保存済みのデータと、最初のリセット基準を開き直します。
--configまたはCALENDARPIT_CONFIGを設定していても、Fixture の内容は無視され、既存のデータベースは上書きされません。- ただし、指定したファイルが存在し、有効な JSON である必要があります。
- インメモリモード: 起動するたびに新しいデータベースになります。そのため、起動時 Fixture は毎回適用され、省略した ID も毎回生成し直されます。
- リセット(
POST /__calendarpit/reset): 再起動をはさんでいても、起動時のリセット基準に戻します。- 接続・トークン・認可コード・同意・watch チャネル・カレンダーリストの設定・同期トークン・ページトークン・カレンダーの削除済みレコード・リクエスト履歴・通知・配信を消去します。
- すぐに保存します。
- SQLite ファイルを削除したり、Fixture のファイルを変更したりはしません。
- Fixture の読み込み(
POST /__calendarpit/fixtures): 現在のデータを置き換え、実行時の状態を消去します。リセット基準は変わりません。 その後にリセットしても、最初の初期データに戻ります。詳しくはテスト制御 APIを参照してください。
ロック、ファイル、並列実行
1 つのファイルに 1 プロセス
1 つの SQLite ファイルを同時に使えるのは、1 つの Calendarpit プロセスだけです。
- Calendarpit は実行中、
<db>.lockに排他ロックをかけます。 - 同じファイルを使う 2 つ目のプロセスは、起動時に
Database is already in use: <path>. Use a separate path or :memory:.というエラーで失敗します。CLI ではこの前にCalendarpit:が付きます。 - ロックはプロセスの終了時に OS が解放します。クラッシュした場合も同じです。
テストを並列に実行する場合は、ワーカーごとに --database :memory: を使うか、別々の SQLite ファイルを指定してください。
補助ファイルとパーミッション
SQLite は WAL モードで動作します。Calendarpit は実行中、データベースと同じ場所に次の補助ファイルを作成します。
<db>-wal<db>-shm<db>.lock<db>.lock-journal
データベース、-wal、-shm、.lock の各ファイルはモード 0600 になります。Calendarpit がデータベース用のディレクトリを作成した場合、そのディレクトリはモード 0700 になります。
実行中は補助ファイルをデータベースと同じ場所に置いたままにしてください。データベースファイルをコピーするときは、先にサーバーを停止してください。
データベースを Git に含めない
データベースにはローカルのテスト用認証情報とクライアントシークレットが入っています。Git には含めないでください。
このリポジトリでは、次のものが Git で無視されます。
.calendarpit/*.sqlite、*.sqlite-wal、*.sqlite-shm、*.sqlite.lockのパターン<db>.lock-journal(.calendarpit/の中にある場合だけ)