OK, I understand now, I think. The underlying problem is that subdocument searches in MongoDB are order-sensitive.
For example, I just tried this in a mongo shell: > db.foo.insert({_id: "one", x: {a: 1, b: 2}}) > db.foo.find({x: {a: 1, b: 2}}) { "_id" : "one", "x" : { "a" : 1, "b" : 2 } } > db.foo.find({x: {b: 2, a: 1}}) > The second find doesn't return anything even though it contains the same fields with the same values as the first. Urk. I did not know about that. What a gotcha! So it *could* technically be OK if the fields in the struct (and any bson.D) are lexically ordered to match the bson Marshaler, but well worth avoiding. I think things would be considerably improved if mgo/bson preserved order by default (by using bson.D) when unmarshaling. Then at least you'd know that the assertion you specify is exactly the one that gets executed. cheers, rog. On 8 June 2016 at 10:42, Menno Smits <menno.sm...@canonical.com> wrote: > > > On 8 June 2016 at 21:05, Tim Penhey <tim.pen...@canonical.com> wrote: >> >> Hi folks, >> >> tl;dr: not use structs in transaction asserts >> >> ... >> >> The solution is to not use a field struct equality, even though it is easy >> to write, but to use the dotted field notation to check the embedded field >> values. > > > > To give a more concrete example, asserting on a embedded document field like > this is problematic: > > ops := []txn.Op{{ > C: "collection", > Id: ..., > Assert: bson.D{{"some-field", Thing{A: "foo", B: 99}}}, > Update: ... > } > > Due to the way mgo works[1], the document the transaction operation is > asserting against may have been written with A and B in reverse order, or > the Thing struct in the Assert may have A and B swapped by the time it's > used. Either way, the assertion will fail randomly. > > The correct approach is to express the assertion like this: > > ops := []txn.Op{{ > C: "collection", > Id: ..., > Assert: bson.D{ > {"some-field.A", "foo"}, > {"some-field.B", 99}, > }, > Update: ... > } > > or this: > > ops := []txn.Op{{ > C: "collection", > Id: ..., > Assert: bson.M{ > "some-field.A": "foo", > "some-field.B": 99, > }, > Update: ... > } > >> >> Yet another thing to add to the list of things to check when doing >> reviews. > > > I think we can go a bit further and error on attempts to use structs for > comparison in txn.Op asserts in Juju's txn layers in state. Just as we > already do some munging and checking of database operations to ensure > correct multi-model behaviour, we should be able to do this same for this > issue and prevent it from happening again. > > - Menno > > [1] If transaction operations are loaded and used from the DB (more likely > under load when multiple runners are acting concurrently), the Insert, > Update and Assert fields are loaded as bson.M (this is what the bson > Unmarshaller does for interface{} typed fields). Once this happens field > ordering is lost. > > > > > -- > Juju-dev mailing list > Juju-dev@lists.ubuntu.com > Modify settings or unsubscribe at: > https://lists.ubuntu.com/mailman/listinfo/juju-dev > -- Juju-dev mailing list Juju-dev@lists.ubuntu.com Modify settings or unsubscribe at: https://lists.ubuntu.com/mailman/listinfo/juju-dev