Skip to content

Commit f78dbac

Browse files
committed
Merge branch 'main' into 2025/03/25/empty-response-not-xcontent
2 parents 0695e3e + 88996bc commit f78dbac

File tree

46 files changed

+1390
-713
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

46 files changed

+1390
-713
lines changed

docs/changelog/125452.yaml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
pr: 125452
2+
summary: Add GCS telemetry with `ThreadLocal`
3+
area: Snapshot/Restore
4+
type: enhancement
5+
issues: []

libs/entitlement/README.md

Lines changed: 187 additions & 6 deletions
Large diffs are not rendered by default.

modules/repository-gcs/src/internalClusterTest/java/org/elasticsearch/repositories/gcs/GoogleCloudStorageBlobStoreRepositoryTests.java

Lines changed: 23 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@
1717
import com.google.cloud.http.HttpTransportOptions;
1818
import com.google.cloud.storage.StorageOptions;
1919
import com.google.cloud.storage.StorageRetryStrategy;
20-
import com.sun.net.httpserver.Headers;
2120
import com.sun.net.httpserver.HttpExchange;
2221
import com.sun.net.httpserver.HttpHandler;
2322

@@ -57,15 +56,12 @@
5756
import java.util.Collection;
5857
import java.util.Collections;
5958
import java.util.Map;
60-
import java.util.regex.Matcher;
61-
import java.util.regex.Pattern;
6259

6360
import static org.elasticsearch.common.io.Streams.readFully;
6461
import static org.elasticsearch.repositories.blobstore.BlobStoreTestUtil.randomPurpose;
6562
import static org.elasticsearch.repositories.gcs.GoogleCloudStorageClientSettings.CREDENTIALS_FILE_SETTING;
6663
import static org.elasticsearch.repositories.gcs.GoogleCloudStorageClientSettings.ENDPOINT_SETTING;
6764
import static org.elasticsearch.repositories.gcs.GoogleCloudStorageClientSettings.TOKEN_URI_SETTING;
68-
import static org.elasticsearch.repositories.gcs.GoogleCloudStorageOperationsStats.Operation;
6965
import static org.elasticsearch.repositories.gcs.GoogleCloudStorageRepository.BASE_PATH;
7066
import static org.elasticsearch.repositories.gcs.GoogleCloudStorageRepository.BUCKET;
7167
import static org.elasticsearch.repositories.gcs.GoogleCloudStorageRepository.CLIENT_NAME;
@@ -107,7 +103,11 @@ protected Map<String, HttpHandler> createHttpHandlers() {
107103

108104
@Override
109105
protected HttpHandler createErroneousHttpHandler(final HttpHandler delegate) {
110-
return new GoogleErroneousHttpHandler(delegate, randomIntBetween(2, 3));
106+
if (delegate instanceof FakeOAuth2HttpHandler) {
107+
return new GoogleErroneousHttpHandler(delegate, randomIntBetween(2, 3));
108+
} else {
109+
return new GoogleCloudStorageStatsCollectorHttpHandler(new GoogleErroneousHttpHandler(delegate, randomIntBetween(2, 3)));
110+
}
111111
}
112112

113113
@Override
@@ -223,15 +223,20 @@ public void testWriteFileMultipleOfChunkSize() throws IOException {
223223
}
224224
}
225225

226+
@Override
227+
public void testRequestStats() throws Exception {
228+
super.testRequestStats();
229+
}
230+
226231
public static class TestGoogleCloudStoragePlugin extends GoogleCloudStoragePlugin {
227232

228233
public TestGoogleCloudStoragePlugin(Settings settings) {
229234
super(settings);
230235
}
231236

232237
@Override
233-
protected GoogleCloudStorageService createStorageService(Settings settings) {
234-
return new GoogleCloudStorageService(settings) {
238+
protected GoogleCloudStorageService createStorageService(boolean isServerless) {
239+
return new GoogleCloudStorageService() {
235240
@Override
236241
StorageOptions createStorageOptions(
237242
final GoogleCloudStorageClientSettings gcsClientSettings,
@@ -277,7 +282,8 @@ public Map<String, Repository.Factory> getRepositories(
277282
this.storageService,
278283
clusterService,
279284
bigArrays,
280-
recoverySettings
285+
recoverySettings,
286+
new GcsRepositoryStatsCollector()
281287
) {
282288
@Override
283289
protected GoogleCloudStorageBlobStore createBlobStore() {
@@ -288,7 +294,8 @@ protected GoogleCloudStorageBlobStore createBlobStore() {
288294
storageService,
289295
bigArrays,
290296
randomIntBetween(1, 8) * 1024,
291-
BackoffPolicy.noBackoff()
297+
BackoffPolicy.noBackoff(),
298+
this.statsCollector()
292299
) {
293300
@Override
294301
long getLargeBlobThresholdInBytes() {
@@ -356,43 +363,24 @@ protected boolean canFailRequest(final HttpExchange exchange) {
356363
@SuppressForbidden(reason = "this tests uses a HttpServer to emulate an GCS endpoint")
357364
private static class GoogleCloudStorageStatsCollectorHttpHandler extends HttpStatsCollectorHandler {
358365

359-
public static final Pattern contentRangeMatcher = Pattern.compile("bytes \\d+-(\\d+)/(\\d+)");
360-
361366
GoogleCloudStorageStatsCollectorHttpHandler(final HttpHandler delegate) {
362367
super(delegate);
363368
}
364369

365370
@Override
366371
public void maybeTrack(HttpExchange exchange) {
367372
final String request = exchange.getRequestMethod() + " " + exchange.getRequestURI().toString();
368-
final Headers requestHeaders = exchange.getRequestHeaders();
369373
if (Regex.simpleMatch("GET */storage/v1/b/*/o/*", request)) {
370-
trackRequest(Operation.GET_OBJECT.key());
374+
trackRequest(StorageOperation.GET.key());
371375
} else if (Regex.simpleMatch("GET /storage/v1/b/*/o*", request)) {
372-
trackRequest(Operation.LIST_OBJECTS.key());
373-
} else if (Regex.simpleMatch("PUT /upload/storage/v1/b/*uploadType=resumable*", request) && isLastPart(requestHeaders)) {
374-
// Resumable uploads are billed as a single operation, that's the reason we're tracking
375-
// the request only when it's the last part.
376-
// See https://cloud.google.com/storage/docs/resumable-uploads#introduction
377-
trackRequest(Operation.INSERT_OBJECT.key());
376+
trackRequest(StorageOperation.LIST.key());
377+
} else if (Regex.simpleMatch("POST /upload/storage/v1/b/*uploadType=resumable*", request)) {
378+
trackRequest(StorageOperation.INSERT.key());
379+
} else if (Regex.simpleMatch("PUT /upload/storage/v1/b/*uploadType=resumable*", request)) {
380+
trackRequest(StorageOperation.INSERT.key());
378381
} else if (Regex.simpleMatch("POST /upload/storage/v1/b/*uploadType=multipart*", request)) {
379-
trackRequest(Operation.INSERT_OBJECT.key());
382+
trackRequest(StorageOperation.INSERT.key());
380383
}
381384
}
382-
383-
boolean isLastPart(Headers requestHeaders) {
384-
if (requestHeaders.containsKey("Content-range") == false) return false;
385-
386-
// https://cloud.google.com/storage/docs/json_api/v1/parameters#contentrange
387-
final String contentRange = requestHeaders.getFirst("Content-range");
388-
389-
final Matcher matcher = contentRangeMatcher.matcher(contentRange);
390-
391-
if (matcher.matches() == false) return false;
392-
393-
String upperBound = matcher.group(1);
394-
String totalLength = matcher.group(2);
395-
return Integer.parseInt(upperBound) == Integer.parseInt(totalLength) - 1;
396-
}
397385
}
398386
}

0 commit comments

Comments
 (0)