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 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
@@ -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
@@ -1,13 +1,17 @@
1
1
  module Nightfury
2
2
  module Metric
3
3
  class Base
4
- attr_reader :name, :redis, :redis_key_prefix, :store_as
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 = end_time.to_i
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
- time = time.to_i
79
- value = "#{time}:#{value}"
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 = data_point.first
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
@@ -1,3 +1,3 @@
1
1
  module Nightfury
2
- VERSION = "0.5"
2
+ VERSION = "0.6"
3
3
  end
@@ -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 == { time_now.to_i.to_s => "2"}
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 == { time_now.to_i.to_s => "3"}
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 + 10
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 - 10)
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 == { time_now.to_i.to_s => "0"}
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 - 10)
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 == { time_now.to_i.to_s => "0"}
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 + 10
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 + 10
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[time_later.to_i.to_s].should == '2'
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 stamp" do
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 + 10
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[time_now.to_i.to_s].should == '1'
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[set_time.to_i.to_s].should == '1'
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 + 1
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[start_time.to_i.to_s].should == '3'
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 + 1
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.5'
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-01-30 00:00:00.000000000 Z
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: