Skip to content

Commit ab547e6

Browse files
committed
[Filestore] issue-3687: implement pagination during ListNodes for local filestore
1 parent 63eb14e commit ab547e6

File tree

10 files changed

+109
-9
lines changed

10 files changed

+109
-9
lines changed

cloud/filestore/config/server.proto

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,9 @@ message TLocalServiceConfig
123123

124124
// Trust guest kernel to check user permissions for fuse operations
125125
optional bool GuestOnlyPermissionsCheckEnabled = 21;
126+
127+
// Max entries each list nodes will return
128+
optional uint32 MaxEntriesPerListNodes = 22;
126129
}
127130

128131
////////////////////////////////////////////////////////////////////////////////

cloud/filestore/libs/service_local/config.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ constexpr TDuration AsyncHandleOpsPeriod = TDuration::MilliSeconds(50);
3737
xxx(ServerWriteBackCacheEnabled, bool, false )\
3838
xxx(DontPopulateNodeCacheWhenListingNodes, bool, false )\
3939
xxx(GuestOnlyPermissionsCheckEnabled, bool, false )\
40+
xxx(MaxEntriesPerListNodes, ui32, 10000 )\
4041
// FILESTORE_SERVICE_CONFIG
4142

4243
#define FILESTORE_SERVICE_DECLARE_CONFIG(name, type, value) \

cloud/filestore/libs/service_local/config.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,8 @@ class TLocalFileStoreConfig
5151
bool GetDontPopulateNodeCacheWhenListingNodes() const;
5252

5353
bool GetGuestOnlyPermissionsCheckEnabled() const;
54+
55+
ui32 GetMaxEntriesPerListNodes() const;
5456
};
5557

5658
} // namespace NCloud::NFileStore

cloud/filestore/libs/service_local/fs_node.cpp

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#include <cloud/filestore/libs/diagnostics/critical_events.h>
66

77
#include <util/generic/guid.h>
8+
#include <util/stream/format.h>
89

910
namespace NCloud::NFileStore {
1011

@@ -205,7 +206,14 @@ NProto::TListNodesResponse TLocalFileSystem::ListNodes(
205206
return TErrorResponse(ErrorInvalidParent(request.GetNodeId()));
206207
}
207208

208-
auto entries = parent->List();
209+
uint64_t offset = 0;
210+
auto& cookie = request.GetCookie();
211+
if (cookie) {
212+
offset = FromString(cookie);
213+
}
214+
215+
auto listRes = parent->List(offset, Config->GetMaxEntriesPerListNodes());
216+
auto& entries = listRes.DirEntries;
209217

210218
NProto::TListNodesResponse response;
211219
response.MutableNames()->Reserve(entries.size());
@@ -237,6 +245,7 @@ NProto::TListNodesResponse TLocalFileSystem::ListNodes(
237245
ConvertStats(entry.second, *response.MutableNodes()->Add());
238246
}
239247

248+
response.SetCookie(ToString(listRes.DirOffset));
240249
return response;
241250
}
242251

cloud/filestore/libs/service_local/index.cpp

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,19 @@ TString TIndexNode::ReadLink() const
9292

9393
TVector<std::pair<TString, TFileStat>> TIndexNode::List(bool ignoreErrors)
9494
{
95-
return NLowLevel::ListDirAt(NodeFd, ignoreErrors);
95+
auto res = NLowLevel::ListDirAt(
96+
NodeFd,
97+
0, // start at begining of dir
98+
0, // don't limit number of entries
99+
ignoreErrors);
100+
101+
return std::move(res.DirEntries);
102+
}
103+
104+
NLowLevel::TListDirResult
105+
TIndexNode::List(uint64_t offset, size_t entriesLimit, bool ignoreErrors)
106+
{
107+
return NLowLevel::ListDirAt(NodeFd, offset, entriesLimit, ignoreErrors);
96108
}
97109

98110
TFileStat TIndexNode::Stat()

cloud/filestore/libs/service_local/index.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,8 @@ class TIndexNode
6868
TIndexNodePtr CreateSocket(const TString& name, int flags);
6969

7070
TVector<std::pair<TString, TFileStat>> List(bool ignoreErrors = false);
71+
NLowLevel::TListDirResult
72+
List(uint64_t offset, size_t entriesLimit, bool ignoreErrors = false);
7173

7274
void Rename(
7375
const TString& name,

cloud/filestore/libs/service_local/lowlevel.cpp

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -297,8 +297,10 @@ TFileSystemStat StatFs(const TFileHandle& handle)
297297
return GetFileSystemStat(fs);
298298
}
299299

300-
TVector<std::pair<TString, TFileStat>> ListDirAt(
300+
TListDirResult ListDirAt(
301301
const TFileHandle& handle,
302+
uint64_t offset,
303+
size_t entriesLimit,
302304
bool ignoreErrors)
303305
{
304306
auto fd = openat(Fd(handle), ".", O_RDONLY);
@@ -320,7 +322,11 @@ TVector<std::pair<TString, TFileStat>> ListDirAt(
320322
}
321323
};
322324

323-
TVector<std::pair<TString, TFileStat>> results;
325+
if (offset) {
326+
seekdir(dir, offset);
327+
}
328+
329+
TListDirResult res;
324330

325331
errno = 0;
326332
while (auto* entry = readdir(dir)) {
@@ -332,22 +338,28 @@ TVector<std::pair<TString, TFileStat>> ListDirAt(
332338
if (ignoreErrors) {
333339
try {
334340
auto stat = StatAt(handle, name);
335-
results.emplace_back(std::move(name), stat);
341+
res.DirEntries.emplace_back(std::move(name), stat);
336342
} catch (const TServiceError& err) {
337343
errno = 0;
338344
continue;
339345
}
340346
} else {
341347
auto stat = StatAt(handle, name);
342-
results.emplace_back(std::move(name), stat);
348+
res.DirEntries.emplace_back(std::move(name), stat);
349+
}
350+
351+
if (entriesLimit && --entriesLimit == 0) {
352+
break;
343353
}
344354
}
345355

356+
res.DirOffset = telldir(dir);
357+
346358
Y_ENSURE_EX(errno == 0, TServiceError(GetSystemErrorCode())
347359
<< "failed to list: "
348360
<< LastSystemErrorText());
349361

350-
return results;
362+
return res;
351363
}
352364

353365
////////////////////////////////////////////////////////////////////////////////

cloud/filestore/libs/service_local/lowlevel.h

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,14 @@ struct TOpenOrCreateResult
105105

106106
////////////////////////////////////////////////////////////////////////////////
107107

108+
struct TListDirResult
109+
{
110+
TVector<std::pair<TString, TFileStat>> DirEntries;
111+
uint64_t DirOffset;
112+
};
113+
114+
////////////////////////////////////////////////////////////////////////////////
115+
108116
TFileHandle Open(const TString& path, int flags, int mode);
109117
TFileHandle Open(const TFileHandle& handle, int flags, int mode);
110118
TFileHandle OpenAt(
@@ -144,8 +152,10 @@ TFileStat Stat(const TFileHandle& handle);
144152
TFileStat StatAt(const TFileHandle& handle, const TString& name);
145153
TFileSystemStat StatFs(const TFileHandle& handle);
146154

147-
TVector<std::pair<TString, TFileStat>> ListDirAt(
155+
TListDirResult ListDirAt(
148156
const TFileHandle& handle,
157+
uint64_t offset,
158+
size_t entriesLimit,
149159
bool ignoreErrors);
150160

151161
//

cloud/filestore/libs/service_local/lowlevel_ut.cpp

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,55 @@ Y_UNIT_TEST_SUITE(TLowlevelTest)
5858
res = NLowLevel::OpenOrCreateAt(node, "2.txt", O_WRONLY, 0755);
5959
UNIT_ASSERT(!res.WasCreated);
6060
}
61+
62+
Y_UNIT_TEST(ShouldDoIterativeListDir)
63+
{
64+
const TTempDir TempDir;
65+
auto rootNode = NLowLevel::Open(TempDir.Name(), O_PATH, 0);
66+
67+
int nodesCount = 10;
68+
TSet<TString> entryNames;
69+
for (int i = 0; i < nodesCount; i++) {
70+
if (i == 0) {
71+
auto name = "dir_" + ToString(i);
72+
entryNames.insert(name);
73+
NLowLevel::MkDirAt(rootNode, name, 0755);
74+
} else {
75+
auto name = "file_" + ToString(i);
76+
entryNames.insert(name);
77+
NLowLevel::OpenAt(
78+
rootNode,
79+
name,
80+
O_CREAT | O_WRONLY,
81+
0755);
82+
}
83+
}
84+
85+
auto checkListDirResult =
86+
[&](NLowLevel::TListDirResult& res, size_t expectedEntriesCount)
87+
{
88+
UNIT_ASSERT_EQUAL(res.DirEntries.size(), expectedEntriesCount);
89+
for (auto& entry: res.DirEntries) {
90+
UNIT_ASSERT_EQUAL_C(
91+
1,
92+
entryNames.count(entry.first),
93+
TStringBuilder() << entry.first << " missing");
94+
entryNames.erase(entry.first);
95+
}
96+
};
97+
98+
auto res = NLowLevel::ListDirAt(rootNode, 0, 5, false);
99+
checkListDirResult(res, 5);
100+
101+
res = NLowLevel::ListDirAt(rootNode, res.DirOffset, 3, false);
102+
checkListDirResult(res, 3);
103+
104+
res = NLowLevel::ListDirAt(rootNode, res.DirOffset, 2, false);
105+
checkListDirResult(res, 2);
106+
107+
res = NLowLevel::ListDirAt(rootNode, res.DirOffset, 2, false);
108+
checkListDirResult(res, 0);
109+
}
61110
};
62111

63112
} // namespace NCloud::NFileStore

cloud/filestore/libs/service_local/session.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,7 @@ class TSession
164164
} catch (...) {
165165
STORAGE_ERROR(
166166
"Failed to open Handle, HandleId=" << it->HandleId <<
167-
", NodeId" << it->NodeId <<
167+
", NodeId=" << it->NodeId <<
168168
", Exception=" << CurrentExceptionMessage());
169169
HandleTable->DeleteRecord(it.GetIndex());
170170
continue;

0 commit comments

Comments
 (0)