The Assert field in mgo/txn.Op is an interface{}, so
when it's marshaled and unmarshaled, the order
can change because unmarshaling unmarshals as bson.M
which does not preserve key order.

https://play.golang.org/p/_1ZPl7iMyn

On 8 June 2016 at 15:55, Gustavo Niemeyer
<gustavo.nieme...@canonical.com> wrote:
> Is it mgo itself that is changing the order internally?
>
> It should not do that.
>
> On Wed, Jun 8, 2016 at 8:00 AM, roger peppe <rogpe...@gmail.com> wrote:
>>
>> 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
>
>
>
>
> --
> gustavo @ http://niemeyer.net

-- 
Juju-dev mailing list
Juju-dev@lists.ubuntu.com
Modify settings or unsubscribe at: 
https://lists.ubuntu.com/mailman/listinfo/juju-dev

Reply via email to