diff --git a/README.md b/README.md index 1b3d8c9..ab2e1e1 100644 --- a/README.md +++ b/README.md @@ -20,206 +20,9 @@ $ npm install machinepack-redis --save ## Quick Start -The example below contains a ready-to-use function useful for obtaining one-off access to a Redis connection. +The example in [examples/basic-usage.js](examples/basic-usage.js) contains a ready-to-use function useful for obtaining one-off access to a Redis connection. Under the covers, in the function's implementation, you can see how to manage the Redis connection lifecycle, as well as how to implement thorough, production-level (anal-retentive) error handling. -```javascript - -/** - * Module dependencies - */ -var Redis = require('machinepack-redis'); - - -/** - * doSomeStuffWithRedis() - * - * An example of a helper function you might write using mp-redis. - * - * @required {String} connectionString - * Connection string to use when connecting to Redis. - * - * @optional {Function} during - * Lifecycle callback to run once the connection is active. Receives connection as first arg, and standard Node cb as second. - * Expected to call `during` when finished (at which point the connection is released). - * - * @optional {Function} onUnexpectedFailure - * Notifier function to run each time an unexpected failure notice is received one way or the other. - * Receives relevant Error as 1st argument. - */ -function doSomeStuffWithRedis(opts, done) { - if (opts.connectionString === undefined) { return done(new Error('`connectionString` is required.')); } - if (opts.during !== undefined & typeof opts.during !== 'function') { - return done(new Error('If provided, `during` must be a function.')); - } - if (opts.onUnexpectedFailure !== undefined & typeof opts.onUnexpectedFailure !== 'function') { - return done(new Error('If provided, `onUnexpectedFailure` must be a function.')); - } - - Redis.createManager({ - connectionString: opts.connectionString, - onUnexpectedFailure: function (err){ - // Use custom notifier function, if one was provided. - if (opts.onUnexpectedFailure) { - opts.onUnexpectedFailure(err); - return; - } - //--• Otherwise, do the default thing (log a warning) - console.warn('WARNING: Redis manager emitted a notice about an unexpected failure. The redis server may have crashed, or become inaccessible. Error details from Redis:', err); - } - }).exec(function (err, report){ - if (err) { - return done(new Error('Could not create manager due to unexpected error: '+ err.stack)); - }//--• No reason to proceed any further. - - var mgr = report.manager; - Redis.getConnection({ - manager: mgr - }).exec(function (err, report){ - if (err) { - return done(new Error('Could not get connection from manager, due to unexpected error: '+err.stack)); - }//--• No reason to proceed any further. - - // Local var for convenience. - var connection = report.connection; - - - console.log('CONNECTED!'); - - // Now do stuff w/ the connection - (opts.during||function noOp(connection, proceed){ - return proceed(); - })(report.connection, function afterwards (err_doingStuff) { - if (err_doingStuff) { - console.log('Unexpected error occurred while doing stuff with this Redis connection. Details: '+err_doingStuff.stack); - console.log('Nonetheless, continuing on to release the connection and destroy the manager....'); - }// >- continue on to attempt to release the connection and destroy the manager. - - // Always release the connection when finished: - Redis.releaseConnection({ - connection: connection - }).exec({ - error: function (err_releaseConnection){ - console.warn(new Error('Could not release Redis connection due to unexpected error: '+err_releaseConnection.stack)); - // ^^Note that we might want to also still attempt to destroy the manager here, even - // though we couldn't release the connection. (However, we don't mess w/ that in this example code.) - - if (err_doingStuff) { return done(err_doingStuff); } - else { - console.warn('Triggering success callback anyway, since everything else seemed to work ok...'); - return done(); - } - }, - success: function (report){ - console.log('Connection released.'); - - // But ALWAYS destroy the connection manager when finished - Redis.destroyManager({manager: mgr}).exec(function (err_destroyMgr){ - if (err_destroyMgr) { - console.warn(new Error('Could not destroy Redis connection manager due to unexpected error: '+ err_destroyMgr.stack)); - - if (err_doingStuff) { return done(err_doingStuff); } - else { - console.warn('Triggering success callback anyway, since everything else seemed to work ok...'); - return done(); - } - }//--• - - console.log('Manager destroyed.'); - - // Now, depending on whether we ran into an error above, finish up accordingly. - if (err_doingStuff) { - // Encountered an error along the way, but at least cleanup worked out ok! - return done(err_doingStuff); - } - else { - // Done. No errors, and we cleaned up everything successfully! - return done(); - } - - }); // - - }// - });// - });// - }); // - }); // -}// - - -// Then e.g. you can do: -doSomeStuffWithRedis({ - connectionString: 'redis://127.0.0.1:6379', - onUnexpectedFailure: function (err){ console.warn('uh oh, looks like our redis might have just gone down:',err); }, - during: function (connection, proceed) { - - // Storing in key `stuff` value `things` - Redis.cacheValue({ - connection: connection, - key: 'stuff', - value: 'things' - }).exec(function (err, report){ - if (err) { - return proceed(new Error('Could not cache value, due to unexpected error. Error details: '+err.stack)); - } - - console.log('stored `stuff` key with `things`'); - - // Get the cached value back - Redis.getCachedValue({ - connection: connection, - key: 'stuff' - }).exec(function (err, report){ - if (err) { - return proceed(new Error('Could not get cached value, due to unexpected error. Error details:', err.stack)); - } - - console.log('stuff `key` contains `%s`', report.value); - - // remove keys. Notice that keys is an array of keys to remove - Redis.destroyCachedValues({ - connection: connection, - keys: ['stuff'] - }).exec(function (err, report){ - if (err) { - return proceed(new Error('Could not get destroy cached values, due to unexpected error. Error details:', err.stack)); - } - - console.log('key `stuff` removed'); - - // Get the cached value back - Redis.getCachedValue({ - connection: connection, - key: 'stuff' - }).exec({ - error: function (err){ - return proceed(new Error('Could not get cached value the 2nd time, due to unexpected error. Error details:', err.stack)); - }, - notFound: function (){ - console.log('As we expected, the `stuff` key was not found this time. Good!'); - return proceed(); - }, - success: function (){ - return proceed(new Error('Consistency violation: Should not have been able to find `stuff` key the 2nd time!!! Must be a bug in our code.')); - } - }); // - - }); // - }); // - }); // - }// - -}, function afterwards(err) { - if (err) { - console.log('Attempted to do some stuff with redis, but encountered an error along the way:',err.stack); - return; - }//--• - - console.log('Successfully did some stuff with Redis!'); -}); -``` - - > ##### Setup instructions for the example above > > First, if your Redis server is not running yet, open a new terminal window and do: @@ -227,14 +30,8 @@ doSomeStuffWithRedis({ > redis-server > ``` > -> Next, copy the example code below to a new `.js` file somewhere in your project (e.g. `examples/basic-usage.js`). -> Then run: -> ```bash -> npm install machinepack-redis --save --save-exact -> ``` -> +> Asuming that you cloned the module, you can just run the example: > -> Finally, run the example: > ```bash > node examples/basic-usage.js > ``` @@ -255,5 +52,5 @@ Learn more at - // ┌─┐┌─┐┌┬┐┌─┐┬─┐ // ├─┤├┤ │ ├┤ ├┬┘ // ┴ ┴└ ┴ └─┘┴└─ooo - // Afterwards, destroy the keys that were set, and then also destroy the manager + // Afterwards, flush the cache, and then also destroy the manager // (which automatically releases any connections). after(function (done){ - Pack.destroyCachedValues({ - connection: connection, - keys: keysUsed - }).exec(function (err){ - // If there is an error deleting keys, log it but don't stop - // (we need to be sure and destroy the manager) - if (err) { - console.error('ERROR: Could not destroy keys in test cleanup. Details:\n', err); - } - Pack.destroyManager({ - manager: manager - }).exec(done); - }); + if (connection) { + Pack.flushCache({ + connection: connection + }).exec(function (err){ + // If there is an error flushing the cache, log it but don't stop + // (we need to be sure and destroy the manager) + if (err) { + console.error('ERROR: Could not flush the cache in test cleanup. Details:\n', err); + } + Pack.destroyManager({ + manager: manager + }).exec(done); + }); + } else { + done(); + } }); // diff --git a/tests/destroy-cached-values.test.js b/tests/destroy-cached-values.test.js index 2aaf9bd..5417d32 100644 --- a/tests/destroy-cached-values.test.js +++ b/tests/destroy-cached-values.test.js @@ -5,7 +5,6 @@ var Pack = require('../'); - /** * Note: These tests should ideally not be redis-specific. * (that way we can reuse them for any driver implementing the "cache" interface layer) @@ -19,7 +18,7 @@ describe('destroyCachedValues()', function (){ // The keys to use during tests. - var keysUsed = ['machinepack-redis.test1', 'machinepack-redis.test2', 'machinepack-redis.test3', 'machinepack-redis.test4', 'machinepack-redis.test5', 'machinepack-redis.test6', 'machinepack-redis.test7', 'machinepack-redis.test8']; + var keysUsed = ['machinepack-cache.test1', 'machinepack-cache.test2', 'machinepack-cache.test3', 'machinepack-cache.test4', 'machinepack-cache.test5', 'machinepack-cache.test6', 'machinepack-cache.test7', 'machinepack-cache.test8']; // ┌┐ ┌─┐┌─┐┌─┐┬─┐┌─┐ @@ -30,7 +29,8 @@ describe('destroyCachedValues()', function (){ // connection from it. before(function (done){ Pack.createManager({ - connectionString: 'redis://127.0.0.1:6379', + // 15 = non standard database for the unit tests + connectionString: 'redis://127.0.0.1:6379/15', meta: { auth_pass: 'qwer1234' // use alternative option } @@ -43,15 +43,22 @@ describe('destroyCachedValues()', function (){ Pack.getConnection({ manager: manager }).exec({ - error: done, + error: function (err){ + done(new Error(JSON.stringify(err))); + }, + failed: function (err){ + done(new Error(JSON.stringify(err))); + }, success: function (report){ // Save reference to connection. connection = report.connection; // Now delete keys just to be safe. - Pack.destroyCachedValues({ + Pack.flushCache({ connection: connection, - keys: keysUsed + meta: { + db: 15 // non standard database for the unit tests + } }).exec(done); } }); @@ -60,7 +67,6 @@ describe('destroyCachedValues()', function (){ }); // - // ╔╗ ╔═╗╔═╗╦╔═╗ ╦ ╦╔═╗╔═╗╔═╗╔═╗ // ╠╩╗╠═╣╚═╗║║ ║ ║╚═╗╠═╣║ ╦║╣ // ╚═╝╩ ╩╚═╝╩╚═╝ ╚═╝╚═╝╩ ╩╚═╝╚═╝ @@ -148,10 +154,18 @@ describe('destroyCachedValues()', function (){ error: function (err){ return done(); }, - invalidKeys: function (report) { return done(new Error('Expecting `error` exit')); }, - failed: function (report) { return done(new Error('Expecting `error` exit')); }, - badConnection: function (report) { return done(new Error('Expecting `error` exit')); }, - success: function (report){ return done(new Error('Expecting `error` exit')); } + invalidKeys: function (report){ + return done(new Error('Expecting `error` exit')); + }, + failed: function (report){ + return done(new Error('Expecting `error` exit')); + }, + badConnection: function (report){ + return done(new Error('Expecting `error` exit')); + }, + success: function (report){ + return done(new Error('Expecting `error` exit')); + } }); @@ -166,10 +180,18 @@ describe('destroyCachedValues()', function (){ error: function (err){ return done(); }, - invalidKeys: function (report) { return done(new Error('Expecting `error` exit')); }, - failed: function (report) { return done(new Error('Expecting `error` exit')); }, - badConnection: function (report) { return done(new Error('Expecting `error` exit')); }, - success: function (report){ return done(new Error('Expecting `error` exit')); } + invalidKeys: function (report){ + return done(new Error('Expecting `error` exit')); + }, + failed: function (report){ + return done(new Error('Expecting `error` exit')); + }, + badConnection: function (report){ + return done(new Error('Expecting `error` exit')); + }, + success: function (report){ + return done(new Error('Expecting `error` exit')); + } }); });// @@ -183,10 +205,18 @@ describe('destroyCachedValues()', function (){ error: function (err){ return done(); }, - invalidKeys: function (report) { return done(new Error('Expecting `error` exit')); }, - failed: function (report) { return done(new Error('Expecting `error` exit')); }, - badConnection: function (report) { return done(new Error('Expecting `error` exit')); }, - success: function (report){ return done(new Error('Expecting `error` exit')); } + invalidKeys: function (report){ + return done(new Error('Expecting `error` exit')); + }, + failed: function (report){ + return done(new Error('Expecting `error` exit')); + }, + badConnection: function (report){ + return done(new Error('Expecting `error` exit')); + }, + success: function (report){ + return done(new Error('Expecting `error` exit')); + } }); });// @@ -200,19 +230,22 @@ describe('destroyCachedValues()', function (){ // Afterwards, destroy the keys that were set, and then also destroy the manager // (which automatically releases any connections). after(function (done){ - Pack.destroyCachedValues({ - connection: connection, - keys: keysUsed - }).exec(function (err){ - // If there is an error deleting keys, log it but don't stop - // (we need to be sure and destroy the manager) - if (err) { - console.error('ERROR: Could not destroy keys in test cleanup. Details:\n', err); - } - Pack.destroyManager({ - manager: manager - }).exec(done); - }); - });// + if (connection) { + Pack.flushCache({ + connection: connection + }).exec(function (err){ + // If there is an error deleting keys, log it but don't stop + // (we need to be sure and destroy the manager) + if (err) { + console.error('ERROR: Could not destroy keys in test cleanup. Details:\n', err); + } + Pack.destroyManager({ + manager: manager + }).exec(done); + }); + } else { + done(); + } + }); // }); diff --git a/tests/flush-cache.test.js b/tests/flush-cache.test.js new file mode 100644 index 0000000..7546cdf --- /dev/null +++ b/tests/flush-cache.test.js @@ -0,0 +1,90 @@ +/** + * Module dependencies + */ + +var Pack = require('../'); +var shouldProperlyStoreValue = require('./helpers/should-properly-store-value.test-helper'); + +/** + * Note: These tests should ideally not be redis-specific. + * (that way we can reuse them for any driver implementing the "cache" interface layer) + */ + +describe('flushCache()', function (){ + + // Used to hold manager and active connection throughout the tests below. + var manager; + var connection; + + // _ _ + // | | (_) + // _ __ ___ ___ ___ _ __ _ __ ___ ___| |_ _ ___ _ __ + //| '_ \ / _ \ / __/ _ \| '_ \| '_ \ / _ \/ __| __| |/ _ \| '_ \ + //| | | | (_) | | (_| (_) | | | | | | | __/ (__| |_| | (_) | | | | + //|_| |_|\___/ \___\___/|_| |_|_| |_|\___|\___|\__|_|\___/|_| |_| + // + describe('with no connection', function (){ + it('should fail', function (done){ + Pack.flushCache({ + connection: {} + }).exec({ + error: done, + badConnection: function (){ + return done(); + }, + success: function (){ + return done(new Error('Expecting `badConnection` exit')); + } + }); + }); + }); + + + // ╔╗ ╔═╗╔═╗╦╔═╗ ╦ ╦╔═╗╔═╗╔═╗╔═╗ + // ╠╩╗╠═╣╚═╗║║ ║ ║╚═╗╠═╣║ ╦║╣ + // ╚═╝╩ ╩╚═╝╩╚═╝ ╚═╝╚═╝╩ ╩╚═╝╚═╝ + describe('with connection', function (){ + + it('should work', function (done){ + Pack.createManager({ + // 15 = non standard database for the unit tests + connectionString: 'redis://127.0.0.1:6379/15', + meta: { + password: 'qwer1234' + } + }).exec({ + error: function (err){ + done(new Error(JSON.stringify(err))); + }, + success: function (report){ + // Save reference to manager. + manager = report.manager; + Pack.getConnection({ + manager: manager + }).exec({ + error: function (err){ + done(new Error(JSON.stringify(err))); + }, + failed: function (err){ + done(new Error(JSON.stringify(err))); + }, + success: function (report){ + // Save reference to connection. + connection = report.connection; + Pack.flushCache({ + connection: connection, + meta: { + db: 15 // non standard database for the unit tests + } + }).exec(done); + } + }); + } + }); + }); // + + }); // + +}); + + diff --git a/tests/get-cached-value.test.js b/tests/get-cached-value.test.js index d3c6ae8..a56b870 100644 --- a/tests/get-cached-value.test.js +++ b/tests/get-cached-value.test.js @@ -64,7 +64,8 @@ describe('getCachedValue()', function (){ // connection from it. Also delete the specified keys, just to be safe. before(function (done){ Pack.createManager({ - connectionString: 'redis://127.0.0.1:6379', + // 15 = non standard database for the unit tests + connectionString: 'redis://127.0.0.1:6379/15', meta: { password: 'qwer1234' } @@ -77,15 +78,19 @@ describe('getCachedValue()', function (){ Pack.getConnection({ manager: manager }).exec({ - error: done, + error: function (err){ + done(new Error(JSON.stringify(err))); + }, + failed: function (err){ + done(new Error(JSON.stringify(err))); + }, success: function (report){ // Save reference to connection. connection = report.connection; // Now delete keys just to be safe. - Pack.destroyCachedValues({ - connection: connection, - keys: keysUsed + Pack.flushCache({ + connection: connection }).exec(done); } }); @@ -154,27 +159,29 @@ describe('getCachedValue()', function (){ });// - // ┌─┐┌─┐┌┬┐┌─┐┬─┐ // ├─┤├┤ │ ├┤ ├┬┘ // ┴ ┴└ ┴ └─┘┴└─ooo // Afterwards, destroy the keys that were set, and then also destroy the manager // (which automatically releases any connections). after(function (done){ - Pack.destroyCachedValues({ - connection: connection, - keys: keysUsed - }).exec(function (err){ - // If there is an error deleting keys, log it but don't stop - // (we need to be sure and destroy the manager) - if (err) { - console.error('ERROR: Could not destroy keys in test cleanup. Details:\n', err); - } - Pack.destroyManager({ - manager: manager - }).exec(done); - }); - });// + if (connection) { + Pack.flushCache({ + connection: connection + }).exec(function (err){ + // If there is an error deleting keys, log it but don't stop + // (we need to be sure and destroy the manager) + if (err) { + console.error('ERROR: Could not destroy keys in test cleanup. Details:\n', err); + } + Pack.destroyManager({ + manager: manager + }).exec(done); + }); + } else { + done(); + } + }); // });// diff --git a/tests/get-connection.test.js b/tests/get-connection.test.js index 9db80c5..3b154ec 100644 --- a/tests/get-connection.test.js +++ b/tests/get-connection.test.js @@ -20,7 +20,8 @@ describe('getConnection()', function (){ it('should connect with a password', function (done){ Pack.createManager({ - connectionString: 'redis://127.0.0.1:6379' + // 15 = non standard database for the unit tests + connectionString: 'redis://127.0.0.1:6379/15' }).exec({ error: done, success: function (result){ @@ -29,7 +30,12 @@ describe('getConnection()', function (){ Pack.getConnection({ manager: manager }).exec({ - error: done, + error: function (err){ + done(new Error(JSON.stringify(err))); + }, + failed: function (err){ + done(new Error(JSON.stringify(err))); + }, success: function (){ done(); } @@ -40,7 +46,8 @@ describe('getConnection()', function (){ it('should fail to connect to an invalid port', function (done){ Pack.createManager({ - connectionString: 'redis://127.0.0.1:9999', + // 15 = non standard database for the unit tests + connectionString: 'redis://127.0.0.1:9999/15', meta: { connect_timeout: 1000, retry_strategy: function (){ diff --git a/tests/helpers/should-properly-store-value.test-helper.js b/tests/helpers/should-properly-store-value.test-helper.js index ee4e6b5..9ca98ed 100644 --- a/tests/helpers/should-properly-store-value.test-helper.js +++ b/tests/helpers/should-properly-store-value.test-helper.js @@ -19,7 +19,7 @@ var Pack = require('../../'); * @param {Function} done */ -module.exports = function shouldProperlyStoreValue (opts, done){ +module.exports = function shouldProperlyStoreValue(opts, done){ Pack.cacheValue({ connection: opts.connection, key: opts.key,