aboutsummaryrefslogtreecommitdiffstats
path: root/configserver
diff options
context:
space:
mode:
authorMartin Polden <mpolden@mpolden.no>2019-06-11 09:19:07 +0200
committerGitHub <noreply@github.com>2019-06-11 09:19:07 +0200
commited998d7574eac2dba223012ca1340d302581778e (patch)
treefe79ac59bdc691055bd0567a4827231eefb85952 /configserver
parentbb0ff41764364aa9cb4100e6443ea2d71b73e133 (diff)
parenta312c7d6260773e375c062e102458239b34ad126 (diff)
Merge pull request #9695 from vespa-engine/ogronnesby/create-new-rotations-cache
Create new rotations cache that keeps info on endpoints and clusters
Diffstat (limited to 'configserver')
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/tenant/ContainerEndpoint.java54
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/tenant/ContainerEndpointSerializer.java76
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/tenant/ContainerEndpointsCache.java59
-rw-r--r--configserver/src/test/java/com/yahoo/vespa/config/server/tenant/ContainerEndpointSerializerTest.java45
-rw-r--r--configserver/src/test/java/com/yahoo/vespa/config/server/tenant/ContainerEndpointsCacheTest.java36
5 files changed, 270 insertions, 0 deletions
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/tenant/ContainerEndpoint.java b/configserver/src/main/java/com/yahoo/vespa/config/server/tenant/ContainerEndpoint.java
new file mode 100644
index 00000000000..fb1035b5a03
--- /dev/null
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/tenant/ContainerEndpoint.java
@@ -0,0 +1,54 @@
+// Copyright 2019 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.config.server.tenant;
+
+import com.yahoo.vespa.applicationmodel.ClusterId;
+
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * ContainerEndpoint tracks the service names that a Container Cluster should be
+ * known as. This is used during request routing both for regular requests and
+ * for health checks in traffic distribution.
+ *
+ * @author ogronnesby
+ */
+public class ContainerEndpoint {
+ private final ClusterId clusterId;
+ private final List<String> names;
+
+ ContainerEndpoint(ClusterId clusterId, List<String> names) {
+ this.clusterId = Objects.requireNonNull(clusterId);
+ this.names = List.copyOf(Objects.requireNonNull(names));
+ }
+
+ public ClusterId clusterId() {
+ return clusterId;
+ }
+
+ public List<String> names() {
+ return names;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ ContainerEndpoint that = (ContainerEndpoint) o;
+ return Objects.equals(clusterId, that.clusterId) &&
+ Objects.equals(names, that.names);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(clusterId, names);
+ }
+
+ @Override
+ public String toString() {
+ return "ContainerEndpoint{" +
+ "clusterId=" + clusterId +
+ ", names=" + names +
+ '}';
+ }
+}
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/tenant/ContainerEndpointSerializer.java b/configserver/src/main/java/com/yahoo/vespa/config/server/tenant/ContainerEndpointSerializer.java
new file mode 100644
index 00000000000..83d65f5b38b
--- /dev/null
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/tenant/ContainerEndpointSerializer.java
@@ -0,0 +1,76 @@
+// Copyright 2019 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.config.server.tenant;
+
+import com.yahoo.slime.ArrayTraverser;
+import com.yahoo.slime.Cursor;
+import com.yahoo.slime.Inspector;
+import com.yahoo.slime.Slime;
+import com.yahoo.vespa.applicationmodel.ClusterId;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Contains all methods for de-/serializing ContainerEndpoints to/from JSON.
+ * Also supports de-/serializing lists of these values.
+ *
+ * @author ogronnesby
+ */
+public class ContainerEndpointSerializer {
+ private static final String clusterIdField = "clusterId";
+ private static final String namesField = "names";
+
+ public static ContainerEndpoint endpointFromSlime(Inspector inspector) {
+ final var clusterId = inspector.field(clusterIdField).asString();
+ final var namesInspector = inspector.field(namesField);
+
+ if (clusterId.isEmpty()) {
+ throw new IllegalStateException("'clusterId' missing on serialized ContainerEndpoint");
+ }
+
+ if (! namesInspector.valid()) {
+ throw new IllegalStateException("'names' missing on serialized ContainerEndpoint");
+ }
+
+ final var names = new ArrayList<String>();
+
+ namesInspector.traverse((ArrayTraverser) (idx, nameInspector) -> {
+ final var containerName = nameInspector.asString();
+ names.add(containerName);
+ });
+
+ return new ContainerEndpoint(new ClusterId(clusterId), names);
+ }
+
+ public static List<ContainerEndpoint> endpointListFromSlime(Slime slime) {
+ final var inspector = slime.get();
+ final var endpoints = new ArrayList<ContainerEndpoint>();
+
+ inspector.traverse((ArrayTraverser) (idx, endpointInspector) -> {
+ final var containerEndpoint = endpointFromSlime(endpointInspector);
+ endpoints.add(containerEndpoint);
+ });
+
+ return endpoints;
+ }
+
+
+ public static void endpointToSlime(Cursor cursor, ContainerEndpoint endpoint) {
+ cursor.setString(clusterIdField, endpoint.clusterId().toString());
+
+ final var namesInspector = cursor.setArray(namesField);
+ endpoint.names().forEach(namesInspector::addString);
+ }
+
+ public static Slime endpointListToSlime(List<ContainerEndpoint> endpoints) {
+ final var slime = new Slime();
+ final var cursor = slime.setArray();
+
+ endpoints.forEach(endpoint -> {
+ final var endpointCursor = cursor.addObject();
+ endpointToSlime(endpointCursor, endpoint);
+ });
+
+ return slime;
+ }
+}
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/tenant/ContainerEndpointsCache.java b/configserver/src/main/java/com/yahoo/vespa/config/server/tenant/ContainerEndpointsCache.java
new file mode 100644
index 00000000000..06f93f2006f
--- /dev/null
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/tenant/ContainerEndpointsCache.java
@@ -0,0 +1,59 @@
+// Copyright 2019 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.config.server.tenant;
+
+import com.yahoo.config.provision.ApplicationId;
+import com.yahoo.path.Path;
+import com.yahoo.vespa.config.SlimeUtils;
+import com.yahoo.vespa.curator.Curator;
+
+import java.io.IOException;
+import java.io.UncheckedIOException;
+import java.util.List;
+
+
+/**
+ * Persists assignment of rotations to an application to ZooKeeper.
+ * The entries are {@link ContainerEndpoint} instances, which keep track of the container
+ * cluster that is the target, the endpoint name, and the rotation used to
+ * give availability to that cluster.
+ *
+ * This is v2 of that storage in a new directory. Previously we only stored
+ * the name of the rotation, since all the other information could be
+ * calculated runtime.
+ *
+ * @author ogronnesby
+ */
+public class ContainerEndpointsCache {
+ private final Path cachePath;
+ private final Curator curator;
+
+ ContainerEndpointsCache(Path tenantPath, Curator curator) {
+ this.cachePath = tenantPath.append("containerEndpointsCache/");
+ this.curator = curator;
+ }
+
+ public List<ContainerEndpoint> read(ApplicationId applicationId) {
+ final var optionalData = curator.getData(applicationPath(applicationId));
+ return optionalData
+ .map(SlimeUtils::jsonToSlime)
+ .map(ContainerEndpointSerializer::endpointListFromSlime)
+ .orElse(List.of());
+ }
+
+ public void write(ApplicationId applicationId, List<ContainerEndpoint> endpoints) {
+ if (endpoints.isEmpty()) return;
+
+ final var slime = ContainerEndpointSerializer.endpointListToSlime(endpoints);
+
+ try {
+ final var bytes = SlimeUtils.toJsonBytes(slime);
+ curator.set(applicationPath(applicationId), bytes);
+ } catch (IOException e) {
+ throw new UncheckedIOException("Error writing endpoints of: " + applicationId, e);
+ }
+ }
+
+ private Path applicationPath(ApplicationId applicationId) {
+ return cachePath.append(applicationId.serializedForm());
+ }
+}
diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/tenant/ContainerEndpointSerializerTest.java b/configserver/src/test/java/com/yahoo/vespa/config/server/tenant/ContainerEndpointSerializerTest.java
new file mode 100644
index 00000000000..b4d52e6d37c
--- /dev/null
+++ b/configserver/src/test/java/com/yahoo/vespa/config/server/tenant/ContainerEndpointSerializerTest.java
@@ -0,0 +1,45 @@
+package com.yahoo.vespa.config.server.tenant;
+
+import com.yahoo.slime.Slime;
+import com.yahoo.vespa.applicationmodel.ClusterId;
+import org.junit.Test;
+
+import java.util.List;
+
+import static org.junit.Assert.assertEquals;
+
+public class ContainerEndpointSerializerTest {
+ @Test
+ public void readSingleEndpoint() {
+ final var slime = new Slime();
+ final var entry = slime.setObject();
+
+ entry.setString("clusterId", "foobar");
+ final var entryNames = entry.setArray("names");
+ entryNames.addString("a");
+ entryNames.addString("b");
+
+ final var endpoint = ContainerEndpointSerializer.endpointFromSlime(slime.get());
+ assertEquals("foobar", endpoint.clusterId().toString());
+ assertEquals(List.of("a", "b"), endpoint.names());
+ }
+
+ @Test
+ public void writeReadSingleEndpoint() {
+ final var endpoint = new ContainerEndpoint(new ClusterId("foo"), List.of("a", "b"));
+ final var serialized = new Slime();
+ ContainerEndpointSerializer.endpointToSlime(serialized.setObject(), endpoint);
+ final var deserialized = ContainerEndpointSerializer.endpointFromSlime(serialized.get());
+
+ assertEquals(endpoint, deserialized);
+ }
+
+ @Test
+ public void writeReadEndpoints() {
+ final var endpoints = List.of(new ContainerEndpoint(new ClusterId("foo"), List.of("a", "b")));
+ final var serialized = ContainerEndpointSerializer.endpointListToSlime(endpoints);
+ final var deserialized = ContainerEndpointSerializer.endpointListFromSlime(serialized);
+
+ assertEquals(endpoints, deserialized);
+ }
+}
diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/tenant/ContainerEndpointsCacheTest.java b/configserver/src/test/java/com/yahoo/vespa/config/server/tenant/ContainerEndpointsCacheTest.java
new file mode 100644
index 00000000000..3598b6e63c3
--- /dev/null
+++ b/configserver/src/test/java/com/yahoo/vespa/config/server/tenant/ContainerEndpointsCacheTest.java
@@ -0,0 +1,36 @@
+// Copyright 2019 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.config.server.tenant;
+
+import com.yahoo.config.provision.ApplicationId;
+import com.yahoo.path.Path;
+import com.yahoo.vespa.applicationmodel.ClusterId;
+import com.yahoo.vespa.curator.mock.MockCurator;
+import org.junit.Test;
+
+import java.util.List;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+public class ContainerEndpointsCacheTest {
+ @Test
+ public void readWriteFromCache() {
+ final var cache = new ContainerEndpointsCache(Path.createRoot(), new MockCurator());
+ final var endpoints = List.of(
+ new ContainerEndpoint(new ClusterId("the-cluster-1"), List.of("a", "b", "c"))
+ );
+
+ cache.write(ApplicationId.defaultId(), endpoints);
+
+ final var deserialized = cache.read(ApplicationId.defaultId());
+
+ assertEquals(endpoints, deserialized);
+ }
+
+ @Test
+ public void readingNonExistingEntry() {
+ final var cache = new ContainerEndpointsCache(Path.createRoot(), new MockCurator());
+ final var endpoints = cache.read(ApplicationId.defaultId());
+ assertTrue(endpoints.isEmpty());
+ }
+} \ No newline at end of file