Socket
Socket
Sign inDemoInstall

gridfs-locks

Package Overview
Dependencies
Maintainers
1
Versions
23
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

gridfs-locks - npm Package Compare versions

Comparing version 1.1.1 to 1.1.2

5

HISTORY.md

@@ -0,1 +1,6 @@

### 1.1.2
- Revert completely to use of MongoDB 2.4.x only update modifiers to maintain compatibility with mongo 2.4.x
- The more efficient MongoDB 2.6 version lives in the mongodb_2.6 branch for now until if can be cleanly included conditionally
### 1.1.1

@@ -2,0 +7,0 @@

306

index.js

@@ -193,20 +193,19 @@ /***********************************************************************

if (self.lockType === 'r') {
query = {files_id: self.fileId, read_locks: {$gt: 0}},
update = {$inc: {read_locks: -1}, $set: {meta: null}};
releaseReadLock(self);
} else if (self.lockType[0] === 'w') {
query = {files_id: self.fileId, write_lock: true},
update = {$set: {write_lock: false, expires: new Date(), meta: null}};
releaseWriteLock(self);
} else {
return emitError(self, "Lock.releaseLock invalid lockType.");
}
return self; // allow chaining
};
var releaseWriteLock = function (self) {
var query = {files_id: self.fileId, write_lock: true},
update = {$set: {write_lock: false, expires: new Date(never), meta: null}};
self.collection.findAndModify(query, [], update, {w: self.lockCollection.writeConcern, new: true}, function (err, doc) {
if (err) { return emitError(self, err); }
var lt = self.lockType;
self.lockType = null;

@@ -219,20 +218,51 @@ self.query = null;

}
self.emit('released', doc);
});
}
// This resets the expire time when there are no locks and this was a release of a read lock
// This prevents a former long expire time from persisting into new read locks unnecessarily.
// There is a small time window between the findAndModify above and the one below where a long expire time
// may be inheritied by a new read lock, but the window should be short enough that this is tolerable
// because the lock->unlock->lock order of operations could just as easily have been lock->lock->unlock
if ((lt === 'r') && doc && (doc.read_locks == 0)) {
query = {files_id: self.fileId, read_locks: 0, write_lock: false};
update = {$set: { expires: new Date() }};
var releaseReadLock = function (self) {
var query = {files_id: self.fileId, read_locks: {$gt: 1}},
update = {$inc: {read_locks: -1}, $set: {meta: null}};
// Case for read_locks > 1
self.collection.findAndModify(query, [], update, {w: self.lockCollection.writeConcern, new: true}, function (err, doc) {
if (err) { return emitError(self, err); }
if (doc) {
self.lockType = null;
self.query = null;
self.update = null;
self.heldLock = null;
return self.emit('released', doc);
} else {
query = {files_id: self.fileId, read_locks: 1};
update = {$set: {read_locks: 0, expires: new Date(never), meta: null}};
// Case for read_locks == 1
self.collection.findAndModify(query, [], update, {w: self.lockCollection.writeConcern, new: true}, function (err, doc) {
if (err) { console.warn("Error returned from expiration time reset on release", err); }
if (err) { return emitError(self, err); }
if (doc) {
self.lockType = null;
self.query = null;
self.update = null;
self.heldLock = null;
return self.emit('released', doc);
} else {
// If another readLock released between the above two findAndModify calls they can both fail... so keep trying.
// console.log("Retrying to release read lock...");
// Avoid an infinite loop when lock document no longer exists
self.collection.findOne({files_id: self.fileId, read_locks: {$gt: 0}}, function (err, doc) {
if (err) { return emitError(self, err); }
if (doc == null) {
return emitError(self, "Lock.releaseLock Valid read Lock document not found in collection. " + JSON.stringify(self.heldLock));
} else {
self.releaseLock();
}
});
}
});
}
self.emit('released', doc);
});
return self; // allow chaining
};

@@ -264,13 +294,25 @@

self.collection.findAndModify({files_id: self.fileId},
self.collection.findAndModify({files_id: self.fileId, expires: { $lt: self.lockExpireTime }},
[],
{$max: {expires: self.lockExpireTime}}, // don't clobber an already extended shared read lock
{$set: {expires: self.lockExpireTime}},
{w: self.lockCollection.writeConcern, new: true},
function (err, doc) {
if (err) { return emitError(self, err); }
if (doc == null) { return emitError(self, "Lock.renewLock document not found in collection"); }
self.heldLock = doc;
self.expiresSoonTimeout = setTimeout(emitExpiresSoonEvent.bind(self, ''), 0.9*(self.lockExpireTime - new Date() - self.pollingInterval));
self.expiredTimeout = setTimeout(emitExpiredEvent.bind(self, ''), (self.lockExpireTime - new Date() - self.pollingInterval));
return self.emit('renewed', doc);
if (doc == null) {
self.collection.findOne({files_id: self.fileId}, function (err, doc) {
if (err) { return emitError(self, err); }
if (doc == null) {
return emitError(self, "Lock.renewLock document not found in collection");
}
self.heldLock = doc;
self.expiresSoonTimeout = setTimeout(emitExpiresSoonEvent.bind(self, ''), 0.9*(self.lockExpireTime - new Date() - self.pollingInterval));
self.expiredTimeout = setTimeout(emitExpiredEvent.bind(self, ''), (self.lockExpireTime - new Date() - self.pollingInterval));
return self.emit('renewed', doc);
});
} else {
self.heldLock = doc;
self.expiresSoonTimeout = setTimeout(emitExpiresSoonEvent.bind(self, ''), 0.9*(self.lockExpireTime - new Date() - self.pollingInterval));
self.expiredTimeout = setTimeout(emitExpiredEvent.bind(self, ''), (self.lockExpireTime - new Date() - self.pollingInterval));
return self.emit('renewed', doc);
}
});

@@ -307,5 +349,8 @@ return self;

self.timeCreated = new Date();
self.lockType = 'r';
self.expired = false;
timeoutReadLockQuery(self);
initializeLockDoc(self, function (err, doc) {
if (err) { return emitError(self, err); }
self.lockType = 'r';
self.expired = false;
timeoutReadLockQuery(self);
});
return self;

@@ -349,5 +394,8 @@ };

self.timeCreated = new Date();
self.lockType = 'w';
self.expired = false;
timeoutWriteLockQuery(self, testingOptions);
initializeLockDoc(self, function (err, doc) {
if (err) { return emitError(self, err); }
self.expired = false;
self.lockType = 'w';
timeoutWriteLockQuery(self, testingOptions);
});
return self;

@@ -364,2 +412,19 @@ };

// Private function that ensures an initialized lock doc is in the database
var initializeLockDoc = function (self, callback) {
self.collection.findAndModify({files_id: self.fileId},
[],
{$setOnInsert: {files_id: self.fileId,
expires: self.timeCreated,
read_locks: 0,
write_lock: false,
write_req: false,
reads: 0,
writes: 0,
meta: null}},
{w: self.lockCollection.writeConcern, upsert: true, new: true},
callback);
};
// Private functions that implement expiration events

@@ -385,53 +450,54 @@

function gotLock(doc) {
self.heldLock = doc;
if (self.lockExpiration) {
self.expiresSoonTimeout = setTimeout(emitExpiresSoonEvent.bind(self),
0.9*(self.lockExpireTime - new Date().getTime() - self.pollingInterval));
self.expiredTimeout = setTimeout(emitExpiredEvent.bind(self),
(self.lockExpireTime - new Date().getTime() - self.pollingInterval));
}
return self.emit('locked', doc);
};
options = options || {};
// Read locks can break write locks with write_req after more than one polling cycle
now = new Date();
self.lockExpireTime = new Date(now.getTime() + (self.lockExpiration || never));
self.lockExpireTime = new Date(new Date().getTime() + (self.lockExpiration || never));
self.query = {files_id: self.fileId,
$or: [{write_lock: false, write_req: false, read_locks: 0},
{write_lock: false, write_req: false, expires: {$lte: self.lockExpireTime}},
{expires: {$lt: new Date(new Date() - 2*self.pollingInterval)}}]};
self.update = {$inc: {read_locks: 1, reads: 1}, $set: {write_lock: false, write_req: false, expires: self.lockExpireTime, meta: self.metaData}};
self.query = { files_id: self.fileId,
$or: [
{ write_lock: false,
write_req: false },
{ write_lock: true,
expires: { $lt: new Date(now - 2*self.pollingInterval) }}
]
};
self.update = { $inc: { read_locks: 1,
reads: 1 },
$set: { write_lock: false,
write_req: false,
meta: self.metaData },
$max: { expires: self.lockExpireTime },
$setOnInsert: {
files_id: self.fileId,
writes: 0 }
};
self.collection.findAndModify(
self.query,
self.collection.findAndModify(self.query,
[],
self.update,
{ w: self.lockCollection.writeConcern, new: true, upsert: true },
{w: self.lockCollection.writeConcern, new: true},
function (err, doc) {
// if (err) { console.log("ERROR:", err); }
//
// XXX: Handle unique index exception when simultaneous upserts collide...
//
if (err && ((err.name !== 'MongoError') || (err.lastErrorObject.code !== 11000))) { return emitError(self, err); }
if (err) { return emitError(self, err); }
if (!doc) {
if (new Date().getTime() - self.timeCreated >= self.timeOut) {
return self.emit('timed-out');
}
return setTimeout(timeoutReadLockQuery, self.pollingInterval, self, options);
// Try again without trying to update the expire time
// console.log("Second try...");
self.lockExpireTime = new Date(new Date().getTime() + (self.lockExpiration || never));
self.query = {files_id: self.fileId, write_lock: false, write_req: false, expires: { $gt: self.lockExpireTime }};
self.update = {$inc: {read_locks: 1, reads: 1}, $set: {meta: self.metaData}};
self.collection.findAndModify(self.query,
[],
self.update,
{w: self.lockCollection.writeConcern, new: true},
function (err, doc) {
if (err) { return emitError(self, err); }
if (!doc) {
if(new Date().getTime() - self.timeCreated >= self.timeOut) {
return self.emit('timed-out');
}
return setTimeout(timeoutReadLockQuery, self.pollingInterval, self, options);
} else {
return gotLock(doc);
}
}
);
} else {
self.heldLock = doc;
if (self.lockExpiration) {
self.expiresSoonTimeout = setTimeout(emitExpiresSoonEvent.bind(self),
0.9*(self.lockExpireTime - new Date().getTime() - self.pollingInterval));
self.expiredTimeout = setTimeout(emitExpiredEvent.bind(self),
(self.lockExpireTime - new Date().getTime() - self.pollingInterval));
}
return self.emit('locked', doc);
return gotLock(doc);
}

@@ -445,38 +511,16 @@ }

var timeoutWriteLockQuery = function (self, options) {
options = options || {};
now = new Date();
self.lockExpireTime = new Date(now.getTime() + (self.lockExpiration || never));
self.query = {files_id: self.fileId,
$or: [{expires: {$lt: new Date()}, write_req: true},
{write_lock: false, read_locks: 0}]};
self.lockExpireTime = new Date(new Date().getTime() + (self.lockExpiration || never));
self.update = {$set: {write_lock: true, write_req: false, read_locks: 0, expires: self.lockExpireTime, meta: self.metaData}, $inc:{writes: 1}};
self.query = { files_id: self.fileId,
$or: [
{ expires: { $lt: now },
write_req: true },
{ write_lock: false,
read_locks: 0 }
]
};
self.update = { $set: { write_lock: true,
write_req: false,
read_locks: 0,
expires: self.lockExpireTime,
meta: self.metaData},
$inc: { writes: 1 },
$setOnInsert: {
files_id: self.fileId,
reads: 0 }
};
self.collection.findAndModify(self.query,
[],
self.update,
{w: self.lockCollection.writeConcern, new: true, upsert: true},
{w: self.lockCollection.writeConcern, new: true},
function (err, doc) {
// if (err) { console.log("ERROR:", err); }
//
// XXX: Handle unique index exception when simultaneous upserts collide...
//
if (err && ((err.name !== 'MongoError') || (err.lastErrorObject.code !== 11000))) { return emitError(self, err); }
if (err) { return emitError(self, err); }
if (doc) {

@@ -491,30 +535,28 @@ self.heldLock = doc;

return self.emit('locked', doc);
} else { // !doc
if (new Date().getTime() - self.timeCreated >= self.timeOut) {
// Clear the write_req flag, since this obtainWriteLock has timed out
self.collection.findAndModify({files_id: self.fileId, write_req: true},
[],
{$set: {write_req: false}},
{w: self.lockCollection.writeConcern, new: true},
function (err, doc) {
if (err) { return emitError(self, err); }
}
);
return self.emit('timed-out');
} else {
// write_req gets set every time because claimed write locks and timed out write requests clear it
self.collection.findAndModify({files_id: self.fileId, write_req: false},
[],
{$set: {write_req: true}},
{new: true},
function (err, doc) {
if (err) { return emitError(self, err); }
self.emit('write-req-set');
});
return setTimeout(timeoutWriteLockQuery, self.pollingInterval, self, options);
}
}
if (new Date().getTime() - self.timeCreated >= self.timeOut) {
// Clear the write_req flag, since this obtainWriteLock has timed out
self.collection.findAndModify({files_id: self.fileId, write_req: true},
[],
{$set: {write_req: false}},
{w: self.lockCollection.writeConcern, new: true},
function (err, doc) {
if (err) { return emitError(self, err); }
}
);
return self.emit('timed-out');
} else {
// write_req gets set every time because claimed write locks and timed out write requests clear it
self.collection.findAndModify({files_id: self.fileId, write_req: false},
[],
{$set: {write_req: true}},
{new: true},
function (err, doc) {
if (err) { return emitError(self, err); }
self.emit('write-req-set');
});
return setTimeout(timeoutWriteLockQuery, self.pollingInterval, self, options);
}
}
);
};
{
"name": "gridfs-locks",
"version": "1.1.1",
"version": "1.1.2",
"description": "Distributed read/write locking based on MongoDB, designed to make GridFS safe for concurrent access",

@@ -8,3 +8,3 @@ "main": "index.js",

"devDependencies": {
"mongodb": ">=1.4.0",
"mongodb": ">=1.4.2",
"coffee-script": "*",

@@ -11,0 +11,0 @@ "mocha": "*"

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc