Why does Grails recommend singleton scope for controllers with actions as methods?
Asked Answered
G

1

8

I know early versions of Grails used prototype scope for controllers because actions were all closures at that time.

I know that the current version documentation recommends singleton scoped controllers for controllers that use methods as actions.

From the following post it seems that methods and singleton scope are more desirable or recommended, but it's not clear why.
ttp://grails.1312388.n4.nabble.com/Default-scope-for-controllers-doc-td4657986.html

We have a large project that uses prototype scoped controllers with actions as methods. Changing to the recommended controller scope involves risk and retesting, and removing any non-singleton friendly state from existing controllers.

I'd like to know why does Grails recommend singleton scope for method as actions controllers? Is it just because that's more common and similar to Spring MVC, and avoids confusion, or is there an opportunity for performance improvement, or what? What do I gain if I make the switch to singleton controllers? What is the cost if I don't make the switch?

Gombosi answered 16/4, 2015 at 18:4 Comment(5)
This is a discussion better suited for groups.google.com/forum/#!forum/grails-dev-discuss. The short answer is that you shouldn't need to create a new controller instance for every single request unless you are doing other sub-optimal things. Singleton controller is almost always the right thing. For a discussion, post to the mailing list. I am leaving this as a comment, not an answer, because this isn't really a good question for StackOverflow.Cati
Jeff, thanks for your comment and short answer. I posted on discussion as you suggested. Surprised that you feel not good StackOverflow question, it seems like a good fit to me give that the documentation recommends a change but gives no reason for the change, and lack of a solid answer to that question elsewhere on Internet that I'be been able to find.Gombosi
"Surprised that you feel not good StackOverflow question" - Part of the reason that it isn't a good SO question is that it asks 4 separate questions. They are related but separate questions. The implications around this are non-trivial and for most folks would require a discussion in order to really get it all sorted out. I see that you have accepted the answer that Burt gave so I am glad that you got what you needed.Cati
Jeff, OK, good point about 4 questions. I think I'd like to leave the question up, since Burt's information is so thorough, and I think, is important for any other large project that started in earlier Grails versions and now must do a cost benefit analysis of refactoring to singleton controllers. Thanks again to both of you for the help. Much appreciated.Gombosi
"is important for any other large project that started in earlier Grails versions and now must do a cost benefit analysis of refactoring to singleton controllers" - There aren't really any significant new benefits to the refactoring. The benefits/drawbacks are the same now as they always were (for the most part). Anyone who could justify taking advantage of stateful controllers in previous versions of Grails would likely use the same arguments to justify doing it with recent versions of Grails. I think those justifications are largely bogus, but they won't have changed.Cati
A
20

I haven't worked much with Rails, but (at least in the versions I played with, things may be different now) the controller is the model, containing the data to be rendered by the view. During request handling you store values in controller instance fields before handing off processing to view renderers. So there it makes sense to create a new controller instance for each request so they're distinct.

Grails was inspired by Rails and uses several of its conventions, most famously convention-over-configuration. But the ability to use the controller as the model was also added as a feature, although it wasn't well documented and I doubt many used it.

The typical way a controller action works when using a GSP to render the response (as opposed to forwarding or redirecting, or rendering directly in the controller, e.g. with render foo as JSON) is to return a Map with one or more key/value pairs from the action, and often the return keyword is omitted since it's optional in Groovy:

class MyController {
   def someAction() {
      def theUser = ...
      def theOtherObject = ...
      [user: theUser, other: theOtherObject]
   }
}

Here the model map has two entries, one keyed by user and the other keyed by other, and those will be the variable names used in the GSP to access the data.

But what most people don't know is that you could also do it like this:

class MyController {

   def user
   def other

   def someAction() {
      user = ...
      other = ...
   }
}

In this case a model map isn't returned from the action, so Grails would populate the model from all properties of the controller class, and in this case the same GSP would work for both approaches since the variable names in the second approach are the same as the map keys in the first.

The option to make controllers singletons was added in 2.0 (technically 1.4 before it was renamed to 2.0, see this JIRA issue) and we also added support for methods as actions in addition to retaining support for closures. The original assumption was that using closures would enable some interesting features, but that never happened. Using methods is more natural since you can override them in subclasses, unlike closures which are just class-scope fields.

As part of the 2.0 rework we removed that Rails-inspired feature on the assumption that since it was essentially undocumented, that the impact on the few odd apps that used the feature wouldn't be significant. I don't recall anyone ever complaining about the loss of that feature.

Although controller classes are typically readily garbage-collectible and creating an instance per request doesn't affect much, it's rare to need per-request state in a controller, so singletons usually make more sense. The default prototype scope was left for backwards compatibility but it's easy to change the default with a Config.groovy property (and the file generated by the create-app script does this).

Although each request does get a new request and response, and if sessions are used each user will have their own, but those are not instance fields of the controllers. They look like they are because we can access request, response, session, params, etc. inside of actions, but those are actually the property access forms of the getRequest(), getResponse(), getSession(), and getParams() methods that are mixed into all controllers during compilation by AST transforms. The methods access their objects not as class fields but via ThreadLocals, so there is per-request state but it's not stored in the controller instance.

I don't remember if there was much in the way of benchmarking to compare using methods versus closures, but Lari Hotari probably did some. If there was a difference it was probably not significant. You could test this in your own application by converting just one or a few controllers and doing before and after tests. If the performance, scaling, and memory differences aren't significant between the two approaches you'll probably be safe staying with prototype score and/or closures. If there is a difference and you don't have instance fields in your controllers, then it'll probably be worth the effort to convert to singletons and methods.

If you do have instance fields they can probably be converted to request attributes - request.foo = 42 is a metaclass shortcut for request.setAttribute('foo', 42), so you could store per-request data safely there instead.

Accident answered 16/4, 2015 at 22:57 Comment(1)
Burt, thank you very much for such a great detailed response. Now I feel like I know the whole story.Gombosi

© 2022 - 2024 — McMap. All rights reserved.