2012-02-09 8 views
2

私は数週間追跡していたバグがあります。しかし、なぜこのようなことが起こるのかは、Oracle - プロシージャコール間でデータ/状態が失われている間違った断続的なエラー

私は非常に大きなOracleパッケージ(約4,100行のコード)を持っています。私はいくつかの手順を呼び出しています。しかし、プロシージャコールの間にデータが失われているようです。

失われているデータは次のとおりです。

dpmethodstate varchar_state_local_type; 

まず、私は、このプロシージャを呼び出す:

PROCEDURE delivery_plan_set_state (
    messages OUT ReferenceCursor, 
    state IN varchar_state_local_type 
) AS 
BEGIN 
    logMessage('state COUNT is: ' || state.COUNT); 
    dpmethodstate := state; 
    FOR I IN 1..dpmethodstate.COUNT LOOP 
     logMessage(dpmethodstate(I)); 
    END LOOP; 
    logMessage('delivery_plan_set_state end - dpmethodstate count is now ' || dpmethodstate.COUNT); 
    OPEN messages FOR SELECT * FROM TABLE(messageQueue); 
    messageQueue := NULL; 
END delivery_plan_set_state; 

私は状態を渡し、単一の文字列の有効な配列です。手順が終了すると、dpmethodstateCOUNT1であることをログで確認できます。

次に、私はこのようになりますexecute_filterプロシージャを呼び出す:

PROCEDURE execute_filter (
    --Whole bunch of OUT parameters 
) AS 
--About 50 different local variables being set here 
BEGIN 
    SELECT TO_CHAR(SYSTIMESTAMP, 'HH24:MI:SS.ff') INTO TIMING FROM DUAL; 
    logMessage('[' || TIMING || '] execute_filter begin'); 
    logMessage('a) dpmethodstate Count is: ' || dpmethodstate.COUNT); 

    --Rest of procedure 

しかし、今回dpmethodstate.COUNT0です。私がdelivery_plan_set_stateから設定した値は消えました!あなたが見ることができるように

proposed 
delivery_plan_set_state end - dpmethodstate count is now 1 
[21:39:48.719017] execute_filter begin 
a) dpmethodstate Count is: 0 

dpmethodstateは、プロシージャ・コールの間で迷子になった:私は私のログを見てみると

が、それは次のようになります。

  1. このパッケージでは他に何もdelivery_plan_set_state以外dpmethodstateの値を設定することが可能ではありません。注意すべき点がいくつかあります。そして私は他の誰もそれを呼んでいないのを見ることができます。
  2. 私のクライアントサイドのコードはC#で書かれており、2つのプロシージャコールの間にはあまり発生しません。
  3. これはおそらく100回に1回発生するため、追跡またはデバッグすることは非常に困難です。

まず、このような問題をデバッグするにはどうすればよいですか?私ができるロギングはもうありますか?また、Oracleはプロシージャー呼び出しの間の状態をどのように維持し、何かが断続的に行うことができますこの状態をリセットしますか?どんなポインタであれ大いに感謝します!

答えて

7

dpmethodstateはパッケージのグローバル変数ですか?私はそれがあると仮定しているが、明示的に言及されているとは思わない。

パッケージのグローバル変数にはセッションスコープがあるため、2つのプロシージャー呼び出しが常に同じ物理データベース接続を使用していることと、その間にこの接続を使用しているものはないことを確認してください。各呼び出しの前にプールから接続を取得し、最初の呼び出し後に接続をプールに戻す何らかの接続プールを使用している場合、開発環境(または使用率の低い環境)ではそれほど珍しいことではありません。同じ接続を2回目のコールの99%の時間にかけますが、1%の時間で別のセッションを取得します。

セッションのSIDSERIAL#を、値を設定する場所と値を取得する場所で記録できますか。

SELECT sid, serial# 
    FROM v$session 
WHERE sid = sys_context('USERENV', 'SID'); 

これらの値が異なる場合、その値が維持されることは期待できません。

それ以外にも、セッションの状態をクリアする方法はありますが、誰かが明示的に行動する必要があります。 DBMS_SESSION.RESET_PACKAGEまたはDBMS_SESSION.MODIFY_PACKAGE_STATE(DBMS_SESSION.REINITIALIZE)を呼び出すと、セッションで設定されたセッション状態がすべて消去されます。パッケージをコンパイルすると同じことが実行されますが、読み込みしようとするとセッションの状態が破棄されたという警告が表示されます。

+0

さて、私はちょっとコネクションプーリングと関係があると思っていました。 Webサーバーは.NET Entity Frameworkを使用していますが、どのように(またはドライバ)接続をプールするのかはわかりません。しかし、2回の呼び出しはミリ秒間隔で行われ、同じHTTP要求で行われます。 .NETは "ちょっとこの接続を捨てて別のものを使う"と言うでしょうが、おそらく彼らはいくつかの奇妙な最適化ロジックを持っています。私はsidとシリアル番号を記録するあなたのアイデアは間違いないと思う、私はこの最初の事を明日試してみよう! –

+0

これは私のローカルデータベース上で動作することを確認しましたが、プロダクションでv $ sessionに対してSELECT権限を与えることに決して同意しませんでしたので、この方法を使用して問題を追跡することはできません。私はこれがこの質問のための最良の答えだと思う! –

+0

@MikeChristensen - 'v $ session'を照会するのではなく、' sys_context( 'USERENV'、 'SID') 'と' sys_context( 'USERENV'、 'SESSIONID')をログできます。まったく同じことではありませんが、異なるセッションがあるかどうかを確認するという同じ目標を達成する必要があります。 –

関連する問題