|
20 | 20 | import java.util.Arrays; |
21 | 21 | import java.util.List; |
22 | 22 | import java.util.Objects; |
| 23 | +import java.util.concurrent.ConcurrentHashMap; |
| 24 | +import java.util.concurrent.ConcurrentMap; |
| 25 | +import java.util.concurrent.locks.Lock; |
| 26 | +import java.util.concurrent.locks.ReentrantLock; |
23 | 27 |
|
24 | 28 | import javax.inject.Inject; |
25 | 29 |
|
| 30 | +import com.cloud.utils.db.TransactionCallback; |
26 | 31 | import org.apache.cloudstack.engine.subsystem.api.storage.DataStore; |
27 | 32 | import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreManager; |
28 | 33 | import org.apache.cloudstack.engine.subsystem.api.storage.ObjectInDataStoreStateMachine.Event; |
@@ -101,6 +106,13 @@ public class DefaultSnapshotStrategy extends SnapshotStrategyBase { |
101 | 106 |
|
102 | 107 | private final List<Snapshot.State> snapshotStatesAbleToDeleteSnapshot = Arrays.asList(Snapshot.State.Destroying, Snapshot.State.Destroyed, Snapshot.State.Error, Snapshot.State.Hidden); |
103 | 108 |
|
| 109 | + private ConcurrentMap<Long, Lock> locks = new ConcurrentHashMap<>(); |
| 110 | + |
| 111 | + private Lock getLock(Long id) { |
| 112 | + locks.putIfAbsent(id, new ReentrantLock()); |
| 113 | + return locks.get(id); |
| 114 | + } |
| 115 | + |
104 | 116 | public SnapshotDataStoreVO getSnapshotImageStoreRef(long snapshotId, long zoneId) { |
105 | 117 | List<SnapshotDataStoreVO> snaps = snapshotStoreDao.listReadyBySnapshot(snapshotId, DataStoreRole.Image); |
106 | 118 | for (SnapshotDataStoreVO ref : snaps) { |
@@ -187,6 +199,8 @@ public SnapshotInfo backupSnapshot(SnapshotInfo snapshot) { |
187 | 199 | } |
188 | 200 |
|
189 | 201 | protected boolean deleteSnapshotChain(SnapshotInfo snapshot, String storageToString) { |
| 202 | + long rootSnapshotId = getRootSnapshotId(snapshot); |
| 203 | + snapshotDao.acquireInLockTable(rootSnapshotId); |
190 | 204 | DataTO snapshotTo = snapshot.getTO(); |
191 | 205 | logger.debug(String.format("Deleting %s chain of snapshots.", snapshotTo)); |
192 | 206 |
|
@@ -248,9 +262,17 @@ protected boolean deleteSnapshotChain(SnapshotInfo snapshot, String storageToStr |
248 | 262 | } catch (Exception e) { |
249 | 263 | logger.error(String.format("Failed to delete snapshot [%s] on storage [%s] due to [%s].", snapshotTo, storageToString, e.getMessage()), e); |
250 | 264 | } |
| 265 | + snapshotDao.releaseFromLockTable(rootSnapshotId); |
251 | 266 | return result; |
252 | 267 | } |
253 | 268 |
|
| 269 | + private Long getRootSnapshotId(SnapshotInfo snapshotInfo) { |
| 270 | + while (snapshotInfo.getParent() != null) { |
| 271 | + snapshotInfo = snapshotInfo.getParent(); |
| 272 | + } |
| 273 | + return snapshotInfo.getSnapshotId(); |
| 274 | + } |
| 275 | + |
254 | 276 | @Override |
255 | 277 | public boolean deleteSnapshot(Long snapshotId, Long zoneId) { |
256 | 278 | SnapshotVO snapshotVO = snapshotDao.findById(snapshotId); |
@@ -392,7 +414,7 @@ protected Boolean deleteSnapshotInfo(SnapshotInfo snapshotInfo, SnapshotVO snaps |
392 | 414 | snapshotObject.processEvent(Snapshot.Event.DestroyRequested); |
393 | 415 | } |
394 | 416 | verifyIfTheSnapshotIsBeingUsedByAnyVolume(snapshotObject); |
395 | | - if (deleteSnapshotChain(snapshotInfo, storageToString)) { |
| 417 | + if (Transaction.execute((TransactionCallback<Boolean>) status -> deleteSnapshotChain(snapshotInfo, storageToString))) { |
396 | 418 | logger.debug(String.format("%s was deleted on %s. We will mark the snapshot as destroyed.", snapshotVo, storageToString)); |
397 | 419 | } else { |
398 | 420 | logger.debug(String.format("%s was not deleted on %s; however, we will mark the snapshot as hidden for future garbage collecting.", snapshotVo, |
|
0 commit comments