Skip to content

Instantly share code, notes, and snippets.

@boutell
Created April 14, 2012 19:52
Show Gist options
  • Save boutell/2387479 to your computer and use it in GitHub Desktop.
Save boutell/2387479 to your computer and use it in GitHub Desktop.
New Mongoose retry-when-save-fails test
/**
* Create a situation where a unique index error will occur. Try to save the
* object again after making the relevant field unique. Then do an update on one of the objects.
*
* Expected behavior: each object after the first saves successfully the second (or third...) time and we wind
* up with ten objects. Actual behavior with current Mongoose: we wind up with one object because the
* isNew flag is never reset to true when an insert fails. Behavior with my pull request: the isNew flag
* is reset if an insert (not an update) fails, allowing save handlers to try again on error after doing things
* like making slugs more unique.
*
* tom@punkave.com 2012-04-14
*/
var mongoose = require('mongoose');
var db = mongoose.connect('mongodb://localhost/mongooseRetrySaveTest');
var Schema = mongoose.Schema;
var mediaItemSchema = new Schema({
slug: { type: String, unique: true }
});
// Don't do anything until the ensureIndex() calls on the model are really complete.
// Otherwise we get unique index errors *after* all the inserts succeed
// According to the node docs and irc discussion node is truly single-threaded and there is
// no risk those ensureIndex calls will complete before my on() handler gets attached. I did
// some digging in the drivers and looks like that is... probably... true (: but I would be happier
// if the model function had an on-complete callback
var MediaItem = mongoose.model('MediaItem', mediaItemSchema);
MediaItem.on('index', function()
{
console.log('got the index event from MediaItem');
cleanup();
});
function cleanup()
{
MediaItem.remove({}, function (err) {
if (err)
{
throw err;
}
console.log("Successfully cleaned up any previous test");
insert();
});
}
function insert()
{
for (i = 0; (i < 10); i++)
{
insertOne();
}
}
function insertOne()
{
var mediaItem = new MediaItem();
mediaItem.slug = "common-slug";
attemptSave(mediaItem);
}
function attemptSave(mediaItem)
{
mediaItem.save(afterSaveAttempt);
function afterSaveAttempt(err, savedItem)
{
console.log("in afterSaveAttempt");
if (err)
{
console.log("there is an error");
if ((err.code === 11000) && (err.err.indexOf('slug') !== -1))
{
console.log("Retrying with a more unique slug");
mediaItem.slug += (Math.floor(Math.random() * 10)).toString();
setTimeout(function() {
console.log('calling save from a timeout function');
mediaItem.save(afterSaveAttempt);
}, 0);
return;
}
else
{
console.log("Throwing error");
throw err;
}
}
console.log(savedItem);
// No error - count it as a success
afterSaveSuccess();
}
}
var saved = 0;
function afterSaveSuccess()
{
console.log("Counting success");
saved++;
if (saved === 10)
{
find();
}
}
// We should have 100 items now, how many do we really have?
function find()
{
MediaItem.find({}, function(err, docs) {
console.log("We have " + docs.length + " objects, we expected 10");
});
update();
}
// Find the first object and demonstrate that my changes
// didn't break update() in mongoose
function update()
{
MediaItem.findOne({slug: 'common-slug'}, function(err, original)
{
if (err)
{
throw err;
}
original.slug = 'totally-different-slug';
original.save(verifyUpdate);
});
}
function verifyUpdate(err, original)
{
if (err)
{
throw err;
}
console.log('update allegedly worked');
MediaItem.findOne({slug: 'totally-different-slug'}, function(err, original)
{
if (err)
{
throw err;
}
console.log('Update REALLY worked');
console.log('Flawless victory!');
});
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment