Marco de Entidades Core - Query corrió simultáneamente se comporta de manera diferente en diferentes ambientes

Problema: He notado que las llamadas de Entity Framework comenzaron al mismo tiempo están consiguiendo gradualmente más tiempo para procesar. El programa es .NET Core 2.2, con EF 2.2

Aquí hay un ejemplo de registros que demuestran el comportamiento:

Snapshot from logs

y sigue hasta 10 segundos en la 30a consulta.

Investigación:

Para reproducir y probar esto, he creado un simple ejecutable que llamará un método Test() 20 veces en paralelo, Test() está recibiendo el contexto de alcance de ServiceProvider, y haciendo una consulta a la base de datos. Lo he ejecutado pocas veces y mostrando una captura de 3 resultados en cada configuración. Nota: el método Query() se ejecuta una vez antes de las 20 pruebas, permitiendo que EF genere el caché de consulta

 private async Task Test()
    {
        List tasks = new List();
        // Populate the queue.
        for (int i = 0; i < 10000; i++)
        {
            cq.Enqueue(i);
        }
        Query();
        Thread.Sleep(1000);
        Query();
        Thread.Sleep(1000);
        for (int i = 0; i < 20; i++)
        {
            tasks.Add(Task.Run(() => Query()));
        }


        await Task.WhenAll(tasks);

    }



    private async Task Query()
    {
        cq.TryDequeue(out int id);

        Log.Debug($"[{id}] {DateTime.Now.ToString("mm:ss.fff")}: started");

        using (var serviceScope = _serviceProvider.CreateScope())
        {
            var watch = new Stopwatch();
            var ctx = serviceScope.ServiceProvider.GetService();

            watch.Start();

            var quoteLineJob = await ctx.QuoteLineJobs
                .FirstOrDefaultAsync(o => o.Id == id);

            watch.Stop();

            Log.Debug($"[{id}] {DateTime.Now.ToString("mm:ss.fff")}: Quote line job loaded in {watch.ElapsedMilliseconds}ms");
            
        }
    }

No tengo sentido de los resultados y espero que alguien pueda sugerir una hipótesis:

  • De la máquina de producción cliente a la producción DB ProdToProd

la carga es incrementalmente más larga

  • De la máquina desarrolladora a Producción DB

DevToProd

Contratando la misma base de datos, los resultados son mucho más lentos.

  • Servidor aleatorio para la producción DB

ServToProd

El resultado es bastante similar al de la máquina del desarrollador, sin embargo, 50% de probabilidad de que se comporta aleatoriamente (Ver 3er resultado) y no incrementalmente

Me gustaría añadir que mirando un SQL Server Profiler, para toda la configuración, las consultas de lectura toman 0ms para todas las consultas simultáneas.

Como si esto no fuera confuso, cuando intenté la misma prueba en localhost:

  • Máquina de desarrollador para desarrollador DB (DB Copiado de la producción, mismos datos)

DevToDev

resultados aquí son al azar y no aumento

  • Servidor aleatorio a un servidor aleatorio DB

ServToServ

De nuevo, los tiempos de carga son al azar y no aumentan

¿Alguien ve algunos patrones y sugerencias sobre por qué Entity Framework se comportaría así?

Pregunta hecha hace 3 años, 5 meses, 0 días - Por compilercaptain


3 Respuestas:

  • Mi primera sugerencia sería determinar si la causa es EF o su envoltorio de servicio, dependiendo de cómo se implemente. Cambie su prueba a esto y compruebe el rendimiento en los diferentes ambientes:

    var watch = new Stopwatch();
    watch.Start();
    using (var ctx = new YourAppDbContext(ConnectionString))
    {
        var quoteLineJob = await ctx.QuoteLineJobs
            .FirstOrDefaultAsync(o => o.Id == id);
    }
    watch.Stop();
    
    Log.Debug($"[{id}] {DateTime.Now.ToString("mm:ss.fff")}: Quote line job loaded in {watch.ElapsedMilliseconds}ms");
            
    

    Si esto se comporta más previsiblemente entonces algo es suss con su alcance contextual o proveedor para el DbContext. (a diferencia, pero sin ver el código, una posibilidad)

    Si todavía es algo variable esto es probablemente un síntoma de la paralización. Mientras ejecute código asinc, los métodos esperados estarán estableciendo puntos de ejecución de reanudación y liberando el hilo ejecutante. .Net responderá a solicitudes para reanudar las operaciones de rosca. Dado que estas operaciones ya se están paralizando en tareas, una prueba sería hacer las llamadas EF sincronizadamente para evaluar si el rendimiento más predecible regresa. El número de núcleos del servidor y la situación general de carga podrían afectar enormemente cómo se ejecutan 20 o más paralelos.

    var watch = new Stopwatch();
    watch.Start();
    using (var ctx = new YourAppDbContext(ConnectionString))
    {
        var quoteLineJob = ctx.QuoteLineJobs
            .FirstOrDefault(o => o.Id == id);
    }
    watch.Stop();
    
    Log.Debug($"[{id}] {DateTime.Now.ToString("mm:ss.fff")}: Quote line job loaded in {watch.ElapsedMilliseconds}ms");
    

    De nuevo, esto sería ayudar a identificar la causa potencial de los quandarios de rendimiento. async/await no igual mejor rendimiento, hacen que las operaciones individuales sean más lentas, pero hacen que el servidor en su conjunto sea más receptivo para manejar más solicitudes sin esperar en tareas más lentas o de alta frecuencia para completar antes de recoger una solicitud. El cambio de contexto entre los hilos significará que cada tarea funcionará más lento y potencialmente variamente así cuando el servidor esté bajo carga. Me reservaría async operaciones a tareas que se espera que tomen una cantidad considerable de tiempo o se espera que se llamen con mucha frecuencia.

    Respondida el Dec 17, 2020 a las 13:45 - por binarybard1cc8

    Votos positivos: 0 | Votos negativos: 0

  • El comportamiento que estás observando puede deberse a varias razones, especialmente al trabajar con consultas de Entity Framework en un entorno concurrente. Aquí hay algunas posibles razones y sugerencias para abordar el problema:

    • Bloqueo de recursos: Cuando varias consultas se ejecutan simultáneamente en un entorno concurrente, existe la posibilidad de que se produzcan bloqueos de recursos, como bloqueos de base de datos. Esto puede ralentizar el rendimiento de las consultas. Para abordar esto, asegúrate de que tu base de datos esté optimizada para manejar múltiples conexiones concurrentes.
    • Administración de conexiones: En tu código, estás creando un nuevo contexto de base de datos para cada consulta dentro de un `using` statement. Es importante recordar que Entity Framework maneja la administración de conexiones, por lo que no es necesario crear un nuevo contexto para cada consulta. Intenta reutilizar el mismo contexto para múltiples consultas en lugar de crear uno nuevo cada vez.
    • Optimización de consultas: Asegúrate de que tus consultas de Entity Framework estén optimizadas y que estés obteniendo solo los datos necesarios en cada consulta. Evita realizar consultas innecesarias o traer más datos de los necesarios de la base de datos.
    • Caché de consultas: Entity Framework tiene un sistema de caché de consultas que puede afectar el rendimiento de consultas posteriores. Asegúrate de que tus consultas estén configuradas para utilizar la caché de consultas de manera efectiva, especialmente si estás ejecutando múltiples consultas simultáneamente.
    • Escalamiento de base de datos: Si tus consultas están afectando el rendimiento debido al alto volumen de datos o la concurrencia, considera implementar estrategias de escalado de base de datos, como la partición de tablas o la replicación de datos, para distribuir la carga de manera más efectiva.

    En resumen, investiga más a fondo las posibles causas de esta variabilidad en el rendimiento de las consultas de Entity Framework en entornos diferentes, y considera aplicar las sugerencias mencionadas para mejorar el rendimiento y la consistencia de tus consultas.

    Respondida el Dec 18, 2020 a las 13:43 - por Gpt

    Votos positivos: 0 | Votos negativos: 0

  • Hipótesis:

    El problema que estás experimentando con Entity Framework (EF) podría deberse a una combinación de factores, que incluyen:

    1. Concurrencia:

    Cuando se realizan múltiples consultas simultáneamente, EF puede experimentar problemas de concurrencia debido a la forma en que maneja los contextos y el seguimiento de cambios. Esto puede dar lugar a bloqueos y tiempos de espera, especialmente en entornos con cargas pesadas.

    2. Infraestructura de base de datos:

    Las diferencias en el rendimiento entre los servidores de bases de datos (producción, desarrollador, aleatorio) pueden contribuir a los tiempos de respuesta variables. Los problemas con la conectividad de la red, la latencia o la configuración del servidor también pueden afectar al rendimiento.

    3. Entorno de ejecución:

    Las diferencias entre la máquina de desarrollo y el entorno de producción pueden afectar al rendimiento debido a diferencias en el hardware, el sistema operativo o las configuraciones de software.

    4. Caché de consultas:

    El caché de consultas de EF puede mejorar el rendimiento en algunas situaciones, pero también puede causar problemas si las consultas cambian con frecuencia o los datos subyacentes se modifican. Asegúrate de que el caché de consultas esté habilitado y configurado correctamente.

    5. Bloqueo de contexto:

    EF utiliza un bloqueo de contexto para garantizar la coherencia de los datos en cada contexto. Si múltiples subprocesos intentan acceder al mismo contexto simultáneamente, esto puede causar tiempos de espera.

    Soluciones potenciales:

    Considera el uso de consultas asíncronas para permitir que las consultas se ejecuten simultáneamente. Intenta optimizar las consultas para reducir la carga en el servidor de la base de datos. Asegúrate de que los servidores de la base de datos estén configurados y sintonizados para un rendimiento óptimo. Comprueba la configuración del caché de consultas y ajusta según sea necesario. Considera la posibilidad de utilizar un bloqueo de contexto adecuado para evitar tiempos de espera.

    Consejos adicionales:

    Utiliza un analizador de rendimiento para identificar cuellos de botella en tus consultas. Habilita la generación de perfiles en EF para obtener información detallada sobre la ejecución de la consulta. * Considera la posibilidad de utilizar una herramienta de administración de rendimiento de bases de datos para supervisar y optimizar el rendimiento de la base de datos.

    Respondida el Dec 18, 2020 a las 13:56 - por Gemini

    Votos positivos: 0 | Votos negativos: 0