db-defaults について
db-defaultsは、個人向けのローカルファーストなPythonプロジェクトで、用途に応じたストレージを選ぶためのスキルです。カテゴリ別の既定案や移行先、コードスニペットを示し、ローカルのpytestで実行できるテスト性も重視します。
主な機能
- 01用途別のストレージ既定案
- 02規模に応じた移行先の説明
- 03カテゴリ別のPythonコード例
- 04ローカルpytestでのテスト性を重視
- 05実行可能なストレージのモックを避ける指針
活用シーン
- 01ローカルPythonプロジェクトのキャッシュ選定
- 02プロジェクトデータ用のリレーショナルストア選定
- 03ローカル全文検索やベクトル検索の選定
- 04ローカル既定案を超える時期の判断
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ theodoread/ agent-skills --skill db-defaultsインストール後、Claude Code で/db-defaultsを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
references/rationale.mdreferences/snippets/analytical-olap.pyblob-storage.pycache.pycron-scheduler.pydocument-store.pyfull-text-search.pygraph-data.pyin-process-state.pyjob-queue.pypubsub.pyrelational-oltp.pyrelational-simple.pytime-series.pyvector-search.py詳細
ユースケース別のデフォルトストレージ技術
個人向けの、Python を優先し、ローカルを優先するプロジェクト向けです。ストレージやキャッシュが必要になったときに、代替案の分析を明示的に求められていない場合に適用します。この表から選び、セッションごとにゼロから再検討しないでください。カテゴリに本当に合わない場合や、規模の要件がデフォルトの限界を超えた場合は、別の選択肢に変えて構いません(各カテゴリの「移行先」を参照)。ここでの目的は、判断そのものを禁じることではなく、新しいセッションやモデルが理由もなく別のものを黙って選ぶのを防ぐことです。
以下のすべての項目に共通する選定基準:MIT/Apache-2.0/BSD 系の寛容なライセンスのみ。人気があり、活発にメンテナンスされていること(雰囲気ではなく、実際の GitHub/PyPI の活動を確認済み)。通常の pytest 実行だけで完全にテストできること(インメモリ、または tmp_path の範囲内で動作し、Docker/クラウドアカウント/CI サービスが不要)。コーディングエージェントが、過剰な手順や間接化なしに正しく使える、ボイラープレートの少ない API であること。選択肢は、プロジェクト内の技術数を減らすことよりも、用途ごとに最適なツールを優先しています。以下のカテゴリが求めていない統合を無理に進めないでください。
テスト可能性を活用してください。これらをモックしないでください。 テストスイートから起動・停止できるストアを選ぶ目的は、テストで実物を動かすことです。そのため、ここでモックすると、選択によって得られた利点を手放すことになります。python-conventions の「Don't double anything the suite can run for real」では、このルールとその範囲を説明しています。
セキュリティは意図的に選定基準から除外しています。 ここにあるデフォルトはすべて、ローカルでの個人規模の利用向けに選ばれています。このページの内容を、本番環境、マルチテナント、インターネットに公開する用途の推奨として扱わないでください。そのような状況では、各カテゴリの「移行先」に記載されたものを選んでください。
プロセス内の一時的な状態
- スニペット:
references/snippets/in-process-state.py - デフォルト:標準ライブラリのみ(
threading.Lock、time.monotonic、dict)。ライブラリは不要です - 理由:永続化は不要です。「ロックひとつで保護した数値ひとつを保持する」だけなら、ライブラリは単なる余計な負担になります
- 移行先:該当なし。再起動後も状態を保持する必要が生じたら、代わりにキャッシュまたはリレーショナルのカテゴリを使ってください
キャッシュ(TTL/退避)
- スニペット:
references/snippets/cache.py - デフォルト:
diskcache(Apache-2.0、純粋な Python) - 理由:ディスクベースで、TTL と退避を処理するため、自前で実装する必要がありません。
pytestのtmp_pathフィクスチャも簡単に使えます - 移行先:Redis。複数のプロセスやマシンで、ひとつのキャッシュを共有する必要が生じた場合に使ってください
リレーショナル — シンプル(テーブル数が少なく、KV 型)
- スニペット:
references/snippets/relational-simple.py - デフォルト:標準ライブラリの
sqlite3、生の SQL - 理由:依存関係が不要で、この程度の規模なら ORM の手続きも不要です。
pytestでは:memory:またはtmp_pathを使えます - 移行先:Postgres
リレーショナル — 複雑な構成/OLTP(多数のテーブル、実際の結合、マイグレーション、頻繁な書き込み)
- スニペット:
references/snippets/relational-oltp.py - デフォルト:
sqlalchemy+alembic(どちらもMIT) - 理由:実際のEngine/Session/宣言的モデルに伴う手間がかかりますが、真にトランザクション性が求められる複数テーブル構成において、もっと軽量なものでは得られないマイグレーションとリレーションシップの安全性を確保できます。
- 列の語彙は一度だけ定義してください。通常の型には宣言的ベースの
type_annotation_mapを使い、列ごとの引数が必要な型にはAnnotatedエイリアスを使います。こうすれば、すべてのモデルにある各Mapped[Decimal]が同じように解決されるため、別のファイルで記憶を頼りに記述した結果、一部の列だけ定義から外れることがありません。これは「なぜCoreではなくORMなのか」という問いへの率直な答えでもあります。Coreでも同じSQLを得られますが、列ごとに語彙を明示する必要があります。 - 次の段階:Postgres
- SQLite層では
Decimalがひそかに破損し、tzinfoが失われます。どちらも移行先のPostgresでは表面化しません。これは特に重要です。というのも、この項目に沿うすべてのプロジェクトでSQLiteを使う理由は、「通常のpytest実行だけで完全にテストできる」ことだからです。SQLAlchemy 2.0.52での測定結果:Numericは警告なしにDecimalをfloat経由で往復させます。1234567890123456789.000000001は1234567890123456768.0000000000になって返ってきます。仕組みはライブラリ内にあります。engine/default.pyはsupports_native_decimal = Falseを設定しており、SQLite方言はこれを上書きしません。そのためsqltypes.pyは「DBAPI returns floats, convert(DBAPIはfloatを返すので変換する)」とコメントされた分岐を選び、スケール10に量子化します。正確な文字列を保存するTypeDecoratorが解決策です。- タイムゾーン対応のdatetimeは、タイムゾーン情報を失って返ってきます。これはSQLiteの制限ではありません。
dialects/sqlite/base.pyのDATETIME.bind_processorは、tzinfoをまったく読み取りません。 - 落とし穴は、この問題が見えにくいことです。
Decimalのケースを最初に調べたとき、10桁の値ではテストに通ってしまいます。floatで正確に表せる値だからです。そのため、現実的な金額を使ったフィクスチャを用いるプロジェクトは、Numericで問題ないと判断してリリースしてしまいます。失敗するほど桁数の大きい値で調べてください。
__table_args__にsqlite_strict=Trueを渡してください。テスト用方言が、その代替として使うPostgresより寛容にならないようにするためです。テスト専用方言を使うと招きやすい失敗パターンです。これはそのまま追加できるキーワード引数ではありません。スニペットに全体の形が示されています。STRICTが受け入れるのはINT/INTEGER/REAL/TEXT/BLOB/ANYのみなので、create_allはNUMERIC(38, 10)、DATETIME、および通常のString(64)が出力するVARCHAR(64)でただちに失敗します(いずれも2.0.52で測定済み)。String(64).with_variant(Text(), "sqlite")を使えば、Postgresでは本来の長さ制限が維持されます。また、前述のDecimal/datetime用TypeDecoratorによって、残る2列も使用可能になります。つまり、STRICTの採用と2つの落とし穴への対処は一体の作業です。どちらも行わないのではなく、両方を一度に行うべき理由がここにあります。- AlembicのSQLiteバッチモードには、データ整合性を損なう危険があります。 SQLiteでは、
batch_alter_tableはテーブルを再作成することで変更を実装するため、そのテーブルのトリガーが削除されます。また、Alembicにはどこにもトリガーを認識する仕組みがありません(パッケージ全体から「trigger」を検索しても、無関係なコメントが1つ見つかるだけです)。さらに、batch.py自身のTODOにあるとおり、リフレクションされ、かつ名前が付いていないCheckConstraintはスキップされます。対策は2つあり、どちらでも有効です。すべての制約に名前を付けるか、SQLite層では一切マイグレーションせず、常に現在のメタデータから作成する方針にします。
分析/OLAP(構造化データに対する読み取り/集計中心のクエリ)
- スニペット:
references/snippets/analytical-olap.py - デフォルト:
duckdb(MIT — MIT ライセンスを「永久に」維持することを目的とした非営利財団が知的財産を保有) - 理由:
duckdb.connect(":memory:")を使うと、単純な接続でネイティブ SQL を利用でき、ORM に伴う煩雑な設定が一切ありません。この用途では SQLAlchemy よりも記述量が大幅に少なく、結合や集計も実際に高速です。書き込みは単一ライターのみ(個人規模のローカル利用なら問題ありません)。複数ライター対応の「Quack」プロトコルは 2026 年 5 月にリリースされましたが、まだ新しいものです - 移行先:Postgres+DuckDB 拡張機能、または MotherDuck
ドキュメントストア(スキーマレス/半構造化 JSON)
- スニペット:
references/snippets/document-store.py - デフォルト:
tinydb(MIT、純粋な Python 製) - 理由:実際の辞書風 API(
db.insert({...})、db.search(Query().field == x))を備えており、SQL は一切不要です。pytest ではMemoryStorage()またはtmp_pathを使用できます。メンテナーは「メンテナンスモード」(機能がそろっており安定している状態)と呼んでいますが、放棄されたわけではなく、現在もバグ修正リリースが行われています - 代替:フィールドクエリが不要で、キー→オブジェクトの純粋なストレージが必要なら
sqlitedict(Apache-2.0)。利用を検討する前に、数年にわたるコミットの空白期間がある点に注意してください - 移行先:MongoDB
全文検索
- スニペット:
references/snippets/full-text-search.py - デフォルト:SQLite の
FTS5仮想テーブル — 標準ライブラリのsqlite3に同梱されており、新たな依存関係は不要です。組み込みのbm25()ランキング機能を利用できます - 代替:データストアから切り離した、単独で使える RAG 形式のスコアラーには
bm25s(MIT)。FTS5 の機能では本当に不十分な場合に、より本格的な Lucene 風検索を行うにはtantivy(MIT、Rust 製検索エンジンtantivyの Python バインディング。tantivy-pyは別の古いパッケージなので、pip install tantivyを使用してください) - 避けるもの:
Whoosh— 長期間メンテナンスされていません - 移行先:Elasticsearch または Meilisearch(どちらも稼働中のサーバーが必要なため、ローカルのデフォルトからは除外しています)
ベクトル/埋め込み類似検索
- スニペット:
references/snippets/vector-search.py - デフォルト:
qdrant-client(Apache-2.0) - 理由:
QdrantClient(":memory:")またはQdrantClient(path=tmp_path)ならサーバーは一切不要で、後から移行する際もコンストラクターの引数をそのまま差し替えるだけです(url=.../クラウド認証情報)。同じクラス、同じメソッドを使用できることは、Qdrant 公式ドキュメントで確認済みです。ベクトル、メタデータ、永続化を 1 つの API で扱えます - 代替:
chromadb(Apache-2.0)— コミュニティや認知度ははるかに高く、初めて使う際の API もよりシンプルといえます(add()/query())。導入のしやすさが、書き換えなしで移行できることの確認済みの道筋より重要なら、妥当な選択肢です - 移行先:Qdrant Cloud(クライアントコードは変更不要)、または Pinecone/Weaviate
バックグラウンドジョブ/メッセージキュー
- スニペット:
references/snippets/job-queue.py - デフォルト:
huey(MIT) - 理由:
SqliteHuey(..., immediate=True)を使うと、@huey.task()関数をプロセス内で同期実行できます。コンシューマーのサブプロセスもオーケストレーションも不要で、単純な pytest テストから関数を直接呼び出せます。小規模なデコレーターベースの API、15 年にわたる実績、オープンな問題のバックログがほぼゼロという特徴があります - 移行先:Celery/RQ/Dramatiq — いずれも実際のブローカー(Redis/RabbitMQ)が必要です。複数のワーカープロセスやマシンが必要になったときこそ、ブローカーを導入する理由になります
Cron/スケジュールされた定期タスク
- スニペット:
references/snippets/cron-scheduler.py - デフォルト:
apscheduler3.x(MIT) - 理由:
sqlite:///URL を指定したSQLAlchemyJobStoreは再起動後もスケジュールを保持でき、外部ブローカーは不要です。スケジューリングがまったく必要ない場合でも、キューとしてそのまま使えます。把握しておくべき実質的なバス係数リスクはありますが、それだけで避ける理由にはなりません。単独の圧倒的なメンテナー(1,134 コミットに対し、次点のコントリビューターは 6 コミット)、約 2 年間レビューされていない PR、そして v4 は 2020 年からアルファ版のままで安定版リリースがありません。そのため、3.x が依然として唯一の現実的な選択肢であり、現在も積極的にパッチが適用されています(2026 年 6 月にもリリースあり)。 - 代替:
schedule(MIT)— 依存関係ゼロで、プロセス内 API も非常にシンプルですが、永続化機能はありません(再起動するとすべてのスケジュールが失われます)。また、2024 年 5 月以降更新されていません。プロセス内だけで完結するケース向けの軽量な代替手段であり、ガバナンス上の理由による置き換えではありません。 - 次の段階:定期ジョブを 1 台のマシンを超えて存続させる必要が生じたら、マネージドスケジューラー(例:クラウド cron)に移行します。
Pub/sub/イベントストリーミング
- スニペット:
references/snippets/pubsub.py - デフォルト:
blinker(MIT)— プロセス内のシグナル/オブザーバーのディスパッチ(signal.connect(receiver)、signal.send(sender)) - 理由:Flask 自身がシグナルに使用しているライブラリです。設定の手間がなく、プロセス内で複数のサブスクライバーに配信でき、サーバーも不要で、テストも非常に簡単です。
- 次の段階:NATS(Apache-2.0)— これは同じツールの大規模版ではなく、まったく異なるツールです。別途プロビジョニングした
nats-serverプロセスが必要で、ローカルテスト用の公式またはメンテナンスされている Python の pytest フィクスチャパッケージもありません(コミュニティによる 2 つの試みはいずれも放棄されています)。そのため、実際にサーバーを運用する段階では優れた製品ですが、ここでのローカル向けデフォルトには該当しません。
グラフデータ
- スニペット:
references/snippets/graph-data.py - デフォルト:
ladybug(MIT、pip install ladybug)— 組み込み型で、Cypher クエリ言語を使用し、サーバーは不要です。 - 理由:これは Kuzu の積極的にメンテナンスされている後継で、Kuzu が 2025 年 10 月にアーカイブされた(Apple に買収された)3 日後にコミュニティがフォークしました。同じ基盤エンジンを新しい体制で引き継いだものであり、書き直しではありません。日々コミットがあり、MIT ライセンスで、活動中のメンテナーも明示されています。プロジェクト名としてはまだ 1 年未満なので、たとえば SQLite のように実戦で十分に検証されたものではなく、有望ながらもまだ新しいものとして扱ってください。
- 軽量な代替:実際のグラフトラバーサル性能やクエリ言語を必要としない関係性のモデリングなら、リレーショナルストア内の単純な
edges(src, dst, relation)テーブルのほうが依然として簡単です。実際のグラフアルゴリズムや、おもちゃの規模を超えるトラバーサルが必要な場合に、特にladybugを選んでください。どちらの場合も、すでに読み込まれたデータに対するメモリ内グラフアルゴリズムにはnetworkx(BSD-3-Clause)を組み合わせられます。 - 次の段階:Neo4j Aura、Amazon Neptune
Blob/オブジェクトストレージ
- スニペット:
references/snippets/blob-storage.py - デフォルト:
pathlib.Pathによる通常のファイル書き込み — ライブラリ不要 - 理由:依存関係がなく、
tmp_pathによるテストも非常に簡単です。具体的な計画がまだないクラウドストレージ向けに抽象化を追加するのは、まさに YAGNI が当てはまるケースです。 - 次の段階:クラウドストレージが「使うかもしれない」ではなく実際の計画になったら、
fsspec(BSD-3-Clause)とs3fs/gcsfsに移行します。同じfs.open()/fs.ls()呼び出しを使い、プロトコル文字列だけを変更します。
時系列データ
- スニペット:
references/snippets/time-series.py - デフォルト:
duckdb(MIT)— 上記の分析/OLAP カテゴリと同じツールです - 理由:DuckDB のウィンドウ関数、
time_bucket、欠損区間の補完、ASOF 結合は、時系列分析において実際に非常に優れており、同じクエリを生の SQL で手作業で作成する場合と比べて、定型コードを大幅に削減できます。この用途に特化した場合、プロジェクトで OLAP にも使うかどうかにかかわらず、より優れたツールです - 軽量な代替手段:必要なのが「タイムスタンプ付きの行を保存し、範囲で絞り込む」だけで、リサンプリングやウィンドウ処理が不要であれば、タイムスタンプをインデックスにした単純な SQLite テーブル
- より大規模な選択肢:InfluxDB、TimescaleDB
このスキルの編集
このファイルはインストール時に ~/.agents/skills/db-defaults に コピー され、シンボリックリンクにはなりません。そのため、デプロイ済みのコピーを編集すると、ローカルで差異が生じます。これはまさにこのスキルが防ぐためのものであり、ほかのマシンには反映されません。インストール元のリポジトリにあるソースを編集してプッシュし、skills add <that source> --global --skill db-defaults を再実行すると、すべてのプロジェクトのコピーが更新されます。自分のフォークではなく、ほかの人のリポジトリからインストールした場合、ソースはその人のものです。その場合は、そちらでイシューを作成するか、プルリクエストを送ってください。
スタータースニペット
references/snippets/ には、上記の各カテゴリにつき、ruff に準拠した自己完結型の実用的な Python ファイルがあります。各ファイルの docstring には検証済みの pip install コマンドが記載され、test_* 関数では pytest-local パターンの実行例を示しています。チュートリアルを応用するのではなく、そのままコピーして貼り付けられます。上記の各カテゴリにある「スニペット:」の行から、それぞれのファイルに直接アクセスできるため、ほかのファイルを開く必要はありません。また、作成中に見つかった実際の命名上の落とし穴も、該当箇所に記録しています。full-text-search.py(tantivy と tantivy-py の PyPI 上の取り違え)と cron-scheduler.py(存在しない SQLiteJobStore というクラス名)です。
詳細な根拠
references/rationale.md を参照してください。GitHub/PyPI の根拠、カテゴリごとに検討して却下した選択肢、そして上記のすべての分岐/代替手段の理由が記載されています。