Mis colegas y yo encontramos una solución. Podemos llamarlo inicialización en tres pasos .
Recuerda que MongoDB garantiza la atomicidad de las operaciones sobre un único documento. Con este hecho en mente podemos operar de la siguiente manera:
- Intente actualizar el documento, incrementando correctamente los contadores en un período de tiempo específico. No haga ningún upsert, solo una operación de actualización a la antigua. Recuerde que la ejecución de una declaración de actualización devuelve la cantidad de documentos escritos. Si el número de documentos escritos es mayor que cero, ya está.
- Si el número de documentos escritos por la actualización es cero, significa que el documento relativo a actualizar aún no está presente en la colección. Intente insertar todo el documento para las etiquetas especificadas. Poner todos los contadores (valores de campo) a cero. Además, la ejecución de una declaración de inserción devuelve el número de documentos escritos. Si devuelve cero o lanza una excepción, no importa:significa que algún otro proceso ya había insertado el documento para las mismas etiquetas.
- Ejecute la misma actualización anterior nuevamente.
El código debería parecerse a algo similar al siguiente fragmento de código.
// Firt of all, try the update
var result = db.test.update(
{timestamp_minute: ISODate("2013-10-10T23:06:00.000Z"), type: “memory_used”},
{$inc: {"values.39": 1}},
{upsert: false}
);
// If the update do not succeed, then try to insert the document
if (result.nModified === 0) {
try {
db.test.insert(/* Put here the whole document */);
} catch (err) {
console.log(err);
}
// Here we are sure that the document exists.
// Retry to execute the update statement
db.test.update(/* Same update as above */);
}
El procedimiento anterior funciona si se cumple una condición previa:_id
el valor debe derivarse de otros campos en el documento. En nuestro ejemplo, _id
el valor sería '2013-10-10T23:06:00.000Z-memory_used
. Solo usando esta técnica, la inserción en el punto 2 fallará correctamente.