Skip to content

HDDS-15145. Use enum for the ID type in SequenceIdGenerator#10211

Open
navinko wants to merge 11 commits into
apache:masterfrom
navinko:HDDS-15145
Open

HDDS-15145. Use enum for the ID type in SequenceIdGenerator#10211
navinko wants to merge 11 commits into
apache:masterfrom
navinko:HDDS-15145

Conversation

@navinko
Copy link
Copy Markdown
Contributor

@navinko navinko commented May 7, 2026

What changes were proposed in this pull request?

HDDS-15145. Use enum for the ID type in SequenceIdGenerator

Please describe your PR in detail:

  1. This PR introduces "SequenceIdType" to replace String based ID type arguments in "SequenceIdGenerator".
    SequenceIdGenerator#getNextId now accepts "SequenceIdType" instead of String values, and its in-memory batch cache uses an EnumMap<SequenceIdType, Batch>.

  2. This gives compile-time safety for supported sequence ID types while preserving the existing RocksDB key strings through "SequenceIdType#getDbKey()".

  3. The existing String constants in SequenceIdGenerator are retained and now mapped with Enum .

  • localId, delTxnId, containerId, CertificateId, rootCertificateId
  • ROOT_CERTIFICATE_ID` remains deprecated, as it was already deprecated before this change.
  1. [Reverted]Updated StateManager and StateManagerImpl to use Enum instead of String as sequenceID and created new codec for it. parameters for allocateBatch and getLastId are now updated to adapt Enum.

  2. Added unit tests for SequenceIdType and updated existing SequenceIdGenerator and RootCARotationManager
    tests

  3. [Removed] Last Created new test for Codec
    TestScmSequenceIdTypeCodec
    LegacyStringSequenceIdCodecForTesting

What is the link to the Apache JIRA

https://issues.apache.org/jira/browse/HDDS-15145

How was this patch tested?

Validated with unit test and Also verified the change in a local Docker compose secure HA cluster. The SCM logs show enum-based allocation paths:

% docker compose logs -f scm1.org 2>&1 | grep -E "SequenceId|Allocate a batch|LOCAL_ID|CONTAINER_ID|DEL_TXN_ID|CERTIFICATE_ID"
scm1.org-1 | 2026-05-12 06:27:02,223 [main] INFO ha.SequenceIdGenerator: upgrade localId to 117883640217600000
scm1.org-1 | 2026-05-12 06:27:02,224 [main] INFO ha.SequenceIdGenerator: upgrade delTxnId to 0
scm1.org-1 | 2026-05-12 06:27:02,226 [main] INFO ha.SequenceIdGenerator: upgrade containerId to 0
scm1.org-1 | 2026-05-12 06:27:02,226 [main] INFO ha.SequenceIdGenerator: upgrade CertificateId to 2
scm1.org-1 | 2026-05-12 06:27:02,227 [main] INFO ha.SequenceIdGenerator: Init the HA SequenceIdGenerator.
scm1.org-1 | 2026-05-12 06:27:02,379 [main] INFO ha.SequenceIdGenerator: upgrade CertificateId to 2
scm1.org-1 | 2026-05-12 06:27:11,473 [1ff7c535-ef0a-4a1a-9e1d-45eee893c7aa@group-954FB075DFAD-StateMachineUpdater] WARN ha.SequenceIdGenerator: Failed to allocate a batch for CertificateId, expected lastId is 0, actual lastId is 2.
scm1.org-1 | 2026-05-12 06:27:11,478 [IPC Server handler 1 on default port 9961] INFO ha.SequenceIdGenerator: Allocate a batch for CERTIFICATE_ID, change lastId from 2 to 3.
scm1.org-1 | 2026-05-12 06:27:12,021 [IPC Server handler 1 on default port 9961] INFO ha.SequenceIdGenerator: Allocate a batch for CERTIFICATE_ID, change lastId from 3 to 4.
scm1.org-1 | 2026-05-12 06:27:16,787 [IPC Server handler 1 on default port 9961] INFO ha.SequenceIdGenerator: Allocate a batch for CERTIFICATE_ID, change lastId from 4 to 5.
scm1.org-1 | 2026-05-12 06:27:22,629 [IPC Server handler 1 on default port 9961] INFO ha.SequenceIdGenerator: Allocate a batch for CERTIFICATE_ID, change lastId from 5 to 6.
scm1.org-1 | 2026-05-12 06:27:22,677 [IPC Server handler 0 on default port 9961] INFO ha.SequenceIdGenerator: Allocate a batch for CERTIFICATE_ID, change lastId from 6 to 7.
scm1.org-1 | 2026-05-12 06:27:22,737 [IPC Server handler 0 on default port 9961] INFO ha.SequenceIdGenerator: Allocate a batch for CERTIFICATE_ID, change lastId from 7 to 8.
scm1.org-1 | 2026-05-12 06:27:22,771 [IPC Server handler 1 on default port 9961] INFO ha.SequenceIdGenerator: Allocate a batch for CERTIFICATE_ID, change lastId from 8 to 9.
scm1.org-1 | 2026-05-12 06:27:22,800 [IPC Server handler 0 on default port 9961] INFO ha.SequenceIdGenerator: Allocate a batch for CERTIFICATE_ID, change lastId from 9 to 10.
scm1.org-1 | 2026-05-12 06:27:22,829 [IPC Server handler 1 on default port 9961] INFO ha.SequenceIdGenerator: Allocate a batch for CERTIFICATE_ID, change lastId from 10 to 11.
scm1.org-1 | 2026-05-12 06:30:11,968 [IPC Server handler 5 on default port 9863] INFO ha.SequenceIdGenerator: Allocate a batch for CONTAINER_ID, change lastId from 0 to 1000.
scm1.org-1 | 2026-05-12 06:30:11,989 [1ff7c535-ef0a-4a1a-9e1d-45eee893c7aa@group-954FB075DFAD-StateMachineUpdater] WARN ha.SequenceIdGenerator: Failed to allocate a batch for localId, expected lastId is 0, actual lastId is 117883640217600000.
scm1.org-1 | 2026-05-12 06:30:11,993 [IPC Server handler 5 on default port 9863] INFO ha.SequenceIdGenerator: Allocate a batch for LOCAL_ID, change lastId from 117883640217600000 to 117883640217601000.
scm1.org-1 | 2026-05-12 06:33:26,933 [IPC Server handler 2 on default port 9863] INFO ha.SequenceIdGenerator: Allocate a batch for DEL_TXN_ID, change lastId from 0 to 1000.

Latest progressing CI : https://github.com/navinko/ozone/actions/runs/25717961667

Copy link
Copy Markdown
Contributor

@sreejasahithi sreejasahithi left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @navinko

Comment on lines +54 to +59
public void testNumberOfEnumConstants() {
// If a new SequenceIdType is added, this test will fail.
// This serves as a reminder to the developer to verify RocksDB backward
// compatibility and update this test class accordingly.
assertEquals(5, SequenceIdType.values().length);
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here testNumberOfEnumConstants (assertEquals(5, SequenceIdType.values().length)) is fully subsumed by testIfNewEnumConstantGetsAdded. Every failure scenario that testNumberOfEnumConstants catches is also caught by testIfNewEnumConstantGetsAdded, which additionally catches renames (count stays 5 but a name changes), and provides a far more descriptive failure message with the "ACTION REQUIRED" instruction.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks , it make sense!
removed this one.

Comment on lines +87 to +91
@Test
public void testReturnsNullIfEnumConstantNotAvailable() {
assertNull(SequenceIdType.fromDbKey("unmapped-key-string"));
assertNull(SequenceIdType.fromDbKey(null));
}
Copy link
Copy Markdown
Contributor

@sreejasahithi sreejasahithi May 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can in a different test consider asserting assertSame(type, fromDbKey(type.getDbKey())) for every constant so the static map stays aligned with getDbKey(), not covered by the unknown/null case alone.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @sreejasahithi for review, to align with current review comments i ahve removed the unused method fromDbKey completely.

Copy link
Copy Markdown
Contributor

@szetszwo szetszwo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@navinko , thanks for working on this! Please see the comments inlined.

/**
* Returns the {@link SequenceIdType} corresponding to the provided RocksDB key string, or null if unmapped.
*/
public static SequenceIdType fromDbKey(String dbKey) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This method is unused except testReturnsNullIfEnumConstantNotAvailable(). Let's remove it and also DB_KEY_MAP.

Indeed, we should always use SequenceIdType and never convert a String to SequenceIdType.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

Comment on lines +67 to +75
public static final String LOCAL_ID = SequenceIdType.LOCAL_ID.getDbKey();
public static final String DEL_TXN_ID = SequenceIdType.DEL_TXN_ID.getDbKey();
public static final String CONTAINER_ID = SequenceIdType.CONTAINER_ID.getDbKey();

// Certificate ID for all services, including root certificates, whose ID
// were using "rootCertificateId" before.
public static final String CERTIFICATE_ID = "CertificateId";
public static final String CERTIFICATE_ID = SequenceIdType.CERTIFICATE_ID.getDbKey();
@Deprecated
public static final String ROOT_CERTIFICATE_ID = "rootCertificateId";
public static final String ROOT_CERTIFICATE_ID = SequenceIdType.ROOT_CERTIFICATE_ID.getDbKey();
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Change them to private.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

public SequenceIdGenerator(ConfigurationSource conf,
SCMHAManager scmhaManager, Table<String, Long> sequenceIdTable) {
this.sequenceIdToBatchMap = new HashMap<>();
this.sequenceIdToBatchMap = new EnumMap<>(SequenceIdType.class);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Make it unmodifiable:

    this.sequenceIdToBatchMap = newSequenceIdToBatchMap();
  static Map<SequenceIdType, Batch> newSequenceIdToBatchMap() {
    final EnumMap<SequenceIdType, Batch> map = new EnumMap<>(SequenceIdType.class);
    for (SequenceIdType type : SequenceIdType.values()) {
      map.put(type, new Batch());
    }
    return Collections.unmodifiableMap(map);
  }

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

Comment on lines +117 to +118
Batch batch = sequenceIdToBatchMap.computeIfAbsent(
sequenceIdName, key -> new Batch());
idType, key -> new Batch());
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use get()

      final Batch batch = sequenceIdToBatchMap.get(idType);

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done


// reload lastId from RocksDB.
batch.lastId = stateManager.getLastId(sequenceIdName);
batch.lastId = stateManager.getLastId(idType.getDbKey());
Copy link
Copy Markdown
Contributor

@szetszwo szetszwo May 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • Change the parameter to SequenceIdType
  • Change StateManagerImpl.sequenceIdToLastIdMap key to SequenceIdType

Copy link
Copy Markdown
Contributor Author

@navinko navinko May 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @sreejasahithi @szetszwo for the suggestion!

I have removed the unused method fromDbKey and DB_KEY_MAP and made all constant under SequenceIdGenerator private alongwith EnumMap as unmodifiable and dis suggested changes in test classes.

I've updated code for StateManagerImpl too , the parameters and map keys to use SequenceIdType instead of Strings. Further , to handle how these new Enums are serialized across the Ratis and safely deserialized from existing RocksDB bytes, I implemented the ScmSequenceIdTypeCodec and registered with ScmCodecFactory. This ensures the Enums translate perfectly into our legacy byte format .

Also referred existing TestPipelineIDCodec to create new unit test for codec and verifies the serialization and deserialzation. In the LegacyStringSequenceIdCodecForTesting class simulated, how Sequence IDs were serialized before the Enum refactoring.

@navinko navinko marked this pull request as draft May 10, 2026 16:47
@navinko navinko marked this pull request as ready for review May 11, 2026 14:16
@navinko navinko requested review from sreejasahithi and szetszwo May 11, 2026 14:17
@navinko
Copy link
Copy Markdown
Contributor Author

navinko commented May 11, 2026

Thanks @szetszwo @sreejasahithi for the review , tried addressing review suggestions .
Kindly do review once you get some time.

Copy link
Copy Markdown
Contributor

@szetszwo szetszwo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • Change the parameter to SequenceIdType
  • Change StateManagerImpl.sequenceIdToLastIdMap key to SequenceIdType

@navinko , my suggestions above actually need a lot of changes. Sorry that I was not aware of it!

Could you revert them here and do it in a separated JIRA, revert all the changes for StateManager and StateManagerImpl and keep using String?

*/
@Replicate
Boolean allocateBatch(String sequenceIdName,
Boolean allocateBatch(SequenceIdType idType,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We cannot change the parameter class for compatibility. Otherwise, the SCM in old version cannot talk to the SCM in new version. Let's do it in a separated JIRA.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @szetszwo will update the PR as suggested .

@navinko navinko marked this pull request as draft May 11, 2026 20:18
@szetszwo
Copy link
Copy Markdown
Contributor

@navinko , thanks for the update! Please remove DB_KEY_MAP since it is not used except for tests.

@navinko
Copy link
Copy Markdown
Contributor Author

navinko commented May 12, 2026

Thanks @szetszwo , reverted all the changes for StateManager and StateManagerImpl and keep using String.

@navinko navinko marked this pull request as ready for review May 12, 2026 06:44
@navinko navinko requested a review from szetszwo May 12, 2026 06:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants