From fe86cef4f3f765d2e78300022f8722e4df12d226 Mon Sep 17 00:00:00 2001 From: Ricardo Zanini Date: Fri, 3 Jul 2026 15:40:23 -0400 Subject: [PATCH 1/2] Fix #1516 - Swap ULID implementation Signed-off-by: Ricardo Zanini --- impl/core/pom.xml | 4 +- ...onotonicUlidWorkflowInstanceIdFactory.java | 16 +++--- impl/pom.xml | 8 +-- .../impl/test/WorkflowInstanceIdTest.java | 49 +++++++++++++++++++ 4 files changed, 61 insertions(+), 16 deletions(-) create mode 100644 impl/test/src/test/java/io/serverlessworkflow/impl/test/WorkflowInstanceIdTest.java diff --git a/impl/core/pom.xml b/impl/core/pom.xml index 089402637..1d3587aae 100644 --- a/impl/core/pom.xml +++ b/impl/core/pom.xml @@ -21,8 +21,8 @@ slf4j-api - de.huxhorn.sulky - de.huxhorn.sulky.ulid + com.github.f4b6a3 + ulid-creator com.cronutils diff --git a/impl/core/src/main/java/io/serverlessworkflow/impl/MonotonicUlidWorkflowInstanceIdFactory.java b/impl/core/src/main/java/io/serverlessworkflow/impl/MonotonicUlidWorkflowInstanceIdFactory.java index 20a722a22..db887bf42 100644 --- a/impl/core/src/main/java/io/serverlessworkflow/impl/MonotonicUlidWorkflowInstanceIdFactory.java +++ b/impl/core/src/main/java/io/serverlessworkflow/impl/MonotonicUlidWorkflowInstanceIdFactory.java @@ -15,9 +15,8 @@ */ package io.serverlessworkflow.impl; -import de.huxhorn.sulky.ulid.ULID; -import java.security.SecureRandom; -import java.util.concurrent.atomic.AtomicReference; +import com.github.f4b6a3.ulid.UlidFactory; +import java.util.random.RandomGenerator; /** * A {@link WorkflowInstanceIdFactory} implementation that generates Monotonic ULIDs as workflow @@ -25,17 +24,14 @@ */ public class MonotonicUlidWorkflowInstanceIdFactory implements WorkflowInstanceIdFactory { - private final ULID ulid; - private final AtomicReference currentUlid; + private final UlidFactory factory; public MonotonicUlidWorkflowInstanceIdFactory() { - this.ulid = new ULID(new SecureRandom()); - this.currentUlid = new AtomicReference<>(ulid.nextValue()); + final RandomGenerator random = RandomGenerator.getDefault(); + factory = UlidFactory.newMonotonicInstance(() -> random.nextLong()); } public String get() { - return currentUlid - .getAndUpdate(previousUlid -> ulid.nextMonotonicValue(previousUlid)) - .toString(); + return factory.create().toString(); } } diff --git a/impl/pom.xml b/impl/pom.xml index 148d4e95e..8399933f5 100644 --- a/impl/pom.xml +++ b/impl/pom.xml @@ -10,7 +10,7 @@ pom 2.4.240 - 8.3.0 + 5.2.4 4.0.0 1.6.2 4.0.2 @@ -136,9 +136,9 @@ ${version.net.thisptr} - de.huxhorn.sulky - de.huxhorn.sulky.ulid - ${version.de.huxhorn.sulky} + com.github.f4b6a3 + ulid-creator + ${version.com.github.f4b6a3} jakarta.ws.rs diff --git a/impl/test/src/test/java/io/serverlessworkflow/impl/test/WorkflowInstanceIdTest.java b/impl/test/src/test/java/io/serverlessworkflow/impl/test/WorkflowInstanceIdTest.java new file mode 100644 index 000000000..76583affd --- /dev/null +++ b/impl/test/src/test/java/io/serverlessworkflow/impl/test/WorkflowInstanceIdTest.java @@ -0,0 +1,49 @@ +/* + * Copyright 2020-Present The Serverless Workflow Specification Authors + * + * Licensed 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 io.serverlessworkflow.impl.test; + +import static io.serverlessworkflow.api.WorkflowReader.readWorkflowFromClasspath; +import static org.assertj.core.api.Assertions.assertThat; + +import io.serverlessworkflow.impl.WorkflowApplication; +import io.serverlessworkflow.impl.WorkflowDefinition; +import io.serverlessworkflow.impl.WorkflowInstance; +import java.io.IOException; +import java.util.Map; +import org.junit.jupiter.api.Test; + +public class WorkflowInstanceIdTest { + + @Test + void testWorkflowInstanceHasValidUlidId() throws IOException { + try (WorkflowApplication application = WorkflowApplication.builder().build()) { + WorkflowDefinition definition = + application.workflowDefinition( + readWorkflowFromClasspath("workflows-samples/simple-expression.yaml")); + + WorkflowInstance instance = definition.instance(Map.of()); + String id = instance.id(); + + // Verify ULID format: 26 characters, Crockford Base32 + assertThat(id).hasSize(26); + assertThat(id).matches("[0-7][0-9A-HJKMNP-TV-Z]{25}"); + + // Verify uniqueness across instances + String id2 = definition.instance(Map.of()).id(); + assertThat(id).isNotEqualTo(id2); + } + } +} From 88b1cb40f9e6dd695c6f521f286a89d30a68133f Mon Sep 17 00:00:00 2001 From: Ricardo Zanini Date: Fri, 3 Jul 2026 15:53:53 -0400 Subject: [PATCH 2/2] Use ThreadLocalRandom Signed-off-by: Ricardo Zanini --- .../impl/MonotonicUlidWorkflowInstanceIdFactory.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/impl/core/src/main/java/io/serverlessworkflow/impl/MonotonicUlidWorkflowInstanceIdFactory.java b/impl/core/src/main/java/io/serverlessworkflow/impl/MonotonicUlidWorkflowInstanceIdFactory.java index db887bf42..673be14a8 100644 --- a/impl/core/src/main/java/io/serverlessworkflow/impl/MonotonicUlidWorkflowInstanceIdFactory.java +++ b/impl/core/src/main/java/io/serverlessworkflow/impl/MonotonicUlidWorkflowInstanceIdFactory.java @@ -16,7 +16,7 @@ package io.serverlessworkflow.impl; import com.github.f4b6a3.ulid.UlidFactory; -import java.util.random.RandomGenerator; +import java.util.concurrent.ThreadLocalRandom; /** * A {@link WorkflowInstanceIdFactory} implementation that generates Monotonic ULIDs as workflow @@ -27,10 +27,10 @@ public class MonotonicUlidWorkflowInstanceIdFactory implements WorkflowInstanceI private final UlidFactory factory; public MonotonicUlidWorkflowInstanceIdFactory() { - final RandomGenerator random = RandomGenerator.getDefault(); - factory = UlidFactory.newMonotonicInstance(() -> random.nextLong()); + factory = UlidFactory.newMonotonicInstance(() -> ThreadLocalRandom.current().nextLong()); } + @Override public String get() { return factory.create().toString(); }