This is an automated email from the ASF dual-hosted git repository.

davidzollo pushed a commit to branch dev
in repository https://gitbox.apache.org/repos/asf/seatunnel.git


The following commit(s) were added to refs/heads/dev by this push:
     new 71ab9e32c3 [Improve][connector-clickhouse] Improved clickhouse source 
enumerator splits allocation algorithm for subtasks (#10866)
71ab9e32c3 is described below

commit 71ab9e32c3bc6ab20c94f176125098cc4fbc979f
Author: JeremyXin <[email protected]>
AuthorDate: Tue May 12 22:47:59 2026 +0800

    [Improve][connector-clickhouse] Improved clickhouse source enumerator 
splits allocation algorithm for subtasks (#10866)
---
 .../split/ClickhouseSourceSplitEnumerator.java     |  19 +++-
 .../split/ClickhouseSourceSplitEnumeratorTest.java | 109 +++++++++++++++++++++
 2 files changed, 124 insertions(+), 4 deletions(-)

diff --git 
a/seatunnel-connectors-v2/connector-clickhouse/src/main/java/org/apache/seatunnel/connectors/seatunnel/clickhouse/source/split/ClickhouseSourceSplitEnumerator.java
 
b/seatunnel-connectors-v2/connector-clickhouse/src/main/java/org/apache/seatunnel/connectors/seatunnel/clickhouse/source/split/ClickhouseSourceSplitEnumerator.java
index fcf85fa226..35e88dbaf0 100644
--- 
a/seatunnel-connectors-v2/connector-clickhouse/src/main/java/org/apache/seatunnel/connectors/seatunnel/clickhouse/source/split/ClickhouseSourceSplitEnumerator.java
+++ 
b/seatunnel-connectors-v2/connector-clickhouse/src/main/java/org/apache/seatunnel/connectors/seatunnel/clickhouse/source/split/ClickhouseSourceSplitEnumerator.java
@@ -41,11 +41,14 @@ import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
+import java.util.Comparator;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ThreadLocalRandom;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.stream.Collectors;
 import java.util.stream.IntStream;
 
 public class ClickhouseSourceSplitEnumerator
@@ -60,6 +63,7 @@ public class ClickhouseSourceSplitEnumerator
     private final Context<ClickhouseSourceSplit> context;
     private final Map<TablePath, List<ClickHouseNode>> nodesMap;
     private final Object stateLock = new Object();
+    private final AtomicInteger assignCount = new AtomicInteger(0);
 
     public ClickhouseSourceSplitEnumerator(
             Context<ClickhouseSourceSplit> context,
@@ -198,8 +202,15 @@ public class ClickhouseSourceSplitEnumerator
 
     private void addPendingSplit(Collection<ClickhouseSourceSplit> splits) {
         int readerCount = context.currentParallelism();
-        for (ClickhouseSourceSplit split : splits) {
-            int ownerReader = getSplitOwner(split.splitId(), readerCount);
+
+        List<ClickhouseSourceSplit> sortedSplits =
+                splits.stream()
+                        
.sorted(Comparator.comparing(ClickhouseSourceSplit::getSplitId))
+                        .collect(Collectors.toList());
+
+        assignCount.set(0);
+        for (ClickhouseSourceSplit split : sortedSplits) {
+            int ownerReader = getSplitOwner(assignCount.getAndIncrement(), 
readerCount);
             LOG.debug("Assigning {} to {} reader.", split, ownerReader);
 
             pendingSplit.computeIfAbsent(ownerReader, r -> new 
ArrayList<>()).add(split);
@@ -210,8 +221,8 @@ public class ClickhouseSourceSplitEnumerator
         pendingSplit.computeIfAbsent(ownerReader, r -> new 
ArrayList<>()).addAll(splits);
     }
 
-    private static int getSplitOwner(String tp, int numReaders) {
-        return (tp.hashCode() & Integer.MAX_VALUE) % numReaders;
+    private static int getSplitOwner(int assignCount, int numReaders) {
+        return assignCount % numReaders;
     }
 
     private List<Shard> getClusterShardList(
diff --git 
a/seatunnel-connectors-v2/connector-clickhouse/src/test/java/org/apache/seatunnel/connectors/seatunnel/clickhouse/source/split/ClickhouseSourceSplitEnumeratorTest.java
 
b/seatunnel-connectors-v2/connector-clickhouse/src/test/java/org/apache/seatunnel/connectors/seatunnel/clickhouse/source/split/ClickhouseSourceSplitEnumeratorTest.java
new file mode 100644
index 0000000000..107a7b7e77
--- /dev/null
+++ 
b/seatunnel-connectors-v2/connector-clickhouse/src/test/java/org/apache/seatunnel/connectors/seatunnel/clickhouse/source/split/ClickhouseSourceSplitEnumeratorTest.java
@@ -0,0 +1,109 @@
+/*
+ * 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.seatunnel.connectors.seatunnel.clickhouse.source.split;
+
+import org.apache.seatunnel.api.common.metrics.MetricsContext;
+import org.apache.seatunnel.api.event.EventListener;
+import org.apache.seatunnel.api.source.SourceEvent;
+import org.apache.seatunnel.api.source.SourceSplitEnumerator;
+import 
org.apache.seatunnel.connectors.seatunnel.clickhouse.state.ClickhouseSourceState;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+public class ClickhouseSourceSplitEnumeratorTest {
+
+    @Test
+    public void shouldBalanceSplitsEvenlyAcrossReaders() throws Exception {
+        TestingContext context = new TestingContext(4);
+
+        ClickhouseSourceSplitEnumerator enumerator =
+                new ClickhouseSourceSplitEnumerator(
+                        context, null, Collections.emptyMap(), 
Collections.emptyMap());
+
+        Method addPendingSplit =
+                ClickhouseSourceSplitEnumerator.class.getDeclaredMethod(
+                        "addPendingSplit", java.util.Collection.class);
+        addPendingSplit.setAccessible(true);
+        addPendingSplit.invoke(enumerator, buildSplits(10));
+
+        ClickhouseSourceState state = enumerator.snapshotState(1L);
+        Map<Integer, List<ClickhouseSourceSplit>> pendingSplit = 
state.getPendingSplit();
+
+        Assertions.assertEquals(4, pendingSplit.size());
+        Assertions.assertEquals(3, pendingSplit.get(0).size());
+        Assertions.assertEquals(3, pendingSplit.get(1).size());
+        Assertions.assertEquals(2, pendingSplit.get(2).size());
+        Assertions.assertEquals(2, pendingSplit.get(3).size());
+    }
+
+    private List<ClickhouseSourceSplit> buildSplits(int size) {
+        List<ClickhouseSourceSplit> splits = new ArrayList<>();
+        for (int i = 0; i < size; i++) {
+            splits.add(
+                    new ClickhouseSourceSplit(
+                            null, null, Collections.emptyList(), null, null, 
"split-" + i));
+        }
+        return splits;
+    }
+
+    private static class TestingContext
+            implements SourceSplitEnumerator.Context<ClickhouseSourceSplit> {
+        private final int parallelism;
+
+        private TestingContext(int parallelism) {
+            this.parallelism = parallelism;
+        }
+
+        @Override
+        public int currentParallelism() {
+            return parallelism;
+        }
+
+        @Override
+        public Set<Integer> registeredReaders() {
+            return Collections.emptySet();
+        }
+
+        @Override
+        public void assignSplit(int subtaskId, List<ClickhouseSourceSplit> 
splits) {}
+
+        @Override
+        public void signalNoMoreSplits(int subtask) {}
+
+        @Override
+        public void sendEventToSourceReader(int subtaskId, SourceEvent event) 
{}
+
+        @Override
+        public MetricsContext getMetricsContext() {
+            return null;
+        }
+
+        @Override
+        public EventListener getEventListener() {
+            return null;
+        }
+    }
+}

Reply via email to