redistat 0.3.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|