semian 0.22.0 → 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: 27fa3d35b047fbe54483763821e246e2ce6f1403d8b3354d5759c4bdf1b43b2b
4
- data.tar.gz: '06303901cc95e293dee76cd712283a4a7db1536771133e83563c969a6f3598e5'
3
+ metadata.gz: 141d6f1891d8a67b232ac835b120c03178439b3d39c9fe3695ac40176f4538d2
4
+ data.tar.gz: ff73c4b952045eeffa6a78e20c55e9c657a5c38c44a72707a9d20eee0645c8ba
5
5
  SHA512:
6
- metadata.gz: 31e47dcea63ba9fc861272d9b8c2d591eb5f2d0783b1d8cde667812ba1cc4307ea52b533f8dc3542d011fb98c770ed011ff3a84c3e1ee7239635b01c0717b1ad
7
- data.tar.gz: e32d7f17f6d73c95418fe6b80f1dce3423271423d61c8acd1fda356dab02aac41c24637d9a0939d39e5ade7cb015a2e47182361ea5c9187b1b39d93d59058510
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
 
@@ -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/grpc.rb CHANGED
@@ -36,10 +36,11 @@ module Semian
36
36
  end
37
37
 
38
38
  class << self
39
- attr_accessor :exceptions
40
39
  attr_reader :semian_configuration
41
40
 
41
+ # rubocop:disable ThreadSafety/ClassInstanceVariable
42
42
  def semian_configuration=(configuration)
43
+ # Only allow setting the configuration once in boot time
43
44
  raise Semian::GRPC::SemianConfigurationChangedError unless @semian_configuration.nil?
44
45
 
45
46
  @semian_configuration = configuration
@@ -48,6 +49,7 @@ module Semian
48
49
  def retrieve_semian_configuration(host)
49
50
  @semian_configuration.call(host) if @semian_configuration.respond_to?(:call)
50
51
  end
52
+ # rubocop:enable ThreadSafety/ClassInstanceVariable
51
53
  end
52
54
 
53
55
  def raw_semian_options
@@ -55,10 +55,12 @@ module Semian
55
55
  end
56
56
 
57
57
  class << self
58
- attr_accessor :exceptions
58
+ attr_accessor :exceptions # rubocop:disable ThreadSafety/ClassAndModuleAttributes
59
59
  attr_reader :semian_configuration
60
60
 
61
+ # rubocop:disable ThreadSafety/ClassInstanceVariable
61
62
  def semian_configuration=(configuration)
63
+ # Only allow setting the configuration once in boot time
62
64
  raise Semian::NetHTTP::SemianConfigurationChangedError unless @semian_configuration.nil?
63
65
 
64
66
  @semian_configuration = configuration
@@ -67,6 +69,7 @@ module Semian
67
69
  def retrieve_semian_configuration(host, port)
68
70
  @semian_configuration.call(host, port) if @semian_configuration.respond_to?(:call)
69
71
  end
72
+ # rubocop:enable ThreadSafety/ClassInstanceVariable
70
73
 
71
74
  def reset_exceptions
72
75
  self.exceptions = Semian::NetHTTP::DEFAULT_ERRORS.dup
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
@@ -94,7 +94,7 @@ module Semian
94
94
  end
95
95
 
96
96
  module RedisClient
97
- EXCEPTIONS = [::RedisClient::ConnectionError, ::RedisClient::OutOfMemoryError]
97
+ EXCEPTIONS = [::RedisClient::ConnectionError, ::RedisClient::OutOfMemoryError].freeze
98
98
 
99
99
  include Semian::Adapter
100
100
  include RedisClientCommon
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Semian
4
- VERSION = "0.22.0"
4
+ VERSION = "0.23.0-rc2"
5
5
  end
metadata CHANGED
@@ -1,16 +1,15 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: semian
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.22.0
4
+ version: 0.23.0.pre.rc2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Scott Francis
8
8
  - Simon Eskildsen
9
9
  - Dale Hamel
10
- autorequire:
11
10
  bindir: bin
12
11
  cert_chain: []
13
- date: 2024-11-05 00:00:00.000000000 Z
12
+ date: 1980-01-02 00:00:00.000000000 Z
14
13
  dependencies: []
15
14
  description: |2
16
15
  A Ruby C extention that is used to control access to shared resources
@@ -64,7 +63,6 @@ metadata:
64
63
  documentation_uri: https://github.com/Shopify/semian
65
64
  homepage_uri: https://github.com/Shopify/semian
66
65
  source_code_uri: https://github.com/Shopify/semian
67
- post_install_message:
68
66
  rdoc_options: []
69
67
  require_paths:
70
68
  - lib
@@ -72,15 +70,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
72
70
  requirements:
73
71
  - - ">="
74
72
  - !ruby/object:Gem::Version
75
- version: 2.7.0
73
+ version: 3.2.0
76
74
  required_rubygems_version: !ruby/object:Gem::Requirement
77
75
  requirements:
78
76
  - - ">="
79
77
  - !ruby/object:Gem::Version
80
78
  version: '0'
81
79
  requirements: []
82
- rubygems_version: 3.5.22
83
- signing_key:
80
+ rubygems_version: 3.6.8
84
81
  specification_version: 4
85
82
  summary: Bulkheading for Ruby with SysV semaphores
86
83
  test_files: []