26
26
import org .apache .flink .connector .kafka .sink .internal .ProducerPool ;
27
27
import org .apache .flink .connector .kafka .sink .internal .ProducerPoolImpl ;
28
28
import org .apache .flink .connector .kafka .sink .internal .ReadableBackchannel ;
29
+ import org .apache .flink .connector .kafka .sink .internal .TransactionAbortStrategyContextImpl ;
30
+ import org .apache .flink .connector .kafka .sink .internal .TransactionAbortStrategyImpl ;
29
31
import org .apache .flink .connector .kafka .sink .internal .TransactionFinished ;
30
- import org .apache .flink .connector .kafka .sink .internal .TransactionalIdFactory ;
32
+ import org .apache .flink .connector .kafka .sink .internal .TransactionNamingStrategyContextImpl ;
33
+ import org .apache .flink .connector .kafka .sink .internal .TransactionNamingStrategyImpl ;
31
34
import org .apache .flink .runtime .checkpoint .CheckpointIDCounter ;
32
35
import org .apache .flink .util .FlinkRuntimeException ;
33
36
54
57
*/
55
58
class ExactlyOnceKafkaWriter <IN > extends KafkaWriter <IN > {
56
59
private static final Logger LOG = LoggerFactory .getLogger (ExactlyOnceKafkaWriter .class );
60
+ /**
61
+ * Prefix for the transactional id. Must be unique across all sinks writing to the same broker.
62
+ */
57
63
private final String transactionalIdPrefix ;
64
+ /**
65
+ * Strategy to abort lingering transactions from previous executions during writer
66
+ * initialization.
67
+ */
68
+ private final TransactionAbortStrategyImpl transactionAbortStrategy ;
69
+ /** Strategy to name transactions. */
70
+ private final TransactionNamingStrategyImpl transactionNamingStrategy ;
58
71
59
72
private final KafkaWriterState kafkaWriterState ;
60
73
private final Collection <KafkaWriterState > recoveredStates ;
@@ -72,6 +85,8 @@ class ExactlyOnceKafkaWriter<IN> extends KafkaWriter<IN> {
72
85
* related methods.
73
86
*/
74
87
private final ReadableBackchannel <TransactionFinished > backchannel ;
88
+ /** The context used to name transactions. */
89
+ private final TransactionNamingStrategyContextImpl namingContext ;
75
90
76
91
/**
77
92
* Constructor creating a kafka writer.
@@ -95,6 +110,8 @@ class ExactlyOnceKafkaWriter<IN> extends KafkaWriter<IN> {
95
110
WriterInitContext sinkInitContext ,
96
111
KafkaRecordSerializationSchema <IN > recordSerializer ,
97
112
SerializationSchema .InitializationContext schemaContext ,
113
+ TransactionAbortStrategyImpl transactionAbortStrategy ,
114
+ TransactionNamingStrategyImpl transactionNamingStrategy ,
98
115
Collection <KafkaWriterState > recoveredStates ) {
99
116
super (
100
117
deliveryGuarantee ,
@@ -104,6 +121,11 @@ class ExactlyOnceKafkaWriter<IN> extends KafkaWriter<IN> {
104
121
schemaContext );
105
122
this .transactionalIdPrefix =
106
123
checkNotNull (transactionalIdPrefix , "transactionalIdPrefix must not be null" );
124
+ this .transactionAbortStrategy =
125
+ checkNotNull (transactionAbortStrategy , "transactionAbortStrategy must not be null" );
126
+ this .transactionNamingStrategy =
127
+ checkNotNull (
128
+ transactionNamingStrategy , "transactionNamingStrategy must not be null" );
107
129
108
130
try {
109
131
recordSerializer .open (schemaContext , kafkaSinkContext );
@@ -127,6 +149,9 @@ class ExactlyOnceKafkaWriter<IN> extends KafkaWriter<IN> {
127
149
subtaskId ,
128
150
sinkInitContext .getTaskInfo ().getAttemptNumber (),
129
151
transactionalIdPrefix );
152
+ this .namingContext =
153
+ new TransactionNamingStrategyContextImpl (
154
+ transactionalIdPrefix , subtaskId , restoredCheckpointId , producerPool );
130
155
}
131
156
132
157
@ Override
@@ -147,13 +172,10 @@ public void initialize() {
147
172
}
148
173
149
174
private FlinkKafkaInternalProducer <byte [], byte []> startTransaction (long checkpointId ) {
175
+ namingContext .setNextCheckpointId (checkpointId );
150
176
FlinkKafkaInternalProducer <byte [], byte []> producer =
151
- producerPool .getTransactionalProducer (
152
- TransactionalIdFactory .buildTransactionalId (
153
- transactionalIdPrefix ,
154
- kafkaSinkContext .getParallelInstanceId (),
155
- checkpointId ),
156
- checkpointId );
177
+ transactionNamingStrategy .getTransactionalProducer (namingContext );
178
+ namingContext .setLastCheckpointId (checkpointId );
157
179
producer .beginTransaction ();
158
180
return producer ;
159
181
}
@@ -236,13 +258,34 @@ private void abortLingeringTransactions(
236
258
}
237
259
}
238
260
239
- try (TransactionAborter transactionAborter =
240
- new TransactionAborter (
241
- kafkaSinkContext .getParallelInstanceId (),
242
- kafkaSinkContext .getNumberOfParallelInstances (),
243
- id -> producerPool .getTransactionalProducer (id , startCheckpointId ),
244
- producerPool ::recycle )) {
245
- transactionAborter .abortLingeringTransactions (prefixesToAbort , startCheckpointId );
246
- }
261
+ LOG .info (
262
+ "Aborting lingering transactions with prefixes {} using {}" ,
263
+ prefixesToAbort ,
264
+ transactionAbortStrategy );
265
+ TransactionAbortStrategyContextImpl context =
266
+ getTransactionAbortStrategyContext (startCheckpointId , prefixesToAbort );
267
+ transactionAbortStrategy .abortTransactions (context );
268
+ }
269
+
270
+ private TransactionAbortStrategyContextImpl getTransactionAbortStrategyContext (
271
+ long startCheckpointId , List <String > prefixesToAbort ) {
272
+ TransactionAbortStrategyImpl .TransactionAborter aborter =
273
+ transactionalId -> {
274
+ // getTransactionalProducer already calls initTransactions, which cancels the
275
+ // transaction
276
+ FlinkKafkaInternalProducer <byte [], byte []> producer =
277
+ producerPool .getTransactionalProducer (transactionalId , 0 );
278
+ LOG .debug ("Aborting transaction {}" , transactionalId );
279
+ producer .flush ();
280
+ short epoch = producer .getEpoch ();
281
+ producerPool .recycle (producer );
282
+ return epoch ;
283
+ };
284
+ return new TransactionAbortStrategyContextImpl (
285
+ kafkaSinkContext .getParallelInstanceId (),
286
+ kafkaSinkContext .getNumberOfParallelInstances (),
287
+ prefixesToAbort ,
288
+ startCheckpointId ,
289
+ aborter );
247
290
}
248
291
}
0 commit comments