Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update transaction documents #804

Merged
merged 6 commits into from
Jan 19, 2023
Merged

Update transaction documents #804

merged 6 commits into from
Jan 19, 2023

Conversation

brfrn169
Copy link
Collaborator

@brfrn169 brfrn169 commented Jan 18, 2023

This PR updates (and improves) the transaction documents. Please take a look!

Copy link
Contributor

@Torch3333 Torch3333 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, thank you!
I just left a minor suggestion.

docs/api-guide.md Outdated Show resolved Hide resolved
brfrn169 and others added 2 commits January 19, 2023 09:47
Co-authored-by: Vincent Guilpain <vincent.guilpain@scalar-labs.com>
@@ -69,8 +69,8 @@ public interface DistributedTransaction extends TransactionCrudOperable {
/**
* Commits a transaction.
*
* @throws CommitConflictException if conflicts happened. You can retry the transaction in this
* case
* @throws CommitConflictException if conflicts happened. You can retry the same transaction from
Copy link
Contributor

@komamitsu komamitsu Jan 19, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the same transaction sounds the previously failed and rollbacked transaction to me. "the same operations in another transaction" or something would be less confusing?

Copy link
Contributor

@komamitsu komamitsu Jan 19, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I googled with "retry the transaction" keyword and it seems very common. So, probably okay. Never mind.

Copy link
Collaborator Author

@brfrn169 brfrn169 Jan 19, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm wondering if it could be better to remove same. What do you think?

Suggested change
* @throws CommitConflictException if conflicts happened. You can retry the same transaction from
* @throws CommitConflictException if conflicts happened. You can retry the transaction from

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, retry the transaction doesn't sound the previously failed transaction itself compared to retry the same transaction to me.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you! I'll fix it that way.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@komamitsu Fixed in b512a20. Please take a look again when you have time!

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💯

// Aborting the transaction fails. You can log it here
tx.rollback();
} catch (RollbackException ex) {
// Rolling back the transaction fails. You can log it here
Copy link
Contributor

@komamitsu komamitsu Jan 19, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[trivial]

Rolling back the transaction fails

failed sounds a bit better?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 1041dda.

int retryCount = 0;

while (true) {
if (retryCount > 0) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[trivial] Incrementing the counter here might be more robust considering future modifications of the exception handling blocks below (e.g. developers notice CrudException should be retried and they change return to break without incrementing the counter)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 1041dda.


The APIs for CRUD operations (`get()`/`scan()`/`put()`/`delete()`/`mutate()`) could throw `CrudException` and `CrudConflictException`.
If you catch `CrudException`, it indicates some failure (e.g., database failure and network error) happens during a transaction, so you should cancel the transaction or retry the transaction after the failure/error is fixed.
If you catch `CrudConflictException`, it indicates a conflict occurs during the transaction so that you can retry the transaction from the beginning, preferably with well-adjusted exponential backoff based on your application and environment.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

while (true) {
if (retryCount > 0) {
// Retry the transaction three times maximum in this sample code
if (retryCount == 3) {
Copy link
Contributor

@komamitsu komamitsu Jan 19, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[trivial] retryCount >= 3 would be a little bit more robust

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 1041dda.

If you catch `UnknownTransactionStatusException`, you are not sure if the transaction succeeds or not.
In such a case, you need to check if the transaction is committed successfully or not and retry it if it fails.
How to identify a transaction status is delegated to users.
You may want to create a transaction status table and update it transactionally with other application data so that you can get the status of a transaction from the status table.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't really get this. Could you tell me a simple example?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is explained in the 3rd way in the following article (Japanese):
http://nippondanji.blogspot.com/2009/02/blog-post_23.html

You can have a status (or log) table and insert a record into this table in addition to the usual operations in a transaction. And when you get UnknownTransactionStatusException and want to check if the transaction is really committed, you can check if the record of the status table exists or not. If the record exists, the transaction has been successfully committed.

Copy link
Contributor

@komamitsu komamitsu Jan 19, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great! Thank you for sharing it.

The blog post seems really helpful for our customers as I think it's hard for them to come up with the idea without the details. How about adding a link to similar articles here? I wish if the blog post were written in English...

Copy link
Collaborator Author

@brfrn169 brfrn169 Jan 19, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about adding a link to similar articles here? I with if the blog post were written in English...

@feeblefakie What do you think? Are there any good articles written in English about that?

Copy link
Contributor

@komamitsu komamitsu Jan 19, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

BTW (a bit off-topic), do you think it would be great if ScalarDB maintains this kind of transaction history in coordinator.history table or something instead of asking application developers to manage that? ScalarDB error handling can check the table when trying to throw UnknownTransactionStatusException.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about adding a link to similar articles here?

Sorry, I didn't find good ones...

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

coordinator.history

Ah, checking coordinator.state might be enough. But the point in this case is that maybe something wrong with the underlying database is happening and it's likely ScalarDB can't access the table... 🤔

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[off-topic]

This is explained in the 3rd way in the following article (Japanese):
http://nippondanji.blogspot.com/2009/02/blog-post_23.html

Just remembered I did the same way before.
https://github.com/treasure-data/digdag/blob/42ea7cb71270957ea6424c2df88dd7c65d248789/digdag-standards/src/main/java/io/digdag/standards/operator/redshift/RedshiftConnection.java#L297-L303 The workflow operator for Redshift does CRUD in the user's database and implicitly inserts a status log record to a secret table in the same transaction

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, checking coordinator.state might be enough. But the point in this case is that maybe something wrong with the underlying database is happening and it's likely ScalarDB can't access the table... 🤔

Yes, we have coordinator.state which has a state record for each transaction, and we can check the record to see if the transaction is committed or not. And we actually expose it through DistributedTransactionManager.getState().

However, I would like to deprecate (and remove) this API in the future because of the following reasons:

  1. The coordinator.state table is an implementation-specific thing (the Consensus Commit transaction has it, but the JDBC transaction doesn't have it).
  2. We don't want to keep the state records in the coordinator.state table permanently. After successfully committing or rolling back a transaction, we can essentially remove the state record of the transaction (except for the getState() reason). So in the future, I want to remove a state record after the transaction is complete.
  3. We plan to implement the one-phase commit optimization. In this optimization, we don't need to write the state record to the coordinator.state table.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see. Regarding 1, I don't think we need to expose it to users as far as we can call it internally considering the mode. As for 2 and 3, it would matter. Also, as I mentioned above, automatically internal checking state (or history) table doesn't work when underlying database isn't working fine. So, it's difficult to suggest the best way to handle this kind of situation. I think this PR overall looks good. Probably we can get feedbacks from customers about this section in the future and let's discuss it deeply again.

Copy link
Contributor

@komamitsu komamitsu left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM! 👍

Copy link
Contributor

@feeblefakie feeblefakie left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Found one typo, but other than that, LGTM! Thank you!

@@ -69,8 +69,8 @@ public interface DistributedTransaction extends TransactionCrudOperable {
/**
* Commits a transaction.
*
* @throws CommitConflictException if conflicts happened. You can retry the transaction in this
* case
* @throws CommitConflictException if a transaction conflict occurs, You can retry the transaction
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
* @throws CommitConflictException if a transaction conflict occurs, You can retry the transaction
* @throws CommitConflictException if a transaction conflict occurs. You can retry the transaction

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah it's not typo actually. The format @throws XXXException if ... is frequently used in this project.

@brfrn169 brfrn169 merged commit 5318515 into master Jan 19, 2023
@brfrn169 brfrn169 deleted the update-transaction-documents branch January 19, 2023 07:15
@brfrn169 brfrn169 added this to Done in 3.9.0 Apr 27, 2023
brfrn169 added a commit that referenced this pull request Apr 27, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
No open projects
3.9.0
Done
Development

Successfully merging this pull request may close these issues.

None yet

4 participants