diff options
author | Martin Polden <mpolden@mpolden.no> | 2019-06-11 09:19:07 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2019-06-11 09:19:07 +0200 |
commit | ed998d7574eac2dba223012ca1340d302581778e (patch) | |
tree | fe79ac59bdc691055bd0567a4827231eefb85952 /configserver | |
parent | bb0ff41764364aa9cb4100e6443ea2d71b73e133 (diff) | |
parent | a312c7d6260773e375c062e102458239b34ad126 (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')
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 |