Last active
February 19, 2020 13:21
-
-
Save bennadel/5a9fa02eaca51b16f5b5969427e7a469 to your computer and use it in GitHub Desktop.
Experiment: Wrapping CFThread Execution In A FusionReactor Tracked Transaction In Lucee CFML 5.2.9.40
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<cfscript> | |
segment = createObject( "java", "com.intergral.fusionreactor.api.FRAPI" ) | |
.getInstance() | |
.createTrackedTransaction( "MyCodeSegment" ) | |
; | |
try { | |
// ... the code you are instrumenting ... // | |
} finally { | |
segment.close(); | |
} | |
</cfscript> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<cfscript> | |
// Instead of just going directly to a CFThread for asynchronous processing, I'm | |
// going to see if I can wrap the CFThread interaction inside a FusionReactor | |
// "tracked transaction" such that I can see how this CFThread is operating and | |
// how it correlates with system resource utilization. | |
// -- | |
// This takes the following arguments: | |
// 1. Name of the FusionReactor transaction. | |
// 2. Arguments to be passed to closure. | |
// 3. Closure to execute asynchronously. | |
// 4. [OPTIONAL] Closure to handle errors. | |
runSegmentAsync( | |
"SendFriendMessagesAsync", | |
{ | |
friends: [ "Sarah", "Max", "Annie", "Timmy" ] | |
}, | |
( friends ) => { | |
var messages = friends.map( | |
( friend ) => { | |
return( formatMessage( friend ) ); | |
} | |
); | |
// Simulating some latency to make the graphs more interesting. | |
sleep( randRange( 10, 50 ) ); | |
// Simulate "sending" messages. | |
systemOutput( serializeJson( messages ), true, true ); | |
} | |
); | |
/** | |
* I format a message for the given friend. | |
* | |
* @name I am the friend's name. | |
*/ | |
public string function formatMessage( required string name ) { | |
return( "Hello, #name#, how goes it?" ); | |
} | |
// ------------------------------------------------------------------------------- // | |
// ------------------------------------------------------------------------------- // | |
/** | |
* I run the given callback asynchronously inside a CFThread and wrapped in a | |
* FusionReactor tracked transaction with the given name. | |
* | |
* @segmentName I am the name of the FusionReactor tracked transaction. | |
* @callbackArguments I am the argumentCollection passed to the thread and callback. | |
* @callback I am the callback to invoke asynchronously. | |
* @errorCallback [OPTIONAL] I am the error handler callback. | |
*/ | |
public void function runSegmentAsync( | |
required string segmentName, | |
required any callbackArguments, | |
required function callback, | |
function errorCallback | |
) { | |
// Since the execution of a CFThread can be a bit of a mystery (from an | |
// debugging standpoint), let's include the StackTrace in the FusionReactor | |
// transaction description. | |
// -- | |
// NOTE: In a production context, I would probably just rely on "SegmentName" to | |
// aid in debugging since I would be afraid that the callstackGet() call would | |
// end-up being a performance hit? Possible just unnecessary worry there, though. | |
var callstackItems = callstackGet().map( | |
( item, i ) => { | |
// The first item in the callstack will always be "THIS" line, which | |
// doesn't add any insight into the processing. As such, let's replace | |
// the first callstack item with a descriptive value. | |
if ( i == 1 ) { | |
return( "Async segment tracking for CFThread." ); | |
} | |
return( item.template & ":" & item.lineNumber ); | |
} | |
); | |
// Inside a CFThread, FusionReactor seems to have trouble figuring out which | |
// "app" the spawned thread is supposed to be associated with. As such, let's get | |
// the app name from the current transaction and pass it through to the thread | |
// where it can be programmatically propagated in the tracked transaction. | |
// -- | |
// CAUTION: In a production setting, you'd have to check to make sure this class | |
// is available AND that the .getInstance() call returns a non-null value. | |
// However, for the sake of simplicity, I'm keeping this demo naive. | |
var transactionAppName = createObject( "java", "com.intergral.fusionreactor.api.FRAPI" ) | |
.getInstance() | |
.getActiveTransaction() | |
.getApplicationName() | |
; | |
thread | |
name = "runSegmentAsync-#createUniqueId()#" | |
action = "run" | |
segmentAppName = transactionAppName | |
segmentName = segmentName | |
segmentDescription = callstackItems.toList( chr( 10 ) ) | |
callback = callback | |
callbackArguments = callbackArguments | |
errorCallback = ( errorCallback ?: nullValue() ) | |
{ | |
var segment = createObject( "java", "com.intergral.fusionreactor.api.FRAPI" ) | |
.getInstance() | |
.createTrackedTransaction( segmentName ) | |
; | |
try { | |
// Propagate application name and execution context. | |
segment.setApplicationName( segmentAppName ); | |
segment.setDescription( segmentDescription ); | |
// Invoking the callback that is being wrapped in a tracked transaction. | |
callback( argumentCollection = callbackArguments ); | |
} catch ( any error ) { | |
if ( structKeyExists( attributes, "errorCallback" ) ) { | |
errorCallback( error ); | |
} else { | |
rethrow; | |
} | |
} finally { | |
segment.close(); | |
} | |
} // END: Thread. | |
} | |
// -- For debugging the demo --. | |
thread action = "join"; | |
dump( cfthread ); | |
</cfscript> | |
<script type="text/javascript"> | |
// Refresh page to generate traffic (the Cloud dashboard seems to have trouble | |
// syncing from my local system unless there is a steady stream of traffic). | |
setTimeout( | |
function() { | |
window.location.reload(); | |
}, | |
1000 | |
); | |
</script> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment