From e7a23602da63627f8b30e094bc41469c301891dd Mon Sep 17 00:00:00 2001 From: Tor Brede Vekterli Date: Mon, 4 Dec 2023 13:46:05 +0100 Subject: Use fake ZooKeeper database implementation for subset of CC tests The fake impl acts "as if" a single-node ZK quorum is present, so it cannot be directly used with most multi-node tests that require multiple nodes to actually participate in leader elections. --- .../core/FakeZooKeeperDatabase.java | 136 +++++++++++++++++++++ 1 file changed, 136 insertions(+) create mode 100644 clustercontroller-core/src/test/java/com/yahoo/vespa/clustercontroller/core/FakeZooKeeperDatabase.java (limited to 'clustercontroller-core/src/test/java/com/yahoo/vespa/clustercontroller/core/FakeZooKeeperDatabase.java') diff --git a/clustercontroller-core/src/test/java/com/yahoo/vespa/clustercontroller/core/FakeZooKeeperDatabase.java b/clustercontroller-core/src/test/java/com/yahoo/vespa/clustercontroller/core/FakeZooKeeperDatabase.java new file mode 100644 index 00000000000..057d137650f --- /dev/null +++ b/clustercontroller-core/src/test/java/com/yahoo/vespa/clustercontroller/core/FakeZooKeeperDatabase.java @@ -0,0 +1,136 @@ +// Copyright Vespa.ai. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.vespa.clustercontroller.core; + +import com.yahoo.vdslib.state.Node; +import com.yahoo.vdslib.state.NodeState; +import com.yahoo.vespa.clustercontroller.core.database.Database; +import com.yahoo.vespa.clustercontroller.core.database.DatabaseFactory; + +import java.util.Map; +import java.util.TreeMap; + +/** + * Memory-backed fake DB implementation that tries to mirror the semantics of the + * (synchronous) ZooKeeper DB implementation. By itself this fake acts as if a quorum + * with a _single_, local ZK instance has been configured. This DB instance cannot be + * used across multiple cluster controller instances. + * + * Threading note: we expect all invocations on this instance to happen from the + * main cluster controller thread (i.e. "as-if" single threaded), but we wrap everything + * in a mutex to stay on the safe side since this isn't explicitly documented as + * part of the API, + */ +public class FakeZooKeeperDatabase extends Database { + + public static class Factory implements DatabaseFactory { + private final FleetControllerContext context; + public Factory(FleetControllerContext context) { + this.context = context; + } + @Override + public Database create(Params params) { + return new FakeZooKeeperDatabase(context, params.listener); + } + } + + private final FleetControllerContext context; + private final Database.DatabaseListener listener; + + private final Object mutex = new Object(); + private boolean closed = false; + private Integer persistedLatestStateVersion = null; + private Map persistedLeaderVotes = new TreeMap<>(); + private Map persistedWantedStates = new TreeMap<>(); + private Map persistedStartTimestamps = new TreeMap<>(); + private ClusterStateBundle persistedBundle = ClusterStateBundle.ofBaselineOnly(AnnotatedClusterState.emptyState()); + + public FakeZooKeeperDatabase(FleetControllerContext context, DatabaseListener listener) { + this.context = context; + this.listener = listener; + } + + @Override + public void close() { + synchronized (mutex) { + closed = true; + } + } + + @Override + public boolean isClosed() { + synchronized (mutex) { + return closed; + } + } + + @Override + public boolean storeMasterVote(int voteForNode) { + Map voteState; + synchronized (mutex) { + persistedLeaderVotes.put(context.id().index(), voteForNode); + voteState = Map.copyOf(persistedLeaderVotes); + } + listener.handleMasterData(voteState); + return true; + } + + @Override + public boolean storeLatestSystemStateVersion(int version) { + synchronized (mutex) { + persistedLatestStateVersion = version; + return true; + } + } + + @Override + public Integer retrieveLatestSystemStateVersion() { + synchronized (mutex) { + return persistedLatestStateVersion; + } + } + + @Override + public boolean storeWantedStates(Map states) { + synchronized (mutex) { + persistedWantedStates = Map.copyOf(states); + } + return true; + } + + @Override + public Map retrieveWantedStates() { + synchronized (mutex) { + return Map.copyOf(persistedWantedStates); + } + } + + @Override + public boolean storeStartTimestamps(Map timestamps) { + synchronized (mutex) { + persistedStartTimestamps = Map.copyOf(timestamps); + return true; + } + } + + @Override + public Map retrieveStartTimestamps() { + synchronized (mutex) { + return Map.copyOf(persistedStartTimestamps); + } + } + + @Override + public boolean storeLastPublishedStateBundle(ClusterStateBundle stateBundle) { + synchronized (mutex) { + persistedBundle = stateBundle; + return true; + } + } + + @Override + public ClusterStateBundle retrieveLastPublishedStateBundle() { + synchronized (mutex) { + return persistedBundle; + } + } +} -- cgit v1.2.3