Mongodb 通过解组得到堆栈溢出

Mongodb got stack overflow by unmarshal

我想在golang中使用mongodb,写了一个示例应用:

package main

import (
    "fmt"
    "labix.org/v2/mgo"
    "labix.org/v2/mgo/bson"
    "os"
)

type Session struct {
    Id   bson.ObjectId          `bson:"_id"`
    Data map[string]interface{} `bson:"data"`
}

func (rcv *Session) SetBSON(raw bson.Raw) error {
    return raw.Unmarshal(rcv)
}


type Authen struct {
    Name  string `bson:"name"`
    Email string `bson:"email"`
}

func main() {
    uri := "mongodb://localhost/"
    if uri == "" {
        fmt.Println("no connection string provided")
        os.Exit(1)
    }

    sess, err := mgo.Dial(uri)
    if err != nil {
        fmt.Printf("Can't connect to mongo, go error %v\n", err)
        os.Exit(1)
    }
    defer sess.Close()

    sess.SetSafe(&mgo.Safe{})
    collection := sess.DB("test").C("sess")

    a := &Authen{Name: "Cormier", Email: "cormier@example.com"}
    s := &Session{}
    s.Id = bson.NewObjectId()
    s.Data = make(map[string]interface{})
    s.Data["logged"] = a

    err = collection.Insert(s)
    if err != nil {
        fmt.Printf("Can't insert document: %v\n", err)
        os.Exit(1)
    }

    c := &Session{}
    c.Id = bson.NewObjectId()
    c.Data = make(map[string]interface{})

    err = sess.DB("test").C("sess").Find(bson.M{}).One(c)
    if err != nil {
        fmt.Printf("got an error finding a doc %v\n")
        os.Exit(1)
    }

}

插入 mongodb 就像一个魅力,但解编回引用我有以下恐慌:

runtime: goroutine stack exceeds 1000000000-byte limit
fatal error: stack overflow

runtime stack:
runtime.throw(0x6d84d9)
        c:/go/src/runtime/panic.go:491 +0xad
runtime.newstack()
        c:/go/src/runtime/stack.c:784 +0x5ef
runtime.morestack()
        c:/go/src/runtime/asm_amd64.s:324 +0x86

我做错了什么?

这个逻辑引入了无限递归:

func (rcv *Session) SetBSON(raw bson.Raw) error {
    return raw.Unmarshal(rcv)
}

Session 实现了 Setter 接口,这意味着它的解组是通过它的 SetBSON 方法发生的,该方法是通过要求 bson 包解组自身来实现的,这将通过调用它的 SetBSON 方法来做到这一点。这永远不会结束,直到堆栈 space 结束。

当然,解决方案是不通过仅要求 bson 包再次解组来实现 Session 的自定义解组。