[History Server] Keep directory placeholders out of ListFiles results - #5089
Open
stantheman0128 wants to merge 2 commits into
Open
[History Server] Keep directory placeholders out of ListFiles results#5089stantheman0128 wants to merge 2 commits into
stantheman0128 wants to merge 2 commits into
Conversation
…ob backends Neither backend had any test touching GetContent. s3_test.go and azureblob_test.go held only TestTrim and TestWalk, which assert nothing and never construct a handler, so the object path that GetContent builds from rootDir, cluster prefix and file name was unverified on both. That path is easy to get wrong: the aliyunoss backend shipped without the root dir in it (ray-project#4820), and the same mistake in either of these would break every log fetch on a deployment that configures a root dir. Add two tests per backend, driving the real SDK against an httptest server so the assertion is on the path that reaches the wire rather than on a helper's return value. s3 uses path style addressing, which the MinIO support already relies on. The second test in each pair covers the list-and-retry fallback, whose listing prefix has to be rooted as well. No production code changes.
ListFiles reports two kinds of entries: files, plain, and subdirectories, with a trailing slash. Callers depend on that distinction. ServerHandler .listFilesRecursive recurses into an entry only when it ends in a slash, and _getNodeLogs hands everything else to the log categorizer. The s3 and aliyunoss backends break the distinction. CreateDirectory writes a trailing-slash placeholder object for the directory itself, and a listing whose prefix is that directory returns that object as a key. path.Base then drops the slash, so the directory reports itself as a file one level down. Listing a node's events directory yields an entry named "events" next to the real event files, and it reaches the user through the log listing and the "internal" log category. The event readers happen to be immune, since isValidEventFile and the event_ prefix check reject the name; ipToNodeId only wastes a lookup on it. The same placeholder can also be picked up by the GetContent fallback, which matches on path.Base: a marker for a directory named x base-matches a request for a file named x, and the fallback then serves the empty marker instead of continuing the scan. The gcs backend already filters these out, with a comment saying so. Skip them in s3 and aliyunoss too, and in azureblob, which writes no placeholder itself but is just as exposed to one written by another tool. Cover the four methods that had no test on these three backends: List, ListFiles, CreateDirectory and WriteFile, which gcs_handler_test.go has covered for gcs all along. As with the GetContent tests, the SDK runs against an httptest server so the assertions land on the requests that reach the wire. aliyunoss had no test that constructed a handler at all.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why are these changes needed?
ListFilesreports two kinds of entries, and its callers rely on telling them apart: a plain name is a file, a name ending in/is a subdirectory.ServerHandler.listFilesRecursiverecurses into an entry only when it ends in a slash, and_getNodeLogspasses everything else to the log categorizer.The s3 and aliyunoss backends break that rule.
CreateDirectorywrites a trailing-slash placeholder object for the directory itself, and an object listing whose prefix is that directory returns the placeholder as one of its keys.path.Basethen strips the slash, so the directory reports itself as a file one level down. FeedinglistFilesRecursiveexactly what the pre-fix s3 backend returns for a node log directory gives:That phantom
eventsentry reaches the user through the**glob log listing and theinternallog category, and fetching it returns nothing. The event readers happen to be immune, becauseisValidEventFileand theevent_prefix check reject the name;ipToNodeIdonly wastes a lookup on it.The same placeholder can also be picked up by the
GetContentfallback, which matches onpath.Base: a marker for a directory namedxbase-matches a request for a file namedx, and the fallback then serves the empty marker instead of continuing the scan.The gcs backend already filters these out, with a comment saying that is what it is doing:
So the fix skips trailing-slash keys in the
Contentsloop of the s3 and aliyunoss_listFiles, and in both listing loops of the azurebloblistBlobs. For azureblob that is defensive rather than a bug fix: itsCreateDirectorydeliberately writes nothing, because a marker blob shows up as<no name>in Azure Storage Explorer, so this codebase never creates one there. A marker left by another tool would still reach the callers, and after this change all four readers answer the same contract.On the test side,
gcs_handler_test.gohas coveredList,ListFiles,CreateDirectoryandWriteFileall along. The other three backends had none of that, and aliyunoss had no test that constructed a handler at all. This PR adds those four methods per backend, driving each SDK against anhttptestserver so the assertions land on the requests that reach the wire, the same approach as theGetContenttests in #5075.Related issue number
None. Found while covering
GetContentfor #5075.Depends on #5075: this branch is stacked on it and reuses the test server helpers it introduces, so the diff here includes that PR's commit until it merges.
Labels
doc-updates-requiredlabel.breaking-changelabel.Checks
Manual test instructions
Against a live MinIO, the same
quay.io/minio/minioimageconfig/minio.yamldeploys, using the in-treeCreateDirectoryandWriteFileto lay down a node'seventsdirectory and then listing it:So the placeholder really does come back in
Contentsrather than being rolled up intoCommonPrefixes, andeventsreally does reach the caller looking like a file.Evidence
Every new test was mutation tested. Twenty-three mutants, all killed:
TestListFilesSeparatesFilesFromDirectories(per backend)ListFilesprefix drops the root dir (x3)ListFilesasks for the listing without the/delimiter (x3)_listFilesignoresCommonPrefixes(s3)Listprefix drops the root dir (x3)TestListReadsClusterMetadataUnderRootDir(per backend)CreateDirectoryomits the trailing slash (s3, aliyunoss)TestCreateDirectoryWritesPlaceholderWhenMissing,TestCreateDirectoryLeavesExistingDirectoryAloneCreateDirectoryignores the existence check (s3)TestCreateDirectoryLeavesExistingDirectoryAloneCreateDirectorystarts writing a markerTestCreateDirectoryWritesNothingWriteFilewrites topath.Baseof the key (x3)TestWriteFileUploadsBodyToGivenKey/...BlobClusterInfoListsorts oldest firstTestListReadsClusterMetadataUnderRootDirgo test ./pkg/...on Windows: green, except the pre-existing failure inpkg/utils(TestGetSessionDir_FatalError_FailsFast), which fails the same way on a clean master checkout and passes on Linux.go test -race ./pkg/... -parallel 4, which is whatmake testruns in CI, on Linux: green, and A/B identical to a clean master checkout.go vet ./...: clean.What was not tested
httpteststub, so on their own they pin down the requests the code sends and how it reads a response of a given shape. The MinIO run above is what backs the premise for s3. For Aliyun OSS and Azure Blob I am relying on API compatibility rather than a run against those services, and the MinIO check was a manual one, not something these tests reproduce.historyservere2e suite needs a Kubernetes cluster and was not run locally.logcollector.Writer.CreateDirectoryandeventcollectorwrite the marker on the same paths thatServerHandlerand the event readers later list.listBlobsis unreachable today, since both callers pass"/". It is there so the two branches agree, but no test covers it.MaxKeys/MaxResultsand the pagination boundary (NextContinuationToken,NextMarker) are not pinned by these tests.