apisonator 2.100.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/CHANGELOG.md +317 -0
- data/Gemfile +11 -0
- data/Gemfile.base +65 -0
- data/Gemfile.lock +319 -0
- data/Gemfile.on_prem +1 -0
- data/Gemfile.on_prem.lock +297 -0
- data/LICENSE +202 -0
- data/NOTICE +15 -0
- data/README.md +230 -0
- data/Rakefile +287 -0
- data/apisonator.gemspec +47 -0
- data/app/api/api.rb +13 -0
- data/app/api/internal/alert_limits.rb +32 -0
- data/app/api/internal/application_keys.rb +49 -0
- data/app/api/internal/application_referrer_filters.rb +43 -0
- data/app/api/internal/applications.rb +77 -0
- data/app/api/internal/errors.rb +54 -0
- data/app/api/internal/events.rb +42 -0
- data/app/api/internal/internal.rb +104 -0
- data/app/api/internal/metrics.rb +40 -0
- data/app/api/internal/service_tokens.rb +46 -0
- data/app/api/internal/services.rb +58 -0
- data/app/api/internal/stats.rb +42 -0
- data/app/api/internal/usagelimits.rb +62 -0
- data/app/api/internal/utilization.rb +23 -0
- data/bin/3scale_backend +223 -0
- data/bin/3scale_backend_worker +26 -0
- data/config.ru +4 -0
- data/config/puma.rb +192 -0
- data/config/schedule.rb +9 -0
- data/ext/mkrf_conf.rb +64 -0
- data/lib/3scale/backend.rb +67 -0
- data/lib/3scale/backend/alert_limit.rb +56 -0
- data/lib/3scale/backend/alerts.rb +137 -0
- data/lib/3scale/backend/analytics/kinesis.rb +3 -0
- data/lib/3scale/backend/analytics/kinesis/adapter.rb +180 -0
- data/lib/3scale/backend/analytics/kinesis/exporter.rb +86 -0
- data/lib/3scale/backend/analytics/kinesis/job.rb +135 -0
- data/lib/3scale/backend/analytics/redshift.rb +3 -0
- data/lib/3scale/backend/analytics/redshift/adapter.rb +367 -0
- data/lib/3scale/backend/analytics/redshift/importer.rb +83 -0
- data/lib/3scale/backend/analytics/redshift/job.rb +33 -0
- data/lib/3scale/backend/application.rb +330 -0
- data/lib/3scale/backend/application_events.rb +76 -0
- data/lib/3scale/backend/background_job.rb +65 -0
- data/lib/3scale/backend/configurable.rb +20 -0
- data/lib/3scale/backend/configuration.rb +151 -0
- data/lib/3scale/backend/configuration/loader.rb +42 -0
- data/lib/3scale/backend/constants.rb +19 -0
- data/lib/3scale/backend/cors.rb +84 -0
- data/lib/3scale/backend/distributed_lock.rb +67 -0
- data/lib/3scale/backend/environment.rb +21 -0
- data/lib/3scale/backend/error_storage.rb +52 -0
- data/lib/3scale/backend/errors.rb +343 -0
- data/lib/3scale/backend/event_storage.rb +120 -0
- data/lib/3scale/backend/experiment.rb +84 -0
- data/lib/3scale/backend/extensions.rb +5 -0
- data/lib/3scale/backend/extensions/array.rb +19 -0
- data/lib/3scale/backend/extensions/hash.rb +26 -0
- data/lib/3scale/backend/extensions/nil_class.rb +13 -0
- data/lib/3scale/backend/extensions/redis.rb +44 -0
- data/lib/3scale/backend/extensions/string.rb +13 -0
- data/lib/3scale/backend/extensions/time.rb +110 -0
- data/lib/3scale/backend/failed_jobs_scheduler.rb +141 -0
- data/lib/3scale/backend/job_fetcher.rb +122 -0
- data/lib/3scale/backend/listener.rb +728 -0
- data/lib/3scale/backend/listener_metrics.rb +99 -0
- data/lib/3scale/backend/logging.rb +48 -0
- data/lib/3scale/backend/logging/external.rb +44 -0
- data/lib/3scale/backend/logging/external/impl.rb +93 -0
- data/lib/3scale/backend/logging/external/impl/airbrake.rb +66 -0
- data/lib/3scale/backend/logging/external/impl/bugsnag.rb +69 -0
- data/lib/3scale/backend/logging/external/impl/default.rb +18 -0
- data/lib/3scale/backend/logging/external/resque.rb +57 -0
- data/lib/3scale/backend/logging/logger.rb +18 -0
- data/lib/3scale/backend/logging/middleware.rb +62 -0
- data/lib/3scale/backend/logging/middleware/json_writer.rb +21 -0
- data/lib/3scale/backend/logging/middleware/text_writer.rb +60 -0
- data/lib/3scale/backend/logging/middleware/writer.rb +143 -0
- data/lib/3scale/backend/logging/worker.rb +107 -0
- data/lib/3scale/backend/manifest.rb +80 -0
- data/lib/3scale/backend/memoizer.rb +277 -0
- data/lib/3scale/backend/metric.rb +275 -0
- data/lib/3scale/backend/metric/collection.rb +91 -0
- data/lib/3scale/backend/oauth.rb +4 -0
- data/lib/3scale/backend/oauth/token.rb +26 -0
- data/lib/3scale/backend/oauth/token_key.rb +30 -0
- data/lib/3scale/backend/oauth/token_storage.rb +313 -0
- data/lib/3scale/backend/oauth/token_value.rb +25 -0
- data/lib/3scale/backend/period.rb +3 -0
- data/lib/3scale/backend/period/boundary.rb +107 -0
- data/lib/3scale/backend/period/cache.rb +28 -0
- data/lib/3scale/backend/period/period.rb +402 -0
- data/lib/3scale/backend/queue_storage.rb +16 -0
- data/lib/3scale/backend/rack.rb +49 -0
- data/lib/3scale/backend/rack/exception_catcher.rb +136 -0
- data/lib/3scale/backend/rack/internal_error_catcher.rb +23 -0
- data/lib/3scale/backend/rack/prometheus.rb +19 -0
- data/lib/3scale/backend/saas.rb +6 -0
- data/lib/3scale/backend/saas_analytics.rb +4 -0
- data/lib/3scale/backend/server.rb +30 -0
- data/lib/3scale/backend/server/falcon.rb +52 -0
- data/lib/3scale/backend/server/puma.rb +71 -0
- data/lib/3scale/backend/service.rb +317 -0
- data/lib/3scale/backend/service_token.rb +97 -0
- data/lib/3scale/backend/stats.rb +8 -0
- data/lib/3scale/backend/stats/aggregator.rb +170 -0
- data/lib/3scale/backend/stats/aggregators/base.rb +72 -0
- data/lib/3scale/backend/stats/aggregators/response_code.rb +58 -0
- data/lib/3scale/backend/stats/aggregators/usage.rb +34 -0
- data/lib/3scale/backend/stats/bucket_reader.rb +135 -0
- data/lib/3scale/backend/stats/bucket_storage.rb +108 -0
- data/lib/3scale/backend/stats/cleaner.rb +195 -0
- data/lib/3scale/backend/stats/codes_commons.rb +14 -0
- data/lib/3scale/backend/stats/delete_job_def.rb +60 -0
- data/lib/3scale/backend/stats/key_generator.rb +73 -0
- data/lib/3scale/backend/stats/keys.rb +104 -0
- data/lib/3scale/backend/stats/partition_eraser_job.rb +58 -0
- data/lib/3scale/backend/stats/partition_generator_job.rb +46 -0
- data/lib/3scale/backend/stats/period_commons.rb +34 -0
- data/lib/3scale/backend/stats/stats_parser.rb +141 -0
- data/lib/3scale/backend/stats/storage.rb +113 -0
- data/lib/3scale/backend/statsd.rb +14 -0
- data/lib/3scale/backend/storable.rb +35 -0
- data/lib/3scale/backend/storage.rb +40 -0
- data/lib/3scale/backend/storage_async.rb +4 -0
- data/lib/3scale/backend/storage_async/async_redis.rb +21 -0
- data/lib/3scale/backend/storage_async/client.rb +205 -0
- data/lib/3scale/backend/storage_async/pipeline.rb +79 -0
- data/lib/3scale/backend/storage_async/resque_extensions.rb +30 -0
- data/lib/3scale/backend/storage_helpers.rb +278 -0
- data/lib/3scale/backend/storage_key_helpers.rb +9 -0
- data/lib/3scale/backend/storage_sync.rb +43 -0
- data/lib/3scale/backend/transaction.rb +62 -0
- data/lib/3scale/backend/transactor.rb +177 -0
- data/lib/3scale/backend/transactor/limit_headers.rb +54 -0
- data/lib/3scale/backend/transactor/notify_batcher.rb +139 -0
- data/lib/3scale/backend/transactor/notify_job.rb +47 -0
- data/lib/3scale/backend/transactor/process_job.rb +33 -0
- data/lib/3scale/backend/transactor/report_job.rb +84 -0
- data/lib/3scale/backend/transactor/status.rb +236 -0
- data/lib/3scale/backend/transactor/usage_report.rb +182 -0
- data/lib/3scale/backend/usage.rb +63 -0
- data/lib/3scale/backend/usage_limit.rb +115 -0
- data/lib/3scale/backend/use_cases/provider_key_change_use_case.rb +60 -0
- data/lib/3scale/backend/util.rb +17 -0
- data/lib/3scale/backend/validators.rb +26 -0
- data/lib/3scale/backend/validators/base.rb +36 -0
- data/lib/3scale/backend/validators/key.rb +17 -0
- data/lib/3scale/backend/validators/limits.rb +57 -0
- data/lib/3scale/backend/validators/oauth_key.rb +15 -0
- data/lib/3scale/backend/validators/oauth_setting.rb +15 -0
- data/lib/3scale/backend/validators/redirect_uri.rb +33 -0
- data/lib/3scale/backend/validators/referrer.rb +60 -0
- data/lib/3scale/backend/validators/service_state.rb +15 -0
- data/lib/3scale/backend/validators/state.rb +15 -0
- data/lib/3scale/backend/version.rb +5 -0
- data/lib/3scale/backend/views/oauth_access_tokens.builder +14 -0
- data/lib/3scale/backend/views/oauth_app_id_by_token.builder +4 -0
- data/lib/3scale/backend/worker.rb +87 -0
- data/lib/3scale/backend/worker_async.rb +88 -0
- data/lib/3scale/backend/worker_metrics.rb +44 -0
- data/lib/3scale/backend/worker_sync.rb +32 -0
- data/lib/3scale/bundler_shim.rb +17 -0
- data/lib/3scale/prometheus_server.rb +10 -0
- data/lib/3scale/tasks/connectivity.rake +41 -0
- data/lib/3scale/tasks/helpers.rb +3 -0
- data/lib/3scale/tasks/helpers/environment.rb +23 -0
- data/lib/3scale/tasks/stats.rake +131 -0
- data/lib/3scale/tasks/swagger.rake +46 -0
- data/licenses.xml +1215 -0
- metadata +227 -0
@@ -0,0 +1,277 @@
|
|
1
|
+
raise "Memoizer is not thread safe" if ThreeScale::Backend::Manifest.thread_safe?
|
2
|
+
|
3
|
+
module ThreeScale
|
4
|
+
module Backend
|
5
|
+
class Memoizer
|
6
|
+
EXPIRE = 60
|
7
|
+
PURGE = 60
|
8
|
+
MAX_ENTRIES = 10000
|
9
|
+
ACTIVE = true
|
10
|
+
private_constant :EXPIRE, :PURGE, :MAX_ENTRIES, :ACTIVE
|
11
|
+
|
12
|
+
def self.reset!
|
13
|
+
# Initialize class instance variables
|
14
|
+
# Note: we would be better off pre-allocating the Hash size
|
15
|
+
# ie. rb_hash_new_with_size(MAX_ENTRIES)
|
16
|
+
@memoizer_cache = Hash.new
|
17
|
+
@memoizer_purge_time = Time.now.getutc.to_i + PURGE
|
18
|
+
@memoizer_stats_count = 0
|
19
|
+
@memoizer_stats_hits = 0
|
20
|
+
end
|
21
|
+
|
22
|
+
reset!
|
23
|
+
|
24
|
+
# Key management:
|
25
|
+
#
|
26
|
+
# When automatically memoizing using the decorator, you should
|
27
|
+
# NEVER assume a specific key format. Here are some helpers to
|
28
|
+
# let you build keys to clear or getting them.
|
29
|
+
#
|
30
|
+
# You should only use build_keys_for_class and build_key
|
31
|
+
#
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
def self.build_class_key(klass)
|
36
|
+
classkey = klass.to_s
|
37
|
+
# This can receive both objects and classes, so check if we can
|
38
|
+
# call singleton_class? before actually doing so.
|
39
|
+
if klass.respond_to? :singleton_class? and klass.singleton_class?
|
40
|
+
# obtain class from Ruby's metaclass notation
|
41
|
+
classkey.split(':'.freeze).delete_if do |k|
|
42
|
+
k[0] == '#'.freeze
|
43
|
+
end.join(':'.freeze).split('>'.freeze).first
|
44
|
+
else
|
45
|
+
classkey
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def self.build_method_key(classkey, methodname)
|
50
|
+
classkey + '.'.freeze + methodname
|
51
|
+
end
|
52
|
+
|
53
|
+
def self.build_args_key(methodkey, *args)
|
54
|
+
if args.empty?
|
55
|
+
methodkey
|
56
|
+
else
|
57
|
+
methodkey + '-'.freeze + args.join('-'.freeze)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
# A method to inspect in debugging mode the contents of the cache
|
62
|
+
def self.cache
|
63
|
+
raise 'Memoizer.cache only in development!' unless ThreeScale::Backend.development?
|
64
|
+
@memoizer_cache
|
65
|
+
end
|
66
|
+
|
67
|
+
public
|
68
|
+
|
69
|
+
# Generate a key for the given class, method and args
|
70
|
+
def self.build_key(klass, method, *args)
|
71
|
+
key = build_class_key klass
|
72
|
+
key = build_method_key key, method.to_s
|
73
|
+
build_args_key key, *args
|
74
|
+
end
|
75
|
+
|
76
|
+
# Pass in the class or object that receives the memoized
|
77
|
+
# methods, and a hash containing the methods as keys and
|
78
|
+
# an array of their arguments in order.
|
79
|
+
def self.build_keys_for_class(klass, methods_n_args)
|
80
|
+
classkey = build_class_key klass
|
81
|
+
methods_n_args.map do |method, args|
|
82
|
+
key = build_method_key(classkey, method.to_s)
|
83
|
+
build_args_key key, *args
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
if ACTIVE
|
88
|
+
Entry = Struct.new(:obj, :expire)
|
89
|
+
private_constant :Entry
|
90
|
+
|
91
|
+
def self.fetch(key)
|
92
|
+
@memoizer_stats_count = @memoizer_stats_count + 1
|
93
|
+
|
94
|
+
cached = @memoizer_cache[key]
|
95
|
+
|
96
|
+
now = Time.now.getutc.to_i
|
97
|
+
purge(now) if now > @memoizer_purge_time
|
98
|
+
|
99
|
+
if cached && now <= cached.expire
|
100
|
+
@memoizer_stats_hits = @memoizer_stats_hits + 1
|
101
|
+
cached
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
def self.memoize(key, obj)
|
106
|
+
@memoizer_cache[key] = Entry.new(obj, Time.now.getutc.to_i + EXPIRE)
|
107
|
+
obj
|
108
|
+
end
|
109
|
+
else
|
110
|
+
def self.fetch(_key)
|
111
|
+
nil
|
112
|
+
end
|
113
|
+
|
114
|
+
def self.memoize(_key, obj)
|
115
|
+
obj
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
def self.get(key)
|
120
|
+
entry = @memoizer_cache[key]
|
121
|
+
entry.obj if entry
|
122
|
+
end
|
123
|
+
|
124
|
+
def self.memoized?(key)
|
125
|
+
!!(fetch key)
|
126
|
+
end
|
127
|
+
|
128
|
+
def self.clear(keys)
|
129
|
+
Array(keys).each do |key|
|
130
|
+
@memoizer_cache.delete key
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
def self.purge(time)
|
135
|
+
@memoizer_purge_time = time + PURGE
|
136
|
+
|
137
|
+
@memoizer_cache.delete_if do |_, entry|
|
138
|
+
time > entry.expire
|
139
|
+
end
|
140
|
+
|
141
|
+
##safety, should never reach this unless massive concurrency
|
142
|
+
reset! if @memoizer_cache.size > MAX_ENTRIES
|
143
|
+
end
|
144
|
+
|
145
|
+
def self.stats
|
146
|
+
{
|
147
|
+
size: @memoizer_cache.size,
|
148
|
+
count: @memoizer_stats_count,
|
149
|
+
hits: @memoizer_stats_hits,
|
150
|
+
}
|
151
|
+
end
|
152
|
+
|
153
|
+
def self.memoize_block(key, &block)
|
154
|
+
entry = fetch key
|
155
|
+
if entry.nil?
|
156
|
+
Memoizer.memoize(key, yield)
|
157
|
+
else
|
158
|
+
entry.obj
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
# Decorator allows a class or module to include it and get
|
163
|
+
# memoize :method1, :method2, ...
|
164
|
+
# using keys "#{classname}.#{methodname}-#{arg1}-#{arg2}-..."
|
165
|
+
module Decorator
|
166
|
+
def self.included(base)
|
167
|
+
base.extend(ClassMethods)
|
168
|
+
end
|
169
|
+
|
170
|
+
module ClassMethods
|
171
|
+
module Helpers
|
172
|
+
module_function
|
173
|
+
|
174
|
+
def memoize_instance_method(m, klass)
|
175
|
+
method_name = m.name
|
176
|
+
method_s = method_name.to_s
|
177
|
+
klass.send :define_method, method_name do |*args|
|
178
|
+
key = Memoizer.build_method_key self.to_s, method_s
|
179
|
+
key = Memoizer.build_args_key key, *args
|
180
|
+
Memoizer.memoize_block(key) do
|
181
|
+
m.bind(self).call(*args)
|
182
|
+
end
|
183
|
+
end
|
184
|
+
end
|
185
|
+
private :memoize_instance_method
|
186
|
+
|
187
|
+
def memoize_class_method(m, partialkey, klass)
|
188
|
+
klass.define_singleton_method(m.name) do |*args|
|
189
|
+
key = Memoizer.build_args_key partialkey, *args
|
190
|
+
Memoizer.memoize_block(key) do
|
191
|
+
m.call(*args)
|
192
|
+
end
|
193
|
+
end
|
194
|
+
end
|
195
|
+
private :memoize_class_method
|
196
|
+
|
197
|
+
# helper to go down one level from the current class context
|
198
|
+
# ie. the reverse of singleton_class: from metaclass to class
|
199
|
+
def get_instance_class(klass)
|
200
|
+
return klass unless klass.singleton_class?
|
201
|
+
# workaround Ruby's lack of the inverse of singleton_class
|
202
|
+
base_s = klass.to_s.split(':').delete_if { |k| k.start_with? '#' }.
|
203
|
+
join(':').split('>').first
|
204
|
+
iklass = Kernel.const_get(base_s)
|
205
|
+
# got the root class, now go up a level shy of self
|
206
|
+
iklass = iklass.singleton_class while iklass.singleton_class != klass
|
207
|
+
iklass
|
208
|
+
end
|
209
|
+
private :get_instance_class
|
210
|
+
end
|
211
|
+
|
212
|
+
# memoize :method, :other_method, ...
|
213
|
+
#
|
214
|
+
# Decorate the methods passed in memoizing their results based
|
215
|
+
# on the parameters they receive using a key with the form:
|
216
|
+
# (ClassName|Instance).methodname[-param1[-param2[-...]]]
|
217
|
+
#
|
218
|
+
# You can call this from a class on instance or class methods, and
|
219
|
+
# from a metaclass on instance methods, which actually are class
|
220
|
+
# methods.
|
221
|
+
#
|
222
|
+
# CAVEAT: if you have an instance method named exactly as an existing
|
223
|
+
# class method you either memoize the instance method BEFORE defining
|
224
|
+
# the class method or use memoize_i on the instance method.
|
225
|
+
#
|
226
|
+
# WARNING: do NOT use this memoize method on frequently called instance
|
227
|
+
# methods since you'll have a noticeable overhead from the necessity
|
228
|
+
# to bind the method to the object.
|
229
|
+
#
|
230
|
+
def memoize(*methods)
|
231
|
+
classkey = Memoizer.build_class_key self
|
232
|
+
# get the base class of self so that we get rid of metaclasses
|
233
|
+
klass = Helpers.get_instance_class self
|
234
|
+
# make sure klass points to the klass that self is a metaclass of
|
235
|
+
# in case we're being invoked from a metaclass
|
236
|
+
methods.each do |m|
|
237
|
+
# For each method, first search for a class method, which is the
|
238
|
+
# common case. If not found, then look for an instance method.
|
239
|
+
#
|
240
|
+
begin
|
241
|
+
key = Memoizer.build_method_key(classkey, m.to_s)
|
242
|
+
original_method = klass.method m
|
243
|
+
raise NameError unless original_method.owner == klass.singleton_class
|
244
|
+
Helpers.memoize_class_method original_method, key, klass
|
245
|
+
rescue NameError
|
246
|
+
# If we cannot find a class method, try an instance method
|
247
|
+
# before bailing out.
|
248
|
+
memoize_i m
|
249
|
+
end
|
250
|
+
end
|
251
|
+
end
|
252
|
+
|
253
|
+
# memoize_i :method, :other_method
|
254
|
+
#
|
255
|
+
# Forces memoization of methods which are instance methods in
|
256
|
+
# the current context level. This can be used to, for example,
|
257
|
+
# override the default look up when we have two methods, class
|
258
|
+
# and instance, which are named the same and we want to memoize
|
259
|
+
# both or only the instance level one.
|
260
|
+
def memoize_i(*methods)
|
261
|
+
methods.each do |m|
|
262
|
+
# We don't support calling this from a metaclass, because
|
263
|
+
# we have not built a correct key. The user should use memoize
|
264
|
+
# instead of this, which will already work with the instance
|
265
|
+
# methods within a metaclass, that is, the class' class methods.
|
266
|
+
raise NameError if singleton_class?
|
267
|
+
original_method = instance_method m
|
268
|
+
raise NameError unless original_method.owner == self
|
269
|
+
Helpers.memoize_instance_method original_method, self
|
270
|
+
end
|
271
|
+
end
|
272
|
+
end
|
273
|
+
|
274
|
+
end
|
275
|
+
end
|
276
|
+
end
|
277
|
+
end
|
@@ -0,0 +1,275 @@
|
|
1
|
+
require 'set'
|
2
|
+
|
3
|
+
module ThreeScale
|
4
|
+
module Backend
|
5
|
+
class Metric
|
6
|
+
module KeyHelpers
|
7
|
+
def key(service_id, id, attribute)
|
8
|
+
encode_key("metric/service_id:#{service_id}/id:#{id}/#{attribute}")
|
9
|
+
end
|
10
|
+
|
11
|
+
def id_key(service_id, name)
|
12
|
+
encode_key("metric/service_id:#{service_id}/name:#{name}/id")
|
13
|
+
end
|
14
|
+
|
15
|
+
def id_set_key(service_id)
|
16
|
+
encode_key("metrics/service_id:#{service_id}/ids")
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
include KeyHelpers
|
21
|
+
extend KeyHelpers
|
22
|
+
|
23
|
+
include Storable
|
24
|
+
|
25
|
+
attr_accessor :service_id, :id, :parent_id, :name
|
26
|
+
attr_writer :children
|
27
|
+
|
28
|
+
def save
|
29
|
+
old_name = self.class.load_name(service_id, id)
|
30
|
+
storage.pipelined do
|
31
|
+
save_attributes
|
32
|
+
save_to_list
|
33
|
+
remove_reverse_mapping(service_id, old_name) if old_name != name
|
34
|
+
end
|
35
|
+
|
36
|
+
# can't include this in the pipeline since it is a potentially
|
37
|
+
# large number of commands.
|
38
|
+
save_children
|
39
|
+
|
40
|
+
self.class.clear_cache(service_id, id, name)
|
41
|
+
end
|
42
|
+
|
43
|
+
def children
|
44
|
+
@children ||= []
|
45
|
+
end
|
46
|
+
|
47
|
+
def to_hash
|
48
|
+
{
|
49
|
+
service_id: service_id,
|
50
|
+
id: id,
|
51
|
+
parent_id: parent_id,
|
52
|
+
name: name
|
53
|
+
}
|
54
|
+
end
|
55
|
+
|
56
|
+
def update(attributes)
|
57
|
+
attributes.each do |attr, val|
|
58
|
+
public_send("#{attr}=", val)
|
59
|
+
end
|
60
|
+
self
|
61
|
+
end
|
62
|
+
|
63
|
+
class << self
|
64
|
+
include Memoizer::Decorator
|
65
|
+
|
66
|
+
def attribute_names
|
67
|
+
%i[service_id id parent_id name children].freeze
|
68
|
+
end
|
69
|
+
|
70
|
+
def load(service_id, id)
|
71
|
+
name, parent_id = storage.mget(key(service_id, id, :name),
|
72
|
+
key(service_id, id, :parent_id))
|
73
|
+
|
74
|
+
name && new(id: id.to_s,
|
75
|
+
service_id: service_id.to_s,
|
76
|
+
name: name,
|
77
|
+
parent_id: parent_id)
|
78
|
+
end
|
79
|
+
|
80
|
+
def load_all(service_id)
|
81
|
+
Collection.new(service_id)
|
82
|
+
end
|
83
|
+
memoize :load_all
|
84
|
+
|
85
|
+
def load_id(service_id, name)
|
86
|
+
storage.get(id_key(service_id, name))
|
87
|
+
end
|
88
|
+
memoize :load_id
|
89
|
+
|
90
|
+
def load_all_ids(service_id)
|
91
|
+
# smembers is guaranteed to return an array of strings, even if empty
|
92
|
+
storage.smembers(id_set_key(service_id))
|
93
|
+
end
|
94
|
+
memoize :load_all_ids
|
95
|
+
|
96
|
+
def load_name(service_id, id)
|
97
|
+
storage.get(key(service_id, id, :name))
|
98
|
+
end
|
99
|
+
memoize :load_name
|
100
|
+
|
101
|
+
def load_all_names(service_id, ids)
|
102
|
+
if ids.nil? || ids.empty?
|
103
|
+
{}
|
104
|
+
else
|
105
|
+
name_keys = ids.map { |id| key(service_id, id, :name) }
|
106
|
+
Hash[ids.zip(storage.mget(name_keys))]
|
107
|
+
end
|
108
|
+
end
|
109
|
+
memoize :load_all_names
|
110
|
+
|
111
|
+
def load_parent_id(service_id, id)
|
112
|
+
storage.get(key(service_id, id, :parent_id))
|
113
|
+
end
|
114
|
+
memoize :load_parent_id
|
115
|
+
|
116
|
+
def save(attributes)
|
117
|
+
metrics = new(attributes)
|
118
|
+
metrics.save
|
119
|
+
metrics
|
120
|
+
end
|
121
|
+
|
122
|
+
# Returns a hash where the keys can only be parent metric ids (as
|
123
|
+
# Strings) and their values are arrays of children.
|
124
|
+
#
|
125
|
+
# The as_names optional parameter (default: true) returns metric names
|
126
|
+
# instead of metric ids when true.
|
127
|
+
def hierarchy(service_id, as_names = true)
|
128
|
+
h_ids = hierarchy_ids(service_id)
|
129
|
+
return h_ids unless as_names
|
130
|
+
|
131
|
+
metric_ids = Set.new(h_ids.keys + h_ids.values.flatten)
|
132
|
+
return {} if metric_ids.empty?
|
133
|
+
|
134
|
+
res = {}
|
135
|
+
metric_names = load_all_names(service_id, metric_ids)
|
136
|
+
|
137
|
+
h_ids.each do |m_id, c_ids|
|
138
|
+
m_name = metric_names[m_id]
|
139
|
+
res[m_name] = c_ids.map do |c_id|
|
140
|
+
metric_names[c_id]
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
res
|
145
|
+
end
|
146
|
+
memoize :hierarchy
|
147
|
+
|
148
|
+
def children(service_id, id)
|
149
|
+
hierarchy(service_id, false)[id.to_s]
|
150
|
+
end
|
151
|
+
|
152
|
+
# Returns the "descendants" of a metric, that is, its children,
|
153
|
+
# grandchildren, etc. in the metric hierarchy of the given service.
|
154
|
+
# In other words, the "descendants" of a metric are its children plus
|
155
|
+
# the descendants of each of them.
|
156
|
+
def descendants(service_id, metric_name)
|
157
|
+
metrics_hierarchy = hierarchy(service_id)
|
158
|
+
children = metrics_hierarchy[metric_name] || []
|
159
|
+
|
160
|
+
children.reduce(children) do |acc, child|
|
161
|
+
acc + descendants(service_id, child)
|
162
|
+
end
|
163
|
+
end
|
164
|
+
memoize :descendants
|
165
|
+
|
166
|
+
# Returns the "ascendants" of a metric, that is, its parent,
|
167
|
+
# grandparent, etc. The "ascendants" of a metric are its parent plus
|
168
|
+
# the ascendants of the parent.
|
169
|
+
def ascendants(service_id, metric_name)
|
170
|
+
parents_of_metric = parents(service_id, [metric_name])
|
171
|
+
|
172
|
+
parents_of_metric.reduce(parents_of_metric) do |acc, parent|
|
173
|
+
acc + ascendants(service_id, parent)
|
174
|
+
end
|
175
|
+
end
|
176
|
+
memoize :ascendants
|
177
|
+
|
178
|
+
# Given an array of metrics, returns an array without duplicates that
|
179
|
+
# includes the names of the metrics that are parent of at least one of
|
180
|
+
# the given metrics.
|
181
|
+
def parents(service_id, metric_names)
|
182
|
+
parents = []
|
183
|
+
|
184
|
+
metric_names.each do |name|
|
185
|
+
parent_id = load_parent_id service_id, load_id(service_id, name)
|
186
|
+
if parent_id
|
187
|
+
parents << load_name(service_id, parent_id)
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
parents.uniq
|
192
|
+
end
|
193
|
+
|
194
|
+
def delete(service_id, id)
|
195
|
+
name = load_name(service_id, id)
|
196
|
+
return false unless name and not name.empty?
|
197
|
+
clear_cache(service_id, id, name)
|
198
|
+
|
199
|
+
storage.pipelined do
|
200
|
+
storage.srem(id_set_key(service_id), id)
|
201
|
+
|
202
|
+
storage.del(key(service_id, id, :name),
|
203
|
+
key(service_id, id, :parent_id),
|
204
|
+
id_key(service_id, name))
|
205
|
+
end
|
206
|
+
|
207
|
+
true
|
208
|
+
end
|
209
|
+
|
210
|
+
def clear_cache(service_id, id, name)
|
211
|
+
metric_ids = load_all_ids(service_id)
|
212
|
+
Memoizer.clear(Memoizer.build_keys_for_class(self,
|
213
|
+
load_all: [service_id],
|
214
|
+
load_all_names: [service_id, metric_ids],
|
215
|
+
load_name: [service_id, id],
|
216
|
+
load_id: [service_id, name],
|
217
|
+
load_all_ids: [service_id]))
|
218
|
+
end
|
219
|
+
|
220
|
+
private
|
221
|
+
|
222
|
+
def hierarchy_ids(service_id)
|
223
|
+
ids = load_all_ids(service_id)
|
224
|
+
parent_ids_keys = ids.map { |id| key(service_id, id, :parent_id) }
|
225
|
+
|
226
|
+
parent_ids = storage.pipelined do
|
227
|
+
parent_ids_keys.each_slice(PIPELINED_SLICE_SIZE).map do |slice|
|
228
|
+
storage.mget(slice)
|
229
|
+
end
|
230
|
+
end.flatten
|
231
|
+
|
232
|
+
parent_child_rels = parent_ids.zip(ids)
|
233
|
+
|
234
|
+
parent_child_rels.inject({}) do |acc, (parent_id, child_id)|
|
235
|
+
if parent_id # nil if child_id has no parent
|
236
|
+
acc[parent_id] ||= []
|
237
|
+
acc[parent_id] << child_id
|
238
|
+
end
|
239
|
+
acc
|
240
|
+
end
|
241
|
+
end
|
242
|
+
end
|
243
|
+
|
244
|
+
private
|
245
|
+
|
246
|
+
def remove_reverse_mapping(service_id, name)
|
247
|
+
storage.del id_key(service_id, name)
|
248
|
+
end
|
249
|
+
|
250
|
+
def save_attributes
|
251
|
+
storage.set(id_key(service_id, name), id)
|
252
|
+
storage.set(key(service_id, id, :name), name)
|
253
|
+
storage.set(key(service_id, id, :parent_id), parent_id) if parent_id
|
254
|
+
end
|
255
|
+
|
256
|
+
def save_to_list
|
257
|
+
storage.sadd(id_set_key(service_id), id)
|
258
|
+
end
|
259
|
+
|
260
|
+
def save_children
|
261
|
+
children.each do |child|
|
262
|
+
child.service_id = service_id
|
263
|
+
child.parent_id = id
|
264
|
+
child.save
|
265
|
+
end
|
266
|
+
end
|
267
|
+
|
268
|
+
end
|
269
|
+
end
|
270
|
+
end
|
271
|
+
|
272
|
+
# this is required by our code, but it is nested inside class Metric
|
273
|
+
# require'ing it here ensures we always reopen the class instead of
|
274
|
+
# defining it.
|
275
|
+
require '3scale/backend/metric/collection'
|