go-bongo / bongo

Go ODM for MongoDB
MIT License
487 stars 40 forks source link

Collection.Save CascadeSave bug #36

Open wxf4150 opened 6 years ago

wxf4150 commented 6 years ago

Collection.Save(document) method invoke "CascadeSave" using goRoutine. when i change document's field 10 times and save every times. the function CascadeSave will invoke 10 times use 10 Routines; the 10 Routines will not ordered, some Routines will concurrency, then parent doc error

jraede commented 6 years ago

What you’re describing here is just the nature of concurrency. I wouldn’t really call this a bug.

If you find yourself saving the same document in multiple different go routines then you probably just need to address that in your application architecture. Can you explain the use case?

Sent via Mobile


From: wxf4150 notifications@github.com Sent: Tuesday, May 29, 2018 3:51:08 AM To: go-bongo/bongo Cc: Subscribed Subject: [go-bongo/bongo] Collection.Save CascadeSave bug (#36)

Collection.Save(document) method invoke "CascadeSave" using goRoutine. when i change document's field 10 times and save every times. the function CascadeSave will invoke 10 times use 10 Routines; the 10 Routines will not ordered, some Routines will concurrency, then parent doc error

— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHubhttps://github.com/go-bongo/bongo/issues/36, or mute the threadhttps://github.com/notifications/unsubscribe-auth/AEXD4tsXWMQ_doIkUxOrbjPYruRXYm3jks5t3P3sgaJpZM4UQ_sD.

wxf4150 commented 6 years ago
var parentColl *bongo.Collection
var childColl *bongo.Collection
type parent struct{
    bongo.DocumentBase `bson:",inline"`
    Childs []*Child
}
type Child struct{
    bongo.DocumentBase `bson:",inline"`
    ParentID bson.ObjectId
    Status int
    diffTracker *bongo.DiffTracker
}

func Test_Bongo(t *testing.T){
    parentColl=utils.Bconn.Collection("parent")
    childColl=utils.Bconn.Collection("child")
    parent:=new(parent)
    parentColl.Save(parent)
    child:=new(Child)
    child.ParentID=parent.Id
    childColl.Save(child)

    for i:=1;i<100;i++{
        child.Status=i
        childColl.Save(child) //will invoke CascadeSave  using (goroutine CascadeSave)
    //code: https://github.com/go-bongo/bongo/blob/master/collection.go#L147
    }

    //**the end**: len(parent.Childs)!=1 or parent.Childs[0].Status!=99
}
func (c *Child) GetCascade(collection *bongo.Collection) []*bongo.CascadeConfig {
    connection := collection.Connection
    rel :=c
    cascadeMulti := &bongo.CascadeConfig{
        Collection:  connection.Collection("parent"),
        //Properties:  []string{"name","orgname","status"},
        Data:rel,
        ThroughProp: "childs",
        RelType:     bongo.REL_MANY,
        Query: bson.M{
            "_id": c.ParentID,
        },
    }
    if c.diffTracker!=nil && c.diffTracker.Modified("ParentID") {
        origId, _ := c.diffTracker.GetOriginalValue("ParentID")
        if origId != nil {
            oldQuery := bson.M{
                "_id": origId,
            }
            cascadeMulti.OldQuery = oldQuery
        }

    }
    return []*bongo.CascadeConfig{ cascadeMulti}
}

the end: parent doccument error, len(parent.Childs)!=1 or parent.Childs[0].Status!=99

wxf4150 commented 6 years ago

or update this way

    for i:=1;i<20;i++{
        cloneChild:=*child
        cloneChild.Status=i
        childColl.Save(&cloneChild) 
    }
    time.Sleep(2*time.Second)

the end: parent doccument error, len(parent.Childs)!=1 or parent.Childs[0].Status!=19

jraede commented 6 years ago

I could see this being a race condition if there are multiple users updating the same record. Solution seems to be just removing the go in front of the CascadeSave here https://github.com/go-bongo/bongo/blob/master/collection.go#L147

Can you submit a PR?