From e4f9d83c405e63f5a7f31253a098b55b28518004 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Attila=20M=C3=A9sz=C3=A1ros?= Date: Fri, 10 Oct 2025 13:40:00 +0200 Subject: [PATCH 01/14] Resource version comparison utility MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Attila Mészáros --- .../PrimaryUpdateAndCacheUtils.java | 20 +++++++++++ .../PrimaryUpdateAndCacheUtilsTest.java | 35 +++++++++++++++++++ 2 files changed, 55 insertions(+) diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/PrimaryUpdateAndCacheUtils.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/PrimaryUpdateAndCacheUtils.java index 6103b4b12b..d353c9649a 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/PrimaryUpdateAndCacheUtils.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/PrimaryUpdateAndCacheUtils.java @@ -450,4 +450,24 @@ public static

P addFinalizerWithSSA( e); } } + + public static int compareResourceVersions(String v1, String v2) { + var v1Length = v1.length(); + var v2Length = v2.length(); + if (v1Length > v2Length) { + return 1; + } + if (v2Length > v1Length) { + return -1; + } + for (int i = 0; i < v1Length; i++) { + if (v1.charAt(i) > v2.charAt(i)) { + return 1; + } + if (v1.charAt(i) < v2.charAt(i)) { + return -1; + } + } + return 0; + } } diff --git a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/api/reconciler/PrimaryUpdateAndCacheUtilsTest.java b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/api/reconciler/PrimaryUpdateAndCacheUtilsTest.java index c85442f00a..ce310200de 100644 --- a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/api/reconciler/PrimaryUpdateAndCacheUtilsTest.java +++ b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/api/reconciler/PrimaryUpdateAndCacheUtilsTest.java @@ -20,6 +20,8 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import io.fabric8.kubernetes.api.model.HasMetadata; import io.fabric8.kubernetes.client.KubernetesClient; @@ -37,6 +39,7 @@ import io.javaoperatorsdk.operator.sample.simple.TestCustomResource; import static io.javaoperatorsdk.operator.api.reconciler.PrimaryUpdateAndCacheUtils.DEFAULT_MAX_RETRY; +import static io.javaoperatorsdk.operator.api.reconciler.PrimaryUpdateAndCacheUtils.compareResourceVersions; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.mockito.ArgumentMatchers.any; @@ -47,6 +50,8 @@ class PrimaryUpdateAndCacheUtilsTest { + private static final Logger log = LoggerFactory.getLogger(PrimaryUpdateAndCacheUtilsTest.class); + Context context = mock(Context.class); KubernetesClient client = mock(KubernetesClient.class); Resource resource = mock(Resource.class); @@ -176,4 +181,34 @@ void cachePollTimeouts() { 10L)); assertThat(ex.getMessage()).contains("Timeout"); } + + @Test + public void compareResourceVersionsTest() { + assertThat(compareResourceVersions("11", "22")).isNegative(); + assertThat(compareResourceVersions("22", "11")).isPositive(); + assertThat(compareResourceVersions("11", "11")).isZero(); + + assertThat(compareResourceVersions("123", "2")).isPositive(); + assertThat(compareResourceVersions("3", "211")).isNegative(); + } + + // naive performance that compares the works case scenario for non parsing variant + @Test + public void compareResourcePerformanceTest() { + var execNum = 10000000; + var startTime = System.currentTimeMillis(); + for (int i = 0; i < execNum; i++) { + var res = compareResourceVersions("123456788", "123456789"); + } + var dur1 = System.currentTimeMillis() - startTime; + log.info("Duration without parsing: {}", dur1); + startTime = System.currentTimeMillis(); + for (int i = 0; i < execNum; i++) { + var res = Long.parseLong("123456788") > Long.parseLong("123456789"); + } + var dur2 = System.currentTimeMillis() - startTime; + log.info("Duration with parsing: {}", dur2); + + assertThat(dur1).isLessThan(dur2); + } } From 56df73143af24747495dd424f44166c6164ed1bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Attila=20M=C3=A9sz=C3=A1ros?= Date: Fri, 10 Oct 2025 14:54:55 +0200 Subject: [PATCH 02/14] sanity check MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Attila Mészáros --- .../api/reconciler/PrimaryUpdateAndCacheUtils.java | 13 +++++++++++-- .../reconciler/PrimaryUpdateAndCacheUtilsTest.java | 3 +++ 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/PrimaryUpdateAndCacheUtils.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/PrimaryUpdateAndCacheUtils.java index d353c9649a..11c0f12864 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/PrimaryUpdateAndCacheUtils.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/PrimaryUpdateAndCacheUtils.java @@ -461,10 +461,19 @@ public static int compareResourceVersions(String v1, String v2) { return -1; } for (int i = 0; i < v1Length; i++) { - if (v1.charAt(i) > v2.charAt(i)) { + var char1 = v1.charAt(i); + var char2 = v2.charAt(i); + if (!Character.isDigit(char1)) { + throw new IllegalStateException("Non numeric characters in resource version (1): " + char1); + } + if (!Character.isDigit(char2)) { + throw new IllegalStateException("Non numeric characters in resource version (2): " + char2); + } + + if (char1 > char2) { return 1; } - if (v1.charAt(i) < v2.charAt(i)) { + if (char1 < char2) { return -1; } } diff --git a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/api/reconciler/PrimaryUpdateAndCacheUtilsTest.java b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/api/reconciler/PrimaryUpdateAndCacheUtilsTest.java index ce310200de..0db50e2c3b 100644 --- a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/api/reconciler/PrimaryUpdateAndCacheUtilsTest.java +++ b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/api/reconciler/PrimaryUpdateAndCacheUtilsTest.java @@ -190,6 +190,9 @@ public void compareResourceVersionsTest() { assertThat(compareResourceVersions("123", "2")).isPositive(); assertThat(compareResourceVersions("3", "211")).isNegative(); + + assertThrows(IllegalStateException.class, () -> compareResourceVersions("aa", "22")); + assertThrows(IllegalStateException.class, () -> compareResourceVersions("11", "ba")); } // naive performance that compares the works case scenario for non parsing variant From e9d70f5c261d5dbde25285f2acb22af4712b4742 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Attila=20M=C3=A9sz=C3=A1ros?= Date: Fri, 10 Oct 2025 14:57:57 +0200 Subject: [PATCH 03/14] wip MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Attila Mészáros --- .../operator/api/reconciler/PrimaryUpdateAndCacheUtilsTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/api/reconciler/PrimaryUpdateAndCacheUtilsTest.java b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/api/reconciler/PrimaryUpdateAndCacheUtilsTest.java index 0db50e2c3b..ff63056325 100644 --- a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/api/reconciler/PrimaryUpdateAndCacheUtilsTest.java +++ b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/api/reconciler/PrimaryUpdateAndCacheUtilsTest.java @@ -198,7 +198,7 @@ public void compareResourceVersionsTest() { // naive performance that compares the works case scenario for non parsing variant @Test public void compareResourcePerformanceTest() { - var execNum = 10000000; + var execNum = 100000000; var startTime = System.currentTimeMillis(); for (int i = 0; i < execNum; i++) { var res = compareResourceVersions("123456788", "123456789"); From a0a143d896097875482618eb327196ac30e6bf07 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Attila=20M=C3=A9sz=C3=A1ros?= Date: Fri, 10 Oct 2025 16:14:09 +0200 Subject: [PATCH 04/14] Update operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/PrimaryUpdateAndCacheUtils.java Co-authored-by: Steven Hawkins --- .../PrimaryUpdateAndCacheUtils.java | 64 +++++++++++++------ 1 file changed, 44 insertions(+), 20 deletions(-) diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/PrimaryUpdateAndCacheUtils.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/PrimaryUpdateAndCacheUtils.java index 11c0f12864..5990134e03 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/PrimaryUpdateAndCacheUtils.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/PrimaryUpdateAndCacheUtils.java @@ -453,30 +453,54 @@ public static

P addFinalizerWithSSA( public static int compareResourceVersions(String v1, String v2) { var v1Length = v1.length(); - var v2Length = v2.length(); - if (v1Length > v2Length) { - return 1; + if (v1Length == 0) { + throw new IllegalStateException("Resource version (1) is empty"); } - if (v2Length > v1Length) { - return -1; + var v2Length = v2.length(); + if (v2Length == 0) { + throw new IllegalStateException("Resource version (2) is empty"); } - for (int i = 0; i < v1Length; i++) { - var char1 = v1.charAt(i); - var char2 = v2.charAt(i); - if (!Character.isDigit(char1)) { - throw new IllegalStateException("Non numeric characters in resource version (1): " + char1); - } - if (!Character.isDigit(char2)) { - throw new IllegalStateException("Non numeric characters in resource version (2): " + char2); - } - - if (char1 > char2) { - return 1; + var maxLength = Math.max(v1Length, v2Length); + boolean v1LeadingZero = true; + boolean v2LeadingZero = true; + int lengthComparison = 0; + int comparison = 0; + for (int i = 0; i < maxLength; i++) { + char char1 = 0; + if (i < v1Length) { + char1 = v1.charAt(i); + if (v1LeadingZero) { + if (char1 == '0') { + throw new IllegalStateException("Resource version (1) cannot begin with 0"); + } + v1LeadingZero = false; + } + if (!Character.isDigit(char1)) { + throw new IllegalStateException( + "Non numeric characters in resource version (1): " + char1); + } } - if (char1 < char2) { - return -1; + if (i < v2Length) { + var char2 = v2.charAt(i); + if (v2LeadingZero) { + if (char2 == '0') { + throw new IllegalStateException("Resource version (1) cannot begin with 0"); + } + v2LeadingZero = false; + } + if (!Character.isDigit(char2)) { + throw new IllegalStateException( + "Non numeric characters in resource version (2): " + char2); + } + if (char1 == 0) { + lengthComparison = -1; + } else if (comparison == 0) { + comparison = Character.compare(char1, char2); + } + } else { + lengthComparison = 1; } } - return 0; + return lengthComparison != 0 ? lengthComparison : comparison; } } From 635c1290f108929bde987279095c64020ff12781 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Attila=20M=C3=A9sz=C3=A1ros?= Date: Fri, 10 Oct 2025 16:18:08 +0200 Subject: [PATCH 05/14] shorter test run MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Attila Mészáros --- .../operator/api/reconciler/PrimaryUpdateAndCacheUtilsTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/api/reconciler/PrimaryUpdateAndCacheUtilsTest.java b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/api/reconciler/PrimaryUpdateAndCacheUtilsTest.java index ff63056325..0db50e2c3b 100644 --- a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/api/reconciler/PrimaryUpdateAndCacheUtilsTest.java +++ b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/api/reconciler/PrimaryUpdateAndCacheUtilsTest.java @@ -198,7 +198,7 @@ public void compareResourceVersionsTest() { // naive performance that compares the works case scenario for non parsing variant @Test public void compareResourcePerformanceTest() { - var execNum = 100000000; + var execNum = 10000000; var startTime = System.currentTimeMillis(); for (int i = 0; i < execNum; i++) { var res = compareResourceVersions("123456788", "123456789"); From c1a1abb613d4730dfeac4d038e66a807088f53c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Attila=20M=C3=A9sz=C3=A1ros?= Date: Fri, 10 Oct 2025 16:25:02 +0200 Subject: [PATCH 06/14] Update operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/PrimaryUpdateAndCacheUtils.java Co-authored-by: Steven Hawkins --- .../operator/api/reconciler/PrimaryUpdateAndCacheUtils.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/PrimaryUpdateAndCacheUtils.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/PrimaryUpdateAndCacheUtils.java index 5990134e03..1da3e455e8 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/PrimaryUpdateAndCacheUtils.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/PrimaryUpdateAndCacheUtils.java @@ -484,7 +484,7 @@ public static int compareResourceVersions(String v1, String v2) { var char2 = v2.charAt(i); if (v2LeadingZero) { if (char2 == '0') { - throw new IllegalStateException("Resource version (1) cannot begin with 0"); + throw new IllegalStateException("Resource version (2) cannot begin with 0"); } v2LeadingZero = false; } From e06826188dece380402997a061a5c660c7c30596 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Attila=20M=C3=A9sz=C3=A1ros?= Date: Fri, 10 Oct 2025 16:35:44 +0200 Subject: [PATCH 07/14] Update operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/PrimaryUpdateAndCacheUtils.java Co-authored-by: Steven Hawkins --- .../api/reconciler/PrimaryUpdateAndCacheUtils.java | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/PrimaryUpdateAndCacheUtils.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/PrimaryUpdateAndCacheUtils.java index 1da3e455e8..7a858b9ff8 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/PrimaryUpdateAndCacheUtils.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/PrimaryUpdateAndCacheUtils.java @@ -463,7 +463,6 @@ public static int compareResourceVersions(String v1, String v2) { var maxLength = Math.max(v1Length, v2Length); boolean v1LeadingZero = true; boolean v2LeadingZero = true; - int lengthComparison = 0; int comparison = 0; for (int i = 0; i < maxLength; i++) { char char1 = 0; @@ -493,14 +492,14 @@ public static int compareResourceVersions(String v1, String v2) { "Non numeric characters in resource version (2): " + char2); } if (char1 == 0) { - lengthComparison = -1; + comparison = -1; } else if (comparison == 0) { comparison = Character.compare(char1, char2); } } else { - lengthComparison = 1; + comparison = 1; } } - return lengthComparison != 0 ? lengthComparison : comparison; + return comparison; } } From 609012dcf4c2417abb89ac31c57f151d162e5e04 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Attila=20M=C3=A9sz=C3=A1ros?= Date: Fri, 10 Oct 2025 18:12:15 +0200 Subject: [PATCH 08/14] wip MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Attila Mészáros --- .../PrimaryUpdateAndCacheUtils.java | 56 +++++++------------ .../PrimaryUpdateAndCacheUtilsTest.java | 6 +- 2 files changed, 24 insertions(+), 38 deletions(-) diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/PrimaryUpdateAndCacheUtils.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/PrimaryUpdateAndCacheUtils.java index 7a858b9ff8..55cf8daa03 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/PrimaryUpdateAndCacheUtils.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/PrimaryUpdateAndCacheUtils.java @@ -453,53 +453,39 @@ public static

P addFinalizerWithSSA( public static int compareResourceVersions(String v1, String v2) { var v1Length = v1.length(); + var v2Length = v2.length(); if (v1Length == 0) { - throw new IllegalStateException("Resource version (1) is empty"); + throw new IllegalArgumentException("resource version must not be empty (1)"); } - var v2Length = v2.length(); if (v2Length == 0) { - throw new IllegalStateException("Resource version (2) is empty"); + throw new IllegalArgumentException("resource version must not be empty (2)"); } - var maxLength = Math.max(v1Length, v2Length); - boolean v1LeadingZero = true; - boolean v2LeadingZero = true; - int comparison = 0; - for (int i = 0; i < maxLength; i++) { - char char1 = 0; - if (i < v1Length) { - char1 = v1.charAt(i); - if (v1LeadingZero) { - if (char1 == '0') { - throw new IllegalStateException("Resource version (1) cannot begin with 0"); - } - v1LeadingZero = false; - } + if (v1Length > v2Length) { + return 1; + } + if (v2Length > v1Length) { + return -1; + } + for (int i = 0; i < v1Length; i++) { + if (v1.charAt(i) > v2.charAt(i)) { + var char1 = v1.charAt(i); + var char2 = v2.charAt(i); if (!Character.isDigit(char1)) { - throw new IllegalStateException( + throw new IllegalArgumentException( "Non numeric characters in resource version (1): " + char1); } - } - if (i < v2Length) { - var char2 = v2.charAt(i); - if (v2LeadingZero) { - if (char2 == '0') { - throw new IllegalStateException("Resource version (2) cannot begin with 0"); - } - v2LeadingZero = false; - } if (!Character.isDigit(char2)) { - throw new IllegalStateException( + throw new IllegalArgumentException( "Non numeric characters in resource version (2): " + char2); } - if (char1 == 0) { - comparison = -1; - } else if (comparison == 0) { - comparison = Character.compare(char1, char2); + if (char1 > char2) { + return 1; + } + if (char1 < char2) { + return -1; } - } else { - comparison = 1; } } - return comparison; + return 0; } } diff --git a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/api/reconciler/PrimaryUpdateAndCacheUtilsTest.java b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/api/reconciler/PrimaryUpdateAndCacheUtilsTest.java index 0db50e2c3b..82cb16434a 100644 --- a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/api/reconciler/PrimaryUpdateAndCacheUtilsTest.java +++ b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/api/reconciler/PrimaryUpdateAndCacheUtilsTest.java @@ -191,14 +191,14 @@ public void compareResourceVersionsTest() { assertThat(compareResourceVersions("123", "2")).isPositive(); assertThat(compareResourceVersions("3", "211")).isNegative(); - assertThrows(IllegalStateException.class, () -> compareResourceVersions("aa", "22")); - assertThrows(IllegalStateException.class, () -> compareResourceVersions("11", "ba")); + assertThrows(IllegalArgumentException.class, () -> compareResourceVersions("aa", "22")); + assertThrows(IllegalArgumentException.class, () -> compareResourceVersions("11", "ba")); } // naive performance that compares the works case scenario for non parsing variant @Test public void compareResourcePerformanceTest() { - var execNum = 10000000; + var execNum = 20000000; var startTime = System.currentTimeMillis(); for (int i = 0; i < execNum; i++) { var res = compareResourceVersions("123456788", "123456789"); From ec7d6e5f915617684b781e3716ebde2d02849f68 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Attila=20M=C3=A9sz=C3=A1ros?= Date: Fri, 10 Oct 2025 18:13:50 +0200 Subject: [PATCH 09/14] wip MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Attila Mészáros --- .../PrimaryUpdateAndCacheUtils.java | 34 +++++++++---------- .../PrimaryUpdateAndCacheUtilsTest.java | 2 ++ 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/PrimaryUpdateAndCacheUtils.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/PrimaryUpdateAndCacheUtils.java index 55cf8daa03..4b477904fa 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/PrimaryUpdateAndCacheUtils.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/PrimaryUpdateAndCacheUtils.java @@ -463,27 +463,25 @@ public static int compareResourceVersions(String v1, String v2) { if (v1Length > v2Length) { return 1; } - if (v2Length > v1Length) { + if (v1Length < v2Length) { return -1; } for (int i = 0; i < v1Length; i++) { - if (v1.charAt(i) > v2.charAt(i)) { - var char1 = v1.charAt(i); - var char2 = v2.charAt(i); - if (!Character.isDigit(char1)) { - throw new IllegalArgumentException( - "Non numeric characters in resource version (1): " + char1); - } - if (!Character.isDigit(char2)) { - throw new IllegalArgumentException( - "Non numeric characters in resource version (2): " + char2); - } - if (char1 > char2) { - return 1; - } - if (char1 < char2) { - return -1; - } + var char1 = v1.charAt(i); + var char2 = v2.charAt(i); + if (!Character.isDigit(char1)) { + throw new IllegalArgumentException( + "Non numeric characters in resource version (1): " + char1); + } + if (!Character.isDigit(char2)) { + throw new IllegalArgumentException( + "Non numeric characters in resource version (2): " + char2); + } + if (char1 > char2) { + return 1; + } + if (char1 < char2) { + return -1; } } return 0; diff --git a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/api/reconciler/PrimaryUpdateAndCacheUtilsTest.java b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/api/reconciler/PrimaryUpdateAndCacheUtilsTest.java index 82cb16434a..0074059b35 100644 --- a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/api/reconciler/PrimaryUpdateAndCacheUtilsTest.java +++ b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/api/reconciler/PrimaryUpdateAndCacheUtilsTest.java @@ -193,6 +193,8 @@ public void compareResourceVersionsTest() { assertThrows(IllegalArgumentException.class, () -> compareResourceVersions("aa", "22")); assertThrows(IllegalArgumentException.class, () -> compareResourceVersions("11", "ba")); + assertThrows(IllegalArgumentException.class, () -> compareResourceVersions("", "22")); + assertThrows(IllegalArgumentException.class, () -> compareResourceVersions("11", "")); } // naive performance that compares the works case scenario for non parsing variant From d3e4886e3a5d729338abc4ab38fff47c50b050e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Attila=20M=C3=A9sz=C3=A1ros?= Date: Fri, 10 Oct 2025 18:19:43 +0200 Subject: [PATCH 10/14] improve MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Attila Mészáros --- .../operator/api/reconciler/PrimaryUpdateAndCacheUtils.java | 6 ++++++ .../api/reconciler/PrimaryUpdateAndCacheUtilsTest.java | 2 ++ 2 files changed, 8 insertions(+) diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/PrimaryUpdateAndCacheUtils.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/PrimaryUpdateAndCacheUtils.java index 4b477904fa..6edb0fc379 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/PrimaryUpdateAndCacheUtils.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/PrimaryUpdateAndCacheUtils.java @@ -460,6 +460,12 @@ public static int compareResourceVersions(String v1, String v2) { if (v2Length == 0) { throw new IllegalArgumentException("resource version must not be empty (2)"); } + if (v1.charAt(0) == '0') { + throw new IllegalArgumentException("resource version (1) must not start with 0"); + } + if (v2.charAt(0) == '0') { + throw new IllegalArgumentException("resource version (2) must not start with 0"); + } if (v1Length > v2Length) { return 1; } diff --git a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/api/reconciler/PrimaryUpdateAndCacheUtilsTest.java b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/api/reconciler/PrimaryUpdateAndCacheUtilsTest.java index 0074059b35..085e1507d7 100644 --- a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/api/reconciler/PrimaryUpdateAndCacheUtilsTest.java +++ b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/api/reconciler/PrimaryUpdateAndCacheUtilsTest.java @@ -195,6 +195,8 @@ public void compareResourceVersionsTest() { assertThrows(IllegalArgumentException.class, () -> compareResourceVersions("11", "ba")); assertThrows(IllegalArgumentException.class, () -> compareResourceVersions("", "22")); assertThrows(IllegalArgumentException.class, () -> compareResourceVersions("11", "")); + assertThrows(IllegalArgumentException.class, () -> compareResourceVersions("01", "123")); + assertThrows(IllegalArgumentException.class, () -> compareResourceVersions("123", "01")); } // naive performance that compares the works case scenario for non parsing variant From 118226b59b021d4b73bf485f8a3b6846ddb290d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Attila=20M=C3=A9sz=C3=A1ros?= Date: Sat, 11 Oct 2025 14:33:14 +0200 Subject: [PATCH 11/14] wip MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Attila Mészáros --- .../PrimaryUpdateAndCacheUtils.java | 20 ------------------- .../PrimaryUpdateAndCacheUtilsTest.java | 7 ------- 2 files changed, 27 deletions(-) diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/PrimaryUpdateAndCacheUtils.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/PrimaryUpdateAndCacheUtils.java index 6edb0fc379..1690dd5972 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/PrimaryUpdateAndCacheUtils.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/PrimaryUpdateAndCacheUtils.java @@ -454,18 +454,6 @@ public static

P addFinalizerWithSSA( public static int compareResourceVersions(String v1, String v2) { var v1Length = v1.length(); var v2Length = v2.length(); - if (v1Length == 0) { - throw new IllegalArgumentException("resource version must not be empty (1)"); - } - if (v2Length == 0) { - throw new IllegalArgumentException("resource version must not be empty (2)"); - } - if (v1.charAt(0) == '0') { - throw new IllegalArgumentException("resource version (1) must not start with 0"); - } - if (v2.charAt(0) == '0') { - throw new IllegalArgumentException("resource version (2) must not start with 0"); - } if (v1Length > v2Length) { return 1; } @@ -475,14 +463,6 @@ public static int compareResourceVersions(String v1, String v2) { for (int i = 0; i < v1Length; i++) { var char1 = v1.charAt(i); var char2 = v2.charAt(i); - if (!Character.isDigit(char1)) { - throw new IllegalArgumentException( - "Non numeric characters in resource version (1): " + char1); - } - if (!Character.isDigit(char2)) { - throw new IllegalArgumentException( - "Non numeric characters in resource version (2): " + char2); - } if (char1 > char2) { return 1; } diff --git a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/api/reconciler/PrimaryUpdateAndCacheUtilsTest.java b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/api/reconciler/PrimaryUpdateAndCacheUtilsTest.java index 085e1507d7..46b3b28330 100644 --- a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/api/reconciler/PrimaryUpdateAndCacheUtilsTest.java +++ b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/api/reconciler/PrimaryUpdateAndCacheUtilsTest.java @@ -190,13 +190,6 @@ public void compareResourceVersionsTest() { assertThat(compareResourceVersions("123", "2")).isPositive(); assertThat(compareResourceVersions("3", "211")).isNegative(); - - assertThrows(IllegalArgumentException.class, () -> compareResourceVersions("aa", "22")); - assertThrows(IllegalArgumentException.class, () -> compareResourceVersions("11", "ba")); - assertThrows(IllegalArgumentException.class, () -> compareResourceVersions("", "22")); - assertThrows(IllegalArgumentException.class, () -> compareResourceVersions("11", "")); - assertThrows(IllegalArgumentException.class, () -> compareResourceVersions("01", "123")); - assertThrows(IllegalArgumentException.class, () -> compareResourceVersions("123", "01")); } // naive performance that compares the works case scenario for non parsing variant From 3a16e124ef2c89f582972243eb924bbbf164ead5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Attila=20M=C3=A9sz=C3=A1ros?= Date: Sat, 11 Oct 2025 14:56:51 +0200 Subject: [PATCH 12/14] revert to KEP compatible way MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Attila Mészáros --- .../PrimaryUpdateAndCacheUtils.java | 56 ++++++++++++++----- .../PrimaryUpdateAndCacheUtilsTest.java | 9 ++- 2 files changed, 51 insertions(+), 14 deletions(-) diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/PrimaryUpdateAndCacheUtils.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/PrimaryUpdateAndCacheUtils.java index 1690dd5972..ddeef3459b 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/PrimaryUpdateAndCacheUtils.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/PrimaryUpdateAndCacheUtils.java @@ -453,23 +453,53 @@ public static

P addFinalizerWithSSA( public static int compareResourceVersions(String v1, String v2) { var v1Length = v1.length(); - var v2Length = v2.length(); - if (v1Length > v2Length) { - return 1; + if (v1Length == 0) { + throw new IllegalArgumentException("Resource version (1) is empty"); } - if (v1Length < v2Length) { - return -1; + var v2Length = v2.length(); + if (v2Length == 0) { + throw new IllegalArgumentException("Resource version (2) is empty"); } - for (int i = 0; i < v1Length; i++) { - var char1 = v1.charAt(i); - var char2 = v2.charAt(i); - if (char1 > char2) { - return 1; + var maxLength = Math.max(v1Length, v2Length); + boolean v1LeadingZero = true; + boolean v2LeadingZero = true; + int comparison = 0; + for (int i = 0; i < maxLength; i++) { + char char1 = 0; + if (i < v1Length) { + char1 = v1.charAt(i); + if (v1LeadingZero) { + if (char1 == '0') { + throw new IllegalArgumentException("Resource version (1) cannot begin with 0"); + } + v1LeadingZero = false; + } + if (!Character.isDigit(char1)) { + throw new IllegalArgumentException( + "Non numeric characters in resource version (1): " + char1); + } } - if (char1 < char2) { - return -1; + if (i < v2Length) { + var char2 = v2.charAt(i); + if (v2LeadingZero) { + if (char2 == '0') { + throw new IllegalArgumentException("Resource version (2) cannot begin with 0"); + } + v2LeadingZero = false; + } + if (!Character.isDigit(char2)) { + throw new IllegalArgumentException( + "Non numeric characters in resource version (2): " + char2); + } + if (char1 == 0) { + comparison = -1; + } else if (comparison == 0) { + comparison = Character.compare(char1, char2); + } + } else { + comparison = 1; } } - return 0; + return comparison; } } diff --git a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/api/reconciler/PrimaryUpdateAndCacheUtilsTest.java b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/api/reconciler/PrimaryUpdateAndCacheUtilsTest.java index 46b3b28330..78415cc861 100644 --- a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/api/reconciler/PrimaryUpdateAndCacheUtilsTest.java +++ b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/api/reconciler/PrimaryUpdateAndCacheUtilsTest.java @@ -190,12 +190,19 @@ public void compareResourceVersionsTest() { assertThat(compareResourceVersions("123", "2")).isPositive(); assertThat(compareResourceVersions("3", "211")).isNegative(); + + assertThrows(IllegalArgumentException.class, () -> compareResourceVersions("aa", "22")); + assertThrows(IllegalArgumentException.class, () -> compareResourceVersions("11", "ba")); + assertThrows(IllegalArgumentException.class, () -> compareResourceVersions("", "22")); + assertThrows(IllegalArgumentException.class, () -> compareResourceVersions("11", "")); + assertThrows(IllegalArgumentException.class, () -> compareResourceVersions("01", "123")); + assertThrows(IllegalArgumentException.class, () -> compareResourceVersions("123", "01")); } // naive performance that compares the works case scenario for non parsing variant @Test public void compareResourcePerformanceTest() { - var execNum = 20000000; + var execNum = 30000000; var startTime = System.currentTimeMillis(); for (int i = 0; i < execNum; i++) { var res = compareResourceVersions("123456788", "123456789"); From 27696a7584c9732f201b1708fa5fc9c1e2add55f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Attila=20M=C3=A9sz=C3=A1ros?= Date: Sat, 11 Oct 2025 15:30:07 +0200 Subject: [PATCH 13/14] additional tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Attila Mészáros --- .../operator/api/reconciler/PrimaryUpdateAndCacheUtilsTest.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/api/reconciler/PrimaryUpdateAndCacheUtilsTest.java b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/api/reconciler/PrimaryUpdateAndCacheUtilsTest.java index 78415cc861..2f563d2d6b 100644 --- a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/api/reconciler/PrimaryUpdateAndCacheUtilsTest.java +++ b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/api/reconciler/PrimaryUpdateAndCacheUtilsTest.java @@ -197,6 +197,8 @@ public void compareResourceVersionsTest() { assertThrows(IllegalArgumentException.class, () -> compareResourceVersions("11", "")); assertThrows(IllegalArgumentException.class, () -> compareResourceVersions("01", "123")); assertThrows(IllegalArgumentException.class, () -> compareResourceVersions("123", "01")); + assertThrows(IllegalArgumentException.class, () -> compareResourceVersions("3213", "123a")); + assertThrows(IllegalArgumentException.class, () -> compareResourceVersions("321", "123a")); } // naive performance that compares the works case scenario for non parsing variant From b3d8451181849ab24e75d113a3bf61b8a33cd408 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Attila=20M=C3=A9sz=C3=A1ros?= Date: Sat, 11 Oct 2025 15:30:53 +0200 Subject: [PATCH 14/14] tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Attila Mészáros --- .../operator/api/reconciler/PrimaryUpdateAndCacheUtilsTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/api/reconciler/PrimaryUpdateAndCacheUtilsTest.java b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/api/reconciler/PrimaryUpdateAndCacheUtilsTest.java index 2f563d2d6b..e50a80334f 100644 --- a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/api/reconciler/PrimaryUpdateAndCacheUtilsTest.java +++ b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/api/reconciler/PrimaryUpdateAndCacheUtilsTest.java @@ -186,8 +186,8 @@ void cachePollTimeouts() { public void compareResourceVersionsTest() { assertThat(compareResourceVersions("11", "22")).isNegative(); assertThat(compareResourceVersions("22", "11")).isPositive(); + assertThat(compareResourceVersions("1", "1")).isZero(); assertThat(compareResourceVersions("11", "11")).isZero(); - assertThat(compareResourceVersions("123", "2")).isPositive(); assertThat(compareResourceVersions("3", "211")).isNegative();