Sharing a Session

641 views
Skip to first unread message

Paddy Foran

unread,
Nov 11, 2011, 5:23:53 PM11/11/11
to mgo-...@googlegroups.com
I'm afraid I'm new to both Mongo and mgo, so I'll probably put plenty of my ignorance on display here. I would like to try and share a session between multiple functions in my program, and I seem to be running into difficulties. My first instinct was to create the session variable in the root of the script

var session *mgo.Session

then in the main() function, initialise the session

func main() {
    session, err := mgo.Mongo("localhost")
    if err != nil {
        panic(err)
    }
    defer session.Close()
}

then perform my database operations in functions

func register(values map[string][]string) (user *User, err error) {
    // assume I'm initialising user correctly
    c := session.DB("development").C("users")
    err = c.Insert(&user)
    return
}

However, when I implemented this approach (what I think is the most obvious approach, but I could just be an idiot), I got a wonderful panic for my trouble.

011/11/11 04:47:47 http: panic serving 127.0.0.1:62204: runtime error: invalid memory address or nil pointer dereference
/Users/paddyforan/go/src/pkg/http/server.go:576 (0x2e58d)
_func_005: buf.Write(debug.Stack())
/Users/paddyforan/go/src/pkg/runtime/proc.c:1356 (0x11453)
panic: reflect·call(d->fn, d->args, d->siz);
/Users/paddyforan/go/src/pkg/runtime/runtime.c:128 (0x11e0a)
panicstring: runtime·panic(err);
/Users/paddyforan/go/src/pkg/runtime/darwin/thread.c:480 (0x165b4)
sigpanic: runtime·panicstring("invalid memory address or nil pointer dereference");
/Users/paddyforan/go/src/pkg/sync/atomic/asm_386.s:47 (0x114fbe)
AddUint32: LOCK
/Users/paddyforan/go/src/pkg/sync/rwmutex.go:30 (0xa9efb)
(*RWMutex).RLock: if atomic.AddInt32(&rw.readerCount, 1) < 0 {
/Users/paddyforan/package/Go/mgo/session.go:2369 (0x50162)
(*Session).acquireSocket: s.m.RLock()
/Users/paddyforan/package/Go/mgo/session.go:2477 (0x50555)
(*Session).writeQuery: socket, err := s.acquireSocket(false)
/Users/paddyforan/package/Go/mgo/session.go:1176 (0x4bec2)
Collection.Insert: _, err := c.DB.Session.writeQuery(&insertOp{c.FullName, docs})
/Users/paddyforan/package/Go/package/user.go:97 (0x314b)
register: err = c.Insert(&user)
/Users/paddyforan/package/Go/package/main.go:147 (0x2b3b)
openID: user, err := register(values)
/Users/paddyforan/go/src/pkg/http/server.go:686 (0x2536b)
HandlerFunc.ServeHTTP: f(w, r)
/Users/paddyforan/go/src/pkg/http/server.go:906 (0x25fca)
(*ServeMux).ServeHTTP: h.ServeHTTP(w, r)
/Users/paddyforan/go/src/pkg/http/server.go:652 (0x251e4)
(*conn).serve: handler.ServeHTTP(w, w.req)
/Users/paddyforan/go/src/pkg/runtime/proc.c:261 (0xfa5b)
goexit: runtime·goexit(void) 

So my question is, really, what is the appropriate way to share a session across several different functions?

As a follow-up question, and here's where my ignorance of Mongo really shines, should I be forming a separate session for separate collections? In what situation is it recommended that I create a new session?

Thanks,
Paddy 

Gustavo Niemeyer

unread,
Nov 11, 2011, 6:29:29 PM11/11/11
to mgo-...@googlegroups.com
Hey Paddy,

> I'm afraid I'm new to both Mongo and mgo, so I'll probably put plenty of my
> ignorance on display here. I would like to try and share a session between
> multiple functions in my program, and I seem to be running into
> difficulties. My first instinct was to create the session variable in the
> root of the script

This isn't what I'd do, but mostly because global state tends to be a
bad idea for a number of reasons, including making it harder to test
your application. That said, there's no reason for it to break.

>> func main() {
>>     session, err := mgo.Mongo("localhost")

This is the bug. session here is a local variable rather than your
global "session" variable, so the global variable is never being
initialized (:= declares and assigns, while = assigns).

>>     if err != nil {
>>         panic(err)
>>     }
>>     defer session.Close()
>> }

This function main is opening and closing the session immediately, so
it doesn't really look realistic. How come your program is not
returning immediately?

> So my question is, really, what is the appropriate way to share a session
> across several different functions?

The best way is also the most obvious: pass it as a parameter.

> As a follow-up question, and here's where my ignorance of Mongo really
> shines, should I be forming a separate session for separate collections? In
> what situation is it recommended that I create a new session?

Don't worry, that's actually a relevant question, even more because
sessions are really a mgo concept rather than a MongoDB one.

If you're handling http requests, a good approach is to use a new
session for each request, by Copy-ing your original session. This will
ensure parallelism and isolation of errors.

--
Gustavo Niemeyer
http://niemeyer.net
http://niemeyer.net/plus
http://niemeyer.net/twitter
http://niemeyer.net/blog

-- I'm not absolutely sure of anything.

Paddy Foran

unread,
Nov 11, 2011, 7:07:22 PM11/11/11
to mgo-...@googlegroups.com
Hi Gustavo,

Thanks for the quick reply!

This isn't what I'd do, but mostly because global state tends to be a
bad idea for a number of reasons, including making it harder to test
your application.  That said, there's no reason for it to break.

Would you mind elaborating on what you would do? I'd like to do things The Right Way(TM). It's generally easier than trying to mould everything to fit my twisted ideas of how the world should work.

This is the bug. session here is a local variable rather than your
global "session" variable, so the global variable is never being
initialized (:= declares and assigns, while = assigns).

Wonderful, so it was an issue with my knowledge of Go. The one thing I didn't fess up to being terrible at. :)

This function main is opening and closing the session immediately, so
it doesn't really look realistic. How come your program is not
returning immediately?

Sorry, stubbed it out in the interest of making the example a little cleaner and easier to follow. Probably stubbed too much. Following the defer statement, the main function initiates a HTTP server and proceeds to ListenAndServe. Which is what ends up calling the register function I showed.

The best way is also the most obvious: pass it as a parameter.

If you're handling http requests, a good approach is to use a new
session for each request, by Copy-ing your original session. This will
ensure parallelism and isolation of errors. 

Interesting. Where would you suggest I store the original, then? Sorry, I know it's probably a "it depends" question, but I'm trying to get a feel for what architecture I'm supposed to be using/working in, and I'm just not understanding the paradigm. Any guidance would be helpful. Also, if there was a more involved sample than the one on niemeyer.net/mgo (one that handled multiple types of requests, or different functions, for example), I'd love to know where I could find it. I just need to figure out what mindset I should be approaching this problem from.

Thanks,
Paddy Foran

Gustavo Niemeyer

unread,
Nov 29, 2011, 9:51:26 AM11/29/11
to mgo-...@googlegroups.com, foran...@gmail.com
> Interesting. Where would you suggest I store the original, then? Sorry, I
> know it's probably a "it depends" question, but I'm trying to get a feel for

You were right, it really depends, and designing the architecture of
your application in a way that fields get passed around taking the
responsibility of each area in account is an area that can't really be
explained or taught without practice and looking at individual cases
and examples.

I think you already got some advice on that area in the go-nuts
mailing list, right?

Paddy Foran

unread,
Nov 29, 2011, 11:58:19 AM11/29/11
to Gustavo Niemeyer, mgo-...@googlegroups.com
Some advice, yeah. Still trying to mature my way into having testable code with proper separation of concerns. I'll get it eventually-- thanks for putting up with me while I try and bumble my way through.

Thanks,
Paddy Foran
Reply all
Reply to author
Forward
0 new messages