local_bus 0.1.1 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: fa03fc7b7c1f5e14bff852435b75e343f150ceee9b5f9638ce365918bc39c939
4
- data.tar.gz: 1aa8d5ffa9895245ff78f1b00c2d4f3697a1183afe82dc4bcd3d7c420453e924
3
+ metadata.gz: f435ed1460835bdd4b50311c366c158f4dda0381038368cd325a13c8ceace3b9
4
+ data.tar.gz: 937b58aa97ed2e9ca2f65eb02b6c401f9e93c4d590aff5966816dd6dd47be8a9
5
5
  SHA512:
6
- metadata.gz: 6e17c54a44652eac0c7edb7715a1db68507a0b82b4c3acdd9bfbc4e41d5741b5b80965d623be8e369c4080e43a0cb8696431d6464588655ecc863c47d8bf81e6
7
- data.tar.gz: 57fbbe8719669a3dbee59252c2d71ce20c1ce232110779ffa9bf8fa4c68e7da1b59cb9fb916d308c7e8d21e499117eb74b4a5e464db2c0eab2866e53855d9c12
6
+ metadata.gz: 74828920b50a51ae3fc00583dee61657d88cadd62482172fc34c1e54e935a652306716242c9152d6a5f35e9660f6d52930c6840966dc281f158c391666588cf7
7
+ data.tar.gz: d7c86027a16144325f3ea4cd3e65f41ea55a56ac6b75e309a75a5397cd7042372002783948f64b6e76d3cdbb0200a1cd0b62be3ec8c632b3639c5c33b0a42a76
data/README.md CHANGED
@@ -30,22 +30,23 @@ LocalBus is a lightweight pub/sub system for Ruby that helps organize and simpli
30
30
 
31
31
  ## Table of Contents
32
32
 
33
- - [Why LocalBus?](#why-localbus)
34
- - [Installation](#installation)
35
- - [Quick Start](#quick-start)
36
- - [Interfaces](#interfaces)
37
- - [Bus (immediate processing)](#bus-immediate-processing)
38
- - [Station (background processing)](#station-background-processing)
39
- - [Advanced Usage & Considerations](#advanced-usage--considerations)
40
- - [Concurrency Controls](#concurrency-controls)
41
- - [Bus Interface (Async)](#bus-interface-async)
42
- - [Station Interface (Thread Pool)](#station-interface-thread-pool)
43
- - [Error Handling & Recovery](#error-handling--recovery)
44
- - [Memory Considerations](#memory-considerations)
45
- - [Blocking Operations](#blocking-operations)
46
- - [Shutdown & Cleanup](#shutdown--cleanup)
47
- - [Limitations](#limitations)
48
- - [Sponsors](#sponsors)
33
+ - [Why LocalBus?](#why-localbus)
34
+ - [Installation](#installation)
35
+ - [Quick Start](#quick-start)
36
+ - [Interfaces](#interfaces)
37
+ - [Bus (immediate processing)](#bus-immediate-processing)
38
+ - [Station (background processing)](#station-background-processing)
39
+ - [Advanced Usage & Considerations](#advanced-usage--considerations)
40
+ - [Concurrency Controls](#concurrency-controls)
41
+ - [Bus Interface (Async)](#bus-interface-async)
42
+ - [Station Interface (Thread Pool)](#station-interface-thread-pool)
43
+ - [Error Handling & Recovery](#error-handling--recovery)
44
+ - [Memory Considerations](#memory-considerations)
45
+ - [Blocking Operations](#blocking-operations)
46
+ - [Shutdown & Cleanup](#shutdown--cleanup)
47
+ - [Limitations](#limitations)
48
+ - [See Also](#see-also)
49
+ - [Sponsors](#sponsors)
49
50
 
50
51
  <!-- Tocer[finish]: Auto-generated, don't remove. -->
51
52
 
@@ -184,7 +185,7 @@ The Bus interface uses Async's Semaphore to limit resource consumption:
184
185
 
185
186
  ```ruby
186
187
  # Configure concurrency limits for the Bus
187
- bus = LocalBus::Bus.new(concurrency_limit: 10)
188
+ bus = LocalBus::Bus.new(max_concurrency: 10)
188
189
 
189
190
  # The semaphore ensures only N concurrent operations run at once
190
191
  bus.subscribe "resource.intensive" do |message|
@@ -193,7 +194,7 @@ bus.subscribe "resource.intensive" do |message|
193
194
  end
194
195
  ```
195
196
 
196
- When the concurrency limit is reached, new publish operations will wait until a slot becomes available. This prevents memory bloat but means you should be mindful of timeouts in your subscribers.
197
+ When the max concurrency limit is reached, new publish operations will wait until a slot becomes available. This prevents memory bloat but means you should be mindful of timeouts in your subscribers.
197
198
 
198
199
  #### Station Interface (Thread Pool)
199
200
 
@@ -203,7 +204,7 @@ The Station interface uses Concurrent Ruby's fixed thread pool with a fallback p
203
204
  # Configure the thread pool size for the Station
204
205
  station = LocalBus::Station.new(
205
206
  max_queue: 5_000, # Maximum number of queued items
206
- threads: 10, # Maximum pool size
207
+ max_threads: 10, # Maximum pool size
207
208
  fallback_policy: :caller_runs # Runs on calling thread
208
209
  )
209
210
  ```
@@ -275,7 +276,7 @@ For example, idempotency _(i.e. messages that can be re-published without uninte
275
276
 
276
277
  - The Bus interface is single-threaded - long-running subscribers can impact latency
277
278
  - The Station interface may drop messages if configured with `:discard` fallback policy
278
- - No persistence - pending messages are lost on process restart
279
+ - No persistence - pending messages may be lost on process restart
279
280
  - No distributed support - communication limited to single process
280
281
  - Large payloads can impact memory usage, especially under high load
281
282
  - No built-in retry mechanism for failed subscribers
data/lib/local_bus/bus.rb CHANGED
@@ -8,27 +8,27 @@ class LocalBus
8
8
  include MonitorMixin
9
9
 
10
10
  # Constructor
11
- # @note Creates a new Bus instance with specified concurrency
12
- # @rbs concurrency: Integer -- maximum number of concurrent tasks (default: Concurrent.processor_count)
13
- def initialize(concurrency: Concurrent.processor_count)
11
+ # @note Creates a new Bus instance with specified max concurrency (i.e. number of tasks that can run in parallel)
12
+ # @rbs max_concurrency: Integer -- maximum number of concurrent tasks (default: Concurrent.processor_count)
13
+ def initialize(max_concurrency: Concurrent.processor_count)
14
14
  super()
15
- @concurrency = concurrency.to_i
15
+ @max_concurrency = max_concurrency.to_i
16
16
  @subscriptions = Concurrent::Hash.new do |hash, key|
17
17
  hash[key] = Concurrent::Set.new
18
18
  end
19
19
  end
20
20
 
21
21
  # Maximum number of concurrent tasks that can run in "parallel"
22
- # @rbs return: Integer -- current concurrency value
23
- def concurrency
24
- synchronize { @concurrency }
22
+ # @rbs return: Integer
23
+ def max_concurrency
24
+ synchronize { @max_concurrency }
25
25
  end
26
26
 
27
- # Sets the concurrency
28
- # @rbs concurrency: Integer -- max number of concurrent tasks that can run in "parallel"
27
+ # Sets the max concurrency
28
+ # @rbs value: Integer -- max number of concurrent tasks that can run in "parallel"
29
29
  # @rbs return: Integer -- new concurrency value
30
- def concurrency=(value)
31
- synchronize { @concurrency = value.to_i }
30
+ def max_concurrency=(value)
31
+ synchronize { @max_concurrency = value.to_i }
32
32
  end
33
33
 
34
34
  # Registered topics that have subscribers
@@ -112,7 +112,7 @@ class LocalBus
112
112
  if subscribers.any?
113
113
  Sync do |task|
114
114
  task.with_timeout timeout.to_f do
115
- semaphore = Async::Semaphore.new(concurrency, parent: barrier)
115
+ semaphore = Async::Semaphore.new(max_concurrency, parent: barrier)
116
116
 
117
117
  subscribers.each do |subscriber|
118
118
  semaphore.async do
@@ -33,21 +33,21 @@ class LocalBus
33
33
 
34
34
  # Constructor
35
35
  # @rbs bus: Bus -- local message bus (default: Bus.new)
36
- # @rbs threads: Integer -- number of threads (default: Concurrent.processor_count)
36
+ # @rbs max_threads: Integer -- number of max_threads (default: Concurrent.processor_count)
37
37
  # @rbs default_timeout: Float -- seconds to wait for a future to complete
38
38
  # @rbs shutdown_timeout: Float -- seconds to wait for all futures to complete on process exit
39
39
  # @rbs options: Hash[Symbol, untyped] -- Concurrent::FixedThreadPool options
40
40
  # @rbs return: void
41
41
  def initialize(
42
42
  bus: Bus.new,
43
- threads: Concurrent.processor_count,
43
+ max_threads: Concurrent.processor_count,
44
44
  default_timeout: 0,
45
45
  shutdown_timeout: 8,
46
46
  **options
47
47
  )
48
48
  super()
49
49
  @bus = bus
50
- @threads = [2, threads].max.to_i
50
+ @max_threads = [2, max_threads].max.to_i
51
51
  @default_timeout = default_timeout.to_f
52
52
  @shutdown_timeout = shutdown_timeout.to_f
53
53
  @shutdown = Concurrent::AtomicBoolean.new(false)
@@ -60,7 +60,7 @@ class LocalBus
60
60
 
61
61
  # Number of threads used to process messages
62
62
  # @rbs return: Integer
63
- attr_reader :threads
63
+ attr_reader :max_threads
64
64
 
65
65
  # Default timeout for message processing (in seconds)
66
66
  # @rbs return: Float
@@ -78,7 +78,7 @@ class LocalBus
78
78
  return if running?
79
79
 
80
80
  start_shutdown_handler
81
- @pool = Concurrent::FixedThreadPool.new(threads, THREAD_POOL_OPTIONS.merge(options))
81
+ @pool = Concurrent::FixedThreadPool.new(max_threads, THREAD_POOL_OPTIONS.merge(options))
82
82
  enable_safe_shutdown on: ["HUP", "INT", "QUIT", "TERM"]
83
83
  end
84
84
  end
@@ -99,6 +99,8 @@ class LocalBus
99
99
 
100
100
  @pool = nil
101
101
  end
102
+ rescue
103
+ nil # ignore errors during shutdown
102
104
  end
103
105
 
104
106
  # Clean up shutdown handler
@@ -213,9 +215,9 @@ class LocalBus
213
215
  # @rbs on: Array[String] -- signals to trap
214
216
  # @rbs return: void
215
217
  def enable_safe_shutdown(on:)
218
+ at_exit { stop }
216
219
  on.each do |signal|
217
220
  trap signal do
218
- # Only queue the signal if we haven't started shutdown
219
221
  @shutdown_queue.push signal unless @shutdown.true?
220
222
  rescue
221
223
  nil
@@ -3,5 +3,5 @@
3
3
  # rbs_inline: enabled
4
4
 
5
5
  class LocalBus
6
- VERSION = "0.1.1"
6
+ VERSION = "0.2.0"
7
7
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: local_bus
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nate Hopkins (hopsoft)
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-11-05 00:00:00.000000000 Z
11
+ date: 2024-11-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: async