From e214c57d505dae4b91d0bde9883f6392bc99ffbe Mon Sep 17 00:00:00 2001 From: Viliam Durina Date: Mon, 9 Feb 2026 16:00:51 +0100 Subject: [PATCH 1/3] Reuse reader's FieldsInfos If an `IndexWriter` is opened using an `IndexCommit` with an opened reader (through `IndexWriteConfig.setIndexCommit()`), the reader's `SegmentReader`s are reused and no files are re-read, but there are two exceptions: the `.fnm` file (field infos) is re-read in `IndexWriter.getFieldNumberMap()`. This in unnecessary, as their contents are already loaded by the reader, and we can reuse this information. This PR modifies the `getFieldNumberMap()` method to reuse this information. The other exception is the last `segments_N` file and the respective `.si` files which are re-read twice; we don't address this issue here. This change is important to our use case because we're storing the index on a high-latency remote location and have a custom directory implementation that caches the files locally. The cache works in a simple mode: it caches files when they are opened and releases them when the file is closed, so every unnecessary file re-opening is harmful. This is greatly aggravated with compound files, which we always use, as the whole compound data file is reopened and the `cfe` file re-loaded. However, we hope this change is beneficial for Lucene in general, as it avoids duplicate re-reading of information we already have loaded. --- .../org/apache/lucene/index/IndexWriter.java | 27 +++++++++++++------ 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/lucene/core/src/java/org/apache/lucene/index/IndexWriter.java b/lucene/core/src/java/org/apache/lucene/index/IndexWriter.java index bd166b71e473..6ec1fe4bc349 100644 --- a/lucene/core/src/java/org/apache/lucene/index/IndexWriter.java +++ b/lucene/core/src/java/org/apache/lucene/index/IndexWriter.java @@ -1119,7 +1119,7 @@ public IndexWriter(Directory d, IndexWriterConfig conf) throws IOException { // start with previous field numbers, but new FieldInfos // NOTE: this is correct even for an NRT reader because we'll pull FieldInfos even for the // un-committed segments: - globalFieldNumberMap = getFieldNumberMap(); + globalFieldNumberMap = getFieldNumberMap(reader); if (create == false && conf.getParentField() != null && globalFieldNumberMap.getFieldNames().isEmpty() == false @@ -1258,17 +1258,28 @@ static FieldInfos readFieldInfos(SegmentCommitInfo si) throws IOException { } /** - * Loads or returns the already loaded global field number map for this {@link SegmentInfos}. If - * this {@link SegmentInfos} has no global field number map, the returned instance is empty. + * Loads the global field number map for this {@link SegmentInfos}. + * If this {@link SegmentInfos} has no global field number map the returned instance is empty. + * + *

If the a {@code reader} is given, then instead of reading the field info file (.fnm), the + * field infos from its leaves are used. */ - private FieldNumbers getFieldNumberMap() throws IOException { + private FieldNumbers getFieldNumberMap(StandardDirectoryReader reader) throws IOException { final FieldNumbers map = new FieldNumbers(config.getSoftDeletesField(), config.getParentField()); - for (SegmentCommitInfo info : segmentInfos) { - FieldInfos fis = readFieldInfos(info); - for (FieldInfo fi : fis) { - map.addOrGet(fi); + if (reader == null) { + for (SegmentCommitInfo info : segmentInfos) { + FieldInfos fis = readFieldInfos(info); + for (FieldInfo fi : fis) { + map.addOrGet(fi); + } + } + } else { + for (LeafReaderContext leafContext : reader.leaves()) { + for (FieldInfo fi : leafContext.reader().getFieldInfos()) { + map.addOrGet(fi); + } } } return map; From 70a0d41a970429dbb8db913d09ab0067201bb060 Mon Sep 17 00:00:00 2001 From: Viliam Durina Date: Mon, 9 Feb 2026 16:35:26 +0100 Subject: [PATCH 2/3] Tidy --- lucene/core/src/java/org/apache/lucene/index/IndexWriter.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lucene/core/src/java/org/apache/lucene/index/IndexWriter.java b/lucene/core/src/java/org/apache/lucene/index/IndexWriter.java index 6ec1fe4bc349..e4c68e6f3df2 100644 --- a/lucene/core/src/java/org/apache/lucene/index/IndexWriter.java +++ b/lucene/core/src/java/org/apache/lucene/index/IndexWriter.java @@ -1258,8 +1258,8 @@ static FieldInfos readFieldInfos(SegmentCommitInfo si) throws IOException { } /** - * Loads the global field number map for this {@link SegmentInfos}. - * If this {@link SegmentInfos} has no global field number map the returned instance is empty. + * Loads the global field number map for this {@link SegmentInfos}. If this {@link SegmentInfos} + * has no global field number map the returned instance is empty. * *

If the a {@code reader} is given, then instead of reading the field info file (.fnm), the * field infos from its leaves are used. From 3001547c8cda970a8e88928f55fb883494b93ac7 Mon Sep 17 00:00:00 2001 From: Viliam Durina Date: Mon, 9 Feb 2026 16:45:29 +0100 Subject: [PATCH 3/3] Add changelog --- lucene/CHANGES.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lucene/CHANGES.txt b/lucene/CHANGES.txt index adf810552d2f..5b1695a3fa2c 100644 --- a/lucene/CHANGES.txt +++ b/lucene/CHANGES.txt @@ -104,6 +104,8 @@ Optimizations * GITHUB#15085, GITHUB#15092: Hunspell suggestions: Ensure candidate roots are not worse before updating. (Ilia Permiashkin) +* GITHUB#15683: Reuse reader's fieldinfos. (Vliam Durina) + Bug Fixes --------------------- * GITHUB#14049: Randomize KNN codec params in RandomCodec. Fixes scalar quantization div-by-zero