You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ozone.apache.org by ad...@apache.org on 2026/10/03 09:51:46 UTC
(ozone) branch master updated: HDDS-14337. Fix SCM startup failure near year boundary when seeding localId (#11184)
This is an automated email from the ASF dual-hosted git repository.
adoroszlai pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/ozone.git
The following commit(s) were added to refs/heads/master by this push:
new 898bb4310f2 HDDS-14337. Fix SCM startup failure near year boundary when seeding localId (#11184)
898bb4310f2 is described below
commit 898bb4310f24be462ea6301ba62488537f5e975e
Author: SaketaChalamchala <sa...@gmail.com>
AuthorDate: Sat Oct 3 02:51:40 2026 -0700
HDDS-14337. Fix SCM startup failure near year boundary when seeding localId (#11184)
---
.../hadoop/hdds/scm/ha/SequenceIdGenerator.java | 17 +++++--
.../hdds/scm/ha/TestSequenceIDGenerator.java | 53 ++++++++++++++++++++++
2 files changed, 66 insertions(+), 4 deletions(-)
diff --git a/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/ha/SequenceIdGenerator.java b/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/ha/SequenceIdGenerator.java
index 08916b65dc5..30b0a62553f 100644
--- a/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/ha/SequenceIdGenerator.java
+++ b/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/ha/SequenceIdGenerator.java
@@ -26,6 +26,7 @@
import java.math.BigInteger;
import java.security.cert.X509Certificate;
import java.time.LocalDate;
+import java.time.ZoneOffset;
import java.util.Collections;
import java.util.EnumMap;
import java.util.Map;
@@ -346,10 +347,7 @@ public static void upgradeToSequenceId(SCMMetadataStore scmMetadataStore)
// scm.db from leader SCM, and drop its own scm.db. Thus the upgrade
// operations can take effect exactly once in a SCM HA cluster.
if (sequenceIdTable.get(SequenceIdType.localId) == null) {
- long millisSinceEpoch = TimeUnit.DAYS.toMillis(
- LocalDate.of(LocalDate.now().getYear() + 1, 1, 1).toEpochDay());
-
- long localId = millisSinceEpoch << Short.SIZE;
+ long localId = computeInitialLocalId(LocalDate.now(ZoneOffset.UTC).getYear());
Preconditions.checkArgument(localId > UniqueId.next());
sequenceIdTable.put(SequenceIdType.localId, localId);
@@ -386,6 +384,17 @@ public static void upgradeToSequenceId(SCMMetadataStore scmMetadataStore)
upgradeToCertificateSequenceId(scmMetadataStore, false);
}
+ /**
+ * Compute the initial localId for a new SCM based on the UTC calendar year.
+ * Uses Jan 2 of (utcYear + 1) so the value stays above legacy UniqueId values
+ * even if the UTC year rolls over before UniqueId.next() is checked.
+ */
+ static long computeInitialLocalId(int utcYear) {
+ long millisSinceEpoch = TimeUnit.DAYS.toMillis(
+ LocalDate.of(utcYear + 1, 1, 2).toEpochDay());
+ return millisSinceEpoch << Short.SIZE;
+ }
+
public static void upgradeToCertificateSequenceId(
SCMMetadataStore scmMetadataStore, boolean force) throws IOException {
Table<SequenceIdType, Long> sequenceIdTable = scmMetadataStore.getSequenceIdTable();
diff --git a/hadoop-hdds/server-scm/src/test/java/org/apache/hadoop/hdds/scm/ha/TestSequenceIDGenerator.java b/hadoop-hdds/server-scm/src/test/java/org/apache/hadoop/hdds/scm/ha/TestSequenceIDGenerator.java
index 1f07927a49d..14fe7d12a3f 100644
--- a/hadoop-hdds/server-scm/src/test/java/org/apache/hadoop/hdds/scm/ha/TestSequenceIDGenerator.java
+++ b/hadoop-hdds/server-scm/src/test/java/org/apache/hadoop/hdds/scm/ha/TestSequenceIDGenerator.java
@@ -20,25 +20,34 @@
import static org.apache.hadoop.hdds.scm.ScmConfigKeys.OZONE_SCM_SEQUENCE_ID_BATCH_SIZE;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.fail;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.CALLS_REAL_METHODS;
import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.mockStatic;
import static org.mockito.Mockito.spy;
import java.io.File;
+import java.time.LocalDate;
+import java.time.ZoneOffset;
import java.util.Objects;
+import java.util.concurrent.TimeUnit;
+import org.apache.hadoop.hdds.HddsUtils;
import org.apache.hadoop.hdds.conf.OzoneConfiguration;
import org.apache.hadoop.hdds.scm.exceptions.SCMException;
import org.apache.hadoop.hdds.scm.metadata.SCMDBTransactionBufferImpl;
import org.apache.hadoop.hdds.scm.metadata.SCMMetadataStore;
import org.apache.hadoop.hdds.scm.metadata.SCMMetadataStoreImpl;
+import org.apache.hadoop.hdds.utils.UniqueId;
import org.apache.hadoop.hdds.utils.db.Table;
import org.apache.hadoop.ozone.container.common.SCMTestUtils;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
+import org.mockito.MockedStatic;
/**
* Tests for {@link SequenceIdGenerator}.
@@ -270,4 +279,48 @@ public void testAllocateBatchFailsOnUnknownSequenceId() throws Exception {
// ignore
}
}
+
+ /**
+ * Regression test for HDDS-14337. Simulates SCM first start when UTC is
+ * already in the new year (Jan 1 2026) while the old formula used the
+ * local timezone year (2025). Verifies upgradeToSequenceId succeeds and
+ * block localIds are allocated.
+ */
+ @SuppressWarnings("ReturnValueIgnored")
+ @Test
+ public void testUpgradeToSequenceIdNearYearBoundary() throws Exception {
+ OzoneConfiguration conf = SCMTestUtils.getConf(testDir);
+ SCMMetadataStore scmMetadataStore = new SCMMetadataStoreImpl(conf);
+ scmMetadataStore.start(conf);
+ SCMHAManager scmHAManager = SCMHAManagerStub
+ .getInstance(true, new SCMDBTransactionBufferImpl());
+
+ LocalDate utcDate = LocalDate.of(2026, 1, 1);
+ long simulatedUtcMillis = TimeUnit.DAYS.toMillis(utcDate.toEpochDay())
+ + TimeUnit.HOURS.toMillis(6);
+ // UTC year is 2026; seed is Jan 2, 2027 (Jan 1 of next year + 1 day).
+ long expectedLocalId = TimeUnit.DAYS.toMillis(
+ LocalDate.of(2027, 1, 2).toEpochDay()) << Short.SIZE;
+
+ try (MockedStatic<HddsUtils> mockedTime = mockStatic(HddsUtils.class);
+ MockedStatic<LocalDate> mockedLocalDate = mockStatic(LocalDate.class, CALLS_REAL_METHODS)) {
+ mockedTime.when(HddsUtils::getTime).thenReturn(simulatedUtcMillis);
+ mockedLocalDate.when(() -> LocalDate.now(ZoneOffset.UTC)).thenReturn(utcDate);
+
+ long legacyLocalId = TimeUnit.DAYS.toMillis(
+ LocalDate.of(2026, 1, 1).toEpochDay()) << Short.SIZE;
+ assertFalse(legacyLocalId > UniqueId.next(),
+ "pre-HDDS-14337 local timezone year would fail the upgrade check");
+
+ SequenceIdGenerator.upgradeToSequenceId(scmMetadataStore);
+ Long initialLocalId = scmMetadataStore.getSequenceIdTable().get(SequenceIdType.localId);
+ assertNotNull(initialLocalId);
+ assertEquals(expectedLocalId, initialLocalId.longValue());
+
+ SequenceIdGenerator sequenceIdGen = new SequenceIdGenerator(
+ conf, scmHAManager, scmMetadataStore.getSequenceIdTable());
+ assertEquals(expectedLocalId + 1, sequenceIdGen.getNextId(SequenceIdType.localId));
+ assertEquals(expectedLocalId + 2, sequenceIdGen.getNextId(SequenceIdType.localId));
+ }
+ }
}
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@ozone.apache.org
For additional commands, e-mail: commits-help@ozone.apache.org