【PL/SQL】コードデプロイの自動化とEdition管理(CI/CD連携)

【PL/SQL】コードデプロイの自動化とEdition管理(CI/CD連携) PL/SQL

継続的インテグレーション/継続的デリバリの流れに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のコードデプロイもアプリケーションと同等の俊敏さで無停止ロールアウトが可能になります。