semian 0.4.0 → 0.4.1
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 +3 -0
- data/README.md +10 -4
- data/ext/semian/semian.c +4 -0
- data/lib/semian/net_http.rb +16 -8
- data/lib/semian/unprotected_resource.rb +1 -1
- data/lib/semian/version.rb +1 -1
- data/semian.gemspec +1 -0
- data/test/net_http_test.rb +35 -1
- data/test/resource_test.rb +6 -0
- metadata +16 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6d26f3db6e9a869dda06d6c9de1b6d450ce50659
|
4
|
+
data.tar.gz: 101fb40d19d110414db888954715e8dd2dfd9f22
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c3ffb155d0b7e026944a0eee7dfa47983c42a88ae9dc0781729a27f7f8b34933f3fd257470ef30ee385c07c3746d7da1ac75170955cc7edfdf42a43e7e0d848d
|
7
|
+
data.tar.gz: 3264158b8d95db9d254d22093856eaeeaf9388941fc5f235606c038c548b8605cc24661f52a545c730745952c171f3770c30f4f7f41db101fb892d24f8a1d9c6
|
data/CHANGELOG.md
CHANGED
data/README.md
CHANGED
@@ -130,12 +130,13 @@ client = Redis.new(semian: {
|
|
130
130
|
#### Net::HTTP
|
131
131
|
For the `Net::HTTP` specific Semian adapter, since many external libraries may create
|
132
132
|
HTTP connections on the user's behalf, the parameters are instead provided
|
133
|
-
by
|
133
|
+
by associating callback functions with `Semian::NetHTTP`, perhaps in an initialization file.
|
134
134
|
|
135
135
|
##### Naming and Options
|
136
136
|
To give Semian parameters, assign a `proc` to `Semian::NetHTTP.semian_configuration`
|
137
|
-
that takes a two parameters, `host` and `port` like `127.0.0.1`
|
138
|
-
and returns a `Hash` with
|
137
|
+
that takes a two parameters, `host` and `port` like `127.0.0.1`,`443` or `github_com`,`80`,
|
138
|
+
and returns a `Hash` with configuration parameters as follows. The `proc` is used as a
|
139
|
+
callback to initialize the configuration options, similar to other adapters.
|
139
140
|
|
140
141
|
```ruby
|
141
142
|
SEMIAN_PARAMETERS = { tickets: 1,
|
@@ -161,6 +162,11 @@ The `semian_options` passed apply to that resource. Semian creates the `semian_i
|
|
161
162
|
from the `name` to look up and store changes in the circuit breaker and bulkhead states
|
162
163
|
and associate successes, failures, errors with the protected resource.
|
163
164
|
|
165
|
+
We only require that:
|
166
|
+
* the `semian_configuration` be **set only once** over the lifetime of the library
|
167
|
+
* the output of the `proc` be the same over time, that is, the configuration produced by
|
168
|
+
each pair of `host`, `port` is **the same each time** the callback is invoked.
|
169
|
+
|
164
170
|
For most purposes, `"#{host}_#{port}"` is a good default `name`. Custom `name` formats
|
165
171
|
can be useful to grouping related subdomains as one resource, so that they all
|
166
172
|
contribute to the same circuit breaker and bulkhead state and fail together.
|
@@ -560,7 +566,7 @@ non-IO.
|
|
560
566
|
[redis-semian-adapter]: lib/semian/redis.rb
|
561
567
|
[semian-adapter]: lib/semian/adapter.rb
|
562
568
|
[nethttp-semian-adapter]: lib/semian/net_http.rb
|
563
|
-
[nethttp-default-errors]: lib/semian/net_http.rb#
|
569
|
+
[nethttp-default-errors]: lib/semian/net_http.rb#L35-L45
|
564
570
|
[semian-instrumentable]: lib/semian/instrumentable.rb
|
565
571
|
[statsd-instrument]: http://github.com/shopify/statsd-instrument
|
566
572
|
[resiliency-blog-post]: http://www.shopify.com/technology/16906928-building-and-testing-resilient-ruby-on-rails-applications
|
data/ext/semian/semian.c
CHANGED
@@ -283,6 +283,10 @@ semian_resource_initialize(VALUE self, VALUE id, VALUE tickets, VALUE permission
|
|
283
283
|
if (TYPE(id) != T_SYMBOL && TYPE(id) != T_STRING) {
|
284
284
|
rb_raise(rb_eTypeError, "id must be a symbol or string");
|
285
285
|
}
|
286
|
+
if (TYPE(tickets) == T_FLOAT) {
|
287
|
+
rb_warn("semian ticket value %f is a float, converting to fixnum", RFLOAT_VALUE(tickets));
|
288
|
+
tickets = INT2FIX((int) RFLOAT_VALUE(tickets));
|
289
|
+
}
|
286
290
|
Check_Type(tickets, T_FIXNUM);
|
287
291
|
Check_Type(permissions, T_FIXNUM);
|
288
292
|
if (TYPE(default_timeout) != T_FIXNUM && TYPE(default_timeout) != T_FLOAT) {
|
data/lib/semian/net_http.rb
CHANGED
@@ -22,12 +22,14 @@ module Semian
|
|
22
22
|
ResourceBusyError = ::Net::ResourceBusyError
|
23
23
|
CircuitOpenError = ::Net::CircuitOpenError
|
24
24
|
|
25
|
-
|
26
|
-
|
25
|
+
class SemianConfigurationChangedError < RuntimeError
|
26
|
+
def initialize(msg = "Cannot re-initialize semian_configuration")
|
27
|
+
super
|
28
|
+
end
|
27
29
|
end
|
28
30
|
|
29
31
|
def semian_identifier
|
30
|
-
"nethttp_#{
|
32
|
+
"nethttp_#{raw_semian_options[:name]}"
|
31
33
|
end
|
32
34
|
|
33
35
|
DEFAULT_ERRORS = [
|
@@ -52,8 +54,13 @@ module Semian
|
|
52
54
|
end
|
53
55
|
|
54
56
|
class << self
|
55
|
-
attr_accessor :semian_configuration
|
56
57
|
attr_accessor :exceptions
|
58
|
+
attr_reader :semian_configuration
|
59
|
+
|
60
|
+
def semian_configuration=(configuration)
|
61
|
+
raise Semian::NetHTTP::SemianConfigurationChangedError unless @semian_configuration.nil?
|
62
|
+
@semian_configuration = configuration
|
63
|
+
end
|
57
64
|
|
58
65
|
def retrieve_semian_configuration(host, port)
|
59
66
|
@semian_configuration.call(host, port) if @semian_configuration.respond_to?(:call)
|
@@ -67,9 +74,10 @@ module Semian
|
|
67
74
|
Semian::NetHTTP.reset_exceptions
|
68
75
|
|
69
76
|
def raw_semian_options
|
70
|
-
|
71
|
-
|
72
|
-
|
77
|
+
@raw_semian_options ||= begin
|
78
|
+
@raw_semian_options = Semian::NetHTTP.retrieve_semian_configuration(address, port)
|
79
|
+
@raw_semian_options = @raw_semian_options.dup unless @raw_semian_options.nil?
|
80
|
+
end
|
73
81
|
end
|
74
82
|
|
75
83
|
def resource_exceptions
|
@@ -77,7 +85,7 @@ module Semian
|
|
77
85
|
end
|
78
86
|
|
79
87
|
def disabled?
|
80
|
-
|
88
|
+
raw_semian_options.nil?
|
81
89
|
end
|
82
90
|
|
83
91
|
def connect
|
@@ -1,6 +1,6 @@
|
|
1
1
|
module Semian
|
2
2
|
# This class acts as a replacement for `ProtectedResource` when
|
3
|
-
# the semian configuration of an `
|
3
|
+
# the semian configuration of an `Adapter` is missing or explicitly disabled
|
4
4
|
class UnprotectedResource
|
5
5
|
attr_reader :name
|
6
6
|
|
data/lib/semian/version.rb
CHANGED
data/semian.gemspec
CHANGED
@@ -19,6 +19,7 @@ Gem::Specification.new do |s|
|
|
19
19
|
s.files = `git ls-files`.split("\n")
|
20
20
|
s.extensions = ['ext/semian/extconf.rb']
|
21
21
|
s.add_development_dependency 'rake-compiler', '~> 0.9'
|
22
|
+
s.add_development_dependency 'rake', '< 11.0'
|
22
23
|
s.add_development_dependency 'timecop'
|
23
24
|
s.add_development_dependency 'mysql2'
|
24
25
|
s.add_development_dependency 'redis'
|
data/test/net_http_test.rb
CHANGED
@@ -21,6 +21,7 @@ class TestNetHTTP < MiniTest::Unit::TestCase
|
|
21
21
|
error_timeout: 10,
|
22
22
|
}.freeze
|
23
23
|
DEFAULT_SEMIAN_CONFIGURATION = proc do |host, port|
|
24
|
+
next nil if host == "127.0.0.1" && port == 8474 # disable if toxiproxy
|
24
25
|
DEFAULT_SEMIAN_OPTIONS.merge(name: "#{host}_#{port}")
|
25
26
|
end
|
26
27
|
|
@@ -191,6 +192,17 @@ class TestNetHTTP < MiniTest::Unit::TestCase
|
|
191
192
|
end
|
192
193
|
end
|
193
194
|
|
195
|
+
def test_custom_raw_semian_options_can_only_assign_once
|
196
|
+
semian_configuration_proc = proc do |host, port|
|
197
|
+
DEFAULT_SEMIAN_OPTIONS.merge(name: "#{host}_#{port}")
|
198
|
+
end
|
199
|
+
with_semian_configuration(semian_configuration_proc) do
|
200
|
+
assert_raises(Semian::NetHTTP::SemianConfigurationChangedError) do
|
201
|
+
Semian::NetHTTP.semian_configuration = semian_configuration_proc
|
202
|
+
end
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
194
206
|
def test_custom_raw_semian_options_work_with_default_fallback
|
195
207
|
with_server do
|
196
208
|
semian_config = {}
|
@@ -230,7 +242,8 @@ class TestNetHTTP < MiniTest::Unit::TestCase
|
|
230
242
|
semian_config["development"]["nethttp_default"] = DEFAULT_SEMIAN_OPTIONS
|
231
243
|
sample_env = "development"
|
232
244
|
|
233
|
-
semian_configuration_proc = proc do
|
245
|
+
semian_configuration_proc = proc do |host, port|
|
246
|
+
next nil if host == "127.0.0.1" && port == 8474 # # disable if toxiproxy
|
234
247
|
semian_identifier = "nethttp_default"
|
235
248
|
semian_config[sample_env][semian_identifier].merge(name: "default")
|
236
249
|
end
|
@@ -344,10 +357,31 @@ class TestNetHTTP < MiniTest::Unit::TestCase
|
|
344
357
|
|
345
358
|
def with_semian_configuration(options = DEFAULT_SEMIAN_CONFIGURATION)
|
346
359
|
orig_semian_options = Semian::NetHTTP.semian_configuration
|
360
|
+
Semian::NetHTTP.instance_variable_set(:@semian_configuration, nil)
|
361
|
+
mutated_objects = {}
|
362
|
+
Semian::NetHTTP.send(:alias_method, :orig_semian_resource, :semian_resource)
|
363
|
+
Semian::NetHTTP.send(:alias_method, :orig_raw_semian_options, :raw_semian_options)
|
364
|
+
Semian::NetHTTP.send(:define_method, :semian_resource) do
|
365
|
+
mutated_objects[self] = [@semian_resource, @raw_semian_options] unless mutated_objects.key?(self)
|
366
|
+
orig_semian_resource
|
367
|
+
end
|
368
|
+
Semian::NetHTTP.send(:define_method, :raw_semian_options) do
|
369
|
+
mutated_objects[self] = [@semian_resource, @raw_semian_options] unless mutated_objects.key?(self)
|
370
|
+
orig_raw_semian_options
|
371
|
+
end
|
372
|
+
|
347
373
|
Semian::NetHTTP.semian_configuration = options
|
348
374
|
yield
|
349
375
|
ensure
|
376
|
+
Semian::NetHTTP.instance_variable_set(:@semian_configuration, nil)
|
350
377
|
Semian::NetHTTP.semian_configuration = orig_semian_options
|
378
|
+
Semian::NetHTTP.send(:alias_method, :semian_resource, :orig_semian_resource)
|
379
|
+
Semian::NetHTTP.send(:alias_method, :raw_semian_options, :orig_raw_semian_options)
|
380
|
+
Semian::NetHTTP.send(:undef_method, :orig_semian_resource, :orig_raw_semian_options)
|
381
|
+
mutated_objects.each do |instance, (res, opt)| # Sadly, only way to fully restore cached properties
|
382
|
+
instance.instance_variable_set(:@semian_resource, res)
|
383
|
+
instance.instance_variable_set(:@raw_semian_options, opt)
|
384
|
+
end
|
351
385
|
end
|
352
386
|
|
353
387
|
def with_custom_errors(errors)
|
data/test/resource_test.rb
CHANGED
@@ -26,6 +26,12 @@ class TestResource < MiniTest::Unit::TestCase
|
|
26
26
|
end
|
27
27
|
end
|
28
28
|
|
29
|
+
def test_initialize_with_float
|
30
|
+
resource = create_resource :testing, tickets: 1.0
|
31
|
+
assert resource
|
32
|
+
assert_equal 1, resource.tickets
|
33
|
+
end
|
34
|
+
|
29
35
|
def test_max_tickets
|
30
36
|
assert Semian::MAX_TICKETS > 0
|
31
37
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: semian
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.4.
|
4
|
+
version: 0.4.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Scott Francis
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
12
|
+
date: 2016-03-10 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rake-compiler
|
@@ -25,6 +25,20 @@ dependencies:
|
|
25
25
|
- - "~>"
|
26
26
|
- !ruby/object:Gem::Version
|
27
27
|
version: '0.9'
|
28
|
+
- !ruby/object:Gem::Dependency
|
29
|
+
name: rake
|
30
|
+
requirement: !ruby/object:Gem::Requirement
|
31
|
+
requirements:
|
32
|
+
- - "<"
|
33
|
+
- !ruby/object:Gem::Version
|
34
|
+
version: '11.0'
|
35
|
+
type: :development
|
36
|
+
prerelease: false
|
37
|
+
version_requirements: !ruby/object:Gem::Requirement
|
38
|
+
requirements:
|
39
|
+
- - "<"
|
40
|
+
- !ruby/object:Gem::Version
|
41
|
+
version: '11.0'
|
28
42
|
- !ruby/object:Gem::Dependency
|
29
43
|
name: timecop
|
30
44
|
requirement: !ruby/object:Gem::Requirement
|