courrier formatting
This commit is contained in:
parent
cd677d1736
commit
9d23eb3297
@ -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];
|
||||
|
||||
Loading…
Reference in New Issue
Block a user