1515#include " config/configuration.h"
1616#include " lsm/core/exceptions.h"
1717#include " lsm/core/internal/files.h"
18+ #include " lsm/io/chunked_remote_file_reader.h"
1819#include " lsm/io/file_io.h"
1920#include " ssx/future-util.h"
2021#include " utils/retry_chain_node.h"
@@ -33,11 +34,14 @@ namespace {
3334
3435static constexpr auto reservation_timeout = std::chrono::seconds(30 );
3536
37+ static constexpr auto cloud_op_timeout = std::chrono::seconds(10 );
38+
39+ std::chrono::milliseconds cloud_op_backoff () {
40+ return config::shard_local_cfg ().cloud_storage_initial_backoff_ms .value ();
41+ }
42+
3643retry_chain_node make_cloud_rtc (ss::abort_source& as) {
37- constexpr auto timeout = std::chrono::seconds (10 );
38- auto backoff
39- = config::shard_local_cfg ().cloud_storage_initial_backoff_ms .value ();
40- return retry_chain_node{as, timeout, backoff};
44+ return retry_chain_node{as, cloud_op_timeout, cloud_op_backoff ()};
4145}
4246
4347bool check_cloud_result (cloud_io::download_result result) {
@@ -239,53 +243,50 @@ class cloud_cache_data_persistence : public data_persistence {
239243 cloud_io::remote* remote,
240244 cloud_storage_clients::bucket_name bucket,
241245 cloud_storage_clients::object_key prefix,
246+ config::binding<size_t > chunk_size,
242247 cloud_io::group_id gid)
243248 : _cache(cache)
244249 , _remote(remote)
245250 , _bucket(std::move(bucket))
246251 , _prefix(std::move(prefix))
252+ , _chunk_size(std::move(chunk_size))
247253 , _gid(gid) {}
248254
249255 ss::future<optional_pointer<random_access_file_reader>>
250256 open_random_access_reader (
251- internal::file_handle h, uint64_t /* file_size*/ ) override {
257+ internal::file_handle h, uint64_t file_size) override {
252258 _as.check ();
253259 auto _ = _gate.hold ();
254260 auto filename = internal::sst_file_name (h);
255- auto key = cache_key (filename);
256261
257- auto root = make_cloud_rtc (_as);
258- while (true ) {
259- auto reader = co_await open_cached_reader (key);
260- if (reader) {
261- co_return reader;
262- }
262+ auto cached = co_await open_cached_reader (cache_key (filename));
263+ if (cached) {
264+ // The whole object is already local -- e.g. just written through
265+ // by the cache-staged writer. Serve it directly rather than
266+ // re-fetching it from the cloud in chunks.
267+ co_return cached;
268+ }
263269
264- auto dl_fut = co_await ss::coroutine::as_future (
265- _remote->download_stream (
266- {
267- .bucket = _bucket,
268- .key = cloud_key (filename),
269- .parent_rtc = root,
270- },
271- [this ,
272- &key](uint64_t content_length, ss::input_stream<char > stream) {
273- return save_to_cache (
274- content_length, std::move (stream), key);
275- },
276- " SST file download" ,
277- /* acquire_hydration_units=*/ true ,
278- /* byte_range=*/ std::nullopt ,
279- /* throttle_metric_ms_cb=*/ {},
280- _gid));
281- if (dl_fut.failed ()) {
282- throw_as_lsm_ex (
283- dl_fut.get_exception (), " error downloading file" );
284- }
285- if (!check_cloud_result (dl_fut.get ())) {
286- co_return std::nullopt ;
287- }
270+ // Hydrate chunks on demand. Chunk entries live in a sibling
271+ // subdirectory so they can never collide with the whole-file key
272+ // probed above.
273+ auto reader = co_await chunked_remote_file_reader::open (
274+ _cache,
275+ _remote,
276+ _bucket,
277+ cloud_key (filename),
278+ chunk_cache_prefix (filename),
279+ file_size,
280+ _chunk_size (),
281+ config::shard_local_cfg ().cloud_storage_hydration_timeout_ms (),
282+ cloud_op_backoff (),
283+ _as,
284+ _gid);
285+ if (!reader) {
286+ co_return std::nullopt ;
288287 }
288+ std::unique_ptr<random_access_file_reader> ptr = std::move (*reader);
289+ co_return ptr;
289290 }
290291
291292 ss::future<std::unique_ptr<sequential_file_writer>>
@@ -403,9 +404,9 @@ class cloud_cache_data_persistence : public data_persistence {
403404 co_return std::nullopt ;
404405 }
405406 auto local_path = _cache->get_local_path (key);
406- std::unique_ptr<random_access_file_reader> ptr;
407- ptr = std::make_unique<disk_file_reader>(
408- std::move (local_path), std::move (item->body ));
407+ std::unique_ptr<random_access_file_reader> ptr
408+ = std::make_unique<disk_file_reader>(
409+ std::move (local_path), std::move (item->body ));
409410 co_return ptr;
410411 } catch (const std::system_error& e) {
411412 if (e.code () == std::errc::no_such_file_or_directory) {
@@ -423,29 +424,16 @@ class cloud_cache_data_persistence : public data_persistence {
423424 }
424425 }
425426
426- ss::future<uint64_t > save_to_cache (
427- uint64_t content_length,
428- ss::input_stream<char > input_stream,
429- const std::filesystem::path& key) {
430- std::exception_ptr ex;
431- try {
432- auto reservation = co_await _cache->reserve_space (
433- content_length, 1 );
434- co_await _cache->put (key, input_stream, reservation);
435- } catch (...) {
436- ex = std::current_exception ();
437- }
438- co_await input_stream.close ();
439- if (ex) {
440- std::rethrow_exception (ex);
441- }
442- co_return content_length;
443- }
444-
445427 std::filesystem::path cache_key (std::string_view name) {
446428 return std::filesystem::path (" lsm" ) / _prefix () / name;
447429 }
448430
431+ // Where the chunked reader caches its chunks for `name`: a sibling of the
432+ // whole-file key so the two never share a path.
433+ std::filesystem::path chunk_cache_prefix (std::string_view name) {
434+ return cache_key (fmt::format (" {}.chunks" , name));
435+ }
436+
449437 cloud_storage_clients::object_key cloud_key (std::string_view name) {
450438 return join_cloud_key (_prefix, name);
451439 }
@@ -454,6 +442,7 @@ class cloud_cache_data_persistence : public data_persistence {
454442 cloud_io::remote* _remote;
455443 cloud_storage_clients::bucket_name _bucket;
456444 cloud_storage_clients::object_key _prefix;
445+ config::binding<size_t > _chunk_size;
457446 cloud_io::group_id _gid;
458447 ss::abort_source _as;
459448 ss::gate _gate;
@@ -600,9 +589,15 @@ ss::future<std::unique_ptr<data_persistence>> open_cloud_cache_data_persistence(
600589 cloud_io::remote* remote,
601590 cloud_storage_clients::bucket_name bucket,
602591 cloud_storage_clients::object_key prefix,
592+ config::binding<size_t > sst_chunk_size,
603593 cloud_io::group_id gid) {
604594 co_return std::make_unique<cloud_cache_data_persistence>(
605- cache, remote, std::move (bucket), std::move (prefix), gid);
595+ cache,
596+ remote,
597+ std::move (bucket),
598+ std::move (prefix),
599+ std::move (sst_chunk_size),
600+ gid);
606601}
607602
608603ss::future<std::unique_ptr<metadata_persistence>>
0 commit comments