solid_objects 0.5.0 → 0.5.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 +35 -0
- data/app/controllers/solid_objects/components_controller.rb +29 -2
- data/benchmark/support.rb +2 -0
- data/docs/benchmarks.md +7 -0
- data/docs/correctness.md +15 -1
- data/docs/operations.md +20 -2
- data/lib/solid_objects/database_adapter.rb +28 -5
- data/lib/solid_objects/database_adapters/sqlite.rb +43 -5
- data/lib/solid_objects/doctor.rb +46 -8
- data/lib/solid_objects/synchronous_invocation.rb +16 -1
- data/lib/solid_objects/version.rb +1 -1
- data/sig/generated/controllers/solid_objects/components_controller.rbs +6 -0
- data/sig/generated/lib/solid_objects/database_adapter.rbs +10 -0
- data/sig/generated/lib/solid_objects/database_adapters/sqlite.rbs +9 -0
- data/sig/generated/lib/solid_objects/doctor.rbs +12 -0
- data/sig/generated/lib/solid_objects/synchronous_invocation.rbs +8 -0
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 8ac17dc212792f2dfa71c01e701a9e4de27f54b6198ec6e64696b855ff82c4c8
|
|
4
|
+
data.tar.gz: 45bff1ddb2bbc0ff80ddfb55488fec0380d02c729e08a7f06b000b744dddb82d
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: ffb590f2e07a0de4d4ba2ab5ac1cb7d76420b742d36d656b85ecdb0fc401dbabd92b75e47df73948f7e1d42fe4a289d2f16fa8d86086c8bff336ac4e33ae89dc
|
|
7
|
+
data.tar.gz: 5a43e24c8358e2352de0ca1428cdc10d15bdf679c62ca1983b420fa1e739d0c975004e0e33ffb64a96fe3d09cbbcd30181db6a95a678c071fd80f85b935811cd
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,40 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.5.2 - 2026-08-09
|
|
4
|
+
|
|
5
|
+
- Read the database clock once per transaction instead of once per step, and
|
|
6
|
+
resolve the SQLite busy wait from configuration instead of querying the
|
|
7
|
+
connection for it. A synchronous call now issues 49 database queries instead
|
|
8
|
+
of 66, which matters most on PostgreSQL and MySQL where every query is a
|
|
9
|
+
network round trip.
|
|
10
|
+
- Apply every migration in the benchmark harness. It applied only the initial
|
|
11
|
+
migration, so the `state_revision` column added in 0.4.0 was missing, every
|
|
12
|
+
message failed at commit, and the synchronous benchmarks timed out.
|
|
13
|
+
|
|
14
|
+
## 0.5.1 - 2026-08-07
|
|
15
|
+
|
|
16
|
+
- Restore the SQLite busy wait that a synchronous invocation suspends for its
|
|
17
|
+
deadline. Rails installs the busy wait as a Ruby busy handler through the
|
|
18
|
+
sqlite3 `timeout` configuration, which `PRAGMA busy_timeout` reports as zero
|
|
19
|
+
and silently replaces, so the previous save and restore left pooled
|
|
20
|
+
connections with no busy handler at all. Every later writer on that
|
|
21
|
+
connection, inside or outside Solid Objects, then failed immediately with
|
|
22
|
+
`SQLite3::BusyException` instead of waiting for the lock. Suspend the busy
|
|
23
|
+
wait only when the adapter can identify how to restore it, so an Active
|
|
24
|
+
Record release that stops exposing the configured timeout loosens
|
|
25
|
+
synchronous deadline bounds instead of stripping lock waiting from a shared
|
|
26
|
+
pooled connection.
|
|
27
|
+
|
|
28
|
+
- Run the doctor round-trip probe on a dedicated caller process, and accept an
|
|
29
|
+
explicit process registry in `SynchronousInvocation`, so the probe can no
|
|
30
|
+
longer stop and delete a shared application caller process, release its
|
|
31
|
+
activations, and unclaim its messages.
|
|
32
|
+
- Report doctor probe cleanup failures as a failed or warned check instead of
|
|
33
|
+
raising a database lock error out of the command and leaking the probe
|
|
34
|
+
caller process.
|
|
35
|
+
- Instrument component refreshes with actor identity, component name, key,
|
|
36
|
+
dependencies, refresh method, revision, and outcome, excluding locals.
|
|
37
|
+
|
|
3
38
|
## 0.5.0 - 2026-08-07
|
|
4
39
|
|
|
5
40
|
- Add repeatable reactive components with signed string or integer keys and
|
|
@@ -8,12 +8,25 @@ module SolidObjects
|
|
|
8
8
|
|
|
9
9
|
# @rbs () -> void
|
|
10
10
|
def show
|
|
11
|
+
SolidObjects.instrument(:"component.refreshed") { |payload| refresh(payload) }
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
private
|
|
15
|
+
|
|
16
|
+
# @rbs (Hash[Symbol, untyped]) -> void
|
|
17
|
+
def refresh(payload)
|
|
11
18
|
registration = ComponentRegistration.from_token(
|
|
12
19
|
params.require(:token)
|
|
13
20
|
)
|
|
21
|
+
payload.merge!(registration_payload(registration))
|
|
14
22
|
requested_revision = requested_revision_key
|
|
15
23
|
snapshot = ActorSnapshot.new(registration.reference)
|
|
16
|
-
|
|
24
|
+
payload[:instance_id] = snapshot.instance_id
|
|
25
|
+
payload[:revision] = snapshot.revision
|
|
26
|
+
if newer_than_snapshot?(requested_revision, snapshot)
|
|
27
|
+
payload[:outcome] = "conflict"
|
|
28
|
+
return head :conflict
|
|
29
|
+
end
|
|
17
30
|
|
|
18
31
|
authorization_context = SolidObjects
|
|
19
32
|
.configuration
|
|
@@ -26,18 +39,32 @@ module SolidObjects
|
|
|
26
39
|
authorization_context:
|
|
27
40
|
).call
|
|
28
41
|
response.headers["Cache-Control"] = "private, no-store"
|
|
42
|
+
payload[:outcome] = "rendered"
|
|
29
43
|
render html: component_frame(registration, snapshot, rendered)
|
|
30
44
|
rescue Unauthorized
|
|
45
|
+
payload[:outcome] = "unauthorized"
|
|
31
46
|
head :forbidden
|
|
32
47
|
rescue UnknownComponent
|
|
48
|
+
payload[:outcome] = "unknown_component"
|
|
33
49
|
head :not_found
|
|
34
50
|
rescue ActionController::ParameterMissing,
|
|
35
51
|
ArgumentError,
|
|
36
52
|
InvalidComponentToken
|
|
53
|
+
payload[:outcome] = "invalid_token"
|
|
37
54
|
head :bad_request
|
|
38
55
|
end
|
|
39
56
|
|
|
40
|
-
|
|
57
|
+
# @rbs (ComponentRegistration) -> Hash[Symbol, untyped]
|
|
58
|
+
def registration_payload(registration)
|
|
59
|
+
{
|
|
60
|
+
actor_type: registration.reference.actor_type,
|
|
61
|
+
actor_id: registration.reference.actor_id,
|
|
62
|
+
component_name: registration.component_name,
|
|
63
|
+
component_key: registration.component_key,
|
|
64
|
+
dependencies: registration.dependencies,
|
|
65
|
+
refresh_method: registration.refresh_method
|
|
66
|
+
}
|
|
67
|
+
end
|
|
41
68
|
|
|
42
69
|
# @rbs () -> Array[Integer]
|
|
43
70
|
def requested_revision_key
|
data/benchmark/support.rb
CHANGED
|
@@ -228,7 +228,9 @@ module SolidObjectsBenchmark
|
|
|
228
228
|
# @rbs () -> void
|
|
229
229
|
def migrate
|
|
230
230
|
require_relative "../db/migrate/20260805000000_create_solid_objects_tables"
|
|
231
|
+
require_relative "../db/migrate/20260806000000_add_state_revision_to_solid_objects_instances"
|
|
231
232
|
CreateSolidObjectsTables.new.migrate(:up)
|
|
233
|
+
AddStateRevisionToSolidObjectsInstances.new.migrate(:up)
|
|
232
234
|
end
|
|
233
235
|
|
|
234
236
|
# @rbs () -> void
|
data/docs/benchmarks.md
CHANGED
|
@@ -41,6 +41,13 @@ scenario used four worker threads.
|
|
|
41
41
|
| Synchronous latency | p50 1.8 ms, p95 25.6 ms, p99 156.2 ms |
|
|
42
42
|
| Activation reuse | 98.0%, four activations for 200 messages |
|
|
43
43
|
| Queries for one message turn | 29 |
|
|
44
|
+
| Queries for one synchronous call | 49 |
|
|
45
|
+
|
|
46
|
+
A synchronous call costs far more queries than a worker turn because the caller
|
|
47
|
+
also registers or heartbeats its caller process, claims the activation, and
|
|
48
|
+
observes the result. Query count, not query time, dominates synchronous latency
|
|
49
|
+
on a networked database: measured locally against SQLite, database time is
|
|
50
|
+
roughly 5% of a call and the remaining 95% is Ruby.
|
|
44
51
|
|
|
45
52
|
The difference between the SQLite development result and the MySQL adoption
|
|
46
53
|
result is why Solid Objects does not publish one latency promise. Network
|
data/docs/correctness.md
CHANGED
|
@@ -167,7 +167,21 @@ result. Adapter lock/query deadlines cover the durable enqueue, caller-process
|
|
|
167
167
|
registration and heartbeat, activation coordination, and result observation.
|
|
168
168
|
SQLite retries busy coordination operations only within the original call
|
|
169
169
|
deadline and reports `waiting_on=database_contention` when the database cannot
|
|
170
|
-
be inspected at timeout.
|
|
170
|
+
be inspected at timeout. To keep those retries in Ruby, the SQLite adapter
|
|
171
|
+
suspends the connection's busy wait for the duration of each deadline-bound
|
|
172
|
+
transaction and restores it afterwards. Restoration reinstalls the Ruby busy
|
|
173
|
+
handler Rails configures from the sqlite3 `timeout` setting, which
|
|
174
|
+
`PRAGMA busy_timeout` neither reports nor preserves, so a synchronous call
|
|
175
|
+
leaves the connection's lock waiting behaviour exactly as it found it for
|
|
176
|
+
later writers inside and outside Solid Objects.
|
|
177
|
+
|
|
178
|
+
The adapter suspends the busy wait only when it can identify how to restore
|
|
179
|
+
it. When a future Active Record release stops exposing the configured
|
|
180
|
+
timeout, the adapter leaves the connection untouched: synchronous deadlines
|
|
181
|
+
lose their tight bound and wait as long as the configured busy wait allows,
|
|
182
|
+
rather than stripping lock waiting from a pooled connection the rest of the
|
|
183
|
+
application shares. A test asserts the timeout stays discoverable so the
|
|
184
|
+
looser bound cannot be adopted silently. If enqueue cannot commit, `SyncEnqueueTimeout` is
|
|
171
185
|
raised and no message reference exists. MySQL lock waits have one-second InnoDB
|
|
172
186
|
granularity. Ruby handlers that already started are not preempted.
|
|
173
187
|
|
data/docs/operations.md
CHANGED
|
@@ -15,6 +15,14 @@ so the schema check compares the required shape instead of a fixed timestamp.
|
|
|
15
15
|
Warnings such as an all-deny neutral policy do not fail the command because a
|
|
16
16
|
context-aware production policy may correctly deny the probe.
|
|
17
17
|
|
|
18
|
+
The round-trip probe runs on its own dedicated caller process rather than the
|
|
19
|
+
shared application caller process, and removes that record together with its
|
|
20
|
+
temporary actor when it finishes. Running the doctor inside a process that
|
|
21
|
+
already serves synchronous calls therefore leaves the application caller
|
|
22
|
+
process, its activations, and its claimed messages untouched, including when an
|
|
23
|
+
application call overlaps the probe. A database busy enough to block cleanup
|
|
24
|
+
reports a failed or warned check rather than raising out of the command.
|
|
25
|
+
|
|
18
26
|
## Runtime
|
|
19
27
|
|
|
20
28
|
Start all configured roles:
|
|
@@ -147,10 +155,20 @@ transaction rejection, commit-action start/completion/failure, effect and
|
|
|
147
155
|
broadcast enqueue/completion, reminder enqueue, actor destruction/expiration,
|
|
148
156
|
retention pruning, process cleanup, and supervisor lifecycle.
|
|
149
157
|
|
|
158
|
+
`solid_objects.component.refreshed` covers every authorized component refresh
|
|
159
|
+
request. Its payload carries the actor identity, `component_name`,
|
|
160
|
+
`component_key`, declared `dependencies`, `refresh_method`, the rendered
|
|
161
|
+
`instance_id` and `revision`, and an `outcome` of `rendered`, `conflict`,
|
|
162
|
+
`unauthorized`, `unknown_component`, or `invalid_token`. Use it to watch
|
|
163
|
+
refresh rate per key, authorization denials, superseded requests, and render
|
|
164
|
+
duration. A rejected token reports only the outcome, since no signed identity
|
|
165
|
+
was recovered.
|
|
166
|
+
|
|
150
167
|
Payloads contain stable runtime identifiers, actor identity, sequence,
|
|
151
168
|
attempts, ownership generations, and safe exception summaries where relevant.
|
|
152
|
-
Arguments, actor state, results, and outbox payloads are
|
|
153
|
-
log subscriber turns the same notifications into
|
|
169
|
+
Arguments, component locals, actor state, results, and outbox payloads are
|
|
170
|
+
excluded. The bundled log subscriber turns the same notifications into
|
|
171
|
+
structured logger hashes.
|
|
154
172
|
|
|
155
173
|
## Retention and backups
|
|
156
174
|
|
|
@@ -4,6 +4,9 @@ require "time"
|
|
|
4
4
|
|
|
5
5
|
module SolidObjects
|
|
6
6
|
class DatabaseAdapter
|
|
7
|
+
TRANSACTION_CLOCK = :solid_objects_transaction_clock
|
|
8
|
+
TRANSACTION_CLOCK_SCOPE = :solid_objects_transaction_clock_scope
|
|
9
|
+
|
|
7
10
|
class << self
|
|
8
11
|
# @rbs (untyped) -> DatabaseAdapter
|
|
9
12
|
def for(connection)
|
|
@@ -46,10 +49,9 @@ module SolidObjects
|
|
|
46
49
|
|
|
47
50
|
# @rbs () -> Time
|
|
48
51
|
def database_now
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
value.is_a?(Time) ? value.utc : Time.parse("#{value} UTC").utc
|
|
52
|
+
return read_database_now unless ActiveSupport::IsolatedExecutionState[TRANSACTION_CLOCK_SCOPE]
|
|
53
|
+
|
|
54
|
+
ActiveSupport::IsolatedExecutionState[TRANSACTION_CLOCK] ||= read_database_now
|
|
53
55
|
end
|
|
54
56
|
|
|
55
57
|
# @rbs () { () -> untyped } -> untyped
|
|
@@ -70,7 +72,7 @@ module SolidObjects
|
|
|
70
72
|
with_transaction_deadline(connection) do
|
|
71
73
|
connection.transaction(requires_new: true) do
|
|
72
74
|
configure_transaction_deadline(connection)
|
|
73
|
-
block.call
|
|
75
|
+
with_transaction_clock { block.call }
|
|
74
76
|
end
|
|
75
77
|
end
|
|
76
78
|
end
|
|
@@ -91,6 +93,27 @@ module SolidObjects
|
|
|
91
93
|
|
|
92
94
|
attr_reader :connection_pool, :fixed_connection
|
|
93
95
|
|
|
96
|
+
# @rbs () { () -> untyped } -> untyped
|
|
97
|
+
def with_transaction_clock
|
|
98
|
+
return yield if ActiveSupport::IsolatedExecutionState[TRANSACTION_CLOCK_SCOPE]
|
|
99
|
+
|
|
100
|
+
ActiveSupport::IsolatedExecutionState[TRANSACTION_CLOCK_SCOPE] = true
|
|
101
|
+
begin
|
|
102
|
+
yield
|
|
103
|
+
ensure
|
|
104
|
+
ActiveSupport::IsolatedExecutionState[TRANSACTION_CLOCK_SCOPE] = false
|
|
105
|
+
ActiveSupport::IsolatedExecutionState[TRANSACTION_CLOCK] = nil
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
# @rbs () -> Time
|
|
110
|
+
def read_database_now
|
|
111
|
+
value = with_connection do |connection|
|
|
112
|
+
connection.select_value("SELECT #{current_time_expression}")
|
|
113
|
+
end
|
|
114
|
+
value.is_a?(Time) ? value.utc : Time.parse("#{value} UTC").utc
|
|
115
|
+
end
|
|
116
|
+
|
|
94
117
|
# @rbs (untyped) { () -> untyped } -> untyped
|
|
95
118
|
def with_transaction_deadline(_connection)
|
|
96
119
|
yield
|
|
@@ -67,11 +67,49 @@ module SolidObjects
|
|
|
67
67
|
def with_transaction_deadline(connection)
|
|
68
68
|
return yield unless SyncDeadline.active?
|
|
69
69
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
70
|
+
busy_wait = restorable_busy_wait(connection)
|
|
71
|
+
return yield unless busy_wait
|
|
72
|
+
|
|
73
|
+
begin
|
|
74
|
+
connection.execute("PRAGMA busy_timeout = 0")
|
|
75
|
+
yield
|
|
76
|
+
ensure
|
|
77
|
+
restore_busy_wait(connection, busy_wait)
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
# @rbs (untyped) -> Hash[Symbol, untyped]?
|
|
82
|
+
def restorable_busy_wait(connection)
|
|
83
|
+
handler_timeout = configured_busy_handler_timeout(connection)
|
|
84
|
+
return { handler_timeout: } if handler_timeout
|
|
85
|
+
|
|
86
|
+
pragma_timeout = connection.select_value("PRAGMA busy_timeout").to_i
|
|
87
|
+
return nil unless pragma_timeout.positive?
|
|
88
|
+
|
|
89
|
+
{ pragma_timeout: }
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
# @rbs (untyped, Hash[Symbol, untyped]) -> void
|
|
93
|
+
def restore_busy_wait(connection, busy_wait)
|
|
94
|
+
handler_timeout = busy_wait[:handler_timeout]
|
|
95
|
+
if handler_timeout
|
|
96
|
+
connection.raw_connection.busy_handler_timeout = handler_timeout
|
|
97
|
+
return
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
connection.execute("PRAGMA busy_timeout = #{busy_wait.fetch(:pragma_timeout)}")
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
# @rbs (untyped) -> Integer?
|
|
104
|
+
def configured_busy_handler_timeout(connection)
|
|
105
|
+
return nil unless connection.respond_to?(:raw_connection)
|
|
106
|
+
return nil unless connection.raw_connection.respond_to?(:busy_handler_timeout=)
|
|
107
|
+
|
|
108
|
+
pool = connection.respond_to?(:pool) ? connection.pool : nil
|
|
109
|
+
return nil unless pool.respond_to?(:db_config)
|
|
110
|
+
|
|
111
|
+
timeout = pool.db_config.configuration_hash[:timeout]
|
|
112
|
+
timeout&.to_i
|
|
75
113
|
end
|
|
76
114
|
|
|
77
115
|
# @rbs (Exception) -> bool
|
data/lib/solid_objects/doctor.rb
CHANGED
|
@@ -215,25 +215,63 @@ module SolidObjects
|
|
|
215
215
|
# @rbs () -> Check
|
|
216
216
|
def check_sync_round_trip
|
|
217
217
|
actor_id = SecureRandom.uuid
|
|
218
|
+
probe_registry = ProcessRegistry.new
|
|
219
|
+
check = run_sync_probe(actor_id, probe_registry)
|
|
220
|
+
leftovers = remove_probe_records(actor_id:, probe_registry:)
|
|
221
|
+
return check if leftovers.empty? || check.failed?
|
|
222
|
+
|
|
223
|
+
warn_check(
|
|
224
|
+
:sync_round_trip,
|
|
225
|
+
"#{check.message}; could not remove the #{leftovers.join(" and ")}"
|
|
226
|
+
)
|
|
227
|
+
end
|
|
228
|
+
|
|
229
|
+
# @rbs (String, ProcessRegistry) -> Check
|
|
230
|
+
def run_sync_probe(actor_id, probe_registry)
|
|
231
|
+
probe_registry.register(kind: "caller", metadata: { execution: "doctor" })
|
|
218
232
|
value = SecureRandom.hex(8)
|
|
219
|
-
process_registry = SolidObjects.caller_process.process_registry
|
|
220
|
-
reference = ProbeActor.ref(actor_id)
|
|
221
233
|
message_reference = Mailbox.new.enqueue(
|
|
222
|
-
|
|
234
|
+
ProbeActor.ref(actor_id),
|
|
223
235
|
:ping,
|
|
224
236
|
{ value: },
|
|
225
237
|
kind: "sync"
|
|
226
238
|
)
|
|
227
|
-
result = SynchronousInvocation
|
|
239
|
+
result = SynchronousInvocation
|
|
240
|
+
.new(process_registry: probe_registry)
|
|
241
|
+
.call(message_reference, timeout: 5.seconds)
|
|
228
242
|
raise Error, "unexpected round-trip result" unless result == value
|
|
229
243
|
|
|
230
244
|
pass(:sync_round_trip, "durable synchronous actor call completed without a worker")
|
|
231
245
|
rescue => error
|
|
232
246
|
fail_check(:sync_round_trip, "#{error.class}: #{error.message}")
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
247
|
+
end
|
|
248
|
+
|
|
249
|
+
# @rbs (actor_id: String, probe_registry: ProcessRegistry) -> Array[String]
|
|
250
|
+
def remove_probe_records(actor_id:, probe_registry:)
|
|
251
|
+
leftovers = []
|
|
252
|
+
leftovers << "probe actor" unless delete_probe_actor(actor_id)
|
|
253
|
+
leftovers << "probe caller process" unless delete_probe_caller_process(probe_registry)
|
|
254
|
+
leftovers
|
|
255
|
+
end
|
|
256
|
+
|
|
257
|
+
# @rbs (String) -> bool
|
|
258
|
+
def delete_probe_actor(actor_id)
|
|
259
|
+
Instance.where(actor_type: ProbeActor.actor_type, actor_id:).delete_all
|
|
260
|
+
true
|
|
261
|
+
rescue
|
|
262
|
+
false
|
|
263
|
+
end
|
|
264
|
+
|
|
265
|
+
# @rbs (ProcessRegistry) -> bool
|
|
266
|
+
def delete_probe_caller_process(probe_registry)
|
|
267
|
+
process_record = probe_registry.process_record
|
|
268
|
+
return true unless process_record
|
|
269
|
+
|
|
270
|
+
probe_registry.stop
|
|
271
|
+
process_record.delete
|
|
272
|
+
true
|
|
273
|
+
rescue
|
|
274
|
+
false
|
|
237
275
|
end
|
|
238
276
|
|
|
239
277
|
# @rbs (Check, Check) -> bool
|
|
@@ -6,6 +6,13 @@ require "solid_objects/sync_diagnostics"
|
|
|
6
6
|
|
|
7
7
|
module SolidObjects
|
|
8
8
|
class SynchronousInvocation
|
|
9
|
+
# @rbs @dedicated_process_registry: ProcessRegistry?
|
|
10
|
+
|
|
11
|
+
# @rbs (?process_registry: ProcessRegistry?) -> void
|
|
12
|
+
def initialize(process_registry: nil)
|
|
13
|
+
@dedicated_process_registry = process_registry
|
|
14
|
+
end
|
|
15
|
+
|
|
9
16
|
# @rbs (MessageReference, timeout: Numeric) -> untyped
|
|
10
17
|
def call(message_reference, timeout:)
|
|
11
18
|
return call_before_deadline(message_reference, timeout:) if SyncDeadline.active?
|
|
@@ -92,9 +99,17 @@ module SolidObjects
|
|
|
92
99
|
)
|
|
93
100
|
end
|
|
94
101
|
|
|
102
|
+
# @rbs () -> ProcessRegistry
|
|
103
|
+
def process_registry
|
|
104
|
+
dedicated_registry = @dedicated_process_registry
|
|
105
|
+
return SolidObjects.caller_process.process_registry unless dedicated_registry
|
|
106
|
+
|
|
107
|
+
dedicated_registry.tap(&:heartbeat)
|
|
108
|
+
end
|
|
109
|
+
|
|
95
110
|
# @rbs (Message, deadline: Float) -> Integer
|
|
96
111
|
def assist(message, deadline:)
|
|
97
|
-
process_registry =
|
|
112
|
+
process_registry = self.process_registry
|
|
98
113
|
activation = ActivationManager
|
|
99
114
|
.new(owner_id: process_registry.process_record.id)
|
|
100
115
|
.claim(instance_id: message.instance_id)
|
|
@@ -7,6 +7,12 @@ module SolidObjects
|
|
|
7
7
|
|
|
8
8
|
private
|
|
9
9
|
|
|
10
|
+
# @rbs (Hash[Symbol, untyped]) -> void
|
|
11
|
+
def refresh: (Hash[Symbol, untyped]) -> void
|
|
12
|
+
|
|
13
|
+
# @rbs (ComponentRegistration) -> Hash[Symbol, untyped]
|
|
14
|
+
def registration_payload: (ComponentRegistration) -> Hash[Symbol, untyped]
|
|
15
|
+
|
|
10
16
|
# @rbs () -> Array[Integer]
|
|
11
17
|
def requested_revision_key: () -> Array[Integer]
|
|
12
18
|
|
|
@@ -2,6 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
module SolidObjects
|
|
4
4
|
class DatabaseAdapter
|
|
5
|
+
TRANSACTION_CLOCK: ::Symbol
|
|
6
|
+
|
|
7
|
+
TRANSACTION_CLOCK_SCOPE: ::Symbol
|
|
8
|
+
|
|
5
9
|
# @rbs (untyped) -> DatabaseAdapter
|
|
6
10
|
def self.for: (untyped) -> DatabaseAdapter
|
|
7
11
|
|
|
@@ -42,6 +46,12 @@ module SolidObjects
|
|
|
42
46
|
|
|
43
47
|
attr_reader fixed_connection: untyped
|
|
44
48
|
|
|
49
|
+
# @rbs () { () -> untyped } -> untyped
|
|
50
|
+
def with_transaction_clock: () { () -> untyped } -> untyped
|
|
51
|
+
|
|
52
|
+
# @rbs () -> Time
|
|
53
|
+
def read_database_now: () -> Time
|
|
54
|
+
|
|
45
55
|
# @rbs (untyped) { () -> untyped } -> untyped
|
|
46
56
|
def with_transaction_deadline: (untyped) { () -> untyped } -> untyped
|
|
47
57
|
|
|
@@ -26,6 +26,15 @@ module SolidObjects
|
|
|
26
26
|
# @rbs (untyped) { () -> untyped } -> untyped
|
|
27
27
|
def with_transaction_deadline: (untyped) { () -> untyped } -> untyped
|
|
28
28
|
|
|
29
|
+
# @rbs (untyped) -> Hash[Symbol, untyped]?
|
|
30
|
+
def restorable_busy_wait: (untyped) -> Hash[Symbol, untyped]?
|
|
31
|
+
|
|
32
|
+
# @rbs (untyped, Hash[Symbol, untyped]) -> void
|
|
33
|
+
def restore_busy_wait: (untyped, Hash[Symbol, untyped]) -> void
|
|
34
|
+
|
|
35
|
+
# @rbs (untyped) -> Integer?
|
|
36
|
+
def configured_busy_handler_timeout: (untyped) -> Integer?
|
|
37
|
+
|
|
29
38
|
# @rbs (Exception) -> bool
|
|
30
39
|
def deadline_error?: (Exception) -> bool
|
|
31
40
|
|
|
@@ -78,6 +78,18 @@ module SolidObjects
|
|
|
78
78
|
# @rbs () -> Check
|
|
79
79
|
def check_sync_round_trip: () -> Check
|
|
80
80
|
|
|
81
|
+
# @rbs (String, ProcessRegistry) -> Check
|
|
82
|
+
def run_sync_probe: (String, ProcessRegistry) -> Check
|
|
83
|
+
|
|
84
|
+
# @rbs (actor_id: String, probe_registry: ProcessRegistry) -> Array[String]
|
|
85
|
+
def remove_probe_records: (actor_id: String, probe_registry: ProcessRegistry) -> Array[String]
|
|
86
|
+
|
|
87
|
+
# @rbs (String) -> bool
|
|
88
|
+
def delete_probe_actor: (String) -> bool
|
|
89
|
+
|
|
90
|
+
# @rbs (ProcessRegistry) -> bool
|
|
91
|
+
def delete_probe_caller_process: (ProcessRegistry) -> bool
|
|
92
|
+
|
|
81
93
|
# @rbs (Check, Check) -> bool
|
|
82
94
|
def ready_for_round_trip?: (Check, Check) -> bool
|
|
83
95
|
|
|
@@ -2,6 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
module SolidObjects
|
|
4
4
|
class SynchronousInvocation
|
|
5
|
+
@dedicated_process_registry: ProcessRegistry?
|
|
6
|
+
|
|
7
|
+
# @rbs (?process_registry: ProcessRegistry?) -> void
|
|
8
|
+
def initialize: (?process_registry: ProcessRegistry?) -> void
|
|
9
|
+
|
|
5
10
|
# @rbs (MessageReference, timeout: Numeric) -> untyped
|
|
6
11
|
def call: (MessageReference, timeout: Numeric) -> untyped
|
|
7
12
|
|
|
@@ -22,6 +27,9 @@ module SolidObjects
|
|
|
22
27
|
# @rbs (Message) -> bot
|
|
23
28
|
def raise_rejection: (Message) -> bot
|
|
24
29
|
|
|
30
|
+
# @rbs () -> ProcessRegistry
|
|
31
|
+
def process_registry: () -> ProcessRegistry
|
|
32
|
+
|
|
25
33
|
# @rbs (Message, deadline: Float) -> Integer
|
|
26
34
|
def assist: (Message, deadline: Float) -> Integer
|
|
27
35
|
|
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.5.
|
|
4
|
+
version: 0.5.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-09 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: actioncable
|