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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: cdc3f7ce1f123bfc666e600050ee264cd4398e4e607f4f2dc1b91b008b7d6f3d
4
- data.tar.gz: 5f8b87e92a43267aa10158cd27a7e3e931bb4a51a6ef301d8a9af9d0ddcbabd5
3
+ metadata.gz: 141d6f1891d8a67b232ac835b120c03178439b3d39c9fe3695ac40176f4538d2
4
+ data.tar.gz: ff73c4b952045eeffa6a78e20c55e9c657a5c38c44a72707a9d20eee0645c8ba
5
5
  SHA512:
6
- metadata.gz: b2bc7b89dde896156164abbac527ccdc242d635ccfa78190afc48a30f07b8b2fb89c5660fffe6ea35d1accfe132fa23c50550f633777d6879d8d077f2554e985
7
- data.tar.gz: 7c2fdf0a5c4db2c3d7b141279cb330c6cc612c55932fa54e628d9da919a2de8174c115eec67886fe40ce3cf6aac8feedca5870a0c767a84459e42d99b64fe8b7
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://engineering.shopify.com/blogs/engineering/circuit-breaker-misconfigured).
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://engineering.shopify.com/blogs/engineering/building-and-testing-resilient-ruby-on-rails-applications
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
- acquire_semaphore_without_gvl(void *p)
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.freeze
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.freeze
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) # rubocop:disable Layout/LineLength
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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Semian
4
- VERSION = "0.22.1"
4
+ VERSION = "0.23.0-rc2"
5
5
  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.22.1
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: 2025-02-10 00:00:00.000000000 Z
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.7.0
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.3
80
+ rubygems_version: 3.6.8
81
81
  specification_version: 4
82
82
  summary: Bulkheading for Ruby with SysV semaphores
83
83
  test_files: []