EFコアと大きなトラフィックが最大プールサイズに達するとエラーに達しました

.net-core asp.net asp.net-web-api c# entity-framework-core

質問

私達は私達のWeb APIアプリで私達のMSSQLデータベースを照会するためにASP.NET Entity Framework Coreを使用しています。時々私達が大きいトラフィックがあるとき、DBに問い合わせることはこのエラーと終わります:

タイムアウト期限が切れました。タイムアウト期間は、プールから接続を取得する前に経過しました。これは、すべてのプール接続が使用中で、最大プールサイズに達したために発生した可能性があります。

私たちのDbContextとクエリの使用パターンが正しいのか、あるいはいくつかのusing / disposeパターンが欠けていてエラーがメモリリークのDbContextであるのだろうか。 )私はドキュメントをフォローしています ...

私のconnectionString:

"myConnection": "Server=xxx;Database=xxx;user id=xxx;password=xxx;Max Pool Size=200;Timeout=200;"

私のStartup.cs

public void ConfigureServices(IServiceCollection services)
    {
    .....
    // scoped context            
    services.AddDbContext<MyDbContext>(
            options => options.UseSqlServer(this.Configuration.GetConnectionString("myConnection")));
    }

それから私は依存性注入によって私はdbcontextを使用しました:

public class MyController : Controller
   public MyController (MyDbContext context)
    {
        this.Context = context;            
    }

    public ActionResult Get(int id)
    {
        // querying
        return this.Context.tRealty.Where(x=>x.id == id).FirstOrDefault();
    }

私は次のようなものを使うべきです:

using (var context = this.Context)
        {
            return this.Context.tRealty.Where(x => x.id == id).FirstOrDefault();
        }

しかし、私はDbContext依存性注入を使用しているとき、これは悪いパターンだと思います。

受け入れられた回答

私は問題がデータベースコンテキストクエリからのオブジェクトをメモリキャッシュに格納することによって引き起こされたと思います。私は内部にいくつかの他のサブクエリを持つデータベースコンテキストに1つの大きなLINQクエリを持っていました。メインクエリの最後にFirstOrDefault()を呼び出しましたが、 サブクエリでは呼び出されませんでした 。コントローラはそれで問題なく、デフォルトでクエリを実現します。

 return this.Context.tRealty.AsNoTracking().Where(
                x => x.Id == id && x.RealtyProcess == RealtyProcess.Visible).Select(
                s => new
                { .....

// subquery
videos = s.TVideo.Where(video => video.RealtyId == id && video.IsPublicOnYouTube).
                        Select(video => video.YouTubeId).ToList()), // missing ToList()
.....
 }).FirstOrDefault();

そして、問題がありました - サブクエリがデータベースメモリへの格納場所にあるときにデータベースコンテキストへの接続を保持していました。私がRedis分散キャッシュを実装したとき、それはいくつかの奇妙なエラーで最初に失敗しました。分散キャッシュにはマテリアToList()オブジェクトが必要なので、 ToList()またはFirstOrDefault()をすべてのサブクエリに書き込むときに役立ちます。

今私はすべてのクエリが明示的にマテリアライズされているし、 最大プールサイズに達していないというエラーが発生しました 。そのため、データベースコンテキストクエリからIn Memoryキャッシュにオブジェクトを格納するときは注意が必要です。メモリ内のどこかに接続を保持することを避けるために、すべてのクエリを実現する必要があります。


人気のある回答

startup.csでDbContextの有効期間を設定することができます。

    services.AddDbContext<MyDbContext>(options => options
                                       .UseSqlServer(connection), ServiceLifetime.Scoped);

また、クエリが単純な読み込みである場合は、 .AsNoTracking()を使用してトラッキングを削除することもできます。

スループットを向上させるもう1つの方法は、簡単な読み取りのためにIsolationLevel.ReadUncommittedでトランザクションブロックを使用してロックを防止することです。ダーティリードを望まない場合は、 Snapshot分離レベルを使用することもできます(これは少し制限があります)。

TransactionOptions transactionOptions = new TransactionOptions() { IsolationLevel = IsolationLevel.ReadUncommitted};
using (TransactionScope transactionScope = new TransactionScope(TransactionScopeOption.Required, transactionOptions))
{
  // insert magic here
}

編集:質問の著者が述べたように、上記のコードはEFコアで(まだ)可能ではありません。

回避策は明示的なトランザクションを使用してここで見つけることができます:

    using (var connection = new SqlConnection(connectionString))
    {
        connection.Open();

        using (var transaction = connection.BeginTransaction())
        {
           // transaction.Commit();
           // transaction.Rollback();
        }
    }

私はこれをテストしていない。

編集2:別のテストされていないスニペットで、分離レベルを設定するコマンドを実行できます。

                using (var c1= new SqlConnection(connectionString))
                {
                    c1.Open();
                    // set isolation level
                    Exec(c1, "SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;");
                    Exec(c1, "BEGIN TRANSACTION;");
                    // do your magic here
                }

Execで:

        private static void Exec(SqlConnection c, string s)
        {
            using (var m = c.CreateCommand())
            {
                m.CommandText = s;
                m.ExecuteNonQuery();
            }
        }

編集3:によると、そのスレッド、トランザクションは、.NET Coreバージョン以降1.2からサポートされます。

@mukundabrtこれはdotnet / corefx#2949によって追跡されます。 TransactionScopeは既に.NET Coreに移植されていますが、.NET Core 1.2でのみ使用可能です。



Related

ライセンスを受けた: CC-BY-SA with attribution
所属していない Stack Overflow
このKBは合法ですか? はい、理由を学ぶ
ライセンスを受けた: CC-BY-SA with attribution
所属していない Stack Overflow
このKBは合法ですか? はい、理由を学ぶ