/** * diqube: Distributed Query Base. * * Copyright (C) 2015 Bastian Gloeckle * * This file is part of diqube. * * diqube is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package org.diqube.consensus.internal; import java.nio.ByteBuffer; import java.util.Deque; import java.util.UUID; import java.util.concurrent.ConcurrentLinkedDeque; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import javax.inject.Inject; import org.apache.thrift.TException; import org.diqube.consensus.ConsensusServer; import org.diqube.context.AutoInstatiate; import org.diqube.context.shutdown.ContextShutdownListener; import org.diqube.context.shutdown.ShutdownAfter; import org.diqube.remote.cluster.thrift.ClusterConsensusService; import org.diqube.remote.cluster.thrift.RConnectionUnknownException; import org.diqube.thrift.base.thrift.RNodeAddress; import org.diqube.thrift.base.thrift.RUUID; import org.diqube.thrift.base.util.RUuidUtil; import io.atomix.catalyst.util.concurrent.CatalystThreadFactory; import io.atomix.catalyst.util.concurrent.ThreadContext; import io.atomix.catalyst.util.concurrent.ThreadPoolContext; /** * Handler for {@link ClusterConsensusService} which handles communication of copycat traffic for our consensus. * * @author Bastian Gloeckle */ @AutoInstatiate public class ClusterConsensusHandler implements ClusterConsensusService.Iface, ContextShutdownListener { @Inject private ClusterConsensusConnectionRegistry registry; @Inject private DiqubeCatalystConnectionFactory factory; @Inject private DiqubeCatalystServer server; @Inject private DiqubeCatalystSerializer serializer; private volatile ThreadContext openConnectionsThreadContext; private Deque<Runnable> cleanupActions = new ConcurrentLinkedDeque<>(); @Override @ShutdownAfter(ConsensusServer.class) public void contextAboutToShutdown() { while (!cleanupActions.isEmpty()) cleanupActions.poll().run(); } /** * Open a new catalyst connection */ @Override public RUUID open(RUUID otherConsensusConnectionEndpointId, RNodeAddress resultAddress) throws TException { DiqubeCatalystConnection newCon = factory.createDiqubeCatalystConnection(getOpenConnectionsThreadContext()); UUID thisConnectionEndpointId = newCon.acceptAndRegister(RUuidUtil.toUuid(otherConsensusConnectionEndpointId), resultAddress); server.newClientConnection(newCon); return RUuidUtil.toRUuid(thisConnectionEndpointId); } /** * Close a catalyst connection */ @Override public void close(RUUID consensusConnectionEndpointId) throws RConnectionUnknownException, TException { DiqubeCatalystConnection con = registry.getConnectionEndpoint(RUuidUtil.toUuid(consensusConnectionEndpointId)); if (con != null) con.close(); else throw new RConnectionUnknownException( "Consensus connection endpoint unknown: " + RUuidUtil.toUuid(consensusConnectionEndpointId).toString()); } /** * Send a "request" of an opened catalyst connection */ @Override public void request(RUUID consensusConnectionEndpointId, RUUID consensusRequestId, ByteBuffer data) throws RConnectionUnknownException, TException { DiqubeCatalystConnection con = registry.getConnectionEndpoint(RUuidUtil.toUuid(consensusConnectionEndpointId)); if (con != null) con.handleRequest(RUuidUtil.toUuid(consensusRequestId), data); else throw new RConnectionUnknownException( "Consensus connection endpoint unknown: " + RUuidUtil.toUuid(consensusConnectionEndpointId).toString()); } /** * Send a "response" (to a request which was received before) of an opened catalyst connection */ @Override public void reply(RUUID consensusConnectionEndpointId, RUUID consensusRequestId, ByteBuffer data) throws RConnectionUnknownException, TException { DiqubeCatalystConnection con = registry.getConnectionEndpoint(RUuidUtil.toUuid(consensusConnectionEndpointId)); if (con != null) con.handleResponse(RUuidUtil.toUuid(consensusRequestId), data); else throw new RConnectionUnknownException( "Consensus connection endpoint unknown: " + RUuidUtil.toUuid(consensusConnectionEndpointId).toString()); } /** * Send an "exceptional response" (to a request which was received before) of an opened catalyst connection */ @Override public void replyException(RUUID consensusConnectionEndpointId, RUUID consensusRequestId, ByteBuffer data) throws RConnectionUnknownException, TException { DiqubeCatalystConnection con = registry.getConnectionEndpoint(RUuidUtil.toUuid(consensusConnectionEndpointId)); if (con != null) con.handleResponseException(RUuidUtil.toUuid(consensusRequestId), data); else throw new RConnectionUnknownException( "Consensus connection endpoint unknown: " + RUuidUtil.toUuid(consensusConnectionEndpointId).toString()); } /** * @return A Catalyst {@link ThreadContext} that can handle executing things for connections that were opened by other * hosts. */ private ThreadContext getOpenConnectionsThreadContext() { if (openConnectionsThreadContext == null) synchronized (this) { if (openConnectionsThreadContext == null) { ScheduledExecutorService executorService = Executors.newScheduledThreadPool(10, new CatalystThreadFactory("diqube-copycat-connection-%d")); openConnectionsThreadContext = new ThreadPoolContext(executorService, serializer); cleanupActions.add(() -> executorService.shutdownNow()); } } return openConnectionsThreadContext; } }