Describe the bug
QdrantEmbeddingStore.addAll() waits for client.upsertAsync(...).get(), and when that call is
interrupted it rethrows the InterruptedException wrapped in a RuntimeException without putting
the thread's interrupt status back. The caller gets the exception, but
Thread.currentThread().isInterrupted() is false.
The same thing happens in removeAll(Collection), removeAll(Filter), search() and
clearStore(). Five call sites, none of them restores the flag.
Other embedding stores in this repo already get it right. MongoDbEmbeddingStore calls
Thread.currentThread().interrupt() before rethrowing, and MilvusV2EmbeddingStore does the same
around its useDatabase call. The same fix has gone in for DockerMcpTransport (#6411),
DefaultMcpClient (#6362) and RetryUtils (#4480), so I think this is a spot that simply got
missed.
Log and Stack trace
Nothing gets logged. The interrupt status is silently cleared.
To Reproduce
Call addAll() against a Qdrant instance, interrupt the calling thread while the upsert is in
flight, then check Thread.currentThread().isInterrupted() in the catch block. It's false.
You don't need a running Qdrant for this either. With a mock client whose future never completes,
the interrupt makes get() fail with InterruptedException, which clears the flag, and nothing
sets it back. A test doing exactly that fails on main today.
Expected behavior
The interrupt status is restored before the RuntimeException is thrown.
Please complete the following information:
- LangChain4j version: 1.21.0-SNAPSHOT (
main)
- LLM(s) used: not applicable
- Java version: 21
- Spring Boot version (if applicable): not applicable
Additional context
All five call sites are in langchain4j-qdrant.
WorkersAiEmbeddingModel in langchain4j-workers-ai has the same pattern in two places, and one
of them swallows the exception completely. Happy to do that one separately if you want it.
I'd like to take this one.
Describe the bug
QdrantEmbeddingStore.addAll()waits forclient.upsertAsync(...).get(), and when that call isinterrupted it rethrows the
InterruptedExceptionwrapped in aRuntimeExceptionwithout puttingthe thread's interrupt status back. The caller gets the exception, but
Thread.currentThread().isInterrupted()isfalse.The same thing happens in
removeAll(Collection),removeAll(Filter),search()andclearStore(). Five call sites, none of them restores the flag.Other embedding stores in this repo already get it right.
MongoDbEmbeddingStorecallsThread.currentThread().interrupt()before rethrowing, andMilvusV2EmbeddingStoredoes the samearound its
useDatabasecall. The same fix has gone in forDockerMcpTransport(#6411),DefaultMcpClient(#6362) andRetryUtils(#4480), so I think this is a spot that simply gotmissed.
Log and Stack trace
Nothing gets logged. The interrupt status is silently cleared.
To Reproduce
Call
addAll()against a Qdrant instance, interrupt the calling thread while the upsert is inflight, then check
Thread.currentThread().isInterrupted()in thecatchblock. It'sfalse.You don't need a running Qdrant for this either. With a mock client whose future never completes,
the interrupt makes
get()fail withInterruptedException, which clears the flag, and nothingsets it back. A test doing exactly that fails on
maintoday.Expected behavior
The interrupt status is restored before the
RuntimeExceptionis thrown.Please complete the following information:
main)Additional context
All five call sites are in
langchain4j-qdrant.WorkersAiEmbeddingModelinlangchain4j-workers-aihas the same pattern in two places, and oneof them swallows the exception completely. Happy to do that one separately if you want it.
I'd like to take this one.