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
@@ -1,10 +1,10 @@
|
|
1
1
|
module Redistat
|
2
2
|
module Options
|
3
|
-
|
3
|
+
|
4
4
|
def self.included(base)
|
5
5
|
base.extend(ClassMethods)
|
6
6
|
end
|
7
|
-
|
7
|
+
|
8
8
|
module ClassMethods
|
9
9
|
def option_accessor(*opts)
|
10
10
|
opts.each do |option|
|
@@ -18,24 +18,24 @@ module Redistat
|
|
18
18
|
end
|
19
19
|
end
|
20
20
|
end
|
21
|
-
|
21
|
+
|
22
22
|
def parse_options(opts)
|
23
23
|
opts ||= {}
|
24
24
|
@raw_options = opts
|
25
25
|
@options = default_options.merge(opts.reject { |k,v| v.nil? })
|
26
26
|
end
|
27
|
-
|
27
|
+
|
28
28
|
def default_options
|
29
29
|
{}
|
30
30
|
end
|
31
|
-
|
31
|
+
|
32
32
|
def options
|
33
33
|
@options ||= {}
|
34
34
|
end
|
35
|
-
|
35
|
+
|
36
36
|
def raw_options
|
37
37
|
@raw_options ||= {}
|
38
38
|
end
|
39
|
-
|
39
|
+
|
40
40
|
end
|
41
|
-
end
|
41
|
+
end
|
@@ -2,42 +2,43 @@ require 'monitor'
|
|
2
2
|
|
3
3
|
module Redistat
|
4
4
|
module Synchronize
|
5
|
-
|
5
|
+
|
6
6
|
class << self
|
7
7
|
def included(base)
|
8
8
|
base.send(:include, InstanceMethods)
|
9
9
|
end
|
10
|
-
|
10
|
+
|
11
11
|
def monitor
|
12
12
|
@monitor ||= Monitor.new
|
13
13
|
end
|
14
|
-
|
14
|
+
|
15
15
|
def thread_safe
|
16
16
|
monitor.synchronize do
|
17
|
-
@thread_safe
|
17
|
+
return @thread_safe unless @thread_safe.nil?
|
18
|
+
@thread_safe = false
|
18
19
|
end
|
19
20
|
end
|
20
|
-
|
21
|
+
|
21
22
|
def thread_safe=(value)
|
22
23
|
monitor.synchronize do
|
23
24
|
@thread_safe = value
|
24
25
|
end
|
25
26
|
end
|
26
27
|
end # << self
|
27
|
-
|
28
|
+
|
28
29
|
module InstanceMethods
|
29
30
|
def thread_safe
|
30
31
|
Synchronize.thread_safe
|
31
32
|
end
|
32
|
-
|
33
|
+
|
33
34
|
def thread_safe=(value)
|
34
35
|
Synchronize.thread_safe = value
|
35
36
|
end
|
36
|
-
|
37
|
+
|
37
38
|
def monitor
|
38
39
|
Synchronize.monitor
|
39
40
|
end
|
40
|
-
|
41
|
+
|
41
42
|
def synchronize(&block)
|
42
43
|
if thread_safe
|
43
44
|
monitor.synchronize(&block)
|
@@ -46,6 +47,6 @@ module Redistat
|
|
46
47
|
end
|
47
48
|
end
|
48
49
|
end # InstanceMethods
|
49
|
-
|
50
|
+
|
50
51
|
end
|
51
|
-
end
|
52
|
+
end
|
data/lib/redistat/model.rb
CHANGED
@@ -2,16 +2,16 @@ module Redistat
|
|
2
2
|
module Model
|
3
3
|
include Database
|
4
4
|
include Options
|
5
|
-
|
5
|
+
|
6
6
|
def self.included(base)
|
7
7
|
base.extend(self)
|
8
8
|
end
|
9
|
-
|
10
|
-
|
9
|
+
|
10
|
+
|
11
11
|
#
|
12
12
|
# statistics store/fetch methods
|
13
|
-
#
|
14
|
-
|
13
|
+
#
|
14
|
+
|
15
15
|
def store(label, stats = {}, date = nil, opts = {}, meta = {})
|
16
16
|
Event.new(self.name, label, date, stats, options.merge(opts), meta).save
|
17
17
|
end
|
@@ -28,42 +28,50 @@ module Redistat
|
|
28
28
|
:from => from,
|
29
29
|
:till => till }.merge(options.merge(opts)) )
|
30
30
|
end
|
31
|
-
|
31
|
+
|
32
32
|
def find_event(event_id)
|
33
33
|
Event.find(self.name, event_id)
|
34
34
|
end
|
35
|
-
|
36
|
-
|
35
|
+
|
36
|
+
|
37
37
|
#
|
38
38
|
# options methods
|
39
39
|
#
|
40
|
-
|
40
|
+
|
41
41
|
option_accessor :depth
|
42
42
|
option_accessor :scope
|
43
43
|
option_accessor :store_event
|
44
44
|
option_accessor :hashed_label
|
45
45
|
option_accessor :label_indexing
|
46
|
-
|
46
|
+
|
47
47
|
alias :class_name :scope
|
48
|
-
|
48
|
+
|
49
|
+
def expire(exp = nil)
|
50
|
+
if !exp.nil?
|
51
|
+
options[:expire] = exp.is_a?(Hash) ? exp : Hash.new(exp)
|
52
|
+
else
|
53
|
+
options[:expire]
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
49
57
|
def connect_to(opts = {})
|
50
58
|
Connection.create(opts.merge(:ref => name))
|
51
59
|
options[:connection_ref] = name
|
52
60
|
end
|
53
|
-
|
54
|
-
|
61
|
+
|
62
|
+
|
55
63
|
#
|
56
64
|
# resource access methods
|
57
65
|
#
|
58
|
-
|
66
|
+
|
59
67
|
def connection
|
60
68
|
db(options[:connection_ref])
|
61
69
|
end
|
62
70
|
alias :redis :connection
|
63
|
-
|
71
|
+
|
64
72
|
def name
|
65
73
|
options[:scope] || (@name ||= self.to_s)
|
66
74
|
end
|
67
|
-
|
75
|
+
|
68
76
|
end
|
69
|
-
end
|
77
|
+
end
|
data/lib/redistat/result.rb
CHANGED
@@ -2,17 +2,17 @@ require 'active_support/core_ext/hash/indifferent_access'
|
|
2
2
|
|
3
3
|
module Redistat
|
4
4
|
class Result < HashWithIndifferentAccess
|
5
|
-
|
5
|
+
|
6
6
|
attr_accessor :from
|
7
7
|
attr_accessor :till
|
8
|
-
|
8
|
+
|
9
9
|
alias :date :from
|
10
10
|
alias :date= :from=
|
11
|
-
|
11
|
+
|
12
12
|
def initialize(options = {})
|
13
13
|
@from = options[:from] ||= nil
|
14
14
|
@till = options[:till] ||= nil
|
15
15
|
end
|
16
|
-
|
16
|
+
|
17
17
|
end
|
18
18
|
end
|
data/lib/redistat/scope.rb
CHANGED
data/lib/redistat/summary.rb
CHANGED
@@ -1,61 +1,68 @@
|
|
1
1
|
module Redistat
|
2
2
|
class Summary
|
3
3
|
include Database
|
4
|
-
|
4
|
+
|
5
5
|
class << self
|
6
|
-
|
6
|
+
|
7
7
|
def default_options
|
8
|
-
{
|
9
|
-
:
|
10
|
-
:
|
8
|
+
{
|
9
|
+
:enable_grouping => true,
|
10
|
+
:label_indexing => true,
|
11
|
+
:connection_ref => nil,
|
12
|
+
:expire => {}
|
13
|
+
}
|
11
14
|
end
|
12
|
-
|
15
|
+
|
13
16
|
def buffer
|
14
17
|
Redistat.buffer
|
15
18
|
end
|
16
|
-
|
19
|
+
|
17
20
|
def update_all(key, stats = {}, depth_limit = nil, opts = {})
|
18
21
|
stats ||= {}
|
19
22
|
return if stats.empty?
|
20
|
-
|
23
|
+
|
21
24
|
options = default_options.merge((opts || {}).reject { |k,v| v.nil? })
|
22
|
-
|
25
|
+
|
23
26
|
depth_limit ||= key.depth
|
24
|
-
|
27
|
+
|
25
28
|
update_through_buffer(key, stats, depth_limit, options)
|
26
29
|
end
|
27
|
-
|
30
|
+
|
28
31
|
def update_through_buffer(*args)
|
29
32
|
update(*args) unless buffer.store(*args)
|
30
33
|
end
|
31
|
-
|
32
|
-
def update(key, stats, depth_limit, opts)
|
34
|
+
|
35
|
+
def update(key, stats, depth_limit, opts = {})
|
33
36
|
if opts[:enable_grouping]
|
34
37
|
stats = inject_group_summaries(stats)
|
35
38
|
key.groups.each do |k|
|
36
|
-
update_key(k, stats, depth_limit, opts
|
39
|
+
update_key(k, stats, depth_limit, opts)
|
37
40
|
k.update_index if opts[:label_indexing]
|
38
41
|
end
|
39
42
|
else
|
40
|
-
update_key(key, stats, depth_limit, opts
|
43
|
+
update_key(key, stats, depth_limit, opts)
|
41
44
|
end
|
42
45
|
end
|
43
|
-
|
46
|
+
|
44
47
|
private
|
45
|
-
|
46
|
-
def update_key(key, stats, depth_limit,
|
48
|
+
|
49
|
+
def update_key(key, stats, depth_limit, opts = {})
|
47
50
|
Date::DEPTHS.each do |depth|
|
48
|
-
update_fields(key, stats, depth,
|
51
|
+
update_fields(key, stats, depth, opts)
|
49
52
|
break if depth == depth_limit
|
50
53
|
end
|
51
54
|
end
|
52
|
-
|
53
|
-
def update_fields(key, stats, depth,
|
55
|
+
|
56
|
+
def update_fields(key, stats, depth, opts = {})
|
54
57
|
stats.each do |field, value|
|
55
|
-
db(connection_ref).hincrby key.to_s(depth), field, value
|
58
|
+
db(opts[:connection_ref]).hincrby key.to_s(depth), field, value
|
59
|
+
end
|
60
|
+
|
61
|
+
if opts[:expire] && !opts[:expire][depth].nil?
|
62
|
+
db(opts[:connection_ref]).expire key.to_s(depth), opts[:expire][depth]
|
56
63
|
end
|
57
64
|
end
|
58
|
-
|
65
|
+
|
59
66
|
def inject_group_summaries!(stats)
|
60
67
|
summaries = {}
|
61
68
|
stats.each do |key, value|
|
@@ -72,11 +79,11 @@ module Redistat
|
|
72
79
|
end
|
73
80
|
stats.merge_and_incr!(summaries)
|
74
81
|
end
|
75
|
-
|
82
|
+
|
76
83
|
def inject_group_summaries(stats)
|
77
84
|
inject_group_summaries!(stats.clone)
|
78
85
|
end
|
79
|
-
|
86
|
+
|
80
87
|
end
|
81
88
|
end
|
82
|
-
end
|
89
|
+
end
|
data/lib/redistat/version.rb
CHANGED
data/redistat.gemspec
CHANGED
@@ -18,13 +18,14 @@ Gem::Specification.new do |s|
|
|
18
18
|
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
19
19
|
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
20
20
|
s.require_paths = ["lib"]
|
21
|
-
|
21
|
+
|
22
22
|
s.add_runtime_dependency 'activesupport', '>= 2.3.6'
|
23
23
|
s.add_runtime_dependency 'json', '>= 1.4.0'
|
24
24
|
s.add_runtime_dependency 'redis', '>= 2.1.0'
|
25
25
|
s.add_runtime_dependency 'time_ext', '>= 0.2.9'
|
26
|
-
|
26
|
+
|
27
|
+
s.add_development_dependency 'rake', '>= 0.8.7'
|
27
28
|
s.add_development_dependency 'rspec', '>= 2.1.0'
|
28
|
-
s.add_development_dependency 'rcov', '>= 0.9.9'
|
29
29
|
s.add_development_dependency 'yard', '>= 0.6.3'
|
30
|
+
s.add_development_dependency 'simplecov', '>= 0.6.1'
|
30
31
|
end
|
data/spec/buffer_spec.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
require "spec_helper"
|
2
2
|
|
3
3
|
describe Redistat::Buffer do
|
4
|
-
|
4
|
+
|
5
5
|
before(:each) do
|
6
6
|
@class = Redistat::Buffer
|
7
7
|
@buffer = Redistat::Buffer.instance
|
@@ -10,17 +10,17 @@ describe Redistat::Buffer do
|
|
10
10
|
@depth_limit = :hour
|
11
11
|
@opts = {:enable_grouping => true}
|
12
12
|
end
|
13
|
-
|
13
|
+
|
14
14
|
# let's cleanup after ourselves for the other specs
|
15
15
|
after(:each) do
|
16
16
|
@class.instance_variable_set("@instance", nil)
|
17
17
|
@buffer.size = 0
|
18
18
|
end
|
19
|
-
|
19
|
+
|
20
20
|
it "should provide instance of itself" do
|
21
21
|
@buffer.should be_a(@class)
|
22
22
|
end
|
23
|
-
|
23
|
+
|
24
24
|
it "should only buffer if buffer size setting is greater than 1" do
|
25
25
|
@buffer.size.should == 0
|
26
26
|
@buffer.send(:should_buffer?).should be_false
|
@@ -31,7 +31,7 @@ describe Redistat::Buffer do
|
|
31
31
|
@buffer.size.should == 2
|
32
32
|
@buffer.send(:should_buffer?).should be_true
|
33
33
|
end
|
34
|
-
|
34
|
+
|
35
35
|
it "should only flush buffer if buffer size is greater than or equal to buffer size setting" do
|
36
36
|
@buffer.size.should == 0
|
37
37
|
@buffer.send(:queue).size.should == 0
|
@@ -50,7 +50,7 @@ describe Redistat::Buffer do
|
|
50
50
|
@buffer.send(:incr_count)
|
51
51
|
@buffer.send(:should_flush?).should be_true
|
52
52
|
end
|
53
|
-
|
53
|
+
|
54
54
|
it "should force flush queue irregardless of result of #should_flush? when #reset_queue is called with true" do
|
55
55
|
@buffer.send(:queue)[:hello] = 'world'
|
56
56
|
@buffer.send(:incr_count)
|
@@ -62,7 +62,7 @@ describe Redistat::Buffer do
|
|
62
62
|
@buffer.send(:reset_queue, true).should == {:hello => 'world'}
|
63
63
|
@buffer.instance_variable_get("@count").should == 0
|
64
64
|
end
|
65
|
-
|
65
|
+
|
66
66
|
it "should #flush_data into Summary.update properly" do
|
67
67
|
# the root level key value doesn't actually matter, but it's something like this...
|
68
68
|
data = {'ScopeName/label/goes/here:2011::true:true' => {
|
@@ -75,14 +75,16 @@ describe Redistat::Buffer do
|
|
75
75
|
Redistat::Summary.should_receive(:update).with(@key, @stats, @depth_limit, @opts)
|
76
76
|
@buffer.send(:flush_data, data)
|
77
77
|
end
|
78
|
-
|
78
|
+
|
79
79
|
it "should build #buffer_key correctly" do
|
80
80
|
opts = {:enable_grouping => true, :label_indexing => false, :connection_ref => nil}
|
81
|
-
@buffer.send(:buffer_key, @key, opts).should ==
|
81
|
+
@buffer.send(:buffer_key, @key, opts).should ==
|
82
|
+
"#{@key.to_s}:connection_ref::enable_grouping:true:label_indexing:false"
|
82
83
|
opts = {:enable_grouping => false, :label_indexing => true, :connection_ref => :omg}
|
83
|
-
@buffer.send(:buffer_key, @key, opts).should ==
|
84
|
+
@buffer.send(:buffer_key, @key, opts).should ==
|
85
|
+
"#{@key.to_s}:connection_ref:omg:enable_grouping:false:label_indexing:true"
|
84
86
|
end
|
85
|
-
|
87
|
+
|
86
88
|
describe "Buffering" do
|
87
89
|
it "should store items on buffer queue" do
|
88
90
|
@buffer.store(@key, @stats, @depth_limit, @opts).should be_false
|
@@ -96,7 +98,7 @@ describe Redistat::Buffer do
|
|
96
98
|
@buffer.send(:queue)[@buffer.send(:queue).keys.first][:stats][:count].should == 2
|
97
99
|
@buffer.send(:queue)[@buffer.send(:queue).keys.first][:stats][:views].should == 6
|
98
100
|
end
|
99
|
-
|
101
|
+
|
100
102
|
it "should flush buffer queue when size is reached" do
|
101
103
|
key = mock('Key', :to_s => "Scope/labelx:2011")
|
102
104
|
@buffer.size = 10
|
@@ -115,18 +117,18 @@ describe Redistat::Buffer do
|
|
115
117
|
4.times { @buffer.store(key, @stats, @depth_limit, @opts).should be_true }
|
116
118
|
end
|
117
119
|
end
|
118
|
-
|
120
|
+
|
119
121
|
describe "Thread-Safety" do
|
120
122
|
it "should read/write to buffer queue in a thread-safe manner" do
|
121
|
-
|
122
|
-
#
|
123
|
-
#
|
124
|
-
#
|
123
|
+
|
124
|
+
# Setting thread_safe to false only makes the spec fail with
|
125
|
+
# JRuby. 1.8.x and 1.9.x both pass fine for some reason
|
126
|
+
# regardless of what the thread_safe option is set to.
|
125
127
|
Redistat.thread_safe = true
|
126
|
-
|
128
|
+
|
127
129
|
key = mock('Key', :to_s => "Scope/labelx:2011")
|
128
130
|
@buffer.size = 100
|
129
|
-
|
131
|
+
|
130
132
|
Redistat::Summary.should_receive(:update).exactly(2).times.and_return do |k, stats, depth_limit, opts|
|
131
133
|
depth_limit.should == @depth_limit
|
132
134
|
opts.should == @opts
|
@@ -138,7 +140,7 @@ describe Redistat::Buffer do
|
|
138
140
|
stats[:views].should == 120
|
139
141
|
end
|
140
142
|
end
|
141
|
-
|
143
|
+
|
142
144
|
threads = []
|
143
145
|
10.times do
|
144
146
|
threads << Thread.new {
|
@@ -146,12 +148,12 @@ describe Redistat::Buffer do
|
|
146
148
|
4.times { @buffer.store(key, @stats, @depth_limit, @opts).should be_true }
|
147
149
|
}
|
148
150
|
end
|
149
|
-
|
151
|
+
|
150
152
|
threads.each { |t| t.join }
|
151
153
|
end
|
152
|
-
|
153
|
-
it "should have
|
154
|
-
|
154
|
+
|
155
|
+
it "should have specs that fail on 1.8.x/1.9.x when thread_safe is disabled"
|
156
|
+
|
155
157
|
end
|
156
|
-
|
158
|
+
|
157
159
|
end
|