fix: 修复上行归属并实现签名质量日报优化
This commit is contained in:
@@ -0,0 +1,143 @@
|
||||
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();
|
||||
}
|
||||
Reference in New Issue
Block a user