継続的インテグレーション/継続的デリバリの流れにPL/SQLのコードデプロイを組み込むなら、Edition-Based Redefinition(EBR)の採用が最短距離です。EBRは“Edition”という論理的な実行空間を並行稼働させ、既存セッションを止めずに新旧実装を切り替えます。この記事では、Editionの基本、Editioning Viewとクロスエディショントリガ、無停止スキーマ進化の手順、SQLclやLiquibaseなどを使ったパイプライン連携、ロールバック戦略までを一気に整理します。
Editionの前提と準備
Editionは同一スキーマ内に新旧のPL/SQLやビューを共存させるための仕組みです。まずデータベースとユーザーでEditionを有効化し、初期Editionを決めます。新規Editionは親Editionを継承して作成し、そのEditionを“有効化したセッション”でコンパイルされたオブジェクトは新Edition側の定義として保持されます。
-- データベースでEditionを有効化済みであることが前提(多くは既定有効)
-- ユーザーをEdition対応にする
ALTER USER app_user ENABLE EDITIONS;
-- 初期Editionの確認
SELECT property_value AS default_edition FROM database_properties WHERE property_name='DEFAULT_EDITION';
-- 新Editionの作成と切り替え
CREATE EDITION v2 UNDER v1;
ALTER SESSION SET EDITION = v2;
CIのビルドステージでは上記のEdition作成とセッション切り替えを最初に行い、その後でパッケージやビューを新Editionへコンパイルします。
Editioning Viewでスキーマ進化を吸収する
テーブルは非エディション化のため、列追加や型変更を“そのまま”上位へ見せると互換が破れます。そこでEditioning View(EV)を公開インターフェースにし、下層テーブルの進化をEVで吸収します。アプリやPL/SQLは常にEVを参照すれば、列の追加・名前変更・意味変更を段階導入できます。
-- 実表(非エディション化)
CREATE TABLE t_order(
id NUMBER PRIMARY KEY,
amount NUMBER(12,2) NOT NULL,
created_at TIMESTAMP NOT NULL
);
-- 初期のEditioning View
CREATE OR REPLACE EDITIONING VIEW v_order AS
SELECT id, amount, created_at FROM t_order;
-- v2で列を増やす場合(実表拡張→EVで公開)
ALTER TABLE t_order ADD (currency CHAR(3) DEFAULT 'JPY' NOT NULL);
ALTER SESSION SET EDITION = v2;
CREATE OR REPLACE EDITIONING VIEW v_order AS
SELECT id, amount, created_at, currency FROM t_order;
この方式なら、旧Editionのコードは旧EVの列セットを見続け、新Editionのみが新列を参照します。アプリの段階移行が容易になります。
クロスエディショントリガでデータ整合を維持する
列の分割や正規化など“意味変更”が伴うときは、旧インターフェースでのDMLを新スキーマへ橋渡しする必要があります。クロスエディショントリガ(CET)は、旧EditionのDMLをフックして新レイアウトへ同期するための仕掛けです。
-- 例:v1ではamountに手数料込み、v2ではamountとfeeに分解
ALTER TABLE t_order ADD (fee NUMBER(12,2) DEFAULT 0 NOT NULL);
-- v1でのUPDATEをv2の分割列へ反映(v1上で定義)
CREATE OR REPLACE TRIGGER cet_t_order
FOR UPDATE OF amount ON t_order
CROSS EDITION
BEGIN
-- 旧: amount = 税込。新: amount=税抜、fee=手数料
:NEW.fee := ROUND(:NEW.amount * 0.03, 2);
:NEW.amount := :NEW.amount - :NEW.fee;
END;
/
切替期間中は旧APIからの更新も新レイアウトへ整合的に反映でき、カットオーバー後にCETを廃止できます。
PL/SQLの再コンパイルとウォームアップ
Edition切替直後の初回実行でJIT/ネイティブコードや実行計画のキャッシュが冷えています。CIのデプロイ末尾に“スモーク呼び出し”を入れ、主要パッケージを一度ずつ実行してウォームアップします。無関係のINVALIDを残さないため、UTL_RECOMPでEdition内の再コンパイルも実施します。
-- 新Editionへ切替後に主要APIを軽く起動
ALTER SESSION SET EDITION = v2;
BEGIN
UTL_RECOMP.RECOMP_SERIAL(schema => USER);
-- スモーク:主要関数を空振りで1回
DECLARE v NUMBER; BEGIN SELECT COUNT(*) INTO v FROM v_order; END;
END;
/
CI/CDパイプラインへの組み込み
ビルドのアウトプットは順序保証されたスクリプト群にします。Edition作成、EV更新、CET導入、パッケージSPEC→BODYの順で適用し、最後に統計更新とウォームアップを入れます。SQLclはCIサーバから非対話で実行でき、Liquibaseは依存順をchangesetで管理できます。
:: SQLcl 例(シンプルなランナー)
sql -cloudconfig config.json app_user@db <<SQL
WHENEVER SQLERROR EXIT SQL.SQLCODE
ALTER USER app_user ENABLE EDITIONS;
DECLARE v_exists NUMBER; BEGIN
SELECT COUNT(*) INTO v_exists FROM dba_editions WHERE edition_name='V2';
IF v_exists = 0 THEN EXECUTE IMMEDIATE 'CREATE EDITION v2 UNDER v1'; END IF;
END;
/
ALTER SESSION SET EDITION = v2;
@01_tables.sql
@02_editioning_views.sql
@03_cross_edition_triggers.sql
@10_pkg_spec.sql
@11_pkg_body.sql
BEGIN UTL_RECOMP.RECOMP_SERIAL(schema=>USER); END;
/
SQL
Liquibaseの場合はpreconditionでEditionの存在を確認し、changesetで適用順を明示します。ロールバック用に“逆操作”のchangesetもペアで用意しておくと事故復旧が迅速になります。
無停止切替の実行順序(青/緑パターン)
まず実表を後方互換で拡張し、EVで旧APIと両立させます。必要ならCETを追加します。新Editionに新ロジックをデプロイし、内部のスモークテストで健全性を確認します。APサーバ側の接続プールを新Editionへ順次切り替え、移行率が100%になったらCETと旧EVを撤去します。プールを段階的に切り替える運用なら、セッションは自然に新ロジックへ流れ、既存セッションは旧ロジックで完走できます。
権限・シノニム・接続の扱い
EXECUTEやSELECTなどのオブジェクト権限はEditionに依らずオブジェクト単位で管理されます。公開インターフェースはEditioning Viewや薄いパッケージに固定し、外部からの参照名を安定させます。接続時に使用Editionを固定したい場合はログイン・トリガまたはサービス毎のALTER SYSTEM SET DEFAULT_EDITIONで制御します。
-- ログイン時にEditionを付与(最小例)
CREATE OR REPLACE TRIGGER set_edition_on_login
AFTER LOGON ON DATABASE
BEGIN
IF SYS_CONTEXT('USERENV','SESSION_USER') = 'APP_USER' THEN
EXECUTE IMMEDIATE 'ALTER SESSION SET EDITION = v2';
END IF;
END;
/
ロールバック戦略とフェイルバック
デプロイ後に問題が判明した場合は、APサーバのEdition指定を旧Editionへ戻すだけで瞬時にフェイルバックできます。データレイアウトの破壊的変更を伴う場合は、CETで双方向整合を維持した期間内に限り安全に巻き戻せます。撤去前に必ず双方向の整合が壊れていないことを確認し、切替点の監査ログや相関IDを採取しておくと調査が速くなります。
品質ゲートとメトリクス
パイプラインの品質ゲートには、INVALIDの有無、依存整合(ALL_DEPENDENCIES)の破損チェック、主要APIのスモーク成功、AWRスナップショット前後の待機増悪の検知を入れます。Edition切替の直後は、DBMS_APPLICATION_INFOとSET_IDENTIFIERで相関IDを載せ、特定リクエストの遅延や例外を即座にトレースします。
よくある落とし穴と回避
テーブル直参照でアプリを作るとEVの恩恵が得られないため、必ずEVか安定ビュー経由に統一します。パッケージ仕様(SPEC)の過度な変更は依存の再コンパイル連鎖を招くため、公開インターフェースは薄く固定し、内部実装はBODYで差し替える方針にします。CETの常時発火はコストになるため、切替期間だけ有効化して速やかに撤去します。Edition名をハードコーディングせず、パラメータやログイン・トリガで制御するのが運用しやすい形です。
まとめ
EBRを軸にしたデプロイは、スキーマ進化をEditioning Viewで吸収し、必要に応じてクロスエディショントリガで整合を橋渡しし、新Editionへ段階的に切り替えるのが基本線です。CI/CDではEdition作成→EV更新→CET導入→SPEC→BODY→再コンパイル→ウォームアップの順序をパイプライン化し、失敗時はEditionのフェイルバックで即時に復旧します。公開インターフェースを安定させて依存連鎖を抑えれば、PL/SQLのコードデプロイもアプリケーションと同等の俊敏さで無停止ロールアウトが可能になります。