【完全ガイド】ORA-04068: existing state of packages discarded の原因と解決方法|PRAGMA SERIALLY_REUSABLE・EBR・24時間運用 徹底解説
- 作成日 2026.08.03
- Oracle Database
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 を根本から解決できるようになります。
- 1. 結論:Package State が原因
- 2. まず理解する:Package State とは
- 3. 【原因①】別セッションからのパッケージ更新
- 4. 【原因②】依存オブジェクトの変更
- 5. 【原因③】PRAGMA SERIALLY_REUSABLE の未使用
- 6. 【原因④】ステートフル依存関係
- 7. 【原因⑤】RAC / GoldenGate 環境
- 8. 【原因⑥】ホットデプロイの失敗
- 9. 【原因⑦】OCI / JDBC からの発生
- 10. 【原因⑧】Rails からの発生
- 11. 【原因⑨】マイグレーションでのパッケージ変更
- 12. 【原因⑩】タイムアウトや再接続
- 13. 診断ツール完全リファレンス
- 14. 4つの解決策 完全リファレンス
- 15. Rails / Java / Python 対応
- 16. 実践シナリオ
- 16.1. シナリオ1:24時間運用でのパッケージデプロイ
- 16.2. シナリオ2:Rails アプリでの ORA-04068 対応
- 16.3. シナリオ3:CI/CD パイプラインでのパッケージデプロイ
- 16.4. シナリオ4:Stateful パッケージのリファクタリング
- 16.5. シナリオ5:セッションでの State リセット
- 16.6. シナリオ6:本番緊急パッチデプロイ
- 16.7. シナリオ7:Docker Oracle での EBR テスト
- 16.8. シナリオ8:バックグラウンドジョブでの対応
- 16.9. シナリオ9:Autonomous DB での対応
- 16.10. シナリオ10:AWR で ORA-04068 頻度分析
- 17. トラブルシューティング
- 18. よくある質問(FAQ)
- 18.1. Q1. Stateless パッケージなら発生しない?
- 18.2. Q2. PRAGMA SERIALLY_REUSABLE のトレードオフ
- 18.3. Q3. EBR の実運用
- 18.4. Q4. 再ログインで解決する理由
- 18.5. Q5. 「1回目失敗、2回目成功」の理由
- 18.6. Q6. Rails / Java でのリトライ
- 18.7. Q7. パッケージ状態のリセット方法
- 18.8. Q8. INVALID の自動再コンパイル
- 18.9. Q9. Autonomous DB での挙動
- 18.10. Q10. マイグレーション後のホットデプロイ
- 18.11. Q11. AWR でのモニタリング
- 18.12. Q12. パフォーマンスへの影響
- 19. 参考リンク
- 20. まとめ
結論: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_REUSABLE | Stateful だが状態リセット可 |
| ② | 例外処理でリトライ | 短期対応 |
| ③ | Edition-Based Redefinition | 24時間運用 |
| ④ | 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_counterg_namec_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 公式
- Oracle Database Error Messages: ORA-04068
- Oracle PL/SQL Language Reference: Package State
- Oracle Database Development Guide: Edition-Based Redefinition
- DBMS_SESSION.RESET_PACKAGE
まとめ
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)もあわせてご確認ください。
-
前の記事
【完全ガイド】ORA-01839: date not valid for month specified の原因と解決方法|うるう年・INTERVAL・ADD_MONTHS 徹底解説 2026.08.03
-
次の記事
【完全ガイド】ORA-08103: object no longer exists の原因と解決方法|DATA_OBJECT_ID・TRUNCATE・パーティション操作 徹底解説 2026.08.04
コメントを書く