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

Introduce MDel do the batch delete and use MultiLockGuard guarantee atomicity #1712

Merged
merged 14 commits into from
Sep 4, 2023

Conversation

enjoy-binbin
Copy link
Member

@enjoy-binbin enjoy-binbin commented Aug 30, 2023

Previously, the DEL/UNLINK commands looped through each key to
delete a single key. We need to change to batch delete (write),
and also the original implementation did not lock the keys and
will break atomicity.

This PR introduces a new Mdel function, which uses MultiLockGuard
to lock all the keys to guarantee atomicity, and use batch to do
the final delete (write).

Now it will be only used in DEL / UNLINKE commands. Noticed that
our DEL / UNLINK test coverage was not sufficient, added some simple
coverage tests to cover it.

Fixes for DEL / UNLINK issues in #1692.

unrelated change: in Exists for loop Get, return an execution error
when !s.ok() && !s.IsNotFound() without continuing the operation
(because it will be some internal RocksDB error).

…tomicity

Previously, the DEL/UNLINK commands looped through each key to
delete a single key. We need to change to batch delete (write),
and also the original implementation did not lock the keys and
will break atomicity.

This PR introduces a new Mdel function, which uses MultiLockGuard
to lock all the keys to guarantee atomicity, and use batch to do
the final delete (write).

Now it will be only used in DEL / UNLINKE commands. Noticed that
our DEL / UNLINK test coverage was not sufficient, added some simple
coverage tests to cover it.

Fixes for DEL / UNLINK issues in 1692.
@enjoy-binbin enjoy-binbin linked an issue Aug 30, 2023 that may be closed by this pull request
2 tasks
@enjoy-binbin enjoy-binbin requested a review from git-hulk August 30, 2023 07:52
torwig
torwig previously approved these changes Aug 30, 2023
@git-hulk
Copy link
Member

We may have the deadlock in ZSet::InterStore(as well as other similar APIs) if the dst key was inside the source key list since the OverWrite function will also lock the dst key: https://github.com/apache/kvrocks/blob/unstable/src/types/redis_zset.cc#L635

This issue was found by TSAN in CI: https://github.com/apache/kvrocks/actions/runs/6022265431/job/16338859035?pr=1712

@torwig
Copy link
Contributor

torwig commented Aug 30, 2023

@git-hulk Good point.

@torwig
Copy link
Contributor

torwig commented Aug 30, 2023

I guess we should lock all the keys including the destination inside the ZSet::InterStore and similar methods (right now we lock the destination inside the Overwite method). Will it work?

@git-hulk
Copy link
Member

I guess we should lock all the keys including the destination inside the ZSet::InterStore and similar methods (right now we lock the destination inside the Overwite method). Will it work?

Yes, I think it can resolve this issue.

@git-hulk
Copy link
Member

I submit another issue to track this since it's related but NOT introduced by this PR. #1715

@enjoy-binbin
Copy link
Member Author

Or can it be ok without locking the dst key? since we are doing a overwrite

@torwig
Copy link
Contributor

torwig commented Aug 30, 2023

@enjoy-binbin I think we need to serialize write operations on the key to avoid "lost updates". However, you made me rethink the previous sentence because we deal with overwriting :)

@git-hulk
Copy link
Member

Is it possible to lock the dst key only and use the read snapshot like SDIFFSTORE/SINTERSTORE?

@enjoy-binbin
Copy link
Member Author

Is it possible to lock the dst key only and use the read snapshot like SDIFFSTORE/SINTERSTORE?

@git-hulk can you describe this a bit more? (for example posting a pseudo code)

@git-hulk
Copy link
Member

git-hulk commented Sep 1, 2023

Is it possible to lock the dst key only and use the read snapshot like SDIFFSTORE/SINTERSTORE?

@git-hulk can you describe this a bit more? (for example posting a pseudo code)

Sure. For now, SDIFFSTORE is implemented in two steps:

  • The first step is to calculate the diff results without locking source keys
  • Then overwrite the dst key with locking the dst key

Please refer https://github.com/apache/kvrocks/blob/unstable/src/types/redis_set.cc#L433

@enjoy-binbin
Copy link
Member Author

enjoy-binbin commented Sep 1, 2023

actually now we will lock the source keys. it was added in #1700, maybe we should remove the source keys locks?

rocksdb::Status Set::Diff(const std::vector<Slice> &keys, std::vector<std::string> *members) {
  std::vector<std::string> lock_keys;
  lock_keys.reserve(keys.size());
  for (const auto key : keys) {
    std::string ns_key = AppendNamespacePrefix(key);
    lock_keys.emplace_back(std::move(ns_key));
  }
  MultiLockGuard guard(storage_->GetLockManager(), lock_keys);

edit: ohh, i think i got your point. maybe we should split InterStore to two function, one do the inter and the other one do the store, just like the Set apis. I wanted to do it (adding a ZINTER command) before, but because we already had a pending #1517

@git-hulk
Copy link
Member

git-hulk commented Sep 1, 2023

Is it possible to lock the dst key only and use the read snapshot like SDIFFSTORE/SINTERSTORE?

@git-hulk can you describe this a bit more? (for example posting a pseudo code)

Sure. For now, SDIFFSTORE is implemented in two steps:

  • The first step is to calculated the diff result without locking source keys
  • Then overwrite the dst key with locking the dst key

actually now we will lock the source keys. it was added in #1700, maybe we should remove the source keys locks?

rocksdb::Status Set::Diff(const std::vector<Slice> &keys, std::vector<std::string> *members) {
  std::vector<std::string> lock_keys;
  lock_keys.reserve(keys.size());
  for (const auto key : keys) {
    std::string ns_key = AppendNamespacePrefix(key);
    lock_keys.emplace_back(std::move(ns_key));
  }
  MultiLockGuard guard(storage_->GetLockManager(), lock_keys);

edit: ohh, i think i got your point. maybe we should split InterStore to two function, one do the inter and the other one do the store, just like the Set apis. I wanted to do it (adding a ZINTER command) before, but because we already had a pending #1517

Great, thank you!

@enjoy-binbin
Copy link
Member Author

i pushed a commit c51975b

split InterStore to two function, one do the inter and the other one do the store, just like the Set apis. to see if the CI can pass

enjoy-binbin added a commit to enjoy-binbin/kvrocks that referenced this pull request Sep 2, 2023
CI TSAN show ZINTERSTORE may has a deadlock after introducing
locks to DEL in apache#1712. In ZSet::InterStore if the dst key was
inside the source key list we may have a deadlock since the
OverWrite function will also lock the dst key.

In this PR, we split Inter in ZSet::InterStore into a separate
function, just like the Set apis.

After this PR, after the CI verification in apache#1712, it can pass
the CI verification now.
@enjoy-binbin
Copy link
Member Author

enjoy-binbin commented Sep 2, 2023

i see the CI is passed, so the split change can fix the deadlock, i am going to submit another PR to make the change, am revert the change in this PR (after we merge the new PR see #1726, we can re-merge it)

@PragmaTwice
Copy link
Member

enjoy-binbin added a commit that referenced this pull request Sep 3, 2023
CI TSAN show ZINTERSTORE may has a deadlock after introducing
locks to DEL in #1712. In ZSet::InterStore if the dst key was
inside the source key list we may have a deadlock since the
OverWrite function will also lock the dst key.

In this PR, we split Inter in ZSet::InterStore into a separate
function, just like the Set apis.

After this PR, after the CI verification in #1712, it can pass
the CI verification now. Closes #1715

This PR also do a saved_cnt cleanup since it is same as members.size().
git-hulk
git-hulk previously approved these changes Sep 3, 2023
mapleFU
mapleFU previously approved these changes Sep 3, 2023
Copy link
Member

@mapleFU mapleFU left a comment

Choose a reason for hiding this comment

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

General LGTM. Just some nits.

src/storage/redis_db.cc Outdated Show resolved Hide resolved
src/storage/redis_db.cc Outdated Show resolved Hide resolved
@enjoy-binbin enjoy-binbin dismissed stale reviews from mapleFU and git-hulk via 998303d September 4, 2023 03:51
src/storage/redis_db.cc Outdated Show resolved Hide resolved
src/storage/redis_db.cc Outdated Show resolved Hide resolved
src/storage/redis_db.cc Outdated Show resolved Hide resolved
Co-authored-by: mwish <1506118561@qq.com>
@enjoy-binbin enjoy-binbin merged commit 91b1d63 into apache:unstable Sep 4, 2023
@enjoy-binbin enjoy-binbin deleted the mdel branch September 4, 2023 07:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Kvrocks can't guarantee atomicity for multiple keys commands
5 participants