Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
80 changes: 80 additions & 0 deletions cloud/filestore/libs/storage/service/service_ut_sharding.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -620,6 +620,86 @@ Y_UNIT_TEST_SUITE(TStorageServiceShardingTest)
}
}

SERVICE_TEST(ShouldAggregateQuotaUsageAcrossShards)
{
TShardedFileSystemConfig fsConfig;
CREATE_ENV_AND_SHARDED_FILESYSTEM();

{
NProtoPrivate::TSetQuotaRequest request;
request.SetFileSystemId(fsConfig.FsId);
request.SetQuotaId(42);
request.SetMaxBytes(1_GB);
request.SetMaxNodes(100);

TString buf;
google::protobuf::util::MessageToJsonString(request, &buf);
service.ExecuteAction("setquota", buf);
}

auto headers = service.InitSession(fsConfig.FsId, "client");

// attach the quota to a directory on main - files are regular nodes,
// so main auto-routes their creation into shards (round-robin) and
// forwards the inherited QuotaId along with each one; the directory
// itself stays on main and its own attach bumps main's own local
// usage by 1
auto dirId = service
.CreateNode(
headers,
TCreateNodeArgs::Directory(RootNodeId, "dir"))
->Record.GetNode()
.GetId();
service.SetNodeAttr(
headers,
fsConfig.FsId,
TSetNodeAttrArgs(dirId).SetQuotaId(42));

constexpr ui32 fileCount = 4;
constexpr ui64 fileSize = 100;
for (ui32 i = 0; i < fileCount; ++i) {
auto fileId =
service
.CreateNode(
headers,
TCreateNodeArgs::File(dirId, "file" + ToString(i)))
->Record.GetNode()
.GetId();
service.SetNodeAttr(
headers,
fsConfig.FsId,
TSetNodeAttrArgs(fileId).SetSize(fileSize));
}

// each shard should have received at least one of the files -
// otherwise this test wouldn't actually be exercising cross-shard
// aggregation
for (const auto& shardId: fsConfig.ShardIds()) {
const auto stats = GetStorageStats(service, shardId);
const auto& usages = stats.GetStats().GetQuotaUsages();
UNIT_ASSERT_VALUES_EQUAL_C(1, usages.size(), shardId);
UNIT_ASSERT_C(usages.contains(42), shardId);
const auto& usage = usages.at(42);
UNIT_ASSERT_C(usage.GetUsedNodes() > 0, shardId);
UNIT_ASSERT_C(usage.GetUsedBytes() > 0, shardId);
}

// forcing main to fan out and aggregate should sum main's own local
// usage (the directory attach point) and both shards' contributions
// (the files, including their bytes) together
const auto aggregate = GetStorageStats(
service,
fsConfig.FsId,
0 /* cacheTTL */,
NProtoPrivate::STATS_REQUEST_MODE_FORCE_FETCH_SHARDS);
const auto& usages = aggregate.GetStats().GetQuotaUsages();
UNIT_ASSERT_VALUES_EQUAL(1, usages.size());
UNIT_ASSERT(usages.contains(42));
const auto& usage = usages.at(42);
UNIT_ASSERT_VALUES_EQUAL(1 + fileCount, usage.GetUsedNodes());
UNIT_ASSERT_VALUES_EQUAL(fileCount * fileSize, usage.GetUsedBytes());
}

SERVICE_TEST(ShouldCheckForShardsInAdapterModeUponSessionCreationInShards)
{
//
Expand Down
21 changes: 21 additions & 0 deletions cloud/filestore/libs/storage/tablet/tablet_actor_counters.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,19 @@ using namespace NMetrics;

namespace {

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

void MergeQuotaUsages(
NProtoPrivate::TStorageStats& dst,
const NProtoPrivate::TStorageStats& src)
{
for (const auto& [quotaId, srcUsage]: src.GetQuotaUsages()) {
auto& dstUsage = (*dst.MutableQuotaUsages())[quotaId];
dstUsage.SetUsedBytes(dstUsage.GetUsedBytes() + srcUsage.GetUsedBytes());
dstUsage.SetUsedNodes(dstUsage.GetUsedNodes() + srcUsage.GetUsedNodes());
}
}

////////////////////////////////////////////////////////////////////////////////
// TAggregateStatsActor always replies with
// TAggregateStatsCompleted to a TIndexTabletActor that created the actor.
Expand Down Expand Up @@ -230,6 +243,8 @@ void TAggregateStatsActor::HandleGetStorageStatsResponse(
dst.SetSevenBytesHandlesCount(
dst.GetSevenBytesHandlesCount() + src.GetSevenBytesHandlesCount());

MergeQuotaUsages(dst, src);

LOG_DEBUG(
ctx,
TFileStoreComponents::TABLET_WORKER,
Expand Down Expand Up @@ -810,6 +825,12 @@ void TIndexTabletActor::FillSelfStorageStats(
stats->SetUnconfirmedDataCount(
UnconfirmedData.size() + UnconfirmedDataInProgress.size());
stats->SetConfirmedDataCount(ConfirmedData.size());

for (const auto& usage: GetQuotaUsages()) {
auto& proto = (*stats->MutableQuotaUsages())[usage.QuotaId];
proto.SetUsedBytes(usage.UsedBytes);
proto.SetUsedNodes(usage.UsedNodes);
}
}

void TIndexTabletActor::HandleGetStorageStats(
Expand Down
30 changes: 30 additions & 0 deletions cloud/filestore/libs/storage/tablet/tablet_actor_monitoring.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1356,15 +1356,45 @@ void TIndexTabletActor::RenderHttpInfo_QuotasTab(IOutputStream& out)
TABLEH() { out << "QuotaId"; }
TABLEH() { out << "MaxBytes"; }
TABLEH() { out << "MaxNodes"; }
TABLEH() { out << "TabletUsedBytes"; }
TABLEH() { out << "TabletUsedNodes"; }
TABLEH() { out << "FilesystemUsedBytes"; }
TABLEH() { out << "FilesystemUsedNodes"; }
TABLEH() { out << "CreatedAt"; }
}
}

const auto& aggregateUsages =
CachedAggregateStats.GetQuotaUsages();

for (const auto& quota: quotas) {
const auto* usage = FindQuotaUsage(quota.GetQuotaId());
const auto aggregateIt =
aggregateUsages.find(quota.GetQuotaId());
const bool hasAggregate =
aggregateIt != aggregateUsages.end();

TABLER() {
TABLED() { out << quota.GetQuotaId(); }
TABLED() { out << FormatByteSize(quota.GetMaxBytes()); }
TABLED() { out << quota.GetMaxNodes(); }
TABLED() {
out << (usage
? FormatByteSize(usage->UsedBytes)
: FormatByteSize(0));
}
TABLED() { out << (usage ? usage->UsedNodes : 0); }
TABLED() {
out << (hasAggregate
? FormatByteSize(
aggregateIt->second.GetUsedBytes())
: FormatByteSize(0));
}
TABLED() {
out << (hasAggregate
? aggregateIt->second.GetUsedNodes()
: 0);
}
TABLED() {
out << TInstant::MicroSeconds(
quota.GetCreationTimestampUs());
Expand Down
2 changes: 2 additions & 0 deletions cloud/filestore/libs/storage/tablet/tablet_state.h
Original file line number Diff line number Diff line change
Expand Up @@ -1370,6 +1370,8 @@ FILESTORE_DUPCACHE_REQUESTS(FILESTORE_DECLARE_DUPCACHE)

void LoadQuotaUsages(const TVector<TQuotaUsage>& usages);

const TQuotaUsage* FindQuotaUsage(ui32 quotaId) const;

TVector<TQuotaUsage> GetQuotaUsages() const;

//
Expand Down
5 changes: 5 additions & 0 deletions cloud/filestore/libs/storage/tablet/tablet_state_quotas.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,11 @@ void TIndexTabletState::LoadQuotaUsages(const TVector<TQuotaUsage>& usages)
}
}

const TQuotaUsage* TIndexTabletState::FindQuotaUsage(ui32 quotaId) const
{
return Impl->Quotas.FindUsage(quotaId);
}

TVector<TQuotaUsage> TIndexTabletState::GetQuotaUsages() const
{
return Impl->Quotas.GetUsages();
Expand Down
9 changes: 9 additions & 0 deletions cloud/filestore/private/api/protos/tablet.proto
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,15 @@ message TStorageStats
repeated TNodeStats NodeStats = 7001;

repeated TNodeLatencyStats LatencyStats = 8001;

// per-quota usage
map<uint32, TQuotaUsageValue> QuotaUsages = 9001;
}

message TQuotaUsageValue
{
uint64 UsedBytes = 1;
uint64 UsedNodes = 2;
}

enum EStatsRequestMode
Expand Down