diff --git a/community/bolt/src/main/java/org/neo4j/bolt/runtime/BoltStateMachineFactoryImpl.java b/community/bolt/src/main/java/org/neo4j/bolt/runtime/BoltStateMachineFactoryImpl.java index 05392c8a8fd50..7eaf6c45d6cbd 100644 --- a/community/bolt/src/main/java/org/neo4j/bolt/runtime/BoltStateMachineFactoryImpl.java +++ b/community/bolt/src/main/java/org/neo4j/bolt/runtime/BoltStateMachineFactoryImpl.java @@ -80,21 +80,21 @@ else if ( protocolVersion == BoltProtocolV3.VERSION ) private BoltStateMachine newStateMachineV1( BoltChannel boltChannel ) { - long bookmarkReadyTimeout = config.get( GraphDatabaseSettings.bookmark_ready_timeout ).toMillis(); - Duration txAwaitDuration = Duration.ofMillis( bookmarkReadyTimeout ); - TransactionStateMachineSPI transactionSPI = new TransactionStateMachineV1SPI( db, availabilityGuard, txAwaitDuration, clock ); - + TransactionStateMachineSPI transactionSPI = new TransactionStateMachineV1SPI( db, availabilityGuard, getAwaitDuration(), clock ); BoltStateMachineSPI boltSPI = new BoltStateMachineV1SPI( boltChannel, usageData, logging, authentication, transactionSPI ); return new BoltStateMachineV1( boltSPI, boltChannel, clock ); } private BoltStateMachine newStateMachineV3( BoltChannel boltChannel ) { - long bookmarkReadyTimeout = config.get( GraphDatabaseSettings.bookmark_ready_timeout ).toMillis(); - Duration txAwaitDuration = Duration.ofMillis( bookmarkReadyTimeout ); - TransactionStateMachineSPI transactionSPI = new TransactionStateMachineV3SPI( db, availabilityGuard, txAwaitDuration, clock ); - + TransactionStateMachineSPI transactionSPI = new TransactionStateMachineV3SPI( db, availabilityGuard, getAwaitDuration(), clock ); BoltStateMachineSPI boltSPI = new BoltStateMachineV1SPI( boltChannel, usageData, logging, authentication, transactionSPI ); return new BoltStateMachineV3( boltSPI, boltChannel, clock ); } + + private Duration getAwaitDuration() + { + long bookmarkReadyTimeout = config.get( GraphDatabaseSettings.bookmark_ready_timeout ).toMillis(); + return Duration.ofMillis( bookmarkReadyTimeout ); + } } diff --git a/community/bolt/src/main/java/org/neo4j/bolt/v1/runtime/TransactionStateMachine.java b/community/bolt/src/main/java/org/neo4j/bolt/v1/runtime/TransactionStateMachine.java index bfcb9bf6de1a2..a3b113f75ff22 100644 --- a/community/bolt/src/main/java/org/neo4j/bolt/v1/runtime/TransactionStateMachine.java +++ b/community/bolt/src/main/java/org/neo4j/bolt/v1/runtime/TransactionStateMachine.java @@ -44,6 +44,8 @@ import org.neo4j.kernel.impl.query.QueryExecutionKernelException; import org.neo4j.values.virtual.MapValue; +import static org.neo4j.util.Preconditions.checkState; + public class TransactionStateMachine implements StatementProcessor { final TransactionStateMachineSPI spi; @@ -367,6 +369,9 @@ State run( MutableTransactionState ctx, TransactionStateMachineSPI spi, String s Duration ignored1, Map ignored2 ) throws KernelException { + checkState( ignored1 == null, "Explicit Transaction should not run with tx_timeout" ); + checkState( ignored2 == null, "Explicit Transaction should not run with tx_metadata" ); + if ( statement.isEmpty() ) { statement = ctx.lastStatement; diff --git a/community/bolt/src/main/java/org/neo4j/bolt/v3/messaging/request/BeginMessage.java b/community/bolt/src/main/java/org/neo4j/bolt/v3/messaging/request/BeginMessage.java index b8805c6b2afb9..32e0d114bc511 100644 --- a/community/bolt/src/main/java/org/neo4j/bolt/v3/messaging/request/BeginMessage.java +++ b/community/bolt/src/main/java/org/neo4j/bolt/v3/messaging/request/BeginMessage.java @@ -20,33 +20,23 @@ package org.neo4j.bolt.v3.messaging.request; import java.time.Duration; -import java.util.HashMap; import java.util.Map; import java.util.Objects; import org.neo4j.bolt.messaging.BoltIOException; import org.neo4j.bolt.messaging.RequestMessage; import org.neo4j.bolt.v1.runtime.bookmarking.Bookmark; -import org.neo4j.graphdb.Node; -import org.neo4j.graphdb.Relationship; -import org.neo4j.graphdb.spatial.Point; -import org.neo4j.internal.kernel.api.exceptions.KernelException; -import org.neo4j.kernel.api.exceptions.Status; -import org.neo4j.kernel.impl.util.BaseToObjectValueWriter; -import org.neo4j.values.AnyValue; -import org.neo4j.values.storable.CoordinateReferenceSystem; -import org.neo4j.values.storable.LongValue; -import org.neo4j.values.storable.Values; import org.neo4j.values.virtual.MapValue; import org.neo4j.values.virtual.VirtualValues; import static java.util.Objects.requireNonNull; +import static org.neo4j.bolt.v3.messaging.request.MessageMetadataParser.parseBookmark; +import static org.neo4j.bolt.v3.messaging.request.MessageMetadataParser.parseTransactionMetadata; +import static org.neo4j.bolt.v3.messaging.request.MessageMetadataParser.parseTransactionTimeout; public class BeginMessage implements RequestMessage { public static final byte SIGNATURE = 0x11; - private static final String TX_TIMEOUT_KEY = "tx_timeout"; - private static final String TX_META_DATA_KEY = "tx_metadata"; private final MapValue meta; private final Bookmark bookmark; @@ -66,56 +56,6 @@ public BeginMessage( MapValue meta ) throws BoltIOException this.txMetadata = parseTransactionMetadata( meta ); } - static Bookmark parseBookmark( MapValue meta ) throws BoltIOException - { - try - { - return Bookmark.fromParamsOrNull( meta ); - } - catch ( KernelException e ) - { - throw new BoltIOException( Status.Request.InvalidFormat, e.getMessage(), e ); - } - } - - static Duration parseTransactionTimeout( MapValue meta ) throws BoltIOException - { - AnyValue anyValue = meta.get( TX_TIMEOUT_KEY ); - if ( anyValue == Values.NO_VALUE ) - { - return null; - } - else if ( anyValue instanceof LongValue ) - { - return Duration.ofMillis( ((LongValue) anyValue).longValue() ); - } - else - { - throw new BoltIOException( Status.Request.InvalidFormat, "Expecting transaction timeout value to be a Long value, but got: " + anyValue ); - } - } - - static Map parseTransactionMetadata( MapValue meta ) throws BoltIOException - { - AnyValue anyValue = meta.get( TX_META_DATA_KEY ); - if ( anyValue == Values.NO_VALUE ) - { - return null; - } - else if ( anyValue instanceof MapValue ) - { - MapValue mapValue = (MapValue) anyValue; - TransactionMetadataWriter writer = new TransactionMetadataWriter(); - Map txMeta = new HashMap<>( mapValue.size() ); - mapValue.foreach( ( key, value ) -> txMeta.put( key, writer.valueAsObject( value ) ) ); - return txMeta; - } - else - { - throw new BoltIOException( Status.Request.InvalidFormat, "Expecting transaction metadata value to be a Map value, but got: " + anyValue ); - } - } - public Bookmark bookmark() { return this.bookmark; @@ -168,31 +108,4 @@ public Map transactionMetadata() { return txMetadata; } - - private static class TransactionMetadataWriter extends BaseToObjectValueWriter - { - @Override - protected Node newNodeProxyById( long id ) - { - throw new UnsupportedOperationException( "Transaction metadata should not contain nodes" ); - } - - @Override - protected Relationship newRelationshipProxyById( long id ) - { - throw new UnsupportedOperationException( "Transaction metadata should not contain relationships" ); - } - - @Override - protected Point newPoint( CoordinateReferenceSystem crs, double[] coordinate ) - { - return Values.pointValue( crs, coordinate ); - } - - Object valueAsObject( AnyValue value ) - { - value.writeTo( this ); - return value(); - } - } } diff --git a/community/bolt/src/main/java/org/neo4j/bolt/v3/messaging/request/MessageMetadataParser.java b/community/bolt/src/main/java/org/neo4j/bolt/v3/messaging/request/MessageMetadataParser.java new file mode 100644 index 0000000000000..19c241d35716a --- /dev/null +++ b/community/bolt/src/main/java/org/neo4j/bolt/v3/messaging/request/MessageMetadataParser.java @@ -0,0 +1,124 @@ +/* + * Copyright (c) 2002-2018 "Neo4j," + * Neo4j Sweden AB [http://neo4j.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 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.bolt.v3.messaging.request; + +import java.time.Duration; +import java.util.HashMap; +import java.util.Map; + +import org.neo4j.bolt.messaging.BoltIOException; +import org.neo4j.bolt.v1.runtime.bookmarking.Bookmark; +import org.neo4j.graphdb.Node; +import org.neo4j.graphdb.Relationship; +import org.neo4j.graphdb.spatial.Point; +import org.neo4j.internal.kernel.api.exceptions.KernelException; +import org.neo4j.kernel.api.exceptions.Status; +import org.neo4j.kernel.impl.util.BaseToObjectValueWriter; +import org.neo4j.values.AnyValue; +import org.neo4j.values.storable.CoordinateReferenceSystem; +import org.neo4j.values.storable.LongValue; +import org.neo4j.values.storable.Values; +import org.neo4j.values.virtual.MapValue; + +/** + * The parsing methods in this class returns null if the specified key is not found in the input message metadata map. + */ +class MessageMetadataParser +{ + private static final String TX_TIMEOUT_KEY = "tx_timeout"; + private static final String TX_META_DATA_KEY = "tx_metadata"; + + static Bookmark parseBookmark( MapValue meta ) throws BoltIOException + { + try + { + return Bookmark.fromParamsOrNull( meta ); + } + catch ( KernelException e ) + { + throw new BoltIOException( Status.Request.InvalidFormat, e.getMessage(), e ); + } + } + + static Duration parseTransactionTimeout( MapValue meta ) throws BoltIOException + { + AnyValue anyValue = meta.get( TX_TIMEOUT_KEY ); + if ( anyValue == Values.NO_VALUE ) + { + return null; + } + else if ( anyValue instanceof LongValue ) + { + return Duration.ofMillis( ((LongValue) anyValue).longValue() ); + } + else + { + throw new BoltIOException( Status.Request.InvalidFormat, "Expecting transaction timeout value to be a Long value, but got: " + anyValue ); + } + } + + static Map parseTransactionMetadata( MapValue meta ) throws BoltIOException + { + AnyValue anyValue = meta.get( TX_META_DATA_KEY ); + if ( anyValue == Values.NO_VALUE ) + { + return null; + } + else if ( anyValue instanceof MapValue ) + { + MapValue mapValue = (MapValue) anyValue; + TransactionMetadataWriter writer = new TransactionMetadataWriter(); + Map txMeta = new HashMap<>( mapValue.size() ); + mapValue.foreach( ( key, value ) -> txMeta.put( key, writer.valueAsObject( value ) ) ); + return txMeta; + } + else + { + throw new BoltIOException( Status.Request.InvalidFormat, "Expecting transaction metadata value to be a Map value, but got: " + anyValue ); + } + } + + private static class TransactionMetadataWriter extends BaseToObjectValueWriter + { + @Override + protected Node newNodeProxyById( long id ) + { + throw new UnsupportedOperationException( "Transaction metadata should not contain nodes" ); + } + + @Override + protected Relationship newRelationshipProxyById( long id ) + { + throw new UnsupportedOperationException( "Transaction metadata should not contain relationships" ); + } + + @Override + protected Point newPoint( CoordinateReferenceSystem crs, double[] coordinate ) + { + return Values.pointValue( crs, coordinate ); + } + + Object valueAsObject( AnyValue value ) + { + value.writeTo( this ); + return value(); + } + } +} diff --git a/community/bolt/src/main/java/org/neo4j/bolt/v3/messaging/request/RunMessage.java b/community/bolt/src/main/java/org/neo4j/bolt/v3/messaging/request/RunMessage.java index 96688ce6011d9..314603751b52f 100644 --- a/community/bolt/src/main/java/org/neo4j/bolt/v3/messaging/request/RunMessage.java +++ b/community/bolt/src/main/java/org/neo4j/bolt/v3/messaging/request/RunMessage.java @@ -30,9 +30,9 @@ import org.neo4j.values.virtual.VirtualValues; import static java.util.Objects.requireNonNull; -import static org.neo4j.bolt.v3.messaging.request.BeginMessage.parseBookmark; -import static org.neo4j.bolt.v3.messaging.request.BeginMessage.parseTransactionMetadata; -import static org.neo4j.bolt.v3.messaging.request.BeginMessage.parseTransactionTimeout; +import static org.neo4j.bolt.v3.messaging.request.MessageMetadataParser.parseBookmark; +import static org.neo4j.bolt.v3.messaging.request.MessageMetadataParser.parseTransactionMetadata; +import static org.neo4j.bolt.v3.messaging.request.MessageMetadataParser.parseTransactionTimeout; public class RunMessage implements RequestMessage { diff --git a/community/bolt/src/main/java/org/neo4j/bolt/v3/runtime/TransactionReadyState.java b/community/bolt/src/main/java/org/neo4j/bolt/v3/runtime/TransactionReadyState.java index ef314889fbd3e..52cec5658202d 100644 --- a/community/bolt/src/main/java/org/neo4j/bolt/v3/runtime/TransactionReadyState.java +++ b/community/bolt/src/main/java/org/neo4j/bolt/v3/runtime/TransactionReadyState.java @@ -101,16 +101,11 @@ private BoltStateMachineState processRunMessage( RunMessage message, StateMachin } private BoltStateMachineState processCommitMessage( StateMachineContext context ) throws Exception - { - appendBookmarkInResponse( context ); - return readyState; - } - - static void appendBookmarkInResponse( StateMachineContext context ) throws KernelException { StatementProcessor statementProcessor = context.connectionState().getStatementProcessor(); Bookmark bookmark = statementProcessor.commitTransaction(); bookmark.attachTo( context.connectionState() ); + return readyState; } private BoltStateMachineState processRollbackMessage( StateMachineContext context ) throws Exception diff --git a/community/kernel/src/main/java/org/neo4j/kernel/api/KernelTransaction.java b/community/kernel/src/main/java/org/neo4j/kernel/api/KernelTransaction.java index 7993c39e035ab..486b4424d19ca 100644 --- a/community/kernel/src/main/java/org/neo4j/kernel/api/KernelTransaction.java +++ b/community/kernel/src/main/java/org/neo4j/kernel/api/KernelTransaction.java @@ -171,8 +171,8 @@ interface CloseListener PropertyCursor ambientPropertyCursor(); /** - * Attaches a map of data to the transaction. - * The daga will be printed when listing queries and inserted in to th query log. + * Attaches a map of data to this transaction. + * The data will be printed when listing queries and inserted in to the query log. * @param metaData The data to add. */ void setMetaData( Map metaData ); diff --git a/community/kernel/src/main/java/org/neo4j/kernel/impl/api/KernelTransactionImplementation.java b/community/kernel/src/main/java/org/neo4j/kernel/impl/api/KernelTransactionImplementation.java index 6c53318ca1fe6..ffbc649348008 100644 --- a/community/kernel/src/main/java/org/neo4j/kernel/impl/api/KernelTransactionImplementation.java +++ b/community/kernel/src/main/java/org/neo4j/kernel/impl/api/KernelTransactionImplementation.java @@ -22,8 +22,6 @@ import java.time.Clock; import java.util.ArrayList; import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Optional; @@ -105,6 +103,7 @@ import org.neo4j.storageengine.api.txstate.TxStateVisitor; import static java.lang.String.format; +import static java.util.Collections.emptyMap; import static java.util.concurrent.TimeUnit.NANOSECONDS; import static org.neo4j.storageengine.api.TransactionApplicationMode.INTERNAL; @@ -215,7 +214,7 @@ public KernelTransactionImplementation( StatementOperationParts statementOperati versionContextSupplier ); this.accessCapability = accessCapability; this.statistics = new Statistics( this, cpuClockRef, heapAllocationRef ); - this.userMetaData = new HashMap<>(); + this.userMetaData = emptyMap(); this.constraintSemantics = constraintSemantics; DefaultCursors cursors = new DefaultCursors( storageReader ); AllStoreHolder allStoreHolder = @@ -944,7 +943,7 @@ private void release() hooksState = null; closeListeners.clear(); reuseCount++; - userMetaData = Collections.emptyMap(); + userMetaData = emptyMap(); userTransactionId = 0; statistics.reset(); operations.release();