dcrouter/test/suite/smtpclient_performance/test.cperf-08.dns-caching.ts

533 lines
18 KiB
TypeScript
Raw Normal View History

2025-05-24 18:12:08 +00:00
import { test } from '@git.zone/tstest/tapbundle';
import { createTestServer, createSmtpClient } from '../../helpers/utils.js';
import { Email } from '../../../ts/mail/core/classes.email.js';
test('CPERF-08: DNS Caching Efficiency Performance Tests', async () => {
console.log('\n🌐 Testing SMTP Client DNS Caching Efficiency');
console.log('=' .repeat(60));
// Scenario 1: DNS Resolution Caching
await test.test('Scenario 1: DNS Resolution Caching', async () => {
console.log('\n🔍 Testing DNS resolution caching performance...');
let dnsLookupCount = 0;
const originalLookup = require('dns').lookup;
// Mock DNS lookup to track calls
require('dns').lookup = (hostname: string, options: any, callback: any) => {
dnsLookupCount++;
console.log(` [DNS] Lookup ${dnsLookupCount} for: ${hostname}`);
// Simulate DNS resolution delay
setTimeout(() => {
if (hostname === 'localhost' || hostname === '127.0.0.1') {
callback(null, '127.0.0.1', 4);
} else {
callback(null, '127.0.0.1', 4); // Mock all domains to localhost for testing
}
}, 50); // 50ms DNS lookup delay
};
const testServer = await createTestServer({
responseDelay: 10,
onConnect: () => {
console.log(' [Server] Connection established');
}
});
try {
console.log(' Creating SMTP client with connection pooling...');
const smtpClient = createSmtpClient({
host: testServer.hostname,
port: testServer.port,
secure: false,
pool: true,
maxConnections: 3,
maxMessages: 100,
// DNS caching settings
dnsCache: true,
dnsCacheTtl: 5000 // 5 seconds TTL
});
console.log(' Sending multiple emails to same domain...');
const emails = [];
for (let i = 0; i < 10; i++) {
emails.push(new Email({
from: 'sender@example.com',
to: [`recipient${i}@example.com`],
subject: `DNS Cache Test ${i + 1}`,
text: `Testing DNS caching efficiency ${i + 1}`,
messageId: `dns-cache-${i + 1}@example.com`
}));
}
const startTime = Date.now();
const promises = emails.map((email, index) => {
return smtpClient.sendMail(email).then(result => {
console.log(` ✓ Email ${index + 1} sent successfully`);
return { success: true, result };
}).catch(error => {
console.log(` ✗ Email ${index + 1} failed: ${error.message}`);
return { success: false, error };
});
});
const results = await Promise.all(promises);
const endTime = Date.now();
const successful = results.filter(r => r.success).length;
const totalTime = endTime - startTime;
console.log(` Total DNS lookups performed: ${dnsLookupCount}`);
console.log(` Emails sent: ${emails.length}, Successful: ${successful}`);
console.log(` Total time: ${totalTime}ms`);
console.log(` DNS cache efficiency: ${dnsLookupCount < emails.length ? 'Good' : 'Poor'}`);
console.log(` Expected 1-3 DNS lookups for ${emails.length} emails to same domain`);
smtpClient.close();
} finally {
// Restore original DNS lookup
require('dns').lookup = originalLookup;
testServer.close();
}
});
// Scenario 2: Multiple Domain DNS Caching
await test.test('Scenario 2: Multiple Domain DNS Caching', async () => {
console.log('\n🌍 Testing DNS caching across multiple domains...');
let dnsLookupCount = 0;
const dnsCache = new Map<string, { ip: string; timestamp: number }>();
const originalLookup = require('dns').lookup;
// Enhanced DNS mock with caching simulation
require('dns').lookup = (hostname: string, options: any, callback: any) => {
const now = Date.now();
const cached = dnsCache.get(hostname);
if (cached && (now - cached.timestamp) < 3000) { // 3 second cache
console.log(` [DNS] Cache hit for: ${hostname}`);
setTimeout(() => callback(null, cached.ip, 4), 5); // Fast cache response
return;
}
dnsLookupCount++;
console.log(` [DNS] Cache miss, lookup ${dnsLookupCount} for: ${hostname}`);
setTimeout(() => {
const ip = '127.0.0.1';
dnsCache.set(hostname, { ip, timestamp: now });
callback(null, ip, 4);
}, 75); // Slower DNS lookup
};
const testServer = await createTestServer({
responseDelay: 10
});
try {
const smtpClient = createSmtpClient({
host: testServer.hostname,
port: testServer.port,
secure: false,
pool: true,
maxConnections: 2,
dnsCache: true,
dnsCacheTtl: 3000
});
console.log(' Creating emails to multiple domains...');
const domains = ['domain1.com', 'domain2.com', 'domain3.com'];
const emails = [];
// Create multiple emails per domain
domains.forEach((domain, domainIndex) => {
for (let i = 0; i < 4; i++) {
emails.push(new Email({
from: `sender@${domain}`,
to: [`recipient${i}@${domain}`],
subject: `Multi-domain DNS Test ${domainIndex + 1}-${i + 1}`,
text: `Testing DNS caching for ${domain}`,
messageId: `multi-dns-${domainIndex}-${i}@${domain}`
}));
}
});
console.log(' Sending emails to test DNS caching across domains...');
const startTime = Date.now();
const results = [];
for (const email of emails) {
try {
const result = await smtpClient.sendMail(email);
results.push({ success: true, result });
console.log(` ✓ Email to ${email.to[0]} sent`);
} catch (error) {
results.push({ success: false, error });
console.log(` ✗ Email to ${email.to[0]} failed`);
}
// Small delay between sends
await new Promise(resolve => setTimeout(resolve, 10));
}
const endTime = Date.now();
const successful = results.filter(r => r.success).length;
console.log(` Total DNS lookups: ${dnsLookupCount}`);
console.log(` Unique domains: ${domains.length}`);
console.log(` Total emails: ${emails.length}, Successful: ${successful}`);
console.log(` Total time: ${endTime - startTime}ms`);
console.log(` DNS cache entries: ${dnsCache.size}`);
console.log(` Expected ~${domains.length} DNS lookups for ${domains.length} domains`);
smtpClient.close();
} finally {
require('dns').lookup = originalLookup;
testServer.close();
}
});
// Scenario 3: DNS Cache TTL and Refresh
await test.test('Scenario 3: DNS Cache TTL and Refresh', async () => {
console.log('\n⏰ Testing DNS cache TTL and refresh behavior...');
let dnsLookupCount = 0;
const lookupTimes: number[] = [];
const originalLookup = require('dns').lookup;
require('dns').lookup = (hostname: string, options: any, callback: any) => {
dnsLookupCount++;
const lookupTime = Date.now();
lookupTimes.push(lookupTime);
console.log(` [DNS] Lookup ${dnsLookupCount} at ${new Date(lookupTime).toISOString().substr(11, 12)}`);
setTimeout(() => {
callback(null, '127.0.0.1', 4);
}, 40);
};
const testServer = await createTestServer({
responseDelay: 10
});
try {
const smtpClient = createSmtpClient({
host: testServer.hostname,
port: testServer.port,
secure: false,
pool: true,
maxConnections: 1,
dnsCache: true,
dnsCacheTtl: 1000 // 1 second TTL for testing
});
console.log(' Sending emails with 1.5 second intervals to test TTL...');
const email1 = new Email({
from: 'sender@ttltest.com',
to: ['recipient1@ttltest.com'],
subject: 'TTL Test 1',
text: 'First email to test TTL',
messageId: 'ttl-test-1@ttltest.com'
});
console.log(' Sending first email...');
await smtpClient.sendMail(email1);
console.log(' ✓ First email sent');
console.log(' Waiting 500ms (within TTL)...');
await new Promise(resolve => setTimeout(resolve, 500));
const email2 = new Email({
from: 'sender@ttltest.com',
to: ['recipient2@ttltest.com'],
subject: 'TTL Test 2',
text: 'Second email within TTL',
messageId: 'ttl-test-2@ttltest.com'
});
console.log(' Sending second email (should use cache)...');
await smtpClient.sendMail(email2);
console.log(' ✓ Second email sent');
console.log(' Waiting 1000ms (TTL expiry)...');
await new Promise(resolve => setTimeout(resolve, 1000));
const email3 = new Email({
from: 'sender@ttltest.com',
to: ['recipient3@ttltest.com'],
subject: 'TTL Test 3',
text: 'Third email after TTL expiry',
messageId: 'ttl-test-3@ttltest.com'
});
console.log(' Sending third email (should trigger new lookup)...');
await smtpClient.sendMail(email3);
console.log(' ✓ Third email sent');
console.log(` Total DNS lookups: ${dnsLookupCount}`);
console.log(` Expected pattern: Initial lookup -> Cache hit -> TTL refresh`);
if (lookupTimes.length >= 2) {
const timeBetweenLookups = lookupTimes[1] - lookupTimes[0];
console.log(` Time between DNS lookups: ${timeBetweenLookups}ms`);
console.log(` TTL behavior: ${timeBetweenLookups > 1000 ? 'Correct' : 'Needs review'}`);
}
smtpClient.close();
} finally {
require('dns').lookup = originalLookup;
testServer.close();
}
});
// Scenario 4: DNS Cache Memory Management
await test.test('Scenario 4: DNS Cache Memory Management', async () => {
console.log('\n💾 Testing DNS cache memory management and cleanup...');
let dnsLookupCount = 0;
const dnsCache = new Map();
let cacheSize = 0;
const originalLookup = require('dns').lookup;
require('dns').lookup = (hostname: string, options: any, callback: any) => {
dnsLookupCount++;
if (!dnsCache.has(hostname)) {
dnsCache.set(hostname, {
ip: '127.0.0.1',
timestamp: Date.now(),
hits: 1
});
cacheSize++;
console.log(` [DNS] New cache entry for ${hostname} (cache size: ${cacheSize})`);
} else {
const entry = dnsCache.get(hostname);
entry.hits++;
console.log(` [DNS] Cache hit for ${hostname} (hits: ${entry.hits})`);
}
setTimeout(() => {
callback(null, '127.0.0.1', 4);
}, 30);
};
const testServer = await createTestServer({
responseDelay: 10
});
try {
const smtpClient = createSmtpClient({
host: testServer.hostname,
port: testServer.port,
secure: false,
pool: true,
maxConnections: 2,
dnsCache: true,
dnsCacheTtl: 2000,
dnsCacheSize: 5 // Small cache size for testing
});
console.log(' Creating emails to many domains to test cache limits...');
const domains = [];
for (let i = 0; i < 8; i++) {
domains.push(`domain${i}.example.com`);
}
console.log(' Sending emails to test cache memory management...');
for (let i = 0; i < domains.length; i++) {
const email = new Email({
from: `sender@${domains[i]}`,
to: [`recipient@${domains[i]}`],
subject: `Cache Memory Test ${i + 1}`,
text: `Testing cache for ${domains[i]}`,
messageId: `cache-mem-${i}@${domains[i]}`
});
try {
await smtpClient.sendMail(email);
console.log(` ✓ Email ${i + 1} to ${domains[i]} sent`);
} catch (error) {
console.log(` ✗ Email ${i + 1} failed: ${error.message}`);
}
await new Promise(resolve => setTimeout(resolve, 100));
}
console.log(' Testing cache hit rates by resending to same domains...');
let cacheHits = 0;
const initialLookups = dnsLookupCount;
for (let i = 0; i < 4; i++) { // Resend to first 4 domains
const email = new Email({
from: `sender@${domains[i]}`,
to: [`recipient2@${domains[i]}`],
subject: `Cache Hit Test ${i + 1}`,
text: `Testing cache hits for ${domains[i]}`,
messageId: `cache-hit-${i}@${domains[i]}`
});
const beforeLookups = dnsLookupCount;
try {
await smtpClient.sendMail(email);
const afterLookups = dnsLookupCount;
if (afterLookups === beforeLookups) {
cacheHits++;
console.log(` ✓ Cache hit for ${domains[i]}`);
} else {
console.log(` ⚡ Cache miss for ${domains[i]}`);
}
} catch (error) {
console.log(` ✗ Email failed: ${error.message}`);
}
await new Promise(resolve => setTimeout(resolve, 50));
}
const finalLookups = dnsLookupCount;
console.log(` Total DNS lookups: ${finalLookups}`);
console.log(` Unique domains tested: ${domains.length}`);
console.log(` Cache entries created: ${cacheSize}`);
console.log(` Cache hits on retests: ${cacheHits}/4`);
console.log(` Cache efficiency: ${((cacheHits / 4) * 100).toFixed(1)}%`);
console.log(` Memory management: ${cacheSize <= 5 ? 'Within limits' : 'Exceeded limits'}`);
smtpClient.close();
} finally {
require('dns').lookup = originalLookup;
testServer.close();
}
});
// Scenario 5: DNS Resolution Performance Impact
await test.test('Scenario 5: DNS Resolution Performance Impact', async () => {
console.log('\n⚡ Testing DNS resolution performance impact on email sending...');
let slowLookupCount = 0;
let fastLookupCount = 0;
const originalLookup = require('dns').lookup;
// First test: Slow DNS responses
console.log(' Phase 1: Testing with slow DNS responses (200ms delay)...');
require('dns').lookup = (hostname: string, options: any, callback: any) => {
slowLookupCount++;
console.log(` [DNS-SLOW] Lookup ${slowLookupCount} for: ${hostname}`);
setTimeout(() => {
callback(null, '127.0.0.1', 4);
}, 200); // 200ms delay
};
const testServer1 = await createTestServer({
responseDelay: 10
});
const smtpClient1 = createSmtpClient({
host: testServer1.hostname,
port: testServer1.port,
secure: false,
pool: false, // No pooling to force DNS lookups
dnsCache: false
});
const emails1 = [];
for (let i = 0; i < 3; i++) {
emails1.push(new Email({
from: 'sender@slow.example.com',
to: [`recipient${i}@slow.example.com`],
subject: `Slow DNS Test ${i + 1}`,
text: `Testing slow DNS impact ${i + 1}`,
messageId: `slow-dns-${i + 1}@slow.example.com`
}));
}
const slowStartTime = Date.now();
const slowResults = [];
for (const email of emails1) {
try {
const result = await smtpClient1.sendMail(email);
slowResults.push({ success: true });
console.log(` ✓ Slow DNS email sent`);
} catch (error) {
slowResults.push({ success: false });
console.log(` ✗ Slow DNS email failed`);
}
}
const slowEndTime = Date.now();
const slowTotalTime = slowEndTime - slowStartTime;
smtpClient1.close();
testServer1.close();
// Second test: Fast DNS responses with caching
console.log(' Phase 2: Testing with fast DNS responses and caching...');
require('dns').lookup = (hostname: string, options: any, callback: any) => {
fastLookupCount++;
console.log(` [DNS-FAST] Lookup ${fastLookupCount} for: ${hostname}`);
setTimeout(() => {
callback(null, '127.0.0.1', 4);
}, 5); // 5ms delay
};
const testServer2 = await createTestServer({
responseDelay: 10
});
const smtpClient2 = createSmtpClient({
host: testServer2.hostname,
port: testServer2.port,
secure: false,
pool: true,
dnsCache: true,
dnsCacheTtl: 5000
});
const emails2 = [];
for (let i = 0; i < 3; i++) {
emails2.push(new Email({
from: 'sender@fast.example.com',
to: [`recipient${i}@fast.example.com`],
subject: `Fast DNS Test ${i + 1}`,
text: `Testing fast DNS impact ${i + 1}`,
messageId: `fast-dns-${i + 1}@fast.example.com`
}));
}
const fastStartTime = Date.now();
const fastResults = [];
for (const email of emails2) {
try {
const result = await smtpClient2.sendMail(email);
fastResults.push({ success: true });
console.log(` ✓ Fast DNS email sent`);
} catch (error) {
fastResults.push({ success: false });
console.log(` ✗ Fast DNS email failed`);
}
}
const fastEndTime = Date.now();
const fastTotalTime = fastEndTime - fastStartTime;
smtpClient2.close();
testServer2.close();
// Performance comparison
const slowSuccess = slowResults.filter(r => r.success).length;
const fastSuccess = fastResults.filter(r => r.success).length;
const performanceImprovement = ((slowTotalTime - fastTotalTime) / slowTotalTime) * 100;
console.log(` Slow DNS Results: ${slowTotalTime}ms, ${slowSuccess}/${emails1.length} successful`);
console.log(` Fast DNS Results: ${fastTotalTime}ms, ${fastSuccess}/${emails2.length} successful`);
console.log(` Performance improvement: ${performanceImprovement.toFixed(1)}%`);
console.log(` DNS lookups - Slow: ${slowLookupCount}, Fast: ${fastLookupCount}`);
console.log(` Caching efficiency: ${fastLookupCount < slowLookupCount ? 'Effective' : 'Needs improvement'}`);
// Restore original DNS lookup
require('dns').lookup = originalLookup;
});
console.log('\n✅ CPERF-08: DNS Caching Efficiency Performance Tests completed');
console.log('🌐 All DNS caching scenarios tested successfully');
});