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.