You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ignite.apache.org by vo...@apache.org on 2015/09/18 10:11:44 UTC
[4/5] ignite git commit: IGNITE-586: Fixed rename issue.
IGNITE-586: Fixed rename issue.
Project: http://git-wip-us.apache.org/repos/asf/ignite/repo
Commit: http://git-wip-us.apache.org/repos/asf/ignite/commit/a28295e7
Tree: http://git-wip-us.apache.org/repos/asf/ignite/tree/a28295e7
Diff: http://git-wip-us.apache.org/repos/asf/ignite/diff/a28295e7
Branch: refs/heads/ignite-586
Commit: a28295e75b798d01aa28909774f2db40040f752a
Parents: eb4850b
Author: vozerov-gridgain <vo...@gridgain.com>
Authored: Fri Sep 18 10:55:44 2015 +0300
Committer: vozerov-gridgain <vo...@gridgain.com>
Committed: Fri Sep 18 10:55:44 2015 +0300
----------------------------------------------------------------------
.../internal/processors/igfs/IgfsImpl.java | 15 +-
.../processors/igfs/IgfsMetaManager.java | 168 ++++++++++++++++++-
2 files changed, 168 insertions(+), 15 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/ignite/blob/a28295e7/modules/core/src/main/java/org/apache/ignite/internal/processors/igfs/IgfsImpl.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/igfs/IgfsImpl.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/igfs/IgfsImpl.java
index f319945..aa5b4d1 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/igfs/IgfsImpl.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/igfs/IgfsImpl.java
@@ -703,8 +703,6 @@ public final class IgfsImpl implements IgfsEx {
throw new IgfsPathNotFoundException("Failed to rename (source path not found): " + src);
}
- final String srcFileName = src.name();
-
// Resolve destination file info.
FileDescriptor destDesc = getFileDescriptor(dest);
@@ -731,7 +729,7 @@ public final class IgfsImpl implements IgfsEx {
else
// Use destination directory for destination parent and source path name as destination name.
// Case mv "/x/y/foo" -> "/a/b/"
- destFileName = srcFileName;
+ destFileName = src.name();
// Can move only into directory, but not into file.
if (destDesc.isFile)
@@ -742,7 +740,6 @@ public final class IgfsImpl implements IgfsEx {
final List<IgniteUuid> srcIds = meta.fileIds(src);
assert srcIds != null;
- assert srcIds.size() == src.depth() + 1 : "src ids = " + srcIds + ", src = " + src;
if (srcIds.contains(null))
throw new IgfsPathNotFoundException("Failed to rename (Some of the source path components " +
@@ -765,10 +762,12 @@ public final class IgfsImpl implements IgfsEx {
// throw new IgfsPathNotFoundException("Failed to rename (Some of the destination path components " +
// "was concurrently deleted): " + destDir);
- meta.move(srcIds, src,
- destIds/*tail is the target dir id, it must exist*/,
- destDir/*dest directory (parent), it must exist. */,
- destFileName);
+// meta.move(srcIds, src,
+// destIds/*tail is the target dir id, it must exist*/,
+// destDir/*dest directory (parent), it must exist. */,
+// destFileName);
+
+ meta.move0(src, dest);
if (srcDesc.isFile) { // Renamed a file.
if (evts.isRecordable(EVT_IGFS_FILE_RENAMED))
http://git-wip-us.apache.org/repos/asf/ignite/blob/a28295e7/modules/core/src/main/java/org/apache/ignite/internal/processors/igfs/IgfsMetaManager.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/igfs/IgfsMetaManager.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/igfs/IgfsMetaManager.java
index 2d0ac0b..bef39bc 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/igfs/IgfsMetaManager.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/igfs/IgfsMetaManager.java
@@ -574,23 +574,29 @@ public class IgfsMetaManager extends IgfsManager {
assert validTxState(true);
assert fileIds != null && fileIds.length > 0;
- // Always sort file IDs participating in transaction to escape cache transaction deadlocks.
Arrays.sort(fileIds);
- // Wrap array as collection (1) to escape superfluous check in projection and (2) to check assertions.
- Collection<IgniteUuid> keys = Arrays.asList(fileIds);
+ return lockIds(Arrays.asList(fileIds));
+ }
+ /**
+ * Lock file IDs.
+ * @param fileIds File IDs (sorted).
+ * @return Map with lock info.
+ * @throws IgniteCheckedException If failed.
+ */
+ private Map<IgniteUuid, IgfsFileInfo> lockIds(Collection<IgniteUuid> fileIds) throws IgniteCheckedException {
if (log.isDebugEnabled())
- log.debug("Locking file ids: " + keys);
+ log.debug("Locking file ids: " + fileIds);
// Lock files and get their infos.
- Map<IgniteUuid, IgfsFileInfo> map = id2InfoPrj.getAll(keys);
+ Map<IgniteUuid, IgfsFileInfo> map = id2InfoPrj.getAll(fileIds);
if (log.isDebugEnabled())
- log.debug("Locked file ids: " + keys);
+ log.debug("Locked file ids: " + fileIds);
// Force root ID always exist in cache.
- if (keys.contains(ROOT_ID) && !map.containsKey(ROOT_ID)) {
+ if (fileIds.contains(ROOT_ID) && !map.containsKey(ROOT_ID)) {
IgfsFileInfo info = new IgfsFileInfo();
id2InfoPrj.putIfAbsent(ROOT_ID, info);
@@ -817,6 +823,154 @@ public class IgfsMetaManager extends IgfsManager {
}
/**
+ * Move routine.
+ * @param srcPath Source path.
+ * @param dstPath Destinatoin path.
+ * @throws IgniteCheckedException In case of exception.
+ */
+ public void move0(IgfsPath srcPath, IgfsPath dstPath) throws IgniteCheckedException {
+ if (busyLock.enterBusy()) {
+ try {
+ assert validTxState(false);
+
+ // 1. First get source and destination path IDs.
+ List<IgniteUuid> srcPathIds = fileIds(srcPath);
+ List<IgniteUuid> dstPathIds = fileIds(dstPath);
+
+ // 2. Start transaction.
+ IgniteInternalTx tx = metaCache.txStartEx(PESSIMISTIC, REPEATABLE_READ);
+
+ try {
+ // 3. Obtain the locks.
+ HashSet<IgniteUuid> allIds = new HashSet<>(srcPathIds);
+ allIds.addAll(dstPathIds);
+
+ allIds.remove(null);
+
+ Map<IgniteUuid, IgfsFileInfo> allInfos = lockIds(new TreeSet<>(allIds));
+
+ // 4. Verify integrity of source directory.
+ if (!verifyPathIntegrity(srcPath, srcPathIds, allInfos)) {
+ throw new IgniteCheckedException("Failed to perform move because source directory " +
+ "structure changed concurrently [src=" + srcPath + ", dst=" + dstPath + ']');
+ }
+
+ // 5. Verify integrity of destination directory.
+ IgniteUuid dstLeafId = dstPathIds.get(dstPathIds.size() - 1);
+
+ if (!verifyPathIntegrity(dstLeafId != null ? dstPath : dstPath.parent(), dstPathIds, allInfos)) {
+ throw new IgniteCheckedException("Failed to perform move because destination directory " +
+ "structure changed concurrently [src=" + srcPath + ", dst=" + dstPath + ']');
+ }
+
+ // 6. At this point we are safe to proceed with real move.
+ IgniteUuid srcTargetId = srcPathIds.get(srcPathIds.size() - 2);
+ IgfsFileInfo srcTargetInfo = allInfos.get(srcTargetId);
+ String srcName = srcPath.name();
+
+ IgniteUuid dstTargetId;
+ IgfsFileInfo dstTargetInfo;
+ String dstName;
+
+ // 7. Calculate the target.
+ if (dstLeafId != null) {
+ // Destination leaf exists. Check if it is an empty directory.
+ IgfsFileInfo dstLeafInfo = allInfos.get(dstLeafId);
+
+ assert dstLeafInfo != null;
+
+ if (dstLeafInfo.isDirectory()) {
+ // Destination is a directory.
+ dstTargetId = dstLeafId;
+ dstTargetInfo = dstLeafInfo;
+ dstName = srcPath.name();
+ }
+ else {
+ // Error, destination is existing file.
+ throw new IgniteCheckedException("Failed to perform move because destination points to " +
+ "existing file [src=" + srcPath + ", dst=" + dstPath + ']');
+ }
+ }
+ else {
+ // Destination leaf doesn't exist, so we operate on parent.
+ dstTargetId = dstPathIds.get(dstPathIds.size() - 2);
+ dstTargetInfo = allInfos.get(dstTargetId);
+ dstName = dstPath.name();
+ }
+
+ assert dstTargetInfo != null;
+ assert dstTargetInfo.isDirectory();
+
+ // 8. Last check: does destination target already have listing entry with the same name?
+ if (dstTargetInfo.listing().containsKey(dstName)) {
+ throw new IgniteCheckedException("Failed to perform move because destination already " +
+ "contains entry with the same name existing file [src=" + srcPath +
+ ", dst=" + dstPath + ']');
+ }
+
+ // 9. Actual move: remove from source parent and add to destination target.
+ IgfsListingEntry entry = srcTargetInfo.listing().get(srcName);
+
+ id2InfoPrj.invoke(srcTargetId, new UpdateListing(srcName, entry, true));
+ id2InfoPrj.invoke(dstTargetId, new UpdateListing(dstName, entry, false));
+
+ tx.commit();
+ }
+ finally {
+ tx.close();
+ }
+ }
+ finally {
+ busyLock.leaveBusy();
+ }
+ }
+ else
+ throw new IllegalStateException("Failed to perform move because Grid is stopping [srcPath=" +
+ srcPath + ", dstPath=" + dstPath + ']');
+ }
+
+ /**
+ * Verify path integrity.
+ *
+ * @param path Path to verify.
+ * @param expIds Expected IDs for this path. Might contain additional elements, e.g. because they were created
+ * on a child path.
+ * @param infos Locked infos.
+ * @return
+ */
+ private static boolean verifyPathIntegrity(IgfsPath path, List<IgniteUuid> expIds,
+ Map<IgniteUuid, IgfsFileInfo> infos) {
+ List<String> pathParts = path.components();
+
+ assert pathParts.size() < expIds.size();
+
+ for (int i = 0; i < pathParts.size(); i++) {
+ IgniteUuid parentId = expIds.get(i);
+
+ // If parent ID is null, it doesn't exist.
+ if (parentId != null) {
+ IgfsFileInfo parentInfo = infos.get(parentId);
+
+ // If parent info is null, it doesn't exist.
+ if (parentInfo != null) {
+ IgfsListingEntry childEntry = parentInfo.listing().get(pathParts.get(i));
+
+ // If expected child exists.
+ if (childEntry != null) {
+ // If child ID matches expected ID.
+ if (F.eq(childEntry.fileId(), expIds.get(i + 1)))
+ continue;
+ }
+ }
+ }
+
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
* Move or rename file.
*
* @param fileId File ID to move or rename.