diff --git a/enterprise/core-edge/src/main/java/org/neo4j/coreedge/VersionCheckerChannelInboundHandler.java b/enterprise/core-edge/src/main/java/org/neo4j/coreedge/VersionCheckerChannelInboundHandler.java new file mode 100644 index 0000000000000..a7c9bd67ed0fb --- /dev/null +++ b/enterprise/core-edge/src/main/java/org/neo4j/coreedge/VersionCheckerChannelInboundHandler.java @@ -0,0 +1,55 @@ +/* + * 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; + +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.SimpleChannelInboundHandler; + +import java.util.function.Predicate; + +import org.neo4j.coreedge.messaging.Message; +import org.neo4j.logging.Log; +import org.neo4j.logging.LogProvider; + +public abstract class VersionCheckerChannelInboundHandler extends SimpleChannelInboundHandler +{ + private final Predicate versionChecker; + private final Log log; + + protected VersionCheckerChannelInboundHandler( Predicate versionChecker, LogProvider logProvider ) + { + this.versionChecker = versionChecker; + this.log = logProvider.getLog( getClass() ); + } + + @Override + protected final void channelRead0( ChannelHandlerContext ctx, M message ) throws Exception + { + if ( !versionChecker.test( message ) ) + { + log.error( "Unsupported version %d, unable to process message %s", message.version(), message ); + return; + } + + doChannelRead0( ctx, message ); + } + + protected abstract void doChannelRead0( ChannelHandlerContext ctx, M msg ) throws Exception; +} diff --git a/enterprise/core-edge/src/main/java/org/neo4j/coreedge/catchup/CatchupServer.java b/enterprise/core-edge/src/main/java/org/neo4j/coreedge/catchup/CatchupServer.java index 91eb57538440e..9e57294755c40 100644 --- a/enterprise/core-edge/src/main/java/org/neo4j/coreedge/catchup/CatchupServer.java +++ b/enterprise/core-edge/src/main/java/org/neo4j/coreedge/catchup/CatchupServer.java @@ -33,6 +33,7 @@ import io.netty.handler.stream.ChunkedWriteHandler; import java.util.concurrent.TimeUnit; +import java.util.function.Predicate; import java.util.function.Supplier; import org.neo4j.coreedge.catchup.CatchupServerProtocol.State; @@ -52,6 +53,7 @@ import org.neo4j.coreedge.core.state.snapshot.CoreSnapshotRequestHandler; import org.neo4j.coreedge.identity.StoreId; import org.neo4j.coreedge.logging.ExceptionLoggingHandler; +import org.neo4j.coreedge.messaging.Message; import org.neo4j.coreedge.messaging.address.ListenSocketAddress; import org.neo4j.helpers.NamedThreadFactory; import org.neo4j.kernel.NeoStoreDataSource; @@ -63,6 +65,8 @@ import org.neo4j.logging.Log; import org.neo4j.logging.LogProvider; +import static org.neo4j.coreedge.messaging.Message.CURRENT_VERSION; + public class CatchupServer extends LifecycleAdapter { private final LogProvider logProvider; @@ -135,14 +139,16 @@ protected void initChannel( SocketChannel ch ) throws Exception pipeline.addLast( decoders( protocol ) ); - pipeline.addLast( new TxPullRequestHandler( protocol, storeIdSupplier, - transactionIdStoreSupplier, logicalTransactionStoreSupplier, - monitors, logProvider ) ); + Predicate versionChecker = (m) -> m.version() == CURRENT_VERSION; + pipeline.addLast( new TxPullRequestHandler( versionChecker, protocol, storeIdSupplier, + 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 GetStoreRequestHandler( versionChecker, protocol, dataSourceSupplier, + checkPointerSupplier, logProvider ) ); + pipeline.addLast( new GetStoreIdRequestHandler( versionChecker, protocol, storeIdSupplier, + logProvider ) ); + pipeline.addLast( + new CoreSnapshotRequestHandler( versionChecker, protocol, coreState, logProvider ) ); pipeline.addLast( new ExceptionLoggingHandler( log ) ); } } ); diff --git a/enterprise/core-edge/src/main/java/org/neo4j/coreedge/catchup/ClientMessageTypeHandler.java b/enterprise/core-edge/src/main/java/org/neo4j/coreedge/catchup/ClientMessageTypeHandler.java index 090d9d61ad2cd..5ea1a3b2bf60c 100644 --- a/enterprise/core-edge/src/main/java/org/neo4j/coreedge/catchup/ClientMessageTypeHandler.java +++ b/enterprise/core-edge/src/main/java/org/neo4j/coreedge/catchup/ClientMessageTypeHandler.java @@ -45,7 +45,10 @@ public void channelRead( ChannelHandlerContext ctx, Object msg ) throws Exceptio { if ( protocol.isExpecting( CatchupClientProtocol.State.MESSAGE_TYPE ) ) { - ResponseMessageType responseMessageType = from( ((ByteBuf) msg).readByte() ); + ByteBuf buffer = (ByteBuf) msg; + byte version = buffer.readByte(); + byte messageType = buffer.readByte(); + ResponseMessageType responseMessageType = from( version, messageType ); switch ( responseMessageType ) { @@ -68,7 +71,7 @@ public void channelRead( ChannelHandlerContext ctx, Object msg ) throws Exceptio protocol.expect( CatchupClientProtocol.State.TX_STREAM_FINISHED ); break; default: - log.warn( "No handler found for message type %s", responseMessageType ); + log.warn( "No handler found for version %d and message type %s", version, responseMessageType ); } ReferenceCountUtil.release( msg ); diff --git a/enterprise/core-edge/src/main/java/org/neo4j/coreedge/catchup/CoreClient.java b/enterprise/core-edge/src/main/java/org/neo4j/coreedge/catchup/CoreClient.java index 26ce1ec2e98b7..4da083c8229e1 100644 --- a/enterprise/core-edge/src/main/java/org/neo4j/coreedge/catchup/CoreClient.java +++ b/enterprise/core-edge/src/main/java/org/neo4j/coreedge/catchup/CoreClient.java @@ -62,6 +62,7 @@ import org.neo4j.logging.LogProvider; import static java.util.Arrays.asList; +import static org.neo4j.coreedge.messaging.Message.CURRENT_VERSION; public abstract class CoreClient extends LifecycleAdapter implements StoreFileReceiver, StoreIdReceiver, StoreFileStreamingCompleteListener, TxStreamCompleteListener, TxPullResponseListener, CoreSnapshotListener @@ -91,27 +92,27 @@ public CoreClient( LogProvider logProvider, ChannelInitializer ch public void requestStore( MemberId serverAddress ) { - GetStoreRequest getStoreRequest = new GetStoreRequest(); + GetStoreRequest getStoreRequest = new GetStoreRequest( CURRENT_VERSION ); send( serverAddress, RequestMessageType.STORE, getStoreRequest ); } public void requestStoreId( MemberId serverAddress ) { - GetStoreIdRequest getStoreIdRequest = new GetStoreIdRequest(); + GetStoreIdRequest getStoreIdRequest = new GetStoreIdRequest( CURRENT_VERSION ); send( serverAddress, RequestMessageType.STORE_ID, getStoreIdRequest ); } public CompletableFuture requestCoreSnapshot( MemberId serverAddress ) { coreSnapshotFuture = new CompletableFuture<>(); - CoreSnapshotRequest coreSnapshotRequest = new CoreSnapshotRequest(); + CoreSnapshotRequest coreSnapshotRequest = new CoreSnapshotRequest( CURRENT_VERSION ); send( serverAddress, RequestMessageType.RAFT_STATE, coreSnapshotRequest ); return coreSnapshotFuture; } public void pollForTransactions( MemberId serverAddress, StoreId storeId, long lastTransactionId ) { - TxPullRequest txPullRequest = new TxPullRequest( lastTransactionId, storeId ); + TxPullRequest txPullRequest = new TxPullRequest( CURRENT_VERSION, lastTransactionId, storeId ); send( serverAddress, RequestMessageType.TX_PULL_REQUEST, txPullRequest ); pullRequestMonitor.txPullRequest( lastTransactionId ); } diff --git a/enterprise/core-edge/src/main/java/org/neo4j/coreedge/catchup/CoreToCoreClient.java b/enterprise/core-edge/src/main/java/org/neo4j/coreedge/catchup/CoreToCoreClient.java index 6494ff15e5392..c7344c0986fe9 100644 --- a/enterprise/core-edge/src/main/java/org/neo4j/coreedge/catchup/CoreToCoreClient.java +++ b/enterprise/core-edge/src/main/java/org/neo4j/coreedge/catchup/CoreToCoreClient.java @@ -26,6 +26,7 @@ import io.netty.handler.timeout.IdleStateHandler; import java.util.concurrent.TimeUnit; +import java.util.function.Predicate; import org.neo4j.coreedge.catchup.storecopy.FileContentHandler; import org.neo4j.coreedge.catchup.storecopy.FileHeaderHandler; @@ -39,10 +40,13 @@ import org.neo4j.coreedge.discovery.CoreTopologyService; import org.neo4j.coreedge.logging.ExceptionLoggingHandler; import org.neo4j.coreedge.messaging.IdleChannelReaperHandler; +import org.neo4j.coreedge.messaging.Message; import org.neo4j.coreedge.messaging.NonBlockingChannels; import org.neo4j.kernel.monitoring.Monitors; import org.neo4j.logging.LogProvider; +import static org.neo4j.coreedge.messaging.Message.CURRENT_VERSION; + public class CoreToCoreClient extends CoreClient { public CoreToCoreClient( LogProvider logProvider, ChannelInitializer channelInitializer, Monitors monitors, @@ -89,12 +93,13 @@ protected void initChannel( SocketChannel ch ) throws Exception pipeline.addLast( owner.decoders( protocol ) ); - pipeline.addLast( new TxPullResponseHandler( protocol, owner ) ); - pipeline.addLast( new CoreSnapshotResponseHandler( protocol, owner ) ); - pipeline.addLast( new StoreCopyFinishedResponseHandler( protocol, owner ) ); - pipeline.addLast( new TxStreamFinishedResponseHandler( protocol, owner ) ); - pipeline.addLast( new FileHeaderHandler( protocol, logProvider ) ); - pipeline.addLast( new FileContentHandler( protocol, owner ) ); + Predicate versionChecker = (m) -> m.version() == CURRENT_VERSION; + pipeline.addLast( new TxPullResponseHandler( versionChecker, protocol, owner, logProvider ) ); + pipeline.addLast( new CoreSnapshotResponseHandler( versionChecker, protocol, owner, logProvider ) ); + pipeline.addLast( new StoreCopyFinishedResponseHandler( versionChecker, protocol, owner, logProvider ) ); + pipeline.addLast( new TxStreamFinishedResponseHandler( versionChecker, protocol, owner, logProvider ) ); + pipeline.addLast( new FileHeaderHandler( versionChecker, protocol, logProvider ) ); + pipeline.addLast( new FileContentHandler( versionChecker, protocol, owner, logProvider ) ); pipeline.addLast( new IdleStateHandler( 0, 0, 2, TimeUnit.MINUTES) ); pipeline.addLast( new IdleChannelReaperHandler(nonBlockingChannels)); diff --git a/enterprise/core-edge/src/main/java/org/neo4j/coreedge/catchup/RequestMessageType.java b/enterprise/core-edge/src/main/java/org/neo4j/coreedge/catchup/RequestMessageType.java index 7bcb77b3f7a70..f9ef6aa7b27ed 100644 --- a/enterprise/core-edge/src/main/java/org/neo4j/coreedge/catchup/RequestMessageType.java +++ b/enterprise/core-edge/src/main/java/org/neo4j/coreedge/catchup/RequestMessageType.java @@ -21,28 +21,33 @@ import org.neo4j.coreedge.messaging.Message; -import static java.lang.String.format; - public enum RequestMessageType implements Message { - TX_PULL_REQUEST( (byte) 1 ), - STORE( (byte) 2 ), - RAFT_STATE( (byte) 3 ), - STORE_ID( (byte) 4 ), - UNKNOWN( (byte) 404 ); + TX_PULL_REQUEST( CURRENT_VERSION, (byte) 1 ), + STORE( CURRENT_VERSION, (byte) 2 ), + RAFT_STATE( CURRENT_VERSION, (byte) 3 ), + STORE_ID( CURRENT_VERSION, (byte) 4 ), + UNKNOWN( CURRENT_VERSION, (byte) 404 ); + private byte version; private byte messageType; - RequestMessageType( byte messageType ) + RequestMessageType( byte version, byte messageType ) { + this.version = version; this.messageType = messageType; } - public static RequestMessageType from( byte b ) + public static RequestMessageType from( byte version, byte messageType ) { + if ( version != CURRENT_VERSION ) + { + return UNKNOWN; + } + for ( RequestMessageType responseMessageType : values() ) { - if ( responseMessageType.messageType == b ) + if ( responseMessageType.messageType == messageType ) { return responseMessageType; } @@ -50,6 +55,12 @@ public static RequestMessageType from( byte b ) return UNKNOWN; } + @Override + public byte version() + { + return version; + } + public byte messageType() { return messageType; @@ -58,6 +69,6 @@ public byte messageType() @Override public String toString() { - return format( "RequestMessageType{messageType=%s}", messageType ); + return "RequestMessageType{" + "version=" + version + ", messageType=" + messageType + '}'; } } diff --git a/enterprise/core-edge/src/main/java/org/neo4j/coreedge/catchup/RequestMessageTypeEncoder.java b/enterprise/core-edge/src/main/java/org/neo4j/coreedge/catchup/RequestMessageTypeEncoder.java index 236114f6a122f..253b6248bf3a7 100644 --- a/enterprise/core-edge/src/main/java/org/neo4j/coreedge/catchup/RequestMessageTypeEncoder.java +++ b/enterprise/core-edge/src/main/java/org/neo4j/coreedge/catchup/RequestMessageTypeEncoder.java @@ -19,18 +19,19 @@ */ package org.neo4j.coreedge.catchup; -import java.util.List; - import io.netty.buffer.ByteBuf; import io.netty.channel.ChannelHandlerContext; import io.netty.handler.codec.MessageToMessageEncoder; +import java.util.List; + public class RequestMessageTypeEncoder extends MessageToMessageEncoder { @Override protected void encode( ChannelHandlerContext ctx, RequestMessageType request, List out ) throws Exception { ByteBuf encoded = ctx.alloc().buffer(); + encoded.writeByte( request.version() ); encoded.writeByte( request.messageType() ); out.add( encoded ); } diff --git a/enterprise/core-edge/src/main/java/org/neo4j/coreedge/catchup/ResponseMessageType.java b/enterprise/core-edge/src/main/java/org/neo4j/coreedge/catchup/ResponseMessageType.java index 1e2c1867a9f17..16a130d31ece1 100644 --- a/enterprise/core-edge/src/main/java/org/neo4j/coreedge/catchup/ResponseMessageType.java +++ b/enterprise/core-edge/src/main/java/org/neo4j/coreedge/catchup/ResponseMessageType.java @@ -19,30 +19,37 @@ */ package org.neo4j.coreedge.catchup; -import static java.lang.String.format; +import org.neo4j.coreedge.messaging.Message; -public enum ResponseMessageType +public enum ResponseMessageType implements Message { - TX( (byte) 1 ), - STORE_ID( (byte) 2 ), - FILE( (byte) 3 ), - STORE_COPY_FINISHED( (byte) 4 ), - CORE_SNAPSHOT( (byte) 5 ), - TX_STREAM_FINISHED( (byte) 6 ), - UNKNOWN( (byte) 200 ),; + TX( CURRENT_VERSION, (byte) 1 ), + STORE_ID( CURRENT_VERSION, (byte) 2 ), + FILE( CURRENT_VERSION, (byte) 3 ), + STORE_COPY_FINISHED( CURRENT_VERSION, (byte) 4 ), + CORE_SNAPSHOT( CURRENT_VERSION, (byte) 5 ), + TX_STREAM_FINISHED( CURRENT_VERSION, (byte) 6 ), + UNKNOWN( CURRENT_VERSION, (byte) 200 ),; + private byte version; private byte messageType; - ResponseMessageType( byte messageType ) + ResponseMessageType( byte version, byte messageType ) { + this.version = version; this.messageType = messageType; } - public static ResponseMessageType from( byte b ) + public static ResponseMessageType from( byte version, byte messageType ) { + if ( version != CURRENT_VERSION ) + { + return UNKNOWN; + } + for ( ResponseMessageType responseMessageType : values() ) { - if ( responseMessageType.messageType == b ) + if ( responseMessageType.messageType == messageType ) { return responseMessageType; } @@ -50,6 +57,12 @@ public static ResponseMessageType from( byte b ) return UNKNOWN; } + @Override + public byte version() + { + return version; + } + public byte messageType() { return messageType; @@ -58,6 +71,6 @@ public byte messageType() @Override public String toString() { - return format( "ResponseMessageType{messageType=%s}", messageType ); + return "ResponseMessageType{" + "version=" + version + ", messageType=" + messageType + '}'; } } diff --git a/enterprise/core-edge/src/main/java/org/neo4j/coreedge/catchup/ResponseMessageTypeEncoder.java b/enterprise/core-edge/src/main/java/org/neo4j/coreedge/catchup/ResponseMessageTypeEncoder.java index 8171909967626..56efe40194eb3 100644 --- a/enterprise/core-edge/src/main/java/org/neo4j/coreedge/catchup/ResponseMessageTypeEncoder.java +++ b/enterprise/core-edge/src/main/java/org/neo4j/coreedge/catchup/ResponseMessageTypeEncoder.java @@ -31,6 +31,7 @@ public class ResponseMessageTypeEncoder extends MessageToMessageEncoder out ) throws Exception { ByteBuf encoded = ctx.alloc().buffer(); + encoded.writeByte( response.version() ); encoded.writeByte( response.messageType() ); out.add( encoded ); } diff --git a/enterprise/core-edge/src/main/java/org/neo4j/coreedge/catchup/ServerMessageTypeHandler.java b/enterprise/core-edge/src/main/java/org/neo4j/coreedge/catchup/ServerMessageTypeHandler.java index 4ea9f097859d6..8b35cc13dcb97 100644 --- a/enterprise/core-edge/src/main/java/org/neo4j/coreedge/catchup/ServerMessageTypeHandler.java +++ b/enterprise/core-edge/src/main/java/org/neo4j/coreedge/catchup/ServerMessageTypeHandler.java @@ -44,7 +44,10 @@ public void channelRead( ChannelHandlerContext ctx, Object msg ) throws Exceptio { if ( protocol.isExpecting( CatchupServerProtocol.State.MESSAGE_TYPE ) ) { - RequestMessageType requestMessageType = RequestMessageType.from( ((ByteBuf) msg).readByte() ); + ByteBuf buffer = (ByteBuf) msg; + byte version = buffer.readByte(); + byte messageType = buffer.readByte(); + RequestMessageType requestMessageType = RequestMessageType.from( version, messageType ); if ( requestMessageType.equals( RequestMessageType.TX_PULL_REQUEST ) ) { @@ -64,7 +67,7 @@ else if ( requestMessageType.equals( RequestMessageType.RAFT_STATE ) ) } else { - log.warn( "No handler found for message type %s", requestMessageType ); + log.warn( "No handler found for version %d and message type %s", version, requestMessageType ); } ReferenceCountUtil.release( msg ); diff --git a/enterprise/core-edge/src/main/java/org/neo4j/coreedge/catchup/SimpleRequestDecoder.java b/enterprise/core-edge/src/main/java/org/neo4j/coreedge/catchup/SimpleRequestDecoder.java index 6c959b9a9e76e..2cb4ea5cbceee 100644 --- a/enterprise/core-edge/src/main/java/org/neo4j/coreedge/catchup/SimpleRequestDecoder.java +++ b/enterprise/core-edge/src/main/java/org/neo4j/coreedge/catchup/SimpleRequestDecoder.java @@ -24,15 +24,15 @@ import io.netty.handler.codec.MessageToMessageDecoder; import java.util.List; +import java.util.function.Function; import org.neo4j.coreedge.messaging.Message; -import org.neo4j.function.Factory; class SimpleRequestDecoder extends MessageToMessageDecoder { - private Factory factory; + private Function factory; - SimpleRequestDecoder( Factory factory ) + SimpleRequestDecoder( Function factory ) { this.factory = factory; } @@ -40,6 +40,7 @@ class SimpleRequestDecoder extends MessageToMessageDecoder @Override protected void decode( ChannelHandlerContext ctx, ByteBuf msg, List out ) throws Exception { - out.add( factory.newInstance() ); + byte version = msg.readByte(); + out.add( factory.apply( version ) ); } } diff --git a/enterprise/core-edge/src/main/java/org/neo4j/coreedge/catchup/storecopy/EdgeToCoreClient.java b/enterprise/core-edge/src/main/java/org/neo4j/coreedge/catchup/storecopy/EdgeToCoreClient.java index 395bbcb91953e..7d51d2419b841 100644 --- a/enterprise/core-edge/src/main/java/org/neo4j/coreedge/catchup/storecopy/EdgeToCoreClient.java +++ b/enterprise/core-edge/src/main/java/org/neo4j/coreedge/catchup/storecopy/EdgeToCoreClient.java @@ -26,6 +26,7 @@ import io.netty.handler.timeout.IdleStateHandler; import java.util.concurrent.TimeUnit; +import java.util.function.Predicate; import org.neo4j.coreedge.catchup.CatchupClientProtocol; import org.neo4j.coreedge.catchup.ClientMessageTypeHandler; @@ -38,10 +39,13 @@ import org.neo4j.coreedge.discovery.TopologyService; import org.neo4j.coreedge.logging.ExceptionLoggingHandler; import org.neo4j.coreedge.messaging.IdleChannelReaperHandler; +import org.neo4j.coreedge.messaging.Message; import org.neo4j.coreedge.messaging.NonBlockingChannels; import org.neo4j.kernel.monitoring.Monitors; import org.neo4j.logging.LogProvider; +import static org.neo4j.coreedge.messaging.Message.CURRENT_VERSION; + public class EdgeToCoreClient extends CoreClient { public EdgeToCoreClient( LogProvider logProvider, ChannelInitializer channelInitializer, Monitors monitors, @@ -88,12 +92,13 @@ protected void initChannel( SocketChannel ch ) throws Exception pipeline.addLast( owner.decoders( protocol ) ); - pipeline.addLast( new GetStoreIdResponseHandler( protocol, owner ) ); - pipeline.addLast( new TxPullResponseHandler( protocol, owner ) ); - pipeline.addLast( new StoreCopyFinishedResponseHandler( protocol, owner ) ); - pipeline.addLast( new TxStreamFinishedResponseHandler( protocol, owner ) ); - pipeline.addLast( new FileHeaderHandler( protocol, logProvider ) ); - pipeline.addLast( new FileContentHandler( protocol, owner ) ); + Predicate versionChecker = ( m ) -> m.version() == CURRENT_VERSION; + pipeline.addLast( new GetStoreIdResponseHandler( versionChecker, protocol, owner, logProvider ) ); + pipeline.addLast( new TxPullResponseHandler( versionChecker, protocol, owner, logProvider ) ); + pipeline.addLast( new StoreCopyFinishedResponseHandler( versionChecker, protocol, owner, logProvider ) ); + pipeline.addLast( new TxStreamFinishedResponseHandler( versionChecker, protocol, owner, logProvider ) ); + pipeline.addLast( new FileHeaderHandler( versionChecker, protocol, logProvider ) ); + pipeline.addLast( new FileContentHandler( versionChecker, protocol, owner, logProvider ) ); pipeline.addLast( new IdleStateHandler( 0, 0, 2, TimeUnit.MINUTES) ); pipeline.addLast( new IdleChannelReaperHandler( nonBlockingChannels ) ); diff --git a/enterprise/core-edge/src/main/java/org/neo4j/coreedge/catchup/storecopy/FileContent.java b/enterprise/core-edge/src/main/java/org/neo4j/coreedge/catchup/storecopy/FileContent.java index 41bc035ab7a72..49e2008543c76 100644 --- a/enterprise/core-edge/src/main/java/org/neo4j/coreedge/catchup/storecopy/FileContent.java +++ b/enterprise/core-edge/src/main/java/org/neo4j/coreedge/catchup/storecopy/FileContent.java @@ -24,12 +24,16 @@ import java.io.IOException; import java.io.OutputStream; -class FileContent implements AutoCloseable +import org.neo4j.coreedge.messaging.BaseMessage; + +class FileContent extends BaseMessage implements AutoCloseable { private final ByteBuf msg; FileContent( ByteBuf msg ) { + // this is an internal message so we can use the current version + super( CURRENT_VERSION ); msg.retain(); this.msg = msg; } diff --git a/enterprise/core-edge/src/main/java/org/neo4j/coreedge/catchup/storecopy/FileContentDecoder.java b/enterprise/core-edge/src/main/java/org/neo4j/coreedge/catchup/storecopy/FileContentDecoder.java index 67fed8d916e4c..6531e36845d1a 100644 --- a/enterprise/core-edge/src/main/java/org/neo4j/coreedge/catchup/storecopy/FileContentDecoder.java +++ b/enterprise/core-edge/src/main/java/org/neo4j/coreedge/catchup/storecopy/FileContentDecoder.java @@ -25,6 +25,10 @@ import java.util.List; +import org.neo4j.coreedge.messaging.Message; + +import static org.neo4j.coreedge.messaging.Message.CURRENT_VERSION; + public class FileContentDecoder extends MessageToMessageDecoder { @Override diff --git a/enterprise/core-edge/src/main/java/org/neo4j/coreedge/catchup/storecopy/FileContentHandler.java b/enterprise/core-edge/src/main/java/org/neo4j/coreedge/catchup/storecopy/FileContentHandler.java index f4d86482449c9..cca352d8bc93a 100644 --- a/enterprise/core-edge/src/main/java/org/neo4j/coreedge/catchup/storecopy/FileContentHandler.java +++ b/enterprise/core-edge/src/main/java/org/neo4j/coreedge/catchup/storecopy/FileContentHandler.java @@ -20,13 +20,16 @@ package org.neo4j.coreedge.catchup.storecopy; import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.SimpleChannelInboundHandler; import java.io.OutputStream; +import java.util.function.Predicate; import org.neo4j.coreedge.catchup.CatchupClientProtocol; +import org.neo4j.coreedge.VersionCheckerChannelInboundHandler; +import org.neo4j.coreedge.messaging.Message; +import org.neo4j.logging.LogProvider; -public class FileContentHandler extends SimpleChannelInboundHandler +public class FileContentHandler extends VersionCheckerChannelInboundHandler { private final CatchupClientProtocol protocol; private long expectedBytes = 0; @@ -34,8 +37,10 @@ public class FileContentHandler extends SimpleChannelInboundHandler versionChecker, CatchupClientProtocol protocol, + StoreFileReceiver location, LogProvider logProvider ) { + super( versionChecker, logProvider ); this.protocol = protocol; this.location = location; } @@ -47,7 +52,7 @@ void setExpectedFile( FileHeader fileHeader ) } @Override - protected void channelRead0( ChannelHandlerContext ctx, FileContent fileContent ) throws Exception + protected void doChannelRead0( ChannelHandlerContext ctx, FileContent fileContent ) throws Exception { try ( FileContent content = fileContent; OutputStream outputStream = location.getStoreFileStreams().createStream( destination ) ) diff --git a/enterprise/core-edge/src/main/java/org/neo4j/coreedge/catchup/storecopy/FileHeader.java b/enterprise/core-edge/src/main/java/org/neo4j/coreedge/catchup/storecopy/FileHeader.java index 90ed51ae8ae00..f39dbc0ce595f 100644 --- a/enterprise/core-edge/src/main/java/org/neo4j/coreedge/catchup/storecopy/FileHeader.java +++ b/enterprise/core-edge/src/main/java/org/neo4j/coreedge/catchup/storecopy/FileHeader.java @@ -19,13 +19,16 @@ */ package org.neo4j.coreedge.catchup.storecopy; -public class FileHeader +import org.neo4j.coreedge.messaging.BaseMessage; + +public class FileHeader extends BaseMessage { private final String fileName; private final long fileLength; - public FileHeader( String fileName, long fileLength ) + public FileHeader( byte version, String fileName, long fileLength ) { + super( version ); this.fileName = fileName; this.fileLength = fileLength; } @@ -43,6 +46,6 @@ public String fileName() @Override public String toString() { - return String.format( "FileHeader{fileName='%s', fileLength=%d}", fileName, fileLength ); + return "FileHeader{" + "fileName='" + fileName + '\'' + ", fileLength=" + fileLength + '}'; } } diff --git a/enterprise/core-edge/src/main/java/org/neo4j/coreedge/catchup/storecopy/FileHeaderDecoder.java b/enterprise/core-edge/src/main/java/org/neo4j/coreedge/catchup/storecopy/FileHeaderDecoder.java index a0da4c8088978..51a115333ef79 100644 --- a/enterprise/core-edge/src/main/java/org/neo4j/coreedge/catchup/storecopy/FileHeaderDecoder.java +++ b/enterprise/core-edge/src/main/java/org/neo4j/coreedge/catchup/storecopy/FileHeaderDecoder.java @@ -30,10 +30,11 @@ public class FileHeaderDecoder extends MessageToMessageDecoder @Override protected void decode( ChannelHandlerContext ctx, ByteBuf msg, List out ) throws Exception { + byte version = msg.readByte(); int nameLength = msg.readInt(); byte[] name = new byte[nameLength]; msg.readBytes( name ); long fileLength = msg.readLong(); - out.add( new FileHeader( new String( name ), fileLength ) ); + out.add( new FileHeader( version, new String( name ), fileLength ) ); } } diff --git a/enterprise/core-edge/src/main/java/org/neo4j/coreedge/catchup/storecopy/FileHeaderEncoder.java b/enterprise/core-edge/src/main/java/org/neo4j/coreedge/catchup/storecopy/FileHeaderEncoder.java index 8365b7063fd57..7a939cb72bb99 100644 --- a/enterprise/core-edge/src/main/java/org/neo4j/coreedge/catchup/storecopy/FileHeaderEncoder.java +++ b/enterprise/core-edge/src/main/java/org/neo4j/coreedge/catchup/storecopy/FileHeaderEncoder.java @@ -31,13 +31,11 @@ public class FileHeaderEncoder extends MessageToMessageEncoder protected void encode( ChannelHandlerContext ctx, FileHeader msg, List out ) throws Exception { ByteBuf buffer = ctx.alloc().buffer(); - + buffer.writeByte( msg.version() ); String name = msg.fileName(); - buffer.writeInt( name.length() ); buffer.writeBytes( name.getBytes() ); buffer.writeLong( msg.fileLength() ); - out.add( buffer ); } } diff --git a/enterprise/core-edge/src/main/java/org/neo4j/coreedge/catchup/storecopy/FileHeaderHandler.java b/enterprise/core-edge/src/main/java/org/neo4j/coreedge/catchup/storecopy/FileHeaderHandler.java index 4a204198090e8..0eeef361fdfd4 100644 --- a/enterprise/core-edge/src/main/java/org/neo4j/coreedge/catchup/storecopy/FileHeaderHandler.java +++ b/enterprise/core-edge/src/main/java/org/neo4j/coreedge/catchup/storecopy/FileHeaderHandler.java @@ -20,27 +20,31 @@ package org.neo4j.coreedge.catchup.storecopy; import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.SimpleChannelInboundHandler; + +import java.util.function.Predicate; import org.neo4j.coreedge.catchup.CatchupClientProtocol; +import org.neo4j.coreedge.VersionCheckerChannelInboundHandler; +import org.neo4j.coreedge.messaging.Message; import org.neo4j.logging.Log; import org.neo4j.logging.LogProvider; import static org.neo4j.coreedge.catchup.CatchupClientProtocol.State; -public class FileHeaderHandler extends SimpleChannelInboundHandler +public class FileHeaderHandler extends VersionCheckerChannelInboundHandler { private final CatchupClientProtocol protocol; private final Log log; - public FileHeaderHandler( CatchupClientProtocol protocol, LogProvider logProvider ) + public FileHeaderHandler( Predicate versionChecker, CatchupClientProtocol protocol, LogProvider logProvider ) { + super( versionChecker, logProvider ); this.protocol = protocol; this.log = logProvider.getLog( getClass() ); } @Override - protected void channelRead0( ChannelHandlerContext ctx, FileHeader msg ) throws Exception + protected void doChannelRead0( ChannelHandlerContext ctx, FileHeader msg ) throws Exception { log.info( "Receiving file: %s (%d bytes)", msg.fileName(), msg.fileLength() ); ctx.pipeline().get( FileContentHandler.class ).setExpectedFile( msg ); diff --git a/enterprise/core-edge/src/main/java/org/neo4j/coreedge/catchup/storecopy/GetStoreIdRequest.java b/enterprise/core-edge/src/main/java/org/neo4j/coreedge/catchup/storecopy/GetStoreIdRequest.java index e2f316198f3c5..4efeff1ee0564 100644 --- a/enterprise/core-edge/src/main/java/org/neo4j/coreedge/catchup/storecopy/GetStoreIdRequest.java +++ b/enterprise/core-edge/src/main/java/org/neo4j/coreedge/catchup/storecopy/GetStoreIdRequest.java @@ -20,9 +20,15 @@ package org.neo4j.coreedge.catchup.storecopy; import org.neo4j.coreedge.catchup.RequestMessageType; +import org.neo4j.coreedge.messaging.BaseMessage; import org.neo4j.coreedge.messaging.Message; -public class GetStoreIdRequest implements Message +public class GetStoreIdRequest extends BaseMessage { public static final RequestMessageType MESSAGE_TYPE = RequestMessageType.STORE_ID; + + public GetStoreIdRequest( byte version ) + { + super( version ); + } } diff --git a/enterprise/core-edge/src/main/java/org/neo4j/coreedge/catchup/storecopy/GetStoreIdRequestEncoder.java b/enterprise/core-edge/src/main/java/org/neo4j/coreedge/catchup/storecopy/GetStoreIdRequestEncoder.java index cf40339eebdab..a0f12bc18d381 100644 --- a/enterprise/core-edge/src/main/java/org/neo4j/coreedge/catchup/storecopy/GetStoreIdRequestEncoder.java +++ b/enterprise/core-edge/src/main/java/org/neo4j/coreedge/catchup/storecopy/GetStoreIdRequestEncoder.java @@ -31,6 +31,7 @@ class GetStoreIdRequestEncoder extends MessageToMessageEncoder out ) throws Exception { ByteBuf buffer = ctx.alloc().buffer(); + buffer.writeByte( msg.version() ); buffer.writeByte( 0 ); out.add( buffer ); } diff --git a/enterprise/core-edge/src/main/java/org/neo4j/coreedge/catchup/storecopy/GetStoreIdRequestHandler.java b/enterprise/core-edge/src/main/java/org/neo4j/coreedge/catchup/storecopy/GetStoreIdRequestHandler.java index 54f0fd0f973bc..1d27a2168fb0a 100644 --- a/enterprise/core-edge/src/main/java/org/neo4j/coreedge/catchup/storecopy/GetStoreIdRequestHandler.java +++ b/enterprise/core-edge/src/main/java/org/neo4j/coreedge/catchup/storecopy/GetStoreIdRequestHandler.java @@ -20,35 +20,41 @@ package org.neo4j.coreedge.catchup.storecopy; import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.SimpleChannelInboundHandler; +import java.util.function.Predicate; import java.util.function.Supplier; import org.neo4j.coreedge.catchup.CatchupServerProtocol; import org.neo4j.coreedge.catchup.ResponseMessageType; +import org.neo4j.coreedge.VersionCheckerChannelInboundHandler; +import org.neo4j.coreedge.identity.StoreId; +import org.neo4j.coreedge.messaging.Message; import org.neo4j.coreedge.messaging.NetworkFlushableByteBuf; import org.neo4j.coreedge.messaging.marsalling.storeid.StoreIdMarshal; -import org.neo4j.coreedge.identity.StoreId; +import org.neo4j.logging.LogProvider; import static org.neo4j.coreedge.catchup.CatchupServerProtocol.State; -public class GetStoreIdRequestHandler extends SimpleChannelInboundHandler +public class GetStoreIdRequestHandler extends VersionCheckerChannelInboundHandler { private final CatchupServerProtocol protocol; private final Supplier storeIdSupplier; - public GetStoreIdRequestHandler( CatchupServerProtocol protocol, Supplier storeIdSupplier ) + public GetStoreIdRequestHandler( Predicate versionChecker, CatchupServerProtocol protocol, + Supplier storeIdSupplier, LogProvider logProvider ) { + super( versionChecker, logProvider ); this.protocol = protocol; this.storeIdSupplier = storeIdSupplier; } @Override - protected void channelRead0( ChannelHandlerContext ctx, GetStoreIdRequest msg ) throws Exception + protected void doChannelRead0( ChannelHandlerContext ctx, GetStoreIdRequest message ) throws Exception { StoreId storeId = storeIdSupplier.get(); ctx.writeAndFlush( ResponseMessageType.STORE_ID ); NetworkFlushableByteBuf channel = new NetworkFlushableByteBuf( ctx.alloc().buffer() ); + channel.put( message.version() ); StoreIdMarshal.marshal( storeId, channel ); ctx.writeAndFlush( channel.buffer() ); protocol.expect( State.MESSAGE_TYPE ); diff --git a/enterprise/core-edge/src/main/java/org/neo4j/coreedge/catchup/storecopy/GetStoreIdResponse.java b/enterprise/core-edge/src/main/java/org/neo4j/coreedge/catchup/storecopy/GetStoreIdResponse.java index 702ab2a387fd7..ed3cefa9f7a88 100644 --- a/enterprise/core-edge/src/main/java/org/neo4j/coreedge/catchup/storecopy/GetStoreIdResponse.java +++ b/enterprise/core-edge/src/main/java/org/neo4j/coreedge/catchup/storecopy/GetStoreIdResponse.java @@ -22,13 +22,15 @@ import java.util.Objects; import org.neo4j.coreedge.identity.StoreId; +import org.neo4j.coreedge.messaging.BaseMessage; -class GetStoreIdResponse +class GetStoreIdResponse extends BaseMessage { private final StoreId storeId; - GetStoreIdResponse( StoreId storeId ) + GetStoreIdResponse( byte version, StoreId storeId ) { + super( version ); this.storeId = storeId; } @@ -48,6 +50,10 @@ public boolean equals( Object o ) { return false; } + if ( !super.equals( o ) ) + { + return false; + } GetStoreIdResponse that = (GetStoreIdResponse) o; return Objects.equals( storeId, that.storeId ); } @@ -55,7 +61,7 @@ public boolean equals( Object o ) @Override public int hashCode() { - return Objects.hash( storeId ); + return Objects.hash( super.hashCode(), storeId ); } @Override diff --git a/enterprise/core-edge/src/main/java/org/neo4j/coreedge/catchup/storecopy/GetStoreIdResponseDecoder.java b/enterprise/core-edge/src/main/java/org/neo4j/coreedge/catchup/storecopy/GetStoreIdResponseDecoder.java index b5934d5a1b6cd..87a603586e2e1 100644 --- a/enterprise/core-edge/src/main/java/org/neo4j/coreedge/catchup/storecopy/GetStoreIdResponseDecoder.java +++ b/enterprise/core-edge/src/main/java/org/neo4j/coreedge/catchup/storecopy/GetStoreIdResponseDecoder.java @@ -29,12 +29,14 @@ import org.neo4j.coreedge.messaging.NetworkReadableClosableChannelNetty4; import org.neo4j.coreedge.messaging.marsalling.storeid.StoreIdMarshal; -public class GetStoreIdResponseDecoder extends MessageToMessageDecoder +public class GetStoreIdResponseDecoder extends MessageToMessageDecoder { @Override protected void decode( ChannelHandlerContext ctx, ByteBuf msg, List out ) throws Exception { - StoreId storeId = StoreIdMarshal.unmarshal( new NetworkReadableClosableChannelNetty4( msg ) ); - out.add( new GetStoreIdResponse( storeId ) ); + NetworkReadableClosableChannelNetty4 channel = new NetworkReadableClosableChannelNetty4( msg ); + byte version = channel.get(); + StoreId storeId = StoreIdMarshal.unmarshal( channel ); + out.add( new GetStoreIdResponse( version, storeId ) ); } } diff --git a/enterprise/core-edge/src/main/java/org/neo4j/coreedge/catchup/storecopy/GetStoreIdResponseHandler.java b/enterprise/core-edge/src/main/java/org/neo4j/coreedge/catchup/storecopy/GetStoreIdResponseHandler.java index aa1d52e2e8e85..0cae468b7490a 100644 --- a/enterprise/core-edge/src/main/java/org/neo4j/coreedge/catchup/storecopy/GetStoreIdResponseHandler.java +++ b/enterprise/core-edge/src/main/java/org/neo4j/coreedge/catchup/storecopy/GetStoreIdResponseHandler.java @@ -20,23 +20,29 @@ package org.neo4j.coreedge.catchup.storecopy; import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.SimpleChannelInboundHandler; + +import java.util.function.Predicate; import org.neo4j.coreedge.catchup.CatchupClientProtocol; +import org.neo4j.coreedge.VersionCheckerChannelInboundHandler; +import org.neo4j.coreedge.messaging.Message; +import org.neo4j.logging.LogProvider; -class GetStoreIdResponseHandler extends SimpleChannelInboundHandler +class GetStoreIdResponseHandler extends VersionCheckerChannelInboundHandler { private final StoreIdReceiver storeIdReceiver; private final CatchupClientProtocol protocol; - GetStoreIdResponseHandler( CatchupClientProtocol protocol, StoreIdReceiver storeIdReceiver ) + GetStoreIdResponseHandler( Predicate versionChecker, CatchupClientProtocol protocol, + StoreIdReceiver storeIdReceiver, LogProvider logProvider ) { + super( versionChecker, logProvider ); this.protocol = protocol; this.storeIdReceiver = storeIdReceiver; } @Override - protected void channelRead0( ChannelHandlerContext ctx, final GetStoreIdResponse msg ) throws Exception + protected void doChannelRead0( ChannelHandlerContext ctx, final GetStoreIdResponse msg ) throws Exception { if ( protocol.isExpecting( CatchupClientProtocol.State.STORE_ID ) ) { diff --git a/enterprise/core-edge/src/main/java/org/neo4j/coreedge/catchup/storecopy/GetStoreRequest.java b/enterprise/core-edge/src/main/java/org/neo4j/coreedge/catchup/storecopy/GetStoreRequest.java index 2976c0e1bb478..2bd4852bf3eb6 100644 --- a/enterprise/core-edge/src/main/java/org/neo4j/coreedge/catchup/storecopy/GetStoreRequest.java +++ b/enterprise/core-edge/src/main/java/org/neo4j/coreedge/catchup/storecopy/GetStoreRequest.java @@ -19,11 +19,17 @@ */ package org.neo4j.coreedge.catchup.storecopy; +import org.neo4j.coreedge.messaging.BaseMessage; import org.neo4j.coreedge.messaging.Message; import org.neo4j.coreedge.catchup.RequestMessageType; -public class GetStoreRequest implements Message +public class GetStoreRequest extends BaseMessage { public static final RequestMessageType MESSAGE_TYPE = RequestMessageType.STORE; + + public GetStoreRequest( byte version ) + { + super( version ); + } } diff --git a/enterprise/core-edge/src/main/java/org/neo4j/coreedge/catchup/storecopy/GetStoreRequestEncoder.java b/enterprise/core-edge/src/main/java/org/neo4j/coreedge/catchup/storecopy/GetStoreRequestEncoder.java index c419c04e95dd4..dd4addb1e466c 100644 --- a/enterprise/core-edge/src/main/java/org/neo4j/coreedge/catchup/storecopy/GetStoreRequestEncoder.java +++ b/enterprise/core-edge/src/main/java/org/neo4j/coreedge/catchup/storecopy/GetStoreRequestEncoder.java @@ -31,6 +31,7 @@ public class GetStoreRequestEncoder extends MessageToMessageEncoder out ) throws Exception { ByteBuf buffer = ctx.alloc().buffer(); + buffer.writeByte( msg.version() ); buffer.writeByte( 0 ); out.add( buffer ); } diff --git a/enterprise/core-edge/src/main/java/org/neo4j/coreedge/catchup/storecopy/GetStoreRequestHandler.java b/enterprise/core-edge/src/main/java/org/neo4j/coreedge/catchup/storecopy/GetStoreRequestHandler.java index 3e799a77fbe6c..42c9f6a93b916 100644 --- a/enterprise/core-edge/src/main/java/org/neo4j/coreedge/catchup/storecopy/GetStoreRequestHandler.java +++ b/enterprise/core-edge/src/main/java/org/neo4j/coreedge/catchup/storecopy/GetStoreRequestHandler.java @@ -19,43 +19,48 @@ */ package org.neo4j.coreedge.catchup.storecopy; +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.stream.ChunkedNioStream; + import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; +import java.util.function.Predicate; import java.util.function.Supplier; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.SimpleChannelInboundHandler; -import io.netty.handler.stream.ChunkedNioStream; - +import org.neo4j.coreedge.VersionCheckerChannelInboundHandler; import org.neo4j.coreedge.catchup.CatchupServerProtocol; import org.neo4j.coreedge.catchup.ResponseMessageType; +import org.neo4j.coreedge.messaging.Message; import org.neo4j.graphdb.ResourceIterator; import org.neo4j.kernel.NeoStoreDataSource; import org.neo4j.kernel.impl.transaction.log.checkpoint.CheckPointer; import org.neo4j.kernel.impl.transaction.log.checkpoint.SimpleTriggerInfo; +import org.neo4j.logging.LogProvider; import static org.neo4j.coreedge.catchup.CatchupServerProtocol.State; +import static org.neo4j.coreedge.messaging.Message.CURRENT_VERSION; -public class GetStoreRequestHandler extends SimpleChannelInboundHandler +public class GetStoreRequestHandler extends VersionCheckerChannelInboundHandler { private final CatchupServerProtocol protocol; private final Supplier dataSource; private Supplier checkPointerSupplier; - public GetStoreRequestHandler( CatchupServerProtocol protocol, + public GetStoreRequestHandler( Predicate versionChecker, CatchupServerProtocol protocol, Supplier dataSource, - Supplier checkPointerSupplier ) + Supplier checkPointerSupplier, LogProvider logProvider ) { + super(versionChecker, logProvider ); this.protocol = protocol; this.dataSource = dataSource; this.checkPointerSupplier = checkPointerSupplier; } @Override - protected void channelRead0( ChannelHandlerContext ctx, GetStoreRequest msg ) throws Exception + protected void doChannelRead0( ChannelHandlerContext ctx, GetStoreRequest msg ) throws Exception { long lastCheckPointedTx = checkPointerSupplier.get().tryCheckPoint( new SimpleTriggerInfo( "Store copy" ) ); sendFiles( ctx ); @@ -77,13 +82,13 @@ private void sendFiles( ChannelHandlerContext ctx ) throws IOException private void sendFile( ChannelHandlerContext ctx, File file ) throws FileNotFoundException { ctx.writeAndFlush( ResponseMessageType.FILE ); - ctx.writeAndFlush( new FileHeader( file.getName(), file.length() ) ); + ctx.writeAndFlush( new FileHeader( CURRENT_VERSION, file.getName(), file.length() ) ); ctx.writeAndFlush( new ChunkedNioStream( new FileInputStream( file ).getChannel() ) ); } private void endStoreCopy( ChannelHandlerContext ctx, long lastCommittedTxBeforeStoreCopy ) { ctx.write( ResponseMessageType.STORE_COPY_FINISHED ); - ctx.writeAndFlush( new StoreCopyFinishedResponse( lastCommittedTxBeforeStoreCopy ) ); + ctx.writeAndFlush( new StoreCopyFinishedResponse( CURRENT_VERSION, lastCommittedTxBeforeStoreCopy ) ); } } diff --git a/enterprise/core-edge/src/main/java/org/neo4j/coreedge/catchup/storecopy/StoreCopyFinishedResponse.java b/enterprise/core-edge/src/main/java/org/neo4j/coreedge/catchup/storecopy/StoreCopyFinishedResponse.java index 4947882535dfb..987ee0e1121f7 100644 --- a/enterprise/core-edge/src/main/java/org/neo4j/coreedge/catchup/storecopy/StoreCopyFinishedResponse.java +++ b/enterprise/core-edge/src/main/java/org/neo4j/coreedge/catchup/storecopy/StoreCopyFinishedResponse.java @@ -19,16 +19,21 @@ */ package org.neo4j.coreedge.catchup.storecopy; -public class StoreCopyFinishedResponse +import java.util.Objects; + +import org.neo4j.coreedge.messaging.BaseMessage; + +class StoreCopyFinishedResponse extends BaseMessage { private final long lastCommittedTxBeforeStoreCopy; - public StoreCopyFinishedResponse( long lastCommittedTxBeforeStoreCopy ) + StoreCopyFinishedResponse( byte version, long lastCommittedTxBeforeStoreCopy ) { + super( version ); this.lastCommittedTxBeforeStoreCopy = lastCommittedTxBeforeStoreCopy; } - public long lastCommittedTxBeforeStoreCopy() + long lastCommittedTxBeforeStoreCopy() { return lastCommittedTxBeforeStoreCopy; } @@ -44,20 +49,17 @@ public boolean equals( Object o ) { return false; } - - StoreCopyFinishedResponse that = (StoreCopyFinishedResponse) o; - - if ( lastCommittedTxBeforeStoreCopy != that.lastCommittedTxBeforeStoreCopy ) + if ( !super.equals( o ) ) { return false; } - - return true; + StoreCopyFinishedResponse that = (StoreCopyFinishedResponse) o; + return lastCommittedTxBeforeStoreCopy == that.lastCommittedTxBeforeStoreCopy; } @Override public int hashCode() { - return (int) (lastCommittedTxBeforeStoreCopy ^ (lastCommittedTxBeforeStoreCopy >>> 32)); + return Objects.hash( super.hashCode(), lastCommittedTxBeforeStoreCopy ); } } diff --git a/enterprise/core-edge/src/main/java/org/neo4j/coreedge/catchup/storecopy/StoreCopyFinishedResponseDecoder.java b/enterprise/core-edge/src/main/java/org/neo4j/coreedge/catchup/storecopy/StoreCopyFinishedResponseDecoder.java index 4752c3184297d..d8f9128aa5ec3 100644 --- a/enterprise/core-edge/src/main/java/org/neo4j/coreedge/catchup/storecopy/StoreCopyFinishedResponseDecoder.java +++ b/enterprise/core-edge/src/main/java/org/neo4j/coreedge/catchup/storecopy/StoreCopyFinishedResponseDecoder.java @@ -30,6 +30,8 @@ public class StoreCopyFinishedResponseDecoder extends MessageToMessageDecoder out ) throws Exception { - out.add( new StoreCopyFinishedResponse( msg.readLong() ) ); + byte version = msg.readByte(); + long lastCommittedTxBeforeStoreCopy = msg.readLong(); + out.add( new StoreCopyFinishedResponse( version, lastCommittedTxBeforeStoreCopy ) ); } } diff --git a/enterprise/core-edge/src/main/java/org/neo4j/coreedge/catchup/storecopy/StoreCopyFinishedResponseEncoder.java b/enterprise/core-edge/src/main/java/org/neo4j/coreedge/catchup/storecopy/StoreCopyFinishedResponseEncoder.java index b906e70ea4613..fda009cddb261 100644 --- a/enterprise/core-edge/src/main/java/org/neo4j/coreedge/catchup/storecopy/StoreCopyFinishedResponseEncoder.java +++ b/enterprise/core-edge/src/main/java/org/neo4j/coreedge/catchup/storecopy/StoreCopyFinishedResponseEncoder.java @@ -31,6 +31,7 @@ public class StoreCopyFinishedResponseEncoder extends MessageToMessageEncoder out ) throws Exception { ByteBuf buffer = ctx.alloc().buffer(); + buffer.writeByte( msg.version() ); buffer.writeLong( msg.lastCommittedTxBeforeStoreCopy() ); out.add( buffer ); } diff --git a/enterprise/core-edge/src/main/java/org/neo4j/coreedge/catchup/storecopy/StoreCopyFinishedResponseHandler.java b/enterprise/core-edge/src/main/java/org/neo4j/coreedge/catchup/storecopy/StoreCopyFinishedResponseHandler.java index dcb4064e647e4..c6be958c70f33 100644 --- a/enterprise/core-edge/src/main/java/org/neo4j/coreedge/catchup/storecopy/StoreCopyFinishedResponseHandler.java +++ b/enterprise/core-edge/src/main/java/org/neo4j/coreedge/catchup/storecopy/StoreCopyFinishedResponseHandler.java @@ -20,26 +20,31 @@ package org.neo4j.coreedge.catchup.storecopy; import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.SimpleChannelInboundHandler; + +import java.util.function.Predicate; import org.neo4j.coreedge.catchup.CatchupClientProtocol; +import org.neo4j.coreedge.VersionCheckerChannelInboundHandler; +import org.neo4j.coreedge.messaging.Message; +import org.neo4j.logging.LogProvider; import static org.neo4j.coreedge.catchup.CatchupClientProtocol.State; -public class StoreCopyFinishedResponseHandler extends SimpleChannelInboundHandler +public class StoreCopyFinishedResponseHandler extends VersionCheckerChannelInboundHandler { private final CatchupClientProtocol protocol; private StoreFileStreamingCompleteListener storeFileStreamingCompleteListener; - public StoreCopyFinishedResponseHandler( CatchupClientProtocol protocol, - StoreFileStreamingCompleteListener storeFileStreamingCompleteListener ) + public StoreCopyFinishedResponseHandler( Predicate versionChecker, CatchupClientProtocol protocol, + StoreFileStreamingCompleteListener storeFileStreamingCompleteListener, LogProvider logProvider ) { + super( versionChecker, logProvider ); this.protocol = protocol; this.storeFileStreamingCompleteListener = storeFileStreamingCompleteListener; } @Override - protected void channelRead0( ChannelHandlerContext ctx, final StoreCopyFinishedResponse msg ) throws Exception + protected void doChannelRead0( ChannelHandlerContext ctx, final StoreCopyFinishedResponse msg ) throws Exception { storeFileStreamingCompleteListener.onFileStreamingComplete( msg.lastCommittedTxBeforeStoreCopy() ); protocol.expect( State.MESSAGE_TYPE ); diff --git a/enterprise/core-edge/src/main/java/org/neo4j/coreedge/catchup/tx/TxPullRequest.java b/enterprise/core-edge/src/main/java/org/neo4j/coreedge/catchup/tx/TxPullRequest.java index 9fe33cfd2aeeb..6451bde1c38fb 100644 --- a/enterprise/core-edge/src/main/java/org/neo4j/coreedge/catchup/tx/TxPullRequest.java +++ b/enterprise/core-edge/src/main/java/org/neo4j/coreedge/catchup/tx/TxPullRequest.java @@ -23,17 +23,19 @@ import org.neo4j.coreedge.catchup.RequestMessageType; import org.neo4j.coreedge.identity.StoreId; +import org.neo4j.coreedge.messaging.BaseMessage; import org.neo4j.coreedge.messaging.Message; -public class TxPullRequest implements Message +public class TxPullRequest extends BaseMessage { public static final RequestMessageType MESSAGE_TYPE = RequestMessageType.TX_PULL_REQUEST; private long txId; private final StoreId storeId; - public TxPullRequest( long txId, StoreId storeId ) + public TxPullRequest( byte version, long txId, StoreId storeId ) { + super( version ); this.txId = txId; this.storeId = storeId; } diff --git a/enterprise/core-edge/src/main/java/org/neo4j/coreedge/catchup/tx/TxPullRequestDecoder.java b/enterprise/core-edge/src/main/java/org/neo4j/coreedge/catchup/tx/TxPullRequestDecoder.java index bdc238406290f..183d5bb1aacb2 100644 --- a/enterprise/core-edge/src/main/java/org/neo4j/coreedge/catchup/tx/TxPullRequestDecoder.java +++ b/enterprise/core-edge/src/main/java/org/neo4j/coreedge/catchup/tx/TxPullRequestDecoder.java @@ -34,8 +34,9 @@ public class TxPullRequestDecoder extends MessageToMessageDecoder @Override protected void decode( ChannelHandlerContext ctx, ByteBuf msg, List out ) throws Exception { + byte version = msg.readByte(); long txId = msg.readLong(); StoreId storeId = StoreIdMarshal.unmarshal( new NetworkReadableClosableChannelNetty4( msg ) ); - out.add( new TxPullRequest( txId, storeId ) ); + out.add( new TxPullRequest( version, txId, storeId ) ); } } diff --git a/enterprise/core-edge/src/main/java/org/neo4j/coreedge/catchup/tx/TxPullRequestEncoder.java b/enterprise/core-edge/src/main/java/org/neo4j/coreedge/catchup/tx/TxPullRequestEncoder.java index 29e4c26e7a7ab..0b83ba97f74a4 100644 --- a/enterprise/core-edge/src/main/java/org/neo4j/coreedge/catchup/tx/TxPullRequestEncoder.java +++ b/enterprise/core-edge/src/main/java/org/neo4j/coreedge/catchup/tx/TxPullRequestEncoder.java @@ -34,6 +34,7 @@ public class TxPullRequestEncoder extends MessageToMessageEncoder protected void encode( ChannelHandlerContext ctx, TxPullRequest request, List out ) throws Exception { ByteBuf encoded = ctx.alloc().buffer(); + encoded.writeByte( request.version() ); encoded.writeLong( request.txId() ); StoreIdMarshal.marshal( request.storeId(), new NetworkFlushableChannelNetty4( encoded ) ); out.add( encoded ); diff --git a/enterprise/core-edge/src/main/java/org/neo4j/coreedge/catchup/tx/TxPullRequestHandler.java b/enterprise/core-edge/src/main/java/org/neo4j/coreedge/catchup/tx/TxPullRequestHandler.java index c3f6556310051..1381ca83ad675 100644 --- a/enterprise/core-edge/src/main/java/org/neo4j/coreedge/catchup/tx/TxPullRequestHandler.java +++ b/enterprise/core-edge/src/main/java/org/neo4j/coreedge/catchup/tx/TxPullRequestHandler.java @@ -20,14 +20,16 @@ package org.neo4j.coreedge.catchup.tx; import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.SimpleChannelInboundHandler; +import java.util.function.Predicate; import java.util.function.Supplier; import org.neo4j.coreedge.catchup.CatchupServerProtocol; import org.neo4j.coreedge.catchup.CatchupServerProtocol.State; import org.neo4j.coreedge.catchup.ResponseMessageType; +import org.neo4j.coreedge.VersionCheckerChannelInboundHandler; import org.neo4j.coreedge.identity.StoreId; +import org.neo4j.coreedge.messaging.Message; import org.neo4j.cursor.IOCursor; import org.neo4j.kernel.impl.transaction.CommittedTransactionRepresentation; import org.neo4j.kernel.impl.transaction.log.LogicalTransactionStore; @@ -37,7 +39,9 @@ import org.neo4j.logging.Log; import org.neo4j.logging.LogProvider; -public class TxPullRequestHandler extends SimpleChannelInboundHandler +import static org.neo4j.coreedge.messaging.Message.CURRENT_VERSION; + +public class TxPullRequestHandler extends VersionCheckerChannelInboundHandler { private final CatchupServerProtocol protocol; private final StoreId storeId; @@ -46,12 +50,13 @@ public class TxPullRequestHandler extends SimpleChannelInboundHandler versionChecker, CatchupServerProtocol protocol, Supplier storeIdSupplier, Supplier transactionIdStoreSupplier, Supplier logicalTransactionStoreSupplier, Monitors monitors, LogProvider logProvider ) { + super( versionChecker, logProvider ); this.protocol = protocol; this.storeId = storeIdSupplier.get(); this.transactionIdStore = transactionIdStoreSupplier.get(); @@ -61,7 +66,7 @@ public TxPullRequestHandler( CatchupServerProtocol protocol, } @Override - protected void channelRead0( ChannelHandlerContext ctx, final TxPullRequest msg ) throws Exception + protected void doChannelRead0( ChannelHandlerContext ctx, final TxPullRequest msg ) throws Exception { long startTxId = Math.max( msg.txId(), TransactionIdStore.BASE_TX_ID ); long endTxId = startTxId; @@ -85,7 +90,7 @@ else if ( transactionIdStore.getLastCommittedTransactionId() > startTxId ) ctx.write( ResponseMessageType.TX ); CommittedTransactionRepresentation tx = cursor.get(); endTxId = tx.getCommitEntry().getTxId(); - ctx.write( new TxPullResponse( storeId, tx ) ); + ctx.write( new TxPullResponse( CURRENT_VERSION, storeId, tx ) ); } ctx.flush(); } @@ -96,7 +101,7 @@ else if ( transactionIdStore.getLastCommittedTransactionId() > startTxId ) } } ctx.write( ResponseMessageType.TX_STREAM_FINISHED ); - ctx.write( new TxStreamFinishedResponse( endTxId, success ) ); + ctx.write( new TxStreamFinishedResponse( CURRENT_VERSION, endTxId, success ) ); ctx.flush(); monitor.increment(); @@ -109,4 +114,5 @@ public void exceptionCaught( ChannelHandlerContext ctx, Throwable cause ) cause.printStackTrace(); ctx.close(); } + } diff --git a/enterprise/core-edge/src/main/java/org/neo4j/coreedge/catchup/tx/TxPullResponse.java b/enterprise/core-edge/src/main/java/org/neo4j/coreedge/catchup/tx/TxPullResponse.java index d47c9d1a3aa5f..d0b93404657f5 100644 --- a/enterprise/core-edge/src/main/java/org/neo4j/coreedge/catchup/tx/TxPullResponse.java +++ b/enterprise/core-edge/src/main/java/org/neo4j/coreedge/catchup/tx/TxPullResponse.java @@ -19,16 +19,20 @@ */ package org.neo4j.coreedge.catchup.tx; +import java.util.Objects; + import org.neo4j.coreedge.identity.StoreId; +import org.neo4j.coreedge.messaging.BaseMessage; import org.neo4j.kernel.impl.transaction.CommittedTransactionRepresentation; -public class TxPullResponse +public class TxPullResponse extends BaseMessage { private final StoreId storeId; private final CommittedTransactionRepresentation tx; - public TxPullResponse( StoreId storeId, CommittedTransactionRepresentation tx ) + public TxPullResponse( byte version, StoreId storeId, CommittedTransactionRepresentation tx ) { + super( version ); this.storeId = storeId; this.tx = tx; } @@ -54,19 +58,18 @@ public boolean equals( Object o ) { return false; } - + if ( !super.equals( o ) ) + { + return false; + } TxPullResponse that = (TxPullResponse) o; - - return (storeId != null ? storeId.equals( that.storeId ) : that.storeId == null) && - (tx != null ? tx.equals( that.tx ) : that.tx == null); + return Objects.equals( storeId, that.storeId ) && Objects.equals( tx, that.tx ); } @Override public int hashCode() { - int result = storeId != null ? storeId.hashCode() : 0; - result = 31 * result + (tx != null ? tx.hashCode() : 0); - return result; + return Objects.hash( super.hashCode(), storeId, tx ); } @Override diff --git a/enterprise/core-edge/src/main/java/org/neo4j/coreedge/catchup/tx/TxPullResponseDecoder.java b/enterprise/core-edge/src/main/java/org/neo4j/coreedge/catchup/tx/TxPullResponseDecoder.java index 2002d938d1b51..e430448752853 100644 --- a/enterprise/core-edge/src/main/java/org/neo4j/coreedge/catchup/tx/TxPullResponseDecoder.java +++ b/enterprise/core-edge/src/main/java/org/neo4j/coreedge/catchup/tx/TxPullResponseDecoder.java @@ -40,6 +40,7 @@ public class TxPullResponseDecoder extends MessageToMessageDecoder protected void decode( ChannelHandlerContext ctx, ByteBuf msg, List out ) throws Exception { NetworkReadableClosableChannelNetty4 logChannel = new NetworkReadableClosableChannelNetty4( msg ); + byte version = logChannel.get(); StoreId storeId = StoreIdMarshal.unmarshal( logChannel ); LogEntryReader reader = new VersionAwareLogEntryReader<>( new RecordStorageCommandReaderFactory() ); @@ -51,7 +52,7 @@ protected void decode( ChannelHandlerContext ctx, ByteBuf msg, List out if ( tx != null ) { - out.add( new TxPullResponse( storeId, tx ) ); + out.add( new TxPullResponse( version, storeId, tx ) ); } } } diff --git a/enterprise/core-edge/src/main/java/org/neo4j/coreedge/catchup/tx/TxPullResponseEncoder.java b/enterprise/core-edge/src/main/java/org/neo4j/coreedge/catchup/tx/TxPullResponseEncoder.java index 9acb31a9a41c4..a3f4706201681 100644 --- a/enterprise/core-edge/src/main/java/org/neo4j/coreedge/catchup/tx/TxPullResponseEncoder.java +++ b/enterprise/core-edge/src/main/java/org/neo4j/coreedge/catchup/tx/TxPullResponseEncoder.java @@ -36,6 +36,7 @@ protected void encode( ChannelHandlerContext ctx, TxPullResponse response, List< { ByteBuf encoded = ctx.alloc().buffer(); NetworkFlushableByteBuf channel = new NetworkFlushableByteBuf( encoded ); + channel.put( response.version() ); StoreIdMarshal.marshal( response.storeId(), channel ); new CommittedTransactionSerializer( channel ).visit( response.tx() ); out.add( encoded ); diff --git a/enterprise/core-edge/src/main/java/org/neo4j/coreedge/catchup/tx/TxPullResponseHandler.java b/enterprise/core-edge/src/main/java/org/neo4j/coreedge/catchup/tx/TxPullResponseHandler.java index de01e1b57020e..75f92016a1e2e 100644 --- a/enterprise/core-edge/src/main/java/org/neo4j/coreedge/catchup/tx/TxPullResponseHandler.java +++ b/enterprise/core-edge/src/main/java/org/neo4j/coreedge/catchup/tx/TxPullResponseHandler.java @@ -20,24 +20,29 @@ package org.neo4j.coreedge.catchup.tx; import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.SimpleChannelInboundHandler; + +import java.util.function.Predicate; import org.neo4j.coreedge.catchup.CatchupClientProtocol; +import org.neo4j.coreedge.VersionCheckerChannelInboundHandler; +import org.neo4j.coreedge.messaging.Message; +import org.neo4j.logging.LogProvider; -public class TxPullResponseHandler extends SimpleChannelInboundHandler +public class TxPullResponseHandler extends VersionCheckerChannelInboundHandler { private final CatchupClientProtocol protocol; private final TxPullResponseListener listener; - public TxPullResponseHandler( CatchupClientProtocol protocol, - TxPullResponseListener listener ) + public TxPullResponseHandler( Predicate versionChecker, CatchupClientProtocol protocol, + TxPullResponseListener listener, LogProvider logProvider ) { + super( versionChecker, logProvider ); this.protocol = protocol; this.listener = listener; } @Override - protected void channelRead0( ChannelHandlerContext ctx, final TxPullResponse msg ) throws Exception + protected void doChannelRead0( ChannelHandlerContext ctx, final TxPullResponse msg ) throws Exception { if ( protocol.isExpecting( CatchupClientProtocol.State.TX_PULL_RESPONSE ) ) { diff --git a/enterprise/core-edge/src/main/java/org/neo4j/coreedge/catchup/tx/TxStreamFinishedResponse.java b/enterprise/core-edge/src/main/java/org/neo4j/coreedge/catchup/tx/TxStreamFinishedResponse.java index a29c863e2aa50..4afb5b788871c 100644 --- a/enterprise/core-edge/src/main/java/org/neo4j/coreedge/catchup/tx/TxStreamFinishedResponse.java +++ b/enterprise/core-edge/src/main/java/org/neo4j/coreedge/catchup/tx/TxStreamFinishedResponse.java @@ -19,13 +19,18 @@ */ package org.neo4j.coreedge.catchup.tx; -class TxStreamFinishedResponse +import java.util.Objects; + +import org.neo4j.coreedge.messaging.BaseMessage; + +class TxStreamFinishedResponse extends BaseMessage { private final long lastTransactionIdSent; private final boolean success; - TxStreamFinishedResponse( long lastTransactionIdSent, boolean success ) + TxStreamFinishedResponse( byte version, long lastTransactionIdSent, boolean success ) { + super( version ); this.lastTransactionIdSent = lastTransactionIdSent; this.success = success; } @@ -51,15 +56,17 @@ public boolean equals( Object o ) { return false; } - + if ( !super.equals( o ) ) + { + return false; + } TxStreamFinishedResponse that = (TxStreamFinishedResponse) o; - return lastTransactionIdSent == that.lastTransactionIdSent && success == that.success; } @Override public int hashCode() { - return (int) (lastTransactionIdSent ^ (lastTransactionIdSent >>> 32)); + return Objects.hash( super.hashCode(), lastTransactionIdSent, success ); } } diff --git a/enterprise/core-edge/src/main/java/org/neo4j/coreedge/catchup/tx/TxStreamFinishedResponseDecoder.java b/enterprise/core-edge/src/main/java/org/neo4j/coreedge/catchup/tx/TxStreamFinishedResponseDecoder.java index 737fe803a14de..30f7861b2e130 100644 --- a/enterprise/core-edge/src/main/java/org/neo4j/coreedge/catchup/tx/TxStreamFinishedResponseDecoder.java +++ b/enterprise/core-edge/src/main/java/org/neo4j/coreedge/catchup/tx/TxStreamFinishedResponseDecoder.java @@ -30,8 +30,9 @@ public class TxStreamFinishedResponseDecoder extends MessageToMessageDecoder out ) throws Exception { + byte version = msg.readByte(); long lastTransactionIdSent = msg.readLong(); boolean success = msg.readBoolean(); - out.add( new TxStreamFinishedResponse( lastTransactionIdSent, success ) ); + out.add( new TxStreamFinishedResponse( version, lastTransactionIdSent, success ) ); } } diff --git a/enterprise/core-edge/src/main/java/org/neo4j/coreedge/catchup/tx/TxStreamFinishedResponseEncoder.java b/enterprise/core-edge/src/main/java/org/neo4j/coreedge/catchup/tx/TxStreamFinishedResponseEncoder.java index 5169cb12d6391..33bc3472873a8 100644 --- a/enterprise/core-edge/src/main/java/org/neo4j/coreedge/catchup/tx/TxStreamFinishedResponseEncoder.java +++ b/enterprise/core-edge/src/main/java/org/neo4j/coreedge/catchup/tx/TxStreamFinishedResponseEncoder.java @@ -32,6 +32,7 @@ protected void encode( ChannelHandlerContext ctx, TxStreamFinishedResponse respo Exception { ByteBuf encoded = ctx.alloc().buffer(); + encoded.writeByte( response.version() ); encoded.writeLong( response.lastTransactionIdSent() ); encoded.writeBoolean( response.isSuccess() ); out.add( encoded ); diff --git a/enterprise/core-edge/src/main/java/org/neo4j/coreedge/catchup/tx/TxStreamFinishedResponseHandler.java b/enterprise/core-edge/src/main/java/org/neo4j/coreedge/catchup/tx/TxStreamFinishedResponseHandler.java index ae217a994aa88..e9a1f22c950ef 100644 --- a/enterprise/core-edge/src/main/java/org/neo4j/coreedge/catchup/tx/TxStreamFinishedResponseHandler.java +++ b/enterprise/core-edge/src/main/java/org/neo4j/coreedge/catchup/tx/TxStreamFinishedResponseHandler.java @@ -20,26 +20,31 @@ package org.neo4j.coreedge.catchup.tx; import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.SimpleChannelInboundHandler; + +import java.util.function.Predicate; import org.neo4j.coreedge.catchup.CatchupClientProtocol; +import org.neo4j.coreedge.VersionCheckerChannelInboundHandler; +import org.neo4j.coreedge.messaging.Message; +import org.neo4j.logging.LogProvider; import static org.neo4j.coreedge.catchup.CatchupClientProtocol.State; -public class TxStreamFinishedResponseHandler extends SimpleChannelInboundHandler +public class TxStreamFinishedResponseHandler extends VersionCheckerChannelInboundHandler { private final CatchupClientProtocol protocol; private final TxStreamCompleteListener listener; - public TxStreamFinishedResponseHandler( CatchupClientProtocol protocol, TxStreamCompleteListener - streamingListener ) + public TxStreamFinishedResponseHandler( Predicate versionChecker, CatchupClientProtocol protocol, + TxStreamCompleteListener streamingListener, LogProvider logProvider ) { + super( versionChecker, logProvider ); this.protocol = protocol; this.listener = streamingListener; } @Override - protected void channelRead0( ChannelHandlerContext ctx, TxStreamFinishedResponse msg ) throws Exception + protected void doChannelRead0( ChannelHandlerContext ctx, TxStreamFinishedResponse msg ) throws Exception { listener.onTxStreamingComplete( msg.lastTransactionIdSent(), msg.isSuccess() ); protocol.expect( State.MESSAGE_TYPE ); diff --git a/enterprise/core-edge/src/main/java/org/neo4j/coreedge/core/consensus/RaftMachine.java b/enterprise/core-edge/src/main/java/org/neo4j/coreedge/core/consensus/RaftMachine.java index 1c3fd14767354..061f37acacaab 100644 --- a/enterprise/core-edge/src/main/java/org/neo4j/coreedge/core/consensus/RaftMachine.java +++ b/enterprise/core-edge/src/main/java/org/neo4j/coreedge/core/consensus/RaftMachine.java @@ -44,6 +44,7 @@ import org.neo4j.coreedge.core.state.storage.StateStorage; import org.neo4j.coreedge.helper.VolatileFuture; import org.neo4j.coreedge.identity.MemberId; +import org.neo4j.coreedge.messaging.Message; import org.neo4j.coreedge.messaging.Outbound; import org.neo4j.kernel.impl.util.Listener; import org.neo4j.kernel.monitoring.Monitors; @@ -53,6 +54,7 @@ import static java.lang.String.format; import static java.util.Collections.singletonList; import static org.neo4j.coreedge.core.consensus.roles.Role.LEADER; +import static org.neo4j.coreedge.messaging.Message.CURRENT_VERSION; /** * Implements the Raft Consensus Algorithm. @@ -124,7 +126,7 @@ private void initTimers() timeout -> { try { - handle( new RaftMessages.Timeout.Election( myself ) ); + handle( new RaftMessages.Timeout.Election( CURRENT_VERSION, myself ) ); } catch ( IOException e ) { @@ -136,7 +138,7 @@ private void initTimers() timeout -> { try { - handle( new RaftMessages.Timeout.Heartbeat( myself ) ); + handle( new RaftMessages.Timeout.Heartbeat( CURRENT_VERSION, myself ) ); } catch ( IOException e ) { diff --git a/enterprise/core-edge/src/main/java/org/neo4j/coreedge/core/consensus/RaftMessageHandler.java b/enterprise/core-edge/src/main/java/org/neo4j/coreedge/core/consensus/RaftMessageHandler.java index 2dcb1e28af065..082f1ba2875d7 100644 --- a/enterprise/core-edge/src/main/java/org/neo4j/coreedge/core/consensus/RaftMessageHandler.java +++ b/enterprise/core-edge/src/main/java/org/neo4j/coreedge/core/consensus/RaftMessageHandler.java @@ -19,13 +19,43 @@ */ package org.neo4j.coreedge.core.consensus; -import java.io.IOException; +import io.netty.channel.ChannelHandlerContext; -import org.neo4j.coreedge.core.consensus.outcome.Outcome; -import org.neo4j.coreedge.core.consensus.state.ReadableRaftState; +import java.util.function.Predicate; +import java.util.function.Supplier; + +import org.neo4j.coreedge.VersionCheckerChannelInboundHandler; +import org.neo4j.coreedge.messaging.Inbound; +import org.neo4j.coreedge.messaging.Message; import org.neo4j.logging.Log; +import org.neo4j.logging.LogProvider; + +import static java.lang.String.format; -public interface RaftMessageHandler +class RaftMessageHandler extends VersionCheckerChannelInboundHandler { - Outcome handle( RaftMessages.RaftMessage message, ReadableRaftState context, Log log ) throws IOException; + private final Supplier> messageHandler; + private final Log log; + + RaftMessageHandler( Predicate versionChecker, + Supplier> messageHandler, LogProvider logProvider ) + { + super( versionChecker, logProvider ); + this.messageHandler = messageHandler; + this.log = logProvider.getLog( getClass() ); + } + + @Override + protected void doChannelRead0( ChannelHandlerContext channelHandlerContext, + RaftMessages.StoreIdAwareMessage storeIdAwareMessage ) throws Exception + { + try + { + messageHandler.get().handle( storeIdAwareMessage ); + } + catch ( Exception e ) + { + log.error( format( "Failed to process message %s", storeIdAwareMessage ), e ); + } + } } diff --git a/enterprise/core-edge/src/main/java/org/neo4j/coreedge/core/consensus/RaftMessages.java b/enterprise/core-edge/src/main/java/org/neo4j/coreedge/core/consensus/RaftMessages.java index c08fea6491f59..c7825a6c87683 100644 --- a/enterprise/core-edge/src/main/java/org/neo4j/coreedge/core/consensus/RaftMessages.java +++ b/enterprise/core-edge/src/main/java/org/neo4j/coreedge/core/consensus/RaftMessages.java @@ -25,11 +25,12 @@ import java.util.List; import java.util.Objects; -import org.neo4j.coreedge.messaging.Message; import org.neo4j.coreedge.core.consensus.log.RaftLogEntry; import org.neo4j.coreedge.core.replication.ReplicatedContent; import org.neo4j.coreedge.identity.MemberId; import org.neo4j.coreedge.identity.StoreId; +import org.neo4j.coreedge.messaging.BaseMessage; +import org.neo4j.coreedge.messaging.Message; import static java.lang.String.format; @@ -99,9 +100,10 @@ class Request extends BaseRaftMessage private long lastLogIndex; private long lastLogTerm; - public Request( MemberId from, long term, MemberId candidate, long lastLogIndex, long lastLogTerm ) + public Request( byte version, MemberId from, long term, MemberId candidate, long lastLogIndex, + long lastLogTerm ) { - super( from, Type.VOTE_REQUEST ); + super( version, from, Type.VOTE_REQUEST ); this.term = term; this.candidate = candidate; this.lastLogIndex = lastLogIndex; @@ -124,21 +126,19 @@ public boolean equals( Object o ) { return false; } + if ( !super.equals( o ) ) + { + return false; + } Request request = (Request) o; - return lastLogIndex == request.lastLogIndex && - lastLogTerm == request.lastLogTerm && - term == request.term && - candidate.equals( request.candidate ); + return term == request.term && lastLogIndex == request.lastLogIndex && + lastLogTerm == request.lastLogTerm && Objects.equals( candidate, request.candidate ); } @Override public int hashCode() { - int result = (int) term; - result = 31 * result + candidate.hashCode(); - result = 31 * result + (int) (lastLogIndex ^ (lastLogIndex >>> 32)); - result = 31 * result + (int) (lastLogTerm ^ (lastLogTerm >>> 32)); - return result; + return Objects.hash( super.hashCode(), term, candidate, lastLogIndex, lastLogTerm ); } @Override @@ -169,9 +169,9 @@ class Response extends BaseRaftMessage private long term; private boolean voteGranted; - public Response( MemberId from, long term, boolean voteGranted ) + public Response( byte version, MemberId from, long term, boolean voteGranted ) { - super( from, Type.VOTE_RESPONSE ); + super( version, from, Type.VOTE_RESPONSE ); this.term = term; this.voteGranted = voteGranted; } @@ -187,19 +187,18 @@ public boolean equals( Object o ) { return false; } - + if ( !super.equals( o ) ) + { + return false; + } Response response = (Response) o; - return term == response.term && voteGranted == response.voteGranted; - } @Override public int hashCode() { - int result = (int) term; - result = 31 * result + (voteGranted ? 1 : 0); - return result; + return Objects.hash( super.hashCode(), term, voteGranted ); } @Override @@ -230,10 +229,10 @@ class Request extends BaseRaftMessage private RaftLogEntry[] entries; private long leaderCommit; - public Request( MemberId from, long leaderTerm, long prevLogIndex, long prevLogTerm, - RaftLogEntry[] entries, long leaderCommit ) + public Request( byte version, MemberId from, long leaderTerm, long prevLogIndex, long prevLogTerm, + RaftLogEntry[] entries, long leaderCommit ) { - super( from, Type.APPEND_ENTRIES_REQUEST ); + super( version, from, Type.APPEND_ENTRIES_REQUEST ); Objects.requireNonNull( entries ); assert !((prevLogIndex == -1 && prevLogTerm != -1) || (prevLogTerm == -1 && prevLogIndex != -1)) : format( "prevLogIndex was %d and prevLogTerm was %d", prevLogIndex, prevLogTerm ); @@ -280,18 +279,20 @@ public boolean equals( Object o ) { return false; } + if ( !super.equals( o ) ) + { + return false; + } Request request = (Request) o; - return Objects.equals( leaderTerm, request.leaderTerm ) && - Objects.equals( prevLogIndex, request.prevLogIndex ) && - Objects.equals( prevLogTerm, request.prevLogTerm ) && - Objects.equals( leaderCommit, request.leaderCommit ) && + return leaderTerm == request.leaderTerm && prevLogIndex == request.prevLogIndex && + prevLogTerm == request.prevLogTerm && leaderCommit == request.leaderCommit && Arrays.equals( entries, request.entries ); } @Override public int hashCode() { - return Objects.hash( leaderTerm, prevLogIndex, prevLogTerm, entries, leaderCommit ); + return Objects.hash( super.hashCode(), leaderTerm, prevLogIndex, prevLogTerm, entries, leaderCommit ); } @Override @@ -310,9 +311,10 @@ class Response extends BaseRaftMessage private long matchIndex; private long appendIndex; - public Response( MemberId from, long term, boolean success, long matchIndex, long appendIndex ) + public Response( byte version, MemberId from, long term, boolean success, long matchIndex, + long appendIndex ) { - super( from, Type.APPEND_ENTRIES_RESPONSE ); + super( version, from, Type.APPEND_ENTRIES_RESPONSE ); this.term = term; this.success = success; this.matchIndex = matchIndex; @@ -382,9 +384,9 @@ class Heartbeat extends BaseRaftMessage private long commitIndex; private long commitIndexTerm; - public Heartbeat( MemberId from, long leaderTerm, long commitIndex, long commitIndexTerm ) + public Heartbeat( byte version, MemberId from, long leaderTerm, long commitIndex, long commitIndexTerm ) { - super( from, Type.HEARTBEAT ); + super( version, from, Type.HEARTBEAT ); this.leaderTerm = leaderTerm; this.commitIndex = commitIndex; this.commitIndexTerm = commitIndexTerm; @@ -420,22 +422,15 @@ public boolean equals( Object o ) { return false; } - Heartbeat heartbeat = (Heartbeat) o; - - return leaderTerm == heartbeat.leaderTerm && - commitIndex == heartbeat.commitIndex && - commitIndexTerm == heartbeat.commitIndexTerm; + return leaderTerm == heartbeat.leaderTerm && commitIndex == heartbeat.commitIndex && + commitIndexTerm == heartbeat.commitIndexTerm; } @Override public int hashCode() { - int result = super.hashCode(); - result = 31 * result + (int) (leaderTerm ^ (leaderTerm >>> 32)); - result = 31 * result + (int) (commitIndex ^ (commitIndex >>> 32)); - result = 31 * result + (int) (commitIndexTerm ^ (commitIndexTerm >>> 32)); - return result; + return Objects.hash( super.hashCode(), leaderTerm, commitIndex, commitIndexTerm ); } @Override @@ -451,9 +446,9 @@ class LogCompactionInfo extends BaseRaftMessage private long leaderTerm; private long prevIndex; - public LogCompactionInfo( MemberId from, long leaderTerm, long prevIndex ) + public LogCompactionInfo( byte version, MemberId from, long leaderTerm, long prevIndex ) { - super( from, Type.LOG_COMPACTION_INFO ); + super( version, from, Type.LOG_COMPACTION_INFO ); this.leaderTerm = leaderTerm; this.prevIndex = prevIndex; } @@ -485,7 +480,6 @@ public boolean equals( Object o ) } LogCompactionInfo other = (LogCompactionInfo) o; - return leaderTerm == other.leaderTerm && prevIndex == other.prevIndex; } @@ -493,10 +487,7 @@ public boolean equals( Object o ) @Override public int hashCode() { - int result = super.hashCode(); - result = 31 * result + (int) (leaderTerm ^ (leaderTerm >>> 32)); - result = 31 * result + (int) (prevIndex ^ (prevIndex >>> 32)); - return result; + return Objects.hash( super.hashCode(), leaderTerm, prevIndex ); } @Override @@ -510,9 +501,9 @@ interface Timeout { class Election extends BaseRaftMessage { - public Election( MemberId from ) + public Election( byte version, MemberId from ) { - super( from, Type.ELECTION_TIMEOUT ); + super( version, from, Type.ELECTION_TIMEOUT ); } @Override @@ -524,9 +515,9 @@ public String toString() class Heartbeat extends BaseRaftMessage { - public Heartbeat( MemberId from ) + public Heartbeat( byte version, MemberId from ) { - super( from, Type.HEARTBEAT_TIMEOUT ); + super( version, from, Type.HEARTBEAT_TIMEOUT ); } @Override @@ -543,9 +534,9 @@ class Request extends BaseRaftMessage { private ReplicatedContent content; - public Request( MemberId from, ReplicatedContent content ) + public Request( byte version, MemberId from, ReplicatedContent content ) { - super( from, Type.NEW_ENTRY_REQUEST ); + super( version, from, Type.NEW_ENTRY_REQUEST ); this.content = content; } @@ -566,16 +557,19 @@ public boolean equals( Object o ) { return false; } + if ( !super.equals( o ) ) + { + return false; + } Request request = (Request) o; - - return !(content != null ? !content.equals( request.content ) : request.content != null); + return Objects.equals( content, request.content ); } @Override public int hashCode() { - return content != null ? content.hashCode() : 0; + return Objects.hash( super.hashCode(), content ); } public ReplicatedContent content() @@ -588,9 +582,9 @@ class BatchRequest extends BaseRaftMessage { private List list; - public BatchRequest( int batchSize ) + public BatchRequest( byte version, int batchSize ) { - super( null, Type.NEW_BATCH_REQUEST ); + super( version, null, Type.NEW_BATCH_REQUEST ); list = new ArrayList<>( batchSize ); } @@ -603,11 +597,18 @@ public void add( ReplicatedContent content ) public boolean equals( Object o ) { if ( this == o ) - { return true; } + { + return true; + } if ( o == null || getClass() != o.getClass() ) - { return false; } + { + return false; + } if ( !super.equals( o ) ) - { return false; } + { + return false; + } + BatchRequest batchRequest = (BatchRequest) o; return Objects.equals( list, batchRequest.list ); } @@ -645,6 +646,12 @@ public StoreIdAwareMessage( StoreId storeId, RaftMessage message ) this.message = message; } + @Override + public byte version() + { + return message.version(); + } + public StoreId storeId() { return storeId; @@ -666,6 +673,7 @@ public boolean equals( Object o ) { return false; } + StoreIdAwareMessage that = (StoreIdAwareMessage) o; return Objects.equals( storeId, that.storeId ) && Objects.equals( message, that.message ); } @@ -681,28 +689,28 @@ public String toString() { return format( "{storeId: %s, message: %s}", storeId, message ); } - } - abstract class BaseRaftMessage implements RaftMessage + abstract class BaseRaftMessage extends BaseMessage implements RaftMessage { protected MemberId from; private Type type; - BaseRaftMessage( MemberId from, Type type ) + BaseRaftMessage( byte version, MemberId from, Type type ) { + super( version ); this.from = from; this.type = type; } @Override - public MemberId from() + public final MemberId from() { return from; } @Override - public Type type() + public final Type type() { return type; } @@ -718,6 +726,10 @@ public boolean equals( Object o ) { return false; } + if ( !super.equals( o ) ) + { + return false; + } BaseRaftMessage that = (BaseRaftMessage) o; return Objects.equals( from, that.from ) && type == that.type; } @@ -725,7 +737,7 @@ public boolean equals( Object o ) @Override public int hashCode() { - return Objects.hash( from, type ); + return Objects.hash( super.hashCode(), from, type ); } } } diff --git a/enterprise/core-edge/src/main/java/org/neo4j/coreedge/core/consensus/RaftServer.java b/enterprise/core-edge/src/main/java/org/neo4j/coreedge/core/consensus/RaftServer.java index 7b4c4cabc39bf..ed96184ff709b 100644 --- a/enterprise/core-edge/src/main/java/org/neo4j/coreedge/core/consensus/RaftServer.java +++ b/enterprise/core-edge/src/main/java/org/neo4j/coreedge/core/consensus/RaftServer.java @@ -19,40 +19,42 @@ */ package org.neo4j.coreedge.core.consensus; -import java.util.concurrent.TimeUnit; - import io.netty.bootstrap.ServerBootstrap; import io.netty.channel.Channel; -import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelOption; import io.netty.channel.ChannelPipeline; import io.netty.channel.EventLoopGroup; -import io.netty.channel.SimpleChannelInboundHandler; 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 org.neo4j.coreedge.messaging.Inbound; -import org.neo4j.coreedge.messaging.marsalling.RaftMessageDecoder; +import java.util.concurrent.TimeUnit; +import java.util.function.Predicate; + import org.neo4j.coreedge.core.replication.ReplicatedContent; -import org.neo4j.coreedge.messaging.marsalling.ChannelMarshal; -import org.neo4j.coreedge.messaging.address.ListenSocketAddress; import org.neo4j.coreedge.logging.ExceptionLoggingHandler; +import org.neo4j.coreedge.messaging.Inbound; +import org.neo4j.coreedge.messaging.Message; +import org.neo4j.coreedge.messaging.address.ListenSocketAddress; +import org.neo4j.coreedge.messaging.marsalling.ChannelMarshal; +import org.neo4j.coreedge.messaging.marsalling.RaftMessageDecoder; import org.neo4j.helpers.NamedThreadFactory; import org.neo4j.kernel.lifecycle.LifecycleAdapter; import org.neo4j.logging.Log; import org.neo4j.logging.LogProvider; -import static java.lang.String.format; +import static org.neo4j.coreedge.messaging.Message.CURRENT_VERSION; public class RaftServer extends LifecycleAdapter implements Inbound { private final ListenSocketAddress listenAddress; private final Log log; + private final LogProvider logProvider; private final ChannelMarshal marshal; + private MessageHandler messageHandler; private EventLoopGroup workerGroup; private Channel channel; @@ -60,39 +62,16 @@ public class RaftServer extends LifecycleAdapter implements Inbound marshal, ListenSocketAddress listenAddress, - LogProvider logProvider ) + LogProvider logProvider ) { this.marshal = marshal; this.listenAddress = listenAddress; + this.logProvider = logProvider; this.log = logProvider.getLog( getClass() ); } @Override public synchronized void start() throws Throwable - { - startNettyServer(); - } - - @Override - public synchronized void stop() throws Throwable - { - try - { - channel.close().sync(); - } - catch ( InterruptedException e ) - { - Thread.currentThread().interrupt(); - log.warn( "Interrupted while closing channel." ); - } - - if ( workerGroup.shutdownGracefully( 2, 5, TimeUnit.SECONDS ).awaitUninterruptibly( 10, TimeUnit.SECONDS ) ) - { - log.warn( "Worker group not shutdown within 10 seconds." ); - } - } - - private void startNettyServer() { workerGroup = new NioEventLoopGroup( 0, threadFactory ); @@ -112,7 +91,8 @@ protected void initChannel( SocketChannel ch ) throws Exception pipeline.addLast( new LengthFieldBasedFrameDecoder( Integer.MAX_VALUE, 0, 4, 0, 4 ) ); pipeline.addLast( new LengthFieldPrepender( 4 ) ); pipeline.addLast( new RaftMessageDecoder( marshal ) ); - pipeline.addLast( new RaftMessageHandler() ); + Predicate versionChecker = ( m ) -> m.version() == CURRENT_VERSION; + pipeline.addLast( new RaftMessageHandler( versionChecker, () -> messageHandler, logProvider ) ); pipeline.addLast( new ExceptionLoggingHandler( log ) ); } } ); @@ -121,26 +101,27 @@ protected void initChannel( SocketChannel ch ) throws Exception } @Override - public void registerHandler( Inbound.MessageHandler handler ) + public synchronized void stop() throws Throwable { - this.messageHandler = handler; - } + try + { + channel.close().sync(); + } + catch ( InterruptedException e ) + { + Thread.currentThread().interrupt(); + log.warn( "Interrupted while closing channel." ); + } - private class RaftMessageHandler extends SimpleChannelInboundHandler - { - @Override - protected void channelRead0( ChannelHandlerContext channelHandlerContext, - RaftMessages.StoreIdAwareMessage storeIdAwareMessage ) throws Exception + if ( workerGroup.shutdownGracefully( 2, 5, TimeUnit.SECONDS ).awaitUninterruptibly( 10, TimeUnit.SECONDS ) ) { - try - { - messageHandler.handle( storeIdAwareMessage ); - } - catch ( Exception e ) - { - log.error( format( "Failed to process message %s", storeIdAwareMessage ), e ); - } + log.warn( "Worker group not shutdown within 10 seconds." ); } } + @Override + public void registerHandler( Inbound.MessageHandler handler ) + { + this.messageHandler = handler; + } } diff --git a/enterprise/core-edge/src/main/java/org/neo4j/coreedge/core/consensus/outcome/Outcome.java b/enterprise/core-edge/src/main/java/org/neo4j/coreedge/core/consensus/outcome/Outcome.java index ec661961e4ba2..d72c64d1a1425 100644 --- a/enterprise/core-edge/src/main/java/org/neo4j/coreedge/core/consensus/outcome/Outcome.java +++ b/enterprise/core-edge/src/main/java/org/neo4j/coreedge/core/consensus/outcome/Outcome.java @@ -24,12 +24,12 @@ import java.util.HashSet; import java.util.Set; -import org.neo4j.coreedge.messaging.Message; import org.neo4j.coreedge.core.consensus.RaftMessages; import org.neo4j.coreedge.core.consensus.roles.Role; -import org.neo4j.coreedge.core.consensus.state.ReadableRaftState; import org.neo4j.coreedge.core.consensus.roles.follower.FollowerStates; +import org.neo4j.coreedge.core.consensus.state.ReadableRaftState; import org.neo4j.coreedge.identity.MemberId; +import org.neo4j.coreedge.messaging.Message; /** * Holds the outcome of a RAFT role's handling of a message. The role handling logic is stateless @@ -42,6 +42,7 @@ public class Outcome implements Message, ConsensusOutcome { /* Common */ + private byte version; private Role nextRole; private long term; @@ -74,12 +75,13 @@ public Outcome( Role currentRole, ReadableRaftState ctx ) defaults( currentRole, ctx ); } - public Outcome( Role nextRole, long term, MemberId leader, long leaderCommit, MemberId votedFor, + public Outcome( byte version, Role nextRole, long term, MemberId leader, long leaderCommit, MemberId votedFor, Set votesForMe, long lastLogIndexBeforeWeBecameLeader, FollowerStates followerStates, boolean renewElectionTimeout, Collection logCommands, Collection outgoingMessages, Collection shipCommands, long commitIndex ) { + this.version = version; this.nextRole = nextRole; this.term = term; this.leader = leader; @@ -98,6 +100,7 @@ public Outcome( Role nextRole, long term, MemberId leader, long leaderCommit, Me private void defaults( Role currentRole, ReadableRaftState ctx ) { + version = Message.CURRENT_VERSION; nextRole = currentRole; term = ctx.term(); @@ -302,4 +305,10 @@ public void setCommitIndex( long commitIndex ) { this.commitIndex = commitIndex; } + + @Override + public byte version() + { + return version; + } } diff --git a/enterprise/core-edge/src/main/java/org/neo4j/coreedge/core/consensus/roles/Appending.java b/enterprise/core-edge/src/main/java/org/neo4j/coreedge/core/consensus/roles/Appending.java index a58089a4733d1..533d6e57d6ca2 100644 --- a/enterprise/core-edge/src/main/java/org/neo4j/coreedge/core/consensus/roles/Appending.java +++ b/enterprise/core-edge/src/main/java/org/neo4j/coreedge/core/consensus/roles/Appending.java @@ -31,8 +31,11 @@ import org.neo4j.coreedge.core.consensus.outcome.TruncateLogCommand; import org.neo4j.coreedge.core.replication.ReplicatedContent; import org.neo4j.coreedge.core.consensus.state.ReadableRaftState; +import org.neo4j.coreedge.messaging.Message; import org.neo4j.logging.Log; +import static org.neo4j.coreedge.messaging.Message.CURRENT_VERSION; + class Appending { static void handleAppendEntriesRequest( ReadableRaftState state, Outcome outcome, @@ -41,8 +44,9 @@ static void handleAppendEntriesRequest( ReadableRaftState state, Outcome outcome { if ( request.leaderTerm() < state.term() ) { - RaftMessages.AppendEntries.Response appendResponse = new RaftMessages.AppendEntries.Response( - state.myself(), state.term(), false, -1, state.entryLog().appendIndex() ); + RaftMessages.AppendEntries.Response appendResponse = + new RaftMessages.AppendEntries.Response( CURRENT_VERSION, state.myself(), state.term(), false, -1, + state.entryLog().appendIndex() ); outcome.addOutgoingMessage( new RaftMessages.Directed( request.from(), appendResponse ) ); return; @@ -56,8 +60,9 @@ static void handleAppendEntriesRequest( ReadableRaftState state, Outcome outcome if ( !Follower.logHistoryMatches( state, request.prevLogIndex(), request.prevLogTerm(), log ) ) { assert request.prevLogIndex() > -1 && request.prevLogTerm() > -1; - RaftMessages.AppendEntries.Response appendResponse = new RaftMessages.AppendEntries.Response( - state.myself(), request.leaderTerm(), false, -1, state.entryLog().appendIndex() ); + RaftMessages.AppendEntries.Response appendResponse = + new RaftMessages.AppendEntries.Response( CURRENT_VERSION, state.myself(), request.leaderTerm(), + false, -1, state.entryLog().appendIndex() ); outcome.addOutgoingMessage( new RaftMessages.Directed( request.from(), appendResponse ) ); return; @@ -105,8 +110,9 @@ else if ( logTerm != request.entries()[offset].term() ) state, request.prevLogIndex() + request.entries().length, request.leaderCommit(), outcome ); long endMatchIndex = request.prevLogIndex() + request.entries().length; // this is the index of the last incoming entry - RaftMessages.AppendEntries.Response appendResponse = new RaftMessages.AppendEntries.Response( - state.myself(), request.leaderTerm(), true, endMatchIndex, endMatchIndex ); + RaftMessages.AppendEntries.Response appendResponse = + new RaftMessages.AppendEntries.Response( CURRENT_VERSION, state.myself(), request.leaderTerm(), true, + endMatchIndex, endMatchIndex ); outcome.addOutgoingMessage( new RaftMessages.Directed( request.from(), appendResponse ) ); } diff --git a/enterprise/core-edge/src/main/java/org/neo4j/coreedge/core/consensus/roles/Candidate.java b/enterprise/core-edge/src/main/java/org/neo4j/coreedge/core/consensus/roles/Candidate.java index d3f2afad77aca..9242a69747e3c 100644 --- a/enterprise/core-edge/src/main/java/org/neo4j/coreedge/core/consensus/roles/Candidate.java +++ b/enterprise/core-edge/src/main/java/org/neo4j/coreedge/core/consensus/roles/Candidate.java @@ -22,16 +22,17 @@ import java.io.IOException; import org.neo4j.coreedge.core.consensus.NewLeaderBarrier; -import org.neo4j.coreedge.core.consensus.RaftMessageHandler; import org.neo4j.coreedge.core.consensus.RaftMessages; import org.neo4j.coreedge.core.consensus.outcome.Outcome; import org.neo4j.coreedge.core.consensus.state.ReadableRaftState; +import org.neo4j.coreedge.messaging.Message; import org.neo4j.logging.Log; import static org.neo4j.coreedge.core.consensus.MajorityIncludingSelfQuorum.isQuorum; import static org.neo4j.coreedge.core.consensus.roles.Role.CANDIDATE; import static org.neo4j.coreedge.core.consensus.roles.Role.FOLLOWER; import static org.neo4j.coreedge.core.consensus.roles.Role.LEADER; +import static org.neo4j.coreedge.messaging.Message.CURRENT_VERSION; class Candidate implements RaftMessageHandler { @@ -65,7 +66,7 @@ public Outcome handle( RaftMessages.RaftMessage message, ReadableRaftState ctx, if ( req.leaderTerm() < ctx.term() ) { RaftMessages.AppendEntries.Response appendResponse = - new RaftMessages.AppendEntries.Response( ctx.myself(), ctx.term(), false, + new RaftMessages.AppendEntries.Response( CURRENT_VERSION, ctx.myself(), ctx.term(), false, req.prevLogIndex(), ctx.entryLog().appendIndex() ); outcome.addOutgoingMessage( new RaftMessages.Directed( req.from(), appendResponse ) ); @@ -130,7 +131,7 @@ else if ( res.term() < ctx.term() || !res.voteGranted() ) } outcome.addOutgoingMessage( new RaftMessages.Directed( req.from(), - new RaftMessages.Vote.Response( ctx.myself(), outcome.getTerm(), false ) ) ); + new RaftMessages.Vote.Response( CURRENT_VERSION, ctx.myself(), outcome.getTerm(), false ) ) ); break; } diff --git a/enterprise/core-edge/src/main/java/org/neo4j/coreedge/core/consensus/roles/Election.java b/enterprise/core-edge/src/main/java/org/neo4j/coreedge/core/consensus/roles/Election.java index f65079c7950f7..301d34a4f416a 100644 --- a/enterprise/core-edge/src/main/java/org/neo4j/coreedge/core/consensus/roles/Election.java +++ b/enterprise/core-edge/src/main/java/org/neo4j/coreedge/core/consensus/roles/Election.java @@ -26,8 +26,11 @@ import org.neo4j.coreedge.core.consensus.outcome.Outcome; import org.neo4j.coreedge.core.consensus.state.ReadableRaftState; import org.neo4j.coreedge.identity.MemberId; +import org.neo4j.coreedge.messaging.Message; import org.neo4j.logging.Log; +import static org.neo4j.coreedge.messaging.Message.CURRENT_VERSION; + public class Election { public static boolean start( ReadableRaftState ctx, Outcome outcome, Log log ) throws IOException @@ -43,8 +46,8 @@ public static boolean start( ReadableRaftState ctx, Outcome outcome, Log log ) outcome.setNextTerm( ctx.term() + 1 ); RaftMessages.Vote.Request voteForMe = - new RaftMessages.Vote.Request( ctx.myself(), outcome.getTerm(), ctx.myself(), ctx.entryLog() - .appendIndex(), ctx.entryLog().readEntryTerm( ctx.entryLog().appendIndex() ) ); + new RaftMessages.Vote.Request( CURRENT_VERSION, ctx.myself(), outcome.getTerm(), ctx.myself(), + ctx.entryLog().appendIndex(), ctx.entryLog().readEntryTerm( ctx.entryLog().appendIndex() ) ); currentMembers.stream().filter( member -> !member.equals( ctx.myself() ) ).forEach( member -> outcome.addOutgoingMessage( new RaftMessages.Directed( member, voteForMe ) ) diff --git a/enterprise/core-edge/src/main/java/org/neo4j/coreedge/core/consensus/roles/Follower.java b/enterprise/core-edge/src/main/java/org/neo4j/coreedge/core/consensus/roles/Follower.java index a68dcc4c85f77..971cd9cd3617b 100644 --- a/enterprise/core-edge/src/main/java/org/neo4j/coreedge/core/consensus/roles/Follower.java +++ b/enterprise/core-edge/src/main/java/org/neo4j/coreedge/core/consensus/roles/Follower.java @@ -21,7 +21,6 @@ import java.io.IOException; -import org.neo4j.coreedge.core.consensus.RaftMessageHandler; import org.neo4j.coreedge.core.consensus.RaftMessages; import org.neo4j.coreedge.core.consensus.RaftMessages.AppendEntries; import org.neo4j.coreedge.core.consensus.RaftMessages.Heartbeat; diff --git a/enterprise/core-edge/src/main/java/org/neo4j/coreedge/core/consensus/roles/Leader.java b/enterprise/core-edge/src/main/java/org/neo4j/coreedge/core/consensus/roles/Leader.java index f63966ecfc56b..463b6066cfffc 100644 --- a/enterprise/core-edge/src/main/java/org/neo4j/coreedge/core/consensus/roles/Leader.java +++ b/enterprise/core-edge/src/main/java/org/neo4j/coreedge/core/consensus/roles/Leader.java @@ -23,15 +23,14 @@ import java.util.List; import org.neo4j.coreedge.core.consensus.Followers; -import org.neo4j.coreedge.core.consensus.RaftMessageHandler; import org.neo4j.coreedge.core.consensus.RaftMessages; import org.neo4j.coreedge.core.consensus.RaftMessages.Heartbeat; import org.neo4j.coreedge.core.consensus.outcome.Outcome; import org.neo4j.coreedge.core.consensus.outcome.ShipCommand; -import org.neo4j.coreedge.core.replication.ReplicatedContent; -import org.neo4j.coreedge.core.consensus.state.ReadableRaftState; import org.neo4j.coreedge.core.consensus.roles.follower.FollowerState; import org.neo4j.coreedge.core.consensus.roles.follower.FollowerStates; +import org.neo4j.coreedge.core.consensus.state.ReadableRaftState; +import org.neo4j.coreedge.core.replication.ReplicatedContent; import org.neo4j.coreedge.identity.MemberId; import org.neo4j.helpers.collection.FilteringIterable; import org.neo4j.logging.Log; @@ -39,6 +38,7 @@ import static java.lang.Math.max; import static org.neo4j.coreedge.core.consensus.roles.Role.FOLLOWER; import static org.neo4j.coreedge.core.consensus.roles.Role.LEADER; +import static org.neo4j.coreedge.messaging.Message.CURRENT_VERSION; public class Leader implements RaftMessageHandler { @@ -51,8 +51,7 @@ private static void sendHeartbeats( ReadableRaftState ctx, Outcome outcome ) thr { long commitIndex = ctx.leaderCommit(); long commitIndexTerm = ctx.entryLog().readEntryTerm( commitIndex ); - Heartbeat heartbeat = new Heartbeat( ctx.myself(), ctx.term(), commitIndex, - commitIndexTerm ); + Heartbeat heartbeat = new Heartbeat( CURRENT_VERSION, ctx.myself(), ctx.term(), commitIndex, commitIndexTerm ); for ( MemberId to : replicationTargets( ctx ) ) { outcome.addOutgoingMessage( new RaftMessages.Directed( to, heartbeat ) ); @@ -97,8 +96,8 @@ public Outcome handle( RaftMessages.RaftMessage message, ReadableRaftState ctx, if ( req.leaderTerm() < ctx.term() ) { RaftMessages.AppendEntries.Response appendResponse = - new RaftMessages.AppendEntries.Response( ctx.myself(), ctx.term(), false, req.prevLogIndex(), - ctx.entryLog().appendIndex() ); + new RaftMessages.AppendEntries.Response( CURRENT_VERSION, ctx.myself(), ctx.term(), false, + req.prevLogIndex(), ctx.entryLog().appendIndex() ); outcome.addOutgoingMessage( new RaftMessages.Directed( req.from(), appendResponse ) ); break; @@ -190,7 +189,8 @@ else if ( response.term() > ctx.term() ) // There are no earlier entries, message the follower that we have compacted so that // it can take appropriate action. outcome.addOutgoingMessage( new RaftMessages.Directed( response.from(), - new RaftMessages.LogCompactionInfo( ctx.myself(), ctx.term(), ctx.entryLog().prevIndex() ) ) ); + new RaftMessages.LogCompactionInfo( CURRENT_VERSION, ctx.myself(), ctx.term(), + ctx.entryLog().prevIndex() ) ) ); } } break; @@ -212,7 +212,7 @@ else if ( response.term() > ctx.term() ) } outcome.addOutgoingMessage( new RaftMessages.Directed( req.from(), - new RaftMessages.Vote.Response( ctx.myself(), ctx.term(), false ) ) ); + new RaftMessages.Vote.Response( CURRENT_VERSION, ctx.myself(), ctx.term(), false ) ) ); break; } diff --git a/enterprise/core-edge/src/test/java/org/neo4j/coreedge/core/consensus/explorer/action/HeartbeatTimeout.java b/enterprise/core-edge/src/main/java/org/neo4j/coreedge/core/consensus/roles/RaftMessageHandler.java similarity index 52% rename from enterprise/core-edge/src/test/java/org/neo4j/coreedge/core/consensus/explorer/action/HeartbeatTimeout.java rename to enterprise/core-edge/src/main/java/org/neo4j/coreedge/core/consensus/roles/RaftMessageHandler.java index 78562ecb93425..9c80be4dcfad6 100644 --- a/enterprise/core-edge/src/test/java/org/neo4j/coreedge/core/consensus/explorer/action/HeartbeatTimeout.java +++ b/enterprise/core-edge/src/main/java/org/neo4j/coreedge/core/consensus/roles/RaftMessageHandler.java @@ -17,32 +17,16 @@ * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ -package org.neo4j.coreedge.core.consensus.explorer.action; +package org.neo4j.coreedge.core.consensus.roles; import java.io.IOException; -import java.util.LinkedList; -import java.util.Queue; import org.neo4j.coreedge.core.consensus.RaftMessages; -import org.neo4j.coreedge.core.consensus.explorer.ClusterState; -import org.neo4j.coreedge.identity.MemberId; +import org.neo4j.coreedge.core.consensus.outcome.Outcome; +import org.neo4j.coreedge.core.consensus.state.ReadableRaftState; +import org.neo4j.logging.Log; -public class HeartbeatTimeout implements Action +public interface RaftMessageHandler { - private final MemberId member; - - public HeartbeatTimeout( MemberId member ) - { - this.member = member; - } - - @Override - public ClusterState advance( ClusterState previous ) throws IOException - { - ClusterState newClusterState = new ClusterState( previous ); - Queue newQueue = new LinkedList<>( previous.queues.get( member ) ); - newQueue.offer( new RaftMessages.Timeout.Heartbeat( member ) ); - newClusterState.queues.put( member, newQueue ); - return newClusterState; - } + Outcome handle( RaftMessages.RaftMessage message, ReadableRaftState context, Log log ) throws IOException; } diff --git a/enterprise/core-edge/src/main/java/org/neo4j/coreedge/core/consensus/roles/Role.java b/enterprise/core-edge/src/main/java/org/neo4j/coreedge/core/consensus/roles/Role.java index 32fe42fbb8689..836a7854d3ee9 100644 --- a/enterprise/core-edge/src/main/java/org/neo4j/coreedge/core/consensus/roles/Role.java +++ b/enterprise/core-edge/src/main/java/org/neo4j/coreedge/core/consensus/roles/Role.java @@ -19,8 +19,6 @@ */ package org.neo4j.coreedge.core.consensus.roles; -import org.neo4j.coreedge.core.consensus.RaftMessageHandler; - public enum Role { FOLLOWER( new Follower() ), diff --git a/enterprise/core-edge/src/main/java/org/neo4j/coreedge/core/consensus/roles/Voting.java b/enterprise/core-edge/src/main/java/org/neo4j/coreedge/core/consensus/roles/Voting.java index 7f385736421eb..aa7425aed4713 100644 --- a/enterprise/core-edge/src/main/java/org/neo4j/coreedge/core/consensus/roles/Voting.java +++ b/enterprise/core-edge/src/main/java/org/neo4j/coreedge/core/consensus/roles/Voting.java @@ -25,6 +25,9 @@ import org.neo4j.coreedge.core.consensus.outcome.Outcome; import org.neo4j.coreedge.core.consensus.state.ReadableRaftState; import org.neo4j.coreedge.identity.MemberId; +import org.neo4j.coreedge.messaging.Message; + +import static org.neo4j.coreedge.messaging.Message.CURRENT_VERSION; public class Voting { @@ -48,9 +51,9 @@ static void handleVoteRequest( ReadableRaftState state, Outcome outcome, outcome.renewElectionTimeout(); } - outcome.addOutgoingMessage( new RaftMessages.Directed( voteRequest.from(), new RaftMessages.Vote.Response( - state.myself(), outcome.getTerm(), - willVoteForCandidate ) ) ); + outcome.addOutgoingMessage( new RaftMessages.Directed( voteRequest.from(), + new RaftMessages.Vote.Response( CURRENT_VERSION, state.myself(), outcome.getTerm(), + willVoteForCandidate ) ) ); } public static boolean shouldVoteFor( MemberId candidate, long contextTerm, long requestTerm, diff --git a/enterprise/core-edge/src/main/java/org/neo4j/coreedge/core/consensus/shipping/RaftLogShipper.java b/enterprise/core-edge/src/main/java/org/neo4j/coreedge/core/consensus/shipping/RaftLogShipper.java index ea4152e84f323..72fe59230ae32 100644 --- a/enterprise/core-edge/src/main/java/org/neo4j/coreedge/core/consensus/shipping/RaftLogShipper.java +++ b/enterprise/core-edge/src/main/java/org/neo4j/coreedge/core/consensus/shipping/RaftLogShipper.java @@ -29,6 +29,7 @@ import org.neo4j.coreedge.core.consensus.log.RaftLogEntry; import org.neo4j.coreedge.core.consensus.log.ReadableRaftLog; import org.neo4j.coreedge.core.consensus.log.segmented.InFlightMap; +import org.neo4j.coreedge.messaging.Message; import org.neo4j.coreedge.messaging.Outbound; import org.neo4j.coreedge.core.state.InFlightLogEntryReader; import org.neo4j.coreedge.identity.MemberId; @@ -41,6 +42,7 @@ import static org.neo4j.coreedge.core.consensus.schedule.RenewableTimeoutService.RenewableTimeout; import static org.neo4j.coreedge.core.consensus.shipping.RaftLogShipper.Mode.PIPELINE; import static org.neo4j.coreedge.core.consensus.shipping.RaftLogShipper.Timeouts.RESEND; +import static org.neo4j.coreedge.messaging.Message.CURRENT_VERSION; /// Optimizations // TODO: Have several outstanding batches in catchup mode, to bridge the latency gap. @@ -380,7 +382,7 @@ private void sendCommitUpdate( LeaderContext leaderContext ) * entry is the current term. */ RaftMessages.Heartbeat appendRequest = - new RaftMessages.Heartbeat( leader, leaderContext.term, leaderContext.commitIndex, + new RaftMessages.Heartbeat( CURRENT_VERSION, leader, leaderContext.term, leaderContext.commitIndex, leaderContext.term ); outbound.send( follower, appendRequest ); @@ -393,9 +395,9 @@ private void sendNewEntries( long prevLogIndex, long prevLogTerm, RaftLogEntry[] lastSentIndex = prevLogIndex + 1; - RaftMessages.AppendEntries.Request appendRequest = new RaftMessages.AppendEntries.Request( - leader, leaderContext.term, prevLogIndex, prevLogTerm, newEntries, leaderContext.commitIndex - ); + RaftMessages.AppendEntries.Request appendRequest = + new RaftMessages.AppendEntries.Request( CURRENT_VERSION, leader, leaderContext.term, prevLogIndex, + prevLogTerm, newEntries, leaderContext.commitIndex ); outbound.send( follower, appendRequest ); } @@ -425,8 +427,9 @@ private void sendEmpty( long logIndex, LeaderContext leaderContext ) return; } - RaftMessages.AppendEntries.Request appendRequest = new RaftMessages.AppendEntries.Request( - leader, leaderContext.term, prevLogIndex, prevLogTerm, RaftLogEntry.empty, leaderContext.commitIndex ); + RaftMessages.AppendEntries.Request appendRequest = + new RaftMessages.AppendEntries.Request( CURRENT_VERSION, leader, leaderContext.term, prevLogIndex, + prevLogTerm, RaftLogEntry.empty, leaderContext.commitIndex ); outbound.send( follower, appendRequest ); } @@ -493,8 +496,9 @@ private void sendRange( long startIndex, long endIndex, LeaderContext leaderCont } else { - RaftMessages.AppendEntries.Request appendRequest = new RaftMessages.AppendEntries.Request( - leader, leaderContext.term, prevLogIndex, prevLogTerm, entries, leaderContext.commitIndex ); + RaftMessages.AppendEntries.Request appendRequest = + new RaftMessages.AppendEntries.Request( CURRENT_VERSION, leader, leaderContext.term, + prevLogIndex, prevLogTerm, entries, leaderContext.commitIndex ); outbound.send( follower, appendRequest ); } @@ -515,8 +519,8 @@ private void sendLogCompactionInfo( LeaderContext leaderContext ) log.warn( "Sending log compaction info. Log pruned? Status=%s, LeaderContext=%s", statusAsString(), leaderContext ); - outbound.send( follower, new RaftMessages.LogCompactionInfo( - leader, leaderContext.term, raftLog.prevIndex() ) ); + outbound.send( follower, new RaftMessages.LogCompactionInfo( CURRENT_VERSION, leader, leaderContext.term, + raftLog.prevIndex() ) ); } private String statusAsString() diff --git a/enterprise/core-edge/src/main/java/org/neo4j/coreedge/core/replication/RaftReplicator.java b/enterprise/core-edge/src/main/java/org/neo4j/coreedge/core/replication/RaftReplicator.java index 5eddca6694c09..d2d5d0deb15bb 100644 --- a/enterprise/core-edge/src/main/java/org/neo4j/coreedge/core/replication/RaftReplicator.java +++ b/enterprise/core-edge/src/main/java/org/neo4j/coreedge/core/replication/RaftReplicator.java @@ -25,6 +25,7 @@ import org.neo4j.coreedge.core.consensus.LeaderLocator; import org.neo4j.coreedge.core.consensus.NoLeaderFoundException; import org.neo4j.coreedge.core.consensus.RaftMessages; +import org.neo4j.coreedge.messaging.Message; import org.neo4j.coreedge.messaging.Outbound; import org.neo4j.coreedge.core.replication.session.LocalSessionPool; import org.neo4j.coreedge.core.replication.session.OperationContext; @@ -32,6 +33,8 @@ import org.neo4j.coreedge.identity.MemberId; import org.neo4j.kernel.impl.util.Listener; +import static org.neo4j.coreedge.messaging.Message.CURRENT_VERSION; + /** * A replicator implementation suitable in a RAFT context. Will handle resending due to timeouts and leader switches. */ @@ -77,7 +80,7 @@ public Future replicate( ReplicatedContent command, boolean trackResult RetryStrategy.Timeout timeout = retryStrategy.newTimeout(); do { - outbound.send( leader, new RaftMessages.NewEntry.Request( me, operation ) ); + outbound.send( leader, new RaftMessages.NewEntry.Request( CURRENT_VERSION, me, operation ) ); try { progress.awaitReplication( timeout.getMillis() ); diff --git a/enterprise/core-edge/src/main/java/org/neo4j/coreedge/core/replication/SendToMyself.java b/enterprise/core-edge/src/main/java/org/neo4j/coreedge/core/replication/SendToMyself.java index b1cf067bc1ec4..6c6285cf71cb6 100644 --- a/enterprise/core-edge/src/main/java/org/neo4j/coreedge/core/replication/SendToMyself.java +++ b/enterprise/core-edge/src/main/java/org/neo4j/coreedge/core/replication/SendToMyself.java @@ -20,8 +20,10 @@ package org.neo4j.coreedge.core.replication; import org.neo4j.coreedge.core.consensus.RaftMessages; -import org.neo4j.coreedge.messaging.Outbound; import org.neo4j.coreedge.identity.MemberId; +import org.neo4j.coreedge.messaging.Outbound; + +import static org.neo4j.coreedge.messaging.Message.CURRENT_VERSION; public class SendToMyself { @@ -36,6 +38,6 @@ public SendToMyself( MemberId myself, Outbound batch if ( batchRequest == null ) { - batchRequest = new RaftMessages.NewEntry.BatchRequest( batch.size() ); + batchRequest = new RaftMessages.NewEntry.BatchRequest( newEntryRequest.version(), batch.size() ); } + + assert batchRequest.version() == newEntryRequest.version(); batchRequest.add( newEntryRequest.content() ); } else diff --git a/enterprise/core-edge/src/main/java/org/neo4j/coreedge/core/state/CommandApplicationProcess.java b/enterprise/core-edge/src/main/java/org/neo4j/coreedge/core/state/CommandApplicationProcess.java index e44ea3108c85d..0470c0c53a79d 100644 --- a/enterprise/core-edge/src/main/java/org/neo4j/coreedge/core/state/CommandApplicationProcess.java +++ b/enterprise/core-edge/src/main/java/org/neo4j/coreedge/core/state/CommandApplicationProcess.java @@ -35,6 +35,7 @@ import org.neo4j.coreedge.core.consensus.log.RaftLogEntry; import org.neo4j.coreedge.core.consensus.log.monitoring.RaftLogCommitIndexMonitor; import org.neo4j.coreedge.core.consensus.log.segmented.InFlightMap; +import org.neo4j.coreedge.messaging.Message; import org.neo4j.kernel.internal.DatabaseHealth; import org.neo4j.kernel.lifecycle.LifecycleAdapter; import org.neo4j.kernel.monitoring.Monitors; @@ -43,6 +44,7 @@ import static java.lang.Math.max; import static java.lang.String.format; +import static org.neo4j.coreedge.messaging.Message.CURRENT_VERSION; public class CommandApplicationProcess extends LifecycleAdapter { @@ -269,7 +271,7 @@ public synchronized CoreSnapshot snapshot() throws IOException, InterruptedExcep long prevIndex = lastApplied; long prevTerm = raftLog.readEntryTerm( prevIndex ); - CoreSnapshot coreSnapshot = new CoreSnapshot( prevIndex, prevTerm ); + CoreSnapshot coreSnapshot = new CoreSnapshot( CURRENT_VERSION, prevIndex, prevTerm ); coreStateMachines.addSnapshots( coreSnapshot ); sessionTracker.addSnapshots( coreSnapshot ); diff --git a/enterprise/core-edge/src/main/java/org/neo4j/coreedge/core/state/snapshot/CoreSnapshot.java b/enterprise/core-edge/src/main/java/org/neo4j/coreedge/core/state/snapshot/CoreSnapshot.java index 2d34d3cfde678..30ef00318b5c1 100644 --- a/enterprise/core-edge/src/main/java/org/neo4j/coreedge/core/state/snapshot/CoreSnapshot.java +++ b/enterprise/core-edge/src/main/java/org/neo4j/coreedge/core/state/snapshot/CoreSnapshot.java @@ -24,21 +24,23 @@ import java.util.Map; import org.neo4j.coreedge.core.state.storage.SafeChannelMarshal; +import org.neo4j.coreedge.messaging.BaseMessage; import org.neo4j.coreedge.messaging.EndOfStreamException; import org.neo4j.storageengine.api.ReadableChannel; import org.neo4j.storageengine.api.WritableChannel; import static java.lang.String.format; -public class CoreSnapshot +public class CoreSnapshot extends BaseMessage { private final long prevIndex; private final long prevTerm; private final Map snapshotCollection = new HashMap<>(); - public CoreSnapshot( long prevIndex, long prevTerm ) + public CoreSnapshot( byte version, long prevIndex, long prevTerm ) { + super( version ); this.prevIndex = prevIndex; this.prevTerm = prevTerm; } @@ -78,6 +80,7 @@ public static class Marshal extends SafeChannelMarshal @Override public void marshal( CoreSnapshot coreSnapshot, WritableChannel buffer ) throws IOException { + buffer.put( coreSnapshot.version() ); buffer.putLong( coreSnapshot.prevIndex ); buffer.putLong( coreSnapshot.prevTerm ); @@ -92,10 +95,11 @@ public void marshal( CoreSnapshot coreSnapshot, WritableChannel buffer ) throws @Override public CoreSnapshot unmarshal0( ReadableChannel channel ) throws IOException, EndOfStreamException { + byte version = channel.get(); long prevIndex = channel.getLong(); long prevTerm = channel.getLong(); - CoreSnapshot coreSnapshot = new CoreSnapshot( prevIndex, prevTerm ); + CoreSnapshot coreSnapshot = new CoreSnapshot( version, prevIndex, prevTerm ); int snapshotCount = channel.getInt(); for ( int i = 0; i < snapshotCount; i++ ) { diff --git a/enterprise/core-edge/src/main/java/org/neo4j/coreedge/core/state/snapshot/CoreSnapshotRequest.java b/enterprise/core-edge/src/main/java/org/neo4j/coreedge/core/state/snapshot/CoreSnapshotRequest.java index d96e283614149..af42217425737 100644 --- a/enterprise/core-edge/src/main/java/org/neo4j/coreedge/core/state/snapshot/CoreSnapshotRequest.java +++ b/enterprise/core-edge/src/main/java/org/neo4j/coreedge/core/state/snapshot/CoreSnapshotRequest.java @@ -20,9 +20,15 @@ package org.neo4j.coreedge.core.state.snapshot; import org.neo4j.coreedge.catchup.RequestMessageType; +import org.neo4j.coreedge.messaging.BaseMessage; import org.neo4j.coreedge.messaging.Message; -public class CoreSnapshotRequest implements Message +public class CoreSnapshotRequest extends BaseMessage { public static final RequestMessageType MESSAGE_TYPE = RequestMessageType.STORE; + + public CoreSnapshotRequest( byte version ) + { + super( version ); + } } diff --git a/enterprise/core-edge/src/main/java/org/neo4j/coreedge/core/state/snapshot/CoreSnapshotRequestEncoder.java b/enterprise/core-edge/src/main/java/org/neo4j/coreedge/core/state/snapshot/CoreSnapshotRequestEncoder.java index fc649b4fbd51d..28ac9889ea826 100644 --- a/enterprise/core-edge/src/main/java/org/neo4j/coreedge/core/state/snapshot/CoreSnapshotRequestEncoder.java +++ b/enterprise/core-edge/src/main/java/org/neo4j/coreedge/core/state/snapshot/CoreSnapshotRequestEncoder.java @@ -31,6 +31,7 @@ public class CoreSnapshotRequestEncoder extends MessageToMessageEncoder out ) throws Exception { ByteBuf buffer = ctx.alloc().buffer(); + buffer.writeByte( msg.version() ); buffer.writeByte( 0 ); out.add( buffer ); } diff --git a/enterprise/core-edge/src/main/java/org/neo4j/coreedge/core/state/snapshot/CoreSnapshotRequestHandler.java b/enterprise/core-edge/src/main/java/org/neo4j/coreedge/core/state/snapshot/CoreSnapshotRequestHandler.java index e1e9a0ea70e6d..660b2e32488e0 100644 --- a/enterprise/core-edge/src/main/java/org/neo4j/coreedge/core/state/snapshot/CoreSnapshotRequestHandler.java +++ b/enterprise/core-edge/src/main/java/org/neo4j/coreedge/core/state/snapshot/CoreSnapshotRequestHandler.java @@ -19,30 +19,35 @@ */ package org.neo4j.coreedge.core.state.snapshot; -import java.io.IOException; - import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.SimpleChannelInboundHandler; + +import java.io.IOException; +import java.util.function.Predicate; import org.neo4j.coreedge.catchup.CatchupServerProtocol; import org.neo4j.coreedge.catchup.ResponseMessageType; +import org.neo4j.coreedge.VersionCheckerChannelInboundHandler; import org.neo4j.coreedge.core.state.CoreState; +import org.neo4j.coreedge.messaging.Message; +import org.neo4j.logging.LogProvider; import static org.neo4j.coreedge.catchup.CatchupServerProtocol.State; -public class CoreSnapshotRequestHandler extends SimpleChannelInboundHandler +public class CoreSnapshotRequestHandler extends VersionCheckerChannelInboundHandler { private final CatchupServerProtocol protocol; private final CoreState coreState; - public CoreSnapshotRequestHandler( CatchupServerProtocol protocol, CoreState coreState ) + public CoreSnapshotRequestHandler( Predicate versionChecker, CatchupServerProtocol protocol, + CoreState coreState, LogProvider logProvider ) { + super( versionChecker, logProvider ); this.protocol = protocol; this.coreState = coreState; } @Override - protected void channelRead0( ChannelHandlerContext ctx, CoreSnapshotRequest msg ) throws Exception + protected void doChannelRead0( ChannelHandlerContext ctx, CoreSnapshotRequest msg ) throws Exception { sendStates( ctx, coreState.snapshot() ); protocol.expect( State.MESSAGE_TYPE ); diff --git a/enterprise/core-edge/src/main/java/org/neo4j/coreedge/core/state/snapshot/CoreSnapshotResponseHandler.java b/enterprise/core-edge/src/main/java/org/neo4j/coreedge/core/state/snapshot/CoreSnapshotResponseHandler.java index b964d97bb64f6..f4220606a6197 100644 --- a/enterprise/core-edge/src/main/java/org/neo4j/coreedge/core/state/snapshot/CoreSnapshotResponseHandler.java +++ b/enterprise/core-edge/src/main/java/org/neo4j/coreedge/core/state/snapshot/CoreSnapshotResponseHandler.java @@ -20,23 +20,29 @@ package org.neo4j.coreedge.core.state.snapshot; import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.SimpleChannelInboundHandler; + +import java.util.function.Predicate; import org.neo4j.coreedge.catchup.CatchupClientProtocol; +import org.neo4j.coreedge.VersionCheckerChannelInboundHandler; +import org.neo4j.coreedge.messaging.Message; +import org.neo4j.logging.LogProvider; -public class CoreSnapshotResponseHandler extends SimpleChannelInboundHandler +public class CoreSnapshotResponseHandler extends VersionCheckerChannelInboundHandler { private final CatchupClientProtocol protocol; private final CoreSnapshotListener listener; - public CoreSnapshotResponseHandler( CatchupClientProtocol protocol, CoreSnapshotListener listener ) + public CoreSnapshotResponseHandler( Predicate versionChecker, CatchupClientProtocol protocol, + CoreSnapshotListener listener, LogProvider logProvider ) { + super( versionChecker, logProvider ); this.protocol = protocol; this.listener = listener; } @Override - protected void channelRead0( ChannelHandlerContext ctx, final CoreSnapshot coreSnapshot ) throws Exception + protected void doChannelRead0( ChannelHandlerContext ctx, final CoreSnapshot coreSnapshot ) throws Exception { if ( protocol.isExpecting( CatchupClientProtocol.State.CORE_SNAPSHOT ) ) { diff --git a/enterprise/core-edge/src/main/java/org/neo4j/coreedge/messaging/BaseMessage.java b/enterprise/core-edge/src/main/java/org/neo4j/coreedge/messaging/BaseMessage.java new file mode 100644 index 0000000000000..c43ced7f8a350 --- /dev/null +++ b/enterprise/core-edge/src/main/java/org/neo4j/coreedge/messaging/BaseMessage.java @@ -0,0 +1,59 @@ +/* + * 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.messaging; + +import java.util.Objects; + +public class BaseMessage implements Message +{ + private final byte version; + + public BaseMessage( byte version ) + { + this.version = version; + } + + @Override + public final byte version() + { + return version; + } + + @Override + public boolean equals( Object o ) + { + if ( this == o ) + { + return true; + } + if ( o == null || getClass() != o.getClass() ) + { + return false; + } + BaseMessage that = (BaseMessage) o; + return version == that.version; + } + + @Override + public int hashCode() + { + return Objects.hash( version ); + } +} diff --git a/enterprise/core-edge/src/main/java/org/neo4j/coreedge/messaging/Message.java b/enterprise/core-edge/src/main/java/org/neo4j/coreedge/messaging/Message.java index bc01453437ad7..e738cf4f62e2f 100644 --- a/enterprise/core-edge/src/main/java/org/neo4j/coreedge/messaging/Message.java +++ b/enterprise/core-edge/src/main/java/org/neo4j/coreedge/messaging/Message.java @@ -24,4 +24,7 @@ */ public interface Message { + byte CURRENT_VERSION = 0; + + byte version(); } diff --git a/enterprise/core-edge/src/main/java/org/neo4j/coreedge/messaging/marsalling/RaftMessageDecoder.java b/enterprise/core-edge/src/main/java/org/neo4j/coreedge/messaging/marsalling/RaftMessageDecoder.java index 4879e9940ee46..7c25aee0d1cfd 100644 --- a/enterprise/core-edge/src/main/java/org/neo4j/coreedge/messaging/marsalling/RaftMessageDecoder.java +++ b/enterprise/core-edge/src/main/java/org/neo4j/coreedge/messaging/marsalling/RaftMessageDecoder.java @@ -28,12 +28,13 @@ import org.neo4j.coreedge.core.consensus.RaftMessages; import org.neo4j.coreedge.core.consensus.log.RaftLogEntry; -import org.neo4j.coreedge.messaging.NetworkReadableClosableChannelNetty4; import org.neo4j.coreedge.core.replication.ReplicatedContent; -import org.neo4j.coreedge.messaging.marsalling.storeid.StoreIdMarshal; -import org.neo4j.coreedge.messaging.EndOfStreamException; import org.neo4j.coreedge.identity.MemberId; import org.neo4j.coreedge.identity.StoreId; +import org.neo4j.coreedge.messaging.EndOfStreamException; +import org.neo4j.coreedge.messaging.Message; +import org.neo4j.coreedge.messaging.NetworkReadableClosableChannelNetty4; +import org.neo4j.coreedge.messaging.marsalling.storeid.StoreIdMarshal; import org.neo4j.storageengine.api.ReadableChannel; import static org.neo4j.coreedge.core.consensus.RaftMessages.Type.APPEND_ENTRIES_REQUEST; @@ -57,6 +58,7 @@ public RaftMessageDecoder( ChannelMarshal marshal ) protected void decode( ChannelHandlerContext ctx, ByteBuf buffer, List list ) throws Exception { ReadableChannel channel = new NetworkReadableClosableChannelNetty4( buffer ); + byte version = channel.get(); StoreId storeId = StoreIdMarshal.unmarshal( channel ); int messageTypeWire = channel.getInt(); @@ -74,15 +76,14 @@ protected void decode( ChannelHandlerContext ctx, ByteBuf buffer, List l long lastLogIndex = channel.getLong(); long lastLogTerm = channel.getLong(); - result = new RaftMessages.Vote.Request( - from, term, candidate, lastLogIndex, lastLogTerm ); + result = new RaftMessages.Vote.Request( version, from, term, candidate, lastLogIndex, lastLogTerm ); } else if ( messageType.equals( VOTE_RESPONSE ) ) { long term = channel.getLong(); boolean voteGranted = channel.get() == 1; - result = new RaftMessages.Vote.Response( from, term, voteGranted ); + result = new RaftMessages.Vote.Response( version, from, term, voteGranted ); } else if ( messageType.equals( APPEND_ENTRIES_REQUEST ) ) { @@ -102,7 +103,7 @@ else if ( messageType.equals( APPEND_ENTRIES_REQUEST ) ) entries[i] = new RaftLogEntry( entryTerm, content ); } - result = new RaftMessages.AppendEntries.Request( from, term, prevLogIndex, prevLogTerm, entries, + result = new RaftMessages.AppendEntries.Request( version, from, term, prevLogIndex, prevLogTerm, entries, leaderCommit ); } else if ( messageType.equals( APPEND_ENTRIES_RESPONSE ) ) @@ -112,13 +113,13 @@ else if ( messageType.equals( APPEND_ENTRIES_RESPONSE ) ) long matchIndex = channel.getLong(); long appendIndex = channel.getLong(); - result = new RaftMessages.AppendEntries.Response( from, term, success, matchIndex, appendIndex ); + result = new RaftMessages.AppendEntries.Response( version, from, term, success, matchIndex, appendIndex ); } else if ( messageType.equals( NEW_ENTRY_REQUEST ) ) { ReplicatedContent content = marshal.unmarshal( channel ); - result = new RaftMessages.NewEntry.Request( from, content ); + result = new RaftMessages.NewEntry.Request( version, from, content ); } else if ( messageType.equals( HEARTBEAT ) ) { @@ -126,14 +127,14 @@ else if ( messageType.equals( HEARTBEAT ) ) long commitIndexTerm = channel.getLong(); long commitIndex = channel.getLong(); - result = new RaftMessages.Heartbeat( from, leaderTerm, commitIndex, commitIndexTerm ); + result = new RaftMessages.Heartbeat( version, from, leaderTerm, commitIndex, commitIndexTerm ); } else if ( messageType.equals( LOG_COMPACTION_INFO ) ) { long leaderTerm = channel.getLong(); long prevIndex = channel.getLong(); - result = new RaftMessages.LogCompactionInfo( from, leaderTerm, prevIndex ); + result = new RaftMessages.LogCompactionInfo( version, from, leaderTerm, prevIndex ); } else { diff --git a/enterprise/core-edge/src/main/java/org/neo4j/coreedge/messaging/marsalling/RaftMessageEncoder.java b/enterprise/core-edge/src/main/java/org/neo4j/coreedge/messaging/marsalling/RaftMessageEncoder.java index 8d355f26c6ffe..a5f538fdd7246 100644 --- a/enterprise/core-edge/src/main/java/org/neo4j/coreedge/messaging/marsalling/RaftMessageEncoder.java +++ b/enterprise/core-edge/src/main/java/org/neo4j/coreedge/messaging/marsalling/RaftMessageEncoder.java @@ -24,13 +24,13 @@ import java.util.List; -import org.neo4j.coreedge.messaging.NetworkFlushableByteBuf; import org.neo4j.coreedge.core.consensus.RaftMessages; import org.neo4j.coreedge.core.consensus.log.RaftLogEntry; import org.neo4j.coreedge.core.replication.ReplicatedContent; -import org.neo4j.coreedge.messaging.marsalling.storeid.StoreIdMarshal; import org.neo4j.coreedge.identity.MemberId; import org.neo4j.coreedge.identity.StoreId; +import org.neo4j.coreedge.messaging.NetworkFlushableByteBuf; +import org.neo4j.coreedge.messaging.marsalling.storeid.StoreIdMarshal; public class RaftMessageEncoder extends MessageToMessageEncoder { @@ -51,6 +51,7 @@ protected synchronized void encode( ChannelHandlerContext ctx, MemberId.MemberIdMarshal memberMarshal = new MemberId.MemberIdMarshal(); NetworkFlushableByteBuf channel = new NetworkFlushableByteBuf( ctx.alloc().buffer() ); + channel.put( message.version() ); StoreIdMarshal.marshal( storeId, channel ); channel.putInt( message.type().ordinal() ); memberMarshal.marshal( message.from(), channel ); diff --git a/enterprise/core-edge/src/test/java/org/neo4j/coreedge/VersionCheckerChannelInboundHandlerTest.java b/enterprise/core-edge/src/test/java/org/neo4j/coreedge/VersionCheckerChannelInboundHandlerTest.java new file mode 100644 index 0000000000000..44aba5a67e9e6 --- /dev/null +++ b/enterprise/core-edge/src/test/java/org/neo4j/coreedge/VersionCheckerChannelInboundHandlerTest.java @@ -0,0 +1,87 @@ +/* + * 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; + +import io.netty.channel.ChannelHandlerContext; +import org.junit.Test; + +import java.util.concurrent.atomic.AtomicReference; +import java.util.function.Predicate; + +import org.neo4j.coreedge.messaging.BaseMessage; +import org.neo4j.coreedge.messaging.Message; +import org.neo4j.logging.AssertableLogProvider; + +import static org.junit.Assert.assertSame; +import static org.neo4j.logging.AssertableLogProvider.inLog; + +public class VersionCheckerChannelInboundHandlerTest +{ + private final AssertableLogProvider logProvider = new AssertableLogProvider(); + + @Test + public void shouldDiscardMessageWithUnknownVersionAndLogAnError() throws Exception + { + // given + Predicate versionChecker = ( m ) -> false; + VersionCheckerChannelInboundHandler handler = + new VersionCheckerChannelInboundHandler( versionChecker, logProvider ) + { + @Override + protected void doChannelRead0( ChannelHandlerContext ctx, Message message ) throws Exception + { + throw new UnsupportedOperationException( "nope" ); + } + }; + + // when + BaseMessage message = new BaseMessage( (byte) 42 ); + handler.channelRead0( null, message ); + + // then + logProvider.assertExactly( inLog( handler.getClass() ) + .error( "Unsupported version %d, unable to process message %s", message.version(), message ) ); + } + + @Test + public void shouldHandleMessageWithCorrectVersion() throws Exception + { + // given + Predicate versionChecker = ( m ) -> true; + AtomicReference processedMessage = new AtomicReference<>(); + VersionCheckerChannelInboundHandler handler = + new VersionCheckerChannelInboundHandler( versionChecker, logProvider ) + { + @Override + protected void doChannelRead0( ChannelHandlerContext ctx, Message message ) throws Exception + { + processedMessage.set( message ); + } + }; + + // when + BaseMessage message = new BaseMessage( (byte) 42 ); + handler.channelRead0( null, message ); + + // then + logProvider.assertNoLoggingOccurred(); + assertSame( message, processedMessage.get() ); + } +} diff --git a/enterprise/core-edge/src/test/java/org/neo4j/coreedge/catchup/storecopy/StoreCopyFinishedResponseEncodeDecodeTest.java b/enterprise/core-edge/src/test/java/org/neo4j/coreedge/catchup/storecopy/StoreCopyFinishedResponseEncodeDecodeTest.java index 087ba0e825822..972e8c0d6a8e7 100644 --- a/enterprise/core-edge/src/test/java/org/neo4j/coreedge/catchup/storecopy/StoreCopyFinishedResponseEncodeDecodeTest.java +++ b/enterprise/core-edge/src/test/java/org/neo4j/coreedge/catchup/storecopy/StoreCopyFinishedResponseEncodeDecodeTest.java @@ -24,6 +24,7 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotSame; +import static org.neo4j.coreedge.messaging.Message.CURRENT_VERSION; public class StoreCopyFinishedResponseEncodeDecodeTest { @@ -34,7 +35,7 @@ public void shouldEncodeAndDecodePullRequestMessage() EmbeddedChannel channel = new EmbeddedChannel( new StoreCopyFinishedResponseEncoder(), new StoreCopyFinishedResponseDecoder() ); final long arbitraryId = 23; - StoreCopyFinishedResponse sent = new StoreCopyFinishedResponse( arbitraryId ); + StoreCopyFinishedResponse sent = new StoreCopyFinishedResponse( CURRENT_VERSION, arbitraryId ); // when channel.writeOutbound( sent ); @@ -45,5 +46,4 @@ public void shouldEncodeAndDecodePullRequestMessage() assertNotSame( sent, received ); assertEquals( sent, received ); } - } diff --git a/enterprise/core-edge/src/test/java/org/neo4j/coreedge/catchup/tx/TxPollingClientTest.java b/enterprise/core-edge/src/test/java/org/neo4j/coreedge/catchup/tx/TxPollingClientTest.java index f7200584239a3..7eae02d7d8bce 100644 --- a/enterprise/core-edge/src/test/java/org/neo4j/coreedge/catchup/tx/TxPollingClientTest.java +++ b/enterprise/core-edge/src/test/java/org/neo4j/coreedge/catchup/tx/TxPollingClientTest.java @@ -26,6 +26,7 @@ import org.neo4j.coreedge.core.consensus.schedule.ControlledRenewableTimeoutService; import org.neo4j.coreedge.identity.MemberId; import org.neo4j.coreedge.identity.StoreId; +import org.neo4j.coreedge.messaging.Message; import org.neo4j.coreedge.messaging.routing.CoreMemberSelectionStrategy; import org.neo4j.kernel.impl.transaction.CommittedTransactionRepresentation; import org.neo4j.kernel.impl.transaction.log.TransactionIdStore; @@ -40,6 +41,7 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import static org.neo4j.coreedge.catchup.tx.TxPollingClient.Timeouts.TX_PULLER_TIMEOUT; +import static org.neo4j.coreedge.messaging.Message.CURRENT_VERSION; import static org.neo4j.kernel.impl.transaction.log.TransactionIdStore.BASE_TX_ID; public class TxPollingClientTest @@ -102,7 +104,8 @@ public void shouldResetTxReceivedTimeoutOnTxReceived() throws Throwable // when StoreId storeId = new StoreId( 1, 2, 3, 4 ); - txPuller.onTxReceived( new TxPullResponse( storeId, mock( CommittedTransactionRepresentation.class ) ) ); + txPuller.onTxReceived( + new TxPullResponse( CURRENT_VERSION, storeId, mock( CommittedTransactionRepresentation.class ) ) ); // then verify( timeoutService.getTimeout( TX_PULLER_TIMEOUT ), times( 2 ) ).renew(); diff --git a/enterprise/core-edge/src/test/java/org/neo4j/coreedge/catchup/tx/TxPullRequestEncodeDecodeTest.java b/enterprise/core-edge/src/test/java/org/neo4j/coreedge/catchup/tx/TxPullRequestEncodeDecodeTest.java index 03e13fc078587..80c966ee65bf5 100644 --- a/enterprise/core-edge/src/test/java/org/neo4j/coreedge/catchup/tx/TxPullRequestEncodeDecodeTest.java +++ b/enterprise/core-edge/src/test/java/org/neo4j/coreedge/catchup/tx/TxPullRequestEncodeDecodeTest.java @@ -26,6 +26,7 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotSame; +import static org.neo4j.coreedge.messaging.Message.CURRENT_VERSION; public class TxPullRequestEncodeDecodeTest { @@ -35,7 +36,7 @@ public void shouldEncodeAndDecodePullRequestMessage() // given EmbeddedChannel channel = new EmbeddedChannel( new TxPullRequestEncoder(), new TxPullRequestDecoder() ); final long arbitraryId = 23; - TxPullRequest sent = new TxPullRequest( arbitraryId, new StoreId( 1, 2, 3, 4 ) ); + TxPullRequest sent = new TxPullRequest( CURRENT_VERSION, arbitraryId, new StoreId( 1, 2, 3, 4 ) ); // when channel.writeOutbound( sent ); @@ -46,5 +47,4 @@ public void shouldEncodeAndDecodePullRequestMessage() assertNotSame( sent, received ); assertEquals( sent, received ); } - } diff --git a/enterprise/core-edge/src/test/java/org/neo4j/coreedge/catchup/tx/TxPullRequestHandlerTest.java b/enterprise/core-edge/src/test/java/org/neo4j/coreedge/catchup/tx/TxPullRequestHandlerTest.java index d4983b45cd011..71925db358f5f 100644 --- a/enterprise/core-edge/src/test/java/org/neo4j/coreedge/catchup/tx/TxPullRequestHandlerTest.java +++ b/enterprise/core-edge/src/test/java/org/neo4j/coreedge/catchup/tx/TxPullRequestHandlerTest.java @@ -25,6 +25,7 @@ import org.neo4j.coreedge.catchup.CatchupServerProtocol; import org.neo4j.coreedge.catchup.ResponseMessageType; import org.neo4j.coreedge.identity.StoreId; +import org.neo4j.coreedge.messaging.Message; import org.neo4j.kernel.impl.transaction.CommittedTransactionRepresentation; import org.neo4j.kernel.impl.transaction.command.Commands; import org.neo4j.kernel.impl.transaction.log.LogPosition; @@ -42,6 +43,7 @@ import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import static org.neo4j.coreedge.messaging.Message.CURRENT_VERSION; import static org.neo4j.kernel.impl.transaction.command.Commands.createNode; import static org.neo4j.kernel.impl.util.Cursors.cursor; import static org.neo4j.kernel.impl.util.Cursors.io; @@ -65,22 +67,22 @@ public void shouldRespondWithStreamOfTransactions() throws Exception tx( 15 ) ) ) ); - TxPullRequestHandler txPullRequestHandler = new TxPullRequestHandler( new CatchupServerProtocol(), + TxPullRequestHandler txPullRequestHandler = new TxPullRequestHandler( (m) -> true, new CatchupServerProtocol(), () -> storeId, () -> transactionIdStore, () -> logicalTransactionStore, new Monitors(), NullLogProvider.getInstance() ); ChannelHandlerContext context = mock( ChannelHandlerContext.class ); // when - txPullRequestHandler.channelRead0( context, new TxPullRequest( 12, storeId ) ); + txPullRequestHandler.doChannelRead0( context, new TxPullRequest( CURRENT_VERSION, 12, storeId ) ); // then verify( context, times( 3 ) ).write( ResponseMessageType.TX ); - verify( context ).write( new TxPullResponse( storeId, tx( 13 ) ) ); - verify( context ).write( new TxPullResponse( storeId, tx( 14 ) ) ); - verify( context ).write( new TxPullResponse( storeId, tx( 15 ) ) ); + verify( context ).write( new TxPullResponse( CURRENT_VERSION, storeId, tx( 13 ) ) ); + verify( context ).write( new TxPullResponse( CURRENT_VERSION, storeId, tx( 14 ) ) ); + verify( context ).write( new TxPullResponse( CURRENT_VERSION, storeId, tx( 15 ) ) ); verify( context ).write( ResponseMessageType.TX_STREAM_FINISHED ); - verify( context ).write( new TxStreamFinishedResponse( 15, true ) ); + verify( context ).write( new TxStreamFinishedResponse( CURRENT_VERSION, 15, true ) ); } @Test @@ -96,18 +98,18 @@ public void shouldRespondWithoutTransactionsIfTheyDoNotExist() throws Exception when( logicalTransactionStore.getTransactions( 13L ) ).thenThrow( new NoSuchTransactionException( 13 ) ); AssertableLogProvider logProvider = new AssertableLogProvider(); - TxPullRequestHandler txPullRequestHandler = new TxPullRequestHandler( new CatchupServerProtocol(), + TxPullRequestHandler txPullRequestHandler = new TxPullRequestHandler( (m) -> true, new CatchupServerProtocol(), () -> storeId, () -> transactionIdStore, () -> logicalTransactionStore, new Monitors(), logProvider ); ChannelHandlerContext context = mock( ChannelHandlerContext.class ); // when - txPullRequestHandler.channelRead0( context, new TxPullRequest( 12, storeId ) ); + txPullRequestHandler.doChannelRead0( context, new TxPullRequest( CURRENT_VERSION, 12, storeId ) ); // then verify( context, never() ).write( ResponseMessageType.TX ); verify( context ).write( ResponseMessageType.TX_STREAM_FINISHED ); - verify( context ).write( new TxStreamFinishedResponse( 12, false ) ); + verify( context ).write( new TxStreamFinishedResponse( CURRENT_VERSION, 12, false ) ); logProvider.assertAtLeastOnce( inLog( TxPullRequestHandler.class ) .info( "Failed to serve TxPullRequest for tx %d because the transaction does not exist.", 12L ) ); } @@ -123,18 +125,18 @@ public void shouldNotStreamTxEntriesIfStoreIdMismatches() throws Exception LogicalTransactionStore logicalTransactionStore = mock( LogicalTransactionStore.class ); AssertableLogProvider logProvider = new AssertableLogProvider(); - TxPullRequestHandler txPullRequestHandler = new TxPullRequestHandler( new CatchupServerProtocol(), + TxPullRequestHandler txPullRequestHandler = new TxPullRequestHandler( (m) -> true, new CatchupServerProtocol(), () -> serverStoreId, () -> transactionIdStore, () -> logicalTransactionStore, new Monitors(), logProvider ); ChannelHandlerContext context = mock( ChannelHandlerContext.class ); // when - txPullRequestHandler.channelRead0( context, new TxPullRequest( 1, clientStoreId ) ); + txPullRequestHandler.doChannelRead0( context, new TxPullRequest( CURRENT_VERSION, 1, clientStoreId ) ); // then verify( context, never() ).write( ResponseMessageType.TX ); verify( context ).write( ResponseMessageType.TX_STREAM_FINISHED ); - verify( context ).write( new TxStreamFinishedResponse( 1, false ) ); + verify( context ).write( new TxStreamFinishedResponse( CURRENT_VERSION, 1, false ) ); logProvider.assertAtLeastOnce( inLog( TxPullRequestHandler.class ) .info( "Failed to serve TxPullRequest for tx %d and storeId %s because that storeId is different " + "from this machine with %s", 1L, clientStoreId, serverStoreId ) ); diff --git a/enterprise/core-edge/src/test/java/org/neo4j/coreedge/catchup/tx/TxPullResponseEncodeDecodeTest.java b/enterprise/core-edge/src/test/java/org/neo4j/coreedge/catchup/tx/TxPullResponseEncodeDecodeTest.java index 685ff85f0c268..5183aefdaaf9f 100644 --- a/enterprise/core-edge/src/test/java/org/neo4j/coreedge/catchup/tx/TxPullResponseEncodeDecodeTest.java +++ b/enterprise/core-edge/src/test/java/org/neo4j/coreedge/catchup/tx/TxPullResponseEncodeDecodeTest.java @@ -23,6 +23,7 @@ import org.junit.Test; import org.neo4j.coreedge.identity.StoreId; +import org.neo4j.coreedge.messaging.Message; import org.neo4j.kernel.impl.store.record.NodeRecord; import org.neo4j.kernel.impl.transaction.CommittedTransactionRepresentation; import org.neo4j.kernel.impl.transaction.command.Command; @@ -35,6 +36,7 @@ import static java.util.Arrays.asList; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotSame; +import static org.neo4j.coreedge.messaging.Message.CURRENT_VERSION; public class TxPullResponseEncodeDecodeTest { @@ -43,7 +45,7 @@ public void shouldEncodeAndDecodePullResponseMessage() { // given EmbeddedChannel channel = new EmbeddedChannel( new TxPullResponseEncoder(), new TxPullResponseDecoder() ); - TxPullResponse sent = new TxPullResponse( new StoreId( 1, 2, 3, 4 ), newCommittedTransactionRepresentation() ); + TxPullResponse sent = new TxPullResponse( CURRENT_VERSION, new StoreId( 1, 2, 3, 4 ), newCommittedTransactionRepresentation() ); // when channel.writeOutbound( sent ); diff --git a/enterprise/core-edge/src/test/java/org/neo4j/coreedge/catchup/tx/TxStreamFinishedResponseEncodeDecodeTest.java b/enterprise/core-edge/src/test/java/org/neo4j/coreedge/catchup/tx/TxStreamFinishedResponseEncodeDecodeTest.java index df35094790b6e..1c4d1d518d92a 100644 --- a/enterprise/core-edge/src/test/java/org/neo4j/coreedge/catchup/tx/TxStreamFinishedResponseEncodeDecodeTest.java +++ b/enterprise/core-edge/src/test/java/org/neo4j/coreedge/catchup/tx/TxStreamFinishedResponseEncodeDecodeTest.java @@ -22,8 +22,11 @@ import io.netty.channel.embedded.EmbeddedChannel; import org.junit.Test; +import org.neo4j.coreedge.messaging.Message; + import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotSame; +import static org.neo4j.coreedge.messaging.Message.CURRENT_VERSION; public class TxStreamFinishedResponseEncodeDecodeTest { @@ -34,7 +37,7 @@ public void shouldEncodeAndDecodePullRequestMessage() EmbeddedChannel channel = new EmbeddedChannel( new TxStreamFinishedResponseEncoder(), new TxStreamFinishedResponseDecoder() ); final long arbitraryId = 23; - TxStreamFinishedResponse sent = new TxStreamFinishedResponse( arbitraryId, true ); + TxStreamFinishedResponse sent = new TxStreamFinishedResponse( CURRENT_VERSION, arbitraryId, true ); // when channel.writeOutbound( sent ); diff --git a/enterprise/core-edge/src/test/java/org/neo4j/coreedge/core/consensus/CatchUpTest.java b/enterprise/core-edge/src/test/java/org/neo4j/coreedge/core/consensus/CatchUpTest.java index a95180d5f621b..723b4d46dd6ec 100644 --- a/enterprise/core-edge/src/test/java/org/neo4j/coreedge/core/consensus/CatchUpTest.java +++ b/enterprise/core-edge/src/test/java/org/neo4j/coreedge/core/consensus/CatchUpTest.java @@ -30,6 +30,7 @@ import org.neo4j.coreedge.core.consensus.membership.RaftTestGroup; import org.neo4j.coreedge.core.replication.ReplicatedContent; import org.neo4j.coreedge.identity.MemberId; +import org.neo4j.coreedge.messaging.Message; import static org.hamcrest.CoreMatchers.hasItems; import static org.hamcrest.Matchers.empty; @@ -37,6 +38,7 @@ import static org.neo4j.coreedge.core.consensus.ReplicatedInteger.valueOf; import static org.neo4j.coreedge.core.consensus.log.RaftLogHelper.readLogEntry; import static org.neo4j.coreedge.identity.RaftTestMember.member; +import static org.neo4j.coreedge.messaging.Message.CURRENT_VERSION; public class CatchUpTest { @@ -56,7 +58,8 @@ public void happyClusterPropagatesUpdates() throws Throwable // when fixture.members().withId( leader ).timeoutService().invokeTimeout( RaftMachine.Timeouts.ELECTION ); net.processMessages(); - fixture.members().withId( leader ).raftInstance().handle( new Request( leaderMember, valueOf( 42 ) ) ); + fixture.members().withId( leader ).raftInstance() + .handle( new Request( CURRENT_VERSION, leaderMember, valueOf( 42 ) ) ); net.processMessages(); // then @@ -89,10 +92,14 @@ public void newMemberWithNoLogShouldCatchUpFromPeers() throws Throwable net.disconnect( sleepyId ); // when - fixture.members().withId( leaderId ).raftInstance().handle( new Request( leader, valueOf( 10 ) ) ); - fixture.members().withId( leaderId ).raftInstance().handle( new Request( leader, valueOf( 20 ) ) ); - fixture.members().withId( leaderId ).raftInstance().handle( new Request( leader, valueOf( 30 ) ) ); - fixture.members().withId( leaderId ).raftInstance().handle( new Request( leader, valueOf( 40 ) ) ); + fixture.members().withId( leaderId ).raftInstance() + .handle( new Request( CURRENT_VERSION, leader, valueOf( 10 ) ) ); + fixture.members().withId( leaderId ).raftInstance() + .handle( new Request( CURRENT_VERSION, leader, valueOf( 20 ) ) ); + fixture.members().withId( leaderId ).raftInstance() + .handle( new Request( CURRENT_VERSION, leader, valueOf( 30 ) ) ); + fixture.members().withId( leaderId ).raftInstance() + .handle( new Request( CURRENT_VERSION, leader, valueOf( 40 ) ) ); net.processMessages(); // then diff --git a/enterprise/core-edge/src/test/java/org/neo4j/coreedge/core/consensus/HeartbeatBuilder.java b/enterprise/core-edge/src/test/java/org/neo4j/coreedge/core/consensus/HeartbeatBuilder.java index 9b482222128d6..bb5138e4353e4 100644 --- a/enterprise/core-edge/src/test/java/org/neo4j/coreedge/core/consensus/HeartbeatBuilder.java +++ b/enterprise/core-edge/src/test/java/org/neo4j/coreedge/core/consensus/HeartbeatBuilder.java @@ -21,8 +21,11 @@ import org.neo4j.coreedge.identity.MemberId; +import static org.neo4j.coreedge.messaging.Message.CURRENT_VERSION; + public class HeartbeatBuilder { + private byte version = CURRENT_VERSION; private long commitIndex = -1; private long leaderTerm = -1; private long commitIndexTerm = -1; @@ -30,7 +33,13 @@ public class HeartbeatBuilder public RaftMessages.Heartbeat build() { - return new RaftMessages.Heartbeat( from, leaderTerm, commitIndex, commitIndexTerm ); + return new RaftMessages.Heartbeat( version, from, leaderTerm, commitIndex, commitIndexTerm ); + } + + public HeartbeatBuilder version( byte version ) + { + this.version = version; + return this; } public HeartbeatBuilder from( MemberId from ) diff --git a/enterprise/core-edge/src/test/java/org/neo4j/coreedge/core/consensus/explorer/ClusterSafetyViolations.java b/enterprise/core-edge/src/test/java/org/neo4j/coreedge/core/consensus/explorer/ClusterSafetyViolations.java index 6df6f5ea326c8..ba312fbb857a2 100644 --- a/enterprise/core-edge/src/test/java/org/neo4j/coreedge/core/consensus/explorer/ClusterSafetyViolations.java +++ b/enterprise/core-edge/src/test/java/org/neo4j/coreedge/core/consensus/explorer/ClusterSafetyViolations.java @@ -26,7 +26,7 @@ import java.util.Map; import java.util.Set; -import org.neo4j.coreedge.core.consensus.RaftMessageHandler; +import org.neo4j.coreedge.core.consensus.roles.RaftMessageHandler; import org.neo4j.coreedge.core.consensus.log.RaftLogEntry; import org.neo4j.coreedge.core.consensus.roles.Leader; import org.neo4j.coreedge.core.consensus.roles.Role; diff --git a/enterprise/core-edge/src/test/java/org/neo4j/coreedge/core/consensus/explorer/action/NewEntry.java b/enterprise/core-edge/src/test/java/org/neo4j/coreedge/core/consensus/explorer/action/NewEntry.java index 44ac73c870f5d..c3aa4c864ceef 100644 --- a/enterprise/core-edge/src/test/java/org/neo4j/coreedge/core/consensus/explorer/action/NewEntry.java +++ b/enterprise/core-edge/src/test/java/org/neo4j/coreedge/core/consensus/explorer/action/NewEntry.java @@ -28,6 +28,8 @@ import org.neo4j.coreedge.core.consensus.explorer.ClusterState; import org.neo4j.coreedge.identity.MemberId; +import static org.neo4j.coreedge.messaging.Message.CURRENT_VERSION; + public class NewEntry implements Action { private final MemberId member; @@ -42,8 +44,8 @@ public ClusterState advance( ClusterState previous ) throws IOException { ClusterState newClusterState = new ClusterState( previous ); Queue newQueue = new LinkedList<>( previous.queues.get( member ) ); - newQueue.offer( new RaftMessages.NewEntry.Request( member, new ReplicatedString( - "content" ) ) ); + newQueue.offer( + new RaftMessages.NewEntry.Request( CURRENT_VERSION, member, new ReplicatedString( "content" ) ) ); newClusterState.queues.put( member, newQueue ); return newClusterState; } diff --git a/enterprise/core-edge/src/test/java/org/neo4j/coreedge/core/consensus/explorer/action/OutOfOrderDeliveryTest.java b/enterprise/core-edge/src/test/java/org/neo4j/coreedge/core/consensus/explorer/action/OutOfOrderDeliveryTest.java index b3669d3b29c08..b8d464ffea475 100644 --- a/enterprise/core-edge/src/test/java/org/neo4j/coreedge/core/consensus/explorer/action/OutOfOrderDeliveryTest.java +++ b/enterprise/core-edge/src/test/java/org/neo4j/coreedge/core/consensus/explorer/action/OutOfOrderDeliveryTest.java @@ -24,9 +24,11 @@ import org.neo4j.coreedge.core.consensus.RaftMessages.Timeout.Election; import org.neo4j.coreedge.core.consensus.RaftMessages.Timeout.Heartbeat; import org.neo4j.coreedge.core.consensus.explorer.ClusterState; +import org.neo4j.coreedge.messaging.Message; import static org.junit.Assert.assertEquals; import static org.neo4j.coreedge.identity.RaftTestMember.member; +import static org.neo4j.coreedge.messaging.Message.CURRENT_VERSION; import static org.neo4j.helpers.collection.Iterators.asSet; public class OutOfOrderDeliveryTest @@ -36,14 +38,14 @@ public void shouldReOrder() throws Exception { // given ClusterState clusterState = new ClusterState( asSet( member( 0 ) ) ); - clusterState.queues.get( member( 0 ) ).add( new Election( member( 0 ) ) ); - clusterState.queues.get( member( 0 ) ).add( new Heartbeat( member( 0 ) ) ); + clusterState.queues.get( member( 0 ) ).add( new Election( CURRENT_VERSION, member( 0 ) ) ); + clusterState.queues.get( member( 0 ) ).add( new Heartbeat( CURRENT_VERSION, member( 0 ) ) ); // when ClusterState reOrdered = new OutOfOrderDelivery( member( 0 ) ).advance( clusterState ); // then - assertEquals( new Heartbeat( member( 0 ) ), reOrdered.queues.get( member( 0 ) ).poll() ); - assertEquals( new Election( member( 0 ) ), reOrdered.queues.get( member( 0 ) ).poll() ); + assertEquals( new Heartbeat( CURRENT_VERSION, member( 0 ) ), reOrdered.queues.get( member( 0 ) ).poll() ); + assertEquals( new Election( CURRENT_VERSION, member( 0 ) ), reOrdered.queues.get( member( 0 ) ).poll() ); } } diff --git a/enterprise/core-edge/src/test/java/org/neo4j/coreedge/core/consensus/roles/AppendEntriesRequestBuilder.java b/enterprise/core-edge/src/test/java/org/neo4j/coreedge/core/consensus/roles/AppendEntriesRequestBuilder.java index 7b8d236df7e32..7e22d252e9e6f 100644 --- a/enterprise/core-edge/src/test/java/org/neo4j/coreedge/core/consensus/roles/AppendEntriesRequestBuilder.java +++ b/enterprise/core-edge/src/test/java/org/neo4j/coreedge/core/consensus/roles/AppendEntriesRequestBuilder.java @@ -25,9 +25,13 @@ import org.neo4j.coreedge.core.consensus.RaftMessages; import org.neo4j.coreedge.core.consensus.log.RaftLogEntry; import org.neo4j.coreedge.identity.MemberId; +import org.neo4j.coreedge.messaging.Message; + +import static org.neo4j.coreedge.messaging.Message.CURRENT_VERSION; public class AppendEntriesRequestBuilder { + private byte version = CURRENT_VERSION; private List logEntries = new LinkedList<>(); private long leaderCommit = -1; private long prevLogTerm = -1; @@ -37,10 +41,16 @@ public class AppendEntriesRequestBuilder public RaftMessages.AppendEntries.Request build() { - return new RaftMessages.AppendEntries.Request( from, leaderTerm, prevLogIndex, prevLogTerm, + return new RaftMessages.AppendEntries.Request( version, from, leaderTerm, prevLogIndex, prevLogTerm, logEntries.toArray( new RaftLogEntry[logEntries.size()] ), leaderCommit ); } + public AppendEntriesRequestBuilder version( byte version ) + { + this.version = version; + return this; + } + public AppendEntriesRequestBuilder from( MemberId from ) { this.from = from; diff --git a/enterprise/core-edge/src/test/java/org/neo4j/coreedge/core/consensus/roles/AppendEntriesResponseBuilder.java b/enterprise/core-edge/src/test/java/org/neo4j/coreedge/core/consensus/roles/AppendEntriesResponseBuilder.java index cd2e95732f2c0..777da287d9160 100644 --- a/enterprise/core-edge/src/test/java/org/neo4j/coreedge/core/consensus/roles/AppendEntriesResponseBuilder.java +++ b/enterprise/core-edge/src/test/java/org/neo4j/coreedge/core/consensus/roles/AppendEntriesResponseBuilder.java @@ -22,8 +22,11 @@ import org.neo4j.coreedge.core.consensus.RaftMessages; import org.neo4j.coreedge.identity.MemberId; +import static org.neo4j.coreedge.messaging.Message.CURRENT_VERSION; + public class AppendEntriesResponseBuilder { + private byte version = CURRENT_VERSION; private boolean success; private long term = -1; private MemberId from; @@ -34,7 +37,13 @@ public RaftMessages.AppendEntries.Response build() { // a response of false should always have a match index of -1 assert success || matchIndex == -1; - return new RaftMessages.AppendEntries.Response( from, term, success, matchIndex, appendIndex ); + return new RaftMessages.AppendEntries.Response( version, from, term, success, matchIndex, appendIndex ); + } + + public AppendEntriesResponseBuilder version( byte version ) + { + this.version = version; + return this; } public AppendEntriesResponseBuilder from( MemberId from ) diff --git a/enterprise/core-edge/src/test/java/org/neo4j/coreedge/core/consensus/roles/AppendingTest.java b/enterprise/core-edge/src/test/java/org/neo4j/coreedge/core/consensus/roles/AppendingTest.java index ff9a474939153..d9f1338050dcc 100644 --- a/enterprise/core-edge/src/test/java/org/neo4j/coreedge/core/consensus/roles/AppendingTest.java +++ b/enterprise/core-edge/src/test/java/org/neo4j/coreedge/core/consensus/roles/AppendingTest.java @@ -32,6 +32,7 @@ import org.neo4j.coreedge.core.consensus.outcome.TruncateLogCommand; import org.neo4j.coreedge.core.consensus.state.ReadableRaftState; import org.neo4j.coreedge.identity.MemberId; +import org.neo4j.coreedge.messaging.Message; import org.neo4j.logging.NullLog; import static org.junit.Assert.fail; @@ -43,6 +44,7 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import static org.neo4j.coreedge.identity.RaftTestMember.member; +import static org.neo4j.coreedge.messaging.Message.CURRENT_VERSION; public class AppendingTest { @@ -68,10 +70,9 @@ public void shouldPerformTruncation() throws Exception // when // the leader asks to append after the commit index an entry that mismatches on term Appending.handleAppendEntriesRequest( state, outcome, - new RaftMessages.AppendEntries.Request( aMember, localTermForAllEntries, appendIndex - 2, - localTermForAllEntries, - new RaftLogEntry[]{ - new RaftLogEntry( localTermForAllEntries + 1, ReplicatedInteger.valueOf( 2 ) )}, + new RaftMessages.AppendEntries.Request( CURRENT_VERSION, aMember, localTermForAllEntries, + appendIndex - 2, localTermForAllEntries, new RaftLogEntry[]{ + new RaftLogEntry( localTermForAllEntries + 1, ReplicatedInteger.valueOf( 2 ) )}, appendIndex + 3 ), NullLog.getInstance() ); // then @@ -99,10 +100,9 @@ public void shouldNotAllowTruncationAtCommit() throws Exception try { Appending.handleAppendEntriesRequest( state, outcome, - new RaftMessages.AppendEntries.Request( aMember, localTermForAllEntries, commitIndex - 1, - localTermForAllEntries, - new RaftLogEntry[]{ - new RaftLogEntry( localTermForAllEntries + 1, ReplicatedInteger.valueOf( 2 ) )}, + new RaftMessages.AppendEntries.Request( CURRENT_VERSION, aMember, localTermForAllEntries, + commitIndex - 1, localTermForAllEntries, new RaftLogEntry[]{ + new RaftLogEntry( localTermForAllEntries + 1, ReplicatedInteger.valueOf( 2 ) )}, commitIndex + 3 ), NullLog.getInstance() ); fail( "Appending should not allow truncation at or before the commit index" ); } @@ -132,10 +132,9 @@ public void shouldNotAllowTruncationBeforeCommit() throws Exception try { Appending.handleAppendEntriesRequest( state, outcome, - new RaftMessages.AppendEntries.Request( aMember, localTermForAllEntries, commitIndex - 2, - localTermForAllEntries, - new RaftLogEntry[]{ - new RaftLogEntry( localTermForAllEntries + 1, ReplicatedInteger.valueOf( 2 ) )}, + new RaftMessages.AppendEntries.Request( CURRENT_VERSION, aMember, localTermForAllEntries, + commitIndex - 2, localTermForAllEntries, new RaftLogEntry[]{ + new RaftLogEntry( localTermForAllEntries + 1, ReplicatedInteger.valueOf( 2 ) )}, commitIndex + 3 ), NullLog.getInstance() ); fail( "Appending should not allow truncation at or before the commit index" ); } @@ -170,10 +169,8 @@ public void shouldNotAttemptToTruncateAtIndexBeforeTheLogPrevIndex() throws Exce // an appendEntriesRequest arrives for appending entries before the prevIndex (for whatever reason) Outcome outcome = mock( Outcome.class ); Appending.handleAppendEntriesRequest( state, outcome, - new RaftMessages.AppendEntries.Request( aMember, prevTerm, prevIndex - 2, - prevTerm, - new RaftLogEntry[]{ - new RaftLogEntry( prevTerm, ReplicatedInteger.valueOf( 2 ) )}, + new RaftMessages.AppendEntries.Request( CURRENT_VERSION, aMember, prevTerm, prevIndex - 2, prevTerm, + new RaftLogEntry[]{new RaftLogEntry( prevTerm, ReplicatedInteger.valueOf( 2 ) )}, commitIndex + 3 ), NullLog.getInstance() ); // then diff --git a/enterprise/core-edge/src/test/java/org/neo4j/coreedge/core/consensus/roles/ElectionTest.java b/enterprise/core-edge/src/test/java/org/neo4j/coreedge/core/consensus/roles/ElectionTest.java index 79273d3681f15..ef3acddc80a9f 100644 --- a/enterprise/core-edge/src/test/java/org/neo4j/coreedge/core/consensus/roles/ElectionTest.java +++ b/enterprise/core-edge/src/test/java/org/neo4j/coreedge/core/consensus/roles/ElectionTest.java @@ -136,6 +136,6 @@ public void candidateShouldVoteForTheSameCandidateInTheSameTerm() throws Excepti raft.handle( voteRequest().from( member1 ).candidate( member1 ).term( 1 ).build() ); // then - verify( outbound, times( 2 ) ).send( member1, voteResponse().term( 1 ).grant().build() ); + verify( outbound, times( 2 ) ).send( member1, voteResponse().from( myself ).term( 1 ).grant().build() ); } } diff --git a/enterprise/core-edge/src/test/java/org/neo4j/coreedge/core/consensus/roles/FollowerTest.java b/enterprise/core-edge/src/test/java/org/neo4j/coreedge/core/consensus/roles/FollowerTest.java index 117682e21a544..18ffc1cad8cc2 100644 --- a/enterprise/core-edge/src/test/java/org/neo4j/coreedge/core/consensus/roles/FollowerTest.java +++ b/enterprise/core-edge/src/test/java/org/neo4j/coreedge/core/consensus/roles/FollowerTest.java @@ -35,6 +35,7 @@ import org.neo4j.coreedge.core.consensus.outcome.Outcome; import org.neo4j.coreedge.core.consensus.state.RaftState; import org.neo4j.coreedge.identity.MemberId; +import org.neo4j.coreedge.messaging.Message; import org.neo4j.logging.Log; import org.neo4j.logging.NullLogProvider; @@ -49,6 +50,7 @@ import static org.neo4j.coreedge.core.consensus.roles.Role.FOLLOWER; import static org.neo4j.coreedge.core.consensus.state.RaftStateBuilder.raftState; import static org.neo4j.coreedge.identity.RaftTestMember.member; +import static org.neo4j.coreedge.messaging.Message.CURRENT_VERSION; import static org.neo4j.helpers.collection.Iterators.asSet; @RunWith(MockitoJUnitRunner.class) @@ -73,7 +75,7 @@ public void followerShouldTransitToCandidateAndInstigateAnElectionAfterTimeout() .build(); // when - Outcome outcome = new Follower().handle( new Election( myself ), state, log() ); + Outcome outcome = new Follower().handle( new Election( CURRENT_VERSION, myself ), state, log() ); state.update( outcome ); @@ -96,7 +98,7 @@ public void shouldBecomeCandidateOnReceivingElectionTimeoutMessage() throws Exce Follower follower = new Follower(); // when - Outcome outcome = follower.handle( new Election( myself ), state, log() ); + Outcome outcome = follower.handle( new Election( CURRENT_VERSION, myself ), state, log() ); // then assertEquals( CANDIDATE, outcome.getRole() ); @@ -143,18 +145,15 @@ public void shouldTruncateIfTermDoesNotMatch() throws Exception Follower follower = new Follower(); - state.update( follower.handle( new AppendEntries.Request( member1, 1, -1, -1, - new RaftLogEntry[]{ - new RaftLogEntry( 2, ContentGenerator.content() ), - }, - -1 ), state, log() ) ); + state.update( follower.handle( new AppendEntries.Request( CURRENT_VERSION, member1, 1, -1, -1, + new RaftLogEntry[]{new RaftLogEntry( 2, ContentGenerator.content() ),}, -1 ), state, log() ) ); RaftLogEntry[] entries = { new RaftLogEntry( 1, new ReplicatedString( "commit this!" ) ), }; Outcome outcome = follower.handle( - new AppendEntries.Request( member1, 1, -1, -1, entries, -1 ), state, log() ); + new AppendEntries.Request( CURRENT_VERSION, member1, 1, -1, -1, entries, -1 ), state, log() ); state.update( outcome ); // then @@ -176,8 +175,8 @@ public void followerLearningAboutHigherCommitCausesValuesTobeAppliedToItsLog() t appendSomeEntriesToLog( state, follower, 3, 0 ); // when receiving AppEntries with high leader commit (3) - Outcome outcome = follower.handle( new AppendEntries.Request( myself, 0, 2, 0, - new RaftLogEntry[] { new RaftLogEntry( 0, ContentGenerator.content() ) }, 3 ), state, log() ); + Outcome outcome = follower.handle( new AppendEntries.Request( CURRENT_VERSION, myself, 0, 2, 0, + new RaftLogEntry[]{new RaftLogEntry( 0, ContentGenerator.content() )}, 3 ), state, log() ); state.update( outcome ); @@ -236,8 +235,8 @@ public void shouldRenewElectionTimeoutOnReceiptOfHeartbeatInCurrentOrHigherTerm( Follower follower = new Follower(); - Outcome outcome = follower.handle( new RaftMessages.Heartbeat( myself, 1, 1, 1 ), - state, log() ); + Outcome outcome = + follower.handle( new RaftMessages.Heartbeat( CURRENT_VERSION, myself, 1, 1, 1 ), state, log() ); // then assertTrue( outcome.electionTimeoutRenewed() ); @@ -254,8 +253,8 @@ public void shouldNotRenewElectionTimeoutOnReceiptOfHeartbeatInLowerTerm() throw Follower follower = new Follower(); - Outcome outcome = follower.handle( new RaftMessages.Heartbeat( myself, 1, 1, 1 ), - state, log() ); + Outcome outcome = + follower.handle( new RaftMessages.Heartbeat( CURRENT_VERSION, myself, 1, 1, 1 ), state, log() ); // then assertFalse( outcome.electionTimeoutRenewed() ); @@ -268,15 +267,13 @@ private void appendSomeEntriesToLog( RaftState raft, Follower follower, int numb { if ( i == 0 ) { - raft.update( follower.handle( new AppendEntries.Request( myself, term, i - 1, -1, - new RaftLogEntry[] { new RaftLogEntry( term, ContentGenerator.content() ) }, -1 - ), raft, log() ) ); + raft.update( follower.handle( new AppendEntries.Request( CURRENT_VERSION, myself, term, i - 1, -1, + new RaftLogEntry[]{new RaftLogEntry( term, ContentGenerator.content() )}, -1 ), raft, log() ) ); } else { - raft.update( follower.handle( new AppendEntries.Request( myself, term, i - 1, term, - new RaftLogEntry[]{new RaftLogEntry( term, ContentGenerator.content() )}, -1 ), raft, - log() ) ); + raft.update( follower.handle( new AppendEntries.Request( CURRENT_VERSION, myself, term, i - 1, term, + new RaftLogEntry[]{new RaftLogEntry( term, ContentGenerator.content() )}, -1 ), raft, log() ) ); } } } diff --git a/enterprise/core-edge/src/test/java/org/neo4j/coreedge/core/consensus/roles/LeaderTest.java b/enterprise/core-edge/src/test/java/org/neo4j/coreedge/core/consensus/roles/LeaderTest.java index 31c126ef101be..2762912c1ebe6 100644 --- a/enterprise/core-edge/src/test/java/org/neo4j/coreedge/core/consensus/roles/LeaderTest.java +++ b/enterprise/core-edge/src/test/java/org/neo4j/coreedge/core/consensus/roles/LeaderTest.java @@ -34,6 +34,7 @@ import org.neo4j.coreedge.core.consensus.log.RaftLogEntry; import org.neo4j.coreedge.core.consensus.log.ReadableRaftLog; import org.neo4j.coreedge.messaging.Inbound; +import org.neo4j.coreedge.messaging.Message; import org.neo4j.coreedge.messaging.Outbound; import org.neo4j.coreedge.core.consensus.outcome.AppendLogEntry; import org.neo4j.coreedge.core.consensus.outcome.BatchAppendLogEntries; @@ -60,6 +61,7 @@ import static org.neo4j.coreedge.core.consensus.roles.Role.FOLLOWER; import static org.neo4j.coreedge.core.consensus.state.RaftStateBuilder.raftState; import static org.neo4j.coreedge.identity.RaftTestMember.member; +import static org.neo4j.coreedge.messaging.Message.CURRENT_VERSION; import static org.neo4j.helpers.collection.Iterables.count; import static org.neo4j.helpers.collection.Iterables.single; import static org.neo4j.helpers.collection.Iterators.asSet; @@ -363,7 +365,7 @@ public void leaderShouldSendHeartbeatsToAllClusterMembersOnReceiptOfHeartbeatTic Leader leader = new Leader(); // when - Outcome outcome = leader.handle( new Heartbeat( member1 ), state, log() ); + Outcome outcome = leader.handle( new Heartbeat( CURRENT_VERSION, member1 ), state, log() ); // then assertTrue( messageFor( outcome, member1 ) instanceof RaftMessages.Heartbeat ); @@ -381,8 +383,8 @@ public void leaderShouldDecideToAppendToItsLogAndSendAppendEntriesMessageOnRecei Leader leader = new Leader(); - RaftMessages.NewEntry.Request newEntryRequest = new RaftMessages.NewEntry.Request( - member( 9 ), CONTENT ); + RaftMessages.NewEntry.Request newEntryRequest = + new RaftMessages.NewEntry.Request( CURRENT_VERSION, member( 9 ), CONTENT ); // when Outcome outcome = leader.handle( newEntryRequest, state, log() ); @@ -410,7 +412,7 @@ public void leaderShouldHandleBatch() throws Exception int BATCH_SIZE = 3; RaftMessages.NewEntry.BatchRequest batchRequest = - new RaftMessages.NewEntry.BatchRequest( BATCH_SIZE ); + new RaftMessages.NewEntry.BatchRequest( CURRENT_VERSION, BATCH_SIZE ); batchRequest.add( valueOf( 0 ) ); batchRequest.add( valueOf( 1 ) ); batchRequest.add( valueOf( 2 ) ); @@ -457,9 +459,9 @@ public void leaderShouldCommitOnMajorityResponse() throws Exception Leader leader = new Leader(); // when a single instance responds (plus self == 2 out of 3 instances) - Outcome outcome = leader.handle( - new RaftMessages.AppendEntries.Response( member1, 0, true, 0, 0 ), - state, log() ); + Outcome outcome = + leader.handle( new RaftMessages.AppendEntries.Response( CURRENT_VERSION, member1, 0, true, 0, 0 ), + state, log() ); // then assertEquals( 0L, outcome.getCommitIndex() ); @@ -487,8 +489,7 @@ public void leaderShouldCommitAllPreviouslyAppendedEntriesWhenCommittingLaterEnt // when Outcome outcome = - leader.handle( new AppendEntries.Response( member1, 0, true, 2, 2 ), - state, log() ); + leader.handle( new AppendEntries.Response( CURRENT_VERSION, member1, 0, true, 2, 2 ), state, log() ); state.update( outcome ); diff --git a/enterprise/core-edge/src/test/java/org/neo4j/coreedge/core/consensus/shipping/RaftLogShipperTest.java b/enterprise/core-edge/src/test/java/org/neo4j/coreedge/core/consensus/shipping/RaftLogShipperTest.java index 0617611e2aa37..23774b9c155fe 100644 --- a/enterprise/core-edge/src/test/java/org/neo4j/coreedge/core/consensus/shipping/RaftLogShipperTest.java +++ b/enterprise/core-edge/src/test/java/org/neo4j/coreedge/core/consensus/shipping/RaftLogShipperTest.java @@ -39,6 +39,7 @@ import org.neo4j.coreedge.core.consensus.log.RaftLogEntry; import org.neo4j.coreedge.core.consensus.log.segmented.InFlightMap; import org.neo4j.coreedge.identity.MemberId; +import org.neo4j.coreedge.messaging.Message; import org.neo4j.helpers.collection.Iterables; import org.neo4j.logging.Log; import org.neo4j.logging.LogProvider; @@ -52,6 +53,7 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import static org.neo4j.coreedge.identity.RaftTestMember.member; +import static org.neo4j.coreedge.messaging.Message.CURRENT_VERSION; import static org.neo4j.test.matchers.Matchers.hasMessage; public class RaftLogShipperTest @@ -121,7 +123,9 @@ public void shouldSendLastEntryOnStart() throws Throwable startLogShipper(); // then - RaftMessages.AppendEntries.Request expected = new RaftMessages.AppendEntries.Request( leader, leaderTerm, 0, entry0.term(), RaftLogEntry.empty, leaderCommit ); + RaftMessages.AppendEntries.Request expected = + new RaftMessages.AppendEntries.Request( CURRENT_VERSION, leader, leaderTerm, 0, entry0.term(), + RaftLogEntry.empty, leaderCommit ); assertThat( outbound.sentTo( follower ), hasItem( expected ) ); } @@ -138,7 +142,9 @@ public void shouldSendPreviousEntryOnMismatch() throws Throwable logShipper.onMismatch( 0, new LeaderContext( 0, 0 ) ); // then - RaftMessages.AppendEntries.Request expected = new RaftMessages.AppendEntries.Request( leader, leaderTerm, -1, -1, RaftLogEntry.empty, leaderCommit ); + RaftMessages.AppendEntries.Request expected = + new RaftMessages.AppendEntries.Request( CURRENT_VERSION, leader, leaderTerm, -1, -1, RaftLogEntry.empty, + leaderCommit ); assertThat( outbound.sentTo( follower ), hasItem( expected ) ); } @@ -158,7 +164,9 @@ public void shouldKeepSendingFirstEntryAfterSeveralMismatches() throws Throwable logShipper.onMismatch( 0, new LeaderContext( 0, 0 ) ); // then - RaftMessages.AppendEntries.Request expected = new RaftMessages.AppendEntries.Request( leader, leaderTerm, -1, -1, RaftLogEntry.empty, leaderCommit ); + RaftMessages.AppendEntries.Request expected = + new RaftMessages.AppendEntries.Request( CURRENT_VERSION, leader, leaderTerm, -1, -1, RaftLogEntry.empty, + leaderCommit ); assertThat( outbound.sentTo( follower ), hasItem( expected ) ); } @@ -237,7 +245,9 @@ public void shouldResendLastSentEntryOnFirstMismatch() throws Throwable logShipper.onMismatch( 1, new LeaderContext( 0, 0 ) ); // then - RaftMessages.AppendEntries.Request expected = new RaftMessages.AppendEntries.Request( leader, leaderTerm, 1, entry1.term(), RaftLogEntry.empty, leaderCommit ); + RaftMessages.AppendEntries.Request expected = + new RaftMessages.AppendEntries.Request( CURRENT_VERSION, leader, leaderTerm, 1, entry1.term(), + RaftLogEntry.empty, leaderCommit ); assertThat( outbound.sentTo( follower ), hasItem( expected ) ); } @@ -261,7 +271,9 @@ public void shouldSendAllEntriesAndCatchupCompletely() throws Throwable startLogShipper(); // back-tracking stage - RaftMessages.AppendEntries.Request expected = new RaftMessages.AppendEntries.Request( leader, leaderTerm, -1, -1, RaftLogEntry.empty, leaderCommit ); + RaftMessages.AppendEntries.Request expected = + new RaftMessages.AppendEntries.Request( CURRENT_VERSION, leader, leaderTerm, -1, -1, RaftLogEntry.empty, + leaderCommit ); while ( !outbound.sentTo( follower ).contains( expected ) ) { logShipper.onMismatch( -1, new LeaderContext( 0, 0 ) ); @@ -300,7 +312,9 @@ public void shouldSendMostRecentlyAvailableEntryIfPruningHappened() throws IOExc logShipper.onMismatch( 0, new LeaderContext( 0, 0 ) ); //then - RaftMessages.AppendEntries.Request expected = new RaftMessages.AppendEntries.Request( leader, leaderTerm, 2, entry2.term(), RaftLogEntry.empty, leaderCommit ); + RaftMessages.AppendEntries.Request expected = + new RaftMessages.AppendEntries.Request( CURRENT_VERSION, leader, leaderTerm, 2, entry2.term(), + RaftLogEntry.empty, leaderCommit ); assertThat( outbound.sentTo( follower ), hasItem( expected ) ); } @@ -324,6 +338,6 @@ public void shouldSendLogCompactionInfoToFollowerOnMatchIfEntryHasBeenPrunedAway //then assertTrue( outbound.hasAnyEntriesTo( follower ) ); assertThat( outbound.sentTo( follower ), - hasMessage( new RaftMessages.LogCompactionInfo( leader, 0, 2 ) ) ); + hasMessage( new RaftMessages.LogCompactionInfo( CURRENT_VERSION, leader, 0, 2 ) ) ); } } diff --git a/enterprise/core-edge/src/test/java/org/neo4j/coreedge/core/consensus/state/RaftStateBuilder.java b/enterprise/core-edge/src/test/java/org/neo4j/coreedge/core/consensus/state/RaftStateBuilder.java index 7cae51b4ef27d..bbf57f952be6a 100644 --- a/enterprise/core-edge/src/test/java/org/neo4j/coreedge/core/consensus/state/RaftStateBuilder.java +++ b/enterprise/core-edge/src/test/java/org/neo4j/coreedge/core/consensus/state/RaftStateBuilder.java @@ -25,19 +25,20 @@ import java.util.List; import java.util.Set; -import org.neo4j.coreedge.core.state.storage.InMemoryStateStorage; -import org.neo4j.coreedge.core.state.storage.StateStorage; import org.neo4j.coreedge.core.consensus.RaftMessages; import org.neo4j.coreedge.core.consensus.log.InMemoryRaftLog; import org.neo4j.coreedge.core.consensus.log.RaftLog; import org.neo4j.coreedge.core.consensus.log.segmented.InFlightMap; import org.neo4j.coreedge.core.consensus.membership.RaftMembership; -import org.neo4j.coreedge.core.consensus.outcome.RaftLogCommand; import org.neo4j.coreedge.core.consensus.outcome.Outcome; +import org.neo4j.coreedge.core.consensus.outcome.RaftLogCommand; import org.neo4j.coreedge.core.consensus.roles.follower.FollowerStates; import org.neo4j.coreedge.core.consensus.term.TermState; import org.neo4j.coreedge.core.consensus.vote.VoteState; +import org.neo4j.coreedge.core.state.storage.InMemoryStateStorage; +import org.neo4j.coreedge.core.state.storage.StateStorage; import org.neo4j.coreedge.identity.MemberId; +import org.neo4j.coreedge.messaging.Message; import org.neo4j.logging.NullLogProvider; import static java.util.Collections.emptySet; @@ -50,18 +51,25 @@ public static RaftStateBuilder raftState() return new RaftStateBuilder(); } - public MemberId myself; + private byte version = Message.CURRENT_VERSION; + private MemberId myself; private Set votingMembers = emptySet(); - public long term; - public MemberId leader; - public long leaderCommit = -1; + private long term; + private MemberId leader; + private long leaderCommit = -1; private MemberId votedFor; private RaftLog entryLog = new InMemoryRaftLog(); private Set votesForMe = emptySet(); private long lastLogIndexBeforeWeBecameLeader = -1; - public long commitIndex = -1; + private long commitIndex = -1; private FollowerStates followerStates = new FollowerStates<>(); + public RaftStateBuilder version( byte version ) + { + this.version = version; + return this; + } + public RaftStateBuilder myself( MemberId myself ) { this.myself = myself; @@ -134,7 +142,7 @@ public RaftState build() throws IOException Collection noMessages = Collections.emptyList(); List noLogCommands = Collections.emptyList(); - state.update( new Outcome( null, term, leader, leaderCommit, votedFor, votesForMe, + state.update( new Outcome( version, null, term, leader, leaderCommit, votedFor, votesForMe, lastLogIndexBeforeWeBecameLeader, followerStates, false, noLogCommands, noMessages, Collections.emptySet(), commitIndex ) ); diff --git a/enterprise/core-edge/src/test/java/org/neo4j/coreedge/core/consensus/state/RaftStateTest.java b/enterprise/core-edge/src/test/java/org/neo4j/coreedge/core/consensus/state/RaftStateTest.java index f698a89fbbc24..e8187ba06f350 100644 --- a/enterprise/core-edge/src/test/java/org/neo4j/coreedge/core/consensus/state/RaftStateTest.java +++ b/enterprise/core-edge/src/test/java/org/neo4j/coreedge/core/consensus/state/RaftStateTest.java @@ -29,20 +29,21 @@ import java.util.List; import java.util.Set; -import org.neo4j.coreedge.core.state.storage.InMemoryStateStorage; import org.neo4j.coreedge.core.consensus.RaftMessages; import org.neo4j.coreedge.core.consensus.log.InMemoryRaftLog; import org.neo4j.coreedge.core.consensus.log.RaftLogEntry; import org.neo4j.coreedge.core.consensus.log.segmented.InFlightMap; import org.neo4j.coreedge.core.consensus.membership.RaftMembership; import org.neo4j.coreedge.core.consensus.outcome.AppendLogEntry; -import org.neo4j.coreedge.core.consensus.outcome.RaftLogCommand; import org.neo4j.coreedge.core.consensus.outcome.Outcome; +import org.neo4j.coreedge.core.consensus.outcome.RaftLogCommand; import org.neo4j.coreedge.core.consensus.outcome.TruncateLogCommand; import org.neo4j.coreedge.core.consensus.roles.follower.FollowerState; import org.neo4j.coreedge.core.consensus.roles.follower.FollowerStates; import org.neo4j.coreedge.core.consensus.term.TermState; import org.neo4j.coreedge.core.consensus.vote.VoteState; +import org.neo4j.coreedge.core.state.storage.InMemoryStateStorage; +import org.neo4j.coreedge.identity.MemberId; import org.neo4j.logging.NullLogProvider; import static java.util.Collections.emptySet; @@ -52,14 +53,14 @@ import static org.neo4j.coreedge.core.consensus.ReplicatedInteger.valueOf; import static org.neo4j.coreedge.core.consensus.roles.Role.CANDIDATE; import static org.neo4j.coreedge.identity.RaftTestMember.member; +import static org.neo4j.coreedge.messaging.Message.CURRENT_VERSION; public class RaftStateTest { - @Test public void shouldUpdateCacheState() throws Exception { - //Test that updates applied to the raft state will be refelcted in the entry cache. + //Test that updates applied to the raft state will be reflected in the entry cache. //given InFlightMap cache = new InFlightMap<>(); @@ -77,9 +78,8 @@ public void shouldUpdateCacheState() throws Exception add( new AppendLogEntry( 3, new RaftLogEntry( 0L, valueOf( 5 ) ) ) ); }}; - Outcome raftTestMemberOutcome = - new Outcome( CANDIDATE, 0, null, -1, null, new HashSet<>(), -1, initialFollowerStates(), true, - logCommands, emptyOutgoingMessages(), Collections.emptySet(), -1 ); + Outcome raftTestMemberOutcome = new Outcome( CURRENT_VERSION, CANDIDATE, 0, null, -1, null, new HashSet<>(), -1, + initialFollowerStates(), true, logCommands, emptyOutgoingMessages(), emptySet(), -1 ); //when raftState.update(raftTestMemberOutcome); @@ -102,12 +102,13 @@ public void shouldRemoveFollowerStateAfterBecomingLeader() throws Exception new InMemoryStateStorage<>( new VoteState( ) ), new InFlightMap<>(), NullLogProvider.getInstance() ); - raftState.update( new Outcome( CANDIDATE, 1, null, -1, null, new HashSet<>(), -1, initialFollowerStates(), true, emptyLogCommands(), - emptyOutgoingMessages(), Collections.emptySet(), -1) ); + raftState.update( new Outcome( CURRENT_VERSION, CANDIDATE, 1, null, -1, null, new HashSet<>(), -1, + initialFollowerStates(), true, emptyLogCommands(), emptyOutgoingMessages(), emptySet(), -1 ) ); // when - raftState.update( new Outcome( CANDIDATE, 1, null, -1, null, new HashSet<>(), -1, new FollowerStates<>(), true, emptyLogCommands(), - emptyOutgoingMessages(), Collections.emptySet(), -1) ); + raftState.update( + new Outcome( CURRENT_VERSION, CANDIDATE, 1, null, -1, null, new HashSet<>(), -1, new FollowerStates<>(), + true, emptyLogCommands(), emptyOutgoingMessages(), emptySet(), -1 ) ); // then assertEquals( 0, raftState.followerStates().size() ); @@ -118,7 +119,7 @@ private Collection emptyOutgoingMessages() return new ArrayList<>(); } - private FollowerStates initialFollowerStates() + private FollowerStates initialFollowerStates() { return new FollowerStates<>( new FollowerStates<>(), member( 1 ), new FollowerState() ); } diff --git a/enterprise/core-edge/src/test/java/org/neo4j/coreedge/core/consensus/vote/VoteRequestBuilder.java b/enterprise/core-edge/src/test/java/org/neo4j/coreedge/core/consensus/vote/VoteRequestBuilder.java index a1f148195a400..e32590eba56b8 100644 --- a/enterprise/core-edge/src/test/java/org/neo4j/coreedge/core/consensus/vote/VoteRequestBuilder.java +++ b/enterprise/core-edge/src/test/java/org/neo4j/coreedge/core/consensus/vote/VoteRequestBuilder.java @@ -19,8 +19,10 @@ */ package org.neo4j.coreedge.core.consensus.vote; -import org.neo4j.coreedge.identity.MemberId; import org.neo4j.coreedge.core.consensus.RaftMessages; +import org.neo4j.coreedge.identity.MemberId; + +import static org.neo4j.coreedge.messaging.Message.CURRENT_VERSION; public class VoteRequestBuilder { @@ -29,10 +31,17 @@ public class VoteRequestBuilder private MemberId candidate; private long lastLogIndex; private long lastLogTerm; + private byte version = CURRENT_VERSION; public RaftMessages.Vote.Request build() { - return new RaftMessages.Vote.Request( from, term, candidate, lastLogIndex, lastLogTerm ); + return new RaftMessages.Vote.Request( version, from, term, candidate, lastLogIndex, lastLogTerm ); + } + + public VoteRequestBuilder version( byte version ) + { + this.version = version; + return this; } public VoteRequestBuilder from( MemberId from ) diff --git a/enterprise/core-edge/src/test/java/org/neo4j/coreedge/core/consensus/vote/VoteResponseBuilder.java b/enterprise/core-edge/src/test/java/org/neo4j/coreedge/core/consensus/vote/VoteResponseBuilder.java index 588d80f09c003..5915c90ea5f09 100644 --- a/enterprise/core-edge/src/test/java/org/neo4j/coreedge/core/consensus/vote/VoteResponseBuilder.java +++ b/enterprise/core-edge/src/test/java/org/neo4j/coreedge/core/consensus/vote/VoteResponseBuilder.java @@ -19,18 +19,27 @@ */ package org.neo4j.coreedge.core.consensus.vote; -import org.neo4j.coreedge.identity.MemberId; import org.neo4j.coreedge.core.consensus.RaftMessages; +import org.neo4j.coreedge.identity.MemberId; + +import static org.neo4j.coreedge.messaging.Message.CURRENT_VERSION; public class VoteResponseBuilder { - boolean voteGranted = false; + private byte version = CURRENT_VERSION; + private boolean voteGranted = false; private long term = -1; private MemberId from = null; public RaftMessages.Vote.Response build() { - return new RaftMessages.Vote.Response( from, term, voteGranted ); + return new RaftMessages.Vote.Response( version, from, term, voteGranted ); + } + + public VoteResponseBuilder version( byte version ) + { + this.version = version; + return this; } public VoteResponseBuilder from( MemberId from ) diff --git a/enterprise/core-edge/src/test/java/org/neo4j/coreedge/core/server/BatchingMessageHandlerTest.java b/enterprise/core-edge/src/test/java/org/neo4j/coreedge/core/server/BatchingMessageHandlerTest.java index 0c191d3610a80..f14be9a78fa2f 100644 --- a/enterprise/core-edge/src/test/java/org/neo4j/coreedge/core/server/BatchingMessageHandlerTest.java +++ b/enterprise/core-edge/src/test/java/org/neo4j/coreedge/core/server/BatchingMessageHandlerTest.java @@ -19,13 +19,13 @@ */ package org.neo4j.coreedge.core.server; +import org.junit.Before; +import org.junit.Test; + import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; -import org.junit.Before; -import org.junit.Test; - import org.neo4j.coreedge.catchup.storecopy.LocalDatabase; import org.neo4j.coreedge.core.consensus.RaftMessages; import org.neo4j.coreedge.core.consensus.ReplicatedString; @@ -37,6 +37,7 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyZeroInteractions; import static org.mockito.Mockito.when; +import static org.neo4j.coreedge.messaging.Message.CURRENT_VERSION; public class BatchingMessageHandlerTest { @@ -60,7 +61,7 @@ public void shouldInvokeInnerHandlerWhenRun() throws Exception raftStateMachine, QUEUE_SIZE, MAX_BATCH, NullLogProvider.getInstance() ); RaftMessages.StoreIdAwareMessage message = new RaftMessages.StoreIdAwareMessage( - localStoreId, new RaftMessages.NewEntry.Request( null, null ) ); + localStoreId, new RaftMessages.NewEntry.Request( CURRENT_VERSION, null, null ) ); batchHandler.handle( message ); verifyZeroInteractions( raftStateMachine ); @@ -78,7 +79,7 @@ public void shouldInvokeHandlerOnQueuedMessage() throws Exception BatchingMessageHandler batchHandler = new BatchingMessageHandler( raftStateMachine, QUEUE_SIZE, MAX_BATCH, NullLogProvider.getInstance() ); RaftMessages.StoreIdAwareMessage message = new RaftMessages.StoreIdAwareMessage( localStoreId, - new RaftMessages.NewEntry.Request( null, null ) ); + new RaftMessages.NewEntry.Request( CURRENT_VERSION, null, null ) ); ExecutorService executor = Executors.newCachedThreadPool(); Future future = executor.submit( batchHandler ); @@ -105,8 +106,8 @@ public void shouldBatchRequests() throws Exception raftStateMachine, QUEUE_SIZE, MAX_BATCH, NullLogProvider.getInstance() ); ReplicatedString contentA = new ReplicatedString( "A" ); ReplicatedString contentB = new ReplicatedString( "B" ); - RaftMessages.NewEntry.Request messageA = new RaftMessages.NewEntry.Request( null, contentA ); - RaftMessages.NewEntry.Request messageB = new RaftMessages.NewEntry.Request( null, contentB ); + RaftMessages.NewEntry.Request messageA = new RaftMessages.NewEntry.Request( CURRENT_VERSION, null, contentA ); + RaftMessages.NewEntry.Request messageB = new RaftMessages.NewEntry.Request( CURRENT_VERSION, null, contentB ); batchHandler.handle( new RaftMessages.StoreIdAwareMessage( localStoreId, messageA ) ); batchHandler.handle( new RaftMessages.StoreIdAwareMessage( localStoreId, messageB ) ); @@ -116,7 +117,7 @@ public void shouldBatchRequests() throws Exception batchHandler.run(); // then - RaftMessages.NewEntry.BatchRequest batchRequest = new RaftMessages.NewEntry.BatchRequest( 2 ); + RaftMessages.NewEntry.BatchRequest batchRequest = new RaftMessages.NewEntry.BatchRequest( CURRENT_VERSION, 2 ); batchRequest.add( contentA ); batchRequest.add( contentB ); verify( raftStateMachine ).handle( new RaftMessages.StoreIdAwareMessage( localStoreId, batchRequest ) ); @@ -133,13 +134,13 @@ public void shouldBatchNewEntriesAndHandleOtherMessagesSingularly() throws Excep ReplicatedString contentC = new ReplicatedString( "C" ); RaftMessages.StoreIdAwareMessage messageA = new RaftMessages.StoreIdAwareMessage( localStoreId, - new RaftMessages.NewEntry.Request( null, contentA ) ); + new RaftMessages.NewEntry.Request( CURRENT_VERSION, null, contentA ) ); RaftMessages.StoreIdAwareMessage messageB = new RaftMessages.StoreIdAwareMessage( localStoreId, - new RaftMessages.Heartbeat( null, 0, 0, 0 ) ); + new RaftMessages.Heartbeat( CURRENT_VERSION, null, 0, 0, 0 ) ); RaftMessages.StoreIdAwareMessage messageC = new RaftMessages.StoreIdAwareMessage( localStoreId, - new RaftMessages.NewEntry.Request( null, contentC ) ); + new RaftMessages.NewEntry.Request( CURRENT_VERSION, null, contentC ) ); RaftMessages.StoreIdAwareMessage messageD = new RaftMessages.StoreIdAwareMessage( localStoreId, - new RaftMessages.Heartbeat( null, 1, 1, 1 ) ); + new RaftMessages.Heartbeat( CURRENT_VERSION, null, 1, 1, 1 ) ); batchHandler.handle( messageA ); batchHandler.handle( messageB ); @@ -151,7 +152,7 @@ public void shouldBatchNewEntriesAndHandleOtherMessagesSingularly() throws Excep batchHandler.run(); // then - RaftMessages.NewEntry.BatchRequest batchRequest = new RaftMessages.NewEntry.BatchRequest( 2 ); + RaftMessages.NewEntry.BatchRequest batchRequest = new RaftMessages.NewEntry.BatchRequest( CURRENT_VERSION, 2 ); batchRequest.add( contentA ); batchRequest.add( contentC ); diff --git a/enterprise/core-edge/src/test/java/org/neo4j/coreedge/core/state/CoreStateTest.java b/enterprise/core-edge/src/test/java/org/neo4j/coreedge/core/state/CoreStateTest.java index 18f0e6cc4808f..bac503a47b007 100644 --- a/enterprise/core-edge/src/test/java/org/neo4j/coreedge/core/state/CoreStateTest.java +++ b/enterprise/core-edge/src/test/java/org/neo4j/coreedge/core/state/CoreStateTest.java @@ -25,6 +25,7 @@ import org.neo4j.coreedge.core.state.snapshot.CoreStateDownloader; import org.neo4j.coreedge.identity.StoreId; import org.neo4j.coreedge.core.consensus.RaftMessages; +import org.neo4j.coreedge.messaging.Message; import org.neo4j.logging.AssertableLogProvider; import org.neo4j.logging.LogProvider; import org.neo4j.logging.NullLogProvider; @@ -37,6 +38,7 @@ import static org.mockito.Mockito.when; import static org.neo4j.coreedge.identity.RaftTestMember.member; +import static org.neo4j.coreedge.messaging.Message.CURRENT_VERSION; public class CoreStateTest { @@ -54,7 +56,7 @@ public void shouldLogOnStoreIdMismatchAndNonEmptyStore() throws Exception CommandApplicationProcess applicationProcess = mock( CommandApplicationProcess.class ); CoreState coreState = new CoreState( null, localDatabase, logProvider, null, null, applicationProcess ); - RaftMessages.NewEntry.Request message = new RaftMessages.NewEntry.Request( member( 0 ), null ); + RaftMessages.NewEntry.Request message = new RaftMessages.NewEntry.Request( CURRENT_VERSION, member( 0 ), null ); // when coreState.handle( new RaftMessages.StoreIdAwareMessage( otherStoreId, message ) ); @@ -70,7 +72,7 @@ public void shouldNotActOnIncomingDefaultStoreIdWithEmptyStore() throws Exceptio // given StoreId localStoreId = new StoreId( 1, 2, 3, 4 ); StoreId otherStoreId = StoreId.DEFAULT; - RaftMessages.NewEntry.Request message = new RaftMessages.NewEntry.Request( member( 0 ), null ); + RaftMessages.NewEntry.Request message = new RaftMessages.NewEntry.Request( CURRENT_VERSION, member( 0 ), null ); CoreStateDownloader downloader = mock( CoreStateDownloader.class ); LocalDatabase localDatabase = mock( LocalDatabase.class ); diff --git a/enterprise/core-edge/src/test/java/org/neo4j/coreedge/messaging/RaftMessageProcessingTest.java b/enterprise/core-edge/src/test/java/org/neo4j/coreedge/messaging/RaftMessageProcessingTest.java index 552d41dcf69d5..71d39722f84ab 100644 --- a/enterprise/core-edge/src/test/java/org/neo4j/coreedge/messaging/RaftMessageProcessingTest.java +++ b/enterprise/core-edge/src/test/java/org/neo4j/coreedge/messaging/RaftMessageProcessingTest.java @@ -42,6 +42,7 @@ import org.neo4j.storageengine.api.WritableChannel; import static org.junit.Assert.assertEquals; +import static org.neo4j.coreedge.messaging.Message.CURRENT_VERSION; @RunWith(MockitoJUnitRunner.class) public class RaftMessageProcessingTest @@ -102,7 +103,7 @@ public void shouldEncodeAndDecodeVoteRequest() { // given MemberId member = new MemberId( UUID.randomUUID() ); - RaftMessages.Vote.Request request = new RaftMessages.Vote.Request( member, 1, member, 1, 1 ); + RaftMessages.Vote.Request request = new RaftMessages.Vote.Request( CURRENT_VERSION, member, 1, member, 1, 1 ); // when channel.writeOutbound( request ); @@ -117,7 +118,7 @@ public void shouldEncodeAndDecodeVoteResponse() { // given MemberId member = new MemberId( UUID.randomUUID() ); - RaftMessages.Vote.Response response = new RaftMessages.Vote.Response( member, 1, true ); + RaftMessages.Vote.Response response = new RaftMessages.Vote.Response( CURRENT_VERSION, member, 1, true ); // when channel.writeOutbound( response ); @@ -132,9 +133,9 @@ public void shouldEncodeAndDecodeAppendEntriesRequest() { // given MemberId member = new MemberId( UUID.randomUUID() ); - RaftLogEntry logEntry = new RaftLogEntry( 1, ReplicatedInteger.valueOf( 1 ) ); + RaftLogEntry[] logEntries = {new RaftLogEntry( 1, ReplicatedInteger.valueOf( 1 ) )}; RaftMessages.AppendEntries.Request request = - new RaftMessages.AppendEntries.Request( member, 1, 1, 99, new RaftLogEntry[] { logEntry }, 1 ); + new RaftMessages.AppendEntries.Request( CURRENT_VERSION, member, 1, 1, 99, logEntries, 1 ); // when channel.writeOutbound( request ); @@ -150,7 +151,7 @@ public void shouldEncodeAndDecodeAppendEntriesResponse() // given MemberId member = new MemberId( UUID.randomUUID() ); RaftMessages.AppendEntries.Response response = - new RaftMessages.AppendEntries.Response( member, 1, false, -1, 0 ); + new RaftMessages.AppendEntries.Response( CURRENT_VERSION, member, 1, false, -1, 0 ); // when channel.writeOutbound( response ); @@ -166,7 +167,7 @@ public void shouldEncodeAndDecodeNewEntryRequest() // given MemberId member = new MemberId( UUID.randomUUID() ); RaftMessages.NewEntry.Request request = - new RaftMessages.NewEntry.Request( member, ReplicatedInteger.valueOf( 12 ) ); + new RaftMessages.NewEntry.Request( CURRENT_VERSION, member, ReplicatedInteger.valueOf( 12 ) ); // when channel.writeOutbound( request ); diff --git a/enterprise/core-edge/src/test/java/org/neo4j/coreedge/messaging/marsalling/RaftMessageEncodingDecodingTest.java b/enterprise/core-edge/src/test/java/org/neo4j/coreedge/messaging/marsalling/RaftMessageEncodingDecodingTest.java index 768eda20a6094..5d127efaba720 100644 --- a/enterprise/core-edge/src/test/java/org/neo4j/coreedge/messaging/marsalling/RaftMessageEncodingDecodingTest.java +++ b/enterprise/core-edge/src/test/java/org/neo4j/coreedge/messaging/marsalling/RaftMessageEncodingDecodingTest.java @@ -40,12 +40,14 @@ import org.neo4j.coreedge.core.state.storage.SafeChannelMarshal; import org.neo4j.coreedge.identity.MemberId; import org.neo4j.coreedge.identity.StoreId; +import org.neo4j.coreedge.messaging.Message; import org.neo4j.storageengine.api.ReadableChannel; import org.neo4j.storageengine.api.WritableChannel; import static org.junit.Assert.assertEquals; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; +import static org.neo4j.coreedge.messaging.Message.CURRENT_VERSION; public class RaftMessageEncodingDecodingTest { @@ -105,7 +107,7 @@ public void shouldSerializeHeartbeats() throws Exception // When MemberId sender = new MemberId( UUID.randomUUID() ); RaftMessages.StoreIdAwareMessage message = new RaftMessages.StoreIdAwareMessage( storeId, - new RaftMessages.Heartbeat( sender, 1, 2, 3 ) ); + new RaftMessages.Heartbeat( CURRENT_VERSION, sender, 1, 2, 3 ) ); encoder.encode( setupContext(), message, resultingBuffers ); // Then