Lazy Instantiation of controllers (and formatters?)

25 views
Skip to first unread message

matryer

unread,
Oct 21, 2011, 9:39:10 AM10/21/11
to goweb
If we think about Go being deployed into GAE, and the current
requirement by Goweb to instantiate a controller before you can route
it - aren't we insisting that EVERY controller gets instantiated each
time the code starts?

If so, wouldn't it be better to accept the Type of controller struct
to create, and get Goweb to instantiate them on demand?

If we build this functionality once, where else might we be able to
use it?

Chris Farmiloe

unread,
Oct 21, 2011, 10:44:18 AM10/21/11
to golang...@googlegroups.com
If I understand you, I think you are talking about an issue I hit recently when running with GOMAXPROCS=4;

Currently there is one global instance of a RestController used to serve all requests.

So if you use variables on your controller type, you could be sharing memory between concurrent requests.

type MyController struct {
    myVar string 
}

func (me *MyController).Read(cx *Context, id string) {
    // set controller var
    me.myVar = cx.PathParams["value"]
    // call some blocking func that halts goroutine
    call.SomeBlockingFunc()
    // now we cannot trust myVar
    myVar == cx.PathParams["value"]  // maybe or maybe not true!
}

This could be solved by just coping the RestController (or Resource in the case of my exp-resources experiment) instance before calling the handler func.

Chris

matryer

unread,
Oct 21, 2011, 1:00:51 PM10/21/11
to goweb
I think we agree that it makes no sense for a controller to have any
state (especially on GAE).

I was thinking more along the lines of saving memory in the first
place. Imagine a complex website with 1,000 controllers... one
request comes in for ONE of the controllers. GAE kicks up the code,
and (due to Goweb requiring you map instances to routes) it creates
1,000 instances - just to serve one request.

It would be better if Goweb could ask a Factory for an instance (from
a given Type) and if the Factory has one, it returns it - or else it
creates an instance and caches it. This way, it will only use memory
it needs.

Mat


On Oct 21, 3:44 pm, Chris Farmiloe <chrisfa...@gmail.com> wrote:
> If I understand you, I think you are talking about an issue I hit recently
> when running with GOMAXPROCS=4;
>
> Currently there is one global instance of a RestController used to serve all
> requests.
>
> So if you use variables on your controller type, you could be sharing memory
> between concurrent requests.
>
> type MyController struct {
>     myVar string
>
> }
>
> func (me *MyController).Read(cx *Context, id string) {
>     // set controller var
>     me.myVar = cx.PathParams["value"]
>     // call some blocking func that halts goroutine
>     call.SomeBlockingFunc()
>     // now we cannot trust myVar
>     myVar == cx.PathParams["value"]  // maybe or maybe not true!
>
> }
>
> This could be solved by just coping the RestController (or Resource in the
> case of my exp-resources experiment) instance before calling the handler
> func.
>
> Chris
>

matryer

unread,
Oct 24, 2011, 1:06:26 PM10/24/11
to goweb
I asked a question on Stack Overflow of how to achieve this and it has
had some answers:
http://stackoverflow.com/questions/7850140/how-do-you-create-a-new-instance-of-a-struct-from-its-type-at-runtime-in-go/7856515#7856515

On Oct 21, 3:44 pm, Chris Farmiloe <chrisfa...@gmail.com> wrote:
> If I understand you, I think you are talking about an issue I hit recently
> when running with GOMAXPROCS=4;
>
> Currently there is one global instance of a RestController used to serve all
> requests.
>
> So if you use variables on your controller type, you could be sharing memory
> between concurrent requests.
>
> type MyController struct {
>     myVar string
>
> }
>
> func (me *MyController).Read(cx *Context, id string) {
>     // set controller var
>     me.myVar = cx.PathParams["value"]
>     // call some blocking func that halts goroutine
>     call.SomeBlockingFunc()
>     // now we cannot trust myVar
>     myVar == cx.PathParams["value"]  // maybe or maybe not true!
>
> }
>
> This could be solved by just coping the RestController (or Resource in the
> case of my exp-resources experiment) instance before calling the handler
> func.
>
> Chris
>

Chris Farmiloe

unread,
Oct 25, 2011, 8:18:59 PM10/25/11
to golang...@googlegroups.com
Actually I think it does probably make sense to store state on controllers.

Where it has come up several times for me is with authentication, where it's useful to store details on your controller struct that are then shared between controller methods, this could be solved by having some kind of free-field on Context to store data, but this is unlikely to work well in all scenarios.

In exp-resources I've taken the factory approach where you pass in a constructor method to MapResource rather than a new(Resource) and the constructor is called for each request that requires it. I don't think we need to worry about memory use of struct being created for each request then being torn down by the GC. Controllers should always be lightweight, but I've found it much more flexible to be able to use the structs if required.

matryer

unread,
Nov 8, 2011, 9:41:48 AM11/8/11
to goweb
Wouldn't the state on the controllers be shared with ALL users?
Reply all
Reply to author
Forward
0 new messages