Open thetumbled opened 1 year ago
The PIP number is duplicated with https://github.com/apache/pulsar/issues/19705
I am not sure that PIP solves some problems; let's see the above process:
txn1
to Committing
status then broker downtxn1
status in a looptxn1
timeout, the client will think this transaction has been committed fail.txn1
to committed
statusin this situation, we still cannot guarantee that messages will not be sent repeat
I am not sure that PIP solves some problems; let's see the above process:
- client sends a commit request to TC
- TC changed
txn1
toCommitting
status then broker down- the client will check the
txn1
status in a loop- when the
txn1
timeout, the client will think this transaction has been committed fail.- broker restart, changed
txn1
tocommitted
statusin this situation, we still cannot guarantee that messages will not be sent repeat
After client commit txn1
, txn1
will not be set as TIME_OUT
. Only when the txn1
be in OPEN
state can it be set to TIME_OUT
state.
org.apache.pulsar.client.impl.transaction.TransactionImpl#run
There may be situation similar to your concerns. For example, if broker take too much time to restart and result into long unavailable time, the client will throw following exceptions:
2023-03-08T18:33:12,415+0800 [pulsar-client-internal-17-1] ERROR org.apache.pulsar.testclient.PerformanceProducer - Commit transaction:(7,206446) failed with exception :
java.util.concurrent.CompletionException: org.apache.pulsar.client.api.PulsarClientException$TimeoutException: Could not get response from transaction meta store within given timeout.
As client will wait for the response to the commit request for at most 30s default, if the unavailable time is longer than 30s, the client will throw exceptions above. At this situation, the client could not know the correct status of transaction, so it should retry to commit the transaction (query the state of txn1
).
I have implement such logic in PerformanceProducer
. https://github.com/apache/pulsar/pull/19781
if (arguments.isEnableTxnTest) {
int retryTimes = arguments.retryTimes;
if (isTimeOutException(exception)) {
for (int i = 0; i < retryTimes; i++) {
try {
transaction.commit().get();
handleTxnOnCommitted(transaction);
break;
} catch (Exception e) {
if (!isTimeOutException(e)) {
break;
}
}
}
}
if (transaction.getState() != Transaction.State.COMMITTED) {
totalSent.add(-handleTxnOnAborted(transaction, messageFormatter,
arguments.baseDirToSaveResendTxnData));
}
}
In most of time, the unavailable time is limited, so retrying policy is effective, users can control the max times to retry.
I wonder whether the retrying logic should be implemented into the TransactionImpl
instead of implemented by users themselves.
- There may be situation similar to your concerns. For example, if broker take too much time to restart and result into long unavailable time, the client will throw following exceptions:
2023-03-08T18:33:12,415+0800 [pulsar-client-internal-17-1] ERROR org.apache.pulsar.testclient.PerformanceProducer - Commit transaction:(7,206446) failed with exception : java.util.concurrent.CompletionException: org.apache.pulsar.client.api.PulsarClientException$TimeoutException: Could not get response from transaction meta store within given timeout.
if client down, and then client restart also will send the repeat message right? In most of time, the unavailable time is limited, so retrying policy is effective, users can control the max times to retry. I wonder whether the retrying logic should be implemented into the
TransactionImpl
instead of implemented by users themselves.
at the Pulsar Transaction in this time, I think make it for users to implement is better, Because it is best not to add this extra config until we have fully designed it
if client down, and then client restart also will send the repeat message right?
When client shutdown, it should persist unfinished transactions to a local snapshot and reconstruct TransactionImpl
object with snapshot to commit again when restarted, or message duplication will occur.
When client shutdown, it should persist unfinished transactions to a local snapshot and reconstruct
TransactionImpl
object with snapshot to commit again when restarted, or message duplication will occur.
I have a question, if you open a txn then client down, the txn only can wait txn timeout to end right?
I have a question, if you open a txn then client down, the txn only can wait txn timeout to end right?
TransactionImpl
object to query the txn state (that is commit again) to know the correct state of txn.
We need to know whether the transaction is committed successfully in broker. If commit fail, client should resend messages in failed txn with a new txn. If commit successfully, client do not need to resend.I have a question, if you open a txn then client down, the txn only can wait txn timeout to end right?
- If a txn is opend and client shutdown before client commit it , we can just let the txn time out, which is consistent.
I am investigating how to solve the problem of zombie transaction
- However, if this txn have been committed in broker but client do not receive the response and update the txn state before shutdown, we should reconstruct
TransactionImpl
object to query the txn state (that is commit again) to know the correct state of txn. We need to know whether the transaction is committed successfully in broker. If commit fail, client should resend messages in failed txn with a new txn. If commit successfully, client do not need to resend.
I think transaction should have a retention time, timeout is timeout, it can't represent the transaction need to retention. becasue some users don't need to check the transaction status again. So it's better to add a metadata in to the transaction like retention time is better.
If the retention time is 0, when the transaction timeout or ended, the transaction can be removed from the memory and storage directly. If the txn ended, we should check the txn can be removed from memory and storage by retention time.
I am investigating how to solve the problem of zombie transaction
what is the problem of zombie transaction?
I think transaction should have a retention time, timeout is timeout, it can't represent the transaction need to retention. becasue some users don't need to check the transaction status again. So it's better to add a metadata in to the transaction like retention time is better. If the retention time is 0, when the transaction timeout or ended, the transaction can be removed from the memory and storage directly. If the txn ended, we should check the txn can be removed from memory and storage by retention time.
TransactionNotFound
exceptions. Solution1 persist txnMeta for transactionTimeout time.
is that, client may shutdown for long time, and the txnMeta
have been removed from broker. For example, set transactionTimeout
to 10s, and client shutdown for 20s, client will meet TransactionNotFound
exceptions when restarted. But with retention mechanism, user can set retention time to 30s based on shutdown time 20s, so client can access to the txnMeta and will not throw TransactionNotFound
exceptions.@thetumbled hi, I discussed with penghui, he said that because the txn in TopicTransactionBuffer has been recorded aborts txn(if transaction retention in tc, tc use memory will increase by the txn retention), so may we should continue to discuss until the solution is perfect
The issue had no activity for 30 days, mark with Stale label.
The issue had no activity for 30 days, mark with Stale label.
Motivation
Message queues such as Kafka and Pulsar can only guarantee the exactly-once semantics provided by the transaction feature under the specific use scenario of
consume-transform-produce
pattern, that is, a transaction contains both production and consumption. The operations in the transaction include the production on the sink side and the offset submission on the source side. Using the atomicity of the transaction, these two operations are either completed at the same time or not completed at the same time. It does not need to worry about whether the transaction is committed successfully, because regardless of whether it is successful or not, the end-to-end state is consistent before and after. Therefore, transaction feature implemented by Kafka and Pulsar only support commit or abort once, and it is illegal to repeatedly submit commit or abort requests afterwards, that is, they do not support the idempotence of commit operations.But in many other use cases, which is different from
consume-transform-produce
pattern, we need to know the accurate state of the transaction after the commit operation is submitted. For example,produce-only
, the transaction only contains the production operation, and the offset submission operation is not included, which is simillar to RocketMQ.Two-Phase Commit
protocol implemented by Flink itself. When connecting to an external system, Flink has requirements for external system to ensure the exact once semantics:The details can be found in the following link: https://www.ververica.com/blog/end-to-end-exactly-once-processing-apache-flink-apache-kafka
Though Kafka do not support for idempotence of commit operations, but Flink-Kafka-Connector do some tricks to achieve the idempotence of commit operations for the last transaction, so that Flink+Kafka can guarantee the exactly-once semantics in most of the cases, but still with some risks.
But for Pulsar, it is impossible to achieve any idempotence of commit operations currently, because the implementation of transaction in pulsar is quite different from kafka. I have post a blog to analyze the difference between Pulsar and Kafka. https://blog.csdn.net/m0_43406494/article/details/130344399
Conclusion: Without the idempotence of commit operations, we cannot shift the services from Flink+Kafka to Flink+Pulsar.
Analysis
The main challenge to make commit and abort operation idempotent is that, broker will remove txnMeta from
txnMetaMap
as soon as the transaction is committed or aborted successfully. As there are multiple commit/abort requests trigger by client or broker itself due to transaction time out, the latter request will meetTransactionNotFound
exception because broker do not hold thetxnMeta
corresponding transaction.Goal
TransactionMetadataPreserver
to store the terminated transaction metadata which is a component of TC.TrsansactionNotFound
exception, we will query theTransactionMetadataPreserver
to know the state of the transaction.clientName
to the transaction, andTransactionMetadataPreserver
will preserveTransactionMetaPersistCount
number of transaction metadata for each client.API Changes
message CommandEndTxn { required uint64 request_id = 1; optional uint64 txnid_least_bits = 2 [default = 0]; optional uint64 txnid_most_bits = 3 [default = 0]; optional TxnAction txn_action = 4; optional string client_name = 5; }
message TransactionMetadataEntry { ... optional string clientName = 13; }
enum ServerError { ... TransactionPreserverClosed = 26; // Transaction metadata preserver is closed }
@ApiModelProperty( name = "clientName", value = "Client name that is used to save transaction metadata." ) private String clientName;