diff --git a/lib/caching.js b/lib/caching.js index a956e30..9a18101 100644 --- a/lib/caching.js +++ b/lib/caching.js @@ -1,4 +1,6 @@ /*jshint maxcomplexity:15*/ +var domain = require('domain'); + var caching = function (args) { args = args || {}; var self = {}; @@ -40,38 +42,43 @@ var caching = function (args) { ttl = undefined; } + if (self.queues[key]) { + self.queues[key].push({cb: cb, domain: process.domain}); + return; + } + + self.queues[key] = [{cb: cb, domain: process.domain}]; + + function fillCallbacks(err, data) { + self.queues[key].forEach(function(task) { + var taskDomain = task.domain || domain.create(); + taskDomain.bind(task.cb)(err, data); + }); + delete self.queues[key]; + } + self.store.get(key, function (err, result) { if (err && (!self.ignoreCacheErrors)) { - cb(err); + fillCallbacks(err); } else if (result) { - cb.call(cb, null, result); - } else if (self.queues[key]) { - self.queues[key].push(cb); + fillCallbacks(null, result); } else { - self.queues[key] = [cb]; - - work(function () { - var work_args = Array.prototype.slice.call(arguments, 0); - if (work_args[0]) { // assume first arg is an error - self.queues[key].forEach(function (done) { - done.call(null, work_args[0]); - }); - delete self.queues[key]; + domain + .create() + .on('error', function(err) { + fillCallbacks(err); + }) + .bind(work)(function (err, data) { + if (err) { + fillCallbacks(err); return; } - // Subsequently assume second arg is result. - self.store.set(key, work_args[1], ttl, function (err) { + self.store.set(key, data, ttl, function (err) { if (err && (!self.ignoreCacheErrors)) { - self.queues[key].forEach(function (done) { - done.call(null, err); - }); + fillCallbacks(err); } else { - self.queues[key].forEach(function (done) { - done.apply(null, work_args); - }); + fillCallbacks(null, data); } - - delete self.queues[key]; }); }); } diff --git a/lib/multi_caching.js b/lib/multi_caching.js index 2dc52e7..3c638bc 100644 --- a/lib/multi_caching.js +++ b/lib/multi_caching.js @@ -1,4 +1,5 @@ var async = require('async'); +var domain = require('domain'); /** * Module that lets you specify a hierarchy of caches. @@ -47,9 +48,24 @@ var multi_caching = function (caches) { ttl = undefined; } + if (self.queues[key]) { + self.queues[key].push({cb: cb, domain: process.domain}); + return; + } + + self.queues[key] = [{cb: cb, domain: process.domain}]; + + function fillCallbacks(err, data) { + self.queues[key].forEach(function(task) { + var taskDomain = task.domain || domain.create(); + taskDomain.bind(task.cb)(err, data); + }); + delete self.queues[key]; + } + get_from_highest_priority_cache(key, function (err, result, index) { if (err) { - return cb(err); + return fillCallbacks(err); } else if (result) { var caches_to_update = caches.slice(0, index); var opts = { @@ -58,38 +74,30 @@ var multi_caching = function (caches) { ttl: ttl }; set_in_multiple_caches(caches_to_update, opts, function (err) { - cb(err, result); + fillCallbacks(err, result); }); - } else if (self.queues[key]) { - self.queues[key].push(cb); } else { - self.queues[key] = [cb]; - work(function () { - var work_args = Array.prototype.slice.call(arguments, 0); - if (work_args[0]) { // assume first arg is an error - self.queues[key].forEach(function (done) { - done.call(null, work_args[0]); - }); - delete self.queues[key]; + domain + .create() + .on('error', function(err) { + fillCallbacks(err); + }) + .bind(work)(function (err, data) { + if (err) { + fillCallbacks(err); return; } var opts = { key: key, - value: work_args[1], + value: data, ttl: ttl }; set_in_multiple_caches(caches, opts, function (err) { if (err) { - self.queues[key].forEach(function (done) { - done.call(null, err); - }); - delete self.queues[key]; - return; + fillCallbacks(err); + } else { + fillCallbacks(null, data); } - self.queues[key].forEach(function (done) { - done.apply(null, work_args); - }); - delete self.queues[key]; }); }); } diff --git a/test/caching.unit.js b/test/caching.unit.js index 33837fd..de8a2ed 100644 --- a/test/caching.unit.js +++ b/test/caching.unit.js @@ -357,6 +357,23 @@ describe("caching", function () { }); }); + context("when an error is thrown in the work function", function () { + var fake_error; + + beforeEach(function() { + fake_error = new Error(support.random.string()); + }); + + it("bubbles up that error", function (done) { + cache.wrap(key, function () { + throw fake_error; + }, ttl, function (err) { + assert.equal(err, fake_error); + done(); + }); + }); + }); + context("when store.get() calls back with an error", function () { context("and ignoreCacheErrors is not set (default is false)", function () { it("bubbles up that error", function (done) { diff --git a/test/multi_caching.unit.js b/test/multi_caching.unit.js index 746c951..31688e0 100644 --- a/test/multi_caching.unit.js +++ b/test/multi_caching.unit.js @@ -449,6 +449,23 @@ describe("multi_caching", function () { memory_store.create.restore(); }); + context("when an error is thrown in the work function", function () { + var fake_error; + + beforeEach(function() { + fake_error = new Error(support.random.string()); + }); + + it("bubbles up that error", function (done) { + multi_cache.wrap(key, function () { + throw fake_error; + }, ttl, function (err) { + assert.equal(err, fake_error); + done(); + }); + }); + }); + context("when store.get() calls back with an error", function () { it("bubbles up that error", function (done) { var fake_error = new Error(support.random.string());