diff --git a/x-pack/plugin/searchable-snapshots/src/main/java/org/elasticsearch/index/store/SearchableSnapshotIndexInput.java b/x-pack/plugin/searchable-snapshots/src/main/java/org/elasticsearch/index/store/SearchableSnapshotIndexInput.java index 8fba34c52385c..d8a540ac497b8 100644 --- a/x-pack/plugin/searchable-snapshots/src/main/java/org/elasticsearch/index/store/SearchableSnapshotIndexInput.java +++ b/x-pack/plugin/searchable-snapshots/src/main/java/org/elasticsearch/index/store/SearchableSnapshotIndexInput.java @@ -7,11 +7,13 @@ import org.apache.lucene.store.BufferedIndexInput; import org.apache.lucene.store.IndexInput; +import org.apache.lucene.util.BytesRef; import org.elasticsearch.common.Nullable; import org.elasticsearch.common.blobstore.BlobContainer; import org.elasticsearch.core.internal.io.IOUtils; import org.elasticsearch.index.snapshots.blobstore.BlobStoreIndexShardSnapshot.FileInfo; +import java.io.ByteArrayInputStream; import java.io.Closeable; import java.io.EOFException; import java.io.IOException; @@ -119,8 +121,7 @@ private void readInternalBytes(final int part, long pos, final byte[] b, int off if (optimizedReadSize < length) { // we did not read everything in an optimized fashion, so read the remainder directly - try (InputStream inputStream - = blobContainer.readBlob(fileInfo.partName(part), pos + optimizedReadSize, length - optimizedReadSize)) { + try (InputStream inputStream = openBlobStream(part, pos + optimizedReadSize, length - optimizedReadSize)) { final int directReadSize = inputStream.read(b, offset + optimizedReadSize, length - optimizedReadSize); assert optimizedReadSize + directReadSize == length : optimizedReadSize + " and " + directReadSize + " vs " + length; position += directReadSize; @@ -192,7 +193,7 @@ private int readFromNewSequentialStream(int part, long pos, byte[] b, int offset // if we open a stream of length streamLength then it will not be completely consumed by this read, so it is worthwhile to open // it and keep it open for future reads - final InputStream inputStream = blobContainer.readBlob(fileInfo.partName(part), pos, streamLength); + final InputStream inputStream = openBlobStream(part, pos, streamLength); streamForSequentialReads = new StreamForSequentialReads(inputStream, part, pos, streamLength); final int read = streamForSequentialReads.read(b, offset, length); @@ -257,6 +258,28 @@ public String toString() { '}'; } + private InputStream openBlobStream(int part, long pos, long length) throws IOException { + final InputStream stream; + if (fileInfo.metadata().hashEqualsContents() == false) { + stream = blobContainer.readBlob(fileInfo.partName(part), pos, length); + } else { + // extract blob content from metadata hash + final BytesRef data = fileInfo.metadata().hash(); + if (part > 0) { + assert fileInfo.numberOfParts() >= part; + for (int i = 0; i < part; i++) { + pos += fileInfo.partBytes(i); + } + } + if ((pos < 0L) || (length < 0L) || (pos + length > data.bytes.length)) { + throw new IllegalArgumentException("Invalid arguments (pos=" + pos + ", length=" + length + + ") for hash content (length=" + data.bytes.length + ')'); + } + stream = new ByteArrayInputStream(data.bytes, Math.toIntExact(pos), Math.toIntExact(length)); + } + return stream; + } + private static class StreamForSequentialReads implements Closeable { private final InputStream inputStream; private final int part; diff --git a/x-pack/plugin/searchable-snapshots/src/main/java/org/elasticsearch/xpack/searchablesnapshots/SearchableSnapshots.java b/x-pack/plugin/searchable-snapshots/src/main/java/org/elasticsearch/xpack/searchablesnapshots/SearchableSnapshots.java index 7374a36fd722d..6ae26d213f28d 100644 --- a/x-pack/plugin/searchable-snapshots/src/main/java/org/elasticsearch/xpack/searchablesnapshots/SearchableSnapshots.java +++ b/x-pack/plugin/searchable-snapshots/src/main/java/org/elasticsearch/xpack/searchablesnapshots/SearchableSnapshots.java @@ -131,7 +131,7 @@ public List getRestHandlers(Settings settings, RestController restC IndexScopedSettings indexScopedSettings, SettingsFilter settingsFilter, IndexNameExpressionResolver indexNameExpressionResolver, Supplier nodesInCluster) { - return List.of(new RestSearchableSnapshotsStatsAction(restController)); + return List.of(new RestSearchableSnapshotsStatsAction()); } } diff --git a/x-pack/plugin/searchable-snapshots/src/main/java/org/elasticsearch/xpack/searchablesnapshots/rest/RestSearchableSnapshotsStatsAction.java b/x-pack/plugin/searchable-snapshots/src/main/java/org/elasticsearch/xpack/searchablesnapshots/rest/RestSearchableSnapshotsStatsAction.java index de2052c32781a..18c865ef23c00 100644 --- a/x-pack/plugin/searchable-snapshots/src/main/java/org/elasticsearch/xpack/searchablesnapshots/rest/RestSearchableSnapshotsStatsAction.java +++ b/x-pack/plugin/searchable-snapshots/src/main/java/org/elasticsearch/xpack/searchablesnapshots/rest/RestSearchableSnapshotsStatsAction.java @@ -8,17 +8,21 @@ import org.elasticsearch.client.node.NodeClient; import org.elasticsearch.common.Strings; import org.elasticsearch.rest.BaseRestHandler; -import org.elasticsearch.rest.RestController; import org.elasticsearch.rest.RestRequest; import org.elasticsearch.rest.action.RestToXContentListener; import org.elasticsearch.xpack.searchablesnapshots.action.SearchableSnapshotsStatsAction; import org.elasticsearch.xpack.searchablesnapshots.action.SearchableSnapshotsStatsRequest; +import java.util.List; + public class RestSearchableSnapshotsStatsAction extends BaseRestHandler { - public RestSearchableSnapshotsStatsAction(final RestController controller) { - controller.registerHandler(RestRequest.Method.GET, "/_searchable_snapshots/stats", this); - controller.registerHandler(RestRequest.Method.GET, "/{index}/_searchable_snapshots/stats", this); + @Override + public List routes() { + return List.of( + new Route(RestRequest.Method.GET, "/_searchable_snapshots/stats"), + new Route(RestRequest.Method.GET, "/{index}/_searchable_snapshots/stats") + ); } @Override