@@ -71,54 +71,55 @@ void DataStoreS3::putDMFileLocalFiles(
7171 auto s3_client = S3::ClientFactory::instance ().sharedTiFlashClient ();
7272
7373 // First, upload non-meta files.
74- std::vector<std::future<void >> upload_results;
75- upload_results.reserve (local_files.size () - 1 );
74+ IOPoolHelper::FutureContainer upload_results (log, local_files.size ());
7675 for (const auto & fname : local_files)
7776 {
7877 if (DMFileMetaV2::isMetaFileName (fname))
7978 continue ;
8079
8180 auto local_fname = fmt::format (" {}/{}" , local_dir, fname);
8281 auto remote_fname = fmt::format (" {}/{}" , remote_dir, fname);
83- auto task = std::make_shared<std::packaged_task<void ()>>(
84- [&, local_fname = std::move (local_fname), remote_fname = std::move (remote_fname)]() -> void {
85- S3::uploadFile (
86- *s3_client,
87- local_fname,
88- remote_fname,
89- EncryptionPath (local_dir, fname, oid.keyspace_id ),
90- file_provider);
82+ auto encryption_path = EncryptionPath (local_dir, fname, oid.keyspace_id );
83+ // Capture shared resources by value in tasks to avoid dangling references on early errors.
84+ auto task = std::make_shared<std::packaged_task<void ()>>( //
85+ [s3_client,
86+ provider = file_provider,
87+ local_fname = std::move (local_fname),
88+ remote_fname = std::move (remote_fname),
89+ encryption_path = std::move (encryption_path)]() -> void {
90+ S3::uploadFile (*s3_client, local_fname, remote_fname, encryption_path, provider);
9191 });
92- upload_results.push_back (task->get_future ());
92+ upload_results.add (task->get_future ());
9393 DataStoreS3Pool::get ().scheduleOrThrowOnError ([task]() { (*task)(); });
9494 }
95- for ( auto & f : upload_results)
96- f. get ();
95+ // Wait for all tasks to finish before returning to keep captured resources alive.
96+ upload_results. getAllResults ();
9797
9898 // Then, upload meta files.
9999 // Only when the meta upload is successful, the dmfile upload can be considered successful.
100- upload_results. clear ( );
100+ IOPoolHelper::FutureContainer meta_upload_results (log, local_files. size () );
101101 for (const auto & fname : local_files)
102102 {
103103 if (!DMFileMetaV2::isMetaFileName (fname))
104104 continue ;
105105
106106 auto local_fname = fmt::format (" {}/{}" , local_dir, fname);
107107 auto remote_fname = fmt::format (" {}/{}" , remote_dir, fname);
108- auto task = std::make_shared<std::packaged_task<void ()>>(
109- [&, local_fname = std::move (local_fname), remote_fname = std::move (remote_fname)]() {
110- S3::uploadFile (
111- *s3_client,
112- local_fname,
113- remote_fname,
114- EncryptionPath (local_dir, fname, oid.keyspace_id ),
115- file_provider);
108+ auto encryption_path = EncryptionPath (local_dir, fname, oid.keyspace_id );
109+ // Capture shared resources by value in tasks to avoid dangling references on early errors.
110+ auto task = std::make_shared<std::packaged_task<void ()>>( //
111+ [s3_client,
112+ provider = file_provider,
113+ local_fname = std::move (local_fname),
114+ remote_fname = std::move (remote_fname),
115+ encryption_path = std::move (encryption_path)]() {
116+ S3::uploadFile (*s3_client, local_fname, remote_fname, encryption_path, provider);
116117 });
117- upload_results. push_back (task->get_future ());
118+ meta_upload_results. add (task->get_future ());
118119 DataStoreS3Pool::get ().scheduleOrThrowOnError ([task]() { (*task)(); });
119120 }
120- for ( auto & f : upload_results)
121- f. get ();
121+ // Wait for all tasks to finish before returning to keep captured resources alive.
122+ meta_upload_results. getAllResults ();
122123
123124 LOG_INFO (log, " Upload DMFile finished, key={}, cost={}ms" , remote_dir, sw.elapsedMilliseconds ());
124125}
@@ -134,30 +135,31 @@ bool DataStoreS3::putCheckpointFiles(
134135 // / then upload the CheckpointManifest to make the files within
135136 // / `upload_seq` public to S3GCManager.
136137
137- std::vector<std::future<void >> upload_results;
138- // upload in parallel
138+ // Upload in parallel.
139139 // Note: Local checkpoint files are always not encrypted.
140+ IOPoolHelper::FutureContainer upload_results (log, local_files.data_files .size ());
140141 for (size_t file_idx = 0 ; file_idx < local_files.data_files .size (); ++file_idx)
141142 {
142- auto task = std::make_shared<std::packaged_task<void ()>>([&, idx = file_idx] {
143- const auto & local_datafile = local_files.data_files [idx];
144- auto s3key = S3::S3Filename::newCheckpointData (store_id, upload_seq, idx);
145- auto lock_key = s3key.toView ().getLockKey (store_id, upload_seq);
146- S3::uploadFile (
147- *s3_client,
148- local_datafile,
149- s3key.toFullKey (),
150- EncryptionPath (local_datafile, " " , NullspaceID),
151- file_provider);
152- S3::uploadEmptyFile (*s3_client, lock_key);
153- });
154- upload_results.push_back (task->get_future ());
143+ auto local_datafile = local_files.data_files [file_idx];
144+ auto s3key = S3::S3Filename::newCheckpointData (store_id, upload_seq, file_idx);
145+ auto remote_key = s3key.toFullKey ();
146+ auto lock_key = s3key.toView ().getLockKey (store_id, upload_seq);
147+ auto encryption_path = EncryptionPath (local_datafile, " " , NullspaceID);
148+ // Capture by value to avoid dangling references.
149+ auto task = std::make_shared<std::packaged_task<void ()>>( //
150+ [s3_client,
151+ provider = file_provider,
152+ local_datafile = std::move (local_datafile),
153+ remote_key = std::move (remote_key),
154+ lock_key = std::move (lock_key),
155+ encryption_path = std::move (encryption_path)] {
156+ S3::uploadFile (*s3_client, local_datafile, remote_key, encryption_path, provider);
157+ S3::uploadEmptyFile (*s3_client, lock_key);
158+ });
159+ upload_results.add (task->get_future ());
155160 DataStoreS3Pool::get ().scheduleOrThrowOnError ([task] { (*task)(); });
156161 }
157- for (auto & f : upload_results)
158- {
159- f.get ();
160- }
162+ upload_results.getAllResults ();
161163
162164 // upload manifest after all CheckpointData uploaded
163165 auto s3key = S3::S3Filename::newCheckpointManifest (store_id, upload_seq);
@@ -180,7 +182,7 @@ std::unordered_map<String, IDataStore::DataFileInfo> DataStoreS3::getDataFilesIn
180182 for (const auto & lock_key : lock_keys)
181183 {
182184 auto task = std::make_shared<std::packaged_task<std::tuple<String, DataFileInfo>()>>(
183- [& s3_client, lock_key = lock_key, log = this ->log ]() noexcept {
185+ [s3_client, lock_key = lock_key, log = this ->log ]() noexcept {
184186 auto key_view = S3::S3FilenameView::fromKey (lock_key);
185187 auto datafile_key = key_view.asDataFile ().toFullKey ();
186188 try
@@ -232,43 +234,37 @@ void DataStoreS3::copyToLocal(
232234{
233235 auto s3_client = S3::ClientFactory::instance ().sharedTiFlashClient ();
234236 const auto remote_dir = S3::S3Filename::fromDMFileOID (remote_oid).toFullKey ();
235- std::vector<std::future<void >> results;
236- results.reserve (target_short_fnames.size ());
237+ IOPoolHelper::FutureContainer results (Logger::get (" DataStoreS3" ), target_short_fnames.size ());
237238 for (const auto & fname : target_short_fnames)
238239 {
239240 auto remote_fname = fmt::format (" {}/{}" , remote_dir, fname);
240241 auto local_fname = fmt::format (" {}/{}" , local_dir, fname);
241- auto task = std::make_shared<std::packaged_task<void ()>>(
242- [& , local_fname = std::move (local_fname), remote_fname = std::move (remote_fname)]() {
242+ auto task = std::make_shared<std::packaged_task<void ()>>( //
243+ [s3_client , local_fname = std::move (local_fname), remote_fname = std::move (remote_fname)]() {
243244 auto tmp_fname = fmt::format (" {}.tmp" , local_fname);
244245 S3::downloadFile (*s3_client, tmp_fname, remote_fname);
245246 Poco::File (tmp_fname).renameTo (local_fname);
246247 });
247- results.push_back (task->get_future ());
248+ results.add (task->get_future ());
248249 DataStoreS3Pool::get ().scheduleOrThrowOnError ([task]() { (*task)(); });
249250 }
250- for (auto & f : results)
251- {
252- f.get ();
253- }
251+ results.getAllResults ();
254252}
255253
256254void DataStoreS3::setTaggingsForKeys (const std::vector<String> & keys, std::string_view tagging)
257255{
258256 auto s3_client = S3::ClientFactory::instance ().sharedTiFlashClient ();
259- std::vector<std::future<void >> results;
260- results.reserve (keys.size ());
257+ IOPoolHelper::FutureContainer results (log, keys.size ());
261258 for (const auto & k : keys)
262259 {
263- auto task = std::make_shared<std::packaged_task<void ()>>(
264- [&s3_client, &tagging, key = k] { rewriteObjectWithTagging (*s3_client, key, String (tagging)); });
265- results.emplace_back (task->get_future ());
260+ auto task = std::make_shared<std::packaged_task<void ()>>( //
261+ [s3_client, tagging_str = String (tagging), key = k] {
262+ rewriteObjectWithTagging (*s3_client, key, tagging_str);
263+ });
264+ results.add (task->get_future ());
266265 DataStoreS3Pool::get ().scheduleOrThrowOnError ([task] { (*task)(); });
267266 }
268- for (auto & f : results)
269- {
270- f.get ();
271- }
267+ results.getAllResults ();
272268}
273269
274270IPreparedDMFileTokenPtr DataStoreS3::prepareDMFile (const S3::DMFileOID & oid, UInt64 page_id)
0 commit comments