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

org.neo4j.coreedge.catchup.CatchupServer Maven / Gradle / Ivy

/*
 * Copyright (c) 2002-2016 "Neo Technology,"
 * Network Engine for Objects in Lund AB [http://neotechnology.com]
 *
 * This file is part of Neo4j.
 *
 * Neo4j 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 .
 */
package org.neo4j.coreedge.catchup;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelInboundHandler;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
import io.netty.handler.codec.LengthFieldPrepender;
import io.netty.handler.stream.ChunkedWriteHandler;

import java.net.BindException;
import java.util.concurrent.TimeUnit;
import java.util.function.BooleanSupplier;
import java.util.function.Supplier;

import org.neo4j.coreedge.VersionDecoder;
import org.neo4j.coreedge.VersionPrepender;
import org.neo4j.coreedge.catchup.CatchupServerProtocol.State;
import org.neo4j.coreedge.catchup.storecopy.FileHeaderEncoder;
import org.neo4j.coreedge.catchup.storecopy.GetStoreIdRequest;
import org.neo4j.coreedge.catchup.storecopy.GetStoreIdRequestHandler;
import org.neo4j.coreedge.catchup.storecopy.GetStoreRequestDecoder;
import org.neo4j.coreedge.catchup.storecopy.GetStoreRequestHandler;
import org.neo4j.coreedge.catchup.storecopy.StoreCopyFinishedResponseEncoder;
import org.neo4j.coreedge.catchup.tx.TxPullRequestDecoder;
import org.neo4j.coreedge.catchup.tx.TxPullRequestHandler;
import org.neo4j.coreedge.catchup.tx.TxPullResponseEncoder;
import org.neo4j.coreedge.catchup.tx.TxStreamFinishedResponseEncoder;
import org.neo4j.coreedge.core.CoreEdgeClusterSettings;
import org.neo4j.coreedge.core.state.CoreState;
import org.neo4j.coreedge.core.state.snapshot.CoreSnapshotEncoder;
import org.neo4j.coreedge.core.state.snapshot.CoreSnapshotRequest;
import org.neo4j.coreedge.core.state.snapshot.CoreSnapshotRequestHandler;
import org.neo4j.coreedge.handlers.ExceptionLoggingHandler;
import org.neo4j.coreedge.handlers.ExceptionMonitoringHandler;
import org.neo4j.coreedge.handlers.ExceptionSwallowingHandler;
import org.neo4j.coreedge.identity.StoreId;
import org.neo4j.graphdb.config.Setting;
import org.neo4j.helpers.ListenSocketAddress;
import org.neo4j.helpers.NamedThreadFactory;
import org.neo4j.kernel.NeoStoreDataSource;
import org.neo4j.kernel.configuration.Config;
import org.neo4j.kernel.impl.transaction.log.LogicalTransactionStore;
import org.neo4j.kernel.impl.transaction.log.TransactionIdStore;
import org.neo4j.kernel.impl.transaction.log.checkpoint.CheckPointer;
import org.neo4j.kernel.lifecycle.LifecycleAdapter;
import org.neo4j.kernel.monitoring.Monitors;
import org.neo4j.logging.Log;
import org.neo4j.logging.LogProvider;

public class CatchupServer extends LifecycleAdapter
{
    private static final Setting setting = CoreEdgeClusterSettings.transaction_listen_address;
    private final LogProvider logProvider;
    private final Log log;
    private final Log userLog;
    private final Monitors monitors;

    private final Supplier storeIdSupplier;
    private final Supplier transactionIdStoreSupplier;
    private final Supplier logicalTransactionStoreSupplier;
    private final Supplier dataSourceSupplier;
    private final BooleanSupplier dataSourceAvailabilitySupplier;

    private final NamedThreadFactory threadFactory = new NamedThreadFactory( "catchup-server" );
    private final CoreState coreState;
    private final ListenSocketAddress listenAddress;

    private EventLoopGroup workerGroup;
    private Channel channel;
    private Supplier checkPointerSupplier;

    public CatchupServer( LogProvider logProvider, LogProvider userLogProvider, Supplier storeIdSupplier,
            Supplier transactionIdStoreSupplier,
            Supplier logicalTransactionStoreSupplier,
            Supplier dataSourceSupplier, BooleanSupplier dataSourceAvailabilitySupplier,
            CoreState coreState, Config config, Monitors monitors, Supplier checkPointerSupplier )
    {
        this.coreState = coreState;
        this.listenAddress = config.get( setting );
        this.transactionIdStoreSupplier = transactionIdStoreSupplier;
        this.storeIdSupplier = storeIdSupplier;
        this.dataSourceAvailabilitySupplier = dataSourceAvailabilitySupplier;
        this.logicalTransactionStoreSupplier = logicalTransactionStoreSupplier;
        this.logProvider = logProvider;
        this.monitors = monitors;
        this.log = logProvider.getLog( getClass() );
        this.userLog = userLogProvider.getLog( getClass() );
        this.dataSourceSupplier = dataSourceSupplier;
        this.checkPointerSupplier = checkPointerSupplier;
    }

    @Override
    public synchronized void start() throws Throwable
    {
        if ( channel != null )
        {
            return;
        }

        workerGroup = new NioEventLoopGroup( 0, threadFactory );

        ServerBootstrap bootstrap = new ServerBootstrap()
                .group( workerGroup )
                .channel( NioServerSocketChannel.class )
                .localAddress( listenAddress.socketAddress() )
                .childHandler( new ChannelInitializer()
                {
                    @Override
                    protected void initChannel( SocketChannel ch )
                    {
                        CatchupServerProtocol protocol = new CatchupServerProtocol();

                        ChannelPipeline pipeline = ch.pipeline();
                        pipeline.addLast( new LengthFieldBasedFrameDecoder( Integer.MAX_VALUE, 0, 4, 0, 4 ) );
                        pipeline.addLast( new LengthFieldPrepender( 4 ) );

                        pipeline.addLast( new VersionDecoder( logProvider ) );
                        pipeline.addLast( new VersionPrepender() );

                        pipeline.addLast( new ResponseMessageTypeEncoder() );
                        pipeline.addLast( new RequestMessageTypeEncoder() );

                        pipeline.addLast( new TxPullResponseEncoder() );
                        pipeline.addLast( new CoreSnapshotEncoder() );
                        pipeline.addLast( new StoreCopyFinishedResponseEncoder() );
                        pipeline.addLast( new TxStreamFinishedResponseEncoder() );
                        pipeline.addLast( new FileHeaderEncoder() );

                        pipeline.addLast( new ServerMessageTypeHandler( protocol, logProvider ) );

                        pipeline.addLast( decoders( protocol ) );

                        pipeline.addLast(
                                new TxPullRequestHandler( protocol, storeIdSupplier, dataSourceAvailabilitySupplier,
                                        transactionIdStoreSupplier, logicalTransactionStoreSupplier, monitors,
                                        logProvider ) ); pipeline.addLast( new ChunkedWriteHandler() );
                        pipeline.addLast( new GetStoreRequestHandler( protocol, dataSourceSupplier,
                                checkPointerSupplier ) );
                        pipeline.addLast( new GetStoreIdRequestHandler( protocol, storeIdSupplier ) );
                        pipeline.addLast( new CoreSnapshotRequestHandler( protocol, coreState ) );

                        pipeline.addLast( new ExceptionLoggingHandler( log ) );
                        pipeline.addLast( new ExceptionMonitoringHandler(
                                monitors.newMonitor( ExceptionMonitoringHandler.Monitor.class, CatchupServer.class ) ) );
                        pipeline.addLast( new ExceptionSwallowingHandler() );
                    }
                } );

        try
        {
            channel = bootstrap.bind().syncUninterruptibly().channel();
        }
        catch( Exception e )
        {
            // thanks to netty we need to catch everything and do an instanceof because it does not declare properly
            // checked exception but it still throws them with some black magic at runtime.
            //noinspection ConstantConditions
            if ( e instanceof BindException )
            {
                userLog.error( "Address is already bound for setting: " + setting + " with value: " + listenAddress );
                log.error( "Address is already bound for setting: " + setting + " with value: " + listenAddress, e );
                throw e;
            }
        }
    }

    private ChannelInboundHandler decoders( CatchupServerProtocol protocol )
    {
        RequestDecoderDispatcher decoderDispatcher =
                new RequestDecoderDispatcher<>( protocol, logProvider );
        decoderDispatcher.register( State.TX_PULL, new TxPullRequestDecoder() );
        decoderDispatcher.register( State.GET_STORE, new GetStoreRequestDecoder() );
        decoderDispatcher.register( State.GET_STORE_ID, new SimpleRequestDecoder( GetStoreIdRequest::new ) );
        decoderDispatcher.register( State.GET_CORE_SNAPSHOT, new SimpleRequestDecoder( CoreSnapshotRequest::new) );
        return decoderDispatcher;
    }

    @Override
    public synchronized void stop() throws Throwable
    {
        if ( channel == null )
        {
            return;
        }

        log.info( "CatchupServer stopping and unbinding from " + listenAddress );
        try
        {
            channel.close().sync();
            channel = null;
        }
        catch ( InterruptedException e )
        {
            Thread.currentThread().interrupt();
            log.warn( "Interrupted while closing channel." );
        }

        if ( workerGroup != null &&
                workerGroup.shutdownGracefully( 2, 5, TimeUnit.SECONDS ).awaitUninterruptibly( 10, TimeUnit.SECONDS ) )
        {
            log.warn( "Worker group not shutdown within 10 seconds." );
        }
        workerGroup = null;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy