courrier formatting
This commit is contained in:
parent
cd677d1736
commit
9d23eb3297
@ -46,9 +46,12 @@ let totalReuseConnections = 0;
|
|||||||
let totalConnectionErrors = 0;
|
let totalConnectionErrors = 0;
|
||||||
let lastMetricsReset = Date.now();
|
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 MAX_POOL_SIZE = 20; // Maximum number of connections to keep in the pool
|
||||||
const CONNECTION_CHECK_INTERVAL = 60 * 1000; // Check every minute
|
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
|
// Clean up idle connections periodically
|
||||||
setInterval(() => {
|
setInterval(() => {
|
||||||
@ -65,56 +68,63 @@ setInterval(() => {
|
|||||||
lastMetricsReset = now;
|
lastMetricsReset = now;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we're over the pool size limit, sort by last used and remove oldest
|
// PERFORMANCE FIX: Group connections by user for better management
|
||||||
if (connectionKeys.length > MAX_POOL_SIZE) {
|
const connectionsByUser: Record<string, string[]> = {};
|
||||||
const sortedConnections = connectionKeys
|
|
||||||
|
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 }))
|
.map(key => ({ key, lastUsed: connectionPool[key].lastUsed }))
|
||||||
.sort((a, b) => a.lastUsed - b.lastUsed);
|
.sort((a, b) => a.lastUsed - b.lastUsed);
|
||||||
|
|
||||||
// Keep the most recently used connections up to the max pool size
|
// Keep the most recently used connections up to the min pool size
|
||||||
const connectionsToRemove = sortedConnections.slice(0, sortedConnections.length - MAX_POOL_SIZE);
|
const connectionsToKeep = sortedConnections.slice(-MIN_POOL_SIZE);
|
||||||
|
const keepKeys = new Set(connectionsToKeep.map(conn => conn.key));
|
||||||
|
|
||||||
connectionsToRemove.forEach(({ key }) => {
|
// Check the rest for idle timeout
|
||||||
const connection = connectionPool[key];
|
sortedConnections.forEach(({ key, lastUsed }) => {
|
||||||
try {
|
// Skip connections to keep and those that are in the process of connecting
|
||||||
if (connection.client.usable) {
|
if (keepKeys.has(key) || connectionPool[key].isConnecting) {
|
||||||
connection.client.logout().catch(err => {
|
return;
|
||||||
console.error(`Error closing excess connection for ${key}:`, err);
|
}
|
||||||
});
|
|
||||||
|
// 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
|
// Log connection pool status with more details
|
||||||
console.log(`[IMAP POOL] Current size: ${connectionKeys.length}, Max: ${MAX_POOL_SIZE}`);
|
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);
|
}, CONNECTION_CHECK_INTERVAL);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -196,7 +206,8 @@ export async function getImapConnection(
|
|||||||
|
|
||||||
// Try to use existing connection if it's usable
|
// Try to use existing connection if it's usable
|
||||||
try {
|
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
|
// Touch the connection to mark it as recently used
|
||||||
connection.lastUsed = Date.now();
|
connection.lastUsed = Date.now();
|
||||||
console.log(`Reusing existing IMAP connection for ${connectionKey}`);
|
console.log(`Reusing existing IMAP connection for ${connectionKey}`);
|
||||||
@ -246,6 +257,15 @@ export async function getImapConnection(
|
|||||||
connectionAttempts: (connectionPool[connectionKey]?.connectionAttempts || 0) + 1
|
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
|
// Create connection promise
|
||||||
const connectionPromise = createImapConnection(credentials, connectionKey)
|
const connectionPromise = createImapConnection(credentials, connectionKey)
|
||||||
.then(client => {
|
.then(client => {
|
||||||
@ -254,6 +274,12 @@ export async function getImapConnection(
|
|||||||
connectionPool[connectionKey].isConnecting = false;
|
connectionPool[connectionKey].isConnecting = false;
|
||||||
connectionPool[connectionKey].lastUsed = Date.now();
|
connectionPool[connectionKey].lastUsed = Date.now();
|
||||||
|
|
||||||
|
// Clear timeout since connection was successful
|
||||||
|
if (connectionTimeout) {
|
||||||
|
clearTimeout(connectionTimeout);
|
||||||
|
connectionTimeout = null;
|
||||||
|
}
|
||||||
|
|
||||||
// Update session data
|
// Update session data
|
||||||
updateSessionData(userId, accountId).catch(err => {
|
updateSessionData(userId, accountId).catch(err => {
|
||||||
console.error(`Failed to update session data: ${err.message}`);
|
console.error(`Failed to update session data: ${err.message}`);
|
||||||
@ -264,6 +290,12 @@ export async function getImapConnection(
|
|||||||
return client;
|
return client;
|
||||||
})
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
|
// Clear timeout to prevent double errors
|
||||||
|
if (connectionTimeout) {
|
||||||
|
clearTimeout(connectionTimeout);
|
||||||
|
connectionTimeout = null;
|
||||||
|
}
|
||||||
|
|
||||||
// Handle connection error
|
// Handle connection error
|
||||||
console.error(`Failed to create IMAP connection for ${connectionKey}:`, error);
|
console.error(`Failed to create IMAP connection for ${connectionKey}:`, error);
|
||||||
delete connectionPool[connectionKey];
|
delete connectionPool[connectionKey];
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user