ProducerController
Esta página aún no está disponible en tu idioma.
ProducerController wraps a sender’s outgoing messages,
assigning sequence numbers + holding them until acked by
the consumer.
import { ProducerController, ProducerControllerOptions } from 'actor-ts';
const producerControllerOptions = ProducerControllerOptions.create<OrderEvent>() .withProducerId('orders') // stable identity .withConsumer(consumerRef) .withWindowSize(16) // flow-control window .withResendTimeout(500);const producer = system.spawn( () => new ProducerController<OrderEvent>(producerControllerOptions), 'producer',);
producer.tell({ kind: 'reliable-delivery.send', body: { orderId: 'o-1' } });Internally:
- Assigns seq 1 to the first message, 2 to the next, …
- Sends each via wrapped tell to the consumer.
- Holds in a buffer until acked.
- Retransmits if no ack within
resendTimeout.
Configuration
Section titled “Configuration”type ProducerControllerOptionsType<T> = { consumer: ActorRef<Delivery<T>>; resendTimeout?: number; // default 500 windowSize?: number; // default 16 producerId?: string; // auto-generated if omitted};| Field | Purpose |
|---|---|
consumer | The ConsumerController ref to deliver to. |
resendTimeout | How long to wait for an ack before retransmitting. |
windowSize | Flow-control window — pauses queueing after N unacked messages in flight. |
producerId | Stable identifier used by the consumer to dedup across restarts. Auto-generated if omitted, but for persisted producers you’ll want a stable string so recovery matches up. |
Backpressure
Section titled “Backpressure”windowSize: 16The producer holds up to 16 unacked messages in flight. Beyond that,
incoming { kind: 'reliable-delivery.send' } messages are queued — the
caller’s tell succeeds, but actual sending pauses until the
consumer’s acks free up the buffer.
There is no signal back to the caller when this queueing happens: a
reliable-delivery.send is fire-and-forget — the producer never replies
to it, so there is no ask-based “I sent it” acknowledgment to await.
For senders that need to know a message got through, pass a confirm
callback on the send. It fires once the consumer acks that message —
with null on success, or an Error if the producer stops while the
message is still queued:
producer.tell({ kind: 'reliable-delivery.send', body: { orderId: 'o-1' }, confirm: (err) => { if (err) console.error('delivery failed', err); else console.log('delivered and acked'); },});To turn that into real backpressure, pace your sends against these confirmations — hold off on the next batch until earlier messages confirm.
Retransmission
Section titled “Retransmission”When an ack doesn’t arrive within resendTimeout:
seq 5 sent at t=0seq 5 ack never arrivesat t=500 → retransmit seq 5at t=1000 → retransmit seq 5... until ack arrives or producer stopsRetransmits use the same seq as the original. The consumer dedups based on the seq.
State + restart
Section titled “State + restart”// Without persistence:producer crashes → buffer lost → unacked messages are gone// On restart, send resumes from seq 1 (no resume)
// With ProducerController + PersistentActor wrapper:producer crashes → recovery rebuilds buffer + last-acked-seq// On restart, send resumes from the right seqFor full durability, wrap the producer in a PersistentActor
pattern — persist outgoing messages before sending; on
restart, replay any unacked.
The framework’s plain ProducerController is in-memory —
sufficient for ephemeral streams. For durable streams, layer
persistence on top.
Multiple consumers
Section titled “Multiple consumers”// One producer per consumer:const p1 = system.spawnAnonymous(() => new ProducerController({ consumer: c1Ref, ... }));const p2 = system.spawnAnonymous(() => new ProducerController({ consumer: c2Ref, ... }));Each ProducerController is 1:1 with one ConsumerController. For fan-out, multiple producers send to different consumers.
For routing-based fan-out (one logical stream split across N consumers), you’d write a custom router on top.
Cluster-aware
Section titled “Cluster-aware”ProducerController’s consumer ref can point at a remote
consumer (different cluster node). The cluster transport
serializes envelopes; retransmissions work the same.
The producer holds the buffer locally — on producer-host crash, those messages are lost unless persisted.
Comparison with raw tell
Section titled “Comparison with raw tell”ref.tell(msg): ProducerController:- No seq, no ack, no retransmit - Reliable delivery contract- Lost on dead recipient - Survives transient failures- Sub-microsecond cost - ~50µs per message overheadUse the controller only for streams where loss is unacceptable;
raw tell for everything else.
Where to next
Section titled “Where to next”- Delivery overview — the bigger picture.
- ConsumerController — the receiver side.
- Ack semantics — when acks fire and what they mean.
- PersistentActor — for durable producer state.
