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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +37 -0
- data/README.md +49 -10
- data/benchmark/idle_polling.rb +98 -0
- data/benchmark/support.rb +2 -0
- data/docs/adr/0011-wake-up-strategy.md +6 -0
- data/docs/architecture.md +2 -2
- data/docs/benchmarks.md +38 -0
- data/docs/development.md +1 -0
- data/docs/operations.md +32 -0
- data/docs/roadmap.md +8 -5
- data/lib/solid_objects/actor.rb +54 -4
- data/lib/solid_objects/administration.rb +44 -0
- data/lib/solid_objects/broadcast_executor.rb +35 -4
- data/lib/solid_objects/configuration.rb +4 -0
- data/lib/solid_objects/effect_executor.rb +34 -3
- data/lib/solid_objects/executor.rb +5 -5
- data/lib/solid_objects/polling_backoff.rb +45 -0
- data/lib/solid_objects/process_registry.rb +51 -0
- data/lib/solid_objects/reminder_scheduler.rb +35 -4
- data/lib/solid_objects/version.rb +1 -1
- data/lib/solid_objects/wake_up.rb +36 -4
- data/lib/solid_objects/wake_up_adapters/postgresql.rb +6 -0
- data/lib/solid_objects/wake_up_adapters/redis.rb +25 -3
- data/lib/solid_objects/worker.rb +39 -3
- data/lib/solid_objects.rb +9 -0
- data/sig/generated/lib/solid_objects/actor.rbs +35 -6
- data/sig/generated/lib/solid_objects/administration.rbs +13 -0
- data/sig/generated/lib/solid_objects/broadcast_executor.rbs +7 -0
- data/sig/generated/lib/solid_objects/configuration.rbs +6 -2
- data/sig/generated/lib/solid_objects/effect_executor.rbs +7 -0
- data/sig/generated/lib/solid_objects/executor.rbs +4 -4
- data/sig/generated/lib/solid_objects/polling_backoff.rbs +29 -0
- data/sig/generated/lib/solid_objects/process_registry.rbs +15 -0
- data/sig/generated/lib/solid_objects/reminder_scheduler.rbs +7 -0
- data/sig/generated/lib/solid_objects/wake_up.rbs +19 -2
- data/sig/generated/lib/solid_objects/wake_up_adapters/postgresql.rbs +3 -0
- data/sig/generated/lib/solid_objects/wake_up_adapters/redis.rbs +17 -2
- data/sig/generated/lib/solid_objects/worker.rbs +7 -0
- data/sig/generated/lib/solid_objects.rbs +3 -0
- 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.
|
|
50
|
-
#
|
|
51
|
-
#
|
|
52
|
-
#
|
|
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 (
|
|
16
|
-
def
|
|
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.
|
|
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-
|
|
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
|