FEATUREわかったつもりの、その先へ
知る・学ぶ

保存の途中で失敗したら?自作CMSで学ぶトランザクション

公開版の更新と編集下書きの削除が別々に確定すると、途中状態が残ります。自作CMSのpublishDraftを読み、同じ構造の小さなSQL実験で、成功とロールバックを比較しました。

CONTENTSこの記事の目次4項目

自作CMSでは、公開済みの記事を直す際に編集用の下書きを残せます。修正を反映するときは、公開版を更新し、不要になった編集下書きを消します。もし更新の直後に失敗したら、片方だけ変わってしまうのでしょうか。現行のpublishDraftを読んだうえで、同じ2段階を小さなSQL実験にして確かめました。

二つの変更を、一つのまとまりとして扱う

トランザクションは、複数のデータ変更をひとまとまりに扱う仕組みです。成功時はCOMMITで確定し、途中で問題があればROLLBACKでそのまとまりの変更を取り消します。PostgreSQLの公式チュートリアルでも、複数の手順を全部行うか、全部行わないかという性質が説明されています。 今回の対象は、公開版と、その編集下書きです。「公開版だけ新しくなり、古い下書きが残る」「下書きだけなくなり、公開版は古い」といった状態を避けたい変更の組です。

図の失敗は、後述する最小SQL実験の結果です。実際の本番公開操作へ障害を入れたものではありません。

更新の直後に、意図的なエラーを起こす

隔離したPostgreSQLで一時テーブルを2つ作り、公開版にold、下書きにnewを入れました。BEGINの後、公開版をnewへ更新し、その直後にゼロ除算を発生させました。次の削除も確定できず、トランザクションは取り消されました。 比較として、同じ初期状態からエラーなしで更新と削除を行い、COMMITした場合も確認しました。

最小SQL実験の確定後の状態
条件公開版の値下書き件数
初期状態old1
更新直後にエラーold1
更新・削除とも成功new0

更新文が一度実行できたことと、最終的に保存が確定したことは違います。失敗した実験では、途中でnewに更新しても、最後に確認するとoldのままでした。下書きも残り、どちらか一方だけを確定しない結果になりました。

実際のCMSでは、どこをまとめているか

publishDraftには@Transactionalが付いています。下書きから公開用の内容を作り、入力やslugの重複を確認したうえで、公開版へ適用し、保存と下書き削除を行う構成です。これらのDB操作を同じトランザクション境界に置いています。 今回実行したのは、この構造を単純化したSQLです。実サービスのSpringやJPAを通して途中障害を注入した試験ではありません。実コードの境界の確認と、PostgreSQLのロールバックの実測を分けて示しています。

トランザクションにも、届く範囲がある

DBの変更をまとめても、別のサービスへ送った通知や、すでに利用者へ見せた画面まで自動的に元へ戻るわけではありません。また、通信が切れて成功応答を受け取れなかったことと、DBで確定しなかったことも同じではありません。 処理を読むときは「何を一緒に確定したいか」「その境界はどこか」「失敗後に何が残るか」を順に確認します。自作CMSの例では、公開版と編集下書きの整合がその対象です。記事作成の検証として本番の公開操作は行っていません。

READ MORE自作CMSの設計記録を見るこの記事で取り上げた公開ページを開きます。

確認日:2026年9月22日。画面・実装・検証結果は確認時点のものです。