diff --git a/curvefs/src/metaserver/inode_manager.cpp b/curvefs/src/metaserver/inode_manager.cpp
index ef998f1ffd..30bbeab65f 100644
--- a/curvefs/src/metaserver/inode_manager.cpp
+++ b/curvefs/src/metaserver/inode_manager.cpp
@@ -25,6 +25,7 @@
 #include <glog/logging.h>
 #include <google/protobuf/util/message_differencer.h>
 #include <list>
+#include <unordered_set>
 
 #include "curvefs/src/common/define.h"
 #include "src/common/timeutility.h"
@@ -130,21 +131,27 @@ void InodeManager::GenerateInodeInternal(uint64_t inodeId,
     return;
 }
 
-MetaStatusCode InodeManager::GetInode(uint32_t fsId, uint64_t inodeId,
-                                      Inode *inode) {
+MetaStatusCode InodeManager::GetInode(uint32_t fsId,
+                                      uint64_t inodeId,
+                                      Inode *inode,
+                                      bool paddingS3ChunkInfo) {
     VLOG(1) << "GetInode, fsId = " << fsId << ", inodeId = " << inodeId;
     NameLockGuard lg(inodeLock_, GetInodeLockName(fsId, inodeId));
-    MetaStatusCode ret = inodeStorage_->Get(Key4Inode(fsId, inodeId), inode);
-    if (ret != MetaStatusCode::OK) {
+    MetaStatusCode rc = inodeStorage_->Get(Key4Inode(fsId, inodeId), inode);
+    if (rc == MetaStatusCode::OK && paddingS3ChunkInfo) {
+        rc = PaddingInodeS3ChunkInfo(fsId, inodeId,
+                                     inode->mutable_s3chunkinfomap());
+    }
+
+    if (rc != MetaStatusCode::OK) {
         LOG(ERROR) << "GetInode fail, fsId = " << fsId
                    << ", inodeId = " << inodeId
-                   << ", ret = " << MetaStatusCode_Name(ret);
-        return ret;
+                   << ", retCode = " << MetaStatusCode_Name(rc);
+        return rc;
     }
 
     VLOG(1) << "GetInode success, fsId = " << fsId << ", inodeId = " << inodeId
             << ", " << inode->ShortDebugString();
-
     return MetaStatusCode::OK;
 }
 
@@ -308,30 +315,59 @@ MetaStatusCode InodeManager::UpdateInode(const UpdateInodeRequest &request) {
 MetaStatusCode InodeManager::GetOrModifyS3ChunkInfo(
     uint32_t fsId, uint64_t inodeId,
     const S3ChunkInfoMap& map2add,
-    std::shared_ptr<Iterator>* iterator,
+    const S3ChunkInfoMap& map2del,
     bool returnS3ChunkInfoMap,
-    bool compaction) {
+    std::shared_ptr<Iterator>* iterator4InodeS3Meta) {
     VLOG(1) << "GetOrModifyS3ChunkInfo, fsId: " << fsId
             << ", inodeId: " << inodeId;
 
     NameLockGuard lg(inodeLock_, GetInodeLockName(fsId, inodeId));
 
-    if (!map2add.empty()) {
-        for (const auto& item : map2add) {
-            uint64_t chunkIndex = item.first;
-            auto list2add = item.second;
-            MetaStatusCode rc = inodeStorage_->AppendS3ChunkInfoList(
-                fsId, inodeId, chunkIndex, list2add, compaction);
-            if (rc != MetaStatusCode::OK) {
-                return rc;
-            }
+    const S3ChunkInfoList* list2add;
+    const S3ChunkInfoList* list2del;
+    std::unordered_set<uint64_t> deleted;
+    for (const auto& item : map2add) {
+        uint64_t chunkIndex = item.first;
+        list2add = &item.second;
+        auto iter = map2del.find(chunkIndex);
+        if (iter != map2del.end()) {
+            list2del = &iter->second;
+        } else {
+            list2del = nullptr;
+        }
+
+        MetaStatusCode rc = inodeStorage_->ModifyInodeS3ChunkInfoList(
+            fsId, inodeId, chunkIndex, list2add, list2del);
+        if (rc != MetaStatusCode::OK) {
+            LOG(ERROR) << "Modify inode s3chunkinfo list failed, fsId=" << fsId
+                       << ", inodeId=" << inodeId << ", retCode=" << rc;
+            return rc;
+        }
+        deleted.insert(chunkIndex);
+    }
+
+    for (const auto& item : map2del) {
+        uint64_t chunkIndex = item.first;
+        if (deleted.find(chunkIndex) != deleted.end()) {  // already deleted
+            continue;
+        }
+
+        list2add = nullptr;
+        list2del = &item.second;
+        MetaStatusCode rc = inodeStorage_->ModifyInodeS3ChunkInfoList(
+            fsId, inodeId, chunkIndex, list2add, list2del);
+        if (rc != MetaStatusCode::OK) {
+            LOG(ERROR) << "Modify inode s3chunkinfo list failed, fsId=" << fsId
+                       << ", inodeId=" << inodeId << ", retCode=" << rc;
+            return rc;
         }
     }
 
     // return if needed
     if (returnS3ChunkInfoMap) {
-        *iterator = inodeStorage_->GetInodeS3ChunkInfoList(fsId, inodeId);
-        if ((*iterator)->Status() != 0) {
+        *iterator4InodeS3Meta = inodeStorage_->GetInodeS3ChunkInfoList(
+            fsId, inodeId);
+        if ((*iterator4InodeS3Meta)->Status() != 0) {
             return MetaStatusCode::STORAGE_INTERNAL_ERROR;
         }
     }
diff --git a/curvefs/src/metaserver/inode_manager.h b/curvefs/src/metaserver/inode_manager.h
index 55b2e94ef2..c1ee17a654 100644
--- a/curvefs/src/metaserver/inode_manager.h
+++ b/curvefs/src/metaserver/inode_manager.h
@@ -60,7 +60,11 @@ class InodeManager {
     MetaStatusCode CreateInode(uint64_t inodeId, const InodeParam &param,
                                Inode *inode);
     MetaStatusCode CreateRootInode(const InodeParam &param);
-    MetaStatusCode GetInode(uint32_t fsId, uint64_t inodeId, Inode *inode);
+
+    MetaStatusCode GetInode(uint32_t fsId,
+                            uint64_t inodeId,
+                            Inode *inode,
+                            bool paddingS3ChunkInfo = false);
 
     MetaStatusCode GetInodeAttr(uint32_t fsId, uint64_t inodeId,
                                 InodeAttr *attr);
@@ -71,12 +75,13 @@ class InodeManager {
 
     MetaStatusCode UpdateInode(const UpdateInodeRequest &request);
 
-    MetaStatusCode GetOrModifyS3ChunkInfo(uint32_t fsId,
-                                          uint64_t inodeId,
-                                          const S3ChunkInfoMap& map2add,
-                                          std::shared_ptr<Iterator>* iterator,
-                                          bool returnS3ChunkInfoMap,
-                                          bool compaction);
+    MetaStatusCode GetOrModifyS3ChunkInfo(
+        uint32_t fsId,
+        uint64_t inodeId,
+        const S3ChunkInfoMap& map2add,
+        const S3ChunkInfoMap& map2del,
+        bool returnS3ChunkInfoMap,
+        std::shared_ptr<Iterator>* iterator4InodeS3Meta);
 
     MetaStatusCode PaddingInodeS3ChunkInfo(int32_t fsId,
                                            uint64_t inodeId,
diff --git a/curvefs/src/metaserver/inode_storage.cpp b/curvefs/src/metaserver/inode_storage.cpp
index 55f218eb9f..827e9e193a 100644
--- a/curvefs/src/metaserver/inode_storage.cpp
+++ b/curvefs/src/metaserver/inode_storage.cpp
@@ -180,37 +180,46 @@ MetaStatusCode InodeStorage::AddS3ChunkInfoList(
     uint32_t fsId,
     uint64_t inodeId,
     uint64_t chunkIndex,
-    const S3ChunkInfoList& list2add) {
-    // key
-    size_t size = list2add.s3chunks_size();
-    uint64_t firstChunkId = list2add.s3chunks(0).chunkid();
-    uint64_t lastChunkId = list2add.s3chunks(size - 1).chunkid();
+    const S3ChunkInfoList* list2add) {
+    if (nullptr == list2add || list2add->s3chunks_size() == 0) {
+        return MetaStatusCode::OK;
+    }
+
+    size_t size = list2add->s3chunks_size();
+    uint64_t firstChunkId = list2add->s3chunks(0).chunkid();
+    uint64_t lastChunkId = list2add->s3chunks(size - 1).chunkid();
     Key4S3ChunkInfoList key(fsId, inodeId, chunkIndex,
                             firstChunkId, lastChunkId, size);
     std::string skey = conv_->SerializeToString(key);
 
-    Status s = txn->SSet(table4s3chunkinfo_, skey, list2add);
+    Status s = txn->SSet(table4s3chunkinfo_, skey, *list2add);
     return s.ok() ? MetaStatusCode::OK :
                     MetaStatusCode::STORAGE_INTERNAL_ERROR;
 }
 
-// NOTE: s3chunkinfo which its chunkid equal or
-// less then min chunkid should be removed
-MetaStatusCode InodeStorage::RemoveS3ChunkInfoList(Transaction txn,
-                                                   uint32_t fsId,
-                                                   uint64_t inodeId,
-                                                   uint64_t chunkIndex,
-                                                   uint64_t minChunkId,
-                                                   uint64_t* size4del) {
+MetaStatusCode InodeStorage::DelS3ChunkInfoList(
+    Transaction txn,
+    uint32_t fsId,
+    uint64_t inodeId,
+    uint64_t chunkIndex,
+    const S3ChunkInfoList* list2del) {
+    if (nullptr == list2del || list2del->s3chunks_size() == 0) {
+        return MetaStatusCode::OK;
+    }
+
+    size_t size = list2del->s3chunks_size();
+    uint64_t delFirstChunkId = list2del->s3chunks(0).chunkid();
+    uint64_t delLastChunkId = list2del->s3chunks(size - 1).chunkid();
+
+    // prefix
     Prefix4ChunkIndexS3ChunkInfoList prefix(fsId, inodeId, chunkIndex);
     std::string sprefix = conv_->SerializeToString(prefix);
     auto iterator = txn->SSeek(table4s3chunkinfo_, sprefix);
     if (iterator->Status() != 0) {
+        LOG(ERROR) << "Get iterator failed, prefix=" << sprefix;
         return MetaStatusCode::STORAGE_INTERNAL_ERROR;
     }
 
-    *size4del = 0;
-    uint64_t lastChunkId;
     Key4S3ChunkInfoList key;
     std::vector<std::string> key2del;
     for (iterator->SeekToFirst(); iterator->Valid(); iterator->Next()) {
@@ -219,61 +228,70 @@ MetaStatusCode InodeStorage::RemoveS3ChunkInfoList(Transaction txn,
             break;
         } else if (!conv_->ParseFromString(skey, &key)) {
             return MetaStatusCode::PARSE_FROM_STRING_FAILED;
-        } else if (key.firstChunkId >= minChunkId) {
-            break;
         }
 
-        // firstChunkId < minChunkId
-        key2del.push_back(skey);
-        *size4del += key.size;
+        // current list range:    [  ]
+        // delete list range :  [      ]
+        if (delFirstChunkId <= key.firstChunkId &&
+            delLastChunkId >= key.lastChunkId) {
+            key2del.push_back(skey);
+        // current list range:       [  ]
+        // delete list range :  [  ]
+        } else if (delLastChunkId < key.firstChunkId) {
+            continue;
+        } else {
+            LOG(ERROR) << "wrong delete list range (" << delFirstChunkId
+                       << "," << delLastChunkId << "), skey=" << skey;
+            return MetaStatusCode::STORAGE_INTERNAL_ERROR;
+        }
     }
 
     for (const auto& skey : key2del) {
         if (!txn->SDel(table4s3chunkinfo_, skey).ok()) {
+            LOG(ERROR) << "Delete key failed, skey=" << skey;
             return MetaStatusCode::STORAGE_INTERNAL_ERROR;
         }
     }
     return MetaStatusCode::OK;
 }
 
-MetaStatusCode InodeStorage::AppendS3ChunkInfoList(
+MetaStatusCode InodeStorage::ModifyInodeS3ChunkInfoList(
     uint32_t fsId,
     uint64_t inodeId,
     uint64_t chunkIndex,
-    const S3ChunkInfoList& list2add,
-    bool compaction) {
+    const S3ChunkInfoList* list2add,
+    const S3ChunkInfoList* list2del) {
     WriteLockGuard writeLockGuard(rwLock_);
-    size_t size = list2add.s3chunks_size();
-    if (size == 0) {
-        return MetaStatusCode::OK;
-    }
-
     auto txn = kvStorage_->BeginTransaction();
     if (nullptr == txn) {
         return MetaStatusCode::STORAGE_INTERNAL_ERROR;
     }
 
-    MetaStatusCode rc;
-    uint64_t size4add = list2add.s3chunks_size();
-    uint64_t size4del = 0;
-    rc = AddS3ChunkInfoList(txn, fsId, inodeId, chunkIndex, list2add);
-    if (rc == MetaStatusCode::OK && compaction) {
-        uint64_t minChunkId = list2add.s3chunks(0).chunkid();
-        rc = RemoveS3ChunkInfoList(txn, fsId, inodeId, chunkIndex,
-                                   minChunkId, &size4del);
+    auto rc = DelS3ChunkInfoList(txn, fsId, inodeId, chunkIndex, list2del);
+    if (rc == MetaStatusCode::OK) {
+        rc = AddS3ChunkInfoList(txn, fsId, inodeId, chunkIndex, list2add);
     }
 
     if (rc != MetaStatusCode::OK) {
-        txn->Rollback();
+        if (!txn->Rollback().ok()) {
+            LOG(ERROR) << "Rollback transaction failed";
+            rc = MetaStatusCode::STORAGE_INTERNAL_ERROR;
+        }
     } else if (!txn->Commit().ok()) {
+        LOG(ERROR) << "Commit transaction failed";
         rc = MetaStatusCode::STORAGE_INTERNAL_ERROR;
     }
 
-    if (rc == MetaStatusCode::OK &&
-        !UpdateInodeS3MetaSize(fsId, inodeId, size4add, size4del)) {
-        rc = MetaStatusCode::STORAGE_INTERNAL_ERROR;
-        LOG(ERROR) << "UpdateInodeS3MetaSize() failed, size4add=" << size4add
-                   << ", size4del" << size4del;
+    if (rc != MetaStatusCode::OK) {
+        return rc;
+    }
+
+    // rc == MetaStatusCode::OK
+    uint64_t size4add = (nullptr == list2add) ? 0 : list2add->s3chunks_size();
+    uint64_t size4del = (nullptr == list2del) ? 0 : list2del->s3chunks_size();
+    if (!UpdateInodeS3MetaSize(fsId, inodeId, size4add, size4del)) {
+        LOG(ERROR) << "Update inode s3meta size failed";
+        return MetaStatusCode::STORAGE_INTERNAL_ERROR;
     }
     return rc;
 }
diff --git a/curvefs/src/metaserver/inode_storage.h b/curvefs/src/metaserver/inode_storage.h
index 433e42d5e4..211192dd7c 100644
--- a/curvefs/src/metaserver/inode_storage.h
+++ b/curvefs/src/metaserver/inode_storage.h
@@ -24,14 +24,13 @@
 #define CURVEFS_SRC_METASERVER_INODE_STORAGE_H_
 
 #include <functional>
+#include <utility>
 #include <unordered_map>
 #include <unordered_set>
-#include <utility>
 #include <list>
 #include <string>
 #include <memory>
 
-#include "absl/container/btree_set.h"
 #include "src/common/concurrent/rw_lock.h"
 #include "curvefs/proto/metaserver.pb.h"
 #include "curvefs/src/metaserver/storage/converter.h"
@@ -45,6 +44,7 @@ using ::curvefs::metaserver::storage::Status;
 using ::curvefs::metaserver::storage::Iterator;
 using ::curvefs::metaserver::storage::KVStorage;
 using ::curvefs::metaserver::storage::StorageTransaction;
+using ::curvefs::metaserver::storage::Hash;
 
 namespace curvefs {
 namespace metaserver {
@@ -108,11 +108,11 @@ class InodeStorage {
      */
     MetaStatusCode Update(const Inode& inode);
 
-    MetaStatusCode AppendS3ChunkInfoList(uint32_t fsId,
-                                         uint64_t inodeId,
-                                         uint64_t chunkIndex,
-                                         const S3ChunkInfoList& list2add,
-                                         bool compaction);
+    MetaStatusCode ModifyInodeS3ChunkInfoList(uint32_t fsId,
+                                              uint64_t inodeId,
+                                              uint64_t chunkIndex,
+                                              const S3ChunkInfoList* list2add,
+                                              const S3ChunkInfoList* list2del);
 
     MetaStatusCode PaddingInodeS3ChunkInfo(int32_t fsId,
                                            uint64_t inodeId,
@@ -138,15 +138,14 @@ class InodeStorage {
         uint32_t fsId,
         uint64_t inodeId,
         uint64_t chunkIndex,
-        const S3ChunkInfoList& list2add);
+        const S3ChunkInfoList* list2add);
 
-    MetaStatusCode RemoveS3ChunkInfoList(
+    MetaStatusCode DelS3ChunkInfoList(
         std::shared_ptr<StorageTransaction> txn,
         uint32_t fsId,
         uint64_t inodeId,
         uint64_t chunkIndex,
-        uint64_t minChunkId,
-        uint64_t* size4del);
+        const S3ChunkInfoList* list2del);
 
     std::string RealTablename(TABLE_TYPE type, std::string tablename) {
         std::ostringstream oss;
diff --git a/curvefs/src/metaserver/metastore.cpp b/curvefs/src/metaserver/metastore.cpp
index beacf5312b..90cab1fcd6 100644
--- a/curvefs/src/metaserver/metastore.cpp
+++ b/curvefs/src/metaserver/metastore.cpp
@@ -525,13 +525,20 @@ MetaStatusCode MetaStoreImpl::GetOrModifyS3ChunkInfo(
     auto partition = GetPartition(request->partitionid());
     if (nullptr == partition) {
         rc = MetaStatusCode::PARTITION_NOT_FOUND;
-    } else {
-        rc = partition->GetOrModifyS3ChunkInfo(request->fsid(),
-                                               request->inodeid(),
-                                               request->s3chunkinfoadd(),
-                                               iterator,
-                                               request->returns3chunkinfomap(),
-                                               request->froms3compaction());
+        response->set_statuscode(rc);
+        return rc;
+    }
+
+    uint32_t fsId = request->fsid();
+    uint64_t inodeId = request->inodeid();
+    rc = partition->GetOrModifyS3ChunkInfo(fsId, inodeId,
+                                           request->s3chunkinfoadd(),
+                                           request->s3chunkinforemove(),
+                                           request->returns3chunkinfomap(),
+                                           iterator);
+    if (rc == MetaStatusCode::OK && !request->supportstreaming()) {
+        rc = partition->PaddingInodeS3ChunkInfo(
+            fsId, inodeId, response->mutable_s3chunkinfomap(), 0);
     }
 
     if (rc == MetaStatusCode::OK && !request->supportstreaming()) {
diff --git a/curvefs/src/metaserver/metastore.h b/curvefs/src/metaserver/metastore.h
index af75cd4736..0a7d51bafc 100644
--- a/curvefs/src/metaserver/metastore.h
+++ b/curvefs/src/metaserver/metastore.h
@@ -77,6 +77,7 @@ using ::curvefs::metaserver::copyset::OnSnapshotSaveDoneClosure;
 using ::curvefs::metaserver::storage::Iterator;
 using ::curvefs::common::StreamServer;
 using ::curvefs::common::StreamConnection;
+using S3ChunkInfoMap = google::protobuf::Map<uint64_t, S3ChunkInfoList>;
 
 class MetaStore {
  public:
diff --git a/curvefs/src/metaserver/metastore_fstream.cpp b/curvefs/src/metaserver/metastore_fstream.cpp
index f797dceb84..97c7cb7616 100644
--- a/curvefs/src/metaserver/metastore_fstream.cpp
+++ b/curvefs/src/metaserver/metastore_fstream.cpp
@@ -170,11 +170,12 @@ bool MetaStoreFStream::LoadInodeS3ChunkInfoList(uint32_t partitionId,
         return false;
     }
 
-    std::shared_ptr<Iterator> iterator;
     S3ChunkInfoMap map2add;
+    S3ChunkInfoMap map2del;
+    std::shared_ptr<Iterator> iterator;
     map2add.insert({key4list.chunkIndex, list});
     MetaStatusCode rc = partition->GetOrModifyS3ChunkInfo(
-        key4list.fsId, key4list.inodeId, map2add, &iterator, false, false);
+        key4list.fsId, key4list.inodeId, map2add, map2del, false, &iterator);
     if (rc != MetaStatusCode::OK) {
         LOG(ERROR) << "GetOrModifyS3ChunkInfo failed, retCode = "
                    << MetaStatusCode_Name(rc);
diff --git a/curvefs/src/metaserver/partition.cpp b/curvefs/src/metaserver/partition.cpp
index 08cfe5a6e1..6a4fddece2 100644
--- a/curvefs/src/metaserver/partition.cpp
+++ b/curvefs/src/metaserver/partition.cpp
@@ -262,20 +262,18 @@ MetaStatusCode Partition::UpdateInode(const UpdateInodeRequest& request) {
 MetaStatusCode Partition::GetOrModifyS3ChunkInfo(
     uint32_t fsId,
     uint64_t inodeId,
-    const S3ChunkInfoMap& list2add,
-    std::shared_ptr<Iterator>* iterator,
+    const S3ChunkInfoMap& map2add,
+    const S3ChunkInfoMap& map2del,
     bool returnS3ChunkInfoMap,
-    bool compaction) {
+    std::shared_ptr<Iterator>* iterator) {
     if (!IsInodeBelongs(fsId, inodeId)) {
         return MetaStatusCode::PARTITION_ID_MISSMATCH;
-    }
-
-    if (GetStatus() == PartitionStatus::DELETING) {
+    } else if (GetStatus() == PartitionStatus::DELETING) {
         return MetaStatusCode::PARTITION_DELETING;
     }
 
     return inodeManager_->GetOrModifyS3ChunkInfo(
-        fsId, inodeId, list2add, iterator, returnS3ChunkInfoMap, compaction);
+        fsId, inodeId, map2add, map2del, returnS3ChunkInfoMap, iterator);
 }
 
 MetaStatusCode Partition::PaddingInodeS3ChunkInfo(int32_t fsId,
diff --git a/curvefs/src/metaserver/partition.h b/curvefs/src/metaserver/partition.h
index 6a0ce60704..f7c700b4be 100644
--- a/curvefs/src/metaserver/partition.h
+++ b/curvefs/src/metaserver/partition.h
@@ -92,10 +92,10 @@ class Partition {
 
     MetaStatusCode GetOrModifyS3ChunkInfo(uint32_t fsId,
                                           uint64_t inodeId,
-                                          const S3ChunkInfoMap& list2add,
-                                          std::shared_ptr<Iterator>* iterator,
+                                          const S3ChunkInfoMap& map2add,
+                                          const S3ChunkInfoMap& map2del,
                                           bool returnS3ChunkInfoMap,
-                                          bool compaction);
+                                          std::shared_ptr<Iterator>* iterator);
 
     MetaStatusCode PaddingInodeS3ChunkInfo(int32_t fsId,
                                            uint64_t inodeId,
diff --git a/curvefs/src/metaserver/s3compact_wq_impl.cpp b/curvefs/src/metaserver/s3compact_wq_impl.cpp
index aac30389fb..36c2029232 100644
--- a/curvefs/src/metaserver/s3compact_wq_impl.cpp
+++ b/curvefs/src/metaserver/s3compact_wq_impl.cpp
@@ -442,7 +442,7 @@ bool S3CompactWorkQueueImpl::CompactPrecheck(const struct S3CompactTask& task,
 
     // inode exist?
     MetaStatusCode ret = task.inodeManager->GetInode(
-        task.inodeKey.fsId, task.inodeKey.inodeId, inode);
+        task.inodeKey.fsId, task.inodeKey.inodeId, inode, true);
     if (ret != MetaStatusCode::OK) {
         LOG(WARNING) << "s3compact: GetInode fail, inodeKey = "
                      << task.inodeKey.fsId << "," << task.inodeKey.inodeId
@@ -456,22 +456,9 @@ bool S3CompactWorkQueueImpl::CompactPrecheck(const struct S3CompactTask& task,
         return false;
     }
 
-    // pandding s3chunkinfomap for inode
-    {
-        auto inodeKey = task.inodeKey;
-        auto inodeManager = task.inodeManager;
-        MetaStatusCode rc = inodeManager->PaddingInodeS3ChunkInfo(
-            inodeKey.fsId, inodeKey.inodeId, inode->mutable_s3chunkinfomap());
-        if (rc != MetaStatusCode::OK) {
-            LOG(ERROR) << "Padding inode s3chunkinfo failed, "
-                       << "retCode = " << MetaStatusCode_Name(ret);
-            return false;
-        }
-
-        if (inode->s3chunkinfomap().size() == 0) {
-            VLOG(6) << "Inode s3chunkinfo is empty";
-            return false;
-        }
+    if (inode->s3chunkinfomap().size() == 0) {
+        VLOG(6) << "Inode s3chunkinfo is empty";
+        return false;
     }
 
     // pass
diff --git a/curvefs/src/metaserver/storage/rocksdb_storage.cpp b/curvefs/src/metaserver/storage/rocksdb_storage.cpp
index 1a778cdf58..31d36b90cf 100644
--- a/curvefs/src/metaserver/storage/rocksdb_storage.cpp
+++ b/curvefs/src/metaserver/storage/rocksdb_storage.cpp
@@ -298,6 +298,13 @@ inline void RocksDBStorage::CommitKeys() {
     for (const auto& pair : pending4del_) {
         counter_->Erase(pair.first, pair.second);
     }
+    pending4set_.clear();
+    pending4del_.clear();
+}
+
+inline void RocksDBStorage::RollbackKeys() {
+    pending4set_.clear();
+    pending4del_.clear();
 }
 
 Status RocksDBStorage::Get(const std::string& name,
@@ -407,6 +414,8 @@ size_t RocksDBStorage::Size(const std::string& name, bool ordered) {
 Status RocksDBStorage::Clear(const std::string& name, bool ordered) {
     if (!inited_) {
         return Status::DBClosed();
+    } else if (InTransaction_) {
+        return Status::NotSupported();
     }
 
     auto handle = GetColumnFamilyHandle(ordered);
@@ -428,6 +437,8 @@ std::shared_ptr<StorageTransaction> RocksDBStorage::BeginTransaction() {
     if (nullptr == txn) {
         return nullptr;
     }
+    pending4set_.clear();
+    pending4del_.clear();
     return std::make_shared<RocksDBStorage>(*this, txn);
 }
 
@@ -436,23 +447,30 @@ Status RocksDBStorage::Commit() {
         return Status::NotSupported();
     }
 
-    Status s = ToStorageStatus(txn_->Commit());
-    if (s.ok()) {
+    ROCKSDB_NAMESPACE::Status s = txn_->Commit();
+    if (!s.ok()) {
+        LOG(ERROR) << "RocksDBStorage commit transaction failed"
+                   << ", status=" << s.ToString();
+    } else {
         CommitKeys();
     }
     delete txn_;
-    return s;
+    return ToStorageStatus(s);
 }
 
 Status RocksDBStorage::Rollback()  {
     if (!InTransaction_ || nullptr == txn_) {
         return Status::NotSupported();
     }
-    pending4set_.clear();
-    pending4del_.clear();
-    Status s = ToStorageStatus(txn_->Commit());
+    ROCKSDB_NAMESPACE::Status s = txn_->Rollback();
+    if (!s.ok()) {
+        LOG(ERROR) << "RocksDBStorage rollback transaction failed"
+                   << ", status=" << s.ToString();
+    } else {
+        RollbackKeys();
+    }
     delete txn_;
-    return s;
+    return ToStorageStatus(s);
 }
 
 bool RocksDBStorage::GetStatistics(StorageStatistics* statistics) {
diff --git a/curvefs/src/metaserver/storage/rocksdb_storage.h b/curvefs/src/metaserver/storage/rocksdb_storage.h
index 007fffdf60..395d6aafec 100644
--- a/curvefs/src/metaserver/storage/rocksdb_storage.h
+++ b/curvefs/src/metaserver/storage/rocksdb_storage.h
@@ -223,6 +223,8 @@ class RocksDBStorage : public KVStorage, public StorageTransaction {
 
     void CommitKeys();
 
+    void RollbackKeys();
+
     Status Get(const std::string& name,
                const std::string& key,
                ValueType* value,
@@ -238,8 +240,11 @@ class RocksDBStorage : public KVStorage, public StorageTransaction {
                bool ordered);
 
     std::shared_ptr<Iterator> Seek(const std::string& name,
-                                    const std::string& prefix);
+                                   const std::string& prefix);
 
+    // TODO(@Wine93): We do not support transactions for the
+    // below 3 methods, maybe we should return Status::NotSupported
+    // when user invoke it in transaction.
     std::shared_ptr<Iterator> GetAll(const std::string& name, bool ordered);
 
     size_t Size(const std::string& name, bool ordered);
diff --git a/curvefs/test/metaserver/inode_manager_test.cpp b/curvefs/test/metaserver/inode_manager_test.cpp
index e15fb8dd37..bc8f2a061a 100644
--- a/curvefs/test/metaserver/inode_manager_test.cpp
+++ b/curvefs/test/metaserver/inode_manager_test.cpp
@@ -149,6 +149,24 @@ class InodeManagerTest : public ::testing::Test {
         return list;
     }
 
+    void CHECK_ITERATOR_S3CHUNKINFOLIST(
+        std::shared_ptr<Iterator> iterator,
+        const std::vector<uint64_t> chunkIndexs,
+        const std::vector<S3ChunkInfoList> lists) {
+        size_t size = 0;
+        Key4S3ChunkInfoList key;
+        S3ChunkInfoList list4get;
+        ASSERT_EQ(iterator->Status(), 0);
+        for (iterator->SeekToFirst(); iterator->Valid(); iterator->Next()) {
+            ASSERT_TRUE(conv_->ParseFromString(iterator->Key(), &key));
+            ASSERT_TRUE(conv_->ParseFromString(iterator->Value(), &list4get));
+            ASSERT_EQ(key.chunkIndex, chunkIndexs[size]);
+            ASSERT_TRUE(EqualS3ChunkInfoList(list4get, lists[size]));
+            size++;
+        }
+        ASSERT_EQ(size, chunkIndexs.size());
+    }
+
  protected:
     std::shared_ptr<InodeManager> manager;
     InodeParam param_;
@@ -230,97 +248,140 @@ TEST_F(InodeManagerTest, test1) {
 }
 
 TEST_F(InodeManagerTest, GetOrModifyS3ChunkInfo) {
-    google::protobuf::Map<uint64_t, S3ChunkInfoList> map4add;
     uint32_t fsId = 1;
     uint32_t inodeId = 1;
-    S3ChunkInfoList list4add = GenS3ChunkInfoList(1, 100);
-    for (int i = 0; i < 10; i++) {
-        map4add[i] = list4add;
-    }
 
     // CASE 1: GetOrModifyS3ChunkInfo() success
     {
+        LOG(INFO) << "CASE 1: GetOrModifyS3ChunkInfo() success";
+        google::protobuf::Map<uint64_t, S3ChunkInfoList> map2add;
+        google::protobuf::Map<uint64_t, S3ChunkInfoList> map2del;
+
+        map2add[1] = GenS3ChunkInfoList(1, 1);
+        map2add[2] = GenS3ChunkInfoList(2, 2);
+        map2add[3] = GenS3ChunkInfoList(3, 3);
+
         std::shared_ptr<Iterator> iterator;
         MetaStatusCode rc = manager->GetOrModifyS3ChunkInfo(
-            fsId, inodeId, map4add, &iterator, true, false);
+            fsId, inodeId, map2add, map2del, true, &iterator);
         ASSERT_EQ(rc, MetaStatusCode::OK);
 
-        size_t size = 0;
-        Key4S3ChunkInfoList key;
-        S3ChunkInfoList list4get;
-        ASSERT_EQ(iterator->Status(), 0);
-        for (iterator->SeekToFirst(); iterator->Valid(); iterator->Next()) {
-            ASSERT_TRUE(conv_->ParseFromString(iterator->Key(), &key));
-            ASSERT_TRUE(conv_->ParseFromString(iterator->Value(), &list4get));
-            ASSERT_EQ(key.chunkIndex, size);
-            ASSERT_TRUE(EqualS3ChunkInfoList(list4add, list4get));
-            size++;
-        }
-        ASSERT_EQ(size, 10);
+        CHECK_ITERATOR_S3CHUNKINFOLIST(iterator,
+            std::vector<uint64_t>{ 1, 2, 3 },
+            std::vector<S3ChunkInfoList>{
+                GenS3ChunkInfoList(1, 1),
+                GenS3ChunkInfoList(2, 2),
+                GenS3ChunkInfoList(3, 3),
+            });
+
+        LOG(INFO) << "CASE 1.1: check idempotent for GetOrModifyS3ChunkInfo()";
+        rc = manager->GetOrModifyS3ChunkInfo(
+            fsId, inodeId, map2add, map2del, true, &iterator);
+        ASSERT_EQ(rc, MetaStatusCode::OK);
+
+        CHECK_ITERATOR_S3CHUNKINFOLIST(iterator,
+            std::vector<uint64_t>{ 1, 2, 3 },
+            std::vector<S3ChunkInfoList>{
+                GenS3ChunkInfoList(1, 1),
+                GenS3ChunkInfoList(2, 2),
+                GenS3ChunkInfoList(3, 3),
+            });
     }
 
-    // CASE 2: idempotent request
+    // CASE 2: GetOrModifyS3ChunkInfo() with delete
     {
+        LOG(INFO) << "CASE 2: GetOrModifyS3ChunkInfo() with delete";
+        google::protobuf::Map<uint64_t, S3ChunkInfoList> map2add;
+        google::protobuf::Map<uint64_t, S3ChunkInfoList> map2del;
+
+        map2del[1] = GenS3ChunkInfoList(1, 1);
+        map2del[2] = GenS3ChunkInfoList(2, 2);
+        map2del[3] = GenS3ChunkInfoList(3, 3);
+
         std::shared_ptr<Iterator> iterator;
         MetaStatusCode rc = manager->GetOrModifyS3ChunkInfo(
-            fsId, inodeId, map4add, &iterator, true, false);
+            fsId, inodeId, map2add, map2del, true, &iterator);
         ASSERT_EQ(rc, MetaStatusCode::OK);
 
-        size_t size = 0;
-        Key4S3ChunkInfoList key;
-        S3ChunkInfoList list4get;
-        ASSERT_EQ(iterator->Status(), 0);
-        for (iterator->SeekToFirst(); iterator->Valid(); iterator->Next()) {
-            ASSERT_TRUE(conv_->ParseFromString(iterator->Key(), &key));
-            ASSERT_TRUE(conv_->ParseFromString(iterator->Value(), &list4get));
-            ASSERT_EQ(key.chunkIndex, size);
-            ASSERT_TRUE(EqualS3ChunkInfoList(list4add, list4get));
-            size++;
-        }
-        ASSERT_EQ(size, 10);
+        CHECK_ITERATOR_S3CHUNKINFOLIST(iterator,
+            std::vector<uint64_t>{},
+            std::vector<S3ChunkInfoList>{});
     }
 
-    // CASE 3: compaction
+    // CASE 3: GetOrModifyS3ChunkInfo() with add and delete
     {
-        map4add.clear();
-        map4add[0] = GenS3ChunkInfoList(100, 100);
-        map4add[1] = GenS3ChunkInfoList(100, 100);
-        map4add[2] = GenS3ChunkInfoList(100, 100);
-        map4add[7] = GenS3ChunkInfoList(100, 100);
-        map4add[8] = GenS3ChunkInfoList(100, 100);
-        map4add[9] = GenS3ChunkInfoList(100, 100);
+        LOG(INFO) << "CASE 3: GetOrModifyS3ChunkInfo() with add and delete";
+        google::protobuf::Map<uint64_t, S3ChunkInfoList> map2add;
+        google::protobuf::Map<uint64_t, S3ChunkInfoList> map2del;
+
+        // step1: append s3chunkinfo
+        map2add[0] = GenS3ChunkInfoList(1, 100);
+        map2add[1] = GenS3ChunkInfoList(1, 100);
+        map2add[2] = GenS3ChunkInfoList(1, 100);
+        map2add[7] = GenS3ChunkInfoList(1, 100);
+        map2add[8] = GenS3ChunkInfoList(1, 100);
+        map2add[9] = GenS3ChunkInfoList(1, 100);
 
         std::shared_ptr<Iterator> iterator;
         MetaStatusCode rc = manager->GetOrModifyS3ChunkInfo(
-            fsId, inodeId, map4add, &iterator, true, true);
+            fsId, inodeId, map2add, map2del, true, &iterator);
         ASSERT_EQ(rc, MetaStatusCode::OK);
         ASSERT_EQ(iterator->Status(), 0);
 
-        size_t size = 0;
-        Key4S3ChunkInfoList key;
-        S3ChunkInfoList list4get;
-        std::vector<S3ChunkInfoList> lists{
-            GenS3ChunkInfoList(100, 100),
-            GenS3ChunkInfoList(100, 100),
-            GenS3ChunkInfoList(100, 100),
-            GenS3ChunkInfoList(1, 100),
-            GenS3ChunkInfoList(1, 100),
-            GenS3ChunkInfoList(1, 100),
-            GenS3ChunkInfoList(1, 100),
-            GenS3ChunkInfoList(100, 100),
-            GenS3ChunkInfoList(100, 100),
-            GenS3ChunkInfoList(100, 100),
-        };
-        for (iterator->SeekToFirst(); iterator->Valid(); iterator->Next()) {
-            LOG(INFO) << "check chunkIndex(" << size << ")"
-                      << ", key=" << iterator->Key();
-            ASSERT_TRUE(conv_->ParseFromString(iterator->Key(), &key));
-            ASSERT_TRUE(conv_->ParseFromString(iterator->Value(), &list4get));
-            ASSERT_EQ(key.chunkIndex, size);
-            ASSERT_TRUE(EqualS3ChunkInfoList(lists[size], list4get));
-            size++;
-        }
-        ASSERT_EQ(size, 10);
+        // step2: delete s3chunkinfo
+        map2add.clear();
+        map2del.clear();
+
+        map2add[0] = GenS3ChunkInfoList(100, 100);
+        map2add[7] = GenS3ChunkInfoList(100, 100);
+        map2add[8] = GenS3ChunkInfoList(100, 100);
+        map2add[9] = GenS3ChunkInfoList(100, 100);
+
+        map2del[0] = GenS3ChunkInfoList(1, 100);
+        map2del[7] = GenS3ChunkInfoList(1, 100);
+        map2del[8] = GenS3ChunkInfoList(1, 100);
+        map2del[9] = GenS3ChunkInfoList(1, 100);
+
+        rc = manager->GetOrModifyS3ChunkInfo(
+            fsId, inodeId, map2add, map2del, true, &iterator);
+        ASSERT_EQ(rc, MetaStatusCode::OK);
+        ASSERT_EQ(iterator->Status(), 0);
+
+        CHECK_ITERATOR_S3CHUNKINFOLIST(iterator,
+            std::vector<uint64_t>{ 0, 1, 2, 7, 8, 9 },
+            std::vector<S3ChunkInfoList>{
+                GenS3ChunkInfoList(100, 100),
+                GenS3ChunkInfoList(1, 100),
+                GenS3ChunkInfoList(1, 100),
+                GenS3ChunkInfoList(100, 100),
+                GenS3ChunkInfoList(100, 100),
+                GenS3ChunkInfoList(100, 100),
+            });
+
+        // step3: delete all s3chunkinfo
+        map2add.clear();
+        map2del.clear();
+        map2add[1] = GenS3ChunkInfoList(100, 100);
+        map2add[2] = GenS3ChunkInfoList(100, 100);
+
+        map2del[1] = GenS3ChunkInfoList(1, 100);
+        map2del[2] = GenS3ChunkInfoList(1, 100);
+
+        rc = manager->GetOrModifyS3ChunkInfo(
+            fsId, inodeId, map2add, map2del, true, &iterator);
+        ASSERT_EQ(rc, MetaStatusCode::OK);
+        ASSERT_EQ(iterator->Status(), 0);
+
+        CHECK_ITERATOR_S3CHUNKINFOLIST(iterator,
+            std::vector<uint64_t>{ 0, 1, 2, 7, 8, 9 },
+            std::vector<S3ChunkInfoList>{
+                GenS3ChunkInfoList(100, 100),
+                GenS3ChunkInfoList(100, 100),
+                GenS3ChunkInfoList(100, 100),
+                GenS3ChunkInfoList(100, 100),
+                GenS3ChunkInfoList(100, 100),
+                GenS3ChunkInfoList(100, 100),
+            });
     }
 }
 
@@ -394,9 +455,8 @@ TEST_F(InodeManagerTest, testGetXAttr) {
 
     Inode inode2;
     param_.type = FsFileType::TYPE_DIRECTORY;
-    ASSERT_EQ(
-        manager->CreateInode(3, param_, &inode2),
-        MetaStatusCode::OK);
+    ASSERT_EQ(manager->CreateInode(3, param_, &inode2),
+              MetaStatusCode::OK);
     ASSERT_FALSE(inode2.xattr().empty());
     ASSERT_EQ(inode2.xattr().find(XATTRFILES)->second, "0");
     ASSERT_EQ(inode2.xattr().find(XATTRSUBDIRS)->second, "0");
diff --git a/curvefs/test/metaserver/inode_storage_test.cpp b/curvefs/test/metaserver/inode_storage_test.cpp
index 5d432509dc..82a4770c20 100644
--- a/curvefs/test/metaserver/inode_storage_test.cpp
+++ b/curvefs/test/metaserver/inode_storage_test.cpp
@@ -315,16 +315,17 @@ TEST_F(InodeStorageTest, testGetXAttr) {
     ASSERT_EQ(xattr.xattrinfos().find(XATTRRFBYTES)->second, "1000");
 }
 
-TEST_F(InodeStorageTest, GetOrModifyS3ChunkInfo) {
+TEST_F(InodeStorageTest, ModifyInodeS3ChunkInfoList) {
     uint32_t fsId = 1;
     uint64_t inodeId = 1;
     InodeStorage storage(kvStorage_, tablename_);
 
-    // CASE 1: empty s3chunkinfo
+    // CASE 1: get empty s3chunkinfo
     {
-        LOG(INFO) << "CASE 1:";
+        LOG(INFO) << "CASE 1: get empty s3chukninfo";
         Inode inode = GenInode(fsId, inodeId);
         ASSERT_EQ(storage.Insert(inode), MetaStatusCode::OK);
+        S3ChunkInfoList list2del;
 
         size_t size = 0;
         auto iterator = storage.GetInodeS3ChunkInfoList(fsId, inodeId);
@@ -337,56 +338,56 @@ TEST_F(InodeStorageTest, GetOrModifyS3ChunkInfo) {
 
     // CASE 2: append one s3chunkinfo
     {
-        LOG(INFO) << "CASE 2:";
+        LOG(INFO) << "CASE 2: append one s3chunkinfo";
         ASSERT_EQ(storage.Clear(), MetaStatusCode::OK);
         std::vector<uint64_t> chunkIndexs{ 1 };
-        std::vector<S3ChunkInfoList> lists{ GenS3ChunkInfoList(1, 1) };
+        std::vector<S3ChunkInfoList> lists2add{ GenS3ChunkInfoList(1, 1) };
 
-        for (size_t size = 0; size < chunkIndexs.size(); size++) {
-            MetaStatusCode rc = storage.AppendS3ChunkInfoList(
-                fsId, inodeId, chunkIndexs[size], lists[size], false);
+        for (size_t i = 0; i < chunkIndexs.size(); i++) {
+            MetaStatusCode rc = storage.ModifyInodeS3ChunkInfoList(
+                fsId, inodeId, chunkIndexs[i], &lists2add[i], nullptr);
             ASSERT_EQ(rc, MetaStatusCode::OK);
         }
 
-        CHECK_INODE_S3CHUNKINFOLIST(
-            &storage, fsId, inodeId, chunkIndexs, lists);
+        CHECK_INODE_S3CHUNKINFOLIST(&storage, fsId, inodeId,
+                                    chunkIndexs, lists2add);
     }
 
     // CASE 3: append multi s3chunkinfos
     {
-        LOG(INFO) << "CASE 3:";
+        LOG(INFO) << "CASE 3: append multi s3chunkinfos";
         ASSERT_EQ(storage.Clear(), MetaStatusCode::OK);
         std::vector<uint64_t> chunkIndexs{ 1, 2, 3 };
-        std::vector<S3ChunkInfoList> lists{
+        std::vector<S3ChunkInfoList> lists2add{
             GenS3ChunkInfoList(1, 1),
             GenS3ChunkInfoList(2, 2),
             GenS3ChunkInfoList(3, 3),
         };
 
-        for (size_t size = 0; size < chunkIndexs.size(); size++) {
-            MetaStatusCode rc = storage.AppendS3ChunkInfoList(
-                fsId, inodeId, chunkIndexs[size], lists[size], false);
+        for (size_t i = 0; i < chunkIndexs.size(); i++) {
+            MetaStatusCode rc = storage.ModifyInodeS3ChunkInfoList(
+                fsId, inodeId, chunkIndexs[i], &lists2add[i], nullptr);
             ASSERT_EQ(rc, MetaStatusCode::OK);
         }
 
-        CHECK_INODE_S3CHUNKINFOLIST(
-            &storage, fsId, inodeId, chunkIndexs, lists);
+        CHECK_INODE_S3CHUNKINFOLIST(&storage, fsId, inodeId,
+                                    chunkIndexs, lists2add);
     }
 
     // CASE 4: check order for s3chunkinfo's chunk index
     {
-        LOG(INFO) << "CASE 4:";
+        LOG(INFO) << "CASE 4: check order for s3chunkinfo's chunk index";
         ASSERT_EQ(storage.Clear(), MetaStatusCode::OK);
         std::vector<uint64_t> chunkIndexs{ 2, 1, 3 };
-        std::vector<S3ChunkInfoList> lists{
+        std::vector<S3ChunkInfoList> lists2add{
             GenS3ChunkInfoList(2, 2),
             GenS3ChunkInfoList(1, 1),
             GenS3ChunkInfoList(3, 3),
         };
 
-        for (size_t size = 0; size < chunkIndexs.size(); size++) {
-            MetaStatusCode rc = storage.AppendS3ChunkInfoList(
-                fsId, inodeId, chunkIndexs[size], lists[size], false);
+        for (size_t i = 0; i < chunkIndexs.size(); i++) {
+            MetaStatusCode rc = storage.ModifyInodeS3ChunkInfoList(
+                fsId, inodeId, chunkIndexs[i], &lists2add[i], nullptr);
             ASSERT_EQ(rc, MetaStatusCode::OK);
         }
 
@@ -401,10 +402,10 @@ TEST_F(InodeStorageTest, GetOrModifyS3ChunkInfo) {
 
     // CASE 5: check order for s3chunkinfo's chunk id
     {
-        LOG(INFO) << "CASE 5:";
+        LOG(INFO) << "CASE 5: check order for s3chunkinfo's chunk id";
         ASSERT_EQ(storage.Clear(), MetaStatusCode::OK);
         std::vector<uint64_t> chunkIndexs{ 2, 1, 3, 1, 2 };
-        std::vector<S3ChunkInfoList> lists{
+        std::vector<S3ChunkInfoList> lists2add{
             GenS3ChunkInfoList(200, 210),
             GenS3ChunkInfoList(120, 130),
             GenS3ChunkInfoList(300, 310),
@@ -412,9 +413,9 @@ TEST_F(InodeStorageTest, GetOrModifyS3ChunkInfo) {
             GenS3ChunkInfoList(220, 230),
         };
 
-        for (size_t size = 0; size < chunkIndexs.size(); size++) {
-            MetaStatusCode rc = storage.AppendS3ChunkInfoList(
-                fsId, inodeId, chunkIndexs[size], lists[size], false);
+        for (size_t i = 0; i < chunkIndexs.size(); i++) {
+            MetaStatusCode rc = storage.ModifyInodeS3ChunkInfoList(
+                fsId, inodeId, chunkIndexs[i], &lists2add[i], nullptr);
             ASSERT_EQ(rc, MetaStatusCode::OK);
         }
 
@@ -428,6 +429,179 @@ TEST_F(InodeStorageTest, GetOrModifyS3ChunkInfo) {
                 GenS3ChunkInfoList(300, 310),
             });
     }
+
+
+    // CASE 6: delete s3chunkinfo list with wrong range
+    {
+        LOG(INFO) << "CASE 6: delete s3chukninfo list with wrong range";
+        ASSERT_EQ(storage.Clear(), MetaStatusCode::OK);
+
+        // step1: add s3chunkinfo
+        {
+            std::vector<uint64_t> chunkIndexs{ 1, 2, 3 };
+            std::vector<S3ChunkInfoList> lists2add{
+                GenS3ChunkInfoList(100, 199),
+                GenS3ChunkInfoList(200, 299),
+                GenS3ChunkInfoList(300, 399),
+            };
+
+            for (size_t i = 0; i < chunkIndexs.size(); i++) {
+                MetaStatusCode rc = storage.ModifyInodeS3ChunkInfoList(
+                    fsId, inodeId, chunkIndexs[i], &lists2add[i], nullptr);
+                ASSERT_EQ(rc, MetaStatusCode::OK);
+            }
+        }
+
+        // step2: compaction
+        {
+            std::vector<uint64_t> chunkIndexs{ 1, 2, 3 };
+            std::vector<S3ChunkInfoList> lists2add{
+                GenS3ChunkInfoList(199, 199),
+                GenS3ChunkInfoList(299, 299),
+                GenS3ChunkInfoList(399, 399),
+            };
+
+            std::vector<S3ChunkInfoList> lists2del{
+                GenS3ChunkInfoList(200, 201),
+                GenS3ChunkInfoList(250, 299),
+                GenS3ChunkInfoList(250, 301),
+            };
+
+            for (size_t i = 0; i < chunkIndexs.size(); i++) {
+                MetaStatusCode rc = storage.ModifyInodeS3ChunkInfoList(
+                    fsId, inodeId, chunkIndexs[i],
+                    &lists2add[i], &lists2del[i]);
+                ASSERT_EQ(rc, MetaStatusCode::STORAGE_INTERNAL_ERROR);
+            }
+        }
+    }
+
+    // CASE 7: delete all s3chunkinfo list
+    {
+        LOG(INFO) << "CASE 7: delete all s3chukninfo list";
+        ASSERT_EQ(storage.Clear(), MetaStatusCode::OK);
+
+        // step1: add s3chunkinfo
+        {
+            std::vector<uint64_t> chunkIndexs{ 1, 2, 3 };
+            std::vector<S3ChunkInfoList> lists2add{
+                GenS3ChunkInfoList(100, 199),
+                GenS3ChunkInfoList(200, 299),
+                GenS3ChunkInfoList(300, 399),
+            };
+
+            for (size_t i = 0; i < chunkIndexs.size(); i++) {
+                MetaStatusCode rc = storage.ModifyInodeS3ChunkInfoList(
+                    fsId, inodeId, chunkIndexs[i], &lists2add[i], nullptr);
+                ASSERT_EQ(rc, MetaStatusCode::OK);
+            }
+        }
+
+        // step2: compaction
+        {
+            std::vector<uint64_t> chunkIndexs{ 1, 2, 3 };
+            std::vector<S3ChunkInfoList> lists2add{
+                GenS3ChunkInfoList(199, 199),
+                GenS3ChunkInfoList(299, 299),
+                GenS3ChunkInfoList(399, 399),
+            };
+
+            std::vector<S3ChunkInfoList> lists2del{
+                GenS3ChunkInfoList(100, 199),
+                GenS3ChunkInfoList(200, 299),
+                GenS3ChunkInfoList(300, 399),
+            };
+
+            for (size_t i = 0; i < chunkIndexs.size(); i++) {
+                MetaStatusCode rc = storage.ModifyInodeS3ChunkInfoList(
+                    fsId, inodeId, chunkIndexs[i],
+                    &lists2add[i], &lists2del[i]);
+                ASSERT_EQ(rc, MetaStatusCode::OK);
+            }
+        }
+
+        // step3: check result
+        CHECK_INODE_S3CHUNKINFOLIST(&storage, fsId, inodeId,
+            std::vector<uint64_t>{ 1, 2, 3 },
+            std::vector<S3ChunkInfoList>{
+                GenS3ChunkInfoList(199, 199),
+                GenS3ChunkInfoList(299, 299),
+                GenS3ChunkInfoList(399, 399),
+        });
+    }
+
+
+    // CASE 8: delete s3chunkinfo list with prefix range
+    {
+        LOG(INFO) << "CASE 2: delete s3chunkinfo list with prefix range";
+        ASSERT_EQ(storage.Clear(), MetaStatusCode::OK);
+
+        // step1: add s3chunkinfo
+        {
+            std::vector<uint64_t> chunkIndexs{ 1, 2, 3 };
+            std::vector<S3ChunkInfoList> lists2add{
+                GenS3ChunkInfoList(100, 199),
+                GenS3ChunkInfoList(1000, 1999),
+                GenS3ChunkInfoList(10000, 19999),
+            };
+
+            for (size_t i = 0; i < chunkIndexs.size(); i++) {
+                MetaStatusCode rc = storage.ModifyInodeS3ChunkInfoList(
+                    fsId, inodeId, chunkIndexs[i], &lists2add[i], nullptr);
+                ASSERT_EQ(rc, MetaStatusCode::OK);
+            }
+        }
+
+        // step2: add s3chunkinfo again
+        {
+            std::vector<uint64_t> chunkIndexs{ 1, 2, 3 };
+            std::vector<S3ChunkInfoList> lists2add{
+                GenS3ChunkInfoList(200, 299),
+                GenS3ChunkInfoList(2000, 2999),
+                GenS3ChunkInfoList(20000, 29999),
+            };
+
+            for (size_t i = 0; i < chunkIndexs.size(); i++) {
+                MetaStatusCode rc = storage.ModifyInodeS3ChunkInfoList(
+                    fsId, inodeId, chunkIndexs[i], &lists2add[i], nullptr);
+                ASSERT_EQ(rc, MetaStatusCode::OK);
+            }
+        }
+
+        // step2: compaction
+        {
+            std::vector<uint64_t> chunkIndexs{ 1, 2, 3 };
+            std::vector<S3ChunkInfoList> lists2add{
+                GenS3ChunkInfoList(199, 199),
+                GenS3ChunkInfoList(2999, 2999),
+                GenS3ChunkInfoList(19999, 19999),
+            };
+
+            std::vector<S3ChunkInfoList> lists2del{
+                GenS3ChunkInfoList(100, 199),
+                GenS3ChunkInfoList(1000, 2999),
+                GenS3ChunkInfoList(10000, 19999),
+            };
+
+            for (size_t i = 0; i < chunkIndexs.size(); i++) {
+                MetaStatusCode rc = storage.ModifyInodeS3ChunkInfoList(
+                    fsId, inodeId, chunkIndexs[i],
+                    &lists2add[i], &lists2del[i]);
+                ASSERT_EQ(rc, MetaStatusCode::OK);
+            }
+        }
+
+        // step3: check result
+        CHECK_INODE_S3CHUNKINFOLIST(&storage, fsId, inodeId,
+            std::vector<uint64_t>{ 1, 1, 2, 3, 3 },
+            std::vector<S3ChunkInfoList>{
+                GenS3ChunkInfoList(199, 199),
+                GenS3ChunkInfoList(200, 299),
+                GenS3ChunkInfoList(2999, 2999),
+                GenS3ChunkInfoList(19999, 19999),
+                GenS3ChunkInfoList(20000, 29999),
+        });
+    }
 }
 
 TEST_F(InodeStorageTest, PaddingInodeS3ChunkInfo) {
@@ -451,8 +625,8 @@ TEST_F(InodeStorageTest, PaddingInodeS3ChunkInfo) {
     };
 
     for (size_t i = 0; i < chunkIndexs.size(); i++) {
-        MetaStatusCode rc = storage.AppendS3ChunkInfoList(
-            fsId, inodeId, chunkIndexs[i], lists2add[i], false);
+        MetaStatusCode rc = storage.ModifyInodeS3ChunkInfoList(
+            fsId, inodeId, chunkIndexs[i], &lists2add[i], nullptr);
         ASSERT_EQ(rc, MetaStatusCode::OK);
     }
     ASSERT_EQ(inode.mutable_s3chunkinfomap()->size(), 0);
@@ -503,15 +677,15 @@ TEST_F(InodeStorageTest, PaddingInodeS3ChunkInfo) {
 TEST_F(InodeStorageTest, GetAllS3ChunkInfoList) {
     InodeStorage storage(kvStorage_, tablename_);
     uint64_t chunkIndex = 1;
-    S3ChunkInfoList list4add = GenS3ChunkInfoList(1, 10);
+    S3ChunkInfoList list2add = GenS3ChunkInfoList(1, 10);
 
     // step1: prepare inode and its s3chunkinfo
     auto prepareInode = [&](uint32_t fsId, uint64_t inodeId) {
         Inode inode = GenInode(fsId, inodeId);
         ASSERT_EQ(storage.Insert(inode), MetaStatusCode::OK);
 
-        MetaStatusCode rc = storage.AppendS3ChunkInfoList(
-            fsId, inodeId, chunkIndex, list4add, false);
+        MetaStatusCode rc = storage.ModifyInodeS3ChunkInfoList(
+            fsId, inodeId, chunkIndex, &list2add, nullptr);
         ASSERT_EQ(rc, MetaStatusCode::OK);
     };
 
@@ -531,7 +705,7 @@ TEST_F(InodeStorageTest, GetAllS3ChunkInfoList) {
         ASSERT_EQ(key.chunkIndex, chunkIndex);
         ASSERT_EQ(key.fsId, fsIds[size]);
         ASSERT_EQ(key.inodeId, inodeIds[size]);
-        ASSERT_TRUE(EqualS3ChunkInfoList(list4add, list4get));
+        ASSERT_TRUE(EqualS3ChunkInfoList(list2add, list4get));
         size++;
     }
     ASSERT_EQ(size, 2);
diff --git a/curvefs/test/metaserver/s3compactwq_test.cpp b/curvefs/test/metaserver/s3compactwq_test.cpp
index 510cc62b03..46e1bef9d8 100644
--- a/curvefs/test/metaserver/s3compactwq_test.cpp
+++ b/curvefs/test/metaserver/s3compactwq_test.cpp
@@ -656,8 +656,8 @@ TEST_F(S3CompactWorkQueueImplTest, test_CompactChunks) {
         ref->set_size(5);
         ref->set_zero(false);
     }
-    auto rc = inodeStorage_->AppendS3ChunkInfoList(
-        inode1.fsid(), inode1.inodeid(), 0, l0, false);
+    auto rc = inodeStorage_->ModifyInodeS3ChunkInfoList(
+        inode1.fsid(), inode1.inodeid(), 0, &l0, nullptr);
     ASSERT_EQ(rc, MetaStatusCode::OK);
     ASSERT_EQ(inodeStorage_->Update(inode1), MetaStatusCode::OK);
     mockImpl_->CompactChunks(t);