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!