apisonator 3.1.0 → 3.3.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +61 -0
- data/Gemfile.base +8 -3
- data/Gemfile.lock +12 -10
- data/Gemfile.on_prem.lock +12 -10
- data/Rakefile +33 -11
- data/app/api/internal/service_tokens.rb +8 -0
- data/app/api/internal/stats.rb +6 -25
- data/lib/3scale/backend/application_events.rb +2 -4
- data/lib/3scale/backend/configuration.rb +1 -6
- data/lib/3scale/backend/errors.rb +0 -6
- data/lib/3scale/backend/job_fetcher.rb +28 -22
- data/lib/3scale/backend/listener_metrics.rb +14 -12
- data/lib/3scale/backend/stats.rb +0 -4
- data/lib/3scale/backend/stats/aggregators/base.rb +8 -1
- data/lib/3scale/backend/stats/cleaner.rb +109 -28
- data/lib/3scale/backend/stats/keys.rb +6 -0
- data/lib/3scale/backend/stats/period_commons.rb +0 -3
- data/lib/3scale/backend/transactor.rb +31 -4
- data/lib/3scale/backend/version.rb +1 -1
- data/lib/3scale/backend/worker_async.rb +22 -1
- data/licenses.xml +6 -6
- metadata +2 -6
- data/lib/3scale/backend/stats/delete_job_def.rb +0 -60
- data/lib/3scale/backend/stats/key_generator.rb +0 -73
- data/lib/3scale/backend/stats/partition_eraser_job.rb +0 -58
- data/lib/3scale/backend/stats/partition_generator_job.rb +0 -46
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: '09ccae8a2719bc3717d22dcdd78c5c996cbd4774f2528b91d77c74fbbe2e51b7'
|
4
|
+
data.tar.gz: 4a9f237a6d1b25f9cc763817030cbe562536a6d5ed89374326db2f99ea82971c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2818f8b3a97b701218c28ae93339650db57a0229181ee3573a24ffe215611747c1381e9f0e643eb9dc85b81d13d43336a08252a88cd63cd7fc78ed9a182c5c83
|
7
|
+
data.tar.gz: 7a9c2e389c239094e2c6f303749056d5a8ea2d216f5097e98e9a87ef5c3bcd4ea2d190c4b8277d659608f7c80f57d5d727bf65eb9330f4c08fb8777ab5a1ae81
|
data/CHANGELOG.md
CHANGED
@@ -2,6 +2,67 @@
|
|
2
2
|
|
3
3
|
Notable changes to Apisonator will be tracked in this document.
|
4
4
|
|
5
|
+
## 3.3.1.1 - 2021-02-12
|
6
|
+
|
7
|
+
### Changed
|
8
|
+
|
9
|
+
- Updated our Puma fork to v4.3.7
|
10
|
+
([#261](https://github.com/3scale/apisonator/pull/261)).
|
11
|
+
|
12
|
+
## 3.3.1 - 2021-02-11
|
13
|
+
|
14
|
+
### Fixed
|
15
|
+
|
16
|
+
- Usages with `#0` (set to 0) no longer generate unnecessary stats keys in Redis
|
17
|
+
([#258](https://github.com/3scale/apisonator/pull/258)).
|
18
|
+
|
19
|
+
## 3.3.0 - 2021-02-09
|
20
|
+
|
21
|
+
### Added
|
22
|
+
|
23
|
+
- Rake task to delete stats keys set to 0 in the DB left there because of [this
|
24
|
+
issue](https://github.com/3scale/apisonator/pull/247)
|
25
|
+
([#250](https://github.com/3scale/apisonator/pull/250)).
|
26
|
+
|
27
|
+
### Fixed
|
28
|
+
|
29
|
+
- Made the worker more reliable when configured in async mode. Now it handles
|
30
|
+
connection errors better
|
31
|
+
([#253](https://github.com/3scale/apisonator/pull/253)),
|
32
|
+
([#254](https://github.com/3scale/apisonator/pull/254)), and
|
33
|
+
([#255](https://github.com/3scale/apisonator/pull/255)).
|
34
|
+
|
35
|
+
### Changed
|
36
|
+
|
37
|
+
- Updated async-redis to v0.5.1
|
38
|
+
([#251](https://github.com/3scale/apisonator/pull/251)).
|
39
|
+
|
40
|
+
## 3.2.1 - 2021-01-22
|
41
|
+
|
42
|
+
### Fixed
|
43
|
+
|
44
|
+
- Reports of 0 hits no longer generate unnecessary stats keys in Redis
|
45
|
+
([#247](https://github.com/3scale/apisonator/pull/247)).
|
46
|
+
|
47
|
+
## 3.2.0 - 2021-01-19
|
48
|
+
|
49
|
+
### Added
|
50
|
+
|
51
|
+
- New endpoint in the internal API to get the provider key for a given (token,
|
52
|
+
service_id) pair ([#243](https://github.com/3scale/apisonator/pull/243)).
|
53
|
+
|
54
|
+
### Changed
|
55
|
+
|
56
|
+
- The config file used when running in a Docker image now parses "1" and "true"
|
57
|
+
(case-insensitive) as true
|
58
|
+
([#245](https://github.com/3scale/apisonator/pull/245)).
|
59
|
+
|
60
|
+
### Fixed
|
61
|
+
|
62
|
+
- Fixed some metrics of the internal API that were not being counted
|
63
|
+
correctly([#244](https://github.com/3scale/apisonator/pull/244)).
|
64
|
+
|
65
|
+
|
5
66
|
## 3.1.0 - 2020-10-14
|
6
67
|
|
7
68
|
### Added
|
data/Gemfile.base
CHANGED
@@ -15,11 +15,15 @@ platform :ruby do
|
|
15
15
|
end
|
16
16
|
|
17
17
|
group :test do
|
18
|
+
# Newer versions of rack-test don't work well with rspec-api-documentation.
|
19
|
+
# See https://github.com/rack/rack-test/pull/223 &
|
20
|
+
# https://github.com/zipmark/rspec_api_documentation/issues/342
|
21
|
+
gem 'rack-test', '= 0.8.2'
|
22
|
+
|
18
23
|
gem 'benchmark-ips', '~> 2.7.2'
|
19
24
|
gem 'mocha', '~> 1.3'
|
20
25
|
gem 'nokogiri', '~> 1.10.8'
|
21
26
|
gem 'pkg-config', '~> 1.1.7'
|
22
|
-
gem 'rack-test', '~> 0.8.2'
|
23
27
|
gem 'resque_unit', '~> 0.4.4', source: 'https://rubygems.org'
|
24
28
|
gem 'test-unit', '~> 3.2.6'
|
25
29
|
gem 'resque_spec', '~> 0.17.0'
|
@@ -42,7 +46,7 @@ group :development, :test do
|
|
42
46
|
end
|
43
47
|
|
44
48
|
# Default server by platform
|
45
|
-
gem 'puma', git: 'https://github.com/3scale/puma',
|
49
|
+
gem 'puma', git: 'https://github.com/3scale/puma', branch: '3scale-4.3.7'
|
46
50
|
# gems required by the runner
|
47
51
|
gem 'gli', '~> 2.16.1', require: nil
|
48
52
|
# Workers
|
@@ -53,13 +57,14 @@ gem 'rake', '~> 13.0'
|
|
53
57
|
gem 'builder', '= 3.2.3'
|
54
58
|
# Use a patched resque to allow reusing their Airbrake Failure class
|
55
59
|
gem 'resque', git: 'https://github.com/3scale/resque', branch: '3scale'
|
60
|
+
gem 'redis-namespace', '~>1.8.0'
|
56
61
|
gem 'rack', '~> 2.1.4'
|
57
62
|
gem 'sinatra', '~> 2.0.3'
|
58
63
|
gem 'sinatra-contrib', '~> 2.0.3'
|
59
64
|
# Optional external error logging services
|
60
65
|
gem 'bugsnag', '~> 6', require: nil
|
61
66
|
gem 'yabeda-prometheus', '~> 0.5.0'
|
62
|
-
gem 'async-redis', '~> 0.5'
|
67
|
+
gem 'async-redis', '~> 0.5.1'
|
63
68
|
gem 'falcon', '~> 0.35'
|
64
69
|
|
65
70
|
# Use a patched redis-rb that fixes an issue when trying to connect with
|
data/Gemfile.lock
CHANGED
@@ -1,9 +1,10 @@
|
|
1
1
|
GIT
|
2
2
|
remote: https://github.com/3scale/puma
|
3
|
-
revision:
|
4
|
-
|
3
|
+
revision: c0601d08695839b8ffd0f380e91c3b91c1e8b754
|
4
|
+
branch: 3scale-4.3.7
|
5
5
|
specs:
|
6
|
-
puma (
|
6
|
+
puma (4.3.7)
|
7
|
+
nio4r (~> 2.0)
|
7
8
|
|
8
9
|
GIT
|
9
10
|
remote: https://github.com/3scale/redis-rb
|
@@ -35,7 +36,7 @@ GIT
|
|
35
36
|
PATH
|
36
37
|
remote: .
|
37
38
|
specs:
|
38
|
-
apisonator (3.1.
|
39
|
+
apisonator (3.3.1.1)
|
39
40
|
|
40
41
|
GEM
|
41
42
|
remote: https://rubygems.org/
|
@@ -70,7 +71,7 @@ GEM
|
|
70
71
|
async (~> 1.14)
|
71
72
|
async-pool (0.2.0)
|
72
73
|
async (~> 1.8)
|
73
|
-
async-redis (0.5.
|
74
|
+
async-redis (0.5.1)
|
74
75
|
async (~> 1.8)
|
75
76
|
async-io (~> 1.10)
|
76
77
|
async-pool (~> 0.2)
|
@@ -142,7 +143,7 @@ GEM
|
|
142
143
|
net-scp (1.2.1)
|
143
144
|
net-ssh (>= 2.6.5)
|
144
145
|
net-ssh (4.2.0)
|
145
|
-
nio4r (2.5.
|
146
|
+
nio4r (2.5.4)
|
146
147
|
nokogiri (1.10.9)
|
147
148
|
mini_portile2 (~> 2.4.0)
|
148
149
|
parslet (1.8.2)
|
@@ -178,7 +179,7 @@ GEM
|
|
178
179
|
rack-test (0.8.2)
|
179
180
|
rack (>= 1.0, < 3)
|
180
181
|
rake (13.0.1)
|
181
|
-
redis-namespace (1.
|
182
|
+
redis-namespace (1.8.0)
|
182
183
|
redis (>= 3.0.4)
|
183
184
|
resque_spec (0.17.0)
|
184
185
|
resque (>= 1.19.0)
|
@@ -241,7 +242,7 @@ GEM
|
|
241
242
|
thread_safe (0.3.6)
|
242
243
|
tilt (2.0.8)
|
243
244
|
timecop (0.9.1)
|
244
|
-
timers (4.3.
|
245
|
+
timers (4.3.2)
|
245
246
|
toml (0.2.0)
|
246
247
|
parslet (~> 1.8.0)
|
247
248
|
tzinfo (1.2.7)
|
@@ -267,7 +268,7 @@ PLATFORMS
|
|
267
268
|
DEPENDENCIES
|
268
269
|
airbrake (= 4.3.1)
|
269
270
|
apisonator!
|
270
|
-
async-redis (~> 0.5)
|
271
|
+
async-redis (~> 0.5.1)
|
271
272
|
async-rspec
|
272
273
|
aws-sdk (= 2.4.2)
|
273
274
|
benchmark-ips (~> 2.7.2)
|
@@ -288,9 +289,10 @@ DEPENDENCIES
|
|
288
289
|
pry-doc (~> 0.11.1)
|
289
290
|
puma!
|
290
291
|
rack (~> 2.1.4)
|
291
|
-
rack-test (
|
292
|
+
rack-test (= 0.8.2)
|
292
293
|
rake (~> 13.0)
|
293
294
|
redis!
|
295
|
+
redis-namespace (~> 1.8.0)
|
294
296
|
resque!
|
295
297
|
resque_spec (~> 0.17.0)
|
296
298
|
resque_unit (~> 0.4.4)!
|
data/Gemfile.on_prem.lock
CHANGED
@@ -1,9 +1,10 @@
|
|
1
1
|
GIT
|
2
2
|
remote: https://github.com/3scale/puma
|
3
|
-
revision:
|
4
|
-
|
3
|
+
revision: c0601d08695839b8ffd0f380e91c3b91c1e8b754
|
4
|
+
branch: 3scale-4.3.7
|
5
5
|
specs:
|
6
|
-
puma (
|
6
|
+
puma (4.3.7)
|
7
|
+
nio4r (~> 2.0)
|
7
8
|
|
8
9
|
GIT
|
9
10
|
remote: https://github.com/3scale/redis-rb
|
@@ -35,7 +36,7 @@ GIT
|
|
35
36
|
PATH
|
36
37
|
remote: .
|
37
38
|
specs:
|
38
|
-
apisonator (3.1.
|
39
|
+
apisonator (3.3.1.1)
|
39
40
|
|
40
41
|
GEM
|
41
42
|
remote: https://rubygems.org/
|
@@ -67,7 +68,7 @@ GEM
|
|
67
68
|
async (~> 1.14)
|
68
69
|
async-pool (0.2.0)
|
69
70
|
async (~> 1.8)
|
70
|
-
async-redis (0.5.
|
71
|
+
async-redis (0.5.1)
|
71
72
|
async (~> 1.8)
|
72
73
|
async-io (~> 1.10)
|
73
74
|
async-pool (~> 0.2)
|
@@ -131,7 +132,7 @@ GEM
|
|
131
132
|
net-scp (1.2.1)
|
132
133
|
net-ssh (>= 2.6.5)
|
133
134
|
net-ssh (4.2.0)
|
134
|
-
nio4r (2.5.
|
135
|
+
nio4r (2.5.4)
|
135
136
|
nokogiri (1.10.9)
|
136
137
|
mini_portile2 (~> 2.4.0)
|
137
138
|
parslet (1.8.2)
|
@@ -166,7 +167,7 @@ GEM
|
|
166
167
|
rack-test (0.8.2)
|
167
168
|
rack (>= 1.0, < 3)
|
168
169
|
rake (13.0.1)
|
169
|
-
redis-namespace (1.
|
170
|
+
redis-namespace (1.8.0)
|
170
171
|
redis (>= 3.0.4)
|
171
172
|
resque_spec (0.17.0)
|
172
173
|
resque (>= 1.19.0)
|
@@ -227,7 +228,7 @@ GEM
|
|
227
228
|
thread_safe (0.3.6)
|
228
229
|
tilt (2.0.8)
|
229
230
|
timecop (0.9.1)
|
230
|
-
timers (4.3.
|
231
|
+
timers (4.3.2)
|
231
232
|
toml (0.2.0)
|
232
233
|
parslet (~> 1.8.0)
|
233
234
|
tzinfo (1.2.7)
|
@@ -250,7 +251,7 @@ PLATFORMS
|
|
250
251
|
|
251
252
|
DEPENDENCIES
|
252
253
|
apisonator!
|
253
|
-
async-redis (~> 0.5)
|
254
|
+
async-redis (~> 0.5.1)
|
254
255
|
async-rspec
|
255
256
|
benchmark-ips (~> 2.7.2)
|
256
257
|
bugsnag (~> 6)
|
@@ -269,9 +270,10 @@ DEPENDENCIES
|
|
269
270
|
pry-doc (~> 0.11.1)
|
270
271
|
puma!
|
271
272
|
rack (~> 2.1.4)
|
272
|
-
rack-test (
|
273
|
+
rack-test (= 0.8.2)
|
273
274
|
rake (~> 13.0)
|
274
275
|
redis!
|
276
|
+
redis-namespace (~> 1.8.0)
|
275
277
|
resque!
|
276
278
|
resque_spec (~> 0.17.0)
|
277
279
|
resque_unit (~> 0.4.4)!
|
data/Rakefile
CHANGED
@@ -261,27 +261,49 @@ task :reschedule_failed_jobs do
|
|
261
261
|
"Pending failed jobs: #{result[:failed_current]}."
|
262
262
|
end
|
263
263
|
|
264
|
-
desc 'Delete stats of services marked for deletion'
|
265
264
|
namespace :stats do
|
265
|
+
desc 'Delete stats of services marked for deletion'
|
266
266
|
task :cleanup, [:redis_urls, :log_deleted_keys] do |_, args|
|
267
|
-
|
267
|
+
redis_conns = redis_conns(args[:redis_urls])
|
268
268
|
|
269
|
-
if
|
269
|
+
if redis_conns.empty?
|
270
270
|
puts 'No Redis URLs specified'
|
271
271
|
exit(false)
|
272
272
|
end
|
273
273
|
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
274
|
+
ThreeScale::Backend::Stats::Cleaner.delete!(
|
275
|
+
redis_conns, log_deleted_keys: logger_for_deleted_keys(args[:log_deleted_keys])
|
276
|
+
)
|
277
|
+
end
|
278
|
+
|
279
|
+
desc 'Delete stats keys set to 0'
|
280
|
+
task :delete_stats_keys_set_to_0, [:redis_urls, :log_deleted_keys] do |_, args|
|
281
|
+
redis_conns = redis_conns(args[:redis_urls])
|
282
|
+
|
283
|
+
if redis_conns.empty?
|
284
|
+
puts 'No Redis URLs specified'
|
285
|
+
exit(false)
|
279
286
|
end
|
280
287
|
|
281
|
-
|
288
|
+
ThreeScale::Backend::Stats::Cleaner.delete_stats_keys_set_to_0(
|
289
|
+
redis_conns, log_deleted_keys: logger_for_deleted_keys(args[:log_deleted_keys])
|
290
|
+
)
|
291
|
+
end
|
292
|
+
end
|
282
293
|
|
283
|
-
|
284
|
-
|
294
|
+
def redis_conns(urls)
|
295
|
+
redis_urls = urls && urls.split(' ')
|
296
|
+
|
297
|
+
return [] if redis_urls.nil? || redis_urls.empty?
|
298
|
+
|
299
|
+
redis_urls.map do |redis_url|
|
300
|
+
parsed_uri = URI.parse(ThreeScale::Backend::Storage::Helpers.send(
|
301
|
+
:to_redis_uri, redis_url)
|
285
302
|
)
|
303
|
+
Redis.new(host: parsed_uri.host, port: parsed_uri.port)
|
286
304
|
end
|
287
305
|
end
|
306
|
+
|
307
|
+
def logger_for_deleted_keys(arg_log_deleted_keys)
|
308
|
+
arg_log_deleted_keys == 'true' ? STDOUT : nil
|
309
|
+
end
|
@@ -7,6 +7,14 @@ module ThreeScale
|
|
7
7
|
ServiceToken.exists?(token, service_id) ? 200 : 404
|
8
8
|
end
|
9
9
|
|
10
|
+
get '/:token/:service_id/provider_key' do |token, service_id|
|
11
|
+
if ServiceToken.exists?(token, service_id)
|
12
|
+
{ status: :found, provider_key: Service.provider_key_for(service_id) }.to_json
|
13
|
+
else
|
14
|
+
respond_with_404('token/service combination not found'.freeze)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
10
18
|
post '/' do
|
11
19
|
check_tokens_param!
|
12
20
|
|
data/app/api/internal/stats.rb
CHANGED
@@ -6,32 +6,13 @@ module ThreeScale
|
|
6
6
|
respond_with_404('service not found') unless Service.exists?(params[:service_id])
|
7
7
|
end
|
8
8
|
|
9
|
-
# This
|
10
|
-
#
|
11
|
-
|
12
|
-
delete '' do |service_id|
|
13
|
-
delete_stats_job_attrs = api_params Stats::DeleteJobDef
|
14
|
-
delete_stats_job_attrs[:service_id] = service_id
|
15
|
-
delete_stats_job_attrs[:from] = delete_stats_job_attrs[:from].to_i
|
16
|
-
delete_stats_job_attrs[:to] = delete_stats_job_attrs[:to].to_i
|
17
|
-
begin
|
18
|
-
Stats::DeleteJobDef.new(delete_stats_job_attrs).run_async
|
19
|
-
rescue DeleteServiceStatsValidationError => e
|
20
|
-
[400, headers, { status: :error, error: e.message }.to_json]
|
21
|
-
else
|
22
|
-
{ status: :to_be_deleted }.to_json
|
23
|
-
end
|
24
|
-
=end
|
25
|
-
|
26
|
-
# This is an alternative to the above. It just adds the service to a
|
27
|
-
# Redis set to marked is as "to be deleted".
|
28
|
-
# Later a script can read that set and actually delete the keys.
|
29
|
-
# Read the docs of the Stats::Cleaner class for more details.
|
9
|
+
# This adds the service to a Redis set to mark is as "to be deleted".
|
10
|
+
# Later a script can read that set and actually delete the keys. Read
|
11
|
+
# the docs of the Stats::Cleaner class for more details.
|
30
12
|
#
|
31
|
-
# Notice that this method ignores the "from" and "to" parameters
|
32
|
-
# system calls this method, they're always
|
33
|
-
#
|
34
|
-
# implementation of the option above easier.
|
13
|
+
# Notice that this method ignores the "from" and "to" parameters used in
|
14
|
+
# previous versions. When system calls this method, they're always
|
15
|
+
# interested in deleting all the keys.
|
35
16
|
delete '' do |service_id|
|
36
17
|
Stats::Cleaner.mark_service_to_be_deleted(service_id)
|
37
18
|
{ status: :to_be_deleted }.to_json
|
@@ -40,10 +40,8 @@ module ThreeScale
|
|
40
40
|
private
|
41
41
|
|
42
42
|
def self.first_traffic(service_id, application_id)
|
43
|
-
|
44
|
-
|
45
|
-
)
|
46
|
-
if storage.sadd(key, encode_key(application_id))
|
43
|
+
if storage.sadd(Stats::Keys.set_of_apps_with_traffic(service_id),
|
44
|
+
encode_key(application_id))
|
47
45
|
EventStorage.store(:first_traffic,
|
48
46
|
{ service_id: service_id,
|
49
47
|
application_id: application_id,
|
@@ -32,8 +32,6 @@ module ThreeScale
|
|
32
32
|
|
33
33
|
CONFIG_DELETE_STATS_BATCH_SIZE = 50
|
34
34
|
private_constant :CONFIG_DELETE_STATS_BATCH_SIZE
|
35
|
-
CONFIG_DELETE_STATS_PARTITION_BATCH_SIZE = 1000
|
36
|
-
private_constant :CONFIG_DELETE_STATS_PARTITION_BATCH_SIZE
|
37
35
|
|
38
36
|
@configuration = Configuration::Loader.new
|
39
37
|
|
@@ -54,7 +52,7 @@ module ThreeScale
|
|
54
52
|
config.add_section(:analytics_redis, :server,
|
55
53
|
:connect_timeout, :read_timeout, :write_timeout)
|
56
54
|
config.add_section(:hoptoad, :service, :api_key)
|
57
|
-
config.add_section(:stats, :bucket_size, :delete_batch_size
|
55
|
+
config.add_section(:stats, :bucket_size, :delete_batch_size)
|
58
56
|
config.add_section(:redshift, :host, :port, :dbname, :user, :password)
|
59
57
|
config.add_section(:statsd, :host, :port)
|
60
58
|
config.add_section(:internal_api, :user, :password)
|
@@ -125,9 +123,6 @@ module ThreeScale
|
|
125
123
|
config.stats.delete_batch_size = parse_int(config.stats.delete_batch_size,
|
126
124
|
CONFIG_DELETE_STATS_BATCH_SIZE)
|
127
125
|
|
128
|
-
config.stats.delete_partition_batch_size = parse_int(config.stats.delete_partition_batch_size,
|
129
|
-
CONFIG_DELETE_STATS_PARTITION_BATCH_SIZE)
|
130
|
-
|
131
126
|
# often we don't have a log_file setting - generate it here from
|
132
127
|
# the log_path setting.
|
133
128
|
log_file = config.log_file
|
@@ -292,12 +292,6 @@ module ThreeScale
|
|
292
292
|
end
|
293
293
|
end
|
294
294
|
|
295
|
-
class DeleteServiceStatsValidationError < Error
|
296
|
-
def initialize(service_id, msg)
|
297
|
-
super "Delete stats job context validation error. Service: #{service_id}. Error: #{msg}"
|
298
|
-
end
|
299
|
-
end
|
300
|
-
|
301
295
|
class EndUsersNoLongerSupported < BadRequest
|
302
296
|
def initialize
|
303
297
|
super 'End-users are no longer supported, do not specify the user_id parameter'.freeze
|
@@ -32,25 +32,6 @@ module ThreeScale
|
|
32
32
|
DEFAULT_WAIT_BEFORE_FETCHING_MORE_JOBS
|
33
33
|
end
|
34
34
|
|
35
|
-
def pop_from_queue
|
36
|
-
begin
|
37
|
-
encoded_job = @redis.blpop(*@queues, timeout: @fetch_timeout)
|
38
|
-
rescue Redis::BaseConnectionError, Errno::ECONNREFUSED, Errno::EPIPE => e
|
39
|
-
raise RedisConnectionError.new(e.message)
|
40
|
-
rescue Redis::CommandError => e
|
41
|
-
# Redis::CommandError from redis-rb can be raised for multiple
|
42
|
-
# reasons, so we need to check the error message to distinguish
|
43
|
-
# connection errors from the rest.
|
44
|
-
if e.message == 'ERR Connection timed out'.freeze
|
45
|
-
raise RedisConnectionError.new(e.message)
|
46
|
-
else
|
47
|
-
raise e
|
48
|
-
end
|
49
|
-
end
|
50
|
-
|
51
|
-
encoded_job
|
52
|
-
end
|
53
|
-
|
54
35
|
def fetch
|
55
36
|
encoded_job = pop_from_queue
|
56
37
|
return nil if encoded_job.nil? || encoded_job.empty?
|
@@ -99,10 +80,11 @@ module ThreeScale
|
|
99
80
|
|
100
81
|
# Re-instantiate Redis instance. This is needed to recover from
|
101
82
|
# Errno::EPIPE, not sure if there are others.
|
102
|
-
@redis =
|
103
|
-
|
104
|
-
|
83
|
+
@redis = Redis::Namespace.new(
|
84
|
+
WorkerAsync.const_get(:RESQUE_REDIS_NAMESPACE),
|
85
|
+
redis: QueueStorage.connection(Backend.environment, Backend.configuration)
|
105
86
|
)
|
87
|
+
|
106
88
|
# If there is a different kind of error, it's probably a
|
107
89
|
# programming error. Like sending an invalid blpop command to
|
108
90
|
# Redis. In that case, let the worker crash.
|
@@ -111,12 +93,36 @@ module ThreeScale
|
|
111
93
|
end
|
112
94
|
end
|
113
95
|
|
96
|
+
rescue Exception => e
|
97
|
+
Worker.logger.notify(e)
|
98
|
+
ensure
|
114
99
|
job_queue.close
|
115
100
|
end
|
116
101
|
|
117
102
|
def shutdown
|
118
103
|
@shutdown = true
|
119
104
|
end
|
105
|
+
|
106
|
+
private
|
107
|
+
|
108
|
+
def pop_from_queue
|
109
|
+
begin
|
110
|
+
encoded_job = @redis.blpop(*@queues, timeout: @fetch_timeout)
|
111
|
+
rescue Redis::BaseConnectionError, Errno::ECONNREFUSED, Errno::EPIPE => e
|
112
|
+
raise RedisConnectionError.new(e.message)
|
113
|
+
rescue Redis::CommandError => e
|
114
|
+
# Redis::CommandError from redis-rb can be raised for multiple
|
115
|
+
# reasons, so we need to check the error message to distinguish
|
116
|
+
# connection errors from the rest.
|
117
|
+
if e.message == 'ERR Connection timed out'.freeze
|
118
|
+
raise RedisConnectionError.new(e.message)
|
119
|
+
else
|
120
|
+
raise e
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
encoded_job
|
125
|
+
end
|
120
126
|
end
|
121
127
|
end
|
122
128
|
end
|
@@ -13,19 +13,21 @@ module ThreeScale
|
|
13
13
|
}
|
14
14
|
private_constant :AUTH_AND_REPORT_REQUEST_TYPES
|
15
15
|
|
16
|
+
# Only the first match is taken into account, that's why for example,
|
17
|
+
# "/\/services\/.*\/stats/" needs to appear before "/\/services/"
|
16
18
|
INTERNAL_API_PATHS = [
|
17
|
-
[/\/services\/.*\/alert_limits/, 'alerts'],
|
18
|
-
[/\/services\/.*\/applications\/.*\/keys/, 'application_keys'],
|
19
|
-
[/\/services\/.*\/applications\/.*\/referrer_filters/, 'application_referrer_filters'],
|
20
|
-
[/\/services\/.*\/applications/, '
|
21
|
-
[/\/services\/.*\/
|
22
|
-
[/\/
|
23
|
-
[/\/
|
24
|
-
[/\/
|
25
|
-
[/\/
|
26
|
-
[/\/services\/.*\/stats/, 'stats'],
|
27
|
-
[/\/services\/.*\/plans\/.*\/usagelimits/, 'usage_limits'],
|
28
|
-
[/\/services
|
19
|
+
[/\/services\/.*\/alert_limits/, 'alerts'.freeze],
|
20
|
+
[/\/services\/.*\/applications\/.*\/keys/, 'application_keys'.freeze],
|
21
|
+
[/\/services\/.*\/applications\/.*\/referrer_filters/, 'application_referrer_filters'.freeze],
|
22
|
+
[/\/services\/.*\/applications\/.*\/utilization/, 'utilization'.freeze],
|
23
|
+
[/\/services\/.*\/applications/, 'applications'.freeze],
|
24
|
+
[/\/services\/.*\/errors/, 'errors'.freeze],
|
25
|
+
[/\/events/, 'events'.freeze],
|
26
|
+
[/\/services\/.*\/metrics/, 'metrics'.freeze],
|
27
|
+
[/\/service_tokens/, 'service_tokens'.freeze],
|
28
|
+
[/\/services\/.*\/stats/, 'stats'.freeze],
|
29
|
+
[/\/services\/.*\/plans\/.*\/usagelimits/, 'usage_limits'.freeze],
|
30
|
+
[/\/services/, 'services'.freeze],
|
29
31
|
].freeze
|
30
32
|
private_constant :INTERNAL_API_PATHS
|
31
33
|
|
data/lib/3scale/backend/stats.rb
CHANGED
@@ -1,8 +1,4 @@
|
|
1
1
|
require '3scale/backend/stats/codes_commons'
|
2
2
|
require '3scale/backend/stats/period_commons'
|
3
3
|
require '3scale/backend/stats/aggregator'
|
4
|
-
require '3scale/backend/stats/delete_job_def'
|
5
|
-
require '3scale/backend/stats/key_generator'
|
6
|
-
require '3scale/backend/stats/partition_generator_job'
|
7
|
-
require '3scale/backend/stats/partition_eraser_job'
|
8
4
|
require '3scale/backend/stats/cleaner'
|
@@ -20,7 +20,14 @@ module ThreeScale
|
|
20
20
|
key = counter_key(prefix_key, granularity.new(timestamp))
|
21
21
|
expire_time = Stats::PeriodCommons.expire_time_for_granularity(granularity)
|
22
22
|
|
23
|
-
|
23
|
+
# We don't need to store stats keys set to 0. It wastes Redis
|
24
|
+
# memory because for rate-limiting and stats, a key of set to 0
|
25
|
+
# is equivalent to a key that does not exist.
|
26
|
+
if cmd == :set && value == 0
|
27
|
+
storage.del(key)
|
28
|
+
else
|
29
|
+
store_key(cmd, key, value, expire_time)
|
30
|
+
end
|
24
31
|
|
25
32
|
unless Stats::PeriodCommons::EXCLUDED_FOR_BUCKETS.include?(granularity)
|
26
33
|
keys_for_bucket << key
|
@@ -45,6 +45,12 @@ module ThreeScale
|
|
45
45
|
STATS_KEY_PREFIX = 'stats/'.freeze
|
46
46
|
private_constant :STATS_KEY_PREFIX
|
47
47
|
|
48
|
+
REDIS_CONN_ERRORS = [Redis::BaseConnectionError, Errno::ECONNREFUSED, Errno::EPIPE].freeze
|
49
|
+
private_constant :REDIS_CONN_ERRORS
|
50
|
+
|
51
|
+
MAX_RETRIES_REDIS_ERRORS = 3
|
52
|
+
private_constant :MAX_RETRIES_REDIS_ERRORS
|
53
|
+
|
48
54
|
class << self
|
49
55
|
include Logging
|
50
56
|
def mark_service_to_be_deleted(service_id)
|
@@ -77,37 +83,73 @@ module ThreeScale
|
|
77
83
|
logger.info("Going to delete the stats keys for these services: #{services.to_a}")
|
78
84
|
|
79
85
|
unless services.empty?
|
80
|
-
|
81
|
-
redis_conns.each do |redis_conn|
|
86
|
+
_ok, failed = redis_conns.partition do |redis_conn|
|
82
87
|
begin
|
83
88
|
delete_keys(redis_conn, services, log_deleted_keys)
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
logger.error("Error while deleting stats of server #{redis_conn}: #{e}")
|
89
|
-
delete_successful = false
|
90
|
-
rescue Redis::CommandError => e
|
91
|
-
# Redis::CommandError from redis-rb can be raised for multiple
|
92
|
-
# reasons, so we need to check the error message to distinguish
|
93
|
-
# connection errors from the rest.
|
94
|
-
if e.message == 'ERR Connection timed out'.freeze
|
95
|
-
logger.error("Error while deleting stats of server #{redis_conn}: #{e}")
|
96
|
-
delete_successful = false
|
97
|
-
else
|
98
|
-
raise e
|
99
|
-
end
|
89
|
+
true
|
90
|
+
rescue => e
|
91
|
+
handle_redis_exception(e, redis_conn)
|
92
|
+
false
|
100
93
|
end
|
101
94
|
end
|
102
95
|
|
103
|
-
remove_services_from_delete_set(services) if
|
96
|
+
with_retries { remove_services_from_delete_set(services) } if failed.empty?
|
97
|
+
|
98
|
+
failed.each do |failed_conn|
|
99
|
+
logger.error("Error while deleting stats of server #{failed_conn}")
|
100
|
+
end
|
104
101
|
end
|
105
102
|
|
106
103
|
logger.info("Finished deleting the stats keys for these services: #{services.to_a}")
|
107
104
|
end
|
108
105
|
|
106
|
+
# Deletes all the stats keys set to 0.
|
107
|
+
#
|
108
|
+
# Stats keys set to 0 are useless and occupy Redis memory
|
109
|
+
# unnecessarily. They were generated due to a bug in previous versions
|
110
|
+
# of Apisonator.
|
111
|
+
# Ref: https://github.com/3scale/apisonator/pull/247
|
112
|
+
#
|
113
|
+
# As the .delete function, this one also receives a collection of
|
114
|
+
# instantiated Redis clients and those need to connect to Redis
|
115
|
+
# servers directly.
|
116
|
+
#
|
117
|
+
# @param [Array] redis_conns Instantiated Redis clients.
|
118
|
+
# @param [IO] log_deleted_keys IO where to write the logs. Defaults to
|
119
|
+
# nil (logs nothing).
|
120
|
+
def delete_stats_keys_set_to_0(redis_conns, log_deleted_keys: nil)
|
121
|
+
_ok, failed = redis_conns.partition do |redis_conn|
|
122
|
+
begin
|
123
|
+
delete_stats_keys_with_val_0(redis_conn, log_deleted_keys)
|
124
|
+
true
|
125
|
+
rescue => e
|
126
|
+
handle_redis_exception(e, redis_conn)
|
127
|
+
false
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
failed.each do |failed_conn|
|
132
|
+
logger.error("Error while deleting stats of server #{failed_conn}")
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
109
136
|
private
|
110
137
|
|
138
|
+
def handle_redis_exception(exception, redis_conn)
|
139
|
+
# If it's a connection error, do nothing so we can continue with
|
140
|
+
# other shards. If it's another kind of error, it could be caused by
|
141
|
+
# a bug, so better re-raise.
|
142
|
+
|
143
|
+
case exception
|
144
|
+
when *REDIS_CONN_ERRORS
|
145
|
+
# Do nothing.
|
146
|
+
when Redis::CommandError
|
147
|
+
raise exception if exception.message != 'ERR Connection timed out'.freeze
|
148
|
+
else
|
149
|
+
raise exception
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
111
153
|
# Returns a set with the services included in the
|
112
154
|
# SET_WITH_SERVICES_MARKED_FOR_DELETION Redis set.
|
113
155
|
def services_to_delete
|
@@ -133,19 +175,21 @@ module ThreeScale
|
|
133
175
|
cursor = 0
|
134
176
|
|
135
177
|
loop do
|
136
|
-
|
178
|
+
with_retries do
|
179
|
+
cursor, keys = redis_conn.scan(cursor, count: SCAN_SLICE)
|
137
180
|
|
138
|
-
|
181
|
+
to_delete = keys.select { |key| delete_key?(key, services) }
|
139
182
|
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
183
|
+
unless to_delete.empty?
|
184
|
+
if log_deleted_keys
|
185
|
+
values = redis_conn.mget(*(to_delete.to_a))
|
186
|
+
to_delete.each_with_index do |k, i|
|
187
|
+
log_deleted_keys.puts "#{k} #{values[i]}"
|
188
|
+
end
|
145
189
|
end
|
146
|
-
end
|
147
190
|
|
148
|
-
|
191
|
+
redis_conn.del(to_delete)
|
192
|
+
end
|
149
193
|
end
|
150
194
|
|
151
195
|
break if cursor.to_i == 0
|
@@ -188,6 +232,43 @@ module ThreeScale
|
|
188
232
|
# simply ignore those keys.
|
189
233
|
nil
|
190
234
|
end
|
235
|
+
|
236
|
+
def delete_stats_keys_with_val_0(redis_conn, log_deleted_keys)
|
237
|
+
cursor = 0
|
238
|
+
|
239
|
+
loop do
|
240
|
+
with_retries do
|
241
|
+
cursor, keys = redis_conn.scan(cursor, count: SCAN_SLICE)
|
242
|
+
|
243
|
+
stats_keys = keys.select { |k| is_stats_key?(k) }
|
244
|
+
|
245
|
+
unless stats_keys.empty?
|
246
|
+
values = redis_conn.mget(*stats_keys)
|
247
|
+
to_delete = stats_keys.zip(values).select { |_, v| v == '0'.freeze }.map(&:first)
|
248
|
+
|
249
|
+
unless to_delete.empty?
|
250
|
+
redis_conn.del(to_delete)
|
251
|
+
to_delete.each { |k| log_deleted_keys.puts k } if log_deleted_keys
|
252
|
+
end
|
253
|
+
end
|
254
|
+
end
|
255
|
+
|
256
|
+
break if cursor.to_i == 0
|
257
|
+
|
258
|
+
sleep(SLEEP_BETWEEN_SCANS)
|
259
|
+
end
|
260
|
+
end
|
261
|
+
|
262
|
+
def with_retries(max = MAX_RETRIES_REDIS_ERRORS)
|
263
|
+
retries = 0
|
264
|
+
begin
|
265
|
+
yield
|
266
|
+
rescue Exception => e
|
267
|
+
retries += 1
|
268
|
+
retry if retries < max
|
269
|
+
raise e
|
270
|
+
end
|
271
|
+
end
|
191
272
|
end
|
192
273
|
end
|
193
274
|
end
|
@@ -70,6 +70,12 @@ module ThreeScale
|
|
70
70
|
key
|
71
71
|
end
|
72
72
|
|
73
|
+
def set_of_apps_with_traffic(service_id)
|
74
|
+
Stats::Keys.applications_key_prefix(
|
75
|
+
Stats::Keys.service_key_prefix(service_id)
|
76
|
+
)
|
77
|
+
end
|
78
|
+
|
73
79
|
# We want all the buckets to go to the same Redis shard.
|
74
80
|
# The reason is that SUNION support in Twemproxy requires that the
|
75
81
|
# supplied keys hash to the same server.
|
@@ -12,9 +12,6 @@ module ThreeScale
|
|
12
12
|
GRANULARITY_EXPIRATION_TIME = { Period[:minute] => 180 }.freeze
|
13
13
|
private_constant :GRANULARITY_EXPIRATION_TIME
|
14
14
|
|
15
|
-
PERMANENT_SERVICE_GRANULARITIES = (SERVICE_GRANULARITIES - GRANULARITY_EXPIRATION_TIME.keys).freeze
|
16
|
-
PERMANENT_EXPANDED_GRANULARITIES = (EXPANDED_GRANULARITIES - GRANULARITY_EXPIRATION_TIME.keys).freeze
|
17
|
-
|
18
15
|
# We are not going to send metrics with granularity 'eternity' or
|
19
16
|
# 'week' to Kinesis, so there is no point in storing them in Redis
|
20
17
|
# buckets.
|
@@ -20,8 +20,14 @@ module ThreeScale
|
|
20
20
|
def report(provider_key, service_id, transactions, context_info = {})
|
21
21
|
service = Service.load_with_provider_key!(service_id, provider_key)
|
22
22
|
|
23
|
-
|
24
|
-
|
23
|
+
# A usage of 0 does not affect rate-limits or stats, so we do not need
|
24
|
+
# to report it.
|
25
|
+
filtered_transactions = filter_usages_with_0(transactions.clone)
|
26
|
+
|
27
|
+
return if filtered_transactions.empty?
|
28
|
+
|
29
|
+
report_enqueue(service.id, filtered_transactions, context_info)
|
30
|
+
notify_report(provider_key, filtered_transactions.size)
|
25
31
|
end
|
26
32
|
|
27
33
|
def authorize(provider_key, params, context_info = {})
|
@@ -137,9 +143,17 @@ module ThreeScale
|
|
137
143
|
|
138
144
|
usage = params[:usage]
|
139
145
|
|
140
|
-
|
146
|
+
filtered_usage = filter_metrics_without_inc(usage.clone) if usage
|
147
|
+
|
148
|
+
if ((filtered_usage && !filtered_usage.empty?) || params[:log]) && status.authorized?
|
141
149
|
application_id = status.application.id
|
142
|
-
|
150
|
+
|
151
|
+
report_enqueue(
|
152
|
+
status.service_id,
|
153
|
+
{ 0 => {"app_id" => application_id, "usage" => filtered_usage, "log" => params[:log] } },
|
154
|
+
request: { extensions: request_info[:extensions] }
|
155
|
+
)
|
156
|
+
|
143
157
|
notify_authrep(provider_key, usage ? 1 : 0)
|
144
158
|
else
|
145
159
|
notify_authorize(provider_key)
|
@@ -182,6 +196,19 @@ module ThreeScale
|
|
182
196
|
end
|
183
197
|
end
|
184
198
|
|
199
|
+
def filter_usages_with_0(transactions)
|
200
|
+
# There are plenty of existing tests using both a string and a symbol
|
201
|
+
# when accessing the usage.
|
202
|
+
transactions.delete_if do |_idx, tx|
|
203
|
+
(usage = tx['usage'.freeze] || tx[:usage]) or next
|
204
|
+
filter_metrics_without_inc(usage).empty?
|
205
|
+
end
|
206
|
+
end
|
207
|
+
|
208
|
+
def filter_metrics_without_inc(usage)
|
209
|
+
usage.delete_if { |_metric, delta| delta.to_s == '0'.freeze }
|
210
|
+
end
|
211
|
+
|
185
212
|
def storage
|
186
213
|
Storage.instance
|
187
214
|
end
|
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'async'
|
2
|
+
require 'redis-namespace'
|
2
3
|
require '3scale/backend/job_fetcher'
|
3
4
|
|
4
5
|
module ThreeScale
|
@@ -10,6 +11,9 @@ module ThreeScale
|
|
10
11
|
DEFAULT_MAX_CONCURRENT_JOBS = 20
|
11
12
|
private_constant :DEFAULT_MAX_CONCURRENT_JOBS
|
12
13
|
|
14
|
+
RESQUE_REDIS_NAMESPACE = :resque
|
15
|
+
private_constant :RESQUE_REDIS_NAMESPACE
|
16
|
+
|
13
17
|
def initialize(options = {})
|
14
18
|
trap('TERM') { shutdown }
|
15
19
|
trap('INT') { shutdown }
|
@@ -17,7 +21,7 @@ module ThreeScale
|
|
17
21
|
@one_off = options[:one_off]
|
18
22
|
@jobs = Queue.new # Thread-safe queue
|
19
23
|
|
20
|
-
@job_fetcher = options[:job_fetcher] || JobFetcher.new
|
24
|
+
@job_fetcher = options[:job_fetcher] || JobFetcher.new(redis_client: redis_client)
|
21
25
|
|
22
26
|
@max_concurrent_jobs = configuration.async_worker.max_concurrent_jobs ||
|
23
27
|
DEFAULT_MAX_CONCURRENT_JOBS
|
@@ -64,6 +68,10 @@ module ThreeScale
|
|
64
68
|
# unblocks when there are new jobs or when .close() is called
|
65
69
|
job = @jobs.pop
|
66
70
|
|
71
|
+
# If job is nil, it means that the queue is closed. No more jobs are
|
72
|
+
# going to be pushed, so shutdown.
|
73
|
+
shutdown unless job
|
74
|
+
|
67
75
|
break if @shutdown
|
68
76
|
|
69
77
|
@reactor.async { perform(job) }
|
@@ -83,6 +91,19 @@ module ThreeScale
|
|
83
91
|
Async { @job_fetcher.start(@jobs) }
|
84
92
|
end
|
85
93
|
end
|
94
|
+
|
95
|
+
# Returns a new Redis client with namespace "resque".
|
96
|
+
# In the async worker, the job fetcher runs in a separate thread, and we
|
97
|
+
# need to avoid sharing an already instantiated client like the one in
|
98
|
+
# Resque::Helpers initialized in lib/3scale/backend.rb (Resque.redis).
|
99
|
+
# Failing to do so, will raise errors because of fibers shared across
|
100
|
+
# threads.
|
101
|
+
def redis_client
|
102
|
+
Redis::Namespace.new(
|
103
|
+
RESQUE_REDIS_NAMESPACE,
|
104
|
+
redis: QueueStorage.connection(Backend.environment, Backend.configuration)
|
105
|
+
)
|
106
|
+
end
|
86
107
|
end
|
87
108
|
end
|
88
109
|
end
|
data/licenses.xml
CHANGED
@@ -23,7 +23,7 @@
|
|
23
23
|
</dependency>
|
24
24
|
<dependency>
|
25
25
|
<packageName>apisonator</packageName>
|
26
|
-
<version>3.1.
|
26
|
+
<version>3.3.1.1</version>
|
27
27
|
<licenses>
|
28
28
|
<license>
|
29
29
|
<name>Apache 2.0</name>
|
@@ -93,7 +93,7 @@
|
|
93
93
|
</dependency>
|
94
94
|
<dependency>
|
95
95
|
<packageName>async-redis</packageName>
|
96
|
-
<version>0.5.
|
96
|
+
<version>0.5.1</version>
|
97
97
|
<licenses>
|
98
98
|
<license>
|
99
99
|
<name>MIT</name>
|
@@ -525,7 +525,7 @@
|
|
525
525
|
</dependency>
|
526
526
|
<dependency>
|
527
527
|
<packageName>nio4r</packageName>
|
528
|
-
<version>2.5.
|
528
|
+
<version>2.5.4</version>
|
529
529
|
<licenses>
|
530
530
|
<license>
|
531
531
|
<name>MIT</name>
|
@@ -709,7 +709,7 @@
|
|
709
709
|
</dependency>
|
710
710
|
<dependency>
|
711
711
|
<packageName>puma</packageName>
|
712
|
-
<version>
|
712
|
+
<version>4.3.7</version>
|
713
713
|
<licenses>
|
714
714
|
<license>
|
715
715
|
<name>New BSD</name>
|
@@ -769,7 +769,7 @@
|
|
769
769
|
</dependency>
|
770
770
|
<dependency>
|
771
771
|
<packageName>redis-namespace</packageName>
|
772
|
-
<version>1.
|
772
|
+
<version>1.8.0</version>
|
773
773
|
<licenses>
|
774
774
|
<license>
|
775
775
|
<name>MIT</name>
|
@@ -1043,7 +1043,7 @@
|
|
1043
1043
|
</dependency>
|
1044
1044
|
<dependency>
|
1045
1045
|
<packageName>timers</packageName>
|
1046
|
-
<version>4.3.
|
1046
|
+
<version>4.3.2</version>
|
1047
1047
|
<licenses>
|
1048
1048
|
<license>
|
1049
1049
|
<name>MIT</name>
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: apisonator
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 3.1.
|
4
|
+
version: 3.3.1.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Adam Ciganek
|
@@ -16,7 +16,7 @@ authors:
|
|
16
16
|
autorequire:
|
17
17
|
bindir: bin
|
18
18
|
cert_chain: []
|
19
|
-
date:
|
19
|
+
date: 2021-02-12 00:00:00.000000000 Z
|
20
20
|
dependencies: []
|
21
21
|
description: This gem provides a daemon that handles authorization and reporting of
|
22
22
|
web services managed by 3scale.
|
@@ -136,11 +136,7 @@ files:
|
|
136
136
|
- lib/3scale/backend/stats/bucket_storage.rb
|
137
137
|
- lib/3scale/backend/stats/cleaner.rb
|
138
138
|
- lib/3scale/backend/stats/codes_commons.rb
|
139
|
-
- lib/3scale/backend/stats/delete_job_def.rb
|
140
|
-
- lib/3scale/backend/stats/key_generator.rb
|
141
139
|
- lib/3scale/backend/stats/keys.rb
|
142
|
-
- lib/3scale/backend/stats/partition_eraser_job.rb
|
143
|
-
- lib/3scale/backend/stats/partition_generator_job.rb
|
144
140
|
- lib/3scale/backend/stats/period_commons.rb
|
145
141
|
- lib/3scale/backend/stats/stats_parser.rb
|
146
142
|
- lib/3scale/backend/stats/storage.rb
|
@@ -1,60 +0,0 @@
|
|
1
|
-
module ThreeScale
|
2
|
-
module Backend
|
3
|
-
module Stats
|
4
|
-
class DeleteJobDef
|
5
|
-
ATTRIBUTES = %i[service_id applications metrics from to context_info].freeze
|
6
|
-
private_constant :ATTRIBUTES
|
7
|
-
attr_reader(*ATTRIBUTES)
|
8
|
-
|
9
|
-
def self.attribute_names
|
10
|
-
ATTRIBUTES
|
11
|
-
end
|
12
|
-
|
13
|
-
def initialize(params = {})
|
14
|
-
ATTRIBUTES.each do |key|
|
15
|
-
instance_variable_set("@#{key}".to_sym, params[key]) unless params[key].nil?
|
16
|
-
end
|
17
|
-
validate
|
18
|
-
end
|
19
|
-
|
20
|
-
def run_async
|
21
|
-
Resque.enqueue(PartitionGeneratorJob, Time.now.getutc.to_f, service_id, applications,
|
22
|
-
metrics, from, to, context_info)
|
23
|
-
end
|
24
|
-
|
25
|
-
def to_json
|
26
|
-
to_hash.to_json
|
27
|
-
end
|
28
|
-
|
29
|
-
def to_hash
|
30
|
-
Hash[ATTRIBUTES.collect { |key| [key, send(key)] }]
|
31
|
-
end
|
32
|
-
|
33
|
-
private
|
34
|
-
|
35
|
-
def validate
|
36
|
-
# from and to valid epoch times
|
37
|
-
raise_validation_error('from field not integer') unless from.is_a? Integer
|
38
|
-
raise_validation_error('from field is zero') if from.zero?
|
39
|
-
raise_validation_error('to field not integer') unless to.is_a? Integer
|
40
|
-
raise_validation_error('to field is zero') if to.zero?
|
41
|
-
raise_validation_error('from < to fields') if Time.at(to) < Time.at(from)
|
42
|
-
# application is array
|
43
|
-
raise_validation_error('applications field') unless applications.is_a? Array
|
44
|
-
raise_validation_error('applications values') unless applications.all? do |x|
|
45
|
-
x.is_a?(String) || x.is_a?(Integer)
|
46
|
-
end
|
47
|
-
# metrics is array
|
48
|
-
raise_validation_error('metrics field') unless metrics.is_a? Array
|
49
|
-
raise_validation_error('metrics values') unless metrics.all? do |x|
|
50
|
-
x.is_a?(String) || x.is_a?(Integer)
|
51
|
-
end
|
52
|
-
end
|
53
|
-
|
54
|
-
def raise_validation_error(msg)
|
55
|
-
raise DeleteServiceStatsValidationError.new(service_id, msg)
|
56
|
-
end
|
57
|
-
end
|
58
|
-
end
|
59
|
-
end
|
60
|
-
end
|
@@ -1,73 +0,0 @@
|
|
1
|
-
module ThreeScale
|
2
|
-
module Backend
|
3
|
-
module Stats
|
4
|
-
class KeyGenerator
|
5
|
-
attr_reader :service_id, :applications, :metrics, :from, :to
|
6
|
-
|
7
|
-
def initialize(service_id:, applications: [], metrics: [], from:, to:, **)
|
8
|
-
@service_id = service_id
|
9
|
-
@applications = applications
|
10
|
-
@metrics = metrics
|
11
|
-
@from = from
|
12
|
-
@to = to
|
13
|
-
end
|
14
|
-
|
15
|
-
def keys
|
16
|
-
response_code_service_keys +
|
17
|
-
response_code_application_keys +
|
18
|
-
usage_service_keys +
|
19
|
-
usage_application_keys
|
20
|
-
end
|
21
|
-
|
22
|
-
private
|
23
|
-
|
24
|
-
def periods(granularities)
|
25
|
-
granularities.flat_map do |granularity|
|
26
|
-
(Period[granularity].new(Time.at(from))..Period[granularity].new(Time.at(to))).to_a
|
27
|
-
end
|
28
|
-
end
|
29
|
-
|
30
|
-
def response_codes
|
31
|
-
CodesCommons::TRACKED_CODES + CodesCommons::TRACKED_CODE_GROUPS
|
32
|
-
end
|
33
|
-
|
34
|
-
def response_code_service_keys
|
35
|
-
periods(PeriodCommons::PERMANENT_SERVICE_GRANULARITIES).flat_map do |period|
|
36
|
-
response_codes.flat_map do |response_code|
|
37
|
-
Keys.service_response_code_value_key(service_id, response_code, period)
|
38
|
-
end
|
39
|
-
end
|
40
|
-
end
|
41
|
-
|
42
|
-
def response_code_application_keys
|
43
|
-
periods(PeriodCommons::PERMANENT_EXPANDED_GRANULARITIES).flat_map do |period|
|
44
|
-
response_codes.flat_map do |response_code|
|
45
|
-
applications.flat_map do |application|
|
46
|
-
Keys.application_response_code_value_key(service_id, application,
|
47
|
-
response_code, period)
|
48
|
-
end
|
49
|
-
end
|
50
|
-
end
|
51
|
-
end
|
52
|
-
|
53
|
-
def usage_service_keys
|
54
|
-
periods(PeriodCommons::PERMANENT_SERVICE_GRANULARITIES).flat_map do |period|
|
55
|
-
metrics.flat_map do |metric|
|
56
|
-
Keys.service_usage_value_key(service_id, metric, period)
|
57
|
-
end
|
58
|
-
end
|
59
|
-
end
|
60
|
-
|
61
|
-
def usage_application_keys
|
62
|
-
periods(PeriodCommons::PERMANENT_EXPANDED_GRANULARITIES).flat_map do |period|
|
63
|
-
metrics.flat_map do |metric|
|
64
|
-
applications.flat_map do |application|
|
65
|
-
Keys.application_usage_value_key(service_id, application, metric, period)
|
66
|
-
end
|
67
|
-
end
|
68
|
-
end
|
69
|
-
end
|
70
|
-
end
|
71
|
-
end
|
72
|
-
end
|
73
|
-
end
|
@@ -1,58 +0,0 @@
|
|
1
|
-
module ThreeScale
|
2
|
-
module Backend
|
3
|
-
module Stats
|
4
|
-
# Job for deleting service stats
|
5
|
-
# Perform actual key deletion from a key partition definition
|
6
|
-
class PartitionEraserJob < BackgroundJob
|
7
|
-
# low priority queue
|
8
|
-
@queue = :stats
|
9
|
-
|
10
|
-
class << self
|
11
|
-
include StorageHelpers
|
12
|
-
include Configurable
|
13
|
-
|
14
|
-
def perform_logged(_enqueue_time, service_id, applications, metrics,
|
15
|
-
from, to, offset, length, context_info = {})
|
16
|
-
job = DeleteJobDef.new(
|
17
|
-
service_id: service_id,
|
18
|
-
applications: applications,
|
19
|
-
metrics: metrics,
|
20
|
-
from: from,
|
21
|
-
to: to
|
22
|
-
)
|
23
|
-
|
24
|
-
validate_job(job, offset, length)
|
25
|
-
|
26
|
-
stats_key_gen = KeyGenerator.new(job.to_hash)
|
27
|
-
|
28
|
-
stats_key_gen.keys.drop(offset).take(length).each_slice(configuration.stats.delete_batch_size) do |slice|
|
29
|
-
storage.del(slice)
|
30
|
-
end
|
31
|
-
|
32
|
-
[true, { job: job.to_hash, offset: offset, lenght: length }.to_json]
|
33
|
-
rescue Backend::Error => error
|
34
|
-
[false, "#{service_id} #{error}"]
|
35
|
-
end
|
36
|
-
|
37
|
-
private
|
38
|
-
|
39
|
-
def validate_job(job, offset, length)
|
40
|
-
unless offset.is_a? Integer
|
41
|
-
raise DeleteServiceStatsValidationError.new(job.service_id, 'offset field value ' \
|
42
|
-
"[#{offset}] validation error")
|
43
|
-
end
|
44
|
-
|
45
|
-
unless length.is_a? Integer
|
46
|
-
raise DeleteServiceStatsValidationError.new(job.service_id, 'length field value ' \
|
47
|
-
"[#{length}] validation error")
|
48
|
-
end
|
49
|
-
end
|
50
|
-
|
51
|
-
def enqueue_time(args)
|
52
|
-
args[0]
|
53
|
-
end
|
54
|
-
end
|
55
|
-
end
|
56
|
-
end
|
57
|
-
end
|
58
|
-
end
|
@@ -1,46 +0,0 @@
|
|
1
|
-
module ThreeScale
|
2
|
-
module Backend
|
3
|
-
module Stats
|
4
|
-
# Job for deleting service stats
|
5
|
-
# Maps delete job definition to a set of non overlapping key set partitions
|
6
|
-
class PartitionGeneratorJob < BackgroundJob
|
7
|
-
# low priority queue
|
8
|
-
@queue = :stats
|
9
|
-
|
10
|
-
class << self
|
11
|
-
include Configurable
|
12
|
-
|
13
|
-
def perform_logged(_enqueue_time, service_id, applications, metrics,
|
14
|
-
from, to, context_info = {})
|
15
|
-
job = DeleteJobDef.new(
|
16
|
-
service_id: service_id,
|
17
|
-
applications: applications,
|
18
|
-
metrics: metrics,
|
19
|
-
from: from,
|
20
|
-
to: to
|
21
|
-
)
|
22
|
-
|
23
|
-
stats_key_gen = KeyGenerator.new(job.to_hash)
|
24
|
-
|
25
|
-
# Generate partitions
|
26
|
-
0.step(stats_key_gen.keys.count, configuration.stats.delete_partition_batch_size).each do |idx|
|
27
|
-
Resque.enqueue(PartitionEraserJob, Time.now.getutc.to_f, service_id, applications,
|
28
|
-
metrics, from, to, idx,
|
29
|
-
configuration.stats.delete_partition_batch_size, context_info)
|
30
|
-
end
|
31
|
-
|
32
|
-
[true, job.to_json]
|
33
|
-
rescue Backend::Error => error
|
34
|
-
[false, "#{service_id} #{error}"]
|
35
|
-
end
|
36
|
-
|
37
|
-
private
|
38
|
-
|
39
|
-
def enqueue_time(args)
|
40
|
-
args[0]
|
41
|
-
end
|
42
|
-
end
|
43
|
-
end
|
44
|
-
end
|
45
|
-
end
|
46
|
-
end
|