redistat 0.3.0 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.travis.yml +8 -0
- data/README.md +219 -97
- data/lib/redistat.rb +13 -13
- data/lib/redistat/buffer.rb +27 -24
- data/lib/redistat/collection.rb +5 -5
- data/lib/redistat/connection.rb +23 -18
- data/lib/redistat/core_ext.rb +1 -1
- data/lib/redistat/core_ext/bignum.rb +2 -2
- data/lib/redistat/core_ext/date.rb +2 -2
- data/lib/redistat/core_ext/fixnum.rb +2 -2
- data/lib/redistat/core_ext/hash.rb +4 -4
- data/lib/redistat/date.rb +11 -11
- data/lib/redistat/event.rb +18 -18
- data/lib/redistat/finder.rb +39 -39
- data/lib/redistat/finder/date_set.rb +4 -4
- data/lib/redistat/key.rb +16 -16
- data/lib/redistat/label.rb +14 -14
- data/lib/redistat/mixins/database.rb +1 -1
- data/lib/redistat/mixins/date_helper.rb +1 -1
- data/lib/redistat/mixins/options.rb +8 -8
- data/lib/redistat/mixins/synchronize.rb +12 -11
- data/lib/redistat/model.rb +25 -17
- data/lib/redistat/result.rb +4 -4
- data/lib/redistat/scope.rb +5 -5
- data/lib/redistat/summary.rb +33 -26
- data/lib/redistat/version.rb +1 -1
- data/redistat.gemspec +4 -3
- data/spec/buffer_spec.rb +27 -25
- data/spec/collection_spec.rb +4 -4
- data/spec/connection_spec.rb +12 -12
- data/spec/core_ext/hash_spec.rb +5 -5
- data/spec/database_spec.rb +3 -3
- data/spec/date_spec.rb +15 -15
- data/spec/event_spec.rb +8 -8
- data/spec/finder/date_set_spec.rb +134 -134
- data/spec/finder_spec.rb +36 -36
- data/spec/key_spec.rb +19 -19
- data/spec/label_spec.rb +10 -10
- data/spec/model_helper.rb +10 -9
- data/spec/model_spec.rb +38 -41
- data/spec/options_spec.rb +9 -9
- data/spec/result_spec.rb +4 -4
- data/spec/scope_spec.rb +6 -6
- data/spec/spec_helper.rb +6 -0
- data/spec/summary_spec.rb +31 -24
- data/spec/synchronize_spec.rb +118 -57
- data/spec/thread_safety_spec.rb +6 -6
- metadata +88 -126
- data/.rvmrc +0 -1
data/lib/redistat/buffer.rb
CHANGED
@@ -3,33 +3,33 @@ require 'redistat/core_ext/hash'
|
|
3
3
|
module Redistat
|
4
4
|
class Buffer
|
5
5
|
include Synchronize
|
6
|
-
|
6
|
+
|
7
7
|
def self.instance
|
8
8
|
@instance ||= self.new
|
9
9
|
end
|
10
|
-
|
10
|
+
|
11
11
|
def size
|
12
12
|
synchronize do
|
13
13
|
@size ||= 0
|
14
14
|
end
|
15
15
|
end
|
16
|
-
|
16
|
+
|
17
17
|
def size=(value)
|
18
18
|
synchronize do
|
19
19
|
@size = value
|
20
20
|
end
|
21
21
|
end
|
22
|
-
|
22
|
+
|
23
23
|
def count
|
24
24
|
@count ||= 0
|
25
25
|
end
|
26
|
-
|
26
|
+
|
27
27
|
def store(key, stats, depth_limit, opts)
|
28
28
|
return false unless should_buffer?
|
29
|
-
|
29
|
+
|
30
30
|
to_flush = {}
|
31
31
|
buffkey = buffer_key(key, opts)
|
32
|
-
|
32
|
+
|
33
33
|
synchronize do
|
34
34
|
if !queue.has_key?(buffkey)
|
35
35
|
queue[buffkey] = { :key => key,
|
@@ -37,19 +37,19 @@ module Redistat
|
|
37
37
|
:depth_limit => depth_limit,
|
38
38
|
:opts => opts }
|
39
39
|
end
|
40
|
-
|
40
|
+
|
41
41
|
queue[buffkey][:stats].merge_and_incr!(stats)
|
42
42
|
incr_count
|
43
|
-
|
43
|
+
|
44
44
|
# return items to be flushed if buffer size limit has been reached
|
45
45
|
to_flush = reset_queue
|
46
46
|
end
|
47
|
-
|
47
|
+
|
48
48
|
# flush any data that's been cleared from the queue
|
49
49
|
flush_data(to_flush)
|
50
50
|
true
|
51
51
|
end
|
52
|
-
|
52
|
+
|
53
53
|
def flush(force = false)
|
54
54
|
to_flush = {}
|
55
55
|
synchronize do
|
@@ -57,28 +57,28 @@ module Redistat
|
|
57
57
|
end
|
58
58
|
flush_data(to_flush)
|
59
59
|
end
|
60
|
-
|
60
|
+
|
61
61
|
private
|
62
|
-
|
62
|
+
|
63
63
|
# should always be called from within a synchronize block
|
64
64
|
def incr_count
|
65
65
|
@count ||= 0
|
66
66
|
@count += 1
|
67
67
|
end
|
68
|
-
|
68
|
+
|
69
69
|
def queue
|
70
70
|
@queue ||= {}
|
71
71
|
end
|
72
|
-
|
72
|
+
|
73
73
|
def should_buffer?
|
74
74
|
size > 1 # buffer size of 1 would be equal to not using buffer
|
75
75
|
end
|
76
|
-
|
76
|
+
|
77
77
|
# should always be called from within a synchronize block
|
78
78
|
def should_flush?
|
79
79
|
(!queue.blank? && count >= size)
|
80
80
|
end
|
81
|
-
|
81
|
+
|
82
82
|
# returns items to be flushed if buffer size limit has been reached
|
83
83
|
# should always be called from within a synchronize block
|
84
84
|
def reset_queue(force = false)
|
@@ -88,20 +88,23 @@ module Redistat
|
|
88
88
|
@count = 0
|
89
89
|
data
|
90
90
|
end
|
91
|
-
|
91
|
+
|
92
92
|
def flush_data(buffer_data)
|
93
93
|
buffer_data.each do |k, item|
|
94
94
|
Summary.update(item[:key], item[:stats], item[:depth_limit], item[:opts])
|
95
95
|
end
|
96
96
|
end
|
97
|
-
|
97
|
+
|
98
|
+
# depth_limit is not needed as it's evident in key.to_s
|
98
99
|
def buffer_key(key, opts)
|
99
|
-
#
|
100
|
-
|
101
|
-
|
100
|
+
# covert keys to strings, as sorting a Hash with Symbol keys fails on
|
101
|
+
# Ruby 1.8.x.
|
102
|
+
opts = opts.inject({}) do |result, (k, v)|
|
103
|
+
result[k.to_s] = v
|
104
|
+
result
|
102
105
|
end
|
103
|
-
"#{key.to_s}:#{
|
106
|
+
"#{key.to_s}:#{opts.sort.flatten.join(':')}"
|
104
107
|
end
|
105
|
-
|
108
|
+
|
106
109
|
end
|
107
110
|
end
|
data/lib/redistat/collection.rb
CHANGED
@@ -1,20 +1,20 @@
|
|
1
1
|
module Redistat
|
2
2
|
class Collection < ::Array
|
3
|
-
|
3
|
+
|
4
4
|
attr_accessor :from
|
5
5
|
attr_accessor :till
|
6
6
|
attr_accessor :depth
|
7
7
|
attr_accessor :total
|
8
|
-
|
8
|
+
|
9
9
|
def initialize(options = {})
|
10
10
|
@from = options[:from] ||= nil
|
11
11
|
@till = options[:till] ||= nil
|
12
12
|
@depth = options[:depth] ||= nil
|
13
13
|
end
|
14
|
-
|
14
|
+
|
15
15
|
def total
|
16
16
|
@total ||= {}
|
17
17
|
end
|
18
|
-
|
18
|
+
|
19
19
|
end
|
20
|
-
end
|
20
|
+
end
|
data/lib/redistat/connection.rb
CHANGED
@@ -2,22 +2,23 @@ require 'monitor'
|
|
2
2
|
|
3
3
|
module Redistat
|
4
4
|
module Connection
|
5
|
-
|
5
|
+
|
6
6
|
REQUIRED_SERVER_VERSION = "1.3.10"
|
7
|
-
|
7
|
+
MIN_EXPIRE_SERVER_VERSION = "2.1.3"
|
8
|
+
|
8
9
|
# TODO: Create a ConnectionPool instance object using Sychronize mixin to replace Connection class
|
9
|
-
|
10
|
+
|
10
11
|
class << self
|
11
|
-
|
12
|
+
|
12
13
|
# TODO: clean/remove all ref-less connections
|
13
|
-
|
14
|
+
|
14
15
|
def get(ref = nil)
|
15
16
|
ref ||= :default
|
16
17
|
synchronize do
|
17
18
|
connections[references[ref]] || create
|
18
19
|
end
|
19
20
|
end
|
20
|
-
|
21
|
+
|
21
22
|
def add(conn, ref = nil)
|
22
23
|
ref ||= :default
|
23
24
|
synchronize do
|
@@ -26,7 +27,7 @@ module Redistat
|
|
26
27
|
connections[conn.client.id] = conn
|
27
28
|
end
|
28
29
|
end
|
29
|
-
|
30
|
+
|
30
31
|
def create(options = {})
|
31
32
|
synchronize do
|
32
33
|
options = options.clone
|
@@ -37,39 +38,43 @@ module Redistat
|
|
37
38
|
conn
|
38
39
|
end
|
39
40
|
end
|
40
|
-
|
41
|
+
|
41
42
|
def connections
|
42
43
|
@connections ||= {}
|
43
44
|
end
|
44
|
-
|
45
|
+
|
45
46
|
def references
|
46
47
|
@references ||= {}
|
47
48
|
end
|
48
|
-
|
49
|
+
|
49
50
|
private
|
50
|
-
|
51
|
+
|
51
52
|
def monitor
|
52
53
|
@monitor ||= Monitor.new
|
53
54
|
end
|
54
|
-
|
55
|
+
|
55
56
|
def synchronize(&block)
|
56
57
|
monitor.synchronize(&block)
|
57
58
|
end
|
58
|
-
|
59
|
+
|
59
60
|
def connection(options)
|
60
61
|
check_redis_version(Redis.new(options))
|
61
62
|
end
|
62
|
-
|
63
|
+
|
63
64
|
def connection_id(options = {})
|
64
65
|
options = options.reverse_merge(default_options)
|
65
66
|
"redis://#{options[:host]}:#{options[:port]}/#{options[:db]}"
|
66
67
|
end
|
67
|
-
|
68
|
+
|
68
69
|
def check_redis_version(conn)
|
69
70
|
raise RedisServerIsTooOld if conn.info["redis_version"] < REQUIRED_SERVER_VERSION
|
71
|
+
if conn.info["redis_version"] < MIN_EXPIRE_SERVER_VERSION
|
72
|
+
STDOUT.puts "WARNING: You MUST upgrade Redis to v2.1.3 or later " +
|
73
|
+
"if you are using key expiry."
|
74
|
+
end
|
70
75
|
conn
|
71
76
|
end
|
72
|
-
|
77
|
+
|
73
78
|
def default_options
|
74
79
|
{
|
75
80
|
:host => '127.0.0.1',
|
@@ -78,7 +83,7 @@ module Redistat
|
|
78
83
|
:timeout => 5
|
79
84
|
}
|
80
85
|
end
|
81
|
-
|
86
|
+
|
82
87
|
end
|
83
88
|
end
|
84
|
-
end
|
89
|
+
end
|
data/lib/redistat/core_ext.rb
CHANGED
@@ -1,9 +1,9 @@
|
|
1
1
|
class Hash
|
2
|
-
|
2
|
+
|
3
3
|
def merge_and_incr(hash)
|
4
4
|
self.clone.merge_and_incr!(hash)
|
5
5
|
end
|
6
|
-
|
6
|
+
|
7
7
|
def merge_and_incr!(hash)
|
8
8
|
raise ArgumentError unless hash.is_a?(Hash)
|
9
9
|
hash.each do |key, value|
|
@@ -11,7 +11,7 @@ class Hash
|
|
11
11
|
end
|
12
12
|
self
|
13
13
|
end
|
14
|
-
|
14
|
+
|
15
15
|
def set_or_incr(key, value)
|
16
16
|
return false unless value.is_a?(Numeric)
|
17
17
|
self[key] = 0 unless self.has_key?(key)
|
@@ -19,5 +19,5 @@ class Hash
|
|
19
19
|
self[key] += value
|
20
20
|
true
|
21
21
|
end
|
22
|
-
|
22
|
+
|
23
23
|
end
|
data/lib/redistat/date.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
module Redistat
|
2
2
|
class Date
|
3
|
-
|
3
|
+
|
4
4
|
attr_accessor :year
|
5
5
|
attr_accessor :month
|
6
6
|
attr_accessor :day
|
@@ -9,9 +9,9 @@ module Redistat
|
|
9
9
|
attr_accessor :sec
|
10
10
|
attr_accessor :usec
|
11
11
|
attr_accessor :depth
|
12
|
-
|
12
|
+
|
13
13
|
DEPTHS = [:year, :month, :day, :hour, :min, :sec, :usec]
|
14
|
-
|
14
|
+
|
15
15
|
def initialize(input, depth = nil)
|
16
16
|
@depth = depth
|
17
17
|
if input.is_a?(::Time)
|
@@ -26,12 +26,12 @@ module Redistat
|
|
26
26
|
from_integer(input)
|
27
27
|
end
|
28
28
|
end
|
29
|
-
|
29
|
+
|
30
30
|
def to_t
|
31
31
|
::Time.local(@year, @month, @day, @hour, @min, @sec, @usec)
|
32
32
|
end
|
33
33
|
alias :to_time :to_t
|
34
|
-
|
34
|
+
|
35
35
|
def to_d
|
36
36
|
::Date.civil(@year, @month, @day)
|
37
37
|
end
|
@@ -41,7 +41,7 @@ module Redistat
|
|
41
41
|
to_time.to_i
|
42
42
|
end
|
43
43
|
alias :to_integer :to_i
|
44
|
-
|
44
|
+
|
45
45
|
def to_s(depth = nil)
|
46
46
|
depth ||= @depth ||= :sec
|
47
47
|
output = ""
|
@@ -57,9 +57,9 @@ module Redistat
|
|
57
57
|
output
|
58
58
|
end
|
59
59
|
alias :to_string :to_s
|
60
|
-
|
60
|
+
|
61
61
|
private
|
62
|
-
|
62
|
+
|
63
63
|
def from_time(input)
|
64
64
|
DEPTHS.each do |k|
|
65
65
|
send("#{k}=", input.send(k))
|
@@ -74,15 +74,15 @@ module Redistat
|
|
74
74
|
send("#{k}=", 0)
|
75
75
|
end
|
76
76
|
end
|
77
|
-
|
77
|
+
|
78
78
|
def from_integer(input)
|
79
79
|
from_time(::Time.at(input))
|
80
80
|
end
|
81
|
-
|
81
|
+
|
82
82
|
def from_string(input)
|
83
83
|
input += "19700101000000"[input.size..-1] if input =~ /^\d\d\d[\d]+$/i
|
84
84
|
from_time(::Time.parse(input))
|
85
85
|
end
|
86
|
-
|
86
|
+
|
87
87
|
end
|
88
88
|
end
|
data/lib/redistat/event.rb
CHANGED
@@ -2,13 +2,13 @@ module Redistat
|
|
2
2
|
class Event
|
3
3
|
include Database
|
4
4
|
include Options
|
5
|
-
|
5
|
+
|
6
6
|
attr_reader :id
|
7
7
|
attr_reader :key
|
8
|
-
|
8
|
+
|
9
9
|
attr_accessor :stats
|
10
10
|
attr_accessor :meta
|
11
|
-
|
11
|
+
|
12
12
|
def default_options
|
13
13
|
{ :depth => :hour,
|
14
14
|
:store_event => false,
|
@@ -16,7 +16,7 @@ module Redistat
|
|
16
16
|
:enable_grouping => true,
|
17
17
|
:label_indexing => true }
|
18
18
|
end
|
19
|
-
|
19
|
+
|
20
20
|
def initialize(scope, label = nil, date = nil, stats = {}, opts = {}, meta = {}, is_new = true)
|
21
21
|
parse_options(opts)
|
22
22
|
@key = Key.new(scope, label, date, @options)
|
@@ -24,35 +24,35 @@ module Redistat
|
|
24
24
|
@meta = meta ||= {}
|
25
25
|
@new = is_new
|
26
26
|
end
|
27
|
-
|
27
|
+
|
28
28
|
def new?
|
29
29
|
@new
|
30
30
|
end
|
31
|
-
|
31
|
+
|
32
32
|
def date
|
33
33
|
@key.date
|
34
34
|
end
|
35
|
-
|
35
|
+
|
36
36
|
def date=(input)
|
37
37
|
@key.date = input
|
38
38
|
end
|
39
|
-
|
39
|
+
|
40
40
|
def scope
|
41
41
|
@key.scope
|
42
42
|
end
|
43
|
-
|
43
|
+
|
44
44
|
def scope=(input)
|
45
45
|
@key.scope = input
|
46
46
|
end
|
47
|
-
|
47
|
+
|
48
48
|
def label
|
49
49
|
@key.label
|
50
50
|
end
|
51
|
-
|
51
|
+
|
52
52
|
def label_hash
|
53
53
|
@key.label_hash
|
54
54
|
end
|
55
|
-
|
55
|
+
|
56
56
|
def label=(input)
|
57
57
|
@key.label = input
|
58
58
|
end
|
@@ -60,7 +60,7 @@ module Redistat
|
|
60
60
|
def next_id
|
61
61
|
db.incr("#{self.scope}#{KEY_NEXT_ID}")
|
62
62
|
end
|
63
|
-
|
63
|
+
|
64
64
|
def save
|
65
65
|
return false if !self.new?
|
66
66
|
Summary.update_all(@key, @stats, depth_limit, @options)
|
@@ -78,21 +78,21 @@ module Redistat
|
|
78
78
|
@new = false
|
79
79
|
self
|
80
80
|
end
|
81
|
-
|
81
|
+
|
82
82
|
def depth_limit
|
83
83
|
@options[:depth] ||= @key.depth
|
84
84
|
end
|
85
|
-
|
85
|
+
|
86
86
|
def self.create(*args)
|
87
87
|
self.new(*args).save
|
88
88
|
end
|
89
|
-
|
89
|
+
|
90
90
|
def self.find(scope, id)
|
91
91
|
event = db.hgetall "#{scope}#{KEY_EVENT}#{id}"
|
92
92
|
return nil if event.size == 0
|
93
93
|
self.new( event["scope"], event["label"], event["date"], JSON.parse(event["stats"]),
|
94
94
|
JSON.parse(event["options"]), JSON.parse(event["meta"]), false )
|
95
95
|
end
|
96
|
-
|
96
|
+
|
97
97
|
end
|
98
|
-
end
|
98
|
+
end
|