5

基本的に変更できない、同じ構造のレガシーデータベースの群れへのさまざまな統合に取り組んでいます。このために、メタ情報、ルーティングルールなどを保持し、レガシーデータベースのデータを一時的に保持するための補助データベースを追加しました。

私たちは主にNHibernateを使用してデータベースに接続しています。1つのアプリケーションはWCFサービスであり、受信データを実際に幅の広い(数十の列)ネストされたテーブルに挿入する必要があります。明らかに、パフォーマンスが問題であるため、NHibernateのトランザクションで可能な限り経済的になることを目指しています。同時に、並行性が問題であるように見えました。本番環境では、ゾンビ化されたトランザクションエラー(デッドロック)が発生し始めていました。

私はこれら2つの問題に対処するためにバランスをとっていますが、並行性の問題を実際に排除していません。

サービスの動作は、次のように、一度に1つのリクエストを処理するように設定されています。

[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall, ConcurrencyMode=ConcurrencyMode.Single]
public class LegacyGateService : ILegacyGateService

以前、インターネットからの「インスピレーション」(読み取り:コピー/貼り付け)の後、補助データベースとレガシーデータベースにそれぞれXxxNHibernateUtilのようなクラスのセットを追加することになりました。これらのクラスはNHibernateセッションを制御し、事前に初期化されたSessionFactoriesからセッションを生成または再利用します。

補助データベースの場合、次のようになります。

public static class LegacyGateNHibernateUtil
{
    private static readonly ISessionFactory sessionFactory = BuildSessionFactory();

    private static ISessionFactory BuildSessionFactory()
    {
        try
        {
            Configuration Cfg = new Configuration();
            Cfg.Configure();
            Cfg.AddAssembly("LegacyGate.Persistence");
            return Cfg.BuildSessionFactory();
        }
        catch (Exception ex)
        {
            throw ex;
        }
    }

    public static ISessionFactory GetSessionFactory()
    {
        return sessionFactory;
    }

    public static ISession GetCurrentSession()
    {
        if (!CurrentSessionContext.HasBind(GetSessionFactory()))
            CurrentSessionContext.Bind(GetSessionFactory().OpenSession());

        return GetSessionFactory().GetCurrentSession();
    }

    public static void DisposeCurrentSession()
    {
        ISession currentSession = CurrentSessionContext.Unbind(GetSessionFactory());

        if (currentSession != null)
        {
            if (currentSession.IsOpen)
                currentSession.Close();
            currentSession.Dispose();
        }
    }
}

トランザクションにセッションが必要な場合は常に、現在のセッションが検索され、サービス要求の呼び出し中に再利用されます。または少なくとも:それが起こっていると思われることです。

編集:セッションコンテキストはもちろん、次のようにhibernate.cfg.xmlで設定されます。

  <property name="current_session_context_class">call</property>

レガシーデータベースの場合、NHibernateUtilはさまざまな可能なデータベースを処理するように適合されています。このため、各接続は独自のSessionFactoryを構築し、Dictionaryコレクションで検索する必要があります。それ以外の点では、原則は同じです。

WCFStormを使用したテストでは、一度に1つのリクエストを送信する場合は問題なく機能しているようですが、負荷テストを開始するとすぐに、エージェントが1つだけで間隔が長い場合でも、同時リクエストを指すさまざまな種類の例外が多数発生します。お互いを妨害するトランザクション。IsolationLevelを微調整してみましたが、今は役に立ちます。

同じデータベースへの異なるトランザクションが整然と処理され、相互に干渉しないように、セッションを異なる方法で生成および処理する必要があると思います。ただし、これを機能させる方法についての洞察が不足しています。どんな助けでも大歓迎です!


編集1つのサービスメソッドで、複数のエージェントを使用してテストする場合、最初の12程度の呼び出しが正常に機能し、その後、補助データベースにのみ関係する次の一連の例外が表示され始めます。

  1. 「IDataReaderをNDataReaderに変換する際に問題が発生しました」/「リーダーが閉じているときにMetaDataを呼び出そうとして無効になりました。」
  2. 「コレクションの読み込みへの不正アクセス」
  3. 「SQL例外で開始に失敗しました」/「タイムアウトが期限切れになりました。操作が完了する前にタイムアウト期間が経過したか、サーバーが応答していません。」
  4. 「クエリを実行できませんでした」/「ExecuteReaderには、開いていて使用可能な接続が必要です。接続の現在の状態は閉じています。」
  5. 「コレクションを初期化できませんでした:」/「タイムアウトが期限切れになりました。操作が完了する前にタイムアウト期間が経過したか、サーバーが応答していません。」
  6. 「トランザクションが正常に開始されませんでした」
  7. 「トランザクションは現在の接続に関連付けられていないか、完了しています。」
  8. 「コレクションを初期化できませんでした:」/「リーダーが閉じているときにReadを呼び出そうとしても無効です。」

少なくとも例外1は、同じセッションが複数のスレッドによってアクセスされていることを示しています(おそらく呼び出し)。また、他のものは、現在のセッションが他のプロセスによって中断されていることを示しています。しかし、呼び出しを分離してキューに入れようとしたときに、これはどのようになりますか?

別のサービス方法の場合、これらの問題は補助データベースでは発生しませんが、しばらくすると、レガシーデータベースへのトランザクションでZombiedTransaction例外(デッドロック)が発生し始めます。それでも...何が得られますか?

4

1 に答える 1

12

簡単な答え:NHibernateセッションを再利用しないでください。

それらは重量のあるオブジェクトではなく、作業単位のパターンに従って作成、操作、および廃棄されるように設計されています。これらを複数のリクエスト間で「共有」しようとすると、意図した使用法に反します。

基本的に、セッションへのアクセスを正しく同期するためのコストは、セッションの再初期化を回避するためにセッションをリサイクルすることによって得られるメリットをほぼ確実に打ち消します。また、これらのコストは、実行するSQLの池の低下であると考えてみましょう。

NHibernateのSessionFactoryが重量級のオブジェクトであることを忘れないでください。スレッドセーフであるため、すぐに使用できるすべてのリクエストで単一のインスタンスを共有でき、共有する必要があります。

コードは概念的に次のようになります。

public class Service : IService
{
    static Service()
    {
        Configuration Cfg = new Configuration();
        Cfg.Configure();
        Cfg.AddAssembly("LegacyGate.Persistence");
        Service.SessionFactory = Cfg.BuildSessionFactory();
    }

    protected static ISessionFactory SessionFactory { get; private set; }

    public void ServiceMethod(...)
    {
        using(var session = Service.SessionFactory.CreateSession())
        {
            // Do database stuff
            ...
        }
    }
}

余談ですが、理想的にはISessionFactory、サービスに依存関係を注入することです。

于 2012-01-05T13:54:26.007 に答える