Skip to content

Commit

Permalink
HDDS-10650. Delete hsync key info from openFileTable while deleting d…
Browse files Browse the repository at this point in the history
…irectory recursively. (apache#6495)
  • Loading branch information
ashishkumar50 authored Apr 24, 2024
1 parent f4d7716 commit fd188d1
Show file tree
Hide file tree
Showing 18 changed files with 216 additions and 58 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -387,6 +387,7 @@ private OzoneConsts() {
/** Metadata stored in OmKeyInfo. */
public static final String HSYNC_CLIENT_ID = "hsyncClientId";
public static final String LEASE_RECOVERY = "leaseRecovery";
public static final String DELETED_HSYNC_KEY = "deletedHsyncKey";
public static final String FORCE_LEASE_RECOVERY_ENV = "OZONE.CLIENT.RECOVER.LEASE.FORCE";

//GDPR
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,9 @@
import java.nio.charset.StandardCharsets;
import java.security.GeneralSecurityException;
import java.security.PrivilegedExceptionAction;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.TimeUnit;
Expand Down Expand Up @@ -84,6 +86,7 @@

import org.apache.hadoop.ozone.om.helpers.OmKeyInfo;
import org.apache.hadoop.ozone.om.helpers.RepeatedOmKeyInfo;
import org.apache.hadoop.ozone.om.service.OpenKeyCleanupService;
import org.apache.hadoop.security.UserGroupInformation;
import org.apache.hadoop.util.Time;
import org.apache.ozone.test.GenericTestUtils;
Expand Down Expand Up @@ -113,6 +116,11 @@
import static org.apache.hadoop.ozone.om.OMConfigKeys.OZONE_DEFAULT_BUCKET_LAYOUT;
import static org.apache.hadoop.ozone.om.OMConfigKeys.OZONE_OM_ADDRESS_KEY;
import static org.apache.hadoop.ozone.om.OMConfigKeys.OZONE_OM_RATIS_ENABLE_KEY;
import static org.apache.hadoop.ozone.om.OMConfigKeys.OZONE_DIR_DELETING_SERVICE_INTERVAL;
import static org.apache.hadoop.ozone.om.OMConfigKeys.OZONE_OM_OPEN_KEY_CLEANUP_SERVICE_INTERVAL;
import static org.apache.hadoop.ozone.om.OMConfigKeys.OZONE_OM_OPEN_KEY_EXPIRE_THRESHOLD;
import static org.apache.hadoop.ozone.om.OMConfigKeys.OZONE_OM_LEASE_HARD_LIMIT;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
Expand Down Expand Up @@ -144,6 +152,10 @@ public class TestHSync {
private static final int FLUSH_SIZE = 2 * CHUNK_SIZE;
private static final int MAX_FLUSH_SIZE = 2 * FLUSH_SIZE;
private static final int BLOCK_SIZE = 2 * MAX_FLUSH_SIZE;
private static final int SERVICE_INTERVAL = 100;
private static final int EXPIRE_THRESHOLD_MS = 140;

private static OpenKeyCleanupService openKeyCleanupService;

@BeforeAll
public static void init() throws Exception {
Expand All @@ -155,9 +167,18 @@ public static void init() throws Exception {
CONF.setInt(OZONE_SCM_RATIS_PIPELINE_LIMIT, 10);
// Reduce KeyDeletingService interval
CONF.setTimeDuration(OZONE_BLOCK_DELETING_SERVICE_INTERVAL, 100, TimeUnit.MILLISECONDS);
CONF.setTimeDuration(OZONE_DIR_DELETING_SERVICE_INTERVAL, 100, TimeUnit.MILLISECONDS);
CONF.setBoolean("ozone.client.incremental.chunk.list", true);
CONF.setBoolean("ozone.client.stream.putblock.piggybacking", true);
CONF.setBoolean(OZONE_CHUNK_LIST_INCREMENTAL, true);
CONF.setTimeDuration(OZONE_OM_OPEN_KEY_CLEANUP_SERVICE_INTERVAL,
SERVICE_INTERVAL, TimeUnit.MILLISECONDS);
CONF.setTimeDuration(OZONE_OM_OPEN_KEY_EXPIRE_THRESHOLD,
EXPIRE_THRESHOLD_MS, TimeUnit.MILLISECONDS);
CONF.setTimeDuration(OZONE_OM_LEASE_HARD_LIMIT,
EXPIRE_THRESHOLD_MS, TimeUnit.MILLISECONDS);
CONF.set(OzoneConfigKeys.OZONE_OM_LEASE_SOFT_LIMIT, "0s");

ClientConfigForTesting.newBuilder(StorageUnit.BYTES)
.setBlockSize(BLOCK_SIZE)
.setChunkSize(CHUNK_SIZE)
Expand All @@ -183,6 +204,10 @@ public static void init() throws Exception {
GenericTestUtils.setLogLevel(BlockOutputStream.LOG, Level.DEBUG);
GenericTestUtils.setLogLevel(BlockInputStream.LOG, Level.DEBUG);
GenericTestUtils.setLogLevel(KeyValueHandler.LOG, Level.DEBUG);

openKeyCleanupService =
(OpenKeyCleanupService) cluster.getOzoneManager().getKeyManager().getOpenKeyCleanupService();
openKeyCleanupService.suspend();
}

@AfterAll
Expand Down Expand Up @@ -393,6 +418,65 @@ public void testHSyncDeletedKey() throws Exception {
}
}

@Test
public void testHSyncOpenKeyDeletionWhileDeleteDirectory() throws Exception {
// Verify that when directory is deleted recursively hsync related openKeys should be deleted,

// Set the fs.defaultFS
final String rootPath = String.format("%s://%s/",
OZONE_OFS_URI_SCHEME, CONF.get(OZONE_OM_ADDRESS_KEY));
CONF.set(CommonConfigurationKeysPublic.FS_DEFAULT_NAME_KEY, rootPath);

final String dir = OZONE_ROOT + bucket.getVolumeName()
+ OZONE_URI_DELIMITER + bucket.getName() + OZONE_URI_DELIMITER + "dir1/dir2";
final Path key1 = new Path(dir, "hsync-key");

try (FileSystem fs = FileSystem.get(CONF)) {
// Create key1
try (FSDataOutputStream os = fs.create(key1, true)) {
os.write(1);
os.hsync();
// There should be 1 key in openFileTable
assertThat(1 == getOpenKeyInfo(BucketLayout.FILE_SYSTEM_OPTIMIZED).size());
// Delete directory recursively
fs.delete(new Path(OZONE_ROOT + bucket.getVolumeName() + OZONE_URI_DELIMITER +
bucket.getName() + OZONE_URI_DELIMITER + "dir1/"), true);

// Verify if DELETED_HSYNC_KEY metadata is added to openKey
GenericTestUtils.waitFor(() -> {
List<OmKeyInfo> omKeyInfo = getOpenKeyInfo(BucketLayout.FILE_SYSTEM_OPTIMIZED);
return omKeyInfo.size() > 0 && omKeyInfo.get(0).getMetadata().containsKey(OzoneConsts.DELETED_HSYNC_KEY);
}, 1000, 12000);

// Resume openKeyCleanupService
openKeyCleanupService.resume();

// Verify entry from openKey gets deleted eventually
GenericTestUtils.waitFor(() ->
0 == getOpenKeyInfo(BucketLayout.FILE_SYSTEM_OPTIMIZED).size(), 1000, 12000);
} catch (OMException ex) {
assertEquals(OMException.ResultCodes.DIRECTORY_NOT_FOUND, ex.getResult());
} finally {
openKeyCleanupService.suspend();
}
}
}

private List<OmKeyInfo> getOpenKeyInfo(BucketLayout bucketLayout) {
List<OmKeyInfo> omKeyInfo = new ArrayList<>();

Table<String, OmKeyInfo> openFileTable =
cluster.getOzoneManager().getMetadataManager().getOpenKeyTable(bucketLayout);
try (TableIterator<String, ? extends Table.KeyValue<String, OmKeyInfo>>
iterator = openFileTable.iterator()) {
while (iterator.hasNext()) {
omKeyInfo.add(iterator.next().getValue());
}
} catch (Exception e) {
}
return omKeyInfo;
}

@Test
public void testUncommittedBlocks() throws Exception {
// Set the fs.defaultFS
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1838,8 +1838,10 @@ public ExpiredOpenKeys getExpiredOpenKeys(Duration expireThreshold,
.filter(id -> id.equals(clientIdString))
.isPresent();

if (!isHsync && openKeyInfo.getCreationTime() <= expiredCreationTimestamp) {
if ((!isHsync && openKeyInfo.getCreationTime() <= expiredCreationTimestamp) ||
(openKeyInfo.getMetadata().containsKey(OzoneConsts.DELETED_HSYNC_KEY))) {
// add non-hsync'ed keys
// also add hsync keys which are already deleted from keyTable
expiredKeys.addOpenKey(openKeyInfo, dbOpenKeyName);
num++;
} else if (isHsync && openKeyInfo.getModificationTime() <= expiredLeaseTimestamp &&
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,10 @@ private RecoverLeaseResponse doWork(OzoneManager ozoneManager,
throw new OMException("Open Key " + dbOpenFileKey + " not found in openKeyTable", KEY_NOT_FOUND);
}

if (openKeyInfo.getMetadata().containsKey(OzoneConsts.DELETED_HSYNC_KEY)) {
throw new OMException("Open Key " + keyName + " is already deleted",
KEY_NOT_FOUND);
}
long openKeyModificationTime = openKeyInfo.getModificationTime();
if (openKeyInfo.getMetadata().containsKey(OzoneConsts.LEASE_RECOVERY)) {
LOG.debug("Key: " + keyName + " is already under recovery");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,10 @@ public OMClientResponse validateAndUpdateCache(OzoneManager ozoneManager, TermIn
throw new OMException("Open Key " + openKeyName + " is under lease recovery",
KEY_UNDER_LEASE_RECOVERY);
}
if (openKeyInfo.getMetadata().containsKey(OzoneConsts.DELETED_HSYNC_KEY)) {
throw new OMException("Open Key " + openKeyName + " is already deleted",
KEY_NOT_FOUND);
}
List<OmKeyLocationInfo> newLocationList = Collections.singletonList(
OmKeyLocationInfo.getFromProtobuf(blockLocation));

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,11 @@
import java.io.IOException;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.commons.lang3.tuple.Pair;
import org.apache.hadoop.ozone.OzoneConsts;
import org.apache.hadoop.ozone.om.OMMetrics;
import org.apache.ratis.server.protocol.TermIndex;
import org.apache.hadoop.ozone.om.OMMetadataManager;
Expand All @@ -39,8 +41,7 @@
import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.OMRequest;
import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.OMResponse;

import java.util.List;

import static org.apache.hadoop.ozone.OzoneConsts.DELETED_HSYNC_KEY;
import static org.apache.hadoop.ozone.om.lock.OzoneManagerLock.Resource.BUCKET_LOCK;

/**
Expand All @@ -66,6 +67,7 @@ public OMClientResponse validateAndUpdateCache(OzoneManager ozoneManager, TermIn
Set<Pair<String, String>> lockSet = new HashSet<>();
Map<Pair<String, String>, OmBucketInfo> volBucketInfoMap = new HashMap<>();
OMMetadataManager omMetadataManager = ozoneManager.getMetadataManager();
Map<String, OmKeyInfo> openKeyInfoMap = new HashMap<>();

OMMetrics omMetrics = ozoneManager.getMetrics();
try {
Expand Down Expand Up @@ -110,6 +112,21 @@ public OMClientResponse validateAndUpdateCache(OzoneManager ozoneManager, TermIn
volumeName, bucketName);
lockSet.add(volBucketPair);
}

// If omKeyInfo has hsync metadata, delete its corresponding open key as well
String dbOpenKey;
String hsyncClientId = keyInfo.getMetadata().get(OzoneConsts.HSYNC_CLIENT_ID);
if (hsyncClientId != null) {
long parentId = keyInfo.getParentObjectID();
dbOpenKey = omMetadataManager.getOpenFileName(path.getVolumeId(), path.getBucketId(),
parentId, keyInfo.getFileName(), hsyncClientId);
OmKeyInfo openKeyInfo = omMetadataManager.getOpenKeyTable(getBucketLayout()).get(dbOpenKey);
if (openKeyInfo != null) {
openKeyInfo.getMetadata().put(DELETED_HSYNC_KEY, "true");
openKeyInfoMap.put(dbOpenKey, openKeyInfo);
}
}

omMetrics.decNumKeys();
OmBucketInfo omBucketInfo = getBucketInfo(omMetadataManager,
volumeName, bucketName);
Expand Down Expand Up @@ -142,7 +159,7 @@ public OMClientResponse validateAndUpdateCache(OzoneManager ozoneManager, TermIn
getOmRequest());
OMClientResponse omClientResponse = new OMDirectoriesPurgeResponseWithFSO(
omResponse.build(), purgeRequests, ozoneManager.isRatisEnabled(),
getBucketLayout(), volBucketInfoMap, fromSnapshotInfo);
getBucketLayout(), volBucketInfoMap, fromSnapshotInfo, openKeyInfoMap);

return omClientResponse;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,10 @@ public OMClientResponse validateAndUpdateCache(OzoneManager ozoneManager, TermIn
throw new OMException("Failed to " + action + " key, as " + dbOpenKey +
" entry is not found in the OpenKey table", KEY_NOT_FOUND);
}
if (omKeyInfo.getMetadata().containsKey(OzoneConsts.DELETED_HSYNC_KEY)) {
throw new OMException("Open Key " + keyName + " is already deleted",
KEY_NOT_FOUND);
}
if (omKeyInfo.getMetadata().containsKey(OzoneConsts.LEASE_RECOVERY) &&
omKeyInfo.getMetadata().containsKey(OzoneConsts.HSYNC_CLIENT_ID)) {
if (!isRecovery) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@
import org.apache.hadoop.hdds.utils.db.cache.CacheKey;
import org.apache.hadoop.hdds.utils.db.cache.CacheValue;

import static org.apache.hadoop.ozone.OzoneConsts.DELETED_HSYNC_KEY;
import static org.apache.hadoop.ozone.om.exceptions.OMException.ResultCodes.KEY_NOT_FOUND;
import static org.apache.hadoop.ozone.om.lock.OzoneManagerLock.Resource.BUCKET_LOCK;

Expand Down Expand Up @@ -161,6 +162,7 @@ public OMClientResponse validateAndUpdateCache(OzoneManager ozoneManager, TermIn
long quotaReleased = sumBlockLengths(omKeyInfo);
omBucketInfo.incrUsedBytes(-quotaReleased);
omBucketInfo.incrUsedNamespace(-1L);
OmKeyInfo deletedOpenKeyInfo = null;

// If omKeyInfo has hsync metadata, delete its corresponding open key as well
String dbOpenKey = null;
Expand All @@ -170,8 +172,9 @@ public OMClientResponse validateAndUpdateCache(OzoneManager ozoneManager, TermIn
dbOpenKey = omMetadataManager.getOpenKey(volumeName, bucketName, keyName, hsyncClientId);
OmKeyInfo openKeyInfo = openKeyTable.get(dbOpenKey);
if (openKeyInfo != null) {
// Remove the open key by putting a tombstone entry
openKeyTable.addCacheEntry(dbOpenKey, trxnLogIndex);
openKeyInfo.getMetadata().put(DELETED_HSYNC_KEY, "true");
openKeyTable.addCacheEntry(dbOpenKey, openKeyInfo, trxnLogIndex);
deletedOpenKeyInfo = openKeyInfo;
} else {
LOG.warn("Potentially inconsistent DB state: open key not found with dbOpenKey '{}'", dbOpenKey);
}
Expand All @@ -180,7 +183,7 @@ public OMClientResponse validateAndUpdateCache(OzoneManager ozoneManager, TermIn
omClientResponse = new OMKeyDeleteResponse(
omResponse.setDeleteKeyResponse(DeleteKeyResponse.newBuilder())
.build(), omKeyInfo, ozoneManager.isRatisEnabled(),
omBucketInfo.copyObject(), dbOpenKey);
omBucketInfo.copyObject(), deletedOpenKeyInfo);

result = Result.SUCCESS;
} catch (IOException | InvalidPathException ex) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
import java.nio.file.InvalidPathException;
import java.util.Map;

import static org.apache.hadoop.ozone.OzoneConsts.DELETED_HSYNC_KEY;
import static org.apache.hadoop.ozone.om.exceptions.OMException.ResultCodes.DIRECTORY_NOT_EMPTY;
import static org.apache.hadoop.ozone.om.exceptions.OMException.ResultCodes.KEY_NOT_FOUND;
import static org.apache.hadoop.ozone.om.lock.OzoneManagerLock.Resource.BUCKET_LOCK;
Expand Down Expand Up @@ -129,6 +130,7 @@ public OMClientResponse validateAndUpdateCache(OzoneManager ozoneManager, TermIn
String ozonePathKey = omMetadataManager.getOzonePathKey(volumeId,
bucketId, omKeyInfo.getParentObjectID(),
omKeyInfo.getFileName());
OmKeyInfo deletedOpenKeyInfo = null;

if (keyStatus.isDirectory()) {
// Check if there are any sub path exists under the user requested path
Expand Down Expand Up @@ -165,8 +167,9 @@ public OMClientResponse validateAndUpdateCache(OzoneManager ozoneManager, TermIn
dbOpenKey = omMetadataManager.getOpenFileName(volumeId, bucketId, parentId, fileName, hsyncClientId);
OmKeyInfo openKeyInfo = openKeyTable.get(dbOpenKey);
if (openKeyInfo != null) {
// Remove the open key by putting a tombstone entry
openKeyTable.addCacheEntry(dbOpenKey, trxnLogIndex);
openKeyInfo.getMetadata().put(DELETED_HSYNC_KEY, "true");
openKeyTable.addCacheEntry(dbOpenKey, openKeyInfo, trxnLogIndex);
deletedOpenKeyInfo = openKeyInfo;
} else {
LOG.warn("Potentially inconsistent DB state: open key not found with dbOpenKey '{}'", dbOpenKey);
}
Expand All @@ -175,7 +178,7 @@ public OMClientResponse validateAndUpdateCache(OzoneManager ozoneManager, TermIn
omClientResponse = new OMKeyDeleteResponseWithFSO(omResponse
.setDeleteKeyResponse(DeleteKeyResponse.newBuilder()).build(),
keyName, omKeyInfo, ozoneManager.isRatisEnabled(),
omBucketInfo.copyObject(), keyStatus.isDirectory(), volumeId, dbOpenKey);
omBucketInfo.copyObject(), keyStatus.isDirectory(), volumeId, deletedOpenKeyInfo);

result = Result.SUCCESS;
} catch (IOException | InvalidPathException ex) {
Expand Down
Loading

0 comments on commit fd188d1

Please sign in to comment.