Skip to content

Commit a20ff51

Browse files
tlrxDaveCTurner
andauthored
C486.Introduce cache index for searchable snapshots (#60522)
If a searchable snapshot shard fails (e.g. its node leaves the cluster) we want to be able to start it up again on a different node as quickly as possible to avoid unnecessarily blocking or failing searches. It isn't feasible to fully restore such shards in an acceptably short time. In particular we would like to be able to deal with the `can_match` phase of a search ASAP so that we can skip unnecessary waiting on shards that may still be warming up but which are not required for the search. This commit solves this problem by introducing a system index that holds much of the data required to start a shard. Today(*) this means it holds the contents of every file with size <8kB, and the first 4kB of every other file in the shard. This system index acts as a second-level cache, behind the first-level node-local disk cache but in front of the blob store itself. Reading chunks from the index is slower than reading them directly from disk, but faster than reading them from the blob store, and is also replicated and accessible to all nodes in the cluster. (*) the exact heuristics for what we should put into the system index are still under investigation and may change in future. This second-level cache is populated when we attempt to read a chunk which is missing from both levels of cache and must therefore be read from the blob store. We also introduce `SearchableSnapshotsBlobStoreCacheIntegTests` which verify that we do not hit the blob store more than necessary when starting up a shard that we've seen before, whether due to a node restart or because a snapshot was mounted multiple times. Co-authored-by: David Turner <[email protected]>
1 parent b440d59 commit a20ff51

File tree

29 files changed

+1911
-279
lines changed

29 files changed

+1911
-279
lines changed

test/framework/src/main/java/org/elasticsearch/test/rest/ESRestTestCase.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1223,6 +1223,7 @@ protected static boolean isXPackTemplate(String name) {
12231223
case "metrics":
12241224
case "metrics-settings":
12251225
case "metrics-mappings":
1226+
case ".snapshot-blob-cache":
12261227
return true;
12271228
default:
12281229
return false;

x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ClientHelper.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ public static Map<String, String> filterSecurityHeaders(Map<String, String> head
6969
public static final String ASYNC_SEARCH_ORIGIN = "async_search";
7070
public static final String IDP_ORIGIN = "idp";
7171
public static final String STACK_ORIGIN = "stack";
72+
public static final String SEARCHABLE_SNAPSHOTS_ORIGIN = "searchable_snapshots";
7273

7374
private ClientHelper() {}
7475

x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/searchablesnapshots/SearchableSnapshotShardStats.java

Lines changed: 51 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
*/
66
package org.elasticsearch.xpack.core.searchablesnapshots;
77

8+
import org.elasticsearch.Version;
89
import org.elasticsearch.cluster.routing.ShardRouting;
910
import org.elasticsearch.common.io.stream.StreamInput;
1011
import org.elasticsearch.common.io.stream.StreamOutput;
@@ -134,16 +135,20 @@ public static class CacheIndexInputStats implements Writeable, ToXContentObject
134135
private final Counter contiguousReads;
135136
private final Counter nonContiguousReads;
136137
private final Counter cachedBytesRead;
138+
private final Counter indexCacheBytesRead;
137139
private final TimedCounter cachedBytesWritten;
138140
private final TimedCounter directBytesRead;
139141
private final TimedCounter optimizedBytesRead;
142+
private final Counter blobStoreBytesRequested;
143+
private final long currentIndexCacheFills;
140144

141145
public CacheIndexInputStats(String fileName, long fileLength, long openCount, long closeCount,
142146
Counter forwardSmallSeeks, Counter backwardSmallSeeks,
143147
Counter forwardLargeSeeks, Counter backwardLargeSeeks,
144148
Counter contiguousReads, Counter nonContiguousReads,
145-
Counter cachedBytesRead, TimedCounter cachedBytesWritten,
146-
TimedCounter directBytesRead, TimedCounter optimizedBytesRead) {
149+
Counter cachedBytesRead, Counter indexCacheBytesRead,
150+
TimedCounter cachedBytesWritten, TimedCounter directBytesRead, TimedCounter optimizedBytesRead,
151+
Counter blobStoreBytesRequested, long currentIndexCacheFills) {
147152
this.fileName = fileName;
148153
this.fileLength = fileLength;
149154
this.openCount = openCount;
@@ -155,9 +160,12 @@ public CacheIndexInputStats(String fileName, long fileLength, long openCount, lo
155160
this.contiguousReads = contiguousReads;
156161
this.nonContiguousReads = nonContiguousReads;
157162
this.cachedBytesRead = cachedBytesRead;
163+
this.indexCacheBytesRead = indexCacheBytesRead;
158164
this.cachedBytesWritten = cachedBytesWritten;
159165
this.directBytesRead = directBytesRead;
160166
this.optimizedBytesRead = optimizedBytesRead;
167+
this.blobStoreBytesRequested = blobStoreBytesRequested;
168+
this.currentIndexCacheFills = currentIndexCacheFills;
161169
}
162170

163171
CacheIndexInputStats(final StreamInput in) throws IOException {
@@ -172,9 +180,21 @@ public CacheIndexInputStats(String fileName, long fileLength, long openCount, lo
172180
this.contiguousReads = new Counter(in);
173181
this.nonContiguousReads = new Counter(in);
174182
this.cachedBytesRead = new Counter(in);
183+
if (in.getVersion().onOrAfter(Version.V_8_0_0)) {
184+
this.indexCacheBytesRead = new Counter(in);
185+
} else {
186+
this.indexCacheBytesRead = new Counter(0, 0, 0, 0);
187+
}
175188
this.cachedBytesWritten = new TimedCounter(in);
176189
this.directBytesRead = new TimedCounter(in);
177190
this.optimizedBytesRead = new TimedCounter(in);
191+
if (in.getVersion().onOrAfter(Version.V_8_0_0)) {
192+
this.blobStoreBytesRequested = new Counter(in);
193+
this.currentIndexCacheFills = in.readVLong();
194+
} else {
195+
this.blobStoreBytesRequested = new Counter(0, 0, 0, 0);
196+
this.currentIndexCacheFills = 0;
197+
}
178198
}
179199

180200
@Override
@@ -191,9 +211,16 @@ public void writeTo(StreamOutput out) throws IOException {
191211
contiguousReads.writeTo(out);
192212
nonContiguousReads.writeTo(out);
193213
cachedBytesRead.writeTo(out);
214+
if (out.getVersion().onOrAfter(Version.V_8_0_0)) {
215+
indexCacheBytesRead.writeTo(out);
216+
}
194217
cachedBytesWritten.writeTo(out);
195218
directBytesRead.writeTo(out);
196219
optimizedBytesRead.writeTo(out);
220+
if (out.getVersion().onOrAfter(Version.V_8_0_0)) {
221+
blobStoreBytesRequested.writeTo(out);
222+
out.writeVLong(currentIndexCacheFills);
223+
}
197224
}
198225

199226
public String getFileName() {
@@ -240,6 +267,10 @@ public Counter getCachedBytesRead() {
240267
return cachedBytesRead;
241268
}
242269

270+
public Counter getIndexCacheBytesRead() {
271+
return indexCacheBytesRead;
272+
}
273+
243274
public TimedCounter getCachedBytesWritten() {
244275
return cachedBytesWritten;
245276
}
@@ -252,6 +283,14 @@ public TimedCounter getOptimizedBytesRead() {
252283
return optimizedBytesRead;
253284
}
254285

286+
public Counter getBlobStoreBytesRequested() {
287+
return blobStoreBytesRequested;
288+
}
289+
290+
public long getCurrentIndexCacheFills() {
291+
return currentIndexCacheFills;
292+
}
293+
255294
@Override
256295
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
257296
builder.startObject();
@@ -263,6 +302,7 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws
263302
builder.field("contiguous_bytes_read", getContiguousReads());
264303
builder.field("non_contiguous_bytes_read", getNonContiguousReads());
265304
builder.field("cached_bytes_read", getCachedBytesRead());
305+
builder.field("index_cache_bytes_read", getIndexCacheBytesRead());
266306
builder.field("cached_bytes_written", getCachedBytesWritten());
267307
builder.field("direct_bytes_read", getDirectBytesRead());
268308
builder.field("optimized_bytes_read", getOptimizedBytesRead());
@@ -278,6 +318,8 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws
278318
builder.field("large", getBackwardLargeSeeks());
279319
builder.endObject();
280320
}
321+
builder.field("blob_store_bytes_requested", getBlobStoreBytesRequested());
322+
builder.field("current_index_cache_fills", getCurrentIndexCacheFills());
281323
}
282324
return builder.endObject();
283325
}
@@ -302,9 +344,12 @@ public boolean equals(Object other) {
302344
&& Objects.equals(contiguousReads, stats.contiguousReads)
303345
&& Objects.equals(nonContiguousReads, stats.nonContiguousReads)
304346
&& Objects.equals(cachedBytesRead, stats.cachedBytesRead)
347+
&& Objects.equals(indexCacheBytesRead, stats.indexCacheBytesRead)
305348
&& Objects.equals(cachedBytesWritten, stats.cachedBytesWritten)
306349
&& Objects.equals(directBytesRead, stats.directBytesRead)
307-
&& Objects.equals(optimizedBytesRead, stats.optimizedBytesRead);
350+
&& Objects.equals(optimizedBytesRead, stats.optimizedBytesRead)
351+
&& Objects.equals(blobStoreBytesRequested, stats.blobStoreBytesRequested)
352+
&& currentIndexCacheFills == stats.currentIndexCacheFills;
308353
}
309354

310355
@Override
@@ -313,8 +358,9 @@ public int hashCode() {
313358
forwardSmallSeeks, backwardSmallSeeks,
314359
forwardLargeSeeks, backwardLargeSeeks,
315360
contiguousReads, nonContiguousReads,
316-
cachedBytesRead, cachedBytesWritten,
317-
directBytesRead, optimizedBytesRead);
361+
cachedBytesRead, indexCacheBytesRead,
362+
cachedBytesWritten, directBytesRead, optimizedBytesRead,
363+
blobStoreBytesRequested, currentIndexCacheFills);
318364
}
319365
}
320366

x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/searchablesnapshots/SearchableSnapshotsConstants.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,4 +42,6 @@ public static boolean isSearchableSnapshotStore(Settings indexSettings) {
4242

4343
public static final String CACHE_PREWARMING_THREAD_POOL_NAME = "searchable_snapshots_cache_prewarming";
4444
public static final String CACHE_PREWARMING_THREAD_POOL_SETTING = "xpack.searchable_snapshots.cache_prewarming_thread_pool";
45+
46+
public static final String SNAPSHOT_BLOB_CACHE_INDEX = ".snapshot-blob-cache";
4547
}

x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/searchablesnapshots/SearchableSnapshotShardStatsTests.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,9 @@ private CacheIndexInputStats randomCacheIndexInputStats() {
4646
randomCounter(), randomCounter(),
4747
randomCounter(), randomCounter(),
4848
randomCounter(), randomCounter(),
49-
randomCounter(), randomTimedCounter(),
50-
randomTimedCounter(), randomTimedCounter());
49+
randomCounter(), randomCounter(), randomTimedCounter(),
50+
randomTimedCounter(), randomTimedCounter(),
51+
randomCounter(), randomNonNegativeLong());
5152
}
5253

5354
private Counter randomCounter() {

x-pack/plugin/searchable-snapshots/qa/rest/src/test/resources/rest-api-spec/test/stats.yml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,11 @@ teardown:
164164
- gte: { indices.docs.shards.0.0.files.0.cached_bytes_read.min: 0 }
165165
- gte: { indices.docs.shards.0.0.files.0.cached_bytes_read.max: 0 }
166166

167+
- gte: { indices.docs.shards.0.0.files.0.index_cache_bytes_read.count: 0 }
168+
- gte: { indices.docs.shards.0.0.files.0.index_cache_bytes_read.sum: 0 }
169+
- gte: { indices.docs.shards.0.0.files.0.index_cache_bytes_read.min: 0 }
170+
- gte: { indices.docs.shards.0.0.files.0.index_cache_bytes_read.max: 0 }
171+
167172
- gte: { indices.docs.shards.0.0.files.0.cached_bytes_written.count: 0 }
168173
- gte: { indices.docs.shards.0.0.files.0.cached_bytes_written.sum: 0 }
169174
- gte: { indices.docs.shards.0.0.files.0.cached_bytes_written.min: 0 }
@@ -203,6 +208,13 @@ teardown:
203208
- gte: { indices.docs.shards.0.0.files.0.backward_seeks.large.min: 0 }
204209
- gte: { indices.docs.shards.0.0.files.0.backward_seeks.large.max: 0 }
205210

211+
- gte: { indices.docs.shards.0.0.files.0.blob_store_bytes_requested.count: 0 }
212+
- gte: { indices.docs.shards.0.0.files.0.blob_store_bytes_requested.sum: 0 }
213+
- gte: { indices.docs.shards.0.0.files.0.blob_store_bytes_requested.min: 0 }
214+
- gte: { indices.docs.shards.0.0.files.0.blob_store_bytes_requested.max: 0 }
215+
216+
- gte: { indices.docs.shards.0.0.files.0.current_index_cache_fills: 0 }
217+
206218
- do:
207219
searchable_snapshots.stats:
208220
index: "d*"

0 commit comments

Comments
 (0)