Commit a3825ab3e716583b5086c7e2a0516da1154fb1b2
1 parent
db834a8cbc
Exists in
master
resend delay
Showing 3 changed files with 179 additions and 31 deletions Side-by-side Diff
package.json
... | ... | @@ -21,16 +21,17 @@ |
21 | 21 | "author": "Adhidarma Hadiwinoto <gua@adhisimon.org>", |
22 | 22 | "license": "BSD", |
23 | 23 | "dependencies": { |
24 | - "mocha": "~2.2.5", | |
25 | - "request": "~2.57.0", | |
26 | - "strftime": "~0.9.2", | |
24 | + "ini": "~1.3.4", | |
25 | + "lru-cache": "^4.0.1", | |
27 | 26 | "mathjs": "~1.7.0", |
28 | - "xmlrpc": "~1.3.1", | |
27 | + "minimist": "~1.2.0", | |
28 | + "mocha": "~2.2.5", | |
29 | 29 | "node-simple-router": "~0.9.4-2", |
30 | + "request": "~2.57.0", | |
30 | 31 | "sate24": "git+http://gitlab.kodesumber.com/reload97/node-sate24.git", |
31 | - "winston": "~1.0.1", | |
32 | - "ini": "~1.3.4", | |
33 | 32 | "sate24-expresso": "git+http://gitlab.kodesumber.com/reload97/sate24-expresso.git", |
34 | - "minimist": "~1.2.0" | |
33 | + "strftime": "~0.9.2", | |
34 | + "winston": "~1.0.1", | |
35 | + "xmlrpc": "~1.3.1" | |
35 | 36 | } |
36 | 37 | } |
resend-delay.js
... | ... | @@ -0,0 +1,108 @@ |
1 | +var LRU = require('lru-cache'); | |
2 | +var winston = require('winston'); | |
3 | + | |
4 | +var logger; | |
5 | +var topupRequest; | |
6 | +var resendHandlers = LRU({max: 2000, maxAge: 1000 * 3600 * 36}); | |
7 | + | |
8 | +function init(options) { | |
9 | + if (!options) { | |
10 | + console.log('Undefined options, terminating....'); | |
11 | + process.exit(1); | |
12 | + } | |
13 | + | |
14 | + if (options.config) { | |
15 | + config = options.config; | |
16 | + } else { | |
17 | + console.log('Undefined options.config, terminating....') | |
18 | + process.exit(1); | |
19 | + } | |
20 | + | |
21 | + if (options.topupRequest) { | |
22 | + topupRequest = options.topupRequest; | |
23 | + } else { | |
24 | + console.log('Undefined options.topupRequest, terminating....') | |
25 | + process.exit(1); | |
26 | + } | |
27 | + | |
28 | + if (options.logger) { | |
29 | + logger = options.logger; | |
30 | + } else { | |
31 | + logger = new winston.Logger({ | |
32 | + transports: [ | |
33 | + new (winston.transports.Console)() | |
34 | + ] | |
35 | + }); | |
36 | + } | |
37 | +} | |
38 | + | |
39 | +function cancel(_task) { | |
40 | + var requestId; | |
41 | + if (typeof task === 'string') { | |
42 | + requestId = task; | |
43 | + } else { | |
44 | + requestId = task.requestId; | |
45 | + } | |
46 | + | |
47 | + if (!requestId) { | |
48 | + logger.warn('Invalid task on cancelResendDelay'); | |
49 | + return; | |
50 | + } | |
51 | + | |
52 | + var oldHandler = resendHandlers.get(requestId); | |
53 | + if (!oldHandler) { | |
54 | + return; | |
55 | + } | |
56 | + | |
57 | + logger.verbose('Canceling resend delay', {task: oldHandler.task}); | |
58 | + | |
59 | + try { | |
60 | + if (oldHandler.handler) { | |
61 | + clearTimeout(oldHandler.handler); | |
62 | + } | |
63 | + } | |
64 | + catch(e) {}; | |
65 | + | |
66 | + try { | |
67 | + resendHandlers.del(requestId); | |
68 | + } | |
69 | + catch(e) {}; | |
70 | +} | |
71 | + | |
72 | +function register(task) { | |
73 | + if (!task.requestId) { | |
74 | + logger.warn('Invalid task on resendDelay') | |
75 | + return; | |
76 | + } | |
77 | + | |
78 | + if (!config || !config.globals || !Number(config.globals.auto_resend_delay_secs || !Number(config.globals.auto_resend_delay_max))) { | |
79 | + return; | |
80 | + } | |
81 | + | |
82 | + var retry = config.globals.auto_resend_delay_max; | |
83 | + var oldHandler = resendHandlers.get(task.requestId); | |
84 | + if (oldHandler) { | |
85 | + retry = oldHandler.retry - 1; | |
86 | + cancel(task); | |
87 | + } | |
88 | + | |
89 | + if (retry <= 0) { | |
90 | + logger.verbose('Resend delay retry exceeded', {task: task}); | |
91 | + cancel(task); | |
92 | + return; | |
93 | + } | |
94 | + | |
95 | + logger.verbose('Registering resend delay task request', {task: task, delay: config.globals.auto_resend_delay_secs, retry: retry}); | |
96 | + var handlerData = { | |
97 | + handler: setTimeout(topupRequest, config.globals.auto_resend_delay_secs * 1000, task), | |
98 | + task: task, | |
99 | + retry: retry | |
100 | + } | |
101 | + | |
102 | + resendHandlers.set(task.requestId, handlerData); | |
103 | +} | |
104 | + | |
105 | + | |
106 | +exports.init = init; | |
107 | +exports.cancel = cancel; | |
108 | +exports.register = register; |
xmlout.js
... | ... | @@ -2,18 +2,29 @@ var xmlrpc = require('xmlrpc'); |
2 | 2 | var url = require('url'); |
3 | 3 | var math = require('mathjs'); |
4 | 4 | var winston = require('winston'); |
5 | +var resendDelay = require('./resend-delay.js'); | |
6 | +var LRU = require('lru-cache'); | |
5 | 7 | |
6 | 8 | var aaa; |
7 | 9 | var logger; |
8 | 10 | var config; |
9 | -var callbackReport; | |
11 | +var _callbackReport; | |
10 | 12 | var redisClient; |
11 | 13 | |
12 | -var max_retry = 2; | |
13 | -var sleep_before_retry = 2000; | |
14 | +var taskHistory = LRU(max: 500, maxAge: 1000 * 3600 * 2); | |
14 | 15 | |
15 | 16 | process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0'; |
16 | 17 | |
18 | +function callbackReport(requestId, responseCode, message) { | |
19 | + if (responseCode != '68') { | |
20 | + resendDelay.cancel(requestId); | |
21 | + } else { | |
22 | + getTaskFromHistory(requestId, function(err, archivedTask) { | |
23 | + resendDelay.register(archivedTask); | |
24 | + } | |
25 | + } | |
26 | + _callbackReport(requestId, responseCode, messsage); | |
27 | +} | |
17 | 28 | |
18 | 29 | function createRedisClient(host, port) { |
19 | 30 | if (!host && !port) { |
... | ... | @@ -28,22 +39,22 @@ function createRedisClient(host, port) { |
28 | 39 | } |
29 | 40 | } |
30 | 41 | |
31 | -function topupRequest(task, retry) { | |
42 | +function topupRequest(task) { | |
32 | 43 | aaa.insertTaskToMongoDb(task); |
33 | 44 | |
34 | - getTaskFromRedis(task, function(err, archivedTask) { | |
35 | - putTaskToRedis(task); | |
45 | + getTaskFromHistory(task, function(err, archivedTask) { | |
46 | + putTaskToHistory(task); | |
36 | 47 | |
37 | 48 | if (archivedTask) { |
38 | 49 | logger.info('Task has been executed before, going to checkStatus', {task: task, archivedTask: archivedTask}); |
39 | - checkStatus(task, retry); | |
50 | + checkStatus(task); | |
40 | 51 | } else { |
41 | - _topupRequest(task, retry); | |
52 | + _topupRequest(task); | |
42 | 53 | } |
43 | 54 | }); |
44 | 55 | } |
45 | 56 | |
46 | -function _topupRequest(task, retry) { | |
57 | +function _topupRequest(task) { | |
47 | 58 | |
48 | 59 | var partnerUrl = url.parse(config.h2h_out.partner); |
49 | 60 | var clientOptions = { |
... | ... | @@ -77,17 +88,7 @@ function _topupRequest(task, retry) { |
77 | 88 | if (error) { |
78 | 89 | |
79 | 90 | logger.warn('XMLRPC Client Error', {requestId: task['requestId'], errorMessage: error}); |
80 | - | |
81 | - if (retry) { | |
82 | - | |
83 | - logger.info('Retrying topUpRequest (' + retry + ')'); | |
84 | - setTimeout(function() { | |
85 | - topupRequest(task, retry - 1); | |
86 | - }, sleep_before_retry); | |
87 | - | |
88 | - } else { | |
89 | - callbackReport(task['requestId'], '91', 'Gangguan koneksi ke suplier: ' + error); | |
90 | - } | |
91 | + callbackReport(task['requestId'], '91', 'Gangguan koneksi ke suplier: ' + error); | |
91 | 92 | return; |
92 | 93 | } |
93 | 94 | |
... | ... | @@ -221,7 +222,7 @@ function checkStatus(task) { |
221 | 222 | }); |
222 | 223 | } |
223 | 224 | |
224 | -function start(_config, _callbackReport, options) { | |
225 | +function start(options) { | |
225 | 226 | if (!options) { |
226 | 227 | console.log('Undefined options, terminating....'); |
227 | 228 | process.exit(1); |
... | ... | @@ -236,7 +237,7 @@ function start(_config, _callbackReport, options) { |
236 | 237 | |
237 | 238 | if (options.aaa) { |
238 | 239 | aaa = options.aaa; |
239 | - callbackReport = options.aaa.callbackReportWithPushToMongoDb; | |
240 | + _callbackReport = options.aaa.callbackReportWithPushToMongoDb; | |
240 | 241 | } else { |
241 | 242 | console.log('Undefined options.aaa, terminating....') |
242 | 243 | process.exit(1); |
... | ... | @@ -252,7 +253,14 @@ function start(_config, _callbackReport, options) { |
252 | 253 | }); |
253 | 254 | } |
254 | 255 | |
256 | + createRedisClient(config.globals.redis_host, config.globals.redis_port); | |
255 | 257 | createServer(); |
258 | + | |
259 | + resendDelay.init({ | |
260 | + config: config, | |
261 | + topupRequest: topupRequest, | |
262 | + logger: logger | |
263 | + }); | |
256 | 264 | } |
257 | 265 | |
258 | 266 | function parseSN(message, _config) { |
... | ... | @@ -300,11 +308,31 @@ function parseSN(message, _config) { |
300 | 308 | } |
301 | 309 | |
302 | 310 | function getTaskKey(task, chipInfo) { |
311 | + var requestId; | |
312 | + | |
313 | + if (typeof task === 'string') { | |
314 | + requestId = task; | |
315 | + } else { | |
316 | + requestId = task.requestId; | |
317 | + } | |
318 | + | |
303 | 319 | if (!chipInfo && config && config.globals && config.globals.gateway_name) { |
304 | 320 | chipInfo = config.globals.gateway_name; |
305 | 321 | } |
306 | 322 | |
307 | - return chipInfo + 'hitachi.rid:' + task.requestId; | |
323 | + return chipInfo + 'hitachi.rid:' + requestId; | |
324 | +} | |
325 | + | |
326 | + | |
327 | +function putTaskToHistory(task, cb) { | |
328 | + var key = getTaskKey(task, config.globals.gateway_name); | |
329 | + logger.verbose('Saving task', {key: key, task: task}); | |
330 | + | |
331 | + try { | |
332 | + taskHistory.set(key, JSON.parse(JSON.stringify(task))); | |
333 | + } catch (e) { } | |
334 | + | |
335 | + putTaskToRedis(task, cb); | |
308 | 336 | } |
309 | 337 | |
310 | 338 | function putTaskToRedis(task, cb) { |
... | ... | @@ -314,7 +342,6 @@ function putTaskToRedis(task, cb) { |
314 | 342 | } |
315 | 343 | |
316 | 344 | var key = getTaskKey(task, config.globals.gateway_name); |
317 | - logger.verbose('Saving task', {key: key, task: task}); | |
318 | 345 | |
319 | 346 | redisClient.set(key, JSON.stringify(task), function() { |
320 | 347 | redisClient.expire(key, 3600*24*30); |
... | ... | @@ -324,6 +351,18 @@ function putTaskToRedis(task, cb) { |
324 | 351 | }); |
325 | 352 | } |
326 | 353 | |
354 | +function getTaskFromHistory(task, cb) { | |
355 | + var key = getTaskKey(task, config.globals.gateway_name); | |
356 | + var archive = taskHistory.get(key); | |
357 | + | |
358 | + if (archive) { | |
359 | + if (cb) { cb(null, archive); } | |
360 | + } | |
361 | + else { | |
362 | + getTaskFromRedis(task, cb); | |
363 | + } | |
364 | +} | |
365 | + | |
327 | 366 | function getTaskFromRedis(task, cb) { |
328 | 367 | if (!redisClient) { |
329 | 368 | if (cb) { cb(null, null); } |