Skip to content

V2 アーキテクチャ

NanaSQLite の V2 モードは、書き込みの多いワークロードに対して非ブロッキングの書き込みを実現するオプションのアーキテクチャです。

制限事項

V2 モードはシングルプロセスでの利用を想定しています。Gunicorn のマルチワーカーなど、複数プロセスからの利用はサポートしていません。

データの永続性に関する警告 (SIGKILL)

V2 モードは atexit を使用して終了時に自動フラッシュを行いますが、OS による強制終了(SIGKILL や電源断)が発生した場合は、メモリ上のバッファが失われます。ミッションクリティカルなデータについては、db.flush(wait=True) を明示的に呼び出すか、デフォルトの immediate モードを使用してください。

概要

V2 エンジンは デュアルレーン 設計を採用しています:

アプリケーション

  ├─ KVS レーン ──────→ ステージングバッファ → コミット
  │   (dict操作)          (書き込み結合)

  └─ Strict レーン ───→ 優先度キュー → 順序通り実行
      (SQL操作)           (FIFO)
  • KVS レーン: db["key"] = value のような辞書操作を高速に処理。同じキーへの書き込みは結合(coalescing)される
  • Strict レーン: sql_insert()sql_update() などの SQL 操作を順序を保って実行

有効化

python
from nanasqlite import NanaSQLite

db = NanaSQLite(
    "app.db",
    v2_mode=True,           # V2 エンジンを有効化
    flush_mode="immediate",  # フラッシュモード
)

CRUD 特化のメモリ優先モード

memory_first=True は V2 エンジンの time フラッシュを利用する、CRUD 速度特化のオプションです。起動時に KVS 全体をメモリへ読み込み、以後の get / set / delete / len / keys / batch_get などをメモリ上で完結させます。変更差分だけがバックグラウンドで SQLite にフラッシュされます。

python
from nanasqlite import NanaSQLite

db = NanaSQLite(
    "app.db",
    memory_first=True,
    memory_flush_interval=5.0,  # デフォルト: 5秒
)

db["session:1"] = {"user": "alice"}
assert db["session:1"]["user"] == "alice"

db.flush(wait=True)  # 重要な区切りでは明示的に永続化
db.close()

通常の v2_mode=True は「書き込みを非同期化する」ための設定です。一方、memory_first=True は「CRUD の正本をメモリ側に置き、差分だけを定期的に永続化する」ための設定です。データセット全体がメモリに載る単一プロセス用途に向いています。

注意

memory_first=True は LRU / TTL / cache_size / cache_persistence_ttl と併用できません。また、プロセスが強制終了された場合は最後のフラッシュ以降の差分が失われる可能性があります。重要な区切りでは db.flush(wait=True) を呼んでください。

フラッシュモード

V2 エンジンはデータをバックグラウンドでデータベースに書き込みます。4 つのフラッシュモードから選択できます:

モード説明データ安全性パフォーマンス
immediate操作ごとに即座にフラッシュ★★★★★★★★
countN 件蓄積後にフラッシュ★★★★★★★★
time一定時間間隔でフラッシュ★★★★★★★★
manual手動フラッシュのみ★★★★★★★★

immediate(デフォルト)

最も安全なモード。操作ごとにデータベースへの書き込みが保証されます:

python
db = NanaSQLite("app.db", v2_mode=True, flush_mode="immediate")
db["key"] = "value"  # 即座にDB書き込み

count

指定件数の操作が蓄積されたらフラッシュ:

python
db = NanaSQLite(
    "app.db",
    v2_mode=True,
    flush_mode="count",
    flush_count=100,  # 100 件ごとにフラッシュ
)

time

指定間隔でフラッシュ:

python
db = NanaSQLite(
    "app.db",
    v2_mode=True,
    flush_mode="time",
    flush_interval=3.0,  # 3 秒ごとにフラッシュ
)

manual

完全に手動制御:

python
db = NanaSQLite("app.db", v2_mode=True, flush_mode="manual")

db["k1"] = "v1"
db["k2"] = "v2"
db["k3"] = "v3"

# 明示的にフラッシュ
db.flush()

manual モードの注意

flush() を呼ばずにプログラムが終了すると、バッファ内のデータが失われます。

チャンクサイズ

大量の書き込みがある場合、チャンクサイズを調整してトランザクションサイズを制御できます:

python
db = NanaSQLite(
    "app.db",
    v2_mode=True,
    v2_chunk_size=1000,  # 1000 件ずつトランザクション
)

Dead Letter Queue (DLQ)

書き込みに失敗した操作は Dead Letter Queue に隔離されます。これにより、エラーが後続の操作をブロックしません。

DLQ はデフォルトで最大 1000 件まで保持します。上限に達した場合は最古のエントリを破棄します。V2Config(max_dlq_size=...) または v2_max_dlq_size=... で調整できます。

DLQ の確認

python
# DLQ の内容を取得
dlq = db.get_dlq()
for item in dlq:
    print(f"失敗した操作: {item}")

DLQ のリトライ

python
# 失敗した操作を再試行
db.retry_dlq()

DLQ の消去

python
# DLQ のエントリをすべて消去
db.clear_dlq()

メトリクス機能 (Metrics)

V2エンジンでは、処理遅延やフラッシュ回数、DLQの発生状況などの詳細なメトリクスを収集できます。メトリクス収集はオプトイン機能です。

python
db = NanaSQLite(
    "app.db",
    v2_mode=True,
    v2_enable_metrics=True,  # メトリクス収集を有効化
)

# メトリクスの取得
metrics = db.get_v2_metrics()
print(f"Total Flushes: {metrics['total_flushes']}")
print(f"DLQ Count: {metrics['dlq_count']}")

StrictTask

Strict レーンにカスタムタスクをエンキューできます:

python
from nanasqlite.v2_engine import StrictTask

# 優先度付きタスクをエンキュー
task = StrictTask(
    priority=1,
    sequence_id=0,
    task_type="sql",
    sql="INSERT INTO logs (message) VALUES (?)",
    parameters=["Important event"],
    on_success=lambda: print("成功"),
    on_error=lambda e: print(f"失敗: {e}"),
)
db._v2_engine.enqueue_strict_task(task)

V2 コンストラクタパラメータ

パラメータデフォルト説明
v2_modeboolFalseV2 エンジンの有効化
flush_modestr"immediate"フラッシュモード
flush_intervalfloat3.0time モードの間隔(秒)
flush_countint100count モードの件数閾値
v2_chunk_sizeint1000トランザクションチャンクサイズ
v2_max_dlq_size`intNone`1000
v2_enable_metricsboolFalse詳細なメトリクス収集を有効化

非同期での V2 利用

AsyncNanaSQLite でも V2 モードが使用できます:

python
from nanasqlite import AsyncNanaSQLite

async def main():
    db = AsyncNanaSQLite(
        "app.db",
        v2_mode=True,
        flush_mode="time",
        flush_interval=5.0,
    )

    await db.aset("key", "value")
    await db.aflush()

    dlq = await db.aget_dlq()

    db.close()

V1 vs V2 比較

項目V1(デフォルト)V2
書き込み同期・ブロッキング非ブロッキング(バッファ経由)
読み取りキャッシュ → DBキャッシュ → バッファ → DB
データ安全性即時保証フラッシュモードに依存
書き込み性能ベースライン高スループット
複雑さシンプルDLQ・フラッシュ管理が必要
プロセスマルチプロセス対応シングルプロセスのみ

いつ V2 を使うべきか

V2 が適している場面:

  • 書き込みが頻繁なアプリケーション(ログ、センサーデータ、チャット)
  • 書き込みレイテンシを最小化したい場合
  • シングルプロセスアプリケーション
  • CRUD レイテンシを最優先し、全データをメモリに載せられる場合は memory_first=True

V1 を使うべき場面:

  • データの即時永続化が必要
  • マルチプロセス環境(Gunicorn ワーカーなど)
  • シンプルな CRUD アプリケーション
  • データセット全体をメモリに載せられない場合