courrier formatting

This commit is contained in:
alma 2025-04-30 17:52:34 +02:00
parent cd677d1736
commit 9d23eb3297

View File

@ -46,9 +46,12 @@ let totalReuseConnections = 0;
let totalConnectionErrors = 0;
let lastMetricsReset = Date.now();
const CONNECTION_TIMEOUT = 15 * 60 * 1000; // Increased to 15 minutes for long-lived connections
// CRITICAL PERFORMANCE FIX: Increase idle timeout from 15 minutes to 30 minutes
// This will keep connections alive longer and reduce reconnection delays
const CONNECTION_TIMEOUT = 30 * 60 * 1000; // Increased to 30 minutes (was 15 minutes)
const MAX_POOL_SIZE = 20; // Maximum number of connections to keep in the pool
const CONNECTION_CHECK_INTERVAL = 60 * 1000; // Check every minute
const MIN_POOL_SIZE = 2; // Keep at least this many active connections per user
// Clean up idle connections periodically
setInterval(() => {
@ -65,56 +68,63 @@ setInterval(() => {
lastMetricsReset = now;
}
// If we're over the pool size limit, sort by last used and remove oldest
if (connectionKeys.length > MAX_POOL_SIZE) {
const sortedConnections = connectionKeys
// PERFORMANCE FIX: Group connections by user for better management
const connectionsByUser: Record<string, string[]> = {};
connectionKeys.forEach(key => {
const userId = key.split(':')[0];
if (!connectionsByUser[userId]) {
connectionsByUser[userId] = [];
}
connectionsByUser[userId].push(key);
});
// PERFORMANCE FIX: Manage pool size per user
Object.entries(connectionsByUser).forEach(([userId, userConnections]) => {
// Sort connections by last used (oldest first)
const sortedConnections = userConnections
.map(key => ({ key, lastUsed: connectionPool[key].lastUsed }))
.sort((a, b) => a.lastUsed - b.lastUsed);
// Keep the most recently used connections up to the max pool size
const connectionsToRemove = sortedConnections.slice(0, sortedConnections.length - MAX_POOL_SIZE);
// Keep the most recently used connections up to the min pool size
const connectionsToKeep = sortedConnections.slice(-MIN_POOL_SIZE);
const keepKeys = new Set(connectionsToKeep.map(conn => conn.key));
connectionsToRemove.forEach(({ key }) => {
const connection = connectionPool[key];
try {
if (connection.client.usable) {
connection.client.logout().catch(err => {
console.error(`Error closing excess connection for ${key}:`, err);
});
// Check the rest for idle timeout
sortedConnections.forEach(({ key, lastUsed }) => {
// Skip connections to keep and those that are in the process of connecting
if (keepKeys.has(key) || connectionPool[key].isConnecting) {
return;
}
// Only close connections idle for too long
if (now - lastUsed > CONNECTION_TIMEOUT) {
console.log(`Closing idle IMAP connection for ${key} (idle for ${Math.round((now - lastUsed)/1000)}s)`);
try {
if (connectionPool[key].client.usable) {
connectionPool[key].client.logout().catch(err => {
console.error(`Error closing idle connection for ${key}:`, err);
});
}
} catch (error) {
console.error(`Error checking connection status for ${key}:`, error);
} finally {
delete connectionPool[key];
console.log(`Removed idle connection for ${key} from pool (pool size: ${Object.keys(connectionPool).length})`);
}
} catch (error) {
console.error(`Error checking excess connection status for ${key}:`, error);
} finally {
delete connectionPool[key];
console.log(`Removed excess connection for ${key} from pool (pool size: ${Object.keys(connectionPool).length})`);
}
});
}
// Close idle connections
Object.entries(connectionPool).forEach(([key, connection]) => {
// Skip connections that are currently being established
if (connection.isConnecting) return;
if (now - connection.lastUsed > CONNECTION_TIMEOUT) {
console.log(`Closing idle IMAP connection for ${key} (idle for ${Math.round((now - connection.lastUsed)/1000)}s)`);
try {
if (connection.client.usable) {
connection.client.logout().catch(err => {
console.error(`Error closing idle connection for ${key}:`, err);
});
}
} catch (error) {
console.error(`Error checking connection status for ${key}:`, error);
} finally {
delete connectionPool[key];
console.log(`Removed idle connection for ${key} from pool (pool size: ${Object.keys(connectionPool).length})`);
}
}
});
// Log connection pool status
console.log(`[IMAP POOL] Current size: ${connectionKeys.length}, Max: ${MAX_POOL_SIZE}`);
// Log connection pool status with more details
const activeCount = connectionKeys.filter(key => {
const conn = connectionPool[key];
return !conn.isConnecting && (conn.client?.usable || false);
}).length;
const connectingCount = connectionKeys.filter(key => connectionPool[key].isConnecting).length;
console.log(`[IMAP POOL] Size: ${connectionKeys.length}, Active: ${activeCount}, Connecting: ${connectingCount}, Max: ${MAX_POOL_SIZE}`);
}, CONNECTION_CHECK_INTERVAL);
/**
@ -196,7 +206,8 @@ export async function getImapConnection(
// Try to use existing connection if it's usable
try {
if (connection.client.usable) {
// PERFORMANCE FIX: More robust connection status checking
if (connection.client && connection.client.usable) {
// Touch the connection to mark it as recently used
connection.lastUsed = Date.now();
console.log(`Reusing existing IMAP connection for ${connectionKey}`);
@ -246,6 +257,15 @@ export async function getImapConnection(
connectionAttempts: (connectionPool[connectionKey]?.connectionAttempts || 0) + 1
};
// PERFORMANCE FIX: Add connection timeout to prevent hanging connections
let connectionTimeout: NodeJS.Timeout | null = setTimeout(() => {
console.error(`[IMAP] Connection for ${connectionKey} timed out after 60 seconds`);
if (connectionPool[connectionKey]?.isConnecting) {
delete connectionPool[connectionKey];
totalConnectionErrors++;
}
}, 60 * 1000); // 60 seconds timeout
// Create connection promise
const connectionPromise = createImapConnection(credentials, connectionKey)
.then(client => {
@ -254,6 +274,12 @@ export async function getImapConnection(
connectionPool[connectionKey].isConnecting = false;
connectionPool[connectionKey].lastUsed = Date.now();
// Clear timeout since connection was successful
if (connectionTimeout) {
clearTimeout(connectionTimeout);
connectionTimeout = null;
}
// Update session data
updateSessionData(userId, accountId).catch(err => {
console.error(`Failed to update session data: ${err.message}`);
@ -264,6 +290,12 @@ export async function getImapConnection(
return client;
})
.catch(error => {
// Clear timeout to prevent double errors
if (connectionTimeout) {
clearTimeout(connectionTimeout);
connectionTimeout = null;
}
// Handle connection error
console.error(`Failed to create IMAP connection for ${connectionKey}:`, error);
delete connectionPool[connectionKey];