semian 0.22.1 → 0.23.0.pre.rc2
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/README.md +29 -2
- data/ext/semian/sysv_semaphores.c +32 -14
- data/lib/semian/activerecord_trilogy_adapter.rb +1 -1
- data/lib/semian/circuit_breaker.rb +0 -1
- data/lib/semian/mysql2.rb +1 -1
- data/lib/semian/redis.rb +1 -1
- data/lib/semian/version.rb +1 -1
- metadata +4 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 141d6f1891d8a67b232ac835b120c03178439b3d39c9fe3695ac40176f4538d2
|
4
|
+
data.tar.gz: ff73c4b952045eeffa6a78e20c55e9c657a5c38c44a72707a9d20eee0645c8ba
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ed1af89aa82e0491264b9367f2dd9fb9bf661b08b454878e7b21cb86724dec98bfed01a53a766a159e0b0f349cb7fcc45a2af6f25720a90a3aae10d2ad3eefaa
|
7
|
+
data.tar.gz: 5097dcc59c75ae818cbcfdf2c99739e1534ca2239de84eeabaafab94d18e0a57a520f5e1266a584d9de679cb3de6ee8b3205f5bb19b9137f9afa6ac257141fa3
|
data/README.md
CHANGED
@@ -534,7 +534,7 @@ It is possible to disable Circuit Breaker with environment variable
|
|
534
534
|
`SEMIAN_CIRCUIT_BREAKER_DISABLED=1`.
|
535
535
|
|
536
536
|
For more information about configuring these parameters, please read
|
537
|
-
[this post](https://
|
537
|
+
[this post](https://shopify.engineering/circuit-breaker-misconfigured).
|
538
538
|
|
539
539
|
### Bulkheading
|
540
540
|
|
@@ -576,6 +576,33 @@ We call this limitation primitive "tickets". In this case, the resource access
|
|
576
576
|
is limited to 5 tickets (see Configuration). The timeout value specifies the
|
577
577
|
maximum amount of time to block if no ticket is available.
|
578
578
|
|
579
|
+
```mermaid
|
580
|
+
|
581
|
+
graph TD;
|
582
|
+
Start[Start]
|
583
|
+
CheckConnection{Request Connection to Resource}
|
584
|
+
AllocateTicket[Allocate Ticket if Available]
|
585
|
+
BlockTimeout[Block until Timeout or Ticket Available]
|
586
|
+
AccessResource[Access Resource]
|
587
|
+
ReleaseTicket[Release Ticket]
|
588
|
+
FailRequest[Fail Request]
|
589
|
+
OpenCircuit[Open Circuit Breaker]
|
590
|
+
|
591
|
+
Start --> CheckConnection
|
592
|
+
CheckConnection -->|Ticket Available| AllocateTicket
|
593
|
+
AllocateTicket --> AccessResource
|
594
|
+
AccessResource --> ReleaseTicket
|
595
|
+
ReleaseTicket --> CheckConnection
|
596
|
+
|
597
|
+
CheckConnection -->|No Ticket Available| BlockTimeout
|
598
|
+
BlockTimeout -->|Timeout| FailRequest
|
599
|
+
BlockTimeout -->|Ticket Available| AccessResource
|
600
|
+
|
601
|
+
FailRequest --> OpenCircuit
|
602
|
+
OpenCircuit --> CheckConnection
|
603
|
+
|
604
|
+
```
|
605
|
+
|
579
606
|
How do we limit the access to a resource for all workers on a server when the
|
580
607
|
workers do not directly share memory? This is implemented with [SysV
|
581
608
|
semaphores][sysv] to provide server-wide access control.
|
@@ -924,7 +951,7 @@ $ bundle install
|
|
924
951
|
[nethttp-default-errors]: lib/semian/net_http.rb#L35-L45
|
925
952
|
[semian-instrumentable]: lib/semian/instrumentable.rb
|
926
953
|
[statsd-instrument]: http://github.com/shopify/statsd-instrument
|
927
|
-
[resiliency-blog-post]: https://
|
954
|
+
[resiliency-blog-post]: https://shopify.engineering/building-and-testing-resilient-ruby-on-rails-applications
|
928
955
|
[toxiproxy]: https://github.com/Shopify/toxiproxy
|
929
956
|
[sysv]: http://man7.org/linux/man-pages/man7/svipc.7.html
|
930
957
|
[cbp]: https://en.wikipedia.org/wiki/Circuit_breaker_design_pattern
|
@@ -1,12 +1,10 @@
|
|
1
1
|
#include "sysv_semaphores.h"
|
2
2
|
#include <time.h>
|
3
|
+
#include <stdbool.h>
|
3
4
|
|
4
5
|
static key_t
|
5
6
|
generate_key(const char *name);
|
6
7
|
|
7
|
-
static void *
|
8
|
-
acquire_semaphore(void *p);
|
9
|
-
|
10
8
|
static int
|
11
9
|
wait_for_new_semaphore_set(key_t key, long permissions);
|
12
10
|
|
@@ -108,6 +106,9 @@ perform_semop(int sem_id, short index, short op, short flags, struct timespec *t
|
|
108
106
|
buf.sem_flg = flags;
|
109
107
|
|
110
108
|
int num_retries = 3;
|
109
|
+
if (flags & IPC_NOWAIT) {
|
110
|
+
num_retries = 0;
|
111
|
+
}
|
111
112
|
|
112
113
|
do {
|
113
114
|
result = semtimedop(sem_id, &buf, 1, ts);
|
@@ -151,17 +152,9 @@ get_semaphore(int key)
|
|
151
152
|
return semget(key, SI_NUM_SEMAPHORES, 0);
|
152
153
|
}
|
153
154
|
|
154
|
-
void
|
155
|
-
|
156
|
-
{
|
157
|
-
WITHOUT_GVL(acquire_semaphore, p, RUBY_UBF_IO, NULL);
|
158
|
-
return NULL;
|
159
|
-
}
|
160
|
-
|
161
|
-
static void *
|
162
|
-
acquire_semaphore(void *p)
|
155
|
+
static void
|
156
|
+
acquire_semaphore_internal(semian_resource_t *res, short flags)
|
163
157
|
{
|
164
|
-
semian_resource_t *res = (semian_resource_t *) p;
|
165
158
|
res->error = 0;
|
166
159
|
res->wait_time = -1;
|
167
160
|
#ifdef DEBUG
|
@@ -170,7 +163,7 @@ acquire_semaphore(void *p)
|
|
170
163
|
|
171
164
|
struct timespec begin, end;
|
172
165
|
int benchmark_result = clock_gettime(CLOCK_MONOTONIC, &begin);
|
173
|
-
if (perform_semop(res->sem_id, SI_SEM_TICKETS, -1, SEM_UNDO, &res->timeout) == -1) {
|
166
|
+
if (perform_semop(res->sem_id, SI_SEM_TICKETS, -1, SEM_UNDO | flags, &res->timeout) == -1) {
|
174
167
|
res->error = errno;
|
175
168
|
}
|
176
169
|
if (benchmark_result == 0) {
|
@@ -178,6 +171,31 @@ acquire_semaphore(void *p)
|
|
178
171
|
res->wait_time = diff_timespec_ms(&end, &begin);
|
179
172
|
}
|
180
173
|
}
|
174
|
+
}
|
175
|
+
|
176
|
+
static void *
|
177
|
+
acquire_semaphore(void *p)
|
178
|
+
{
|
179
|
+
semian_resource_t *res = (semian_resource_t *) p;
|
180
|
+
acquire_semaphore_internal(res, 0);
|
181
|
+
return NULL;
|
182
|
+
}
|
183
|
+
|
184
|
+
// Eagerly attempt to acquire the semaphore
|
185
|
+
static bool
|
186
|
+
try_acquire_semaphore(void *p)
|
187
|
+
{
|
188
|
+
semian_resource_t *res = (semian_resource_t *) p;
|
189
|
+
acquire_semaphore_internal(res, IPC_NOWAIT);
|
190
|
+
return !res->error;
|
191
|
+
}
|
192
|
+
|
193
|
+
void *
|
194
|
+
acquire_semaphore_without_gvl(void *p)
|
195
|
+
{
|
196
|
+
if (!try_acquire_semaphore(p)) {
|
197
|
+
WITHOUT_GVL(acquire_semaphore, p, RUBY_UBF_IO, NULL);
|
198
|
+
}
|
181
199
|
return NULL;
|
182
200
|
}
|
183
201
|
|
@@ -29,7 +29,7 @@ module Semian
|
|
29
29
|
ResourceBusyError = ::ActiveRecord::ConnectionAdapters::TrilogyAdapter::ResourceBusyError
|
30
30
|
CircuitOpenError = ::ActiveRecord::ConnectionAdapters::TrilogyAdapter::CircuitOpenError
|
31
31
|
|
32
|
-
QUERY_ALLOWLIST = %r{\A(?:/\*.*?\*/)?\s*(ROLLBACK|COMMIT|RELEASE\s+SAVEPOINT)}i
|
32
|
+
QUERY_ALLOWLIST = %r{\A(?:/\*.*?\*/)?\s*(ROLLBACK|COMMIT|RELEASE\s+SAVEPOINT)}i
|
33
33
|
|
34
34
|
# The common case here is NOT to have transaction management statements, therefore
|
35
35
|
# we are exploiting the fact that Active Record will use COMMIT/ROLLBACK as
|
@@ -18,7 +18,6 @@ module Semian
|
|
18
18
|
def initialize(name, exceptions:, success_threshold:, error_threshold:,
|
19
19
|
error_timeout:, implementation:, half_open_resource_timeout: nil,
|
20
20
|
error_threshold_timeout: nil, error_threshold_timeout_enabled: true)
|
21
|
-
|
22
21
|
@name = name.to_sym
|
23
22
|
@success_count_threshold = success_threshold
|
24
23
|
@error_count_threshold = error_threshold
|
data/lib/semian/mysql2.rb
CHANGED
@@ -39,7 +39,7 @@ module Semian
|
|
39
39
|
DEFAULT_HOST = "localhost"
|
40
40
|
DEFAULT_PORT = 3306
|
41
41
|
|
42
|
-
QUERY_ALLOWLIST = %r{\A(?:/\*.*?\*/)?\s*(ROLLBACK|COMMIT|RELEASE\s+SAVEPOINT)}i
|
42
|
+
QUERY_ALLOWLIST = %r{\A(?:/\*.*?\*/)?\s*(ROLLBACK|COMMIT|RELEASE\s+SAVEPOINT)}i
|
43
43
|
|
44
44
|
class << self
|
45
45
|
# The naked methods are exposed as `raw_query` and `raw_connect` for instrumentation purpose
|
data/lib/semian/redis.rb
CHANGED
@@ -163,7 +163,7 @@ module Semian
|
|
163
163
|
end
|
164
164
|
|
165
165
|
def dns_resolve_failure?(e)
|
166
|
-
e.to_s.match?(/(can't resolve)|(name or service not known)|(nodename nor servname provided, or not known)|(failure in name resolution)/i)
|
166
|
+
e.to_s.match?(/(can't resolve)|(name or service not known)|(nodename nor servname provided, or not known)|(failure in name resolution)/i)
|
167
167
|
end
|
168
168
|
end
|
169
169
|
end
|
data/lib/semian/version.rb
CHANGED
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
|
+
version: 0.23.0.pre.rc2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Scott Francis
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
- Dale Hamel
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
12
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
13
13
|
dependencies: []
|
14
14
|
description: |2
|
15
15
|
A Ruby C extention that is used to control access to shared resources
|
@@ -70,14 +70,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
70
70
|
requirements:
|
71
71
|
- - ">="
|
72
72
|
- !ruby/object:Gem::Version
|
73
|
-
version: 2.
|
73
|
+
version: 3.2.0
|
74
74
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
75
75
|
requirements:
|
76
76
|
- - ">="
|
77
77
|
- !ruby/object:Gem::Version
|
78
78
|
version: '0'
|
79
79
|
requirements: []
|
80
|
-
rubygems_version: 3.6.
|
80
|
+
rubygems_version: 3.6.8
|
81
81
|
specification_version: 4
|
82
82
|
summary: Bulkheading for Ruby with SysV semaphores
|
83
83
|
test_files: []
|