This is an automated email from the ASF dual-hosted git repository.
epugh pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/solr.git
The following commit(s) were added to refs/heads/main by this push:
new 74b098e9839 Fix HTTP 500 in filestore getMetadata racing with
concurrent delete (#4367)
74b098e9839 is described below
commit 74b098e9839ba8739b5ec402c460092bec4c551b
Author: Eric Pugh <[email protected]>
AuthorDate: Sat May 2 10:18:18 2026 -0400
Fix HTTP 500 in filestore getMetadata racing with concurrent delete (#4367)
---
.../PR#4367-filestore-getmetadata-delete-race.yml | 7 +++++++
.../org/apache/solr/filestore/ClusterFileStore.java | 20 ++++++++++++++++----
.../org/apache/solr/filestore/DistribFileStore.java | 7 +++++++
3 files changed, 30 insertions(+), 4 deletions(-)
diff --git a/changelog/unreleased/PR#4367-filestore-getmetadata-delete-race.yml
b/changelog/unreleased/PR#4367-filestore-getmetadata-delete-race.yml
new file mode 100644
index 00000000000..96be57260aa
--- /dev/null
+++ b/changelog/unreleased/PR#4367-filestore-getmetadata-delete-race.yml
@@ -0,0 +1,7 @@
+# See https://github.com/apache/solr/blob/main/dev-docs/changelog.adoc
+title: Filestore metadata API no longer returns HTTP 500 when a file is
deleted concurrently with a metadata read; the response now matches the
not-found case (null entry).
+type: fixed # added, changed, fixed, deprecated, removed, dependency_update,
security, other
+authors:
+ - name: Eric Pugh
+links:
+ - https://github.com/apache/solr/pull/4367
diff --git a/solr/core/src/java/org/apache/solr/filestore/ClusterFileStore.java
b/solr/core/src/java/org/apache/solr/filestore/ClusterFileStore.java
index c405fcbcfe9..bcd4e562053 100644
--- a/solr/core/src/java/org/apache/solr/filestore/ClusterFileStore.java
+++ b/solr/core/src/java/org/apache/solr/filestore/ClusterFileStore.java
@@ -31,6 +31,7 @@ import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.Objects;
import java.util.stream.Collectors;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.solr.api.JerseyResource;
@@ -228,13 +229,14 @@ public class ClusterFileStore extends JerseyResource
implements ClusterFileStore
String parentPath = path.substring(0, path.lastIndexOf('/'));
List<FileStore.FileDetails> l = fileStore.list(parentPath, s ->
s.equals(fileName));
- dirListingResponse.files =
- Collections.singletonMap(path, l.isEmpty() ? null :
convertToResponse(l.get(0)));
+ FileStoreEntryMetadata entry = l.isEmpty() ? null :
convertToResponse(l.get(0));
+ dirListingResponse.files = Collections.singletonMap(path, entry);
break;
case DIRECTORY:
final var directoryContents =
fileStore.list(path, null).stream()
.map(details -> convertToResponse(details))
+ .filter(Objects::nonNull)
.collect(Collectors.toList());
dirListingResponse.files = Map.of(path, directoryContents);
break;
@@ -254,8 +256,18 @@ public class ClusterFileStore extends JerseyResource
implements ClusterFileStore
return entryMetadata;
}
- entryMetadata.size = details.size();
- entryMetadata.timestamp = details.getTimeStamp();
+ long size = details.size();
+ if (size < 0) {
+ // File was deleted concurrently between listing and reading its
attributes.
+ return null;
+ }
+ final var timestamp = details.getTimeStamp();
+ if (timestamp == null) {
+ // File was deleted concurrently between reading its size and timestamp.
+ return null;
+ }
+ entryMetadata.size = size;
+ entryMetadata.timestamp = timestamp;
if (details.getMetaData() != null) {
details.getMetaData().toMap(entryMetadata.unknownProperties());
}
diff --git a/solr/core/src/java/org/apache/solr/filestore/DistribFileStore.java
b/solr/core/src/java/org/apache/solr/filestore/DistribFileStore.java
index 398075663a4..7fffd9ebf08 100644
--- a/solr/core/src/java/org/apache/solr/filestore/DistribFileStore.java
+++ b/solr/core/src/java/org/apache/solr/filestore/DistribFileStore.java
@@ -30,6 +30,7 @@ import java.nio.ByteBuffer;
import java.nio.channels.SeekableByteChannel;
import java.nio.file.FileSystems;
import java.nio.file.Files;
+import java.nio.file.NoSuchFileException;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import java.util.ArrayList;
@@ -288,6 +289,9 @@ public class DistribFileStore implements FileStore {
public Date getTimeStamp() {
try {
return new Date(Files.getLastModifiedTime(realPath()).toMillis());
+ } catch (NoSuchFileException e) {
+ // File was deleted concurrently between listing and reading its
attributes.
+ return null;
} catch (IOException e) {
throw new SolrException(
SERVER_ERROR, "Failed to retrieve the last modified time for:
" + realPath(), e);
@@ -303,6 +307,9 @@ public class DistribFileStore implements FileStore {
public long size() {
try {
return Files.size(realPath());
+ } catch (NoSuchFileException e) {
+ // File was deleted concurrently between listing and reading its
attributes.
+ return -1;
} catch (IOException e) {
throw new SolrException(
SERVER_ERROR, "Failed to retrieve the file size for: " +
realPath(), e);