rhcf-timeseries 1.0.3 → 2.0.0pre
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/rhcf/timeseries/manager.rb +13 -9
- data/lib/rhcf/timeseries/query.rb +15 -6
- data/lib/rhcf/timeseries/redis_strategies.rb +145 -60
- data/lib/rhcf/timeseries/version.rb +1 -1
- data/spec/lib/rhcf/timeseries/manager_spec.rb +67 -0
- metadata +4 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 60bb46afd3e19f758a6167bcc079283ad7d821d0
|
4
|
+
data.tar.gz: 132cd23eb7fc694f11a3dfd9e5200add5441c295
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 281bef05f4f3d6090d0e915b9c19328906cd9ffd12ab9f2ae530b8543481c116ad6c33f5804603dabbb8db2d17de9674818b7d007e4962df1755a427fc2cacff
|
7
|
+
data.tar.gz: d0e4eef0b658335d3856348e053b219014deb3c16c6022b1d2217b630ba4c7f8a95e73dd13f0136a9e8be98ee91a4dfd32d1d4470a5313df592567a8ab9c280d
|
@@ -50,15 +50,15 @@ module Rhcf
|
|
50
50
|
@connection_to_use || fail("No connection given")
|
51
51
|
end
|
52
52
|
|
53
|
-
def store(
|
53
|
+
def store(evt_path, subject_point_hash, moment = Time.now, descend_subject = true, descend_event = true)
|
54
54
|
resolutions = resolutions_of(moment)
|
55
55
|
|
56
|
-
descend(
|
57
|
-
|
58
|
-
descend(
|
56
|
+
descend(evt_path, descend_event) do |event_path|
|
57
|
+
subject_point_hash.each do |subj_path, point_value|
|
58
|
+
descend(subj_path, descend_subject) do |subject_path|
|
59
59
|
resolutions.each do |res|
|
60
60
|
resolution_name, resolution_value = *res
|
61
|
-
store_point_value(
|
61
|
+
store_point_value(event_path, resolution_name, resolution_value, subject_path, point_value)
|
62
62
|
end
|
63
63
|
end
|
64
64
|
end
|
@@ -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(
|
102
|
-
Rhcf::Timeseries::Query.new(
|
101
|
+
def find(evt_filter, from, to = Time.now, subj_filter = nil)
|
102
|
+
Rhcf::Timeseries::Query.new(evt_filter, from, to, self, subj_filter)
|
103
103
|
end
|
104
104
|
|
105
105
|
def resolution(id)
|
@@ -112,8 +112,12 @@ module Rhcf
|
|
112
112
|
@_resolutions ||= @resolution_ids.map { |id| resolution(id) }
|
113
113
|
end
|
114
114
|
|
115
|
-
def
|
116
|
-
@strategy.
|
115
|
+
def ranking(evt_filter, resolution_id, points_on_range, subj_filter, limit)
|
116
|
+
@strategy.ranking(self, evt_filter, resolution_id, points_on_range, subj_filter, limit)
|
117
|
+
end
|
118
|
+
|
119
|
+
def crunch_values(evt_filter, resolution_id, point, subj_filter)
|
120
|
+
@strategy.crunch_values(self, evt_filter, resolution_id, point, subj_filter)
|
117
121
|
end
|
118
122
|
end
|
119
123
|
end
|
@@ -1,16 +1,15 @@
|
|
1
1
|
module Rhcf
|
2
2
|
module Timeseries
|
3
3
|
class Query
|
4
|
-
def initialize(
|
4
|
+
def initialize(evt_filter, from, to, series, subj_filter = nil)
|
5
5
|
from, to = to, from if from > to
|
6
6
|
|
7
7
|
@series = series
|
8
|
-
@
|
8
|
+
@evt_filter = evt_filter
|
9
9
|
@from = from
|
10
10
|
@to = to
|
11
11
|
|
12
|
-
@
|
13
|
-
@limit = limit
|
12
|
+
@subj_filter = subj_filter
|
14
13
|
end
|
15
14
|
|
16
15
|
def total(resolution_id=nil)
|
@@ -25,12 +24,18 @@ module Rhcf
|
|
25
24
|
accumulator
|
26
25
|
end
|
27
26
|
|
27
|
+
def ranking(limit, resolution_id = nil)
|
28
|
+
resolution_id ||= better_resolution[:id]
|
29
|
+
points_on_range = point_range(resolution_id)
|
30
|
+
@series.ranking(@evt_filter, resolution_id, points_on_range, @subj_filter, limit)
|
31
|
+
end
|
32
|
+
|
28
33
|
def points(resolution_id)
|
29
34
|
list =[]
|
30
35
|
|
31
36
|
point_range(resolution_id) do |point|
|
32
37
|
|
33
|
-
values = @series.crunch_values(@
|
38
|
+
values = @series.crunch_values(@evt_filter, resolution_id, point, @subj_filter)
|
34
39
|
|
35
40
|
next if values.empty?
|
36
41
|
data = {moment: point, values: values }
|
@@ -44,14 +49,18 @@ module Rhcf
|
|
44
49
|
end
|
45
50
|
|
46
51
|
def point_range(resolution_id)
|
52
|
+
points_on_range = []
|
47
53
|
resolution = @series.resolution(resolution_id)
|
48
54
|
span = resolution[:span]
|
49
55
|
ptr = @from.dup
|
50
56
|
while ptr < @to
|
51
57
|
point = @series.resolution_value_at(ptr, resolution_id)
|
52
|
-
yield point
|
58
|
+
yield point if block_given?
|
59
|
+
points_on_range << point
|
53
60
|
ptr += span.to_i
|
54
61
|
end
|
62
|
+
|
63
|
+
points_on_range
|
55
64
|
rescue FloatDomainError
|
56
65
|
# OK
|
57
66
|
end
|
@@ -5,16 +5,16 @@ module Rhcf
|
|
5
5
|
'H'
|
6
6
|
end
|
7
7
|
|
8
|
-
def crunch_values(manager,
|
9
|
-
values = hgetall(manager, EVENT_POINT_TOKEN,
|
10
|
-
values.reject!{|event, value| !
|
8
|
+
def crunch_values(manager, evt_filter, resolution_id, time_point, subj_filter)
|
9
|
+
values = hgetall(manager, EVENT_POINT_TOKEN, evt_filter, resolution_id, time_point)
|
10
|
+
values.reject!{|event, value| !subj_filter.match?(event) } if subj_filter
|
11
11
|
values
|
12
12
|
end
|
13
13
|
|
14
|
-
def store_point_value(manager,
|
15
|
-
key =
|
16
|
-
manager.connection_to_use.hincrby(key,
|
17
|
-
manager.connection_to_use.expire(key, DEFAULT_RESOLUTIONS_MAP[
|
14
|
+
def store_point_value(manager, event_path, resolution_id, resolution_val, subject_path, increment)
|
15
|
+
key = point_prefix(manager, event_path, resolution_id, resolution_val)
|
16
|
+
manager.connection_to_use.hincrby(key, subject_path, increment)
|
17
|
+
manager.connection_to_use.expire(key, DEFAULT_RESOLUTIONS_MAP[resolution_id][:ttl])
|
18
18
|
end
|
19
19
|
|
20
20
|
def hgetall(manager, k,s,r,p)
|
@@ -23,30 +23,49 @@ module Rhcf
|
|
23
23
|
hash[_k] = value.to_i
|
24
24
|
end
|
25
25
|
end
|
26
|
+
|
27
|
+
def point_prefix(manager, evt_filter, resolution_id, time_point = nil, subj_path = nil)
|
28
|
+
[manager.prefix, EVENT_POINT_TOKEN, evt_filter, resolution_id, time_point, subj_path].compact.join(NAMESPACE_SEPARATOR)
|
29
|
+
end
|
30
|
+
|
31
|
+
def set_prefix(manager, evt_filter, resolution_id, time_point = nil)
|
32
|
+
[manager.prefix, EVENT_SET_TOKEN, evt_filter, resolution_id, time_point].compact.join(NAMESPACE_SEPARATOR)
|
33
|
+
end
|
34
|
+
|
35
|
+
|
36
|
+
|
26
37
|
end
|
27
38
|
|
28
39
|
class RedisStringBasedStrategy
|
40
|
+
|
41
|
+
def point_prefix(manager, evt_filter, resolution_id, time_point = nil, subj_path = nil)
|
42
|
+
[manager.prefix, EVENT_POINT_TOKEN, evt_filter, resolution_id, time_point, subj_path].compact.join(NAMESPACE_SEPARATOR)
|
43
|
+
end
|
44
|
+
|
45
|
+
def set_prefix(manager, evt_filter, resolution_id, time_point = nil)
|
46
|
+
[manager.prefix, EVENT_SET_TOKEN, evt_filter, resolution_id, time_point].compact.join(NAMESPACE_SEPARATOR)
|
47
|
+
end
|
48
|
+
|
49
|
+
|
29
50
|
def id
|
30
51
|
fail 'AbstractStrategy'
|
31
52
|
end
|
32
53
|
|
33
|
-
def store_point_value(manager,
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
manager.connection_to_use.
|
38
|
-
|
54
|
+
def store_point_value(manager, event_path, resolution_name, resolution_val, subj_path, increment)
|
55
|
+
set_key = set_prefix(manager, event_path, resolution_name, resolution_val)
|
56
|
+
counter_key = point_prefix(manager, event_path, resolution_name, resolution_val, subj_path)
|
57
|
+
|
58
|
+
manager.connection_to_use.sadd(set_key, subj_path)
|
59
|
+
manager.connection_to_use.incrby(counter_key, increment)
|
39
60
|
|
40
|
-
|
41
|
-
|
42
|
-
manager.connection_to_use.sadd(key, event_path)
|
43
|
-
manager.connection_to_use.expire(key, DEFAULT_RESOLUTIONS_MAP[resolution_name][:ttl])
|
61
|
+
manager.connection_to_use.expire(counter_key, DEFAULT_RESOLUTIONS_MAP[resolution_name][:ttl])
|
62
|
+
manager.connection_to_use.expire(set_key, DEFAULT_RESOLUTIONS_MAP[resolution_name][:ttl])
|
44
63
|
end
|
45
64
|
|
46
|
-
def events_for_subject_on(manager,
|
47
|
-
key =
|
65
|
+
def events_for_subject_on(manager, evt_filter, res_point, resolution_id, subj_filter)
|
66
|
+
key = set_prefix(manager, evt_filter, resolution_id, res_point)
|
48
67
|
events = manager.connection_to_use.smembers(key)
|
49
|
-
events = events.select{|event|
|
68
|
+
events = events.select{|event| subj_filter.match?(event) } if subj_filter
|
50
69
|
events
|
51
70
|
end
|
52
71
|
end
|
@@ -56,9 +75,9 @@ module Rhcf
|
|
56
75
|
'M'
|
57
76
|
end
|
58
77
|
|
59
|
-
def crunch_values(manager,
|
60
|
-
events = events_for_subject_on(manager,
|
61
|
-
mget(manager, EVENT_POINT_TOKEN,
|
78
|
+
def crunch_values(manager, evt_filter, resolution_id, time_point, subj_filter)
|
79
|
+
events = events_for_subject_on(manager, evt_filter, time_point, resolution_id, subj_filter)
|
80
|
+
mget(manager, EVENT_POINT_TOKEN, evt_filter, resolution_id, time_point, events)
|
62
81
|
end
|
63
82
|
|
64
83
|
def mget(manager, k, s, r, p, es)
|
@@ -76,24 +95,39 @@ module Rhcf
|
|
76
95
|
class RedisMgetLuaStrategy < RedisMgetStrategy
|
77
96
|
def id; 'ME'; end
|
78
97
|
|
79
|
-
def
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
98
|
+
def ranking(manager, evt_filter, resolution_id, points_on_range, subj_filter, limit)
|
99
|
+
point_prefix = point_prefix(manager, evt_filter, resolution_id)
|
100
|
+
set_prefix = set_prefix(manager, evt_filter, resolution_id)
|
101
|
+
|
102
|
+
manager.connection_to_use.evalsha(evalsha_for(manager, :ranking),
|
103
|
+
keys: points_on_range,
|
104
|
+
argv: [
|
105
|
+
evt_filter,
|
106
|
+
subj_filter && subj_filter.to_lua_pattern,
|
107
|
+
set_prefix,
|
108
|
+
point_prefix,
|
109
|
+
limit
|
110
|
+
])
|
111
|
+
end
|
112
|
+
|
113
|
+
def events_for_subject_on(manager, evt_filter, time_point, resolution_id, subj_filter)
|
114
|
+
key = set_prefix(manager, resolution_id, evt_filter, time_point)
|
115
|
+
events = if subj_filter
|
116
|
+
manager.connection_to_use.evalsha(evalsha_for(manager, :smembers_matching),
|
117
|
+
keys: [key], argv: [subj_filter.to_lua_pattern])
|
84
118
|
else
|
85
119
|
manager.connection_to_use.smembers(key)
|
86
120
|
end
|
87
121
|
events
|
88
122
|
end
|
89
123
|
|
90
|
-
def crunch_values(manager,
|
91
|
-
|
92
|
-
|
93
|
-
set_key = [manager.prefix, EVENT_SET_TOKEN, resolution_id, point, subject].join(NAMESPACE_SEPARATOR)
|
124
|
+
def crunch_values(manager, evt_filter, resolution_id, time_point, subj_filter)
|
125
|
+
point_prefix = point_prefix(manager, resolution_id, evt_filter, time_point)
|
126
|
+
set_key = point_prefix(manager, resolution_id, evt_filter, time_point)
|
94
127
|
|
95
|
-
data = manager.connection_to_use.evalsha(evalsha_for(:mget_matching_smembers),
|
96
|
-
keys: [set_key],
|
128
|
+
data = manager.connection_to_use.evalsha(evalsha_for(manager, :mget_matching_smembers),
|
129
|
+
keys: [set_key],
|
130
|
+
argv: [point_prefix, subj_filter && subj_filter.to_lua_pattern])
|
97
131
|
|
98
132
|
return {} if data.nil?
|
99
133
|
result = {}
|
@@ -110,17 +144,63 @@ module Rhcf
|
|
110
144
|
result
|
111
145
|
end
|
112
146
|
|
113
|
-
def evalsha_for(sym_os_lua_script)
|
114
|
-
|
147
|
+
def evalsha_for(manager, sym_os_lua_script)
|
148
|
+
register_lua_scripts(manager.connection_to_use).fetch(sym_os_lua_script)
|
115
149
|
end
|
116
150
|
|
117
|
-
def register_lua_scripts
|
151
|
+
def register_lua_scripts(connection)
|
118
152
|
|
119
153
|
@lua_script_register ||=
|
120
154
|
begin
|
155
|
+
ranking_script = <<-EOF
|
156
|
+
local evt_filter = ARGV[1]
|
157
|
+
local subj_filter = ARGV[2]
|
158
|
+
local set_prefix = ARGV[3]
|
159
|
+
local point_prefix = ARGV[4]
|
160
|
+
local limit = tonumber(ARGV[5])
|
161
|
+
|
162
|
+
local set_keys = {}
|
163
|
+
for _, time in pairs(KEYS) do
|
164
|
+
table.insert(set_keys, set_prefix .. '|' .. time)
|
165
|
+
end
|
166
|
+
|
167
|
+
local all_subjects = redis.call("SUNION", unpack(set_keys))
|
168
|
+
local filtered_subjects = {}
|
169
|
+
|
170
|
+
if subj_filter then
|
171
|
+
for _, val in pairs(all_subjects) do
|
172
|
+
if string.match(val, subj_filter) then
|
173
|
+
table.insert(filtered_subjects, val)
|
174
|
+
end
|
175
|
+
end
|
176
|
+
else
|
177
|
+
filtered_subjects = all_subjects
|
178
|
+
end
|
179
|
+
|
180
|
+
local counter_tuples = {}
|
181
|
+
|
182
|
+
for _, subject in pairs(filtered_subjects) do
|
183
|
+
local my_counter_keys = {}
|
184
|
+
|
185
|
+
for _, time in pairs(KEYS) do
|
186
|
+
table.insert(my_counter_keys, point_prefix .. "|" .. time .. "|" .. subject)
|
187
|
+
end
|
188
|
+
|
189
|
+
local counter_total = 0
|
190
|
+
for _, val in pairs(redis.call("MGET", unpack(my_counter_keys))) do
|
191
|
+
counter_total = counter_total + ( tonumber(val) or 0 )
|
192
|
+
end
|
193
|
+
|
194
|
+
table.insert(counter_tuples, {subject, counter_total} )
|
195
|
+
end
|
196
|
+
|
197
|
+
table.sort(counter_tuples, function(a, b) return b[2] < a[2] end )
|
198
|
+
return counter_tuples
|
199
|
+
EOF
|
200
|
+
|
121
201
|
smembers_matching = <<-EOF
|
122
202
|
local matches = {}
|
123
|
-
for _, val in
|
203
|
+
for _, val in pairs(redis.call('smembers', KEYS[1])) do
|
124
204
|
if string.match(val, ARGV[1]) then
|
125
205
|
table.insert(matches, val)
|
126
206
|
end
|
@@ -136,23 +216,26 @@ module Rhcf
|
|
136
216
|
local keys = {}
|
137
217
|
local keys_to_mget = {}
|
138
218
|
|
219
|
+
|
139
220
|
local function log(msg)
|
140
|
-
|
221
|
+
redis.call('publish', 'log', "XDEBUG: " .. msg)
|
141
222
|
end
|
142
223
|
|
143
224
|
local function mget_in_batches(keys_to_mget)
|
144
|
-
local step
|
145
|
-
local results
|
225
|
+
local step = 1024
|
226
|
+
local results = {}
|
146
227
|
local last_end = 0
|
147
|
-
local partial
|
228
|
+
local partial = {}
|
229
|
+
|
148
230
|
|
149
231
|
local function mget_batch(ini , fin)
|
150
232
|
log("Getting from " .. ini .. ' to ' .. fin .. ' on a total of ' .. #keys_to_mget)
|
233
|
+
|
151
234
|
partial = redis.call('MGET', unpack(keys_to_mget, ini, fin))
|
152
235
|
for _, value in pairs(partial) do table.insert(results, value) end
|
153
236
|
end
|
154
237
|
|
155
|
-
for ending = step,
|
238
|
+
for ending = step, #keys_to_mget, step do
|
156
239
|
mget_batch(last_end + 1, ending)
|
157
240
|
last_end = ending
|
158
241
|
end
|
@@ -166,16 +249,14 @@ module Rhcf
|
|
166
249
|
|
167
250
|
local function sort_and_limit_tuples(subjects, values)
|
168
251
|
local dictionary = {}
|
169
|
-
for i,
|
252
|
+
for i, evt_filter in pairs(subjects) do
|
170
253
|
local value = values[i] or 0
|
171
|
-
|
172
|
-
dictionary[subject] = (dictionary[subject] or 0) + value
|
254
|
+
dictionary[evt_filter] = (dictionary[evt_filter] or 0) + value
|
173
255
|
end
|
174
256
|
|
175
257
|
local tuples = {}
|
176
|
-
for
|
177
|
-
|
178
|
-
table.insert(tuples, { subject, value } )
|
258
|
+
for evt_filter, value in pairs(dictionary) do
|
259
|
+
table.insert(tuples, { evt_filter, value } )
|
179
260
|
end
|
180
261
|
|
181
262
|
table.sort(tuples, function(a, b) return b[2] < a[2] end )
|
@@ -186,16 +267,19 @@ module Rhcf
|
|
186
267
|
for i, tuple in pairs(tuples) do
|
187
268
|
if #new_subjects >= limit then break end
|
188
269
|
|
189
|
-
local
|
270
|
+
local evt_filter = tuple[1]
|
190
271
|
local value = tuple[2]
|
191
272
|
|
192
|
-
table.insert(new_subjects,
|
273
|
+
table.insert(new_subjects, evt_filter)
|
193
274
|
table.insert(new_counts, value)
|
194
275
|
end
|
195
276
|
|
196
277
|
return {new_subjects, new_counts}
|
197
278
|
end
|
198
279
|
|
280
|
+
log("SETKEY " .. set_key ) -- #.. " | KEY prefix: " .. key_prefix .. " FILTER PATTERN: " .. filter_pattern)
|
281
|
+
-- log("SETKEY " .. set_key .. " | KEY prefix: " .. key_prefix .. " FILTER PATTERN: " .. filter_pattern)
|
282
|
+
|
199
283
|
for _, val in ipairs(redis.call('smembers', set_key)) do
|
200
284
|
if (filter_pattern and string.match(val, filter_pattern)) or not filter_pattern then
|
201
285
|
table.insert(keys, val)
|
@@ -205,9 +289,10 @@ module Rhcf
|
|
205
289
|
|
206
290
|
if table.getn(keys) > 0 then
|
207
291
|
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
|
292
|
+
-- local sorted = sort_and_limit_tuples(keys, values)
|
293
|
+
-- log ("Values card " .. #values .. " | keys card: " .. #keys)
|
294
|
+
-- return sorted
|
295
|
+
return {keys, values}
|
211
296
|
else
|
212
297
|
return {{},{}}
|
213
298
|
end
|
@@ -215,7 +300,8 @@ module Rhcf
|
|
215
300
|
|
216
301
|
{
|
217
302
|
mget_matching_smembers: connection.script(:load, mget_matching_smembers),
|
218
|
-
smembers_matching: connection.script(:load, smembers_matching)
|
303
|
+
smembers_matching: connection.script(:load, smembers_matching),
|
304
|
+
ranking: connection.script(:load, ranking_script)
|
219
305
|
}
|
220
306
|
end
|
221
307
|
end
|
@@ -226,18 +312,17 @@ module Rhcf
|
|
226
312
|
'G'
|
227
313
|
end
|
228
314
|
|
229
|
-
def crunch_values(manager,
|
230
|
-
events = events_for_subject_on(manager,
|
315
|
+
def crunch_values(manager, evt_filter, resolution_id, time_point, subj_filter, limit = 100)
|
316
|
+
events = events_for_subject_on(manager, evt_filter, time_point, resolution_id, subj_filter)
|
231
317
|
values = {}
|
232
318
|
events.each do |event|
|
233
|
-
value = get(manager,
|
319
|
+
value = get(manager, point_prefix(manager, evt_filter, resolution_id, time_point))
|
234
320
|
values[event] = value.to_i
|
235
321
|
end
|
236
322
|
values
|
237
323
|
end
|
238
324
|
|
239
|
-
def get(manager,
|
240
|
-
a_key = [manager.prefix, a_key].flatten.join(NAMESPACE_SEPARATOR)
|
325
|
+
def get(manager, a_key)
|
241
326
|
manager.connection_to_use.get(a_key)
|
242
327
|
end
|
243
328
|
end
|
@@ -5,6 +5,26 @@ require 'rhcf/timeseries/manager'
|
|
5
5
|
require 'benchmark'
|
6
6
|
require 'stackprof'
|
7
7
|
|
8
|
+
|
9
|
+
|
10
|
+
def generate_subjects(n_pages, n_edits)
|
11
|
+
edits = [].tap do |me|
|
12
|
+
1.upto(n_edits) do |i|
|
13
|
+
me << "edit#{i}"
|
14
|
+
end
|
15
|
+
end
|
16
|
+
[].tap {|me| n_pages.times { me << [edits.sample,SecureRandom.hex].join('/') } }
|
17
|
+
end
|
18
|
+
|
19
|
+
def generate_pageviews(evts_count, subjects, mintime, maxtime)
|
20
|
+
pts = []
|
21
|
+
evts_count.times do
|
22
|
+
time = Time.at(rand( (mintime.to_i)..(maxtime.to_i) ))
|
23
|
+
pts << [ subjects.sample ,time]
|
24
|
+
end
|
25
|
+
pts
|
26
|
+
end
|
27
|
+
|
8
28
|
describe Rhcf::Timeseries::Manager do
|
9
29
|
let(:redis){Redis.new}
|
10
30
|
subject{Rhcf::Timeseries::Manager.new(connection: redis)}
|
@@ -58,6 +78,53 @@ describe Rhcf::Timeseries::Manager do
|
|
58
78
|
end
|
59
79
|
end
|
60
80
|
|
81
|
+
describe 'ranking' do
|
82
|
+
subject do
|
83
|
+
Rhcf::Timeseries::Manager.new(connection: redis,
|
84
|
+
resolutions: [:hour],
|
85
|
+
strategy: Rhcf::Timeseries::RedisMgetLuaStrategy)
|
86
|
+
end
|
87
|
+
|
88
|
+
let(:n_evts) { 3000 }
|
89
|
+
let(:n_pages) { 15 }
|
90
|
+
let(:n_edits) { 3 }
|
91
|
+
let(:start_time) { Time.parse('2015-01-01')}
|
92
|
+
let(:end_time) { Time.parse('2015-01-02')}
|
93
|
+
|
94
|
+
let(:subjects){ generate_subjects(n_pages, n_edits) }
|
95
|
+
let(:points) { generate_pageviews(n_evts, subjects, start_time, end_time)}
|
96
|
+
|
97
|
+
before do
|
98
|
+
points.each do |subject_and_time|
|
99
|
+
evt_subject, time = *subject_and_time
|
100
|
+
subject.store('pageview', {evt_subject => 1}, time, false, false)
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
let(:query) { subject.find("pageview", start_time, end_time) }
|
105
|
+
|
106
|
+
let(:top10_forca_bruta) {
|
107
|
+
acc = {}
|
108
|
+
points.each do |item|
|
109
|
+
s,_t = * item
|
110
|
+
acc[s] ||=0
|
111
|
+
acc[s] +=1
|
112
|
+
|
113
|
+
end
|
114
|
+
acc.sort_by{|i| i.last}.reverse[0,10]
|
115
|
+
}
|
116
|
+
|
117
|
+
it do
|
118
|
+
expect(points.count).to eq n_evts
|
119
|
+
expect(query.ranking(10).count).to eq 10
|
120
|
+
expect(query.ranking(10)).to eq top10_forca_bruta
|
121
|
+
expect(query.points(:hour).count).to eq 24
|
122
|
+
expect(redis.keys('*').count).to eq 24 + 24 * n_pages # ao infinito
|
123
|
+
end
|
124
|
+
|
125
|
+
|
126
|
+
end
|
127
|
+
|
61
128
|
describe "find and total" do
|
62
129
|
let(:start_time){ Time.parse("2000-01-01 00:00:00") }
|
63
130
|
before do
|
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:
|
4
|
+
version: 2.0.0pre
|
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-06-
|
11
|
+
date: 2015-06-15 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -172,9 +172,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
172
172
|
version: '0'
|
173
173
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
174
174
|
requirements:
|
175
|
-
- - "
|
175
|
+
- - ">"
|
176
176
|
- !ruby/object:Gem::Version
|
177
|
-
version:
|
177
|
+
version: 1.3.1
|
178
178
|
requirements: []
|
179
179
|
rubyforge_project:
|
180
180
|
rubygems_version: 2.2.2
|