[ColdBox-3.7.0] model doesn't get cached in Session

I can’t seem to figure out why this is. In my coldbox.cfc config file, in my root, not in a module, I have set for my development environment handlerCaching=false. In my live environment I have set handlerCaching=true, which seems to me to be the best setup for this setting.
I have a User.cfc model and a userService.cfc model as dependencies in my Main.cfc handler. This User.cfc model gets cached in the Session scope, through the use of the scope property in the model itself. This works fine in development, but doesn’t get added to the Session scope in the live environment.

I understood that the handlerCaching setting is used to cache the handler cfc’s, that’s why I went a bit overboard and restarted Coldfusion on my live environment, which for me would mean that all caching would be gone and the application would start from a clean plate. But still the User.cfc model doesn’t get added to the Session scope with handlerCaching=true.
When I turn it off though, it gets added without a problem. But when I turn it back on, my User.cfc model is gone again.

In my Main.cfc I use the following line to add the dependency:

Can anyone explain to me what is going on, and what I might need to do to fix this?

Don’t ever directly inject transients into a singleton like a handler. In your production environment, you only have a single instance of the handler, and you don’t want it sharing the same user instance for everyone on the site. That’s known as scope-widening injection. You are getting a user stored in session, but just the first (and only user) that hits the site gets it stored in his session.

What you need to do is inject WireBox and ask WireBox for the user whenever you need it. If you’re in a handler, view, interceptor, etc, you can just call getModel() and skip the WireBox injection.

// Need to interact with user here:
user = WireBox.getInstance(“user@main”);
// … or this which doesn’t require WireBox to be injected.
user = getModel(“user@main”);

I’d also like to point out, this is a good example of why you need a test/stage/qa sort of environment where you test your app with production settings.

Thanks!

~Brad

ColdBox Platform Evangelist
Ortus Solutions, Corp

E-mail: brad@coldbox.org
ColdBox Platform: http://www.coldbox.org
Blog: http://www.codersrevolution.com

Brad,

first of all, thank you for answering so fast. I really appreciate it.

I needed to think your reply over a couple of times, because I wasn’t really able to understand what you were trying to say. But When I looked at it with a fresh mind this morning, I thought I was getting it. I think what you are trying to say is, because I cache the handlers through the coldbox configuration setting, the models I inject directly using the cfproperty tag get cached alongside the handler cfc. Am I following you correctly?

My second (set of) question(s) would then be, is there really any place you can inject these dependecies directly? I thought it was mighty handy (and much more readable) to have a cfproperty declare these models with a named instance:



And this at the top of my handler. I actually thought I was using Wirebox to instantiate these models correctly, since I declared them in my Wirebox.cfc with the following bindings:
// user bindings
map(‘user@main’).to(‘model.User’);
map(‘userService@main’).to(‘model.UserService’);

// internal bindings
map(‘TranslationService@internal’).to(‘model.TranslationService’);
map(‘Settings@internal’).to(‘model.Settings’);
map(‘SettingService@internal’).to(‘model.SettingService’);

But apparently I’m proverbally ‘doing it wrong’…
While I follow your reasoning with this, it still doesn’t explain why the user model doesn’t get cached in the session scope when handlerCaching is enabled. When I turn handlerCaching off, the session scope has the model sitting neatly in the session object (when I dump it out), and when I turn it back on, the cached model is just gone. I would expect it to be cached, even though it would be the same instance for everyone…

And I support your last statement completely. When I said ‘live environment’, I actually meant ‘a staging where a live configuration is used’. To avoid confusion, I actually meant the difference between the development and live environment configuration. I directly go from development to live.

best regards,
Mikaël

What Brad is trying to say, is that any time you cache something, it becomes a singleton. In the case of a user object, this is usually seen to be transient because it holds user data per request. In your case I am going to make the same assumption that Brad has made, which is to assume that the user object is a transient object.

In your handler, it would be better to wrap anything to do with the user, with a service object. This way you can make calls to the service object that will get the data for you.

It is good practice to also keep your handler to as much of minimum code as possible.

I’m already using service objects, but only to change the data in the model it is servicing, not for getting data to the presentation layer. The only code right now in my handlers are method calls in my service objects and get calls to my actual models (mostly). I’ll try and see if I can move those get calls to the service objects.

I can see how that could help solve my problem. Thanks for the advice.

best regards,
Mikaël

That’s right, which is why we are saying that the user object is injected but the state never changes. The rule of thumb is that if you expect the data to be changed, never inject it this way. Like Brad said, you’re better of doing a getModel() if you need the updated data stored in the object.

Don’t abandon injection completely, just learn what is safe to inject. Any other objects that at are meant to be singletons (i.e. there is only one instance of that object that’s created and your entire application shares it) can be safely injected inside another singleton like your handlers. Singletons are usually handlers, services, DAO, interceptors. Of course, make sure are writing your singletons to be thread safe. This means var scoping all variables and being careful what you put in the this and variables scope.

There should be no problem injecting the following as long as they are thread safe:

> I actually thought I was using Wirebox to instantiate these models correctly

The WireBox binder config doesn’t instantiate anything. It simply stores the blueprint for how to make an object. The object is instantiated when it is inject, or any time getmodel() requests an instance.

For transient object-- these are objects that are created over and over again by your application and contain data specific to one user and their request. This of it this way, if 5 users hit the exact same page of your site at the exact same time and ran the exact same code, would you want them sharing the exact same user object with the same data between all 5 of them. The answer is most likely no, which is why you need to not inject the user, but ask WireBox for it every time and then keep it in a LOCAL variable so it doesn’t bleed over into anyone else’s request.

The cfproperty injections at the top of a component are only processed once when the CFC is first created. However, getModel() is ran every time and potentially returns a different object. As for your session behavior, it most likely is in a session somewhere, just not yours. Session scopes are unique to each user of your site. That includes any other devs, qa staff, and the load balancer ping that hits the server every 15 seconds. When an object is created, it is placed in the session that belongs to whatever user happens to be on the site and hitting the page that causes its creation. Chances are, you’re simply too slow and someone or something else is hitting the site first and causing the handler to be created, which is kicking off the one-time creation of the user object, and it is being persisted in their session, not yours.

Thanks!

~Brad

ColdBox Platform Evangelist
Ortus Solutions, Corp

E-mail: brad@coldbox.org
ColdBox Platform: http://www.coldbox.org
Blog: http://www.codersrevolution.com