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]
