scwhittle commented on code in PR #40593:
URL: https://github.com/apache/beam/pull/40593#discussion_r4280641824
##########
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:
maybe this should be
`workQueue.isEmpty() || executorQueueSize() == 0` if the size is changed to
be correct
##########
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:
how about ImmutableList instead of wrapping arraylist?
##########
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:
should we just create these queues in the constructor? simpler hot-path and
not much overhead for 64 empty queues
##########
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:
can we keep track of the stale shell count without removing them so that
this reflects the actual queued work without the stale shells?
I'm worried that this might confuse budgeting.
##########
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 a confusing method name when queue can still contain work after it
returns. Should we be marking work as claimed so it won't be returned after
this?
##########
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:
the test name makes it sound like we want a stale shell, I think we should
combine what this verifies into testPollWork (and ideally make it so the impl
detail of stale shells isn't visible in the queue size)
--
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]