solid_objects 0.13.0 → 0.13.2

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.
Files changed (41) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +37 -0
  3. data/README.md +49 -10
  4. data/benchmark/idle_polling.rb +98 -0
  5. data/benchmark/support.rb +2 -0
  6. data/docs/adr/0011-wake-up-strategy.md +6 -0
  7. data/docs/architecture.md +2 -2
  8. data/docs/benchmarks.md +38 -0
  9. data/docs/development.md +1 -0
  10. data/docs/operations.md +32 -0
  11. data/docs/roadmap.md +8 -5
  12. data/lib/solid_objects/actor.rb +54 -4
  13. data/lib/solid_objects/administration.rb +44 -0
  14. data/lib/solid_objects/broadcast_executor.rb +35 -4
  15. data/lib/solid_objects/configuration.rb +4 -0
  16. data/lib/solid_objects/effect_executor.rb +34 -3
  17. data/lib/solid_objects/executor.rb +5 -5
  18. data/lib/solid_objects/polling_backoff.rb +45 -0
  19. data/lib/solid_objects/process_registry.rb +51 -0
  20. data/lib/solid_objects/reminder_scheduler.rb +35 -4
  21. data/lib/solid_objects/version.rb +1 -1
  22. data/lib/solid_objects/wake_up.rb +36 -4
  23. data/lib/solid_objects/wake_up_adapters/postgresql.rb +6 -0
  24. data/lib/solid_objects/wake_up_adapters/redis.rb +25 -3
  25. data/lib/solid_objects/worker.rb +39 -3
  26. data/lib/solid_objects.rb +9 -0
  27. data/sig/generated/lib/solid_objects/actor.rbs +35 -6
  28. data/sig/generated/lib/solid_objects/administration.rbs +13 -0
  29. data/sig/generated/lib/solid_objects/broadcast_executor.rbs +7 -0
  30. data/sig/generated/lib/solid_objects/configuration.rbs +6 -2
  31. data/sig/generated/lib/solid_objects/effect_executor.rbs +7 -0
  32. data/sig/generated/lib/solid_objects/executor.rbs +4 -4
  33. data/sig/generated/lib/solid_objects/polling_backoff.rbs +29 -0
  34. data/sig/generated/lib/solid_objects/process_registry.rbs +15 -0
  35. data/sig/generated/lib/solid_objects/reminder_scheduler.rbs +7 -0
  36. data/sig/generated/lib/solid_objects/wake_up.rbs +19 -2
  37. data/sig/generated/lib/solid_objects/wake_up_adapters/postgresql.rbs +3 -0
  38. data/sig/generated/lib/solid_objects/wake_up_adapters/redis.rbs +17 -2
  39. data/sig/generated/lib/solid_objects/worker.rbs +7 -0
  40. data/sig/generated/lib/solid_objects.rbs +3 -0
  41. metadata +7 -2
@@ -2,6 +2,8 @@
2
2
 
3
3
  module SolidObjects
4
4
  class Configuration
5
+ @table_name_prefix: String
6
+
5
7
  @process_alive_threshold: Float
6
8
 
7
9
  @shutdown_timeout: Float
@@ -56,10 +58,10 @@ module SolidObjects
56
58
 
57
59
  @authorize_administration: Proc
58
60
 
59
- @table_name_prefix: String
60
-
61
61
  @polling_interval: Float
62
62
 
63
+ @idle_polling_interval: Float
64
+
63
65
  @sync_polling_interval: Float
64
66
 
65
67
  @lease_duration: Float
@@ -94,6 +96,8 @@ module SolidObjects
94
96
 
95
97
  attr_accessor polling_interval: untyped
96
98
 
99
+ attr_accessor idle_polling_interval: untyped
100
+
97
101
  attr_accessor sync_polling_interval: untyped
98
102
 
99
103
  attr_accessor lease_duration: untyped
@@ -23,6 +23,8 @@ module SolidObjects
23
23
  class EffectExecutor
24
24
  ACTOR_MESSAGE_EFFECT: ::String
25
25
 
26
+ @polling_backoff: PollingBackoff
27
+
26
28
  @shutdown_requested: bool
27
29
 
28
30
  @stopped: bool
@@ -52,12 +54,17 @@ module SolidObjects
52
54
  # @rbs () -> bool
53
55
  def shutdown_requested?: () -> bool
54
56
 
57
+ # @rbs () -> Float
58
+ def current_polling_interval: () -> Float
59
+
55
60
  private
56
61
 
57
62
  attr_reader process_registry: untyped
58
63
 
59
64
  attr_reader database_adapter: untyped
60
65
 
66
+ attr_reader polling_backoff: untyped
67
+
61
68
  # @rbs () -> Effect?
62
69
  def claim_next: () -> Effect?
63
70
 
@@ -46,10 +46,10 @@ module SolidObjects
46
46
  def enqueue_effects: (message: Message, instance: Instance, intents: Array[Actor::EffectIntent]) -> Array[Effect]
47
47
 
48
48
  # A reminder is one named alarm per actor, so scheduling a name that is
49
- # already armed moves it rather than adding a second. An actor that arms a
50
- # reminder per queued item therefore keeps only the last, and nothing else
51
- # about that is visible: the write succeeds and the earlier wake-up simply
52
- # never happens.
49
+ # already armed moves it rather than adding a second. Without a key that
50
+ # name is the operation, so an actor arming a reminder per queued item keeps
51
+ # only the last; passing schedule a key gives each item its own name and so
52
+ # its own alarm.
53
53
  # Moves are returned rather than reported here, so the report happens after
54
54
  # the turn commits. A rolled back turn would otherwise announce an alarm
55
55
  # that never moved, which is the opposite of the visibility this event
@@ -0,0 +1,29 @@
1
+ # Generated from lib/solid_objects/polling_backoff.rb with RBS::Inline
2
+
3
+ module SolidObjects
4
+ class PollingBackoff
5
+ @minimum_interval: Float
6
+
7
+ @maximum_interval: Float
8
+
9
+ @current_interval: Float
10
+
11
+ @on_change: Proc?
12
+
13
+ attr_reader current_interval: untyped
14
+
15
+ # @rbs (minimum_interval: Numeric, maximum_interval: Numeric, ?on_change: Proc?) -> void
16
+ def initialize: (minimum_interval: Numeric, maximum_interval: Numeric, ?on_change: Proc?) -> void
17
+
18
+ # @rbs () -> void
19
+ def record_idle: () -> void
20
+
21
+ # @rbs (:work | :wake_up) -> void
22
+ def reset: (:work | :wake_up) -> void
23
+
24
+ private
25
+
26
+ # @rbs (Float, :idle | :work | :wake_up) -> void
27
+ def change: (Float, :idle | :work | :wake_up) -> void
28
+ end
29
+ end
@@ -8,6 +8,21 @@ module SolidObjects
8
8
  # @rbs (Process, ?now: Time) -> bool
9
9
  def self.deregister: (Process, ?now: Time) -> bool
10
10
 
11
+ # @rbs () -> void
12
+ def self.warn_if_polling_is_only_cross_process_wake_up: () -> void
13
+
14
+ # @rbs () -> void
15
+ def self.reset_polling_warning!: () -> void
16
+
17
+ # @rbs () -> bool
18
+ private def self.another_live_process?: () -> bool
19
+
20
+ # @rbs () -> bool
21
+ private def self.polling_warning_emitted?: () -> bool
22
+
23
+ # @rbs () -> Mutex
24
+ private def self.polling_warning_mutex: () -> Mutex
25
+
11
26
  # @rbs (Process, Time) -> void
12
27
  private def self.cleanup_process: (Process, Time) -> void
13
28
 
@@ -2,6 +2,8 @@
2
2
 
3
3
  module SolidObjects
4
4
  class ReminderScheduler
5
+ @polling_backoff: PollingBackoff
6
+
5
7
  @shutdown_requested: bool
6
8
 
7
9
  @stopped: bool
@@ -31,12 +33,17 @@ module SolidObjects
31
33
  # @rbs () -> bool
32
34
  def shutdown_requested?: () -> bool
33
35
 
36
+ # @rbs () -> Float
37
+ def current_polling_interval: () -> Float
38
+
34
39
  private
35
40
 
36
41
  attr_reader process_registry: untyped
37
42
 
38
43
  attr_reader database_adapter: untyped
39
44
 
45
+ attr_reader polling_backoff: untyped
46
+
40
47
  # @rbs (now: Time?) -> Reminder?
41
48
  def claim_next: (now: Time?) -> Reminder?
42
49
 
@@ -2,18 +2,35 @@
2
2
 
3
3
  module SolidObjects
4
4
  class WakeUp
5
+ class Watch
6
+ @wake_up: WakeUp
7
+
8
+ @generation: Integer
9
+
10
+ # @rbs (WakeUp, Integer) -> void
11
+ def initialize: (WakeUp, Integer) -> void
12
+
13
+ # @rbs (timeout: Numeric) -> bool
14
+ def wait: (timeout: Numeric) -> bool
15
+ end
16
+
5
17
  @mutex: Mutex
6
18
 
7
19
  @condition: Thread::ConditionVariable
8
20
 
21
+ @generation: Integer
22
+
9
23
  # @rbs () -> void
10
24
  def initialize: () -> void
11
25
 
12
26
  # @rbs () -> void
13
27
  def signal: () -> void
14
28
 
15
- # @rbs (timeout: Numeric) -> void
16
- def wait: (timeout: Numeric) -> void
29
+ # @rbs () -> Watch
30
+ def watch: () -> Watch
31
+
32
+ # @rbs (timeout: Numeric, ?generation: Integer?) -> bool
33
+ def wait: (timeout: Numeric, ?generation: Integer?) -> bool
17
34
 
18
35
  private
19
36
 
@@ -31,6 +31,9 @@ module SolidObjects
31
31
  # @rbs (timeout: Numeric) -> bool
32
32
  def wait: (timeout: Numeric) -> bool
33
33
 
34
+ # @rbs () -> self
35
+ def watch: () -> self
36
+
34
37
  # Starts listening before a caller blocks, so a notification sent between
35
38
  # startup and the first wait is not missed.
36
39
  # @rbs () -> bool
@@ -10,6 +10,18 @@ module SolidObjects
10
10
  # polling interval remains the upper bound, so a missed or failed
11
11
  # notification costs latency rather than correctness.
12
12
  class Redis
13
+ class Watch
14
+ @adapter: Redis
15
+
16
+ @generation: Integer
17
+
18
+ # @rbs (Redis, Integer) -> void
19
+ def initialize: (Redis, Integer) -> void
20
+
21
+ # @rbs (timeout: Numeric) -> bool
22
+ def wait: (timeout: Numeric) -> bool
23
+ end
24
+
13
25
  CHANNEL: ::String
14
26
 
15
27
  FAILED_WAIT_INTERVAL: ::Float
@@ -43,8 +55,11 @@ module SolidObjects
43
55
  # The counter is snapshotted before subscribing, and re-checked before
44
56
  # blocking, so a signal delivered while this caller was still getting
45
57
  # ready is observed rather than absorbed into the new baseline.
46
- # @rbs (timeout: Numeric) -> bool
47
- def wait: (timeout: Numeric) -> bool
58
+ # @rbs (timeout: Numeric, ?generation: Integer?) -> bool
59
+ def wait: (timeout: Numeric, ?generation: Integer?) -> bool
60
+
61
+ # @rbs () -> Watch
62
+ def watch: () -> Watch
48
63
 
49
64
  # Redis delivers to a subscribed connection only, and a subscribed
50
65
  # connection cannot serve other callers, so one background subscription
@@ -2,6 +2,8 @@
2
2
 
3
3
  module SolidObjects
4
4
  class Worker
5
+ @polling_backoff: PollingBackoff
6
+
5
7
  @shutdown_requested: bool
6
8
 
7
9
  @stopped: bool
@@ -36,6 +38,9 @@ module SolidObjects
36
38
  # @rbs () -> bool
37
39
  def shutdown_requested?: () -> bool
38
40
 
41
+ # @rbs () -> Float
42
+ def current_polling_interval: () -> Float
43
+
39
44
  private
40
45
 
41
46
  attr_reader process_registry: untyped
@@ -44,6 +49,8 @@ module SolidObjects
44
49
 
45
50
  attr_reader activations: untyped
46
51
 
52
+ attr_reader polling_backoff: untyped
53
+
47
54
  # @rbs () -> Activation?
48
55
  def cached_ready_activation: () -> Activation?
49
56
 
@@ -36,6 +36,9 @@ module SolidObjects
36
36
  # @rbs () -> DeadLetterManager
37
37
  def self.dead_letters: () -> DeadLetterManager
38
38
 
39
+ # @rbs () -> Administration
40
+ def self.administration: () -> Administration
41
+
39
42
  # @rbs (String | Symbol) -> String
40
43
  def self.table_name: (String | Symbol) -> String
41
44
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: solid_objects
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.13.0
4
+ version: 0.13.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Lucas Carlson
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2026-08-15 00:00:00.000000000 Z
11
+ date: 2026-08-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: actioncable
@@ -319,6 +319,7 @@ files:
319
319
  - benchmark/concurrent_actors.rb
320
320
  - benchmark/enqueue.rb
321
321
  - benchmark/hot_actor.rb
322
+ - benchmark/idle_polling.rb
322
323
  - benchmark/processing.rb
323
324
  - benchmark/query_count.rb
324
325
  - benchmark/support.rb
@@ -381,6 +382,7 @@ files:
381
382
  - lib/solid_objects/actor_registry.rb
382
383
  - lib/solid_objects/actor_snapshot.rb
383
384
  - lib/solid_objects/actor_view.rb
385
+ - lib/solid_objects/administration.rb
384
386
  - lib/solid_objects/application_actor_loader.rb
385
387
  - lib/solid_objects/application_write_guard.rb
386
388
  - lib/solid_objects/broadcast_executor.rb
@@ -419,6 +421,7 @@ files:
419
421
  - lib/solid_objects/message_reference.rb
420
422
  - lib/solid_objects/operation_dispatcher.rb
421
423
  - lib/solid_objects/payload_broadcast.rb
424
+ - lib/solid_objects/polling_backoff.rb
422
425
  - lib/solid_objects/process_pruner.rb
423
426
  - lib/solid_objects/process_registry.rb
424
427
  - lib/solid_objects/reference.rb
@@ -466,6 +469,7 @@ files:
466
469
  - sig/generated/lib/solid_objects/actor_registry.rbs
467
470
  - sig/generated/lib/solid_objects/actor_snapshot.rbs
468
471
  - sig/generated/lib/solid_objects/actor_view.rbs
472
+ - sig/generated/lib/solid_objects/administration.rbs
469
473
  - sig/generated/lib/solid_objects/application_actor_loader.rbs
470
474
  - sig/generated/lib/solid_objects/application_write_guard.rbs
471
475
  - sig/generated/lib/solid_objects/broadcast_executor.rbs
@@ -504,6 +508,7 @@ files:
504
508
  - sig/generated/lib/solid_objects/message_reference.rbs
505
509
  - sig/generated/lib/solid_objects/operation_dispatcher.rbs
506
510
  - sig/generated/lib/solid_objects/payload_broadcast.rbs
511
+ - sig/generated/lib/solid_objects/polling_backoff.rbs
507
512
  - sig/generated/lib/solid_objects/process_pruner.rbs
508
513
  - sig/generated/lib/solid_objects/process_registry.rbs
509
514
  - sig/generated/lib/solid_objects/reference.rbs