Last active
April 2, 2018 21:12
-
-
Save mpalmerlee/9709085 to your computer and use it in GitHub Desktop.
Save MongoDB Document With Concurrent Edit Protection
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var mongoose = require("mongoose"); | |
var ObjectId = mongoose.Schema.Types.ObjectId; | |
//create schema for a post | |
var PostSchema = new mongoose.Schema({ | |
nonce: ObjectId, //this is used for protecting against concurrent edits: http://docs.mongodb.org/ecosystem/use-cases/metadata-and-asset-management/ | |
name: String, | |
dateCreated: { type: Date, default: Date.now }, | |
dateLastChanged: { type: Date, default: Date.now }, | |
postData: mongoose.Schema.Types.Mixed | |
}); | |
//compile schema to model | |
exports.PostModel = db.model('post', PostSchema); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var postService = require('./postService'); | |
exports.AppendPostText = function(postId, text, callback){ | |
saveGameByIdWithConcurrencyProtection(postId, function(doc, cb){ | |
doc.postData.text += text; | |
cb(null, data); | |
}, 0, function(err, doc){ | |
callback(err, doc); | |
}); | |
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var mongoose = require("mongoose"); | |
var models = require("./documentModel"); | |
/* | |
This method uses the nonce field in the document to protect against concurrent edits on the post document | |
http://docs.mongodb.org/ecosystem/use-cases/metadata-and-asset-management/#create-and-edit-content-nodes | |
transformFunction will be given the post doc and should callback the data to update: | |
transformFunction(doc, callback) | |
*/ | |
var savePostByIdWithConcurrencyProtection = function(postId, transformFunction, retries, callback){ | |
FindPostById(postId, function(err, doc){ | |
if(err || !doc){ | |
callback(err || "Unable to find post in savePostByIdWithConcurrencyProtection"); | |
return; | |
} | |
transformFunction(doc, function(err, data){ | |
if(err){ | |
callback(err); | |
return; | |
} | |
data.nonce = new mongoose.Types.ObjectId; | |
//setTimeout(function(){ //setTimeout for testing concurrent edits | |
models.PostModel.update({"_id":postId, "nonce":doc.nonce}, data, function(err, numberAffected, raw){ | |
if(err){ | |
callback(err); | |
return; | |
} | |
//console.log("savePostByIdWithConcurrencyProtection: ", numberAffected, raw); | |
if(!numberAffected && retries < 10){ | |
//we weren't able to update the doc because someone else modified it first, retry | |
console.log("Unable to savePostByIdWithConcurrencyProtection, retrying ", retries); | |
//retry with a little delay | |
setTimeout(function(){ | |
savePostByIdWithConcurrencyProtection(postId, transformFunction, (retries + 1), callback); | |
}, 20); | |
} else if(retries >= 10){ | |
//there is probably something wrong, just return an error | |
callback("Couldn't update document after 10 retries in savePostByIdWithConcurrencyProtection"); | |
} else { | |
FindPostById(postId, callback); | |
} | |
}); | |
//}, 5000); | |
}); | |
}); | |
}; | |
var FindPostById = function(id, callback){ | |
//search for an existing post | |
models.PostModel.findOne({"_id":id}, function(err, doc){ | |
if(err){ | |
console.error("FindPostById:", err); | |
} else if(!doc){ | |
var msg = "Could Not FindPostById:" + id; | |
console.error(msg); | |
return callback(msg); | |
} | |
callback(err, doc); | |
}); | |
}; | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment