144 lines
5.8 KiB
JavaScript
144 lines
5.8 KiB
JavaScript
import assert from 'node:assert/strict';
|
|
import { createRequire } from 'node:module';
|
|
import { execFileSync } from 'node:child_process';
|
|
import Module from 'node:module';
|
|
import { randomUUID } from 'node:crypto';
|
|
const require = createRequire(new URL('../../api/package.json', import.meta.url));
|
|
const url = new URL(process.env.SIGNATURE_TEST_DATABASE_URL || '');
|
|
assert(url.hostname === '127.0.0.1' && url.pathname.startsWith('/cmpp_qa_signature_'));
|
|
process.env.DATABASE_URL = url.toString();
|
|
process.env.NODE_ENV = 'test';
|
|
require('reflect-metadata');
|
|
const { PrismaService } = require('./dist/prisma/prisma.service'),
|
|
{ Prisma } = require('@prisma/client');
|
|
const { activityCounts } = require('./dist/signature-analytics/analytics-aggregate');
|
|
const { todayKey, addDays, startOfDay } = require('./dist/signature-analytics/analytics-date');
|
|
const oldSource = execFileSync(
|
|
'git',
|
|
['show', '4eb7b16d122da14f921093716d4ca1ed390d9e4c:api/src/signature-retirement/signature-retirement.service.ts'],
|
|
{ encoding: 'utf8' },
|
|
);
|
|
const compiled = require('typescript').transpileModule(oldSource, {
|
|
compilerOptions: { module: 1, target: 9, experimentalDecorators: true, emitDecoratorMetadata: true },
|
|
}).outputText;
|
|
const oldModule = new Module(require.resolve('./dist/signature-retirement/signature-retirement.service'));
|
|
oldModule.filename = require.resolve('./dist/signature-retirement/signature-retirement.service');
|
|
oldModule.paths = require.resolve.paths('@prisma/client');
|
|
oldModule._compile(compiled, oldModule.filename);
|
|
const db = new PrismaService(),
|
|
prefix = 'bench' + randomUUID().slice(0, 8),
|
|
N = 300,
|
|
K = 100,
|
|
D = addDays(todayKey(), -1),
|
|
start = startOfDay(D),
|
|
end = startOfDay(todayKey());
|
|
let last;
|
|
const traced = new Proxy(db, {
|
|
get(t, k) {
|
|
if (k === '$queryRaw')
|
|
return async (q) => {
|
|
last = q;
|
|
return db.$queryRaw(q);
|
|
};
|
|
const v = t[k];
|
|
return typeof v === 'function' ? v.bind(t) : v;
|
|
},
|
|
});
|
|
try {
|
|
const tenant = await db.tenant.create({ data: { name: prefix, code: prefix } }),
|
|
app = await db.smsApplication.create({
|
|
data: {
|
|
tenantId: tenant.id,
|
|
name: prefix,
|
|
cmppAccount: prefix,
|
|
cmppEnterpriseCode: '000001',
|
|
secretHash: 'isolated',
|
|
interfaceEnabled: false,
|
|
},
|
|
}),
|
|
channel = await db.smsChannel.create({
|
|
data: {
|
|
name: prefix,
|
|
code: prefix,
|
|
srcId: '1069',
|
|
gatewayHost: '127.0.0.1',
|
|
gatewayPort: 1,
|
|
account: 'isolated',
|
|
passwordCipher: 'not-a-secret',
|
|
carriers: ['mobile'],
|
|
status: 'disabled',
|
|
},
|
|
});
|
|
await db.$executeRaw`INSERT INTO "SmsSignature"(id,"tenantId","applicationId",name,"updatedAt") SELECT ${prefix}||n,${tenant.id},${app.id},${prefix}||n,NOW() FROM generate_series(1,${N}) n`;
|
|
await db.$executeRaw`INSERT INTO "SmsMessageRecord"(id,"messageId","tenantId","applicationId","signatureId","phoneNumber",content,carrier,"queuedAt","billingUnits","updatedAt") SELECT ${prefix}||n||'-'||k,${prefix}||n||'-'||k,${tenant.id},${app.id},${prefix}||n,'13800000200','isolated','mobile',${start},1,NOW() FROM generate_series(1,${N}) n CROSS JOIN generate_series(1,${K}) k`;
|
|
await db.$executeRaw`INSERT INTO "SmsSubmitRecord"(id,"messageRecordId","submitId","channelId","submitStatus","submittedAt","createdAt","updatedAt") SELECT id,id,id,${channel.id},'accepted',${start},${start},NOW() FROM "SmsMessageRecord" WHERE "tenantId"=${tenant.id}`;
|
|
await db.$executeRawUnsafe('ANALYZE "SmsMessageRecord"');
|
|
await db.$executeRawUnsafe('ANALYZE "SmsSubmitRecord"');
|
|
const dims = Array.from({ length: N }, (_, i) =>
|
|
['enterprise', 'channel'].map((type) => ({
|
|
dimensionKey: JSON.stringify([type, prefix + (i + 1)]),
|
|
dimensionType: type,
|
|
tenantId: tenant.id,
|
|
applicationId: app.id,
|
|
signatureId: prefix + (i + 1),
|
|
channelKey: type === 'channel' ? channel.id : '',
|
|
channelId: type === 'channel' ? channel.id : null,
|
|
carrier: 'mobile',
|
|
approvedAt: startOfDay(addDays(D, -60)),
|
|
signatureName: prefix + (i + 1),
|
|
tenantName: prefix,
|
|
applicationName: prefix,
|
|
channelName: prefix,
|
|
})),
|
|
).flat();
|
|
const old = new oldModule.exports.SignatureRetirementService(traced);
|
|
const before = performance.now();
|
|
for (const d of dims) {
|
|
const r = await old.activityCounts(d, start, end);
|
|
assert.equal(r.acceptedBusinessCount, K);
|
|
}
|
|
const oldMs = performance.now() - before;
|
|
const sampleOld = last;
|
|
const now = performance.now(),
|
|
counts = await activityCounts(traced, dims, D),
|
|
newMs = performance.now() - now,
|
|
sampleNew = last;
|
|
for (const d of dims) assert.equal(counts.get(d.dimensionKey).acceptedBusinessCount, K);
|
|
const explain = async (q) =>
|
|
(await db.$queryRaw(Prisma.sql`EXPLAIN (ANALYZE,BUFFERS,FORMAT JSON) ${q}`))[0]['QUERY PLAN'][0];
|
|
const oldPlan = await explain(sampleOld),
|
|
newPlan = await explain(sampleNew);
|
|
const scanRows = (p) => {
|
|
let sum = 0;
|
|
const walk = (n) => {
|
|
if (
|
|
n['Node Type']?.includes('Scan') &&
|
|
['SmsSubmitRecord', 'SmsMessageRecord', 'SmsReceiptRecord', 'SmsMessageSegmentAudit'].includes(
|
|
n['Relation Name'],
|
|
)
|
|
)
|
|
sum += (n['Actual Rows'] + (n['Rows Removed by Filter'] || 0)) * (n['Actual Loops'] || 0);
|
|
for (const c of n.Plans || []) walk(c);
|
|
};
|
|
walk(p.Plan);
|
|
return sum;
|
|
};
|
|
console.log(
|
|
JSON.stringify({
|
|
messages: N * K,
|
|
dimensions: dims.length,
|
|
oldMs,
|
|
newMs,
|
|
reduction: 1 - newMs / oldMs,
|
|
oldSampleRows: scanRows(oldPlan),
|
|
estimatedOldRows: scanRows(oldPlan) * dims.length,
|
|
newRows: scanRows(newPlan),
|
|
note: 'warm cache, per-dimension sample extrapolation; not production CPU or whole job capacity',
|
|
oldPlan,
|
|
newPlan,
|
|
}),
|
|
);
|
|
} finally {
|
|
await db.onModuleDestroy();
|
|
}
|