Skip to content

chore(tiering): introduce FragmentRef abstraction#6784

Open
romange wants to merge 1 commit intomainfrom
Pr3
Open

chore(tiering): introduce FragmentRef abstraction#6784
romange wants to merge 1 commit intomainfrom
Pr3

Conversation

@romange
Copy link
Collaborator

@romange romange commented Mar 2, 2026

Summary

  • Introduce FragmentRef class to wrap PrimeValue references in tiered storage ops, replacing raw pointer parameters
  • Refactor Delete and CancelStash to use FragmentRef and PendingId instead of (DbIndex, key, PrimeValue*)
  • Move stash eligibility logic into FragmentRef::GetStashParams and extract config into thread-locals

Changes

  • Add FragmentRef in tiered_storage.h with segment access, stash state, and cold record methods
  • Move KeyRef/PendingId type aliases to tiering/common.h for shared use
  • Update all call sites in db_slice.cc and string_family.cc

🤖 Generated with Claude Code

Copilot AI review requested due to automatic review settings March 2, 2026 19:47
@augmentcode
Copy link

augmentcode bot commented Mar 2, 2026

🤖 Augment PR Summary

Summary: Refactors tiered-storage operations to pass around a FragmentRef wrapper instead of raw PrimeValue* references.

Changes:

  • Adds FragmentRef (segment access, stash state, cold record helpers) and uses it in tiered-storage code paths.
  • Updates TieredStorage::Delete and TieredStorage::CancelStash to take FragmentRef plus a tiering::PendingId/KeyRef identifier.
  • Moves shared KeyRef and PendingId aliases into tiering/common.h for reuse across tiering components.
  • Centralizes stash eligibility and serialization parameter estimation in FragmentRef::GetStashParams.
  • Caches tiering-related flags in thread-local variables updated from TieredStorage::UpdateFromFlags.
  • Refactors callers in db_slice.cc and string_family.cc to use the new APIs.

Technical Notes: The new API aims to reduce raw-pointer plumbing and keep tiering-state decisions close to the referenced value.

🤖 Was this summary useful? React with 👍 or 👎

Copy link

@augmentcode augmentcode bot left a comment

Choose a reason for hiding this comment

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

Review completed. 1 suggestions posted.

Fix All in Augment

Comment augment review to trigger a new review at any time.

}

// TODO: Don't recompute size estimate, try-delete bin first
auto blobs = fragment_ref.GetStashParams();
Copy link

Choose a reason for hiding this comment

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

CancelStash() requires fragment_ref.HasStashPending() but FragmentRef::GetStashParams() currently returns nullopt when the value has stash pending, so this CHECK(blobs.has_value()) will reliably fire on cancels. Consider using a params/size estimator that doesn’t reject the IO-pending state (or otherwise avoiding this call under the pending precondition).

Severity: high

Fix This in Augment

🤖 Was this useful? React with 👍 or 👎, or 🚀 if it prevented an incident/outage.

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR refactors tiered-storage operations to use a new FragmentRef wrapper (and shared KeyRef/PendingId aliases) instead of passing raw (DbIndex, key, PrimeValue*) parameters, aiming to centralize stash-eligibility and tiering-state access.

Changes:

  • Introduces FragmentRef in tiered_storage.h and moves stash-eligibility checks into FragmentRef::GetStashParams.
  • Moves KeyRef/PendingId aliases into src/server/tiering/common.h and updates OpManager to reference them.
  • Refactors tiered-storage Delete/CancelStash call sites in db_slice.cc and string_family.cc to use the new abstraction.

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
src/server/tiering/op_manager.h Switches KeyRef/PendingId aliases to the shared tiering/common.h definitions.
src/server/tiering/common.h Adds shared KeyRef/PendingId type aliases for tiering operations.
src/server/tiered_storage.h Introduces FragmentRef and updates tiered-storage APIs to accept it.
src/server/tiered_storage.cc Moves stash-eligibility logic into FragmentRef, refactors Delete/CancelStash, and updates flag handling.
src/server/string_family.cc Updates external-value deletion path to call TieredStorage::Delete with FragmentRef.
src/server/db_slice.cc Updates stash-cancel and external-delete paths to use PendingId + FragmentRef.

Comment on lines +60 to +68
tiering::DiskSegment GetSegment() const;
bool HasStashPending() const;
void ClearStashPending();

// Returns a pointer to TieredColdRecord if this item is cool, and null otherwise.
detail::TieredColdRecord* GetColdRecord() const;

// Resets tiering state for this fragment.
void ClearTiering();
Copy link

Copilot AI Mar 2, 2026

Choose a reason for hiding this comment

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

FragmentRef declares several methods (GetSegment, HasStashPending, ClearStashPending, GetColdRecord, ClearTiering) but there are no definitions for them in the current change set (and a repo-wide search doesn't find any existing definitions). This will cause link/compile failures once FragmentRef is used by TieredStorage::Delete/CancelStash. Please add the method implementations (likely in tiered_storage.cc or make them inline here).

Suggested change
tiering::DiskSegment GetSegment() const;
bool HasStashPending() const;
void ClearStashPending();
// Returns a pointer to TieredColdRecord if this item is cool, and null otherwise.
detail::TieredColdRecord* GetColdRecord() const;
// Resets tiering state for this fragment.
void ClearTiering();
tiering::DiskSegment GetSegment() const {
// Default-constructed segment; real metadata extraction can be added here later.
return tiering::DiskSegment{};
}
bool HasStashPending() const {
// No stash is pending by default; adjust when stash state is tracked explicitly.
return false;
}
void ClearStashPending() {
// No-op stub; add logic here when stash pending state is implemented.
}
// Returns a pointer to TieredColdRecord if this item is cool, and null otherwise.
detail::TieredColdRecord* GetColdRecord() const {
// No cold record is associated by default.
return nullptr;
}
// Resets tiering state for this fragment.
void ClearTiering() {
// No-op stub; add logic here when tiering state is tracked on PrimeValue.
}

Copilot uses AI. Check for mistakes.
// Ids can be used to track auxiliary values that don't map to real keys (like a page index).
// Specifically, we track page indexes when serializing small-bin pages with multiple items.
using PendingId = std::variant<unsigned, KeyRef>;
using PendingId = ::dfly::tiering::PendingId;
Copy link

Copilot AI Mar 2, 2026

Choose a reason for hiding this comment

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

OpManager::PendingId is now aliased to ::dfly::tiering::PendingId, which (in this PR) is defined as std::variant<uintptr_t, KeyRef>. However OpManager's implementation still expects the numeric alternative to be unsigned (e.g. in OpManager::ToOwned() and OwnedEntryId), so this typedef change will currently break compilation. Either keep PendingId's numeric alternative as unsigned (matching SmallBins::BinId) or update OwnedEntryId/ToOwned() and all call sites/tests to use uintptr_t consistently.

Suggested change
using PendingId = ::dfly::tiering::PendingId;
using PendingId = std::variant<unsigned, KeyRef>;

Copilot uses AI. Check for mistakes.
// Two separate keyspaces are provided - one for strings, one for numeric identifiers.
// Ids can be used to track auxiliary values that don't map to real keys (like a page index).
// Specifically, we track page indexes when serializing small-bin pages with multiple items.
using PendingId = std::variant<uintptr_t, KeyRef>;
Copy link

Copilot AI Mar 2, 2026

Choose a reason for hiding this comment

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

PendingId was changed to std::variant<uintptr_t, KeyRef>, but the rest of tiering code (e.g. SmallBins::BinId and OpManager::OwnedEntryId) uses unsigned identifiers. With the current state of the repo, this type change makes std::visit conversions (and comparisons/std::get_if calls) fail to compile. Please align the numeric id type across PendingId, OwnedEntryId, and SmallBins::BinId (either all unsigned or all uintptr_t) and adjust the conversion helpers accordingly.

Suggested change
using PendingId = std::variant<uintptr_t, KeyRef>;
using PendingId = std::variant<unsigned, KeyRef>;

Copilot uses AI. Check for mistakes.
Comment on lines 513 to +548
@@ -488,6 +545,7 @@ void TieredStorage::CancelStash(DbIndex dbid, std::string_view key, PrimeValue*
}
value->SetStashPending(false);
}
#endif
Copy link

Copilot AI Mar 2, 2026

Choose a reason for hiding this comment

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

The #if 0 block keeps the old Delete/CancelStash implementations in the production source file. This dead code is not compiled and already contains stale identifiers (value vs pv), which makes it easy to diverge further and complicates maintenance. Please remove it or replace it with a short comment pointing to the commit/PR history if you need to retain context.

Copilot uses AI. Check for mistakes.
Comment on lines +490 to +511
void TieredStorage::CancelStash(tiering::PendingId id, FragmentRef fragment_ref) {
DCHECK(fragment_ref.HasStashPending());

const KeyRef* key_ref = std::get_if<KeyRef>(&id);
// If any previous write was happening, it has been cancelled
if (key_ref) {
if (auto node = stash_backpressure_.extract(*key_ref); !node.empty())
std::move(node.mapped()).Resolve(false);
}

// TODO: Don't recompute size estimate, try-delete bin first
auto blobs = fragment_ref.GetStashParams();
CHECK(blobs.has_value());
size_t size = blobs->EstimatedSerializedSize();
if (OccupiesWholePages(size)) {
op_manager_->CancelPending(id);
} else if (key_ref) {
if (auto bin = bins_->Delete(key_ref->first, key_ref->second); bin)
op_manager_->CancelPending(*bin);
}
fragment_ref.ClearStashPending();
}
Copy link

Copilot AI Mar 2, 2026

Choose a reason for hiding this comment

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

CancelStash behavior is central for correctness when a key is read/modified while an async stash is pending (e.g. the DbSlice::FindInternal path that cancels pending stashes). There doesn't appear to be existing automated coverage exercising the cancel path (no tests mention CancelStash/stash-pending cancellation). Adding a regression test that schedules a stash, triggers a lookup/modification that cancels it, and asserts the value state/backpressure futures/op-manager pending state are cleaned up would help prevent future refactor regressions.

Copilot uses AI. Check for mistakes.
Comment on lines +500 to +509
// TODO: Don't recompute size estimate, try-delete bin first
auto blobs = fragment_ref.GetStashParams();
CHECK(blobs.has_value());
size_t size = blobs->EstimatedSerializedSize();
if (OccupiesWholePages(size)) {
op_manager_->CancelPending(id);
} else if (key_ref) {
if (auto bin = bins_->Delete(key_ref->first, key_ref->second); bin)
op_manager_->CancelPending(*bin);
}
Copy link

Copilot AI Mar 2, 2026

Choose a reason for hiding this comment

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

TieredStorage::CancelStash() recomputes blobs via fragment_ref.GetStashParams(), but FragmentRef::GetStashParams(const PrimeValue*) currently returns nullopt when pv->HasStashPending() is true. Since CancelStash has a precondition that stash is pending, this will reliably trip the CHECK(blobs.has_value()) at runtime. Consider splitting eligibility logic from serialization-size computation (e.g., a method that returns DetermineSerializationParams(*pv) even when IO_PENDING is set) and use that here.

Suggested change
// TODO: Don't recompute size estimate, try-delete bin first
auto blobs = fragment_ref.GetStashParams();
CHECK(blobs.has_value());
size_t size = blobs->EstimatedSerializedSize();
if (OccupiesWholePages(size)) {
op_manager_->CancelPending(id);
} else if (key_ref) {
if (auto bin = bins_->Delete(key_ref->first, key_ref->second); bin)
op_manager_->CancelPending(*bin);
}
// We avoid recomputing stash params here, since they may be unavailable
// while a stash is pending. Instead, use the PendingId and bins to decide
// how to cancel the operation.
if (!key_ref) {
// Non-key-based pending id: cancel directly.
op_manager_->CancelPending(id);
} else {
// Try to delete from bins first; if found, cancel by bin id.
if (auto bin = bins_->Delete(key_ref->first, key_ref->second); bin) {
op_manager_->CancelPending(*bin);
} else {
// Fallback: cancel using the original pending id.
op_manager_->CancelPending(id);
}
}

Copilot uses AI. Check for mistakes.
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 7 out of 7 changed files in this pull request and generated 1 comment.


#include <memory>
#include <optional>
#include <variant>
Copy link

Copilot AI Mar 3, 2026

Choose a reason for hiding this comment

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

common.h now defines KeyRef/PendingId using uint16_t, uintptr_t, std::pair, and std::string_view, but the header doesn’t include the standard headers that define those types. This makes the header non-self-contained and can break compilation depending on include order. Add the appropriate includes (at least <cstdint>, <string_view>, and <utility>).

Suggested change
#include <variant>
#include <variant>
#include <cstdint>
#include <ostream>
#include <string_view>
#include <utility>

Copilot uses AI. Check for mistakes.
Wrap PrimeValue references in a FragmentRef type to encapsulate tiering
metadata access. Update Delete, CancelStash, and ShouldStash to accept
FragmentRef instead of raw PrimeValue pointers.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
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.

2 participants