Experiência maluca: agrupar leituras do MongoDB em lote

Observação: isso não é algo que você deve usar na produção. É apenas um experimento neste estágio, independentemente dos resultados do teste. Não use isso.

O problema

Por alguma razão, seu aplicativo está enviando muitas solicitações individuais ao banco de dados Mongo. Muitos deles vão para a mesma coleção. Por causa do número de solicitações, eles estão atrapalhando um ao outro. E todos eles incorrem em custos associados à codificação, decodificação, latências de rede (yay TCP).

A (ideia para) solução

Digamos que interceptemos todas as criações de cursor e, em seguida, mesclemos as solicitações na mesma coleção, enviemos apenas uma única consulta ao Mongo e dividamos os resultados assim que os recebemos. Portanto, em vez de duas consultas distintas:

{_id: 1}, {_id: 2}

Acabamos de enviar um:

{_id: {$in: [1,2]}}

Funciona?

Sim! Podemos observar que o tempo entre a primeira consulta e a última é reduzido em até 70%. Ou em termos reais, 12 ms x 35 ms.

Claro, esse número é um pouco confuso: em um teste maior (8.500 registros, melhoria de 840 para 450ms de 47%), as latências médias aumentam em 221% (75ms vs 166ms). (Ou seja, o tempo entre fazer o cursor e toArray tirar qualquer coisa da outra extremidade.)

Isso ajuda o mongo?

Dependendo da consulta, sim, com certeza! Em alguns testes, isso fez com que a carga do mongo fosse de 1 ms contra 35 ms em solicitações paralelas. Em outros, não teve efeito. Eu ainda não vi isso aumentar significativamente a carga do mongo, mas não estou descartando a possibilidade de isso aumentar em alguns casos.

Isso é uma panacéia?

Claro que não! Algumas consultas simplesmente não se prestam a esse tipo de coisa. Considere, por exemplo, o seguinte par:

{a: 1, b: 3}, {a: 2, b: 5}

Isso se tornaria quando combinado:

{a: {$in: [1, 2]}, b: {$in: [3, 5]}}

Que corresponderia a documentos como:

{a: 1, b: 5} or {a: 2, b: 3}

Eles são filtrados pelo receptor, mas ainda são transferidos pelo fio. Se a combinação de campos em sua consulta for altamente seletiva, mas os campos individuais não, isso provavelmente não ajudará (e pode até prejudicar nesses casos).

Que outros problemas existem?

Atualmente, a implementação é muito estúpida sobre como mescla e verifica as consultas. Por exemplo, qualquer uso de operadores de consulta ($ lt, $ in, $ regex) o quebrará completamente. Há suporte para verificar os objetos de consulta antes de enviá-los em lote, mas isso ainda não foi feito.

Os cursores retornados não suportam (e pelo que eu posso dizer, não podem) limitar, ignorar, contar, etc. Embora seja fácil implementá-los de forma que os cursores sejam desbloqueados, novamente isso ainda não foi feito.

Posso experimentar?

Sim, claro! Está aqui: https://github.com/richthegeek/mongroup

Posso ajudar?

Bem, você certamente pode criar problemas ou bifurcar ou enviar-me PRs ou o que for. Se esses ganhos de desempenho realmente acontecerem, eu adoraria que isso fosse estável o suficiente para uso na produção.

Sugestões, comentários, críticas?

Manda brasa!