Latest Code

This commit is contained in:
Dobie Wollert
2015-04-06 03:28:20 -04:00
parent 966152a631
commit d3089dcd17
105 changed files with 8731 additions and 96 deletions

2
node_modules/strong-store-cluster/BUG.txt generated vendored Normal file
View File

@ -0,0 +1,2 @@
A new Client() is created for each 'online' worker... but clients are never
released.

19
node_modules/strong-store-cluster/LICENSE generated vendored Normal file
View File

@ -0,0 +1,19 @@
Copyright (c) 2013 Strongloop, Inc.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

19
node_modules/strong-store-cluster/Makefile generated vendored Normal file
View File

@ -0,0 +1,19 @@
# Makefile
-include local.mk
.PHONY: test default
default: test
test:
@npm test
jenkins-build: jenkins-install jenkins-test
jenkins-install:
npm install
jenkins-test:
./node_modules/.bin/mocha --timeout 5000 --slow 1000 --ui tdd --reporter xunit > xunit.xml

189
node_modules/strong-store-cluster/README.md generated vendored Normal file
View File

@ -0,0 +1,189 @@
# Strong Store Cluster
Strong Store for Cluster provides a key/value collection that can be accesses by
all processes in a node cluster.
## Example
```javascript
// get the collection and give it a name
var collection = require('strong-store-cluster').collection('test');
// don't let keys expire, ever - the number represents seconds
collection.configure({ expireKeys: 0 });
collection.set('ThisIsMyKey', { a: 0, b: 'Hiya', c: { d: 99}}, function(err) {
if (err) {
console.error('There was an error');
return;
}
collection.get('ThisIsMyKey', function(err, obj) {
if (err) {
console.error('There was an error in collection.get.');
return;
}
console.log('The object: ',obj);
});
});
```
## API documentation
### store.collection(name)
Returns a Collection object which lets you share data between node processes.
### Class: Collection
A `Collection` instance provides access to a shared key-value store
shared by multiple node instances.
How collections are named and stored is determined by the storage backend. The
`strong-store-cluster` implementation stores collections in the master process
(if you're using cluster), and accepts any arbitrary string as a collection
name.
A `Collection` object is also an `EventEmitter`.
#### collection.configure([options])
* `options` (`Object`) contains configurations options to be changed
* `expireKeys` (`Number`) seconds after which keys in this
collection are to be expired.
Set configuration options for the collection.
Currently only one configurable option is supported: `expireKeys`. When set
to a nonzero value, keys will automatically expire after they've not been
read or updated for some time. The timeout is specified in seconds. There's no
guarantee that the key will be discared after exactly that number of seconds
has passed. However keys will never be automatically deleted _sooner_ than what
the `expireKeys` setting allows.
It is perfectly legal to call the `configure` method from multiple node
processes (e.g. both in a worker and in the master process). However you
should be careful to set the _same_ option values every time, otherwise the
effect is undefined.
#### collection.get(key, callback)
* `key` (`String`) key to retrieve
* `callback` (`Function`) called when the value has been retrieved
Read the value associated with a particular key. The callback is called with
two arguments, `(err, value)`. When the key wasn't found in the collection, it
is automatically created and it's `value` is set to `undefined`.
#### collection.set(key, [value], [callback])
* `key` (`String`) key to set or update
* `value` (`object`) value to associate with the key
* `callback` (`Function`) called when the value has been retrieved
Set the value associated with `key`. The `value` must be either undefined or
a value that can be serialized with JSON.stringify.
When the `value` parameter is omitted or set to `undefined`, the key is
deleted, so effectively it's the same as calling `collection.del(key)`.
The `callback` function receives only one argument, `err`. When the
callback is omitted, the master process does not send a confirmation
after updating the key, and any errors are silently ignored.
#### collection.del(key, [callback])
* `key` (`String`) key to delete
* `callback` (`Function`) called when the value has been retrieved
Delete a key from the collection.
This operation is the equivalent of setting the key to `undefined`.
The `callback` function receives only one argument, `err`. When the
callback is omitted, the master process does not send a confirmation
after deleting the key, and any errors are silently ignored.
#### collection.acquire(key, callback)
* `key` (`String`) key to delete
* `callback` (`Function`) called when the key has been locked
Lock a key for exclusive read and write access.
The `acquire` methods waits until it can grab an exclusive lock on the
specified key. When the lock is acquired, no other process can read, write or
delete this particular key. When the lock is no longer needed, it should be
relinquished with `keylock.release()`.
Three parameters are passed to the `callback` function:
`(err, keylock, value)`. The `keylock` argument receives a `KeyLock` class
instance, which lets you read and manipulate the key's value as well as
eventually release the lock. The `value` argument is set to the initial value
associated with the key.
#### Event: 'error'
* `err` (`Error`)
The error event is emitted whenever an unrecoverable error is encountered.
### Class: KeyLock
A `KeyLock` instance represents a key that has been locked. The `KeyLock`
class implements methods that lets you manipulate the key and release
the lock.
#### keylock.get()
* Returns: (`Object`) value that's currently associated with the key
This function returns the value that's currently associated with the locked
key.
Initially this is the same as the `value` argument that was passed to the
`collection.acquire()` callback, but it does immediately reflect changes that
are made with `keylock.set()` and `keylock.del()`.
#### keylock.set([value])
Updates the value associated with a locked key.
The change isn't pushed back to the master process immediately; the change
is committed when the lock is released again. The change however is reflected
immediately in the return value from `keylock.get()`.
After the lock has been released, the key can no longer be updated through the
`KeyLock` instance. Any attempt to do so will make it throw.
Setting the value to `undefined` marks the key for deletion, e.g. it's
equivalent to `keylock.del()`.
#### keylock.del()
Mark a locked key for deletion. See `keylock.set()`.
#### keylock.release([callback])
Release the lock that protects a key. If the key was updated with
`keylock.set()` or `keylock.del()`, these changes are committed.
When a lock has been released, it is no longer possible to manipulate the
key using `KeyLock` methods. Releasing the lock twice isn't allowed either.
The `get()` method will still work but it won't reflect any value changes
that were made after releasing.
The `callback` function receives only one argument, `err`. When the
callback is omitted, the master process does not send a confirmation
after releasing the key, and any errors are silently ignored.

6
node_modules/strong-store-cluster/docs.json generated vendored Normal file
View File

@ -0,0 +1,6 @@
{
"title": "Strong Store for Cluster",
"content": [
"README.md"
]
}

18
node_modules/strong-store-cluster/index.js generated vendored Normal file
View File

@ -0,0 +1,18 @@
var assert = require('assert');
var cluster = require('cluster');
var VERSION = require('./package.json').version;
if(cluster._strongStoreCluster) {
assert(
cluster._strongStoreCluster.VERSION === VERSION,
'Multiple versions of strong-strore-cluster are being initialized.\n' +
'This version ' + VERSION + ' is incompatible with already initialized\n' +
'version ' + cluster._strongStoreCluster.VERSION + '.\n'
);
module.exports = cluster._strongStoreCluster;
return;
}
module.exports = require('./lib/lib.js');
module.exports.VERSION = VERSION;
cluster._strongStoreCluster = module.exports;

22
node_modules/strong-store-cluster/lib/collection.js generated vendored Normal file
View File

@ -0,0 +1,22 @@
module.exports = collection;
var hasOwnProperty = Object.prototype.hasOwnProperty;
var cluster = require('cluster');
if (cluster.isMaster)
var Collection = require('./master/Collection.js');
else
var Collection = require('./worker/Collection.js');
var collections = {};
function collection(name) {
if (!hasOwnProperty.call(collections, name))
return collections[name] = new Collection(name);
else
return collections[name];
}

8
node_modules/strong-store-cluster/lib/lib.js generated vendored Normal file
View File

@ -0,0 +1,8 @@
var cluster = require('cluster'),
collection = require('./collection.js');
exports.collection = collection;
if (cluster.isMaster)
require('./master/setup.js');

107
node_modules/strong-store-cluster/lib/master/Client.js generated vendored Normal file
View File

@ -0,0 +1,107 @@
module.exports = Client;
var collection = require('../collection.js');
function Client(worker) {
this._worker = worker;
this._id = worker.id;
this._locks = {};
this._onMessage = this.onMessage.bind(this);
worker.on('message', this._onMessage);
}
Client.prototype.onMessage = function(msg) {
if (msg.type === 'DSM_REQUEST')
this[msg.method](msg);
};
Client.prototype.get = function(msg) {
var self = this,
entry = collection(msg.collection)._entry(msg.key);
entry.get(this._id, function(err, json) {
if (err) self._sendError(msg, err);
else self._sendReply(msg, { json: json });
});
};
Client.prototype.set = function(msg) {
var self = this,
entry = collection(msg.collection)._entry(msg.key);
entry.set(msg.json, this._id, function(err) {
if (err) self._sendError(msg, err);
else self._sendReply(msg);
});
};
Client.prototype.acquire = function(msg) {
var self = this,
entry = collection(msg.collection)._entry(msg.key);
entry.acquire(this._id, function(err, json) {
if (err) self._sendError(msg, err);
else self._sendReply(msg, { json: json });
});
};
Client.prototype.release = function(msg) {
var self = this,
entry = collection(msg.collection)._entry(msg.key);
entry.release(this._id, function(err, json) {
if (err) self._sendError(msg, err);
else self._sendReply(msg);
});
};
Client.prototype.setRelease = function(msg) {
var self = this,
entry = collection(msg.collection)._entry(msg.key);
entry.setRelease(msg.json, this._id, function(err) {
if (err) self._sendError(msg, err);
else self._sendReply(msg);
});
};
Client.prototype.configure = function(msg) {
var coll = collection(msg.collection),
err = undefined;
try {
coll._applyConfig(msg.config);
this._sendReply(msg);
} catch (err) {
this._sendError(msg, err);
}
};
// This function clobbers the data argument if specified!
Client.prototype._sendReply = function(msg, data) {
if (!msg.requestId)
return;
data = data || {};
data.type = 'DSM_REPLY';
data.requestId = msg.requestId;
data.err = undefined;
this._worker.send(data);
};
Client.prototype._sendError = function(msg, err) {
if (!msg.requestId)
return;
var data = {};
data.type = 'DSM_REPLY';
data.requestId = msg.requestId;
data.err = err;
this._worker.send(data);
};

View File

@ -0,0 +1,153 @@
module.exports = Collection;
var hasOwnProperty = Object.prototype.hasOwnProperty;
var assert = require('assert'),
inherits = require('util').inherits,
EventEmitter = require('events').EventEmitter;
var Entry = require('./Entry.js'),
KeyLock = require('./KeyLock.js');
function Collection(name) {
this._name = name;
this._entries = {};
this._count = 0;
this._expireKeys = null;
this._expireKeysTimer = null;
this._sweep = this._sweep.bind(this);
}
inherits(Collection, EventEmitter);
Collection.prototype._entry = function(key) {
if (!hasOwnProperty.call(this._entries, key)) {
if (!this._count++ && this._expireKeys)
this._startExpireKeysTimer();
return this._entries[key] = new Entry(this, key);
} else {
return this._entries[key];
}
};
Collection.prototype._remove = function(key) {
assert(hasOwnProperty.call(this._entries, key));
delete this._entries[key];
if (!--this._count && this.expireKeys)
this._stopExpireKeysTimer();
};
Collection.prototype.get = function(key, cb) {
this._entry(key).get(-1, function(err, json) {
if (err)
cb(err);
else if (!json)
cb(null, undefined);
else
cb(null, JSON.parse(json));
});
};
Collection.prototype.set = function(key, value, cb) {
cb = cb || noop;
this._entry(key).set(JSON.stringify(value), -1, cb);
};
Collection.prototype.del = function(key, cb) {
cb = cb || noop;
this._entry(key).set(undefined, -1, cb);
};
Collection.prototype.acquire = function(key, cb) {
var self = this,
entry = this._entry(key);
entry.acquire(-1, function(err, json) {
var lock = new KeyLock(entry, json);
return cb(null, lock, lock.get());
});
};
Collection.prototype._applyConfig = function(config) {
config = config || {};
for (var key in config) {
if (!hasOwnProperty.call(config, key))
continue;
switch (key) {
case 'expireKeys':
if (config.expireKeys === this._expireKeys)
break;
this._stopExpireKeysTimer();
this._expireKeys = config.expireKeys;
this._startExpireKeysTimer();
break;
default:
throw new Error('Unspported configuration option: ' + key);
}
}
};
Collection.prototype._startExpireKeysTimer = function() {
assert(!this._expireKeysTimer);
if (!this._expireKeys || !this._count)
return;
var interval = Math.ceil(this._expireKeys * 1000 / 2);
this._expireKeysTimer = setInterval(this._sweep, interval);
this._expireKeysTimer.unref();
};
Collection.prototype._stopExpireKeysTimer = function() {
if (!this._expireKeysTimer)
return;
clearInterval(this._expireKeysTimer);
this._expireKeysTimer = null;
};
Collection.prototype._sweep = function() {
var entries = this._entries,
key;
for (key in entries) {
if (!hasOwnProperty.call(entries, key))
continue;
if (entries[key].age(1) > 2)
this._remove(key);
}
if (!this._count)
this._stopExpireKeysTimer();
};
Collection.prototype.configure = function(config) {
var self = this;
try {
this._applyConfig(config);
} catch (err) {
process.nextTick(function() {
self.emit('error', err);
});
}
return this;
};
function noop() {
}

103
node_modules/strong-store-cluster/lib/master/Entry.js generated vendored Normal file
View File

@ -0,0 +1,103 @@
module.exports = Entry;
var assert = require('assert');
function Entry(collection, key) {
this._collection = collection;
this._key = key;
this._value = undefined;
this._queue = [];
this._age = 0;
}
Entry.prototype.get = function(requestor, cb) {
var self = this;
if (!this._queue.length) {
var value = self._value;
self._age = 0;
process.nextTick(function() {
cb(null, value);
});
} else {
this.acquire(requestor, function(err) {
self.release(requestor, noop);
cb(null, self._value);
});
}
};
Entry.prototype.set = function(newValue, requestor, cb) {
var self = this;
if (!this._queue.length) {
this._value = newValue;
this._age = 0;
if (newValue === undefined)
this._collection._remove(this._key);
process.nextTick(cb);
} else {
this.acquire(requestor, function() {
self._value = newValue;
if (newValue !== undefined)
self.release(requestor, cb);
else {
self._collection._remove(self._key);
cb();
}
});
}
};
Entry.prototype.acquire = function(requestor, cb) {
var self = this;
this._queue.push([cb, requestor]);
if (this._queue.length === 1) {
process.nextTick(function() {
cb(null, self._value);
});
}
};
Entry.prototype.release = function(requestor, cb) {
var self = this,
queue = this._queue;
setImmediate(function() {
assert.strictEqual(requestor, queue.shift()[1]);
self._age = 0;
cb();
if (queue.length)
queue[0][0](null, self._value);
});
};
Entry.prototype.setRelease = function(newValue, requestor, cb) {
this._value = newValue;
this.release(requestor, cb);
};
Entry.prototype.age = function(d) {
if (this._queue.length)
return this._age = 0;
else
return this._age += (d || 0);
};
function noop() {
}

View File

@ -0,0 +1,51 @@
module.exports = KeyLock;
function KeyLock(entry, json) {
this._entry = entry;
this._json = json;
this._updated = false;
this._released = false;
}
KeyLock.prototype.get = function() {
if (!this._json)
return undefined;
else
return JSON.parse(this._json);
};
KeyLock.prototype.set = function(newValue) {
if (this._released)
throw new Error("Can't set after releasing a lock.");
this._json = JSON.stringify(newValue);
this._updated = true;
};
KeyLock.prototype.del = function() {
if (this._released)
throw new Error("Can't delete after releasing a lock.");
this._json = undefined;
this._updated = true;
};
KeyLock.prototype.release = function(cb) {
if (this._released)
throw new Error('KeyLock has already been released.');
cb = cb || noop;
this._released = true;
if (!this._updated)
this._entry.release(-1, cb);
else
this._entry.setRelease(this._json, -1, cb);
};
function noop() {
}

10
node_modules/strong-store-cluster/lib/master/setup.js generated vendored Normal file
View File

@ -0,0 +1,10 @@
var cluster = require('cluster'),
Client = require('./Client.js');
for (var i = 0; i < cluster.workers.length; i++)
new Client(cluster.workers[i]);
cluster.on('online', function(worker) {
new Client(worker);
});

View File

@ -0,0 +1,68 @@
module.exports = Collection;
var inherits = require('util').inherits,
EventEmitter = require('events').EventEmitter,
KeyLock = require('./KeyLock.js'),
request = require('./request.js');
function Collection(name) {
this._name = name;
}
inherits(Collection, EventEmitter);
Collection.prototype.get = function(key, cb) {
this._request('get', key, null, function(err, msg) {
if (err)
return cb(err);
else if (!msg.json)
return cb(null, undefined);
else
return cb(null, JSON.parse(msg.json));
});
};
Collection.prototype.set = function(key, value, cb) {
var data = { json: JSON.stringify(value) };
this._request('set', key, data, cb && function(err, msg) {
return cb(err);
});
};
Collection.prototype.del = function(key, cb) {
return this._request('set', key, null, cb && function(err, msg) {
return cb(err);
});
};
Collection.prototype.acquire = function(key, cb) {
var self = this;
this._request('acquire', key, null, function(err, msg) {
if (err)
return cb(err);
var json = msg.json;
var lock = new KeyLock(self, key, json);
cb(null, lock, lock.get());
});
};
Collection.prototype.configure = function(config) {
var self = this;
this._request('configure', null, { config: config }, function(err, msg) {
if (err)
self.emit('error', err);
});
return this;
};
// This function clobbers `data` if specified
Collection.prototype._request = function(method, key, data, cb) {
request(method, this._name, key, data, cb);
};

View File

@ -0,0 +1,56 @@
module.exports = KeyLock;
var request = require('./request.js');
function KeyLock(collection, key, json) {
this._collection = collection;
this._key = key;
this._json = json;
this._updated = false;
this._released = false;
}
KeyLock.prototype.get = function() {
if (!this._json)
return undefined;
else
return JSON.parse(this._json);
};
KeyLock.prototype.set = function(newValue) {
if (this._released)
throw new Error("Can't set after releasing a lock.");
this._json = JSON.stringify(newValue);
this._updated = true;
};
KeyLock.prototype.del = function() {
if (this._released)
throw new Error("Can't delete after releasing a lock.");
this._json = undefined;
this._updated = true;
};
KeyLock.prototype.release = function(cb) {
if (this._released)
throw new Error('KeyLock has already been released.');
this._released = true;
if (!this._updated)
this._collection._request('release', this._key, null, cb && afterRelease);
else
this._collection._request('setRelease',
this._key,
{ json: this._json },
cb && afterRelease);
function afterRelease(err, msg) {
return cb(err);
}
};

View File

@ -0,0 +1,51 @@
module.exports = request;
process.on('message', onMessage);
var requestIdCounter = 0;
var requestCallbacks = {};
// This function clobbers `data` if specified
function request(method, collection, key, data, cb) {
data = data || {};
data.type = 'DSM_REQUEST';
data.method = method;
data.collection = collection;
data.key = key;
if (cb) {
var requestId = getRequestId();
requestCallbacks[requestId] = cb;
data.requestId = requestId;
}
process.send(data);
}
function onMessage(msg) {
if (msg.type !== 'DSM_REPLY')
return;
var requestId = msg.requestId;
var cb = requestCallbacks[requestId];
delete requestCallbacks[requestId];
if (msg.err) {
var err = new Error('Master error: ' + msg.err);
return cb(err);
}
cb(null, msg);
}
function getRequestId() {
return ++requestIdCounter;
}

44
node_modules/strong-store-cluster/package.json generated vendored Normal file

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,17 @@
var helper = require('./helper/cluster-helper');
suite('concurrent increment', function() {
test('master only', function(cb) {
helper.run('do-concurrent-inc', true, 0, 4, cb);
});
test('four workers', function(cb) {
helper.run('do-concurrent-inc', false, 4, 4, cb);
});
test('master and four workers', function(cb) {
helper.run('do-concurrent-inc', true, 4, 4, cb);
});
});

12
node_modules/strong-store-cluster/test/get-set-del.js generated vendored Normal file
View File

@ -0,0 +1,12 @@
var helper = require('./helper/cluster-helper');
suite('get-set-del', function() {
test('master', function(cb) {
helper.run('do-get-set-del', true, 0, 1, cb);
});
test('worker', function(cb) {
helper.run('do-get-set-del', false, 1, 1, cb);
});
});

View File

@ -0,0 +1,83 @@
var assert = require('assert'),
cluster = require('cluster');
if (!process.env.CLUSTER_TEST) {
// We're require()'d by the test harness. Export a function that start
// the test.
exports.run = function(filename, inMaster, workers, concurrency, cb) {
assert(filename);
assert(inMaster || workers);
assert(concurrency > 0);
var env = {};
for (var key in process.env)
env[key] = process.env[key];
env.CLUSTER_TEST = require.resolve('./' + filename);
env.CLUSTER_TEST_IN_MASTER = inMaster;
env.CLUSTER_TEST_WORKERS = workers;
env.CLUSTER_TEST_CONCURRENCY = concurrency;
var cp = require('child_process').fork(module.filename,
{env: env, stdio: 'inherit'});
cp.on('exit', function(exitCode, termSig) {
assert(exitCode === 0);
assert(!termSig);
cb();
});
};
} else {
// We're being spawned as a standalone process. Execute the test and/or spawn
// cluster workers that do.
var filename = process.env.CLUSTER_TEST,
inMaster = !!+process.env.CLUSTER_TEST_IN_MASTER,
workers = ~~ + process.env.CLUSTER_TEST_WORKERS,
concurrency = ~~ + process.env.CLUSTER_TEST_CONCURRENCY;
var test = require(filename);
// Both the master and the worker have .setup() always called once.
test.setup && test.setup();
var waiting = 0;
// If we're the master process, spawn a number of workers.
if (cluster.isMaster && workers) {
for (var i = 0; i < workers; i++)
var worker = cluster.fork();
waiting += workers;
cluster.on('exit', function(worker, exitCode, termSig) {
assert(exitCode === 0);
assert(!termSig);
done();
});
}
// If we're either a worker, or the master is supposed to run the tests,
// run the test cases.
if (cluster.isWorker || inMaster) {
waiting += concurrency;
for (var i = 0; i < concurrency; i++)
test.run(done);
}
}
function done() {
assert(--waiting >= 0);
if (waiting === 0)
return test.teardown && test.teardown();
}

View File

@ -0,0 +1,51 @@
exports.run = run;
exports.teardown = teardown;
var ROUNDS = 100;
assert = require('assert'),
cluster = require('cluster'),
store = require('../..');
function run(cb) {
var left = ROUNDS,
coll = store.collection('counter');
increment();
function increment() {
coll.acquire('counter', function(err, lock, val) {
assert(!err);
if (!val)
val = 1;
else
val++;
lock.set(val);
lock.release();
if (--left > 0)
increment();
else
cb();
});
}
}
function teardown() {
if (cluster.isWorker)
process._channel.unref();
if (cluster.isMaster) {
store.collection('counter').get('counter', function(err, value) {
assert(value % ROUNDS === 0);
assert(value >= ROUNDS);
});
}
}

View File

@ -0,0 +1,60 @@
exports.run = run;
exports.teardown = teardown;
var assert = require('assert'),
cluster = require('cluster'),
store = require('../..');
function run(cb) {
var testsRun = 0;
testWith('test1', 'key1', 'zulis', onDone);
testWith('test2', 'quux', 'stoll', onDone);
testWith('test2', 'key1', 'urals', onDone);
testWith('test2', 'key2', 'quipp', onDone);
function onDone() {
if (++testsRun === 4)
cb();
}
}
function testWith(collectionName, key, testValue, cb) {
var coll = store.collection(collectionName);
coll.get(key, function(err, value) {
assert(!err);
assert(value === undefined);
coll.set(key, testValue, function(err) {
assert(!err);
coll.get(key, function(err, value) {
assert(!err);
assert(value === testValue);
coll.del(key, function(err) {
assert(!err);
coll.get(key, function(err, value) {
assert(!err);
assert(value === undefined);
cb();
});
});
});
});
});
}
function teardown() {
if (cluster.isWorker)
process._channel.unref();
}

View File

@ -0,0 +1,80 @@
exports.run = run;
exports.teardown = teardown;
var assert = require('assert'),
cluster = require('cluster'),
store = require('../..');
function run(cb) {
var testsRun = 0;
testWith('test1', 'key1', {foo: 'zulis'}, onDone);
testWith('test2', 'quux', ['stoll'], onDone);
testWith('test2', 'key1', 'urals', onDone);
testWith('test2', 'key2', 42, onDone);
function onDone() {
if (++testsRun === 4)
cb();
}
}
function testWith(collectionName, key, testValue, cb) {
var coll = store.collection(collectionName);
coll.set(key, testValue, function(err, value) {
assert(!err);
coll.acquire(key, function(err, lock, value) {
assert(!err);
assert.deepEqual(testValue, value);
assert.deepEqual(testValue, lock.get());
// Non-primitive values should be deep-cloned.
if (typeof testValue === 'object') {
assert(testValue !== value);
assert(testValue !== lock.get());
}
lock.set('other');
assert('other' === lock.get());
lock.release(function(err) {
assert(!err);
});
coll.acquire(key, function(err, lock, value) {
assert(!err);
assert('other' === value);
assert('other' === lock.get());
lock.del();
assert(undefined === lock.get());
lock.release(function(err) {
assert(!err);
});
coll.get(key, function(err, value) {
assert(!err);
assert(undefined === value);
// That was it!
cb();
});
});
});
});
}
function teardown() {
if (cluster.isWorker)
process._channel.unref();
}

View File

@ -0,0 +1,12 @@
var helper = require('./helper/cluster-helper');
suite('lock-get-set-del', function() {
test('master', function(cb) {
helper.run('do-lock-get-set-del', true, 0, 1, cb);
});
test('worker', function(cb) {
helper.run('do-lock-get-set-del', false, 1, 1, cb);
});
});