1717#include < butil/fast_rand.h>
1818#include < butil/time.h>
1919#include < bvar/bvar.h>
20+ #include < fmt/format.h>
2021
2122#include < optional>
2223#include < set>
@@ -176,6 +177,25 @@ bool should_retry(const Status& st, int64_t attempted_retries) {
176177 return MatchPattern (message, config::lake_vacuum_retry_pattern.value ());
177178}
178179
180+ // Returns Status::TimedOut once |deadline_ms| (milliseconds since the Epoch) has passed.
181+ // deadline_ms <= 0 means no deadline. The deadline is anchored at the time the BE received
182+ // the vacuum request, so it also expires for tasks that waited too long in the thread pool
183+ // queue: by then the FE caller has given up waiting and would re-dispatch the partition,
184+ // continuing would only keep a vacuum worker occupied for a response nobody reads.
185+ Status check_vacuum_deadline (int64_t deadline_ms) {
186+ if (deadline_ms <= 0 ) {
187+ return Status::OK ();
188+ }
189+ int64_t now_ms = butil::gettimeofday_ms ();
190+ TEST_SYNC_POINT_CALLBACK (" vacuum:check_deadline" , &now_ms);
191+ if (now_ms >= deadline_ms) {
192+ return Status::TimedOut (
193+ fmt::format (" vacuum task deadline exceeded, now={} deadline={}, the FE caller has given up waiting" ,
194+ now_ms, deadline_ms));
195+ }
196+ return Status::OK ();
197+ }
198+
179199Status delete_files_with_retry (FileSystem* fs, std::span<const std::string> paths) {
180200 const int64_t base = config::lake_vacuum_retry_min_delay_ms;
181201 const int64_t max_retries = config::lake_vacuum_retry_max_attempts;
@@ -417,7 +437,7 @@ static Status collect_files_to_vacuum(TabletManager* tablet_mgr, std::string_vie
417437 AsyncFileDeleter* datafile_deleter, AsyncFileDeleter* metafile_deleter,
418438 AsyncSharedFileDeleter* shared_file_deleter, int64_t * total_datafile_size,
419439 int64_t * vacuumed_version, int64_t * extra_datafile_size,
420- const TabletRetainInfo& retain_info) {
440+ const TabletRetainInfo& retain_info, int64_t deadline_ms ) {
421441 auto t0 = butil::gettimeofday_ms ();
422442 auto meta_dir = join_path (root_dir, kMetadataDirectoryName );
423443 auto data_dir = join_path (root_dir, kSegmentDirectoryName );
@@ -437,6 +457,7 @@ static Status collect_files_to_vacuum(TabletManager* tablet_mgr, std::string_vie
437457 // Starting at |*final_retain_version|, read the tablet metadata forward along
438458 // the |prev_garbage_version| pointer until the tablet metadata does not exist.
439459 while (version >= min_version) {
460+ RETURN_IF_ERROR (check_vacuum_deadline (deadline_ms));
440461 // fill data cache to avoid read bundle meta file from remote storage repeatedly.
441462 auto res = tablet_mgr->get_tablet_metadata (
442463 tablet_id, version, false /* Not need to fill meta cache */ ,
@@ -557,7 +578,7 @@ static Status vacuum_tablet_metadata(TabletManager* tablet_mgr, std::string_view
557578 int64_t grace_timestamp, bool enable_file_bundling,
558579 bool enable_shared_file_cleanup, int64_t * vacuumed_files,
559580 int64_t * vacuumed_file_size, int64_t * vacuumed_version, int64_t * extra_file_size,
560- const std::unordered_set<int64_t >& retain_versions) {
581+ const std::unordered_set<int64_t >& retain_versions, int64_t deadline_ms ) {
561582 DCHECK (tablet_mgr != nullptr );
562583 DCHECK (std::is_sorted (tablet_infos.begin (), tablet_infos.end (),
563584 [](const auto & a, const auto & b) { return a.tablet_id () < b.tablet_id (); }));
@@ -586,7 +607,7 @@ static Status vacuum_tablet_metadata(TabletManager* tablet_mgr, std::string_view
586607 RETURN_IF_ERROR (collect_files_to_vacuum (tablet_mgr, root_dir, tablet_info, grace_timestamp, min_retain_version,
587608 vacuum_version_range.get (), &datafile_deleter, &metafile_deleter,
588609 &shared_file_deleter, vacuumed_file_size, &tablet_vacuumed_version,
589- extra_file_size, tablet_retain_info));
610+ extra_file_size, tablet_retain_info, deadline_ms ));
590611 RETURN_IF_ERROR (datafile_deleter.finish ());
591612 (*vacuumed_files) += datafile_deleter.delete_count ();
592613 if (!enable_file_bundling) {
@@ -795,7 +816,8 @@ Status vacuum_load_spill(std::string_view root_location, int64_t min_active_txn_
795816 return ret;
796817}
797818
798- Status vacuum_impl (TabletManager* tablet_mgr, const VacuumRequest& request, VacuumResponse* response) {
819+ Status vacuum_impl (TabletManager* tablet_mgr, const VacuumRequest& request, VacuumResponse* response,
820+ int64_t deadline_ms) {
799821 if (UNLIKELY (tablet_mgr == nullptr )) {
800822 return Status::InvalidArgument (" tablet_mgr is null" );
801823 }
@@ -808,6 +830,9 @@ Status vacuum_impl(TabletManager* tablet_mgr, const VacuumRequest& request, Vacu
808830 if (UNLIKELY (request.grace_timestamp () <= 0 )) {
809831 return Status::InvalidArgument (" value of grace_timestamp is zero or nagative" );
810832 }
833+ // The task may have stayed in the thread pool queue long enough that the FE caller
834+ // already timed out and gave up, abort without doing any work in that case.
835+ RETURN_IF_ERROR (check_vacuum_deadline (deadline_ms));
811836
812837 auto tablet_infos = std::vector<TabletInfoPB>();
813838 if (request.tablet_infos_size () > 0 ) {
@@ -852,7 +877,8 @@ Status vacuum_impl(TabletManager* tablet_mgr, const VacuumRequest& request, Vacu
852877 request.has_enable_shared_file_cleanup () ? request.enable_shared_file_cleanup () : enable_file_bundling;
853878 RETURN_IF_ERROR (vacuum_tablet_metadata (tablet_mgr, root_loc, tablet_infos, min_retain_version, grace_timestamp,
854879 enable_file_bundling, enable_shared_file_cleanup, &vacuumed_files,
855- &vacuumed_file_size, &vacuumed_version, &extra_file_size, retain_versions));
880+ &vacuumed_file_size, &vacuumed_version, &extra_file_size, retain_versions,
881+ deadline_ms));
856882 extra_file_size -= vacuumed_file_size;
857883 if (request.delete_txn_log ()) {
858884 RETURN_IF_ERROR (vacuum_txn_log (root_loc, min_active_txn_id, &vacuumed_files, &vacuumed_file_size));
@@ -871,8 +897,8 @@ Status vacuum_impl(TabletManager* tablet_mgr, const VacuumRequest& request, Vacu
871897 return Status::OK ();
872898}
873899
874- void vacuum (TabletManager* tablet_mgr, const VacuumRequest& request, VacuumResponse* response) {
875- auto st = vacuum_impl (tablet_mgr, request, response);
900+ void vacuum (TabletManager* tablet_mgr, const VacuumRequest& request, VacuumResponse* response, int64_t deadline_ms ) {
901+ auto st = vacuum_impl (tablet_mgr, request, response, deadline_ms );
876902 LOG_IF (ERROR , !st.ok ()) << st;
877903 st.to_protobuf (response->mutable_status ());
878904}
0 commit comments