CassandraVectorStore does not close ExecutorService causing thread leak
Nobody has claimed this yet.
Assessment
- Difficulty
- 2/5
- Estimated time
- 1-3 hours
- Newbie friendliness
- 78/100
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
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
- Create a CassandraVectorStore instance with default settings
- Use the vector store for operations
- Call the
close()method - Observe that the ExecutorService thread pool is not shut down
- 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:
- The CqlSession (if
closeSessionOnCloseis configured as true) - 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
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from spring-projects/spring-ai
-
status: waiting-for-triage
Difficulty 2/5 1-3 hours Newbie friendliness 84/100
spring-projects/spring-ai#7022 · 2 comments ·
-
status: waiting-for-triage
Difficulty 2/5 1-3 hours Newbie friendliness 88/100
spring-projects/spring-ai#7000 ·
-
status: waiting-for-triage
Difficulty 1/5 Under an hour Newbie friendliness 90/100
spring-projects/spring-ai#6998 ·
-
[Bug - MCP server] @McpTool error messages are emitted twice when the thrown exception has no cause Openstatus: waiting-for-triage
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
spring-projects/spring-ai#6948 · 1 comment · 1 reaction ·
-
status: waiting-for-triage
Difficulty 1/5 Under an hour Newbie friendliness 90/100
spring-projects/spring-ai#6940 · 1 comment ·
All issues in spring-projects/spring-ai
Similar issues
-
Difficulty 2/5 1-3 hours Newbie friendliness 82/100
infinispan/infinispan#18150 ·
-
Difficulty 2/5 1-3 hours Newbie friendliness 84/100
-
untriaged
Difficulty 2/5 1-3 hours Newbie friendliness 82/100
opensearch-project/k-NN#3597 ·
-
bug
Difficulty 2/5 1-3 hours Newbie friendliness 88/100
-
bug
Difficulty 2/5 1-3 hours Newbie friendliness 82/100