Copilot commented on code in PR #16858:
URL: https://github.com/apache/lucene/pull/16858#discussion_r4434772291


##########
lucene/benchmark/src/java/org/apache/lucene/benchmark/byTask/tasks/JoinSearchTask.java:
##########
@@ -0,0 +1,178 @@
+/*
+ * 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.lucene.benchmark.byTask.tasks;
+
+import static org.apache.lucene.benchmark.byTask.feeds.JoinDocMaker.BRAND;
+import static org.apache.lucene.benchmark.byTask.feeds.JoinDocMaker.BRANDS;
+import static org.apache.lucene.benchmark.byTask.feeds.JoinDocMaker.COLOR;
+import static org.apache.lucene.benchmark.byTask.feeds.JoinDocMaker.COLORS;
+import static org.apache.lucene.benchmark.byTask.feeds.JoinDocMaker.FK;
+import static org.apache.lucene.benchmark.byTask.feeds.JoinDocMaker.FK_NUM;
+import static org.apache.lucene.benchmark.byTask.feeds.JoinDocMaker.ID;
+import static org.apache.lucene.benchmark.byTask.feeds.JoinDocMaker.ID_NUM;
+import static org.apache.lucene.benchmark.byTask.feeds.JoinDocMaker.PRODUCT;
+import static org.apache.lucene.benchmark.byTask.feeds.JoinDocMaker.SIZE;
+import static org.apache.lucene.benchmark.byTask.feeds.JoinDocMaker.SIZES;
+import static org.apache.lucene.benchmark.byTask.feeds.JoinDocMaker.TYPE;
+import static org.apache.lucene.benchmark.byTask.feeds.JoinDocMaker.value;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Random;
+import java.util.TreeSet;
+import org.apache.lucene.benchmark.byTask.PerfRunData;
+import org.apache.lucene.benchmark.byTask.feeds.JoinDocMaker;
+import org.apache.lucene.index.SortedDocValues;
+import org.apache.lucene.index.Term;
+import org.apache.lucene.search.BooleanClause.Occur;
+import org.apache.lucene.search.BooleanQuery;
+import org.apache.lucene.search.IndexSearcher;
+import org.apache.lucene.search.Query;
+import org.apache.lucene.search.SearcherManager;
+import org.apache.lucene.search.TermInSetQuery;
+import org.apache.lucene.search.TermQuery;
+import org.apache.lucene.search.join.BitSetProducer;
+import org.apache.lucene.search.join.JoinUtil;
+import org.apache.lucene.search.join.QueryBitSetProducer;
+import org.apache.lucene.search.join.ScoreMode;
+import org.apache.lucene.search.join.ToParentBlockJoinQuery;
+import org.apache.lucene.util.BytesRef;
+
+/**
+ * Counts products joined from skus, over the indices written by {@link 
JoinDocMaker} (see
+ * conf/join-index.alg). Each query selects skus by a few colors or a few 
sizes, joins them to their
+ * products and filters products by a few brands. Every instance draws the 
same query sequence from
+ * <b>join.seed</b>, so instances with different algorithms are comparable.
+ *
+ * <p>The parameter is the algorithm:
+ *
+ * <ul>
+ *   <li><b>term</b> - skus' {@link SortedDocValues} FK to products' PK terms 
(products, skus)
+ *   <li><b>numeric</b> - skus' {@link 
org.apache.lucene.index.NumericDocValues} FK to products' PK
+ *       points (products, skus)
+ *   <li><b>globalOrd</b> - global ordinals over {@link SortedDocValues} FK 
(mixed)
+ *   <li><b>block</b> - {@link ToParentBlockJoinQuery} (block)
+ * </ul>
+ *
+ * <p>Other config properties: <b>join.dir</b> and <b>join.searchThreads</b>, 
see {@link
+ * JoinIndices}; <b>join.scoreMode</b> - Default: None.
+ */
+public class JoinSearchTask extends PerfTask {
+
+  private static final BitSetProducer PRODUCTS =
+      new QueryBitSetProducer(new TermQuery(new Term(TYPE, PRODUCT)));
+
+  private final Random random;
+  private final ScoreMode scoreMode;
+  private String algorithm;
+  private SearcherManager fromManager;
+  private SearcherManager toManager;
+  private IndexSearcher from;
+  private IndexSearcher to;
+  private long totalHits;
+
+  public JoinSearchTask(PerfRunData runData) {
+    super(runData);
+    random = new Random(runData.getConfig().get("join.seed", 42));
+    scoreMode = ScoreMode.valueOf(runData.getConfig().get("join.scoreMode", 
"None"));
+  }
+
+  @Override
+  public void setup() throws Exception {
+    super.setup();
+    if (algorithm == null) {
+      throw new IllegalStateException(
+          "JoinSearch requires a parameter: term|numeric|globalOrd|block");
+    }
+    if (fromManager == null) {
+      JoinIndices indices = JoinIndices.get(getRunData());
+      String layout = algorithm.equals("globalOrd") ? "mixed" : algorithm;
+      boolean split = algorithm.equals("term") || algorithm.equals("numeric");
+      fromManager = indices.index(split ? "skus" : layout).manager();
+      toManager = indices.index(split ? "products" : layout).manager();
+    }
+    from = fromManager.acquire();
+    to = toManager.acquire();

Review Comment:
   For globalOrd, both managers refer to the mixed index, but separate 
acquire() calls can return different searchers if a concurrent 
JoinUpdate(mixed) refreshes between them. The join is then built against from's 
reader and executed against to's reader, which GlobalOrdinalsQuery rejects with 
IllegalStateException. Pin the same searcher for both sides when the managers 
are identical, retaining a second reader reference to balance the existing two 
releases.



##########
lucene/benchmark/src/java/org/apache/lucene/benchmark/byTask/tasks/JoinSearchTask.java:
##########
@@ -0,0 +1,178 @@
+/*
+ * 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.lucene.benchmark.byTask.tasks;
+
+import static org.apache.lucene.benchmark.byTask.feeds.JoinDocMaker.BRAND;
+import static org.apache.lucene.benchmark.byTask.feeds.JoinDocMaker.BRANDS;
+import static org.apache.lucene.benchmark.byTask.feeds.JoinDocMaker.COLOR;
+import static org.apache.lucene.benchmark.byTask.feeds.JoinDocMaker.COLORS;
+import static org.apache.lucene.benchmark.byTask.feeds.JoinDocMaker.FK;
+import static org.apache.lucene.benchmark.byTask.feeds.JoinDocMaker.FK_NUM;
+import static org.apache.lucene.benchmark.byTask.feeds.JoinDocMaker.ID;
+import static org.apache.lucene.benchmark.byTask.feeds.JoinDocMaker.ID_NUM;
+import static org.apache.lucene.benchmark.byTask.feeds.JoinDocMaker.PRODUCT;
+import static org.apache.lucene.benchmark.byTask.feeds.JoinDocMaker.SIZE;
+import static org.apache.lucene.benchmark.byTask.feeds.JoinDocMaker.SIZES;
+import static org.apache.lucene.benchmark.byTask.feeds.JoinDocMaker.TYPE;
+import static org.apache.lucene.benchmark.byTask.feeds.JoinDocMaker.value;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Random;
+import java.util.TreeSet;
+import org.apache.lucene.benchmark.byTask.PerfRunData;
+import org.apache.lucene.benchmark.byTask.feeds.JoinDocMaker;
+import org.apache.lucene.index.SortedDocValues;
+import org.apache.lucene.index.Term;
+import org.apache.lucene.search.BooleanClause.Occur;
+import org.apache.lucene.search.BooleanQuery;
+import org.apache.lucene.search.IndexSearcher;
+import org.apache.lucene.search.Query;
+import org.apache.lucene.search.SearcherManager;
+import org.apache.lucene.search.TermInSetQuery;
+import org.apache.lucene.search.TermQuery;
+import org.apache.lucene.search.join.BitSetProducer;
+import org.apache.lucene.search.join.JoinUtil;
+import org.apache.lucene.search.join.QueryBitSetProducer;
+import org.apache.lucene.search.join.ScoreMode;
+import org.apache.lucene.search.join.ToParentBlockJoinQuery;
+import org.apache.lucene.util.BytesRef;
+
+/**
+ * Counts products joined from skus, over the indices written by {@link 
JoinDocMaker} (see
+ * conf/join-index.alg). Each query selects skus by a few colors or a few 
sizes, joins them to their
+ * products and filters products by a few brands. Every instance draws the 
same query sequence from
+ * <b>join.seed</b>, so instances with different algorithms are comparable.
+ *
+ * <p>The parameter is the algorithm:
+ *
+ * <ul>
+ *   <li><b>term</b> - skus' {@link SortedDocValues} FK to products' PK terms 
(products, skus)
+ *   <li><b>numeric</b> - skus' {@link 
org.apache.lucene.index.NumericDocValues} FK to products' PK
+ *       points (products, skus)
+ *   <li><b>globalOrd</b> - global ordinals over {@link SortedDocValues} FK 
(mixed)
+ *   <li><b>block</b> - {@link ToParentBlockJoinQuery} (block)
+ * </ul>
+ *
+ * <p>Other config properties: <b>join.dir</b> and <b>join.searchThreads</b>, 
see {@link
+ * JoinIndices}; <b>join.scoreMode</b> - Default: None.
+ */
+public class JoinSearchTask extends PerfTask {
+
+  private static final BitSetProducer PRODUCTS =
+      new QueryBitSetProducer(new TermQuery(new Term(TYPE, PRODUCT)));
+
+  private final Random random;
+  private final ScoreMode scoreMode;
+  private String algorithm;
+  private SearcherManager fromManager;
+  private SearcherManager toManager;
+  private IndexSearcher from;
+  private IndexSearcher to;
+  private long totalHits;
+
+  public JoinSearchTask(PerfRunData runData) {
+    super(runData);
+    random = new Random(runData.getConfig().get("join.seed", 42));
+    scoreMode = ScoreMode.valueOf(runData.getConfig().get("join.scoreMode", 
"None"));
+  }
+
+  @Override
+  public void setup() throws Exception {
+    super.setup();
+    if (algorithm == null) {
+      throw new IllegalStateException(
+          "JoinSearch requires a parameter: term|numeric|globalOrd|block");
+    }
+    if (fromManager == null) {
+      JoinIndices indices = JoinIndices.get(getRunData());
+      String layout = algorithm.equals("globalOrd") ? "mixed" : algorithm;
+      boolean split = algorithm.equals("term") || algorithm.equals("numeric");
+      fromManager = indices.index(split ? "skus" : layout).manager();
+      toManager = indices.index(split ? "products" : layout).manager();
+    }
+    from = fromManager.acquire();
+    to = toManager.acquire();
+  }
+
+  @Override
+  public int doLogic() throws Exception {
+    boolean byColor = random.nextBoolean();
+    Query skus = terms(byColor ? COLOR : SIZE, byColor ? COLORS : SIZES, 2 + 
random.nextInt(3));
+    Query brands = terms(BRAND, BRANDS, 1 + random.nextInt(3));
+    Query join =
+        switch (algorithm) {
+          case "term" -> JoinUtil.createJoinQuery(FK, false, ID, skus, from, 
scoreMode);
+          case "numeric" ->
+              JoinUtil.createJoinQuery(FK_NUM, false, ID_NUM, Long.class, 
skus, from, scoreMode);
+          case "globalOrd" ->
+              JoinUtil.createJoinQuery(
+                  FK,
+                  skus,
+                  new TermQuery(new Term(TYPE, PRODUCT)),
+                  from,
+                  scoreMode,
+                  ((JoinIndices.JoinSearcher) from).ordinalMap());
+          case "block" -> new ToParentBlockJoinQuery(skus, PRODUCTS, 
scoreMode);
+          default -> throw new IllegalArgumentException("Unknown join 
algorithm: " + algorithm);
+        };
+    totalHits +=
+        to.count(
+            new BooleanQuery.Builder().add(join, Occur.MUST).add(brands, 
Occur.FILTER).build());

Review Comment:
   For a non-`None` `join.scoreMode`, `count()` still requests no scores. 
`ToParentBlockJoinQuery.createWeight()` therefore changes the child score mode 
to `None`, whereas the three `JoinUtil` branches have already paid for score 
collection/aggregation when creating the join query. This makes the configured 
score-mode comparison measure different work across algorithms. Either restrict 
this count-only benchmark to `None`, or use a collector that requires and 
consumes scores for the other modes.



##########
lucene/benchmark/src/java/org/apache/lucene/benchmark/byTask/tasks/JoinUpdateTask.java:
##########
@@ -0,0 +1,170 @@
+/*
+ * 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.lucene.benchmark.byTask.tasks;
+
+import static org.apache.lucene.benchmark.byTask.feeds.JoinDocMaker.ID;
+import static org.apache.lucene.benchmark.byTask.feeds.JoinDocMaker.PID;
+import static org.apache.lucene.benchmark.byTask.feeds.JoinDocMaker.productId;
+import static org.apache.lucene.benchmark.byTask.feeds.JoinDocMaker.skuId;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Random;
+import org.apache.lucene.benchmark.byTask.PerfRunData;
+import org.apache.lucene.benchmark.byTask.feeds.JoinDocMaker;
+import org.apache.lucene.benchmark.byTask.feeds.JoinDocMaker.Dataset;
+import org.apache.lucene.index.IndexWriter;
+import org.apache.lucene.index.Term;
+
+/**
+ * Overrides a batch of random existing products and/or skus with new versions 
of their values (see
+ * {@link JoinDocMaker}), then refreshes the {@link JoinIndices} searchers, so 
it measures update
+ * plus NRT reopen. Data size and seed come from the {@link Dataset} recorded 
in the index at
+ * indexing time.
+ *
+ * <p>The parameter is the layout group: <b>split</b> (products and skus 
indices), <b>mixed</b> or
+ * <b>block</b>. The block layout rewrites the whole product block on any 
update, i.e. suffers
+ * update amplification. All groups draw the same update sequence from 
<b>join.seed</b>, so after
+ * the same number of updates all join algorithms agree.
+ *
+ * <p>Other config properties: <b>join.update.side</b> - products | skus | 
both. Default: both;
+ * <b>join.update.batch</b> - updates per task run. Default: 100.
+ */
+public class JoinUpdateTask extends PerfTask {
+
+  /** Update sequence and the latest version of each updated doc, per layout 
group. */
+  private static final class Versions {
+    final Random random;
+    final Map<Long, Long> products = new HashMap<>();
+    final Map<Long, Long> skus = new HashMap<>();
+    long version;
+
+    Versions(long seed) {
+      random = new Random(seed);
+    }
+  }
+
+  private final String side;
+  private final int batch;
+  private String group;
+  private JoinIndices.Index productIndex;
+  private JoinIndices.Index skuIndex;
+  private Versions versions;
+  private Dataset docs;
+
+  public JoinUpdateTask(PerfRunData runData) {
+    super(runData);
+    side = runData.getConfig().get("join.update.side", "both");
+    batch = runData.getConfig().get("join.update.batch", 100);
+    if (!List.of("products", "skus", "both").contains(side)) {
+      throw new IllegalArgumentException(
+          "Unknown join.update.side: " + side + ", expected 
products|skus|both");
+    }
+  }
+
+  @Override
+  public void setup() throws Exception {
+    super.setup();

Review Comment:
   These values are captured while the algorithm is parsed, and the same task 
instances are reused after `NewRound`. Consequently, `join-update-search.alg` 
reports rounds for `products`, `skus`, and `both`, but every round actually 
performs product-only updates. Read and validate the round-dependent 
configuration in `setup()` instead, so the measured updates match the reported 
side.



-- 
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]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to