You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ignite.apache.org by ni...@apache.org on 2021/03/26 07:51:37 UTC

[ignite] branch ignite-cdc updated: IGNITE-14360 Refactor FileLockHolder for reusage (#8905)

This is an automated email from the ASF dual-hosted git repository.

nizhikov pushed a commit to branch ignite-cdc
in repository https://gitbox.apache.org/repos/asf/ignite.git


The following commit(s) were added to refs/heads/ignite-cdc by this push:
     new e579b1e  IGNITE-14360 Refactor FileLockHolder for reusage (#8905)
e579b1e is described below

commit e579b1e557c597d159945c252ea1af793046b1ce
Author: Nikolay <ni...@apache.org>
AuthorDate: Fri Mar 26 10:51:20 2021 +0300

    IGNITE-14360 Refactor FileLockHolder for reusage (#8905)
---
 .../cache/persistence/FileLockHolder.java          | 202 +++++++++++++++++++++
 .../GridCacheDatabaseSharedManager.java            | 169 +++--------------
 .../filename/PdsConsistentIdProcessor.java         |  22 +--
 .../persistence/filename/PdsFolderSettings.java    |   8 +-
 4 files changed, 238 insertions(+), 163 deletions(-)

diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/FileLockHolder.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/FileLockHolder.java
new file mode 100644
index 0000000..b7b3d5c
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/FileLockHolder.java
@@ -0,0 +1,202 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.processors.cache.persistence;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.RandomAccessFile;
+import java.nio.ByteBuffer;
+import java.nio.channels.FileChannel;
+import java.nio.channels.FileLock;
+import java.nio.channels.OverlappingFileLockException;
+import java.nio.file.Paths;
+import org.apache.ignite.IgniteCheckedException;
+import org.apache.ignite.IgniteException;
+import org.apache.ignite.IgniteLogger;
+import org.apache.ignite.internal.util.typedef.internal.U;
+
+/**
+ * Abstract file lock holder.
+ * Implementations should provide {@link #lockId()} that will appear in error message for concurrent processes
+ * that will try to lock the same file and {@link #warningMessage(String)} to print on each lock try.
+ *
+ * @see GridCacheDatabaseSharedManager.NodeFileLockHolder
+ */
+public abstract class FileLockHolder implements AutoCloseable {
+    /** Lock file name. */
+    private static final String lockFileName = "lock";
+
+    /** File. */
+    private final File file;
+
+    /** Channel. */
+    private final RandomAccessFile lockFile;
+
+    /** Lock. */
+    private volatile FileLock lock;
+
+    /** Logger. */
+    private final IgniteLogger log;
+
+    /**
+     * @param rootDir Root directory for lock file.
+     * @param log Log.
+     */
+    protected FileLockHolder(String rootDir, IgniteLogger log) {
+        try {
+            file = Paths.get(rootDir, lockFileName).toFile();
+
+            lockFile = new RandomAccessFile(file, "rw");
+
+            this.log = log;
+        }
+        catch (IOException e) {
+            throw new IgniteException(e);
+        }
+    }
+
+    /**
+     * This id will appear in error message of concurrent processes that will try to lock on the same file.
+     *
+     * @return Lock ID to store in the file.
+     */
+    public abstract String lockId();
+
+    /**
+     * @param lockId Existing lock id.
+     * @return Warning message.
+     */
+    protected abstract String warningMessage(String lockId);
+
+    /**
+     * @param lockWaitTimeMillis During which time thread will try capture file lock.
+     * @throws IgniteCheckedException If failed to capture file lock.
+     */
+    public void tryLock(long lockWaitTimeMillis) throws IgniteCheckedException {
+        assert lockFile != null;
+
+        FileChannel ch = lockFile.getChannel();
+
+        String failMsg;
+
+        try {
+            String content = null;
+
+            // Try to get lock, if not available wait 1 sec and re-try.
+            for (int i = 0; i < lockWaitTimeMillis; i += 1000) {
+                try {
+                    lock = ch.tryLock(0, 1, false);
+
+                    if (lock != null && lock.isValid()) {
+                        writeContent(lockId());
+
+                        return;
+                    }
+                }
+                catch (OverlappingFileLockException ignore) {
+                    if (content == null)
+                        content = readContent();
+
+                    log.warning(warningMessage(content));
+                }
+
+                U.sleep(1000);
+            }
+
+            if (content == null)
+                content = readContent();
+
+            failMsg = "Failed to acquire file lock [holder=" + content + ", time=" + (lockWaitTimeMillis / 1000) +
+                " sec, path=" + file.getAbsolutePath() + ']';
+        }
+        catch (Exception e) {
+            throw new IgniteCheckedException(e);
+        }
+
+        if (failMsg != null)
+            throw new IgniteCheckedException(failMsg);
+    }
+
+    /**
+     * Write node id (who captured lock) into lock file.
+     *
+     * @param content Node id.
+     * @throws IOException if some fail while write node it.
+     */
+    private void writeContent(String content) throws IOException {
+        FileChannel ch = lockFile.getChannel();
+
+        byte[] bytes = content.getBytes();
+
+        ByteBuffer buf = ByteBuffer.allocate(bytes.length);
+        buf.put(bytes);
+
+        buf.flip();
+
+        ch.write(buf, 1);
+
+        ch.force(false);
+    }
+
+    /**
+     *
+     */
+    private String readContent() throws IOException {
+        FileChannel ch = lockFile.getChannel();
+
+        ByteBuffer buf = ByteBuffer.allocate((int)(ch.size() - 1));
+
+        ch.read(buf, 1);
+
+        String content = new String(buf.array());
+
+        buf.clear();
+
+        return content;
+    }
+
+    /**
+     * Locked or not.
+     */
+    public boolean isLocked() {
+        return lock != null && lock.isValid();
+    }
+
+    /**
+     * Releases file lock
+     */
+    public void release() {
+        U.releaseQuiet(lock);
+    }
+
+    /**
+     * Closes file channel
+     */
+    @Override public void close() {
+        release();
+
+        U.closeQuiet(lockFile);
+    }
+
+    /**
+     * @return Absolute path to lock file.
+     */
+    public String lockPath() {
+        return file.getAbsolutePath();
+    }
+}
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/GridCacheDatabaseSharedManager.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/GridCacheDatabaseSharedManager.java
index 8c0228e..15557ec 100755
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/GridCacheDatabaseSharedManager.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/GridCacheDatabaseSharedManager.java
@@ -19,15 +19,10 @@ package org.apache.ignite.internal.processors.cache.persistence;
 
 import java.io.File;
 import java.io.IOException;
-import java.io.RandomAccessFile;
 import java.io.Serializable;
 import java.nio.ByteBuffer;
 import java.nio.ByteOrder;
-import java.nio.channels.FileChannel;
-import java.nio.channels.FileLock;
-import java.nio.channels.OverlappingFileLockException;
 import java.nio.file.Path;
-import java.nio.file.Paths;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
@@ -299,7 +294,7 @@ public class GridCacheDatabaseSharedManager extends IgniteCacheDatabaseSharedMan
      * Lock holder for compatible folders mode. Null if lock holder was created at start node. <br>
      * In this case lock is held on PDS resover manager and it is not required to manage locking here
      */
-    @Nullable private FileLockHolder fileLockHolder;
+    @Nullable private NodeFileLockHolder fileLockHolder;
 
     /** Lock wait time. */
     private final long lockWaitTime;
@@ -554,7 +549,7 @@ public class GridCacheDatabaseSharedManager extends IgniteCacheDatabaseSharedMan
                 () -> cpFreqDeviation.getOrDefault(DEFAULT_CHECKPOINT_DEVIATION)
             );
 
-            final FileLockHolder preLocked = kernalCtx.pdsFolderResolver()
+            final NodeFileLockHolder preLocked = kernalCtx.pdsFolderResolver()
                 .resolveFolders()
                 .getLockedFileLockHolder();
 
@@ -735,12 +730,12 @@ public class GridCacheDatabaseSharedManager extends IgniteCacheDatabaseSharedMan
     /**
      * @param preLocked Pre-locked file lock holder.
      */
-    private void acquireFileLock(FileLockHolder preLocked) throws IgniteCheckedException {
+    private void acquireFileLock(NodeFileLockHolder preLocked) throws IgniteCheckedException {
         if (cctx.kernalContext().clientNode())
             return;
 
         fileLockHolder = preLocked == null ?
-            new FileLockHolder(storeMgr.workDir().getPath(), cctx.kernalContext(), log) : preLocked;
+            new NodeFileLockHolder(storeMgr.workDir().getPath(), cctx.kernalContext(), log) : preLocked;
 
         if (!fileLockHolder.isLocked()) {
             if (log.isDebugEnabled())
@@ -2945,53 +2940,25 @@ public class GridCacheDatabaseSharedManager extends IgniteCacheDatabaseSharedMan
     }
 
     /**
-     *
+     * Node file lock holder.
      */
-    public static class FileLockHolder implements AutoCloseable {
-        /** Lock file name. */
-        private static final String lockFileName = "lock";
-
-        /** File. */
-        private File file;
-
-        /** Channel. */
-        private RandomAccessFile lockFile;
-
-        /** Lock. */
-        private volatile FileLock lock;
-
+    public static class NodeFileLockHolder extends FileLockHolder {
         /** Kernal context to generate Id of locked node in file. */
-        @NotNull private GridKernalContext ctx;
-
-        /** Logger. */
-        private IgniteLogger log;
+        @NotNull private final GridKernalContext ctx;
 
         /**
-         * @param path Path.
+         * @param rootDir Root directory for lock file.
+         * @param ctx Kernal context.
+         * @param log Log.
          */
-        public FileLockHolder(String path, @NotNull GridKernalContext ctx, IgniteLogger log) {
-            try {
-                file = Paths.get(path, lockFileName).toFile();
-
-                lockFile = new RandomAccessFile(file, "rw");
+        public NodeFileLockHolder(String rootDir, @NotNull GridKernalContext ctx, IgniteLogger log) {
+            super(rootDir, log);
 
-                this.ctx = ctx;
-                this.log = log;
-            }
-            catch (IOException e) {
-                throw new IgniteException(e);
-            }
+            this.ctx = ctx;
         }
 
-        /**
-         * @param lockWaitTimeMillis During which time thread will try capture file lock.
-         * @throws IgniteCheckedException If failed to capture file lock.
-         */
-        public void tryLock(long lockWaitTimeMillis) throws IgniteCheckedException {
-            assert lockFile != null;
-
-            FileChannel ch = lockFile.getChannel();
-
+        /** {@inheritDoc} */
+        @Override public String lockId() {
             SB sb = new SB();
 
             //write node id
@@ -3022,108 +2989,14 @@ public class GridCacheDatabaseSharedManager extends IgniteCacheDatabaseSharedMan
 
             sb.a("]");
 
-            String failMsg;
-
-            try {
-                String content = null;
-
-                // Try to get lock, if not available wait 1 sec and re-try.
-                for (int i = 0; i < lockWaitTimeMillis; i += 1000) {
-                    try {
-                        lock = ch.tryLock(0, 1, false);
-
-                        if (lock != null && lock.isValid()) {
-                            writeContent(sb.toString());
-
-                            return;
-                        }
-                    }
-                    catch (OverlappingFileLockException ignore) {
-                        if (content == null)
-                            content = readContent();
-
-                        log.warning("Failed to acquire file lock. Will try again in 1s " +
-                            "[nodeId=" + ctx.localNodeId() + ", holder=" + content +
-                            ", path=" + file.getAbsolutePath() + ']');
-                    }
-
-                    U.sleep(1000);
-                }
-
-                if (content == null)
-                    content = readContent();
-
-                failMsg = "Failed to acquire file lock [holder=" + content + ", time=" + (lockWaitTimeMillis / 1000) +
-                    " sec, path=" + file.getAbsolutePath() + ']';
-            }
-            catch (Exception e) {
-                throw new IgniteCheckedException(e);
-            }
-
-            if (failMsg != null)
-                throw new IgniteCheckedException(failMsg);
-        }
-
-        /**
-         * Write node id (who captured lock) into lock file.
-         *
-         * @param content Node id.
-         * @throws IOException if some fail while write node it.
-         */
-        private void writeContent(String content) throws IOException {
-            FileChannel ch = lockFile.getChannel();
-
-            byte[] bytes = content.getBytes();
-
-            ByteBuffer buf = ByteBuffer.allocate(bytes.length);
-            buf.put(bytes);
-
-            buf.flip();
-
-            ch.write(buf, 1);
-
-            ch.force(false);
+            return sb.toString();
         }
 
-        /**
-         *
-         */
-        private String readContent() throws IOException {
-            FileChannel ch = lockFile.getChannel();
-
-            ByteBuffer buf = ByteBuffer.allocate((int)(ch.size() - 1));
-
-            ch.read(buf, 1);
-
-            String content = new String(buf.array());
-
-            buf.clear();
-
-            return content;
-        }
-
-        /** Locked or not. */
-        public boolean isLocked() {
-            return lock != null && lock.isValid();
-        }
-
-        /** Releases file lock */
-        public void release() {
-            U.releaseQuiet(lock);
-        }
-
-        /** Closes file channel */
-        @Override public void close() {
-            release();
-
-            U.closeQuiet(lockFile);
-        }
-
-        /**
-         * @return Absolute path to lock file.
-         */
-        private String lockPath() {
-            return file.getAbsolutePath();
+        /** {@inheritDoc} */
+        @Override protected String warningMessage(String lockId) {
+            return "Failed to acquire file lock. Will try again in 1s " +
+                "[nodeId=" + ctx.localNodeId() + ", holder=" + lockId +
+                ", path=" + lockPath() + ']';
         }
     }
 
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/filename/PdsConsistentIdProcessor.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/filename/PdsConsistentIdProcessor.java
index 951a2e1..35f06dd 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/filename/PdsConsistentIdProcessor.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/filename/PdsConsistentIdProcessor.java
@@ -35,7 +35,7 @@ import org.apache.ignite.configuration.DataStorageConfiguration;
 import org.apache.ignite.configuration.IgniteConfiguration;
 import org.apache.ignite.internal.GridKernalContext;
 import org.apache.ignite.internal.processors.GridProcessorAdapter;
-import org.apache.ignite.internal.processors.cache.persistence.GridCacheDatabaseSharedManager;
+import org.apache.ignite.internal.processors.cache.persistence.GridCacheDatabaseSharedManager.NodeFileLockHolder;
 import org.apache.ignite.internal.util.typedef.internal.A;
 import org.apache.ignite.internal.util.typedef.internal.CU;
 import org.apache.ignite.internal.util.typedef.internal.SB;
@@ -179,7 +179,7 @@ public class PdsConsistentIdProcessor extends GridProcessorAdapter implements Pd
         // If such a folder exists, we start up with this ID (compatibility mode)
         final String subFolder = U.maskForFileName(consistentId.toString());
 
-        final GridCacheDatabaseSharedManager.FileLockHolder oldStyleFolderLockHolder = tryLock(new File(pstStoreBasePath, subFolder));
+        final NodeFileLockHolder oldStyleFolderLockHolder = tryLock(new File(pstStoreBasePath, subFolder));
 
         if (oldStyleFolderLockHolder != null)
             return new PdsFolderSettings(pstStoreBasePath,
@@ -199,7 +199,7 @@ public class PdsConsistentIdProcessor extends GridProcessorAdapter implements Pd
         }
 
         for (FolderCandidate next : getNodeIndexSortedCandidates(pstStoreBasePath)) {
-            final GridCacheDatabaseSharedManager.FileLockHolder fileLockHolder = tryLock(next.subFolderFile());
+            final NodeFileLockHolder fileLockHolder = tryLock(next.subFolderFile());
 
             if (fileLockHolder != null) {
                 if (log.isInfoEnabled())
@@ -214,7 +214,7 @@ public class PdsConsistentIdProcessor extends GridProcessorAdapter implements Pd
         }
 
         // was not able to find free slot, allocating new
-        try (final GridCacheDatabaseSharedManager.FileLockHolder rootDirLock = lockRootDirectory(pstStoreBasePath)) {
+        try (final NodeFileLockHolder rootDirLock = lockRootDirectory(pstStoreBasePath)) {
             final List<FolderCandidate> sortedCandidates = getNodeIndexSortedCandidates(pstStoreBasePath);
             final int nodeIdx = sortedCandidates.isEmpty() ? 0 : (sortedCandidates.get(sortedCandidates.size() - 1).nodeIndex() + 1);
 
@@ -327,7 +327,7 @@ public class PdsConsistentIdProcessor extends GridProcessorAdapter implements Pd
         final UUID uuid = UUID.randomUUID();
         final String consIdBasedFolder = genNewStyleSubfolderName(nodeIdx, uuid);
         final File newRandomFolder = U.resolveWorkDirectory(pstStoreBasePath.getAbsolutePath(), consIdBasedFolder, false); //mkdir here
-        final GridCacheDatabaseSharedManager.FileLockHolder fileLockHolder = tryLock(newRandomFolder);
+        final NodeFileLockHolder fileLockHolder = tryLock(newRandomFolder);
 
         if (fileLockHolder != null) {
             if (log.isInfoEnabled())
@@ -362,10 +362,10 @@ public class PdsConsistentIdProcessor extends GridProcessorAdapter implements Pd
      * @return locked directory, should be released and closed later
      * @throws IgniteCheckedException if failed
      */
-    @NotNull private GridCacheDatabaseSharedManager.FileLockHolder lockRootDirectory(File pstStoreBasePath)
+    @NotNull private NodeFileLockHolder lockRootDirectory(File pstStoreBasePath)
         throws IgniteCheckedException {
 
-        GridCacheDatabaseSharedManager.FileLockHolder rootDirLock;
+        NodeFileLockHolder rootDirLock;
         int retry = 0;
 
         while ((rootDirLock = tryLock(pstStoreBasePath)) == null) {
@@ -414,13 +414,13 @@ public class PdsConsistentIdProcessor extends GridProcessorAdapter implements Pd
      * @return non null holder if lock was successful, null in case lock failed. If directory does not exist method will
      * always fail to lock.
      */
-    private GridCacheDatabaseSharedManager.FileLockHolder tryLock(File dbStoreDirWithSubdirectory) {
+    private NodeFileLockHolder tryLock(File dbStoreDirWithSubdirectory) {
         if (!dbStoreDirWithSubdirectory.exists())
             return null;
 
         final String path = dbStoreDirWithSubdirectory.getAbsolutePath();
-        final GridCacheDatabaseSharedManager.FileLockHolder fileLockHolder
-            = new GridCacheDatabaseSharedManager.FileLockHolder(path, ctx, log);
+        final NodeFileLockHolder fileLockHolder
+            = new NodeFileLockHolder(path, ctx, log);
 
         try {
             fileLockHolder.tryLock(1000);
@@ -500,7 +500,7 @@ public class PdsConsistentIdProcessor extends GridProcessorAdapter implements Pd
     /** {@inheritDoc} */
     @Override public void stop(boolean cancel) throws IgniteCheckedException {
         if (settings != null) {
-            final GridCacheDatabaseSharedManager.FileLockHolder fileLockHolder = settings.getLockedFileLockHolder();
+            final NodeFileLockHolder fileLockHolder = settings.getLockedFileLockHolder();
 
             if (fileLockHolder != null)
                 fileLockHolder.close();
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/filename/PdsFolderSettings.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/filename/PdsFolderSettings.java
index c47cbc9..72e0720 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/filename/PdsFolderSettings.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/filename/PdsFolderSettings.java
@@ -19,7 +19,7 @@ package org.apache.ignite.internal.processors.cache.persistence.filename;
 
 import java.io.File;
 import java.io.Serializable;
-import org.apache.ignite.internal.processors.cache.persistence.GridCacheDatabaseSharedManager;
+import org.apache.ignite.internal.processors.cache.persistence.GridCacheDatabaseSharedManager.NodeFileLockHolder;
 import org.apache.ignite.internal.util.typedef.internal.S;
 import org.apache.ignite.internal.util.typedef.internal.U;
 import org.jetbrains.annotations.NotNull;
@@ -48,7 +48,7 @@ public class PdsFolderSettings {
      * directory. This value is to be used at activate instead of locking. <br> May be null in case preconfigured
      * consistent ID is used or in case lock holder was already taken by other processor.
      */
-    @Nullable private final GridCacheDatabaseSharedManager.FileLockHolder fileLockHolder;
+    @Nullable private final NodeFileLockHolder fileLockHolder;
 
     /**
      * Indicates if compatible mode is enabled, in that case all sub folders are generated from consistent ID without
@@ -68,7 +68,7 @@ public class PdsFolderSettings {
     public PdsFolderSettings(@Nullable final File persistentStoreRootPath,
         final String folderName,
         final Serializable consistentId,
-        @Nullable final GridCacheDatabaseSharedManager.FileLockHolder fileLockHolder,
+        @Nullable final NodeFileLockHolder fileLockHolder,
         final boolean compatible) {
 
         this.consistentId = consistentId;
@@ -125,7 +125,7 @@ public class PdsFolderSettings {
      *
      * @return File lock holder with prelocked db directory.
      */
-    @Nullable public GridCacheDatabaseSharedManager.FileLockHolder getLockedFileLockHolder() {
+    @Nullable public NodeFileLockHolder getLockedFileLockHolder() {
         return fileLockHolder;
     }