Пул Asyncpg ожидает всех результатов

#python #python-3.x #postgresql #connection-pooling #asyncpg

#python #python-3.x #postgresql #объединение в пул соединений #asyncpg

Вопрос:

Я использую asyncpg для выполнения сложного запроса, который часто занимает около 15-20 секунд для вычисления. Я экспериментировал с минимальным и максимальным размерами пула и заметил странное поведение. Когда я пытаюсь выполнить несколько запросов, они выполняются одновременно, но результаты возвращаются в пакете. Пакет всегда имеет тот же размер, что и пул. Такое поведение приводит к длительному времени первого ответа, например, 50 секунд или около того. После первой партии мне нужно подождать еще 50 секунд, чтобы получить другие результаты. Похоже, что asyncpg ожидает сбора всех результатов из пула и их возврата, вместо того, чтобы возвращать результаты, как только они будут готовы.

Есть ли способ получить результат одного запроса сразу после его выполнения? Может быть, какая-то настройка, которую я пропускаю?

Некоторое время я мог поддерживать 25 подключений, но это похоже на взлом, который мне действительно не нравится.

Я использую DSN для создания пула, если это важно.

 db_pool = await asyncpg.create_pool(loop=self._loop, **params)
  

Вот код, который я использую для отправки запроса в БД:

     async def first(self, query: str, values: List, db_name: str = 'default'):
        pool = self.get_pool(db_name)

        async with pool.acquire() as conn:
            return await conn.fetchrow(query, *values)
  

Прямо сейчас я тестирую размеры пула по умолчанию (10 max, 10 min) и отправляю 25 запросов одновременно.

 result for 336997:  45.43796348571777 s
result for 368406:  45.43796348571777 s
result for 288307:  45.81912803649902 s
result for 283236:  46.499717235565186 s
result for 296000:  49.140310764312744 s
result for 304671:  49.20531177520752 s
result for 283685:  49.26837992668152 s
result for 283772:  49.3363881111145 s
result for 283720:  49.3753764629364 s
result for 294811:  49.39737892150879 s
result for 325604:  112.60201215744019 s
result for 336997:  112.60101222991943 s
result for 283028:  112.62509346008301 s
result for 291122:  113.41229104995728 s
result for 281105:  115.48561716079712 s
result for 304874:  115.59060764312744 s
result for 281875:  115.73372554779053 s
result for 283219:  115.73472547531128 s
result for 312094:  116.00303101539612 s
result for 312094:  116.0290174484253 s
result for 368406:  157.77449679374695 s
result for 325604:  157.77449679374695 s
result for 281932:  157.79654741287231 s
result for 290687:  157.79554748535156 s
result for 304874:  158.38678884506226 s
  

Если я установлю max и min равными 25, когда я получу эти результаты:

 result for 368406:  96.23042106628418 s
result for 368406:  96.22842144966125 s
result for 283236:  97.59920930862427 s
result for 304671:  99.69211030006409 s
result for 281932:  107.54676508903503 s
result for 283685:  107.95523738861084 s
result for 281875:  108.28549408912659 s
result for 283720:  108.39060115814209 s
result for 283028:  108.388601064682 s
result for 296000:  108.44459056854248 s
result for 283772:  108.55759739875793 s
result for 291122:  108.59360837936401 s
result for 294811:  108.90663266181946 s
result for 336997:  109.07162356376648 s
result for 325604:  109.07562470436096 s
result for 325604:  109.17762279510498 s
result for 336997:  109.30463767051697 s
result for 312094:  109.40563464164734 s
result for 312094:  109.40663456916809 s
result for 281105:  109.63970899581909 s
result for 290687:  109.66070008277893 s
result for 304874:  109.66170001029968 s
result for 288307:  109.68773555755615 s
result for 304874:  109.68273568153381 s
result for 283219:  109.68573522567749 s
  

5/5 соединений приносят ужасные результаты

 result for 288307:  38.87580060958862 s
result for 325604:  38.87280225753784 s
result for 283219:  38.87380290031433 s
result for 283772:  38.98385691642761 s
result for 304671:  39.80011057853699 s
result for 368406:  94.94180464744568 s
result for 283720:  94.94180464744568 s
result for 296000:  94.98080492019653 s
result for 294811:  95.45388603210449 s
result for 283685:  95.85135459899902 s
result for 291122:  156.6862394809723 s
result for 325604:  156.68523907661438 s
result for 336997:  156.68724012374878 s
result for 304874:  156.8772156238556 s
result for 283236:  157.72206735610962 s
result for 336997:  219.94082736968994 s
result for 368406:  219.96384191513062 s
result for 312094:  220.39882922172546 s
result for 281105:  220.8170771598816 s
result for 283028:  221.18352794647217 s
result for 290687:  283.105571269989 s
result for 281932:  283.10657024383545 s
result for 304874:  283.105571269989 s
result for 281875:  283.21556425094604 s
result for 312094:  283.7060844898224 s
  

Комментарии:

1. Как вы отправляете 25 запросов одновременно, если у вас есть максимум 10 подключений в одном из ваших тестов? По крайней мере, некоторые запросы должны обрабатываться последовательно в этом экземпляре.

2. Вы понимаете, что postgres порождает процесс для обслуживания запросов / запросов от каждого соединения, да? Таким образом, для параллельного выполнения 25 запросов у вас должно быть 25 подключений. Вас также должно беспокоить снижение скорости обработки каждого запроса, поскольку у вас больше подключений (от 40 секунд / запрос с 5 соединениями до> 100 секунд / запрос с 25 соединениями). Это говорит о том, что существует конфликт с каким-то ресурсом, который замедляет обработку. Это аналогичным образом повлияет на любые другие одновременные запросы к базе данных, выполняемые другими приложениями.

3. @Dunes Я понимаю, что все 25 запросов не будут обрабатываться одновременно, если есть только 10 подключений. Мне было интересно, почему пул ожидает результатов от всех подключений, вместо того, чтобы возвращать результат, как только он будет готов к 1 соединению и начнет работать над новым запросом, как только соединение станет доступным? Почему он ожидает других подключений в пуле?

4. Похоже, что он не ждет. Если вы посмотрите на пул из 10, первые 10 результатов вернутся между 45 и 49 секундами. Если бы он собирал результаты, я бы ожидал увидеть их все в течение примерно 100 миллисекундного окна. Возможно, есть какое-то свойство запроса или набора данных, которое означает, что он имеет довольно равномерное время обработки.

Ответ №1:

Каждый запрос ожидает только своих собственных результатов. Если все запросы занимают примерно одинаковый объем работы, то все они (из тех, которые фактически выполняются) завершаются примерно через одинаковое количество времени. И в любом случае не все так похоже, разброс от 45,4 до 49,4 не является тривиальным. Если бы он каким-то образом намеренно синхронизировал их, вы бы подумали, что это сработало бы лучше.

Некоторое время я мог поддерживать 25 подключений, но это похоже на взлом, который мне действительно не нравится.

Что бы вы предпочли сделать вместо этого? Использовать меньшее число? Более высокий? Случайное число, которое меняется с минуты на минуту? Вы должны установить ограничение на пул соединений. Почему 25 будет взломом, а какое-то другое число не будет?

Ответ №2:

Это была проблема на стороне БД, нам пришлось увеличить ресурсы, чтобы решить эту проблему.