Skip to content
This repository was archived by the owner on Sep 12, 2024. It is now read-only.

Commit 61e3e94

Browse files
authored
Merge pull request #1278 from spotify/dxia/periodically-refresh-master-srvs
client: periodically refresh helios master SRV records
2 parents 2bc4aa7 + ff7904f commit 61e3e94

File tree

6 files changed

+134
-29
lines changed

6 files changed

+134
-29
lines changed

helios-client/src/main/java/com/spotify/helios/client/HeliosClient.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@
6565
import com.google.common.util.concurrent.ThreadFactoryBuilder;
6666
import com.spotify.helios.common.HeliosException;
6767
import com.spotify.helios.common.Json;
68-
import com.spotify.helios.common.Resolver;
68+
import com.spotify.helios.common.PeriodicResolver;
6969
import com.spotify.helios.common.Version;
7070
import com.spotify.helios.common.VersionCompatibility;
7171
import com.spotify.helios.common.descriptors.Deployment;
@@ -102,6 +102,7 @@
102102
import java.util.List;
103103
import java.util.Map;
104104
import java.util.Set;
105+
import java.util.concurrent.Executors;
105106
import java.util.concurrent.ScheduledExecutorService;
106107
import java.util.concurrent.ScheduledThreadPoolExecutor;
107108
import java.util.concurrent.ThreadFactory;
@@ -632,7 +633,8 @@ public Builder setUser(final String user) {
632633
}
633634

634635
public Builder setDomain(final String domain) {
635-
return setEndpointSupplier(Endpoints.of(Resolver.supplier("helios", domain)));
636+
return setEndpointSupplier(Endpoints.of(PeriodicResolver.create("helios", domain,
637+
Executors.newSingleThreadScheduledExecutor())));
636638
}
637639

638640
public Builder setEndpoints(final List<URI> endpoints) {
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
/*-
2+
* -\-\-
3+
* Helios Client
4+
* --
5+
* Copyright (C) 2016 - 2019 Spotify AB
6+
* --
7+
* Licensed under the Apache License, Version 2.0 (the "License");
8+
* you may not use this file except in compliance with the License.
9+
* You may obtain a copy of the License at
10+
*
11+
* http://www.apache.org/licenses/LICENSE-2.0
12+
*
13+
* Unless required by applicable law or agreed to in writing, software
14+
* distributed under the License is distributed on an "AS IS" BASIS,
15+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16+
* See the License for the specific language governing permissions and
17+
* limitations under the License.
18+
* -/-/-
19+
*/
20+
21+
package com.spotify.helios.common;
22+
23+
import com.google.common.annotations.VisibleForTesting;
24+
import com.google.common.base.Supplier;
25+
import java.net.URI;
26+
import java.util.List;
27+
import java.util.concurrent.ScheduledExecutorService;
28+
import java.util.concurrent.TimeUnit;
29+
30+
/**
31+
* A wrapper around {@link Resolver} that periodically refreshes SRV records.
32+
*/
33+
public class PeriodicResolver implements Supplier<List<URI>> {
34+
35+
private List<URI> endpoints;
36+
37+
private PeriodicResolver(final String srvName,
38+
final String domain,
39+
final Resolver resolver,
40+
final ScheduledExecutorService executorService) {
41+
endpoints = resolver.resolve(srvName, domain);
42+
executorService.scheduleWithFixedDelay(() ->
43+
endpoints = resolver.resolve(srvName, domain), 0, 1, TimeUnit.MINUTES);
44+
}
45+
46+
public static PeriodicResolver create(final String srvName,
47+
final String domain,
48+
final ScheduledExecutorService executorService) {
49+
return new PeriodicResolver(srvName, domain, new Resolver(), executorService);
50+
}
51+
52+
@VisibleForTesting
53+
public static PeriodicResolver create(final String srvName,
54+
final String domain,
55+
final Resolver resolver,
56+
final ScheduledExecutorService executorService) {
57+
return new PeriodicResolver(srvName, domain, resolver, executorService);
58+
}
59+
60+
@Override
61+
public List<URI> get() {
62+
return endpoints;
63+
}
64+
}

helios-client/src/main/java/com/spotify/helios/common/Resolver.java

Lines changed: 13 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@
2424
import static java.lang.System.getenv;
2525

2626
import com.google.common.base.Optional;
27-
import com.google.common.base.Supplier;
2827
import com.google.common.collect.ImmutableList;
2928
import com.spotify.dns.DnsSrvResolver;
3029
import com.spotify.dns.DnsSrvResolvers;
@@ -33,7 +32,11 @@
3332
import java.net.URISyntaxException;
3433
import java.util.List;
3534

36-
public abstract class Resolver {
35+
/**
36+
* A utility class that resolves DNS SRV records. Used by {@link PeriodicResolver} to discover
37+
* helios masters.
38+
*/
39+
class Resolver {
3740

3841
private static final String HTTPS_SRV_FORMAT = env("HELIOS_HTTPS_SRV_FORMAT", "_%s._https.%s");
3942
private static final String HTTP_SRV_FORMAT = env("HELIOS_HTTP_SRV_FORMAT", "_%s._http.%s");
@@ -44,23 +47,16 @@ private static String env(final String name, final String defaultValue) {
4447
return Optional.fromNullable(getenv(name)).or(defaultValue);
4548
}
4649

47-
public static Supplier<List<URI>> supplier(final String srvName, final String domain) {
48-
return supplier(srvName, domain, DEFAULT_RESOLVER);
50+
List<URI> resolve(final String srvName, final String domain) {
51+
return resolve(srvName, domain, DEFAULT_RESOLVER);
4952
}
5053

51-
static Supplier<List<URI>> supplier(final String srvName, final String domain,
52-
final DnsSrvResolver resolver) {
53-
return new Supplier<List<URI>>() {
54-
@Override
55-
public List<URI> get() {
56-
// Try to get HTTPS SRV records first and fallback to HTTP
57-
List<URI> uris = resolve(srvName, "https", domain, resolver);
58-
if (uris.isEmpty()) {
59-
uris = resolve(srvName, "http", domain, resolver);
60-
}
61-
return uris;
62-
}
63-
};
54+
List<URI> resolve(final String srvName, final String domain, final DnsSrvResolver resolver) {
55+
List<URI> uris = resolve(srvName, "https", domain, resolver);
56+
if (uris.isEmpty()) {
57+
return resolve(srvName, "http", domain, resolver);
58+
}
59+
return uris;
6460
}
6561

6662
private static List<URI> resolve(final String srvName,
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
/*-
2+
* -\-\-
3+
* Helios Client
4+
* --
5+
* Copyright (C) 2016 - 2019 Spotify AB
6+
* --
7+
* Licensed under the Apache License, Version 2.0 (the "License");
8+
* you may not use this file except in compliance with the License.
9+
* You may obtain a copy of the License at
10+
*
11+
* http://www.apache.org/licenses/LICENSE-2.0
12+
*
13+
* Unless required by applicable law or agreed to in writing, software
14+
* distributed under the License is distributed on an "AS IS" BASIS,
15+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16+
* See the License for the specific language governing permissions and
17+
* limitations under the License.
18+
* -/-/-
19+
*/
20+
21+
package com.spotify.helios.common;
22+
23+
import static org.hamcrest.MatcherAssert.assertThat;
24+
import static org.mockito.Mockito.mock;
25+
import static org.mockito.Mockito.when;
26+
27+
import com.google.common.collect.ImmutableList;
28+
import java.net.URI;
29+
import java.util.concurrent.Executors;
30+
import org.hamcrest.Matchers;
31+
import org.junit.Test;
32+
33+
public class PeriodicResolverTest {
34+
35+
private final Resolver resolver = mock(Resolver.class);
36+
37+
@Test
38+
public void testGet() {
39+
final URI uri = URI.create("https://foo.bar.com");
40+
when(resolver.resolve("foo", "bar")).thenReturn(ImmutableList.of(uri));
41+
final PeriodicResolver sut = PeriodicResolver.create("foo", "bar", resolver,
42+
Executors.newSingleThreadScheduledExecutor());
43+
assertThat(sut.get(), Matchers.contains(uri));
44+
}
45+
}

helios-client/src/test/java/com/spotify/helios/common/ResolverTest.java

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@
2424
import static org.junit.Assert.assertThat;
2525
import static org.mockito.Mockito.when;
2626

27-
import com.google.common.base.Supplier;
2827
import com.google.common.collect.ImmutableList;
2928
import com.spotify.dns.DnsSrvResolver;
3029
import com.spotify.dns.LookupResult;
@@ -44,7 +43,7 @@ public class ResolverTest {
4443
DnsSrvResolver resolver;
4544

4645
@Test
47-
public void testSupplier() throws Exception {
46+
public void testResolve() throws Exception {
4847
final List<LookupResult> lookupResults = ImmutableList.of(
4948
LookupResult.create("master1.example.com", 443, 1, 1, 1),
5049
LookupResult.create("master2.example.com", 443, 1, 1, 1),
@@ -58,15 +57,14 @@ public void testSupplier() throws Exception {
5857

5958
when(resolver.resolve("_helios._https.example.com")).thenReturn(lookupResults);
6059

61-
final Supplier<List<URI>> supplier = Resolver.supplier("helios", "example.com", resolver);
62-
final List<URI> uris = supplier.get();
60+
final List<URI> uris = new Resolver().resolve("helios", "example.com", resolver);
6361

6462
assertThat(uris.size(), equalTo(3));
6563
assertThat(uris, Matchers.containsInAnyOrder(expectedUris));
6664
}
6765

6866
@Test
69-
public void testSupplierWithHttpFallback() throws Exception {
67+
public void testResolveWithHttpFallback() throws Exception {
7068
final List<LookupResult> lookupResults = ImmutableList.of(
7169
LookupResult.create("master1.example.com", 80, 1, 1, 1),
7270
LookupResult.create("master2.example.com", 80, 1, 1, 1),
@@ -79,11 +77,10 @@ public void testSupplierWithHttpFallback() throws Exception {
7977
};
8078

8179
when(resolver.resolve("_helios._https.example.com"))
82-
.thenReturn(Collections.<LookupResult>emptyList());
80+
.thenReturn(Collections.emptyList());
8381
when(resolver.resolve("_helios._http.example.com")).thenReturn(lookupResults);
8482

85-
final Supplier<List<URI>> supplier = Resolver.supplier("helios", "example.com", resolver);
86-
final List<URI> uris = supplier.get();
83+
final List<URI> uris = new Resolver().resolve("helios", "example.com", resolver);
8784

8885
assertThat(uris.size(), equalTo(3));
8986
assertThat(uris, Matchers.containsInAnyOrder(expectedUris));

helios-tools/src/main/java/com/spotify/helios/cli/Target.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,10 @@
2323
import com.google.common.base.Supplier;
2424
import com.google.common.base.Suppliers;
2525
import com.google.common.collect.ImmutableList;
26-
import com.spotify.helios.common.Resolver;
26+
import com.spotify.helios.common.PeriodicResolver;
2727
import java.net.URI;
2828
import java.util.List;
29+
import java.util.concurrent.Executors;
2930

3031
/**
3132
* A target cluster identified by an endpoint string that can be used with a {@link
@@ -64,7 +65,7 @@ public String getDomain() {
6465

6566
@Override
6667
public Supplier<List<URI>> getEndpointSupplier() {
67-
return Resolver.supplier(srv, domain);
68+
return PeriodicResolver.create(srv, domain, Executors.newSingleThreadScheduledExecutor());
6869
}
6970

7071
@Override

0 commit comments

Comments
 (0)