Skip to content

Commit 3071ce3

Browse files
committed
release v1.0.0
* add garbage collector.
1 parent 67c3f21 commit 3071ce3

File tree

4 files changed

+195
-59
lines changed

4 files changed

+195
-59
lines changed

CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,10 @@
1+
# 1.0.0
2+
3+
2015-11-12
4+
5+
- add garbage collector.
6+
- improve multiple request processing logic.
7+
18
# 0.9.1
29

310
2015-11-09

README.md

Lines changed: 94 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
# redmoon
22

3+
[![version](https://img.shields.io/npm/v/redmoon.svg) ![download](https://img.shields.io/npm/dm/redmoon.svg)](https://www.npmjs.com/package/redmoon)
4+
5+
Asynchronous broker for multi-source data collection using redis.
6+
7+
If redmoon.load is called, and it returned to find the data in the cache, if cache data does not exist, call the specified function(subscriber) to subscribe.
8+
39
[![js-standard-style](https://cdn.rawgit.com/feross/standard/master/badge.svg)](https://github.com/feross/standard)
410

511
## Usage
@@ -9,12 +15,14 @@
915
// setting
1016
var redmoon = require('redmoon').create(config)
1117

12-
redmoon.on('timeout', function(keyword) {
13-
console.error('redmoon timeout', keyword)
14-
});
15-
redmoon.on('error', function(err) {
16-
console.error('redmoon error', err)
17-
});
18+
// event handle
19+
redmoon
20+
.on('timeout', function(keyword) {
21+
console.error('redmoon timeout', keyword)
22+
})
23+
.on('error', function(err) {
24+
console.error('redmoon error', err)
25+
});
1826

1927
// express
2028
app.get('/search/:key/:page/:limit?', function (req, res) {
@@ -28,72 +36,122 @@ app.get('/search/:key/:page/:limit?', function (req, res) {
2836

2937
// collector
3038
// The first event is no meta data in moon.
31-
// Thus, the logic required for implementation so as to avoid unnecessary requests using totalcount.
32-
redmoon.subscribe(function (moon) {
33-
var provider = ['youtube', 'dailymotion']
34-
for (var i = 0; i < provider.length; i++) {
35-
search(function (err, result) {
36-
var meta = {
37-
provider: result.provider,
38-
totalcount: result.totalcount
39-
}
40-
41-
redmoon.add(function (err) {
42-
redmoon.trigger(param.topic)
43-
}, moon, meta, result.items)
44-
}, provider[i], moon)
45-
}
46-
})
39+
redmoon
40+
.subscribe(subscriber)
41+
.garbage.start(3600) // It deletes the data collected before to 60 minutes.
42+
43+
var subscriber = function (moon) {
44+
redmoon.atomic(moon.uuid, function(cb) {
45+
process(cb, moon)
46+
}, function(err, data) {
47+
complete(moon, data)
48+
})
49+
}
50+
51+
var process = function (cb, moon) {
52+
// call cb when complete load data
53+
cb(err, {
54+
meta: {},
55+
items: {}
56+
})
57+
}
58+
59+
var complete = function(moon, data) {
60+
redmoon.add(function(err) {
61+
redmoon.trigger(moon.topic)
62+
}, moon, data.meta, data.items)
63+
}
64+
65+
66+
// moon
67+
// {
68+
// uuid: uuid,
69+
// key: key,
70+
// topic: topic,
71+
// meta: meta // if first event then it is undefined
72+
// }
4773

4874
```
4975

5076
## Methods
5177

5278
### redmoon.connect(config)
5379

54-
connect redis.
80+
Connect redis.
5581

5682
### redmoon.events()
5783

58-
initialize redmoon events.
84+
Initialize redmoon events.
5985
* error
6086
* ready
6187
* connect
6288
* end
6389

64-
### redmoon.load(cb, key, page, limit)
90+
### redmoon.load(cb(err, items, meta), key, page, limit)
91+
92+
Load collected data.
93+
If not exist data then trigger event for collection.
94+
95+
### redmoon.loadMeta(cb(err, metas), uuid, count)
6596

66-
load collected data.
67-
if not exist data then trigger event for collection.
97+
Load meta data.
6898

6999
### redmoon.add(cb, moon, meta, items)
70100

71-
add collected data to cache.
101+
Add collected data to cache.
72102

73103
### redmoon.subscribe(cb(err, moon))
74104

75-
set callback function for the collection event.
105+
Set callback function for the collection event.
76106

77107
### redmoon.unsubscribe(cb())
78108

79-
unset callback function for the collection event.
109+
Unset callback function for the collection event.
80110

81111
### redmoon.trigger(topic, data)
82112

83-
trigger event for the specified topic.
113+
Trigger event for the specified topic.
114+
115+
### redmoon.atomic(uuid, process(cb), complete(err, data))
116+
117+
The function to prevent the duplication proccess.
118+
119+
If it has available resources, given `process` calls and then release the resources used by `cb` call in process.
120+
After the release, and it executes the transfer of data in `proccess` to `complete`.
84121

85122
### redmoon.end()
86123

87-
close the connection to the redis server.
124+
Close the connection to the redis server.
88125

89-
### Redmoon.create(config)
126+
### redmoon.truncate(unix)
127+
128+
Deleting cache data collected before a specified time.
90129

91-
create Redmoon instance with config.
130+
### redmmon.toTopic(uuid)
92131

132+
It converts uuid to the topic.
133+
134+
### redmoon.garbage.start(offset)
135+
136+
Start garbage collection.
137+
Deleting cache data collected before a offset time.
138+
139+
### redmoon.garbage.stop()
140+
141+
Stop garbage collection.
142+
143+
### Redmoon.uuid(key)
144+
145+
Make unique string.
146+
147+
### Redmmon.uuid()
148+
149+
Return unixtimestamp.
150+
151+
### Redmoon.create(config)
93152

94-
## Todo
153+
Create Redmoon instance with config.
95154

96-
* Garbage collector.
97155

98156
## Release History
99157

lib/redmoon.js

Lines changed: 88 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -9,20 +9,35 @@ function Redmoon (config) {
99
this._client = null
1010
this._nrp = null
1111

12+
this._timer = null
13+
1214
this.config = {
1315
scope: 'redmoon',
1416
name: 'default',
1517
host: '127.0.0.1',
1618
port: 6379,
1719
timeout: 5000, // 5 sec
18-
buffer: 100,
19-
ttl: 5
20+
buffer: 100, // buffer count for preloading
21+
ttl: 5, // max time for atomic
22+
interval: 60 // interval value for garbage collection
2023
}
2124
this.key = {
2225
atomic: [this.config.scope, 'atomic'].join(':'), // hash
2326
meta: [this.config.scope, 'meta'].join(':'), // hash
2427
cycle: [this.config.scope, 'cycle'].join(':') // set (sorted)
2528
}
29+
30+
this.garbage = {
31+
start: function (offset) {
32+
this._timer = setInterval(function () {
33+
this.truncate(Redmoon.unix() - offset)
34+
}.bind(this), this.config.interval * 1000)
35+
}.bind(this),
36+
stop: function () {
37+
clearInterval(this._timer)
38+
this._timer = null
39+
}.bind(this)
40+
}
2641
}
2742

2843
util.inherits(Redmoon, events.EventEmitter)
@@ -37,6 +52,9 @@ Redmoon.prototype.connect = function (config) {
3752
typeof config.host !== 'undefined' && (this.config.host = config.host)
3853
typeof config.port !== 'undefined' && (this.config.port = config.port)
3954
typeof config.timeout !== 'undefined' && (this.config.timeout = config.timeout)
55+
typeof config.buffer !== 'undefined' && (this.config.buffer = config.buffer)
56+
typeof config.ttl !== 'undefined' && (this.config.ttl = config.ttl)
57+
typeof config.interval !== 'undefined' && (this.config.interval = config.interval)
4058

4159
this._client = require('redis').createClient(this.config.port, this.config.host)
4260
this._nrp = new NRP(this.config)
@@ -72,14 +90,11 @@ Redmoon.prototype.load = function (cb, key, page, limit) {
7290
limit = limit || 10
7391

7492
// check exist search result on meta
75-
this._client.send_command('HSCAN', [this.key.meta, 0, 'MATCH', uuid + ':*', 'COUNT', 1000], function (err, metas) {
93+
this.loadMeta(function (err, metas) {
7694
if (err) {
7795
return cb(err)
7896
}
7997

80-
// migrate
81-
metas = metas[1]
82-
8398
if (metas.length > 0) {
8499
var start = (page - 1) * limit
85100
var end = start + limit - 1
@@ -132,12 +147,20 @@ Redmoon.prototype.load = function (cb, key, page, limit) {
132147
self._nrp.off(topic)
133148
self.load(cb, key, page, limit)
134149
})
135-
self._nrp.emit([self.config.name, uuid].join(':'), {
150+
self._nrp.emit([self.config.scope, self.config.name, 'request', uuid].join(':'), {
136151
uuid: uuid,
137152
key: key,
138153
topic: topic
139154
})
140155
}
156+
}, uuid)
157+
158+
return this
159+
}
160+
161+
Redmoon.prototype.loadMeta = function (cb, uuid, count) {
162+
this._client.send_command('HSCAN', [this.key.meta, 0, 'MATCH', uuid + ':*', 'COUNT', count || 1000], function (err, metas) {
163+
cb(err, metas ? metas[1] : [])
141164
})
142165

143166
return this
@@ -161,26 +184,29 @@ Redmoon.prototype.add = function (cb, moon, meta, items) {
161184
}
162185

163186
Redmoon.prototype.subscribe = function (cb) {
164-
this._nrp.on(this.config.name + ':*', cb)
187+
this._nrp.on([this.config.scope, this.config.name, 'request', '*'].join(':'), cb)
188+
return this
165189
}
166190

167191
Redmoon.prototype.unsubscribe = function (cb) {
168-
this._nrp.off(this.config.name + ':*', cb)
192+
this._nrp.off([this.config.scope, this.config.name, 'request', '*'].join(':'), cb)
193+
return this
169194
}
170195

171196
Redmoon.prototype.trigger = function (topic, data) {
172197
this._nrp.emit(topic, data || {})
198+
return this
173199
}
174200

175-
// 중복 이벤트 처리를 방지 목적.
176-
Redmoon.prototype.atomic = function (cb, uuid) {
201+
// atomic processing
202+
Redmoon.prototype.atomic = function (uuid, process, complete) {
177203
// atomic
178204
var self = this
179205
var key = [this.key.atomic, uuid].join(':')
180206

181207
this._client.get(key, function (err, item) {
182208
if (err) {
183-
return console.error(err)
209+
return self.emit('error', err)
184210
}
185211

186212
if (item) {
@@ -191,13 +217,19 @@ Redmoon.prototype.atomic = function (cb, uuid) {
191217
self._client.multi()
192218
.set(key, uuid)
193219
.expire(key, self.config.ttl)
194-
.exec(cb)
195-
})
196-
}
220+
.exec(function (err) {
221+
if (err) {
222+
return self.emit('error', err)
223+
}
197224

198-
// 중복 이벤트 처리를 방지 목적.
199-
Redmoon.prototype.release = function (cb, uuid) {
200-
this._client.del([this.key.atomic, uuid].join(':'), cb)
225+
process(function (err, data) {
226+
self._client.del([self.key.atomic, uuid].join(':'), function () {
227+
complete(err, data)
228+
})
229+
})
230+
})
231+
})
232+
return this
201233
}
202234

203235
Redmoon.prototype.end = function () {
@@ -210,10 +242,47 @@ Redmoon.prototype.end = function () {
210242
this._nrp.end()
211243
this._nrp = null
212244
}
245+
return this
246+
}
247+
248+
Redmoon.prototype.truncate = function (unix) {
249+
var self = this
250+
unix = unix || Redmoon.unix() - 3600 // default 1 hour
251+
252+
this._client.zrangebyscore(this.key.cycle, 0, unix, function (err, items) {
253+
if (err) {
254+
return self.emit('error', err)
255+
}
256+
257+
for (var i = 0; i < items.length; i++) {
258+
self.loadMeta((function (uuid) {
259+
return function (err, metas) {
260+
if (err) {
261+
return self.emit('error', err)
262+
}
263+
264+
var multi = self._client.multi()
265+
266+
for (var j = 0; j < metas.length; j += 2) {
267+
multi.hdel(self.key.meta, metas[j])
268+
}
269+
270+
multi.del([self.config.scope, self.config.name, uuid].join(':'))
271+
multi.zrem(self.key.cycle, uuid)
272+
multi.exec(function (err, replies) {
273+
if (err) {
274+
return self.emit('error', err)
275+
}
276+
})
277+
}
278+
})(items[i]), items[i])
279+
}
280+
})
281+
return this
213282
}
214283

215284
Redmoon.prototype.toTopic = function (uuid) {
216-
return [shortid.generate(), this.config.name, 'result', uuid].join(':')
285+
return [this.config.name, 'result', uuid].join(':')
217286
}
218287

219288
Redmoon.uuid = function (key) {

0 commit comments

Comments
 (0)