diff --git a/java-storage/samples/install-without-bom/pom.xml b/java-storage/samples/install-without-bom/pom.xml index acea9f11b9e5..013d0a397326 100644 --- a/java-storage/samples/install-without-bom/pom.xml +++ b/java-storage/samples/install-without-bom/pom.xml @@ -30,12 +30,12 @@ com.google.cloud google-cloud-storage - 2.64.0 + 2.75.0 com.google.cloud google-cloud-storage-control - 2.64.0 + 2.75.0 @@ -78,7 +78,7 @@ com.google.cloud google-cloud-storage - 2.64.0 + 2.75.0 tests test diff --git a/java-storage/samples/snippets/pom.xml b/java-storage/samples/snippets/pom.xml index b261b5110150..6394382cf2ad 100644 --- a/java-storage/samples/snippets/pom.xml +++ b/java-storage/samples/snippets/pom.xml @@ -31,7 +31,7 @@ com.google.cloud libraries-bom - 26.78.0 + 26.90.0 pom import @@ -99,7 +99,7 @@ com.google.cloud google-cloud-storage - 2.64.0 + 2.75.0 tests test diff --git a/java-storage/samples/snippets/src/main/java/com/example/storage/control/v2/RapidCacheCreate.java b/java-storage/samples/snippets/src/main/java/com/example/storage/control/v2/RapidCacheCreate.java new file mode 100644 index 000000000000..f9d265996a51 --- /dev/null +++ b/java-storage/samples/snippets/src/main/java/com/example/storage/control/v2/RapidCacheCreate.java @@ -0,0 +1,75 @@ +/* + * Copyright 2026 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.example.storage.control.v2; + +// [START storage_control_create_rapid_cache] + +import com.google.api.gax.longrunning.OperationFuture; +import com.google.protobuf.Duration; +import com.google.storage.control.v2.BucketName; +import com.google.storage.control.v2.CreateRapidCacheMetadata; +import com.google.storage.control.v2.CreateRapidCacheRequest; +import com.google.storage.control.v2.RapidCache; +import com.google.storage.control.v2.RapidCacheName; +import com.google.storage.control.v2.StorageControlClient; +import java.io.IOException; +import java.util.concurrent.ExecutionException; + +public final class RapidCacheCreate { + + public static void rapidCacheCreate(String bucketName, String zoneName) + throws InterruptedException, ExecutionException, IOException { + // The ID of your GCS bucket + // String bucketName = "your-unique-bucket-name"; + + // The zone in which to create the Rapid Cache. The zone must be in the same region as the + // bucket. + // String zoneName = "us-central1-a"; + + try (StorageControlClient storageControl = StorageControlClient.create()) { + + // Rapid Cache Ultra requires a regional bucket with hierarchical namespace enabled. + RapidCache rapidCache = + RapidCache.newBuilder() + // The Rapid Cache ID must be the same as the zone. + .setName(RapidCacheName.format("_", bucketName, zoneName)) + .setZone(zoneName) + .setCacheType("rapid-cache-ultra") + // How long a cache entry can live (between 1 hour and 7 days). 86400s = 24 hours. + .setTtl(Duration.newBuilder().setSeconds(86400).build()) + .build(); + + CreateRapidCacheRequest request = + CreateRapidCacheRequest.newBuilder() + // Set project to "_" to signify globally scoped bucket + .setParent(BucketName.format("_", bucketName)) + .setRapidCache(rapidCache) + .build(); + + // Start a long-running operation (LRO). + OperationFuture operation = + storageControl.createRapidCacheAsync(request); + + // Await the LRO's completion. + RapidCache createdRapidCache = operation.get(); + System.out.printf("Created Rapid Cache: %s%n", createdRapidCache.getName()); + System.out.printf("Cache type: %s%n", createdRapidCache.getCacheType()); + System.out.printf("State: %s%n", createdRapidCache.getState()); + } + } +} +// [END storage_control_create_rapid_cache] diff --git a/java-storage/samples/snippets/src/main/java/com/example/storage/control/v2/RapidCacheDisable.java b/java-storage/samples/snippets/src/main/java/com/example/storage/control/v2/RapidCacheDisable.java new file mode 100644 index 000000000000..0442a04249f3 --- /dev/null +++ b/java-storage/samples/snippets/src/main/java/com/example/storage/control/v2/RapidCacheDisable.java @@ -0,0 +1,61 @@ +/* + * Copyright 2026 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.example.storage.control.v2; + +// [START storage_control_disable_rapid_cache] + +import com.google.api.gax.longrunning.OperationFuture; +import com.google.storage.control.v2.DisableRapidCacheMetadata; +import com.google.storage.control.v2.DisableRapidCacheRequest; +import com.google.storage.control.v2.RapidCache; +import com.google.storage.control.v2.RapidCacheName; +import com.google.storage.control.v2.StorageControlClient; +import java.io.IOException; +import java.util.concurrent.ExecutionException; + +public final class RapidCacheDisable { + + public static void rapidCacheDisable(String bucketName, String cacheId) + throws InterruptedException, ExecutionException, IOException { + // The ID of your GCS bucket + // String bucketName = "your-unique-bucket-name"; + + // The ID of the Rapid Cache + // String cacheId = "your-rapid-cache-id"; + + try (StorageControlClient storageControl = StorageControlClient.create()) { + + // Set project to "_" to signify globally scoped bucket + String rapidCacheName = RapidCacheName.format("_", bucketName, cacheId); + DisableRapidCacheRequest request = + DisableRapidCacheRequest.newBuilder().setName(rapidCacheName).build(); + + // Start a long-running operation (LRO). + OperationFuture operation = + storageControl.disableRapidCacheAsync(request); + + // Await the LRO's completion. The cache moves to the "disabled" state within seconds, but + // the operation only completes once the cache has been fully removed, which can take longer + // than the client's default polling timeout. If the wait times out, the cache is already + // disabled; use RapidCacheGet to check its state. + RapidCache disabledRapidCache = operation.get(); + System.out.printf("Disabled Rapid Cache: %s%n", disabledRapidCache.getName()); + System.out.printf("State: %s%n", disabledRapidCache.getState()); + } + } +} +// [END storage_control_disable_rapid_cache] diff --git a/java-storage/samples/snippets/src/main/java/com/example/storage/control/v2/RapidCacheGet.java b/java-storage/samples/snippets/src/main/java/com/example/storage/control/v2/RapidCacheGet.java new file mode 100644 index 000000000000..2e6a57557f25 --- /dev/null +++ b/java-storage/samples/snippets/src/main/java/com/example/storage/control/v2/RapidCacheGet.java @@ -0,0 +1,53 @@ +/* + * Copyright 2026 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.example.storage.control.v2; + +// [START storage_control_get_rapid_cache] + +import com.google.storage.control.v2.GetRapidCacheRequest; +import com.google.storage.control.v2.RapidCache; +import com.google.storage.control.v2.RapidCacheName; +import com.google.storage.control.v2.StorageControlClient; +import java.io.IOException; + +public final class RapidCacheGet { + + public static void rapidCacheGet(String bucketName, String cacheId) throws IOException { + // The ID of your GCS bucket + // String bucketName = "your-unique-bucket-name"; + + // The ID of the Rapid Cache + // String cacheId = "your-rapid-cache-id"; + + try (StorageControlClient storageControl = StorageControlClient.create()) { + + GetRapidCacheRequest request = + GetRapidCacheRequest.newBuilder() + // Set project to "_" to signify globally scoped bucket + .setName(RapidCacheName.format("_", bucketName, cacheId)) + .build(); + + RapidCache rapidCache = storageControl.getRapidCache(request); + + System.out.printf("Got Rapid Cache: %s%n", rapidCache.getName()); + System.out.printf("Zone: %s%n", rapidCache.getZone()); + System.out.printf("Cache type: %s%n", rapidCache.getCacheType()); + System.out.printf("State: %s%n", rapidCache.getState()); + } + } +} +// [END storage_control_get_rapid_cache] diff --git a/java-storage/samples/snippets/src/main/java/com/example/storage/control/v2/RapidCacheList.java b/java-storage/samples/snippets/src/main/java/com/example/storage/control/v2/RapidCacheList.java new file mode 100644 index 000000000000..a55b7a9202ad --- /dev/null +++ b/java-storage/samples/snippets/src/main/java/com/example/storage/control/v2/RapidCacheList.java @@ -0,0 +1,52 @@ +/* + * Copyright 2026 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.example.storage.control.v2; + +// [START storage_control_list_rapid_caches] + +import com.google.storage.control.v2.BucketName; +import com.google.storage.control.v2.ListRapidCachesRequest; +import com.google.storage.control.v2.RapidCache; +import com.google.storage.control.v2.StorageControlClient; +import com.google.storage.control.v2.StorageControlClient.ListRapidCachesPagedResponse; +import java.io.IOException; + +public final class RapidCacheList { + + public static void rapidCacheList(String bucketName) throws IOException { + // The ID of your GCS bucket + // String bucketName = "your-unique-bucket-name"; + + try (StorageControlClient storageControl = StorageControlClient.create()) { + + ListRapidCachesRequest request = + ListRapidCachesRequest.newBuilder() + // Set project to "_" to signify globally scoped bucket + .setParent(BucketName.format("_", bucketName)) + .build(); + + // The paged response handles pagination automatically. + ListRapidCachesPagedResponse response = storageControl.listRapidCaches(request); + for (RapidCache rapidCache : response.iterateAll()) { + System.out.println(rapidCache.getName()); + } + + System.out.printf("Listed Rapid Caches in bucket %s%n", bucketName); + } + } +} +// [END storage_control_list_rapid_caches] diff --git a/java-storage/samples/snippets/src/main/java/com/example/storage/control/v2/RapidCacheUpdate.java b/java-storage/samples/snippets/src/main/java/com/example/storage/control/v2/RapidCacheUpdate.java new file mode 100644 index 000000000000..22af896b191b --- /dev/null +++ b/java-storage/samples/snippets/src/main/java/com/example/storage/control/v2/RapidCacheUpdate.java @@ -0,0 +1,73 @@ +/* + * Copyright 2026 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.example.storage.control.v2; + +// [START storage_control_update_rapid_cache] + +import com.google.api.gax.longrunning.OperationFuture; +import com.google.protobuf.Duration; +import com.google.protobuf.FieldMask; +import com.google.storage.control.v2.RapidCache; +import com.google.storage.control.v2.RapidCacheName; +import com.google.storage.control.v2.StorageControlClient; +import com.google.storage.control.v2.UpdateRapidCacheMetadata; +import com.google.storage.control.v2.UpdateRapidCacheRequest; +import java.io.IOException; +import java.util.concurrent.ExecutionException; + +public final class RapidCacheUpdate { + + public static void rapidCacheUpdate(String bucketName, String cacheId, long ttlSeconds) + throws InterruptedException, ExecutionException, IOException { + // The ID of your GCS bucket + // String bucketName = "your-unique-bucket-name"; + + // The ID of the Rapid Cache + // String cacheId = "your-rapid-cache-id"; + + // The new time to live (TTL) for cache entries, in seconds. The TTL must be between 1 hour and + // 7 days. + // long ttlSeconds = 172800; // 2 days + + try (StorageControlClient storageControl = StorageControlClient.create()) { + + RapidCache pendingUpdate = + RapidCache.newBuilder() + // Set project to "_" to signify globally scoped bucket + .setName(RapidCacheName.format("_", bucketName, cacheId)) + .setTtl(Duration.newBuilder().setSeconds(ttlSeconds).build()) + .build(); + + UpdateRapidCacheRequest request = + UpdateRapidCacheRequest.newBuilder() + .setRapidCache(pendingUpdate) + // Only the fields listed in the update mask are changed. + .setUpdateMask(FieldMask.newBuilder().addPaths("ttl").build()) + .build(); + + // Start a long-running operation (LRO). + OperationFuture operation = + storageControl.updateRapidCacheAsync(request); + + // Await the LRO's completion. + RapidCache updatedRapidCache = operation.get(); + System.out.printf("Updated Rapid Cache: %s%n", updatedRapidCache.getName()); + System.out.printf("TTL: %d seconds%n", updatedRapidCache.getTtl().getSeconds()); + } + } +} +// [END storage_control_update_rapid_cache] diff --git a/java-storage/samples/snippets/src/test/java/com/example/storage/control/v2/ITRapidCacheTest.java b/java-storage/samples/snippets/src/test/java/com/example/storage/control/v2/ITRapidCacheTest.java new file mode 100644 index 000000000000..b6d733f4d34d --- /dev/null +++ b/java-storage/samples/snippets/src/test/java/com/example/storage/control/v2/ITRapidCacheTest.java @@ -0,0 +1,200 @@ +/* + * Copyright 2026 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.example.storage.control.v2; + +import static com.google.common.truth.Truth.assertThat; +import static org.junit.Assume.assumeTrue; + +import com.example.storage.TestBase; +import com.google.cloud.storage.BucketInfo; +import com.google.cloud.storage.BucketInfo.HierarchicalNamespace; +import com.google.cloud.storage.BucketInfo.IamConfiguration; +import com.google.cloud.storage.StorageException; +import com.google.cloud.storage.it.runner.registry.Zone; +import com.google.cloud.storage.testing.RemoteStorageHelper; +import com.google.common.base.MoreObjects; +import com.google.common.base.Strings; +import com.google.storage.control.v2.RapidCacheName; +import com.google.storage.control.v2.StorageControlClient; +import java.util.concurrent.CancellationException; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +public final class ITRapidCacheTest extends TestBase { + + private static final String CACHE_TYPE = "rapid-cache-ultra"; + // Wait for the bucket metadata to propagate before creating the Rapid Cache. + private static final long BUCKET_PROPAGATION_SECONDS = 20; + // The disable operation only completes once the cache has been fully removed, which can take + // more than an hour. The cache moves to the "disabled" state within seconds, so the test bounds + // the wait and then checks the state instead. + private static final long DISABLE_TIMEOUT_MINUTES = 10; + // Bucket deletion is rejected while the Rapid Cache is still being removed. + private static final long BUCKET_DELETE_TIMEOUT_SECONDS = 120; + private static final long BUCKET_DELETE_INTERVAL_SECONDS = 15; + + // The zone in which to create the Rapid Cache. Override with the RAPID_CACHE_ZONE environment + // variable. + private static final Zone ZONE = + Zone.parse( + MoreObjects.firstNonNull( + Strings.emptyToNull(System.getenv("RAPID_CACHE_ZONE")), "us-central1-a")); + + private String bucketName; + // The Rapid Cache ID must be the same as the zone. + private final String cacheId = ZONE.getZone(); + private boolean cacheDisabled; + private int outputOffset; + + @Before + public void setUp() throws InterruptedException { + assumeTrue( + "RapidCache sample tests skipped in CI due to very long operation times.", + Strings.isNullOrEmpty(System.getenv("JOB_TYPE"))); + // Rapid Cache Ultra requires a regional bucket with hierarchical namespace enabled, located in + // the same region as the cache zone. + String name = RemoteStorageHelper.generateBucketName(); + storage.create( + BucketInfo.newBuilder(name) + .setLocation(ZONE.getRegion()) + .setIamConfiguration( + // Hierarchical namespace buckets must use uniform bucket-level access. + IamConfiguration.newBuilder().setIsUniformBucketLevelAccessEnabled(true).build()) + .setHierarchicalNamespace(HierarchicalNamespace.newBuilder().setEnabled(true).build()) + .build()); + bucketName = name; + TimeUnit.SECONDS.sleep(BUCKET_PROPAGATION_SECONDS); + } + + @After + public void tearDown() throws InterruptedException { + if (bucketName == null) { + return; + } + if (!cacheDisabled) { + disableQuietly(); + } + // Best-effort cleanup. A bucket with a Rapid Cache cannot be deleted until the cache has been + // fully removed, which can take a long time after it is disabled. Any leftover bucket is + // removed by the stale bucket cleanup. + long deadline = System.nanoTime() + TimeUnit.SECONDS.toNanos(BUCKET_DELETE_TIMEOUT_SECONDS); + while (true) { + try { + storage.delete(bucketName); + return; + } catch (StorageException e) { + if (e.getCode() != 409 || System.nanoTime() > deadline) { + System.err.printf("Leaving bucket %s for cleanup: %s%n", bucketName, e.getMessage()); + return; + } + TimeUnit.SECONDS.sleep(BUCKET_DELETE_INTERVAL_SECONDS); + } + } + } + + @Test + public void createGetListUpdateDisable() throws Exception { + String cacheName = RapidCacheName.format("_", bucketName, cacheId); + + // Test create Rapid Cache + RapidCacheCreate.rapidCacheCreate(bucketName, ZONE.getZone()); + String out = newOutput(); + assertThat(out).contains("Created Rapid Cache: " + cacheName); + assertThat(out).contains("Cache type: " + CACHE_TYPE); + + // Test get Rapid Cache + RapidCacheGet.rapidCacheGet(bucketName, cacheId); + out = newOutput(); + assertThat(out).contains("Got Rapid Cache: " + cacheName); + assertThat(out).contains("Zone: " + ZONE.getZone()); + assertThat(out).contains("Cache type: " + CACHE_TYPE); + assertThat(out).contains("State: running"); + + // Test list Rapid Caches + RapidCacheList.rapidCacheList(bucketName); + assertThat(newOutput()).contains(cacheName); + + // Test update Rapid Cache + RapidCacheUpdate.rapidCacheUpdate(bucketName, cacheId, 172800); + out = newOutput(); + assertThat(out).contains("Updated Rapid Cache: " + cacheName); + assertThat(out).contains("TTL: 172800 seconds"); + + // Test disable Rapid Cache. The sample waits for the operation to complete, so run it with a + // bounded wait. + ExecutorService executor = Executors.newSingleThreadExecutor(); + try { + Future disable = + executor.submit( + () -> { + RapidCacheDisable.rapidCacheDisable(bucketName, cacheId); + return null; + }); + try { + disable.get(DISABLE_TIMEOUT_MINUTES, TimeUnit.MINUTES); + cacheDisabled = true; + assertThat(newOutput()).contains("Disabled Rapid Cache: " + cacheName); + } catch (TimeoutException | ExecutionException e) { + // A TimeoutException means our wait expired. A CancellationException means the client + // stopped polling the operation (the default LRO polling timeout is 5 minutes). In both + // cases the operation is still running on the server, and the cache must already be in + // the "disabled" state. Any other error fails the test. + if (e instanceof ExecutionException && !(e.getCause() instanceof CancellationException)) { + throw e; + } + // Cancelling the local wait does not cancel the operation on the server. + disable.cancel(true); + cacheDisabled = true; + newOutput(); + RapidCacheGet.rapidCacheGet(bucketName, cacheId); + assertThat(newOutput()).contains("State: disabled"); + } + } finally { + executor.shutdownNow(); + } + } + + /** + * Used in teardown: starts disabling the Rapid Cache without waiting for the operation, ignoring + * errors (for example, if the cache was never created). + */ + private void disableQuietly() { + try (StorageControlClient storageControl = StorageControlClient.create()) { + storageControl + .disableRapidCacheAsync(RapidCacheName.format("_", bucketName, cacheId)) + .getInitialFuture() + .get(1, TimeUnit.MINUTES); + } catch (Exception e) { + System.err.printf("Failed to disable Rapid Cache in bucket %s: %s%n", bucketName, e); + } + } + + /** Returns the output captured since the previous call. */ + private String newOutput() { + String all = stdOut.getCapturedOutputAsUtf8String(); + String out = all.substring(outputOffset); + outputOffset = all.length(); + return out; + } +}