AJ ONeal
8 years ago
commit
f07410bc14
9 changed files with 395 additions and 0 deletions
@ -0,0 +1,37 @@ |
|||
# Logs |
|||
logs |
|||
*.log |
|||
npm-debug.log* |
|||
|
|||
# Runtime data |
|||
pids |
|||
*.pid |
|||
*.seed |
|||
|
|||
# Directory for instrumented libs generated by jscoverage/JSCover |
|||
lib-cov |
|||
|
|||
# Coverage directory used by tools like istanbul |
|||
coverage |
|||
|
|||
# nyc test coverage |
|||
.nyc_output |
|||
|
|||
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) |
|||
.grunt |
|||
|
|||
# node-waf configuration |
|||
.lock-wscript |
|||
|
|||
# Compiled binary addons (http://nodejs.org/api/addons.html) |
|||
build/Release |
|||
|
|||
# Dependency directories |
|||
node_modules |
|||
jspm_packages |
|||
|
|||
# Optional npm cache directory |
|||
.npm |
|||
|
|||
# Optional REPL history |
|||
.node_repl_history |
@ -0,0 +1,21 @@ |
|||
MIT License |
|||
|
|||
Copyright (c) 2016 AJ ONeal |
|||
|
|||
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. |
@ -0,0 +1,148 @@ |
|||
cluster-store |
|||
============= |
|||
|
|||
Makes any storage strategy similar to `express/session` useful in both `cluster` and non-`cluster` environments |
|||
by wrapping it with `cluster-rpc`. |
|||
|
|||
Also works with **level-session-store** (leveldb), **connect-session-knex** (SQLite3), **session-file-store** (fs), |
|||
and any other embedded / in-process store. |
|||
|
|||
Note: Most people would probably prefer to just use Redis rather than wrap a dumb memstore as a service... |
|||
but I am not most people. |
|||
|
|||
Install |
|||
======= |
|||
|
|||
``` |
|||
npm install --save memstore-cluster@2.x |
|||
``` |
|||
|
|||
v1.x vs v2.x |
|||
------------ |
|||
|
|||
The old v1 used `ws` which makes it usable when clustering node processes without using `cluster`. |
|||
|
|||
If you need that functionaliy, use it. |
|||
|
|||
Usage |
|||
===== |
|||
|
|||
### standalone (non-cluster) |
|||
-------------- |
|||
|
|||
The usage is exactly the same as **master**, no changes necessary. |
|||
|
|||
### master |
|||
|
|||
In the **master**/**standalone** process you will create the real store instance |
|||
and then in the you must pass each worker via `addWorker()` so that it signals |
|||
the worker to create its own rpc-wrapped instance. |
|||
|
|||
```javascript |
|||
'use strict'; |
|||
|
|||
var cluster = require('cluster'); |
|||
|
|||
var cstore = require('cluster-store/master').create({ |
|||
name: 'foo-store' |
|||
}); |
|||
|
|||
cstore.addWorker(cluster.fork()); |
|||
|
|||
cstore.then(function (store) { |
|||
store.set('foo', 'bar'); |
|||
}); |
|||
``` |
|||
|
|||
### worker |
|||
|
|||
```javascript |
|||
'use strict'; |
|||
|
|||
// retrieve the instance |
|||
var cstore = require('cluster-store/worker').create({ |
|||
name: 'foo-store' |
|||
}); |
|||
|
|||
cstore.then(function (store) { |
|||
store.get('foo', function (err, result) { |
|||
console.log(result); |
|||
}); |
|||
}); |
|||
``` |
|||
|
|||
API |
|||
=== |
|||
|
|||
This is modeled after Express' |
|||
[Session Store Implementation](https://github.com/expressjs/session#session-store-implementation) |
|||
|
|||
**Note**: These are only exposed if the underlying store supports them. |
|||
|
|||
CRUD methods |
|||
------------ |
|||
|
|||
* `store.set(id, data, fn) => (error)` |
|||
* `store.get(id, fn) => (error, data)` |
|||
* `store.touch(id, data, fn) => (error)` |
|||
* `store.destroy(id, fn) => (error)` |
|||
|
|||
Helpers |
|||
------- |
|||
|
|||
* `store.all(fn) => (error, array)` |
|||
* `store.clear(fn) => (error)` |
|||
* `store.length(fn) => (error, length)` |
|||
|
|||
See <https://github.com/expressjs/session#session-store-implementation>@4.x for full details |
|||
|
|||
Example |
|||
======= |
|||
|
|||
```javascript |
|||
'use strict'; |
|||
|
|||
var cluster = require('cluster'); |
|||
var cstore; |
|||
|
|||
|
|||
if (cluster.isMaster) { |
|||
|
|||
|
|||
cstore = require('./master').create({ |
|||
name: 'foo-level' |
|||
}); |
|||
cstore.addWorker(cluster.fork()); |
|||
cstore.then(function (store) { |
|||
store.put('foo', 'bar'); |
|||
}); |
|||
|
|||
|
|||
} |
|||
else { |
|||
|
|||
|
|||
cstore = require('./worker').create({ |
|||
name: 'foo-level' |
|||
}); |
|||
|
|||
|
|||
} |
|||
|
|||
|
|||
cstore.then(function (store) { |
|||
setTimeout(function () { |
|||
store.get('foo', function (err, result) { |
|||
console.log(cluster.isMaster && '0' || cluster.worker.id.toString(), "store.get('foo')", result); |
|||
|
|||
if (!cluster.isMaster) { |
|||
process.exit(0); |
|||
} |
|||
}); |
|||
}, 250); |
|||
}); |
|||
|
|||
process.on('unhandledRejection', function (err) { |
|||
console.log('unhandledRejection', err); |
|||
}); |
|||
``` |
@ -0,0 +1,12 @@ |
|||
'use strict'; |
|||
|
|||
console.error(""); |
|||
console.error("One does not simply require('memstore-cluster');"); |
|||
console.error(""); |
|||
console.error("Usage:"); |
|||
console.error("\trequire('memstore-cluster/master').create({ name: ... });"); |
|||
console.error("\trequire('memstore-cluster/worker').create({ name: ... });"); |
|||
console.error(""); |
|||
console.error(""); |
|||
|
|||
process.exit(1); |
@ -0,0 +1,18 @@ |
|||
'use strict'; |
|||
|
|||
module.exports.create = function (opts) { |
|||
opts = opts || {}; |
|||
|
|||
var db = require('./memstore').create(); |
|||
|
|||
return require('cluster-rpc/master').create({ |
|||
instance: opts.store || db |
|||
, methods: [ |
|||
'set', 'get', 'touch', 'destroy' |
|||
, 'all', 'length', 'clear' |
|||
, 'on', 'off', 'removeEventListener', 'addEventListener' |
|||
] |
|||
, name: 'memstore.' + (opts.name || '') |
|||
, master: opts.master |
|||
}); |
|||
}; |
@ -0,0 +1,73 @@ |
|||
'use strict'; |
|||
|
|||
/*global Promise*/ |
|||
var defer; |
|||
|
|||
if ('function' === typeof setImmediate) { |
|||
defer = setImmediate; |
|||
} else { |
|||
defer = function (fn) { process.nextTick(fn.bind.apply(fn, arguments)); }; |
|||
} |
|||
|
|||
function create(opts) { |
|||
opts = opts || {}; |
|||
// don't leak prototypes as implicitly non-null
|
|||
var db = Object.create(null); |
|||
|
|||
function log() { |
|||
if (!opts.debug) { |
|||
return; |
|||
} |
|||
|
|||
console.log.apply(console, arguments); |
|||
} |
|||
|
|||
return { |
|||
// required / recommended
|
|||
set: function (id, data, fn) { |
|||
log('set(id, data)', id, data); |
|||
db[id] = data; |
|||
|
|||
if (!fn) { return; } |
|||
defer(fn, null); |
|||
} |
|||
, get: function (id, fn) { |
|||
log('get(id)', id); |
|||
if (!fn) { return; } |
|||
defer(fn, null, 'undefined' === typeof db[id] ? null : db[id]); |
|||
} |
|||
, touch: function (id, data, fn) { |
|||
db[id] = data; |
|||
|
|||
if (!fn) { return; } |
|||
defer(fn, null); |
|||
} |
|||
, destroy: function (id, fn) { |
|||
log('destroy(id)', id); |
|||
delete db[id]; |
|||
|
|||
if (!fn) { return; } |
|||
defer(fn, null); |
|||
} |
|||
// optional
|
|||
, all: function (fn) { |
|||
if (!fn) { return; } |
|||
defer(fn, null, Object.keys(db).map(function (key) { |
|||
return db[key]; |
|||
})); |
|||
} |
|||
, length: function (fn) { |
|||
if (!fn) { return; } |
|||
defer(fn, null, Object.keys(db).length); |
|||
} |
|||
, clear: function (fn) { |
|||
log('clear()', id); |
|||
db = Object.create(null); |
|||
|
|||
if (!fn) { return; } |
|||
defer(fn, null); |
|||
} |
|||
}; |
|||
} |
|||
|
|||
module.exports.create = create; |
@ -0,0 +1,32 @@ |
|||
{ |
|||
"name": "cluster-store", |
|||
"version": "2.0.0", |
|||
"description": "A wrapper to enable the use of any in-process store with node cluster via cluster process and worker messages (i.e. for Raspberry Pi servers).", |
|||
"main": "index.js", |
|||
"scripts": { |
|||
"test": "node test-cluster.js", |
|||
"start": "node server.js" |
|||
}, |
|||
"repository": { |
|||
"type": "git", |
|||
"url": "git+https://github.com/coolaj86/cluster-store.git" |
|||
}, |
|||
"keywords": [ |
|||
"store", |
|||
"session", |
|||
"connect", |
|||
"express", |
|||
"memstore", |
|||
"cluster", |
|||
"rpi2" |
|||
], |
|||
"author": "AJ ONeal <coolaj86@gmail.com> (http://coolaj86.com/)", |
|||
"license": "Apache-2.0", |
|||
"bugs": { |
|||
"url": "https://github.com/coolaj86/cluster-store/issues" |
|||
}, |
|||
"homepage": "https://github.com/coolaj86/cluster-store#readme", |
|||
"dependencies": { |
|||
"cluster-rpc": "^1.0.3" |
|||
} |
|||
} |
@ -0,0 +1,46 @@ |
|||
'use strict'; |
|||
|
|||
var cluster = require('cluster'); |
|||
var cstore; |
|||
//global.Promise = require('bluebird');
|
|||
|
|||
|
|||
if (cluster.isMaster) { |
|||
|
|||
|
|||
cstore = require('./master').create({ |
|||
name: 'foo-level' |
|||
}); |
|||
cstore.addWorker(cluster.fork()); |
|||
cstore.then(function (db) { |
|||
db.set('foo', 'bar'); |
|||
}); |
|||
|
|||
|
|||
} |
|||
else { |
|||
|
|||
|
|||
cstore = require('./worker').create({ |
|||
name: 'foo-level' |
|||
}); |
|||
|
|||
|
|||
} |
|||
|
|||
|
|||
cstore.then(function (db) { |
|||
setTimeout(function () { |
|||
db.get('foo', function (err, result) { |
|||
console.log(cluster.isMaster && '0' || cluster.worker.id.toString(), "db.get('foo')", result); |
|||
|
|||
if (!cluster.isMaster) { |
|||
process.exit(0); |
|||
} |
|||
}); |
|||
}, 250); |
|||
}); |
|||
|
|||
process.on('unhandledRejection', function (err) { |
|||
console.log('unhandledRejection', err); |
|||
}); |
@ -0,0 +1,8 @@ |
|||
'use strict'; |
|||
|
|||
module.exports.create = function (opts) { |
|||
return require('cluster-rpc/worker').create({ |
|||
name: 'memstore.' + (opts.name || '') |
|||
, worker: opts.worker |
|||
}); |
|||
}; |
Loading…
Reference in new issue