Skip to content

Commit 96e8f1f

Browse files
committed
logging
1 parent 35f1809 commit 96e8f1f

File tree

1 file changed

+145
-100
lines changed

1 file changed

+145
-100
lines changed

benchmark/performance.js

Lines changed: 145 additions & 100 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111
/* eslint-disable no-console */
1212

13+
const core = require('@actions/core');
1314
const Parse = require('parse/node');
1415
const { performance, PerformanceObserver } = require('perf_hooks');
1516
const { MongoClient } = require('mongodb');
@@ -20,11 +21,16 @@ const SERVER_URL = 'http://localhost:1337/parse';
2021
const APP_ID = 'benchmark-app-id';
2122
const MASTER_KEY = 'benchmark-master-key';
2223
const ITERATIONS = parseInt(process.env.BENCHMARK_ITERATIONS || '1000', 10);
24+
const LOG_ITERATIONS = false;
2325

2426
// Parse Server instance
2527
let parseServer;
2628
let mongoClient;
2729

30+
// Logging helpers
31+
const logInfo = message => core.info(message);
32+
const logError = message => core.error(message);
33+
2834
/**
2935
* Initialize Parse Server for benchmarking
3036
*/
@@ -94,33 +100,49 @@ function resetParseServer() {
94100
}
95101

96102
/**
97-
* Measure average time for an async operation over multiple iterations
98-
* Uses warmup iterations, median metric, and outlier filtering for robustness
103+
* Measure average time for an async operation over multiple iterations.
104+
* @param {Object} options - Measurement options
105+
* @param {string} options.name - Name of the operation being measured
106+
* @param {Function} options.operation - Async function to measure
107+
* @param {number} [options.iterations=ITERATIONS] - Number of iterations to run
108+
* @param {boolean} [options.skipWarmup=false] - Skip warmup phase
99109
*/
100-
async function measureOperation(name, operation, iterations = ITERATIONS, skipWarmup = false) {
101-
const warmupCount = skipWarmup ? 0 : Math.floor(iterations * 0.2); // 20% warmup iterations
110+
async function measureOperation({ name, operation, iterations = ITERATIONS, skipWarmup = false }) {
111+
const warmupCount = skipWarmup ? 0 : Math.floor(iterations * 0.2);
102112
const times = [];
103113

104114
if (warmupCount > 0) {
105-
console.log(`Starting warmup phase (${warmupCount} iterations)...`);
115+
logInfo(`Starting warmup phase of ${warmupCount} iterations...`);
106116
const warmupStart = performance.now();
107117
for (let i = 0; i < warmupCount; i++) {
108118
await operation();
109119
}
110-
console.log(`Warmup took: ${(performance.now() - warmupStart).toFixed(2)}ms`);
120+
logInfo(`Warmup took: ${(performance.now() - warmupStart).toFixed(2)}ms`);
111121
}
112122

113123
// Measurement phase
114-
console.log(`Starting measurement phase (${iterations} iterations)...`);
124+
logInfo(`Starting measurement phase of ${iterations} iterations...`);
125+
const progressInterval = Math.ceil(iterations / 10); // Log every 10%
126+
const measurementStart = performance.now();
127+
115128
for (let i = 0; i < iterations; i++) {
116129
const start = performance.now();
117130
await operation();
118131
const end = performance.now();
119132
const duration = end - start;
120133
times.push(duration);
121-
console.log(`Iteration ${i + 1}: ${duration.toFixed(2)}ms`);
134+
135+
// Log progress every 10% or individual iterations if LOG_ITERATIONS is enabled
136+
if (LOG_ITERATIONS) {
137+
logInfo(`Iteration ${i + 1}: ${duration.toFixed(2)}ms`);
138+
} else if ((i + 1) % progressInterval === 0 || i + 1 === iterations) {
139+
const progress = Math.round(((i + 1) / iterations) * 100);
140+
logInfo(`Progress: ${progress}%`);
141+
}
122142
}
123143

144+
logInfo(`Measurement took: ${(performance.now() - measurementStart).toFixed(2)}ms`);
145+
124146
// Sort times for percentile calculations
125147
times.sort((a, b) => a - b);
126148

@@ -157,13 +179,16 @@ async function measureOperation(name, operation, iterations = ITERATIONS, skipWa
157179
async function benchmarkObjectCreate() {
158180
let counter = 0;
159181

160-
return measureOperation('Object Create', async () => {
161-
const TestObject = Parse.Object.extend('BenchmarkTest');
162-
const obj = new TestObject();
163-
obj.set('testField', `test-value-${counter++}`);
164-
obj.set('number', counter);
165-
obj.set('boolean', true);
166-
await obj.save();
182+
return measureOperation({
183+
name: 'Object Create',
184+
operation: async () => {
185+
const TestObject = Parse.Object.extend('BenchmarkTest');
186+
const obj = new TestObject();
187+
obj.set('testField', `test-value-${counter++}`);
188+
obj.set('number', counter);
189+
obj.set('boolean', true);
190+
await obj.save();
191+
},
167192
});
168193
}
169194

@@ -185,9 +210,12 @@ async function benchmarkObjectRead() {
185210

186211
let counter = 0;
187212

188-
return measureOperation('Object Read', async () => {
189-
const query = new Parse.Query('BenchmarkTest');
190-
await query.get(objects[counter++ % objects.length].id);
213+
return measureOperation({
214+
name: 'Object Read',
215+
operation: async () => {
216+
const query = new Parse.Query('BenchmarkTest');
217+
await query.get(objects[counter++ % objects.length].id);
218+
},
191219
});
192220
}
193221

@@ -210,11 +238,14 @@ async function benchmarkObjectUpdate() {
210238

211239
let counter = 0;
212240

213-
return measureOperation('Object Update', async () => {
214-
const obj = objects[counter++ % objects.length];
215-
obj.increment('counter');
216-
obj.set('lastUpdated', new Date());
217-
await obj.save();
241+
return measureOperation({
242+
name: 'Object Update',
243+
operation: async () => {
244+
const obj = objects[counter++ % objects.length];
245+
obj.increment('counter');
246+
obj.set('lastUpdated', new Date());
247+
await obj.save();
248+
},
218249
});
219250
}
220251

@@ -237,10 +268,13 @@ async function benchmarkSimpleQuery() {
237268

238269
let counter = 0;
239270

240-
return measureOperation('Simple Query', async () => {
241-
const query = new Parse.Query('BenchmarkTest');
242-
query.equalTo('category', counter++ % 10);
243-
await query.find();
271+
return measureOperation({
272+
name: 'Simple Query',
273+
operation: async () => {
274+
const query = new Parse.Query('BenchmarkTest');
275+
query.equalTo('category', counter++ % 10);
276+
await query.find();
277+
},
244278
});
245279
}
246280

@@ -250,18 +284,21 @@ async function benchmarkSimpleQuery() {
250284
async function benchmarkBatchSave() {
251285
const BATCH_SIZE = 10;
252286

253-
return measureOperation('Batch Save (10 objects)', async () => {
254-
const TestObject = Parse.Object.extend('BenchmarkTest');
255-
const objects = [];
256-
257-
for (let i = 0; i < BATCH_SIZE; i++) {
258-
const obj = new TestObject();
259-
obj.set('batchField', `batch-${i}`);
260-
obj.set('timestamp', new Date());
261-
objects.push(obj);
262-
}
287+
return measureOperation({
288+
name: 'Batch Save (10 objects)',
289+
operation: async () => {
290+
const TestObject = Parse.Object.extend('BenchmarkTest');
291+
const objects = [];
292+
293+
for (let i = 0; i < BATCH_SIZE; i++) {
294+
const obj = new TestObject();
295+
obj.set('batchField', `batch-${i}`);
296+
obj.set('timestamp', new Date());
297+
objects.push(obj);
298+
}
263299

264-
await Parse.Object.saveAll(objects);
300+
await Parse.Object.saveAll(objects);
301+
},
265302
});
266303
}
267304

@@ -271,13 +308,16 @@ async function benchmarkBatchSave() {
271308
async function benchmarkUserSignup() {
272309
let counter = 0;
273310

274-
return measureOperation('User Signup', async () => {
275-
counter++;
276-
const user = new Parse.User();
277-
user.set('username', `benchmark_user_${Date.now()}_${counter}`);
278-
user.set('password', 'benchmark_password');
279-
user.set('email', `benchmark${counter}@example.com`);
280-
await user.signUp();
311+
return measureOperation({
312+
name: 'User Signup',
313+
operation: async () => {
314+
counter++;
315+
const user = new Parse.User();
316+
user.set('username', `benchmark_user_${Date.now()}_${counter}`);
317+
user.set('password', 'benchmark_password');
318+
user.set('email', `benchmark${counter}@example.com`);
319+
await user.signUp();
320+
},
281321
});
282322
}
283323

@@ -300,10 +340,13 @@ async function benchmarkUserLogin() {
300340

301341
let counter = 0;
302342

303-
return measureOperation('User Login', async () => {
304-
const userCreds = users[counter++ % users.length];
305-
await Parse.User.logIn(userCreds.username, userCreds.password);
306-
await Parse.User.logOut();
343+
return measureOperation({
344+
name: 'User Login',
345+
operation: async () => {
346+
const userCreds = users[counter++ % users.length];
347+
await Parse.User.logIn(userCreds.username, userCreds.password);
348+
await Parse.User.logOut();
349+
},
307350
});
308351
}
309352

@@ -317,57 +360,59 @@ async function benchmarkQueryWithInclude() {
317360
const Level1Class = Parse.Object.extend('Level1');
318361
const RootClass = Parse.Object.extend('Root');
319362

320-
// Create 10 Level2 objects
321-
const level2Objects = [];
322-
for (let i = 0; i < 10; i++) {
323-
const obj = new Level2Class();
324-
obj.set('name', `level2-${i}`);
325-
obj.set('value', i);
326-
level2Objects.push(obj);
327-
}
328-
await Parse.Object.saveAll(level2Objects);
329-
330-
// Create 10 Level1 objects, each pointing to a Level2 object
331-
const level1Objects = [];
332-
for (let i = 0; i < 10; i++) {
333-
const obj = new Level1Class();
334-
obj.set('name', `level1-${i}`);
335-
obj.set('level2', level2Objects[i % level2Objects.length]);
336-
level1Objects.push(obj);
337-
}
338-
await Parse.Object.saveAll(level1Objects);
339-
340-
// Create 10 Root objects, each pointing to a Level1 object
341-
const rootObjects = [];
342-
for (let i = 0; i < 10; i++) {
343-
const obj = new RootClass();
344-
obj.set('name', `root-${i}`);
345-
obj.set('level1', level1Objects[i % level1Objects.length]);
346-
rootObjects.push(obj);
347-
}
348-
await Parse.Object.saveAll(rootObjects);
363+
return measureOperation({
364+
name: 'Query with Include (2 levels)',
365+
skipWarmup: true,
366+
operation: async () => {
367+
// Create 10 Level2 objects
368+
const level2Objects = [];
369+
for (let i = 0; i < 10; i++) {
370+
const obj = new Level2Class();
371+
obj.set('name', `level2-${i}`);
372+
obj.set('value', i);
373+
level2Objects.push(obj);
374+
}
375+
await Parse.Object.saveAll(level2Objects);
376+
377+
// Create 10 Level1 objects, each pointing to a Level2 object
378+
const level1Objects = [];
379+
for (let i = 0; i < 10; i++) {
380+
const obj = new Level1Class();
381+
obj.set('name', `level1-${i}`);
382+
obj.set('level2', level2Objects[i % level2Objects.length]);
383+
level1Objects.push(obj);
384+
}
385+
await Parse.Object.saveAll(level1Objects);
386+
387+
// Create 10 Root objects, each pointing to a Level1 object
388+
const rootObjects = [];
389+
for (let i = 0; i < 10; i++) {
390+
const obj = new RootClass();
391+
obj.set('name', `root-${i}`);
392+
obj.set('level1', level1Objects[i % level1Objects.length]);
393+
rootObjects.push(obj);
394+
}
395+
await Parse.Object.saveAll(rootObjects);
349396

350-
const result = await measureOperation('Query with Include (2 levels)', async () => {
351-
const query = new Parse.Query('Root');
352-
query.include('level1.level2');
353-
await query.find();
397+
const query = new Parse.Query('Root');
398+
query.include('level1.level2');
399+
await query.find();
400+
},
354401
});
355-
356-
return result;
357402
}
358403

359404
/**
360405
* Run all benchmarks
361406
*/
362407
async function runBenchmarks() {
363-
console.log('Starting Parse Server Performance Benchmarks...');
364-
console.log(`Iterations per benchmark: ${ITERATIONS}`);
408+
logInfo('Starting Parse Server Performance Benchmarks...');
409+
logInfo(`Iterations per benchmark: ${ITERATIONS}`);
365410

366411
let server;
367412

368413
try {
369414
// Initialize Parse Server
370-
console.log('Initializing Parse Server...');
415+
logInfo('Initializing Parse Server...');
371416
server = await initializeParseServer();
372417

373418
// Wait for server to be ready
@@ -377,36 +422,36 @@ async function runBenchmarks() {
377422

378423
// Define all benchmarks to run
379424
const benchmarks = [
380-
{ name: 'Object Create', fn: benchmarkObjectCreate },
381-
{ name: 'Object Read', fn: benchmarkObjectRead },
382-
{ name: 'Object Update', fn: benchmarkObjectUpdate },
383-
{ name: 'Simple Query', fn: benchmarkSimpleQuery },
384-
{ name: 'Batch Save', fn: benchmarkBatchSave },
385-
{ name: 'User Signup', fn: benchmarkUserSignup },
386-
{ name: 'User Login', fn: benchmarkUserLogin },
425+
// { name: 'Object Create', fn: benchmarkObjectCreate },
426+
// { name: 'Object Read', fn: benchmarkObjectRead },
427+
// { name: 'Object Update', fn: benchmarkObjectUpdate },
428+
// { name: 'Simple Query', fn: benchmarkSimpleQuery },
429+
// { name: 'Batch Save', fn: benchmarkBatchSave },
430+
// { name: 'User Signup', fn: benchmarkUserSignup },
431+
// { name: 'User Login', fn: benchmarkUserLogin },
387432
{ name: 'Query with Include', fn: benchmarkQueryWithInclude },
388433
];
389434

390435
// Run each benchmark with database cleanup
391436
for (const benchmark of benchmarks) {
392-
console.log(`Running ${benchmark.name} benchmark...`);
437+
logInfo(`\nRunning benchmark '${benchmark.name}'...`);
393438
resetParseServer();
394439
await cleanupDatabase();
395440
results.push(await benchmark.fn());
396441
}
397442

398443
// Output results in github-action-benchmark format (stdout)
399-
console.log(JSON.stringify(results, null, 2));
444+
logInfo(JSON.stringify(results, null, 2));
400445

401446
// Output summary to stderr for visibility
402-
console.log('Benchmarks completed successfully!');
403-
console.log('Summary:');
447+
logInfo('Benchmarks completed successfully!');
448+
logInfo('Summary:');
404449
results.forEach(result => {
405-
console.log(` ${result.name}: ${result.value.toFixed(2)} ${result.unit} (${result.extra})`);
450+
logInfo(` ${result.name}: ${result.value.toFixed(2)} ${result.unit} (${result.extra})`);
406451
});
407452

408453
} catch (error) {
409-
console.error('Error running benchmarks:', error);
454+
logError('Error running benchmarks:', error);
410455
process.exit(1);
411456
} finally {
412457
// Cleanup

0 commit comments

Comments
 (0)