dcrouter/test/suite/smtpclient_performance/test.cperf-04.cpu-utilization.ts
2025-05-26 10:35:50 +00:00

373 lines
12 KiB
TypeScript

import { tap, expect } from '@git.zone/tstest/tapbundle';
import { startTestServer, stopTestServer, type ITestServer } from '../../helpers/server.loader.js';
import { createSmtpClient, createPooledSmtpClient } from '../../../ts/mail/delivery/smtpclient/index.js';
import type { SmtpClient } from '../../../ts/mail/delivery/smtpclient/smtp-client.js';
import { Email } from '../../../ts/mail/core/classes.email.js';
let testServer: ITestServer;
// Helper function to measure CPU usage
const measureCpuUsage = async (duration: number) => {
const start = process.cpuUsage();
const startTime = Date.now();
await new Promise(resolve => setTimeout(resolve, duration));
const end = process.cpuUsage(start);
const elapsed = Date.now() - startTime;
// Ensure minimum elapsed time to avoid division issues
const actualElapsed = Math.max(elapsed, 1);
return {
user: end.user / 1000, // Convert to milliseconds
system: end.system / 1000,
total: (end.user + end.system) / 1000,
elapsed: actualElapsed,
userPercent: (end.user / 1000) / actualElapsed * 100,
systemPercent: (end.system / 1000) / actualElapsed * 100,
totalPercent: Math.min(((end.user + end.system) / 1000) / actualElapsed * 100, 100)
};
};
tap.test('setup - start SMTP server for CPU tests', async () => {
testServer = await startTestServer({
port: 0,
enableStarttls: false,
authRequired: false
});
expect(testServer.port).toBeGreaterThan(0);
});
tap.test('CPERF-04: CPU usage during connection establishment', async (tools) => {
tools.timeout(30000);
console.log('Testing CPU usage during connection establishment...');
// Measure baseline CPU
const baseline = await measureCpuUsage(1000);
console.log(`Baseline CPU: ${baseline.totalPercent.toFixed(2)}%`);
// Ensure we have a meaningful duration for measurement
const connectionCount = 5;
const startTime = Date.now();
const cpuStart = process.cpuUsage();
for (let i = 0; i < connectionCount; i++) {
const client = await createSmtpClient({
host: testServer.hostname,
port: testServer.port,
secure: false,
debug: false
});
await client.close();
// Small delay to ensure measurable duration
await new Promise(resolve => setTimeout(resolve, 100));
}
const elapsed = Date.now() - startTime;
const cpuEnd = process.cpuUsage(cpuStart);
// Ensure minimum elapsed time
const actualElapsed = Math.max(elapsed, 100);
const cpuPercent = Math.min(((cpuEnd.user + cpuEnd.system) / 1000) / actualElapsed * 100, 100);
console.log(`CPU usage for ${connectionCount} connections:`);
console.log(` Total time: ${actualElapsed}ms`);
console.log(` CPU time: ${(cpuEnd.user + cpuEnd.system) / 1000}ms`);
console.log(` CPU usage: ${cpuPercent.toFixed(2)}%`);
console.log(` Average per connection: ${(cpuPercent / connectionCount).toFixed(2)}%`);
// CPU usage should be reasonable (relaxed for test environment)
expect(cpuPercent).toBeLessThan(100); // Must be less than 100%
});
tap.test('CPERF-04: CPU usage during message sending', async (tools) => {
tools.timeout(30000);
console.log('\nTesting CPU usage during message sending...');
const client = await createSmtpClient({
host: testServer.hostname,
port: testServer.port,
secure: false,
debug: false
});
const messageCount = 10; // Reduced for more stable measurement
// Measure CPU during message sending
const cpuStart = process.cpuUsage();
const startTime = Date.now();
for (let i = 0; i < messageCount; i++) {
const email = new Email({
from: 'sender@example.com',
to: [`recipient${i}@example.com`],
subject: `CPU test message ${i + 1}`,
text: `Testing CPU usage during message ${i + 1}`
});
await client.sendMail(email);
// Small delay between messages
await new Promise(resolve => setTimeout(resolve, 50));
}
const elapsed = Date.now() - startTime;
const cpuEnd = process.cpuUsage(cpuStart);
const actualElapsed = Math.max(elapsed, 100);
const cpuPercent = Math.min(((cpuEnd.user + cpuEnd.system) / 1000) / actualElapsed * 100, 100);
await client.close();
console.log(`CPU usage for ${messageCount} messages:`);
console.log(` Total time: ${actualElapsed}ms`);
console.log(` CPU time: ${(cpuEnd.user + cpuEnd.system) / 1000}ms`);
console.log(` CPU usage: ${cpuPercent.toFixed(2)}%`);
console.log(` Messages per second: ${(messageCount / (actualElapsed / 1000)).toFixed(2)}`);
console.log(` CPU per message: ${(cpuPercent / messageCount).toFixed(2)}%`);
// CPU usage should be efficient (relaxed for test environment)
expect(cpuPercent).toBeLessThan(100);
});
tap.test('CPERF-04: CPU usage with parallel operations', async (tools) => {
tools.timeout(30000);
console.log('\nTesting CPU usage with parallel operations...');
// Create multiple clients for parallel operations
const clientCount = 2; // Reduced
const messagesPerClient = 3; // Reduced
const clients = [];
for (let i = 0; i < clientCount; i++) {
clients.push(await createSmtpClient({
host: testServer.hostname,
port: testServer.port,
secure: false,
debug: false
}));
}
// Measure CPU during parallel operations
const cpuStart = process.cpuUsage();
const startTime = Date.now();
const promises = [];
for (let clientIndex = 0; clientIndex < clientCount; clientIndex++) {
for (let msgIndex = 0; msgIndex < messagesPerClient; msgIndex++) {
const email = new Email({
from: 'sender@example.com',
to: [`recipient${clientIndex}-${msgIndex}@example.com`],
subject: `Parallel CPU test ${clientIndex}-${msgIndex}`,
text: 'Testing CPU with parallel operations'
});
promises.push(clients[clientIndex].sendMail(email));
}
}
await Promise.all(promises);
const elapsed = Date.now() - startTime;
const cpuEnd = process.cpuUsage(cpuStart);
const actualElapsed = Math.max(elapsed, 100);
const cpuPercent = Math.min(((cpuEnd.user + cpuEnd.system) / 1000) / actualElapsed * 100, 100);
// Close all clients
await Promise.all(clients.map(client => client.close()));
const totalMessages = clientCount * messagesPerClient;
console.log(`CPU usage for ${totalMessages} messages across ${clientCount} clients:`);
console.log(` Total time: ${actualElapsed}ms`);
console.log(` CPU time: ${(cpuEnd.user + cpuEnd.system) / 1000}ms`);
console.log(` CPU usage: ${cpuPercent.toFixed(2)}%`);
// Parallel operations should complete successfully
expect(cpuPercent).toBeLessThan(100);
});
tap.test('CPERF-04: CPU usage with large messages', async (tools) => {
tools.timeout(30000);
console.log('\nTesting CPU usage with large messages...');
const client = await createSmtpClient({
host: testServer.hostname,
port: testServer.port,
secure: false,
debug: false
});
const messageSizes = [
{ name: 'small', size: 1024 }, // 1KB
{ name: 'medium', size: 10240 }, // 10KB
{ name: 'large', size: 51200 } // 50KB (reduced from 100KB)
];
for (const { name, size } of messageSizes) {
const cpuStart = process.cpuUsage();
const startTime = Date.now();
const email = new Email({
from: 'sender@example.com',
to: ['recipient@example.com'],
subject: `Large message test (${name})`,
text: 'x'.repeat(size)
});
await client.sendMail(email);
const elapsed = Date.now() - startTime;
const cpuEnd = process.cpuUsage(cpuStart);
const actualElapsed = Math.max(elapsed, 1);
const cpuPercent = Math.min(((cpuEnd.user + cpuEnd.system) / 1000) / actualElapsed * 100, 100);
console.log(`CPU usage for ${name} message (${size} bytes):`);
console.log(` Time: ${actualElapsed}ms`);
console.log(` CPU: ${cpuPercent.toFixed(2)}%`);
console.log(` Throughput: ${(size / 1024 / (actualElapsed / 1000)).toFixed(2)} KB/s`);
// Small delay between messages
await new Promise(resolve => setTimeout(resolve, 100));
}
await client.close();
});
tap.test('CPERF-04: CPU usage with connection pooling', async (tools) => {
tools.timeout(30000);
console.log('\nTesting CPU usage with connection pooling...');
const pooledClient = await createPooledSmtpClient({
host: testServer.hostname,
port: testServer.port,
secure: false,
maxConnections: 2, // Reduced
debug: false
});
const messageCount = 8; // Reduced
// Measure CPU with pooling
const cpuStart = process.cpuUsage();
const startTime = Date.now();
const promises = [];
for (let i = 0; i < messageCount; i++) {
const email = new Email({
from: 'sender@example.com',
to: [`recipient${i}@example.com`],
subject: `Pooled CPU test ${i + 1}`,
text: 'Testing CPU usage with connection pooling'
});
promises.push(pooledClient.sendMail(email));
}
await Promise.all(promises);
const elapsed = Date.now() - startTime;
const cpuEnd = process.cpuUsage(cpuStart);
const actualElapsed = Math.max(elapsed, 100);
const cpuPercent = Math.min(((cpuEnd.user + cpuEnd.system) / 1000) / actualElapsed * 100, 100);
await pooledClient.close();
console.log(`CPU usage for ${messageCount} messages with pooling:`);
console.log(` Total time: ${actualElapsed}ms`);
console.log(` CPU time: ${(cpuEnd.user + cpuEnd.system) / 1000}ms`);
console.log(` CPU usage: ${cpuPercent.toFixed(2)}%`);
// Pooling should complete successfully
expect(cpuPercent).toBeLessThan(100);
});
tap.test('CPERF-04: CPU profile over time', async (tools) => {
tools.timeout(30000);
console.log('\nTesting CPU profile over time...');
const client = await createSmtpClient({
host: testServer.hostname,
port: testServer.port,
secure: false,
debug: false
});
const duration = 8000; // 8 seconds (reduced)
const interval = 2000; // Sample every 2 seconds
const samples = [];
const endTime = Date.now() + duration;
let emailsSent = 0;
while (Date.now() < endTime) {
const sampleStart = Date.now();
const cpuStart = process.cpuUsage();
// Send some emails
for (let i = 0; i < 2; i++) { // Reduced from 3
const email = new Email({
from: 'sender@example.com',
to: ['recipient@example.com'],
subject: `CPU profile test ${++emailsSent}`,
text: `Testing CPU profile at ${new Date().toISOString()}`
});
await client.sendMail(email);
// Small delay between emails
await new Promise(resolve => setTimeout(resolve, 100));
}
const sampleElapsed = Date.now() - sampleStart;
const cpuEnd = process.cpuUsage(cpuStart);
const actualElapsed = Math.max(sampleElapsed, 100);
const cpuPercent = Math.min(((cpuEnd.user + cpuEnd.system) / 1000) / actualElapsed * 100, 100);
samples.push({
time: Date.now() - (endTime - duration),
cpu: cpuPercent,
emails: 2
});
console.log(`[${samples[samples.length - 1].time}ms] CPU: ${cpuPercent.toFixed(2)}%, Emails sent: ${emailsSent}`);
// Wait for next interval
const waitTime = interval - sampleElapsed;
if (waitTime > 0 && Date.now() + waitTime < endTime) {
await new Promise(resolve => setTimeout(resolve, waitTime));
}
}
await client.close();
// Calculate average CPU
const avgCpu = samples.reduce((sum, s) => sum + s.cpu, 0) / samples.length;
const maxCpu = Math.max(...samples.map(s => s.cpu));
const minCpu = Math.min(...samples.map(s => s.cpu));
console.log(`\nCPU profile summary:`);
console.log(` Samples: ${samples.length}`);
console.log(` Average CPU: ${avgCpu.toFixed(2)}%`);
console.log(` Min CPU: ${minCpu.toFixed(2)}%`);
console.log(` Max CPU: ${maxCpu.toFixed(2)}%`);
console.log(` Total emails: ${emailsSent}`);
// CPU should be bounded
expect(avgCpu).toBeLessThan(100); // Average CPU less than 100%
expect(maxCpu).toBeLessThan(100); // Max CPU less than 100%
});
tap.test('cleanup - stop SMTP server', async () => {
await stopTestServer(testServer);
});
export default tap.start();