From 7c4e2006788590c14bfe06b89113aff16807617e Mon Sep 17 00:00:00 2001 From: Andrei Strelkovskii Date: Tue, 1 Oct 2024 14:27:47 +0300 Subject: [PATCH] merge to stable-23-3: filestore - LargeDeletionMarkers-related followup work, TFileRingBuffer, some small fixes and enhancements (#2183) * unstable-process: logging open sockets upon process start failure (#2048) * issue-1541: implemented a ring buffer over a file to use it in HandleOpsQueue (#2047) * issue-1541: ring buffer over a file * issue-1541: TFileRingBufferTest::Should{Restore,Validate} + fixes * issue-1541: randomized test for TFileRingBuffer + fixes * issue-1541: moved TFileRingBuffer implementation to .cpp * issue-1541: minor rename * issue-1541: fixed ubsan (#2087) * NBS and Filestore cluster deployment instructions (#2094) * issue-1795: LargeDeletionMarkers - simple integration test (#2122) * fixed StatFs diagnostics RequestType (#2127) * issue-1795: backpressure for LargeDeletionMarkers (#2147) * issue-1795: LargeDeletionMarkers backpressure - some fixes and cleanup (#2160) * filestore-client: fixed crashes on DestroySession error, fixed ls node type output (#2168) * filestore-client: fixed crashes on DestroySession error, fixed ls node type output * filestore-client: fixed crashes on DestroySession error, fixed ls node type output - cleanup * issue-1795: implemented Unsafe{Delete,Update,Get}Node to use it in private api for manual interventions - e.g. to clean up OrphanNodes (#2165) * issue-1795: implemented Unsafe{Delete,Update,Get}Node to use it in private api for manual interventions - e.g. to clean up OrphanNodes * issue-1795: tiny cleanup * issue-1795: private api Unsafe{Delete,Update,Get}Node actions + ut * issue-1795: cleanup * updated CMakeLists.txt after merge, fixed includes * fixed CMakeLists.txt --- README.md | 7 +- cloud/filestore/apps/client/lib/command.h | 13 +- cloud/filestore/apps/client/lib/ls.cpp | 2 + cloud/filestore/config/storage.proto | 7 + .../libs/diagnostics/critical_events.h | 1 + cloud/filestore/libs/storage/api/tablet.h | 12 + cloud/filestore/libs/storage/core/config.cpp | 11 +- cloud/filestore/libs/storage/core/config.h | 1 + .../service/CMakeLists.darwin-x86_64.txt | 1 + .../service/CMakeLists.linux-aarch64.txt | 1 + .../service/CMakeLists.linux-x86_64.txt | 1 + .../service/CMakeLists.windows-x86_64.txt | 1 + .../libs/storage/service/service_actor.h | 12 + .../storage/service/service_actor_actions.cpp | 12 + .../service_actor_actions_unsafe_node_ops.cpp | 164 +++ .../service/service_actor_actions_ut.cpp | 117 +- cloud/filestore/libs/storage/service/ya.make | 1 + .../tablet/CMakeLists.darwin-x86_64.txt | 1 + .../tablet/CMakeLists.linux-aarch64.txt | 1 + .../tablet/CMakeLists.linux-x86_64.txt | 1 + .../tablet/CMakeLists.windows-x86_64.txt | 1 + .../filestore/libs/storage/tablet/helpers.cpp | 18 +- cloud/filestore/libs/storage/tablet/helpers.h | 10 +- .../libs/storage/tablet/tablet_actor.cpp | 28 +- .../libs/storage/tablet/tablet_actor.h | 6 +- .../tablet/tablet_actor_allocatedata.cpp | 6 +- .../storage/tablet/tablet_actor_cleanup.cpp | 3 +- .../tablet/tablet_actor_compaction.cpp | 8 +- .../storage/tablet/tablet_actor_counters.cpp | 13 +- .../tablet/tablet_actor_createhandle.cpp | 6 +- .../tablet/tablet_actor_destroyhandle.cpp | 10 +- .../tablet/tablet_actor_destroysession.cpp | 9 +- .../storage/tablet/tablet_actor_loadstate.cpp | 15 +- .../tablet/tablet_actor_renamenode.cpp | 11 +- .../tablet/tablet_actor_resetsession.cpp | 9 +- .../tablet/tablet_actor_setnodeattr.cpp | 7 +- .../storage/tablet/tablet_actor_truncate.cpp | 8 +- .../tablet/tablet_actor_unlinknode.cpp | 7 +- .../tablet/tablet_actor_unsafe_node_ops.cpp | 276 ++++ .../storage/tablet/tablet_actor_zerorange.cpp | 15 +- .../libs/storage/tablet/tablet_database.cpp | 43 + .../libs/storage/tablet/tablet_database.h | 8 + .../storage/tablet/tablet_database_ut.cpp | 35 + .../libs/storage/tablet/tablet_schema.h | 16 +- .../libs/storage/tablet/tablet_state.cpp | 12 + .../libs/storage/tablet/tablet_state.h | 26 +- .../libs/storage/tablet/tablet_state_data.cpp | 51 +- .../libs/storage/tablet/tablet_state_impl.h | 1 + .../storage/tablet/tablet_state_nodes.cpp | 50 +- .../filestore/libs/storage/tablet/tablet_tx.h | 84 +- .../libs/storage/tablet/tablet_ut_data.cpp | 108 ++ cloud/filestore/libs/storage/tablet/ya.make | 1 + .../vfs_fuse/CMakeLists.darwin-x86_64.txt | 1 + .../vfs_fuse/CMakeLists.linux-aarch64.txt | 1 + .../libs/vfs_fuse/CMakeLists.linux-x86_64.txt | 1 + .../vfs_fuse/CMakeLists.windows-x86_64.txt | 1 + .../libs/vfs_fuse/file_ring_buffer.cpp | 313 +++++ .../libs/vfs_fuse/file_ring_buffer.h | 36 + .../libs/vfs_fuse/file_ring_buffer_ut.cpp | 338 +++++ .../libs/vfs_fuse/handle_ops_queue.cpp | 16 +- .../libs/vfs_fuse/handle_ops_queue.h | 14 +- cloud/filestore/libs/vfs_fuse/loop.cpp | 2 +- .../vfs_fuse/ut/CMakeLists.darwin-x86_64.txt | 1 + .../vfs_fuse/ut/CMakeLists.linux-aarch64.txt | 1 + .../vfs_fuse/ut/CMakeLists.linux-x86_64.txt | 1 + .../vfs_fuse/ut/CMakeLists.windows-x86_64.txt | 1 + cloud/filestore/libs/vfs_fuse/ut/ya.make | 1 + .../vhost/CMakeLists.darwin-x86_64.txt | 1 + .../vhost/CMakeLists.linux-aarch64.txt | 1 + .../vhost/CMakeLists.linux-x86_64.txt | 1 + .../vhost/CMakeLists.windows-x86_64.txt | 1 + cloud/filestore/libs/vfs_fuse/ya.make.inc | 1 + .../filestore/private/api/protos/tablet.proto | 40 + .../tests/client/canondata/result.json | 3 + .../test.test_large_file/results.txt | Bin 0 -> 2235 bytes cloud/filestore/tests/client/nfs-storage.txt | 2 + cloud/filestore/tests/client/test.py | 80 +- cloud/filestore/tests/python/lib/client.py | 4 + .../testing/unstable-process/__main__.py | 1 + .../config_cluster_template.yaml | 133 ++ deploy/8_node_cluster/nbs/cfg/nbs-auth.txt | 4 + .../nbs/cfg/nbs-breakpad-sender.json | 1 + .../8_node_cluster/nbs/cfg/nbs-breakpad.json | 1 + deploy/8_node_cluster/nbs/cfg/nbs-client.txt | 49 + deploy/8_node_cluster/nbs/cfg/nbs-diag.txt | 89 ++ .../8_node_cluster/nbs/cfg/nbs-discovery.txt | 1 + .../8_node_cluster/nbs/cfg/nbs-disk-agent.txt | 83 ++ .../nbs/cfg/nbs-disk-registry-proxy.txt | 2 + deploy/8_node_cluster/nbs/cfg/nbs-domains.txt | 128 ++ .../8_node_cluster/nbs/cfg/nbs-features.txt | 12 + .../8_node_cluster/nbs/cfg/nbs-http-proxy.txt | 1 + deploy/8_node_cluster/nbs/cfg/nbs-iam.txt | 1 + deploy/8_node_cluster/nbs/cfg/nbs-ic.txt | 6 + deploy/8_node_cluster/nbs/cfg/nbs-log.txt | 104 ++ .../8_node_cluster/nbs/cfg/nbs-logbroker.txt | 1 + deploy/8_node_cluster/nbs/cfg/nbs-names.txt | 52 + deploy/8_node_cluster/nbs/cfg/nbs-notify.txt | 1 + deploy/8_node_cluster/nbs/cfg/nbs-rdma.txt | 15 + deploy/8_node_cluster/nbs/cfg/nbs-server.txt | 27 + .../nbs/cfg/nbs-shared-cache.txt | 1 + .../nbs/cfg/nbs-stats-upload.txt | 1 + deploy/8_node_cluster/nbs/cfg/nbs-storage.txt | 46 + deploy/8_node_cluster/nbs/cfg/nbs-sys.txt | 55 + .../8_node_cluster/nbs/cfg/nbs_disk_agent.cfg | 92 ++ deploy/8_node_cluster/nbs/cfg/nbs_server.cfg | 136 ++ .../nfs-control/cfg/nfs-auth.txt | 3 + .../nfs-control/cfg/nfs-diag.txt | 19 + .../nfs-control/cfg/nfs-domains.txt | 128 ++ .../nfs-control/cfg/nfs-features.txt | 3 + .../8_node_cluster/nfs-control/cfg/nfs-ic.txt | 6 + .../nfs-control/cfg/nfs-log.txt | 39 + .../nfs-control/cfg/nfs-names.txt | 52 + .../nfs-control/cfg/nfs-server.txt | 6 + .../nfs-control/cfg/nfs-storage.txt | 17 + .../nfs-control/cfg/nfs-sys.txt | 43 + .../nfs-control/cfg/nfs_server.cfg | 71 + deploy/8_node_cluster/nfs/cfg/nfs-auth.txt | 3 + deploy/8_node_cluster/nfs/cfg/nfs-diag.txt | 19 + deploy/8_node_cluster/nfs/cfg/nfs-domains.txt | 128 ++ .../8_node_cluster/nfs/cfg/nfs-http-proxy.txt | 3 + deploy/8_node_cluster/nfs/cfg/nfs-ic.txt | 6 + deploy/8_node_cluster/nfs/cfg/nfs-log.txt | 39 + deploy/8_node_cluster/nfs/cfg/nfs-names.txt | 52 + .../8_node_cluster/nfs/cfg/nfs-options.json | 1 + deploy/8_node_cluster/nfs/cfg/nfs-server.txt | 6 + .../nfs/cfg/nfs-storage-vhost.txt | 4 + deploy/8_node_cluster/nfs/cfg/nfs-storage.txt | 5 + deploy/8_node_cluster/nfs/cfg/nfs-sys.txt | 55 + deploy/8_node_cluster/nfs/cfg/nfs-vhost.txt | 16 + deploy/8_node_cluster/nfs/cfg/nfs_server.cfg | 71 + deploy/8_node_cluster/nfs/cfg/nfs_vhost.cfg | 72 + deploy/8_node_cluster/nfs/cfg/vhost-log.txt | 27 + .../cfg/Configure-my_cluster-init-1.txt | 71 + .../storage/cfg/Configure-my_cluster.txt | 1248 +++++++++++++++++ .../storage/cfg/Console-Config-my_cluster.txt | 55 + .../storage/cfg/CreateTenant-1.txt | 27 + .../storage/cfg/CreateTenant-2.txt | 19 + .../storage/cfg/CreateTenant-3.txt | 12 + .../storage/cfg/CreateTenant-4.txt | 12 + .../8_node_cluster/storage/cfg/DefineBox.txt | 82 ++ .../storage/cfg/DefineStoragePools.txt | 1 + .../storage/cfg/app_config.proto | 1212 ++++++++++++++++ deploy/8_node_cluster/storage/cfg/auth.txt | 1 + deploy/8_node_cluster/storage/cfg/boot.txt | 609 ++++++++ deploy/8_node_cluster/storage/cfg/bs.txt | 259 ++++ .../8_node_cluster/storage/cfg/channels.txt | 19 + deploy/8_node_cluster/storage/cfg/cms.txt | 1 + deploy/8_node_cluster/storage/cfg/config.yaml | 221 +++ deploy/8_node_cluster/storage/cfg/domains.txt | 128 ++ deploy/8_node_cluster/storage/cfg/dyn_ns.txt | 2 + .../storage/cfg/dynamic_server.cfg | 100 ++ .../storage/cfg/feature_flags.txt | 3 + deploy/8_node_cluster/storage/cfg/grpc.txt | 12 + deploy/8_node_cluster/storage/cfg/ic.txt | 6 + .../8_node_cluster/storage/cfg/init_cms.bash | 2 + .../storage/cfg/init_compute.bash | 1 + .../storage/cfg/init_databases.bash | 3 + .../storage/cfg/init_root_storage.bash | 1 + .../storage/cfg/init_storage.bash | 3 + deploy/8_node_cluster/storage/cfg/kikimr.cfg | 94 ++ deploy/8_node_cluster/storage/cfg/kqp.txt | 6 + deploy/8_node_cluster/storage/cfg/log.txt | 2 + deploy/8_node_cluster/storage/cfg/names.txt | 91 ++ .../storage/cfg/netclassifier.txt | 1 + deploy/8_node_cluster/storage/cfg/pq.txt | 2 + deploy/8_node_cluster/storage/cfg/pqcd.txt | 1 + deploy/8_node_cluster/storage/cfg/sqs.txt | 1 + deploy/8_node_cluster/storage/cfg/sys.txt | 44 + deploy/8_node_cluster/storage/cfg/tracing.txt | 1 + deploy/8_node_cluster/storage/cfg/vdisks.txt | 17 + deploy/README.md | 27 + 171 files changed, 8353 insertions(+), 111 deletions(-) create mode 100644 cloud/filestore/libs/storage/service/service_actor_actions_unsafe_node_ops.cpp create mode 100644 cloud/filestore/libs/storage/tablet/tablet_actor_unsafe_node_ops.cpp create mode 100644 cloud/filestore/libs/vfs_fuse/file_ring_buffer.cpp create mode 100644 cloud/filestore/libs/vfs_fuse/file_ring_buffer.h create mode 100644 cloud/filestore/libs/vfs_fuse/file_ring_buffer_ut.cpp create mode 100644 cloud/filestore/tests/client/canondata/test.test_large_file/results.txt create mode 100644 deploy/8_node_cluster/config_cluster_template.yaml create mode 100644 deploy/8_node_cluster/nbs/cfg/nbs-auth.txt create mode 100644 deploy/8_node_cluster/nbs/cfg/nbs-breakpad-sender.json create mode 100644 deploy/8_node_cluster/nbs/cfg/nbs-breakpad.json create mode 100644 deploy/8_node_cluster/nbs/cfg/nbs-client.txt create mode 100644 deploy/8_node_cluster/nbs/cfg/nbs-diag.txt create mode 100644 deploy/8_node_cluster/nbs/cfg/nbs-discovery.txt create mode 100755 deploy/8_node_cluster/nbs/cfg/nbs-disk-agent.txt create mode 100644 deploy/8_node_cluster/nbs/cfg/nbs-disk-registry-proxy.txt create mode 100644 deploy/8_node_cluster/nbs/cfg/nbs-domains.txt create mode 100644 deploy/8_node_cluster/nbs/cfg/nbs-features.txt create mode 100644 deploy/8_node_cluster/nbs/cfg/nbs-http-proxy.txt create mode 100755 deploy/8_node_cluster/nbs/cfg/nbs-iam.txt create mode 100644 deploy/8_node_cluster/nbs/cfg/nbs-ic.txt create mode 100644 deploy/8_node_cluster/nbs/cfg/nbs-log.txt create mode 100644 deploy/8_node_cluster/nbs/cfg/nbs-logbroker.txt create mode 100644 deploy/8_node_cluster/nbs/cfg/nbs-names.txt create mode 100644 deploy/8_node_cluster/nbs/cfg/nbs-notify.txt create mode 100755 deploy/8_node_cluster/nbs/cfg/nbs-rdma.txt create mode 100644 deploy/8_node_cluster/nbs/cfg/nbs-server.txt create mode 100755 deploy/8_node_cluster/nbs/cfg/nbs-shared-cache.txt create mode 100644 deploy/8_node_cluster/nbs/cfg/nbs-stats-upload.txt create mode 100644 deploy/8_node_cluster/nbs/cfg/nbs-storage.txt create mode 100644 deploy/8_node_cluster/nbs/cfg/nbs-sys.txt create mode 100644 deploy/8_node_cluster/nbs/cfg/nbs_disk_agent.cfg create mode 100644 deploy/8_node_cluster/nbs/cfg/nbs_server.cfg create mode 100755 deploy/8_node_cluster/nfs-control/cfg/nfs-auth.txt create mode 100755 deploy/8_node_cluster/nfs-control/cfg/nfs-diag.txt create mode 100644 deploy/8_node_cluster/nfs-control/cfg/nfs-domains.txt create mode 100755 deploy/8_node_cluster/nfs-control/cfg/nfs-features.txt create mode 100755 deploy/8_node_cluster/nfs-control/cfg/nfs-ic.txt create mode 100755 deploy/8_node_cluster/nfs-control/cfg/nfs-log.txt create mode 100644 deploy/8_node_cluster/nfs-control/cfg/nfs-names.txt create mode 100755 deploy/8_node_cluster/nfs-control/cfg/nfs-server.txt create mode 100755 deploy/8_node_cluster/nfs-control/cfg/nfs-storage.txt create mode 100755 deploy/8_node_cluster/nfs-control/cfg/nfs-sys.txt create mode 100755 deploy/8_node_cluster/nfs-control/cfg/nfs_server.cfg create mode 100644 deploy/8_node_cluster/nfs/cfg/nfs-auth.txt create mode 100644 deploy/8_node_cluster/nfs/cfg/nfs-diag.txt create mode 100644 deploy/8_node_cluster/nfs/cfg/nfs-domains.txt create mode 100644 deploy/8_node_cluster/nfs/cfg/nfs-http-proxy.txt create mode 100644 deploy/8_node_cluster/nfs/cfg/nfs-ic.txt create mode 100644 deploy/8_node_cluster/nfs/cfg/nfs-log.txt create mode 100644 deploy/8_node_cluster/nfs/cfg/nfs-names.txt create mode 100644 deploy/8_node_cluster/nfs/cfg/nfs-options.json create mode 100644 deploy/8_node_cluster/nfs/cfg/nfs-server.txt create mode 100644 deploy/8_node_cluster/nfs/cfg/nfs-storage-vhost.txt create mode 100644 deploy/8_node_cluster/nfs/cfg/nfs-storage.txt create mode 100644 deploy/8_node_cluster/nfs/cfg/nfs-sys.txt create mode 100644 deploy/8_node_cluster/nfs/cfg/nfs-vhost.txt create mode 100644 deploy/8_node_cluster/nfs/cfg/nfs_server.cfg create mode 100644 deploy/8_node_cluster/nfs/cfg/nfs_vhost.cfg create mode 100755 deploy/8_node_cluster/nfs/cfg/vhost-log.txt create mode 100644 deploy/8_node_cluster/storage/cfg/Configure-my_cluster-init-1.txt create mode 100644 deploy/8_node_cluster/storage/cfg/Configure-my_cluster.txt create mode 100644 deploy/8_node_cluster/storage/cfg/Console-Config-my_cluster.txt create mode 100644 deploy/8_node_cluster/storage/cfg/CreateTenant-1.txt create mode 100644 deploy/8_node_cluster/storage/cfg/CreateTenant-2.txt create mode 100644 deploy/8_node_cluster/storage/cfg/CreateTenant-3.txt create mode 100644 deploy/8_node_cluster/storage/cfg/CreateTenant-4.txt create mode 100644 deploy/8_node_cluster/storage/cfg/DefineBox.txt create mode 100644 deploy/8_node_cluster/storage/cfg/DefineStoragePools.txt create mode 100644 deploy/8_node_cluster/storage/cfg/app_config.proto create mode 100644 deploy/8_node_cluster/storage/cfg/auth.txt create mode 100644 deploy/8_node_cluster/storage/cfg/boot.txt create mode 100644 deploy/8_node_cluster/storage/cfg/bs.txt create mode 100644 deploy/8_node_cluster/storage/cfg/channels.txt create mode 100644 deploy/8_node_cluster/storage/cfg/cms.txt create mode 100644 deploy/8_node_cluster/storage/cfg/config.yaml create mode 100644 deploy/8_node_cluster/storage/cfg/domains.txt create mode 100644 deploy/8_node_cluster/storage/cfg/dyn_ns.txt create mode 100644 deploy/8_node_cluster/storage/cfg/dynamic_server.cfg create mode 100644 deploy/8_node_cluster/storage/cfg/feature_flags.txt create mode 100644 deploy/8_node_cluster/storage/cfg/grpc.txt create mode 100644 deploy/8_node_cluster/storage/cfg/ic.txt create mode 100644 deploy/8_node_cluster/storage/cfg/init_cms.bash create mode 100644 deploy/8_node_cluster/storage/cfg/init_compute.bash create mode 100644 deploy/8_node_cluster/storage/cfg/init_databases.bash create mode 100644 deploy/8_node_cluster/storage/cfg/init_root_storage.bash create mode 100644 deploy/8_node_cluster/storage/cfg/init_storage.bash create mode 100644 deploy/8_node_cluster/storage/cfg/kikimr.cfg create mode 100644 deploy/8_node_cluster/storage/cfg/kqp.txt create mode 100644 deploy/8_node_cluster/storage/cfg/log.txt create mode 100644 deploy/8_node_cluster/storage/cfg/names.txt create mode 100644 deploy/8_node_cluster/storage/cfg/netclassifier.txt create mode 100644 deploy/8_node_cluster/storage/cfg/pq.txt create mode 100644 deploy/8_node_cluster/storage/cfg/pqcd.txt create mode 100644 deploy/8_node_cluster/storage/cfg/sqs.txt create mode 100644 deploy/8_node_cluster/storage/cfg/sys.txt create mode 100644 deploy/8_node_cluster/storage/cfg/tracing.txt create mode 100644 deploy/8_node_cluster/storage/cfg/vdisks.txt create mode 100644 deploy/README.md diff --git a/README.md b/README.md index aa46283d920..7419306dfdc 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,16 @@ -## Network Block Store +## Network Block Store and Network File Store Network Block Device implementation over YDB BlobStorage or over our own storage nodes. Offers reliable thin-provisioned block devices which support snapshots. +Network File System implementation over YDB BlobStorage. Offers POSIX-compliant scalable filesystem which can be attached to virtual machines via virtiofs or simply mounted via FUSE. ### Quickstart Follow the instructions [here](example/README.md) to build and run NBS on your machine and to attach an NBS-based disk via NBD. NBS-based disks can be attached via vhost-user-blk as well. +Follow the instructions [here](cloud/filestore/README.md) to build and run Filestore on your machine and to attach it to a virtual machine via virtiofs or mount it on your host via FUSE. + Follow the instructions [here](CLANG-FORMAT.md) to install clang-format for formatting the code. ### How to Deploy -TODO +Follow the instructions [here](deploy/README.md) to deploy NBS and Filestore on a cluster. diff --git a/cloud/filestore/apps/client/lib/command.h b/cloud/filestore/apps/client/lib/command.h index 6c2983535c9..baa533cccc1 100644 --- a/cloud/filestore/apps/client/lib/command.h +++ b/cloud/filestore/apps/client/lib/command.h @@ -77,6 +77,11 @@ class TCommand return Opts; } + TLog& AccessLog() + { + return Log; + } + protected: virtual void Init(); @@ -177,16 +182,22 @@ class TFileStoreCommand class TSessionGuard final { TFileStoreCommand& FileStoreCmd; + TLog& Log; public: explicit TSessionGuard(TFileStoreCommand& fileStoreCmd) : FileStoreCmd(fileStoreCmd) + , Log(FileStoreCmd.AccessLog()) { } ~TSessionGuard() { - FileStoreCmd.DestroySession(); + try { + FileStoreCmd.DestroySession(); + } catch (...) { + STORAGE_ERROR("~TSessionGuard: " << CurrentExceptionMessage()); + } } }; diff --git a/cloud/filestore/apps/client/lib/ls.cpp b/cloud/filestore/apps/client/lib/ls.cpp index e1a91937b86..a29360e6476 100644 --- a/cloud/filestore/apps/client/lib/ls.cpp +++ b/cloud/filestore/apps/client/lib/ls.cpp @@ -94,6 +94,8 @@ TString NodeTypeToString(NProto::ENodeType nodeType) return "l"; case NProto::E_SOCK_NODE: return "s"; + case NProto::E_SYMLINK_NODE: + return "L"; default: ythrow yexception() << "must be unreachable"; } diff --git a/cloud/filestore/config/storage.proto b/cloud/filestore/config/storage.proto index bbb09da9690..e7438b7fa2a 100644 --- a/cloud/filestore/config/storage.proto +++ b/cloud/filestore/config/storage.proto @@ -416,4 +416,11 @@ message TStorageConfig // automatically recover after various races that may happen during index // tablet startup due to bugs. optional uint32 MaxBackpressurePeriodBeforeSuicide = 392; // in ms + + // settings for ydb config dispatcher service. + // optional NCloud.NProto.TConfigDispatcherSettings ConfigDispatcherSettings = 393; + + // If the number of blocks marked for deletion via large deletion markers + // exceeds this threshold, large truncate-like operations will be rejected. + optional uint64 LargeDeletionMarkersThresholdForBackpressure = 394; } diff --git a/cloud/filestore/libs/diagnostics/critical_events.h b/cloud/filestore/libs/diagnostics/critical_events.h index cbf49802045..cc00fb6706a 100644 --- a/cloud/filestore/libs/diagnostics/critical_events.h +++ b/cloud/filestore/libs/diagnostics/critical_events.h @@ -22,6 +22,7 @@ namespace NCloud::NFileStore{ xxx(AsyncDestroyHandleFailed) \ xxx(DuplicateRequestId) \ xxx(InvalidDupCacheEntry) \ + xxx(GeneratedOrphanNode) \ // FILESTORE_CRITICAL_EVENTS #define FILESTORE_IMPOSSIBLE_EVENTS(xxx) \ diff --git a/cloud/filestore/libs/storage/api/tablet.h b/cloud/filestore/libs/storage/api/tablet.h index 4d285c0ecdb..838fe363e55 100644 --- a/cloud/filestore/libs/storage/api/tablet.h +++ b/cloud/filestore/libs/storage/api/tablet.h @@ -32,6 +32,9 @@ namespace NCloud::NFileStore::NStorage { xxx(GetStorageConfig, __VA_ARGS__) \ xxx(GetNodeAttrBatch, __VA_ARGS__) \ xxx(WriteCompactionMap, __VA_ARGS__) \ + xxx(UnsafeDeleteNode, __VA_ARGS__) \ + xxx(UnsafeUpdateNode, __VA_ARGS__) \ + xxx(UnsafeGetNode, __VA_ARGS__) \ // FILESTORE_TABLET_REQUESTS //////////////////////////////////////////////////////////////////////////////// @@ -97,6 +100,15 @@ struct TEvIndexTablet EvWriteCompactionMapRequest = EvBegin + 33, EvWriteCompactionMapResponse, + EvUnsafeDeleteNodeRequest = EvBegin + 35, + EvUnsafeDeleteNodeResponse, + + EvUnsafeUpdateNodeRequest = EvBegin + 37, + EvUnsafeUpdateNodeResponse, + + EvUnsafeGetNodeRequest = EvBegin + 39, + EvUnsafeGetNodeResponse, + EvEnd }; diff --git a/cloud/filestore/libs/storage/core/config.cpp b/cloud/filestore/libs/storage/core/config.cpp index d25335010c0..b3cf7608c5b 100644 --- a/cloud/filestore/libs/storage/core/config.cpp +++ b/cloud/filestore/libs/storage/core/config.cpp @@ -50,11 +50,12 @@ using TAliases = NProto::TStorageConfig::TFilestoreAliases; xxx(MaxBlocksPerTruncateTx, ui32, 0 /*TODO: 32GiB/4KiB*/ )\ xxx(MaxTruncateTxInflight, ui32, 10 )\ \ - xxx(MaxFileBlocks, ui32, 300_GB / 4_KB )\ - xxx(LargeDeletionMarkersEnabled, bool, false )\ - xxx(LargeDeletionMarkerBlocks, ui64, 1_GB / 4_KB )\ - xxx(LargeDeletionMarkersThreshold, ui64, 128_GB / 4_KB )\ - xxx(LargeDeletionMarkersCleanupThreshold, ui64, 1_TB / 4_KB )\ + xxx(MaxFileBlocks, ui32, 300_GB / 4_KB )\ + xxx(LargeDeletionMarkersEnabled, bool, false )\ + xxx(LargeDeletionMarkerBlocks, ui64, 1_GB / 4_KB )\ + xxx(LargeDeletionMarkersThreshold, ui64, 128_GB / 4_KB )\ + xxx(LargeDeletionMarkersCleanupThreshold, ui64, 1_TB / 4_KB )\ + xxx(LargeDeletionMarkersThresholdForBackpressure, ui64, 10_TB / 4_KB )\ \ xxx(CompactionRetryTimeout, TDuration, TDuration::Seconds(1) )\ xxx(BlobIndexOpsPriority, \ diff --git a/cloud/filestore/libs/storage/core/config.h b/cloud/filestore/libs/storage/core/config.h index d77c8d27c07..45c54e55e28 100644 --- a/cloud/filestore/libs/storage/core/config.h +++ b/cloud/filestore/libs/storage/core/config.h @@ -266,6 +266,7 @@ class TStorageConfig ui64 GetLargeDeletionMarkerBlocks() const; ui64 GetLargeDeletionMarkersThreshold() const; ui64 GetLargeDeletionMarkersCleanupThreshold() const; + ui64 GetLargeDeletionMarkersThresholdForBackpressure() const; bool GetMultipleStageRequestThrottlingEnabled() const; }; diff --git a/cloud/filestore/libs/storage/service/CMakeLists.darwin-x86_64.txt b/cloud/filestore/libs/storage/service/CMakeLists.darwin-x86_64.txt index 9b37bfce981..7cdec8231d3 100644 --- a/cloud/filestore/libs/storage/service/CMakeLists.darwin-x86_64.txt +++ b/cloud/filestore/libs/storage/service/CMakeLists.darwin-x86_64.txt @@ -40,6 +40,7 @@ target_sources(filestore-libs-storage-service PRIVATE ${CMAKE_SOURCE_DIR}/cloud/filestore/libs/storage/service/service_actor_actions_get_storage_config.cpp ${CMAKE_SOURCE_DIR}/cloud/filestore/libs/storage/service/service_actor_actions_get_storage_config_fields.cpp ${CMAKE_SOURCE_DIR}/cloud/filestore/libs/storage/service/service_actor_actions_reassign_tablet.cpp + ${CMAKE_SOURCE_DIR}/cloud/filestore/libs/storage/service/service_actor_actions_unsafe_node_ops.cpp ${CMAKE_SOURCE_DIR}/cloud/filestore/libs/storage/service/service_actor_actions_write_compaction_map.cpp ${CMAKE_SOURCE_DIR}/cloud/filestore/libs/storage/service/service_actor_actions.cpp ${CMAKE_SOURCE_DIR}/cloud/filestore/libs/storage/service/service_actor_alterfs.cpp diff --git a/cloud/filestore/libs/storage/service/CMakeLists.linux-aarch64.txt b/cloud/filestore/libs/storage/service/CMakeLists.linux-aarch64.txt index faab3139840..359a37bf00f 100644 --- a/cloud/filestore/libs/storage/service/CMakeLists.linux-aarch64.txt +++ b/cloud/filestore/libs/storage/service/CMakeLists.linux-aarch64.txt @@ -41,6 +41,7 @@ target_sources(filestore-libs-storage-service PRIVATE ${CMAKE_SOURCE_DIR}/cloud/filestore/libs/storage/service/service_actor_actions_get_storage_config.cpp ${CMAKE_SOURCE_DIR}/cloud/filestore/libs/storage/service/service_actor_actions_get_storage_config_fields.cpp ${CMAKE_SOURCE_DIR}/cloud/filestore/libs/storage/service/service_actor_actions_reassign_tablet.cpp + ${CMAKE_SOURCE_DIR}/cloud/filestore/libs/storage/service/service_actor_actions_unsafe_node_ops.cpp ${CMAKE_SOURCE_DIR}/cloud/filestore/libs/storage/service/service_actor_actions_write_compaction_map.cpp ${CMAKE_SOURCE_DIR}/cloud/filestore/libs/storage/service/service_actor_actions.cpp ${CMAKE_SOURCE_DIR}/cloud/filestore/libs/storage/service/service_actor_alterfs.cpp diff --git a/cloud/filestore/libs/storage/service/CMakeLists.linux-x86_64.txt b/cloud/filestore/libs/storage/service/CMakeLists.linux-x86_64.txt index b563f2fde2b..2d175587201 100644 --- a/cloud/filestore/libs/storage/service/CMakeLists.linux-x86_64.txt +++ b/cloud/filestore/libs/storage/service/CMakeLists.linux-x86_64.txt @@ -41,6 +41,7 @@ target_sources(filestore-libs-storage-service PRIVATE ${CMAKE_SOURCE_DIR}/cloud/filestore/libs/storage/service/service_actor_actions_get_storage_config.cpp ${CMAKE_SOURCE_DIR}/cloud/filestore/libs/storage/service/service_actor_actions_get_storage_config_fields.cpp ${CMAKE_SOURCE_DIR}/cloud/filestore/libs/storage/service/service_actor_actions_reassign_tablet.cpp + ${CMAKE_SOURCE_DIR}/cloud/filestore/libs/storage/service/service_actor_actions_unsafe_node_ops.cpp ${CMAKE_SOURCE_DIR}/cloud/filestore/libs/storage/service/service_actor_actions_write_compaction_map.cpp ${CMAKE_SOURCE_DIR}/cloud/filestore/libs/storage/service/service_actor_actions.cpp ${CMAKE_SOURCE_DIR}/cloud/filestore/libs/storage/service/service_actor_alterfs.cpp diff --git a/cloud/filestore/libs/storage/service/CMakeLists.windows-x86_64.txt b/cloud/filestore/libs/storage/service/CMakeLists.windows-x86_64.txt index 9b37bfce981..7cdec8231d3 100644 --- a/cloud/filestore/libs/storage/service/CMakeLists.windows-x86_64.txt +++ b/cloud/filestore/libs/storage/service/CMakeLists.windows-x86_64.txt @@ -40,6 +40,7 @@ target_sources(filestore-libs-storage-service PRIVATE ${CMAKE_SOURCE_DIR}/cloud/filestore/libs/storage/service/service_actor_actions_get_storage_config.cpp ${CMAKE_SOURCE_DIR}/cloud/filestore/libs/storage/service/service_actor_actions_get_storage_config_fields.cpp ${CMAKE_SOURCE_DIR}/cloud/filestore/libs/storage/service/service_actor_actions_reassign_tablet.cpp + ${CMAKE_SOURCE_DIR}/cloud/filestore/libs/storage/service/service_actor_actions_unsafe_node_ops.cpp ${CMAKE_SOURCE_DIR}/cloud/filestore/libs/storage/service/service_actor_actions_write_compaction_map.cpp ${CMAKE_SOURCE_DIR}/cloud/filestore/libs/storage/service/service_actor_actions.cpp ${CMAKE_SOURCE_DIR}/cloud/filestore/libs/storage/service/service_actor_alterfs.cpp diff --git a/cloud/filestore/libs/storage/service/service_actor.h b/cloud/filestore/libs/storage/service/service_actor.h index aea9e4e8b0e..03ebce5aa86 100644 --- a/cloud/filestore/libs/storage/service/service_actor.h +++ b/cloud/filestore/libs/storage/service/service_actor.h @@ -189,6 +189,18 @@ class TStorageServiceActor final TRequestInfoPtr requestInfo, TString input); + NActors::IActorPtr CreateUnsafeDeleteNodeActionActor( + TRequestInfoPtr requestInfo, + TString input); + + NActors::IActorPtr CreateUnsafeUpdateNodeActionActor( + TRequestInfoPtr requestInfo, + TString input); + + NActors::IActorPtr CreateUnsafeGetNodeActionActor( + TRequestInfoPtr requestInfo, + TString input); + private: void RenderSessions(IOutputStream& out); void RenderLocalFileStores(IOutputStream& out); diff --git a/cloud/filestore/libs/storage/service/service_actor_actions.cpp b/cloud/filestore/libs/storage/service/service_actor_actions.cpp index 990dafb6a71..806be7146da 100644 --- a/cloud/filestore/libs/storage/service/service_actor_actions.cpp +++ b/cloud/filestore/libs/storage/service/service_actor_actions.cpp @@ -72,6 +72,18 @@ void TStorageServiceActor::HandleExecuteAction( "writecompactionmap", &TStorageServiceActor::CreateWriteCompactionMapActionActor }, + { + "unsafedeletenode", + &TStorageServiceActor::CreateUnsafeDeleteNodeActionActor + }, + { + "unsafeupdatenode", + &TStorageServiceActor::CreateUnsafeUpdateNodeActionActor + }, + { + "unsafegetnode", + &TStorageServiceActor::CreateUnsafeGetNodeActionActor + }, }; auto it = actions.find(action); diff --git a/cloud/filestore/libs/storage/service/service_actor_actions_unsafe_node_ops.cpp b/cloud/filestore/libs/storage/service/service_actor_actions_unsafe_node_ops.cpp new file mode 100644 index 00000000000..c49e7ee5f98 --- /dev/null +++ b/cloud/filestore/libs/storage/service/service_actor_actions_unsafe_node_ops.cpp @@ -0,0 +1,164 @@ +#include "service_actor.h" + +#include +#include +#include +#include +#include + +#include + +#include + +namespace NCloud::NFileStore::NStorage { + +using namespace NActors; + +using namespace NKikimr; + +namespace { + +//////////////////////////////////////////////////////////////////////////////// + +template +class TTabletActionActor final + : public TActorBootstrapped> +{ +private: + const TRequestInfoPtr RequestInfo; + const TString Input; + + using TBase = TActorBootstrapped>; + +public: + TTabletActionActor( + TRequestInfoPtr requestInfo, + TString input); + + void Bootstrap(const TActorContext& ctx); + +private: + void ReplyAndDie( + const TActorContext& ctx, + const TResponse::ProtoRecordType& responseRecord); + +private: + STFUNC(StateWork) + { + switch (ev->GetTypeRewrite()) { + HFunc(TResponse, HandleResponse); + + default: + HandleUnexpectedEvent(ev, TFileStoreComponents::SERVICE); + break; + } + } + + void HandleResponse(const TResponse::TPtr& ev, const TActorContext& ctx); +}; + +//////////////////////////////////////////////////////////////////////////////// + +template +TTabletActionActor::TTabletActionActor( + TRequestInfoPtr requestInfo, + TString input) + : RequestInfo(std::move(requestInfo)) + , Input(std::move(input)) +{} + +template +void TTabletActionActor::Bootstrap( + const TActorContext& ctx) +{ + typename TRequest::ProtoRecordType request; + if (!google::protobuf::util::JsonStringToMessage(Input, &request).ok()) { + ReplyAndDie( + ctx, + TErrorResponse(E_ARGUMENT, "Failed to parse input")); + return; + } + + if (!request.GetFileSystemId()) { + ReplyAndDie( + ctx, + TErrorResponse(E_ARGUMENT, "FileSystem id should be supplied")); + return; + } + + auto requestToTablet = std::make_unique(); + requestToTablet->Record = std::move(request); + + NCloud::Send( + ctx, + MakeIndexTabletProxyServiceId(), + std::move(requestToTablet)); + + TBase::Become(&TTabletActionActor::StateWork); +} + +template +void TTabletActionActor::ReplyAndDie( + const TActorContext& ctx, + const TResponse::ProtoRecordType& response) +{ + auto msg = std::make_unique( + response.GetError()); + + google::protobuf::util::MessageToJsonString( + response, + msg->Record.MutableOutput()); + + NCloud::Reply(ctx, *RequestInfo, std::move(msg)); + TBase::Die(ctx); +} + +//////////////////////////////////////////////////////////////////////////////// + +template +void TTabletActionActor::HandleResponse( + const TResponse::TPtr& ev, + const TActorContext& ctx) +{ + ReplyAndDie(ctx, ev->Get()->Record); +} + +} // namespace + +IActorPtr TStorageServiceActor::CreateUnsafeDeleteNodeActionActor( + TRequestInfoPtr requestInfo, + TString input) +{ + using TUnsafeDeleteNodeActor = TTabletActionActor< + TEvIndexTablet::TEvUnsafeDeleteNodeRequest, + TEvIndexTablet::TEvUnsafeDeleteNodeResponse>; + return std::make_unique( + std::move(requestInfo), + std::move(input)); +} + +IActorPtr TStorageServiceActor::CreateUnsafeUpdateNodeActionActor( + TRequestInfoPtr requestInfo, + TString input) +{ + using TUnsafeUpdateNodeActor = TTabletActionActor< + TEvIndexTablet::TEvUnsafeUpdateNodeRequest, + TEvIndexTablet::TEvUnsafeUpdateNodeResponse>; + return std::make_unique( + std::move(requestInfo), + std::move(input)); +} + +IActorPtr TStorageServiceActor::CreateUnsafeGetNodeActionActor( + TRequestInfoPtr requestInfo, + TString input) +{ + using TUnsafeGetNodeActor = TTabletActionActor< + TEvIndexTablet::TEvUnsafeGetNodeRequest, + TEvIndexTablet::TEvUnsafeGetNodeResponse>; + return std::make_unique( + std::move(requestInfo), + std::move(input)); +} + +} // namespace NCloud::NFileStore::NStorage diff --git a/cloud/filestore/libs/storage/service/service_actor_actions_ut.cpp b/cloud/filestore/libs/storage/service/service_actor_actions_ut.cpp index e99345d2f49..3d15bf88c1a 100644 --- a/cloud/filestore/libs/storage/service/service_actor_actions_ut.cpp +++ b/cloud/filestore/libs/storage/service/service_actor_actions_ut.cpp @@ -4,6 +4,9 @@ #include #include #include +#include + +#include namespace NCloud::NFileStore::NStorage { @@ -81,7 +84,8 @@ Y_UNIT_TEST_SUITE(TStorageServiceActionsTest) ui32 nodeIdx = env.CreateNode("nfs"); TServiceClient service(env.GetRuntime(), nodeIdx); - auto response = service.AssertExecuteActionFailed("NonExistingAction", "{}"); + auto response = + service.AssertExecuteActionFailed("NonExistingAction", "{}"); UNIT_ASSERT_VALUES_UNEQUAL(S_OK, response->GetStatus()); } @@ -177,6 +181,117 @@ Y_UNIT_TEST_SUITE(TStorageServiceActionsTest) true); } } + + Y_UNIT_TEST(ShouldPerformUnsafeNodeManipulations) + { + NProto::TStorageConfig config; + TTestEnv env{{}, config}; + env.CreateSubDomain("nfs"); + + ui32 nodeIdx = env.CreateNode("nfs"); + + TServiceClient service(env.GetRuntime(), nodeIdx); + + const TString fsId = "test"; + service.CreateFileStore(fsId, 1'000); + + auto headers = service.InitSession("test", "client"); + + service.CreateNode(headers, TCreateNodeArgs::File(RootNodeId, "file1")); + service.CreateNode(headers, TCreateNodeArgs::File(RootNodeId, "file2")); + + ui64 id1 = 0; + ui64 id2 = 0; + ui64 id3 = 0; + + { + auto r = service.ListNodes(headers, fsId, RootNodeId)->Record; + UNIT_ASSERT_VALUES_EQUAL(2, r.NodesSize()); + UNIT_ASSERT_VALUES_EQUAL(0, r.GetNodes(0).GetSize()); + UNIT_ASSERT_VALUES_EQUAL(0, r.GetNodes(1).GetSize()); + + id1 = r.GetNodes(0).GetId(); + id2 = r.GetNodes(1).GetId(); + + UNIT_ASSERT_VALUES_UNEQUAL(0, id1); + UNIT_ASSERT_VALUES_UNEQUAL(0, id2); + } + + id3 = Max(id1, id2) + 1; + + { + NProtoPrivate::TUnsafeUpdateNodeRequest request; + request.SetFileSystemId(fsId); + auto* node = request.MutableNode(); + node->SetId(id3); + node->SetSize(333); + TString buf; + google::protobuf::util::MessageToJsonString(request, &buf); + service.ExecuteAction("UnsafeUpdateNode", buf); + } + + { + NProtoPrivate::TUnsafeGetNodeRequest request; + request.SetFileSystemId(fsId); + request.SetId(id3); + TString buf; + google::protobuf::util::MessageToJsonString(request, &buf); + auto r = service.ExecuteAction("UnsafeGetNode", buf)->Record; + + NProtoPrivate::TUnsafeGetNodeResponse response; + UNIT_ASSERT(google::protobuf::util::JsonStringToMessage( + r.GetOutput(), + &response).ok()); + + UNIT_ASSERT_VALUES_EQUAL(id3, response.GetNode().GetId()); + UNIT_ASSERT_VALUES_EQUAL(333, response.GetNode().GetSize()); + } + + { + NProtoPrivate::TUnsafeUpdateNodeRequest request; + request.SetFileSystemId(fsId); + auto* node = request.MutableNode(); + node->SetId(id2); + node->SetSize(222); + TString buf; + google::protobuf::util::MessageToJsonString(request, &buf); + service.ExecuteAction("UnsafeUpdateNode", buf); + } + + { + auto r = service.ListNodes(headers, fsId, RootNodeId)->Record; + UNIT_ASSERT_VALUES_EQUAL(2, r.NodesSize()); + UNIT_ASSERT_VALUES_EQUAL(0, r.GetNodes(0).GetSize()); + UNIT_ASSERT_VALUES_EQUAL(222, r.GetNodes(1).GetSize()); + } + + { + NProtoPrivate::TUnsafeDeleteNodeRequest request; + request.SetFileSystemId(fsId); + request.SetId(id3); + TString buf; + google::protobuf::util::MessageToJsonString(request, &buf); + service.ExecuteAction("UnsafeDeleteNode", buf); + } + + { + NProtoPrivate::TUnsafeGetNodeRequest request; + request.SetFileSystemId(fsId); + request.SetId(id3); + TString buf; + google::protobuf::util::MessageToJsonString(request, &buf); + auto r = service.ExecuteAction("UnsafeGetNode", buf)->Record; + + NProtoPrivate::TUnsafeGetNodeResponse response; + UNIT_ASSERT(google::protobuf::util::JsonStringToMessage( + r.GetOutput(), + &response).ok()); + + UNIT_ASSERT(!response.HasNode()); + } + + service.DestroySession(headers); + } } } // namespace NCloud::NFileStore::NStorage diff --git a/cloud/filestore/libs/storage/service/ya.make b/cloud/filestore/libs/storage/service/ya.make index c7a1b49d183..7984946192f 100644 --- a/cloud/filestore/libs/storage/service/ya.make +++ b/cloud/filestore/libs/storage/service/ya.make @@ -13,6 +13,7 @@ SRCS( service_actor_actions_get_storage_config_fields.cpp service_actor_actions_get_storage_config.cpp service_actor_actions_reassign_tablet.cpp + service_actor_actions_unsafe_node_ops.cpp service_actor_actions_write_compaction_map.cpp service_actor_actions.cpp service_actor_alterfs.cpp diff --git a/cloud/filestore/libs/storage/tablet/CMakeLists.darwin-x86_64.txt b/cloud/filestore/libs/storage/tablet/CMakeLists.darwin-x86_64.txt index 3096a07705a..c84bf453b10 100644 --- a/cloud/filestore/libs/storage/tablet/CMakeLists.darwin-x86_64.txt +++ b/cloud/filestore/libs/storage/tablet/CMakeLists.darwin-x86_64.txt @@ -117,6 +117,7 @@ target_sources(libs-storage-tablet PRIVATE ${CMAKE_SOURCE_DIR}/cloud/filestore/libs/storage/tablet/tablet_actor_throttling.cpp ${CMAKE_SOURCE_DIR}/cloud/filestore/libs/storage/tablet/tablet_actor_truncate.cpp ${CMAKE_SOURCE_DIR}/cloud/filestore/libs/storage/tablet/tablet_actor_unlinknode.cpp + ${CMAKE_SOURCE_DIR}/cloud/filestore/libs/storage/tablet/tablet_actor_unsafe_node_ops.cpp ${CMAKE_SOURCE_DIR}/cloud/filestore/libs/storage/tablet/tablet_actor_updateconfig.cpp ${CMAKE_SOURCE_DIR}/cloud/filestore/libs/storage/tablet/tablet_actor_waitready.cpp ${CMAKE_SOURCE_DIR}/cloud/filestore/libs/storage/tablet/tablet_actor_writebatch.cpp diff --git a/cloud/filestore/libs/storage/tablet/CMakeLists.linux-aarch64.txt b/cloud/filestore/libs/storage/tablet/CMakeLists.linux-aarch64.txt index 064d549aa8a..db6c0073329 100644 --- a/cloud/filestore/libs/storage/tablet/CMakeLists.linux-aarch64.txt +++ b/cloud/filestore/libs/storage/tablet/CMakeLists.linux-aarch64.txt @@ -118,6 +118,7 @@ target_sources(libs-storage-tablet PRIVATE ${CMAKE_SOURCE_DIR}/cloud/filestore/libs/storage/tablet/tablet_actor_throttling.cpp ${CMAKE_SOURCE_DIR}/cloud/filestore/libs/storage/tablet/tablet_actor_truncate.cpp ${CMAKE_SOURCE_DIR}/cloud/filestore/libs/storage/tablet/tablet_actor_unlinknode.cpp + ${CMAKE_SOURCE_DIR}/cloud/filestore/libs/storage/tablet/tablet_actor_unsafe_node_ops.cpp ${CMAKE_SOURCE_DIR}/cloud/filestore/libs/storage/tablet/tablet_actor_updateconfig.cpp ${CMAKE_SOURCE_DIR}/cloud/filestore/libs/storage/tablet/tablet_actor_waitready.cpp ${CMAKE_SOURCE_DIR}/cloud/filestore/libs/storage/tablet/tablet_actor_writebatch.cpp diff --git a/cloud/filestore/libs/storage/tablet/CMakeLists.linux-x86_64.txt b/cloud/filestore/libs/storage/tablet/CMakeLists.linux-x86_64.txt index 064d549aa8a..db6c0073329 100644 --- a/cloud/filestore/libs/storage/tablet/CMakeLists.linux-x86_64.txt +++ b/cloud/filestore/libs/storage/tablet/CMakeLists.linux-x86_64.txt @@ -118,6 +118,7 @@ target_sources(libs-storage-tablet PRIVATE ${CMAKE_SOURCE_DIR}/cloud/filestore/libs/storage/tablet/tablet_actor_throttling.cpp ${CMAKE_SOURCE_DIR}/cloud/filestore/libs/storage/tablet/tablet_actor_truncate.cpp ${CMAKE_SOURCE_DIR}/cloud/filestore/libs/storage/tablet/tablet_actor_unlinknode.cpp + ${CMAKE_SOURCE_DIR}/cloud/filestore/libs/storage/tablet/tablet_actor_unsafe_node_ops.cpp ${CMAKE_SOURCE_DIR}/cloud/filestore/libs/storage/tablet/tablet_actor_updateconfig.cpp ${CMAKE_SOURCE_DIR}/cloud/filestore/libs/storage/tablet/tablet_actor_waitready.cpp ${CMAKE_SOURCE_DIR}/cloud/filestore/libs/storage/tablet/tablet_actor_writebatch.cpp diff --git a/cloud/filestore/libs/storage/tablet/CMakeLists.windows-x86_64.txt b/cloud/filestore/libs/storage/tablet/CMakeLists.windows-x86_64.txt index 3096a07705a..c84bf453b10 100644 --- a/cloud/filestore/libs/storage/tablet/CMakeLists.windows-x86_64.txt +++ b/cloud/filestore/libs/storage/tablet/CMakeLists.windows-x86_64.txt @@ -117,6 +117,7 @@ target_sources(libs-storage-tablet PRIVATE ${CMAKE_SOURCE_DIR}/cloud/filestore/libs/storage/tablet/tablet_actor_throttling.cpp ${CMAKE_SOURCE_DIR}/cloud/filestore/libs/storage/tablet/tablet_actor_truncate.cpp ${CMAKE_SOURCE_DIR}/cloud/filestore/libs/storage/tablet/tablet_actor_unlinknode.cpp + ${CMAKE_SOURCE_DIR}/cloud/filestore/libs/storage/tablet/tablet_actor_unsafe_node_ops.cpp ${CMAKE_SOURCE_DIR}/cloud/filestore/libs/storage/tablet/tablet_actor_updateconfig.cpp ${CMAKE_SOURCE_DIR}/cloud/filestore/libs/storage/tablet/tablet_actor_waitready.cpp ${CMAKE_SOURCE_DIR}/cloud/filestore/libs/storage/tablet/tablet_actor_writebatch.cpp diff --git a/cloud/filestore/libs/storage/tablet/helpers.cpp b/cloud/filestore/libs/storage/tablet/helpers.cpp index cf73eec0344..2f17e422d94 100644 --- a/cloud/filestore/libs/storage/tablet/helpers.cpp +++ b/cloud/filestore/libs/storage/tablet/helpers.cpp @@ -87,7 +87,10 @@ NProto::TNode CopyAttrs(const NProto::TNode& src, ui32 mode) return node; } -void ConvertNodeFromAttrs(NProto::TNodeAttr& dst, ui64 id, const NProto::TNode& src) +void ConvertNodeFromAttrs( + NProto::TNodeAttr& dst, + ui64 id, + const NProto::TNode& src) { dst.SetId(id); dst.SetType(src.GetType()); @@ -101,6 +104,19 @@ void ConvertNodeFromAttrs(NProto::TNodeAttr& dst, ui64 id, const NProto::TNode& dst.SetLinks(src.GetLinks()); } +void ConvertAttrsToNode(const NProto::TNodeAttr& src, NProto::TNode* dst) +{ + dst->SetType(src.GetType()); + dst->SetMode(src.GetMode()); + dst->SetUid(src.GetUid()); + dst->SetGid(src.GetGid()); + dst->SetATime(src.GetATime()); + dst->SetMTime(src.GetMTime()); + dst->SetCTime(src.GetCTime()); + dst->SetSize(src.GetSize()); + dst->SetLinks(src.GetLinks()); +} + //////////////////////////////////////////////////////////////////////////////// NProto::TError ValidateNodeName(const TString& name) diff --git a/cloud/filestore/libs/storage/tablet/helpers.h b/cloud/filestore/libs/storage/tablet/helpers.h index c026f2abc91..8b915c0f4f7 100644 --- a/cloud/filestore/libs/storage/tablet/helpers.h +++ b/cloud/filestore/libs/storage/tablet/helpers.h @@ -64,7 +64,8 @@ namespace NCloud::NFileStore::NStorage { //////////////////////////////////////////////////////////////////////////////// -enum ECopyAttrsMode { +enum ECopyAttrsMode +{ E_CM_CTIME = 1, // CTime E_CM_MTIME = 2, // MTime @@ -84,7 +85,12 @@ NProto::TNode CreateSocketAttrs(ui32 mode, ui32 uid, ui32 gid); NProto::TNode CopyAttrs(const NProto::TNode& src, ui32 mode = E_CM_CTIME); -void ConvertNodeFromAttrs(NProto::TNodeAttr& dst, ui64 id, const NProto::TNode& src); +void ConvertNodeFromAttrs( + NProto::TNodeAttr& dst, + ui64 id, + const NProto::TNode& src); + +void ConvertAttrsToNode(const NProto::TNodeAttr& src, NProto::TNode* dst); //////////////////////////////////////////////////////////////////////////////// diff --git a/cloud/filestore/libs/storage/tablet/tablet_actor.cpp b/cloud/filestore/libs/storage/tablet/tablet_actor.cpp index a148082bd22..2fcb5afa42c 100644 --- a/cloud/filestore/libs/storage/tablet/tablet_actor.cpp +++ b/cloud/filestore/libs/storage/tablet/tablet_actor.cpp @@ -509,10 +509,19 @@ TCleanupInfo TIndexTabletActor::GetCleanupInfo() const avgCleanupScore >= Config->GetCleanupThresholdAverage(); bool isPriority = false; - if (auto priorityRange = NextPriorityRangeForCleanup()) { - cleanupRangeId = priorityRange->RangeId; - cleanupScore = Max(); - isPriority = true; + TString dummy; + // if we are close to our write backpressure thresholds, it's better to + // clean up normal deletion markers first in order not to freeze write + // requests + // + // large deletion marker cleanup is a slower process and having too many + // large deletion markers affects a much smaller percentage of workloads + if (!IsCloseToBackpressureThresholds(&dummy)) { + if (auto priorityRange = NextPriorityRangeForCleanup()) { + cleanupRangeId = priorityRange->RangeId; + cleanupScore = Max(); + isPriority = true; + } } return { @@ -532,6 +541,17 @@ TCleanupInfo TIndexTabletActor::GetCleanupInfo() const || isPriority}; } +bool TIndexTabletActor::IsCloseToBackpressureThresholds(TString* message) const +{ + auto bpThresholds = BuildBackpressureThresholds(); + const double scale = + Config->GetBackpressurePercentageForFairBlobIndexOpsPriority() + / 100.; + bpThresholds.CompactionScore *= scale; + bpThresholds.CleanupScore *= scale; + return !IsWriteAllowed(bpThresholds, GetBackpressureValues(), message); +} + //////////////////////////////////////////////////////////////////////////////// void TIndexTabletActor::HandleWakeup( diff --git a/cloud/filestore/libs/storage/tablet/tablet_actor.h b/cloud/filestore/libs/storage/tablet/tablet_actor.h index ef2af6bf376..cfb0ab84f5f 100644 --- a/cloud/filestore/libs/storage/tablet/tablet_actor.h +++ b/cloud/filestore/libs/storage/tablet/tablet_actor.h @@ -151,6 +151,8 @@ class TIndexTabletActor final std::atomic NodesOpenForReadingBySingleSession{0}; std::atomic NodesOpenForReadingByMultipleSessions{0}; + std::atomic OrphanNodesCount{0}; + NMetrics::TDefaultWindowCalculator MaxUsedQuota{0}; using TLatHistogram = NMetrics::THistogram; @@ -216,7 +218,8 @@ class TIndexTabletActor final const TChannelsStats& channelsStats, const TReadAheadCacheStats& readAheadStats, const TNodeIndexCacheStats& nodeIndexCacheStats, - const TNodeToSessionCounters& nodeToSessionCounters); + const TNodeToSessionCounters& nodeToSessionCounters, + const TMiscNodeStats& miscNodeStats); } Metrics; const IProfileLogPtr ProfileLog; @@ -523,6 +526,7 @@ class TIndexTabletActor final ui32 ScaleCompactionThreshold(ui32 t) const; TCompactionInfo GetCompactionInfo() const; TCleanupInfo GetCleanupInfo() const; + bool IsCloseToBackpressureThresholds(TString* message) const; void HandleWakeup( const NActors::TEvents::TEvWakeup::TPtr& ev, diff --git a/cloud/filestore/libs/storage/tablet/tablet_actor_allocatedata.cpp b/cloud/filestore/libs/storage/tablet/tablet_actor_allocatedata.cpp index 9e6a9b35116..93e380a7bea 100644 --- a/cloud/filestore/libs/storage/tablet/tablet_actor_allocatedata.cpp +++ b/cloud/filestore/libs/storage/tablet/tablet_actor_allocatedata.cpp @@ -199,11 +199,15 @@ void TIndexTabletActor::ExecuteTx_AllocateData( if (args.CommitId == InvalidCommitId) { return RebootTabletOnCommitOverflow(ctx, "AllocateData"); } - ZeroRange( + auto e = ZeroRange( db, args.NodeId, args.CommitId, TByteRange(args.Offset, minBorder - args.Offset, GetBlockSize())); + if (HasError(e)) { + args.Error = std::move(e); + return; + } } if (!needExtend) { diff --git a/cloud/filestore/libs/storage/tablet/tablet_actor_cleanup.cpp b/cloud/filestore/libs/storage/tablet/tablet_actor_cleanup.cpp index 2ea823b6427..617668a84cb 100644 --- a/cloud/filestore/libs/storage/tablet/tablet_actor_cleanup.cpp +++ b/cloud/filestore/libs/storage/tablet/tablet_actor_cleanup.cpp @@ -36,7 +36,8 @@ void TIndexTabletActor::HandleCleanup( return; } - auto response = std::make_unique(error); + auto response = + std::make_unique(error); NCloud::Reply(ctx, *ev, std::move(response)); }; diff --git a/cloud/filestore/libs/storage/tablet/tablet_actor_compaction.cpp b/cloud/filestore/libs/storage/tablet/tablet_actor_compaction.cpp index 4f05c8288ea..8f388e7ca70 100644 --- a/cloud/filestore/libs/storage/tablet/tablet_actor_compaction.cpp +++ b/cloud/filestore/libs/storage/tablet/tablet_actor_compaction.cpp @@ -326,14 +326,8 @@ void TIndexTabletActor::EnqueueBlobIndexOpIfNeeded(const TActorContext& ctx) if (IsBlobIndexOpsQueueEmpty()) { auto blobIndexOpsPriority = Config->GetBlobIndexOpsPriority(); - auto bpThresholds = BuildBackpressureThresholds(); - const double scale = - Config->GetBackpressurePercentageForFairBlobIndexOpsPriority() - / 100.; - bpThresholds.CompactionScore *= scale; - bpThresholds.CleanupScore *= scale; TString message; - if (!IsWriteAllowed(bpThresholds, GetBackpressureValues(), &message)) { + if (IsCloseToBackpressureThresholds(&message)) { // if we are close to our backpressure thresholds, we should fall // back to fair scheduling so that all operations show some progress blobIndexOpsPriority = NProto::BIOP_FAIR; diff --git a/cloud/filestore/libs/storage/tablet/tablet_actor_counters.cpp b/cloud/filestore/libs/storage/tablet/tablet_actor_counters.cpp index 1ebe52099f3..4db8cc72d1a 100644 --- a/cloud/filestore/libs/storage/tablet/tablet_actor_counters.cpp +++ b/cloud/filestore/libs/storage/tablet/tablet_actor_counters.cpp @@ -322,6 +322,8 @@ void TIndexTabletActor::TMetrics::Register( NodesOpenForReadingByMultipleSessions, EMetricType::MT_ABSOLUTE); + REGISTER_AGGREGATABLE_SUM(OrphanNodesCount, EMetricType::MT_ABSOLUTE); + // Throttling REGISTER_LOCAL(MaxReadBandwidth, EMetricType::MT_ABSOLUTE); REGISTER_LOCAL(MaxWriteBandwidth, EMetricType::MT_ABSOLUTE); @@ -400,7 +402,8 @@ void TIndexTabletActor::TMetrics::Update( const TChannelsStats& channelsStats, const TReadAheadCacheStats& readAheadStats, const TNodeIndexCacheStats& nodeIndexCacheStats, - const TNodeToSessionCounters& nodeToSessionCounters) + const TNodeToSessionCounters& nodeToSessionCounters, + const TMiscNodeStats& miscNodeStats) { const ui32 blockSize = fileSystem.GetBlockSize(); @@ -472,6 +475,8 @@ void TIndexTabletActor::TMetrics::Update( NodesOpenForReadingByMultipleSessions, nodeToSessionCounters.NodesOpenForReadingByMultipleSessions); + Store(OrphanNodesCount, miscNodeStats.OrphanNodesCount); + BusyIdleCalc.OnUpdateStats(); } @@ -520,7 +525,8 @@ void TIndexTabletActor::RegisterStatCounters() CalculateChannelsStats(), CalculateReadAheadCacheStats(), CalculateNodeIndexCacheStats(), - GetNodeToSessionCounters()); + GetNodeToSessionCounters(), + GetMiscNodeStats()); Metrics.Register(fsId, storageMediaKind); } @@ -566,7 +572,8 @@ void TIndexTabletActor::HandleUpdateCounters( CalculateChannelsStats(), CalculateReadAheadCacheStats(), CalculateNodeIndexCacheStats(), - GetNodeToSessionCounters()); + GetNodeToSessionCounters(), + GetMiscNodeStats()); SendMetricsToExecutor(ctx); UpdateCountersScheduled = false; diff --git a/cloud/filestore/libs/storage/tablet/tablet_actor_createhandle.cpp b/cloud/filestore/libs/storage/tablet/tablet_actor_createhandle.cpp index 12652022067..ef74c8a47b4 100644 --- a/cloud/filestore/libs/storage/tablet/tablet_actor_createhandle.cpp +++ b/cloud/filestore/libs/storage/tablet/tablet_actor_createhandle.cpp @@ -309,12 +309,16 @@ void TIndexTabletActor::ExecuteTx_CreateHandle( } else if (args.FollowerId.Empty() && HasFlag(args.Flags, NProto::TCreateHandleRequest::E_TRUNCATE)) { - Truncate( + auto e = Truncate( db, args.TargetNodeId, args.WriteCommitId, args.TargetNode->Attrs.GetSize(), 0); + if (HasError(e)) { + args.Error = std::move(e); + return; + } auto attrs = CopyAttrs(args.TargetNode->Attrs, E_CM_CMTIME); attrs.SetSize(0); diff --git a/cloud/filestore/libs/storage/tablet/tablet_actor_destroyhandle.cpp b/cloud/filestore/libs/storage/tablet/tablet_actor_destroyhandle.cpp index e88ea160713..a4395fca245 100644 --- a/cloud/filestore/libs/storage/tablet/tablet_actor_destroyhandle.cpp +++ b/cloud/filestore/libs/storage/tablet/tablet_actor_destroyhandle.cpp @@ -104,11 +104,19 @@ void TIndexTabletActor::ExecuteTx_DestroyHandle( if (args.Node->Attrs.GetLinks() == 0 && !HasOpenHandles(args.Node->NodeId)) { - RemoveNode( + auto e = RemoveNode( db, *args.Node, args.Node->MinCommitId, commitId); + + if (HasError(e)) { + WriteOrphanNode(db, TStringBuilder() + << "DestroyHandle: " << args.SessionId + << ", Handle: " << args.Request.GetHandle() + << ", RemoveNode: " << args.Node->NodeId + << ", Error: " << FormatError(e), args.Node->NodeId); + } } EnqueueTruncateIfNeeded(ctx); diff --git a/cloud/filestore/libs/storage/tablet/tablet_actor_destroysession.cpp b/cloud/filestore/libs/storage/tablet/tablet_actor_destroysession.cpp index c79608df8a2..1e46210ead7 100644 --- a/cloud/filestore/libs/storage/tablet/tablet_actor_destroysession.cpp +++ b/cloud/filestore/libs/storage/tablet/tablet_actor_destroysession.cpp @@ -308,11 +308,18 @@ void TIndexTabletActor::ExecuteTx_DestroySession( auto it = args.Nodes.find(nodeId); if (it != args.Nodes.end() && !HasOpenHandles(nodeId)) { - RemoveNode( + auto e = RemoveNode( db, *it, it->MinCommitId, commitId); + + if (HasError(e)) { + WriteOrphanNode(db, TStringBuilder() + << "DestroySession: " << args.SessionId + << ", RemoveNode: " << nodeId + << ", Error: " << FormatError(e), nodeId); + } } } diff --git a/cloud/filestore/libs/storage/tablet/tablet_actor_loadstate.cpp b/cloud/filestore/libs/storage/tablet/tablet_actor_loadstate.cpp index 576b5c1e6b1..4c53ec35f09 100644 --- a/cloud/filestore/libs/storage/tablet/tablet_actor_loadstate.cpp +++ b/cloud/filestore/libs/storage/tablet/tablet_actor_loadstate.cpp @@ -112,6 +112,7 @@ bool TIndexTabletActor::PrepareTx_LoadState( db.ReadSessionHistoryEntries(args.SessionHistory), db.ReadOpLog(args.OpLog), db.ReadLargeDeletionMarkers(args.LargeDeletionMarkers), + db.ReadOrphanNodes(args.OrphanNodeIds), }; bool ready = std::accumulate( @@ -231,9 +232,16 @@ void TIndexTabletActor::CompleteTx_LoadState( LOG_INFO_S(ctx, TFileStoreComponents::TABLET, LogTag << " Initializing tablet state"); - LOG_INFO_S(ctx, TFileStoreComponents::TABLET, - LogTag << " Read " << args.LargeDeletionMarkers.size() - << " large deletion markers"); + if (args.LargeDeletionMarkers) { + LOG_INFO_S(ctx, TFileStoreComponents::TABLET, + LogTag << " Read " << args.LargeDeletionMarkers.size() + << " large deletion markers"); + } + if (args.OrphanNodeIds) { + LOG_INFO_S(ctx, TFileStoreComponents::TABLET, + LogTag << " Read " << args.OrphanNodeIds.size() + << " orphan nodes"); + } LoadState( Executor()->Generation(), @@ -242,6 +250,7 @@ void TIndexTabletActor::CompleteTx_LoadState( args.FileSystemStats, args.TabletStorageInfo, args.LargeDeletionMarkers, + args.OrphanNodeIds, config); UpdateLogTag(); diff --git a/cloud/filestore/libs/storage/tablet/tablet_actor_renamenode.cpp b/cloud/filestore/libs/storage/tablet/tablet_actor_renamenode.cpp index b4a16465a75..451adf5f318 100644 --- a/cloud/filestore/libs/storage/tablet/tablet_actor_renamenode.cpp +++ b/cloud/filestore/libs/storage/tablet/tablet_actor_renamenode.cpp @@ -322,13 +322,22 @@ void TIndexTabletActor::ExecuteTx_RenameNode( } // remove target ref and unlink target node - UnlinkNode( + auto e = UnlinkNode( db, args.NewParentNode->NodeId, args.NewName, *args.NewChildNode, args.NewChildRef->MinCommitId, args.CommitId); + + if (HasError(e)) { + const auto nodeId = args.NewChildNode->NodeId; + WriteOrphanNode(db, TStringBuilder() + << "RenameNode: " << args.SessionId + << ", ParentNodeId: " << args.NewParentNode->NodeId + << ", NodeId: " << nodeId + << ", Error: " << FormatError(e), nodeId); + } } else { // remove target ref UnlinkExternalNode( diff --git a/cloud/filestore/libs/storage/tablet/tablet_actor_resetsession.cpp b/cloud/filestore/libs/storage/tablet/tablet_actor_resetsession.cpp index 4445cde0808..21d6cf98c4d 100644 --- a/cloud/filestore/libs/storage/tablet/tablet_actor_resetsession.cpp +++ b/cloud/filestore/libs/storage/tablet/tablet_actor_resetsession.cpp @@ -142,11 +142,18 @@ void TIndexTabletActor::ExecuteTx_ResetSession( nodeId, it->Attrs.GetSize()); - RemoveNode( + auto e = RemoveNode( db, *it, it->MinCommitId, commitId); + + if (HasError(e)) { + WriteOrphanNode(db, TStringBuilder() + << "DestroySession: " << args.SessionId + << ", RemoveNode: " << nodeId + << ", Error: " << FormatError(e), nodeId); + } } } diff --git a/cloud/filestore/libs/storage/tablet/tablet_actor_setnodeattr.cpp b/cloud/filestore/libs/storage/tablet/tablet_actor_setnodeattr.cpp index d1b49ddbfe5..bc7db996d84 100644 --- a/cloud/filestore/libs/storage/tablet/tablet_actor_setnodeattr.cpp +++ b/cloud/filestore/libs/storage/tablet/tablet_actor_setnodeattr.cpp @@ -167,13 +167,18 @@ void TIndexTabletActor::ExecuteTx_SetNodeAttr( attrs.SetCTime(update.GetCTime()); } if (HasFlag(flags, NProto::TSetNodeAttrRequest::F_SET_ATTR_SIZE)) { - Truncate( + auto e = Truncate( db, args.NodeId, args.CommitId, attrs.GetSize(), update.GetSize()); + if (HasError(e)) { + args.Error = e; + return; + } + attrs.SetSize(update.GetSize()); } diff --git a/cloud/filestore/libs/storage/tablet/tablet_actor_truncate.cpp b/cloud/filestore/libs/storage/tablet/tablet_actor_truncate.cpp index 7a2f116cb75..f19b44227f4 100644 --- a/cloud/filestore/libs/storage/tablet/tablet_actor_truncate.cpp +++ b/cloud/filestore/libs/storage/tablet/tablet_actor_truncate.cpp @@ -322,7 +322,7 @@ void TIndexTabletActor::ExecuteTx_TruncateRange( args.Range.Length, args.ProfileLogRequest); - TruncateRange(db, args.NodeId, commitId, args.Range); + args.Error = TruncateRange(db, args.NodeId, commitId, args.Range); } void TIndexTabletActor::CompleteTx_TruncateRange( @@ -334,7 +334,7 @@ void TIndexTabletActor::CompleteTx_TruncateRange( std::move(args.ProfileLogRequest), ctx.Now(), GetFileSystemId(), - {}, + args.Error, ProfileLog); LOG_DEBUG(ctx, TFileStoreComponents::TABLET, @@ -343,7 +343,9 @@ void TIndexTabletActor::CompleteTx_TruncateRange( args.NodeId, args.Range.Describe().c_str()); - auto response = std::make_unique(); + auto response = + std::make_unique( + std::move(args.Error)); NCloud::Reply(ctx, *args.RequestInfo, std::move(response)); EnqueueCollectGarbageIfNeeded(ctx); diff --git a/cloud/filestore/libs/storage/tablet/tablet_actor_unlinknode.cpp b/cloud/filestore/libs/storage/tablet/tablet_actor_unlinknode.cpp index a03677707ab..27ac03b21e5 100644 --- a/cloud/filestore/libs/storage/tablet/tablet_actor_unlinknode.cpp +++ b/cloud/filestore/libs/storage/tablet/tablet_actor_unlinknode.cpp @@ -383,13 +383,18 @@ void TIndexTabletActor::ExecuteTx_UnlinkNode( db.WriteOpLogEntry(args.OpLogEntry); } else { - UnlinkNode( + auto e = UnlinkNode( db, args.ParentNodeId, args.Name, *args.ChildNode, args.ChildRef->MinCommitId, args.CommitId); + + if (HasError(e)) { + args.Error = std::move(e); + return; + } } auto* session = FindSession(args.SessionId); diff --git a/cloud/filestore/libs/storage/tablet/tablet_actor_unsafe_node_ops.cpp b/cloud/filestore/libs/storage/tablet/tablet_actor_unsafe_node_ops.cpp new file mode 100644 index 00000000000..cef98ff1bdb --- /dev/null +++ b/cloud/filestore/libs/storage/tablet/tablet_actor_unsafe_node_ops.cpp @@ -0,0 +1,276 @@ +#include "tablet_actor.h" + +#include "helpers.h" + +namespace NCloud::NFileStore::NStorage { + +using namespace NActors; + +using namespace NKikimr; +using namespace NKikimr::NTabletFlatExecutor; + +//////////////////////////////////////////////////////////////////////////////// + +void TIndexTabletActor::HandleUnsafeDeleteNode( + const TEvIndexTablet::TEvUnsafeDeleteNodeRequest::TPtr& ev, + const TActorContext& ctx) +{ + auto* msg = ev->Get(); + + auto requestInfo = CreateRequestInfo( + ev->Sender, + ev->Cookie, + msg->CallContext); + + AddTransaction(*requestInfo); + + LOG_WARN(ctx, TFileStoreComponents::TABLET, + "%s UnsafeDeleteNode: %s", + LogTag.c_str(), + msg->Record.DebugString().Quote().c_str()); + + ExecuteTx( + ctx, + std::move(requestInfo), + std::move(msg->Record)); +} + +//////////////////////////////////////////////////////////////////////////////// + +bool TIndexTabletActor::PrepareTx_UnsafeDeleteNode( + const TActorContext& ctx, + TTransactionContext& tx, + TTxIndexTablet::TUnsafeDeleteNode& args) +{ + Y_UNUSED(ctx); + + auto commitId = GetCurrentCommitId(); + + TIndexTabletDatabaseProxy db(tx.DB, args.NodeUpdates); + return ReadNode(db, args.Request.GetId(), commitId, args.Node); +} + +void TIndexTabletActor::ExecuteTx_UnsafeDeleteNode( + const TActorContext& ctx, + TTransactionContext& tx, + TTxIndexTablet::TUnsafeDeleteNode& args) +{ + TIndexTabletDatabaseProxy db(tx.DB, args.NodeUpdates); + + auto commitId = GenerateCommitId(); + if (commitId == InvalidCommitId) { + return RebootTabletOnCommitOverflow(ctx, "UnsafeDeleteNode"); + } + + if (!args.Node) { + LOG_WARN(ctx, TFileStoreComponents::TABLET, + "%s UnsafeDeleteNode: %s - node not found", + LogTag.c_str(), + args.Request.DebugString().Quote().c_str()); + return; + } + + args.Error = RemoveNode(db, *args.Node, args.Node->MinCommitId, commitId); +} + +void TIndexTabletActor::CompleteTx_UnsafeDeleteNode( + const TActorContext& ctx, + TTxIndexTablet::TUnsafeDeleteNode& args) +{ + RemoveTransaction(*args.RequestInfo); + + LOG_WARN(ctx, TFileStoreComponents::TABLET, + "%s UnsafeDeleteNode: %s, status: %s", + LogTag.c_str(), + args.Request.DebugString().Quote().c_str(), + FormatError(args.Error).c_str()); + + auto response = + std::make_unique( + std::move(args.Error)); + + NCloud::Reply(ctx, *args.RequestInfo, std::move(response)); +} + +//////////////////////////////////////////////////////////////////////////////// + +void TIndexTabletActor::HandleUnsafeUpdateNode( + const TEvIndexTablet::TEvUnsafeUpdateNodeRequest::TPtr& ev, + const TActorContext& ctx) +{ + auto* msg = ev->Get(); + + auto requestInfo = CreateRequestInfo( + ev->Sender, + ev->Cookie, + msg->CallContext); + + AddTransaction(*requestInfo); + + LOG_WARN(ctx, TFileStoreComponents::TABLET, + "%s UnsafeUpdateNode: %s", + LogTag.c_str(), + msg->Record.DebugString().Quote().c_str()); + + ExecuteTx( + ctx, + std::move(requestInfo), + std::move(msg->Record)); +} + +//////////////////////////////////////////////////////////////////////////////// + +bool TIndexTabletActor::PrepareTx_UnsafeUpdateNode( + const TActorContext& ctx, + TTransactionContext& tx, + TTxIndexTablet::TUnsafeUpdateNode& args) +{ + Y_UNUSED(ctx); + + auto commitId = GetCurrentCommitId(); + + TIndexTabletDatabaseProxy db(tx.DB, args.NodeUpdates); + return ReadNode(db, args.Request.GetNode().GetId(), commitId, args.Node); +} + +void TIndexTabletActor::ExecuteTx_UnsafeUpdateNode( + const TActorContext& ctx, + TTransactionContext& tx, + TTxIndexTablet::TUnsafeUpdateNode& args) +{ + TIndexTabletDatabaseProxy db(tx.DB, args.NodeUpdates); + + auto commitId = GenerateCommitId(); + if (commitId == InvalidCommitId) { + return RebootTabletOnCommitOverflow(ctx, "UnsafeUpdateNode"); + } + + NProto::TNode prevNode; + NProto::TNode node; + ui64 nodeCommitId = commitId; + + if (args.Node) { + prevNode = args.Node->Attrs; + node = args.Node->Attrs; + nodeCommitId = args.Node->MinCommitId; + } else { + LOG_WARN(ctx, TFileStoreComponents::TABLET, + "%s UnsafeUpdateNode: %s - node not found, creating", + LogTag.c_str(), + args.Request.DebugString().Quote().c_str()); + + // creation here is still done via UpdateNode since CreateNode generates + // a new nodeId + } + + ConvertAttrsToNode(args.Request.GetNode(), &node); + UpdateNode( + db, + args.Request.GetNode().GetId(), + nodeCommitId, + commitId, + node, + prevNode); +} + +void TIndexTabletActor::CompleteTx_UnsafeUpdateNode( + const TActorContext& ctx, + TTxIndexTablet::TUnsafeUpdateNode& args) +{ + RemoveTransaction(*args.RequestInfo); + + TString oldNode; + if (args.Node) { + oldNode = args.Node->Attrs.Utf8DebugString(); + } + LOG_WARN(ctx, TFileStoreComponents::TABLET, + "%s UnsafeUpdateNode: %s, old node: %s", + LogTag.c_str(), + args.Request.DebugString().Quote().c_str(), + oldNode.Quote().c_str()); + + auto response = + std::make_unique(); + + NCloud::Reply(ctx, *args.RequestInfo, std::move(response)); +} + +//////////////////////////////////////////////////////////////////////////////// + +void TIndexTabletActor::HandleUnsafeGetNode( + const TEvIndexTablet::TEvUnsafeGetNodeRequest::TPtr& ev, + const TActorContext& ctx) +{ + auto* msg = ev->Get(); + + auto requestInfo = CreateRequestInfo( + ev->Sender, + ev->Cookie, + msg->CallContext); + + AddTransaction(*requestInfo); + + LOG_WARN(ctx, TFileStoreComponents::TABLET, + "%s UnsafeGetNode: %s", + LogTag.c_str(), + msg->Record.DebugString().Quote().c_str()); + + ExecuteTx( + ctx, + std::move(requestInfo), + std::move(msg->Record)); +} + +//////////////////////////////////////////////////////////////////////////////// + +bool TIndexTabletActor::ValidateTx_UnsafeGetNode( + const TActorContext& ctx, + TTxIndexTablet::TUnsafeGetNode& args) +{ + Y_UNUSED(ctx); + Y_UNUSED(args); + + return true; +} + +bool TIndexTabletActor::PrepareTx_UnsafeGetNode( + const NActors::TActorContext& ctx, + IIndexTabletDatabase& db, + TTxIndexTablet::TUnsafeGetNode& args) +{ + Y_UNUSED(ctx); + + return ReadNode( + db, + args.Request.GetId(), + GetCurrentCommitId(), + args.Node); +} + +void TIndexTabletActor::CompleteTx_UnsafeGetNode( + const TActorContext& ctx, + TTxIndexTablet::TUnsafeGetNode& args) +{ + RemoveTransaction(*args.RequestInfo); + + TString node; + if (args.Node) { + node = args.Node->Attrs.Utf8DebugString(); + } + LOG_WARN(ctx, TFileStoreComponents::TABLET, + "%s UnsafeGetNode: %s, node: %s", + LogTag.c_str(), + args.Request.DebugString().Quote().c_str(), + node.Quote().c_str()); + + auto response = + std::make_unique(); + if (args.Node) { + auto& attrs = *response->Record.MutableNode(); + ConvertNodeFromAttrs(attrs, args.Node->NodeId, args.Node->Attrs); + } + + NCloud::Reply(ctx, *args.RequestInfo, std::move(response)); +} + +} // namespace NCloud::NFileStore::NStorage diff --git a/cloud/filestore/libs/storage/tablet/tablet_actor_zerorange.cpp b/cloud/filestore/libs/storage/tablet/tablet_actor_zerorange.cpp index f0a6baf1c87..6574e32caf2 100644 --- a/cloud/filestore/libs/storage/tablet/tablet_actor_zerorange.cpp +++ b/cloud/filestore/libs/storage/tablet/tablet_actor_zerorange.cpp @@ -69,8 +69,6 @@ void TIndexTabletActor::ExecuteTx_ZeroRange( TTransactionContext& tx, TTxIndexTablet::TZeroRange& args) { - Y_UNUSED(ctx); - TIndexTabletDatabase db(tx.DB); ui64 commitId = GenerateCommitId(); @@ -84,7 +82,7 @@ void TIndexTabletActor::ExecuteTx_ZeroRange( args.Range.Length, args.ProfileLogRequest); - ZeroRange(db, args.NodeId, commitId, args.Range); + args.Error = ZeroRange(db, args.NodeId, commitId, args.Range); } void TIndexTabletActor::CompleteTx_ZeroRange( @@ -96,16 +94,19 @@ void TIndexTabletActor::CompleteTx_ZeroRange( std::move(args.ProfileLogRequest), ctx.Now(), GetFileSystemId(), - {}, + args.Error, ProfileLog); LOG_DEBUG(ctx, TFileStoreComponents::TABLET, - "%s ZeroRange %lu %s completed", + "%s ZeroRange %lu %s completed: %s", LogTag.c_str(), args.NodeId, - args.Range.Describe().c_str()); + args.Range.Describe().c_str(), + FormatError(args.Error).Quote().c_str()); - auto response = std::make_unique(); + auto response = + std::make_unique( + std::move(args.Error)); NCloud::Reply(ctx, *args.RequestInfo, std::move(response)); EnqueueCollectGarbageIfNeeded(ctx); diff --git a/cloud/filestore/libs/storage/tablet/tablet_database.cpp b/cloud/filestore/libs/storage/tablet/tablet_database.cpp index 27bcece8ec2..cc5f9318869 100644 --- a/cloud/filestore/libs/storage/tablet/tablet_database.cpp +++ b/cloud/filestore/libs/storage/tablet/tablet_database.cpp @@ -1404,6 +1404,49 @@ bool TIndexTabletDatabase::ReadLargeDeletionMarkers( return true; } +//////////////////////////////////////////////////////////////////////////////// +// OrphanNodes + +void TIndexTabletDatabase::WriteOrphanNode(ui64 nodeId) +{ + using TTable = TIndexTabletSchema::OrphanNodes; + + Table() + .Key(nodeId) + .Update(); +} + +void TIndexTabletDatabase::DeleteOrphanNode(ui64 nodeId) +{ + using TTable = TIndexTabletSchema::OrphanNodes; + + Table() + .Key(nodeId) + .Delete(); +} + +bool TIndexTabletDatabase::ReadOrphanNodes(TVector& nodeIds) +{ + using TTable = TIndexTabletSchema::OrphanNodes; + + auto it = Table() + .Select(); + + if (!it.IsReady()) { + return false; // not ready + } + + while (it.IsValid()) { + nodeIds.emplace_back(it.GetValue()); + + if (!it.Next()) { + return false; // not ready + } + } + + return true; +} + //////////////////////////////////////////////////////////////////////////////// // NewBlobs diff --git a/cloud/filestore/libs/storage/tablet/tablet_database.h b/cloud/filestore/libs/storage/tablet/tablet_database.h index 6a99be794f5..7a841690a13 100644 --- a/cloud/filestore/libs/storage/tablet/tablet_database.h +++ b/cloud/filestore/libs/storage/tablet/tablet_database.h @@ -427,6 +427,14 @@ FILESTORE_FILESYSTEM_STATS(FILESTORE_DECLARE_STATS) bool ReadLargeDeletionMarkers(TVector& deletionMarkers); + // + // OrphanNodes + // + + void WriteOrphanNode(ui64 nodeId); + void DeleteOrphanNode(ui64 nodeId); + bool ReadOrphanNodes(TVector& nodeIds); + // // NewBlobs // diff --git a/cloud/filestore/libs/storage/tablet/tablet_database_ut.cpp b/cloud/filestore/libs/storage/tablet/tablet_database_ut.cpp index 66f064c7047..5020a24f845 100644 --- a/cloud/filestore/libs/storage/tablet/tablet_database_ut.cpp +++ b/cloud/filestore/libs/storage/tablet/tablet_database_ut.cpp @@ -511,6 +511,41 @@ Y_UNIT_TEST_SUITE(TIndexTabletDatabaseTest) UNIT_ASSERT_VALUES_EQUAL(toString(entries), toString(markers)); }); } + + Y_UNIT_TEST(ShouldStoreOrphanNodes) + { + TTestExecutor executor; + executor.WriteTx([&] (TIndexTabletDatabase db) { + db.InitSchema(false); + }); + + executor.WriteTx([&] (TIndexTabletDatabase db) { + db.WriteOrphanNode(111); + db.WriteOrphanNode(222); + db.WriteOrphanNode(333); + }); + + executor.ReadTx([&] (TIndexTabletDatabase db) { + TVector nodeIds; + UNIT_ASSERT(db.ReadOrphanNodes(nodeIds)); + UNIT_ASSERT_VALUES_EQUAL(3, nodeIds.size()); + UNIT_ASSERT_VALUES_EQUAL(111, nodeIds[0]); + UNIT_ASSERT_VALUES_EQUAL(222, nodeIds[1]); + UNIT_ASSERT_VALUES_EQUAL(333, nodeIds[2]); + }); + + executor.WriteTx([&] (TIndexTabletDatabase db) { + db.DeleteOrphanNode(222); + }); + + executor.ReadTx([&] (TIndexTabletDatabase db) { + TVector nodeIds; + UNIT_ASSERT(db.ReadOrphanNodes(nodeIds)); + UNIT_ASSERT_VALUES_EQUAL(2, nodeIds.size()); + UNIT_ASSERT_VALUES_EQUAL(111, nodeIds[0]); + UNIT_ASSERT_VALUES_EQUAL(333, nodeIds[1]); + }); + } } } // namespace NCloud::NFileStore::NStorage diff --git a/cloud/filestore/libs/storage/tablet/tablet_schema.h b/cloud/filestore/libs/storage/tablet/tablet_schema.h index 6cec54933d2..5b997fcd051 100644 --- a/cloud/filestore/libs/storage/tablet/tablet_schema.h +++ b/cloud/filestore/libs/storage/tablet/tablet_schema.h @@ -533,6 +533,19 @@ struct TIndexTabletSchema using CompactionPolicy = TCompactionPolicy; }; + struct OrphanNodes: TTableSchema<27> + { + struct NodeId : Column<1, NKikimr::NScheme::NTypeIds::Uint64> {}; + + using TKey = TableKey; + + using TColumns = TableColumns< + NodeId + >; + + using StoragePolicy = TStoragePolicy; + }; + using TTables = SchemaTables< FileSystem, Sessions, @@ -559,7 +572,8 @@ struct TIndexTabletSchema TruncateQueue, SessionHistory, OpLog, - LargeDeletionMarkers + LargeDeletionMarkers, + OrphanNodes >; using TSettings = SchemaSettings< diff --git a/cloud/filestore/libs/storage/tablet/tablet_state.cpp b/cloud/filestore/libs/storage/tablet/tablet_state.cpp index bda81044bcf..bedcc447452 100644 --- a/cloud/filestore/libs/storage/tablet/tablet_state.cpp +++ b/cloud/filestore/libs/storage/tablet/tablet_state.cpp @@ -83,6 +83,7 @@ void TIndexTabletState::LoadState( const NProto::TFileSystemStats& fileSystemStats, const NCloud::NProto::TTabletStorageInfo& tabletStorageInfo, const TVector& largeDeletionMarkers, + const TVector& orphanNodeIds, const TThrottlerConfig& throttlerConfig) { Generation = generation; @@ -102,6 +103,8 @@ void TIndexTabletState::LoadState( LargeDeletionMarkersThreshold = config.GetLargeDeletionMarkersThreshold(); LargeDeletionMarkersCleanupThreshold = config.GetLargeDeletionMarkersCleanupThreshold(); + LargeDeletionMarkersThresholdForBackpressure = + config.GetLargeDeletionMarkersThresholdForBackpressure(); FileSystem.CopyFrom(fileSystem); FileSystemStats.CopyFrom(fileSystemStats); @@ -130,6 +133,8 @@ void TIndexTabletState::LoadState( for (const auto& deletionMarker: largeDeletionMarkers) { Impl->LargeBlocks.AddDeletionMarker(deletionMarker); } + + Impl->OrphanNodeIds.insert(orphanNodeIds.begin(), orphanNodeIds.end()); } void TIndexTabletState::UpdateConfig( @@ -170,4 +175,11 @@ void TIndexTabletState::DumpStats(IOutputStream& os) const ); } +TMiscNodeStats TIndexTabletState::GetMiscNodeStats() const +{ + return { + .OrphanNodesCount = static_cast(Impl->OrphanNodeIds.size()), + }; +} + } // namespace NCloud::NFileStore::NStorage diff --git a/cloud/filestore/libs/storage/tablet/tablet_state.h b/cloud/filestore/libs/storage/tablet/tablet_state.h index 4adf124f2cd..286295e45f2 100644 --- a/cloud/filestore/libs/storage/tablet/tablet_state.h +++ b/cloud/filestore/libs/storage/tablet/tablet_state.h @@ -143,6 +143,11 @@ struct TNodeToSessionCounters i64 NodesOpenForReadingByMultipleSessions{0}; }; +struct TMiscNodeStats +{ + i64 OrphanNodesCount{0}; +}; + //////////////////////////////////////////////////////////////////////////////// class TIndexTabletState @@ -171,6 +176,7 @@ class TIndexTabletState /*const*/ ui64 LargeDeletionMarkerBlocks = 0; /*const*/ ui64 LargeDeletionMarkersThreshold = 0; /*const*/ ui64 LargeDeletionMarkersCleanupThreshold = 0; + /*const*/ ui64 LargeDeletionMarkersThresholdForBackpressure = 0; bool StateLoaded = false; @@ -190,6 +196,7 @@ class TIndexTabletState const NProto::TFileSystemStats& fileSystemStats, const NCloud::NProto::TTabletStorageInfo& tabletStorageInfo, const TVector& largeDeletionMarkers, + const TVector& orphanNodeIds, const TThrottlerConfig& throttlerConfig); bool IsStateLoaded() const @@ -275,6 +282,8 @@ class TIndexTabletState return NodeToSessionCounters; } + TMiscNodeStats GetMiscNodeStats() const; + const NProto::TFileStorePerformanceProfile& GetPerformanceProfile() const; const TFileStoreAllocRegistry& GetFileStoreProfilingRegistry() const @@ -380,13 +389,13 @@ FILESTORE_FILESYSTEM_STATS(FILESTORE_DECLARE_COUNTER) const NProto::TNode& attrs, const NProto::TNode& prevAttrs); - void RemoveNode( + [[nodiscard]] NProto::TError RemoveNode( TIndexTabletDatabase& db, const IIndexTabletDatabase::TNode& node, ui64 minCommitId, ui64 maxCommitId); - void UnlinkNode( + [[nodiscard]] NProto::TError UnlinkNode( TIndexTabletDatabase& db, ui64 parentNodeId, const TString& name, @@ -423,6 +432,11 @@ FILESTORE_FILESYSTEM_STATS(FILESTORE_DECLARE_COUNTER) bool HasBlocksLeft( ui32 blocks) const; + void WriteOrphanNode( + TIndexTabletDatabase& db, + const TString& message, + ui64 nodeId); + private: void UpdateUsedBlocksCount( TIndexTabletDatabase& db, @@ -1207,7 +1221,7 @@ FILESTORE_DUPCACHE_REQUESTS(FILESTORE_DECLARE_DUPCACHE) void AddTruncate(TIndexTabletDatabase& db, ui64 nodeId, TByteRange range); void DeleteTruncate(TIndexTabletDatabase& db, ui64 nodeId); - void Truncate( + [[nodiscard]] NProto::TError Truncate( TIndexTabletDatabase& db, ui64 nodeId, ui64 commitId, @@ -1220,7 +1234,7 @@ FILESTORE_DUPCACHE_REQUESTS(FILESTORE_DECLARE_DUPCACHE) // - aligns up range in the tail; // - deletes all blocks in NEW range; // - writes fresh bytes (zeroes) on unaligned head, if range.Offset != 0. - void TruncateRange( + [[nodiscard]] NProto::TError TruncateRange( TIndexTabletDatabase& db, ui64 nodeId, ui64 commitId, @@ -1230,14 +1244,14 @@ FILESTORE_DUPCACHE_REQUESTS(FILESTORE_DECLARE_DUPCACHE) // resizing the node. This function: // - writes fresh bytes (zeroes) on unaligned head, if any; // - writes fresh bytes (zeroes) on unaligned tail, if any. - void ZeroRange( + [[nodiscard]] NProto::TError ZeroRange( TIndexTabletDatabase& db, ui64 nodeId, ui64 commitId, TByteRange range); private: - void DeleteRange( + [[nodiscard]] NProto::TError DeleteRange( TIndexTabletDatabase& db, ui64 nodeId, ui64 commitId, diff --git a/cloud/filestore/libs/storage/tablet/tablet_state_data.cpp b/cloud/filestore/libs/storage/tablet/tablet_state_data.cpp index ef9728d3bd5..7d0f08439de 100644 --- a/cloud/filestore/libs/storage/tablet/tablet_state_data.cpp +++ b/cloud/filestore/libs/storage/tablet/tablet_state_data.cpp @@ -74,7 +74,7 @@ bool TIndexTabletState::GenerateBlobId( return true; } -void TIndexTabletState::Truncate( +NProto::TError TIndexTabletState::Truncate( TIndexTabletDatabase& db, ui64 nodeId, ui64 commitId, @@ -82,20 +82,20 @@ void TIndexTabletState::Truncate( ui64 targetSize) { if (currentSize <= targetSize) { - return; + return {}; } TByteRange range(targetSize, currentSize - targetSize, GetBlockSize()); if (TruncateBlocksThreshold && range.BlockCount() > TruncateBlocksThreshold) { EnqueueTruncateOp(nodeId, range); - return; + return {}; } - TruncateRange(db, nodeId, commitId, range); + return TruncateRange(db, nodeId, commitId, range); } -void TIndexTabletState::TruncateRange( +NProto::TError TIndexTabletState::TruncateRange( TIndexTabletDatabase& db, ui64 nodeId, ui64 commitId, @@ -106,7 +106,10 @@ void TIndexTabletState::TruncateRange( AlignUp(range.End(), range.BlockSize) - range.Offset, range.BlockSize); - DeleteRange(db, nodeId, commitId, tailAlignedRange); + auto e = DeleteRange(db, nodeId, commitId, tailAlignedRange); + if (HasError(e)) { + return e; + } const TByteRange headBound( range.Offset, @@ -123,15 +126,20 @@ void TIndexTabletState::TruncateRange( } InvalidateReadAheadCache(nodeId); + + return {}; } -void TIndexTabletState::ZeroRange( +NProto::TError TIndexTabletState::ZeroRange( TIndexTabletDatabase& db, ui64 nodeId, ui64 commitId, TByteRange range) { - DeleteRange(db, nodeId, commitId, range); + auto e = DeleteRange(db, nodeId, commitId, range); + if (HasError(e)) { + return e; + } const TByteRange headBound( range.Offset, @@ -160,9 +168,11 @@ void TIndexTabletState::ZeroRange( // FIXME: do not allocate each time TString(tailBound.Length, 0)); } + + return {}; } -void TIndexTabletState::DeleteRange( +NProto::TError TIndexTabletState::DeleteRange( TIndexTabletDatabase& db, ui64 nodeId, ui64 commitId, @@ -170,16 +180,16 @@ void TIndexTabletState::DeleteRange( { const ui64 deletedBlockCount = range.AlignedBlockCount(); if (deletedBlockCount) { - MarkFreshBlocksDeleted( - db, - nodeId, - commitId, - range.FirstAlignedBlock(), - deletedBlockCount); - const bool useLargeDeletionMarkers = LargeDeletionMarkersEnabled && deletedBlockCount >= LargeDeletionMarkersThreshold; if (useLargeDeletionMarkers) { + const auto t = LargeDeletionMarkersThresholdForBackpressure; + if (GetLargeDeletionMarkersCount() >= t) { + return MakeError(E_REJECTED, TStringBuilder() + << "too many large deletion markers: " + << GetLargeDeletionMarkersCount() << " >= " << t); + } + SplitRange( range.FirstAlignedBlock(), deletedBlockCount, @@ -213,6 +223,13 @@ void TIndexTabletState::DeleteRange( blocksCount); }); } + + MarkFreshBlocksDeleted( + db, + nodeId, + commitId, + range.FirstAlignedBlock(), + deletedBlockCount); } WriteFreshBytesDeletionMarker( @@ -221,6 +238,8 @@ void TIndexTabletState::DeleteRange( commitId, range.Offset, range.Length); + + return {}; } void TIndexTabletState::EnqueueTruncateOp(ui64 nodeId, TByteRange range) diff --git a/cloud/filestore/libs/storage/tablet/tablet_state_impl.h b/cloud/filestore/libs/storage/tablet/tablet_state_impl.h index bfdf07c22ab..e4847015859 100644 --- a/cloud/filestore/libs/storage/tablet/tablet_state_impl.h +++ b/cloud/filestore/libs/storage/tablet/tablet_state_impl.h @@ -59,6 +59,7 @@ struct TIndexTabletState::TImpl TReadAheadCache ReadAheadCache; TNodeIndexCache NodeIndexCache; TInMemoryIndexState InMemoryIndexState; + TSet OrphanNodeIds; TCheckpointStore Checkpoints; TChannels Channels; diff --git a/cloud/filestore/libs/storage/tablet/tablet_state_nodes.cpp b/cloud/filestore/libs/storage/tablet/tablet_state_nodes.cpp index 331d739b53e..10bc8b89276 100644 --- a/cloud/filestore/libs/storage/tablet/tablet_state_nodes.cpp +++ b/cloud/filestore/libs/storage/tablet/tablet_state_nodes.cpp @@ -98,12 +98,27 @@ void TIndexTabletState::UpdateNode( InvalidateNodeIndexCache(nodeId); } -void TIndexTabletState::RemoveNode( +NProto::TError TIndexTabletState::RemoveNode( TIndexTabletDatabase& db, const IIndexTabletDatabase::TNode& node, ui64 minCommitId, ui64 maxCommitId) { + // SymLinks have size (equal to TargetPath) but store no real data so there + // is no need to write deletion markers upon SymLink removal + if (!node.Attrs.GetSymLink()) { + auto e = Truncate( + db, + node.NodeId, + maxCommitId, + node.Attrs.GetSize(), + 0); + + if (HasError(e)) { + return e; + } + } + db.DeleteNode(node.NodeId); DecrementUsedNodesCount(db); @@ -116,21 +131,12 @@ void TIndexTabletState::RemoveNode( AddCheckpointNode(db, checkpointId, node.NodeId); } - // SymLinks have size (equal to TargetPath) but store no real data so there - // is no need to write deletion markers upon SymLink removal - if (!node.Attrs.GetSymLink()) { - Truncate( - db, - node.NodeId, - maxCommitId, - node.Attrs.GetSize(), - 0); - } - InvalidateNodeIndexCache(node.NodeId); + + return {}; } -void TIndexTabletState::UnlinkNode( +NProto::TError TIndexTabletState::UnlinkNode( TIndexTabletDatabase& db, ui64 parentNodeId, const TString& name, @@ -148,11 +154,15 @@ void TIndexTabletState::UnlinkNode( attrs, node.Attrs); } else { - RemoveNode( + auto e = RemoveNode( db, node, minCommitId, maxCommitId); + + if (HasError(e)) { + return e; + } } RemoveNodeRef( @@ -165,6 +175,8 @@ void TIndexTabletState::UnlinkNode( "", // followerId "" // followerName ); + + return {}; } void TIndexTabletState::UnlinkExternalNode( @@ -232,6 +244,16 @@ void TIndexTabletState::RewriteNode( InvalidateNodeIndexCache(nodeId); } +void TIndexTabletState::WriteOrphanNode( + TIndexTabletDatabase& db, + const TString& message, + ui64 nodeId) +{ + ReportGeneratedOrphanNode(message); + db.WriteOrphanNode(nodeId); + Impl->OrphanNodeIds.insert(nodeId); +} + //////////////////////////////////////////////////////////////////////////////// // NodeAttrs diff --git a/cloud/filestore/libs/storage/tablet/tablet_tx.h b/cloud/filestore/libs/storage/tablet/tablet_tx.h index 240cd262f64..ccc3f80b320 100644 --- a/cloud/filestore/libs/storage/tablet/tablet_tx.h +++ b/cloud/filestore/libs/storage/tablet/tablet_tx.h @@ -75,6 +75,8 @@ namespace NCloud::NFileStore::NStorage { xxx(GetNodeAttrBatch, __VA_ARGS__) \ xxx(GetNodeXAttr, __VA_ARGS__) \ xxx(ListNodeXAttr, __VA_ARGS__) \ + \ + xxx(UnsafeGetNode, __VA_ARGS__) \ // FILESTORE_TABLET_RO_TRANSACTIONS #define FILESTORE_TABLET_RW_TRANSACTIONS(xxx, ...) \ @@ -131,6 +133,9 @@ namespace NCloud::NFileStore::NStorage { \ xxx(DeleteOpLogEntry, __VA_ARGS__) \ xxx(CommitNodeCreationInFollower, __VA_ARGS__) \ + \ + xxx(UnsafeDeleteNode, __VA_ARGS__) \ + xxx(UnsafeUpdateNode, __VA_ARGS__) \ // FILESTORE_TABLET_RW_TRANSACTIONS #define FILESTORE_TABLET_TRANSACTIONS(xxx, ...) \ @@ -170,7 +175,8 @@ struct TIndexStateNodeUpdates TVector NodeUpdates; }; -struct TProfileAware { +struct TProfileAware +{ NProto::TProfileLogRequestInfo ProfileLogRequest; explicit TProfileAware(EFileStoreSystemRequest requestType) noexcept @@ -308,6 +314,7 @@ struct TTxIndexTablet TVector SessionHistory; TVector OpLog; TVector LargeDeletionMarkers; + TVector OrphanNodeIds; NProto::TError Error; @@ -331,6 +338,7 @@ struct TTxIndexTablet SessionHistory.clear(); OpLog.clear(); LargeDeletionMarkers.clear(); + OrphanNodeIds.clear(); } }; @@ -1778,6 +1786,8 @@ struct TTxIndexTablet const ui64 NodeId; const TByteRange Range; + NProto::TError Error; + TTruncateRange( TRequestInfoPtr requestInfo, ui64 nodeId, @@ -1791,6 +1801,7 @@ struct TTxIndexTablet void Clear() { TProfileAware::Clear(); + Error.Clear(); } }; @@ -1826,6 +1837,8 @@ struct TTxIndexTablet const ui64 NodeId; const TByteRange Range; + NProto::TError Error; + TZeroRange( TRequestInfoPtr requestInfo, ui64 nodeId, @@ -1839,6 +1852,7 @@ struct TTxIndexTablet void Clear() { TProfileAware::Clear(); + Error.Clear(); } }; @@ -1956,7 +1970,7 @@ struct TTxIndexTablet NProto::TCreateNodeResponse Response; const ui64 EntryId; - explicit TCommitNodeCreationInFollower( + TCommitNodeCreationInFollower( TString sessionId, ui64 requestId, NProto::TCreateNodeResponse response, @@ -1971,6 +1985,72 @@ struct TTxIndexTablet { } }; + + // + // UnsafeNodeOps + // + + struct TUnsafeDeleteNode: TIndexStateNodeUpdates + { + const TRequestInfoPtr RequestInfo; + const NProtoPrivate::TUnsafeDeleteNodeRequest Request; + + TMaybe Node; + NProto::TError Error; + + TUnsafeDeleteNode( + TRequestInfoPtr requestInfo, + NProtoPrivate::TUnsafeDeleteNodeRequest request) + : RequestInfo(std::move(requestInfo)) + , Request(std::move(request)) + {} + + void Clear() + { + Node.Clear(); + Error.Clear(); + } + }; + + struct TUnsafeUpdateNode: TIndexStateNodeUpdates + { + const TRequestInfoPtr RequestInfo; + const NProtoPrivate::TUnsafeUpdateNodeRequest Request; + + TMaybe Node; + + TUnsafeUpdateNode( + TRequestInfoPtr requestInfo, + NProtoPrivate::TUnsafeUpdateNodeRequest request) + : RequestInfo(std::move(requestInfo)) + , Request(std::move(request)) + {} + + void Clear() + { + Node.Clear(); + } + }; + + struct TUnsafeGetNode + { + const TRequestInfoPtr RequestInfo; + const NProtoPrivate::TUnsafeGetNodeRequest Request; + + TMaybe Node; + + TUnsafeGetNode( + TRequestInfoPtr requestInfo, + NProtoPrivate::TUnsafeGetNodeRequest request) + : RequestInfo(std::move(requestInfo)) + , Request(std::move(request)) + {} + + void Clear() + { + Node.Clear(); + } + }; }; } // namespace NCloud::NFileStore::NStorage diff --git a/cloud/filestore/libs/storage/tablet/tablet_ut_data.cpp b/cloud/filestore/libs/storage/tablet/tablet_ut_data.cpp index 478586de12f..83419df0e6a 100644 --- a/cloud/filestore/libs/storage/tablet/tablet_ut_data.cpp +++ b/cloud/filestore/libs/storage/tablet/tablet_ut_data.cpp @@ -6086,6 +6086,8 @@ Y_UNIT_TEST_SUITE(TIndexTabletTest_Data) storageConfig.SetLargeDeletionMarkerBlocks(1_GB / block); storageConfig.SetLargeDeletionMarkersThreshold(128_GB / block); storageConfig.SetLargeDeletionMarkersCleanupThreshold(3_TB / block); + storageConfig.SetLargeDeletionMarkersThresholdForBackpressure( + 10_TB / block); const auto blobSize = 2 * block; storageConfig.SetWriteBlobThreshold(blobSize); @@ -6217,6 +6219,112 @@ Y_UNIT_TEST_SUITE(TIndexTabletTest_Data) } } + TABLET_TEST(ShouldRejectLargeFileTruncationIfLargeDeletionMarkerCountIsTooHigh) + { + const auto block = tabletConfig.BlockSize; + + NProto::TStorageConfig storageConfig; + storageConfig.SetMaxFileBlocks(5_TB / block); + storageConfig.SetLargeDeletionMarkersEnabled(true); + storageConfig.SetLargeDeletionMarkerBlocks(1_GB / block); + storageConfig.SetLargeDeletionMarkersThreshold(128_GB / block); + storageConfig.SetLargeDeletionMarkersCleanupThreshold(20_TB / block); + storageConfig.SetLargeDeletionMarkersThresholdForBackpressure( + 4_TB / block); + const auto blobSize = 2 * block; + storageConfig.SetWriteBlobThreshold(blobSize); + + TTestEnv env({}, storageConfig); + env.CreateSubDomain("nfs"); + + ui32 nodeIdx = env.CreateNode("nfs"); + ui64 tabletId = env.BootIndexTablet(nodeIdx); + + tabletConfig.BlockCount = 10_TB / block; + + TIndexTabletClient tablet( + env.GetRuntime(), + nodeIdx, + tabletId, + tabletConfig); + tablet.InitSession("client", "session"); + + auto id1 = + CreateNode(tablet, TCreateNodeArgs::File(RootNodeId, "test1")); + auto id2 = + CreateNode(tablet, TCreateNodeArgs::File(RootNodeId, "test2")); + CreateNode(tablet, TCreateNodeArgs::File(RootNodeId, "test3")); + + for (const auto id: {id1, id2}) { + TSetNodeAttrArgs args(id); + args.SetFlag(NProto::TSetNodeAttrRequest::F_SET_ATTR_SIZE); + args.SetSize(5_TB); + tablet.SetNodeAttr(args); + UNIT_ASSERT_VALUES_EQUAL(5_TB, GetNodeAttrs(tablet, id).GetSize()); + } + + { + tablet.SendUnlinkNodeRequest(RootNodeId, "test1", false); + auto response = tablet.RecvUnlinkNodeResponse(); + UNIT_ASSERT_VALUES_EQUAL_C( + S_OK, + response->GetStatus(), + response->GetErrorReason()); + } + + { + tablet.SendUnlinkNodeRequest(RootNodeId, "test2", false); + auto response = tablet.RecvUnlinkNodeResponse(); + UNIT_ASSERT_VALUES_EQUAL_C( + E_REJECTED, + response->GetStatus(), + response->GetErrorReason()); + } + + { + auto response = tablet.GetStorageStats(); + const auto& stats = response->Record.GetStats(); + UNIT_ASSERT_VALUES_EQUAL( + 5_TB / block, + stats.GetLargeDeletionMarkersCount()); + UNIT_ASSERT_VALUES_EQUAL(2, stats.GetUsedNodesCount()); + } + + tablet.AdvanceTime(TDuration::Seconds(15)); + env.GetRuntime().DispatchEvents({}, TDuration::Seconds(5)); + + auto registry = env.GetRegistry(); + { + TTestRegistryVisitor visitor; + registry->Visit(TInstant::Zero(), visitor); + visitor.ValidateExpectedCounters({ + {{{"sensor", "OrphanNodesCount"}, {"filesystem", "test"}}, 0}, + }); + } + + tablet.RenameNode(RootNodeId, "test3", RootNodeId, "test2"); + + tablet.AdvanceTime(TDuration::Seconds(15)); + env.GetRuntime().DispatchEvents({}, TDuration::Seconds(5)); + + { + TTestRegistryVisitor visitor; + registry->Visit(TInstant::Zero(), visitor); + visitor.ValidateExpectedCounters({ + {{{"sensor", "OrphanNodesCount"}, {"filesystem", "test"}}, 1}, + }); + } + + { + auto response = tablet.GetStorageStats(); + const auto& stats = response->Record.GetStats(); + UNIT_ASSERT_VALUES_EQUAL( + 5_TB / block, + stats.GetLargeDeletionMarkersCount()); + UNIT_ASSERT_VALUES_EQUAL(2, stats.GetUsedNodesCount()); + } + } + TABLET_TEST_4K_ONLY(ShouldHandleRangeIdCollisionsInCompactionMapStats) { const auto block = tabletConfig.BlockSize; diff --git a/cloud/filestore/libs/storage/tablet/ya.make b/cloud/filestore/libs/storage/tablet/ya.make index 1fa863c0cb6..348e0ffa87e 100644 --- a/cloud/filestore/libs/storage/tablet/ya.make +++ b/cloud/filestore/libs/storage/tablet/ya.make @@ -64,6 +64,7 @@ SRCS( tablet_actor_throttling.cpp tablet_actor_truncate.cpp tablet_actor_unlinknode.cpp + tablet_actor_unsafe_node_ops.cpp tablet_actor_updateconfig.cpp tablet_actor_waitready.cpp tablet_actor_writebatch.cpp diff --git a/cloud/filestore/libs/vfs_fuse/CMakeLists.darwin-x86_64.txt b/cloud/filestore/libs/vfs_fuse/CMakeLists.darwin-x86_64.txt index 23d6e7ca940..95cb033a932 100644 --- a/cloud/filestore/libs/vfs_fuse/CMakeLists.darwin-x86_64.txt +++ b/cloud/filestore/libs/vfs_fuse/CMakeLists.darwin-x86_64.txt @@ -31,6 +31,7 @@ target_link_libraries(filestore-libs-vfs_fuse PUBLIC target_sources(filestore-libs-vfs_fuse PRIVATE ${CMAKE_SOURCE_DIR}/cloud/filestore/libs/vfs_fuse/cache.cpp ${CMAKE_SOURCE_DIR}/cloud/filestore/libs/vfs_fuse/config.cpp + ${CMAKE_SOURCE_DIR}/cloud/filestore/libs/vfs_fuse/file_ring_buffer.cpp ${CMAKE_SOURCE_DIR}/cloud/filestore/libs/vfs_fuse/fs.cpp ${CMAKE_SOURCE_DIR}/cloud/filestore/libs/vfs_fuse/fs_impl.cpp ${CMAKE_SOURCE_DIR}/cloud/filestore/libs/vfs_fuse/fs_impl_attr.cpp diff --git a/cloud/filestore/libs/vfs_fuse/CMakeLists.linux-aarch64.txt b/cloud/filestore/libs/vfs_fuse/CMakeLists.linux-aarch64.txt index 94d43d7b3c6..40a721a60da 100644 --- a/cloud/filestore/libs/vfs_fuse/CMakeLists.linux-aarch64.txt +++ b/cloud/filestore/libs/vfs_fuse/CMakeLists.linux-aarch64.txt @@ -32,6 +32,7 @@ target_link_libraries(filestore-libs-vfs_fuse PUBLIC target_sources(filestore-libs-vfs_fuse PRIVATE ${CMAKE_SOURCE_DIR}/cloud/filestore/libs/vfs_fuse/cache.cpp ${CMAKE_SOURCE_DIR}/cloud/filestore/libs/vfs_fuse/config.cpp + ${CMAKE_SOURCE_DIR}/cloud/filestore/libs/vfs_fuse/file_ring_buffer.cpp ${CMAKE_SOURCE_DIR}/cloud/filestore/libs/vfs_fuse/fs.cpp ${CMAKE_SOURCE_DIR}/cloud/filestore/libs/vfs_fuse/fs_impl.cpp ${CMAKE_SOURCE_DIR}/cloud/filestore/libs/vfs_fuse/fs_impl_attr.cpp diff --git a/cloud/filestore/libs/vfs_fuse/CMakeLists.linux-x86_64.txt b/cloud/filestore/libs/vfs_fuse/CMakeLists.linux-x86_64.txt index edf5cf58287..46343a852f6 100644 --- a/cloud/filestore/libs/vfs_fuse/CMakeLists.linux-x86_64.txt +++ b/cloud/filestore/libs/vfs_fuse/CMakeLists.linux-x86_64.txt @@ -33,6 +33,7 @@ target_link_libraries(filestore-libs-vfs_fuse PUBLIC target_sources(filestore-libs-vfs_fuse PRIVATE ${CMAKE_SOURCE_DIR}/cloud/filestore/libs/vfs_fuse/cache.cpp ${CMAKE_SOURCE_DIR}/cloud/filestore/libs/vfs_fuse/config.cpp + ${CMAKE_SOURCE_DIR}/cloud/filestore/libs/vfs_fuse/file_ring_buffer.cpp ${CMAKE_SOURCE_DIR}/cloud/filestore/libs/vfs_fuse/fs.cpp ${CMAKE_SOURCE_DIR}/cloud/filestore/libs/vfs_fuse/fs_impl.cpp ${CMAKE_SOURCE_DIR}/cloud/filestore/libs/vfs_fuse/fs_impl_attr.cpp diff --git a/cloud/filestore/libs/vfs_fuse/CMakeLists.windows-x86_64.txt b/cloud/filestore/libs/vfs_fuse/CMakeLists.windows-x86_64.txt index 23d6e7ca940..95cb033a932 100644 --- a/cloud/filestore/libs/vfs_fuse/CMakeLists.windows-x86_64.txt +++ b/cloud/filestore/libs/vfs_fuse/CMakeLists.windows-x86_64.txt @@ -31,6 +31,7 @@ target_link_libraries(filestore-libs-vfs_fuse PUBLIC target_sources(filestore-libs-vfs_fuse PRIVATE ${CMAKE_SOURCE_DIR}/cloud/filestore/libs/vfs_fuse/cache.cpp ${CMAKE_SOURCE_DIR}/cloud/filestore/libs/vfs_fuse/config.cpp + ${CMAKE_SOURCE_DIR}/cloud/filestore/libs/vfs_fuse/file_ring_buffer.cpp ${CMAKE_SOURCE_DIR}/cloud/filestore/libs/vfs_fuse/fs.cpp ${CMAKE_SOURCE_DIR}/cloud/filestore/libs/vfs_fuse/fs_impl.cpp ${CMAKE_SOURCE_DIR}/cloud/filestore/libs/vfs_fuse/fs_impl_attr.cpp diff --git a/cloud/filestore/libs/vfs_fuse/file_ring_buffer.cpp b/cloud/filestore/libs/vfs_fuse/file_ring_buffer.cpp new file mode 100644 index 00000000000..e2d79050771 --- /dev/null +++ b/cloud/filestore/libs/vfs_fuse/file_ring_buffer.cpp @@ -0,0 +1,313 @@ +#include "file_ring_buffer.h" + +#include + +#include +#include +#include +#include + +#include + +namespace NCloud::NFileStore { + +namespace { + +//////////////////////////////////////////////////////////////////////////////// + +constexpr ui32 VERSION = 1; +constexpr ui32 INVALID_POS = Max(); +constexpr TStringBuf INVALID_MARKER = "invalid_entry_marker"; + +//////////////////////////////////////////////////////////////////////////////// + +struct THeader +{ + ui32 Version = 0; + ui32 Capacity = 0; + ui32 ReadPos = 0; + ui32 WritePos = 0; +}; + +struct Y_PACKED TEntryHeader +{ + ui32 Size = 0; + ui32 Checksum = 0; +}; + +void WriteEntry(IOutputStream& os, TStringBuf data) +{ + TEntryHeader eh; + eh.Size = data.Size(); + eh.Checksum = Crc32c(data.Data(), data.Size()); + os.Write(&eh, sizeof(eh)); + os.Write(data); +} + +} // namespace + +//////////////////////////////////////////////////////////////////////////////// + +class TFileRingBuffer::TImpl +{ +private: + TFileMap Map; + const ui32 MaxEntrySize; + + char* Data = nullptr; + ui32 Count = 0; + const char* End = nullptr; + +private: + THeader* Header() + { + return reinterpret_cast(Map.Ptr()); + } + + const THeader* Header() const + { + return reinterpret_cast(Map.Ptr()); + } + + void SkipSlackSpace() + { + if (Header()->ReadPos == Header()->WritePos) { + Header()->ReadPos = 0; + Header()->WritePos = 0; + return; + } + + const auto* b = Data + Header()->ReadPos; + const auto* eh = reinterpret_cast(b); + if (eh->Size == 0) { + Header()->ReadPos = 0; + } + } + + using TVisitor = std::function; + + ui32 VisitEntry(const TVisitor& visitor, ui32 pos) const + { + const auto* b = Data + pos; + if (b + sizeof(TEntryHeader) > End) { + visitor(0, INVALID_MARKER); + return INVALID_POS; + } + + const auto* eh = reinterpret_cast(b); + if (eh->Size == 0) { + return 0; + } + + TStringBuf entry(b + sizeof(TEntryHeader), eh->Size); + if (entry.Data() + entry.Size() > End) { + visitor(eh->Checksum, INVALID_MARKER); + return INVALID_POS; + } + visitor(eh->Checksum, {b + sizeof(TEntryHeader), eh->Size}); + return pos + sizeof(TEntryHeader) + eh->Size; + } + + void Visit(const TVisitor& visitor) const + { + ui32 pos = Header()->ReadPos; + while (pos > Header()->WritePos && pos != INVALID_POS) { + pos = VisitEntry(visitor, pos); + } + + while (pos < Header()->WritePos && pos != INVALID_POS) { + pos = VisitEntry(visitor, pos); + if (!pos) { + // can happen if the buffer is corrupted + break; + } + } + } + +public: + TImpl(const TString& filePath, ui32 capacity, ui32 maxEntrySize) + : Map(filePath, TMemoryMapCommon::oRdWr) + , MaxEntrySize(maxEntrySize) + { + Y_ABORT_UNLESS(MaxEntrySize + sizeof(TEntryHeader) <= capacity); + + const ui32 realSize = sizeof(THeader) + capacity; + if (Map.Length() < realSize) { + Map.ResizeAndRemap(0, realSize); + } else { + Map.Map(0, realSize); + } + + if (Header()->Version) { + Y_ABORT_UNLESS(Header()->Version == VERSION); + Y_ABORT_UNLESS(Header()->Capacity == capacity); + } else { + Header()->Capacity = capacity; + Header()->Version = VERSION; + } + + Data = static_cast(Map.Ptr()) + sizeof(THeader); + End = Data + capacity; + + SkipSlackSpace(); + Visit([this] (ui32 checksum, TStringBuf entry) { + Y_UNUSED(checksum); + Y_UNUSED(entry); + ++Count; + }); + } + +public: + bool Push(TStringBuf data) + { + if (data.Empty() || data.Size() > MaxEntrySize) { + return false; + } + + const auto sz = data.Size() + sizeof(TEntryHeader); + auto* ptr = Data + Header()->WritePos; + + if (!Empty()) { + // checking that we have a contiguous chunk of sz + 1 bytes + // 1 extra byte is needed to distinguish between an empty buffer + // and a buffer which is completely full + if (Header()->ReadPos < Header()->WritePos) { + // we have a single contiguous occupied region + ui32 freeSpace = Header()->Capacity - Header()->WritePos; + if (freeSpace <= sz) { + if (Header()->ReadPos <= sz) { + // out of space + return false; + } + + memset(ptr, 0, freeSpace); + ptr = Data; + } + } else { + // we have two occupied regions + ui32 freeSpace = Header()->ReadPos - Header()->WritePos; + if (freeSpace <= sz) { + // out of space + return false; + } + } + } + + TMemoryOutput mo(ptr, sz); + WriteEntry(mo, data); + + Header()->WritePos = ptr - Data + sz; + ++Count; + + return true; + } + + TStringBuf Front() const + { + if (Empty()) { + return {}; + } + + const auto* b = Data + Header()->ReadPos; + if (b + sizeof(TEntryHeader) > End) { + // corruption + // TODO: report? + return {}; + } + + const auto* eh = reinterpret_cast(b); + TStringBuf result{b + sizeof(TEntryHeader), eh->Size}; + if (result.Data() + result.Size() > End) { + // corruption + // TODO: report? + return {}; + } + + return result; + } + + void Pop() + { + auto data = Front(); + if (!data) { + return; + } + + Header()->ReadPos += sizeof(TEntryHeader) + data.Size(); + --Count; + + SkipSlackSpace(); + } + + ui32 Size() const + { + return Count; + } + + bool Empty() const + { + const bool result = Header()->ReadPos == Header()->WritePos; + Y_DEBUG_ABORT_UNLESS(result == (Count == 0)); + return result; + } + + auto Validate() const + { + TVector entries; + + Visit([&] (ui32 checksum, TStringBuf entry) { + const ui32 actualChecksum = Crc32c(entry.Data(), entry.Size()); + if (actualChecksum != checksum) { + entries.push_back({ + TString(entry), + checksum, + actualChecksum}); + } + }); + + return entries; + } +}; + +//////////////////////////////////////////////////////////////////////////////// + +TFileRingBuffer::TFileRingBuffer( + const TString& filePath, + ui32 capacity, + ui32 maxEntrySize) + : Impl(new TImpl(filePath, capacity, maxEntrySize)) +{} + +TFileRingBuffer::~TFileRingBuffer() = default; + +bool TFileRingBuffer::Push(TStringBuf data) +{ + return Impl->Push(data); +} + +TStringBuf TFileRingBuffer::Front() const +{ + return Impl->Front(); +} + +void TFileRingBuffer::Pop() +{ + Impl->Pop(); +} + +ui32 TFileRingBuffer::Size() const +{ + return Impl->Size(); +} + +bool TFileRingBuffer::Empty() const +{ + return Impl->Empty(); +} + +TVector TFileRingBuffer::Validate() const +{ + return Impl->Validate(); +} + +} // namespace NCloud::NFileStore diff --git a/cloud/filestore/libs/vfs_fuse/file_ring_buffer.h b/cloud/filestore/libs/vfs_fuse/file_ring_buffer.h new file mode 100644 index 00000000000..8da9fa935ae --- /dev/null +++ b/cloud/filestore/libs/vfs_fuse/file_ring_buffer.h @@ -0,0 +1,36 @@ +#pragma once + +#include +#include + +namespace NCloud::NFileStore { + +//////////////////////////////////////////////////////////////////////////////// + +struct TBrokenFileRingBufferEntry +{ + TString Data; + ui32 ExpectedChecksum = 0; + ui32 ActualChecksum = 0; +}; + +class TFileRingBuffer +{ +private: + class TImpl; + std::unique_ptr Impl; + +public: + TFileRingBuffer(const TString& filePath, ui32 capacity, ui32 maxEntrySize); + ~TFileRingBuffer(); + +public: + bool Push(TStringBuf data); + TStringBuf Front() const; + void Pop(); + ui32 Size() const; + bool Empty() const; + TVector Validate() const; +}; + +} // namespace NCloud::NFileStore diff --git a/cloud/filestore/libs/vfs_fuse/file_ring_buffer_ut.cpp b/cloud/filestore/libs/vfs_fuse/file_ring_buffer_ut.cpp new file mode 100644 index 00000000000..7e789467bc8 --- /dev/null +++ b/cloud/filestore/libs/vfs_fuse/file_ring_buffer_ut.cpp @@ -0,0 +1,338 @@ +#include "file_ring_buffer.h" + +#include + +#include +#include +#include +#include +#include + +namespace NCloud::NFileStore { + +namespace { + +//////////////////////////////////////////////////////////////////////////////// + +TString Dump(const TVector& entries) +{ + TStringBuilder sb; + + for (ui32 i = 0; i < entries.size(); ++i) { + if (i) { + sb << ", "; + } + + sb << "data=" << entries[i].Data + << " ecsum=" << entries[i].ExpectedChecksum + << " csum=" << entries[i].ActualChecksum; + } + + return sb; +} + +TString PopAll(TFileRingBuffer& rb) +{ + TStringBuilder sb; + + while (!rb.Empty()) { + if (sb.Size()) { + sb << ", "; + } + + sb << rb.Front(); + rb.Pop(); + } + + return sb; +} + +//////////////////////////////////////////////////////////////////////////////// + +struct TReferenceImplementation +{ + static constexpr ui32 EntryOverhead = 8; + + const ui32 MaxWeight; + const ui32 MaxEntrySize; + + TDeque Q; + ui32 ReadPos = 0; + ui32 WritePos = 0; + ui32 SlackSpace = 0; + + explicit TReferenceImplementation(ui32 maxWeight, ui32 maxEntrySize) + : MaxWeight(maxWeight) + , MaxEntrySize(maxEntrySize) + {} + + bool Push(TStringBuf data) + { + if (data.Empty() || data.Size() > MaxEntrySize) { + return false; + } + + const ui32 sz = EntryOverhead + data.Size(); + + if (!Empty()) { + if (ReadPos < WritePos) { + const auto avail = MaxWeight - WritePos; + if (avail <= sz) { + if (ReadPos <= sz) { + // out of space + return false; + } + + SlackSpace = avail; + WritePos = 0; + } + } else { + const auto avail = ReadPos - WritePos; + if (avail <= sz) { + // out of space + return false; + } + } + } + + WritePos += sz; + Q.emplace_back(data); + return true; + } + + TStringBuf Front() const + { + if (!Q) { + return {}; + } + + return Q.front(); + } + + void Pop() + { + if (!Q) { + return; + } + + const ui32 sz = Q.front().Size() + EntryOverhead; + ReadPos += sz; + if (MaxWeight - ReadPos <= SlackSpace) { + UNIT_ASSERT_VALUES_EQUAL(SlackSpace, MaxWeight - ReadPos); + if (ReadPos == WritePos) { + WritePos = 0; + } + ReadPos = 0; + SlackSpace = 0; + } + + Q.pop_front(); + } + + bool Empty() const + { + return Q.empty(); + } + + ui32 Size() const + { + return Q.size(); + } + + auto Validate() const + { + return TVector(); + } +}; + +} // namespace + +//////////////////////////////////////////////////////////////////////////////// + +Y_UNIT_TEST_SUITE(TFileRingBufferTest) +{ + template + void DoTestShouldPushPop(TRingBuffer& rb) + { + UNIT_ASSERT_VALUES_EQUAL(0, rb.Size()); + UNIT_ASSERT(rb.Empty()); + + UNIT_ASSERT(!rb.Push("longvasya11")); // too long + UNIT_ASSERT(!rb.Push("")); // empty + UNIT_ASSERT(rb.Push("vasya")); + UNIT_ASSERT(rb.Push("petya")); + UNIT_ASSERT(rb.Push("vasya2")); + UNIT_ASSERT(rb.Push("petya2")); + UNIT_ASSERT(!rb.Push("vasya3")); // out of space + + UNIT_ASSERT_VALUES_EQUAL("", Dump(rb.Validate())); + UNIT_ASSERT_VALUES_EQUAL(4, rb.Size()); + UNIT_ASSERT_VALUES_EQUAL("vasya", rb.Front()); + rb.Pop(); + + UNIT_ASSERT_VALUES_EQUAL("", Dump(rb.Validate())); + UNIT_ASSERT_VALUES_EQUAL(3, rb.Size()); + UNIT_ASSERT(!rb.Push("vasya3")); + + UNIT_ASSERT_VALUES_EQUAL("petya", rb.Front()); + rb.Pop(); + + UNIT_ASSERT_VALUES_EQUAL("", Dump(rb.Validate())); + UNIT_ASSERT_VALUES_EQUAL(2, rb.Size()); + UNIT_ASSERT(rb.Push("vasya3")); + + UNIT_ASSERT_VALUES_EQUAL("", Dump(rb.Validate())); + UNIT_ASSERT_VALUES_EQUAL(3, rb.Size()); + UNIT_ASSERT_VALUES_EQUAL("vasya2", rb.Front()); + rb.Pop(); + + UNIT_ASSERT_VALUES_EQUAL("", Dump(rb.Validate())); + UNIT_ASSERT_VALUES_EQUAL(2, rb.Size()); + UNIT_ASSERT_VALUES_EQUAL("petya2", rb.Front()); + rb.Pop(); + + UNIT_ASSERT_VALUES_EQUAL("", Dump(rb.Validate())); + UNIT_ASSERT_VALUES_EQUAL(1, rb.Size()); + UNIT_ASSERT_VALUES_EQUAL("vasya3", rb.Front()); + rb.Pop(); + + UNIT_ASSERT_VALUES_EQUAL("", Dump(rb.Validate())); + UNIT_ASSERT_VALUES_EQUAL(0, rb.Size()); + UNIT_ASSERT(rb.Empty()); + } + + Y_UNIT_TEST(ShouldPushPop) + { + const auto f = TTempFileHandle(); + const ui32 len = 64; + const ui32 maxEntrySize = 10; + TFileRingBuffer rb(f.GetName(), len, maxEntrySize); + + DoTestShouldPushPop(rb); + } + + Y_UNIT_TEST(ShouldPushPopReferenceImplementation) + { + const ui32 len = 64; + const ui32 maxEntrySize = 10; + TReferenceImplementation rb(len, maxEntrySize); + + DoTestShouldPushPop(rb); + } + + Y_UNIT_TEST(ShouldRestore) + { + const auto f = TTempFileHandle(); + const ui32 len = 64; + const ui32 maxEntrySize = 10; + auto rb = std::make_unique( + f.GetName(), + len, + maxEntrySize); + + UNIT_ASSERT(rb->Push("vasya")); + UNIT_ASSERT(rb->Push("petya")); + UNIT_ASSERT(rb->Push("vasya2")); + UNIT_ASSERT(rb->Push("petya2")); + rb->Pop(); + rb->Pop(); + UNIT_ASSERT(rb->Push("vasya3")); + UNIT_ASSERT(rb->Push("xxx")); + + rb = std::make_unique( + f.GetName(), + len, + maxEntrySize); + + UNIT_ASSERT_VALUES_EQUAL("", Dump(rb->Validate())); + UNIT_ASSERT_VALUES_EQUAL(4, rb->Size()); + + UNIT_ASSERT_VALUES_EQUAL("vasya2, petya2, vasya3, xxx", PopAll(*rb)); + } + + Y_UNIT_TEST(ShouldValidate) + { + const auto f = TTempFileHandle(); + const ui32 len = 64; + const ui32 maxEntrySize = 10; + TFileRingBuffer rb(f.GetName(), len, maxEntrySize); + + UNIT_ASSERT(rb.Push("vasya")); + UNIT_ASSERT(rb.Push("petya")); + UNIT_ASSERT(rb.Push("vasya2")); + UNIT_ASSERT(rb.Push("petya2")); + + UNIT_ASSERT_VALUES_EQUAL("", Dump(rb.Validate())); + TFileMap m(f.GetName(), TMemoryMapCommon::oRdWr); + m.Map(0, len); + char* data = static_cast(m.Ptr()); + data[10] = 'A'; + + UNIT_ASSERT_VALUES_EQUAL( + "data=invalid_entry_marker ecsum=0 csum=11034342", + Dump(rb.Validate())); + } + + TString GenerateData(ui32 sz) + { + TString s(sz, 0); + for (ui32 i = 0; i < sz; ++i) { + s[i] = 'a' + RandomNumber('z' - 'a' + 1); + } + return s; + } + + Y_UNIT_TEST(RandomizedPushPopRestore) + { + const auto f = TTempFileHandle(); + const ui32 len = 1_MB; + const ui32 testBytes = 16_MB; + const ui32 maxEntrySize = 4_KB; + const ui32 testUpToEntrySize = 5_KB; + const double restoreProbability = 0.05; + std::unique_ptr rb; + TReferenceImplementation ri(len, maxEntrySize); + + auto restore = [&] () { + rb = std::make_unique( + f.GetName(), + len, + maxEntrySize); + }; + + restore(); + + ui32 remainingBytes = testBytes; + while (remainingBytes || !ri.Empty()) { + const bool shouldPush = remainingBytes && RandomNumber(); + if (shouldPush) { + const ui32 entrySize = + RandomNumber(Min(remainingBytes + 1, testUpToEntrySize)); + const auto data = GenerateData(entrySize); + const bool pushed = ri.Push(data); + UNIT_ASSERT_VALUES_EQUAL(pushed, rb->Push(data)); + if (pushed) { + remainingBytes -= entrySize; + // Cerr << "PUSH\t" << data << Endl; + } + } else { + UNIT_ASSERT_VALUES_EQUAL(ri.Front(), rb->Front()); + // Cerr << "POP\t" << ri.Front() << Endl; + ri.Pop(); + rb->Pop(); + } + + // Cerr << ri.Size() << " " << remainingBytes << Endl; + + if (RandomNumber() < restoreProbability) { + restore(); + } + + UNIT_ASSERT_VALUES_EQUAL(ri.Size(), rb->Size()); + UNIT_ASSERT_VALUES_EQUAL(ri.Empty(), rb->Empty()); + UNIT_ASSERT_VALUES_EQUAL("", Dump(rb->Validate())); + } + } +} + +} // namespace NCloud::NFileStore diff --git a/cloud/filestore/libs/vfs_fuse/handle_ops_queue.cpp b/cloud/filestore/libs/vfs_fuse/handle_ops_queue.cpp index 9ec05a7338d..a4ac62ba5db 100644 --- a/cloud/filestore/libs/vfs_fuse/handle_ops_queue.cpp +++ b/cloud/filestore/libs/vfs_fuse/handle_ops_queue.cpp @@ -6,30 +6,30 @@ namespace NCloud::NFileStore::NFuse { void THandleOpsQueue::AddDestroyRequest(ui64 nodeId, ui64 handle) { - NProto::TQueueEntry request; - request.MutableDestroyHandleRequest()->SetHandle(handle); - request.MutableDestroyHandleRequest()->SetNodeId(nodeId); - Requests.push(request); + NProto::TQueueEntry request; + request.MutableDestroyHandleRequest()->SetHandle(handle); + request.MutableDestroyHandleRequest()->SetNodeId(nodeId); + Requests.push(request); } const NProto::TQueueEntry& THandleOpsQueue::Front() { - return Requests.front(); + return Requests.front(); } bool THandleOpsQueue::Empty() const { - return Requests.empty(); + return Requests.empty(); } void THandleOpsQueue::Pop() { - Requests.pop(); + Requests.pop(); } ui64 THandleOpsQueue::Size() const { - return Requests.size(); + return Requests.size(); } } // namespace NCloud::NFileStore::NFuse diff --git a/cloud/filestore/libs/vfs_fuse/handle_ops_queue.h b/cloud/filestore/libs/vfs_fuse/handle_ops_queue.h index 849e16bd9bd..2b3a8af430f 100644 --- a/cloud/filestore/libs/vfs_fuse/handle_ops_queue.h +++ b/cloud/filestore/libs/vfs_fuse/handle_ops_queue.h @@ -1,3 +1,5 @@ +#pragma once + #include #include @@ -9,14 +11,14 @@ namespace NCloud::NFileStore::NFuse { class THandleOpsQueue { private: - TQueue Requests; + TQueue Requests; public: - void AddDestroyRequest(ui64 nodeId, ui64 handle); - const NProto::TQueueEntry& Front(); - void Pop(); - ui64 Size() const; - bool Empty() const; + void AddDestroyRequest(ui64 nodeId, ui64 handle); + const NProto::TQueueEntry& Front(); + void Pop(); + ui64 Size() const; + bool Empty() const; }; } // namespace NCloud::NFileStore::NFuse diff --git a/cloud/filestore/libs/vfs_fuse/loop.cpp b/cloud/filestore/libs/vfs_fuse/loop.cpp index fc41789be7f..c0ebb8c54fc 100644 --- a/cloud/filestore/libs/vfs_fuse/loop.cpp +++ b/cloud/filestore/libs/vfs_fuse/loop.cpp @@ -1007,7 +1007,7 @@ class TFileSystemLoop final // ops.statfs = [] (fuse_req_t req, fuse_ino_t ino) { - CALL(StatFs, EFileStoreRequest::GetFileStoreInfo, 0, req, ino); + CALL(StatFs, EFileStoreRequest::StatFileStore, 0, req, ino); }; // diff --git a/cloud/filestore/libs/vfs_fuse/ut/CMakeLists.darwin-x86_64.txt b/cloud/filestore/libs/vfs_fuse/ut/CMakeLists.darwin-x86_64.txt index a6906d8cce9..71ef4a61853 100644 --- a/cloud/filestore/libs/vfs_fuse/ut/CMakeLists.darwin-x86_64.txt +++ b/cloud/filestore/libs/vfs_fuse/ut/CMakeLists.darwin-x86_64.txt @@ -25,6 +25,7 @@ target_link_options(cloud-filestore-libs-vfs_fuse-ut PRIVATE CoreFoundation ) target_sources(cloud-filestore-libs-vfs_fuse-ut PRIVATE + ${CMAKE_SOURCE_DIR}/cloud/filestore/libs/vfs_fuse/file_ring_buffer_ut.cpp ${CMAKE_SOURCE_DIR}/cloud/filestore/libs/vfs_fuse/fs_ut.cpp ) set_property( diff --git a/cloud/filestore/libs/vfs_fuse/ut/CMakeLists.linux-aarch64.txt b/cloud/filestore/libs/vfs_fuse/ut/CMakeLists.linux-aarch64.txt index 2b27e93f90e..7e2fff15300 100644 --- a/cloud/filestore/libs/vfs_fuse/ut/CMakeLists.linux-aarch64.txt +++ b/cloud/filestore/libs/vfs_fuse/ut/CMakeLists.linux-aarch64.txt @@ -28,6 +28,7 @@ target_link_options(cloud-filestore-libs-vfs_fuse-ut PRIVATE -ldl ) target_sources(cloud-filestore-libs-vfs_fuse-ut PRIVATE + ${CMAKE_SOURCE_DIR}/cloud/filestore/libs/vfs_fuse/file_ring_buffer_ut.cpp ${CMAKE_SOURCE_DIR}/cloud/filestore/libs/vfs_fuse/fs_ut.cpp ) set_property( diff --git a/cloud/filestore/libs/vfs_fuse/ut/CMakeLists.linux-x86_64.txt b/cloud/filestore/libs/vfs_fuse/ut/CMakeLists.linux-x86_64.txt index 8c2b35eef01..942ab76a0af 100644 --- a/cloud/filestore/libs/vfs_fuse/ut/CMakeLists.linux-x86_64.txt +++ b/cloud/filestore/libs/vfs_fuse/ut/CMakeLists.linux-x86_64.txt @@ -29,6 +29,7 @@ target_link_options(cloud-filestore-libs-vfs_fuse-ut PRIVATE -ldl ) target_sources(cloud-filestore-libs-vfs_fuse-ut PRIVATE + ${CMAKE_SOURCE_DIR}/cloud/filestore/libs/vfs_fuse/file_ring_buffer_ut.cpp ${CMAKE_SOURCE_DIR}/cloud/filestore/libs/vfs_fuse/fs_ut.cpp ) set_property( diff --git a/cloud/filestore/libs/vfs_fuse/ut/CMakeLists.windows-x86_64.txt b/cloud/filestore/libs/vfs_fuse/ut/CMakeLists.windows-x86_64.txt index 8287aecf90d..a1c55759c71 100644 --- a/cloud/filestore/libs/vfs_fuse/ut/CMakeLists.windows-x86_64.txt +++ b/cloud/filestore/libs/vfs_fuse/ut/CMakeLists.windows-x86_64.txt @@ -18,6 +18,7 @@ target_link_libraries(cloud-filestore-libs-vfs_fuse-ut PUBLIC filestore-libs-vhost ) target_sources(cloud-filestore-libs-vfs_fuse-ut PRIVATE + ${CMAKE_SOURCE_DIR}/cloud/filestore/libs/vfs_fuse/file_ring_buffer_ut.cpp ${CMAKE_SOURCE_DIR}/cloud/filestore/libs/vfs_fuse/fs_ut.cpp ) set_property( diff --git a/cloud/filestore/libs/vfs_fuse/ut/ya.make b/cloud/filestore/libs/vfs_fuse/ut/ya.make index d92df2d4e6c..37b10750f8b 100644 --- a/cloud/filestore/libs/vfs_fuse/ut/ya.make +++ b/cloud/filestore/libs/vfs_fuse/ut/ya.make @@ -5,6 +5,7 @@ INCLUDE(${ARCADIA_ROOT}/cloud/filestore/tests/recipes/medium.inc) SRCDIR(cloud/filestore/libs/vfs_fuse) SRCS( + file_ring_buffer_ut.cpp fs_ut.cpp ) diff --git a/cloud/filestore/libs/vfs_fuse/vhost/CMakeLists.darwin-x86_64.txt b/cloud/filestore/libs/vfs_fuse/vhost/CMakeLists.darwin-x86_64.txt index 03363f6de8e..9b59bf00a07 100644 --- a/cloud/filestore/libs/vfs_fuse/vhost/CMakeLists.darwin-x86_64.txt +++ b/cloud/filestore/libs/vfs_fuse/vhost/CMakeLists.darwin-x86_64.txt @@ -30,6 +30,7 @@ target_link_libraries(libs-vfs_fuse-vhost PUBLIC target_sources(libs-vfs_fuse-vhost PRIVATE ${CMAKE_SOURCE_DIR}/cloud/filestore/libs/vfs_fuse/cache.cpp ${CMAKE_SOURCE_DIR}/cloud/filestore/libs/vfs_fuse/config.cpp + ${CMAKE_SOURCE_DIR}/cloud/filestore/libs/vfs_fuse/file_ring_buffer.cpp ${CMAKE_SOURCE_DIR}/cloud/filestore/libs/vfs_fuse/fs.cpp ${CMAKE_SOURCE_DIR}/cloud/filestore/libs/vfs_fuse/fs_impl.cpp ${CMAKE_SOURCE_DIR}/cloud/filestore/libs/vfs_fuse/fs_impl_attr.cpp diff --git a/cloud/filestore/libs/vfs_fuse/vhost/CMakeLists.linux-aarch64.txt b/cloud/filestore/libs/vfs_fuse/vhost/CMakeLists.linux-aarch64.txt index 0ea8ccccb98..0412eaded12 100644 --- a/cloud/filestore/libs/vfs_fuse/vhost/CMakeLists.linux-aarch64.txt +++ b/cloud/filestore/libs/vfs_fuse/vhost/CMakeLists.linux-aarch64.txt @@ -32,6 +32,7 @@ target_link_libraries(libs-vfs_fuse-vhost PUBLIC target_sources(libs-vfs_fuse-vhost PRIVATE ${CMAKE_SOURCE_DIR}/cloud/filestore/libs/vfs_fuse/cache.cpp ${CMAKE_SOURCE_DIR}/cloud/filestore/libs/vfs_fuse/config.cpp + ${CMAKE_SOURCE_DIR}/cloud/filestore/libs/vfs_fuse/file_ring_buffer.cpp ${CMAKE_SOURCE_DIR}/cloud/filestore/libs/vfs_fuse/fs.cpp ${CMAKE_SOURCE_DIR}/cloud/filestore/libs/vfs_fuse/fs_impl.cpp ${CMAKE_SOURCE_DIR}/cloud/filestore/libs/vfs_fuse/fs_impl_attr.cpp diff --git a/cloud/filestore/libs/vfs_fuse/vhost/CMakeLists.linux-x86_64.txt b/cloud/filestore/libs/vfs_fuse/vhost/CMakeLists.linux-x86_64.txt index 0ea8ccccb98..0412eaded12 100644 --- a/cloud/filestore/libs/vfs_fuse/vhost/CMakeLists.linux-x86_64.txt +++ b/cloud/filestore/libs/vfs_fuse/vhost/CMakeLists.linux-x86_64.txt @@ -32,6 +32,7 @@ target_link_libraries(libs-vfs_fuse-vhost PUBLIC target_sources(libs-vfs_fuse-vhost PRIVATE ${CMAKE_SOURCE_DIR}/cloud/filestore/libs/vfs_fuse/cache.cpp ${CMAKE_SOURCE_DIR}/cloud/filestore/libs/vfs_fuse/config.cpp + ${CMAKE_SOURCE_DIR}/cloud/filestore/libs/vfs_fuse/file_ring_buffer.cpp ${CMAKE_SOURCE_DIR}/cloud/filestore/libs/vfs_fuse/fs.cpp ${CMAKE_SOURCE_DIR}/cloud/filestore/libs/vfs_fuse/fs_impl.cpp ${CMAKE_SOURCE_DIR}/cloud/filestore/libs/vfs_fuse/fs_impl_attr.cpp diff --git a/cloud/filestore/libs/vfs_fuse/vhost/CMakeLists.windows-x86_64.txt b/cloud/filestore/libs/vfs_fuse/vhost/CMakeLists.windows-x86_64.txt index 03363f6de8e..9b59bf00a07 100644 --- a/cloud/filestore/libs/vfs_fuse/vhost/CMakeLists.windows-x86_64.txt +++ b/cloud/filestore/libs/vfs_fuse/vhost/CMakeLists.windows-x86_64.txt @@ -30,6 +30,7 @@ target_link_libraries(libs-vfs_fuse-vhost PUBLIC target_sources(libs-vfs_fuse-vhost PRIVATE ${CMAKE_SOURCE_DIR}/cloud/filestore/libs/vfs_fuse/cache.cpp ${CMAKE_SOURCE_DIR}/cloud/filestore/libs/vfs_fuse/config.cpp + ${CMAKE_SOURCE_DIR}/cloud/filestore/libs/vfs_fuse/file_ring_buffer.cpp ${CMAKE_SOURCE_DIR}/cloud/filestore/libs/vfs_fuse/fs.cpp ${CMAKE_SOURCE_DIR}/cloud/filestore/libs/vfs_fuse/fs_impl.cpp ${CMAKE_SOURCE_DIR}/cloud/filestore/libs/vfs_fuse/fs_impl_attr.cpp diff --git a/cloud/filestore/libs/vfs_fuse/ya.make.inc b/cloud/filestore/libs/vfs_fuse/ya.make.inc index 0d9604a29c5..e01bfd59275 100644 --- a/cloud/filestore/libs/vfs_fuse/ya.make.inc +++ b/cloud/filestore/libs/vfs_fuse/ya.make.inc @@ -1,6 +1,7 @@ SRCS( cache.cpp config.cpp + file_ring_buffer.cpp fs.cpp fs_impl.cpp fs_impl_attr.cpp diff --git a/cloud/filestore/private/api/protos/tablet.proto b/cloud/filestore/private/api/protos/tablet.proto index 42e6440a190..c4cb014fe15 100644 --- a/cloud/filestore/private/api/protos/tablet.proto +++ b/cloud/filestore/private/api/protos/tablet.proto @@ -601,3 +601,43 @@ message TWriteCompactionMapResponse // Optional error, set only if error happened. NCloud.NProto.TError Error = 1; } + +//////////////////////////////////////////////////////////////////////////////// +// Direct inode manipulation. + +message TUnsafeDeleteNodeRequest +{ + string FileSystemId = 1; + uint64 Id = 2; +} + +message TUnsafeDeleteNodeResponse +{ + // Optional error, set only if error happened. + NCloud.NProto.TError Error = 1; +} + +message TUnsafeUpdateNodeRequest +{ + string FileSystemId = 1; + NProto.TNodeAttr Node = 2; +} + +message TUnsafeUpdateNodeResponse +{ + // Optional error, set only if error happened. + NCloud.NProto.TError Error = 1; +} + +message TUnsafeGetNodeRequest +{ + string FileSystemId = 1; + uint64 Id = 2; +} + +message TUnsafeGetNodeResponse +{ + // Optional error, set only if error happened. + NCloud.NProto.TError Error = 1; + NProto.TNodeAttr Node = 2; +} diff --git a/cloud/filestore/tests/client/canondata/result.json b/cloud/filestore/tests/client/canondata/result.json index a90d991861a..1bb59bd5bb0 100644 --- a/cloud/filestore/tests/client/canondata/result.json +++ b/cloud/filestore/tests/client/canondata/result.json @@ -11,6 +11,9 @@ "test.test_describe_sessions": { "uri": "file://test.test_describe_sessions/results.txt" }, + "test.test_large_file": { + "uri": "file://test.test_large_file/results.txt" + }, "test.test_list_filestores": { "uri": "file://test.test_list_filestores/results.txt" }, diff --git a/cloud/filestore/tests/client/canondata/test.test_large_file/results.txt b/cloud/filestore/tests/client/canondata/test.test_large_file/results.txt new file mode 100644 index 0000000000000000000000000000000000000000..b62d5e3351d2c01dc2277aa0632312e37c80e1f1 GIT binary patch literal 2235 zcmb~ z))$;v1vbyX($dt>(9Fo(+}sR@aSWqiGz3ONV5EfrNu`L9$!Os*(h|t1e@8