rhcf-timeseries 1.0.2 → 1.0.3

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 95f23eccdfd639db8cdbbf3bf4dc6441d957411a
4
- data.tar.gz: b28a08bed2007266e3fc6fe578fc41e0586f3e2b
3
+ metadata.gz: 43ad07821290e60cc41c8741c44ef2f2e5b7769d
4
+ data.tar.gz: dae3053f370a61499301acfe5b841156ace2bc3a
5
5
  SHA512:
6
- metadata.gz: d39583afe3b37ac668b8f457b9edd72a9fa538eca111761fe89d8e48984c5c0ada796500f05c3e3bc33a5d4c800c165ae642bbc765a61769afaabaa4bf9fc568
7
- data.tar.gz: 85f73e0303a7cdc3479296da317a8640c8f4a62e4e4d02e76f162200ebbd54237063e209c435271b2990293fdb9e797043984a565cec543c6e476c6099c3241a
6
+ metadata.gz: 42b5890b40e7084f54987deac6f52050c65bd01191edc2730346703962489e41468b93700113d96d7f219995374cde592c49957b56a1c22d2d40dfcb6087eea6
7
+ data.tar.gz: 2671b695bd0edb1d1b8e077d77dde591b3d16f31455c7315024fd0f9495740f2a54bf7e58202f381996d72693d84fb820f4facfea72e0d0dc6f50c817885c766
@@ -98,8 +98,8 @@ module Rhcf
98
98
  @strategy.store_point_value(self, subject_path, resolution_name, resolution_value, point_value, event_path)
99
99
  end
100
100
 
101
- def find(subject, from, to = Time.now, filter = nil)
102
- Rhcf::Timeseries::Query.new(subject, from, to, self, filter)
101
+ def find(subject, from, to = Time.now, filter = nil, limit = 1000)
102
+ Rhcf::Timeseries::Query.new(subject, from, to, self, filter, limit)
103
103
  end
104
104
 
105
105
  def resolution(id)
@@ -112,8 +112,8 @@ module Rhcf
112
112
  @_resolutions ||= @resolution_ids.map { |id| resolution(id) }
113
113
  end
114
114
 
115
- def crunch_values(subject, resolution_id, point, filter)
116
- @strategy.crunch_values(self, subject, resolution_id, point, filter)
115
+ def crunch_values(subject, resolution_id, point, filter, limit = 1000)
116
+ @strategy.crunch_values(self, subject, resolution_id, point, filter, limit)
117
117
  end
118
118
  end
119
119
  end
@@ -1,14 +1,16 @@
1
1
  module Rhcf
2
2
  module Timeseries
3
3
  class Query
4
- def initialize(subject, from, to, series, filter = nil)
5
- fail ArgumentError, "Argument 'from' can not be bigger then 'to'" if from > to
4
+ def initialize(subject, from, to, series, filter = nil, limit = 1000)
5
+ from, to = to, from if from > to
6
+
6
7
  @series = series
7
8
  @subject = subject
8
9
  @from = from
9
10
  @to = to
10
11
 
11
12
  @filter = filter
13
+ @limit = limit
12
14
  end
13
15
 
14
16
  def total(resolution_id=nil)
@@ -28,7 +30,7 @@ module Rhcf
28
30
 
29
31
  point_range(resolution_id) do |point|
30
32
 
31
- values = @series.crunch_values(@subject, resolution_id, point, @filter)
33
+ values = @series.crunch_values(@subject, resolution_id, point, @filter, @limit)
32
34
 
33
35
  next if values.empty?
34
36
  data = {moment: point, values: values }
@@ -5,7 +5,7 @@ module Rhcf
5
5
  'H'
6
6
  end
7
7
 
8
- def crunch_values(manager, subject, resolution_id, point, filter)
8
+ def crunch_values(manager, subject, resolution_id, point, filter, limit = 100)
9
9
  values = hgetall(manager, EVENT_POINT_TOKEN, subject, resolution_id, point)
10
10
  values.reject!{|event, value| !filter.match?(event) } if filter
11
11
  values
@@ -56,7 +56,7 @@ module Rhcf
56
56
  'M'
57
57
  end
58
58
 
59
- def crunch_values(manager, subject, resolution_id, point, filter)
59
+ def crunch_values(manager, subject, resolution_id, point, filter, limit = 100)
60
60
  events = events_for_subject_on(manager, subject, point, resolution_id, filter)
61
61
  mget(manager, EVENT_POINT_TOKEN, subject, resolution_id, point, events)
62
62
  end
@@ -87,20 +87,25 @@ module Rhcf
87
87
  events
88
88
  end
89
89
 
90
- def crunch_values(manager, subject, resolution_id, point, filter)
90
+ def crunch_values(manager, subject, resolution_id, point, filter, limit = 1000)
91
91
  register_lua_scripts!(manager.connection_to_use)
92
92
  point_prefix = [manager.prefix, EVENT_POINT_TOKEN, subject, resolution_id, point].join(NAMESPACE_SEPARATOR)
93
93
  set_key = [manager.prefix, EVENT_SET_TOKEN, resolution_id, point, subject].join(NAMESPACE_SEPARATOR)
94
94
 
95
95
  data = manager.connection_to_use.evalsha(evalsha_for(:mget_matching_smembers),
96
- keys: [set_key], argv: [point_prefix, filter && filter.to_lua_pattern])
96
+ keys: [set_key], argv: [point_prefix, filter && filter.to_lua_pattern, limit])
97
97
 
98
98
  return {} if data.nil?
99
99
  result = {}
100
+ begin
100
101
  data.first.each_with_index do |evt, idx|
101
102
  value = data.last[idx].to_i
102
103
  result[evt] = value
103
104
  end
105
+ rescue
106
+ p $!, $!.message
107
+ raise
108
+ end
104
109
 
105
110
  result
106
111
  end
@@ -110,6 +115,7 @@ module Rhcf
110
115
  end
111
116
 
112
117
  def register_lua_scripts!(connection)
118
+
113
119
  @lua_script_register ||=
114
120
  begin
115
121
  smembers_matching = <<-EOF
@@ -123,13 +129,73 @@ module Rhcf
123
129
  EOF
124
130
 
125
131
  mget_matching_smembers = <<-EOF
126
- local matches = {}
127
132
  local set_key = KEYS[1]
128
133
  local key_prefix = ARGV[1]
129
134
  local filter_pattern = ARGV[2]
135
+ local limit = tonumber(ARGV[3])
130
136
  local keys = {}
131
137
  local keys_to_mget = {}
132
138
 
139
+ local function log(msg)
140
+ -- redis.call('publish', 'log', msg)
141
+ end
142
+
143
+ local function mget_in_batches(keys_to_mget)
144
+ local step = 1024
145
+ local results = {}
146
+ local last_end = 0
147
+ local partial = {}
148
+
149
+ local function mget_batch(ini , fin)
150
+ log("Getting from " .. ini .. ' to ' .. fin .. ' on a total of ' .. #keys_to_mget)
151
+ partial = redis.call('MGET', unpack(keys_to_mget, ini, fin))
152
+ for _, value in pairs(partial) do table.insert(results, value) end
153
+ end
154
+
155
+ for ending = step, #keys_to_mget, step do
156
+ mget_batch(last_end + 1, ending)
157
+ last_end = ending
158
+ end
159
+
160
+ if last_end < #keys_to_mget then
161
+ mget_batch(last_end + 1, #keys_to_mget)
162
+ end
163
+
164
+ return results;
165
+ end
166
+
167
+ local function sort_and_limit_tuples(subjects, values)
168
+ local dictionary = {}
169
+ for i, subject in pairs(subjects) do
170
+ local value = values[i] or 0
171
+ -- redis.call('publish', 'log', subject .. ' += ' .. value)
172
+ dictionary[subject] = (dictionary[subject] or 0) + value
173
+ end
174
+
175
+ local tuples = {}
176
+ for subject, value in pairs(dictionary) do
177
+ -- redis.call('publish', 'log', subject .. ' = ' .. value)
178
+ table.insert(tuples, { subject, value } )
179
+ end
180
+
181
+ table.sort(tuples, function(a, b) return b[2] < a[2] end )
182
+
183
+ local new_subjects = {}
184
+ local new_counts = {}
185
+
186
+ for i, tuple in pairs(tuples) do
187
+ if #new_subjects >= limit then break end
188
+
189
+ local subject = tuple[1]
190
+ local value = tuple[2]
191
+
192
+ table.insert(new_subjects, subject)
193
+ table.insert(new_counts, value)
194
+ end
195
+
196
+ return {new_subjects, new_counts}
197
+ end
198
+
133
199
  for _, val in ipairs(redis.call('smembers', set_key)) do
134
200
  if (filter_pattern and string.match(val, filter_pattern)) or not filter_pattern then
135
201
  table.insert(keys, val)
@@ -138,9 +204,13 @@ module Rhcf
138
204
  end
139
205
 
140
206
  if table.getn(keys) > 0 then
141
- return {keys, redis.call('MGET', unpack(keys_to_mget)) }
207
+ local values = mget_in_batches(keys_to_mget)
208
+ local sorted = sort_and_limit_tuples(keys, values)
209
+ log ("Values card " .. #values .. " | keys card: " .. #keys)
210
+ return sorted
211
+ else
212
+ return {{},{}}
142
213
  end
143
- return {{},{}}
144
214
  EOF
145
215
 
146
216
  {
@@ -156,7 +226,7 @@ module Rhcf
156
226
  'G'
157
227
  end
158
228
 
159
- def crunch_values(manager, subject, resolution_id, point, filter)
229
+ def crunch_values(manager, subject, resolution_id, point, filter, limit = 100)
160
230
  events = events_for_subject_on(manager, subject, point, resolution_id, filter)
161
231
  values = {}
162
232
  events.each do |event|
@@ -1,5 +1,5 @@
1
1
  module Rhcf
2
2
  module Timeseries
3
- VERSION = "1.0.2"
3
+ VERSION = "1.0.3"
4
4
  end
5
5
  end
@@ -1,19 +1,7 @@
1
- # This file was generated by the `rspec --init` command. Conventionally, all
2
- # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
3
- # Require this file using `require "spec_helper"` to ensure that it is only
4
- # loaded once.
5
- #
6
- # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
7
1
  Dir[File.join(File.dirname(__FILE__), "support/**/*.rb")].each {|f| require f}
8
2
  require 'timecop'
9
3
  RSpec.configure do |config|
10
4
  config.run_all_when_everything_filtered = true
11
5
  config.filter_run :focus
12
-
13
- # Run specs in random order to surface order dependencies. If you find an
14
- # order dependency and want to debug it, you can fix the order by providing
15
- # the seed, which is printed after each run.
16
- # --seed 1234
17
6
  config.order = 'random'
18
-
19
7
  end
@@ -1,5 +1,6 @@
1
1
  require 'redis'
2
2
  require 'rhcf/timeseries/manager'
3
+
3
4
  RSpec.shared_examples 'a valid strategy' do
4
5
  let(:redis){Redis.new}
5
6
  let(:manager) { Rhcf::Timeseries::Manager.new(connection: redis, strategy: described_class) }
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rhcf-timeseries
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.2
4
+ version: 1.0.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Romeu Fonseca
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-04-01 00:00:00.000000000 Z
11
+ date: 2015-06-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler