You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Add the staging side of the transmit family to the Ruby gem, so a Rails
actor can replay its operations onto another runtime the same way a
solid-objects-js actor does with this.transmit().increment({ amount }):
classCounter < SolidObjects::Actormessagedefincrement(amount: 1)self.count += amounttransmit.increment(amount:)countendendSolidObjects.register_transmitdo |envelope|
DeliverToUpstream.call(envelope)# raise while offline; the outbox retriesend
This is the counterpart of #47 (SolidObjects::Transmission.receive, the
ingest). #47 gives Ruby the receiving side; this issue gives it the
sending side. Together the transmit family becomes symmetric across
runtimes, over one wire contract.
Why
Rails-to-Rails and Rails-to-Node replication. An edge or on-prem
Rails install can replay operations to a central runtime with the same
at-least-once, per-actor-ordered, idempotent guarantees the browser
gets. The Node ingest (receiveTransmitEnvelope, solid-objects-js#18)
already accepts the envelopes, so Rails-to-Node works the day this
ships.
emit already stages transactional effects, and SolidObjects.register_effect(name, &handler) already registers drain
handlers with at-least-once retries and dead letters.
OperationDispatcher is the fluent staging proxy schedule uses. The transmit method is the same pattern with an EffectIntent named solid-objects.transmit whose arguments hold operation and arguments.
The ordered drain (a claimed transmit effect delivers every undelivered
sibling for its actor up to its own mailbox sequence, oldest first) is
one SQL query over the effects and messages tables, ported from the JS
implementation.
Shape
Actor#transmit returns an OperationDispatcher that stages the
effect intent in the same commit as the state change. Same-actor target
by default; emit("solid-objects.transmit", ...) with explicit actorType / actorId covers a different target, matching JS.
SolidObjects.register_transmit(&deliver) wraps register_effect("solid-objects.transmit") with envelope construction
and the ordered drain. The block receives one envelope per staged
effect and raises to retry.
Failing Minitest first: transmit.increment(amount: 2) stages one
effect in the actor commit; register_transmit delivers envelopes in
per-actor order across a failed delivery; a raised delivery retries
with backoff and dead-letters on exhaustion; the envelope keys are
camelCase.
Implement Actor#transmit and SolidObjects.register_transmit.
Documentation, roadmap entry, parity ledger row (staging becomes
native in both runtimes), both changelogs, both suites green.
Dependencies and order
Land #47 first; this builds on its envelope validation, error class, and
fixtures. A cross-runtime integration test (Rails stages, Node ingests)
can live in the JS repository's compatibility suite afterwards.
Summary
Add the staging side of the transmit family to the Ruby gem, so a Rails
actor can replay its operations onto another runtime the same way a
solid-objects-js actor does with
this.transmit().increment({ amount }):This is the counterpart of #47 (
SolidObjects::Transmission.receive, theingest). #47 gives Ruby the receiving side; this issue gives it the
sending side. Together the transmit family becomes symmetric across
runtimes, over one wire contract.
Why
Rails install can replay operations to a central runtime with the same
at-least-once, per-actor-ordered, idempotent guarantees the browser
gets. The Node ingest (
receiveTransmitEnvelope,solid-objects-js#18)
already accepts the envelopes, so Rails-to-Node works the day this
ships.
this gem. With RFP: SolidObjects::Transmission.receive, the server ingest for browser transmit envelopes #47 alone, the transmit family is JS-full and Ruby-half.
The staging mechanism costs little here because the primitives exist.
Why this is small
emitalready stages transactional effects, andSolidObjects.register_effect(name, &handler)already registers drainhandlers with at-least-once retries and dead letters.
OperationDispatcheris the fluent staging proxyscheduleuses. Thetransmitmethod is the same pattern with anEffectIntentnamedsolid-objects.transmitwhose arguments holdoperationandarguments.sibling for its actor up to its own mailbox sequence, oldest first) is
one SQL query over the effects and messages tables, ported from the JS
implementation.
Shape
Actor#transmitreturns anOperationDispatcherthat stages theeffect intent in the same commit as the state change. Same-actor target
by default;
emit("solid-objects.transmit", ...)with explicitactorType/actorIdcovers a different target, matching JS.SolidObjects.register_transmit(&deliver)wrapsregister_effect("solid-objects.transmit")with envelope constructionand the ordered drain. The block receives one envelope per staged
effect and raises to retry.
camelCase keys (
effectId,actorType,actorId,operation,arguments), so anyreceiveTransmitEnvelope/Transmission.receiveingest accepts it. The receiving side dedups ontransmit:<effectId>.Plan
transmit.increment(amount: 2)stages oneeffect in the actor commit;
register_transmitdelivers envelopes inper-actor order across a failed delivery; a raised delivery retries
with backoff and dead-letters on exhaustion; the envelope keys are
camelCase.
Actor#transmitandSolidObjects.register_transmit.the fixture file both repositories share.
native in both runtimes), both changelogs, both suites green.
Dependencies and order
Land #47 first; this builds on its envelope validation, error class, and
fixtures. A cross-runtime integration test (Rails stages, Node ingests)
can live in the JS repository's compatibility suite afterwards.