All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.swirlds.virtualmap.internal.reconnect.TopToBottomTraversalOrder Maven / Gradle / Ivy

/*
 * Copyright (C) 2024 Hedera Hashgraph, LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.swirlds.virtualmap.internal.reconnect;

import static com.swirlds.virtualmap.internal.Path.ROOT_PATH;

import com.swirlds.common.merkle.synchronization.task.ReconnectNodeCount;
import com.swirlds.virtualmap.internal.Path;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

/**
 * Virtual node traversal policy, which starts sending requests from the root node and proceeds
 * virtual tree rank by rank, top to bottom. Every node is checked to have a clean parent on
 * its path to the root. If a clean parent is found, the node is skipped (not sent to the teacher).
 *
 * 

Clean nodes are tracked in a concurrent set. This set is populated in {@link * #nodeReceived(long, boolean)} on the receiving thread. Since teacher responses are always * lagging behind learner requests, some clean nodes may be requested redundantly. That is, * a request for a clean node is sent before a response for any of its clean parents is * received from the teacher. */ public class TopToBottomTraversalOrder implements NodeTraversalOrder { private ReconnectNodeCount nodeCount; private long reconnectFirstLeafPath; private long reconnectLastLeafPath; // Last sent path. Initialized to 0, since the root path is always sent first private long lastPath = 0; // Clean node paths, as received from the teacher. Only internal paths are recorded here, // there is no need to track clean leaves, since they don't have children. This set is // populated on the receiving thread and queried on the sending thread private final Set cleanNodes = ConcurrentHashMap.newKeySet(); public TopToBottomTraversalOrder() {} @Override public void start(final long firstLeafPath, final long lastLeafPath, final ReconnectNodeCount nodeCount) { this.reconnectFirstLeafPath = firstLeafPath; this.reconnectLastLeafPath = lastLeafPath; this.nodeCount = nodeCount; } @Override public void nodeReceived(final long path, final boolean isClean) { final boolean isLeaf = path >= reconnectFirstLeafPath; if (isClean && !isLeaf) { cleanNodes.add(path); } if (isLeaf) { nodeCount.incrementLeafCount(); if (isClean) { nodeCount.incrementRedundantLeafCount(); } } else { nodeCount.incrementInternalCount(); if (isClean) { nodeCount.incrementRedundantInternalCount(); } } } @Override public long getNextPathToSend() { assert lastPath != Path.INVALID_PATH; long path = lastPath + 1; long result = skipCleanPaths(path); // Find the highest clean path and skip all paths in its sub-tree. Repeat while ((result != Path.INVALID_PATH) && (result != path)) { path = result; result = skipCleanPaths(path); } return lastPath = result; } /** * For the given path, find the highest clean parent path on the way to the root. If such * a clean parent is found, all paths in the parent's sub-tree at the same rank as the * initial path are skipped, and the next path outside of the sub-tree is returned (it may * also be clean, this is handled in the loop in {@link #getNextPathToSend()}. Is the * sub-tree spans up to the last leaf path, this method returns Path.INVALID_PATH, which * indicates there are no more requests to the teacher to send. If no clean parents are * found, the initial path is returned. */ private long skipCleanPaths(final long path) { assert path > 0; if (path > reconnectLastLeafPath) { return Path.INVALID_PATH; } // Find the highest clean parent and its rank long parent = Path.getParentPath(path); long cleanParent = Path.INVALID_PATH; int parentRanksAbove = 1; int cleanParentRanksAbove = 1; while (parent != ROOT_PATH) { if (cleanNodes.contains(parent)) { cleanParent = parent; cleanParentRanksAbove = parentRanksAbove; } parentRanksAbove++; parent = Path.getParentPath(parent); } final long result; if (cleanParent == Path.INVALID_PATH) { // no clean parent found result = path; } else { // If found, get the right-most path in parent's sub-tree at the initial rank // and return the next path result = Path.getRightGrandChildPath(cleanParent, cleanParentRanksAbove) + 1; } assert result >= path; return (result <= reconnectLastLeafPath) ? result : Path.INVALID_PATH; } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy