rhcf-timeseries 1.0.2 → 1.0.3

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.
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