nightfury 0.5 → 0.6
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/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:
|