0

ユーザー統計を管理するためのオブジェクトを含むセッション スコープ クラスがあります。ユーザーが (SSO を介して) ログインすると、アプリケーション スコープのメソッドがアクティブなセッションのテーブルをチェックします。セッションが見つかった場合は、テーブルのセッション ID を使用してセッションが無効になります。

セッション スコープ クラスの userStats テーブルに行が追加されます。

/**
     * get all the info needed for collecting user stats, add userStats to the users session and save to userStats table
     * this happens after session is created
     * @param request
     */
    private void createUserStats(HttpServletRequest request){
        if (!this.sessionExists) {
            this.userStats = new UserStats(this.user, request.getSession(true)
                    .getId(), System.getProperty("atcots_host_name"));
            request.getSession().setAttribute("userstats", this.userStats);
            Events.instance().raiseEvent("userBoundToSession", this.userStats);
            this.sessionExists = true;
            log.info("user " + this.user + " is now logged on");

            // add this to the db
            try {
                this.saveUserStatsToDb();
            } catch (Exception e) {
                log.error("could not save " + this.userStats.getId().getPeoplesoftId() + " information to db");
                e.printStackTrace();
            }           

        }

    }

ユーザーのセッションが破棄されると、この行はログオフ時間で更新されます。

説明も複製もできない理由により、過去 2 週間で 2 人のユーザーがログインして行をロックしました。その場合、そのユーザーによるデータベース呼び出しはすべて不可能になり、このユーザーはアプリケーションを事実上使用できなくなります。

 [org.hibernate.util.JDBCExceptionReporter] (http-127.0.0.1-8180-3) SQL Error: 0, SQLState: null
2012-07-26 18:45:53,427 ERROR [org.hibernate.util.JDBCExceptionReporter] (http-127.0.0.1-8180-3) Transaction is not active: tx=TransactionImple < ac, BasicAction: -75805e7d:3300:5011c807:6a status: ActionStatus.ABORT_ONLY >; - nested throwable: (javax.resource.ResourceException: Transaction is not active: tx=TransactionImple < ac, BasicAction: -75805e7d:3300:5011c807:6a status: ActionStatus.ABORT_ONLY >)

これらの統計の収集は重要ですが、生死に関わることではありません。情報が得られない場合は、あきらめて動かし続けたいと思います。しかし、それは起こっていません。何が起こっているかというと、entityManager がロールバックのトランザクションをマークしていて、その後の db 呼び出しが上記のエラーを返しているということです。私はもともとユーザーの統計をアプリケーションスコープで保存しました。そのため、行がロックされると、アプリケーション全体のentityManagerがロックされました(これはうまくいきませんでした)。メソッドをセッションスコープに移動すると、問題のあるユーザーのみがロックアウトされます。

I tried setting the entityManger to a lesser scope(I tried EVENT and METHOD):
        ((EntityManager) Component.getInstance("entityManager", ScopeType.EVENT)).persist(this.userStats);
        ((EntityManager) Component.getInstance("entityManager", ScopeType.EVENT)).flush();

これはdb呼び出しをまったく行いません。トランザクションを手動でロールバックしようとしましたが、喜びはありません。

会話スコープ レベルで使用されるデータを含むテーブルの行をロックすると、結果は壊滅的なものにはなりません。データは保存されませんが、回復します。

到着予定時刻:

AsynchronousEvent を発生させようとしました - これはローカルで動作しますが、リモート テスト サーバーにデプロイされます - これは奇妙です - 私は得る:

    DEBUG [org.quartz.core.JobRunShell] (qtz_Worker-1) Calling execute on job DEFAULT.2d0badb3:139030aec6e:-7f34
    INFO  [com.mypkg.myapp.criteria.SessionCriteria] (qtz_Worker-1) observing predestroy for seam
    DEBUG [com.mypkg.myapp.criteria.SessionCriteria] (qtz_Worker-1) destroy destroy destroy sessionCriteria
    ERROR [org.jboss.seam.async.AsynchronousExceptionHandler] (qtz_Worker-1) Exeception thrown whilst executing asynchronous call
    java.lang.IllegalArgumentException: attempt to create create event with null entity
            at org.hibernate.event.PersistEvent.<init>(PersistEvent.java:45)
            at org.hibernate.event.PersistEvent.<init>(PersistEvent.java:38)
            at org.hibernate.impl.SessionImpl.persist(SessionImpl.java:619)
            at org.hibernate.impl.SessionImpl.persist(SessionImpl.java:623)
...

奇妙な点は、Quartz ハンドラを通過しているように見えることです。

ETA 再び:

それほど奇妙ではありませんが、Quartz を非同期ハンドラーとして設定しました。これは、ジョブのスケジューリング専用だと思いました。また、非同期メソッドはセッション コンテキストにアクセスできないため、実際に永続化するオブジェクトを保持するには、監視メソッドにパラメーターを追加する必要がありました。

@Observer("saveUserStatsEvent")
@Transactional
public void saveUserStatsToDb(UserStats userstats) throws Exception {
    if(userstats != null){
        log.debug("persisting userstats to db");
        this.getEntityManager().persist(userstats);
        this.getEntityManager().flush();
    }
}

これから回復するにはどうすればよいですか?

4

1 に答える 1

0

まず、スコープを指定しても、指定したスコープでComponent.getInstance()コンポーネントを作成した結果にはなりません。EntityManagerインスタンスは常に会話コンテキスト内に存在します (一時的または長時間実行されます)。のscopeパラメーターはgetInstance()、すべてのコンテキストで高価な検索を回避するために、コンポーネントが存在するコンテキストを示唆する唯一の目的を果たします (コンテキストを指定しないか、間違ったコンテキストを指定した場合に発生します)。

前のエラーのため、トランザクションはロールバックのマークが付けられています。とにかくエンティティマネージャーがコミットされた場合、それはトランザクションではありません (実際、トランザクションは、エラーが発生した場合に何も永続化されないことを保証します)。ログイン トランザクションを統計収集から分離する場合、最も簡単な解決策は、非同期イベント内でメソッドを実行することですsaveUserStatsToDb(トランザクションはスレッドにバインドされるため、別のスレッドを使用すると、イベントが別のトランザクションで処理されることが保証されます)。

このようなもの:

@Observer("saveUserStatsEvent")
@Transactional
public void saveUserStatsToDb(UserStats stats) {
    ((EntityManager)Component.getInstance("entityManager")).persist(stats);
}

そしてあなたのcreateUserStats方法では:

Events.instance().raiseAsynchronousEvent("saveUserStatsEvent", this.userStats);

ただし、これはトランザクションを 2 つに分割することで問題を回避するだけです。本当に解決したいのは、問題の根底にあるロック状態です。

于 2012-08-03T17:32:39.457 に答える