apisonator 3.2.1 → 3.3.0
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 +4 -4
- data/CHANGELOG.md +21 -0
- data/Gemfile.base +2 -1
- data/Gemfile.lock +7 -6
- data/Gemfile.on_prem.lock +7 -6
- data/Rakefile +33 -11
- data/lib/3scale/backend/application_events.rb +2 -4
- data/lib/3scale/backend/job_fetcher.rb +28 -22
- data/lib/3scale/backend/stats/cleaner.rb +109 -28
- data/lib/3scale/backend/stats/keys.rb +6 -0
- data/lib/3scale/backend/version.rb +1 -1
- data/lib/3scale/backend/worker_async.rb +22 -1
- data/licenses.xml +5 -5
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: a8e132216edb5cd3b44967acab917d4ca4f50c692c22fb5b81c7ffff9c5d598b
|
|
4
|
+
data.tar.gz: e2e31c92b450398dbcd3a3dcc3d31ed39b2d530048de23aa06b95c4930809249
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 288b9c506a32caf81e2b3bcbf06b347e1f39f32a295a61d146ebc9b7f5953458f27114c3eceb08ef00da6293487d02f49b5c7688af16f87c0aa0ff4c70cfa48f
|
|
7
|
+
data.tar.gz: 025f8fcf1edb087de5e97ad43ecca61e25c97f061f0002b75d210a1052fa76355c5ea8a34515018d41b21428791d255341e2f36d2c178872ae549d3f97f2e52e
|
data/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,27 @@
|
|
|
2
2
|
|
|
3
3
|
Notable changes to Apisonator will be tracked in this document.
|
|
4
4
|
|
|
5
|
+
## 3.3.0 - 2021-02-09
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
|
|
9
|
+
- Rake task to delete stats keys set to 0 in the DB left there because of [this
|
|
10
|
+
issue](https://github.com/3scale/apisonator/pull/247)
|
|
11
|
+
([#250](https://github.com/3scale/apisonator/pull/250)).
|
|
12
|
+
|
|
13
|
+
### Fixed
|
|
14
|
+
|
|
15
|
+
- Made the worker more reliable when configured in async mode. Now it handles
|
|
16
|
+
connection errors better
|
|
17
|
+
([#253](https://github.com/3scale/apisonator/pull/253)),
|
|
18
|
+
([#254](https://github.com/3scale/apisonator/pull/254)), and
|
|
19
|
+
([#255](https://github.com/3scale/apisonator/pull/255)).
|
|
20
|
+
|
|
21
|
+
### Changed
|
|
22
|
+
|
|
23
|
+
- Updated async-redis to v0.5.1
|
|
24
|
+
([#251](https://github.com/3scale/apisonator/pull/251)).
|
|
25
|
+
|
|
5
26
|
## 3.2.1 - 2021-01-22
|
|
6
27
|
|
|
7
28
|
### Fixed
|
data/Gemfile.base
CHANGED
|
@@ -53,13 +53,14 @@ gem 'rake', '~> 13.0'
|
|
|
53
53
|
gem 'builder', '= 3.2.3'
|
|
54
54
|
# Use a patched resque to allow reusing their Airbrake Failure class
|
|
55
55
|
gem 'resque', git: 'https://github.com/3scale/resque', branch: '3scale'
|
|
56
|
+
gem 'redis-namespace', '~>1.8.0'
|
|
56
57
|
gem 'rack', '~> 2.1.4'
|
|
57
58
|
gem 'sinatra', '~> 2.0.3'
|
|
58
59
|
gem 'sinatra-contrib', '~> 2.0.3'
|
|
59
60
|
# Optional external error logging services
|
|
60
61
|
gem 'bugsnag', '~> 6', require: nil
|
|
61
62
|
gem 'yabeda-prometheus', '~> 0.5.0'
|
|
62
|
-
gem 'async-redis', '~> 0.5'
|
|
63
|
+
gem 'async-redis', '~> 0.5.1'
|
|
63
64
|
gem 'falcon', '~> 0.35'
|
|
64
65
|
|
|
65
66
|
# Use a patched redis-rb that fixes an issue when trying to connect with
|
data/Gemfile.lock
CHANGED
|
@@ -35,7 +35,7 @@ GIT
|
|
|
35
35
|
PATH
|
|
36
36
|
remote: .
|
|
37
37
|
specs:
|
|
38
|
-
apisonator (3.
|
|
38
|
+
apisonator (3.3.0)
|
|
39
39
|
|
|
40
40
|
GEM
|
|
41
41
|
remote: https://rubygems.org/
|
|
@@ -70,7 +70,7 @@ GEM
|
|
|
70
70
|
async (~> 1.14)
|
|
71
71
|
async-pool (0.2.0)
|
|
72
72
|
async (~> 1.8)
|
|
73
|
-
async-redis (0.5.
|
|
73
|
+
async-redis (0.5.1)
|
|
74
74
|
async (~> 1.8)
|
|
75
75
|
async-io (~> 1.10)
|
|
76
76
|
async-pool (~> 0.2)
|
|
@@ -142,7 +142,7 @@ GEM
|
|
|
142
142
|
net-scp (1.2.1)
|
|
143
143
|
net-ssh (>= 2.6.5)
|
|
144
144
|
net-ssh (4.2.0)
|
|
145
|
-
nio4r (2.5.
|
|
145
|
+
nio4r (2.5.4)
|
|
146
146
|
nokogiri (1.10.9)
|
|
147
147
|
mini_portile2 (~> 2.4.0)
|
|
148
148
|
parslet (1.8.2)
|
|
@@ -178,7 +178,7 @@ GEM
|
|
|
178
178
|
rack-test (0.8.2)
|
|
179
179
|
rack (>= 1.0, < 3)
|
|
180
180
|
rake (13.0.1)
|
|
181
|
-
redis-namespace (1.
|
|
181
|
+
redis-namespace (1.8.0)
|
|
182
182
|
redis (>= 3.0.4)
|
|
183
183
|
resque_spec (0.17.0)
|
|
184
184
|
resque (>= 1.19.0)
|
|
@@ -241,7 +241,7 @@ GEM
|
|
|
241
241
|
thread_safe (0.3.6)
|
|
242
242
|
tilt (2.0.8)
|
|
243
243
|
timecop (0.9.1)
|
|
244
|
-
timers (4.3.
|
|
244
|
+
timers (4.3.2)
|
|
245
245
|
toml (0.2.0)
|
|
246
246
|
parslet (~> 1.8.0)
|
|
247
247
|
tzinfo (1.2.7)
|
|
@@ -267,7 +267,7 @@ PLATFORMS
|
|
|
267
267
|
DEPENDENCIES
|
|
268
268
|
airbrake (= 4.3.1)
|
|
269
269
|
apisonator!
|
|
270
|
-
async-redis (~> 0.5)
|
|
270
|
+
async-redis (~> 0.5.1)
|
|
271
271
|
async-rspec
|
|
272
272
|
aws-sdk (= 2.4.2)
|
|
273
273
|
benchmark-ips (~> 2.7.2)
|
|
@@ -291,6 +291,7 @@ DEPENDENCIES
|
|
|
291
291
|
rack-test (~> 0.8.2)
|
|
292
292
|
rake (~> 13.0)
|
|
293
293
|
redis!
|
|
294
|
+
redis-namespace (~> 1.8.0)
|
|
294
295
|
resque!
|
|
295
296
|
resque_spec (~> 0.17.0)
|
|
296
297
|
resque_unit (~> 0.4.4)!
|
data/Gemfile.on_prem.lock
CHANGED
|
@@ -35,7 +35,7 @@ GIT
|
|
|
35
35
|
PATH
|
|
36
36
|
remote: .
|
|
37
37
|
specs:
|
|
38
|
-
apisonator (3.
|
|
38
|
+
apisonator (3.3.0)
|
|
39
39
|
|
|
40
40
|
GEM
|
|
41
41
|
remote: https://rubygems.org/
|
|
@@ -67,7 +67,7 @@ GEM
|
|
|
67
67
|
async (~> 1.14)
|
|
68
68
|
async-pool (0.2.0)
|
|
69
69
|
async (~> 1.8)
|
|
70
|
-
async-redis (0.5.
|
|
70
|
+
async-redis (0.5.1)
|
|
71
71
|
async (~> 1.8)
|
|
72
72
|
async-io (~> 1.10)
|
|
73
73
|
async-pool (~> 0.2)
|
|
@@ -131,7 +131,7 @@ GEM
|
|
|
131
131
|
net-scp (1.2.1)
|
|
132
132
|
net-ssh (>= 2.6.5)
|
|
133
133
|
net-ssh (4.2.0)
|
|
134
|
-
nio4r (2.5.
|
|
134
|
+
nio4r (2.5.4)
|
|
135
135
|
nokogiri (1.10.9)
|
|
136
136
|
mini_portile2 (~> 2.4.0)
|
|
137
137
|
parslet (1.8.2)
|
|
@@ -166,7 +166,7 @@ GEM
|
|
|
166
166
|
rack-test (0.8.2)
|
|
167
167
|
rack (>= 1.0, < 3)
|
|
168
168
|
rake (13.0.1)
|
|
169
|
-
redis-namespace (1.
|
|
169
|
+
redis-namespace (1.8.0)
|
|
170
170
|
redis (>= 3.0.4)
|
|
171
171
|
resque_spec (0.17.0)
|
|
172
172
|
resque (>= 1.19.0)
|
|
@@ -227,7 +227,7 @@ GEM
|
|
|
227
227
|
thread_safe (0.3.6)
|
|
228
228
|
tilt (2.0.8)
|
|
229
229
|
timecop (0.9.1)
|
|
230
|
-
timers (4.3.
|
|
230
|
+
timers (4.3.2)
|
|
231
231
|
toml (0.2.0)
|
|
232
232
|
parslet (~> 1.8.0)
|
|
233
233
|
tzinfo (1.2.7)
|
|
@@ -250,7 +250,7 @@ PLATFORMS
|
|
|
250
250
|
|
|
251
251
|
DEPENDENCIES
|
|
252
252
|
apisonator!
|
|
253
|
-
async-redis (~> 0.5)
|
|
253
|
+
async-redis (~> 0.5.1)
|
|
254
254
|
async-rspec
|
|
255
255
|
benchmark-ips (~> 2.7.2)
|
|
256
256
|
bugsnag (~> 6)
|
|
@@ -272,6 +272,7 @@ DEPENDENCIES
|
|
|
272
272
|
rack-test (~> 0.8.2)
|
|
273
273
|
rake (~> 13.0)
|
|
274
274
|
redis!
|
|
275
|
+
redis-namespace (~> 1.8.0)
|
|
275
276
|
resque!
|
|
276
277
|
resque_spec (~> 0.17.0)
|
|
277
278
|
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
|
|
@@ -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,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
|
|
@@ -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.
|
|
@@ -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.
|
|
26
|
+
<version>3.3.0</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>
|
|
@@ -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.
|
|
4
|
+
version: 3.3.0
|
|
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: 2021-
|
|
19
|
+
date: 2021-02-09 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.
|