Updates
This commit is contained in:
+53
-3
@@ -56,16 +56,31 @@ const pluginRegistry = new Map();
|
||||
|
||||
// Serialize HyperDB insert/delete/flush per plugin (HyperDB refuses flush while mutating)
|
||||
const dbWriteQueues = new Map();
|
||||
const dbWriteBatchDepth = new Map();
|
||||
|
||||
/**
|
||||
* Run a write operation after prior writes for the same plugin complete.
|
||||
* Nested calls (inside db.runWrite) run inline without re-queueing.
|
||||
* @param {string} pluginDomain
|
||||
* @param {Function} fn
|
||||
* @returns {Promise<*>}
|
||||
*/
|
||||
function enqueueDbWrite(pluginDomain, fn) {
|
||||
const depth = dbWriteBatchDepth.get(pluginDomain) || 0;
|
||||
if (depth > 0) {
|
||||
return fn();
|
||||
}
|
||||
|
||||
const prev = dbWriteQueues.get(pluginDomain) || Promise.resolve();
|
||||
const next = prev.then(() => fn(), () => fn());
|
||||
const run = async () => {
|
||||
dbWriteBatchDepth.set(pluginDomain, depth + 1);
|
||||
try {
|
||||
return await fn();
|
||||
} finally {
|
||||
dbWriteBatchDepth.set(pluginDomain, depth);
|
||||
}
|
||||
};
|
||||
const next = prev.then(run, run);
|
||||
dbWriteQueues.set(pluginDomain, next.catch(() => {}));
|
||||
return next;
|
||||
}
|
||||
@@ -3955,6 +3970,19 @@ const sdk = {
|
||||
* @param {Object} doc - Document to insert
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
/**
|
||||
* Run multiple inserts/deletes/flushes as one serialized write batch.
|
||||
* @param {Function} fn - Async function performing DB writes
|
||||
* @returns {Promise<*>}
|
||||
*/
|
||||
async runWrite(fn) {
|
||||
const pluginDomain = _getPluginDomain();
|
||||
if (!pluginDomain) {
|
||||
throw new Error('Plugin domain not available');
|
||||
}
|
||||
return enqueueDbWrite(pluginDomain, () => fn());
|
||||
},
|
||||
|
||||
async insert(collection, doc) {
|
||||
const pluginDomain = _getPluginDomain();
|
||||
if (!pluginDomain) {
|
||||
@@ -3994,11 +4022,24 @@ const sdk = {
|
||||
throw new Error('Database is closed');
|
||||
}
|
||||
logDebug('PluginSDK', `[db.insert] Calling db.insert()...`);
|
||||
await db.insert(collection, doc);
|
||||
const INSERT_TIMEOUT = 120000;
|
||||
const insertPromise = db.insert(collection, doc);
|
||||
const timeoutPromise = new Promise((_, reject) => {
|
||||
setTimeout(() => reject(new Error(`db.insert() timeout after ${INSERT_TIMEOUT}ms`)), INSERT_TIMEOUT);
|
||||
});
|
||||
await Promise.race([insertPromise, timeoutPromise]);
|
||||
logDebug('PluginSDK', `[db.insert] Insert completed in ${Date.now() - startTime}ms`);
|
||||
return;
|
||||
} catch (err) {
|
||||
logError('PluginSDK', `[db.insert] Attempt ${attempt} failed: ${err.message}`);
|
||||
const isWriteContention = err.message && (
|
||||
err.message.includes('Insert/delete in progress') ||
|
||||
err.message.includes('refusing to commit')
|
||||
);
|
||||
if (isWriteContention && attempt < MAX_ATTEMPTS) {
|
||||
await new Promise((resolve) => setTimeout(resolve, 50));
|
||||
continue;
|
||||
}
|
||||
const recovered = await this._recoverFromDbError(err, db, attempt, MAX_ATTEMPTS);
|
||||
if (recovered) {
|
||||
db = recovered;
|
||||
@@ -4175,7 +4216,7 @@ const sdk = {
|
||||
logDebug('PluginSDK', `[db.flush] Got database instance, closed=${db.closed}`);
|
||||
|
||||
// Flush with automatic database recreation on SESSION_CLOSED errors
|
||||
const MAX_ATTEMPTS = 2;
|
||||
const MAX_ATTEMPTS = 15;
|
||||
for (let attempt = 1; attempt <= MAX_ATTEMPTS; attempt++) {
|
||||
try {
|
||||
logDebug('PluginSDK', `[db.flush] Attempt ${attempt}: Calling db.ready()...`);
|
||||
@@ -4199,6 +4240,15 @@ const sdk = {
|
||||
return;
|
||||
} catch (err) {
|
||||
logError('PluginSDK', `[db.flush] Attempt ${attempt} failed: ${err.message}`);
|
||||
const isWriteContention = err.message && (
|
||||
err.message.includes('Insert/delete in progress') ||
|
||||
err.message.includes('refusing to commit') ||
|
||||
err.message.includes('Database has changed')
|
||||
);
|
||||
if (isWriteContention && attempt < MAX_ATTEMPTS) {
|
||||
await new Promise((resolve) => setTimeout(resolve, 50));
|
||||
continue;
|
||||
}
|
||||
const isSessionClosed = err.message && (
|
||||
err.message.includes('SESSION_CLOSED') ||
|
||||
err.message.includes('Database not available') ||
|
||||
|
||||
Reference in New Issue
Block a user