morningman opened a new pull request, #68847:
URL: https://github.com/apache/doris/pull/68847
### What problem does this PR solve?
Issue Number: None
Related PR: #68797, #66932
Problem Summary:
**In short.** The BE file cache can return the bytes of a different file for
two kinds of external reads. An `http()` URL whose content changes keeps being
served from blocks cached from the old content. Two object storage endpoints,
or two HDFS clusters, that hold the same path with the same modification time
share cache blocks. Both happen on master whenever a session turns on
`enable_file_cache`, and on branch-4.x, where the cache is on by default.
#68797 turns the cache on by default on master and is blocked on these two
cases. This PR keeps HTTP responses out of the file cache and adds the storage
to the cache key of external files.
**Background.** `CachedRemoteFileReader` keys an external file as
`path:mtime`: the path its reader reports plus the modification time from the
file listing. The key is meant to change whenever the file does. `FileFactory`
wraps S3, HDFS and HTTP readers in the cache when the query enables it. #66932
already exempted chunked HTTP responses (the `cdc_stream` table function),
whose content is a stream.
**The problem, and what it cost.**
- **HTTP has no version.** `HttpTableValuedFunction` lists a URL with
modification time 0, so every response of that URL gets the key `url:0`. When
the content behind the URL changes, a later query reads blocks cached from the
earlier content. If the length did not change, nothing fails and the query
silently returns the old rows; if it did, old and new bytes can be mixed.
- **The key does not name the storage.** An S3 reader reports
`s3://bucket/key`, without the endpoint. An HDFS reader reports the path with
the name node stripped (`convert_path`). Two S3-compatible endpoints (for
example two MinIO deployments) holding the same bucket and key, or two HDFS
clusters holding the same path, get the same key as soon as the modification
times match. S3 reports them in seconds, so files written by the same job in
the same second match. A query on one storage can then read blocks of the other
storage's file.
**How this PR fixes it.**
- `FileFactory` no longer wraps HTTP readers in the file cache. This covers
every HTTP source read through `FileFactory`, including `hf://` URLs, and
replaces the chunked-response check from #66932, which this rule includes.
- `FileReaderOptions` gets `fs_identity`, the storage an external file is
read from. `FileFactory` sets it to the S3 endpoint (`AWS_ENDPOINT`) for S3
readers and to the name node (`fs_name`) for HDFS readers.
`CachedRemoteFileReader` keys external files as `fs_identity:path:mtime`;
readers that do not set it, such as broker reads, keep `path:mtime`.
**Results.**
| Case | Before | After |
|---|---|---|
| `http()` URL whose content was replaced, file cache on | second query can
return rows of the old content | always reads the current content; HTTP data is
not cached |
| Same bucket, key and mtime on two S3-compatible endpoints | one cache
entry, shared | one entry per endpoint |
| Same path and mtime on two HDFS clusters | one cache entry, shared | one
entry per name node |
| Existing cached blocks of external S3 / HDFS files after upgrade | - | not
found under the new key; the first reads go to the storage again and the old
blocks age out |
**Classes, and how they call each other.**
```
FileScanner / format readers
└─ FileFactory::create_file_reader(system_properties, file_description,
reader_options)
├─ FILE_S3 : options.fs_identity = s3_conf.client_conf.endpoint ─┐
├─ FILE_HDFS : options.fs_identity = fs_name ──────────────────────┤
│
create_cached_file_reader(reader, options)
│ └─
CachedRemoteFileReader::_init_external_table_cache
│ key =
fs_identity:path:mtime (path:mtime if empty)
└─ FILE_HTTP : HttpFileReader, not wrapped in the file cache
```
### Release note
The file cache no longer keeps data read over HTTP (the `http()` table
function, including `hf://` URLs), because an HTTP source has no modification
time to tell versions apart. Cached data of external files on S3-compatible
storage and HDFS is now kept per endpoint or name node; after upgrading, data
cached by an earlier version is not reused for these files and is evicted over
time.
### Check List (For Author)
- Test <!-- At least one of them must be included. -->
- [x] Regression test
- [x] Unit Test
- [ ] Manual test (add detailed scripts or steps below)
- [ ] No need to test or manual test. Explain why:
- [ ] This is a refactor/code format and no logic has been changed.
- [ ] Previous test can cover this change.
- [ ] No code files have been changed.
- [ ] Other reason <!-- Add your reason? -->
Regression test: `test_http_tvf` adds `replaced_before` /
`replaced_after`. With `enable_file_cache = true`, the suite's local HTTP
server replaces the file behind one URL with content of the same length between
two queries, and the second query must return the new row. Run locally with the
BE file cache on, it passes. With HTTP readers wrapped in the file cache again,
the second query returns the old row (`1, aaaa` instead of `2, bbbb`). Only the
two new blocks of `test_http_tvf.out` were taken from the generated file: a
local regeneration also rewrites `sql08` (local time zone) and drops two blocks
the suite no longer produces.
BE unit tests:
- `HttpFileReaderTest.PlainResponseIsNotFileCachedEither`: an HTTP
reader is not wrapped in the file cache even when the query asks for the cache.
-
`AsyncCachedRemoteFileReaderTest.external_cache_key_tells_storages_apart`: the
same path and modification time on two storages get different keys; a reader
without an identity keeps `path:mtime`.
-
`AsyncCachedRemoteFileReaderTest.file_factory_keys_s3_files_by_endpoint`:
readers that `FileFactory` builds for two S3 endpoints get different keys.
`run-be-ut.sh --run
--filter='HttpFileReaderTest.*:AsyncCachedRemoteFileReaderTest.*'` passed. Each
new case fails when its part of the change is reverted: HTTP readers wrapped in
the cache again (SEGV in `FileCacheFactory::get_by_path`, since that test has
no cache), the S3 reader built without its endpoint, or the key built without
the identity. The HDFS wiring has no unit test, because creating an HDFS reader
in BE UT would start a JVM.
- Behavior changed:
- [ ] No.
- [x] Yes. HTTP reads no longer use the file cache. External S3 / HDFS
files are cached under a new key, so blocks cached before the upgrade are not
reused.
- Does this need documentation?
- [x] No.
- [ ] Yes. <!-- Add document PR link here. eg:
https://github.com/apache/doris-website/pull/1214 -->
### Check List (For Reviewer who merge this PR)
- [ ] Confirm the release note
- [ ] Confirm test cases
- [ ] Confirm document
- [ ] Add branch pick label <!-- Add branch pick label that this PR should
merge into -->
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]