|
21 | 21 | #include "stats.h" |
22 | 22 | #include "stored_value_factories.h" |
23 | 23 |
|
| 24 | +#include <memcached/3rd_party/folly/lang/Assume.h> |
24 | 25 | #include <phosphor/phosphor.h> |
25 | 26 | #include <platform/compress.h> |
26 | 27 |
|
@@ -323,7 +324,7 @@ void HashTable::commit(const HashTable::HashBucketLock& hbl, StoredValue& v) { |
323 | 324 | } |
324 | 325 |
|
325 | 326 | // Change the pending item to Committed. |
326 | | - v.setCommitted(); |
| 327 | + v.setCommitted(CommittedState::CommittedViaPrepare); |
327 | 328 |
|
328 | 329 | // Update stats for pending -> Committed item |
329 | 330 | valueStats.epilogue(pendingPreProps, &v); |
@@ -372,9 +373,7 @@ HashTable::UpdateResult HashTable::unlocked_updateStoredValue( |
372 | 373 |
|
373 | 374 | return {status, &v}; |
374 | 375 | } |
375 | | - |
376 | | - throw std::logic_error( |
377 | | - "HashTable::unlocked_updateStoredValue: unreachable"); |
| 376 | + folly::assume_unreachable(); |
378 | 377 | } |
379 | 378 |
|
380 | 379 | StoredValue* HashTable::unlocked_addNewStoredValue(const HashBucketLock& hbl, |
@@ -529,19 +528,54 @@ HashTable::unlocked_replaceByCopy(const HashBucketLock& hbl, |
529 | 528 | return {values[hbl.getBucketNum()].get().get(), std::move(releasedSv)}; |
530 | 529 | } |
531 | 530 |
|
532 | | -void HashTable::unlocked_softDelete(const std::unique_lock<std::mutex>& htLock, |
533 | | - StoredValue& v, |
534 | | - bool onlyMarkDeleted, |
535 | | - DeleteSource delSource) { |
536 | | - const auto preProps = valueStats.prologue(&v); |
| 531 | +HashTable::DeleteResult HashTable::unlocked_softDelete( |
| 532 | + const HashBucketLock& hbl, |
| 533 | + StoredValue& v, |
| 534 | + bool onlyMarkDeleted, |
| 535 | + DeleteSource delSource, |
| 536 | + HashTable::SyncDelete syncDelete) { |
| 537 | + switch (v.getCommitted()) { |
| 538 | + case CommittedState::Pending: |
| 539 | + // Cannot update a SV if it's a Pending item. |
| 540 | + return {DeletionStatus::IsPendingSyncWrite, nullptr}; |
537 | 541 |
|
538 | | - if (onlyMarkDeleted) { |
539 | | - v.markDeleted(delSource); |
540 | | - } else { |
541 | | - v.del(delSource); |
542 | | - } |
| 542 | + case CommittedState::CommittedViaMutation: |
| 543 | + case CommittedState::CommittedViaPrepare: |
| 544 | + if (syncDelete == SyncDelete::Yes) { |
| 545 | + // Logically we /can/ delete a non-Pending StoredValue with a |
| 546 | + // SyncDelete, however internally this is implemented as a separate |
| 547 | + // (new) StoredValue object for the Pending delete. |
| 548 | + |
| 549 | + // Clone the existing StoredValue (for it's key, CAS, other |
| 550 | + // metadata), set the opcode to pending and call del() to prepare it |
| 551 | + // as a deleted item. The existing StoredValue keeps the same state |
| 552 | + // until we Commit the pending one. |
| 553 | + auto pendingDel = valFact->copyStoredValue( |
| 554 | + v, std::move(values[hbl.getBucketNum()])); |
| 555 | + pendingDel->setCommitted(CommittedState::Pending); |
| 556 | + pendingDel->del(delSource); |
| 557 | + |
| 558 | + // Adding a new item into the HashTable; update stats. |
| 559 | + const auto emptyProperties = valueStats.prologue(nullptr); |
| 560 | + valueStats.epilogue(emptyProperties, pendingDel.get().get()); |
| 561 | + values[hbl.getBucketNum()] = std::move(pendingDel); |
| 562 | + |
| 563 | + return {DeletionStatus::Success, |
| 564 | + values[hbl.getBucketNum()].get().get()}; |
| 565 | + } |
| 566 | + // non-syncDelete requests, can directly update existing SV. |
| 567 | + const auto preProps = valueStats.prologue(&v); |
543 | 568 |
|
544 | | - valueStats.epilogue(preProps, &v); |
| 569 | + if (onlyMarkDeleted) { |
| 570 | + v.markDeleted(delSource); |
| 571 | + } else { |
| 572 | + v.del(delSource); |
| 573 | + } |
| 574 | + |
| 575 | + valueStats.epilogue(preProps, &v); |
| 576 | + return {DeletionStatus::Success, &v}; |
| 577 | + } |
| 578 | + folly::assume_unreachable(); |
545 | 579 | } |
546 | 580 |
|
547 | 581 | StoredValue* HashTable::unlocked_find(const DocKey& key, |
|
0 commit comments