Skip to content

Commit ff7e83c

Browse files
authored
Merge pull request #666 from ColdBox/claude/coldbox-perf-analysis-ractd4
feat: add ColdBox performance analysis suite (BE vs stable 8.1)
2 parents e173c11 + e018c65 commit ff7e83c

28 files changed

Lines changed: 1513 additions & 1 deletion

box.json

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,12 @@
7575
"log:lucee":"server log coldbox-lucee@5 --follow",
7676
"log:boxlang":"server log coldbox-boxlang-cfml@1 --follow",
7777
"log:boxlangprime":"server log coldbox-boxlang@1 --follow",
78-
"log:adobe":"server log coldbox-adobe@2025 --follow"
78+
"log:adobe":"server log coldbox-adobe@2025 --follow",
79+
"perf:run":"task run tests/perf-harness/PerformanceSuite.cfc",
80+
"perf:run:quick":"task run tests/perf-harness/PerformanceSuite.cfc engines=boxlang-cfml iterations=10 warmup=3 coldStart=false",
81+
"perf:be":"task run tests/perf-harness/PerformanceSuite.cfc versions=be",
82+
"perf:stable":"task run tests/perf-harness/PerformanceSuite.cfc versions=stable",
83+
"perf:lucee":"task run tests/perf-harness/PerformanceSuite.cfc engines=lucee-7",
84+
"perf:adobe":"task run tests/perf-harness/PerformanceSuite.cfc engines=adobe-2025"
7985
}
8086
}

tests/perf-harness/PerformanceSuite.cfc

Lines changed: 847 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
/**
2+
* Shared ColdBox configuration for performance harness.
3+
* appName and appKey are overridden per-version in be-app/config and stable-app/config.
4+
*/
5+
component {
6+
7+
function configure(){
8+
variables.coldbox = {
9+
appName : "ColdBoxPerfHarness",
10+
eventName : "event",
11+
reinitPassword : "",
12+
reinitKey : "fwreinit",
13+
handlersIndexAutoReload : false,
14+
debugMode : false,
15+
defaultEvent : "Main.index",
16+
requestStartHandler : "",
17+
requestEndHandler : "",
18+
applicationStartHandler : "",
19+
applicationEndHandler : "",
20+
sessionStartHandler : "",
21+
sessionEndHandler : "",
22+
missingTemplateHandler : "",
23+
applicationHelper : "",
24+
viewsHelper : "",
25+
modulesExternalLocation : [],
26+
viewsExternalLocation : "",
27+
layoutsExternalLocation : "",
28+
handlersExternalLocation: "",
29+
requestContextDecorator : "",
30+
exceptionHandler : "",
31+
invalidEventHandler : "",
32+
customErrorTemplate : "",
33+
handlerCaching : true,
34+
eventCaching : false,
35+
proxyReturnCollection : false
36+
};
37+
38+
variables.layoutSettings = {
39+
defaultLayout : "Main.cfm",
40+
defaultView : ""
41+
};
42+
43+
variables.modules = {
44+
autoReload : false,
45+
include : [ "perf-module" ],
46+
exclude : []
47+
};
48+
49+
variables.interceptors = [
50+
{ class : "#appMapping#.interceptors.PerfInterceptor" }
51+
];
52+
53+
variables.logBox = {
54+
appenders : {
55+
console : { class : "ConsoleAppender" }
56+
},
57+
root : { levelmax : "WARN", appenders : "*" }
58+
};
59+
}
60+
61+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
/**
2+
* Performance harness router — five measurable test scenarios.
3+
*/
4+
component {
5+
6+
/**
7+
* ColdBox hook: called by RoutingService instead of reading CGI.PATH_INFO.
8+
* When the Tuckey URL rewriter does a forward(), the original request URI
9+
* is in the javax.servlet.forward.request_uri attribute; CGI.PATH_INFO is empty.
10+
* This provider extracts the original path and strips the app sub-path prefix.
11+
*/
12+
function pathInfoProvider( event ){
13+
var forwardURI = getPageContext().getRequest().getAttribute( "javax.servlet.forward.request_uri" )
14+
if ( !isNull( forwardURI ) && len( forwardURI ) ) {
15+
return reReplaceNoCase( forwardURI, "^/tests/perf-harness/(be-app|stable-app)", "" )
16+
}
17+
return CGI.PATH_INFO
18+
}
19+
20+
function configure(){
21+
// Health check — minimal response, no DI, no view
22+
route( "/perf/health" ).to( "Main.health" )
23+
24+
// Simple view — renders view + layout
25+
route( "/perf/view" ).to( "Main.index" )
26+
27+
// JSON API — DI injection + renderData
28+
route( "/perf/api" ).to( "Api.list" )
29+
30+
// Complex view — multiple model injections + data loop
31+
route( "/perf/complex" ).to( "Main.complex" )
32+
33+
// Module request — full HMVC module routing
34+
route( "/perf/module" ).to( "perf-module:Items.index" )
35+
36+
// Default convention routing
37+
route( "/:handler/:action?" ).end()
38+
}
39+
40+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
/**
2+
* REST API handler — tests DI + JSON serialization pipeline.
3+
*/
4+
component extends="coldbox.system.EventHandler" {
5+
6+
property name="userService" inject="UserService";
7+
8+
// JSON list — injects UserService and renders JSON
9+
function list( event, rc, prc ){
10+
var data = {
11+
status : "success",
12+
count : 10,
13+
engine : server.keyExists( "coldfusion" ) ? "Adobe CF" : ( server.keyExists( "lucee" ) ? "Lucee" : "BoxLang" ),
14+
users : userService.getUsers( 10 ),
15+
metadata : {
16+
generated : now(),
17+
framework : "ColdBox"
18+
}
19+
}
20+
event.renderData( type="json", data=data )
21+
}
22+
23+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
/**
2+
* Main handler for performance test scenarios.
3+
*/
4+
component extends="coldbox.system.EventHandler" {
5+
6+
property name="userService" inject="UserService";
7+
property name="productService" inject="ProductService";
8+
9+
// Baseline — no DI usage, no view, minimal processing
10+
function health( event, rc, prc ){
11+
event.renderData( type="text", data="ok", statusCode=200 )
12+
}
13+
14+
// Simple view — renders main/index with layout
15+
function index( event, rc, prc ){
16+
prc.message = "ColdBox Performance Harness"
17+
prc.timestamp = now()
18+
prc.version = getColdBoxSetting( "version", "unknown" )
19+
event.setView( "main/index" )
20+
}
21+
22+
// Complex view — resolves two model dependencies, loops data
23+
function complex( event, rc, prc ){
24+
prc.users = userService.getUsers( 10 )
25+
prc.products = productService.getProducts( 5 )
26+
event.setView( "main/complex" )
27+
}
28+
29+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
/**
2+
* Records per-request timing into the PRC scope for debugging.
3+
*/
4+
component {
5+
6+
void function configure(){
7+
}
8+
9+
void function preProcess( event, data, rc, prc ){
10+
arguments.prc._perfStart = getTickCount()
11+
}
12+
13+
void function postProcess( event, data, rc, prc ){
14+
if( arguments.prc.keyExists( "_perfStart" ) ){
15+
arguments.prc._perfElapsed = getTickCount() - arguments.prc._perfStart
16+
}
17+
}
18+
19+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8">
5+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
6+
<title>ColdBox Perf Harness</title>
7+
<style>
8+
body { font-family: system-ui, sans-serif; margin: 2rem; color: #333; }
9+
h2 { color: #0066cc; }
10+
ul { line-height: 1.8; }
11+
</style>
12+
</head>
13+
<body>
14+
<cfoutput>
15+
<header>
16+
<h1>ColdBox Performance Harness</h1>
17+
</header>
18+
<main>
19+
#renderView()#
20+
</main>
21+
</cfoutput>
22+
</body>
23+
</html>
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
/**
2+
* Product service singleton — provides test product data.
3+
*/
4+
component singleton {
5+
6+
variables.CATEGORIES = [ "Electronics", "Clothing", "Books", "Food", "Tools" ]
7+
8+
function getProducts( numeric count=5 ){
9+
var result = []
10+
for( var i = 1; i <= arguments.count; i++ ){
11+
result.append({
12+
id : i,
13+
name : "Product #i#",
14+
sku : "SKU-#numberFormat( i, "00000" )#",
15+
price : precisionEvaluate( i * 9.99 ),
16+
category : variables.CATEGORIES[ ( ( i - 1 ) mod variables.CATEGORIES.len() ) + 1 ],
17+
inStock : ( i mod 4 != 0 ),
18+
tags : [ "tag#i#", "perf", "test" ]
19+
})
20+
}
21+
return result
22+
}
23+
24+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
/**
2+
* User service singleton — provides test user data.
3+
*/
4+
component singleton {
5+
6+
function getUsers( numeric count=10 ){
7+
var result = []
8+
for( var i = 1; i <= arguments.count; i++ ){
9+
result.append({
10+
id : i,
11+
firstName : "User",
12+
lastName : "Number#i#",
13+
email : "user#i#@perf.test",
14+
role : ( i mod 3 == 0 ) ? "admin" : "user",
15+
active : true,
16+
createdAt : now()
17+
})
18+
}
19+
return result
20+
}
21+
22+
function getUserById( required numeric id ){
23+
return {
24+
id : arguments.id,
25+
firstName : "User",
26+
lastName : "Number#arguments.id#",
27+
email : "user#arguments.id#@perf.test",
28+
role : "user",
29+
active : true,
30+
createdAt : now()
31+
}
32+
}
33+
34+
}

0 commit comments

Comments
 (0)