Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
bbec4b9
T1.1. Add bucket versioning three-state enum to proto and bucket helpers
symious Jul 16, 2026
d944440
T1.2. Enforce bucket versioning state machine in OMBucketSetPropertyR…
symious Jul 16, 2026
7c484c2
T1.3. Add versionId, isDeleteMarker and isNullVersion to OmKeyInfo
symious Jul 16, 2026
89c02bb
T1.4. Add versionedKeyTable column family for noncurrent object versions
symious Jul 16, 2026
f74c331
T1.5. Gate object versioning behind OMLayoutFeature.OBJECT_VERSIONING
symious Jul 16, 2026
37c3396
T1. Address comments
symious Jul 17, 2026
e89e8be
T1. Use a 0x00 separator in versionedKeyTable dbKeys
symious Jul 31, 2026
3be7956
T1. Do not derive a versioning status from the legacy flag
symious Jul 31, 2026
f13c90c
T2.1. Add pluggable VersionIdGenerator with the transaction index def…
symious Jul 23, 2026
71b8f92
T2.2. Require versionIds to increase within a key and enforce it at c…
symious Jul 23, 2026
cb00be9
T2.3. Add PinnedFirstVersionIdGenerator
symious Jul 23, 2026
5c65493
T2. Address comments
symious Jul 31, 2026
e6cc33d
T3.1. Keep the overwritten current version in the versionedKeyTable
symious Jul 29, 2026
1414769
T3.2. Insert a delete marker on DELETE without a versionId
symious Jul 29, 2026
e75f103
T3.3. Count every version against the bucket quota
symious Jul 29, 2026
0737502
T3. Base block reclamation on the versioning status, not the legacy flag
symious Jul 31, 2026
a9d68ea
T4.1. Read a specific object version by versionId
symious Jul 31, 2026
0eec00f
T4.2. Permanently delete a noncurrent version by versionId
symious Jul 31, 2026
ba36f75
T4.3. Promote the next-newest version when the current one is deleted
symious Jul 31, 2026
71f7856
T5.1. Take the null version slot on a write while versioning is suspe…
symious Aug 4, 2026
d1b922c
T5.2. Write a null delete marker while versioning is suspended
symious Aug 4, 2026
504f330
T5.3. Address pre-versioning records as the key's null version
symious Aug 4, 2026
7a5bc50
T5.4. Keep the superseded version when a multipart upload completes
symious Aug 4, 2026
b04e62d
T6.1. Reclaim the versions of a key beyond its bucket's maxVersions
symious Aug 7, 2026
1b69e71
T6.2. Expire a version once it has been noncurrent long enough
symious Aug 7, 2026
08092d1
T6.3. Remove a key whose only remaining version is a delete marker
symious Aug 7, 2026
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 @@ -180,6 +180,15 @@ public final class OzoneConsts {

public static final String OM_KEY_PREFIX = "/";
public static final String DOUBLE_SLASH_OM_KEY_PREFIX = "//";
/**
* Separates a key name from the versionId suffix in versionedKeyTable DB keys.
* OM_KEY_PREFIX cannot be used: OBJECT_STORE key names contain '/' verbatim, so
* a key's versions would interleave with the versions of keys nested under it.
* 0x00 is the minimum byte value, so {keyName} + this separator can never be a
* prefix of {keyName} + "/". Written as an octal escape because a literal
* unicode escape for NUL is expanded by the Java lexer before parsing.
*/
public static final String OM_VERSIONED_KEY_SEPARATOR = "\0";
public static final String OM_USER_PREFIX = "$";
public static final String OM_S3_PREFIX = "S3:";
public static final String OM_S3_CALLER_CONTEXT_PREFIX = "S3Auth:S3G|";
Expand Down Expand Up @@ -299,6 +308,10 @@ public final class OzoneConsts {
public static final String STORAGE_TYPE = "storageType";
public static final String RESOURCE_TYPE = "resourceType";
public static final String IS_VERSION_ENABLED = "isVersionEnabled";
public static final String VERSIONING_STATUS = "versioningStatus";
public static final String MAX_VERSIONS = "maxVersions";
public static final String NONCURRENT_VERSION_EXPIRATION_DAYS = "noncurrentVersionExpirationDays";
public static final String EXPIRED_DELETE_MARKER_CLEANUP = "expiredDeleteMarkerCleanup";
public static final String CREATION_TIME = "creationTime";
public static final String MODIFICATION_TIME = "modificationTime";
public static final String DATA_SIZE = "dataSize";
Expand Down
60 changes: 60 additions & 0 deletions hadoop-hdds/common/src/main/resources/ozone-default.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5163,4 +5163,64 @@
<tag>OZONE, RATIS, OM</tag>
<description>The maximum number of events that can be pending in OM Ratis.</description>
</property>
<property>
<name>ozone.om.versioning.version-id-generator</name>
<value>org.apache.hadoop.ozone.om.helpers.TransactionIndexVersionIdGenerator</value>
<tag>OZONE, OM</tag>
<description>
Implementation of org.apache.hadoop.ozone.om.helpers.VersionIdGenerator used to assign the
versionId of an object version. The default uses the index of the committing transaction;
PinnedFirstVersionIdGenerator additionally pins the first version of every key to a reserved
sentinel, so that it can be referenced without listing the key's versions first.
The setting is cluster-wide and may be changed on a running cluster; a write whose generated
versionId already exists on the key is rejected, and the existing version has to be deleted
before that id can be written again.
</description>
</property>
<property>
<name>ozone.om.versioning.max.versions</name>
<value>100</value>
<tag>OZONE, OM</tag>
<description>
Maximum number of versions retained per key on a bucket that does not set maxVersions of its
own, counting the key's current version and its delete markers. Versions beyond the limit are
reclaimed oldest-first by VersionCleanupService. 0 means unlimited.
</description>
</property>
<property>
<name>ozone.om.version.cleanup.service.interval</name>
<value>1h</value>
<tag>OZONE, OM, PERFORMANCE</tag>
<description>
Time interval of the OM VersionCleanupService, which reclaims the noncurrent object versions
of a key that exceed the bucket's maxVersions. Unit could be defined with postfix (ns,ms,s,m,h,d).
</description>
</property>
<property>
<name>ozone.om.version.cleanup.service.timeout</name>
<value>300s</value>
<tag>OZONE, OM, PERFORMANCE</tag>
<description>
Timeout value of a VersionCleanupService run. Unit could be defined with postfix (ns,ms,s,m,h,d).
</description>
</property>
<property>
<name>ozone.om.version.cleanup.limit.per.task</name>
<value>1000</value>
<tag>OZONE, OM, PERFORMANCE</tag>
<description>
The maximum number of object versions VersionCleanupService submits for reclamation in one run.
</description>
</property>
<property>
<name>ozone.om.version.cleanup.marker.scan.budget</name>
<value>100000</value>
<tag>OZONE, OM, PERFORMANCE</tag>
<description>
The maximum number of keyTable entries VersionCleanupService examines in one run while looking
for keys left with only a delete marker. There is no index of delete markers, so they are found
by walking the keyTable; each run walks at most this many entries and the next one resumes where
it stopped, so no single run iterates the whole table.
</description>
</property>
</configuration>
Original file line number Diff line number Diff line change
Expand Up @@ -320,6 +320,7 @@ public static boolean isReadOnly(OMRequest omRequest) {
case RecoverLease:
case SetTimes:
case AbortExpiredMultiPartUploads:
case ReclaimObjectVersions:
case SetSnapshotProperty:
case QuotaRepair:
case PutObjectTagging:
Expand Down Expand Up @@ -440,6 +441,7 @@ public static boolean shouldSendToFollower(OMRequest omRequest) {
case RecoverLease:
case SetTimes:
case AbortExpiredMultiPartUploads:
case ReclaimObjectVersions:
case SetSnapshotProperty:
case QuotaRepair:
case PutObjectTagging:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@
import org.apache.hadoop.hdds.client.ReplicationFactor;
import org.apache.hadoop.hdds.client.ReplicationType;
import org.apache.hadoop.ozone.om.helpers.BucketLayout;
import org.apache.hadoop.ozone.om.helpers.TransactionIndexVersionIdGenerator;
import org.apache.hadoop.ozone.om.helpers.VersionIdGenerator;
import org.apache.ratis.util.TimeDuration;

/**
Expand Down Expand Up @@ -715,6 +717,35 @@ public final class OMConfigKeys {
"ozone.om.ratis.events.max.limit";
public static final int OZONE_OM_RATIS_EVENTS_MAX_LIMIT_DEFAULT = 100;

public static final String OZONE_OM_VERSIONING_VERSION_ID_GENERATOR =
"ozone.om.versioning.version-id-generator";
public static final Class<? extends VersionIdGenerator>
OZONE_OM_VERSIONING_VERSION_ID_GENERATOR_DEFAULT = TransactionIndexVersionIdGenerator.class;

public static final String OZONE_OM_VERSIONING_MAX_VERSIONS =
"ozone.om.versioning.max.versions";
public static final int OZONE_OM_VERSIONING_MAX_VERSIONS_DEFAULT = 100;

public static final String OZONE_OM_VERSION_CLEANUP_SERVICE_INTERVAL =
"ozone.om.version.cleanup.service.interval";
public static final String
OZONE_OM_VERSION_CLEANUP_SERVICE_INTERVAL_DEFAULT = "1h";

public static final String OZONE_OM_VERSION_CLEANUP_SERVICE_TIMEOUT =
"ozone.om.version.cleanup.service.timeout";
public static final String OZONE_OM_VERSION_CLEANUP_SERVICE_TIMEOUT_DEFAULT
= "300s";

public static final String OZONE_OM_VERSION_CLEANUP_LIMIT_PER_TASK =
"ozone.om.version.cleanup.limit.per.task";
public static final int OZONE_OM_VERSION_CLEANUP_LIMIT_PER_TASK_DEFAULT =
1000;

public static final String OZONE_OM_VERSION_CLEANUP_MARKER_SCAN_BUDGET =
"ozone.om.version.cleanup.marker.scan.budget";
public static final int OZONE_OM_VERSION_CLEANUP_MARKER_SCAN_BUDGET_DEFAULT =
100000;

/**
* Never constructed.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -281,5 +281,11 @@ public enum ResultCodes {
ETAG_NOT_AVAILABLE,

ATOMIC_WRITE_CONFLICT,

/**
* The addressed version exists but is a delete marker. Kept distinct from
* KEY_NOT_FOUND because S3 answers 405 rather than 404 for it.
*/
KEY_IS_DELETE_MARKER,
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
/*
* 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.hadoop.ozone.om.helpers;

import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.BucketVersioningStatusProto;

/**
* S3-compatible bucket versioning state machine:
* UNVERSIONED -&gt; ENABLED &lt;-&gt; SUSPENDED.
* Once versioning has been enabled, a bucket can never return to UNVERSIONED.
*/
public enum BucketVersioningStatus {
UNVERSIONED,
ENABLED,
SUSPENDED;

public static BucketVersioningStatus fromProto(BucketVersioningStatusProto proto) {
switch (proto) {
case VERSIONING_ENABLED:
return ENABLED;
case VERSIONING_SUSPENDED:
return SUSPENDED;
case UNVERSIONED:
default:
return UNVERSIONED;
}
}

public BucketVersioningStatusProto toProto() {
switch (this) {
case ENABLED:
return BucketVersioningStatusProto.VERSIONING_ENABLED;
case SUSPENDED:
return BucketVersioningStatusProto.VERSIONING_SUSPENDED;
default:
return BucketVersioningStatusProto.UNVERSIONED;
}
}

/** The legacy isVersionEnabled flag value kept in sync with this status. */
public boolean toVersionEnabledFlag() {
return this == ENABLED;
}

/**
* Whether a bucket may transition from this status to {@code target}.
* The only forbidden transition is back to UNVERSIONED after versioning
* has been enabled or suspended (matches the S3 state machine).
*/
public boolean canTransitionTo(BucketVersioningStatus target) {
return this == UNVERSIONED || target != UNVERSIONED;
}
}
Loading