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

mhubail pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/asterixdb.git


The following commit(s) were added to refs/heads/master by this push:
     new 56bca65d94 [NO ISSUE][STO] Download metadata files on lazy caching
56bca65d94 is described below

commit 56bca65d9485c303d57e2e9bbd974a85aefe359a
Author: Murtadha Hubail <mhub...@apache.org>
AuthorDate: Tue Oct 10 03:09:54 2023 +0300

    [NO ISSUE][STO] Download metadata files on lazy caching
    
    - user model changes: no
    - storage format changes: no
    - interface changes: yes
    
    Details:
    
    - Download metadata files for the node storage partitions
      on bootstrap on lazy caching.
    - Use the local + uncached files list to perform list operations
      rather than calling the cloud API.
    - Delay partition clean up on promotion for cloud deployments.
    
    Change-Id: I8ba767d5ffa0257a429af8c455bacea3df7ff7a7
    Reviewed-on: https://asterix-gerrit.ics.uci.edu/c/asterixdb/+/17856
    Reviewed-by: Wail Alkowaileet <wael....@gmail.com>
    Integration-Tests: Jenkins <jenk...@fulliautomatix.ics.uci.edu>
    Tested-by: Jenkins <jenk...@fulliautomatix.ics.uci.edu>
---
 .../org/apache/asterix/app/cc/GlobalTxManager.java |  3 ++-
 .../org/apache/asterix/app/nc/ReplicaManager.java  |  3 +--
 .../app/replication/NcLifecycleCoordinator.java    |  5 ++--
 .../apache/asterix/cloud/LazyCloudIOManager.java   | 28 +++++++++++++++----
 .../apache/asterix/cloud/lazy/IParallelCacher.java |  4 +++
 .../asterix/cloud/lazy/NoOpParallelCacher.java     |  8 ++++++
 .../apache/asterix/cloud/lazy/ParallelCacher.java  | 31 +++++++++++++++-------
 .../lazy/accessor/ReplaceableCloudAccessor.java    | 21 ++++++++++++---
 .../asterix/common/utils/StorageConstants.java     |  1 +
 .../asterix/common/utils/StoragePathUtil.java      |  8 ++++++
 .../PersistentLocalResourceRepository.java         |  1 +
 .../hyracks/storage/common/LocalResource.java      |  2 +-
 12 files changed, 91 insertions(+), 24 deletions(-)

diff --git 
a/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/cc/GlobalTxManager.java
 
b/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/cc/GlobalTxManager.java
index b4223bc4cb..0786895581 100644
--- 
a/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/cc/GlobalTxManager.java
+++ 
b/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/cc/GlobalTxManager.java
@@ -32,6 +32,7 @@ import org.apache.asterix.common.cluster.IGlobalTxManager;
 import org.apache.asterix.common.exceptions.ACIDException;
 import org.apache.asterix.common.messaging.api.ICCMessageBroker;
 import org.apache.asterix.common.transactions.IGlobalTransactionContext;
+import org.apache.asterix.common.utils.StorageConstants;
 import 
org.apache.asterix.transaction.management.service.transaction.GlobalTransactionContext;
 import 
org.apache.asterix.transaction.management.service.transaction.GlobalTxInfo;
 import org.apache.hyracks.api.application.ICCServiceContext;
@@ -182,7 +183,7 @@ public class GlobalTxManager implements IGlobalTxManager {
 
     @Override
     public void rollback() throws Exception {
-        Set<FileReference> txnLogFileRefs = 
ioManager.list(ioManager.resolve("."));
+        Set<FileReference> txnLogFileRefs = 
ioManager.list(ioManager.resolve(StorageConstants.GLOBAL_TXN_DIR_NAME));
         for (FileReference txnLogFileRef : txnLogFileRefs) {
             IGlobalTransactionContext context = new 
GlobalTransactionContext(txnLogFileRef, ioManager);
             txnContextRepository.put(context.getJobId(), context);
diff --git 
a/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/nc/ReplicaManager.java
 
b/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/nc/ReplicaManager.java
index 0684442bee..101b1b0fe2 100644
--- 
a/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/nc/ReplicaManager.java
+++ 
b/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/nc/ReplicaManager.java
@@ -129,9 +129,8 @@ public class ReplicaManager implements IReplicaManager {
         LOGGER.warn("promoting partition {}", partition);
         final PersistentLocalResourceRepository localResourceRepository =
                 (PersistentLocalResourceRepository) 
appCtx.getLocalResourceRepository();
-        localResourceRepository.cleanup(partition);
-        localResourceRepository.clearResourcesCache();
         if (!appCtx.isCloudDeployment()) {
+            localResourceRepository.cleanup(partition);
             final IRecoveryManager recoveryManager = 
appCtx.getTransactionSubsystem().getRecoveryManager();
             
recoveryManager.replayReplicaPartitionLogs(Stream.of(partition).collect(Collectors.toSet()),
 true);
         }
diff --git 
a/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/replication/NcLifecycleCoordinator.java
 
b/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/replication/NcLifecycleCoordinator.java
index 5d0901d1a5..4135f356b3 100644
--- 
a/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/replication/NcLifecycleCoordinator.java
+++ 
b/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/replication/NcLifecycleCoordinator.java
@@ -219,7 +219,7 @@ public class NcLifecycleCoordinator implements 
INcLifecycleCoordinator {
     protected List<INCLifecycleTask> buildIdleNcRegTasks(String newNodeId, 
boolean metadataNode, SystemState state,
             Set<Integer> activePartitions) {
         final List<INCLifecycleTask> tasks = new ArrayList<>();
-        Set<Integer> nodeActivePartitions = getNodeActivePartitions(newNodeId, 
activePartitions, metadataNode);
+        Set<Integer> nodeActivePartitions = getNodeActivePartitions(newNodeId, 
activePartitions, metadataNode, state);
         tasks.add(new UpdateNodeStatusTask(NodeStatus.BOOTING, 
nodeActivePartitions));
         int metadataPartitionId = 
clusterManager.getMetadataPartition().getPartitionId();
         // Add any cloud-related tasks
@@ -322,7 +322,8 @@ public class NcLifecycleCoordinator implements 
INcLifecycleCoordinator {
         return true;
     }
 
-    protected Set<Integer> getNodeActivePartitions(String nodeId, Set<Integer> 
nodePartitions, boolean metadataNode) {
+    protected Set<Integer> getNodeActivePartitions(String nodeId, Set<Integer> 
nodePartitions, boolean metadataNode,
+            SystemState state) {
         if (metadataNode) {
             
nodePartitions.add(clusterManager.getMetadataPartition().getPartitionId());
         }
diff --git 
a/asterixdb/asterix-cloud/src/main/java/org/apache/asterix/cloud/LazyCloudIOManager.java
 
b/asterixdb/asterix-cloud/src/main/java/org/apache/asterix/cloud/LazyCloudIOManager.java
index 1a99a345a9..97a61733f0 100644
--- 
a/asterixdb/asterix-cloud/src/main/java/org/apache/asterix/cloud/LazyCloudIOManager.java
+++ 
b/asterixdb/asterix-cloud/src/main/java/org/apache/asterix/cloud/LazyCloudIOManager.java
@@ -18,6 +18,7 @@
  */
 package org.apache.asterix.cloud;
 
+import static org.apache.asterix.cloud.lazy.ParallelCacher.METADATA_FILTER;
 import static 
org.apache.asterix.common.utils.StorageConstants.PARTITION_DIR_PREFIX;
 import static 
org.apache.asterix.common.utils.StorageConstants.STORAGE_ROOT_DIR_NAME;
 
@@ -79,7 +80,8 @@ final class LazyCloudIOManager extends AbstractCloudIOManager 
{
      */
 
     @Override
-    protected void downloadPartitions(boolean metadataNode, int 
metadataPartition) throws HyracksDataException {
+    protected synchronized void downloadPartitions(boolean metadataNode, int 
metadataPartition)
+            throws HyracksDataException {
         // Get the files in all relevant partitions from the cloud
         Set<String> cloudFiles = cloudClient.listObjects(bucket, 
STORAGE_ROOT_DIR_NAME, IoUtil.NO_OP_FILTER).stream()
                 .filter(f -> 
partitions.contains(StoragePathUtil.getPartitionNumFromRelativePath(f)))
@@ -99,12 +101,15 @@ final class LazyCloudIOManager extends 
AbstractCloudIOManager {
         cloudFiles.removeAll(localFiles);
         int remainingUncachedFiles = cloudFiles.size();
         if (remainingUncachedFiles > 0) {
+            LOGGER.debug("The number of uncached files: {}. Uncached files: 
{}", remainingUncachedFiles, cloudFiles);
             // Get list of FileReferences from the list of cloud (i.e., 
resolve each path's string to FileReference)
             List<FileReference> uncachedFiles = resolve(cloudFiles);
             // Create a parallel downloader using the given cloudClient
             IParallelDownloader downloader = 
cloudClient.createParallelDownloader(bucket, localIoManager);
             // Download metadata partition (if this node is a metadata node)
             downloadMetadataPartition(downloader, uncachedFiles, metadataNode, 
metadataPartition);
+            // Download all metadata files to avoid (List) calls to the cloud 
when listing/reading these files
+            downloadMetadataFiles(downloader, uncachedFiles);
             // Create a parallel cacher which download and monitor all 
uncached files
             ParallelCacher cacher = new ParallelCacher(downloader, 
uncachedFiles);
             // Local cache misses some files, cloud-based accessor is needed 
for read operations
@@ -113,20 +118,18 @@ final class LazyCloudIOManager extends 
AbstractCloudIOManager {
             // Everything is cached, no need to invoke cloud-based accessor 
for read operations
             accessor = new LocalAccessor(cloudClient, bucket, localIoManager);
         }
-
-        LOGGER.info("The number of uncached files: {}. Uncached files: {}", 
remainingUncachedFiles, cloudFiles);
     }
 
     private void downloadMetadataPartition(IParallelDownloader downloader, 
List<FileReference> uncachedFiles,
             boolean metadataNode, int metadataPartition) throws 
HyracksDataException {
         String partitionDir = PARTITION_DIR_PREFIX + metadataPartition;
         if (metadataNode && uncachedFiles.stream().anyMatch(f -> 
f.getRelativePath().contains(partitionDir))) {
-            LOGGER.info("Downloading metadata partition {}, Current uncached 
files: {}", metadataPartition,
+            LOGGER.debug("Downloading metadata partition {}, Current uncached 
files: {}", metadataPartition,
                     uncachedFiles);
             FileReference metadataDir = resolve(STORAGE_ROOT_DIR_NAME + 
File.separator + partitionDir);
             downloader.downloadDirectories(Collections.singleton(metadataDir));
             uncachedFiles.removeIf(f -> 
f.getRelativePath().contains(partitionDir));
-            LOGGER.info("Finished downloading metadata partition. Current 
uncached files: {}", uncachedFiles);
+            LOGGER.debug("Finished downloading metadata partition. Current 
uncached files: {}", uncachedFiles);
         }
     }
 
@@ -192,4 +195,19 @@ final class LazyCloudIOManager extends 
AbstractCloudIOManager {
             LOGGER.debug("{} {}", op, fileReference.getRelativePath());
         }
     }
+
+    private void downloadMetadataFiles(IParallelDownloader downloader, 
List<FileReference> uncachedFiles)
+            throws HyracksDataException {
+        Set<FileReference> uncachedMetadataFiles = 
ParallelCacher.getFiles(uncachedFiles, METADATA_FILTER);
+        if (!uncachedMetadataFiles.isEmpty()) {
+            LOGGER.debug("Downloading metadata files for all partitions; 
current uncached files: {}", uncachedFiles);
+            downloader.downloadFiles(uncachedMetadataFiles);
+            uncachedFiles.removeAll(uncachedMetadataFiles);
+            LOGGER.debug("Finished downloading metadata files for all 
partitions. Current uncached files: {}",
+                    uncachedFiles);
+        } else {
+            LOGGER.debug("all metadata files for all partitions are already 
cached; current uncached files: {} ",
+                    uncachedFiles);
+        }
+    }
 }
diff --git 
a/asterixdb/asterix-cloud/src/main/java/org/apache/asterix/cloud/lazy/IParallelCacher.java
 
b/asterixdb/asterix-cloud/src/main/java/org/apache/asterix/cloud/lazy/IParallelCacher.java
index 7e4bc4fe76..66003563bd 100644
--- 
a/asterixdb/asterix-cloud/src/main/java/org/apache/asterix/cloud/lazy/IParallelCacher.java
+++ 
b/asterixdb/asterix-cloud/src/main/java/org/apache/asterix/cloud/lazy/IParallelCacher.java
@@ -18,7 +18,9 @@
  */
 package org.apache.asterix.cloud.lazy;
 
+import java.io.FilenameFilter;
 import java.util.Collection;
+import java.util.Set;
 
 import org.apache.hyracks.api.exceptions.HyracksDataException;
 import org.apache.hyracks.api.io.FileReference;
@@ -32,6 +34,8 @@ public interface IParallelCacher {
      */
     boolean isCached(FileReference indexDir);
 
+    Set<FileReference> getUncachedFiles(FileReference dir, FilenameFilter 
filter);
+
     /**
      * Downloads all index's data files for all partitions.
      * The index is inferred from the path of the provided file.
diff --git 
a/asterixdb/asterix-cloud/src/main/java/org/apache/asterix/cloud/lazy/NoOpParallelCacher.java
 
b/asterixdb/asterix-cloud/src/main/java/org/apache/asterix/cloud/lazy/NoOpParallelCacher.java
index 010433dbbf..a77652c1b3 100644
--- 
a/asterixdb/asterix-cloud/src/main/java/org/apache/asterix/cloud/lazy/NoOpParallelCacher.java
+++ 
b/asterixdb/asterix-cloud/src/main/java/org/apache/asterix/cloud/lazy/NoOpParallelCacher.java
@@ -18,7 +18,10 @@
  */
 package org.apache.asterix.cloud.lazy;
 
+import java.io.FilenameFilter;
 import java.util.Collection;
+import java.util.Collections;
+import java.util.Set;
 
 import org.apache.hyracks.api.io.FileReference;
 
@@ -30,6 +33,11 @@ public class NoOpParallelCacher implements IParallelCacher {
         return false;
     }
 
+    @Override
+    public Set<FileReference> getUncachedFiles(FileReference dir, 
FilenameFilter filter) {
+        return Collections.emptySet();
+    }
+
     @Override
     public boolean downloadData(FileReference indexFile) {
         return false;
diff --git 
a/asterixdb/asterix-cloud/src/main/java/org/apache/asterix/cloud/lazy/ParallelCacher.java
 
b/asterixdb/asterix-cloud/src/main/java/org/apache/asterix/cloud/lazy/ParallelCacher.java
index 2d55d06598..3cc481e607 100644
--- 
a/asterixdb/asterix-cloud/src/main/java/org/apache/asterix/cloud/lazy/ParallelCacher.java
+++ 
b/asterixdb/asterix-cloud/src/main/java/org/apache/asterix/cloud/lazy/ParallelCacher.java
@@ -26,6 +26,7 @@ import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
+import java.util.stream.Collectors;
 
 import org.apache.asterix.cloud.clients.IParallelDownloader;
 import org.apache.asterix.common.utils.StorageConstants;
@@ -41,12 +42,11 @@ import org.apache.logging.log4j.Logger;
  * @see org.apache.asterix.cloud.lazy.accessor.ReplaceableCloudAccessor
  */
 public final class ParallelCacher implements IParallelCacher {
-    private static final Logger LOGGER = LogManager.getLogger();
-    private static final FilenameFilter METADATA_FILTER =
-            ((dir, name) -> 
name.startsWith(StorageConstants.INDEX_NON_DATA_FILES_PREFIX));
 
+    public static final FilenameFilter METADATA_FILTER =
+            ((dir, name) -> 
name.startsWith(StorageConstants.INDEX_NON_DATA_FILES_PREFIX));
+    private static final Logger LOGGER = LogManager.getLogger();
     private final IParallelDownloader downloader;
-
     /**
      * Uncached Indexes subpaths
      */
@@ -82,6 +82,19 @@ public final class ParallelCacher implements IParallelCacher 
{
         return !indexSubPath.isEmpty() && 
!uncachedIndexes.contains(indexSubPath);
     }
 
+    @Override
+    public Set<FileReference> getUncachedFiles(FileReference dir, 
FilenameFilter filter) {
+        if 
(dir.getRelativePath().endsWith(StorageConstants.STORAGE_ROOT_DIR_NAME)) {
+            return uncachedDataFiles.stream()
+                    .filter(f -> StoragePathUtil.hasSameStorageRoot(dir, f) && 
filter.accept(null, f.getName()))
+                    .collect(Collectors.toSet());
+        }
+        return uncachedDataFiles
+                .stream().filter(f -> StoragePathUtil.hasSameStorageRoot(dir, 
f)
+                        && StoragePathUtil.isRelativeParent(dir, f) && 
filter.accept(null, f.getName()))
+                .collect(Collectors.toSet());
+    }
+
     @Override
     public synchronized boolean downloadData(FileReference indexFile) throws 
HyracksDataException {
         String indexSubPath = StoragePathUtil.getIndexSubPath(indexFile, 
false);
@@ -92,13 +105,13 @@ public final class ParallelCacher implements 
IParallelCacher {
             }
         }
 
-        LOGGER.info("Downloading data files for {} in all partitions: {}", 
indexSubPath, toDownload);
+        LOGGER.debug("Downloading data files for {} in all partitions: {}", 
indexSubPath, toDownload);
         Collection<FileReference> failed = 
downloader.downloadDirectories(toDownload);
         if (!failed.isEmpty()) {
             LOGGER.warn("Failed to download data files {}. Re-downloading: 
{}", indexSubPath, failed);
             downloader.downloadFiles(failed);
         }
-        LOGGER.info("Finished downloading data files for {}", indexSubPath);
+        LOGGER.debug("Finished downloading data files for {}", indexSubPath);
         uncachedIndexes.remove(indexSubPath);
         uncachedDataFiles.removeIf(f -> 
f.getRelativePath().contains(indexSubPath));
         return isEmpty();
@@ -114,9 +127,9 @@ public final class ParallelCacher implements 
IParallelCacher {
             }
         }
 
-        LOGGER.info("Downloading metadata files for {} in all partitions: {}", 
indexSubPath, toDownload);
+        LOGGER.debug("Downloading metadata files for {} in all partitions: 
{}", indexSubPath, toDownload);
         downloader.downloadFiles(toDownload);
-        LOGGER.info("Finished downloading metadata files for {}", 
indexSubPath);
+        LOGGER.debug("Finished downloading metadata files for {}", 
indexSubPath);
         uncachedMetadataFiles.removeAll(toDownload);
         return isEmpty();
     }
@@ -149,7 +162,7 @@ public final class ParallelCacher implements 
IParallelCacher {
         LOGGER.info("Parallel cacher was closed");
     }
 
-    private Set<FileReference> getFiles(List<FileReference> uncachedFiles, 
FilenameFilter filter) {
+    public static Set<FileReference> getFiles(List<FileReference> 
uncachedFiles, FilenameFilter filter) {
         Set<FileReference> fileReferences = ConcurrentHashMap.newKeySet();
         for (FileReference fileReference : uncachedFiles) {
             if (filter.accept(null, fileReference.getName())) {
diff --git 
a/asterixdb/asterix-cloud/src/main/java/org/apache/asterix/cloud/lazy/accessor/ReplaceableCloudAccessor.java
 
b/asterixdb/asterix-cloud/src/main/java/org/apache/asterix/cloud/lazy/accessor/ReplaceableCloudAccessor.java
index 3dd579b7e4..bfa353a2a0 100644
--- 
a/asterixdb/asterix-cloud/src/main/java/org/apache/asterix/cloud/lazy/accessor/ReplaceableCloudAccessor.java
+++ 
b/asterixdb/asterix-cloud/src/main/java/org/apache/asterix/cloud/lazy/accessor/ReplaceableCloudAccessor.java
@@ -26,17 +26,21 @@ import org.apache.asterix.cloud.CloudFileHandle;
 import org.apache.asterix.cloud.bulk.IBulkOperationCallBack;
 import org.apache.asterix.cloud.clients.ICloudClient;
 import org.apache.asterix.cloud.lazy.IParallelCacher;
+import org.apache.asterix.common.utils.StorageConstants;
 import org.apache.asterix.common.utils.StoragePathUtil;
 import org.apache.hyracks.api.exceptions.HyracksDataException;
 import org.apache.hyracks.api.io.FileReference;
 import org.apache.hyracks.api.io.IIOManager;
 import org.apache.hyracks.control.nc.io.IOManager;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
 
 /**
  * ReplaceableCloudAccessor will be used when some (or all) of the files in 
the cloud storage are not cached locally.
  * It will be replaced by {@link LocalAccessor} once everything is cached
  */
 public class ReplaceableCloudAccessor extends AbstractLazyAccessor {
+    private static final Logger LOGGER = LogManager.getLogger();
     private final Set<Integer> partitions;
     private final ILazyAccessorReplacer replacer;
     private final IParallelCacher cacher;
@@ -78,10 +82,18 @@ public class ReplaceableCloudAccessor extends 
AbstractLazyAccessor {
 
     @Override
     public Set<FileReference> doList(FileReference dir, FilenameFilter filter) 
throws HyracksDataException {
-        if (cacher.isCached(dir)) {
-            return localIoManager.list(dir, filter);
+        if (isTxnDir(dir)) {
+            return cloudBackedList(dir, filter);
         }
-        return cloudBackedList(dir, filter);
+        Set<FileReference> localList = localIoManager.list(dir, filter);
+        Set<FileReference> uncachedFiles = cacher.getUncachedFiles(dir, 
filter);
+        localList.addAll(uncachedFiles);
+        return localList;
+    }
+
+    private static boolean isTxnDir(FileReference dir) {
+        return 
dir.getRelativePath().startsWith(StorageConstants.METADATA_TXN_NOWAL_DIR_NAME)
+                || dir.getName().equals(StorageConstants.GLOBAL_TXN_DIR_NAME);
     }
 
     @Override
@@ -129,6 +141,7 @@ public class ReplaceableCloudAccessor extends 
AbstractLazyAccessor {
     }
 
     private Set<FileReference> cloudBackedList(FileReference dir, 
FilenameFilter filter) throws HyracksDataException {
+        LOGGER.debug("CLOUD LIST: {}", dir);
         Set<String> cloudFiles = cloudClient.listObjects(bucket, 
dir.getRelativePath(), filter);
         if (cloudFiles.isEmpty()) {
             return Collections.emptySet();
@@ -151,7 +164,7 @@ public class ReplaceableCloudAccessor extends 
AbstractLazyAccessor {
         // Add the remaining files that are not stored locally in their 
designated partitions (if any)
         for (String cloudFile : cloudFiles) {
             FileReference localFile = localIoManager.resolve(cloudFile);
-            if (isInNodePartition(cloudFile) && 
dir.getDeviceHandle().equals(localFile.getDeviceHandle())) {
+            if (isInNodePartition(cloudFile) && 
StoragePathUtil.hasSameStorageRoot(dir, localFile)) {
                 localFiles.add(localFile);
             }
         }
diff --git 
a/asterixdb/asterix-common/src/main/java/org/apache/asterix/common/utils/StorageConstants.java
 
b/asterixdb/asterix-common/src/main/java/org/apache/asterix/common/utils/StorageConstants.java
index ed9c48e68b..5dcaaf493a 100644
--- 
a/asterixdb/asterix-common/src/main/java/org/apache/asterix/common/utils/StorageConstants.java
+++ 
b/asterixdb/asterix-common/src/main/java/org/apache/asterix/common/utils/StorageConstants.java
@@ -31,6 +31,7 @@ import 
org.apache.hyracks.storage.am.lsm.common.impls.ConcurrentMergePolicyFacto
 public class StorageConstants {
 
     public static final String METADATA_TXN_NOWAL_DIR_NAME = "mtd-txn-logs";
+    public static final String GLOBAL_TXN_DIR_NAME = ".";
     public static final String STORAGE_ROOT_DIR_NAME = "storage";
     public static final String INGESTION_LOGS_DIR_NAME = "ingestion_logs";
     public static final String PARTITION_DIR_PREFIX = "partition_";
diff --git 
a/asterixdb/asterix-common/src/main/java/org/apache/asterix/common/utils/StoragePathUtil.java
 
b/asterixdb/asterix-common/src/main/java/org/apache/asterix/common/utils/StoragePathUtil.java
index 9862be7826..e9a8753995 100644
--- 
a/asterixdb/asterix-common/src/main/java/org/apache/asterix/common/utils/StoragePathUtil.java
+++ 
b/asterixdb/asterix-common/src/main/java/org/apache/asterix/common/utils/StoragePathUtil.java
@@ -218,4 +218,12 @@ public class StoragePathUtil {
         }
         return relativePath.substring(start, end);
     }
+
+    public static boolean hasSameStorageRoot(FileReference file1, 
FileReference file2) {
+        return file1.getDeviceHandle().equals(file2.getDeviceHandle());
+    }
+
+    public static boolean isRelativeParent(FileReference parent, FileReference 
child) {
+        return child.getRelativePath().startsWith(parent.getRelativePath());
+    }
 }
diff --git 
a/asterixdb/asterix-transactions/src/main/java/org/apache/asterix/transaction/management/resource/PersistentLocalResourceRepository.java
 
b/asterixdb/asterix-transactions/src/main/java/org/apache/asterix/transaction/management/resource/PersistentLocalResourceRepository.java
index 33cbf2dc04..96ae699588 100644
--- 
a/asterixdb/asterix-transactions/src/main/java/org/apache/asterix/transaction/management/resource/PersistentLocalResourceRepository.java
+++ 
b/asterixdb/asterix-transactions/src/main/java/org/apache/asterix/transaction/management/resource/PersistentLocalResourceRepository.java
@@ -566,6 +566,7 @@ public class PersistentLocalResourceRepository implements 
ILocalResourceReposito
                 throw HyracksDataException.create(e);
             }
         } finally {
+            clearResourcesCache();
             afterReadAccess();
         }
     }
diff --git 
a/hyracks-fullstack/hyracks/hyracks-storage-common/src/main/java/org/apache/hyracks/storage/common/LocalResource.java
 
b/hyracks-fullstack/hyracks/hyracks-storage-common/src/main/java/org/apache/hyracks/storage/common/LocalResource.java
index f5e29455c0..7722fb1f01 100644
--- 
a/hyracks-fullstack/hyracks/hyracks-storage-common/src/main/java/org/apache/hyracks/storage/common/LocalResource.java
+++ 
b/hyracks-fullstack/hyracks/hyracks-storage-common/src/main/java/org/apache/hyracks/storage/common/LocalResource.java
@@ -106,7 +106,7 @@ public class LocalResource implements Serializable, 
IJsonSerializable {
     @Override
     public String toString() {
         return new 
StringBuilder("{\"").append(LocalResource.class.getSimpleName()).append("\" : 
").append("{\"id\" = ")
-                .append(id).append(", \"resource\" : 
").append(resource).append(", \"version\" : ").append(version)
+                .append(id).append(", \"resource\" : 
").append(getPath()).append(", \"version\" : ").append(version)
                 .append(" } ").toString();
     }
 

Reply via email to