【完全ガイド】ORA-04068: existing state of packages discarded の原因と解決方法|PRAGMA SERIALLY_REUSABLE・EBR・24時間運用 徹底解説

【完全ガイド】ORA-04068: existing state of packages discarded の原因と解決方法|PRAGMA SERIALLY_REUSABLE・EBR・24時間運用 徹底解説

Oracle PL/SQL 開発者・DBA が24時間運用システムで頻繁に遭遇する厄介なエラー:

ORA-04068: existing state of packages has been discarded
ORA-04061: existing state of package "SCOTT.MY_PKG" has been invalidated
ORA-04065: not executed, altered or dropped package "SCOTT.MY_PKG"
ORA-06508: PL/SQL: could not find program unit being called: "SCOTT.MY_PKG"
ORA-06512: at line 1

**「パッケージの状態が破棄された」**という難解なメッセージ。4つのエラーが連鎖して発生する、Oracle 特有の奇妙な挙動:

  • 本番運用中に PL/SQL パッケージを更新 → 使用中セッションが失敗
  • 依存オブジェクト変更(テーブル ALTER)で全パッケージ無効化
  • セッションによって成功/失敗が変わる
  • 1回目は失敗、2回目は成功という不思議な挙動
  • リトライで解決するがロジック分岐が必要
  • 24時間365日運用の敵

このエラーの本質的な原因は、Oracle のPackage State(パッケージ状態) の仕組みにあります:

Package State = パッケージ内のグローバル変数・カーソル・定数の値

セッション毎にメモリ内に保持
→ パッケージ再コンパイル時に「破棄」
→ 次回アクセス時に ORA-04068

現場では:

  • 業務時間中のホットフィックスが困難
  • メンテナンスウィンドウ必須
  • アプリからの再試行ロジックが必要
  • PRAGMA SERIALLY_REUSABLE の使い分け
  • Edition-Based Redefinition (EBR) の活用
  • Stateless パッケージ設計の原則
  • Rails / Java からの例外ハンドリング

さらに、多くの日本語記事が「再ログインで解決」で終わりますが、実務では:

  • **なぜ「1回目失敗、2回目成功」**なのか(Oracle が状態リセット)
  • PRAGMA SERIALLY_REUSABLEメリットと重大なデメリット
  • ORA-06534: Cannot access Serially Reusable package への対処
  • Edition-Based Redefinition (EBR)本格運用
  • JDBC / node-oracledb / oracledb からのハンドリング
  • Rails でのリトライ戦略
  • 本番デプロイのベストプラクティス
  • CI/CD パイプラインへの組み込み

本記事では、ORA-04068: existing state of packages has been discarded完全な原因と解決方法を、リファレンスとして実用的に整理します。Package State の本質、10大発生パターン、4つの解決策、EBR の実践、Rails/Java/Python 対応、24時間運用戦略、実践シナリオ、FAQまで完全網羅。この1本で ORA-04068 を根本から解決できるようになります。


目次

結論:Package State が原因

時間がない方向けに、最速の対処を先に示します。

エラーメッセージの読み方

4つのエラーが連鎖:

ORA-04068: existing state of packages has been discarded    ← 状態破棄
ORA-04061: existing state of package X has been invalidated ← 無効化
ORA-04065: not executed, altered or dropped package X       ← 変更/削除
ORA-06508: could not find program unit                       ← ユニット不在
ORA-06512: at line 1                                          ← 発生位置

核心ORA-04068、他は連鎖エラー。

エラーが起きる条件

条件1: パッケージが Package State を持つ
       (グローバル変数/カーソル/定数)
条件2: パッケージが再コンパイルされた
       or 依存オブジェクトが変更された
条件3: セッションが状態を保持している
       and パッケージにアクセス
→ ORA-04068

診断

-- 状態を持つパッケージか確認
SELECT DISTINCT owner, object_name 
FROM all_source
WHERE type IN ('PACKAGE', 'PACKAGE BODY')
  AND (
    UPPER(text) LIKE '%VARIABLE%' OR
    UPPER(text) LIKE '%:=%' OR
    UPPER(text) LIKE '%CURSOR%'
  );

-- 無効化されたパッケージ
SELECT owner, object_name, object_type, status
FROM all_objects
WHERE object_type IN ('PACKAGE', 'PACKAGE BODY')
  AND status = 'INVALID';

4つの解決策

#手法使う場面
PRAGMA SERIALLY_REUSABLEStateful だが状態リセット可
例外処理でリトライ短期対応
Edition-Based Redefinition24時間運用
Stateless 設計根本解決

詳細は以下で解説します。


まず理解する:Package State とは

状態を持つパッケージ

パッケージ内でグローバル変数・カーソル・定数を宣言:

CREATE OR REPLACE PACKAGE my_pkg IS
  -- Package State
  g_counter NUMBER := 0;
  g_name    VARCHAR2(50);
  CURSOR c_orders IS SELECT * FROM orders;
  
  PROCEDURE increment;
END my_pkg;
/

Package State:

  • g_counter
  • g_name
  • c_orders

セッション毎にメモリ内に保持される。

状態を持たないパッケージ

CREATE OR REPLACE PACKAGE my_stateless_pkg IS
  -- グローバル変数なし
  -- カーソルなし
  PROCEDURE do_something(p_id NUMBER);
END my_stateless_pkg;
/

変数を持たない = Stateless。ORA-04068 は発生しない。

状態の生存期間

1. セッション開始
2. パッケージ初回参照 → State 初期化
3. State はセッション終了まで保持
4. 別セッションからの再コンパイル → 現セッションの State 無効化
5. 次のアクセス → ORA-04068

エラー連鎖の意味

ORA-04068: 状態が破棄された(トリガー)
ORA-04061: 状態が無効化された(原因)
ORA-04065: パッケージが変更/削除された(背景)
ORA-06508: パッケージが見つからない(結果)

【原因①】別セッションからのパッケージ更新

症状

セッション1:

BEGIN
  my_pkg.increment;  -- g_counter を +1
END;
/
-- g_counter = 1(State 保持)

セッション2(別ユーザー):

CREATE OR REPLACE PACKAGE BODY my_pkg IS
  ...  -- 更新
END my_pkg;
/

セッション1(再度):

BEGIN
  my_pkg.increment;
END;
/
-- ORA-04068

セッション1(もう一度):

BEGIN
  my_pkg.increment;
END;
/
-- 成功(State リセット、g_counter = 1 から再開)

「1回目失敗、2回目成功」 の理由。

解決

  • 例外ハンドリングでリトライ
  • Serially Reusable
  • EBR

【原因②】依存オブジェクトの変更

症状

-- テーブル定義変更
ALTER TABLE orders ADD (new_col NUMBER);

-- orders を参照するパッケージが自動 INVALID
SELECT status FROM user_objects 
WHERE object_name = 'MY_PKG';
-- INVALID

診断

-- 依存関係
SELECT owner, name, type, referenced_name, referenced_type
FROM all_dependencies
WHERE referenced_name = 'ORDERS';

解決

再コンパイル:

ALTER PACKAGE my_pkg COMPILE;
ALTER PACKAGE my_pkg COMPILE BODY;

または UTL_RECOMP で一括:

EXEC UTL_RECOMP.recomp_serial('SCOTT');

自動再コンパイル

無効化されたパッケージも、次のアクセス時に自動再コンパイル
→ しかし、State を持つと ORA-04068

【原因③】PRAGMA SERIALLY_REUSABLE の未使用

症状

Stateful パッケージが常時 ORA-04068 リスク:

CREATE OR REPLACE PACKAGE stateful_pkg IS
  g_var NUMBER := 0;
  PROCEDURE proc1;
END stateful_pkg;
/

解決: SERIALLY_REUSABLE

CREATE OR REPLACE PACKAGE serially_reusable_pkg IS
  PRAGMA SERIALLY_REUSABLE;   -- ← 追加
  g_var NUMBER := 0;
  PROCEDURE proc1;
END serially_reusable_pkg;
/

CREATE OR REPLACE PACKAGE BODY serially_reusable_pkg IS
  PRAGMA SERIALLY_REUSABLE;   -- ← BODY にも
  PROCEDURE proc1 IS ...
END serially_reusable_pkg;
/

動作の違い

通常パッケージ:
  State はセッション終了まで保持
  → 変更時に ORA-04068

SERIALLY_REUSABLE:
  State は1回の呼び出しのみ
  → 呼び出し毎にリセット
  → ORA-04068 発生しない

重大なデメリット

変数の永続性が失われる:

BEGIN
  serially_reusable_pkg.g_var := 5;
END;
/

BEGIN
  DBMS_OUTPUT.PUT_LINE(serially_reusable_pkg.g_var);
  -- 0(初期値、5 ではない)
END;
/

セッション内でも状態が保持されない。「グローバル変数」の意味を失う。

さらに: ORA-06534

Serially Reusable パッケージから DML 発行不可:

CREATE OR REPLACE PACKAGE BODY pkg IS
  PRAGMA SERIALLY_REUSABLE;
  PROCEDURE do_insert IS
  BEGIN
    INSERT INTO t VALUES (1);
    -- ORA-06534: Cannot access Serially Reusable package
  END;
END pkg;
/

厳しい制限。用途は限定的。


【原因④】ステートフル依存関係

症状

Stateful パッケージが別の Stateful パッケージを呼ぶ:

-- pkg_A は Stateful
CREATE OR REPLACE PACKAGE pkg_A IS
  g_var NUMBER;
END pkg_A;

-- pkg_B が pkg_A に依存
CREATE OR REPLACE PACKAGE pkg_B IS
  PROCEDURE use_A;
END pkg_B;

pkg_A 更新 → pkg_B 使用セッションも ORA-04068

解決

  • 依存関係を最小化
  • EBR で全体を管理

【原因⑤】RAC / GoldenGate 環境

症状

ノード間でパッケージ状態不整合:

Node 1: パッケージ v1
Node 2: パッケージ v2(再コンパイル済)
セッションがフェイルオーバー → ORA-04068

解決

EBR で明示的バージョン管理


【原因⑥】ホットデプロイの失敗

症状

業務時間中の更新:

14:00 開発チーム: パッケージ更新
14:00-14:30 アプリユーザー: ORA-04068 大量発生

解決

メンテナンスウィンドウ or EBR


【原因⑦】OCI / JDBC からの発生

症状

Java Spring / Rails などから:

ORA-04068 の発生タイミング:
- コネクションプールから既存接続を取得
- パッケージが再コンパイルされている
- ORA-04068 発生

解決

接続プール側でリトライ設定:

// HikariCP
config.setConnectionInitSql("BEGIN DBMS_SESSION.RESET_PACKAGE; END;");
// 接続取得時に State リセット

DBMS_SESSION.RESET_PACKAGE:

BEGIN
  DBMS_SESSION.RESET_PACKAGE;
END;
/
-- 全パッケージの State をリセット

【原因⑧】Rails からの発生

症状

Rails アプリで:

User.stored_procedure_call
# ORA-04068

解決

リトライ機構:

class OraclePackageCaller
  def self.call_with_retry(procedure_name, *args, retries: 2)
    yield
  rescue ActiveRecord::StatementInvalid => e
    if e.message.include?("ORA-04068") && retries > 0
      retries -= 1
      retry
    else
      raise
    end
  end
end

Rails 8 系の詳細は Rails 8 アップグレードガイドの記事、Solid Queue 使い方の記事も参照してください。


【原因⑨】マイグレーションでのパッケージ変更

症状

db:migrate 中のパッケージ更新:

class UpdateOrderPackage < ActiveRecord::Migration[8.0]
  def change
    execute <<-SQL
      CREATE OR REPLACE PACKAGE order_pkg IS
        ...
      END order_pkg;
    SQL
  end
end

マイグレーション後、既存セッションで ORA-04068

解決

  • マイグレーション後にアプリ再起動
  • Zero-downtime deployment
  • EBR

pending migration の詳細は rails db:migrate 使い方の記事も参照してください。


【原因⑩】タイムアウトや再接続

症状

アプリの自動再接続:

接続タイムアウト → 再接続
→ 新しいセッション(State なし)
→ しかし内部的な Cache 問題で ORA-04068

解決

明示的な State リセット:

DBMS_SESSION.MODIFY_PACKAGE_STATE(DBMS_SESSION.REINITIALIZE);

診断ツール完全リファレンス

パッケージの状態確認

-- INVALID なパッケージ
SELECT owner, object_name, object_type
FROM all_objects
WHERE object_type IN ('PACKAGE', 'PACKAGE BODY')
  AND status = 'INVALID';

-- 全パッケージリスト
SELECT owner, object_name, status, last_ddl_time
FROM all_objects
WHERE object_type = 'PACKAGE'
ORDER BY last_ddl_time DESC;

Stateful パッケージ検出

-- ヒューリスティック検索
SELECT DISTINCT owner, name
FROM all_source
WHERE type = 'PACKAGE'
  AND (
    REGEXP_LIKE(text, '\s*[a-zA-Z_]+\s+.*\s*:=', 'i')  -- 変数初期化
    OR UPPER(text) LIKE '%CURSOR%'                     -- カーソル
  );

依存関係の追跡

-- パッケージが依存しているもの
SELECT referenced_owner, referenced_name, referenced_type
FROM all_dependencies
WHERE name = 'MY_PKG'
  AND owner = 'SCOTT';

-- パッケージに依存しているもの
SELECT owner, name, type
FROM all_dependencies
WHERE referenced_name = 'MY_PKG'
  AND referenced_owner = 'SCOTT';

DBA_DDL_LOCKS

-- パッケージのロック状況
SELECT session_id, name, type
FROM dba_ddl_locks
WHERE type LIKE '%PACKAGE%';

4つの解決策 完全リファレンス

解決策① PRAGMA SERIALLY_REUSABLE

CREATE OR REPLACE PACKAGE pkg IS
  PRAGMA SERIALLY_REUSABLE;
  ...
END pkg;
/

CREATE OR REPLACE PACKAGE BODY pkg IS
  PRAGMA SERIALLY_REUSABLE;
  ...
END pkg;
/

メリット: ORA-04068 発生しない デメリット:

  • セッション内で状態非保持
  • DML 発行不可
  • カーソル制限

用途: ステートを持つ意味がないパッケージ(実はステートレスに設計すべき)

解決策② 例外処理でリトライ

PL/SQL 内:

DECLARE
  v_retries PLS_INTEGER := 0;
BEGIN
  <<retry>>
  BEGIN
    my_pkg.some_procedure;
  EXCEPTION
    WHEN OTHERS THEN
      IF SQLCODE = -4068 AND v_retries < 3 THEN
        v_retries := v_retries + 1;
        GOTO retry;
      ELSE
        RAISE;
      END IF;
  END;
END;
/

アプリ側でも同様のロジック

解決策③ Edition-Based Redefinition (EBR)

24時間365日運用の決定版:

-- ① Edition 作成
CREATE EDITION new_edition AS CHILD OF ora$base;

-- ② 新しい Edition に切り替え
ALTER SESSION SET EDITION = new_edition;

-- ③ パッケージを新 Edition で更新
CREATE OR REPLACE PACKAGE my_pkg IS
  ...
END my_pkg;
/

-- ④ 既存セッションは ora$base の古いパッケージを使い続ける
-- ⑤ 新規セッションが new_edition の新パッケージを使う

-- ⑥ 全セッションが切り替わったら
DROP EDITION ora$base CASCADE;

メリット:

  • ORA-04068 完全回避
  • 業務時間中のデプロイ可能
  • ロールバック容易

デメリット:

  • 複雑な設定
  • 全オブジェクトが Editionable でない
  • 学習コスト

解決策④ Stateless 設計

根本的な解決:

-- ❌ Stateful
CREATE OR REPLACE PACKAGE pkg IS
  g_counter NUMBER := 0;   -- グローバル変数
  PROCEDURE increment;
END pkg;

-- ✅ Stateless
CREATE OR REPLACE PACKAGE pkg IS
  -- グローバル変数なし
  PROCEDURE increment(p_counter IN OUT NUMBER);
  FUNCTION get_counter(p_key VARCHAR2) RETURN NUMBER;
END pkg;

-- 状態はテーブルに保存

Stateless パッケージが最も安全で保守しやすい。


Rails / Java / Python 対応

Rails

リトライロジック:

class OracleRetryable
  MAX_RETRIES = 2
  
  def self.with_retry
    retries = 0
    begin
      yield
    rescue ActiveRecord::StatementInvalid => e
      if (e.message.include?("ORA-04068") || 
          e.message.include?("ORA-06508")) && retries < MAX_RETRIES
        retries += 1
        Rails.logger.warn "ORA-04068 retry #{retries}"
        # 短い待機
        sleep(0.1)
        retry
      else
        raise
      end
    end
  end
end

# 使用
OracleRetryable.with_retry do
  User.call_stored_procedure
end

Java (Spring)

@Component
public class OracleRetryableCaller {
    private static final int MAX_RETRIES = 2;
    
    @Retryable(
        value = SQLException.class,
        maxAttempts = 2,
        backoff = @Backoff(delay = 100)
    )
    public void callWithRetry(Runnable action) {
        try {
            action.run();
        } catch (SQLException e) {
            if (e.getErrorCode() == 4068 || e.getErrorCode() == 6508) {
                throw new RuntimeException("Package invalidated", e);
            }
            throw e;
        }
    }
}

Python (oracledb)

import oracledb
import time
from functools import wraps

def with_ora_04068_retry(max_retries=2):
    def decorator(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            for attempt in range(max_retries + 1):
                try:
                    return func(*args, **kwargs)
                except oracledb.DatabaseError as e:
                    error_obj, = e.args
                    if error_obj.code in (4068, 6508) and attempt < max_retries:
                        time.sleep(0.1)
                        continue
                    raise
        return wrapper
    return decorator

@with_ora_04068_retry(max_retries=2)
def call_package():
    cursor.callproc('my_pkg.some_proc')

実践シナリオ

シナリオ1:24時間運用でのパッケージデプロイ

旧: メンテナンスウィンドウ

02:00-04:00 サービス停止
02:15 パッケージ更新
02:30 動作確認
04:00 サービス再開

新: EBR 活用

1. 新 Edition 作成
2. 新パッケージデプロイ
3. アプリを新 Edition に切替
4. 旧 Edition Drop
5. 業務停止なし

シナリオ2:Rails アプリでの ORA-04068 対応

# config/initializers/oracle_retry.rb
module ActiveRecord
  class Base
    class << self
      alias_method :original_execute, :execute
      
      def execute(sql, name = nil)
        retries = 0
        begin
          original_execute(sql, name)
        rescue ActiveRecord::StatementInvalid => e
          if e.message.include?("ORA-04068") && retries < 2
            retries += 1
            retry
          end
          raise
        end
      end
    end
  end
end

シナリオ3:CI/CD パイプラインでのパッケージデプロイ

# .github/workflows/deploy.yml
- name: Deploy packages
  run: |
    sqlplus $DB_USER/$DB_PASSWORD @deploy.sql

- name: Restart application
  run: |
    kamal deploy
    # または
    systemctl restart my-app

Kamal 2 デプロイの詳細は Kamal 2 デプロイの記事、systemctl の詳細は systemctl vs service の記事を参照してください。

シナリオ4:Stateful パッケージのリファクタリング

-- 修正前(Stateful)
CREATE OR REPLACE PACKAGE order_pkg IS
  g_order_id NUMBER;
  PROCEDURE start_order;
  PROCEDURE add_item(p_item NUMBER);
END order_pkg;

-- 修正後(Stateless)
CREATE OR REPLACE PACKAGE order_pkg IS
  FUNCTION create_order RETURN NUMBER;
  PROCEDURE add_item(p_order_id NUMBER, p_item NUMBER);
END order_pkg;

-- 状態はテーブル or セッションコンテキスト

シナリオ5:セッションでの State リセット

-- アプリケーション起動時
BEGIN
  DBMS_SESSION.RESET_PACKAGE;
END;
/
-- 全パッケージ State クリア

シナリオ6:本番緊急パッチデプロイ

-- 1. パッケージ更新
CREATE OR REPLACE PACKAGE ... 

-- 2. 影響セッション確認
SELECT s.username, s.machine, s.program, s.status
FROM v$session s
JOIN v$open_cursor c ON s.sid = c.sid
WHERE c.sql_text LIKE '%MY_PKG%';

-- 3. INACTIVE を KILL(新規接続で新パッケージ)
BEGIN
  FOR c IN (SELECT sid, serial# FROM v$session 
            WHERE status = 'INACTIVE' AND last_call_et > 300) LOOP
    EXECUTE IMMEDIATE 'ALTER SYSTEM KILL SESSION ''' || 
      c.sid || ',' || c.serial# || ''' IMMEDIATE';
  END LOOP;
END;
/

シナリオ7:Docker Oracle での EBR テスト

docker exec -it oracle-xe sqlplus scott/tiger <<EOF
-- Edition 作成
CREATE EDITION new_ed AS CHILD OF ora$base;
ALTER SESSION SET EDITION = new_ed;

CREATE OR REPLACE PACKAGE pkg IS PROCEDURE p; END pkg;
/

-- 動作確認
BEGIN pkg.p; END;
/
EOF

Docker 関連は docker daemon 接続エラーの記事も参照してください。

シナリオ8:バックグラウンドジョブでの対応

class RecalculateOrderJob < ApplicationJob
  retry_on ActiveRecord::StatementInvalid, wait: 5.seconds, attempts: 3 do |job, error|
    if error.message.include?("ORA-04068")
      Rails.logger.info "Retrying due to ORA-04068"
    else
      raise error
    end
  end
  
  def perform(order_id)
    # パッケージ呼び出し
    ActiveRecord::Base.connection.execute(
      "BEGIN order_pkg.recalculate(#{order_id}); END;"
    )
  end
end

シナリオ9:Autonomous DB での対応

-- Autonomous DB は自動チューニング
-- EBR も完全対応
-- ORA-04068 の頻度は減少

シナリオ10:AWR で ORA-04068 頻度分析

SELECT sql_id, sample_time, sql_exec_start
FROM v$active_session_history
WHERE sql_id IN (
  SELECT DISTINCT sql_id FROM v$active_session_history
  WHERE event = 'library cache lock'
);

パフォーマンス系は Oracle EXPLAIN PLAN 見方の記事も参考にしてください。


トラブルシューティング

パッケージが INVALID のまま

-- 手動再コンパイル
ALTER PACKAGE my_pkg COMPILE;
ALTER PACKAGE my_pkg COMPILE BODY;

-- エラー確認
SHOW ERRORS PACKAGE BODY my_pkg;

依存関係の循環

-- 循環依存確認
SELECT * FROM all_dependencies 
WHERE (name, referenced_name) IN (
  SELECT referenced_name, name FROM all_dependencies
);

SERIALLY_REUSABLE でも発生

  • BODY にも PRAGMA 忘れ
  • 依存パッケージが Stateful

EBR の Editionable でないオブジェクト

-- Editionable でない
SELECT object_name, editionable
FROM user_objects
WHERE editionable = 'N';
-- Table, Index 等

BODY を Editionable View で隠す等の設計が必要。

アプリのリトライが効かない

接続プールが古い状態を保持:

接続プールをリセット
コネクションを全て閉じ直す

よくある質問(FAQ)

Q1. Stateless パッケージなら発生しない?

基本的に発生しない。ただし依存オブジェクトの変更で INVALID になり、初回アクセスでエラーの可能性はあり。

Q2. PRAGMA SERIALLY_REUSABLE のトレードオフ

  • メリット: ORA-04068 回避
  • デメリット: 状態非保持、DML制限

Q3. EBR の実運用

大企業/金融系で導入例多数。設計コスト大

Q4. 再ログインで解決する理由

新セッション = 新 State。古い State はセッション終了で破棄。

Q5. 「1回目失敗、2回目成功」の理由

1回目: 古い State との整合性エラー 2回目: State リセット後の新規実行

Q6. Rails / Java でのリトライ

retryable gem や Spring @Retryable で自動化。

Q7. パッケージ状態のリセット方法

DBMS_SESSION.RESET_PACKAGE;

Q8. INVALID の自動再コンパイル

デフォルトで有効、アクセス時に自動。

Q9. Autonomous DB での挙動

同じ。EBR 完全サポート

Q10. マイグレーション後のホットデプロイ

Rails では db:migrate 後にアプリ再起動が安全。

Q11. AWR でのモニタリング

library cache lock イベントで検出。

Q12. パフォーマンスへの影響

SERIALLY_REUSABLE はメモリコピー発生、少しオーバーヘッド。


参考リンク

Oracle 公式


まとめ

ORA-04068: existing state of packages has been discarded の要点を再整理します。

エラーの本質

Stateful パッケージが再コンパイルされ、
セッションの Package State が破棄された
→ 次のアクセスで ORA-04068
→ さらにアクセスすると成功(State リセット済み)

エラー連鎖

ORA-04068: 状態破棄(トリガー)
ORA-04061: 状態無効化
ORA-04065: パッケージ変更/削除
ORA-06508: パッケージ不在
ORA-06512: 発生位置

発生条件

条件1: Package State を持つ
条件2: 再コンパイルされた/依存オブジェクト変更
条件3: セッションが State を保持している

10大原因

#原因対処
別セッションから更新リトライ / EBR
依存オブジェクト変更再コンパイル
SERIALLY_REUSABLE 未使用PRAGMA 追加
ステートフル依存依存最小化
RAC / GG 環境EBR
ホットデプロイメンテナンス窓 / EBR
OCI / JDBC 経由RESET_PACKAGE
Rails からリトライ機構
マイグレーションアプリ再起動
再接続時RESET_PACKAGE

4つの解決策

-- ① PRAGMA SERIALLY_REUSABLE
CREATE PACKAGE pkg IS
  PRAGMA SERIALLY_REUSABLE;
  ...
END;

-- ② 例外リトライ
EXCEPTION
  WHEN OTHERS THEN
    IF SQLCODE = -4068 THEN RETRY
    ...

-- ③ Edition-Based Redefinition
CREATE EDITION new_ed AS CHILD OF ora$base;
ALTER SESSION SET EDITION = new_ed;

-- ④ Stateless 設計
-- グローバル変数なし
-- 状態はテーブル/コンテキスト

診断クエリ

-- INVALID パッケージ
SELECT * FROM user_objects 
WHERE object_type IN ('PACKAGE', 'PACKAGE BODY')
  AND status = 'INVALID';

-- 依存関係
SELECT * FROM all_dependencies 
WHERE name = 'MY_PKG';

DBMS_SESSION.RESET_PACKAGE

BEGIN
  DBMS_SESSION.RESET_PACKAGE;
END;
/
-- 全パッケージ State リセット

24時間運用戦略

【Level 1】メンテナンスウィンドウ
- サービス停止して更新
- 最も安全、業務影響大

【Level 2】リトライロジック
- アプリで例外捕捉
- 業務継続、若干のエラー許容

【Level 3】Edition-Based Redefinition (EBR)
- 完全ゼロダウンタイム
- 設計コスト大、24時間365日運用の決定版

SERIALLY_REUSABLE の使い所

YES:
- Stateful だが Session 内で状態不要
- 純粋関数的なパッケージ

NO:
- 真にセッション状態が必要
- DML を発行する
- ロング トランザクション
→ Stateless 設計 or EBR 検討

Stateless パッケージ設計原則

1. グローバル変数を持たない
2. カーソルはローカルで宣言
3. 状態はパラメータで渡す
4. 状態はテーブル/コンテキストに保存
5. パッケージは純粋関数集

これらの知識は、Oracle での PL/SQL 開発・パッケージ設計・24時間運用・EBR 実装・Rails / Java / Python アプリ運用・本番デプロイ・DBA 業務など、あらゆる場面で活用できます。本記事をブックマークしておけば、ORA-04068 に出会っても冷静に的確に対処できるようになります。


本記事は2026年6月時点の情報をもとに、Oracle Database 19c〜23ai での動作確認・公式ドキュメントに基づき作成しています。Oracle のバージョンにより挙動が異なる場合があるため、最新の情報は Oracle 公式ドキュメント(docs.oracle.com)もあわせてご確認ください。