arunpandianp commented on code in PR #40593:
URL: https://github.com/apache/beam/pull/40593#discussion_r4292181147


##########
runners/google-cloud-dataflow-java/worker/src/main/java/org/apache/beam/runners/dataflow/worker/util/KeyGroupWorkIndex.java:
##########
@@ -0,0 +1,175 @@
+/*
+ * 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.beam.runners.dataflow.worker.util;
+
+import java.util.ArrayDeque;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import javax.annotation.concurrent.ThreadSafe;
+import org.apache.beam.runners.dataflow.worker.streaming.Work;
+import 
org.apache.beam.runners.dataflow.worker.util.BoundedQueueExecutor.QueuedWork;
+import 
org.apache.beam.vendor.guava.v32_1_2_jre.com.google.common.annotations.VisibleForTesting;
+import org.checkerframework.checker.nullness.qual.Nullable;
+
+/**
+ * Sharded FIFO index of {@link QueuedWork} by computation and {@link 
Work.KeyGroup} for work
+ * stealing by multi-key bundles.
+ */
+@ThreadSafe
+final class KeyGroupWorkIndex {
+
+  private static final int NUM_SHARDS = 64;
+  private static final int INITIAL_QUEUE_CAPACITY = 4;
+
+  // Each shard map and its queues are guarded by synchronized (shard).
+  private final List<Map<QueueKey, ArrayDeque<QueuedWork>>> shards;
+
+  KeyGroupWorkIndex() {
+    List<Map<QueueKey, ArrayDeque<QueuedWork>>> list = new 
ArrayList<>(NUM_SHARDS);
+    for (int i = 0; i < NUM_SHARDS; i++) {
+      list.add(new HashMap<>());
+    }
+    this.shards = Collections.unmodifiableList(list);

Review Comment:
   done.



##########
runners/google-cloud-dataflow-java/worker/src/main/java/org/apache/beam/runners/dataflow/worker/util/KeyGroupWorkIndex.java:
##########
@@ -0,0 +1,175 @@
+/*
+ * 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.beam.runners.dataflow.worker.util;
+
+import java.util.ArrayDeque;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import javax.annotation.concurrent.ThreadSafe;
+import org.apache.beam.runners.dataflow.worker.streaming.Work;
+import 
org.apache.beam.runners.dataflow.worker.util.BoundedQueueExecutor.QueuedWork;
+import 
org.apache.beam.vendor.guava.v32_1_2_jre.com.google.common.annotations.VisibleForTesting;
+import org.checkerframework.checker.nullness.qual.Nullable;
+
+/**
+ * Sharded FIFO index of {@link QueuedWork} by computation and {@link 
Work.KeyGroup} for work
+ * stealing by multi-key bundles.
+ */
+@ThreadSafe
+final class KeyGroupWorkIndex {
+
+  private static final int NUM_SHARDS = 64;
+  private static final int INITIAL_QUEUE_CAPACITY = 4;
+
+  // Each shard map and its queues are guarded by synchronized (shard).
+  private final List<Map<QueueKey, ArrayDeque<QueuedWork>>> shards;
+
+  KeyGroupWorkIndex() {
+    List<Map<QueueKey, ArrayDeque<QueuedWork>>> list = new 
ArrayList<>(NUM_SHARDS);
+    for (int i = 0; i < NUM_SHARDS; i++) {
+      list.add(new HashMap<>());
+    }
+    this.shards = Collections.unmodifiableList(list);
+  }
+
+  private Map<QueueKey, ArrayDeque<QueuedWork>> shardFor(QueueKey queueKey) {
+    return shards.get(Math.floorMod(queueKey.hashCode(), NUM_SHARDS));
+  }
+
+  /** Adds {@code work} to its key-group queue if it has not been claimed yet. 
*/
+  void add(QueuedWork work) {
+    if (work.isClaimed()) {
+      return;
+    }
+    QueueKey queueKey = work.queueKey();
+    Map<QueueKey, ArrayDeque<QueuedWork>> shard = shardFor(queueKey);
+    synchronized (shard) {
+      if (work.isClaimed()) {
+        return;
+      }
+      ArrayDeque<QueuedWork> queue =
+          shard.computeIfAbsent(queueKey, unused -> new 
ArrayDeque<>(INITIAL_QUEUE_CAPACITY));
+      drainClaimedFromHead(queue);
+      queue.addLast(work);
+    }
+  }
+
+  /**
+   * Drains claimed work from the head of {@code work}'s key-group queue. If 
an earlier item is
+   * still unclaimed, {@code work} remains in the queue with its references 
cleared and is drained
+   * in amortized O(1) time once the earlier item is claimed.
+   */
+  void remove(QueuedWork work) {

Review Comment:
   This is called after the work is claimed. renamed to `drainClaimed` and 
added a isClaimed check. 



##########
runners/google-cloud-dataflow-java/worker/src/main/java/org/apache/beam/runners/dataflow/worker/util/KeyGroupWorkIndex.java:
##########
@@ -0,0 +1,175 @@
+/*
+ * 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.beam.runners.dataflow.worker.util;
+
+import java.util.ArrayDeque;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import javax.annotation.concurrent.ThreadSafe;
+import org.apache.beam.runners.dataflow.worker.streaming.Work;
+import 
org.apache.beam.runners.dataflow.worker.util.BoundedQueueExecutor.QueuedWork;
+import 
org.apache.beam.vendor.guava.v32_1_2_jre.com.google.common.annotations.VisibleForTesting;
+import org.checkerframework.checker.nullness.qual.Nullable;
+
+/**
+ * Sharded FIFO index of {@link QueuedWork} by computation and {@link 
Work.KeyGroup} for work
+ * stealing by multi-key bundles.
+ */
+@ThreadSafe
+final class KeyGroupWorkIndex {
+
+  private static final int NUM_SHARDS = 64;
+  private static final int INITIAL_QUEUE_CAPACITY = 4;
+
+  // Each shard map and its queues are guarded by synchronized (shard).
+  private final List<Map<QueueKey, ArrayDeque<QueuedWork>>> shards;
+
+  KeyGroupWorkIndex() {
+    List<Map<QueueKey, ArrayDeque<QueuedWork>>> list = new 
ArrayList<>(NUM_SHARDS);
+    for (int i = 0; i < NUM_SHARDS; i++) {
+      list.add(new HashMap<>());
+    }
+    this.shards = Collections.unmodifiableList(list);
+  }
+
+  private Map<QueueKey, ArrayDeque<QueuedWork>> shardFor(QueueKey queueKey) {
+    return shards.get(Math.floorMod(queueKey.hashCode(), NUM_SHARDS));
+  }
+
+  /** Adds {@code work} to its key-group queue if it has not been claimed yet. 
*/
+  void add(QueuedWork work) {
+    if (work.isClaimed()) {
+      return;
+    }
+    QueueKey queueKey = work.queueKey();
+    Map<QueueKey, ArrayDeque<QueuedWork>> shard = shardFor(queueKey);
+    synchronized (shard) {
+      if (work.isClaimed()) {
+        return;
+      }
+      ArrayDeque<QueuedWork> queue =
+          shard.computeIfAbsent(queueKey, unused -> new 
ArrayDeque<>(INITIAL_QUEUE_CAPACITY));

Review Comment:
   We create the 64 shards in constructor. The queueKey is based on keyGroup 
and is not known during the construction. Also extracted Shard to a separate 
class to improve readability.



##########
runners/google-cloud-dataflow-java/worker/src/main/java/org/apache/beam/runners/dataflow/worker/util/BoundedQueueExecutor.java:
##########
@@ -185,6 +200,18 @@ public boolean executorQueueIsEmpty() {
     return executor.getQueue().isEmpty();
   }
 
+  /** Returns the number of entries in the executor queue, including stale 
shells. */

Review Comment:
   The method is used only by tests. Renamed it to rawQueueSizeForTesting to be 
clear.



##########
runners/google-cloud-dataflow-java/worker/src/test/java/org/apache/beam/runners/dataflow/worker/util/BoundedQueueExecutorTest.java:
##########
@@ -693,9 +602,291 @@ public void 
testPollWork_skipsFailedWorkAndCallsOnFailedWorkHandler() throws Exc
     assertNull(testExecutor.pollWork("compA", keyGroup, stealHandle, 
onFailedWorkHandler));
 
     blockerStop.countDown();
+    testExecutor.shutdown();
+    assertEquals(0, testExecutor.elementsOutstanding());
+    assertEquals(0, testExecutor.bytesOutstanding());
+  }
+
+  @Test
+  public void testPollWork_stolenWorkLeavesNoOpStaleShell() throws Exception {

Review Comment:
   done.



##########
runners/google-cloud-dataflow-java/worker/src/main/java/org/apache/beam/runners/dataflow/worker/util/BoundedQueueExecutor.java:
##########
@@ -185,6 +200,18 @@ public boolean executorQueueIsEmpty() {
     return executor.getQueue().isEmpty();

Review Comment:
   Updated to return true only when there are no stolen items in the queue. 
Also renamed it to executorQueueIsEmptyForTesting to prevent accidental prod 
use.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to