33 */
44
55var assert = require ( 'assert' ) ;
6+ var microtime = require ( './microtime' ) ;
67
78/**
89 * Expose `Limiter`.
@@ -27,7 +28,7 @@ function Limiter(opts) {
2728 assert ( this . db , '.db required' ) ;
2829 this . max = opts . max || 2500 ;
2930 this . duration = opts . duration || 3600000 ;
30- this . prefix = 'limit:' + this . id + ':' ;
31+ this . key = 'limit:' + this . id ;
3132}
3233
3334/**
@@ -47,88 +48,38 @@ Limiter.prototype.inspect = function() {
4748 * Get values and header / status code and invoke `fn(err, info)`.
4849 *
4950 * redis is populated with the following keys
50- * that expire after N seconds :
51+ * that expire after N milliseconds :
5152 *
52- * - limit:<id>:count
53- * - limit:<id>:limit
54- * - limit:<id>:reset
53+ * - limit:<id>
5554 *
5655 * @param {Function } fn
5756 * @api public
5857 */
5958
60- Limiter . prototype . get = function ( fn ) {
61- var count = this . prefix + 'count' ;
62- var limit = this . prefix + 'limit' ;
63- var reset = this . prefix + 'reset' ;
59+ Limiter . prototype . get = function ( fn ) {
60+ var key = this . key ;
6461 var duration = this . duration ;
6562 var max = this . max ;
6663 var db = this . db ;
67-
68- function create ( ) {
69- var ex = ( Date . now ( ) + duration ) / 1000 | 0 ;
70-
71- db . multi ( )
72- . set ( [ count , max , 'PX' , duration , 'NX' ] )
73- . set ( [ limit , max , 'PX' , duration , 'NX' ] )
74- . set ( [ reset , ex , 'PX' , duration , 'NX' ] )
75- . exec ( function ( err , res ) {
76- if ( err ) return fn ( err ) ;
77-
78- // If the request has failed, it means the values already
79- // exist in which case we need to get the latest values.
80- if ( isFirstReplyNull ( res ) ) return mget ( ) ;
81-
82- fn ( null , {
83- total : max ,
84- remaining : max ,
85- reset : ex
86- } ) ;
87- } ) ;
88- }
89-
90- function decr ( res ) {
91- var n = ~ ~ res [ 0 ] ;
92- var max = ~ ~ res [ 1 ] ;
93- var ex = ~ ~ res [ 2 ] ;
94- var dateNow = Date . now ( ) ;
95-
96- if ( n <= 0 ) return done ( ) ;
97-
98- function done ( ) {
99- fn ( null , {
100- total : max ,
101- remaining : n < 0 ? 0 : n ,
102- reset : ex
103- } ) ;
104- }
105-
106- db . multi ( )
107- . decr ( count )
108- . pexpire ( [ count , ex * 1000 - dateNow ] )
109- . pexpire ( [ limit , ex * 1000 - dateNow ] )
110- . pexpire ( [ reset , ex * 1000 - dateNow ] )
111- . exec ( function ( err , res ) {
112- if ( err ) return fn ( err ) ;
113- if ( isFirstReplyNull ( res ) ) return mget ( ) ;
114- n = Array . isArray ( res [ 0 ] ) ? ~ ~ res [ 0 ] [ 1 ] : ~ ~ res [ 0 ] ;
115- done ( ) ;
116- } ) ;
117- }
118-
119- function mget ( ) {
120- db . watch ( [ count ] , function ( err ) {
64+ var now = microtime . now ( ) ;
65+ var start = now - duration * 1000 ;
66+
67+ db . multi ( )
68+ . zremrangebyscore ( [ key , 0 , start ] )
69+ . zcard ( [ key ] )
70+ . zadd ( [ key , now , now ] )
71+ . zrange ( [ key , 0 , 0 ] )
72+ . pexpire ( [ key , duration ] )
73+ . exec ( function ( err , res ) {
12174 if ( err ) return fn ( err ) ;
122- db . mget ( [ count , limit , reset ] , function ( err , res ) {
123- if ( err ) return fn ( err ) ;
124- if ( ! res [ 0 ] && res [ 0 ] !== 0 ) return create ( ) ;
125-
126- decr ( res ) ;
75+ var count = parseInt ( Array . isArray ( res [ 0 ] ) ? res [ 1 ] [ 1 ] : res [ 1 ] ) ;
76+ var oldest = parseInt ( Array . isArray ( res [ 0 ] ) ? res [ 3 ] [ 1 ] : res [ 3 ] ) ;
77+ fn ( null , {
78+ remaining : count < max ? max - count : 0 ,
79+ reset : Math . floor ( ( oldest + duration ) / 1000000 ) ,
80+ total : max
12781 } ) ;
12882 } ) ;
129- }
130-
131- mget ( ) ;
13283} ;
13384
13485/**
0 commit comments