Skip to content

Commit 329ab44

Browse files
committed
Move Clock out of ServiceOptions, use it in RetryHelper
1 parent e213176 commit 329ab44

7 files changed

Lines changed: 82 additions & 68 deletions

File tree

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
/*
2+
* Copyright 2016 Google Inc. All Rights Reserved.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.google.cloud;
18+
19+
import java.io.ObjectStreamException;
20+
import java.io.Serializable;
21+
22+
/**
23+
* A class providing access to the current time in milliseconds. This class is mainly used for
24+
* testing and will be replaced by Java8's {@code java.time.Clock}.
25+
*
26+
* <p>Implementations should implement {@code Serializable} wherever possible and must document
27+
* whether or not they do support serialization.
28+
*/
29+
public abstract class Clock {
30+
31+
private static final Clock DEFAULT_TIME_SOURCE = new DefaultClock();
32+
33+
/**
34+
* Returns current time in milliseconds according to this clock.
35+
*/
36+
public abstract long millis();
37+
38+
/**
39+
* Returns the default clock. Default clock uses {@link System#currentTimeMillis()} to get time
40+
* in milliseconds.
41+
*/
42+
public static Clock defaultClock() {
43+
return DEFAULT_TIME_SOURCE;
44+
}
45+
46+
private static class DefaultClock extends Clock implements Serializable {
47+
48+
private static final long serialVersionUID = -5077300394286703864L;
49+
50+
@Override
51+
public long millis() {
52+
return System.currentTimeMillis();
53+
}
54+
55+
private Object readResolve() throws ObjectStreamException {
56+
return DEFAULT_TIME_SOURCE;
57+
}
58+
}
59+
}

gcloud-java-core/src/main/java/com/google/cloud/RetryHelper.java

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,10 @@
2121
import static java.lang.StrictMath.min;
2222
import static java.lang.StrictMath.pow;
2323
import static java.lang.StrictMath.random;
24-
import static java.util.concurrent.TimeUnit.MILLISECONDS;
2524

2625
import com.google.common.annotations.VisibleForTesting;
2726
import com.google.common.base.MoreObjects;
2827
import com.google.common.base.MoreObjects.ToStringHelper;
29-
import com.google.common.base.Stopwatch;
3028

3129
import java.io.InterruptedIOException;
3230
import java.nio.channels.ClosedByInterruptException;
@@ -45,7 +43,7 @@ public class RetryHelper<V> {
4543

4644
private static final Logger log = Logger.getLogger(RetryHelper.class.getName());
4745

48-
private final Stopwatch stopwatch;
46+
private final Clock clock;
4947
private final Callable<V> callable;
5048
private final RetryParams params;
5149
private final ExceptionHandler exceptionHandler;
@@ -153,10 +151,10 @@ static Context getContext() {
153151

154152
@VisibleForTesting
155153
RetryHelper(Callable<V> callable, RetryParams params, ExceptionHandler exceptionHandler,
156-
Stopwatch stopwatch) {
154+
Clock clock) {
157155
this.callable = checkNotNull(callable);
158156
this.params = checkNotNull(params);
159-
this.stopwatch = checkNotNull(stopwatch);
157+
this.clock = checkNotNull(clock);
160158
this.exceptionHandler = checkNotNull(exceptionHandler);
161159
exceptionHandler.verifyCaller(callable);
162160
}
@@ -165,15 +163,15 @@ static Context getContext() {
165163
public String toString() {
166164
ToStringHelper toStringHelper = MoreObjects.toStringHelper(this);
167165
toStringHelper.add("params", params);
168-
toStringHelper.add("stopwatch", stopwatch);
166+
toStringHelper.add("clock", clock);
169167
toStringHelper.add("attemptNumber", attemptNumber);
170168
toStringHelper.add("callable", callable);
171169
toStringHelper.add("exceptionHandler", exceptionHandler);
172170
return toStringHelper.toString();
173171
}
174172

175173
private V doRetry() throws RetryHelperException {
176-
stopwatch.start();
174+
long start = clock.millis();
177175
while (true) {
178176
attemptNumber++;
179177
Exception exception;
@@ -196,7 +194,7 @@ private V doRetry() throws RetryHelperException {
196194
}
197195
if (attemptNumber >= params.retryMaxAttempts()
198196
|| attemptNumber >= params.retryMinAttempts()
199-
&& stopwatch.elapsed(MILLISECONDS) >= params.totalRetryPeriodMillis()) {
197+
&& clock.millis() - start >= params.totalRetryPeriodMillis()) {
200198
throw new RetriesExhaustedException(this + ": Too many failures, giving up", exception);
201199
}
202200
long sleepDurationMillis = getSleepDuration(params, attemptNumber);
@@ -234,13 +232,13 @@ public static <V> V runWithRetries(Callable<V> callable) throws RetryHelperExcep
234232

235233
public static <V> V runWithRetries(Callable<V> callable, RetryParams params,
236234
ExceptionHandler exceptionHandler) throws RetryHelperException {
237-
return runWithRetries(callable, params, exceptionHandler, Stopwatch.createUnstarted());
235+
return runWithRetries(callable, params, exceptionHandler, Clock.defaultClock());
238236
}
239237

240238
@VisibleForTesting
241239
static <V> V runWithRetries(Callable<V> callable, RetryParams params,
242-
ExceptionHandler exceptionHandler, Stopwatch stopwatch) throws RetryHelperException {
243-
RetryHelper<V> retryHelper = new RetryHelper<>(callable, params, exceptionHandler, stopwatch);
240+
ExceptionHandler exceptionHandler, Clock clock) throws RetryHelperException {
241+
RetryHelper<V> retryHelper = new RetryHelper<>(callable, params, exceptionHandler, clock);
244242
Context previousContext = getContext();
245243
setContext(new Context(retryHelper));
246244
try {

gcloud-java-core/src/main/java/com/google/cloud/ServiceOptions.java

Lines changed: 0 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,6 @@
4343
import java.io.InputStream;
4444
import java.io.InputStreamReader;
4545
import java.io.ObjectInputStream;
46-
import java.io.ObjectStreamException;
4746
import java.io.Serializable;
4847
import java.lang.reflect.Method;
4948
import java.net.HttpURLConnection;
@@ -125,45 +124,6 @@ public HttpTransport create() {
125124
}
126125
}
127126

128-
/**
129-
* A class providing access to the current time in milliseconds. This class is mainly used for
130-
* testing and will be replaced by Java8's {@code java.time.Clock}.
131-
*
132-
* <p>Implementations should implement {@code Serializable} wherever possible and must document
133-
* whether or not they do support serialization.
134-
*/
135-
public abstract static class Clock {
136-
137-
private static final ServiceOptions.Clock DEFAULT_TIME_SOURCE = new DefaultClock();
138-
139-
/**
140-
* Returns current time in milliseconds according to this clock.
141-
*/
142-
public abstract long millis();
143-
144-
/**
145-
* Returns the default clock. Default clock uses {@link System#currentTimeMillis()} to get time
146-
* in milliseconds.
147-
*/
148-
public static ServiceOptions.Clock defaultClock() {
149-
return DEFAULT_TIME_SOURCE;
150-
}
151-
152-
private static class DefaultClock extends ServiceOptions.Clock implements Serializable {
153-
154-
private static final long serialVersionUID = -5077300394286703864L;
155-
156-
@Override
157-
public long millis() {
158-
return System.currentTimeMillis();
159-
}
160-
161-
private Object readResolve() throws ObjectStreamException {
162-
return DEFAULT_TIME_SOURCE;
163-
}
164-
}
165-
}
166-
167127
/**
168128
* Builder for {@code ServiceOptions}.
169129
*

gcloud-java-core/src/test/java/com/google/cloud/RetryHelperTest.java

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,6 @@
2424

2525
import com.google.cloud.RetryHelper.NonRetriableException;
2626
import com.google.cloud.RetryHelper.RetriesExhaustedException;
27-
import com.google.common.base.Stopwatch;
28-
import com.google.common.base.Ticker;
2927

3028
import org.junit.Test;
3129

@@ -157,24 +155,24 @@ public void testTriesNoMoreThanMaxTimes() {
157155
}
158156
}
159157

160-
private static class FakeTicker extends Ticker {
161-
private final AtomicLong nanos = new AtomicLong();
158+
private static class FakeClock extends Clock {
162159

163-
// Advances the ticker value by {@code time} in {@code timeUnit}.
160+
private final AtomicLong millis = new AtomicLong();
161+
162+
// Advances the clock value by {@code time} in {@code timeUnit}.
164163
void advance(long time, TimeUnit timeUnit) {
165-
nanos.addAndGet(timeUnit.toNanos(time));
164+
millis.addAndGet(timeUnit.toMillis(time));
166165
}
167166

168167
@Override
169-
public long read() {
170-
return nanos.get();
168+
public long millis() {
169+
return millis.get();
171170
}
172171
}
173172

174173
@Test
175174
public void testTriesNoMoreLongerThanTotalRetryPeriod() {
176-
final FakeTicker ticker = new FakeTicker();
177-
Stopwatch stopwatch = Stopwatch.createUnstarted(ticker);
175+
final FakeClock fakeClock = new FakeClock();
178176
// The 8th attempt (after min and before max) will trigger a 1 second (virtual) delay exceeding
179177
// total retry period which is set just under 1 second. Test occurs faster than realtime.
180178
RetryParams params = RetryParams.builder().initialRetryDelayMillis(0)
@@ -190,11 +188,11 @@ public void testTriesNoMoreLongerThanTotalRetryPeriod() {
190188
@Override public void run() {
191189
timesCalled.incrementAndGet();
192190
if (timesCalled.get() == sleepOnAttempt) {
193-
ticker.advance(1000, TimeUnit.MILLISECONDS);
191+
fakeClock.advance(1000, TimeUnit.MILLISECONDS);
194192
}
195193
throw new RuntimeException();
196194
}
197-
}), params, handler, stopwatch);
195+
}), params, handler, fakeClock);
198196
fail();
199197
} catch (RetriesExhaustedException expected) {
200198
// verify timesCalled

gcloud-java-core/src/test/java/com/google/cloud/ServiceOptionsTest.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@
2222
import static org.junit.Assert.assertTrue;
2323
import static org.junit.Assert.fail;
2424

25-
import com.google.cloud.ServiceOptions.Clock;
2625
import com.google.cloud.ServiceOptions.DefaultHttpTransportFactory;
2726
import com.google.cloud.ServiceOptions.HttpTransportFactory;
2827
import com.google.cloud.spi.ServiceRpcFactory;

gcloud-java-dns/src/test/java/com/google/cloud/dns/DnsImplTest.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,9 @@
2222
import com.google.api.services.dns.model.Change;
2323
import com.google.api.services.dns.model.ManagedZone;
2424
import com.google.api.services.dns.model.ResourceRecordSet;
25+
import com.google.cloud.Clock;
2526
import com.google.cloud.Page;
2627
import com.google.cloud.RetryParams;
27-
import com.google.cloud.ServiceOptions;
2828
import com.google.cloud.dns.spi.DnsRpc;
2929
import com.google.cloud.dns.spi.DnsRpcFactory;
3030
import com.google.common.collect.ImmutableList;
@@ -100,7 +100,7 @@ public class DnsImplTest {
100100

101101
// Other
102102
private static final Map<DnsRpc.Option, ?> EMPTY_RPC_OPTIONS = ImmutableMap.of();
103-
private static final ServiceOptions.Clock TIME_SOURCE = new ServiceOptions.Clock() {
103+
private static final Clock TIME_SOURCE = new Clock() {
104104
@Override
105105
public long millis() {
106106
return 42000L;

gcloud-java-storage/src/test/java/com/google/cloud/storage/StorageImplTest.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,10 @@
2626

2727
import com.google.api.services.storage.model.StorageObject;
2828
import com.google.cloud.AuthCredentials.ServiceAccountAuthCredentials;
29+
import com.google.cloud.Clock;
2930
import com.google.cloud.Page;
3031
import com.google.cloud.ReadChannel;
3132
import com.google.cloud.RetryParams;
32-
import com.google.cloud.ServiceOptions;
3333
import com.google.cloud.WriteChannel;
3434
import com.google.cloud.storage.Storage.CopyRequest;
3535
import com.google.cloud.storage.spi.StorageRpc;
@@ -225,7 +225,7 @@ public class StorageImplTest {
225225
+ "EkPPhszldvQTY486uPxyD/D7HdfnGW/Nbw5JUhfvecAdudDEhNAQ3PNabyDMI+TpiHy4NTWOrgdcWrzj6VXcdc"
226226
+ "+uuABnPwRCdcyJ1xl2kOrPksRnp1auNGMLOe4IpEBjGY7baX9UG8+A45MbG0aHmkR59Op/aR9XowIDAQAB";
227227

228-
private static final ServiceOptions.Clock TIME_SOURCE = new ServiceOptions.Clock() {
228+
private static final Clock TIME_SOURCE = new Clock() {
229229
@Override
230230
public long millis() {
231231
return 42000L;

0 commit comments

Comments
 (0)