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 +4 -4
- data/lib/startback/audit/prometheus.rb +2 -1
- data/lib/startback/caching/entity_cache.rb +53 -8
- data/lib/startback/caching/listeners.rb +41 -0
- data/lib/startback/caching/logger.rb +24 -0
- data/lib/startback/caching/prometheus.rb +53 -0
- data/lib/startback/caching.rb +6 -0
- data/lib/startback/support/robustness.rb +4 -1
- data/lib/startback/version.rb +1 -1
- data/spec/unit/caching/test_entity_cache.rb +54 -6
- metadata +8 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a46ae64f74d522af60d8efac5d0ea966587c556a12d791a4817c77a27b4eb74b
|
4
|
+
data.tar.gz: 528fd26764c05ae46e88c4009dcfe801ad8c8fa95b57eb3ae158ae205b26be49
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
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
|
-
#
|
26
|
-
#
|
27
|
-
#
|
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
|
-
|
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
|
-
|
116
|
+
@options[:listeners].cache_hit(self, context, pkey, cached)
|
82
117
|
end
|
83
118
|
|
84
119
|
def cache_outdated(pkey, cached)
|
85
|
-
|
120
|
+
@options[:listeners].cache_outdated(self, context, pkey, cached)
|
86
121
|
end
|
87
122
|
|
88
123
|
def cache_miss(pkey)
|
89
|
-
|
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
|
@@ -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: {
|
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
|
data/lib/startback/version.rb
CHANGED
@@ -1,14 +1,13 @@
|
|
1
1
|
require 'spec_helper'
|
2
|
-
require 'startback/caching
|
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(
|
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.
|
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-
|
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.
|
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.
|
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
|