semian 0.4.0 → 0.4.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|