Skip to content

[History Server] Cover GetContent object paths for the s3 and azureblob backends - #5075

Open
stantheman0128 wants to merge 1 commit into
ray-project:masterfrom
stantheman0128:test/historyserver-getcontent-coverage
Open

[History Server] Cover GetContent object paths for the s3 and azureblob backends#5075
stantheman0128 wants to merge 1 commit into
ray-project:masterfrom
stantheman0128:test/historyserver-getcontent-coverage

Conversation

@stantheman0128

Copy link
Copy Markdown

Why are these changes needed?

GetContent builds its object path from three pieces, rootDir, the cluster path prefix, and the file name. Neither the s3 nor the azureblob backend had a single test touching that. pkg/storage/s3/s3_test.go and pkg/storage/azureblob/azureblob_test.go contained only TestTrim and TestWalk, which never construct a handler, never call a storage method, and contain no assertion that can fail.

This is not a hypothetical gap. The aliyunoss backend shipped without the root dir in that path, which is #4820: on a deployment started with --ray-root-dir, listing the log tree worked and every fetch from it returned NoSuchKey. The same mistake in s3 or azureblob would produce the same silent breakage, and nothing in the suite would notice.

This PR adds two tests per backend. There is no production change.

How the tests work

Each test drives the real SDK against an httptest server, so the assertion is on the object path that actually reaches the wire rather than on a helper's return value. For s3 that means path style addressing, which the backend already supports for MinIO. For azureblob the container client is pointed at the test server with no credential.

The first test in each pair pins the full object path. The second covers the list-and-retry fallback, whose listing prefix has to be rooted as well, otherwise the retry has nothing to find.

The two backends need slightly different fallback setups because their matching rules differ. s3 matches a listed candidate by base name, so the test puts the object one directory deeper. azureblob matches on the full path, so its test misses the first download instead. That one uses BlobNotFound rather than a server error on purpose: the SDK retries server errors, which would satisfy the download before the fallback ever ran.

Related issue number

None. The gap was found while investigating #4820. It is not part of the #5065 follow-up epic, whose six items are all something else.

Labels

  • If this PR has user-facing changes that require documentation updates at release time, I have added the doc-updates-required label.
  • If this PR contains breaking changes, I have added the breaking-change label.

Checks

  • I've made sure the tests are passing.
  • Testing Strategy
    • Unit tests

Evidence

New tests pass on both backends:

$ go test ./pkg/storage/s3/ ./pkg/storage/azureblob/ -run TestGetContent -v -count=1
=== RUN   TestGetContentUsesRootDir
--- PASS: TestGetContentUsesRootDir (0.00s)
=== RUN   TestGetContentFallbackListsUnderRootDir
--- PASS: TestGetContentFallbackListsUnderRootDir (0.00s)
ok  	.../pkg/storage/s3
ok  	.../pkg/storage/azureblob

A passing test against already correct code proves nothing on its own, so both backends were mutation tested. The production file was swapped in via go test -overlay, leaving the tree untouched.

Mutation A, drop the root dir and cluster prefix from the object path. Both tests go red on both backends:

--- FAIL: TestGetContentUsesRootDir
    s3_test.go:147: GetContent returned nil; keys requested:
      [session_2026-05-08.../event_CORE_WORKER_256.log],
      want "ray-logs/ray_cluster_history/raycluster/default/my-cluster/session_.../event_CORE_WORKER_256.log"
--- FAIL: TestGetContentFallbackListsUnderRootDir

Mutation B, leave the object path correct and break only the fallback listing prefix. Only the fallback test goes red, on both backends:

--- FAIL: TestGetContentFallbackListsUnderRootDir
    s3_test.go:195: GetContent returned nil; list prefixes tried:
      [ray_cluster_history/raycluster/default/my-cluster/session_.../events/]

Mutation B is the useful one: it shows the two tests in each pair are not redundant, since the second catches a defect the first cannot see.

Rest of the module. go vet ./... is clean and every non e2e package passes. The one failure, TestGetSessionDir_FatalError_FailsFast in pkg/utils, fails identically on a clean origin/master checkout on this machine and is unrelated to this change: it depends on symlink behavior this Windows host does not provide.

What was not tested

  • Listing semantics. The fake responses ignore the delimiter parameter, so flat versus hierarchical listing is not covered. Changing the s3 fallback from _listFiles(dirPath, "", false) to _listFiles(dirPath, "/", false) still passes. These tests are scoped to the object path, not to listing behavior.
  • go test -race, which make test runs in CI, needs a C toolchain this machine does not have. Shared state in the tests is behind a mutex, since the servers handle requests on their own goroutines.
  • No run against real S3, MinIO, or Azure Blob. These tests deliberately assert on the emitted object path, not on storage behavior.

This change was developed with AI assistance from Claude. I reviewed and verified it before submitting.

…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.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant