mirror of
https://github.com/atlanticbiomedical/biomedjs.git
synced 2025-07-02 00:47:26 -04:00
Latest Code
This commit is contained in:
2
node_modules/strong-store-cluster/BUG.txt
generated
vendored
Normal file
2
node_modules/strong-store-cluster/BUG.txt
generated
vendored
Normal 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
19
node_modules/strong-store-cluster/LICENSE
generated
vendored
Normal 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
19
node_modules/strong-store-cluster/Makefile
generated
vendored
Normal 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
189
node_modules/strong-store-cluster/README.md
generated
vendored
Normal 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
6
node_modules/strong-store-cluster/docs.json
generated
vendored
Normal 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
18
node_modules/strong-store-cluster/index.js
generated
vendored
Normal 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
22
node_modules/strong-store-cluster/lib/collection.js
generated
vendored
Normal 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
8
node_modules/strong-store-cluster/lib/lib.js
generated
vendored
Normal 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
107
node_modules/strong-store-cluster/lib/master/Client.js
generated
vendored
Normal 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);
|
||||
};
|
153
node_modules/strong-store-cluster/lib/master/Collection.js
generated
vendored
Normal file
153
node_modules/strong-store-cluster/lib/master/Collection.js
generated
vendored
Normal 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
103
node_modules/strong-store-cluster/lib/master/Entry.js
generated
vendored
Normal 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() {
|
||||
}
|
51
node_modules/strong-store-cluster/lib/master/KeyLock.js
generated
vendored
Normal file
51
node_modules/strong-store-cluster/lib/master/KeyLock.js
generated
vendored
Normal 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
10
node_modules/strong-store-cluster/lib/master/setup.js
generated
vendored
Normal 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);
|
||||
});
|
68
node_modules/strong-store-cluster/lib/worker/Collection.js
generated
vendored
Normal file
68
node_modules/strong-store-cluster/lib/worker/Collection.js
generated
vendored
Normal 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);
|
||||
};
|
56
node_modules/strong-store-cluster/lib/worker/KeyLock.js
generated
vendored
Normal file
56
node_modules/strong-store-cluster/lib/worker/KeyLock.js
generated
vendored
Normal 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);
|
||||
}
|
||||
};
|
51
node_modules/strong-store-cluster/lib/worker/request.js
generated
vendored
Normal file
51
node_modules/strong-store-cluster/lib/worker/request.js
generated
vendored
Normal 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
44
node_modules/strong-store-cluster/package.json
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
17
node_modules/strong-store-cluster/test/concurrent-inc.js
generated
vendored
Normal file
17
node_modules/strong-store-cluster/test/concurrent-inc.js
generated
vendored
Normal 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
12
node_modules/strong-store-cluster/test/get-set-del.js
generated
vendored
Normal 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);
|
||||
});
|
||||
});
|
83
node_modules/strong-store-cluster/test/helper/cluster-helper.js
generated
vendored
Normal file
83
node_modules/strong-store-cluster/test/helper/cluster-helper.js
generated
vendored
Normal 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();
|
||||
}
|
||||
|
51
node_modules/strong-store-cluster/test/helper/do-concurrent-inc.js
generated
vendored
Normal file
51
node_modules/strong-store-cluster/test/helper/do-concurrent-inc.js
generated
vendored
Normal 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);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
60
node_modules/strong-store-cluster/test/helper/do-get-set-del.js
generated
vendored
Normal file
60
node_modules/strong-store-cluster/test/helper/do-get-set-del.js
generated
vendored
Normal 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();
|
||||
}
|
80
node_modules/strong-store-cluster/test/helper/do-lock-get-set-del.js
generated
vendored
Normal file
80
node_modules/strong-store-cluster/test/helper/do-lock-get-set-del.js
generated
vendored
Normal 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();
|
||||
}
|
12
node_modules/strong-store-cluster/test/lock-get-set-del.js
generated
vendored
Normal file
12
node_modules/strong-store-cluster/test/lock-get-set-del.js
generated
vendored
Normal 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);
|
||||
});
|
||||
});
|
Reference in New Issue
Block a user