@@ -620,6 +620,74 @@ Y_UNIT_TEST_SUITE(TStorageServiceShardingTest)
620620 }
621621 }
622622
623+ SERVICE_TEST (ShouldAggregateQuotaUsageAcrossShards)
624+ {
625+ TShardedFileSystemConfig fsConfig;
626+ CREATE_ENV_AND_SHARDED_FILESYSTEM ();
627+
628+ {
629+ NProtoPrivate::TSetQuotaRequest request;
630+ request.SetFileSystemId (fsConfig.FsId );
631+ request.SetQuotaId (42 );
632+ request.SetMaxBytes (1_GB);
633+ request.SetMaxNodes (100 );
634+
635+ TString buf;
636+ google::protobuf::util::MessageToJsonString (request, &buf);
637+ service.ExecuteAction (" setquota" , buf);
638+ }
639+
640+ auto headers = service.InitSession (fsConfig.FsId , " client" );
641+
642+ // attach the quota to a directory on main - files are regular nodes,
643+ // so main auto-routes their creation into shards (round-robin) and
644+ // forwards the inherited QuotaId along with each one; the directory
645+ // itself stays on main and its own attach bumps main's own local
646+ // usage by 1
647+ auto dirId = service
648+ .CreateNode (
649+ headers,
650+ TCreateNodeArgs::Directory (RootNodeId, " dir" ))
651+ ->Record .GetNode ()
652+ .GetId ();
653+ service.SetNodeAttr (
654+ headers,
655+ fsConfig.FsId ,
656+ TSetNodeAttrArgs (dirId).SetQuotaId (42 ));
657+
658+ constexpr ui32 fileCount = 4 ;
659+ for (ui32 i = 0 ; i < fileCount; ++i) {
660+ service.CreateNode (
661+ headers,
662+ TCreateNodeArgs::File (dirId, " file" + ToString (i)));
663+ }
664+
665+ // each shard should have received at least one of the files -
666+ // otherwise this test wouldn't actually be exercising cross-shard
667+ // aggregation
668+ for (const auto & shardId: fsConfig.ShardIds ()) {
669+ const auto stats = GetStorageStats (service, shardId);
670+ const auto & usages = stats.GetStats ().GetQuotaUsages ();
671+ UNIT_ASSERT_VALUES_EQUAL_C (1 , usages.size (), shardId);
672+ UNIT_ASSERT_VALUES_EQUAL_C (42u , usages[0 ].GetQuotaId (), shardId);
673+ UNIT_ASSERT_C (usages[0 ].GetUsedNodes () > 0 , shardId);
674+ }
675+
676+ // forcing main to fan out and aggregate should sum main's own local
677+ // usage (the directory attach point) and both shards' contributions
678+ // (the files) together
679+ const auto aggregate = GetStorageStats (
680+ service,
681+ fsConfig.FsId ,
682+ 0 /* cacheTTL */ ,
683+ NProtoPrivate::STATS_REQUEST_MODE_FORCE_FETCH_SHARDS );
684+ const auto & usages = aggregate.GetStats ().GetQuotaUsages ();
685+ UNIT_ASSERT_VALUES_EQUAL (1 , usages.size ());
686+ UNIT_ASSERT_VALUES_EQUAL (42u , usages[0 ].GetQuotaId ());
687+ UNIT_ASSERT_VALUES_EQUAL (1 + fileCount, usages[0 ].GetUsedNodes ());
688+ UNIT_ASSERT_VALUES_EQUAL (0u , usages[0 ].GetUsedBytes ());
689+ }
690+
623691 SERVICE_TEST (ShouldCheckForShardsInAdapterModeUponSessionCreationInShards)
624692 {
625693 //
0 commit comments