nightfury 0.5 → 0.6
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/nightfury.rb +14 -0
- data/lib/nightfury/identity.rb +6 -0
- data/lib/nightfury/metric.rb +5 -1
- data/lib/nightfury/metric/time_series.rb +15 -13
- data/lib/nightfury/version.rb +1 -1
- data/spec/nightfury/identity_spec.rb +15 -0
- data/spec/nightfury/metric/count_time_series_spec.rb +15 -15
- data/spec/nightfury/metric/time_series_spec.rb +15 -29
- metadata +2 -2
data/lib/nightfury.rb
CHANGED
@@ -4,8 +4,22 @@ require 'json'
|
|
4
4
|
require 'active_support/core_ext/object/blank'
|
5
5
|
require 'active_support/core_ext/class/attribute'
|
6
6
|
require 'active_support/core_ext/string/inflections'
|
7
|
+
require 'active_support/core_ext/time/calculations'
|
8
|
+
require 'active_support/core_ext/date/calculations'
|
9
|
+
require 'active_support/core_ext/date_time/calculations'
|
10
|
+
require 'active_support/core_ext/numeric/time'
|
7
11
|
require 'active_support/concern'
|
8
12
|
|
13
|
+
class Time
|
14
|
+
def round(seconds = 60)
|
15
|
+
Time.at((self.to_f / seconds).round * seconds)
|
16
|
+
end
|
17
|
+
|
18
|
+
def floor(seconds = 60)
|
19
|
+
Time.at((self.to_f / seconds).floor * seconds)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
9
23
|
module Nightfury
|
10
24
|
class << self
|
11
25
|
attr_accessor :redis, :namespace
|
data/lib/nightfury/identity.rb
CHANGED
@@ -36,8 +36,10 @@ module Nightfury
|
|
36
36
|
end
|
37
37
|
|
38
38
|
attr_accessor :id, :tags
|
39
|
+
attr_reader :redis
|
39
40
|
|
40
41
|
def initialize(id, options={})
|
42
|
+
@redis = Nightfury.redis
|
41
43
|
@id = id
|
42
44
|
@tags = options[:tags]
|
43
45
|
end
|
@@ -49,6 +51,10 @@ module Nightfury
|
|
49
51
|
"#{store_name}.#{id}#{tag_ids}"
|
50
52
|
end
|
51
53
|
|
54
|
+
def new_record?
|
55
|
+
redis.keys("#{key_prefix}*").empty?
|
56
|
+
end
|
57
|
+
|
52
58
|
private
|
53
59
|
|
54
60
|
def generate_tag_ids
|
data/lib/nightfury/metric.rb
CHANGED
@@ -1,13 +1,17 @@
|
|
1
1
|
module Nightfury
|
2
2
|
module Metric
|
3
3
|
class Base
|
4
|
-
|
4
|
+
|
5
|
+
ALLOWED_STEPS = [:minute, :hour, :week, :month, :year]
|
6
|
+
|
7
|
+
attr_reader :name, :redis, :redis_key_prefix, :store_as, :step
|
5
8
|
|
6
9
|
def initialize(name, options={})
|
7
10
|
@name = name
|
8
11
|
@redis = Nightfury.redis
|
9
12
|
@redis_key_prefix = options[:redis_key_prefix]
|
10
13
|
@store_as = options[:store_as]
|
14
|
+
@step = ALLOWED_STEPS.include?(options[:step]) ? options[:step] : :minute
|
11
15
|
end
|
12
16
|
|
13
17
|
def redis_key
|
@@ -20,7 +20,7 @@ module Nightfury
|
|
20
20
|
return nil unless redis.exists(redis_key)
|
21
21
|
data_point = ''
|
22
22
|
if timestamp
|
23
|
-
timestamp = timestamp.to_i
|
23
|
+
timestamp = get_step_time(timestamp).to_i
|
24
24
|
data_point = redis.zrangebyscore(redis_key, 0, timestamp, withscores: true)
|
25
25
|
data_point = data_point.each_slice(2).map {|pair| pair }.last
|
26
26
|
else
|
@@ -37,8 +37,8 @@ module Nightfury
|
|
37
37
|
|
38
38
|
def get_range(start_time, end_time)
|
39
39
|
return nil unless redis.exists(redis_key)
|
40
|
-
start_time = start_time.to_i
|
41
|
-
end_time
|
40
|
+
start_time = get_step_time(start_time).to_i
|
41
|
+
end_time = get_step_time(end_time).to_i
|
42
42
|
data_points = redis.zrangebyscore(redis_key, start_time, end_time, withscores: true)
|
43
43
|
decode_many_data_points(data_points)
|
44
44
|
end
|
@@ -75,9 +75,8 @@ module Nightfury
|
|
75
75
|
private
|
76
76
|
|
77
77
|
def add_value_to_timeline(value, time)
|
78
|
-
|
79
|
-
|
80
|
-
redis.zadd redis_key, time, value
|
78
|
+
timestamp = get_step_time(time).to_i
|
79
|
+
redis.zadd redis_key, timestamp, value
|
81
80
|
end
|
82
81
|
|
83
82
|
def decode_many_data_points(data_points)
|
@@ -91,13 +90,7 @@ module Nightfury
|
|
91
90
|
end
|
92
91
|
|
93
92
|
def decode_data_point(data_point)
|
94
|
-
data_point
|
95
|
-
colon_index = data_point.index(':')
|
96
|
-
|
97
|
-
[
|
98
|
-
data_point[0...colon_index],
|
99
|
-
data_point[colon_index+1..-1]
|
100
|
-
]
|
93
|
+
[data_point[1], data_point[0]]
|
101
94
|
end
|
102
95
|
|
103
96
|
def save_meta
|
@@ -108,6 +101,15 @@ module Nightfury
|
|
108
101
|
def init_time_series
|
109
102
|
redis.zadd redis_key, 0, default_meta.to_json
|
110
103
|
end
|
104
|
+
|
105
|
+
def get_step_time(time)
|
106
|
+
case step
|
107
|
+
when :minute then time.round(60)
|
108
|
+
when :hour then time.round(1.hour)
|
109
|
+
when :week then time.round(1.week)
|
110
|
+
when :month then time.round(Time.days_in_month(time.month, time.year))
|
111
|
+
end
|
112
|
+
end
|
111
113
|
end
|
112
114
|
end
|
113
115
|
end
|
data/lib/nightfury/version.rb
CHANGED
@@ -41,6 +41,11 @@ describe Nightfury::Identity::Base do
|
|
41
41
|
d.id.should == 1
|
42
42
|
end
|
43
43
|
|
44
|
+
it "should respond to redis" do
|
45
|
+
d = Dummy.new(1)
|
46
|
+
d.redis.should == Nightfury.redis
|
47
|
+
end
|
48
|
+
|
44
49
|
it "should generates a key prefix" do
|
45
50
|
d = Dummy.new(1)
|
46
51
|
d.key_prefix.should == 'dummy.1'
|
@@ -52,6 +57,16 @@ describe Nightfury::Identity::Base do
|
|
52
57
|
d.key_prefix.should == 'd.1'
|
53
58
|
end
|
54
59
|
|
60
|
+
it "should be able tell if self is a new record" do
|
61
|
+
Dummy.metric(:new_metric)
|
62
|
+
|
63
|
+
d = Dummy.new(1)
|
64
|
+
d.should be_new_record
|
65
|
+
|
66
|
+
d.new_metric.set(1)
|
67
|
+
d.should_not be_new_record
|
68
|
+
end
|
69
|
+
|
55
70
|
it "should include tags in the key prefix" do
|
56
71
|
DummyTwo = Class.new(Dummy)
|
57
72
|
DummyTwo.store_as = :d
|
@@ -14,7 +14,7 @@ describe Nightfury::Metric::CountTimeSeries do
|
|
14
14
|
end
|
15
15
|
Timecop.return
|
16
16
|
|
17
|
-
count_series.get.should ==
|
17
|
+
count_series.get.values.first.should == "2"
|
18
18
|
end
|
19
19
|
|
20
20
|
it "should be able to increment value by a given step" do
|
@@ -28,18 +28,18 @@ describe Nightfury::Metric::CountTimeSeries do
|
|
28
28
|
end
|
29
29
|
Timecop.return
|
30
30
|
|
31
|
-
count_series.get.should ==
|
31
|
+
count_series.get.values.first.should == "3"
|
32
32
|
end
|
33
33
|
|
34
34
|
|
35
|
-
it "should be able to increment value at a given timestamp" do
|
35
|
+
it "should be able to increment value at a step near the given timestamp" do
|
36
36
|
count_series = Nightfury::Metric::CountTimeSeries.new(1)
|
37
37
|
time_now = Time.now
|
38
|
-
time_later = time_now +
|
38
|
+
time_later = time_now + 61
|
39
39
|
# Add a data point
|
40
40
|
count_series.set(1, time_now - 10)
|
41
41
|
count_series.incr(1,time_later)
|
42
|
-
count_series.get.should == { time_later.to_i.to_s => "2"}
|
42
|
+
count_series.get.should == { time_later.round(60).to_i.to_s => "2"}
|
43
43
|
end
|
44
44
|
end
|
45
45
|
|
@@ -48,50 +48,50 @@ describe Nightfury::Metric::CountTimeSeries do
|
|
48
48
|
count_series = Nightfury::Metric::CountTimeSeries.new(1)
|
49
49
|
time_now = Time.now
|
50
50
|
count_series.incr(2, time_now)
|
51
|
-
count_series.get.should == { time_now.to_i.to_s => "2"}
|
51
|
+
count_series.get.should == { time_now.round(60).to_i.to_s => "2"}
|
52
52
|
end
|
53
53
|
end
|
54
54
|
end
|
55
55
|
|
56
56
|
describe "Decr" do
|
57
57
|
context "Has data points" do
|
58
|
-
it "should be able to decrement value by 1 at the current timestamp by default" do
|
58
|
+
it "should be able to decrement value by 1 at a step near the current timestamp by default" do
|
59
59
|
count_series = Nightfury::Metric::CountTimeSeries.new(1)
|
60
60
|
time_now = Time.now
|
61
61
|
# Add a data point
|
62
|
-
count_series.set(1, time_now -
|
62
|
+
count_series.set(1, time_now - 61)
|
63
63
|
|
64
64
|
Timecop.freeze(time_now) do
|
65
65
|
count_series.decr
|
66
66
|
end
|
67
67
|
Timecop.return
|
68
68
|
|
69
|
-
count_series.get.should ==
|
69
|
+
count_series.get.values.first.should == "0"
|
70
70
|
end
|
71
71
|
|
72
72
|
it "should be able to decrement value by a given step" do
|
73
73
|
count_series = Nightfury::Metric::CountTimeSeries.new(1)
|
74
74
|
time_now = Time.now
|
75
75
|
# Add a data point
|
76
|
-
count_series.set(2, time_now -
|
76
|
+
count_series.set(2, time_now - 61)
|
77
77
|
|
78
78
|
Timecop.freeze(time_now) do
|
79
79
|
count_series.decr(2)
|
80
80
|
end
|
81
81
|
Timecop.return
|
82
82
|
|
83
|
-
count_series.get.should ==
|
83
|
+
count_series.get.values.first.should == "0"
|
84
84
|
end
|
85
85
|
|
86
86
|
|
87
|
-
it "should be able to decrement value at a given timestamp" do
|
87
|
+
it "should be able to decrement value at nearest step of a given timestamp" do
|
88
88
|
count_series = Nightfury::Metric::CountTimeSeries.new(1)
|
89
89
|
time_now = Time.now
|
90
|
-
time_later = time_now +
|
90
|
+
time_later = time_now + 61
|
91
91
|
# Add a data point
|
92
92
|
count_series.set(1, time_now - 10)
|
93
93
|
count_series.decr(1,time_later)
|
94
|
-
count_series.get.should == { time_later.to_i.to_s => "0"}
|
94
|
+
count_series.get.should == { time_later.round(60).to_i.to_s => "0"}
|
95
95
|
end
|
96
96
|
end
|
97
97
|
|
@@ -100,7 +100,7 @@ describe Nightfury::Metric::CountTimeSeries do
|
|
100
100
|
count_series = Nightfury::Metric::CountTimeSeries.new(1)
|
101
101
|
time_now = Time.now
|
102
102
|
count_series.decr(2, time_now)
|
103
|
-
count_series.get.should == { time_now.to_i.to_s => "-2"}
|
103
|
+
count_series.get.should == { time_now.round(60).to_i.to_s => "-2"}
|
104
104
|
end
|
105
105
|
end
|
106
106
|
end
|
@@ -31,11 +31,11 @@ describe Nightfury::Metric::TimeSeries do
|
|
31
31
|
it "should get the most recent data point" do
|
32
32
|
ts_metric = Nightfury::Metric::TimeSeries.new(:time)
|
33
33
|
time_now = Time.now
|
34
|
-
time_later = time_now +
|
34
|
+
time_later = time_now + 60
|
35
35
|
ts_metric.set(1, time_now)
|
36
36
|
ts_metric.set(2, time_later)
|
37
37
|
result = ts_metric.get
|
38
|
-
result
|
38
|
+
result.values.first.should == '2'
|
39
39
|
end
|
40
40
|
|
41
41
|
it "should return nil if there are no data points" do
|
@@ -45,14 +45,14 @@ describe Nightfury::Metric::TimeSeries do
|
|
45
45
|
end
|
46
46
|
|
47
47
|
context "with timestamp" do
|
48
|
-
it "should get the data point at the time
|
48
|
+
it "should get the data point at the nearest time step" do
|
49
49
|
ts_metric = Nightfury::Metric::TimeSeries.new(:time)
|
50
50
|
time_now = Time.now
|
51
|
-
time_later = time_now +
|
51
|
+
time_later = time_now + 60
|
52
52
|
ts_metric.set(1, time_now)
|
53
53
|
ts_metric.set(2, time_later)
|
54
54
|
result = ts_metric.get(time_now)
|
55
|
-
result
|
55
|
+
result.values.first.should == '1'
|
56
56
|
end
|
57
57
|
|
58
58
|
context "no data point at the timestamp" do
|
@@ -66,7 +66,7 @@ describe Nightfury::Metric::TimeSeries do
|
|
66
66
|
set_time = Time.now - 60
|
67
67
|
ts_metric.set(1, set_time)
|
68
68
|
result = ts_metric.get(Time.now)
|
69
|
-
result
|
69
|
+
result.values.first.should == '1'
|
70
70
|
end
|
71
71
|
end
|
72
72
|
end
|
@@ -92,16 +92,14 @@ describe Nightfury::Metric::TimeSeries do
|
|
92
92
|
|
93
93
|
10.times do |i|
|
94
94
|
ts_metric.set(i, loop_time)
|
95
|
-
loop_time = loop_time +
|
95
|
+
loop_time = loop_time + 61
|
96
96
|
end
|
97
97
|
|
98
|
-
start_time = time + 3
|
99
|
-
end_time = time + 5
|
98
|
+
start_time = time + (3*60)
|
99
|
+
end_time = time + (5*60)
|
100
100
|
|
101
101
|
result = ts_metric.get_range(start_time, end_time)
|
102
|
-
result
|
103
|
-
result[(start_time.to_i + 1).to_s].should == '4'
|
104
|
-
result[end_time.to_i.to_s].should == '5'
|
102
|
+
result.values.should == ['3','4','5']
|
105
103
|
end
|
106
104
|
end
|
107
105
|
|
@@ -125,7 +123,7 @@ describe Nightfury::Metric::TimeSeries do
|
|
125
123
|
|
126
124
|
10.times do |i|
|
127
125
|
ts_metric.set(i, loop_time)
|
128
|
-
loop_time = loop_time +
|
126
|
+
loop_time = loop_time + 61
|
129
127
|
end
|
130
128
|
|
131
129
|
result = ts_metric.get_all
|
@@ -150,13 +148,13 @@ describe Nightfury::Metric::TimeSeries do
|
|
150
148
|
end
|
151
149
|
|
152
150
|
describe "add the value to timeline" do
|
153
|
-
it "should default time to current time" do
|
151
|
+
it "should default time to the step near the current time" do
|
154
152
|
time_now = Time.now
|
155
153
|
ts_metric = Nightfury::Metric::TimeSeries.new(:avg_time)
|
156
154
|
|
157
155
|
flexmock(ts_metric.redis).should_receive(:zadd)
|
158
156
|
.with(ts_metric.redis_key,
|
159
|
-
time_now.to_i,
|
157
|
+
time_now.round(60).to_i,
|
160
158
|
FlexMock.any)
|
161
159
|
.once
|
162
160
|
|
@@ -166,30 +164,18 @@ describe Nightfury::Metric::TimeSeries do
|
|
166
164
|
Timecop.return
|
167
165
|
end
|
168
166
|
|
169
|
-
it "should add at specified time" do
|
167
|
+
it "should add at the step near to specified time" do
|
170
168
|
time = Time.now - 60
|
171
169
|
ts_metric = Nightfury::Metric::TimeSeries.new(:avg_time)
|
172
170
|
|
173
171
|
flexmock(ts_metric.redis).should_receive(:zadd)
|
174
172
|
.with(ts_metric.redis_key,
|
175
|
-
time.to_i,
|
173
|
+
time.round(60).to_i,
|
176
174
|
FlexMock.any)
|
177
175
|
.once
|
178
176
|
|
179
177
|
ts_metric.set(1, time)
|
180
178
|
end
|
181
|
-
|
182
|
-
it "should add the time to the value to avoid duplicate value in the set" do
|
183
|
-
time = Time.now
|
184
|
-
ts_metric = Nightfury::Metric::TimeSeries.new(:avg_time)
|
185
|
-
|
186
|
-
flexmock(ts_metric.redis).should_receive(:zadd)
|
187
|
-
.with(FlexMock.any,
|
188
|
-
FlexMock.any,
|
189
|
-
"#{time.to_i}:1")
|
190
|
-
.once
|
191
|
-
ts_metric.set(1, time)
|
192
|
-
end
|
193
179
|
end
|
194
180
|
end
|
195
181
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: nightfury
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: '0.
|
4
|
+
version: '0.6'
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -10,7 +10,7 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date: 2013-
|
13
|
+
date: 2013-02-21 00:00:00.000000000 Z
|
14
14
|
dependencies: []
|
15
15
|
description: Nightfury is a reporting/analytics backend written on Redis
|
16
16
|
email:
|