Skip to content
Merged
Show file tree
Hide file tree
Changes from 27 commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
daaeed8
fix: mongodb-runner usage and default version to 6.0.2
Moumouls Oct 26, 2024
5811465
fix: json
Moumouls Oct 26, 2024
6e89fd1
feat: parallel include
Moumouls Oct 28, 2024
28ffcd7
Merge branch 'alpha' of github.com:parse-community/parse-server into …
Moumouls Sep 13, 2025
328a4be
Merge branch 'alpha' of github.com:parse-community/parse-server into …
Moumouls Nov 8, 2025
8b99dc5
feat: add test to battle test include
Moumouls Nov 8, 2025
d854752
Merge branch 'alpha' into moumouls/concurrent-include
Moumouls Nov 8, 2025
c613e3f
Merge branch 'alpha' into moumouls/concurrent-include
mtrezza Nov 8, 2025
af50fd3
Merge branch 'alpha' into moumouls/concurrent-include
mtrezza Nov 9, 2025
87339eb
add perf test
mtrezza Nov 9, 2025
2618e96
Merge branch 'alpha' into moumouls/concurrent-include
mtrezza Nov 9, 2025
e46dba6
Merge branch 'alpha' into moumouls/concurrent-include
mtrezza Nov 9, 2025
b7c919d
Merge branch 'alpha' into moumouls/concurrent-include
mtrezza Nov 9, 2025
d432e4c
less verbose
mtrezza Nov 9, 2025
bb74681
db proxy
mtrezza Nov 9, 2025
0bef43f
refactor
mtrezza Nov 9, 2025
97bb64b
Merge branch 'alpha' into moumouls/concurrent-include
mtrezza Nov 9, 2025
713a4d0
remove proxy
mtrezza Nov 9, 2025
0e17c8f
fix
mtrezza Nov 9, 2025
35f1809
Merge branch 'moumouls/concurrent-include' of https://github.com/Moum…
mtrezza Nov 9, 2025
96e8f1f
logging
mtrezza Nov 9, 2025
375c807
db latency
mtrezza Nov 9, 2025
b8ccc4a
Update MongoLatencyWrapper.js
mtrezza Nov 16, 2025
6840b60
schema concurrency fix
mtrezza Nov 16, 2025
523b886
Revert "schema concurrency fix"
mtrezza Nov 16, 2025
e4dbfdf
all benchmarks
mtrezza Nov 16, 2025
735d506
faster
mtrezza Nov 16, 2025
199b726
Merge branch 'alpha' into moumouls/concurrent-include
mtrezza Nov 17, 2025
170ce00
fix
mtrezza Nov 17, 2025
24312ec
fix benchmark
mtrezza Nov 17, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/ci-performance.yml
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ jobs:
env:
NODE_ENV: production
run: |
echo "Running baseline benchmarks with CPU affinity (using PR's benchmark script)..."
echo "Running baseline benchmarks..."
if [ ! -f "benchmark/performance.js" ]; then
echo "⚠️ Benchmark script not found - this is expected for new features"
echo "Skipping baseline benchmark"
Expand Down Expand Up @@ -135,7 +135,7 @@ jobs:
env:
NODE_ENV: production
run: |
echo "Running PR benchmarks with CPU affinity..."
echo "Running PR benchmarks..."
taskset -c 0 npm run benchmark > pr-output.txt 2>&1 || npm run benchmark > pr-output.txt 2>&1 || true
echo "Benchmark command completed with exit code: $?"
echo "Output file size: $(wc -c < pr-output.txt) bytes"
Expand Down
137 changes: 137 additions & 0 deletions benchmark/MongoLatencyWrapper.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
/**
* MongoDB Latency Wrapper
*
* Utility to inject artificial latency into MongoDB operations for performance testing.
* This wrapper temporarily wraps MongoDB Collection methods to add delays before
* database operations execute.
*
* Usage:
* const { wrapMongoDBWithLatency } = require('./MongoLatencyWrapper');
*
* // Before initializing Parse Server
* const unwrap = wrapMongoDBWithLatency(10); // 10ms delay
*
* // ... run benchmarks ...
*
* // Cleanup when done
* unwrap();
*/

const { Collection } = require('mongodb');

// Store original methods for restoration
const originalMethods = new Map();

/**
* Wrap a Collection method to add artificial latency
* @param {string} methodName - Name of the method to wrap
* @param {number} latencyMs - Delay in milliseconds
*/
function wrapMethod(methodName, latencyMs) {
if (!originalMethods.has(methodName)) {
originalMethods.set(methodName, Collection.prototype[methodName]);
}

const originalMethod = originalMethods.get(methodName);

Collection.prototype[methodName] = function (...args) {
// For methods that return cursors (like find, aggregate), we need to delay the execution
// but still return a cursor-like object
const result = originalMethod.apply(this, args);

// Check if result has cursor methods (toArray, forEach, etc.)
if (result && typeof result.toArray === 'function') {
// Wrap cursor methods that actually execute the query
const originalToArray = result.toArray.bind(result);
result.toArray = function() {
// Wait for the original promise to settle, then delay the result
return originalToArray().then(
value => new Promise(resolve => setTimeout(() => resolve(value), latencyMs)),
error => new Promise((_, reject) => setTimeout(() => reject(error), latencyMs))
);
};
return result;
}

// For promise-returning methods, wrap the promise with delay
if (result && typeof result.then === 'function') {
// Wait for the original promise to settle, then delay the result
return result.then(
value => new Promise(resolve => setTimeout(() => resolve(value), latencyMs)),
error => new Promise((_, reject) => setTimeout(() => reject(error), latencyMs))
);
}

// For synchronous methods, just add delay
return new Promise((resolve) => {
setTimeout(() => {
resolve(result);
}, latencyMs);
});
};
}

/**
* Wrap MongoDB Collection methods with artificial latency
* @param {number} latencyMs - Delay in milliseconds to inject before each operation
* @returns {Function} unwrap - Function to restore original methods
*/
function wrapMongoDBWithLatency(latencyMs) {
if (typeof latencyMs !== 'number' || latencyMs < 0) {
throw new Error('latencyMs must be a non-negative number');
}

if (latencyMs === 0) {
// eslint-disable-next-line no-console
console.log('Latency is 0ms, skipping MongoDB wrapping');
return () => {}; // No-op unwrap function
}

// eslint-disable-next-line no-console
console.log(`Wrapping MongoDB operations with ${latencyMs}ms artificial latency`);

// List of MongoDB Collection methods to wrap
const methodsToWrap = [
'find',
'findOne',
'countDocuments',
'estimatedDocumentCount',
'distinct',
'aggregate',
'insertOne',
'insertMany',
'updateOne',
'updateMany',
'replaceOne',
'deleteOne',
'deleteMany',
'findOneAndUpdate',
'findOneAndReplace',
'findOneAndDelete',
'createIndex',
'createIndexes',
'dropIndex',
'dropIndexes',
'drop',
];

methodsToWrap.forEach(methodName => {
wrapMethod(methodName, latencyMs);
});

// Return unwrap function to restore original methods
return function unwrap() {
// eslint-disable-next-line no-console
console.log('Removing MongoDB latency wrapper, restoring original methods');

originalMethods.forEach((originalMethod, methodName) => {
Collection.prototype[methodName] = originalMethod;
});

originalMethods.clear();
};
}

module.exports = {
wrapMongoDBWithLatency,
};
Loading
Loading