Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
5fe2ff7
Add existing update / object IDs to OmKeyArgs and OmKeyInfo
Mar 12, 2024
aa3bd32
modified createKeyRequest and commitKeyRequest with tests
Mar 13, 2024
9799ba5
Push objectid / updateid through various classes
Mar 14, 2024
5040d2b
Adapt client to be able to overwrite a key
Mar 15, 2024
6f45e67
Remove debugging log messages
Mar 15, 2024
0635134
Fix find bugs
Mar 15, 2024
c151122
Ensure test only runs for non FSO for now
Mar 15, 2024
908f9cd
Fix similar commit test on FSO
Mar 15, 2024
3c9c0e2
Rename overWrite to overwrite
Mar 20, 2024
17b5ff1
Overload constructors to avoid passing nulls for the new variables
Mar 20, 2024
0fe12c5
Avoid setting acl in overwrite request. Enhance test to ensure object…
Mar 20, 2024
ae4044c
Add overwrite object / update ID to the audit if they are present
Mar 21, 2024
7bb59e9
Minor review comments
Mar 22, 2024
8890ed8
Remove overwriteObjectID and base all logic on updateID only
Mar 22, 2024
a360d45
Block non ObjectStore buckets from using optimistic overwrite for now
Mar 22, 2024
3f3a49d
Validate existing ACLs are copied over from existing to overwrite key
Mar 22, 2024
736740a
Merge branch 'master' into HDDS-10527
Apr 2, 2024
525685c
Trigger rebuild
Apr 2, 2024
112e2e1
Rename overwriteUpdateID to overwriteGeneration
Apr 30, 2024
bcd1a28
Remove updateID references from client and replace with generation
Apr 30, 2024
4e6a97c
Change client API to rewriteKey
Apr 30, 2024
247620a
Fixed logic to not copy meta data from existing key, but take it from…
Apr 30, 2024
c6bc7e7
Merge branch 'master' into HDDS-10527
Apr 30, 2024
b91a051
Review comments
May 7, 2024
9dc6d31
Change BucketLayout.DEFAULT to getBucketLayout()
May 9, 2024
80e1802
Move getGeneration to OMKeyInfo
May 9, 2024
bb7b74f
Rename any instances of overwrite to rewrite
May 9, 2024
a87e092
Remove generation from OzoneKey
May 9, 2024
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 @@ -343,6 +343,7 @@ private OzoneConsts() {
public static final String BUCKET_LAYOUT = "bucketLayout";
public static final String TENANT = "tenant";
public static final String USER_PREFIX = "userPrefix";
public static final String REWRITE_GENERATION = "rewriteGeneration";

// For multi-tenancy
public static final String TENANT_ID_USERNAME_DELIMITER = "$";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -470,6 +470,28 @@ public OzoneOutputStream createKey(String key, long size,
.createKey(volumeName, name, key, size, replicationConfig, keyMetadata);
}

/**
* This API allows to atomically update an existing key. The key read before invoking this API
* should remain unchanged for this key to be written. This is controlled by the generation
* field in the existing Key param. If the key is replaced or updated the generation will change. If the
* generation has changed since the existing Key was read, either the initial key create will fail,
* or the key will fail to commit after the data has been written as the checks are carried out
* both at key open and commit time.
*
* @param keyName Existing key to rewrite. This must exist in the bucket.
* @param size The size of the new key
* @param existingKeyGeneration The generation of the existing key which is checked for changes at key create
* and commit time.
* @param replicationConfig The replication configuration for the key to be rewritten.
* @param metadata custom key value metadata
* @return OzoneOutputStream to which the data has to be written.
* @throws IOException
*/
public OzoneOutputStream rewriteKey(String keyName, long size, long existingKeyGeneration,
ReplicationConfig replicationConfig, Map<String, String> metadata) throws IOException {
return proxy.rewriteKey(volumeName, name, keyName, size, existingKeyGeneration, replicationConfig, metadata);
}

/**
* Creates a new key in the bucket, with default replication type RATIS and
* with replication factor THREE.
Expand Down Expand Up @@ -1784,8 +1806,7 @@ private void addKeyPrefixInfoToResultList(String keyPrefix,
keyInfo.getDataSize(), keyInfo.getCreationTime(),
keyInfo.getModificationTime(),
keyInfo.getReplicationConfig(),
keyInfo.isFile(),
keyInfo.getOwnerName());
keyInfo.isFile(), keyInfo.getOwnerName());
keysResultList.add(ozoneKey);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,10 +70,6 @@ public class OzoneKey {
*/
private final boolean isFile;

/**
* Constructs OzoneKey from OmKeyInfo.
*
*/
@SuppressWarnings("parameternumber")
public OzoneKey(String volumeName, String bucketName,
String keyName, long size, long creationTime,
Expand Down Expand Up @@ -201,6 +197,10 @@ public boolean isFile() {
return isFile;
}

/**
* Constructs OzoneKey from OmKeyInfo.
*
*/
public static OzoneKey fromKeyInfo(OmKeyInfo keyInfo) {
return new OzoneKey(keyInfo.getVolumeName(), keyInfo.getBucketName(),
keyInfo.getKeyName(), keyInfo.getDataSize(), keyInfo.getCreationTime(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,12 @@ public class OzoneKeyDetails extends OzoneKey {

private final CheckedSupplier<OzoneInputStream, IOException> contentSupplier;

/**
* The generation of an existing key. This can be used with atomic commits, to
* ensure the key has not changed since the key details were read.
*/
private final Long generation;

/**
* Constructs OzoneKeyDetails from OmKeyInfo.
*/
Expand All @@ -53,12 +59,30 @@ public OzoneKeyDetails(String volumeName, String bucketName, String keyName,
Map<String, String> metadata,
FileEncryptionInfo feInfo,
CheckedSupplier<OzoneInputStream, IOException> contentSupplier,
boolean isFile, String owner) {
boolean isFile, String owner, Long generation) {
super(volumeName, bucketName, keyName, size, creationTime,
modificationTime, replicationConfig, metadata, isFile, owner);
this.ozoneKeyLocations = ozoneKeyLocations;
this.feInfo = feInfo;
this.contentSupplier = contentSupplier;
this.generation = generation;
}

/**
* Constructs OzoneKeyDetails from OmKeyInfo.
*/
@SuppressWarnings("parameternumber")
public OzoneKeyDetails(String volumeName, String bucketName, String keyName,
long size, long creationTime, long modificationTime,
List<OzoneKeyLocation> ozoneKeyLocations,
ReplicationConfig replicationConfig,
Map<String, String> metadata,
FileEncryptionInfo feInfo,
CheckedSupplier<OzoneInputStream, IOException> contentSupplier,
boolean isFile, String owner) {
this(volumeName, bucketName, keyName, size, creationTime,
modificationTime, ozoneKeyLocations, replicationConfig, metadata, feInfo, contentSupplier,
isFile, owner, null);
}

/**
Expand All @@ -72,6 +96,10 @@ public FileEncryptionInfo getFileEncryptionInfo() {
return feInfo;
}

public Long getGeneration() {
return generation;
}

/**
* Get OzoneInputStream to read the content of the key.
* @return OzoneInputStream
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -353,6 +353,29 @@ OzoneOutputStream createKey(String volumeName, String bucketName,
Map<String, String> metadata)
throws IOException;

/**
* This API allows to atomically update an existing key. The key read before invoking this API
* should remain unchanged for this key to be written. This is controlled by the generation
* field in the existing Key param. If the key is replaced or updated the generation will change. If the
* generation has changed since the existing Key was read, either the initial key create will fail,
* or the key will fail to commit after the data has been written as the checks are carried out
* both at key open and commit time.
*
* @param volumeName Name of the Volume
* @param bucketName Name of the Bucket
* @param keyName Existing key to rewrite. This must exist in the bucket.
* @param size The size of the new key
* @param existingKeyGeneration The generation of the existing key which is checked for changes at key create
* and commit time.
* @param replicationConfig The replication configuration for the key to be rewritten.
* @param metadata custom key value metadata
* @return {@link OzoneOutputStream}
* @throws IOException
*/
OzoneOutputStream rewriteKey(String volumeName, String bucketName, String keyName,
long size, long existingKeyGeneration, ReplicationConfig replicationConfig,
Map<String, String> metadata) throws IOException;

/**
* Writes a key in an existing bucket.
* @param volumeName Name of the Volume
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1387,26 +1387,7 @@ public OzoneOutputStream createKey(
ReplicationConfig replicationConfig,
Map<String, String> metadata)
throws IOException {
verifyVolumeName(volumeName);
verifyBucketName(bucketName);
if (checkKeyNameEnabled) {
HddsClientUtils.verifyKeyName(keyName);
}
HddsClientUtils.checkNotNull(keyName);
if (omVersion
.compareTo(OzoneManagerVersion.ERASURE_CODED_STORAGE_SUPPORT) < 0) {
if (replicationConfig != null &&
replicationConfig.getReplicationType()
== HddsProtos.ReplicationType.EC) {
throw new IOException("Can not set the replication of the key to"
+ " Erasure Coded replication, as OzoneManager does not support"
+ " Erasure Coded replication.");
}
}

if (replicationConfig != null) {
replicationConfigValidator.validate(replicationConfig);
}
createKeyPreChecks(volumeName, bucketName, keyName, replicationConfig);
String ownerName = getRealUserInfo().getShortUserName();

OmKeyArgs.Builder builder = new OmKeyArgs.Builder()
Expand All @@ -1431,6 +1412,59 @@ public OzoneOutputStream createKey(
return createOutputStream(openKey);
}

@Override
public OzoneOutputStream rewriteKey(String volumeName, String bucketName, String keyName,
long size, long existingKeyGeneration, ReplicationConfig replicationConfig,
Map<String, String> metadata) throws IOException {
createKeyPreChecks(volumeName, bucketName, keyName, replicationConfig);
String ownerName = getRealUserInfo().getShortUserName();

OmKeyArgs.Builder builder = new OmKeyArgs.Builder()
.setVolumeName(volumeName)
.setBucketName(bucketName)
.setKeyName(keyName)
.setDataSize(size)
.setReplicationConfig(replicationConfig)
.addAllMetadataGdpr(metadata)
.setLatestVersionLocation(getLatestVersionLocation)
.setOwnerName(ownerName)
.setRewriteGeneration(existingKeyGeneration);

OpenKeySession openKey = ozoneManagerClient.openKey(builder.build());
// For bucket with layout OBJECT_STORE, when create an empty file (size=0),
// OM will set DataSize to OzoneConfigKeys#OZONE_SCM_BLOCK_SIZE,
// which will cause S3G's atomic write length check to fail,
// so reset size to 0 here.
if (isS3GRequest.get() && size == 0) {
openKey.getKeyInfo().setDataSize(0);
}
return createOutputStream(openKey);
}

private void createKeyPreChecks(String volumeName, String bucketName, String keyName,
ReplicationConfig replicationConfig) throws IOException {
verifyVolumeName(volumeName);
verifyBucketName(bucketName);
if (checkKeyNameEnabled) {
HddsClientUtils.verifyKeyName(keyName);
}
HddsClientUtils.checkNotNull(keyName);
if (omVersion
.compareTo(OzoneManagerVersion.ERASURE_CODED_STORAGE_SUPPORT) < 0) {
if (replicationConfig != null &&
replicationConfig.getReplicationType()
== HddsProtos.ReplicationType.EC) {
throw new IOException("Can not set the replication of the key to"
+ " Erasure Coded replication, as OzoneManager does not support"
+ " Erasure Coded replication.");
}
}

if (replicationConfig != null) {
replicationConfigValidator.validate(replicationConfig);
}
}

@Override
public OzoneDataStreamOutput createStreamKey(
String volumeName, String bucketName, String keyName, long size,
Expand Down Expand Up @@ -1722,7 +1756,8 @@ private OzoneKeyDetails getOzoneKeyDetails(OmKeyInfo keyInfo) {
keyInfo.getModificationTime(), ozoneKeyLocations,
keyInfo.getReplicationConfig(), keyInfo.getMetadata(),
keyInfo.getFileEncryptionInfo(),
() -> getInputStreamWithRetryFunction(keyInfo), keyInfo.isFile(), keyInfo.getOwnerName());
() -> getInputStreamWithRetryFunction(keyInfo), keyInfo.isFile(), keyInfo.getOwnerName(),
keyInfo.getGeneration());
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,13 @@ public final class OmKeyArgs implements Auditable {
private final boolean recursive;
private final boolean headOp;
private final boolean forceUpdateContainerCacheFromSCM;
// RewriteGeneration, when used in key creation indicates that a
// key with the same keyName should exist with the given generation.
// For a key commit to succeed, the original key should still be present with the
// generation unchanged.
// This allows a key to be created an committed atomically if the original has not
// been modified.
private Long rewriteGeneration = null;

private OmKeyArgs(Builder b) {
this.volumeName = b.volumeName;
Expand All @@ -72,6 +79,7 @@ private OmKeyArgs(Builder b) {
this.headOp = b.headOp;
this.forceUpdateContainerCacheFromSCM = b.forceUpdateContainerCacheFromSCM;
this.ownerName = b.ownerName;
this.rewriteGeneration = b.rewriteGeneration;
}

public boolean getIsMultipartKey() {
Expand Down Expand Up @@ -150,6 +158,10 @@ public boolean isForceUpdateContainerCacheFromSCM() {
return forceUpdateContainerCacheFromSCM;
}

public Long getRewriteGeneration() {
return rewriteGeneration;
}

@Override
public Map<String, String> toAuditMap() {
Map<String, String> auditMap = new LinkedHashMap<>();
Expand All @@ -173,7 +185,7 @@ public void addLocationInfo(OmKeyLocationInfo locationInfo) {
}

public OmKeyArgs.Builder toBuilder() {
return new OmKeyArgs.Builder()
OmKeyArgs.Builder builder = new OmKeyArgs.Builder()
.setVolumeName(volumeName)
.setBucketName(bucketName)
.setKeyName(keyName)
Expand All @@ -190,11 +202,16 @@ public OmKeyArgs.Builder toBuilder() {
.setLatestVersionLocation(latestVersionLocation)
.setAcls(acls)
.setForceUpdateContainerCacheFromSCM(forceUpdateContainerCacheFromSCM);

if (rewriteGeneration != null) {
builder.setRewriteGeneration(rewriteGeneration);
}
return builder;
}

@Nonnull
public KeyArgs toProtobuf() {
return KeyArgs.newBuilder()
KeyArgs.Builder builder = KeyArgs.newBuilder()
.setVolumeName(getVolumeName())
.setBucketName(getBucketName())
.setKeyName(getKeyName())
Expand All @@ -203,8 +220,11 @@ public KeyArgs toProtobuf() {
.setLatestVersionLocation(getLatestVersionLocation())
.setHeadOp(isHeadOp())
.setForceUpdateContainerCacheFromSCM(
isForceUpdateContainerCacheFromSCM())
.build();
isForceUpdateContainerCacheFromSCM());
if (rewriteGeneration != null) {
builder.setRewriteGeneration(rewriteGeneration);
}
return builder.build();
}

/**
Expand All @@ -228,6 +248,7 @@ public static class Builder {
private boolean recursive;
private boolean headOp;
private boolean forceUpdateContainerCacheFromSCM;
private Long rewriteGeneration = null;

public Builder setVolumeName(String volume) {
this.volumeName = volume;
Expand Down Expand Up @@ -327,6 +348,11 @@ public Builder setForceUpdateContainerCacheFromSCM(boolean value) {
return this;
}

public Builder setRewriteGeneration(long generation) {
this.rewriteGeneration = generation;
return this;
}

public OmKeyArgs build() {
return new OmKeyArgs(this);
}
Expand Down
Loading