<@U08AVR46R> I'm not quite following that, but all...
# box-products
b
@danmurphy I'm not quite following that, but all handler caching does is make the handlers singletons, as opposed to re-created each request. Perhaps check for var scoping issues?
d
We really dug through that to see if there is anything but couldn’t find the culprit (well, besides the injected Service property).
Basically, this is what the handler looks like. It is called via fetch from React. If the handler is setup like this, we end up getting the same results back from both methods.
Copy code
component extends=coldbox.system.RestHandler {

    property name='myService' inject='id';

    safetyCatches( event, rc, prc) {
        var issues = myService.list( params );
    }

    improvements( event, rc, prc) {
        var issues = myService.list( params );
    }

}
Doing this instead fixes the problem…
Copy code
component extends=coldbox.system.RestHandler {

    safetyCatches( event, rc, prc) {
        var issues = getInstance( 'myService' ).list( params );
    }

    improvements( event, rc, prc) {
        var issues = getInstance( 'myService' ).list( params );
    }

}
b
Yeah, I still don't understand your problem, lol. You say you're getting the same result back from each method, but your running the same code in each method. So... yeah... 🤔
Keep in mind the var scoping issues may be part of the
myService
Whether or not it's marked for WireBox to treat it as a singleton, injecting it into another singleton will still force it's scope to be the same as the composing instance
d
They are passing very different params in both cases.
b
I'd say it looks like var scoping issues in
myService
or downstream of that
d
So you’re saying
myService
is being injected as a singleton because handlerCaching is on, which makes the handler a Singleton. Gotcha, good to know.
b
I wouldn't use the words "injected as a singleton", but yes it's potentially a scope-widening injection if you weren't expecting it
d
This is the actual entire list function. Does anything stick out for var scoping issues?
Copy code
component {

	property name='jira' inject='coldbox:setting:jira';

	function init() {
		return this;
	}

	function list( required jql, maxResults = 50 ) {
		cfhttp(
			method   = 'GET',
			url      = 'https://' & jira.hostname & '/rest/api/2/search?jql=' & arguments.jql & '&maxResults=' & arguments.maxResults,
			charset  = 'utf-8',
			username = jira.username,
			password = jira.password,
			result   = 'result'
		) {
			cfhttpparam(
				type  = 'header',
				name  = 'Content-Type',
				value = 'application/json'
			);
		}

		if ( result.statuscode EQ 200 ) {
			var content = deserializeJSON( result.filecontent );
			result.append( { 'total' : content.total } );
			result.append( { 'issues' : content.issues } );
			return result;
		} else {
			return result;
		}
	}
}
b
result
isn't varred
d
Does the result need to be vared somehow?
Ha, jinx.
I’ve never thought of that in some of those functions that have a called out return variable.
cfhttp, cfexecute, etc.
Would that just be something like local.result?
b
That'st he easiest
d
Is there a better way then?
b
Also, you should test the singleton leak detector, even though it wouldn't recognize that CFC as a singleton since it's not annotated.
I prefer
Copy code
result   = 'local.result'
you can also do
Copy code
var result='';

cfttp...
but I prefer to keep it together and not separate the declaration
d
Yeah, that’s what I meant (the result = ‘local.result’)
You’re talking about this? Sounds fancy, ha. https://forgebox.io/view/singleton-leak-detector
I’ll definitely var result and see if that fixes it and then circle back with that Singleton Leak Detector (running to a meeting fright now).
This is great! Been pulling our hair out over this one.
b
Yep, that's the module
d
Thanks a ton Brad!
b
The only trick is you have to hit the pages/CFCs/code in question for it to catch the leaks
d
We have some tests around some of this (I think) so that should work?
b
That is tricky-- test cases are a great way to hit all the coverage, BUT ... • they need to be integration tests that load the full framework • the leak detector needs to be installed and loaded in the coldbox instance that the tests are running in • your tests need to NOT unload ColdBox at the end
I'm not actually sure the the way ColdBox is bootstrapped in the integration test harness allows for you to hit arbitrary URLs. I'm sure it's possible, but I've never done it, lol
I suppose the application.cfc in the test harness would need to have the plumbing to process coldbox requests so you could hit the module route that the singleton leak detector exposes
This sounds like a great reason for that module to have an export features to run headlessly 😆
Actually, that would be a cool built in feature even for model unit tests-- the ability to check for extra variables-scoped vars that appeared in the CFC during the test 🤔
d
I used to work on an app where pretty much all model CFCs were singletons for performance reasons. We built variables scope leak detection into our testing infrastructure, because devs would forgot to do the var-ing thing and poke themselves in the eye.
Definitely worth having if you do singletons.
d
Sure enough, adding changing the result parameter to
local.result
makes it all work as expected. Man, good lesson learned. Now to go search for other uses of cfhttp, cfexecute, cfldap, etc. where they just have a return variable and we’re probably not scoping it…
(poking around with that Singleton Leak Detector next).
b
@danmurphy You probably have a var that's null and I need to add a null check to that code
That line is just looping over the
variable
scope from the CFC
d
If you have something you’d like to change it to to test out a fix, let me know and I can throw it in there.
b
@danmurphy What CF engine/version are you using?
I'm trying to test a fix real quick, but my null variables are getting ignored, I think due to the way each() is ignoring them
d
Lucee 5.3.9.141
b
Do you have anything funky like full null support turned on?
d
We do not.
b
Ok, I think that was just the Adobe version I was testing on being weird
d
This happened immediately after a fwreinit and is the same no matter how many pages I visit first before going to the
/leakDetector
url (which sounds like something I could’ve used when we still had babies in diapers).
b
Yes, I know
like I said, it just needed some isnull checks
You probably could have added them yourself in a coupla' minutes hours ago 😉
bump the package version and see if it works better now
d
Coming up now!
Trying to poke around and see how it works, exactly.
b
The module is very simply really- there's not even that much code
Every time a Singleton is created by WireBox, we snapshot the vars in the CFC's variables scope
When you hit the UI to check for leaks, we snapshot the variables again and compare for • any added keys • any modified keys
If we find any, we assume you've been running code that's touching the variables scope and shouldn't be
Any variables with the same name as a property are ignored as well as a few annoying built-ins like
variables.cfstoredProc
which get created EVEN IF you are using the
results
attribute.
Yeah, the CF engine creates that
I'm not entirely sure why I'm not already ignoring that one
I think because the app I first wrote this for uses all stored procs, lol
d
It did find a legit one though!
b
EXECUTIONTIME
was another one I already ignore. There was some tag in CF that always created that variable regardles
Sweet! That makes it all worth it 🙂
d
It isn’t finding the one that started this thread. If I give it the Singleton annotation, it does find that one too.
b
Yeah, it only introspects • handlers • CFC's with the singleon annotation
d
(Which I think we tried to fix this issue at one point by marking it not as a Singleton, but that was probably silly of us).
b
I just pushed a little update to ignore
cfquery
d
Honestly, the whole Singleton thing still confuses me. 😬 Is there a “talk to me like I’m 5” statement for when objects should be Singletons or not?
b
Are you asking how singletons are treated or the reason why you'd want them to be treated in that manner?
d
Why you’d want them to be treated in that manner or not.
b
Honestly, in many cases it doesn't really matter. If a singleton doesn't actually store any state (variables) that it needs to mutate over time then you could re-create the CFC every time you wanted to use it and it's not like it would behave any different outside of the unnecessary overhead of CFC creation on every use.
If you have some sort of "IP filter" service that tracks an array of the last 1000 IPs to hit your site and verify if any of them are hitting the site to fast to block them, then you'd need it to be persistent, and typically a singleton
Otherwise, when you created the CFC on every request, it would be empty!
My rule of thumb is pretty much any CFC that ISN'T transient (i.e holding data that's specific to a single request or session) should be a singleton just for performance
So this would be all • services • DAOs • gateways (if you use those) • handlers
There's no reason to ever create more than one of those really
But an
order.cfc
stored in someone's shopping cart with their items, or a
user.cfc
in session to track the logged in user or a
product.cfc
instance returned from the DB to represent something being displayed on that page would all be transients. You create them when you need and they are only visilble to one request or user's sesion, etc
d
So basically, Beans aren’t Singletons for sure. Everything else should be.
b
And technically the user example isn't even a scope-less instance. single and transient are the most common scopes, but
session
is also a scope you can ask WireBox to store a CFC in automatically
"bean" is a little narrow of a defintion, but yes
But again, it's not exactly black and white. WireBox knows of several different types of scopes including cachebox-related ones
I.e. an object which is automatically persisted in cachebox for you. Created if necessary, otherwise pulled from the cache
d
A good test could be to explicitly mark all the things as Singletons if they aren’t already (services, DAOs) and then run this leak detector.
b
If you had something like this User.cfc
Copy code
component scope="session" {
}
Then you can just do
Copy code
currentUser = getinstance( 'user' )
and wirebox will give you the current user for that session, creating it if necessary
just like singletons are more/less/mostly stored in the
application
scope (technically the "singleton" scope is bound to the wirebox injector)
d
We’ve never used scope on a component like that before. Huh.
b
I have to run-- picking up kids from school time...
Yep, wirebox does more than most people ever realize 😉
d
No prob. Thanks Brad!
b
@danmurphy The detection to tell if a variable changed is very simple
Copy code
oldValue.toString() != scopeValue.toString()
the best thing to do would be to add
Copy code
property name="allowedFilters";
to the CFC and the leak detector will start ignoring it
I added cflock to ignore too
d
You're awesome!