startback 0.19.1 → 0.19.3

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5dbbbb6cabdc48303170d9c6b200d3edf37b3dfbb550da83e6fd9c92fe92e22c
4
- data.tar.gz: dfa9d185162331efaca0cb40daa2d5e9b90eaefd5cfeb46bf746138ef4929146
3
+ metadata.gz: a46ae64f74d522af60d8efac5d0ea966587c556a12d791a4817c77a27b4eb74b
4
+ data.tar.gz: 528fd26764c05ae46e88c4009dcfe801ad8c8fa95b57eb3ae158ae205b26be49
5
5
  SHA512:
6
- metadata.gz: 727a5c35b248b995a761d6e4240a5557de08e6ec6bd52936d85ef52d8bb44b131841a7a2996482649b1acf0fba97bda4cffe1914a8d2fe1af4f5809c31b4129f
7
- data.tar.gz: c494d72be435620dc0a6607eb7758cb6ff4f8aa62b397634fb4179040b1a90994bb8c92657ce88ae1977448dcb35c1f3e779ddfecdda435bb36dfdc3394b1675
6
+ metadata.gz: e64ba6da11affe1a7639cd1c5bec8ff9b1088c74eaad3f08f70e7280c95a5af624bcd1bd3d9ca965aec844aaaae3957ada94bcb4ac55ebd0a133b6f0154a99b4
7
+ data.tar.gz: 405daa018fb688b1f00a2b814881cd7f30e7f43892d91d27ef4fbf0367579c160866674130ee5ac187002f0b36e978c840a9329721f9dd7b36668be1345cfd9e
@@ -7,7 +7,8 @@ module Startback
7
7
  # Prometheus exporter abstraction, that can be registered as an around
8
8
  # hook on OperationRunner and as a prometheus client on Context instances.
9
9
  #
10
- # The exporter uses the ruby client for prometheus to expose metrics regarding Operation runs.
10
+ # The exporter uses the ruby client for prometheus to expose metrics
11
+ # regarding Operation runs.
11
12
  #
12
13
  # The following metrics are exported:
13
14
  #
@@ -1,3 +1,7 @@
1
+ # For backward compatibility, may be removed in next released
2
+ require_relative 'listeners'
3
+ require_relative 'logger'
4
+
1
5
  module Startback
2
6
  module Caching
3
7
  #
@@ -22,12 +26,15 @@ module Startback
22
26
  # An EntityCache takes an actual store at construction. The object must meet the
23
27
  # specification writtern in Store. The 'cache' ruby gem can be used in practice.
24
28
  #
25
- # Cache hits, outdated and miss are logged in debug, info, and info severity.
26
- # The `cache_hit`, `cache_outdated`, `cache_miss` protected methods MAY be
27
- # overriden to change that behavior.
29
+ # This class supports listeners to track cache hits, misses, outdates and
30
+ # failures. Listeners can be provided at construction via the options, or by
31
+ # overriding the `default_listeners` method. The default implementation simply
32
+ # logs.
33
+ #
34
+ # By default, this class raises an error if something goes wrong with the cache.
35
+ # You can disable this by using the `raise_on_cache_fail` option.
28
36
  #
29
37
  class EntityCache
30
- include Support::Robustness
31
38
 
32
39
  class << self
33
40
 
@@ -40,9 +47,22 @@ module Startback
40
47
 
41
48
  end # class DSL
42
49
 
43
- def initialize(store, context = nil)
50
+ DEFAULT_OPTIONS = {
51
+
52
+ # Whether a cache fail raises an exception or not
53
+ raise_on_cache_fail: true,
54
+
55
+ # Default listeners to use, if any. When nil is used, `default_listener`
56
+ # method is used to create them.
57
+ listeners: nil,
58
+
59
+ }
60
+
61
+ def initialize(store, context = nil, options = {})
44
62
  @store = store
45
63
  @context = context
64
+ @options = DEFAULT_OPTIONS.merge(options)
65
+ @options[:listeners] ||= default_listeners
46
66
  end
47
67
  attr_reader :store, :context
48
68
 
@@ -66,6 +86,10 @@ module Startback
66
86
  load_entity(pkey).tap{|to_cache|
67
87
  store.set(cache_key, to_cache, caching_options)
68
88
  }
89
+ rescue => ex
90
+ cache_fail(pkey, ex)
91
+ raise if raise_on_cache_fail?
92
+ load_entity(pkey)
69
93
  end
70
94
 
71
95
  # Invalidates the cache under a given key.
@@ -75,18 +99,33 @@ module Startback
75
99
  store.delete(cache_key)
76
100
  end
77
101
 
102
+ protected
103
+
104
+ def raise_on_cache_fail?
105
+ @options[:raise_on_cache_fail]
106
+ end
107
+
108
+ def register(listener)
109
+ @options[:listeners].register(listener)
110
+ self
111
+ end
112
+
78
113
  protected
79
114
 
80
115
  def cache_hit(pkey, cached)
81
- log(:debug, self, "cache_hit", context, op_data: pkey)
116
+ @options[:listeners].cache_hit(self, context, pkey, cached)
82
117
  end
83
118
 
84
119
  def cache_outdated(pkey, cached)
85
- log(:info, self, "cache_outdated", context, op_data: pkey)
120
+ @options[:listeners].cache_outdated(self, context, pkey, cached)
86
121
  end
87
122
 
88
123
  def cache_miss(pkey)
89
- log(:info, self, "cache_miss", context, op_data: pkey)
124
+ @options[:listeners].cache_miss(self, context, pkey)
125
+ end
126
+
127
+ def cache_fail(pkey, ex)
128
+ @options[:listeners].cache_fail(self, context, pkey, ex)
90
129
  end
91
130
 
92
131
  def default_caching_options
@@ -152,6 +191,12 @@ module Startback
152
191
  raise NotImplementedError, "#{self.class.name}#load_entity"
153
192
  end
154
193
 
194
+ protected
195
+
196
+ def default_listeners
197
+ Listeners.new << Logger.new
198
+ end
199
+
155
200
  end # class EntityCache
156
201
  end # module Caching
157
202
  end # module Startback
@@ -0,0 +1,41 @@
1
+ module Startback
2
+ module Caching
3
+ class Listeners
4
+
5
+ def initialize
6
+ @listeners = []
7
+ end
8
+
9
+ def register(listener)
10
+ @listeners << listener
11
+ self
12
+ end
13
+ alias :<< :register
14
+
15
+ def cache_hit(*args, &bl)
16
+ @listeners.each do |l|
17
+ l.cache_hit(*args, &bl)
18
+ end
19
+ end
20
+
21
+ def cache_outdated(*args, &bl)
22
+ @listeners.each do |l|
23
+ l.cache_outdated(*args, &bl)
24
+ end
25
+ end
26
+
27
+ def cache_miss(*args, &bl)
28
+ @listeners.each do |l|
29
+ l.cache_miss(*args, &bl)
30
+ end
31
+ end
32
+
33
+ def cache_fail(*args, &bl)
34
+ @listeners.each do |l|
35
+ l.cache_fail(*args, &bl)
36
+ end
37
+ end
38
+
39
+ end # class Listeners
40
+ end # module Caching
41
+ end # module Startback
@@ -0,0 +1,24 @@
1
+ module Startback
2
+ module Caching
3
+ class Logger
4
+ include Support::Robustness
5
+
6
+ def cache_hit(entity_cache, context, pkey, cached)
7
+ log(:debug, entity_cache, "cache_hit", context, op_data: pkey)
8
+ end
9
+
10
+ def cache_outdated(entity_cache, context, pkey, cached)
11
+ log(:info, entity_cache, "cache_outdated", context, op_data: pkey)
12
+ end
13
+
14
+ def cache_miss(entity_cache, context, pkey)
15
+ log(:info, entity_cache, "cache_miss", context, op_data: pkey)
16
+ end
17
+
18
+ def cache_fail(entity_cache, context, pkey, ex)
19
+ log(:error, entity_cache, "cache_fail", context, op_data: pkey, error: ex)
20
+ end
21
+
22
+ end # class Logger
23
+ end # module Caching
24
+ end # module Startback
@@ -0,0 +1,53 @@
1
+ require 'prometheus/client'
2
+
3
+ module Startback
4
+ module Caching
5
+ class Prometheus
6
+
7
+ def initialize
8
+ @cache_hit_counter = cache_counter('hit')
9
+ @cache_outdated_counter = cache_counter('outdated')
10
+ @cache_miss_counter = cache_counter('miss')
11
+ @cache_fail_counter = cache_counter('fail')
12
+ end
13
+
14
+ def cache_hit(entity_cache, *args, &bl)
15
+ @cache_hit_counter.increment(labels: {
16
+ entity_cache: entity_cache.class.name,
17
+ })
18
+ end
19
+
20
+ def cache_outdated(entity_cache, *args, &bl)
21
+ @cache_outdated_counter.increment(labels: {
22
+ entity_cache: entity_cache.class.name,
23
+ })
24
+ end
25
+
26
+ def cache_miss(entity_cache, *args, &bl)
27
+ @cache_miss_counter.increment(labels: {
28
+ entity_cache: entity_cache.class.name,
29
+ })
30
+ end
31
+
32
+ def cache_fail(entity_cache, *args, &bl)
33
+ @cache_fail_counter.increment(labels: {
34
+ entity_cache: entity_cache.class.name,
35
+ })
36
+ end
37
+
38
+ private
39
+
40
+ def cache_counter(what)
41
+ name = "entity_cache_#{what}".to_sym
42
+ ::Prometheus::Client::Counter.new(
43
+ name,
44
+ docstring: "A counter of EntityCache #{what}",
45
+ labels: [:entity_cache],
46
+ ).tap do |counter|
47
+ ::Prometheus::Client.registry.register(counter)
48
+ end
49
+ end
50
+
51
+ end # class Prometheus
52
+ end # module Caching
53
+ end # module Startback
@@ -0,0 +1,6 @@
1
+ require_relative 'caching/store'
2
+ require_relative 'caching/no_store'
3
+ require_relative 'caching/listeners'
4
+ require_relative 'caching/logger'
5
+ require_relative 'caching/prometheus'
6
+ require_relative 'caching/entity_cache'
@@ -43,7 +43,10 @@ module Startback
43
43
  @@default_logger ||= begin
44
44
  l = ::Logger.new(STDOUT)
45
45
  l.formatter = LogFormatter.new
46
- l.warn(op: "#{self}", op_data: { msg: "Using default logger to STDOUT" })
46
+ l.warn(op: "#{self}", op_data: {
47
+ msg: "Using default logger to STDOUT",
48
+ caller: caller
49
+ })
47
50
  @@default_logger = l
48
51
  end
49
52
  @@default_logger
@@ -2,7 +2,7 @@ module Startback
2
2
  module Version
3
3
  MAJOR = 0
4
4
  MINOR = 19
5
- TINY = 1
5
+ TINY = 3
6
6
  end
7
7
  VERSION = "#{Version::MAJOR}.#{Version::MINOR}.#{Version::TINY}"
8
8
  end
@@ -1,14 +1,13 @@
1
1
  require 'spec_helper'
2
- require 'startback/caching/entity_cache'
3
- require 'startback/caching/store'
2
+ require 'startback/caching'
4
3
  module Startback
5
4
  module Caching
6
5
  describe EntityCache do
7
6
 
8
7
  class BaseCache < EntityCache
9
8
 
10
- def initialize(context = nil)
11
- super(Store.new, context)
9
+ def initialize(options = {})
10
+ super(Store.new, context, options)
12
11
  @called = 0
13
12
  @last_key = nil
14
13
  end
@@ -76,9 +75,9 @@ module Startback
76
75
 
77
76
  describe "get" do
78
77
 
79
- subject{
78
+ subject do
80
79
  cache.get("a key")
81
- }
80
+ end
82
81
 
83
82
  it 'yields to load_raw_data only once with the short key' do
84
83
  expect(subject).to eql("a value")
@@ -87,6 +86,13 @@ module Startback
87
86
  expect(cache.last_key).to eql("a key")
88
87
  end
89
88
 
89
+ it 'raises when an error occurs' do
90
+ expect_any_instance_of(Store).to receive(:exist?).and_raise("Cache failed")
91
+ expect {
92
+ subject
93
+ }.to raise_error(/Cache failed/)
94
+ end
95
+
90
96
  end
91
97
 
92
98
  describe "primary_key" do
@@ -131,6 +137,48 @@ module Startback
131
137
 
132
138
  end
133
139
 
140
+ describe 'when disabling error raising' do
141
+ let(:cache) do
142
+ BaseCache.new(:raise_on_cache_fail => false)
143
+ end
144
+
145
+ subject do
146
+ cache.get("a key")
147
+ end
148
+
149
+ it 'yields to load_raw_data only once with the short key' do
150
+ expect(subject).to eql("a value")
151
+ end
152
+
153
+ it 'does not raise when an error occurs' do
154
+ expect_any_instance_of(Store).to receive(:exist?).and_raise("Cache failed")
155
+ expect_any_instance_of(Caching::Logger).to receive(:cache_fail)
156
+ expect(subject).to eql("a value")
157
+ end
158
+ end
159
+
160
+ describe "with prometheus listener too" do
161
+
162
+ let(:listener) do
163
+ Caching::Prometheus.new
164
+ end
165
+
166
+ let(:cache) do
167
+ BaseCache.new.send(:register, listener)
168
+ end
169
+
170
+ before do
171
+ expect(listener).to receive(:cache_miss)
172
+ expect(listener).to receive(:cache_hit)
173
+ end
174
+
175
+ it 'yields to load_raw_data only once with the extend key' do
176
+ expect(cache.get("a key")).to eql("a value")
177
+ expect(cache.get("a key")).to eql("a value")
178
+ end
179
+
180
+ end
181
+
134
182
  end
135
183
  end
136
184
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: startback
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.19.1
4
+ version: 0.19.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Bernard Lambeau
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-06-01 00:00:00.000000000 Z
11
+ date: 2023-06-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rspec
@@ -150,7 +150,7 @@ dependencies:
150
150
  requirements:
151
151
  - - ">="
152
152
  - !ruby/object:Gem::Version
153
- version: '2.0'
153
+ version: '2.1'
154
154
  - - "<"
155
155
  - !ruby/object:Gem::Version
156
156
  version: '3.0'
@@ -160,7 +160,7 @@ dependencies:
160
160
  requirements:
161
161
  - - ">="
162
162
  - !ruby/object:Gem::Version
163
- version: '2.0'
163
+ version: '2.1'
164
164
  - - "<"
165
165
  - !ruby/object:Gem::Version
166
166
  version: '3.0'
@@ -392,8 +392,12 @@ files:
392
392
  - lib/startback/audit/span.rb
393
393
  - lib/startback/audit/trace_logger.rb
394
394
  - lib/startback/audit/tracer.rb
395
+ - lib/startback/caching.rb
395
396
  - lib/startback/caching/entity_cache.rb
397
+ - lib/startback/caching/listeners.rb
398
+ - lib/startback/caching/logger.rb
396
399
  - lib/startback/caching/no_store.rb
400
+ - lib/startback/caching/prometheus.rb
397
401
  - lib/startback/caching/store.rb
398
402
  - lib/startback/context.rb
399
403
  - lib/startback/context/h_factory.rb