oxide
oxide copied to clipboard
Wrap batch updates on a transaction
When you send an update command with multiple clauses, it should be atomic and if one of the updates don't work, all updates should be rolled back.
Apparently mongodb doesn't work this way -- seems like the updates are atomic per document. You can see that the update to the last item of this collection wasn't applied at all, the name was not changed, even though the error was on the counter field.
test> db.trx.insertMany([
{x: 1, name: 'John', counter: 1},
{x: 2, name: 'Alex', counter: 2},
{x: 3, name: 'Kate', counter: 'Str'}
])
{
acknowledged: true,
insertedIds: {
'0': ObjectId("62d304384ca23db6dd87d2c4"),
'1': ObjectId("62d304384ca23db6dd87d2c5"),
'2': ObjectId("62d304384ca23db6dd87d2c6")
}
}
test> db.trx.updateMany({}, { $set: {name: 'Old'}, $inc: {counter: 1} })
MongoServerError: Cannot apply $inc to a value of non-numeric type. {_id: ObjectId('62d304384ca23db6dd87d2c6')} has the field 'counter' of non-numeric type string
test> db.trx.find()
[
{
_id: ObjectId("62d304384ca23db6dd87d2c4"),
x: 1,
name: 'Old',
counter: 2
},
{
_id: ObjectId("62d304384ca23db6dd87d2c5"),
x: 2,
name: 'Old',
counter: 3
},
{
_id: ObjectId("62d304384ca23db6dd87d2c6"),
x: 3,
name: 'Kate',
counter: 'Str'
}
]
test>
We might need to have a streaming update:
- retrieve all the matching rows for the
querypart of the update - get next row
- run all the updates for the row wrapped on a transaction
- if an error occurs rollback and continue
OK, this keeps getting more interesting:
test> db.trx.insertMany([
{x: 1, name: 'John', counter: 1},
{x: 2, name: 'Alex', counter: 'Str'},
{x: 3, name: 'Kate', counter: 3}
])
{
acknowledged: true,
insertedIds: {
'0': ObjectId("62d305c44ca23db6dd87d2c7"),
'1': ObjectId("62d305c44ca23db6dd87d2c8"),
'2': ObjectId("62d305c44ca23db6dd87d2c9")
}
}
test> db.trx.updateMany({}, { $set: {name: 'Old'}, $inc: {counter: 1} })
MongoServerError: Cannot apply $inc to a value of non-numeric type. {_id: ObjectId('62d305c44ca23db6dd87d2c8')} has the field 'counter' of non-numeric type string
test> db.trx.find()
[
{
_id: ObjectId("62d305c44ca23db6dd87d2c7"),
x: 1,
name: 'Old',
counter: 2
},
{
_id: ObjectId("62d305c44ca23db6dd87d2c8"),
x: 2,
name: 'Alex',
counter: 'Str'
},
{
_id: ObjectId("62d305c44ca23db6dd87d2c9"),
x: 3,
name: 'Kate',
counter: 3
}
]
So, here's how this should take place:
- retrieve all the matching rows for the
querypart of the update - get next row
- run all the updates for the row wrapped on a transaction
- if an error occurs rollback and abort further updates