Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,14 @@ public class OzoneBucket extends WithMetadata {
* Bucket Owner.
*/
private String owner;
/**
* Pending deletion bytes (Includes bytes retained by snapshots).
*/
private long pendingDeleteBytes;
/**
* Pending deletion namespace (Includes keys retained by snapshots).
*/
private long pendingDeleteNamespace;

protected OzoneBucket(Builder builder) {
super(builder);
Expand All @@ -167,6 +175,8 @@ protected OzoneBucket(Builder builder) {
}
this.usedBytes = builder.usedBytes;
this.usedNamespace = builder.usedNamespace;
this.pendingDeleteBytes = builder.pendingDeleteBytes;
this.pendingDeleteNamespace = builder.pendingDeleteNamespace;
this.creationTime = Instant.ofEpochMilli(builder.creationTime);
if (builder.modificationTime != 0) {
this.modificationTime = Instant.ofEpochMilli(builder.modificationTime);
Expand Down Expand Up @@ -610,6 +620,14 @@ public long getUsedNamespace() {
return usedNamespace;
}

public long getPendingDeleteBytes() {
return pendingDeleteBytes;
}

public long getPendingDeleteNamespace() {
return pendingDeleteNamespace;
}

/**
* Returns Iterator to iterate over all keys in the bucket.
* The result can be restricted using key prefix, will return all
Expand Down Expand Up @@ -1127,6 +1145,8 @@ public static class Builder extends WithMetadata.Builder {
private long quotaInNamespace;
private BucketLayout bucketLayout;
private String owner;
private long pendingDeleteBytes;
private long pendingDeleteNamespace;

protected Builder() {
}
Expand Down Expand Up @@ -1223,6 +1243,16 @@ public Builder setOwner(String owner) {
return this;
}

public Builder setPendingDeleteBytes(long pendingDeleteBytes) {
this.pendingDeleteBytes = pendingDeleteBytes;
return this;
}

public Builder setPendingDeleteNamespace(long pendingDeleteNamespace) {
this.pendingDeleteNamespace = pendingDeleteNamespace;
return this;
}

public OzoneBucket build() {
return new OzoneBucket(this);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1299,6 +1299,8 @@ public OzoneBucket getBucketDetails(
.setSourceBucket(bucketInfo.getSourceBucket())
.setUsedBytes(bucketInfo.getTotalBucketSpace())
.setUsedNamespace(bucketInfo.getTotalBucketNamespace())
.setPendingDeleteBytes(bucketInfo.getSnapshotUsedBytes())
.setPendingDeleteNamespace(bucketInfo.getSnapshotUsedNamespace())
.setQuotaInBytes(bucketInfo.getQuotaInBytes())
.setQuotaInNamespace(bucketInfo.getQuotaInNamespace())
.setBucketLayout(bucketInfo.getBucketLayout())
Expand Down Expand Up @@ -1330,6 +1332,8 @@ public List<OzoneBucket> listBuckets(String volumeName, String bucketPrefix,
.setSourceBucket(bucket.getSourceBucket())
.setUsedBytes(bucket.getTotalBucketSpace())
.setUsedNamespace(bucket.getTotalBucketNamespace())
.setPendingDeleteBytes(bucket.getSnapshotUsedBytes())
.setPendingDeleteNamespace(bucket.getSnapshotUsedNamespace())
.setQuotaInBytes(bucket.getQuotaInBytes())
.setQuotaInNamespace(bucket.getQuotaInNamespace())
.setBucketLayout(bucket.getBucketLayout())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
import static org.apache.hadoop.ozone.OzoneConfigKeys.OZONE_BLOCK_DELETING_SERVICE_INTERVAL;
import static org.apache.hadoop.ozone.OzoneConfigKeys.OZONE_SCM_BLOCK_SIZE;
import static org.apache.hadoop.ozone.OzoneConfigKeys.OZONE_SCM_BLOCK_SIZE_DEFAULT;
import static org.apache.hadoop.ozone.OzoneConfigKeys.OZONE_SNAPSHOT_DELETING_SERVICE_INTERVAL;
import static org.apache.hadoop.ozone.OzoneConsts.DEFAULT_OM_UPDATE_ID;
import static org.apache.hadoop.ozone.OzoneConsts.ETAG;
import static org.apache.hadoop.ozone.OzoneConsts.GB;
Expand Down Expand Up @@ -211,6 +212,7 @@
import org.junit.jupiter.params.provider.CsvSource;
import org.junit.jupiter.params.provider.EnumSource;
import org.junit.jupiter.params.provider.MethodSource;
import org.junit.jupiter.params.provider.ValueSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

Expand Down Expand Up @@ -263,6 +265,7 @@ static void startCluster(OzoneConfiguration conf, MiniOzoneCluster.Builder build
conf.setInt(OZONE_SCM_RATIS_PIPELINE_LIMIT, 10);
conf.setTimeDuration(OZONE_BLOCK_DELETING_SERVICE_INTERVAL, 1, TimeUnit.SECONDS);
conf.setTimeDuration(OZONE_DIR_DELETING_SERVICE_INTERVAL, 1, TimeUnit.SECONDS);
conf.setTimeDuration(OZONE_SNAPSHOT_DELETING_SERVICE_INTERVAL, 1, TimeUnit.SECONDS);

ClientConfigForTesting.newBuilder(StorageUnit.MB)
.setDataStreamMinPacketSize(1)
Expand Down Expand Up @@ -1154,6 +1157,71 @@ public void testDeleteAuditLog() throws Exception {
", replicationConfig=EC{rs-3-2-1024k}}\",\"unDeletedKeysList\"");
}

/**
* Verifies pendingDelete* fields are populated after key delete,
* with/without snapshot retention.
*
* @param withSnapshot whether to create a snapshot before deleting the key
* @throws Exception on failure
*/
@ParameterizedTest
@ValueSource(booleans = {false, true})
public void testBucketPendingDeleteBytes(boolean withSnapshot) throws Exception {
String volumeName = UUID.randomUUID().toString();
String bucketName = UUID.randomUUID().toString();
String keyName = UUID.randomUUID().toString();
String snapshotName = "snap-" + UUID.randomUUID();
String value = "sample value";
int valueLength = value.getBytes(UTF_8).length;

store.createVolume(volumeName);
OzoneVolume volume = store.getVolume(volumeName);
volume.createBucket(bucketName);
OzoneBucket bucket = volume.getBucket(bucketName);

writeKey(bucket, keyName, ONE, value, valueLength);

OzoneBucket bucketAfterKeyWrite = store.getVolume(volumeName)
.getBucket(bucketName);
assertThat(bucketAfterKeyWrite.getUsedBytes()).isEqualTo(valueLength);
assertThat(bucketAfterKeyWrite.getUsedNamespace()).isEqualTo(1);
assertThat(bucketAfterKeyWrite.getPendingDeleteBytes()).isEqualTo(0);
assertThat(bucketAfterKeyWrite.getPendingDeleteNamespace()).isEqualTo(0);

if (withSnapshot) {
store.createSnapshot(volumeName, bucketName, snapshotName);
}
bucket.deleteKey(keyName);
// After delete: usedBytes should still be totalBucketSpace.
OzoneBucket bucketAfterKeyDelete = store.getVolume(volumeName)
.getBucket(bucketName);
assertThat(bucketAfterKeyDelete.getUsedBytes()).isEqualTo(valueLength);
assertThat(bucketAfterKeyDelete.getUsedNamespace()).isEqualTo(1);
assertThat(bucketAfterKeyDelete.getPendingDeleteBytes()).isEqualTo(valueLength);
assertThat(bucketAfterKeyDelete.getPendingDeleteNamespace()).isEqualTo(1);

if (withSnapshot) {
// if snapshot is present bytes won't be released until snapshot is deleted.
store.deleteSnapshot(volumeName, bucketName, snapshotName);
}

GenericTestUtils.waitFor(() -> {
OzoneBucket buck = null;
try {
buck = store.getVolume(volumeName).getBucket(bucketName);
} catch (IOException e) {
fail("Failed to get bucket details", e);
}
return buck.getUsedBytes() == 0 && buck.getUsedNamespace() == 0;
}, 1000, 30000);
OzoneBucket bucketAfterKeyPurge = store.getVolume(volumeName)
.getBucket(bucketName);
assertThat(bucketAfterKeyPurge.getUsedBytes()).isEqualTo(0);
assertThat(bucketAfterKeyPurge.getUsedNamespace()).isEqualTo(0);
assertThat(bucketAfterKeyPurge.getPendingDeleteBytes()).isEqualTo(0);
assertThat(bucketAfterKeyPurge.getPendingDeleteNamespace()).isEqualTo(0);
}

protected void verifyReplication(String volumeName, String bucketName,
String keyName, ReplicationConfig replication)
throws IOException {
Expand Down