This is an automated email from the ASF dual-hosted git repository.

JingsongLi pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/paimon.git


The following commit(s) were added to refs/heads/master by this push:
     new 3304abe262 [core] Only remove tag metadata when deleting tags (#10823)
3304abe262 is described below

commit 3304abe2624b069f5242bae7b2ba9fe377bd930d
Author: Jingsong Lee <[email protected]>
AuthorDate: Tue Oct 27 12:58:02 2026 +0800

    [core] Only remove tag metadata when deleting tags (#10823)
---
 docs/docs/concepts/spec/manifest.md                |   3 +-
 docs/docs/maintenance/manage-snapshots.mdx         |   7 +-
 docs/docs/maintenance/manage-tags.mdx              |   4 +
 .../java/org/apache/paimon/AbstractFileStore.java  |  21 +---
 .../src/main/java/org/apache/paimon/FileStore.java |   3 -
 .../org/apache/paimon/operation/TagDeletion.java   | 124 --------------------
 .../paimon/table/AbstractFileStoreTable.java       |   7 +-
 .../main/java/org/apache/paimon/table/Table.java   |   2 +-
 .../org/apache/paimon/tag/BatchReadTagCreator.java |   6 +-
 .../org/apache/paimon/tag/TagAutoCreation.java     |   8 +-
 .../java/org/apache/paimon/tag/TagAutoManager.java |   7 +-
 .../org/apache/paimon/tag/TagBatchCreation.java    |  20 +---
 .../java/org/apache/paimon/tag/TagTimeExpire.java  |  17 +--
 .../java/org/apache/paimon/utils/TagManager.java   | 127 ++------------------
 .../DataEvolutionRowIdReassignerTest.java          |   3 +
 .../paimon/operation/ExpireSnapshotsTest.java      |  14 +--
 .../apache/paimon/operation/FileDeletionTest.java  | 129 +++++++++++++++------
 .../operation/LocalOrphanFilesCleanTest.java       |   6 +-
 .../paimon/table/IndexFileExpireTableTest.java     |   9 +-
 .../org/apache/paimon/tag/TagAutoManagerTest.java  |   1 -
 .../procedure/RollbackToAsLatestProcedure.java     |   6 +-
 .../sink/AutoTagForSavepointCommitterOperator.java |  15 +--
 ...utoTagForSavepointCommitterOperatorFactory.java |   6 -
 .../org/apache/paimon/flink/sink/FlinkSink.java    |   1 -
 .../paimon/flink/sink/RowAppendTableSink.java      |   4 -
 .../paimon/flink/sink/SavepointTagUtils.java       |   9 +-
 .../flink/sink/coordinator/SavepointTagger.java    |   7 +-
 .../AutoTagForSavepointCommitterOperatorTest.java  |   2 -
 .../paimon/flink/sink/SavepointTagUtilsTest.java   |   2 -
 .../CommittingWriteOperatorCoordinatorTest.java    |   1 -
 .../sink/coordinator/SavepointTaggerTest.java      |   1 -
 .../procedure/RollbackToAsLatestProcedure.java     |   6 +-
 32 files changed, 154 insertions(+), 424 deletions(-)

diff --git a/docs/docs/concepts/spec/manifest.md 
b/docs/docs/concepts/spec/manifest.md
index 6f72db9067..a23106c874 100644
--- a/docs/docs/concepts/spec/manifest.md
+++ b/docs/docs/concepts/spec/manifest.md
@@ -61,7 +61,8 @@ skip manifests before opening them.
 | `_EXTRA_FILES` | ARRAY of STRING, nullable | Names of additional files in 
the manifest directory; defaults to null. |
 
 Each extra file belongs exclusively to one manifest. It is retained and 
cleaned up together with
-that manifest during snapshot, tag, or changelog deletion.
+that manifest during snapshot or changelog expiration and orphan file cleanup. 
Tag deletion retains
+both the manifest and its extra files for later orphan file cleanup.
 
 ### Manifest Sidecar
 
diff --git a/docs/docs/maintenance/manage-snapshots.mdx 
b/docs/docs/maintenance/manage-snapshots.mdx
index 205894d7b7..656a8a76ee 100644
--- a/docs/docs/maintenance/manage-snapshots.mdx
+++ b/docs/docs/maintenance/manage-snapshots.mdx
@@ -398,9 +398,10 @@ changelog producer.
 
 ## Remove Orphan Files
 
-Paimon files are deleted physically only when expiring snapshots. However, it 
is possible that some unexpected errors occurred
-when deleting files, so that there may exist files that are not used by Paimon 
snapshots (so-called "orphan files"). You can
-submit a `remove_orphan_files` job to clean them:
+Snapshot expiration physically deletes unused files. Deleting a tag only 
removes its tag file,
+and unexpected errors during file deletion can also leave unused files behind. 
Files that are no longer
+referenced by any snapshot, tag, or changelog are called "orphan files". You 
can submit a `remove_orphan_files` job
+to clean them:
 
 <Tabs groupId="remove-orphan-files">
 
diff --git a/docs/docs/maintenance/manage-tags.mdx 
b/docs/docs/maintenance/manage-tags.mdx
index 490f264a41..54b1ab780c 100644
--- a/docs/docs/maintenance/manage-tags.mdx
+++ b/docs/docs/maintenance/manage-tags.mdx
@@ -177,6 +177,10 @@ CALL sys.create_tag(table => 'test.t', tag => 'test_tag');
 
 You can delete a tag by its name.
 
+Deleting a tag only removes the tag file. Its manifest, index, and data files 
are retained.
+Use [Remove Orphan Files](./manage-snapshots#remove-orphan-files) to reclaim 
files that are no longer
+referenced by any snapshot, tag, or changelog, subject to the orphan file 
cleanup retention period.
+
 <Tabs groupId="delete-tag">
 
 <TabItem value="flink-sql" label="Flink SQL">
diff --git a/paimon-core/src/main/java/org/apache/paimon/AbstractFileStore.java 
b/paimon-core/src/main/java/org/apache/paimon/AbstractFileStore.java
index 9e554ce8e2..8663945778 100644
--- a/paimon-core/src/main/java/org/apache/paimon/AbstractFileStore.java
+++ b/paimon-core/src/main/java/org/apache/paimon/AbstractFileStore.java
@@ -46,7 +46,6 @@ import org.apache.paimon.operation.ManifestsReader;
 import org.apache.paimon.operation.NormalPartitionExpire;
 import org.apache.paimon.operation.PartitionExpire;
 import org.apache.paimon.operation.SnapshotDeletion;
-import org.apache.paimon.operation.TagDeletion;
 import org.apache.paimon.operation.commit.CommitRollback;
 import org.apache.paimon.operation.commit.ConflictDetection;
 import org.apache.paimon.partition.PartitionExpireStrategy;
@@ -367,20 +366,6 @@ abstract class AbstractFileStore<T> implements 
FileStore<T> {
         return new TagManager(fileIO, options.path(), options.branch(), 
options);
     }
 
-    @Override
-    public TagDeletion newTagDeletion() {
-        return new TagDeletion(
-                fileIO,
-                pathFactory(),
-                manifestFileFactory().create(),
-                manifestListFactory().create(),
-                newIndexFileHandler(),
-                newStatsFileHandler(),
-                options.cleanEmptyDirectories(),
-                options.fileOperationThreadNum(),
-                options.scanManifestParallelism());
-    }
-
     public abstract Comparator<InternalRow> newKeyComparator();
 
     @Override
@@ -573,11 +558,7 @@ abstract class AbstractFileStore<T> implements 
FileStore<T> {
     @Override
     public TagAutoManager newTagAutoManager(FileStoreTable table) {
         return TagAutoManager.create(
-                options,
-                snapshotManager(),
-                newTagManager(),
-                newTagDeletion(),
-                createTagCallbacks(table));
+                options, snapshotManager(), newTagManager(), 
createTagCallbacks(table));
     }
 
     @Override
diff --git a/paimon-core/src/main/java/org/apache/paimon/FileStore.java 
b/paimon-core/src/main/java/org/apache/paimon/FileStore.java
index d9b143f75f..a948d1505e 100644
--- a/paimon-core/src/main/java/org/apache/paimon/FileStore.java
+++ b/paimon-core/src/main/java/org/apache/paimon/FileStore.java
@@ -30,7 +30,6 @@ import org.apache.paimon.operation.FileStoreWrite;
 import org.apache.paimon.operation.PartitionExpire;
 import org.apache.paimon.operation.SnapshotDeletion;
 import org.apache.paimon.operation.SplitRead;
-import org.apache.paimon.operation.TagDeletion;
 import org.apache.paimon.partition.PartitionExpireStrategy;
 import org.apache.paimon.service.ServiceManager;
 import org.apache.paimon.stats.StatsFileHandler;
@@ -100,8 +99,6 @@ public interface FileStore<T> {
 
     TagManager newTagManager();
 
-    TagDeletion newTagDeletion();
-
     @Nullable
     PartitionExpire newPartitionExpire(String commitUser, FileStoreTable 
table);
 
diff --git 
a/paimon-core/src/main/java/org/apache/paimon/operation/TagDeletion.java 
b/paimon-core/src/main/java/org/apache/paimon/operation/TagDeletion.java
deleted file mode 100644
index e6f9d9d673..0000000000
--- a/paimon-core/src/main/java/org/apache/paimon/operation/TagDeletion.java
+++ /dev/null
@@ -1,124 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.paimon.operation;
-
-import org.apache.paimon.Snapshot;
-import org.apache.paimon.data.BinaryRow;
-import org.apache.paimon.fs.FileIO;
-import org.apache.paimon.fs.Path;
-import org.apache.paimon.index.IndexFileHandler;
-import org.apache.paimon.io.DataFilePathFactory;
-import org.apache.paimon.manifest.ExpireFileEntry;
-import org.apache.paimon.manifest.ManifestFile;
-import org.apache.paimon.manifest.ManifestFileMeta;
-import org.apache.paimon.manifest.ManifestList;
-import org.apache.paimon.stats.StatsFileHandler;
-import org.apache.paimon.utils.DataFilePathFactories;
-import org.apache.paimon.utils.FileStorePathFactory;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import javax.annotation.Nullable;
-
-import java.io.IOException;
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.function.Predicate;
-
-/** Delete tag files. */
-public class TagDeletion extends FileDeletionBase<Snapshot> {
-
-    private static final Logger LOG = 
LoggerFactory.getLogger(TagDeletion.class);
-
-    public TagDeletion(
-            FileIO fileIO,
-            FileStorePathFactory pathFactory,
-            ManifestFile manifestFile,
-            ManifestList manifestList,
-            IndexFileHandler indexFileHandler,
-            StatsFileHandler statsFileHandler,
-            boolean cleanEmptyDirectories,
-            int fileOperationThreadNum,
-            @Nullable Integer manifestReadParallelism) {
-        super(
-                fileIO,
-                pathFactory,
-                manifestFile,
-                manifestList,
-                indexFileHandler,
-                statsFileHandler,
-                cleanEmptyDirectories,
-                fileOperationThreadNum,
-                manifestReadParallelism);
-    }
-
-    @Override
-    public void cleanDeletedDataFiles(Snapshot taggedSnapshot, 
Predicate<ExpireFileEntry> skipper) {
-        Collection<ExpireFileEntry> manifestEntries;
-        try {
-            List<ManifestFileMeta> manifests =
-                    tryReadManifestList(taggedSnapshot.baseManifestList());
-            
manifests.addAll(tryReadManifestList(taggedSnapshot.deltaManifestList()));
-            manifestEntries = readMergedDataFiles(manifests);
-        } catch (IOException e) {
-            LOG.info("Skip data file clean for the tag of id {}.", 
taggedSnapshot.id(), e);
-            return;
-        }
-
-        Set<Path> dataFileToDelete = new HashSet<>();
-        DataFilePathFactories factories = new 
DataFilePathFactories(pathFactory);
-        for (ExpireFileEntry entry : manifestEntries) {
-            DataFilePathFactory dataFilePathFactory =
-                    factories.get(entry.partition(), entry.bucket());
-            if (!skipper.test(entry)) {
-                dataFileToDelete.add(dataFilePathFactory.toPath(entry));
-                for (String file : entry.extraFiles()) {
-                    
dataFileToDelete.add(dataFilePathFactory.toAlignedPath(file, entry));
-                }
-
-                recordDeletionBuckets(entry);
-            }
-        }
-        executeAll(dataFileToDelete, fileIO::deleteQuietly);
-    }
-
-    public void cleanUnusedDataFiles(Snapshot taggedSnapshot, 
Predicate<ExpireFileEntry> skipper) {
-        cleanDeletedDataFiles(taggedSnapshot, skipper);
-    }
-
-    @Override
-    public void cleanUnusedManifests(Snapshot taggedSnapshot, Set<String> 
skippingSet) {
-        // doesn't clean changelog files because they are handled by 
SnapshotDeletion
-        executeAll(planManifestsCleaner(taggedSnapshot, skippingSet, true, 
false));
-    }
-
-    public Predicate<ExpireFileEntry> dataFileSkipper(List<Snapshot> 
fromSnapshots)
-            throws Exception {
-        Map<BinaryRow, Map<Integer, Set<String>>> skipped = new HashMap<>();
-        for (Snapshot snapshot : fromSnapshots) {
-            addMergedDataFiles(skipped, snapshot);
-        }
-        return entry -> containsDataFile(skipped, entry);
-    }
-}
diff --git 
a/paimon-core/src/main/java/org/apache/paimon/table/AbstractFileStoreTable.java 
b/paimon-core/src/main/java/org/apache/paimon/table/AbstractFileStoreTable.java
index cb4349a59e..459713687e 100644
--- 
a/paimon-core/src/main/java/org/apache/paimon/table/AbstractFileStoreTable.java
+++ 
b/paimon-core/src/main/java/org/apache/paimon/table/AbstractFileStoreTable.java
@@ -769,12 +769,7 @@ abstract class AbstractFileStoreTable implements 
FileStoreTable {
 
     @Override
     public void deleteTag(String tagName) {
-        tagManager()
-                .deleteTag(
-                        tagName,
-                        store().newTagDeletion(),
-                        snapshotManager(),
-                        store().createTagCallbacks(this));
+        tagManager().deleteTag(tagName, store().createTagCallbacks(this));
     }
 
     @Override
diff --git a/paimon-core/src/main/java/org/apache/paimon/table/Table.java 
b/paimon-core/src/main/java/org/apache/paimon/table/Table.java
index b4fe2bc237..846054b9f4 100644
--- a/paimon-core/src/main/java/org/apache/paimon/table/Table.java
+++ b/paimon-core/src/main/java/org/apache/paimon/table/Table.java
@@ -143,7 +143,7 @@ public interface Table extends Serializable {
     @Experimental
     void replaceTag(String tagName, @Nullable Long fromSnapshotId, @Nullable 
Duration timeRetained);
 
-    /** Delete a tag by name. */
+    /** Delete a tag by name, leaving its referenced files to orphan file 
cleanup. */
     @Experimental
     void deleteTag(String tagName);
 
diff --git 
a/paimon-core/src/main/java/org/apache/paimon/tag/BatchReadTagCreator.java 
b/paimon-core/src/main/java/org/apache/paimon/tag/BatchReadTagCreator.java
index 8637709b31..04b7e1b036 100644
--- a/paimon-core/src/main/java/org/apache/paimon/tag/BatchReadTagCreator.java
+++ b/paimon-core/src/main/java/org/apache/paimon/tag/BatchReadTagCreator.java
@@ -78,10 +78,8 @@ public class BatchReadTagCreator {
     public void deleteReadTag(String tagName) {
         try {
             if (tagManager.tagExists(tagName)) {
-                // Directly delete the tag metadata file instead of using 
TagManager.deleteTag(),
-                // which would also scan and delete unreferenced data files — 
too heavyweight for a
-                // read-path cleanup. Any orphan data files left behind will 
be reclaimed by
-                // OrphanFilesClean.
+                // Read protection tags do not need deletion callbacks. Any 
orphan files left
+                // behind will be reclaimed by OrphanFilesClean.
                 
snapshotManager.fileIO().deleteQuietly(tagManager.tagPath(tagName));
                 LOG.info("Deleted batch read protection tag '{}'.", tagName);
             }
diff --git 
a/paimon-core/src/main/java/org/apache/paimon/tag/TagAutoCreation.java 
b/paimon-core/src/main/java/org/apache/paimon/tag/TagAutoCreation.java
index 47a186cbd4..b6dd51f360 100644
--- a/paimon-core/src/main/java/org/apache/paimon/tag/TagAutoCreation.java
+++ b/paimon-core/src/main/java/org/apache/paimon/tag/TagAutoCreation.java
@@ -21,7 +21,6 @@ package org.apache.paimon.tag;
 import org.apache.paimon.CoreOptions;
 import org.apache.paimon.Snapshot;
 import org.apache.paimon.annotation.VisibleForTesting;
-import org.apache.paimon.operation.TagDeletion;
 import org.apache.paimon.table.sink.TagCallback;
 import org.apache.paimon.tag.TagTimeExtractor.ProcessTimeExtractor;
 import org.apache.paimon.tag.TagTimeExtractor.WatermarkExtractor;
@@ -53,7 +52,6 @@ public class TagAutoCreation {
 
     private final SnapshotManager snapshotManager;
     private final TagManager tagManager;
-    private final TagDeletion tagDeletion;
     private final TagTimeExtractor timeExtractor;
     private final TagPeriodHandler periodHandler;
     private final Duration delay;
@@ -70,7 +68,6 @@ public class TagAutoCreation {
     private TagAutoCreation(
             SnapshotManager snapshotManager,
             TagManager tagManager,
-            TagDeletion tagDeletion,
             TagTimeExtractor timeExtractor,
             TagPeriodHandler periodHandler,
             Duration delay,
@@ -82,7 +79,6 @@ public class TagAutoCreation {
             ZoneId sinkProcessTimeZone) {
         this.snapshotManager = snapshotManager;
         this.tagManager = tagManager;
-        this.tagDeletion = tagDeletion;
         this.timeExtractor = timeExtractor;
         this.periodHandler = periodHandler;
         this.delay = delay;
@@ -196,7 +192,7 @@ public class TagAutoCreation {
                                 "Delete tag {}, because the number of 
auto-created tags reached numRetainedMax of {}.",
                                 toDeleteTag,
                                 numRetainedMax);
-                        tagManager.deleteTag(toDeleteTag, tagDeletion, 
snapshotManager, callbacks);
+                        tagManager.deleteTag(toDeleteTag, callbacks);
                         i++;
                         if (i == toDelete) {
                             break;
@@ -225,7 +221,6 @@ public class TagAutoCreation {
             CoreOptions options,
             SnapshotManager snapshotManager,
             TagManager tagManager,
-            TagDeletion tagDeletion,
             List<TagCallback> callbacks) {
         TagTimeExtractor extractor = 
TagTimeExtractor.createForAutoTag(options);
         if (extractor == null) {
@@ -234,7 +229,6 @@ public class TagAutoCreation {
         return new TagAutoCreation(
                 snapshotManager,
                 tagManager,
-                tagDeletion,
                 extractor,
                 TagPeriodHandler.create(options),
                 options.tagCreationDelay(),
diff --git 
a/paimon-core/src/main/java/org/apache/paimon/tag/TagAutoManager.java 
b/paimon-core/src/main/java/org/apache/paimon/tag/TagAutoManager.java
index 31ca385d1e..d649c7b99c 100644
--- a/paimon-core/src/main/java/org/apache/paimon/tag/TagAutoManager.java
+++ b/paimon-core/src/main/java/org/apache/paimon/tag/TagAutoManager.java
@@ -19,7 +19,6 @@
 package org.apache.paimon.tag;
 
 import org.apache.paimon.CoreOptions;
-import org.apache.paimon.operation.TagDeletion;
 import org.apache.paimon.table.sink.TagCallback;
 import org.apache.paimon.utils.SnapshotManager;
 import org.apache.paimon.utils.TagManager;
@@ -53,7 +52,6 @@ public class TagAutoManager {
             CoreOptions options,
             SnapshotManager snapshotManager,
             TagManager tagManager,
-            TagDeletion tagDeletion,
             List<TagCallback> callbacks) {
 
         TagTimeExtractor extractor = 
TagTimeExtractor.createForAutoTag(options);
@@ -61,10 +59,9 @@ public class TagAutoManager {
         return new TagAutoManager(
                 extractor == null
                         ? null
-                        : TagAutoCreation.create(
-                                options, snapshotManager, tagManager, 
tagDeletion, callbacks),
+                        : TagAutoCreation.create(options, snapshotManager, 
tagManager, callbacks),
                 options.tagTimeExpireEnabled()
-                        ? TagTimeExpire.create(snapshotManager, tagManager, 
tagDeletion, callbacks)
+                        ? TagTimeExpire.create(snapshotManager, tagManager, 
callbacks)
                         : null);
     }
 
diff --git 
a/paimon-core/src/main/java/org/apache/paimon/tag/TagBatchCreation.java 
b/paimon-core/src/main/java/org/apache/paimon/tag/TagBatchCreation.java
index 2b637db1cb..d049129311 100644
--- a/paimon-core/src/main/java/org/apache/paimon/tag/TagBatchCreation.java
+++ b/paimon-core/src/main/java/org/apache/paimon/tag/TagBatchCreation.java
@@ -20,7 +20,6 @@ package org.apache.paimon.tag;
 
 import org.apache.paimon.CoreOptions;
 import org.apache.paimon.Snapshot;
-import org.apache.paimon.operation.TagDeletion;
 import org.apache.paimon.table.FileStoreTable;
 import org.apache.paimon.utils.SnapshotManager;
 import org.apache.paimon.utils.TagManager;
@@ -44,14 +43,12 @@ public class TagBatchCreation {
     private final CoreOptions options;
     private final TagManager tagManager;
     private final SnapshotManager snapshotManager;
-    private final TagDeletion tagDeletion;
     private final ZoneId zoneId;
 
     public TagBatchCreation(FileStoreTable table) {
         this.table = table;
         this.snapshotManager = table.snapshotManager();
         this.tagManager = table.tagManager();
-        this.tagDeletion = table.store().newTagDeletion();
         this.options = table.coreOptions();
         this.zoneId = options.sinkProcessTimeZone();
     }
@@ -70,8 +67,7 @@ public class TagBatchCreation {
                                 + 
localDateTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));
         try {
             // If the tag already exists, delete the tag
-            tagManager.deleteTag(
-                    tagName, tagDeletion, snapshotManager, 
table.store().createTagCallbacks(table));
+            tagManager.deleteTag(tagName, 
table.store().createTagCallbacks(table));
             // Create a new tag
             tagManager.createTag(
                     snapshot,
@@ -86,11 +82,7 @@ public class TagBatchCreation {
                     snapshot.id(),
                     e);
             if (tagManager.tagExists(tagName)) {
-                tagManager.deleteTag(
-                        tagName,
-                        tagDeletion,
-                        snapshotManager,
-                        table.store().createTagCallbacks(table));
+                tagManager.deleteTag(tagName, 
table.store().createTagCallbacks(table));
             }
         }
         // Expire the tag
@@ -108,17 +100,13 @@ public class TagBatchCreation {
             while (tagCount > tagNumRetainedMax) {
                 for (List<String> tagNames : tagManager.tags().values()) {
                     if (tagCount - tagNames.size() > tagNumRetainedMax) {
-                        tagManager.deleteAllTagsOfOneSnapshot(
-                                tagNames, tagDeletion, snapshotManager);
+                        tagManager.deleteAllTagsOfOneSnapshot(tagNames);
                         tagCount = tagCount - tagNames.size();
                     } else {
                         List<String> sortedTagNames = 
tagManager.sortTagsOfOneSnapshot(tagNames);
                         for (String toBeDeleted : sortedTagNames) {
                             tagManager.deleteTag(
-                                    toBeDeleted,
-                                    tagDeletion,
-                                    snapshotManager,
-                                    table.store().createTagCallbacks(table));
+                                    toBeDeleted, 
table.store().createTagCallbacks(table));
                             tagCount--;
                             if (tagCount == tagNumRetainedMax) {
                                 break;
diff --git a/paimon-core/src/main/java/org/apache/paimon/tag/TagTimeExpire.java 
b/paimon-core/src/main/java/org/apache/paimon/tag/TagTimeExpire.java
index 0b57dfa884..8c6d8958a6 100644
--- a/paimon-core/src/main/java/org/apache/paimon/tag/TagTimeExpire.java
+++ b/paimon-core/src/main/java/org/apache/paimon/tag/TagTimeExpire.java
@@ -19,7 +19,6 @@
 package org.apache.paimon.tag;
 
 import org.apache.paimon.fs.FileStatus;
-import org.apache.paimon.operation.TagDeletion;
 import org.apache.paimon.table.sink.TagCallback;
 import org.apache.paimon.utils.DateTimeUtils;
 import org.apache.paimon.utils.Pair;
@@ -42,19 +41,14 @@ public class TagTimeExpire {
 
     private final SnapshotManager snapshotManager;
     private final TagManager tagManager;
-    private final TagDeletion tagDeletion;
     private final List<TagCallback> callbacks;
 
     private LocalDateTime olderThanTime;
 
     private TagTimeExpire(
-            SnapshotManager snapshotManager,
-            TagManager tagManager,
-            TagDeletion tagDeletion,
-            List<TagCallback> callbacks) {
+            SnapshotManager snapshotManager, TagManager tagManager, 
List<TagCallback> callbacks) {
         this.snapshotManager = snapshotManager;
         this.tagManager = tagManager;
-        this.tagDeletion = tagDeletion;
         this.callbacks = callbacks;
     }
 
@@ -95,7 +89,7 @@ public class TagTimeExpire {
                         timeRetained,
                         createTime,
                         olderThanTime);
-                tagManager.deleteTag(tagName, tagDeletion, snapshotManager, 
callbacks);
+                tagManager.deleteTag(tagName, callbacks);
                 expired.add(tagName);
             }
         }
@@ -108,10 +102,7 @@ public class TagTimeExpire {
     }
 
     public static TagTimeExpire create(
-            SnapshotManager snapshotManager,
-            TagManager tagManager,
-            TagDeletion tagDeletion,
-            List<TagCallback> callbacks) {
-        return new TagTimeExpire(snapshotManager, tagManager, tagDeletion, 
callbacks);
+            SnapshotManager snapshotManager, TagManager tagManager, 
List<TagCallback> callbacks) {
+        return new TagTimeExpire(snapshotManager, tagManager, callbacks);
     }
 }
diff --git a/paimon-core/src/main/java/org/apache/paimon/utils/TagManager.java 
b/paimon-core/src/main/java/org/apache/paimon/utils/TagManager.java
index 8bbcf55791..040046c09a 100644
--- a/paimon-core/src/main/java/org/apache/paimon/utils/TagManager.java
+++ b/paimon-core/src/main/java/org/apache/paimon/utils/TagManager.java
@@ -25,8 +25,6 @@ import org.apache.paimon.fs.FileIO;
 import org.apache.paimon.fs.FileStatus;
 import org.apache.paimon.fs.Path;
 import org.apache.paimon.iceberg.IcebergCommitCallback;
-import org.apache.paimon.manifest.ExpireFileEntry;
-import org.apache.paimon.operation.TagDeletion;
 import org.apache.paimon.table.sink.TagCallback;
 import org.apache.paimon.tag.Tag;
 import org.apache.paimon.tag.TagPeriodHandler;
@@ -46,7 +44,6 @@ import java.util.Collection;
 import java.util.Comparator;
 import java.util.List;
 import java.util.Optional;
-import java.util.Set;
 import java.util.SortedMap;
 import java.util.TreeMap;
 import java.util.function.Predicate;
@@ -210,31 +207,19 @@ public class TagManager {
         }
     }
 
-    /** Make sure the tagNames are ALL tags of one snapshot. */
-    public void deleteAllTagsOfOneSnapshot(
-            List<String> tagNames, TagDeletion tagDeletion, SnapshotManager 
snapshotManager) {
-        Snapshot taggedSnapshot = getOrThrow(tagNames.get(0)).trimToSnapshot();
-        List<Snapshot> taggedSnapshots;
-
-        // skip file deletion if snapshot exists
-        if (snapshotManager.snapshotExists(taggedSnapshot.id())) {
-            tagNames.forEach(tagName -> 
fileIO.deleteQuietly(tagPath(tagName)));
-            return;
-        } else {
-            // FileIO discovers tags by tag file, so we should read all tags 
before we delete tag
-            taggedSnapshots = taggedSnapshots();
-            tagNames.forEach(tagName -> 
fileIO.deleteQuietly(tagPath(tagName)));
-        }
-
-        doClean(taggedSnapshot, taggedSnapshots, snapshotManager, tagDeletion);
+    /**
+     * Delete only tag files, leaving referenced files to orphan file cleanup. 
Make sure the
+     * tagNames are ALL tags of one snapshot.
+     */
+    public void deleteAllTagsOfOneSnapshot(List<String> tagNames) {
+        tagNames.forEach(tagName -> fileIO.deleteQuietly(tagPath(tagName)));
     }
 
-    /** Ignore errors if the tag doesn't exist. */
-    public void deleteTag(
-            String tagName,
-            TagDeletion tagDeletion,
-            SnapshotManager snapshotManager,
-            List<TagCallback> callbacks) {
+    /**
+     * Delete only the tag file, leaving referenced files to orphan file 
cleanup. Ignore errors if
+     * the tag doesn't exist.
+     */
+    public void deleteTag(String tagName, List<TagCallback> callbacks) {
         checkArgument(!StringUtils.isNullOrWhitespaceOnly(tagName), "Tag name 
shouldn't be blank.");
         Optional<Tag> tag = get(tagName);
         if (!tag.isPresent()) {
@@ -242,25 +227,7 @@ public class TagManager {
             return;
         }
 
-        Snapshot taggedSnapshot = tag.get().trimToSnapshot();
-        List<Snapshot> taggedSnapshots;
-
-        // skip file deletion if snapshot exists
-        if 
(snapshotManager.copyWithBranch(branch).snapshotExists(taggedSnapshot.id())) {
-            deleteTagMetaFile(tagName, callbacks);
-            return;
-        } else {
-            // FileIO discovers tags by tag file, so we should read all tags 
before we delete tag
-            SortedMap<Snapshot, List<String>> tags = tags();
-            deleteTagMetaFile(tagName, callbacks);
-            // skip data file clean if more than 1 tags are created based on 
this snapshot
-            if (tags.get(taggedSnapshot).size() > 1) {
-                return;
-            }
-            taggedSnapshots = new ArrayList<>(tags.keySet());
-        }
-
-        doClean(taggedSnapshot, taggedSnapshots, snapshotManager, tagDeletion);
+        deleteTagMetaFile(tagName, callbacks);
     }
 
     private void deleteTagMetaFile(String tagName, List<TagCallback> 
callbacks) {
@@ -274,64 +241,6 @@ public class TagManager {
         }
     }
 
-    private void doClean(
-            Snapshot taggedSnapshot,
-            List<Snapshot> taggedSnapshots,
-            SnapshotManager snapshotManager,
-            TagDeletion tagDeletion) {
-        // collect skipping sets from the left neighbor tag and the nearest 
right neighbor (either
-        // the earliest snapshot or right neighbor tag)
-        List<Snapshot> skippedSnapshots = new ArrayList<>();
-
-        int index = findIndex(taggedSnapshot, taggedSnapshots);
-        // the left neighbor tag
-        if (index - 1 >= 0) {
-            skippedSnapshots.add(taggedSnapshots.get(index - 1));
-        }
-        // the nearest right neighbor
-        Snapshot right = 
snapshotManager.copyWithBranch(branch).earliestSnapshot();
-        if (index + 1 < taggedSnapshots.size()) {
-            Snapshot rightTag = taggedSnapshots.get(index + 1);
-            right = right.id() < rightTag.id() ? right : rightTag;
-        }
-        skippedSnapshots.add(right);
-
-        // delete data files and empty directories
-        Predicate<ExpireFileEntry> dataFileSkipper = null;
-        boolean success = true;
-        try {
-            dataFileSkipper = tagDeletion.dataFileSkipper(skippedSnapshots);
-        } catch (Exception e) {
-            LOG.info(
-                    String.format(
-                            "Skip cleaning data files for tag of snapshot %s 
due to failed to build skipping set.",
-                            taggedSnapshot.id()),
-                    e);
-            success = false;
-        }
-        if (success) {
-            tagDeletion.cleanUnusedDataFiles(taggedSnapshot, dataFileSkipper);
-            tagDeletion.cleanEmptyDirectories();
-        }
-
-        // delete manifests
-        success = true;
-        Set<String> manifestSkippingSet = null;
-        try {
-            manifestSkippingSet = 
tagDeletion.manifestSkippingSet(skippedSnapshots);
-        } catch (Exception e) {
-            LOG.info(
-                    String.format(
-                            "Skip cleaning manifest files for tag of snapshot 
%s due to failed to build skipping set.",
-                            taggedSnapshot.id()),
-                    e);
-            success = false;
-        }
-        if (success) {
-            tagDeletion.cleanUnusedManifests(taggedSnapshot, 
manifestSkippingSet);
-        }
-    }
-
     /** Check if a tag exists. */
     public boolean tagExists(String tagName) {
         Path path = tagPath(tagName);
@@ -467,18 +376,6 @@ public class TagManager {
         return 
tags().values().stream().flatMap(Collection::stream).collect(Collectors.toList());
     }
 
-    private int findIndex(Snapshot taggedSnapshot, List<Snapshot> 
taggedSnapshots) {
-        for (int i = 0; i < taggedSnapshots.size(); i++) {
-            if (taggedSnapshot.id() == taggedSnapshots.get(i).id()) {
-                return i;
-            }
-        }
-        throw new RuntimeException(
-                String.format(
-                        "Didn't find tag with snapshot id '%s'. This is 
unexpected.",
-                        taggedSnapshot.id()));
-    }
-
     private void validateNoAutoTag(String tagName, Snapshot snapshot) {
         if (tagPeriodHandler == null || !tagPeriodHandler.isAutoTag(tagName)) {
             return;
diff --git 
a/paimon-core/src/test/java/org/apache/paimon/append/dataevolution/DataEvolutionRowIdReassignerTest.java
 
b/paimon-core/src/test/java/org/apache/paimon/append/dataevolution/DataEvolutionRowIdReassignerTest.java
index b0ad384968..1b71781ed8 100644
--- 
a/paimon-core/src/test/java/org/apache/paimon/append/dataevolution/DataEvolutionRowIdReassignerTest.java
+++ 
b/paimon-core/src/test/java/org/apache/paimon/append/dataevolution/DataEvolutionRowIdReassignerTest.java
@@ -334,6 +334,9 @@ public class DataEvolutionRowIdReassignerTest extends 
TableTestBase {
         assertThat(table.fileIO().exists(plan)).isTrue();
 
         table.deleteTag("reassign");
+        assertThat(table.tagManager().tagExists("reassign")).isFalse();
+        assertThat(table.fileIO().exists(plan)).isTrue();
+        new LocalOrphanFilesClean(table, System.currentTimeMillis() + 
2000).clean();
         assertThat(table.fileIO().exists(plan)).isFalse();
     }
 
diff --git 
a/paimon-core/src/test/java/org/apache/paimon/operation/ExpireSnapshotsTest.java
 
b/paimon-core/src/test/java/org/apache/paimon/operation/ExpireSnapshotsTest.java
index c0a113a90e..446391fdfe 100644
--- 
a/paimon-core/src/test/java/org/apache/paimon/operation/ExpireSnapshotsTest.java
+++ 
b/paimon-core/src/test/java/org/apache/paimon/operation/ExpireSnapshotsTest.java
@@ -257,11 +257,7 @@ public class ExpireSnapshotsTest {
         // randomly delete tags
         for (int id = 1; id <= latestSnapshotId; id++) {
             if (random.nextBoolean()) {
-                tagManager.deleteTag(
-                        "tag" + id,
-                        store.newTagDeletion(),
-                        snapshotManager,
-                        Collections.emptyList());
+                tagManager.deleteTag("tag" + id, Collections.emptyList());
             }
         }
 
@@ -480,7 +476,7 @@ public class ExpireSnapshotsTest {
     }
 
     @ParameterizedTest
-    @ValueSource(strings = {"snapshot", "tag", "changelog"})
+    @ValueSource(strings = {"snapshot", "changelog"})
     public void testCleanManifestExtraFilesFollowsManifestRetention(String 
cleaner)
             throws Exception {
         ManifestFileMeta expired =
@@ -501,10 +497,8 @@ public class ExpireSnapshotsTest {
                     expiredSnapshot.deltaManifestList(),
                     deletion.manifestSkippingSet(skippingSnapshots));
         } else {
-            FileDeletionBase<Snapshot> deletion =
-                    "tag".equals(cleaner) ? store.newTagDeletion() : 
snapshotDeletion;
-            deletion.cleanUnusedManifests(
-                    expiredSnapshot, 
deletion.manifestSkippingSet(skippingSnapshots));
+            snapshotDeletion.cleanUnusedManifests(
+                    expiredSnapshot, 
snapshotDeletion.manifestSkippingSet(skippingSnapshots));
         }
 
         
assertThat(fileIO.exists(store.pathFactory().toManifestFilePath("expired"))).isFalse();
diff --git 
a/paimon-core/src/test/java/org/apache/paimon/operation/FileDeletionTest.java 
b/paimon-core/src/test/java/org/apache/paimon/operation/FileDeletionTest.java
index 4443813d77..a67d16dfd6 100644
--- 
a/paimon-core/src/test/java/org/apache/paimon/operation/FileDeletionTest.java
+++ 
b/paimon-core/src/test/java/org/apache/paimon/operation/FileDeletionTest.java
@@ -44,6 +44,8 @@ import org.apache.paimon.schema.TableSchema;
 import org.apache.paimon.stats.StatsFileHandler;
 import org.apache.paimon.table.ExpireSnapshots;
 import org.apache.paimon.table.ExpireSnapshotsImpl;
+import org.apache.paimon.table.FileStoreTable;
+import org.apache.paimon.table.FileStoreTableFactory;
 import org.apache.paimon.types.RowType;
 import org.apache.paimon.utils.ChangelogManager;
 import org.apache.paimon.utils.FileStorePathFactory;
@@ -73,6 +75,7 @@ import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.ThreadLocalRandom;
 import java.util.stream.Collectors;
+import java.util.stream.Stream;
 
 import static org.apache.paimon.CoreOptions.SNAPSHOT_CLEAN_EMPTY_DIRECTORIES;
 import static 
org.apache.paimon.operation.FileStoreTestUtils.assertNFilesExists;
@@ -389,9 +392,14 @@ public class FileDeletionTest {
         assertPathExists(fileIO, 
pathFactory.toManifestListPath(tag1.deltaManifestList()));
     }
 
-    @Test
-    public void testDeleteTagWithSnapshot() throws Exception {
-        TestFileStore store = 
createStore(TestKeyValueGenerator.GeneratorMode.NON_PARTITIONED, 3);
+    @ParameterizedTest
+    @ValueSource(booleans = {true, false})
+    public void testDeleteTagWithSnapshot(boolean deleteAllTags) throws 
Exception {
+        TestFileStore store =
+                createStore(
+                        TestKeyValueGenerator.GeneratorMode.NON_PARTITIONED,
+                        3,
+                        CoreOptions.ChangelogProducer.NONE);
         tagManager = new TagManager(fileIO, store.options().path());
         SnapshotManager snapshotManager = store.snapshotManager();
         TestKeyValueGenerator gen =
@@ -417,14 +425,14 @@ public class FileDeletionTest {
         ManifestList manifestList = store.manifestListFactory().create();
         Snapshot snapshot1 = snapshotManager.snapshot(1);
         List<ManifestFileMeta> snapshot1Data = 
manifestList.readDataManifests(snapshot1);
-        Snapshot snapshot3 = snapshotManager.snapshot(3);
-        List<ManifestFileMeta> snapshot3Data = 
manifestList.readDataManifests(snapshot3);
-
         List<String> manifestLists =
                 Arrays.asList(snapshot1.baseManifestList(), 
snapshot1.deltaManifestList());
 
         // create tag1
         createTag(snapshot1, "tag1", store.options().tagDefaultTimeRetained());
+        if (deleteAllTags) {
+            createTag(snapshot1, "tag1-copy", 
store.options().tagDefaultTimeRetained());
+        }
 
         // expire snapshot 1, 2
         store.newExpire(1, 1, Long.MAX_VALUE).expire();
@@ -441,26 +449,72 @@ public class FileDeletionTest {
             assertPathExists(fileIO, 
pathFactory.toManifestListPath(manifestListName));
         }
 
-        tagManager.deleteTag(
-                "tag1", store.newTagDeletion(), snapshotManager, 
Collections.emptyList());
+        Set<java.nio.file.Path> retainedFiles = tableFiles();
+        retainedFiles.remove(Paths.get(tagManager.tagPath("tag1").toString()));
+        if (deleteAllTags) {
+            
retainedFiles.remove(Paths.get(tagManager.tagPath("tag1-copy").toString()));
+            tagManager.deleteAllTagsOfOneSnapshot(Arrays.asList("tag1", 
"tag1-copy"));
+            assertThat(tagManager.tagExists("tag1-copy")).isFalse();
+        } else {
+            tagManager.deleteTag("tag1", Collections.emptyList());
+        }
+        assertThat(tagManager.tagExists("tag1")).isFalse();
+        
assertThat(tableFiles()).containsExactlyInAnyOrderElementsOf(retainedFiles);
 
         // check data files
-        assertPathNotExists(fileIO, pathFactory.bucketPath(partition, 0));
+        assertPathExists(fileIO, pathFactory.bucketPath(partition, 0));
         assertPathExists(fileIO, pathFactory.bucketPath(partition, 1));
         assertPathExists(fileIO, pathFactory.bucketPath(partition, 2));
 
         // check manifests
         for (ManifestFileMeta manifestFileMeta : snapshot1Data) {
             Path path = 
pathFactory.toManifestFilePath(manifestFileMeta.fileName());
-            if (snapshot3Data.contains(manifestFileMeta)) {
-                assertPathExists(fileIO, path);
-            } else {
-                assertPathNotExists(fileIO, path);
-            }
+            assertPathExists(fileIO, path);
+        }
+        for (String manifestListName : manifestLists) {
+            assertPathExists(fileIO, 
pathFactory.toManifestListPath(manifestListName));
+        }
+
+        FileStoreTable table = FileStoreTableFactory.create(fileIO, 
store.options().path());
+        assertThat(new 
LocalOrphanFilesClean(table).clean().getDeletedFilesPath()).isEmpty();
+        
assertThat(tableFiles()).containsExactlyInAnyOrderElementsOf(retainedFiles);
+        List<Path> orphanDataFiles =
+                
Arrays.stream(fileIO.listStatus(pathFactory.bucketPath(partition, 0)))
+                        .map(status -> status.getPath())
+                        .filter(path -> path.getName().startsWith("data-"))
+                        .collect(Collectors.toList());
+        assertThat(orphanDataFiles).isNotEmpty();
+
+        // Remove obsolete entries from the retained manifests so the old data 
is unreferenced.
+        try (FileStoreCommitImpl commit = store.newCommit()) {
+            commit.compactManifest();
+        }
+        store.newExpire(1, 1, Long.MAX_VALUE).expire();
+        for (Path path : orphanDataFiles) {
+            assertPathExists(fileIO, path);
         }
+
+        List<Path> deleted =
+                new LocalOrphanFilesClean(table, System.currentTimeMillis() + 
2000)
+                        .clean()
+                        .getDeletedFilesPath();
+        assertThat(deleted).containsAll(orphanDataFiles);
+        assertThat(deleted.stream().map(path -> path.toUri().getPath()))
+                .contains(
+                        pathFactory
+                                
.toManifestListPath(snapshot1.deltaManifestList())
+                                .toUri()
+                                .getPath());
+        assertPathNotExists(fileIO, pathFactory.bucketPath(partition, 0));
+        assertPathExists(fileIO, pathFactory.bucketPath(partition, 1));
+        assertPathExists(fileIO, pathFactory.bucketPath(partition, 2));
         for (String manifestListName : manifestLists) {
             assertPathNotExists(fileIO, 
pathFactory.toManifestListPath(manifestListName));
         }
+        for (ManifestFileMeta meta :
+                
manifestList.readDataManifests(snapshotManager.latestSnapshot())) {
+            assertPathExists(fileIO, 
pathFactory.toManifestFilePath(meta.fileName()));
+        }
     }
 
     @Test
@@ -518,29 +572,21 @@ public class FileDeletionTest {
             assertPathExists(fileIO, 
pathFactory.toManifestListPath(manifestListName));
         }
 
-        tagManager.deleteTag(
-                "tag2", store.newTagDeletion(), snapshotManager, 
Collections.emptyList());
+        tagManager.deleteTag("tag2", Collections.emptyList());
+        assertThat(tagManager.tagExists("tag2")).isFalse();
 
         // check data files
         assertPathExists(fileIO, pathFactory.bucketPath(partition, 0));
-        assertPathNotExists(fileIO, pathFactory.bucketPath(partition, 1));
+        assertPathExists(fileIO, pathFactory.bucketPath(partition, 1));
 
         // check manifests
-        Snapshot tag1 = tagManager.getOrThrow("tag1");
-        Snapshot tag3 = tagManager.getOrThrow("tag3");
-        List<ManifestFileMeta> existing = manifestList.readDataManifests(tag1);
-        existing.addAll(manifestList.readDataManifests(tag3));
         for (ManifestFileMeta manifestFileMeta : snapshot2Data) {
             Path path = 
pathFactory.toManifestFilePath(manifestFileMeta.fileName());
-            if (existing.contains(manifestFileMeta)) {
-                assertPathExists(fileIO, path);
-            } else {
-                assertPathNotExists(fileIO, path);
-            }
+            assertPathExists(fileIO, path);
         }
 
         for (String manifestListName : manifestLists) {
-            assertPathNotExists(fileIO, 
pathFactory.toManifestListPath(manifestListName));
+            assertPathExists(fileIO, 
pathFactory.toManifestListPath(manifestListName));
         }
     }
 
@@ -696,8 +742,7 @@ public class FileDeletionTest {
                                 
.snapshotTimeRetain(Duration.ofMillis(Long.MAX_VALUE))
                                 .build())
                 .expire();
-        tagManager.deleteTag(
-                "tag1", store.newTagDeletion(), snapshotManager, 
Collections.emptyList());
+        tagManager.deleteTag("tag1", Collections.emptyList());
         expireSnapshots
                 .config(
                         ExpireConfig.builder()
@@ -851,17 +896,27 @@ public class FileDeletionTest {
         return createStore(mode, 2);
     }
 
+    private Set<java.nio.file.Path> tableFiles() throws IOException {
+        try (Stream<java.nio.file.Path> files = Files.walk(tempDir)) {
+            return 
files.filter(Files::isRegularFile).collect(Collectors.toSet());
+        }
+    }
+
     private TestFileStore createStore(TestKeyValueGenerator.GeneratorMode 
mode, int buckets)
             throws Exception {
-        ThreadLocalRandom random = ThreadLocalRandom.current();
-
-        CoreOptions.ChangelogProducer changelogProducer;
-        if (random.nextBoolean()) {
-            changelogProducer = CoreOptions.ChangelogProducer.INPUT;
-        } else {
-            changelogProducer = CoreOptions.ChangelogProducer.NONE;
-        }
+        return createStore(
+                mode,
+                buckets,
+                ThreadLocalRandom.current().nextBoolean()
+                        ? CoreOptions.ChangelogProducer.INPUT
+                        : CoreOptions.ChangelogProducer.NONE);
+    }
 
+    private TestFileStore createStore(
+            TestKeyValueGenerator.GeneratorMode mode,
+            int buckets,
+            CoreOptions.ChangelogProducer changelogProducer)
+            throws Exception {
         RowType rowType, partitionType;
         switch (mode) {
             case NON_PARTITIONED:
diff --git 
a/paimon-core/src/test/java/org/apache/paimon/operation/LocalOrphanFilesCleanTest.java
 
b/paimon-core/src/test/java/org/apache/paimon/operation/LocalOrphanFilesCleanTest.java
index 49e22ee551..6659205bc3 100644
--- 
a/paimon-core/src/test/java/org/apache/paimon/operation/LocalOrphanFilesCleanTest.java
+++ 
b/paimon-core/src/test/java/org/apache/paimon/operation/LocalOrphanFilesCleanTest.java
@@ -410,11 +410,15 @@ public class LocalOrphanFilesCleanTest {
             Map<Long, List<TestPojo>> snapshotData,
             Map<Long, List<InternalRow>> changelogData)
             throws Exception {
+        // Deleted tags can leave additional orphan files beyond the manually 
added ones.
         assertThat(deleteFiles.stream().map(p -> p.toUri().getPath()))
-                .containsExactlyInAnyOrderElementsOf(
+                .containsAll(
                         manuallyAddedFiles.stream()
                                 .map(p -> p.toUri().getPath())
                                 .collect(Collectors.toList()));
+        for (Path path : deleteFiles) {
+            assertThat(fileIO.exists(path)).as("Deleted orphan file %s", 
path).isFalse();
+        }
 
         Set<Snapshot> snapshots = new HashSet<>();
         table.snapshotManager().snapshots().forEachRemaining(snapshots::add);
diff --git 
a/paimon-core/src/test/java/org/apache/paimon/table/IndexFileExpireTableTest.java
 
b/paimon-core/src/test/java/org/apache/paimon/table/IndexFileExpireTableTest.java
index 7293e8f8be..b6f66316fe 100644
--- 
a/paimon-core/src/test/java/org/apache/paimon/table/IndexFileExpireTableTest.java
+++ 
b/paimon-core/src/test/java/org/apache/paimon/table/IndexFileExpireTableTest.java
@@ -130,14 +130,19 @@ public class IndexFileExpireTableTest extends 
PrimaryKeyTableTestBase {
 
         // test delete tag after expiring snapshots
         table.createTag("tag3", 3);
+        Snapshot taggedSnapshot = table.tagManager().getOrThrow("tag3");
         expire.expireUntil(1, 7);
+        indexFileSize = indexFileSize();
+        indexManifestSize = indexManifestSize();
         table.deleteTag("tag3");
+        assertThat(table.tagManager().tagExists("tag3")).isFalse();
 
         TagManager tagManager = new TagManager(LocalFileIO.create(), 
table.location());
+        checkIndexFiles(taggedSnapshot);
         checkIndexFiles(7);
         checkIndexFiles(tagManager.getOrThrow("tag5"));
-        assertThat(indexFileSize()).isEqualTo(4);
-        assertThat(indexManifestSize()).isEqualTo(2);
+        assertThat(indexFileSize()).isEqualTo(indexFileSize);
+        assertThat(indexManifestSize()).isEqualTo(indexManifestSize);
     }
 
     @Test
diff --git 
a/paimon-core/src/test/java/org/apache/paimon/tag/TagAutoManagerTest.java 
b/paimon-core/src/test/java/org/apache/paimon/tag/TagAutoManagerTest.java
index d7951fd584..fb01f2d557 100644
--- a/paimon-core/src/test/java/org/apache/paimon/tag/TagAutoManagerTest.java
+++ b/paimon-core/src/test/java/org/apache/paimon/tag/TagAutoManagerTest.java
@@ -546,7 +546,6 @@ public class TagAutoManagerTest extends 
PrimaryKeyTableTestBase {
                         table.coreOptions(),
                         table.snapshotManager(),
                         table.store().newTagManager(),
-                        table.store().newTagDeletion(),
                         Collections.emptyList());
 
         // threshold = tagTime + 2 days (nextTag + 1 period)
diff --git 
a/paimon-flink/paimon-flink-common/src/main/java/org/apache/paimon/flink/procedure/RollbackToAsLatestProcedure.java
 
b/paimon-flink/paimon-flink-common/src/main/java/org/apache/paimon/flink/procedure/RollbackToAsLatestProcedure.java
index 363a55b190..e41e822a86 100644
--- 
a/paimon-flink/paimon-flink-common/src/main/java/org/apache/paimon/flink/procedure/RollbackToAsLatestProcedure.java
+++ 
b/paimon-flink/paimon-flink-common/src/main/java/org/apache/paimon/flink/procedure/RollbackToAsLatestProcedure.java
@@ -119,11 +119,7 @@ public class RollbackToAsLatestProcedure extends 
ProcedureBase {
         } catch (Exception e) {
             try {
                 if (createdRollbackTag != null && canDeleteCreatedTag) {
-                    tagManager.deleteTag(
-                            createdRollbackTag,
-                            store.newTagDeletion(),
-                            store.snapshotManager(),
-                            Collections.emptyList());
+                    tagManager.deleteTag(createdRollbackTag, 
Collections.emptyList());
                 }
             } catch (Exception cleanupException) {
                 e.addSuppressed(cleanupException);
diff --git 
a/paimon-flink/paimon-flink-common/src/main/java/org/apache/paimon/flink/sink/AutoTagForSavepointCommitterOperator.java
 
b/paimon-flink/paimon-flink-common/src/main/java/org/apache/paimon/flink/sink/AutoTagForSavepointCommitterOperator.java
index 48f8d65e2e..523946d877 100644
--- 
a/paimon-flink/paimon-flink-common/src/main/java/org/apache/paimon/flink/sink/AutoTagForSavepointCommitterOperator.java
+++ 
b/paimon-flink/paimon-flink-common/src/main/java/org/apache/paimon/flink/sink/AutoTagForSavepointCommitterOperator.java
@@ -19,7 +19,6 @@
 package org.apache.paimon.flink.sink;
 
 import org.apache.paimon.Snapshot;
-import org.apache.paimon.operation.TagDeletion;
 import org.apache.paimon.table.sink.TagCallback;
 import org.apache.paimon.utils.SerializableSupplier;
 import org.apache.paimon.utils.SnapshotManager;
@@ -63,8 +62,6 @@ public class AutoTagForSavepointCommitterOperator<CommitT, 
GlobalCommitT>
 
     private final SerializableSupplier<TagManager> tagManagerFactory;
 
-    private final SerializableSupplier<TagDeletion> tagDeletionFactory;
-
     private final SerializableSupplier<List<TagCallback>> callbacksSupplier;
 
     private final NavigableSet<Long> identifiersForTags;
@@ -75,8 +72,6 @@ public class AutoTagForSavepointCommitterOperator<CommitT, 
GlobalCommitT>
 
     private transient TagManager tagManager;
 
-    private transient TagDeletion tagDeletion;
-
     private transient List<TagCallback> callbacks;
 
     private transient ListState<Long> identifiersForTagsState;
@@ -85,13 +80,11 @@ public class AutoTagForSavepointCommitterOperator<CommitT, 
GlobalCommitT>
             CommitterOperator<CommitT, GlobalCommitT> commitOperator,
             SerializableSupplier<SnapshotManager> snapshotManagerFactory,
             SerializableSupplier<TagManager> tagManagerFactory,
-            SerializableSupplier<TagDeletion> tagDeletionFactory,
             SerializableSupplier<List<TagCallback>> callbacksSupplier,
             Duration tagTimeRetained) {
         this.commitOperator = commitOperator;
         this.tagManagerFactory = tagManagerFactory;
         this.snapshotManagerFactory = snapshotManagerFactory;
-        this.tagDeletionFactory = tagDeletionFactory;
         this.callbacksSupplier = callbacksSupplier;
         this.identifiersForTags = new TreeSet<>();
         this.tagTimeRetained = tagTimeRetained;
@@ -105,7 +98,6 @@ public class AutoTagForSavepointCommitterOperator<CommitT, 
GlobalCommitT>
         } finally {
             snapshotManager = snapshotManagerFactory.get();
             tagManager = tagManagerFactory.get();
-            tagDeletion = tagDeletionFactory.get();
             callbacks = callbacksSupplier.get();
 
             identifiersForTagsState =
@@ -152,12 +144,7 @@ public class AutoTagForSavepointCommitterOperator<CommitT, 
GlobalCommitT>
         commitOperator.notifyCheckpointAborted(checkpointId);
         identifiersForTags.remove(checkpointId);
         SavepointTagUtils.deleteTagIfMatches(
-                tagManager,
-                commitOperator.getCommitUser(),
-                checkpointId,
-                tagDeletion,
-                snapshotManager,
-                callbacks);
+                tagManager, commitOperator.getCommitUser(), checkpointId, 
callbacks);
     }
 
     private void createTagForIdentifiers(List<Long> identifiers) {
diff --git 
a/paimon-flink/paimon-flink-common/src/main/java/org/apache/paimon/flink/sink/AutoTagForSavepointCommitterOperatorFactory.java
 
b/paimon-flink/paimon-flink-common/src/main/java/org/apache/paimon/flink/sink/AutoTagForSavepointCommitterOperatorFactory.java
index 1787f8e7ad..4c8f19a49c 100644
--- 
a/paimon-flink/paimon-flink-common/src/main/java/org/apache/paimon/flink/sink/AutoTagForSavepointCommitterOperatorFactory.java
+++ 
b/paimon-flink/paimon-flink-common/src/main/java/org/apache/paimon/flink/sink/AutoTagForSavepointCommitterOperatorFactory.java
@@ -18,7 +18,6 @@
 
 package org.apache.paimon.flink.sink;
 
-import org.apache.paimon.operation.TagDeletion;
 import org.apache.paimon.table.sink.TagCallback;
 import org.apache.paimon.utils.SerializableSupplier;
 import org.apache.paimon.utils.SnapshotManager;
@@ -48,8 +47,6 @@ public class 
AutoTagForSavepointCommitterOperatorFactory<CommitT, GlobalCommitT>
 
     private final SerializableSupplier<TagManager> tagManagerFactory;
 
-    private final SerializableSupplier<TagDeletion> tagDeletionFactory;
-
     private final SerializableSupplier<List<TagCallback>> callbacksSupplier;
 
     private final NavigableSet<Long> identifiersForTags;
@@ -60,13 +57,11 @@ public class 
AutoTagForSavepointCommitterOperatorFactory<CommitT, GlobalCommitT>
             CommitterOperatorFactory<CommitT, GlobalCommitT> 
commitOperatorFactory,
             SerializableSupplier<SnapshotManager> snapshotManagerFactory,
             SerializableSupplier<TagManager> tagManagerFactory,
-            SerializableSupplier<TagDeletion> tagDeletionFactory,
             SerializableSupplier<List<TagCallback>> callbacksSupplier,
             Duration tagTimeRetained) {
         this.commitOperatorFactory = commitOperatorFactory;
         this.tagManagerFactory = tagManagerFactory;
         this.snapshotManagerFactory = snapshotManagerFactory;
-        this.tagDeletionFactory = tagDeletionFactory;
         this.callbacksSupplier = callbacksSupplier;
         this.identifiersForTags = new TreeSet<>();
         this.tagTimeRetained = tagTimeRetained;
@@ -81,7 +76,6 @@ public class 
AutoTagForSavepointCommitterOperatorFactory<CommitT, GlobalCommitT>
                         commitOperatorFactory.createStreamOperator(parameters),
                         snapshotManagerFactory,
                         tagManagerFactory,
-                        tagDeletionFactory,
                         callbacksSupplier,
                         tagTimeRetained);
     }
diff --git 
a/paimon-flink/paimon-flink-common/src/main/java/org/apache/paimon/flink/sink/FlinkSink.java
 
b/paimon-flink/paimon-flink-common/src/main/java/org/apache/paimon/flink/sink/FlinkSink.java
index 21e9b722c4..a1fd0cd7aa 100644
--- 
a/paimon-flink/paimon-flink-common/src/main/java/org/apache/paimon/flink/sink/FlinkSink.java
+++ 
b/paimon-flink/paimon-flink-common/src/main/java/org/apache/paimon/flink/sink/FlinkSink.java
@@ -260,7 +260,6 @@ public abstract class FlinkSink<T> implements Serializable {
                                     committerOperator,
                             table::snapshotManager,
                             table::tagManager,
-                            () -> table.store().newTagDeletion(),
                             () -> table.store().createTagCallbacks(table),
                             table.coreOptions().tagDefaultTimeRetained());
         }
diff --git 
a/paimon-flink/paimon-flink-common/src/main/java/org/apache/paimon/flink/sink/RowAppendTableSink.java
 
b/paimon-flink/paimon-flink-common/src/main/java/org/apache/paimon/flink/sink/RowAppendTableSink.java
index 2e6b54fef1..87a28bb10b 100644
--- 
a/paimon-flink/paimon-flink-common/src/main/java/org/apache/paimon/flink/sink/RowAppendTableSink.java
+++ 
b/paimon-flink/paimon-flink-common/src/main/java/org/apache/paimon/flink/sink/RowAppendTableSink.java
@@ -23,7 +23,6 @@ import org.apache.paimon.flink.FlinkConnectorOptions;
 import 
org.apache.paimon.flink.sink.coordinator.CommittingWriteOperatorCoordinator;
 import org.apache.paimon.flink.sink.coordinator.SavepointTagger;
 import org.apache.paimon.manifest.ManifestCommittable;
-import org.apache.paimon.operation.TagDeletion;
 import org.apache.paimon.options.Options;
 import org.apache.paimon.table.FileStoreTable;
 import org.apache.paimon.table.sink.TagCallback;
@@ -143,8 +142,6 @@ public class RowAppendTableSink extends 
AppendTableSink<InternalRow> {
         private static SavepointTagger.Factory 
createSavepointTaggerFactory(FileStoreTable table) {
             SerializableSupplier<SnapshotManager> snapshotManagerFactory = 
table::snapshotManager;
             SerializableSupplier<TagManager> tagManagerFactory = 
table::tagManager;
-            SerializableSupplier<TagDeletion> tagDeletionFactory =
-                    () -> table.store().newTagDeletion();
             SerializableSupplier<List<TagCallback>> callbacksSupplier =
                     () -> table.store().createTagCallbacks(table);
             Duration tagTimeRetained = 
table.coreOptions().tagDefaultTimeRetained();
@@ -152,7 +149,6 @@ public class RowAppendTableSink extends 
AppendTableSink<InternalRow> {
                     new SavepointTagger(
                             snapshotManagerFactory.get(),
                             tagManagerFactory.get(),
-                            tagDeletionFactory.get(),
                             callbacksSupplier.get(),
                             tagTimeRetained,
                             commitUser);
diff --git 
a/paimon-flink/paimon-flink-common/src/main/java/org/apache/paimon/flink/sink/SavepointTagUtils.java
 
b/paimon-flink/paimon-flink-common/src/main/java/org/apache/paimon/flink/sink/SavepointTagUtils.java
index b530cd864c..8905ad6ba6 100644
--- 
a/paimon-flink/paimon-flink-common/src/main/java/org/apache/paimon/flink/sink/SavepointTagUtils.java
+++ 
b/paimon-flink/paimon-flink-common/src/main/java/org/apache/paimon/flink/sink/SavepointTagUtils.java
@@ -18,10 +18,8 @@
 
 package org.apache.paimon.flink.sink;
 
-import org.apache.paimon.operation.TagDeletion;
 import org.apache.paimon.table.sink.TagCallback;
 import org.apache.paimon.tag.Tag;
-import org.apache.paimon.utils.SnapshotManager;
 import org.apache.paimon.utils.TagManager;
 
 import java.util.List;
@@ -52,16 +50,11 @@ public class SavepointTagUtils {
             TagManager tagManager,
             String commitUser,
             long commitIdentifier,
-            TagDeletion tagDeletion,
-            SnapshotManager snapshotManager,
             List<TagCallback> callbacks) {
         String tagName = tagNameOf(commitIdentifier);
         tagManager
                 .get(tagName)
                 .filter(tag -> isSavepointTagFor(tag, commitUser, 
commitIdentifier))
-                .ifPresent(
-                        ignored ->
-                                tagManager.deleteTag(
-                                        tagName, tagDeletion, snapshotManager, 
callbacks));
+                .ifPresent(ignored -> tagManager.deleteTag(tagName, 
callbacks));
     }
 }
diff --git 
a/paimon-flink/paimon-flink-common/src/main/java/org/apache/paimon/flink/sink/coordinator/SavepointTagger.java
 
b/paimon-flink/paimon-flink-common/src/main/java/org/apache/paimon/flink/sink/coordinator/SavepointTagger.java
index e762f55e01..882c96eddf 100644
--- 
a/paimon-flink/paimon-flink-common/src/main/java/org/apache/paimon/flink/sink/coordinator/SavepointTagger.java
+++ 
b/paimon-flink/paimon-flink-common/src/main/java/org/apache/paimon/flink/sink/coordinator/SavepointTagger.java
@@ -20,7 +20,6 @@ package org.apache.paimon.flink.sink.coordinator;
 
 import org.apache.paimon.Snapshot;
 import org.apache.paimon.flink.sink.SavepointTagUtils;
-import org.apache.paimon.operation.TagDeletion;
 import org.apache.paimon.table.sink.TagCallback;
 import org.apache.paimon.utils.SnapshotManager;
 import org.apache.paimon.utils.TagManager;
@@ -47,7 +46,6 @@ public class SavepointTagger {
 
     private final SnapshotManager snapshotManager;
     private final TagManager tagManager;
-    private final TagDeletion tagDeletion;
     private final List<TagCallback> callbacks;
     private final Duration tagTimeRetained;
     // findSnapshotsForIdentifiers filters by commit user, so the tagger must 
be bound to the user
@@ -59,13 +57,11 @@ public class SavepointTagger {
     public SavepointTagger(
             SnapshotManager snapshotManager,
             TagManager tagManager,
-            TagDeletion tagDeletion,
             List<TagCallback> callbacks,
             Duration tagTimeRetained,
             String commitUser) {
         this.snapshotManager = checkNotNull(snapshotManager);
         this.tagManager = checkNotNull(tagManager);
-        this.tagDeletion = checkNotNull(tagDeletion);
         this.callbacks = checkNotNull(callbacks);
         this.tagTimeRetained = tagTimeRetained;
         this.commitUser = checkNotNull(commitUser);
@@ -90,8 +86,7 @@ public class SavepointTagger {
     /** Drops an aborted savepoint's pending intent and removes any tag 
already created for it. */
     public void dropAborted(long checkpointId) {
         pendingIdentifiers.remove(checkpointId);
-        SavepointTagUtils.deleteTagIfMatches(
-                tagManager, commitUser, checkpointId, tagDeletion, 
snapshotManager, callbacks);
+        SavepointTagUtils.deleteTagIfMatches(tagManager, commitUser, 
checkpointId, callbacks);
     }
 
     private void createTags(Collection<Long> identifiers) {
diff --git 
a/paimon-flink/paimon-flink-common/src/test/java/org/apache/paimon/flink/sink/AutoTagForSavepointCommitterOperatorTest.java
 
b/paimon-flink/paimon-flink-common/src/test/java/org/apache/paimon/flink/sink/AutoTagForSavepointCommitterOperatorTest.java
index 1ebddec2bf..91e1bf3aa0 100644
--- 
a/paimon-flink/paimon-flink-common/src/test/java/org/apache/paimon/flink/sink/AutoTagForSavepointCommitterOperatorTest.java
+++ 
b/paimon-flink/paimon-flink-common/src/test/java/org/apache/paimon/flink/sink/AutoTagForSavepointCommitterOperatorTest.java
@@ -239,7 +239,6 @@ public class AutoTagForSavepointCommitterOperatorTest 
extends CommitterOperatorT
                                 table, commitUser, committableStateManager),
                 table::snapshotManager,
                 table::tagManager,
-                () -> table.store().newTagDeletion(),
                 () -> table.store().createTagCallbacks(table),
                 table.store().options().tagDefaultTimeRetained());
     }
@@ -257,7 +256,6 @@ public class AutoTagForSavepointCommitterOperatorTest 
extends CommitterOperatorT
                                 table, commitUser, committableStateManager, 
initializeFunction),
                 table::snapshotManager,
                 table::tagManager,
-                () -> table.store().newTagDeletion(),
                 () -> table.store().createTagCallbacks(table),
                 table.store().options().tagDefaultTimeRetained());
     }
diff --git 
a/paimon-flink/paimon-flink-common/src/test/java/org/apache/paimon/flink/sink/SavepointTagUtilsTest.java
 
b/paimon-flink/paimon-flink-common/src/test/java/org/apache/paimon/flink/sink/SavepointTagUtilsTest.java
index 2e858e41c8..b513dec752 100644
--- 
a/paimon-flink/paimon-flink-common/src/test/java/org/apache/paimon/flink/sink/SavepointTagUtilsTest.java
+++ 
b/paimon-flink/paimon-flink-common/src/test/java/org/apache/paimon/flink/sink/SavepointTagUtilsTest.java
@@ -125,8 +125,6 @@ public class SavepointTagUtilsTest extends 
CommitterTestBase {
                 table.tagManager(),
                 commitUser,
                 commitIdentifier,
-                table.store().newTagDeletion(),
-                table.snapshotManager(),
                 table.store().createTagCallbacks(table));
     }
 }
diff --git 
a/paimon-flink/paimon-flink-common/src/test/java/org/apache/paimon/flink/sink/coordinator/CommittingWriteOperatorCoordinatorTest.java
 
b/paimon-flink/paimon-flink-common/src/test/java/org/apache/paimon/flink/sink/coordinator/CommittingWriteOperatorCoordinatorTest.java
index 57ac6b66aa..6fec03d7b3 100644
--- 
a/paimon-flink/paimon-flink-common/src/test/java/org/apache/paimon/flink/sink/coordinator/CommittingWriteOperatorCoordinatorTest.java
+++ 
b/paimon-flink/paimon-flink-common/src/test/java/org/apache/paimon/flink/sink/coordinator/CommittingWriteOperatorCoordinatorTest.java
@@ -1259,7 +1259,6 @@ public class CommittingWriteOperatorCoordinatorTest 
extends CommitterTestBase {
                         new SavepointTagger(
                                 table.snapshotManager(),
                                 table.tagManager(),
-                                table.store().newTagDeletion(),
                                 table.store().createTagCallbacks(table),
                                 table.coreOptions().tagDefaultTimeRetained(),
                                 user));
diff --git 
a/paimon-flink/paimon-flink-common/src/test/java/org/apache/paimon/flink/sink/coordinator/SavepointTaggerTest.java
 
b/paimon-flink/paimon-flink-common/src/test/java/org/apache/paimon/flink/sink/coordinator/SavepointTaggerTest.java
index a84bc205c9..b99599a9a9 100644
--- 
a/paimon-flink/paimon-flink-common/src/test/java/org/apache/paimon/flink/sink/coordinator/SavepointTaggerTest.java
+++ 
b/paimon-flink/paimon-flink-common/src/test/java/org/apache/paimon/flink/sink/coordinator/SavepointTaggerTest.java
@@ -191,7 +191,6 @@ public class SavepointTaggerTest extends CommitterTestBase {
         return new SavepointTagger(
                 table.snapshotManager(),
                 table.tagManager(),
-                table.store().newTagDeletion(),
                 table.store().createTagCallbacks(table),
                 table.coreOptions().tagDefaultTimeRetained(),
                 commitUser);
diff --git 
a/paimon-spark/paimon-spark-common/src/main/java/org/apache/paimon/spark/procedure/RollbackToAsLatestProcedure.java
 
b/paimon-spark/paimon-spark-common/src/main/java/org/apache/paimon/spark/procedure/RollbackToAsLatestProcedure.java
index 69366598ee..43705c1e12 100644
--- 
a/paimon-spark/paimon-spark-common/src/main/java/org/apache/paimon/spark/procedure/RollbackToAsLatestProcedure.java
+++ 
b/paimon-spark/paimon-spark-common/src/main/java/org/apache/paimon/spark/procedure/RollbackToAsLatestProcedure.java
@@ -151,11 +151,7 @@ public class RollbackToAsLatestProcedure extends 
BaseProcedure {
                     } catch (Exception e) {
                         try {
                             if (createdRollbackTag != null && 
canDeleteCreatedTag) {
-                                tagManager.deleteTag(
-                                        createdRollbackTag,
-                                        store.newTagDeletion(),
-                                        store.snapshotManager(),
-                                        Collections.emptyList());
+                                tagManager.deleteTag(createdRollbackTag, 
Collections.emptyList());
                             }
                         } catch (Exception cleanupException) {
                             e.addSuppressed(cleanupException);

Reply via email to