Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
701c613
SAI-5884: hook lucene Unloader into Solr container lifecycle
magibney Oct 10, 2025
5fde980
fix NPE when metrics handler and/or CoreContainer is null
magibney Oct 10, 2025
7e9d140
lucene hash: allow backing val to be GC'd
magibney Oct 14, 2025
02aa56b
lucene hash: safer one-time set of external refqueue handling
magibney Oct 14, 2025
4043715
suppress reference equality warnings for Boolean
magibney Oct 15, 2025
eaa3997
lucene hash: reorder field assignments in Unloader ctor (NPE, etc.)
magibney Oct 15, 2025
4722e1e
lucene hash: Unloader ctor should hold a ref to prevent immediate col…
magibney Oct 15, 2025
b5315f9
unrelated -- make tlog buffer size configurable
magibney Oct 15, 2025
81407f1
lucene hash: also unload PointsReader
magibney Oct 16, 2025
2ba68ed
lucene hash: sysprop-configurable totally disable unloading
magibney Oct 17, 2025
471e0ba
unrelated: prevent NPE in SizeAwareDirectory
magibney Oct 20, 2025
1f35b45
lucene hash: dead ref GC fix
magibney Oct 20, 2025
76d0b25
lucene hash: always track the _raw_ instance
magibney Oct 20, 2025
ddd14f6
avoid UnloadHelper NPE (tests-only?) on null solrMetricsContext
magibney Oct 21, 2025
bc6b8a7
Merge remote-tracking branch 'origin/fs/branch_9_7' into michaelgibne…
magibney Oct 22, 2025
c777531
lucene hash: fix regression where shared objects were tracked
magibney Oct 23, 2025
5027d7f
lucene hash: make lucene.unload.parallelRefQueueCount sysprop-configu…
magibney Oct 24, 2025
05a2746
lucene hash: make lucene.unload.assignRefQueueByThread sysprop-config…
magibney Oct 24, 2025
5c9eee5
lucene hash: place refs in a pre-tracking "holding area"
magibney Oct 28, 2025
f64ac41
suppress ReferenceEquality warning (shifted due to refactoring)
magibney Oct 29, 2025
d8cad5c
lucene hash: Ref should extent PhantomReference, not WeakReference
magibney Oct 29, 2025
db9213f
lucene hash: Only directly track top-level objects
magibney Oct 30, 2025
00bc624
lucene hash: do ref-tracking on an arbitrary resource-associated sent…
magibney Oct 30, 2025
20283a3
lucene hash: simplify
magibney Oct 31, 2025
5bde430
more, and more usable, metrics
magibney Nov 3, 2025
6bd3222
last used before load histogram should display differently
magibney Nov 3, 2025
979452b
allow helper-controlled deferral of unloading
magibney Nov 4, 2025
fcf4789
add p75 to `lastAccessToReloadMillis`
magibney Nov 4, 2025
8937f23
support disabling "adaptive defer"
magibney Nov 4, 2025
ce232aa
measuring time since last access makes no sense on initial load (mess…
magibney Nov 4, 2025
406fa9b
lucene hash: fix typo in sysprop name
magibney Nov 4, 2025
ab94a74
dynamic clusterprops control over unloading ttl
magibney Nov 10, 2025
9daedfb
logs/metrics to identify source of resource reloads
magibney Nov 18, 2025
0acc2d6
lucene hash: support per-format disabling of unloading
magibney Nov 18, 2025
5ddb771
improve reload metrics output
magibney Nov 18, 2025
1df39da
lucene hash: move Unloader invocation to PerFieldPostingsFormat and P…
magibney Nov 19, 2025
15db0f7
temporary commit for evaluting reload paths
magibney Nov 19, 2025
23a4b50
more nuanced disabling of unloading
magibney Nov 21, 2025
de11cd6
configure a static min seg size for unloading
magibney Nov 21, 2025
07431d2
make unloadMinSegSize dynamically configurable
magibney Nov 21, 2025
250026e
Merge remote-tracking branch 'origin/fs/branch_9_7' into michaelgibne…
magibney Dec 4, 2025
fc53df4
add comment about counterintuitive "disable" code
magibney Dec 4, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/solr-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,4 +36,4 @@ jobs:
- name: Initialize gradle settings
run: ./gradlew localSettings
- name: Test Solr
run: ./gradlew test -Ptests.directory=org.apache.solr.storage.TeeDirectory
run: ./gradlew test -Ptests.directory=org.apache.solr.storage.TeeDirectory -Plucene.unload.ttl=1 -Plucene.unload.initial=0
4 changes: 4 additions & 0 deletions gradle/testing/defaults-tests.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@ allprojects {
// completes.
// [propName: 'tests.foo', value: "bar", description: "Sets foo in tests."],
testOptions = [
[propName: 'lucene.unload.ttl', value: '60m', description: "Time since last use at which a resource becomes eligible for unloading"],
[propName: 'lucene.unload.initial', value: '1m', description: "Extra time allotted for first resource use after load/reload"],
[propName: 'lucene.unload.disable', value: 'false', description: "Disables resource unloading"],
[propName: 'lucene.unload.trackReloads', value: 'false', description: "tracks reloads via logs and metrics"],
// asserts, debug output.
[propName: 'tests.verbose', value: false, description: "Enables verbose mode (emits full test outputs immediately)."],
[propName: 'tests.workDir',
Expand Down
2 changes: 1 addition & 1 deletion lucene
Submodule lucene updated 26 files
+2 −2 .github/workflows/gradle-precommit.yml
+4 −0 gradle/testing/defaults-tests.gradle
+12 −3 lucene/core/src/java/org/apache/lucene/codecs/perfield/PerFieldDocValuesFormat.java
+11 −3 lucene/core/src/java/org/apache/lucene/codecs/perfield/PerFieldPostingsFormat.java
+9 −1 lucene/core/src/java/org/apache/lucene/index/SegmentCoreReaders.java
+1,318 −0 lucene/core/src/java/org/apache/lucene/index/Unloader.java
+193 −0 lucene/core/src/java/org/apache/lucene/index/UnloadingDocValuesProducer.java
+270 −0 lucene/core/src/java/org/apache/lucene/index/UnloadingFieldsProducer.java
+153 −0 lucene/core/src/java/org/apache/lucene/index/UnloadingPointsReader.java
+34 −1 lucene/core/src/java/org/apache/lucene/store/ByteBuffersDirectory.java
+35 −1 lucene/core/src/java/org/apache/lucene/store/FSDirectory.java
+1 −0 lucene/core/src/java/org/apache/lucene/store/MappedByteBufferIndexInputProvider.java
+51 −0 lucene/core/src/java/org/apache/lucene/store/UnloaderCoordinationPoint.java
+2 −0 lucene/core/src/test/org/apache/lucene/index/TestAddIndexes.java
+29 −24 lucene/core/src/test/org/apache/lucene/index/TestCodecHoldsOpenFiles.java
+2 −0 lucene/core/src/test/org/apache/lucene/index/TestDirectoryReaderReopen.java
+20 −0 lucene/core/src/test/org/apache/lucene/index/TestForTooMuchCloning.java
+3 −0 lucene/core/src/test/org/apache/lucene/index/TestIndexWriterCommit.java
+4 −2 lucene/core/src/test/org/apache/lucene/index/TestLazyProxSkipping.java
+27 −0 lucene/core/src/test/org/apache/lucene/index/TestMultiDocValues.java
+2 −0 lucene/core/src/test/org/apache/lucene/index/TestMultiLevelSkipList.java
+2 −0 lucene/core/src/test/org/apache/lucene/index/TestPointValues.java
+1 −0 lucene/core/src/test/org/apache/lucene/index/TestReadOnlyIndex.java
+11 −0 lucene/core/src/test/org/apache/lucene/index/TestSizeBoundedForceMerge.java
+3 −0 lucene/core/src/test/org/apache/lucene/index/TestTryDelete.java
+224 −0 lucene/core/src/test/org/apache/lucene/index/TestUnloader.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import java.io.File;
import java.io.IOException;
import java.lang.invoke.MethodHandles;
import java.lang.ref.WeakReference;
import java.nio.file.DirectoryStream;
import java.nio.file.Files;
import java.nio.file.Path;
Expand All @@ -31,10 +32,13 @@
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import org.apache.lucene.index.Unloader;
import org.apache.lucene.store.AlreadyClosedException;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.LockFactory;
import org.apache.lucene.store.UnloaderCoordinationPoint;
import org.apache.lucene.util.IOUtils;
import org.apache.solr.common.SolrException;
import org.apache.solr.common.SolrException.ErrorCode;
Expand Down Expand Up @@ -408,6 +412,7 @@ public final Directory get(String path, DirContext dirContext, String rawLockTyp

if (directory == null) {
directory = create(fullPath, createLockFactory(rawLockType), dirContext);
UnloaderCoordinationPoint.setUnloadHelperSupplier(directory, unloadHelperSupplier);
assert ObjectReleaseTracker.track(directory);
boolean success = false;
try {
Expand All @@ -430,6 +435,19 @@ public final Directory get(String path, DirContext dirContext, String rawLockTyp
}
}

private volatile WeakReference<CoreContainer> cc;
private volatile Supplier<Unloader.UnloadHelper> unloadHelperSupplier;

@Override
public void initCoreContainer(CoreContainer cc) {
super.initCoreContainer(cc);
// no purpose setting up unloading on nodes that don't use it
if (cc == null || cc.nodeRoles.getRoleMode(NodeRoles.Role.DATA).equals(NodeRoles.MODE_OFF)) {
return;
}
this.cc = new WeakReference<>(cc);
}

/*
* (non-Javadoc)
*
Expand All @@ -454,7 +472,23 @@ public void incRef(Directory directory) {
}

@Override
@SuppressWarnings("unchecked")
public void init(NamedList<?> args) {
CoreContainer cc;
String duff = (String) args.get("disableUnloadForField");
if (duff != null && duff.isEmpty()) {
duff = null;
}
String duffF = duff;
int unloadMinSegSize = args.toSolrParams().getInt("unloadMinSegSize", 2_000);
if (this.cc != null && (cc = this.cc.get()) != null) {
unloadHelperSupplier =
(Supplier<Unloader.UnloadHelper>)
cc.getObjectCache()
.computeIfAbsent(
"nodeLevelUnloadMetrics",
(k) -> new UnloadHelper<>(cc, duffF, unloadMinSegSize));
}
maxWriteMBPerSecFlush = (Double) args.get("maxWriteMBPerSecFlush");
maxWriteMBPerSecMerge = (Double) args.get("maxWriteMBPerSecMerge");
maxWriteMBPerSecRead = (Double) args.get("maxWriteMBPerSecRead");
Expand Down
11 changes: 11 additions & 0 deletions solr/core/src/java/org/apache/solr/core/CoreContainer.java
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeoutException;
import java.util.function.Function;
import java.util.function.Supplier;
Expand Down Expand Up @@ -189,6 +191,13 @@ public Executor getCollectorExecutor() {
return collectorExecutor;
}

private final ScheduledExecutorService unloaderExecutor =
Executors.newSingleThreadScheduledExecutor(new SolrNamedThreadFactory("unloaderExecutor"));

public ScheduledExecutorService getUnloaderExecutor() {
return unloaderExecutor;
}

public static class CoreLoadFailure {

public final CoreDescriptor cd;
Expand Down Expand Up @@ -1279,6 +1288,7 @@ public boolean isShutDown() {
/** Close / shut down. Only called by {@link org.apache.solr.servlet.CoreContainerProvider}. */
public void shutdown() {

unloaderExecutor.shutdownNow();
ZkController zkController = getZkController();
if (zkController != null) {
if (distributedCollectionCommandRunner.isPresent()) {
Expand All @@ -1296,6 +1306,7 @@ public void shutdown() {

ExecutorUtil.shutdownAndAwaitTermination(coreContainerAsyncTaskExecutor);
ExecutorUtil.shutdownAndAwaitTermination(collectorExecutor);
ExecutorUtil.awaitTermination(unloaderExecutor);
ExecutorService customThreadPool =
ExecutorUtil.newMDCAwareCachedThreadPool(new SolrNamedThreadFactory("closeThreadPool"));

Expand Down
Loading