347 lines
12 KiB
JavaScript
347 lines
12 KiB
JavaScript
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/[email protected]/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;
|
|
}
|