Skip to content
This repository has been archived by the owner on Jan 3, 2024. It is now read-only.

Make MP mark_complete a write transaction #225

Merged
merged 2 commits into from
Oct 13, 2023

Conversation

irq0
Copy link
Member

@irq0 irq0 commented Oct 11, 2023

Make SQLiteMultipart::mark_complete a write transaction so that the
default retry timeout handler kicks in.

Fixes: https://github.com/aquarist-labs/s3gw/issues/710#issuecomment-1757011216

Checklist

  • Tracker (select at least one)
    • References tracker ticket
    • Very recent bug; references commit where it was introduced
    • New feature (ticket optional)
    • Doc update (no ticket needed)
    • Code cleanup (no ticket needed)
  • Documentation (select at least one)
    • Updates relevant documentation
    • No doc update is appropriate
  • Tests (select at least one)

Marcel Lauhoff added 2 commits October 11, 2023 10:42
Remove SQLiteMultipart::mark_complete(const std::string& upload_id), a
unused variant of mark_complete(upload_id, duplicate)

Signed-off-by: Marcel Lauhoff <[email protected]>
Move the duplicate check (MP marked completed or later) untill after
the update that puts it into COMPLETE. (Ideally this would be one
SQLite statement with 'returning').

This way the transaction is a write transaction and doesn't require a
read to write lock upgrade and the default retry handler catches.

Signed-off-by: Marcel Lauhoff <[email protected]>
Comment on lines +362 to +372
if (storage.changes() == 0) {
const auto state = storage.select(
columns(&DBMultipart::state),
where(is_equal(&DBMultipart::upload_id, upload_id))
);
if (state.size() == 0) {
return false;
}
*duplicate = (std::get<0>(state[0]) >= MultipartState::COMPLETE);
} else {
*duplicate = false;
Copy link
Member

Choose a reason for hiding this comment

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

To clarify, this only makes the operation potentially write-only, right? If the conditions are met, this will still be a read-write transaction. Or am I misunderstanding the point?

Copy link
Member Author

Choose a reason for hiding this comment

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

It is a write transaction, because it starts with an update. Write transactions may also do reads. Read-then-write transactions require an upgrade to a write transaction. Starting the write transaction directly is subject to the busy timeout handler. The read-then-write in many of our cases not. They may trigger the deadlock prevention logic if multiple concurrent connections already have the read lock.

This gives us two options to get transaction retries: the busy transaction retry utility or starting with writes to use the busy timeout handler.

Since the update is the important bit here and not dependent on a previous select, making it a write transaction makes most sense I think.

Related docs:
https://www.sqlite.org/lang_transaction.html#:~:text=2.1.%20Read%20transactions%20versus%20write%20transactions

Copy link
Member

Choose a reason for hiding this comment

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

Yep, makes sense. Thanks! :)

@irq0 irq0 merged commit 5a7aa0d into aquarist-labs:s3gw Oct 13, 2023
7 checks passed
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
area/rgw-sfs RGW & SFS related
Projects
Status: Done
Development

Successfully merging this pull request may close these issues.

[BUG] concurrent multipart uploads occasionally raises unhandled exception
2 participants