mirror of
https://github.com/danny-avila/LibreChat.git
synced 2026-02-18 08:28:10 +01:00
* fix: handle MongoDB connection errors to prevent process crashes
Add mongoose.connection.on('error') listener in connect.js to catch
connection-level errors emitted by MongoDB driver's SDAM monitoring.
Without this listener, these errors become uncaught exceptions per
Node.js EventEmitter behavior.
Also add MongoDB error patterns to the uncaughtException handler in
server/index.js as defense-in-depth, following the same pattern used
for GoogleGenerativeAI, Meilisearch, and OpenAI errors.
Fixes #11808
* style: fix prettier formatting in uncaughtException handler
* refactor: move error listener to module level
* fix: use precise MongoDB error matching in uncaughtException handler
* fix: replace process.exit(1) with graceful error logging
Instead of maintaining a growing list of error patterns that should
not crash the process, invert the default behavior: log all unhandled
errors and keep running. The existing specific handlers are preserved
for their contextual log messages.
This prevents process crashes from any transient error (MongoDB timeouts,
network issues, third-party library bugs) without needing to add new
patterns each time a new error type is encountered. Unnecessary restarts
are expensive as they trigger full Meilisearch re-syncs under load.
* fix: address review feedback
- connect.js: pass full error object to logger instead of just message
- server/index.js: add optional chaining for nullish err
- server/index.js: make crash-on-unknown-error opt-in via
CRASH_ON_UNCAUGHT_EXCEPTION env var (defaults to graceful logging)
* fix: rename to CONTINUE_ON_UNCAUGHT_EXCEPTION, default to exit
---------
Co-authored-by: Feng Lu <feng.lu@kindredgroup.com>
83 lines
3.2 KiB
JavaScript
83 lines
3.2 KiB
JavaScript
require('dotenv').config();
|
|
const { isEnabled } = require('@librechat/api');
|
|
const { logger } = require('@librechat/data-schemas');
|
|
|
|
const mongoose = require('mongoose');
|
|
const MONGO_URI = process.env.MONGO_URI;
|
|
|
|
if (!MONGO_URI) {
|
|
throw new Error('Please define the MONGO_URI environment variable');
|
|
}
|
|
/** The maximum number of connections in the connection pool. */
|
|
const maxPoolSize = parseInt(process.env.MONGO_MAX_POOL_SIZE) || undefined;
|
|
/** The minimum number of connections in the connection pool. */
|
|
const minPoolSize = parseInt(process.env.MONGO_MIN_POOL_SIZE) || undefined;
|
|
/** The maximum number of connections that may be in the process of being established concurrently by the connection pool. */
|
|
const maxConnecting = parseInt(process.env.MONGO_MAX_CONNECTING) || undefined;
|
|
/** The maximum number of milliseconds that a connection can remain idle in the pool before being removed and closed. */
|
|
const maxIdleTimeMS = parseInt(process.env.MONGO_MAX_IDLE_TIME_MS) || undefined;
|
|
/** The maximum time in milliseconds that a thread can wait for a connection to become available. */
|
|
const waitQueueTimeoutMS = parseInt(process.env.MONGO_WAIT_QUEUE_TIMEOUT_MS) || undefined;
|
|
/** Set to false to disable automatic index creation for all models associated with this connection. */
|
|
const autoIndex =
|
|
process.env.MONGO_AUTO_INDEX != undefined
|
|
? isEnabled(process.env.MONGO_AUTO_INDEX) || false
|
|
: undefined;
|
|
|
|
/** Set to `false` to disable Mongoose automatically calling `createCollection()` on every model created on this connection. */
|
|
const autoCreate =
|
|
process.env.MONGO_AUTO_CREATE != undefined
|
|
? isEnabled(process.env.MONGO_AUTO_CREATE) || false
|
|
: undefined;
|
|
/**
|
|
* Global is used here to maintain a cached connection across hot reloads
|
|
* in development. This prevents connections growing exponentially
|
|
* during API Route usage.
|
|
*/
|
|
let cached = global.mongoose;
|
|
|
|
if (!cached) {
|
|
cached = global.mongoose = { conn: null, promise: null };
|
|
}
|
|
|
|
mongoose.connection.on('error', (err) => {
|
|
logger.error('[connectDb] MongoDB connection error:', err);
|
|
});
|
|
|
|
async function connectDb() {
|
|
if (cached.conn && cached.conn?._readyState === 1) {
|
|
return cached.conn;
|
|
}
|
|
|
|
const disconnected = cached.conn && cached.conn?._readyState !== 1;
|
|
if (!cached.promise || disconnected) {
|
|
const opts = {
|
|
bufferCommands: false,
|
|
...(maxPoolSize ? { maxPoolSize } : {}),
|
|
...(minPoolSize ? { minPoolSize } : {}),
|
|
...(maxConnecting ? { maxConnecting } : {}),
|
|
...(maxIdleTimeMS ? { maxIdleTimeMS } : {}),
|
|
...(waitQueueTimeoutMS ? { waitQueueTimeoutMS } : {}),
|
|
...(autoIndex != undefined ? { autoIndex } : {}),
|
|
...(autoCreate != undefined ? { autoCreate } : {}),
|
|
// useNewUrlParser: true,
|
|
// useUnifiedTopology: true,
|
|
// bufferMaxEntries: 0,
|
|
// useFindAndModify: true,
|
|
// useCreateIndex: true
|
|
};
|
|
logger.info('Mongo Connection options');
|
|
logger.info(JSON.stringify(opts, null, 2));
|
|
mongoose.set('strictQuery', true);
|
|
cached.promise = mongoose.connect(MONGO_URI, opts).then((mongoose) => {
|
|
return mongoose;
|
|
});
|
|
}
|
|
cached.conn = await cached.promise;
|
|
|
|
return cached.conn;
|
|
}
|
|
|
|
module.exports = {
|
|
connectDb,
|
|
};
|