@@ -2619,8 +2619,50 @@ size_t MergeTreeData::clearOldBrokenPartsFromDetachedDirectory()
26192619 if (detached_parts.empty ())
26202620 return 0 ;
26212621
2622- PartsTemporaryRename renamed_parts (*this , " detached/" );
2622+ auto get_last_touched_time = [&](const DetachedPartInfo & part_info) -> time_t
2623+ {
2624+ auto path = fs::path (relative_data_path) / " detached" / part_info.dir_name ;
2625+ time_t last_change_time = part_info.disk ->getLastChanged (path);
2626+ time_t last_modification_time = part_info.disk ->getLastModified (path).epochTime ();
2627+ return std::max (last_change_time, last_modification_time);
2628+ };
26232629
2630+ time_t ttl_seconds = getSettings ()->merge_tree_clear_old_broken_detached_parts_ttl_timeout_seconds ;
2631+
2632+ size_t unfinished_deleting_parts = 0 ;
2633+ time_t current_time = time (nullptr );
2634+ for (const auto & part_info : detached_parts)
2635+ {
2636+ if (!part_info.dir_name .starts_with (" deleting_" ))
2637+ continue ;
2638+
2639+ time_t startup_time = current_time - static_cast <time_t >(Context::getGlobalContextInstance ()->getUptimeSeconds ());
2640+ time_t last_touch_time = get_last_touched_time (part_info);
2641+
2642+ // / Maybe it's being deleted right now (for example, in ALTER DROP DETACHED)
2643+ bool had_restart = last_touch_time < startup_time;
2644+ bool ttl_expired = last_touch_time + ttl_seconds <= current_time;
2645+ if (!had_restart && !ttl_expired)
2646+ continue ;
2647+
2648+ // / We were trying to delete this detached part but did not finish deleting, probably because the server crashed
2649+ LOG_INFO (log, " Removing detached part {} that we failed to remove previously" , part_info.dir_name );
2650+ try
2651+ {
2652+ removeDetachedPart (part_info.disk , fs::path (relative_data_path) / " detached" / part_info.dir_name / " " , part_info.dir_name );
2653+ ++unfinished_deleting_parts;
2654+ }
2655+ catch (...)
2656+ {
2657+ tryLogCurrentException (log);
2658+ }
2659+ }
2660+
2661+ if (!getSettings ()->merge_tree_enable_clear_old_broken_detached )
2662+ return unfinished_deleting_parts;
2663+
2664+ const auto full_path = fs::path (relative_data_path) / " detached" ;
2665+ size_t removed_count = 0 ;
26242666 for (const auto & part_info : detached_parts)
26252667 {
26262668 if (!part_info.valid_name || part_info.prefix .empty ())
@@ -2635,31 +2677,24 @@ size_t MergeTreeData::clearOldBrokenPartsFromDetachedDirectory()
26352677 if (!can_be_removed_by_timeout)
26362678 continue ;
26372679
2638- time_t current_time = time (nullptr );
2639- ssize_t threshold = current_time - getSettings ()->merge_tree_clear_old_broken_detached_parts_ttl_timeout_seconds ;
2640- auto path = fs::path (relative_data_path) / " detached" / part_info.dir_name ;
2641- time_t last_change_time = part_info.disk ->getLastChanged (path);
2642- time_t last_modification_time = part_info.disk ->getLastModified (path).epochTime ();
2643- time_t last_touch_time = std::max (last_change_time, last_modification_time);
2680+ ssize_t threshold = current_time - ttl_seconds;
2681+ time_t last_touch_time = get_last_touched_time (part_info);
26442682
26452683 if (last_touch_time == 0 || last_touch_time >= threshold)
26462684 continue ;
26472685
2648- renamed_parts.addPart (part_info.dir_name , " deleting_" + part_info.dir_name , part_info.disk );
2649- }
2650-
2651- LOG_INFO (log, " Will clean up {} detached parts" , renamed_parts.old_and_new_names .size ());
2686+ const String & old_name = part_info.dir_name ;
2687+ String new_name = " deleting_" + part_info.dir_name ;
2688+ part_info.disk ->moveFile (fs::path (full_path) / old_name, fs::path (full_path) / new_name);
26522689
2653- renamed_parts.tryRenameAll ();
2654-
2655- for (auto & [old_name, new_name, disk] : renamed_parts.old_and_new_names )
2656- {
2657- removeDetachedPart (disk, fs::path (relative_data_path) / " detached" / new_name / " " , old_name);
2690+ removeDetachedPart (part_info.disk , fs::path (relative_data_path) / " detached" / new_name / " " , old_name);
26582691 LOG_WARNING (log, " Removed broken detached part {} due to a timeout for broken detached parts" , old_name);
2659- old_name. clear () ;
2692+ ++removed_count ;
26602693 }
26612694
2662- return renamed_parts.old_and_new_names .size ();
2695+ LOG_INFO (log, " Cleaned up {} detached parts" , removed_count);
2696+
2697+ return removed_count + unfinished_deleting_parts;
26632698}
26642699
26652700size_t MergeTreeData::clearOldWriteAheadLogs ()
@@ -4035,7 +4070,7 @@ void MergeTreeData::restoreAndActivatePart(const DataPartPtr & part, DataPartsLo
40354070void MergeTreeData::outdateUnexpectedPartAndCloneToDetached (const DataPartPtr & part_to_detach)
40364071{
40374072 LOG_INFO (log, " Cloning part {} to unexpected_{} and making it obsolete." , part_to_detach->getDataPartStorage ().getPartDirectory (), part_to_detach->name );
4038- part_to_detach->makeCloneInDetached (" unexpected" , getInMemoryMetadataPtr ());
4073+ part_to_detach->makeCloneInDetached (" unexpected" , getInMemoryMetadataPtr (), /* disk_transaction */ {} );
40394074
40404075 DataPartsLock lock = lockParts ();
40414076 part_to_detach->is_unexpected_local_part = true ;
@@ -5797,18 +5832,21 @@ MergeTreeData::MutableDataPartsVector MergeTreeData::tryLoadPartsToAttach(const
57975832{
57985833 const String source_dir = " detached/" ;
57995834
5800- std::map<String, DiskPtr> name_to_disk;
5801-
58025835 // / Let's compose a list of parts that should be added.
58035836 if (attach_part)
58045837 {
58055838 const String part_id = partition->as <ASTLiteral &>().value .safeGet <String>();
58065839 validateDetachedPartName (part_id);
5807- auto disk = getDiskForDetachedPart (part_id);
5808- renamed_parts.addPart (part_id, " attaching_" + part_id, disk);
5809-
5810- if (MergeTreePartInfo::tryParsePartName (part_id, format_version))
5811- name_to_disk[part_id] = getDiskForDetachedPart (part_id);
5840+ if (temporary_parts.contains (String (DETACHED_DIR_NAME) + " /" + part_id))
5841+ {
5842+ LOG_WARNING (log, " Will not try to attach part {} because its directory is temporary, "
5843+ " probably it's being detached right now" , part_id);
5844+ }
5845+ else
5846+ {
5847+ auto disk = getDiskForDetachedPart (part_id);
5848+ renamed_parts.addPart (part_id, " attaching_" + part_id, disk);
5849+ }
58125850 }
58135851 else
58145852 {
@@ -5825,6 +5863,12 @@ MergeTreeData::MutableDataPartsVector MergeTreeData::tryLoadPartsToAttach(const
58255863
58265864 for (const auto & part_info : detached_parts)
58275865 {
5866+ if (temporary_parts.contains (String (DETACHED_DIR_NAME) + " /" + part_info.dir_name ))
5867+ {
5868+ LOG_WARNING (log, " Will not try to attach part {} because its directory is temporary, "
5869+ " probably it's being detached right now" , part_info.dir_name );
5870+ continue ;
5871+ }
58285872 LOG_DEBUG (log, " Found part {}" , part_info.dir_name );
58295873 active_parts.add (part_info.dir_name );
58305874 }
@@ -5835,6 +5879,8 @@ MergeTreeData::MutableDataPartsVector MergeTreeData::tryLoadPartsToAttach(const
58355879 for (const auto & part_info : detached_parts)
58365880 {
58375881 const String containing_part = active_parts.getContainingPart (part_info.dir_name );
5882+ if (containing_part.empty ())
5883+ continue ;
58385884
58395885 LOG_DEBUG (log, " Found containing part {} for part {}" , containing_part, part_info.dir_name );
58405886
0 commit comments