Статьи

Выпущен PyMongo 2.6

Я рад сообщить, что мы с Берни Хакеттом выпустили PyMongo 2.6 , преемник PyMongo 2.5.2, с новыми функциями и исправлениями.

Пул подключений

Большая новость в PyMongo 2.6 заключается в том, что max_pool_sizeопция на самом деле означает то, что она говорит сейчас.

PyMongo открывает сокеты по мере необходимости для поддержки числа параллельных операций, требуемых многопоточным приложением. В версиях до 2.6 по умолчанию max_pool_sizeбыло 10, и оно фактически не ограничивало количество открытых соединений; он только определил количество соединений, которые будут оставаться открытыми, когда они больше не используются. Рассмотрим этот код:

client = MongoClient(max_pool_size=5)

Старый пул соединений PyMongo будет открывать столько сокетов, сколько вам нужно, без ограничений, во время скачка нагрузки, но как только он закончится, он закроет все, кроме пяти.

В PyMongo 2.6 max означает max. Пул соединений откроется в большинстве max_pool_sizeсокетов для удовлетворения спроса. Так что если у вас max_pool_size5 и пять потоков в данный момент выполняют операции MongoDB, шестой поток заблокирует ожидание возврата сокета в пул одним из предыдущих потоков. Очевидно, это может замедлить ваше приложение, поэтому мы увеличили значение по умолчанию max_pool_sizeс 10 до 100.

Если вы не указали max_pool_sizeпри создании MongoClientили MongoReplicaSetClient, то при обновлении вы получите новое значение по умолчанию, и вам не о чем беспокоиться. Но если вы уже были заменяемым по умолчания, вы должны обеспечить вашим max_pool_sizeявляются достаточно большим , чтобы соответствовать вашему высокому ожидаемому количеству одновременных операций. Розетки открываются только при необходимости, поэтому их стоимость не требует max_pool_sizeбольших затрат . Ошибка в сторону большего значения.

Кроме того, мы добавили еще две опции: waitQueueMultipleограничивает число потоков, которые могут ожидать сокетов, прежде чем PyMongo начнет выдавать исключения. Например, чтобы число официантов было меньше или равно 500:

client = MongoClient(max_pool_size=50, waitQueueMultiple=10)

Когда 500 потоков ожидают сокета, дополнительные официанты генерируют исключения. Используйте эту опцию, чтобы ограничить количество очередей в вашем приложении во время скачка нагрузки за счет дополнительных исключений.

Как только пул достигнет своего максимального размера, дополнительным потокам будет разрешено бесконечно ждать появления соединений, если вы не установили waitQueueTimeoutMS:

client = MongoClient(waitQueueTimeoutMS=100)

Поток, ожидающий соединения более 100 мс (в этом примере), генерирует исключение. Используйте эту опцию, если более важно ограничить продолжительность операций во время скачка нагрузки, чем для завершения каждой операции.

Вся эта сложная работа над пулом соединений была щедро предоставлена Джастином Патрином , разработчиком Idle Games.

Джастин патрин

С необычайным терпением Джастин работал с нами целый месяц, чтобы разобраться с нашей критикой и всеми крайними случаями. Добавление функции в пул соединений PyMongo максимально сложно. Мы поддерживаем Python с 2.4 по 3.3, PyPy и Jython, а также Gevent, и у каждого из них очень разное поведение в отношении параллелизма, ввода-вывода, времени жизни объектов и локальных потоков. Нашему пулу нужен специальный код для каждого из них. Джастин изучил ошибки и сделал один из лучших вкладов, полученных PyMongo от стороннего программиста.

Пакетные вставки

Сопровождающий PyMongo Берни Хакетт добавил замечательную новую функцию: если вы передаете insert()огромный список документов, PyMongo автоматически разбивает его на фрагменты по 48 МБ и передает каждый блок в MongoDB. В прошлом вам приходилось определять, какой размер сообщения будет принимать MongoDB, и следить за тем, чтобы ваши пакетные вставки не превышали его, в противном случае PyMongo выдает исключение. Теперь вы можете передавать произвольное количество документов на сервер за один вызов метода.

Агрегирующие курсоры

Начиная с MongoDB 2.5.1, вы можете передавать результаты из структуры агрегации, а не получать их в одном пакете. Это, наконец, снимает печально известное ограничение в 16 МБ на результаты агрегирования и приближает структуру агрегации к стандартной findоперации. В PyMongo 2.6 мы добавили поддержку этой функции сервера.

Старый стиль получает все документы сразу:

>>> collection.aggregate(pipeline)
{
    'ok': 1.0,
    'result': [
        {'key': 'value1'},
        {'key': 'value2'}
    ]
}

Но теперь вы можете перебирать результаты, и их количество не ограничено:

>>> for doc in collection.aggregate(pipeline, cursor={}):
...     print doc
{'key': 'value1'}
{'key': 'value2'}

Курсоры выхлопных газов

Что касается курсоров, то у MongoDB уже давно есть «исчерпывающие курсоры»: вместо того, чтобы ждать, пока клиент запросит каждую партию результатов запроса, исчерпывающий курсор направляет пакеты клиенту как можно быстрее. Этот перегруженный курсор используется внутри для репликации и дампов базы данных. Берни добавил поддержку выхлопных курсоров в PyMongo, так что теперь вы также можете воспользоваться ими для инструментов Python, которым необходимо извлекать огромные наборы данных из MongoDB по сети с высокой задержкой.

get_default_database

Один сервер MongoDB может иметь несколько баз данных, но нет стандартного способа использовать файл конфигурации, чтобы сообщить вашему приложению, какую базу данных использовать. Вы можете поместить имя базы данных в URI MongoDB, например «mongodb: // host: port / my_database», но когда вы передаете URI, MongoClientвы просто получаете соединение со всем сервером. Не было никакого способа сказать: «Дайте мне соединение только с базой данных, указанной в URI». Я добавил get_default_databaseметод, так что теперь вы можете сделать:

uri = 'mongodb://host/my_database'
client = MongoClient(uri)
db = client.get_default_database()

dbявляется ссылкой на базу данных, указанную вами в URI. Это делает ваше приложение более настраиваемым только с помощью URI, что должно облегчить жизнь разработчикам и работникам.

разное

Вы можете увидеть все 22 проблемы, решенные в этом выпуске, в нашем трекере ошибок. Также было очень интересное исправление от автора GitHub для нашей совместимости с Gevent . Обнови, проверь max_pool_sizeи наслаждайся!