apisonator 3.0.1.1 → 3.1.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: bd4ac963d174350e0e93513a0bfe572110c9e90659f7907277996358c9761902
4
- data.tar.gz: 3c327a805079b402cc65330cb4ff3e84a247ef9c57c781a9e4e103be283cc8a5
3
+ metadata.gz: edd1887f6aa71c17fb5279f98988c9a3b5e631e80bc1f4142b6619b5f5e24f48
4
+ data.tar.gz: b6478d14e88a177480396a7d46d5f4e33cea683aea17303175ad18ae646e546f
5
5
  SHA512:
6
- metadata.gz: 52f2e4795a62a1ed314b21d1b3839dc78c2e6f8ee5bcb6fce02d9b5cd1ced3004915a78927b9fd667d210a2bb547fe34f27844dd267cdb405ff36eb629d7e1c8
7
- data.tar.gz: df81a8aceb61a99c1151f3418c156ce70e1c18a9e2ab28db80d9f35424bf73fe82cfe46bed02c6da5e229cb212344432dd603520cd1dea8940e4448c14e6c3e8
6
+ metadata.gz: 944b75385817abc4d2f8828c61b2301d5cdfbb6c1bb681b7a3165590f23ad1b6c80451fcf579b8f3cf0010fe5a95e65605998f5d8ec94f1ff4ce690556b3011a
7
+ data.tar.gz: f3f44b1b2ebfe6c8fcbbf36b90dd44e5a7875d997a36b97288417472fa4156689508bfc95afdf196a7a168f768aa6e38521502b93078f128994959d184c1cd37
@@ -2,6 +2,26 @@
2
2
 
3
3
  Notable changes to Apisonator will be tracked in this document.
4
4
 
5
+ ## 3.1.0 - 2020-10-14
6
+
7
+ ### Added
8
+
9
+ - Prometheus metrics for the internal API
10
+ ([#236](https://github.com/3scale/apisonator/pull/236)).
11
+ - Docs with a detailed explanation about how counter updates are performed
12
+ ([#239](https://github.com/3scale/apisonator/pull/239)).
13
+
14
+ ### Changed
15
+
16
+ - NotifyJobs are run only when the service ID is explicitly defined
17
+ ([#238](https://github.com/3scale/apisonator/pull/238)).
18
+
19
+ ### Fixed
20
+
21
+ - Fixed corner case that raised "TransactionTimestampNotWithinRange" in notify
22
+ jobs ([#235](https://github.com/3scale/apisonator/pull/235)).
23
+
24
+
5
25
  ## 3.0.1.1 - 2020-07-28
6
26
 
7
27
  ### Changed
@@ -35,7 +35,7 @@ GIT
35
35
  PATH
36
36
  remote: .
37
37
  specs:
38
- apisonator (3.0.1.1)
38
+ apisonator (3.1.0)
39
39
 
40
40
  GEM
41
41
  remote: https://rubygems.org/
@@ -35,7 +35,7 @@ GIT
35
35
  PATH
36
36
  remote: .
37
37
  specs:
38
- apisonator (3.0.1.1)
38
+ apisonator (3.1.0)
39
39
 
40
40
  GEM
41
41
  remote: https://rubygems.org/
@@ -1,6 +1,7 @@
1
1
  require '3scale/backend/configuration/loader'
2
2
  require '3scale/backend/environment'
3
3
  require '3scale/backend/configurable'
4
+ require '3scale/backend/errors'
4
5
 
5
6
  module ThreeScale
6
7
  module Backend
@@ -77,9 +78,6 @@ module ThreeScale
77
78
  master_metrics = [:transactions, :transactions_authorize]
78
79
  config.master.metrics = Struct.new(*master_metrics).new
79
80
 
80
- # Default config
81
- config.master_service_id = 1
82
-
83
81
  # This setting controls whether the listener can create event buckets in
84
82
  # Redis. We do not want all the listeners creating buckets yet, as we do
85
83
  # not know exactly the rate at which we can send events to Kinesis
@@ -4,14 +4,34 @@ require 'rack'
4
4
  module ThreeScale
5
5
  module Backend
6
6
  class ListenerMetrics
7
- REQUEST_TYPES = {
7
+ AUTH_AND_REPORT_REQUEST_TYPES = {
8
8
  '/transactions/authorize.xml' => 'authorize',
9
9
  '/transactions/oauth_authorize.xml' => 'authorize_oauth',
10
10
  '/transactions/authrep.xml' => 'authrep',
11
11
  '/transactions/oauth_authrep.xml' => 'authrep_oauth',
12
12
  '/transactions.xml' => 'report'
13
13
  }
14
- private_constant :REQUEST_TYPES
14
+ private_constant :AUTH_AND_REPORT_REQUEST_TYPES
15
+
16
+ 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/, 'applications'],
21
+ [/\/services\/.*\/errors/, 'errors'],
22
+ [/\/events/, 'events'],
23
+ [/\/services\/.*\/metrics/, 'metrics'],
24
+ [/\/service_tokens/, 'service_tokens'],
25
+ [/\/services/, 'services'],
26
+ [/\/services\/.*\/stats/, 'stats'],
27
+ [/\/services\/.*\/plans\/.*\/usagelimits/, 'usage_limits'],
28
+ [/\/services\/.*\/applications\/.*\/utilization/, 'utilization'],
29
+ ].freeze
30
+ private_constant :INTERNAL_API_PATHS
31
+
32
+ # Most requests will be under 100ms, so use a higher granularity from there
33
+ TIME_BUCKETS = [0.01, 0.02, 0.03, 0.04, 0.05, 0.06, 0.07, 0.08, 0.09, 0.1, 0.25, 0.5, 0.75, 1]
34
+ private_constant :TIME_BUCKETS
15
35
 
16
36
  class << self
17
37
  ERRORS_4XX_TO_TRACK = Set[403, 404, 409].freeze
@@ -27,9 +47,12 @@ module ThreeScale
27
47
  end
28
48
 
29
49
  def report_resp_code(path, resp_code)
30
- Yabeda.apisonator_listener.response_codes.increment(
50
+ req_type = req_type(path)
51
+ prometheus_group = prometheus_group(req_type)
52
+
53
+ Yabeda.send(prometheus_group).response_codes.increment(
31
54
  {
32
- request_type: REQUEST_TYPES[path],
55
+ request_type: req_type,
33
56
  resp_code: code_group(resp_code)
34
57
  },
35
58
  by: 1
@@ -37,8 +60,11 @@ module ThreeScale
37
60
  end
38
61
 
39
62
  def report_response_time(path, request_time)
40
- Yabeda.apisonator_listener.response_times.measure(
41
- { request_type: REQUEST_TYPES[path] },
63
+ req_type = req_type(path)
64
+ prometheus_group = prometheus_group(req_type)
65
+
66
+ Yabeda.send(prometheus_group).response_times.measure(
67
+ { request_type: req_type },
42
68
  request_time
43
69
  )
44
70
  end
@@ -69,8 +95,21 @@ module ThreeScale
69
95
  comment 'Response times'
70
96
  unit :seconds
71
97
  tags %i[request_type]
72
- # Most requests will be under 100ms, so use a higher granularity from there
73
- buckets [0.01, 0.02, 0.03, 0.04, 0.05, 0.06, 0.07, 0.08, 0.09, 0.1, 0.25, 0.5, 0.75, 1]
98
+ buckets TIME_BUCKETS
99
+ end
100
+ end
101
+
102
+ group :apisonator_listener_internal_api do
103
+ counter :response_codes do
104
+ comment 'Response codes'
105
+ tags %i[request_type resp_code]
106
+ end
107
+
108
+ histogram :response_times do
109
+ comment 'Response times'
110
+ unit :seconds
111
+ tags %i[request_type]
112
+ buckets TIME_BUCKETS
74
113
  end
75
114
  end
76
115
  end
@@ -93,6 +132,24 @@ module ThreeScale
93
132
  'unknown'.freeze
94
133
  end
95
134
  end
135
+
136
+ def req_type(path)
137
+ AUTH_AND_REPORT_REQUEST_TYPES[path] || internal_api_req_type(path)
138
+ end
139
+
140
+ def internal_api_req_type(path)
141
+ (_regex, type) = INTERNAL_API_PATHS.find { |(regex, _)| regex.match path }
142
+ type
143
+ end
144
+
145
+ # Returns the group as defined in .define_metrics
146
+ def prometheus_group(request_type)
147
+ if AUTH_AND_REPORT_REQUEST_TYPES.values.include? request_type
148
+ :apisonator_listener
149
+ else
150
+ :apisonator_listener_internal_api
151
+ end
152
+ end
96
153
  end
97
154
  end
98
155
  end
@@ -30,9 +30,13 @@ module ThreeScale
30
30
  end
31
31
 
32
32
  def notify(provider_key, usage)
33
- # batch several notifications together so that we can process just one
33
+ # We need the master service ID to report its metrics. If it's not
34
+ # set, we don't need to notify anything.
35
+ # Batch several notifications together so that we can process just one
34
36
  # job for a group of them.
35
- notify_batch(provider_key, usage)
37
+ unless configuration.master_service_id.to_s.empty?
38
+ notify_batch(provider_key, usage)
39
+ end
36
40
  end
37
41
 
38
42
  def notify_batch(provider_key, usage)
@@ -7,8 +7,6 @@ module ThreeScale
7
7
  extend Configurable
8
8
  @queue = :main
9
9
 
10
- InvalidMasterServiceId = Class.new(ThreeScale::Backend::Error)
11
-
12
10
  class << self
13
11
  def perform_logged(provider_key, usage, timestamp, _enqueue_time)
14
12
  application_id = Application.load_id_by_key(master_service_id, provider_key)
@@ -16,12 +14,42 @@ module ThreeScale
16
14
  if application_id && Application.exists?(master_service_id, application_id)
17
15
  master_metrics = Metric.load_all(master_service_id)
18
16
 
19
- ProcessJob.perform([{
20
- service_id: master_service_id,
21
- application_id: application_id,
22
- timestamp: timestamp,
23
- usage: master_metrics.process_usage(usage)
24
- }])
17
+ begin
18
+ ProcessJob.perform([{
19
+ service_id: master_service_id,
20
+ application_id: application_id,
21
+ timestamp: timestamp,
22
+ usage: master_metrics.process_usage(usage)
23
+ }])
24
+ rescue MetricInvalid => e
25
+ # This happens when the master account in Porta does not have
26
+ # the notify metrics defined (by default "transactions" and
27
+ # "transactions/authorize"). These metrics need to be created in
28
+ # Porta, Apisonator does not have a way to guarantee that
29
+ # they're defined.
30
+ # Notice that this rescue prevents the job from being retried.
31
+ # Apisonator can't know when the metrics will be created (if
32
+ # ever) so it's better to log the error rather than retrying
33
+ # these jobs for an undefined period of time.
34
+ Worker.logger.notify(e)
35
+ return [false, "#{e}"]
36
+ rescue TransactionTimestampNotWithinRange => e
37
+ # This is very unlikely to happen. The timestamps in a notify
38
+ # job are not set by users, they are set by the listeners. If
39
+ # this error happens it might mean that:
40
+ # a) The worker started processing this job way after the
41
+ # listener produced it. This can happen for example if we make
42
+ # some requests to a listener with no workers. The listeners
43
+ # will enqueue some notify jobs. If we start a worker hours
44
+ # later, we might see this error.
45
+ # b) There's some kind of clock skew issue.
46
+ # c) There's a bug.
47
+ #
48
+ # We can't raise here, because then, the job will be retried,
49
+ # but it's going to fail always if it has an old timestamp.
50
+ Worker.logger.notify(e)
51
+ return [false, "#{provider_key} #{application_id} #{e}"]
52
+ end
25
53
  end
26
54
  [true, "#{provider_key} #{application_id || '--'}"]
27
55
  end
@@ -29,15 +57,7 @@ module ThreeScale
29
57
  private
30
58
 
31
59
  def master_service_id
32
- value = configuration.master_service_id
33
-
34
- unless value
35
- raise InvalidMasterServiceId,
36
- "Can't find master service id. Make sure the \"master_service_id\" "\
37
- 'configuration value is set correctly'
38
- end
39
-
40
- value.to_s
60
+ configuration.master_service_id.to_s
41
61
  end
42
62
  end
43
63
  end
@@ -1,5 +1,5 @@
1
1
  module ThreeScale
2
2
  module Backend
3
- VERSION = '3.0.1.1'
3
+ VERSION = '3.1.0'
4
4
  end
5
5
  end
@@ -23,7 +23,7 @@
23
23
  </dependency>
24
24
  <dependency>
25
25
  <packageName>apisonator</packageName>
26
- <version>3.0.1.1</version>
26
+ <version>3.1.0</version>
27
27
  <licenses>
28
28
  <license>
29
29
  <name>Apache 2.0</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.0.1.1
4
+ version: 3.1.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: 2020-07-28 00:00:00.000000000 Z
19
+ date: 2020-10-14 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.