Skip to content

Commit de77d6b

Browse files
authored
Merge branch 'alpha' into append-metadata
2 parents 8b92d9b + 9ed9af4 commit de77d6b

File tree

20 files changed

+1078
-191
lines changed

20 files changed

+1078
-191
lines changed

.github/workflows/ci-performance.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ jobs:
7070
env:
7171
NODE_ENV: production
7272
run: |
73-
echo "Running baseline benchmarks with CPU affinity (using PR's benchmark script)..."
73+
echo "Running baseline benchmarks..."
7474
if [ ! -f "benchmark/performance.js" ]; then
7575
echo "⚠️ Benchmark script not found - this is expected for new features"
7676
echo "Skipping baseline benchmark"
@@ -135,7 +135,7 @@ jobs:
135135
env:
136136
NODE_ENV: production
137137
run: |
138-
echo "Running PR benchmarks with CPU affinity..."
138+
echo "Running PR benchmarks..."
139139
taskset -c 0 npm run benchmark > pr-output.txt 2>&1 || npm run benchmark > pr-output.txt 2>&1 || true
140140
echo "Benchmark command completed with exit code: $?"
141141
echo "Output file size: $(wc -c < pr-output.txt) bytes"

benchmark/MongoLatencyWrapper.js

Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
/**
2+
* MongoDB Latency Wrapper
3+
*
4+
* Utility to inject artificial latency into MongoDB operations for performance testing.
5+
* This wrapper temporarily wraps MongoDB Collection methods to add delays before
6+
* database operations execute.
7+
*
8+
* Usage:
9+
* const { wrapMongoDBWithLatency } = require('./MongoLatencyWrapper');
10+
*
11+
* // Before initializing Parse Server
12+
* const unwrap = wrapMongoDBWithLatency(10); // 10ms delay
13+
*
14+
* // ... run benchmarks ...
15+
*
16+
* // Cleanup when done
17+
* unwrap();
18+
*/
19+
20+
const { Collection } = require('mongodb');
21+
22+
// Store original methods for restoration
23+
const originalMethods = new Map();
24+
25+
/**
26+
* Wrap a Collection method to add artificial latency
27+
* @param {string} methodName - Name of the method to wrap
28+
* @param {number} latencyMs - Delay in milliseconds
29+
*/
30+
function wrapMethod(methodName, latencyMs) {
31+
if (!originalMethods.has(methodName)) {
32+
originalMethods.set(methodName, Collection.prototype[methodName]);
33+
}
34+
35+
const originalMethod = originalMethods.get(methodName);
36+
37+
Collection.prototype[methodName] = function (...args) {
38+
// For methods that return cursors (like find, aggregate), we need to delay the execution
39+
// but still return a cursor-like object
40+
const result = originalMethod.apply(this, args);
41+
42+
// Check if result has cursor methods (toArray, forEach, etc.)
43+
if (result && typeof result.toArray === 'function') {
44+
// Wrap cursor methods that actually execute the query
45+
const originalToArray = result.toArray.bind(result);
46+
result.toArray = function() {
47+
// Wait for the original promise to settle, then delay the result
48+
return originalToArray().then(
49+
value => new Promise(resolve => setTimeout(() => resolve(value), latencyMs)),
50+
error => new Promise((_, reject) => setTimeout(() => reject(error), latencyMs))
51+
);
52+
};
53+
return result;
54+
}
55+
56+
// For promise-returning methods, wrap the promise with delay
57+
if (result && typeof result.then === 'function') {
58+
// Wait for the original promise to settle, then delay the result
59+
return result.then(
60+
value => new Promise(resolve => setTimeout(() => resolve(value), latencyMs)),
61+
error => new Promise((_, reject) => setTimeout(() => reject(error), latencyMs))
62+
);
63+
}
64+
65+
// For synchronous methods, just add delay
66+
return new Promise((resolve) => {
67+
setTimeout(() => {
68+
resolve(result);
69+
}, latencyMs);
70+
});
71+
};
72+
}
73+
74+
/**
75+
* Wrap MongoDB Collection methods with artificial latency
76+
* @param {number} latencyMs - Delay in milliseconds to inject before each operation
77+
* @returns {Function} unwrap - Function to restore original methods
78+
*/
79+
function wrapMongoDBWithLatency(latencyMs) {
80+
if (typeof latencyMs !== 'number' || latencyMs < 0) {
81+
throw new Error('latencyMs must be a non-negative number');
82+
}
83+
84+
if (latencyMs === 0) {
85+
// eslint-disable-next-line no-console
86+
console.log('Latency is 0ms, skipping MongoDB wrapping');
87+
return () => {}; // No-op unwrap function
88+
}
89+
90+
// eslint-disable-next-line no-console
91+
console.log(`Wrapping MongoDB operations with ${latencyMs}ms artificial latency`);
92+
93+
// List of MongoDB Collection methods to wrap
94+
const methodsToWrap = [
95+
'find',
96+
'findOne',
97+
'countDocuments',
98+
'estimatedDocumentCount',
99+
'distinct',
100+
'aggregate',
101+
'insertOne',
102+
'insertMany',
103+
'updateOne',
104+
'updateMany',
105+
'replaceOne',
106+
'deleteOne',
107+
'deleteMany',
108+
'findOneAndUpdate',
109+
'findOneAndReplace',
110+
'findOneAndDelete',
111+
'createIndex',
112+
'createIndexes',
113+
'dropIndex',
114+
'dropIndexes',
115+
'drop',
116+
];
117+
118+
methodsToWrap.forEach(methodName => {
119+
wrapMethod(methodName, latencyMs);
120+
});
121+
122+
// Return unwrap function to restore original methods
123+
return function unwrap() {
124+
// eslint-disable-next-line no-console
125+
console.log('Removing MongoDB latency wrapper, restoring original methods');
126+
127+
originalMethods.forEach((originalMethod, methodName) => {
128+
Collection.prototype[methodName] = originalMethod;
129+
});
130+
131+
originalMethods.clear();
132+
};
133+
}
134+
135+
module.exports = {
136+
wrapMongoDBWithLatency,
137+
};

0 commit comments

Comments
 (0)