Add initial setup guide, test scripts, and configuration files

This commit is contained in:
Jose Eduardo 2025-08-11 13:38:15 -04:00
parent 4e5350faab
commit 0bea9a93d8
7 changed files with 779 additions and 0 deletions

3
rinha-test/.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
partial-results.json
report.html
docker-compose.logs

43
rinha-test/MINIGUIA.md Normal file
View File

@ -0,0 +1,43 @@
*Esse guia foi gentilmente elaborado por [leonardosegfault](https://github.com/leonardosegfault). Link do guia original [aqui](https://github.com/zanfranceschi/rinha-de-backend-2025/issues/11).*
# Mini Guia de Setup
Assim como eu, acredito que haja muitas pessoas que tiveram ou terão dúvidas de realizar a configuração inicial, então decidi escrever esse minúsculo guia temporário para que você também não tenha que ficar caçando no repositório, como eu fiz.
O objetivo é ser bastante breve, então eu intencionalmente omiti alguns detalhes pois a própria documentação dos projetos já instruem adequadamente. Se tiver problemas ou soluções específicas, abra uma Issue que o pessoal te ajuda.
A IA também pode ser sua amiga, então elabore um texto bonitinho para que ela compreenda seu problema e consiga chegar numa solução — apesar de eu recomendar que faça sozinho para exercitar o cérebro.
### 1. Docker
Docker será utilizado do início ao final do projeto para que haja um ambiente consistente e isolado. Assim tudo poderá ser configurado e rodado com poucos comandos.
[Depois de instalado](https://docs.docker.com/get-started/get-docker/) vá até a pasta `payment-processor` e rode `docker compose up` para inicializar os servidores do payment processor.
Se tudo ocorrer como esperado, o servidor default (http://127.0.0.1:8001/) e de fallback (http://127.0.0.1:8002/) estarão online e com [seus endpoints](https://github.com/zanfranceschi/rinha-de-backend-2025/blob/main/INSTRUCOES.md#detalhes-dos-endpoints) funcionando bonitinho.
### 2. K6
Essa ferramenta [será utilizada para testar requisições na sua API local](https://github.com/zanfranceschi/rinha-de-backend-2025/tree/main/rinha-test#instru%C3%A7%C3%B5es-para-execu%C3%A7%C3%A3o-dos-testes-locais). Assim, você poderá validar se seu backend está processando as informações direitinho e tankando o estresse.
Siga as [instruções para instalar no seu sistema operacional](https://grafana.com/docs/k6/latest/set-up/install-k6/) e depois vá para a pasta `rinha-test` para rodar o teste com `k6 run rinha.js` — mas não agora, tem algumas coisas que ainda serão resolvidas logo abaixo.
### 3. Seu Backend
Sinta-se livre para criar o seu backend com os [endpoints](https://github.com/zanfranceschi/rinha-de-backend-2025/blob/main/INSTRUCOES.md#detalhes-dos-endpoints) necessários.
Se por algum motivo você quiser testar apenas um cenário, comente os demais no arquivo [`rinha.js`](https://github.com/zanfranceschi/rinha-de-backend-2025/blob/2ac3f62f225afd6748e9164be3c4d4ebe5d3474e/rinha-test/rinha.js#L35-L128).
### 4. Divirta-se
Desenvolva sua aplicação baseada nas especificações e realize os testes para checar se está tudo nos conformes.
Você também pode assistir [sua API apanhando ao vivo através do dashboard](https://github.com/zanfranceschi/rinha-de-backend-2025/tree/main/rinha-test#acompanhando-os-testes-via-dashboard-e-report), se quiser:
![img](https://github.com/user-attachments/assets/3be1b160-c2a0-4ab8-bab5-117e9dfe5ec9)
*(demo da minha API capenga)*
Não se esqueça de fechar a página ou clicar no **Report** para que o teste finalize e exiba os stats no terminal.
Boa sorte!

46
rinha-test/README.md Normal file
View File

@ -0,0 +1,46 @@
# Rinha de Backend - 2025
## Instruções para Execução dos Testes Locais
A ferramenta de testes para essa edição da Rinha de Backend é o [k6](https://k6.io/).
![k6 logo](../misc/imgs/K6-logo.svg)
Instale o k6 caso ainda já não o tenha feito. Siga as instruções [aqui](https://grafana.com/docs/k6/latest/set-up/install-k6/).
### Execução dos Testes
Antes de executar os testes, você precisa subir os containers do seu backend e dos [Payment Processors](../payment-processor/docker-compose.yml). Após ter feito isso, basta entrar no diretório [rinha-test](./) e executar o seguinte comando:
```shell
k6 run rinha.js
```
Você deverá ver algo como a imagem seguinte.
![console com o k6 em execução](../misc/imgs/k6-executando.png)
### Acompanhando os Testes via Dashboard e Report
Se quiser acompanhar os testes via dashboard e obter um relatório HTML do k6, você pode configurar as seguintes variáveis de ambiente. Para mais informações, acesse a [documentação oficial](https://grafana.com/docs/k6/latest/results-output/web-dashboard/).
```shell
export K6_WEB_DASHBOARD=true
export K6_WEB_DASHBOARD_PORT=5665
export K6_WEB_DASHBOARD_PERIOD=2s
export K6_WEB_DASHBOARD_OPEN=true
export K6_WEB_DASHBOARD_EXPORT='report.html'
```
### Número Máximo de Requisições Simultâneas
Se quiser alterar o número máximo de requisições simultâneas, você poderá definiar a variável `MAX_REQUESTS` no comando para executar o teste em vez de alterar o script.
```shell
k6 run -e MAX_REQUESTS=550 rinha.js
```
### Contribuição com o Script de Teste
O script de testes foi feito por mim (Zan), mas como não tenho proficiência em Javascript, muito provavelmente existem muitos pontos de melhoria. Sugestões de melhoria no script de testes são muito bem-vindas! Abra um PR e contribua!

View File

@ -0,0 +1,45 @@
import json
from os import walk
from os.path import join, isfile
import sys
summary = []
for (dirpath, dirnames, filenames) in walk("../participantes/"):
for filename in filenames:
entry = {}
if (filename == "info.json"):
info_file = join(dirpath, "info.json")
with open(info_file) as f:
try:
entry.update({
"info": json.loads(f.read())
})
except Exception as ex:
entry.update({
"info": None
})
partial_result_file = join(dirpath, "partial-results.json")
errors_log_file = join(dirpath, "error.logs")
entry.update({"erro_na_execucao": isfile(errors_log_file)})
if (isfile(partial_result_file)):
with open(partial_result_file) as f:
partial_results = f.read()
if (partial_results):
entry.update({
"resultado_partial": json.loads(partial_results)
})
else:
entry.update({
"resultado_partial": None
})
if (entry):
summary.append(entry)
summary_file = sys.argv[1] if len(sys.argv) > 1 else "../previa-resultados+participantes-info.json"
with open(summary_file, 'w') as pf:
pf.write(json.dumps(summary))

149
rinha-test/requests.js Normal file
View File

@ -0,0 +1,149 @@
import { Httpx } from 'https://jslib.k6.io/httpx/0.1.0/index.js';
import exec from 'k6/execution';
const initialToken = '123';
export const token = __ENV.TOKEN ?? initialToken;
const paymentProcessorDefaultHttp = new Httpx({
baseURL: 'http://localhost:8001',
headers: {
'Content-Type': 'application/json',
'X-Rinha-Token': token
},
timeout: 1500,
});
const paymentProcessorFallbacktHttp = new Httpx({
baseURL: 'http://localhost:8002',
headers: {
'Content-Type': 'application/json',
'X-Rinha-Token': token
},
timeout: 1500,
});
const backendHttp = new Httpx({
baseURL: "http://localhost:9999",
//baseURL: "http://localhost:5123",
headers: {
"Content-Type": "application/json",
},
timeout: 1500,
});
const paymentProcessorHttp = {
"default": paymentProcessorDefaultHttp,
"fallback": paymentProcessorFallbacktHttp,
};
export async function setPPToken(service, token) {
const httpClient = paymentProcessorHttp[service];
const params = { headers: { 'X-Rinha-Token': initialToken } };
const payload = JSON.stringify({
token: token
});
const response = await httpClient.asyncPut('/admin/configurations/token', payload, params);
if (response.status != 204) {
exec.test.abort(`Erro ao definir token para ${service} (HTTP ${response.status}).`);
}
}
export async function setPPDelay(service, ms) {
const httpClient = paymentProcessorHttp[service];
const payload = JSON.stringify({
delay: ms
});
const response = await httpClient.asyncPut('/admin/configurations/delay', payload);
if (response.status != 200) {
exec.test.abort(`Erro ao definir delay para ${service} (HTTP ${response.status}).`);
}
}
export async function setPPFailure(service, failure) {
const httpClient = paymentProcessorHttp[service];
const payload = JSON.stringify({
failure: failure
});
const response = await httpClient.asyncPut('/admin/configurations/failure', payload);
if (response.status != 200) {
exec.test.abort(`Erro ao definir failure para ${service} (HTTP ${response.status}).`);
}
}
export async function resetPPDatabase(service) {
const httpClient = paymentProcessorHttp[service];
const response = await httpClient.asyncPost('/admin/purge-payments');
if (response.status != 200) {
exec.test.abort(`Erro ao resetar database para ${service} (HTTP ${response.status}).`);
}
}
export async function getPPPaymentsSummary(service, from, to) {
const httpClient = paymentProcessorHttp[service];
const response = await httpClient.asyncGet(`/admin/payments-summary?from=${from}&to=${to}`);
if (response.status == 200) {
return JSON.parse(response.body);
}
console.error(`Não foi possível obter resposta de '/admin/payments-summary?from=${from}&to=${to}' para ${service} (HTTP ${response.status})`);
return {
totalAmount: 0,
totalRequests: 0,
feePerTransaction: 0,
totalFee: 0
}
}
export async function resetBackendDatabase() {
try {
await backendHttp.asyncPost('/purge-payments');
} catch (error) {
console.info("Seu backend provavelmente não possui um endpoint para resetar o banco. Isso não é um problem.", error.message);
}
}
export async function getBackendPaymentsSummary(from, to) {
const response = await backendHttp.asyncGet(`/payments-summary?from=${from}&to=${to}`);
if (response.status == 200) {
return JSON.parse(response.body);
}
console.error(`Não foi possível obter resposta de '/payments-summary?from=${from}&to=${to}' para o backend (HTTP ${response.status})`);
return {
default: {
totalAmount: 0,
totalRequests: 0
},
fallback: {
totalAmount: 0,
totalRequests: 0
}
}
}
export async function requestBackendPayment(payload) {
const response = await backendHttp.asyncPost('/payments', JSON.stringify(payload));
return response;
}

346
rinha-test/rinha.js Normal file
View File

@ -0,0 +1,346 @@
import { textSummary } from "https://jslib.k6.io/k6-summary/0.1.0/index.js";
import { uuidv4 } from "https://jslib.k6.io/k6-utils/1.4.0/index.js";
import { sleep } from "k6";
import exec from "k6/execution";
import { Counter } from "k6/metrics";
import {
token,
setPPToken,
setPPDelay,
setPPFailure,
resetPPDatabase,
getPPPaymentsSummary,
resetBackendDatabase,
getBackendPaymentsSummary,
requestBackendPayment
} from "./requests.js";
// https://mikemcl.github.io/big.js/
import Big from "https://cdn.jsdelivr.net/npm/big.js@7.0.1/big.min.js";
const MAX_REQUESTS = __ENV.MAX_REQUESTS ?? 500;
export const options = {
summaryTrendStats: [
"p(99)",
"count",
],
thresholds: {
//http_req_failed: [{ threshold: "rate < 0.01", abortOnFail: false }],
//payments_inconsistency: ["count == 0"]
//http_req_duration: ['p(99) < 50'],
//payments_count: ['count > 3500'],
},
scenarios: {
payments: {
exec: "payments",
executor: "ramping-vus",
startVUs: 1,
gracefulRampDown: "0s",
stages: [{ target: MAX_REQUESTS, duration: "60s" }],
},
payments_consistency: {
exec: "checkPaymentsConsistency",
executor: "constant-vus",
//startTime: "5s",
duration: "60s",
vus: "1",
},
stage_00: {
exec: "define_stage",
startTime: "1s",
executor: "constant-vus",
vus: 1,
duration: "1s",
tags: {
defaultDelay: "0",
defaultFailure: "false",
fallbackDelay: "0",
fallbackFailure: "false",
},
},
stage_01: {
exec: "define_stage",
startTime: "10s",
executor: "constant-vus",
vus: 1,
duration: "1s",
tags: {
defaultDelay: "100",
defaultFailure: "false",
fallbackDelay: "0",
fallbackFailure: "false",
},
},
stage_02: {
exec: "define_stage",
startTime: "20s",
executor: "constant-vus",
vus: 1,
duration: "1s",
tags: {
defaultDelay: "100",
defaultFailure: "true",
fallbackDelay: "0",
fallbackFailure: "false",
},
},
stage_03: {
exec: "define_stage",
startTime: "30s",
executor: "constant-vus",
vus: 1,
duration: "1s",
tags: {
defaultDelay: "2000",
defaultFailure: "true",
fallbackDelay: "1000",
fallbackFailure: "true",
},
},
stage_04: {
exec: "define_stage",
startTime: "40s",
executor: "constant-vus",
vus: 1,
duration: "1s",
tags: {
defaultDelay: "20",
defaultFailure: "false",
fallbackDelay: "20",
fallbackFailure: "false",
},
},
stage_05: {
exec: "define_stage",
startTime: "50s",
executor: "constant-vus",
vus: 1,
duration: "1s",
tags: {
defaultDelay: "0",
defaultFailure: "false",
fallbackDelay: "5000",
fallbackFailure: "false",
},
},
},
};
const transactionsSuccessCounter = new Counter("transactions_success");
const transactionsFailureCounter = new Counter("transactions_failure");
const totalTransactionsAmountCounter = new Counter("total_transactions_amount");
const paymentsInconsistencyCounter = new Counter("payments_inconsistency");
const defaultTotalAmountCounter = new Counter("default_total_amount");
const defaultTotalRequestsCounter = new Counter("default_total_requests");
const fallbackTotalAmountCounter = new Counter("fallback_total_amount");
const fallbackTotalRequestsCounter = new Counter("fallback_total_requests");
const defaultTotalFeeCounter = new Counter("default_total_fee");
const fallbackTotalFeeCounter = new Counter("fallback_total_fee");
export async function setup() {
await setPPToken("default", token);
await setPPToken("fallback", token);
await resetPPDatabase("default");
await resetPPDatabase("fallback");
await resetBackendDatabase();
}
const paymentRequestFixedAmount = new Big(19.90);
export async function teardown() {
const to = new Date();
const from = new Date(to.getTime() - 70 * 1000); // 1 minuto e 10 segundos atrás
console.info(`summaries from ${from.toISOString()} to ${to.toISOString()}`);
const defaultResponse = await getPPPaymentsSummary("default", from.toISOString(), to.toISOString());
const fallbackResponse = await getPPPaymentsSummary("fallback", from.toISOString(), to.toISOString());
const backendPaymentsSummary = await getBackendPaymentsSummary(from.toISOString(), to.toISOString());
const totalTransactionsAmount = new Big(backendPaymentsSummary.default.totalAmount)
.plus(backendPaymentsSummary.fallback.totalAmount);
totalTransactionsAmountCounter.add(totalTransactionsAmount.toNumber());
defaultTotalAmountCounter.add(backendPaymentsSummary.default.totalAmount);
defaultTotalRequestsCounter.add(backendPaymentsSummary.default.totalRequests);
fallbackTotalAmountCounter.add(backendPaymentsSummary.fallback.totalAmount);
fallbackTotalRequestsCounter.add(backendPaymentsSummary.fallback.totalRequests);
const defaultTotalFee = new Big(defaultResponse.feePerTransaction).times(backendPaymentsSummary.default.totalAmount);
const fallbackTotalFee = new Big(fallbackResponse.feePerTransaction).times(backendPaymentsSummary.fallback.totalAmount);
defaultTotalFeeCounter.add(defaultTotalFee.toNumber());
fallbackTotalFeeCounter.add(fallbackTotalFee.toNumber());
}
export async function payments() {
const payload = {
correlationId: uuidv4(),
amount: paymentRequestFixedAmount.toNumber()
};
const response = await requestBackendPayment(payload);
if ([200, 201, 202, 204].includes(response.status)) {
transactionsSuccessCounter.add(1);
transactionsFailureCounter.add(0);
} else {
transactionsSuccessCounter.add(0);
transactionsFailureCounter.add(1);
}
sleep(1);
}
export async function checkPaymentsConsistency() {
const now = new Date();
const from = new Date(now - 1000 * 15).toISOString();
const to = new Date(now - 1500).toISOString();
const defaultAdminPaymentsSummaryPromise = getPPPaymentsSummary(
"default",
from,
to,
);
const fallbackAdminPaymentsSummaryPromise = getPPPaymentsSummary(
"fallback",
from,
to,
);
const backendPaymentsSummaryPromise = getBackendPaymentsSummary(from, to);
const [defaultAdminPaymentsSummary, fallbackAdminPaymentsSummary, backendPaymentsSummary] = await Promise.all([
defaultAdminPaymentsSummaryPromise,
fallbackAdminPaymentsSummaryPromise,
backendPaymentsSummaryPromise
]);
const inconsistencies =
Math.abs(
(backendPaymentsSummary.default.totalRequests - defaultAdminPaymentsSummary.totalRequests) +
(backendPaymentsSummary.fallback.totalRequests - fallbackAdminPaymentsSummary.totalRequests)
);
paymentsInconsistencyCounter.add(inconsistencies);
if (inconsistencies > 0) {
console.warn(`${inconsistencies} inconsistências encontradas.`);
}
sleep(10);
}
export async function define_stage() {
const defaultMs = parseInt(exec.vu.metrics.tags["defaultDelay"]);
const fallbackMs = parseInt(exec.vu.metrics.tags["fallbackDelay"]);
const defaultFailure = exec.vu.metrics.tags["defaultFailure"] === "true";
const fallbackFailure = exec.vu.metrics.tags["fallbackFailure"] === "true";
await setPPDelay("default", defaultMs);
await setPPDelay("fallback", fallbackMs);
await setPPFailure("default", defaultFailure);
await setPPFailure("fallback", fallbackFailure);
sleep(1);
}
export function handleSummary(data) {
const total_transactions_requested = data.metrics.transactions_success.values.count;
const actual_total_amount = data.metrics.total_transactions_amount.values.count;
const default_total_fee = data.metrics.default_total_fee.values.count;
const fallback_total_fee = data.metrics.fallback_total_fee.values.count;
const total_fee = new Big(default_total_fee).plus(fallback_total_fee).toNumber();
const p_99 = new Big(data.metrics["http_req_duration{expected_response:true}"].values["p(99)"]).round(2).toNumber();
const p_99_bonus = Math.max(new Big((11 - p_99) * 0.02).round(2).toNumber(), 0);
const contains_inconsistencies = data.metrics.payments_inconsistency.values.count > 0;
const inconsistencies_fine = contains_inconsistencies ? 0.35 : 0;
// caixa dois
const lag = data.metrics.transactions_success.values.count - (data.metrics.default_total_requests.values.count + data.metrics.fallback_total_requests.values.count);
const slush_fund = lag < 0;
const liquid_partial_amount = new Big(actual_total_amount).minus(total_fee).toNumber();
const liquid_amount = new Big(liquid_partial_amount)
.plus(new Big(liquid_partial_amount).times(p_99_bonus))
.minus(new Big(liquid_partial_amount).times(inconsistencies_fine)).toNumber();
const name = __ENV.PARTICIPANT ?? "anonymous";
const custom_data = {
participante: name,
total_liquido: liquid_amount,
total_bruto: actual_total_amount,
total_taxas: total_fee,
descricao: "'total_liquido' é sua pontuação final. Equivale ao seu lucro. Fórmula: total_liquido + (total_liquido * p99.bonus) - (total_liquido * multa.porcentagem)",
p99: {
valor: `${p_99}ms`,
bonus: `${new Big(p_99_bonus).times(100)}%`,
max_requests: MAX_REQUESTS,
descricao: "Fórmula para o bônus: max((11 - p99.valor) * 0.02, 0)",
},
multa: {
porcentagem: inconsistencies_fine,
total: new Big(liquid_partial_amount).times(inconsistencies_fine).toNumber(),
composicao: {
num_inconsistencias: data.metrics.payments_inconsistency.values.count,
descricao: "Se 'num_inconsistencias' > 0, há multa de 35%.",
}
},
caixa_dois: {
detectado: slush_fund,
descricao: "Se 'lag' for negativo, significa que seu backend registrou mais pagamentos do que solicitado, automaticamente desclassificando sua submissão!",
},
lag: {
num_pagamentos_total: data.metrics.default_total_requests.values.count + data.metrics.fallback_total_requests.values.count,
num_pagamentos_solicitados: data.metrics.transactions_success.values.count,
lag: data.metrics.transactions_success.values.count - (data.metrics.default_total_requests.values.count + data.metrics.fallback_total_requests.values.count),
descricao: "Lag é a diferença entre a quantidade de solicitações de pagamentos e o que foi realmente computado pelo backend. Mostra a perda de pagamentos possivelmente por estarem enfileirados."
},
pagamentos_solicitados: {
qtd_sucesso: data.metrics.transactions_success.values.count,
qtd_falha: data.metrics.transactions_failure.values.count,
descricao: "'qtd_sucesso' foram requests bem sucedidos para 'POST /payments' e 'qtd_falha' os requests com erro."
},
pagamentos_realizados_default: {
total_bruto: data.metrics.default_total_amount.values.count,
num_pagamentos: data.metrics.default_total_requests.values.count,
total_taxas: data.metrics.default_total_fee.values.count,
descricao: "Informações do backend sobre solicitações de pagamento para o Payment Processor Default."
},
pagamentos_realizados_fallback: {
total_bruto: data.metrics.fallback_total_amount.values.count,
num_pagamentos: data.metrics.fallback_total_requests.values.count,
total_taxas: data.metrics.fallback_total_fee.values.count,
descricao: "Informações do backend sobre solicitações de pagamento para o Payment Processor Fallback."
}
};
const result = {
stdout: textSummary(data),
};
const participant = __ENV.PARTICIPANT;
let summaryJsonFileName = `../participantes/${participant}/partial-results.json`
if (participant == undefined) {
summaryJsonFileName = `./partial-results.json`
}
result[summaryJsonFileName] = JSON.stringify(custom_data, null, 2);
return result;
}

147
rinha-test/run-tests.sh Executable file
View File

@ -0,0 +1,147 @@
#!/usr/bin/env bash
export GIT_EDITOR=true
startContainers() {
pushd ../payment-processor > /dev/null
docker compose up --build -d 1> /dev/null 2>&1
popd > /dev/null
pushd ../participantes/$1 > /dev/null
services=$(docker compose config --services | wc -l)
echo "" > docker-compose.logs
nohup docker compose up --build >> docker-compose.logs &
popd > /dev/null
#expectedServicesUp=$(( services + 4 ))
#servicesUp=$(docker ps | grep ' Up ' | wc -l)
}
stopContainers() {
pushd ../participantes/$1
docker compose down -v --remove-orphans
docker compose rm -s -v -f
find * ! -group $(whoami) | xargs sudo rm -rf
popd > /dev/null
pushd ../payment-processor > /dev/null
docker compose down --volumes > /dev/null
popd > /dev/null
}
MAX_REQUESTS=550
while true; do
# docker system prune -a -f --volumes
for directory in ../participantes/*; do
(
git pull
participant=$(echo $directory | sed -e 's/..\/participantes\///g' -e 's/\///g')
echo "========================================"
echo " Participant $participant starting..."
echo "========================================"
testedFile="$directory/partial-results.json"
if ! test -f $testedFile; then
touch $testedFile
echo "executing test for $participant..."
stopContainers $participant
startContainers $participant
success=1
max_attempts=15
attempt=1
while [ $success -ne 0 ] && [ $max_attempts -ge $attempt ]; do
curl -f -s --max-time 3 localhost:9999/payments-summary
success=$?
echo "tried $attempt out of $max_attempts..."
sleep 5
((attempt++))
done
if [ $success -eq 0 ]; then
echo "" > $directory/k6.logs
k6 run -e MAX_REQUESTS=$MAX_REQUESTS -e PARTICIPANT=$participant -e TOKEN=$(uuidgen) --log-output=file=$directory/k6.logs rinha.js
stopContainers $participant
echo "======================================="
echo "working on $participant"
sed -i '1001,$d' $directory/docker-compose.logs
sed -i '1001,$d' $directory/k6.logs
echo "log truncated at line 1000" >> $directory/docker-compose.logs
echo "log truncated at line 1000" >> $directory/k6.logs
else
stopContainers $participant
echo "[$(date)] Seu backend não respondeu nenhuma das $max_attempts tentativas de GET para http://localhost:9999/payments-summary. Teste abortado." > $directory/error.logs
echo "[$(date)] Inspecione o arquivo docker-compose.logs para mais informações." >> $directory/error.logs
echo "Could not get a successful response from backend... aborting test for $participant"
fi
git add $directory
git commit -m "add $participant's partial result"
git push
echo "================================="
echo " Finished testing $participant!"
echo "================================="
sleep 5
else
echo "================================="
echo " Skipping $participant"
echo "================================="
fi
)
done
date
echo "generating results preview..."
PREVIA_RESULTADOS=../PREVIA_RESULTADOS.md
results=$(find ../participantes/*/partial-results.json -size +1b | wc -l)
errors=$(find ../participantes/*/partial-results.json -size 0 | wc -l)
total=$(find ../participantes/*/partial-results.json | wc -l)
echo -e "# Prévia do Resultados da Rinha de Backend 2025" > $PREVIA_RESULTADOS
echo -e "Atualizado em **$(date)**" >> $PREVIA_RESULTADOS
echo -e "$total submissões / $results resultados / $errors submissões com erro" >> $PREVIA_RESULTADOS
echo -e "*Testes executados com MAX_REQUESTS=$MAX_REQUESTS*."
echo -e "\n" >> $PREVIA_RESULTADOS
echo -e "| participante | p99 | bônus por desempenho (%) | multa ($) | lucro | submissão |" >> $PREVIA_RESULTADOS
echo -e "| -- | -- | -- | -- | -- | -- |" >> $PREVIA_RESULTADOS
for partialResult in ../participantes/*/partial-results.json; do
(
participant=$(echo $partialResult | sed -e 's/..\/participantes\///g' -e 's/\///g' -e 's/partial\-results\.json//g')
link="https://github.com/zanfranceschi/rinha-de-backend-2025/tree/main/participantes/$participant"
if [ -s $partialResult ]; then
cat $partialResult | jq -r '(["|", .participante, "|", .p99.valor, "|", .p99.bonus, "|", .multa.total, "|", .total_liquido, "|", "['$participant']('$link')"]) | @tsv' >> $PREVIA_RESULTADOS
fi
)
done
echo -e "### Submissões com Erro" >> $PREVIA_RESULTADOS
echo -e "\n" >> $PREVIA_RESULTADOS
echo -e "| participante | submissão |" >> $PREVIA_RESULTADOS
echo -e "| -- | -- |" >> $PREVIA_RESULTADOS
for errorLog in ../participantes/*/error.logs; do
(
participant=$(echo $errorLog | sed -e 's/..\/participantes\///g' -e 's/\///g' -e 's/error\.logs//g')
link="https://github.com/zanfranceschi/rinha-de-backend-2025/tree/main/participantes/$participant"
echo "| $participant | [logs]($link) |" >> $PREVIA_RESULTADOS
)
done
PREVIA_RESULTADOS_JSON=../previa-resultados+participantes-info.json
python3 previa_resultados_json.py $PREVIA_RESULTADOS_JSON
git pull
git add $PREVIA_RESULTADOS_JSON
git add $PREVIA_RESULTADOS
git commit -m "previa resultados @ $(date)"
git push
echo "$(date) - waiting some time until next round..."
sleep 300
done