diff options
author | Øyvind Grønnesby <oyving@verizonmedia.com> | 2019-06-05 17:10:07 +0200 |
---|---|---|
committer | Øyvind Grønnesby <oyving@verizonmedia.com> | 2019-06-05 17:10:07 +0200 |
commit | 793209c73c3f118dac7b2cb5a6ef72c555439fe8 (patch) | |
tree | 10d1f5e920419ff835843a28891f5138494d8a65 /configserver | |
parent | a5d2470ac60e9bcb8c67174a203b28faea11bef2 (diff) |
Create new rotations cache that keeps info on endpoints and clusters
Diffstat (limited to 'configserver')
3 files changed, 273 insertions, 0 deletions
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/tenant/RotationAssignment.java b/configserver/src/main/java/com/yahoo/vespa/config/server/tenant/RotationAssignment.java new file mode 100644 index 00000000000..e72570b63a7 --- /dev/null +++ b/configserver/src/main/java/com/yahoo/vespa/config/server/tenant/RotationAssignment.java @@ -0,0 +1,79 @@ +// 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.Rotation; +import com.yahoo.slime.Cursor; +import com.yahoo.slime.Inspector; +import com.yahoo.vespa.applicationmodel.ClusterId; + +import java.util.Objects; + +public class RotationAssignment { + private final String endpointId; + private final Rotation rotation; + private final ClusterId containerId; + + RotationAssignment(String endpointId, Rotation rotationName, ClusterId clusterId) { + this.endpointId = Objects.requireNonNull(endpointId); + this.rotation = Objects.requireNonNull(rotationName); + this.containerId = Objects.requireNonNull(clusterId); + } + + public String getEndpointId() { + return endpointId; + } + + public Rotation getRotation() { + return rotation; + } + + public ClusterId getContainerId() { + return containerId; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + RotationAssignment that = (RotationAssignment) o; + return Objects.equals(endpointId, that.endpointId) && + Objects.equals(rotation, that.rotation) && + Objects.equals(containerId, that.containerId); + } + + @Override + public int hashCode() { + return Objects.hash(endpointId, rotation, containerId); + } + + static RotationAssignment fromSlime(Inspector inspector) { + final var endpointId = inspector.field("endpointId").asString(); + final var rotationId = inspector.field("rotationId").asString(); + final var containerId = inspector.field("containerId").asString(); + + + if (endpointId.equals("")) { + throw new IllegalStateException("Missing 'endpointId' in RotationsCache"); + } + + if (rotationId.equals("")) { + throw new IllegalStateException("Missing 'rotationId' in RotationsCache"); + } + + if (containerId.equals("")) { + throw new IllegalStateException("Missing 'containerId' in RotationsCache"); + } + + return new RotationAssignment( + endpointId, + new Rotation(rotationId), + new ClusterId(containerId) + ); + } + + void toSlime(Cursor cursor) { + cursor.setString("endpointId", endpointId); + cursor.setString("rotationId", rotation.getId()); + cursor.setString("containerId", containerId.toString()); + } +} diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/tenant/RotationsCache.java b/configserver/src/main/java/com/yahoo/vespa/config/server/tenant/RotationsCache.java new file mode 100644 index 00000000000..d1c9dc329ed --- /dev/null +++ b/configserver/src/main/java/com/yahoo/vespa/config/server/tenant/RotationsCache.java @@ -0,0 +1,77 @@ +// 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.slime.ObjectTraverser; +import com.yahoo.slime.Slime; +import com.yahoo.vespa.applicationmodel.ClusterId; +import com.yahoo.vespa.config.SlimeUtils; +import com.yahoo.vespa.curator.Curator; + +import java.io.IOException; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + + +/** + * @author ogronnesby + */ +public class RotationsCache { + private final Path cachePath; + private final Curator curator; + + public RotationsCache(Path tenantPath, Curator curator) { + this.cachePath = tenantPath.append("rotationsCache-v2/"); + this.curator = curator; + } + + public Map<ClusterId, RotationAssignment> getRotationAssignment(ApplicationId applicationId) { + final var optionalData = curator.getData(applicationPath(applicationId)); + return optionalData + .map(SlimeUtils::jsonToSlime) + .map(RotationsCache::entryFromSlime) + .orElse(Collections.emptyMap()); + } + + public void putRotationAssignment(ApplicationId applicationId, Map<ClusterId, RotationAssignment> assignments) { + if (assignments.isEmpty()) return; + try { + curator.set( + applicationPath(applicationId), + SlimeUtils.toJsonBytes(entryToSlime(assignments)) + ); + } catch (IOException e) { + throw new RuntimeException("Error writing rotations of: " + applicationId, e); + } + } + + static Map<ClusterId, RotationAssignment> entryFromSlime(Slime slime) { + final var assignmentMap = new HashMap<ClusterId, RotationAssignment>(); + + slime.get().traverse((ObjectTraverser) (name, inspector) -> { + final var containerId = new ClusterId(name); + final var assignment = RotationAssignment.fromSlime(inspector); + assignmentMap.put(containerId, assignment); + }); + + return Map.copyOf(assignmentMap); + } + + static Slime entryToSlime(Map<ClusterId, RotationAssignment> assignments) { + final var slime = new Slime(); + final var cursor = slime.setObject(); + + assignments.forEach((clusterId, assignment) -> { + final var assignmentCursor = cursor.setObject(clusterId.toString()); + assignment.toSlime(assignmentCursor); + }); + + return slime; + } + + private Path applicationPath(ApplicationId applicationId) { + return cachePath.append(applicationId.serializedForm()); + } +} diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/tenant/RotationsCacheTest.java b/configserver/src/test/java/com/yahoo/vespa/config/server/tenant/RotationsCacheTest.java new file mode 100644 index 00000000000..b0108d028d2 --- /dev/null +++ b/configserver/src/test/java/com/yahoo/vespa/config/server/tenant/RotationsCacheTest.java @@ -0,0 +1,117 @@ +// 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.config.provision.Rotation; +import com.yahoo.path.Path; +import com.yahoo.slime.Slime; +import com.yahoo.vespa.applicationmodel.ClusterId; +import com.yahoo.vespa.curator.mock.MockCurator; +import org.junit.Test; + +import java.util.HashMap; +import java.util.Set; + +import static org.junit.Assert.assertEquals; + +public class RotationsCacheTest { + @Test + public void assignmentDeserialization() { + final var slime = new Slime(); + final var cursor = slime.setObject(); + + cursor.setString("endpointId", "nallefisk"); + cursor.setString("containerId", "froskelosk"); + cursor.setString("rotationId", "rotterott"); + + final var assignment = RotationAssignment.fromSlime(cursor); + assertEquals("nallefisk", assignment.getEndpointId()); + assertEquals("froskelosk", assignment.getContainerId().toString()); + assertEquals("rotterott", assignment.getRotation().getId()); + } + + @Test + public void assignmentSerialization() { + final var assignment = new RotationAssignment( + "sluttpeker", + new Rotation("rotasjon"), + new ClusterId("klynge") + ); + + final var serialized = new Slime(); + assignment.toSlime(serialized.setObject()); + + assertEquals(assignment, RotationAssignment.fromSlime(serialized.get())); + } + + @Test + public void mapDeserialization() { + final var slime = new Slime(); + final var cursor = slime.setObject(); + + final var clusterId = new ClusterId("nalle"); + + final var entry = cursor.setObject("nalle"); + entry.setString("endpointId", "nallefisk"); + entry.setString("containerId", clusterId.toString()); + entry.setString("rotationId", "rotterott"); + + final var assignments = RotationsCache.entryFromSlime(slime); + assertEquals(Set.of(clusterId), assignments.keySet()); + + // check the entry + final var assignment = assignments.get(clusterId); + assertEquals(clusterId, assignment.getContainerId()); + assertEquals("nallefisk", assignment.getEndpointId()); + assertEquals(new Rotation("rotterott"), assignment.getRotation()); + } + + @Test + public void mapSerialization() { + final var assignments = new HashMap<ClusterId, RotationAssignment>(); + + assignments.put( + new ClusterId("the-nallefisk-1"), + new RotationAssignment( + "the-endpoint-1", + new Rotation("the-rotation-1"), + new ClusterId("the-cluster-1") + ) + ); + + assignments.put( + new ClusterId("the-nallefisk-2"), + new RotationAssignment( + "the-endpoint-2", + new Rotation("the-rotation-2"), + new ClusterId("the-cluster-2") + ) + ); + + final var serialized = RotationsCache.entryToSlime(assignments); + final var deserialized = RotationsCache.entryFromSlime(serialized); + + assertEquals(assignments, deserialized); + } + + @Test + public void cacheStoreAndLoad() { + final var rotations = new RotationsCache(Path.createRoot().append("foo"), new MockCurator()); + final var assignments = new HashMap<ClusterId, RotationAssignment>(); + + assignments.put( + new ClusterId("the-nallefisk-1"), + new RotationAssignment( + "the-endpoint-1", + new Rotation("the-rotation-1"), + new ClusterId("the-cluster-1") + ) + ); + + rotations.putRotationAssignment(ApplicationId.defaultId(), assignments); + final var fetched = rotations.getRotationAssignment(ApplicationId.defaultId()); + + assertEquals(assignments, fetched); + } + +} |