|
17 | 17 | package com.google.cloud.storage; |
18 | 18 |
|
19 | 19 | import static com.google.cloud.storage.ByteSizeConstants._2MiB; |
| 20 | +import static com.google.cloud.storage.TestUtils.apiException; |
20 | 21 | import static com.google.cloud.storage.TestUtils.assertAll; |
21 | 22 | import static com.google.cloud.storage.TestUtils.getChecksummedData; |
22 | 23 | import static com.google.cloud.storage.TestUtils.xxd; |
|
28 | 29 | import com.google.api.core.ApiFutures; |
29 | 30 | import com.google.api.gax.retrying.RetrySettings; |
30 | 31 | import com.google.api.gax.rpc.AbortedException; |
| 32 | +import com.google.api.gax.rpc.ApiException; |
31 | 33 | import com.google.api.gax.rpc.DataLossException; |
32 | 34 | import com.google.api.gax.rpc.OutOfRangeException; |
33 | 35 | import com.google.api.gax.rpc.UnavailableException; |
@@ -1067,6 +1069,74 @@ public void streaming() throws Exception { |
1067 | 1069 | } |
1068 | 1070 | } |
1069 | 1071 |
|
| 1072 | + @Test |
| 1073 | + public void retryableErrorWhileOpeningIsRetried() throws Exception { |
| 1074 | + AtomicInteger reqCounter = new AtomicInteger(0); |
| 1075 | + FakeStorage fake = |
| 1076 | + FakeStorage.of( |
| 1077 | + ImmutableMap.of( |
| 1078 | + REQ_OPEN, |
| 1079 | + respond -> { |
| 1080 | + int i = reqCounter.incrementAndGet(); |
| 1081 | + if (i <= 1) { |
| 1082 | + ApiException apiException = |
| 1083 | + apiException(Code.UNAVAILABLE, String.format("{unavailable %d}", i)); |
| 1084 | + respond.onError(apiException); |
| 1085 | + } else { |
| 1086 | + respond.onNext(RES_OPEN); |
| 1087 | + } |
| 1088 | + })); |
| 1089 | + |
| 1090 | + try (FakeServer fakeServer = FakeServer.of(fake); |
| 1091 | + Storage storage = |
| 1092 | + fakeServer |
| 1093 | + .getGrpcStorageOptions() |
| 1094 | + .toBuilder() |
| 1095 | + .setRetrySettings(RetrySettings.newBuilder().setMaxAttempts(3).build()) |
| 1096 | + .build() |
| 1097 | + .getService()) { |
| 1098 | + |
| 1099 | + BlobId id = BlobId.of("b", "o"); |
| 1100 | + ApiFuture<BlobDescriptor> futureBlobDescriptor = storage.getBlobDescriptor(id); |
| 1101 | + try (BlobDescriptor bd = futureBlobDescriptor.get(20, TimeUnit.SECONDS)) { |
| 1102 | + assertThat(bd).isNotNull(); |
| 1103 | + } |
| 1104 | + } |
| 1105 | + } |
| 1106 | + |
| 1107 | + @Test |
| 1108 | + public void onCompleteWithoutAValue() throws Exception { |
| 1109 | + // I'm not sure if this is something that can actually happen in practice, but is being here |
| 1110 | + // to ensure it's at least accounted for, rather than a null pointer exception or something else |
| 1111 | + // equally cryptic. |
| 1112 | + FakeStorage fake = FakeStorage.of(ImmutableMap.of(REQ_OPEN, StreamObserver::onCompleted)); |
| 1113 | + |
| 1114 | + try (FakeServer fakeServer = FakeServer.of(fake); |
| 1115 | + Storage storage = |
| 1116 | + fakeServer |
| 1117 | + .getGrpcStorageOptions() |
| 1118 | + .toBuilder() |
| 1119 | + .setRetrySettings(RetrySettings.newBuilder().setMaxAttempts(3).build()) |
| 1120 | + .build() |
| 1121 | + .getService()) { |
| 1122 | + |
| 1123 | + BlobId id = BlobId.of("b", "o"); |
| 1124 | + ApiFuture<BlobDescriptor> futureBlobDescriptor = storage.getBlobDescriptor(id); |
| 1125 | + ExecutionException ee = |
| 1126 | + assertThrows( |
| 1127 | + ExecutionException.class, () -> futureBlobDescriptor.get(20, TimeUnit.SECONDS)); |
| 1128 | + assertAll( |
| 1129 | + () -> assertThat(ee).hasCauseThat().isInstanceOf(StorageException.class), |
| 1130 | + () -> |
| 1131 | + assertThat(ee).hasCauseThat().hasCauseThat().isInstanceOf(UnavailableException.class), |
| 1132 | + () -> assertThat(((StorageException) ee.getCause()).getCode()).isEqualTo(0), |
| 1133 | + () -> { |
| 1134 | + String messages = TestUtils.messagesToText(ee.getCause()); |
| 1135 | + assertThat(messages).contains("Unretryable error"); |
| 1136 | + }); |
| 1137 | + } |
| 1138 | + } |
| 1139 | + |
1070 | 1140 | private static void runTestAgainstFakeServer( |
1071 | 1141 | FakeStorage fakeStorage, RangeSpec range, ChecksummedTestContent expected) throws Exception { |
1072 | 1142 |
|
|
0 commit comments