UNPKG

6.47 kBJavaScriptView Raw
1/**
2 * joola.io
3 *
4 * Copyright Joola Smart Solutions, Ltd. <info@joo.la>
5 *
6 * Licensed under GNU General Public License 3.0 or later.
7 * Some rights reserved. See LICENSE, AUTHORS.
8 *
9 * @license GPL-3.0+ <http://spdx.org/licenses/GPL-3.0+>
10 */
11var cache = function (joola, next) {
12 joola.cache = {};
13 joola.cache.sweeperTimer = null;
14 joola.cache.sweeperTimeout = 10 * 1000;
15 joola.cache.redisCacheKey = "joola:cache";
16
17 joola.cache._start = function (joola, callback) {
18 joola.cache.runSweeper();
19 return callback();
20 };
21
22 joola.cache._teardown = function (joola, next) {
23 joola.cache.stopTimers(joola);
24 return next();
25 };
26
27 joola.cache.stopTimers = function (joola) {
28 clearTimeout(joola.cache.sweeperTimer);
29 };
30
31 joola.cache.prepareDomain = function () {
32 // until the redis module handles domains, we need to force the callback to be bound properly
33 // https://github.com/mranney/node_redis/pull/310/files
34 return {
35 bind: function (callback) {
36 return callback
37 }
38 }
39 };
40
41 joola.cache.size = function (next) {
42 var domain = joola.cache.prepareDomain();
43 joola.redis.client.hlen(joola.cache.redisCacheKey, domain.bind(function (err, count) {
44 next(null, count);
45 }));
46 };
47
48 joola.cache.load = function (store, key, options, next) {
49 if (typeof options == "function") {
50 next = options;
51 options = {}
52 }
53 var domain = joola.cache.prepareDomain();
54 joola.redis.client.hget(joola.cache.redisCacheKey + ':' + store, key, domain.bind(function (err, cacheObj) {
55 if (err != null) {
56 //joola.error(err);
57 return next(new Error("Failed to get value from redis"), null, null, null, null);
58 }
59 try {
60 cacheObj = JSON.parse(cacheObj);
61 } catch (e) {
62 }
63 if (cacheObj == null) {
64 if (typeof next == "function") {
65 process.nextTick(function () {
66 return next(new Error("Object not found"), null, null, null, null);
67 });
68 }
69 } else if (cacheObj.expireTimestamp >= new Date().getTime() || cacheObj.expireTimestamp == null) {
70 cacheObj.readAt = new Date().getTime();
71 if (cacheObj.expireTimestamp != null && options.expireTimeMS)
72 cacheObj.expireTimestamp = new Date().getTime() + options.expireTimeMS;
73 joola.redis.client.hset(joola.cache.redisCacheKey, key, JSON.stringify(cacheObj), domain.bind(function () {
74 if (typeof next == "function") {
75 process.nextTick(function () {
76 return next(null, cacheObj.value, cacheObj.expireTimestamp, cacheObj.createdAt, cacheObj.readAt);
77 });
78 }
79 }));
80 } else {
81 if (typeof next == "function") {
82 process.nextTick(function () {
83 return next(new Error("Object expired"), null, null, null, null);
84 });
85 }
86 }
87 }));
88 };
89
90 joola.cache.destroy = function (key, next) {
91 var domain = joola.cache.prepareDomain();
92 joola.redis.client.hdel(joola.cache.redisCacheKey, key, domain.bind(function (err, count) {
93 //joola.stats.increment("cache:cachedObjects", -1 );
94 if (err != null) {
95 joola.logger.error(err);
96 }
97 var resp = true;
98 if (count != 1) {
99 resp = false;
100 }
101 if (typeof next == "function") {
102 process.nextTick(function () {
103 next(null, resp);
104 });
105 }
106 }));
107 };
108
109 joola.cache.sweeper = function (next) {
110 var domain = joola.cache.prepareDomain();
111 joola.redis.client.hkeys(joola.cache.redisCacheKey, domain.bind(function (err, keys) {
112 var started = 0;
113 var sweepedKeys = [];
114 keys.forEach(function (key) {
115 started++;
116 joola.redis.client.hget(joola.cache.redisCacheKey, key, domain.bind(function (err, cacheObj) {
117 if (err != null) {
118 joola.logger.error(err);
119 }
120 try {
121 JSON.parse(cacheObj);
122 } catch (e) {
123 }
124 if (cacheObj != null) {
125 if (cacheObj.expireTimestamp != null && cacheObj.expireTimestamp < new Date().getTime()) {
126 joola.redis.client.hdel(joola.cache.redisCacheKey, key, domain.bind(function (err) {
127 sweepedKeys.push(key);
128 started--;
129 if (started == 0 && typeof next == "function") {
130 next(err, sweepedKeys);
131 }
132 }));
133 } else {
134 started--;
135 if (started == 0 && typeof next == "function") {
136 next(err, sweepedKeys);
137 }
138 }
139 } else {
140 started--;
141 if (started == 0 && typeof next == "function") {
142 next(err, sweepedKeys);
143 }
144 }
145 }));
146 });
147 if (keys.length == 0 && typeof next == "function") {
148 next(err, sweepedKeys);
149 }
150 }));
151 };
152
153 joola.cache.save = function (store, key, value, expireTimeMS, next) {
154 var domain = joola.cache.prepareDomain();
155 if (typeof expireTimeMS == "function" && typeof next == "undefined") {
156 next = expireTimeMS;
157 expireTimeMS = null;
158 }
159 if (expireTimeMS != null) {
160 var expireTimestamp = new Date().getTime() + expireTimeMS;
161 } else {
162 expireTimestamp = null;
163 }
164 var cacheObj = {
165 value: value,
166 expireTimestamp: expireTimestamp,
167 createdAt: new Date().getTime(),
168 readAt: null
169 };
170 joola.redis.client.hset(joola.cache.redisCacheKey + ':' + store, key, JSON.stringify(cacheObj), domain.bind(function () {
171 if (typeof next == "function") {
172 process.nextTick(function () {
173 next(null, true);
174 });
175 }
176 }));
177 };
178
179 joola.cache.runSweeper = function () {
180 clearTimeout(joola.cache.sweeperTimer);
181 joola.cache.sweeper(function (err, sweepedKeys) {
182 if (sweepedKeys.length > 0) {
183 joola.logger.info("cleaned " + sweepedKeys.length + " expired cache keys", "debug");
184 }
185 if (joola.running) {
186 joola.cache.sweeperTimer = setTimeout(joola.cache.runSweeper, joola.cache.sweeperTimeout, joola);
187 }
188 });
189 };
190
191 next();
192};
193
194/////////////////////////////////////////////////////////////////////
195// exports
196exports.cache = cache;