CassandraVectorStore does not close ExecutorService causing thread leak

Open Beginner friendly
#5,931 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Assessment

Difficulty
2/5
Estimated time
1-3 hours
Newbie friendliness
78/100
Issue type
Bug
Clarity
Clearly specified
Activity status
Quiet
Tech stack
cassandra, java
Domain
databases

Research direction

Start with vector-stores/spring-ai-cassandra-store/src/main/java/org/springframework/ai/vectorstore/cassandra/CassandraVectorStore.java, focusing on executor creation near line 236 and close() near line 510. Reproduce the lifecycle described in the issue, then verify that closing a store releases its ExecutorService while preserving the configured CqlSession behavior.

Written by the indexing model from the issue text.

Description

status: waiting-for-triage

Bug description
The CassandraVectorStore class creates a fixed-size thread pool (ExecutorService) in its constructor but does not shut it down in the close() method. This causes a thread leak where threads accumulate and are never garbage collected, potentially leading to resource exhaustion in long-running applications.

Environment

  • Spring AI version: 2.0.0-SNAPSHOT
  • Java version: 17+
  • Vector store: CassandraVectorStore
  • Cassandra Driver: com.datastax.oss:java-driver-core
  • OS: All platforms

Steps to reproduce

  1. Create a CassandraVectorStore instance with default settings
  2. Use the vector store for operations
  3. Call the close() method
  4. Observe that the ExecutorService thread pool is not shut down
  5. Repeat steps 1-3 multiple times to see thread accumulation

Code example:

// Each iteration creates a new thread pool that is never closed
for (int i = 0; i < 10; i++) {
    CassandraVectorStore store = CassandraVectorStore.builder(embeddingModel)
        .session(cqlSession)
        .keyspace("test_ks")
        .table("test_table")
        .initializeSchema(true)
        .build();
    
    // Perform some operations
    store.add(List.of(new Document("test", "content", Map.of())));
    
    // Close the store - but ExecutorService is NOT closed!
    store.close();
}

// Result: 10 thread pools with unused threads still running

Expected behavior
When close() is called on CassandraVectorStore, all resources should be properly released, including:

  1. The CqlSession (if closeSessionOnClose is configured as true)
  2. The ExecutorService thread pool (currently NOT closed - this is the bug)

The current implementation only closes the CqlSession:

@Override
public void close() throws Exception {
    if (this.closeSessionOnClose) {
        this.session.close();
    }
    // ExecutorService is NOT closed - thread leak!
}

Minimal Complete Reproducible example
Here's a complete test that demonstrates the issue:

import org.springframework.ai.document.Document;
import org.springframework.ai.embedding.EmbeddingModel;
import org.springframework.ai.vectorstore.cassandra.CassandraVectorStore;
import com.datastax.oss.driver.api.core.CqlSession;

import java.lang.management.ManagementFactory;
import java.lang.management.ThreadMXBean;
import java.util.List;
import java.util.Map;

public class CassandraThreadLeakTest {
    
    public static void main(String[] args) {
        ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean();
        
        // Get initial thread count
        long initialThreadCount = threadMXBean.getThreadCount();
        System.out.println("Initial thread count: " + initialThreadCount);
        
        // Create and close multiple CassandraVectorStore instances
        for (int i = 0; i < 5; i++) {
            CqlSession session = createTestSession();
            EmbeddingModel embeddingModel = createTestEmbeddingModel();
            
            CassandraVectorStore store = CassandraVectorStore.builder(embeddingModel)
                .session(session)
                .keyspace("test_keyspace_" + i)
                .table("test_table")
                .initializeSchema(false)
                .fixedThreadPoolExecutorSize(4)
                .build();
            
            store.add(List.of(new Document("doc-" + i, "content", Map.of())));
            store.close();
            session.close();
            
            long currentThreadCount = threadMXBean.getThreadCount();
            System.out.println("After iteration " + (i + 1) + ": " + currentThreadCount + " threads");
        }
        
        long finalThreadCount = threadMXBean.getThreadCount();
        System.out.println("Final thread count: " + finalThreadCount);
        System.out.println("Thread leak: " + (finalThreadCount - initialThreadCount) + " threads");
    }
}

Additional context

  • File location: vector-stores/spring-ai-cassandra-store/src/main/java/org/springframework/ai/vectorstore/cassandra/CassandraVectorStore.java
  • Line number: ~236 (executor creation), ~510 (close method)
  • This affects all users who create/destroy CassandraVectorStore instances dynamically
  • Impact: In multi-tenant scenarios, this can lead to significant thread accumulation

I'm happy to submit a PR with the fix if this issue is accepted.

Dominant language
Java
Stars
9.5k
Forks
2.9k
Avg merge
1d 10h
Merged PRs (30d)
5

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from spring-projects/spring-ai

All issues in spring-projects/spring-ai

Similar issues

More Java issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.