1
1
2
- // todo
3
- // mongoose-ttl
4
- // mongoose-keywords
5
- // mongoose-query-cache
6
- // mongoose-timestamps (use createdAt as default name)
7
- // mongoose-created
8
- // more
2
+ /**
3
+ * Module dependencies.
4
+ */
9
5
10
- // + tests <<== START HERE !!!!
6
+ var Model = require ( 'mongoose' ) . Model
7
+ , ms = require ( 'ms' )
11
8
12
- var Model = require ( 'mongoose' ) . Model ;
9
+ /**
10
+ * Exports.
11
+ */
13
12
14
13
module . exports = exports = ttl ;
15
14
15
+ /**
16
+ * Mongoose-TTL Plugin
17
+ *
18
+ * Provides timespan support for documents.
19
+ *
20
+ * Options:
21
+ * ttl: the time each doc should live in the db (default 60 seconds)
22
+ * interval: how often the expired doc reaper runs (default 5 mins)
23
+ * reap: enable the expired doc reaper (default true)
24
+ * onReap: callback passed to reaper execution
25
+ *
26
+ * Example:
27
+ *
28
+ * var schema = new Schema({..});
29
+ * schema.plugin(ttl, { ttl: 5000 });
30
+ *
31
+ * The ttl option supports the ms module by @guille meaning
32
+ * we can specify ttls with friendlier syntax. Example:
33
+ *
34
+ * value milliseconds
35
+ * ========================
36
+ * '2d' 172800000
37
+ * '1.5h' 5400000
38
+ * '1h' 3600000
39
+ * '1m' 60000
40
+ * '5s' 5000
41
+ * '500ms' 500
42
+ * 100 100
43
+ *
44
+ * The expired document reaper can be disabled by passing `reap: false`.
45
+ * Useful when working in multi-core environments when we only want one
46
+ * process executing it.
47
+ *
48
+ * var schema = new Schema({..});
49
+ * schema.plugin(ttl, { ttl: 5000, reap: false });
50
+ * var Cache = db.model('Cache', schema);
51
+ * if (isMyWorker) Cache.startTTLReaper();
52
+ *
53
+ * The reaper can also be stopped.
54
+ *
55
+ * Cache.stopTTLReaper();
56
+ *
57
+ * Time-to-live is specified at the collection level, however
58
+ * it can also be overridden for a given document.
59
+ *
60
+ * var cache = new Cache;
61
+ * cache.ttl = '2m' // lives for two minutes
62
+ * cache.save();
63
+ *
64
+ * We can also reset the ttl for a given document to its
65
+ * default plugin state.
66
+ *
67
+ * cache.resetTTL();
68
+ *
69
+ * @param {Schema } schema
70
+ * @param {Object } options
71
+ */
72
+
16
73
function ttl ( schema , options ) {
17
74
options || ( options = { } ) ;
18
75
19
- var key = '__ttl' // non-configurable
20
- , ttl = options . ttl || 60000 // doc age limit
21
- , interval = options . interval || 60000 * 5 // how often to .remove() expired docs
22
- , cb = 'function' == typeof options . cb ? cb : undefined // reaper callback
76
+ var key = '__ttl'
77
+ , overridden = '__ttlOverride'
78
+ , ttl = options . ttl || 60000
79
+ , interval = options . interval || 60000 * 5
80
+ , reap = false !== options . reap
81
+ , onReap = 'function' == typeof options . onReap
82
+ ? options . onReap
83
+ : undefined
23
84
24
85
var o = { } ;
25
86
o [ key ] = Date ;
26
-
27
87
schema . add ( o ) ;
28
88
29
- var index = { } ;
30
- index [ key ] = 1 ;
31
- schema . index ( index ) ;
89
+ schema . index ( key , { background : true } ) ;
32
90
33
91
schema . pre ( 'save' , function ( next ) {
34
- this [ key ] = new Date ;
92
+ if ( overridden in this ) {
93
+ // nothing to do
94
+ } else {
95
+ this [ key ] = fromNow ( ) ;
96
+ }
35
97
next ( ) ;
36
98
} ) ;
37
99
38
- function applyTTL ( cond ) {
39
- if ( cond [ key ] ) {
40
- cond . $and || ( cond . $and = [ ] ) ;
41
- var a = { } ;
42
- a [ key ] = cond [ key ] ;
43
- cond . $and . push ( a ) ;
44
- var b = { } ;
45
- b [ key ] = { $gte : Date . now ( ) - ttl } ;
46
- cond . $and . push ( b ) ;
47
- delete cond [ key ] ;
48
- } else {
49
- cond [ key ] = { $gte : Date . now ( ) - ttl }
50
- }
100
+ /**
101
+ * startTTLReaper
102
+ *
103
+ * Starts reaping expired docs from the db.
104
+ */
105
+
106
+ schema . statics . startTTLReaper = function startTTLReaper ( ) {
107
+ if ( key in this ) return ;
108
+
109
+ var self = this ;
110
+ self [ key ] = setInterval ( ( function remove ( ) {
111
+ self
112
+ . remove ( )
113
+ . where ( key ) . $lte ( new Date )
114
+ . exec ( onReap ) ;
115
+ return remove ;
116
+ } ) ( ) , interval ) ;
51
117
}
52
118
53
119
/**
54
- * Override Model.init
120
+ * stopTTLReaper
121
+ *
122
+ * Stops removing expired docs from the db.
55
123
*/
56
124
57
- schema . statics . init = function ( ) {
125
+ schema . statics . stopTTLReaper = function stopTTLReapter ( ) {
126
+ clearInterval ( this [ key ] ) ;
127
+ delete this [ key ] ;
128
+ } ;
129
+
130
+ /**
131
+ * Clobber Model.init.
132
+ */
133
+
134
+ schema . statics . init = function initTTL ( ) {
58
135
init ( this ) ;
59
136
return Model . init . call ( this ) ;
60
137
}
@@ -63,14 +140,16 @@ function ttl (schema, options) {
63
140
* init
64
141
*
65
142
* Hook into all model queries to include the TTL
66
- * filter and kick off the expired doc reaper.
143
+ * filter and kick off the expired doc reaper if
144
+ * enabled.
145
+ * @private
67
146
*/
68
147
69
148
function init ( model ) {
70
149
if ( model . __ttl ) return ;
71
150
72
151
var distinct_ = model . distinct ;
73
- model . distinct = function ( field , cond , cb ) {
152
+ model . distinct = function distinct ( field , cond , cb ) {
74
153
applyTTL ( cond ) ;
75
154
return distinct_ . call ( model , field , cond , cb ) ;
76
155
}
@@ -101,35 +180,68 @@ function ttl (schema, options) {
101
180
}
102
181
} ) ;
103
182
104
- startTTL ( model ) ;
183
+ if ( reap ) {
184
+ model . startTTLReaper ( ) ;
185
+ }
105
186
}
106
187
107
188
/**
108
- * startTTL
189
+ * Getters/setters
190
+ */
191
+
192
+ var virt = schema . virtual ( 'ttl' ) ;
193
+
194
+ virt . get ( function ( ) {
195
+ if ( this [ key ] ) return this [ key ] ;
196
+ this . ttl = ttl ;
197
+ return this . ttl ;
198
+ } ) ;
199
+
200
+ virt . set ( function ( val ) {
201
+ if ( 'reset' == val ) return this . resetTTL ( ) ;
202
+ this [ overridden ] = arguments . length ? val : ttl ;
203
+ return this [ key ] = fromNow ( this [ overridden ] ) ;
204
+ } ) ;
205
+
206
+ /**
207
+ * resetTTL
109
208
*
110
- * Initializes the timer which removes expired docs
111
- * from the DB .
209
+ * Resets this documents ttl to the default specified
210
+ * in the plugin options or plugin default .
112
211
*/
113
212
114
- function startTTL ( model ) {
115
- var remove ;
116
- model . __ttl = setInterval ( remove = function ( ) {
117
- model
118
- . remove ( )
119
- . where ( key ) . $lt ( Date . now ( ) - ttl )
120
- . exec ( cb ) ;
121
- } , interval ) ;
122
- setTimeout ( remove , 10000 ) ;
213
+ schema . methods . resetTTL = function resetTTL ( ) {
214
+ delete this . _doc [ key ] ;
215
+ delete this [ overridden ] ;
123
216
}
124
217
125
218
/**
126
- * clearTTL
127
- *
128
- * Stops hitting the db to remove expired docs.
219
+ * fromNow
220
+ * @private
129
221
*/
130
222
131
- schema . statics . clearTTL = function clearTTL ( ) {
132
- clearInterval ( this . __ttl ) ;
133
- } ;
223
+ function fromNow ( val ) {
224
+ var v = arguments . length ? val : ttl ;
225
+ return new Date ( Date . now ( ) + ms ( v ) ) ;
226
+ }
134
227
228
+ /**
229
+ * Applies ttl to query conditions.
230
+ * @private
231
+ */
232
+
233
+ function applyTTL ( cond ) {
234
+ if ( cond [ key ] ) {
235
+ cond . $and || ( cond . $and = [ ] ) ;
236
+ var a = { } ;
237
+ a [ key ] = cond [ key ] ;
238
+ cond . $and . push ( a ) ;
239
+ var b = { } ;
240
+ b [ key ] = { $gt : new Date } ;
241
+ cond . $and . push ( b ) ;
242
+ delete cond [ key ] ;
243
+ } else {
244
+ cond [ key ] = { $gt : new Date } ;
245
+ }
246
+ }
135
247
}
0 commit comments