@@ -120,76 +120,6 @@ public class TransactionManager {
120
120
private final Set <TopicPartition > newPartitionsInTransaction ;
121
121
private final Set <TopicPartition > pendingPartitionsInTransaction ;
122
122
private final Set <TopicPartition > partitionsInTransaction ;
123
-
124
- /**
125
- * During its normal course of operations, the transaction manager transitions through different internal
126
- * states (i.e. by updating {@link #currentState}) to one of those defined in {@link State}. These state transitions
127
- * result from actions on one of the following classes of threads:
128
- *
129
- * <ul>
130
- * <li><em>Application</em> threads that invokes {@link Producer} API calls</li>
131
- * <li><em>{@link Sender}</em> thread operations</li>
132
- * </ul>
133
- *
134
- * When an invalid state transition is detected during execution on an <em>application</em> thread, the
135
- * {@link #currentState} is <em>not updated</em> and an {@link IllegalStateException} is thrown. This gives the
136
- * application the opportunity to fix the issue without permanently poisoning the state of the
137
- * transaction manager. The {@link Producer} API calls that perform a state transition include:
138
- *
139
- * <ul>
140
- * <li>{@link Producer#initTransactions()} calls {@link #initializeTransactions()}</li>
141
- * <li>{@link Producer#beginTransaction()} calls {@link #beginTransaction()}</li>
142
- * <li>{@link Producer#commitTransaction()}} calls {@link #beginCommit()}</li>
143
- * <li>{@link Producer#abortTransaction()} calls {@link #beginAbort()}
144
- * </li>
145
- * <li>{@link Producer#sendOffsetsToTransaction(Map, ConsumerGroupMetadata)} calls
146
- * {@link #sendOffsetsToTransaction(Map, ConsumerGroupMetadata)}
147
- * </li>
148
- * <li>{@link Producer#send(ProducerRecord)} (and its variants) calls
149
- * {@link #maybeAddPartition(TopicPartition)} and
150
- * {@link #maybeTransitionToErrorState(RuntimeException)}
151
- * </li>
152
- * </ul>
153
- *
154
- * <p/>
155
- *
156
- * The {@link Producer} is implemented such that much of its work delegated to and performed asynchronously on the
157
- * <em>{@link Sender}</em> thread. This includes record batching, network I/O, broker response handlers, etc. If an
158
- * invalid state transition is detected in the <em>{@link Sender}</em> thread, in addition to throwing an
159
- * {@link IllegalStateException}, the transaction manager intentionally "poisons" itself by setting its
160
- * {@link #currentState} to {@link State#FATAL_ERROR}, a state from which it cannot recover.
161
- *
162
- * <p/>
163
- *
164
- * It's important to prevent possible corruption when the transaction manager has determined that it is in a
165
- * fatal state. Subsequent transaction operations attempted via either the <em>application</em> or the
166
- * <em>{@link Sender}</em> thread should fail. This is achieved when these operations invoke the
167
- * {@link #maybeFailWithError()} method, as it causes a {@link KafkaException} to be thrown, ensuring the stated
168
- * transactional guarantees are not violated.
169
- *
170
- * <p/>
171
- *
172
- * See KAFKA-14831 for more detail.
173
- */
174
- public interface InvalidStateTransitionAttemptHandler {
175
-
176
- /**
177
- * Callback to determine if the transaction manager's {@link #currentState} should be set to
178
- * {@link State#FATAL_ERROR} to prevent possible transaction corruption.
179
- *
180
- * @return {@code true} to set state to {@link State#FATAL_ERROR} before throwing an exception,
181
- * {@code false} to throw an exception without first changing the state
182
- */
183
- boolean isFatalState ();
184
- }
185
-
186
- /**
187
- * The default logic is to poison the internal {@link #currentState state} if an invalid state transition is
188
- * attempted on the {@link Sender.SenderThread sender's thread}.
189
- */
190
- public static final InvalidStateTransitionAttemptHandler DEFAULT_INVALID_STATE_TRANSITION_ATTEMPT_HANDLER = () -> Thread .currentThread () instanceof Sender .SenderThread ;
191
- private final InvalidStateTransitionAttemptHandler invalidStateTransitionAttemptHandler ;
192
-
193
123
private PendingStateTransition pendingTransition ;
194
124
195
125
// This is used by the TxnRequestHandlers to control how long to back off before a given request is retried.
@@ -274,15 +204,6 @@ public TransactionManager(final LogContext logContext,
274
204
final int transactionTimeoutMs ,
275
205
final long retryBackoffMs ,
276
206
final ApiVersions apiVersions ) {
277
- this (logContext , transactionalId , transactionTimeoutMs , retryBackoffMs , apiVersions , DEFAULT_INVALID_STATE_TRANSITION_ATTEMPT_HANDLER );
278
- }
279
-
280
- public TransactionManager (final LogContext logContext ,
281
- final String transactionalId ,
282
- final int transactionTimeoutMs ,
283
- final long retryBackoffMs ,
284
- final ApiVersions apiVersions ,
285
- final InvalidStateTransitionAttemptHandler invalidStateTransitionAttemptHandler ) {
286
207
this .producerIdAndEpoch = ProducerIdAndEpoch .NONE ;
287
208
this .transactionalId = transactionalId ;
288
209
this .log = logContext .logger (TransactionManager .class );
@@ -299,7 +220,6 @@ public TransactionManager(final LogContext logContext,
299
220
this .retryBackoffMs = retryBackoffMs ;
300
221
this .txnPartitionMap = new TxnPartitionMap (logContext );
301
222
this .apiVersions = apiVersions ;
302
- this .invalidStateTransitionAttemptHandler = invalidStateTransitionAttemptHandler ;
303
223
}
304
224
305
225
public synchronized TransactionalRequestResult initializeTransactions () {
@@ -1086,7 +1006,7 @@ private void transitionTo(State target, RuntimeException error) {
1086
1006
String message = idString + "Invalid transition attempted from state "
1087
1007
+ currentState .name () + " to state " + target .name ();
1088
1008
1089
- if (invalidStateTransitionAttemptHandler . isFatalState ()) {
1009
+ if (shouldSetToFatalErrorState ()) {
1090
1010
currentState = State .FATAL_ERROR ;
1091
1011
lastError = new IllegalStateException (message );
1092
1012
throw lastError ;
@@ -1109,6 +1029,63 @@ private void transitionTo(State target, RuntimeException error) {
1109
1029
currentState = target ;
1110
1030
}
1111
1031
1032
+ /**
1033
+ * During its normal course of operations, the transaction manager transitions through different internal
1034
+ * states (i.e. by updating {@link #currentState}) to one of those defined in {@link State}. These state transitions
1035
+ * result from actions on one of the following classes of threads:
1036
+ *
1037
+ * <ul>
1038
+ * <li><em>Application</em> threads that invokes {@link Producer} API calls</li>
1039
+ * <li><em>{@link Sender}</em> thread operations</li>
1040
+ * </ul>
1041
+ *
1042
+ * When an invalid state transition is detected during execution on an <em>application</em> thread, the
1043
+ * {@link #currentState} is <em>not updated</em> and an {@link IllegalStateException} is thrown. This gives the
1044
+ * application the opportunity to fix the issue without permanently poisoning the state of the
1045
+ * transaction manager. The {@link Producer} API calls that perform a state transition include:
1046
+ *
1047
+ * <ul>
1048
+ * <li>{@link Producer#initTransactions()} calls {@link #initializeTransactions()}</li>
1049
+ * <li>{@link Producer#beginTransaction()} calls {@link #beginTransaction()}</li>
1050
+ * <li>{@link Producer#commitTransaction()}} calls {@link #beginCommit()}</li>
1051
+ * <li>{@link Producer#abortTransaction()} calls {@link #beginAbort()}
1052
+ * </li>
1053
+ * <li>{@link Producer#sendOffsetsToTransaction(Map, ConsumerGroupMetadata)} calls
1054
+ * {@link #sendOffsetsToTransaction(Map, ConsumerGroupMetadata)}
1055
+ * </li>
1056
+ * <li>{@link Producer#send(ProducerRecord)} (and its variants) calls
1057
+ * {@link #maybeAddPartition(TopicPartition)} and
1058
+ * {@link #maybeTransitionToErrorState(RuntimeException)}
1059
+ * </li>
1060
+ * </ul>
1061
+ *
1062
+ * <p/>
1063
+ *
1064
+ * The {@link Producer} is implemented such that much of its work delegated to and performed asynchronously on the
1065
+ * <em>{@link Sender}</em> thread. This includes record batching, network I/O, broker response handlers, etc. If an
1066
+ * invalid state transition is detected in the <em>{@link Sender}</em> thread, in addition to throwing an
1067
+ * {@link IllegalStateException}, the transaction manager intentionally "poisons" itself by setting its
1068
+ * {@link #currentState} to {@link State#FATAL_ERROR}, a state from which it cannot recover.
1069
+ *
1070
+ * <p/>
1071
+ *
1072
+ * It's important to prevent possible corruption when the transaction manager has determined that it is in a
1073
+ * fatal state. Subsequent transaction operations attempted via either the <em>application</em> or the
1074
+ * <em>{@link Sender}</em> thread should fail. This is achieved when these operations invoke the
1075
+ * {@link #maybeFailWithError()} method, as it causes a {@link KafkaException} to be thrown, ensuring the stated
1076
+ * transactional guarantees are not violated.
1077
+ *
1078
+ * <p/>
1079
+ *
1080
+ * See KAFKA-14831 for more detail.
1081
+ *
1082
+ * @return {@code true} to set state to {@link State#FATAL_ERROR} before throwing an exception,
1083
+ * {@code false} to throw an exception without first changing the state
1084
+ */
1085
+ protected boolean shouldSetToFatalErrorState () {
1086
+ return Thread .currentThread () instanceof Sender .SenderThread ;
1087
+ }
1088
+
1112
1089
private void ensureTransactional () {
1113
1090
if (!isTransactional ())
1114
1091
throw new IllegalStateException ("Transactional method invoked on a non-transactional producer." );
0 commit comments