Class PubSubDeadLetterQueue
- All Implemented Interfaces:
DeadLetterQueue,Serializable
DeadLetterQueue publishing every terminally failed element to a Pub/Sub topic, used
through FailureHandler.sendToDeadLetterQueue(DeadLetterQueue).
It sees failures through the shared FailedElement contract, so one instance serves
every connector in this repository — a BigQuery or Cloud Tasks job dead-letters to a topic by
adding the flink-connector-gcp-pubsub artifact as a dependency. It does not go through
the Pub/Sub sink: it owns an SDK Publisher of its own, so a job that dead-letters is not
also a Pub/Sub sink job.
BigQuerySink.<Order>builder()
.table(TableDestination.of("my-project", "my_dataset", "orders"))
.serializer(serializer)
.failureHandler(
FailureHandler.sendToDeadLetterQueue(
PubSubDeadLetterQueue.builder()
.topic(
TopicDestination.of(
"my-project", "dead-letters"))
.build()))
.build();
The envelope
The message data is the element's payload bytes, or
empty when that method returns null. A concrete failure may also supply an intentionally
empty payload, so consumers must use the attributes rather than data length alone to classify the
failure. Every message carries five attributes:
| Attribute | Value |
|---|---|
dlq-connector | bigquery, bigtable, cloudtasks,
pubsub or spanner |
dlq-destination | the resource the element was bound for, or a
connector-defined sentinel such as unresolved |
dlq-error | the failure description, truncated to Pub/Sub's 1024-byte attribute-value limit |
dlq-timestamp | when the element was offered, ISO-8601 |
dlq-subtask | the offering sink subtask's index |
The failure's cause chain is not in the envelope: it has no bounded string form. Enable
DEBUG logging on this class to see each element's untruncated error in the job logs.
An element whose payload is close to Pub/Sub's 10 MB message limit may not fit once the attributes are added — an oversized element is exactly the kind a sink rejects, so this is the expected shape of that case. The publish then fails and fails the job, since a dead letter that cannot be delivered must not be silently lost.
Buffering
Publishes are batched by the SDK and awaited in flush(), so the usual case — a rare
failure among healthy records — costs no round trip per element. Because a systematic failure (a
serializer bug rejecting every record) would otherwise let one whole checkpoint interval's
publishes accumulate unawaited, the in-flight count is bounded: at PubSubDeadLetterQueue.Builder.maxInFlightMessages(int) the queue awaits what it holds before accepting more.
Both of those waits — the one in flush(), which runs at every checkpoint barrier, and
the one the in-flight bound triggers inside offer(FailedElement) — are bounded by PubSubDeadLetterQueue.Builder.flushTimeout(Duration): one deadline covering all of that wait's publishes, not one per
publish, and not what a whole checkpoint interval spends. Expiry fails the job and drops nothing.
The queue's close waits for the same publishes under a budget of its own, PubSubDeadLetterQueue.Builder.shutdownTimeout(Duration).
Metrics
open(FailureHandlerContext) registers five names on the metric group the context
carries — the host sink writer's, so a BigQuery job dead-lettering to a topic reports them
beside BigQuery's own. They are deadLettersPublished, inFlightDeadLetters,
deadLetterFlushMillis, longestDeadLetterFlushMillis and
deadLetterPublisherShutdownsAbandoned; what each means is on this connector's documentation
page, under "Dead-letter metrics". The count of elements offered is not among them
because every sink here already reports it as numRecordsSendErrors on that same group.
Credentials
Production publishers use application-default credentials unless PubSubDeadLetterQueue.Builder.serviceAccountKeyFile(String) selects a service-account JSON key. The configured path
crosses Flink serialization, and each host sink writer reads the file when it opens the queue.
Parsed credentials are never stored in the job graph. The queue does not inherit the host
connector's credential setting, so the dead-letter publisher may use a separate identity.
Instances are configured on the job graph and serialized to the tasks; the publisher itself is
created in open(FailureHandlerContext). Lifecycle, and the at-least-once guarantee that
comes with it, are the DeadLetterQueue contract's.
- See Also:
-
Nested Class Summary
Nested Classes -
Field Summary
FieldsModifier and TypeFieldDescriptionstatic final DurationThe defaultPubSubDeadLetterQueue.Builder.flushTimeout(Duration): a tenth of Flink's defaultexecution.checkpointing.timeout, so a dead-letter outage costs a fraction of a checkpoint's budget rather than all of it.static final intThe default in-flight bound: high enough that a rare failure never waits.static final DurationThe defaultPubSubDeadLetterQueue.Builder.shutdownTimeout(Duration), matching the sink's own.static final intPubSubDeadLetterQueue.Builder.maxInFlightMessages(int)value buffering untilflush().static final intPubSubDeadLetterQueue.Builder.maxInFlightMessages(int)value publishing each element synchronously. -
Method Summary
Modifier and TypeMethodDescriptionbuilder()Returns a builder.voidclose()Releases resources held by the queue when the sink writer closes.voidflush()Durably persists every element offered so far; called at every checkpoint barrier and at end of input (and at any additional sink-triggered flush, such as an optional periodic flush interval).voidoffer(FailedElement element) Accepts one terminally failed element; implementations may buffer untilDeadLetterQueue.flush().voidopen(FailureHandlerContext context) Called once, before the firstDeadLetterQueue.offer(io.github.flink.gcp.connector.base.failure.FailedElement), when the sink writer is created.toString()
-
Field Details
-
DEFAULT_MAX_IN_FLIGHT_MESSAGES
public static final int DEFAULT_MAX_IN_FLIGHT_MESSAGESThe default in-flight bound: high enough that a rare failure never waits.- See Also:
-
WRITE_THROUGH
public static final int WRITE_THROUGHPubSubDeadLetterQueue.Builder.maxInFlightMessages(int)value publishing each element synchronously.- See Also:
-
UNBOUNDED
public static final int UNBOUNDEDPubSubDeadLetterQueue.Builder.maxInFlightMessages(int)value buffering untilflush().- See Also:
-
DEFAULT_SHUTDOWN_TIMEOUT
The defaultPubSubDeadLetterQueue.Builder.shutdownTimeout(Duration), matching the sink's own. -
DEFAULT_FLUSH_TIMEOUT
The defaultPubSubDeadLetterQueue.Builder.flushTimeout(Duration): a tenth of Flink's defaultexecution.checkpointing.timeout, so a dead-letter outage costs a fraction of a checkpoint's budget rather than all of it. It is deliberately not derived from the SDK's retry ladder (5 s, 20 s then 60 s per attempt, within 600 s): a batch published onofferis usually already on its third attempt by the time the barrier's flush waits for it, so no fixed budget corresponds to a whole number of attempts.
-
-
Method Details
-
builder
Returns a builder. The dead-letter topic is required; every other knob is optional, though a service-account key file cannot be combined with an emulator endpoint.- Returns:
- the builder
-
open
Description copied from interface:DeadLetterQueueCalled once, before the firstDeadLetterQueue.offer(io.github.flink.gcp.connector.base.failure.FailedElement), when the sink writer is created.- Specified by:
openin interfaceDeadLetterQueue- Parameters:
context- the writer's subtask index and metric group- Throws:
IOException- if the queue cannot be opened; this fails the job
-
offer
Description copied from interface:DeadLetterQueueAccepts one terminally failed element; implementations may buffer untilDeadLetterQueue.flush().- Specified by:
offerin interfaceDeadLetterQueue- Parameters:
element- the failed element- Throws:
IOException- if the element cannot be accepted; this fails the job
-
flush
Description copied from interface:DeadLetterQueueDurably persists every element offered so far; called at every checkpoint barrier and at end of input (and at any additional sink-triggered flush, such as an optional periodic flush interval).- Specified by:
flushin interfaceDeadLetterQueue- Throws:
IOException- if persistence fails; this fails the ongoing checkpoint
-
close
Description copied from interface:DeadLetterQueueReleases resources held by the queue when the sink writer closes.- Specified by:
closein interfaceDeadLetterQueue- Throws:
Exception
-
toString
-