VOLTAR à lista de blogs
Learn About

Desempenho do DecisionRules: números reais de latência e throughput dos nossos testes de carga

Dois testes de carga k6, 23,1 milhões de requisições e zero falhas. Veja os percentis de latência e os números de throughput medidos para o DecisionRules no Kubernetes, incluindo o ponto em que o desempenho começa a se degradar.

Desempenho do DecisionRules: números reais de latência e throughput dos nossos testes de carga hero image

A maioria das alegações de desempenho no mercado de motores de regras não pode ser comprovada. “Tempos de resposta abaixo de um segundo.” “Escala massiva.” “Throughput de nível empresarial.” Nada disso informa o que colocar em um plano de capacidade.

Este artigo faz o oposto. A seguir estão os resultados medidos de dois testes de carga k6 executados contra o DecisionRules no Kubernetes, incluindo as condições do teste, a distribuição dos percentis e o ponto exato em que a latência começa a se degradar. Cada número pode ser reproduzido a partir dos relatórios brutos do k6 e está limitado à configuração que o produziu.

Em resumo: uma única regra de negócio de complexidade média é resolvida em menos de 10 milissegundos em média — e permanece abaixo de 10 ms até cerca de 8.900 resoluções por segundo. Levado ao throughput máximo, a mesma plataforma sustenta mais de 15.000 resoluções por segundo com latência média de apenas 13,1 milissegundos. Throughput e latência não são a relação de compromisso que a maioria das pessoas espera, desde que a implantação possa escalar horizontalmente.


Resumo dos números verificados

MétricaValorCondições
Latência média, uma tabela de decisão 8.1 ms ~5.200 resoluções/s, 50 clientes simultâneos
Latência média, uma árvore de decisão 8.2 ms ~5.200 resoluções/s, 50 clientes simultâneos
Latência média, regra de fluxo com 3 tabelas 12.1 ms ~5.200 resoluções/s, 50 clientes simultâneos
Latência média combinada, carga moderada 9.5 ms ~5.200 resoluções/s, todos os tipos de regra
Latência média combinada, throughput máximo 13.1 ms ~15.200 resoluções/s, 200 clientes simultâneos
Latência p95, throughput máximo 29.0 ms ~15.200 resoluções/s, 200 clientes simultâneos
Throughput sustentado, escalabilidade elástica ~15.200 resoluções/s 200 clientes simultâneos, limite do HPA de 11 réplicas
Throughput sustentado, limitado a 5 réplicas ~9.800 resoluções/s 200 clientes simultâneos, limite do HPA de 5 réplicas
Total de requisições nos dois testes 23,170,203 ~42 minutos de tempo de teste combinado
Requisições com falha nos dois testes 0 taxa de erro de 0,000%

Todos os valores de latência são tempos de ida e volta observados pelo cliente (http_req_duration no k6) — incluindo o tráfego de rede até o ingresso e de volta, não apenas a execução no servidor.

O que testamos e como

Ambos os testes usaram k6 para acionar a Rule Solver API por HTTP contra o DecisionRules implantado no Kubernetes com um Horizontal Pod Autoscaler. Os geradores de carga foram executados na mesma região do cluster. A validação de resposta foi ativada em todas as chamadas: uma requisição só conta como bem-sucedida se retornar HTTP 200 e a saída da decisão passar por uma verificação de conteúdo. Em 23,1 milhões de chamadas, 100% das verificações foram aprovadas.

Cada iteração do k6 executou uma transação de negócio composta por três chamadas separadas ao Rule Solver:


RegraTipoComplexidade
Regra 1 Tabela de decisão, 20 linhas Média
Regra 2 Árvore de decisão Média
Regra 3 Fluxo — executa três tabelas de decisão e, em seguida, realiza cálculos de resumo sobre os resultados combinados Maior

Essa combinação é intencional. As regras 1 e 2 representam o caso cotidiano: uma única lógica de decisão autônoma. A regra 3 representa um processo de decisão orquestrado, em que uma chamada de API aciona várias regras e pós-processamento. Testar ambas no mesmo teste permite separar o custo de resolver uma regra do custo de orquestrar um processo.

O k6 informa iterações por segundo, e cada iteração aqui corresponde a três chamadas ao Solver. Todos os números de throughput neste artigo foram recalculados para resoluções por segundo.

As cargas úteis eram realistas, mas não grandes: aproximadamente 422 bytes de JSON de entrada por chamada e 1,2 KB de saída de decisão por resposta.

A quantidade de usuários virtuais avançou por 50 → 100 → 150 → 200, mantendo cada nível por cinco minutos para que o escalonador automático se estabilizasse antes das medições. Os dois testes foram idênticos em todos os aspectos, exceto pelo limite do escalonador — 11 réplicas em um e 5 no outro.


Carga normal: regras individuais são resolvidas em menos de 10 milissegundos

Com 50 clientes simultâneos — 5.243 resoluções por segundo de forma sustentada, o que já representa tráfego de produção considerável — os números por regra são:

RegraMédiaMedianap90p95p99
Tabela de decisão (20 linhas) 8.1 ms 6.9 ms 13.0 ms 14.8 ms 18.1 ms
Decision Tree 8.2 ms 6.8 ms 13.4 ms 15.2 ms 18.7 ms
Fluxo (3 tabelas + cálculos) 12.1 ms 10.2 ms 19.4 ms 21.3 ms 25.1 ms
Combinado, todas as chamadas 9.5 ms 8.4 ms 16.2 ms 18.4 ms 22.9 ms

Uma tabela de decisão ou árvore de decisão de complexidade média é resolvida em cerca de 8 milissegundos em média, menos de 7 milissegundos na mediana, medido do lado do cliente com a ida e volta pela rede incluída. Dois terços desse tempo são da resolução da regra; o restante é HTTP e trânsito de rede.

O número abaixo de 10 milissegundos não é um efeito de baixa carga. Ele se mantém à medida que o throughput aumenta:


ThroughputTabela de decisão (média)Árvore de decisão (média)Abaixo de 10 ms?
5,243 solves/s 8.1 ms 8.2 ms Sim
8,908 solves/s 9.6 ms 9.7 ms Sim
12,302 solves/s 10.3 ms 10.6 ms Mediana ainda abaixo de 10 ms
15,215 solves/s 11.1 ms 11.5 ms Não — 11 ms

A latência média de uma regra individual permanece abaixo de 10 milissegundos até aproximadamente 8.900 resoluções por segundo e a mediana permanece abaixo de 10 milissegundos até aproximadamente 12,300 solves per second. Somente na maior carga testada uma regra individual leva mais de 11 milissegundos.

Se sua carga de trabalho é dominada por tabelas e árvores de decisão individuais, em vez de orquestração em várias etapas, uma resposta média abaixo de 10 milissegundos é uma expectativa razoável, não o melhor caso.

O custo da regra de fluxo

A regra de fluxo — três tabelas de decisão mais cálculos de resumo, tudo por trás de uma chamada de API — teve média de 12,1 ms em carga moderada e 16,6 ms no throughput máximo. Em comparação com uma única tabela na mesma carga, isso representa um multiplicador de 1,5×, estável em todos os níveis de simultaneidade testados.

Essa proporção é a parte interessante. O fluxo realiza pelo menos três vezes o trabalho de resolução de regras de uma tabela de decisão única, além da agregação dos resultados, por cerca de uma vez e meia a latência. Executar essas três tabelas como três chamadas separadas à API do Solver custaria aproximadamente 33 milissegundos no throughput máximo — três idas e voltas, três conexões HTTP, três trajetos de rede. Orquestrá-las em um único fluxo custa 16,6 ms.

Consolidar a lógica de decisão em várias etapas em um fluxo é aproximadamente duas vezes mais rápido do que chamar as regras individualmente a partir da sua aplicação.


Throughput máximo: 15.200 resoluções por segundo

Aumentando a simultaneidade em uma implantação autorizada a escalar para 11 réplicas:


Concurrent clientsSustained throughputMedianaMédiap90p95p99
50 5,243 solves/s 8.4 ms 9.5 ms 16.2 ms 18.4 ms 22.9 ms
100 8,908 solves/s 9.4 ms 11.2 ms 19.4 ms 23.1 ms 29.1 ms
150 12,302 solves/s 10.5 ms 12.1 ms 22.3 ms 27.2 ms 35.2 ms
200 15,215 solves/s 11.9 ms 13.1 ms 24.1 ms 29.0 ms 37.9 ms

Leia a primeira e a última linhas em conjunto. O throughput aumentou 2.9×. A latência média aumentou em 3,6 milissegundos. A latência mediana aumentou em 3,5 milissegundos.

Isso se aproxima de uma escalabilidade linear. Cada aumento de simultaneidade de 50 clientes trouxe cerca de 3.400 resoluções adicionais por segundo, e o custo marginal de latência de cada etapa permaneceu abaixo de 2 ms. O sistema ainda estava escalando quando o teste terminou — 15.215 resoluções/s é a maior taxa que medimos, não um limite que encontramos.

De forma sustentada, essa taxa representa 54,8 milhões de decisões por hora, ou 1,31 bilhão por dia. Nenhuma requisição falhou nas 12.756.996 chamadas deste teste.

Latência da transação completa

Como cada iteração fez as três chamadas em sequência, o teste também mede a transação de negócio completa:


ThroughputMédia da transaçãoMedianp95p99
5,243 solves/s 28.6 ms 24.3 ms 47.9 ms 53.5 ms
8,908 solves/s 33.7 ms 29.6 ms 62.2 ms 68.2 ms
12,302 solves/s 36.6 ms 35.1 ms 76.2 ms 82.8 ms
15,215 solves/s 39.4 ms 37.9 ms 82.4 ms 90.5 ms

Um processo de decisão completo — uma tabela de decisão, uma árvore de decisão e um fluxo de três tabelas, executados sequencialmente como três chamadas de API — é concluído em 28,6 ms em carga moderada e 39,4 ms no throughput máximo. A latência da transação acompanha quase exatamente a soma das suas partes, o que significa que a sobrecarga de emitir várias chamadas sequenciais ao Solver é insignificante; o custo está nas próprias regras.


O que saturou — e por que estamos mostrando isso

O segundo teste foi idêntico em todos os aspectos, exceto um: o limite do escalonador automático foi definido como 5 réplicas em vez de 11. Este é o resultado mais instrutivo do conjunto.


Clientes simultâneosHPA máx. 5HPA máx. 11Diferença de throughput
50 5,529 solves/s 5,243 solves/s
100 9,115 solves/s 8,908 solves/s
150 9,540 solves/s 12,302 solves/s - 23%
200 9,794 solves/s 15,215 solves/s - 36%

E a latência correspondente:

Clientes simultâneosMédia (máx. 5)Média (máx. 11)p95 (máx. 5)p95 (máx. 11)
50 9.0 ms 9.5 ms 15.1 ms 18.4 ms
100 10.9 ms 11.2 ms 22.2 ms 23.1 ms
150 15.7 ms 12.1 ms 27.9 ms 27.2 ms
200 20.3 ms 13.1 ms 35.4 ms 29.0 ms

Até 100 clientes simultâneos, as duas configurações são indistinguíveis — a implantação limitada é até marginalmente mais rápida, porque cinco réplicas foram suficientes e a distribuição das requisições foi mais equilibrada. Depois desse ponto, elas divergem acentuadamente. A implantação limitada atinge seu teto em cerca de 9.800 resoluções/s e deixa de ganhar throughput; a carga adicional se transforma em filas, e a latência média mais que dobra, de 9,0 ms para 20,3 ms. A latência de uma regra individual se degrada na mesma proporção, de 7,6 ms para 17,4 ms.

Vale a pena dizer claramente: a implantação limitada não falhou. Zero erros em 10,4 milhões de requisições, p99 ainda abaixo de 47 ms, todos os limites de latência configurados continuaram sendo atendidos. Ela simplesmente deixou de ficar mais rápida.

Um motor de regras que se degrada de forma gradual sob saturação é, sem dúvida, mais valioso do que um que é rápido até deixar de ser. Mas a lição de implantação é direta: se sua carga de pico excede o que o limite de réplicas pode absorver, aumente esse limite. O motor escala; a restrição aqui foi a configuração do escalonador automático, não o software.


Como usar estes números

Se você está dimensionando uma implantação. O teste saturado fornece o número mais claro por réplica: cinco réplicas atingiram um platô em aproximadamente 9.800 resoluções/s, ou cerca de 1.950 resoluções/s por réplica na saturação. Para ter margem, planeje ~1.400 resoluções/s por réplica e defina o máximo do seu HPA acima da quantidade resultante, não exatamente nela. (As contagens de pods não foram registradas na saída do k6; por isso, são derivadas dos limites do escalonador automático, e não de contagens de réplicas observadas.)

Se você tem um SLA de latência. Para uma única tabela ou árvore de decisão de complexidade média sob carga normal, reserve 20 ms no p99, medidos do lado do cliente na mesma região. Para um fluxo que orquestra várias tabelas, reserve 25–41 ms no p99 dependendo da carga. Para uma transação de negócio com três chamadas, reserve 90 ms no p99 no throughput máximo.

Se você está projetando lógica de decisão. Consolide. Três regras chamadas individualmente custam aproximadamente o dobro das mesmas três regras orquestradas em um fluxo, porque você paga a ida e volta pela rede uma vez em vez de três.

Se você está comparando motores. Compare distribuições de percentis, não médias. É fácil obter uma boa média sendo rápido no caminho ideal. A diferença entre p50 e p99 mostra se o sistema forma filas, e filas são o que quebram sistemas em produção.


Escopo e limitações

Estes resultados descrevem as configurações testadas e não devem ser generalizados além delas:

  • A complexidade da regra influencia a latência mais do que qualquer outro fator. Estes testes usaram uma tabela de decisão de 20 linhas, uma árvore de decisão e um fluxo de três tabelas, todos de complexidade média. Uma tabela de decisão com 10.000 linhas, fluxos de trabalho profundamente aninhados ou regras com scripts pesados produzirão números diferentes.
  • O tamanho da carga útil importa. Estes testes usaram requisições de ~422 bytes e respostas de ~1,2 KB. Cargas úteis consideravelmente maiores alterarão tanto a latência quanto o throughput.
  • A topologia de rede está excluída. Os geradores de carga foram executados na mesma região. Chamadores entre regiões ou continentes adicionam tempo de trânsito que não tem relação com o motor.
  • Estes não são os números máximos alcançáveis. O teste elástico ainda escalava linearmente quando terminou. Reportamos o que medimos, não o que extrapolamos.
  • Os números de throughput são recalculados, e não extraídos da exibição do painel do k6, pelo motivo descrito na seção O que testamos e como acima.

Testes realizados com k6 contra o DecisionRules no Kubernetes com escalonamento automático horizontal de pods. A latência foi medida como tempo de ida e volta HTTP observado pelo cliente (http_req_duration). O throughput foi recalculado a partir das contagens brutas por intervalo. Relatórios brutos do k6 disponíveis mediante solicitação.

A principal conclusão

Todos os números aqui vêm de um relatório do k6 que você pode reproduzir, com as condições declaradas, os percentis publicados em vez de reduzidos a médias e o teste que saturou apresentado ao lado daquele que não saturou. Essa última parte é essencial. Um benchmark que mostra apenas a configuração em que tudo correu bem não diz nada sobre o que acontece quando não corre.

Se você está avaliando um motor de regras, peça o mesmo. Se as distribuições de percentis e as condições declaradas não estiverem disponíveis, o que está sendo mostrado não é uma medição.

Para testar o DecisionRules com suas próprias regras e cargas úteis, agende uma demonstração ou inicie uma avaliação gratuita.

Ondrej Brejla

Ondrej Brejla

Analista de Negócios