Skip to content

Commit

Permalink
add checkpoints request validation and postponed checkpoint requests
Browse files Browse the repository at this point in the history
  • Loading branch information
gy2411 committed Jan 25, 2024
1 parent b2a5feb commit 86b92a4
Show file tree
Hide file tree
Showing 7 changed files with 962 additions and 35 deletions.
141 changes: 141 additions & 0 deletions cloud/blockstore/libs/storage/volume/model/checkpoint.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,16 @@ std::optional<ECheckpointType> TCheckpointStore::GetCheckpointType(
return ptr ? ptr->Type : std::optional<ECheckpointType>{};
}

bool TCheckpointStore::IsCheckpointDeleted(const TString& checkpointId) const
{
return DeletedCheckpoints.contains(checkpointId);
}

bool TCheckpointStore::IsCheckpointInSavedStatusExist(const TString& checkpointId) const
{
return CheckpointsWithSavedRequest.contains(checkpointId);
}

TVector<TString> TCheckpointStore::GetLightCheckpoints() const
{
TVector<TString> checkpoints(Reserve(ActiveCheckpoints.size()));
Expand Down Expand Up @@ -198,6 +208,9 @@ TCheckpointRequest& TCheckpointStore::AddCheckpointRequest(
auto [it, inserted] =
CheckpointRequests.insert({requestId, std::move(checkpointRequest)});
Y_DEBUG_ABORT_UNLESS(inserted);
if (it->second.State == ECheckpointRequestState::Saved) {
CheckpointsWithSavedRequest.insert(it->second.CheckpointId);
}
Apply(it->second);
return it->second;
}
Expand Down Expand Up @@ -231,7 +244,11 @@ void TCheckpointStore::DeleteCheckpointData(const TString& checkpointId)

void TCheckpointStore::DeleteCheckpoint(const TString& checkpointId)
{
if (!ActiveCheckpoints.contains(checkpointId)) {
return;
}
ActiveCheckpoints.erase(checkpointId);
DeletedCheckpoints.insert(checkpointId);
CalcCheckpointsState();
}

Expand Down Expand Up @@ -271,4 +288,128 @@ void TCheckpointStore::Apply(const TCheckpointRequest& checkpointRequest)
}
}

TSet<TString>& TCheckpointStore::GetCheckpoitsWithSavedRequest()
{
return CheckpointsWithSavedRequest;
}

const TSet<TString>& TCheckpointStore::GetCheckpoitsWithSavedRequest() const
{
return CheckpointsWithSavedRequest;
}

////////////////////////////////////////////////////////////////////////////////

ECheckpointRequestValidityStatus TCheckpointStore::ValidateCheckpointRequest(
const TString& checkpointId,
ECheckpointRequestType requestType,
ECheckpointType checkpointType,
TString* message)
{
if (checkpointId.empty()) {
*message = "Checkpoint id should be nonempty";
return ECheckpointRequestValidityStatus::Invalid;
}

if (checkpointType != ECheckpointType::Normal
&& (requestType == ECheckpointRequestType::DeleteData
|| requestType == ECheckpointRequestType::CreateWithoutData)) {
*message = ToString(requestType) + " request makes sence for normal checkpoints only";
return ECheckpointRequestValidityStatus::Invalid;
}

const auto actualCheckpointType = GetCheckpointType(checkpointId);

if (actualCheckpointType && actualCheckpointType != checkpointType) {
*message = TStringBuilder()
<< "Checkpoint exists and has another type "
<< ToString(*actualCheckpointType);
return ECheckpointRequestValidityStatus::Invalid;
}

const bool checkpointExists = actualCheckpointType.has_value();
const bool checkpointHasData = checkpointExists && DoesCheckpointHaveData(checkpointId);
const bool checkpointDeleted = IsCheckpointDeleted(checkpointId);

if (!checkpointExists && !checkpointDeleted) {
// Checkpoint never existed.
switch (requestType) {
case ECheckpointRequestType::Create:
case ECheckpointRequestType::CreateWithoutData: {
return ECheckpointRequestValidityStatus::Ok;
}
case ECheckpointRequestType::DeleteData: {
*message = "Checkpoint does not exist";
return ECheckpointRequestValidityStatus::Invalid;
}
case ECheckpointRequestType::Delete: {
*message = "Checkpoint does not exist";
return ECheckpointRequestValidityStatus::Already;
}
}
} else if (checkpointExists && checkpointHasData) {
// Checkpoint exists and has data.
switch (requestType) {
case ECheckpointRequestType::Create: {
*message = "Checkpoint exists";
return ECheckpointRequestValidityStatus::Already;
}
case ECheckpointRequestType::DeleteData: {
return ECheckpointRequestValidityStatus::Ok;
}
case ECheckpointRequestType::Delete: {
return ECheckpointRequestValidityStatus::Ok;
}
case ECheckpointRequestType::CreateWithoutData: {
*message = "Checkpoint exists and has data";
return ECheckpointRequestValidityStatus::Invalid;
}
}
} else if (checkpointExists && !checkpointHasData) {
// Checkpoint exists and has no data.
switch (requestType) {
case ECheckpointRequestType::Create: {
if (checkpointType == ECheckpointType::Normal) {
*message = "Checkpoint exists and has no data";
return ECheckpointRequestValidityStatus::Invalid;
} else {
*message = "Checkpoint exists";
return ECheckpointRequestValidityStatus::Already;
}
}
case ECheckpointRequestType::DeleteData: {
*message = "Data is already deleted";
return ECheckpointRequestValidityStatus::Already;
}
case ECheckpointRequestType::Delete: {
return ECheckpointRequestValidityStatus::Ok;
}
case ECheckpointRequestType::CreateWithoutData: {
*message = "Checkpoint exists";
return ECheckpointRequestValidityStatus::Already;
}
}
} else if (!checkpointExists && checkpointDeleted) {
// Checkpoint was deleted.
switch (requestType) {
case ECheckpointRequestType::Create: {
case ECheckpointRequestType::CreateWithoutData:
*message = "Checkpoint is already deleted";
return ECheckpointRequestValidityStatus::Invalid;
}
case ECheckpointRequestType::DeleteData: {
*message = "Checkpoint is already deleted";
return ECheckpointRequestValidityStatus::Invalid;
}
case ECheckpointRequestType::Delete: {
*message = "Checkpoint is already deleted";
return ECheckpointRequestValidityStatus::Already;
}
}
} else {
*message = "Checkpoint validation should never reach this line";
return ECheckpointRequestValidityStatus::Invalid;
}
}

} // namespace NCloud::NBlockStore::NStorage
73 changes: 73 additions & 0 deletions cloud/blockstore/libs/storage/volume/model/checkpoint.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

#include <util/datetime/base.h>
#include <util/generic/map.h>
#include <util/generic/queue.h>
#include <util/generic/set.h>
#include <util/generic/string.h>
#include <util/generic/vector.h>

Expand All @@ -27,6 +29,14 @@ enum class ECheckpointRequestType
CreateWithoutData = 3,
};

// Request status that determines either the request should be executed or return immediately
enum class ECheckpointRequestValidityStatus
{
Ok = 0, // Request should be executed
Already = 1, // Nothing to do, request should finish successfully without execution
Invalid = 2, // Request is invalid, request should finish with error without execution
};

// The type of checkpoint. See documentation in doc/checkpoint.md.
enum class ECheckpointType
{
Expand Down Expand Up @@ -85,6 +95,8 @@ class TCheckpointStore
private:
TMap<ui64, TCheckpointRequest> CheckpointRequests;
TActiveCheckpointsMap ActiveCheckpoints;
TSet<TString> DeletedCheckpoints;
TSet<TString> CheckpointsWithSavedRequest;
ui64 LastCheckpointRequestId = 0;
ui64 CheckpointRequestInProgress = 0;
bool CheckpointBeingCreated = false;
Expand All @@ -103,6 +115,12 @@ class TCheckpointStore
ECheckpointRequestType reqType,
ECheckpointType type);

[[nodiscard]] ECheckpointRequestValidityStatus ValidateCheckpointRequest(
const TString& checkpointId,
ECheckpointRequestType requestType,
ECheckpointType checkpointType,
TString* message);

void SetCheckpointRequestSaved(ui64 requestId);
void SetCheckpointRequestInProgress(ui64 requestId);
void SetCheckpointRequestFinished(
Expand All @@ -118,6 +136,8 @@ class TCheckpointStore

[[nodiscard]] std::optional<ECheckpointType> GetCheckpointType(
const TString& checkpointId) const;
[[nodiscard]] bool IsCheckpointDeleted(const TString& checkpointId) const;
[[nodiscard]] bool IsCheckpointInSavedStatusExist(const TString& checkpointId) const;
[[nodiscard]] TVector<TString> GetLightCheckpoints() const;
[[nodiscard]] TVector<TString> GetCheckpointsWithData() const;
[[nodiscard]] const TActiveCheckpointsMap& GetActiveCheckpoints() const;
Expand All @@ -129,6 +149,9 @@ class TCheckpointStore

[[nodiscard]] TVector<TCheckpointRequest> GetCheckpointRequests() const;

TSet<TString>& GetCheckpoitsWithSavedRequest();
const TSet<TString>& GetCheckpoitsWithSavedRequest() const;

private:
[[nodiscard]] TCheckpointRequest& GetRequest(ui64 requestId);
TCheckpointRequest& AddCheckpointRequest(
Expand All @@ -142,4 +165,54 @@ class TCheckpointStore
void Apply(const TCheckpointRequest& checkpointRequest);
};

////////////////////////////////////////////////////////////////////////////////

template<typename TCheckpointRequestInfo>
struct TPostponedCheckpointRequest {
ui64 RequestId;
TCheckpointRequestInfo Info;

TPostponedCheckpointRequest(
ui64 requestId,
TCheckpointRequestInfo info)
: RequestId(requestId)
, Info(std::move(info))
{}
};

template<typename TCheckpointRequestInfo>
class TPostponedCheckpointRequestsQueue
{
private:
using TRequest = TPostponedCheckpointRequest<TCheckpointRequestInfo>;
TMap<TString, TQueue<TRequest>> Requests;

public:
bool HasPostponedRequest(const TString& checkpointId) const
{
return Requests.contains(checkpointId);
}

void AddPostponedRequest(TString checkpointId, TRequest request)
{
Requests[checkpointId].push(std::move(request));
}

std::optional<TRequest> TakePostponedRequest(TString checkpointId)
{
auto queue = Requests.FindPtr(checkpointId);
if (!queue) {
return std::nullopt;
}

auto request = std::move(queue->front());
queue->pop();
if (queue->empty()) {
Requests.erase(checkpointId);
}

return request;
}
};

} // namespace NCloud::NBlockStore::NStorage
Loading

0 comments on commit 86b92a4

Please sign in to comment.