Skip to content

Fix for GPU accelerated vector indexing silently falling back to using CPU instead#4328

Open
rahulgoswami wants to merge 3 commits intoapache:mainfrom
Commvault:fix-GPU-vector-indexing
Open

Fix for GPU accelerated vector indexing silently falling back to using CPU instead#4328
rahulgoswami wants to merge 3 commits intoapache:mainfrom
Commvault:fix-GPU-vector-indexing

Conversation

@rahulgoswami
Copy link
Copy Markdown
Member

@rahulgoswami rahulgoswami commented Apr 23, 2026

https://issues.apache.org/jira/browse/SOLR-18210

Description

GPU accelerated vector indexing uses the Lucene99AcceleratedHNSWVectorsFormat coming from cuvs-lucene library. It currently falls back to using Lucene99HnswVectorsWriter (which uses the CPU instead) due to a library loading issue.

Solution

Root Cause: Initialization Race Between Two Independent Code Paths

There are two independent code paths that access com.nvidia.cuvs.spi.CuVSServiceProvider$Holder.INSTANCE, but only one of them loads the required libcudart.so native library first. The wrong one wins the race.

Path A : GpuMetricsService (runs FIRST, does NOT load libcudart)

org.apache.solr.cuvs.GpuMetricsService is started on a ScheduledExecutorService during CoreContainer initialization. Its updateGpuMetrics() method directly
calls:

GpuMetricsService.updateGpuMetrics() // scheduled executor thread
→ CuVSProvider.provider() // cuvs-java: CuVSProvider.java:159
→ CuVSServiceProvider$Holder.INSTANCE // triggers $Holder.

This triggers $Holder class init → loadProvider() → builtinProvider(), → eventually lands in "throws UnsatisfiedLinkError: unresolved symbol: cudaMemcpyAsync" , causing $Holder init to fail.

Once the class initializer fails, all future access throws NoClassDefFoundError.

Path B : Utils.cuVSResourcesOrNull() (runs SECOND, DOES load libcudart)

Lucene99AcceleratedHNSWVectorsFormat class init calls Utils.cuVSResourcesOrNull() (class com.nvidia.cuvs.lucene.Utils):

static CuVSResources cuVSResourcesOrNull() {
    try {
        System.loadLibrary("cudart");            // loads libcudart.so into this classloader
    } catch (UnsatisfiedLinkError e) {
        log.warning("Could not load CUDA runtime library: " + e.getMessage());
    }
    return CuVSResources.create();               // would trigger $Holder.<clinit> successfully
}

This method correctly calls System.loadLibrary("cudart") before touching $Holder. If it ran first, cudaMemcpyAsync would be resolvable via SymbolLookup.loaderLookup() and GPU init would succeed. But by the
time this path runs, $Holder is already poisoned by Path A.

This causes Lucene99AcceleratedHNSWVectorsFormat.supported() to return false, causing a silent fallback to Lucene99HnswVectorsWriter, whereby the indexing succeeds successfully with a log warning "GPU based indexing not supported, falling back to using the Lucene99HnswVectorsWriter"

Fix :
Load the cuda runtime library when GpuMetricsService initializes

Tests

Built the solr-cuvs.jar locally and placed it in WEB-INF/lib of the solr web app. Then ran vector indexing on an L40S GPU machine with the configuration mentioned in the document https://solr.apache.org/guide/solr/latest/query-guide/dense-vector-search.html#gpu-acceleration.
The log then prints "cuVS is supported so using the Lucene99AcceleratedHNSWVectorsWriter" coming from cuvs-lucene's Lucene99AcceleratedHNSWVectorsFormat

@rahulgoswami rahulgoswami changed the title Fix for GPU vector indexing silently falling back to using CPU instead Fix for GPU accelerated vector indexing silently falling back to using CPU instead Apr 23, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant