Interface ExactlyOnceProducer
- All Superinterfaces:
AutoCloseable, ProducerBase
try (IntegrationClient client = IntegrationClient.builder("http://localhost:8080").build();
ExactlyOnceProducer producer =
client.newExactlyOnceProducer("group-a/orders/0")) {
long offset = checkpoint.load() + 1;
producer.send(
offset,
Invocation.create()
.setServiceName("Orders")
.setHandlerName("ingest")
.setBody(payload));
checkpoint.store(producer.flush());
}
Buffering
With a positiveProducerOptions.bufferMemory(), send(long, Invocation) first admits the invocation
to a byte-bounded local buffer while it waits to be handed to the transport. A value of zero
disables local buffering, so send instead waits until the protocol and transport are
writable. Either wait is bounded by ProducerOptions.maxBlockTime(). The returned future
tracks durable acknowledgement, not admission. Send several invocations without awaiting each
future, then use ProducerBase.flush() or ProducerBase.flushAsync() to await them in bulk. ProducerBase.close() does not flush.
Non-blocking admission
For event-loop or callback-based code,trySend(long, Invocation) does not wait for admission. SendAttempt.Accepted contains the durable-acknowledgement future. On SendAttempt.Backpressured, use SendAttempt.Backpressured.ready() to schedule a retry of
the same offset and invocation on the event loop; readiness is a notification, not an admission
reservation.
static CompletableFuture<SendResult> sendWithoutBlocking(
ExactlyOnceProducer producer, long offset, Invocation invocation, Executor eventLoop) {
SendAttempt attempt = producer.trySend(offset, invocation);
if (attempt instanceof SendAttempt.Accepted accepted) {
return accepted.acknowledgement();
}
return ((SendAttempt.Backpressured) attempt)
.ready()
.thenComposeAsync(
ignored -> sendWithoutBlocking(producer, offset, invocation, eventLoop), eventLoop);
}
Producer identity and deduplication
Each invocation has a strictly increasing offset. Restate deduplicates on(producerId,
offset), so choose a producer id that is stable across restarts and distinct per
independent offset sequence: for example, a Kafka groupId/topic/partition or a
Postgres logical-replication slot.
After a crash, replay from the last checkpoint; Restate drops already-committed offsets.
ProducerBase.flush() and ProducerBase.waitAcknowledged(long) report the durable watermark to checkpoint,
and ProducerBase.lastAcknowledgedOffset() remains available after a stream failure.
A producer is not thread-safe and fails fast with ConcurrentModificationException if used from more than one thread at once.
-
Method Summary
Modifier and TypeMethodDescriptionsend(long offset, Invocation invocation) Sends an invocation atoffset.trySend(long offset, Invocation invocation) Attempts to send an invocation atoffsetwithout blocking.Methods inherited from interface ProducerBase
close, flush, flushAsync, lastAcknowledgedOffset, lastSentOffset, waitAcknowledged
-
Method Details
-
send
CompletableFuture<SendResult> send(long offset, Invocation invocation) throws ProducerBufferExhaustedException Sends an invocation atoffset.This method waits up to
ProducerOptions.maxBlockTime()when the local buffer is full, or, when buffering is disabled, until protocol and transport readiness permit a direct write. The invocation is refused withProducerBufferExhaustedExceptionif the timeout elapses. A zero duration makes this method fail immediately under backpressure.The returned future completes when the invocation is durably committed by Restate.
- Parameters:
offset- the offset to assign to this record; must be strictly greater than the previous oneinvocation- the invocation to send- Returns:
- a future completing, once the record is durably committed by Restate, with the
SendResultcarryingoffset - Throws:
ProducerBufferExhaustedException- if the producer cannot admit the invocation before the configured maximum blocking time elapses, or the thread is interrupted while waitingIllegalStateException- if a reentrant producer callback invokes this method when it would blockIllegalArgumentException- ifoffsetis not strictly greater thanProducerBase.lastSentOffset(), or buffering is enabled and the serialized invocation is larger thanProducerOptions.bufferMemory()ConcurrentModificationException- if the producer is used concurrently from another thread
-
trySend
Attempts to send an invocation atoffsetwithout blocking.An
SendAttempt.Acceptedcarries the durable-acknowledgement future. ASendAttempt.Backpressuredcarries a future that completes when retrying may succeed; the notification does not reserve admission.- Parameters:
offset- the offset to assign; must be strictly greater than the previous accepted offsetinvocation- the invocation to send- Returns:
- the admission result
- Throws:
IllegalArgumentException- ifoffsetis not strictly greater thanProducerBase.lastSentOffset(), or buffering is enabled and the serialized invocation is larger thanProducerOptions.bufferMemory()ConcurrentModificationException- if the producer is used concurrently from another thread
-