データベース GUI が既定で書き込みをステージすべき理由
どんなデータベース GUI にも、クリックが本番環境への書き込みに変わる瞬間があります。DynoTable では、その書き込みはまずステージングエリアを通ります。例外は 3 つあり、後述します。
アイテムの編集、コンテキストメニューからの行の削除、PartiQL の INSERT、UPDATE、
DELETE、そして AI エージェントによる変更は、いずれも属性ごとの差分としてそこに入ります。その後、コミットがステージされた集合を DynamoDB のトランザクションとして送ります。
モデルは git です。ステージはレビューする場所で、コミットは git push --force-with-lease のように振る舞います。リモートが、あなたが最後に見たときのままである場合にのみ成功します。
すべてのアイテム編集は DynamoDB に届く前にステージに入る
ステージされた編集は、サイドパネルに差分カードとして表示され、各属性の古い値と新しい値が示されます。グリッドでも行に色が付くので、ステージされた状態はデータそのものから見て取れます。

レビューは任意で、既定値は速さを優先しています。エディタの既定の「保存」操作は、1 回の操作でステージとコミットを行います。代わりに ⌘S を押すと、編集はレビューのためにステージされたまま残ります。
コンテキストメニューから 1 行を削除すると、ピッカーが開きます。初期状態では「削除を Commit」が事前に選択されており、これも 1 回の操作でステージとコミットを行います。「削除をステージ」を選ぶと、削除はレビューのために残ります。
どちらの保存経路も同じコミットパイプラインを通るため、1 回の操作での保存も、レビューを経た保存と同じ競合チェックを受けます。
ステージを経由しない書き込みが 3 つあります。1 つ目は選択した行の一括削除です。「N 件の削除を Commit」を選ぶか、⌘⇧⌫ を押すと、条件なしで書き込みます。2 つ目はインポートで、独自の経路で書き込みます。
3 つ目は直接実行で、クエリに一致するすべてのアイテムを選択した場合にのみ提示されます。その 2 つのオプション、編集を直接書き込む と 直接削除 は、各アイテムを読み取りながら書き込みます。ステージはなく、アイテム数の上限もありません。
最近コミットされた変更は、一括削除も含めてステージングパネルの「履歴」タブに記録され、そこから元に戻せます。インポートと直接実行は記録されないため、どちらも元に戻せません。
エージェントにはそのような近道はありません。その書き込みツールである stageItem と bulkEdit(1 回の呼び出しで最大 10,000 アイテム)は、ステージするだけです。直接実行を開始できるエージェントツールはありません。
コミットは常に人間が行います。日々のワークフローはステージングのドキュメントで扱っています。
ステージは DynamoDB の型付きの値をそのまま保つ
ステージされた値は、{N: "42"} や {S: "42"} のような DynamoDB 独自の型付きの形式を保ちます。扱いやすい unmarshal 済みの JSON ではなくこちらを選んだのは、ステージに欠かせない 2 つの性質のためです。
差分は型を知っている必要があります。{N: "1"} と {S: "1"} は異なる値であり、unmarshal 済みの値から作ったアイテムのキーのハッシュでは、この 2 つが衝突してしまいます。
ラウンドトリップもロスレスでなければなりません。SDK の unmarshal 済みの数値ラッパーは、アプリのローカルストアへの JSON シリアライズと復元に耐えられず、その結果ユーザー入力との深い等価判定が壊れます。
コミットが低レベルクライアントを通じて DynamoDB とやり取りするのも同じ理由です。条件がチェックする値は、あなたがレビューした値そのものです。
コミットは変更した属性に対する --force-with-lease である
レビューした内容と書き込まれる内容が違うなら、レビュー UI に価値はありません。ステージしてからコミットするまでのあいだに、他の誰かがその行を変更しているかもしれません。
そこでコミットは、各書き込みをステージした時点のスナップショットに固定する条件式を付けます。これは属性ごとに適用される楽観的ロックです。
- 作成 は、そのアイテムがまだ存在しないことをアサートします。
- 更新 は、変更しようとしているすべての属性が、あなたが見た値をまだ保持していること、あるいはそのとき存在しなかった属性がまだ存在しないことをアサートします。
- 属性の削除 は、その属性がまだ古い値と等しいことをアサートします。あなたの属性の削除がステージされているあいだに、チームメイトが
noteを"old"から"new"に変更していた場合、コミットがその編集を黙って削除してはなりません。
通常のアイテムの削除は弱点です。その唯一の条件はアイテムがまだ存在することなので、削除をステージした後に誰かがアイテムを編集していても、コミットされます。
ステージされた削除は、コミット時点でアイテムが保持している内容をすべて削除するものとして扱ってください。差分カード上のスナップショットは、ステージした時点での内容を示します。
履歴が最後の砦です。「元に戻す」は逆の変更をレビューのためにステージするので、削除されたアイテムは、削除をステージした時点の状態で戻ってきます。チームメイトによるその後の編集は、そのスナップショットには含まれていません。
最初に失敗したチャンクでコミットは停止する
コミットは、DynamoDB のトランザクションの上限に合わせたサイズのチャンクでステージを送ります。トランザクションあたり 100 アイテムに加え、操作ごと・リクエストごとのバイト予算があります。チャンクは 1 つずつ送られ、それぞれに 30 秒のタイムアウトがあります。
コミットの実行中、アプリはそのテーブルのコミットロックを保持します。コミットが終わるまで、UI からそのテーブルにステージしたり、そこから破棄したりすることはできません。
条件が失敗すると、DynamoDB はトランザクション全体をキャンセルし、どのアイテムがチェックに失敗したかを報告します。その行には、どちらの方向であれ黙って上書きするのではなく、競合バナーが表示されます。
- ドリフト。 アイテムがリモートで変更されました。「リモートにリベース」はスナップショットを現在のアイテムで更新するので、変更をもう一度レビューできます。カード上の「破棄」で変更を取り下げます。
- リモートのアイテムが削除された。 リベースする対象がないため、「破棄」するしかありません。
- ネットワークまたはサービスの障害。 「再試行」でその 1 件の変更を再送し、「破棄」で取り下げます。
いずれかのチャンクが失敗すると、コミットは停止します。成功したチャンクはコミットされたままで、失敗したチャンク以降は何も試みられません。
キャンセルされたトランザクションは何も書き込みません。タイムアウトはそれほど確実ではありません。待つのをやめた後にチャンクが反映されている可能性があるため、その行は再試行するか破棄するまで不明としてマークされます。
ベストエフォートでの続行は意図的に退けました。競合を越えて書き込みを続けるレビューツールは、誰もレビューしていない変更セットを適用していることになります。
ステージをタブにキー付けしたせいで、エージェントの編集が間違ったタブに送られた
最初のバージョンでは、各ステージをそれを作成したタブにスコープしていました。これが実際のバグを生みました。エージェントのステージングツールは対象をアクティブなタブから選んでおり、アクティブなタブがテーブルビューでない場合、テーブルの不一致チェックが実行されませんでした。
そのため、SQL Workbench のタブにフォーカスがある状態でアシスタントに行の修正を頼むと、その編集は Workbench のステージに入りました。そして、ステージされた変更を表示するチップは間違ったタブを開きました。
コミットロックもタブごとでした。1 つのステージを複数のタブで共有するなら、ロックもそれと一緒に移す必要がありました。そうしなければ、同じテーブルを開いた 2 つのタブが同じ行を同時にコミットできてしまいます。
修正は、すべてをテーブルでキー付けすることでした。接続プロファイル、リージョン、テーブル名です。タブはもうキーの一部ではありません。
- テーブルごとに 1 つのステージ。そのテーブルのあらゆるビューから見えます。タブを閉じて開き直しても残り、エージェントはテーブルのタブが開いていなくてもステージできます。
- テーブルごとに 1 つのコミットロック。テーブルの 2 つのビューが同時にコミットすることはできません。
- エージェントの編集は、どのタブにフォーカスがあっても、そのテーブルのステージに入ります。
キー付けをやり直したことで、コードも減りました。以前は起動時のスイープが、閉じたタブのステージを片付けていました。ステージがタブより長く生き残る新しいモデルでは、それが機能を破壊するものだと計画のレビュアーが指摘し、私たちはそれを削除しました。
タブを閉じたときの破棄と、その確認ダイアログもなくなりました。現在、ステージが孤立する唯一の原因は接続プロファイルの削除であり、プロファイルを削除するとそのステージは明示的にクリアされます。
既存のステージは新しいキーへ移す必要がありました。これはアプリで初めて行を書き換えるマイグレーションでした。同じアイテムのタブごとのコピーは最新の編集にまとめられ、このマイグレーションには専用の seed してからマイグレートするテストが用意されました。
ロックを誰が解放するかを決めるコードを削除した
コミットは、それを開始したウィンドウより長く続くことがあります。アプリは実行中のすべてのコミットを追跡しているので、リロードしたウィンドウは再アタッチして、その完了を見届けられます。
時が経つにつれ、コミットを開始または再開する 3 つのコード経路のうち 2 つが、1 つのコミットに 2 つのオブザーバーをアタッチするようになりました。そして 1 つの問いを巡って調停の仕組みが育ちました。どのオブザーバーがコミットロックを解放してよいのか、という問いです。
それには順序の競合状態を説明する長いコメントと、進捗通知のための別のトラッカーが付いていました。
修正では、各コミットにちょうど 1 つの所有者を与えました。所有者は 1 回だけアタッチし、ロックを単独で保持し、進捗を報告します。コミットを開始した側は必ず応答を受け取り、呼び出しがハングすることはありません。
調停のコードは丸ごと削除し、進捗の報告は新しい所有者に移しました。
ステージングパネルのメモリベンチマークを新しいフローに合わせて書き直したところ、ヒープの中央値は約 3 分の 1 低くなりました。新旧のシナリオは異なるので、この数値はおおまかな目安として扱ってください。
単一の所有権から 2 つの振る舞いが導かれます。どちらも、本番環境に書き込むツールにとって最低限の条件だと私たちは考えています。1 つ目は、実行中のコミットがウィンドウのリロード後も続くことです。リロードしたウィンドウが再アタッチし、最後まで見届けます。
2 つ目は、コミットの途中でライセンスが読み取り専用に切り替わっても続くことです。新しいコミットはブロックされますが、すでに実行中の書き込みは最後まで見届けられ、決して放棄されません。
エージェントにはコミットツールがない
ステージングは私たちの AI 機能より前から存在します。エージェントの書き込みツールはステージするだけで、
stageItem の説明はモデルに一字一句次のように伝えます。
The user reviews + commits from the staging panel — this tool never writes to DynamoDB directly.コミットする、あるいは直接実行を開始するエージェントツールは存在しないため、権限でゲートしたり監査したりすべき、ステージを経由しない書き込み経路もありません。
MCP サーバーは、同意のスコープでも同じ線を引いています。中間のスコープは「読み取りとステージ」という名前で、最上位のスコープの説明でさえ「書き込みは引き続きステージを経由します。」で終わります。
どちらのスコープでも、プロンプトインジェクションを受けたエージェントはゴミのような変更を提案できますが、それを DynamoDB にコミットできるエージェントツールはありません。変更は、人間がステージからコミットするか、エージェントが入力したエディタのバッファを保存するまで待機します。
コミット時のロックはエージェントにも適用されます。エージェントが古いスナップショットに対してステージした更新や属性の削除は、より新しい人間の編集を上書きするのではなく、競合として表面化します。
エージェントがステージした通常の削除がチェックするのは、あなたの削除と同じく、アイテムが存在することだけです。
ステージングはエージェントの書き込みを守ります。エージェントのクエリを信頼できるものにするでは、その読み取りを扱っています。
データベースに書き込むあらゆるツールに求めるべきこと
- 意図と書き込みのあいだに、属性ごとの差分を示すレビューのステップがあること。
- 作成、更新、属性の削除について、レビューしたスナップショットに対する楽観的ロックがあること。削除が何をチェックするかを確認してください。ステージされた削除はアイテムが存在することだけをチェックし、 1 回の操作での一括削除は何もチェックしません。
- 一括削除も含めたコミット済みの変更の履歴があり、元に戻す操作がレビューを経由すること。
- 競合を越えて書き込むのではなく、最初の失敗で停止するアトミックなバッチであること。
- ウィンドウのリロード後もコミットが続き、どこまで進んだかを報告すること。
- AI エージェントについては、ステージングが唯一の書き込み経路であり、コミットツールがないこと。
ステージングのドキュメントはワークフローを示し、 DynamoDB データの編集はそれが守る基本を扱っています。あるいは DynoTable をダウンロードし、⌘S でいくつか編集をステージして、一括変更を実行前に読んでみてください。


