1010
1111/* eslint-disable no-console */
1212
13+ const core = require ( '@actions/core' ) ;
1314const Parse = require ( 'parse/node' ) ;
1415const { performance, PerformanceObserver } = require ( 'perf_hooks' ) ;
1516const { MongoClient } = require ( 'mongodb' ) ;
@@ -20,11 +21,16 @@ const SERVER_URL = 'http://localhost:1337/parse';
2021const APP_ID = 'benchmark-app-id' ;
2122const MASTER_KEY = 'benchmark-master-key' ;
2223const ITERATIONS = parseInt ( process . env . BENCHMARK_ITERATIONS || '1000' , 10 ) ;
24+ const LOG_ITERATIONS = false ;
2325
2426// Parse Server instance
2527let parseServer ;
2628let 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
157179async 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() {
250284async 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() {
271308async 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 */
362407async 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