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

com.swirlds.common.merkle.synchronization.task.TeacherPushSendTask Maven / Gradle / Ivy

Go to download

Swirlds is a software platform designed to build fully-distributed applications that harness the power of the cloud without servers. Now you can develop applications with fairness in decision making, speed, trust and reliability, at a fraction of the cost of traditional server-based platforms.

There is a newer version: 0.56.6
Show newest version
/*
 * Copyright (C) 2021-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.common.merkle.synchronization.task;

import static com.swirlds.common.merkle.synchronization.task.LessonType.CUSTOM_VIEW_ROOT;
import static com.swirlds.common.merkle.synchronization.task.LessonType.INTERNAL_NODE_DATA;
import static com.swirlds.common.merkle.synchronization.task.LessonType.LEAF_NODE_DATA;
import static com.swirlds.common.merkle.synchronization.task.LessonType.NODE_IS_UP_TO_DATE;
import static com.swirlds.logging.legacy.LogMarker.RECONNECT;
import static java.util.concurrent.TimeUnit.NANOSECONDS;

import com.swirlds.base.time.Time;
import com.swirlds.common.merkle.synchronization.config.ReconnectConfig;
import com.swirlds.common.merkle.synchronization.streams.AsyncInputStream;
import com.swirlds.common.merkle.synchronization.streams.AsyncOutputStream;
import com.swirlds.common.merkle.synchronization.utility.MerkleSynchronizationException;
import com.swirlds.common.merkle.synchronization.views.CustomReconnectRoot;
import com.swirlds.common.merkle.synchronization.views.TeacherTreeView;
import com.swirlds.common.threading.pool.StandardWorkGroup;
import com.swirlds.common.utility.throttle.RateLimiter;
import edu.umd.cs.findbugs.annotations.NonNull;
import java.util.Queue;
import java.util.concurrent.atomic.AtomicBoolean;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

/**
 * This class encapsulates all logic for the teacher's sending task.
 *
 * @param  the type of data used by the view to represent a node
 */
public class TeacherPushSendTask {

    private static final Logger logger = LogManager.getLogger(TeacherPushSendTask.class);

    private static final String NAME = "teacher-send-task";

    /**
     * The lesson used to describe an up to date node is always eactly the same. No need to create a new object each
     * time.
     */
    private static final Lesson UP_TO_DATE_LESSON = new Lesson<>(NODE_IS_UP_TO_DATE, null);

    private final ReconnectConfig reconnectConfig;
    private final StandardWorkGroup workGroup;
    private final AsyncInputStream in;
    private final AsyncOutputStream> out;
    private final Queue subtrees;
    private final TeacherTreeView view;

    private final AtomicBoolean senderIsFinished;

    private final RateLimiter rateLimiter;
    private final int sleepNanos;

    /**
     * Create new thread that will send data lessons and queries for a subtree.
     *
     * @param time                  the wall clock time
     * @param reconnectConfig       the configuration for reconnect
     * @param workGroup             the work group managing the reconnect
     * @param in                    the input stream
     * @param out                   the output stream, this object is responsible for closing this object when finished
     * @param subtrees              a queue containing roots of subtrees to send, may have more roots added by this
     *                              class
     * @param view                  an object that interfaces with the subtree
     * @param senderIsFinished      set to true when this thread has finished
     */
    public TeacherPushSendTask(
            @NonNull final Time time,
            @NonNull final ReconnectConfig reconnectConfig,
            final StandardWorkGroup workGroup,
            final AsyncInputStream in,
            final AsyncOutputStream> out,
            final Queue subtrees,
            final TeacherTreeView view,
            final AtomicBoolean senderIsFinished) {
        this.reconnectConfig = reconnectConfig;
        this.workGroup = workGroup;
        this.in = in;
        this.out = out;
        this.subtrees = subtrees;
        this.view = view;
        this.senderIsFinished = senderIsFinished;

        final int maxRate = reconnectConfig.teacherMaxNodesPerSecond();
        if (maxRate > 0) {
            rateLimiter = new RateLimiter(time, maxRate);
            sleepNanos = (int) reconnectConfig.teacherRateLimiterSleep().toNanos();
        } else {
            rateLimiter = null;
            sleepNanos = -1;
        }
    }

    /**
     * Start the thread that sends lessons and queries to the learner.
     */
    public void start() {
        workGroup.execute(NAME, this::run);
    }

    /**
     * When a {@link Lesson} for in an internal node is sent, that lesson contains embedded queries. This method
     * prepares for the responses to those queries.
     */
    private void prepareForQueryResponse(final T parent, final int childIndex) {
        in.anticipateMessage();
        final T child = view.getChildAndPrepareForQueryResponse(parent, childIndex);
        view.addToHandleQueue(child);
    }

    /**
     * Send a lesson that describes the root of a subtree with a custom view.
     */
    private Lesson buildCustomReconnectRootLesson(final T node) {
        final Lesson lesson = new Lesson<>(CUSTOM_VIEW_ROOT, new CustomViewRootLesson(view.getClassId(node)));
        final CustomReconnectRoot subtreeRoot = (CustomReconnectRoot) view.getMerkleRoot(node);

        subtrees.add(new TeacherSubtree(subtreeRoot, subtreeRoot.buildTeacherView(reconnectConfig)));

        return lesson;
    }

    /**
     * Send a lesson that contains data for a leaf or an internal node.
     */
    private Lesson buildDataLesson(final T node) {
        final Lesson lesson;
        if (view.isInternal(node, true)) {
            lesson = new Lesson<>(INTERNAL_NODE_DATA, new InternalDataLesson<>(view, node));
            final int childCount = view.getNumberOfChildren(node);
            for (int childIndex = 0; childIndex < childCount; childIndex++) {
                prepareForQueryResponse(node, childIndex);
            }
        } else {
            lesson = new Lesson<>(LEAF_NODE_DATA, new LeafDataLesson<>(view, node));
        }

        return lesson;
    }

    /**
     * 

* Send a lesson about a node. Each query sent to the learner is always followed by a lesson (eventually). Some * lessons are just confirmations that the learner has the data. Others actually contain the data required by the * learner to reconstruct the node. *

* *

* Lessons containing data about an internal node may also contain queries. The queries will be for the children of * the internal node. *

*/ @SuppressWarnings("unchecked") private void sendLesson(final T node) throws InterruptedException { final Lesson lesson; final boolean learnerHasConfirmed = view.hasLearnerConfirmedFor(node); if (learnerHasConfirmed) { lesson = (Lesson) UP_TO_DATE_LESSON; } else if (view.isCustomReconnectRoot(node)) { lesson = buildCustomReconnectRootLesson(node); } else { lesson = buildDataLesson(node); } out.sendAsync(lesson); } /** * Enforce the rate limit. * * @throws InterruptedException if the thread is interrupted while sleeping */ private void rateLimit() throws InterruptedException { if (rateLimiter != null) { while (!rateLimiter.requestAndTrigger()) { NANOSECONDS.sleep(sleepNanos); } } } /** * This thread is responsible for sending lessons (and nested queries) to the learner. */ private void run() { try (out) { out.sendAsync(buildDataLesson(view.getRoot())); while (view.areThereNodesToHandle()) { rateLimit(); final T node = view.getNextNodeToHandle(); sendLesson(node); } } catch (final InterruptedException ex) { logger.warn(RECONNECT.getMarker(), "teacher's sending thread interrupted"); Thread.currentThread().interrupt(); } catch (final Exception ex) { throw new MerkleSynchronizationException("exception in the teacher's receiving thread", ex); } finally { senderIsFinished.set(true); } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy