diff --git a/engine/storage/src/main/java/org/apache/cloudstack/storage/image/db/SnapshotDataStoreDaoImpl.java b/engine/storage/src/main/java/org/apache/cloudstack/storage/image/db/SnapshotDataStoreDaoImpl.java index fa6c8d1f606..c3cfe89d491 100644 --- a/engine/storage/src/main/java/org/apache/cloudstack/storage/image/db/SnapshotDataStoreDaoImpl.java +++ b/engine/storage/src/main/java/org/apache/cloudstack/storage/image/db/SnapshotDataStoreDaoImpl.java @@ -19,7 +19,6 @@ package org.apache.cloudstack.storage.image.db; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; -import java.util.ArrayList; import java.util.Date; import java.util.List; import java.util.Map; @@ -33,6 +32,7 @@ import org.apache.cloudstack.engine.subsystem.api.storage.ObjectInDataStoreState import org.apache.cloudstack.engine.subsystem.api.storage.ObjectInDataStoreStateMachine.State; import org.apache.cloudstack.storage.datastore.db.SnapshotDataStoreDao; import org.apache.cloudstack.storage.datastore.db.SnapshotDataStoreVO; +import org.apache.commons.collections.CollectionUtils; import org.apache.log4j.Logger; import org.springframework.stereotype.Component; @@ -45,130 +45,84 @@ import com.cloud.utils.db.Filter; import com.cloud.utils.db.GenericDaoBase; import com.cloud.utils.db.SearchBuilder; import com.cloud.utils.db.SearchCriteria; -import com.cloud.utils.db.SearchCriteria.Op; import com.cloud.utils.db.TransactionLegacy; import com.cloud.utils.db.UpdateBuilder; @Component public class SnapshotDataStoreDaoImpl extends GenericDaoBase implements SnapshotDataStoreDao { private static final Logger s_logger = Logger.getLogger(SnapshotDataStoreDaoImpl.class); - private SearchBuilder updateStateSearch; - private SearchBuilder storeSearch; - private SearchBuilder storeStateSearch; - private SearchBuilder destroyedSearch; - private SearchBuilder cacheSearch; - private SearchBuilder storeSnapshotSearch; - private SearchBuilder snapshotIdSearch; - private SearchBuilder volumeIdSearch; - private SearchBuilder volumeIdAndStateReadySearch; - private SearchBuilder volumeSearch; + private static final String STORE_ID = "store_id"; + private static final String STORE_ROLE = "store_role"; + private static final String STATE = "state"; + private static final String REF_CNT = "ref_cnt"; + private static final String ID = "id"; + private static final String UPDATED_COUNT = "updatedCount"; + private static final String SNAPSHOT_ID = "snapshot_id"; + private static final String VOLUME_ID = "volume_id"; + private static final String CREATED = "created"; + private SearchBuilder searchFilteringStoreIdEqStoreRoleEqStateNeqRefCntNeq; + protected SearchBuilder searchFilteringStoreIdEqStateEqStoreRoleEqIdEqUpdateCountEqSnapshotIdEqVolumeIdEq; private SearchBuilder stateSearch; - private SearchBuilder parentSnapshotSearch; - private SearchBuilder snapshotVOSearch; + protected SearchBuilder snapshotVOSearch; private SearchBuilder snapshotCreatedSearch; - protected SearchBuilder snapshotSearch; - public static ArrayList hypervisorsSupportingSnapshotsChaining = new ArrayList(); + protected static final List HYPERVISORS_SUPPORTING_SNAPSHOTS_CHAINING = List.of(Hypervisor.HypervisorType.XenServer); @Inject - private SnapshotDao _snapshotDao; + protected SnapshotDao snapshotDao; - private final String findLatestSnapshot = "select store_id, store_role, snapshot_id from cloud.snapshot_store_ref where " + + private static final String FIND_OLDEST_OR_LATEST_SNAPSHOT = "select store_id, store_role, snapshot_id from cloud.snapshot_store_ref where " + " store_role = ? and volume_id = ? and state = 'Ready'" + - " order by created DESC " + - " limit 1"; - private final String findOldestSnapshot = "select store_id, store_role, snapshot_id from cloud.snapshot_store_ref where " + - " store_role = ? and volume_id = ? and state = 'Ready'" + - " order by created ASC " + + " order by created %s " + " limit 1"; @Override public boolean configure(String name, Map params) throws ConfigurationException { super.configure(name, params); - // Note that snapshot_store_ref stores snapshots on primary as well as - // those on secondary, so we need to - // use (store_id, store_role) to search - storeSearch = createSearchBuilder(); - storeSearch.and("store_id", storeSearch.entity().getDataStoreId(), SearchCriteria.Op.EQ); - storeSearch.and("store_role", storeSearch.entity().getRole(), SearchCriteria.Op.EQ); - storeSearch.and("state", storeSearch.entity().getState(), SearchCriteria.Op.NEQ); - storeSearch.done(); + searchFilteringStoreIdEqStoreRoleEqStateNeqRefCntNeq = createSearchBuilder(); + searchFilteringStoreIdEqStoreRoleEqStateNeqRefCntNeq.and(STORE_ID, searchFilteringStoreIdEqStoreRoleEqStateNeqRefCntNeq.entity().getDataStoreId(), SearchCriteria.Op.EQ); + searchFilteringStoreIdEqStoreRoleEqStateNeqRefCntNeq.and(STORE_ROLE, searchFilteringStoreIdEqStoreRoleEqStateNeqRefCntNeq.entity().getRole(), SearchCriteria.Op.EQ); + searchFilteringStoreIdEqStoreRoleEqStateNeqRefCntNeq.and(STATE, searchFilteringStoreIdEqStoreRoleEqStateNeqRefCntNeq.entity().getState(), SearchCriteria.Op.NEQ); + searchFilteringStoreIdEqStoreRoleEqStateNeqRefCntNeq.and(REF_CNT, searchFilteringStoreIdEqStoreRoleEqStateNeqRefCntNeq.entity().getRefCnt(), SearchCriteria.Op.NEQ); + searchFilteringStoreIdEqStoreRoleEqStateNeqRefCntNeq.done(); - storeStateSearch = createSearchBuilder(); - storeStateSearch.and("store_id", storeStateSearch.entity().getDataStoreId(), SearchCriteria.Op.EQ); - storeStateSearch.and("state", storeStateSearch.entity().getState(), SearchCriteria.Op.EQ); - storeStateSearch.done(); + searchFilteringStoreIdEqStateEqStoreRoleEqIdEqUpdateCountEqSnapshotIdEqVolumeIdEq = createSearchBuilder(); + searchFilteringStoreIdEqStateEqStoreRoleEqIdEqUpdateCountEqSnapshotIdEqVolumeIdEq.and(STORE_ID, + searchFilteringStoreIdEqStateEqStoreRoleEqIdEqUpdateCountEqSnapshotIdEqVolumeIdEq.entity().getDataStoreId(), SearchCriteria.Op.EQ); - destroyedSearch = createSearchBuilder(); - destroyedSearch.and("store_id", destroyedSearch.entity().getDataStoreId(), SearchCriteria.Op.EQ); - destroyedSearch.and("store_role", destroyedSearch.entity().getRole(), SearchCriteria.Op.EQ); - destroyedSearch.and("state", destroyedSearch.entity().getState(), SearchCriteria.Op.EQ); - destroyedSearch.done(); + searchFilteringStoreIdEqStateEqStoreRoleEqIdEqUpdateCountEqSnapshotIdEqVolumeIdEq.and(STATE, + searchFilteringStoreIdEqStateEqStoreRoleEqIdEqUpdateCountEqSnapshotIdEqVolumeIdEq.entity().getState(), SearchCriteria.Op.EQ); - cacheSearch = createSearchBuilder(); - cacheSearch.and("store_id", cacheSearch.entity().getDataStoreId(), SearchCriteria.Op.EQ); - cacheSearch.and("store_role", cacheSearch.entity().getRole(), SearchCriteria.Op.EQ); - cacheSearch.and("state", cacheSearch.entity().getState(), SearchCriteria.Op.NEQ); - cacheSearch.and("ref_cnt", cacheSearch.entity().getRefCnt(), SearchCriteria.Op.NEQ); - cacheSearch.done(); + searchFilteringStoreIdEqStateEqStoreRoleEqIdEqUpdateCountEqSnapshotIdEqVolumeIdEq.and(STORE_ROLE, + searchFilteringStoreIdEqStateEqStoreRoleEqIdEqUpdateCountEqSnapshotIdEqVolumeIdEq.entity().getRole(), SearchCriteria.Op.EQ); - updateStateSearch = this.createSearchBuilder(); - updateStateSearch.and("id", updateStateSearch.entity().getId(), Op.EQ); - updateStateSearch.and("state", updateStateSearch.entity().getState(), Op.EQ); - updateStateSearch.and("updatedCount", updateStateSearch.entity().getUpdatedCount(), Op.EQ); - updateStateSearch.done(); + searchFilteringStoreIdEqStateEqStoreRoleEqIdEqUpdateCountEqSnapshotIdEqVolumeIdEq.and(ID, + searchFilteringStoreIdEqStateEqStoreRoleEqIdEqUpdateCountEqSnapshotIdEqVolumeIdEq.entity().getId(), SearchCriteria.Op.EQ); - snapshotSearch = createSearchBuilder(); - snapshotSearch.and("snapshot_id", snapshotSearch.entity().getSnapshotId(), SearchCriteria.Op.EQ); - snapshotSearch.and("store_role", snapshotSearch.entity().getRole(), SearchCriteria.Op.EQ); - snapshotSearch.and("state", snapshotSearch.entity().getState(), SearchCriteria.Op.EQ); - snapshotSearch.done(); + searchFilteringStoreIdEqStateEqStoreRoleEqIdEqUpdateCountEqSnapshotIdEqVolumeIdEq.and(UPDATED_COUNT, + searchFilteringStoreIdEqStateEqStoreRoleEqIdEqUpdateCountEqSnapshotIdEqVolumeIdEq.entity().getUpdatedCount(), SearchCriteria.Op.EQ); - storeSnapshotSearch = createSearchBuilder(); - storeSnapshotSearch.and("snapshot_id", storeSnapshotSearch.entity().getSnapshotId(), SearchCriteria.Op.EQ); - storeSnapshotSearch.and("store_id", storeSnapshotSearch.entity().getDataStoreId(), SearchCriteria.Op.EQ); - storeSnapshotSearch.and("store_role", storeSnapshotSearch.entity().getRole(), SearchCriteria.Op.EQ); - storeSnapshotSearch.and("state", storeSnapshotSearch.entity().getState(), SearchCriteria.Op.EQ); - storeSnapshotSearch.done(); + searchFilteringStoreIdEqStateEqStoreRoleEqIdEqUpdateCountEqSnapshotIdEqVolumeIdEq.and(SNAPSHOT_ID, + searchFilteringStoreIdEqStateEqStoreRoleEqIdEqUpdateCountEqSnapshotIdEqVolumeIdEq.entity().getSnapshotId(), SearchCriteria.Op.EQ); - snapshotIdSearch = createSearchBuilder(); - snapshotIdSearch.and("snapshot_id", snapshotIdSearch.entity().getSnapshotId(), SearchCriteria.Op.EQ); - snapshotIdSearch.done(); + searchFilteringStoreIdEqStateEqStoreRoleEqIdEqUpdateCountEqSnapshotIdEqVolumeIdEq.and(VOLUME_ID, + searchFilteringStoreIdEqStateEqStoreRoleEqIdEqUpdateCountEqSnapshotIdEqVolumeIdEq.entity().getVolumeId(), SearchCriteria.Op.EQ); - volumeIdSearch = createSearchBuilder(); - volumeIdSearch.and("volume_id", volumeIdSearch.entity().getVolumeId(), SearchCriteria.Op.EQ); - volumeIdSearch.done(); + searchFilteringStoreIdEqStateEqStoreRoleEqIdEqUpdateCountEqSnapshotIdEqVolumeIdEq.done(); - volumeSearch = createSearchBuilder(); - volumeSearch.and("volume_id", volumeSearch.entity().getVolumeId(), SearchCriteria.Op.EQ); - volumeSearch.and("store_role", volumeSearch.entity().getRole(), SearchCriteria.Op.EQ); - volumeSearch.and("snapshot_id", volumeSearch.entity().getSnapshotId(), SearchCriteria.Op.EQ); - volumeSearch.done(); - - volumeIdAndStateReadySearch = createSearchBuilder(); - volumeIdAndStateReadySearch.and("volume_id", volumeIdAndStateReadySearch.entity().getVolumeId(), SearchCriteria.Op.EQ); - volumeIdAndStateReadySearch.and("state", volumeIdAndStateReadySearch.entity().getState(), SearchCriteria.Op.EQ); - volumeIdAndStateReadySearch.done(); stateSearch = createSearchBuilder(); - stateSearch.and("state", stateSearch.entity().getState(), SearchCriteria.Op.IN); + stateSearch.and(STATE, stateSearch.entity().getState(), SearchCriteria.Op.IN); stateSearch.done(); - parentSnapshotSearch = createSearchBuilder(); - parentSnapshotSearch.and("volume_id", parentSnapshotSearch.entity().getVolumeId(), SearchCriteria.Op.EQ); - parentSnapshotSearch.and("store_id", parentSnapshotSearch.entity().getDataStoreId(), SearchCriteria.Op.EQ); - parentSnapshotSearch.and("store_role", parentSnapshotSearch.entity().getRole(), SearchCriteria.Op.EQ); - parentSnapshotSearch.and("state", parentSnapshotSearch.entity().getState(), SearchCriteria.Op.EQ); - parentSnapshotSearch.done(); - - snapshotVOSearch = _snapshotDao.createSearchBuilder(); - snapshotVOSearch.and("volume_id", snapshotVOSearch.entity().getVolumeId(), SearchCriteria.Op.EQ); + snapshotVOSearch = snapshotDao.createSearchBuilder(); + snapshotVOSearch.and(VOLUME_ID, snapshotVOSearch.entity().getVolumeId(), SearchCriteria.Op.EQ); snapshotVOSearch.done(); snapshotCreatedSearch = createSearchBuilder(); - snapshotCreatedSearch.and("store_id", snapshotCreatedSearch.entity().getDataStoreId(), Op.EQ); - snapshotCreatedSearch.and("created", snapshotCreatedSearch.entity().getCreated(), Op.BETWEEN); + snapshotCreatedSearch.and(STORE_ID, snapshotCreatedSearch.entity().getDataStoreId(), SearchCriteria.Op.EQ); + snapshotCreatedSearch.and(CREATED, snapshotCreatedSearch.entity().getCreated(), SearchCriteria.Op.BETWEEN); snapshotCreatedSearch.done(); return true; @@ -180,142 +134,105 @@ public class SnapshotDataStoreDaoImpl extends GenericDaoBase sc = updateStateSearch.create(); - sc.setParameters("id", dataObj.getId()); - sc.setParameters("state", currentState); - sc.setParameters("updatedCount", dataObj.getUpdatedCount()); + SearchCriteria sc = searchFilteringStoreIdEqStateEqStoreRoleEqIdEqUpdateCountEqSnapshotIdEqVolumeIdEq.create(); + sc.setParameters(ID, dataObj.getId()); + sc.setParameters(STATE, currentState); + sc.setParameters(UPDATED_COUNT, dataObj.getUpdatedCount()); dataObj.incrUpdatedCount(); UpdateBuilder builder = getUpdateBuilder(dataObj); - builder.set(dataObj, "state", nextState); + builder.set(dataObj, STATE, nextState); builder.set(dataObj, "updated", new Date()); - int rows = update(dataObj, sc); - if (rows == 0 && s_logger.isDebugEnabled()) { - SnapshotDataStoreVO dbVol = findByIdIncludingRemoved(dataObj.getId()); - if (dbVol != null) { - StringBuilder str = new StringBuilder("Unable to update ").append(dataObj.toString()); - str.append(": DB Data={id=") - .append(dbVol.getId()) - .append("; state=") - .append(dbVol.getState()) - .append("; updatecount=") - .append(dbVol.getUpdatedCount()) - .append(";updatedTime=") - .append(dbVol.getUpdated()); - str.append(": New Data={id=") - .append(dataObj.getId()) - .append("; state=") - .append(nextState) - .append("; event=") - .append(event) - .append("; updatecount=") - .append(dataObj.getUpdatedCount()) - .append("; updatedTime=") - .append(dataObj.getUpdated()); - str.append(": stale Data={id=") - .append(dataObj.getId()) - .append("; state=") - .append(currentState) - .append("; event=") - .append(event) - .append("; updatecount=") - .append(oldUpdated) - .append("; updatedTime=") - .append(oldUpdatedTime); - } else { - s_logger.debug("Unable to update objectIndatastore: id=" + dataObj.getId() + ", as there is no such object exists in the database anymore"); - } + if (update(dataObj, sc) > 0) { + return true; } - return rows > 0; + + SnapshotDataStoreVO dbVol = findByIdIncludingRemoved(dataObj.getId()); + String message; + if (dbVol != null) { + message = String.format("Unable to update %s: DB Data={id=%s; state=%s; updatecount=%s;updatedTime=%s: New Data={id=%s; state=%s; event=%s; updatecount=%s; " + + "updatedTime=%s: stale Data={id=%s; state=%s; event=%s; updatecount=%s; updatedTime=%s", dataObj, dbVol.getId(), dbVol.getState(), + dbVol.getUpdatedCount(), dbVol.getUpdated(), dataObj.getId(), event, nextState, dataObj.getUpdatedCount(), dataObj.getUpdated(), dataObj.getId(), + currentState, event, oldUpdated, oldUpdatedTime); + } else { + message = String.format("Unable to update objectIndatastore: id=%s, as there is no such object exists in the database anymore", dataObj.getId()); + } + + s_logger.debug(message); + return false; } @Override public List listByStoreId(long id, DataStoreRole role) { - SearchCriteria sc = storeSearch.create(); - sc.setParameters("store_id", id); - sc.setParameters("store_role", role); - sc.setParameters("state", ObjectInDataStoreStateMachine.State.Destroyed); + SearchCriteria sc = searchFilteringStoreIdEqStoreRoleEqStateNeqRefCntNeq.create(); + sc.setParameters(STORE_ID, id); + sc.setParameters(STORE_ROLE, role); + sc.setParameters(STATE, ObjectInDataStoreStateMachine.State.Destroyed); return listBy(sc); } @Override public List listByStoreIdAndState(long id, ObjectInDataStoreStateMachine.State state) { - SearchCriteria sc = storeStateSearch.create(); - sc.setParameters("store_id", id); - sc.setParameters("state", state); + SearchCriteria sc = searchFilteringStoreIdEqStateEqStoreRoleEqIdEqUpdateCountEqSnapshotIdEqVolumeIdEq.create(); + sc.setParameters(STORE_ID, id); + sc.setParameters(STATE, state); return listBy(sc); } @Override public void deletePrimaryRecordsForStore(long id, DataStoreRole role) { - SearchCriteria sc = storeSearch.create(); - sc.setParameters("store_id", id); - sc.setParameters("store_role", role); - TransactionLegacy txn = TransactionLegacy.currentTxn(); - txn.start(); + SearchCriteria sc = searchFilteringStoreIdEqStoreRoleEqStateNeqRefCntNeq.create(); + sc.setParameters(STORE_ID, id); + sc.setParameters(STORE_ROLE, role); remove(sc); - txn.commit(); } @Override public void deleteSnapshotRecordsOnPrimary() { - SearchCriteria sc = storeSearch.create(); - sc.setParameters("store_role", DataStoreRole.Primary); - TransactionLegacy txn = TransactionLegacy.currentTxn(); - txn.start(); + SearchCriteria sc = searchFilteringStoreIdEqStoreRoleEqStateNeqRefCntNeq.create(); + sc.setParameters(STORE_ROLE, DataStoreRole.Primary); remove(sc); - txn.commit(); } @Override public SnapshotDataStoreVO findByStoreSnapshot(DataStoreRole role, long storeId, long snapshotId) { - SearchCriteria sc = storeSnapshotSearch.create(); - sc.setParameters("store_id", storeId); - sc.setParameters("snapshot_id", snapshotId); - sc.setParameters("store_role", role); + SearchCriteria sc = searchFilteringStoreIdEqStateEqStoreRoleEqIdEqUpdateCountEqSnapshotIdEqVolumeIdEq.create(); + sc.setParameters(STORE_ID, storeId); + sc.setParameters(SNAPSHOT_ID, snapshotId); + sc.setParameters(STORE_ROLE, role); return findOneBy(sc); } @Override public SnapshotDataStoreVO findLatestSnapshotForVolume(Long volumeId, DataStoreRole role) { - TransactionLegacy txn = TransactionLegacy.currentTxn(); - try ( - PreparedStatement pstmt = txn.prepareStatement(findLatestSnapshot); - ){ - pstmt.setString(1, role.toString()); - pstmt.setLong(2, volumeId); - try (ResultSet rs = pstmt.executeQuery();) { - while (rs.next()) { - long sid = rs.getLong(1); - long snid = rs.getLong(3); - return findByStoreSnapshot(role, sid, snid); - } - } - } catch (SQLException e) { - s_logger.debug("Failed to find latest snapshot for volume: " + volumeId + " due to: " + e.toString()); - } - return null; + return findOldestOrLatestSnapshotForVolume(volumeId, role, false); } @Override public SnapshotDataStoreVO findOldestSnapshotForVolume(Long volumeId, DataStoreRole role) { - TransactionLegacy txn = TransactionLegacy.currentTxn(); - try ( - PreparedStatement pstmt = txn.prepareStatement(findOldestSnapshot); - ){ - pstmt.setString(1, role.toString()); - pstmt.setLong(2, volumeId); - try (ResultSet rs = pstmt.executeQuery();) { - while (rs.next()) { - long sid = rs.getLong(1); - long snid = rs.getLong(3); - return findByStoreSnapshot(role, sid, snid); + return findOldestOrLatestSnapshotForVolume(volumeId, role, true); + } + + protected SnapshotDataStoreVO findOldestOrLatestSnapshotForVolume(long volumeId, DataStoreRole role, boolean oldest) { + String order = oldest ? "ASC" : "DESC"; + + try (TransactionLegacy transactionLegacy = TransactionLegacy.currentTxn()) { + try (PreparedStatement preparedStatement = transactionLegacy.prepareStatement(String.format(FIND_OLDEST_OR_LATEST_SNAPSHOT, order))) { + preparedStatement.setString(1, role.toString()); + preparedStatement.setLong(2, volumeId); + + try (ResultSet resultSet = preparedStatement.executeQuery()) { + if (resultSet.next()) { + long storeId = resultSet.getLong(1); + long snapshotId = resultSet.getLong(3); + return findByStoreSnapshot(role, storeId, snapshotId); + } } } } catch (SQLException e) { - s_logger.debug("Failed to find oldest snapshot for volume: " + volumeId + " due to: " + e.toString()); + s_logger.warn(String.format("Failed to find %s snapshot for volume [%s] in %s store due to [%s].", oldest ? "oldest" : "latest", volumeId, role, e.getMessage()), e); } return null; } @@ -323,17 +240,20 @@ public class SnapshotDataStoreDaoImpl extends GenericDaoBase sc = parentSnapshotSearch.create(); - sc.setParameters("volume_id", volumeId); - sc.setParameters("store_role", role.toString()); - sc.setParameters("state", ObjectInDataStoreStateMachine.State.Ready.name()); - sc.setParameters("store_id", storeId); + if (!isSnapshotChainingRequired(volumeId)) { + s_logger.trace(String.format("Snapshot chaining is not required for snapshots of volume [%s]. Returning null as parent.", volumeId)); + return null; + } - List snapshotList = listBy(sc, new Filter(SnapshotDataStoreVO.class, "created", false, null, null)); - if (snapshotList != null && snapshotList.size() != 0) { - return snapshotList.get(0); - } + SearchCriteria sc = searchFilteringStoreIdEqStateEqStoreRoleEqIdEqUpdateCountEqSnapshotIdEqVolumeIdEq.create(); + sc.setParameters(VOLUME_ID, volumeId); + sc.setParameters(STORE_ROLE, role.toString()); + sc.setParameters(STATE, ObjectInDataStoreStateMachine.State.Ready.name()); + sc.setParameters(STORE_ID, storeId); + + List snapshotList = listBy(sc, new Filter(SnapshotDataStoreVO.class, CREATED, false, null, null)); + if (CollectionUtils.isNotEmpty(snapshotList)) { + return snapshotList.get(0); } return null; } @@ -341,130 +261,138 @@ public class SnapshotDataStoreDaoImpl extends GenericDaoBase sc = createSearchCriteriaBySnapshotIdAndStoreRole(snapshotId, role); - sc.setParameters("state", State.Ready); + sc.setParameters(STATE, State.Ready); return findOneBy(sc); } @Override public SnapshotDataStoreVO findBySourceSnapshot(long snapshotId, DataStoreRole role) { SearchCriteria sc = createSearchCriteriaBySnapshotIdAndStoreRole(snapshotId, role); - sc.setParameters("state", State.Migrating); + sc.setParameters(STATE, State.Migrating); return findOneBy(sc); } @Override public List listAllByVolumeAndDataStore(long volumeId, DataStoreRole role) { - SearchCriteria sc = volumeSearch.create(); - sc.setParameters("volume_id", volumeId); - sc.setParameters("store_role", role); + SearchCriteria sc = searchFilteringStoreIdEqStateEqStoreRoleEqIdEqUpdateCountEqSnapshotIdEqVolumeIdEq.create(); + sc.setParameters(VOLUME_ID, volumeId); + sc.setParameters(STORE_ROLE, role); return listBy(sc); } @Override public SnapshotDataStoreVO findByVolume(long volumeId, DataStoreRole role) { - SearchCriteria sc = volumeSearch.create(); - sc.setParameters("volume_id", volumeId); - sc.setParameters("store_role", role); + SearchCriteria sc = searchFilteringStoreIdEqStateEqStoreRoleEqIdEqUpdateCountEqSnapshotIdEqVolumeIdEq.create(); + sc.setParameters(VOLUME_ID, volumeId); + sc.setParameters(STORE_ROLE, role); return findOneBy(sc); } @Override public SnapshotDataStoreVO findByVolume(long snapshotId, long volumeId, DataStoreRole role) { - SearchCriteria sc = volumeSearch.create(); - sc.setParameters("snapshot_id", snapshotId); - sc.setParameters("volume_id", volumeId); - sc.setParameters("store_role", role); + SearchCriteria sc = searchFilteringStoreIdEqStateEqStoreRoleEqIdEqUpdateCountEqSnapshotIdEqVolumeIdEq.create(); + sc.setParameters(SNAPSHOT_ID, snapshotId); + sc.setParameters(VOLUME_ID, volumeId); + sc.setParameters(STORE_ROLE, role); return findOneBy(sc); } @Override public List findBySnapshotId(long snapshotId) { - SearchCriteria sc = snapshotIdSearch.create(); - sc.setParameters("snapshot_id", snapshotId); + SearchCriteria sc = searchFilteringStoreIdEqStateEqStoreRoleEqIdEqUpdateCountEqSnapshotIdEqVolumeIdEq.create(); + sc.setParameters(SNAPSHOT_ID, snapshotId); return listBy(sc); } @Override public List listDestroyed(long id) { - SearchCriteria sc = destroyedSearch.create(); - sc.setParameters("store_id", id); - sc.setParameters("store_role", DataStoreRole.Image); - sc.setParameters("state", ObjectInDataStoreStateMachine.State.Destroyed); + SearchCriteria sc = searchFilteringStoreIdEqStateEqStoreRoleEqIdEqUpdateCountEqSnapshotIdEqVolumeIdEq.create(); + sc.setParameters(STORE_ID, id); + sc.setParameters(STORE_ROLE, DataStoreRole.Image); + sc.setParameters(STATE, ObjectInDataStoreStateMachine.State.Destroyed); return listBy(sc); } @Override public List listActiveOnCache(long id) { - SearchCriteria sc = cacheSearch.create(); - sc.setParameters("store_id", id); - sc.setParameters("store_role", DataStoreRole.ImageCache); - sc.setParameters("state", ObjectInDataStoreStateMachine.State.Destroyed); - sc.setParameters("ref_cnt", 0); + SearchCriteria sc = searchFilteringStoreIdEqStoreRoleEqStateNeqRefCntNeq.create(); + sc.setParameters(STORE_ID, id); + sc.setParameters(STORE_ROLE, DataStoreRole.ImageCache); + sc.setParameters(STATE, ObjectInDataStoreStateMachine.State.Destroyed); + sc.setParameters(REF_CNT, 0); return listBy(sc); } + /** + * Creates an entry for each record, but with empty install path since the content is not on region-wide store yet. + */ @Override public void duplicateCacheRecordsOnRegionStore(long storeId) { - // find all records on image cache - SearchCriteria sc = storeSnapshotSearch.create(); - sc.setParameters("store_role", DataStoreRole.ImageCache); + SearchCriteria sc = searchFilteringStoreIdEqStateEqStoreRoleEqIdEqUpdateCountEqSnapshotIdEqVolumeIdEq.create(); + sc.setParameters(STORE_ROLE, DataStoreRole.ImageCache); sc.setParameters("destroyed", false); List snapshots = listBy(sc); - // create an entry for each record, but with empty install path since the content is not yet on region-wide store yet - if (snapshots != null) { - s_logger.info("Duplicate " + snapshots.size() + " snapshot cache store records to region store"); - for (SnapshotDataStoreVO snap : snapshots) { - SnapshotDataStoreVO snapStore = findByStoreSnapshot(DataStoreRole.Image, storeId, snap.getSnapshotId()); - if (snapStore != null) { - s_logger.info("There is already entry for snapshot " + snap.getSnapshotId() + " on region store " + storeId); - continue; - } - s_logger.info("Persisting an entry for snapshot " + snap.getSnapshotId() + " on region store " + storeId); - SnapshotDataStoreVO ss = new SnapshotDataStoreVO(); - ss.setSnapshotId(snap.getSnapshotId()); - ss.setDataStoreId(storeId); - ss.setRole(DataStoreRole.Image); - ss.setVolumeId(snap.getVolumeId()); - ss.setParentSnapshotId(snap.getParentSnapshotId()); - ss.setState(snap.getState()); - ss.setSize(snap.getSize()); - ss.setPhysicalSize(snap.getPhysicalSize()); - ss.setRefCnt(snap.getRefCnt()); - persist(ss); - // increase ref_cnt so that this will not be recycled before the content is pushed to region-wide store - snap.incrRefCnt(); - update(snap.getId(), snap); - } + + if (snapshots == null) { + s_logger.debug(String.format("There are no snapshots on cache store to duplicate to region store [%s].", storeId)); + return; } + s_logger.info(String.format("Duplicating [%s] snapshot cache store records to region store [%s].", snapshots.size(), storeId)); + + for (SnapshotDataStoreVO snap : snapshots) { + SnapshotDataStoreVO snapStore = findByStoreSnapshot(DataStoreRole.Image, storeId, snap.getSnapshotId()); + + if (snapStore != null) { + s_logger.debug(String.format("There is already an entry for snapshot [%s] on region store [%s].", snap.getSnapshotId(), storeId)); + continue; + } + + s_logger.info(String.format("Persisting an entry for snapshot [%s] on region store [%s].", snap.getSnapshotId(), storeId)); + SnapshotDataStoreVO ss = new SnapshotDataStoreVO(); + ss.setSnapshotId(snap.getSnapshotId()); + ss.setDataStoreId(storeId); + ss.setRole(DataStoreRole.Image); + ss.setVolumeId(snap.getVolumeId()); + ss.setParentSnapshotId(snap.getParentSnapshotId()); + ss.setState(snap.getState()); + ss.setSize(snap.getSize()); + ss.setPhysicalSize(snap.getPhysicalSize()); + ss.setRefCnt(snap.getRefCnt()); + persist(ss); + + snap.incrRefCnt(); + update(snap.getId(), snap); + } } @Override public SnapshotDataStoreVO findReadyOnCache(long snapshotId) { - SearchCriteria sc = storeSnapshotSearch.create(); - sc.setParameters("snapshot_id", snapshotId); - sc.setParameters("store_role", DataStoreRole.ImageCache); - sc.setParameters("state", ObjectInDataStoreStateMachine.State.Ready); + SearchCriteria sc = searchFilteringStoreIdEqStateEqStoreRoleEqIdEqUpdateCountEqSnapshotIdEqVolumeIdEq.create(); + sc.setParameters(SNAPSHOT_ID, snapshotId); + sc.setParameters(STORE_ROLE, DataStoreRole.ImageCache); + sc.setParameters(STATE, ObjectInDataStoreStateMachine.State.Ready); return findOneIncludingRemovedBy(sc); } @Override public List listOnCache(long snapshotId) { - SearchCriteria sc = storeSnapshotSearch.create(); - sc.setParameters("snapshot_id", snapshotId); - sc.setParameters("store_role", DataStoreRole.ImageCache); + SearchCriteria sc = searchFilteringStoreIdEqStateEqStoreRoleEqIdEqUpdateCountEqSnapshotIdEqVolumeIdEq.create(); + sc.setParameters(SNAPSHOT_ID, snapshotId); + sc.setParameters(STORE_ROLE, DataStoreRole.ImageCache); return search(sc, null); } @Override public void updateStoreRoleToCache(long storeId) { - SearchCriteria sc = storeSearch.create(); - sc.setParameters("store_id", storeId); + SearchCriteria sc = searchFilteringStoreIdEqStoreRoleEqStateNeqRefCntNeq.create(); + sc.setParameters(STORE_ID, storeId); sc.setParameters("destroyed", false); List snaps = listBy(sc); if (snaps != null) { - s_logger.info("Update to cache store role for " + snaps.size() + " entries in snapshot_store_ref"); + s_logger.info(String.format("Updating role to cache store for [%s] entries in snapshot_store_ref.", snaps.size())); for (SnapshotDataStoreVO snap : snaps) { + s_logger.debug(String.format("Updating role to cache store for entry [%s].", snap)); snap.setRole(DataStoreRole.ImageCache); update(snap.getId(), snap); } @@ -473,8 +401,8 @@ public class SnapshotDataStoreDaoImpl extends GenericDaoBase sc = volumeIdSearch.create(); - sc.setParameters("volume_id", oldVolId); + SearchCriteria sc = searchFilteringStoreIdEqStateEqStoreRoleEqIdEqUpdateCountEqSnapshotIdEqVolumeIdEq.create(); + sc.setParameters(VOLUME_ID, oldVolId); SnapshotDataStoreVO snapshot = createForUpdate(); snapshot.setVolumeId(newVolId); UpdateBuilder ub = getUpdateBuilder(snapshot); @@ -484,23 +412,23 @@ public class SnapshotDataStoreDaoImpl extends GenericDaoBase listByState(ObjectInDataStoreStateMachine.State... states) { SearchCriteria sc = stateSearch.create(); - sc.setParameters("state", (Object[])states); + sc.setParameters(STATE, (Object[])states); return listBy(sc, null); } @Override public List findSnapshots(Long storeId, Date start, Date end) { SearchCriteria sc = snapshotCreatedSearch.create(); - sc.setParameters("store_id", storeId); + sc.setParameters(STORE_ID, storeId); if (start != null && end != null) { - sc.setParameters("created", start, end); + sc.setParameters(CREATED, start, end); } return search(sc, null); } public SnapshotDataStoreVO findDestroyedReferenceBySnapshot(long snapshotId, DataStoreRole role) { SearchCriteria sc = createSearchCriteriaBySnapshotIdAndStoreRole(snapshotId, role); - sc.setParameters("state", State.Destroyed); + sc.setParameters(STATE, State.Destroyed); return findOneBy(sc); } @@ -509,44 +437,32 @@ public class SnapshotDataStoreDaoImpl extends GenericDaoBase createSearchCriteriaBySnapshotIdAndStoreRole(long snapshotId, DataStoreRole role) { - SearchCriteria sc = snapshotSearch.create(); - sc.setParameters("snapshot_id", snapshotId); - sc.setParameters("store_role", role); + SearchCriteria sc = searchFilteringStoreIdEqStateEqStoreRoleEqIdEqUpdateCountEqSnapshotIdEqVolumeIdEq.create(); + sc.setParameters(SNAPSHOT_ID, snapshotId); + sc.setParameters(STORE_ROLE, role); return sc; } - private boolean isSnapshotChainingRequired(long volumeId) { - - hypervisorsSupportingSnapshotsChaining.add(Hypervisor.HypervisorType.XenServer); - + protected boolean isSnapshotChainingRequired(long volumeId) { SearchCriteria sc = snapshotVOSearch.create(); - sc.setParameters("volume_id", volumeId); + sc.setParameters(VOLUME_ID, volumeId); - SnapshotVO volSnapshot = _snapshotDao.findOneBy(sc); + SnapshotVO snapshot = snapshotDao.findOneBy(sc); - if (volSnapshot != null && hypervisorsSupportingSnapshotsChaining.contains(volSnapshot.getHypervisorType())) { - return true; - } - - return false; + return snapshot != null && HYPERVISORS_SUPPORTING_SNAPSHOTS_CHAINING.contains(snapshot.getHypervisorType()); } @Override public boolean expungeReferenceBySnapshotIdAndDataStoreRole(long snapshotId, DataStoreRole dataStoreRole) { SnapshotDataStoreVO snapshotDataStoreVo = findOneBy(createSearchCriteriaBySnapshotIdAndStoreRole(snapshotId, dataStoreRole)); - - if (snapshotDataStoreVo == null) { - return true; - } - - return expunge(snapshotDataStoreVo.getId()); + return snapshotDataStoreVo == null || expunge(snapshotDataStoreVo.getId()); } @Override public List listReadyByVolumeId(long volumeId) { - SearchCriteria sc = volumeIdAndStateReadySearch.create(); - sc.setParameters("volume_id", volumeId); - sc.setParameters("state", State.Ready); + SearchCriteria sc = searchFilteringStoreIdEqStateEqStoreRoleEqIdEqUpdateCountEqSnapshotIdEqVolumeIdEq.create(); + sc.setParameters(VOLUME_ID, volumeId); + sc.setParameters(STATE, State.Ready); return listBy(sc); } } diff --git a/engine/storage/src/test/java/org/apache/cloudstack/storage/image/db/SnapshotDataStoreDaoImplTest.java b/engine/storage/src/test/java/org/apache/cloudstack/storage/image/db/SnapshotDataStoreDaoImplTest.java index b7be8e3e526..0a02721f67f 100644 --- a/engine/storage/src/test/java/org/apache/cloudstack/storage/image/db/SnapshotDataStoreDaoImplTest.java +++ b/engine/storage/src/test/java/org/apache/cloudstack/storage/image/db/SnapshotDataStoreDaoImplTest.java @@ -19,7 +19,10 @@ package org.apache.cloudstack.storage.image.db; +import com.cloud.hypervisor.Hypervisor; import com.cloud.storage.DataStoreRole; +import com.cloud.storage.SnapshotVO; +import com.cloud.storage.dao.SnapshotDao; import com.cloud.utils.db.SearchBuilder; import com.cloud.utils.db.SearchCriteria; import org.apache.cloudstack.storage.datastore.db.SnapshotDataStoreVO; @@ -45,9 +48,16 @@ public class SnapshotDataStoreDaoImplTest { @Mock SearchBuilder searchBuilderMock; + @Mock + SnapshotDao snapshotDaoMock; + + @Mock + SnapshotVO snapshotVoMock; + @Before public void init(){ - snapshotDataStoreDaoImplSpy.snapshotSearch = searchBuilderMock; + snapshotDataStoreDaoImplSpy.searchFilteringStoreIdEqStateEqStoreRoleEqIdEqUpdateCountEqSnapshotIdEqVolumeIdEq = searchBuilderMock; + snapshotDataStoreDaoImplSpy.snapshotDao = snapshotDaoMock; } @Test @@ -74,4 +84,63 @@ public class SnapshotDataStoreDaoImplTest { Mockito.verify(searchCriteriaMock).setParameters("snapshot_id", 0l); Mockito.verify(searchCriteriaMock).setParameters("store_role", DataStoreRole.Image); } + + @Test + public void isSnapshotChainingRequiredTestSnapshotIsNullReturnFalse() { + snapshotDataStoreDaoImplSpy.snapshotVOSearch = searchBuilderMock; + Mockito.doReturn(searchCriteriaMock).when(searchBuilderMock).create(); + Mockito.doReturn(null).when(snapshotDaoMock).findOneBy(Mockito.any()); + Assert.assertFalse(snapshotDataStoreDaoImplSpy.isSnapshotChainingRequired(2)); + } + + @Test + public void isSnapshotChainingRequiredTestSnapshotIsNotNullReturnAccordingHypervisorType() { + snapshotDataStoreDaoImplSpy.snapshotVOSearch = searchBuilderMock; + Mockito.doReturn(searchCriteriaMock).when(searchBuilderMock).create(); + Mockito.doReturn(snapshotVoMock).when(snapshotDaoMock).findOneBy(Mockito.any()); + + for (Hypervisor.HypervisorType hypervisorType : Hypervisor.HypervisorType.values()) { + Mockito.doReturn(hypervisorType).when(snapshotVoMock).getHypervisorType(); + boolean result = snapshotDataStoreDaoImplSpy.isSnapshotChainingRequired(2); + + if (SnapshotDataStoreDaoImpl.HYPERVISORS_SUPPORTING_SNAPSHOTS_CHAINING.contains(hypervisorType)) { + Assert.assertTrue(result); + } else { + Assert.assertFalse(result); + } + } + } + + @Test + public void expungeReferenceBySnapshotIdAndDataStoreRoleTestSnapshotDataStoreIsNullReturnTrue() { + Mockito.doReturn(searchCriteriaMock).when(snapshotDataStoreDaoImplSpy).createSearchCriteriaBySnapshotIdAndStoreRole(Mockito.anyLong(), Mockito.any()); + Mockito.doReturn(null).when(snapshotDataStoreDaoImplSpy).findOneBy(Mockito.any()); + + for (DataStoreRole value : DataStoreRole.values()) { + Assert.assertTrue(snapshotDataStoreDaoImplSpy.expungeReferenceBySnapshotIdAndDataStoreRole(1, value)); + } + } + + @Test + public void expungeReferenceBySnapshotIdAndDataStoreRoleTestSnapshotDataStoreIsNotNullAndExpungeIsTrueReturnTrue() { + Mockito.doReturn(searchCriteriaMock).when(snapshotDataStoreDaoImplSpy).createSearchCriteriaBySnapshotIdAndStoreRole(Mockito.anyLong(), Mockito.any()); + Mockito.doReturn(snapshotDataStoreVoMock).when(snapshotDataStoreDaoImplSpy).findOneBy(Mockito.any()); + Mockito.doReturn(true).when(snapshotDataStoreDaoImplSpy).expunge(Mockito.anyLong()); + + for (DataStoreRole value : DataStoreRole.values()) { + Assert.assertTrue(snapshotDataStoreDaoImplSpy.expungeReferenceBySnapshotIdAndDataStoreRole(1, value)); + } + } + + @Test + public void expungeReferenceBySnapshotIdAndDataStoreRoleTestSnapshotDataStoreIsNotNullAndExpungeIsFalseReturnTrue() { + Mockito.doReturn(searchCriteriaMock).when(snapshotDataStoreDaoImplSpy).createSearchCriteriaBySnapshotIdAndStoreRole(Mockito.anyLong(), Mockito.any()); + Mockito.doReturn(snapshotDataStoreVoMock).when(snapshotDataStoreDaoImplSpy).findOneBy(Mockito.any()); + Mockito.doReturn(false).when(snapshotDataStoreDaoImplSpy).expunge(Mockito.anyLong()); + + for (DataStoreRole value : DataStoreRole.values()) { + Assert.assertFalse(snapshotDataStoreDaoImplSpy.expungeReferenceBySnapshotIdAndDataStoreRole(1, value)); + } + } + }