You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tinkerpop.apache.org by fl...@apache.org on 2021/10/01 13:52:48 UTC

[tinkerpop] 01/01: TINKERPOP-2556 First try, tests don't terminate for some reason

This is an automated email from the ASF dual-hosted git repository.

florianhockmann pushed a commit to branch TINKERPOP-2556
in repository https://gitbox.apache.org/repos/asf/tinkerpop.git

commit dad0e36cad6dffef9ab65f582a77dace27e4af1a
Author: Florian Hockmann <fh...@florian-hockmann.de>
AuthorDate: Fri Oct 1 15:52:32 2021 +0200

    TINKERPOP-2556 First try, tests don't terminate for some reason
---
 .../Driver/Remote/DriverRemoteConnection.cs        | 35 ++++++++-
 .../Driver/Remote/DriverRemoteTransaction.cs       | 69 ++++++++++++++++
 gremlin-dotnet/src/Gremlin.Net/Gremlin.Net.csproj  |  5 +-
 .../Process/Remote/IRemoteConnection.cs            |  6 ++
 .../src/Gremlin.Net/Process/Traversal/Bytecode.cs  |  2 +-
 .../IRemoteConnection.cs => Traversal/GraphOp.cs}  | 26 +++----
 .../Process/Traversal/GraphTraversalSource.cs      | 32 ++++++--
 .../ITransaction.cs}                               | 17 ++--
 .../GraphTraversalModificationTests.cs             | 91 ++++++++++++++++++++++
 .../DriverRemoteConnection/GraphTraversalTests.cs  |  2 +-
 .../Driver/Remote/DriverRemoteTransactionTests.cs  | 57 ++++++++++++++
 11 files changed, 303 insertions(+), 39 deletions(-)

diff --git a/gremlin-dotnet/src/Gremlin.Net/Driver/Remote/DriverRemoteConnection.cs b/gremlin-dotnet/src/Gremlin.Net/Driver/Remote/DriverRemoteConnection.cs
index 006cf8a..63649a1 100644
--- a/gremlin-dotnet/src/Gremlin.Net/Driver/Remote/DriverRemoteConnection.cs
+++ b/gremlin-dotnet/src/Gremlin.Net/Driver/Remote/DriverRemoteConnection.cs
@@ -25,10 +25,10 @@ using System;
 using System.Collections.Generic;
 using System.Threading.Tasks;
 using Gremlin.Net.Driver.Messages;
-using Gremlin.Net.Driver;
 using Gremlin.Net.Process.Remote;
 using Gremlin.Net.Process.Traversal;
 using Gremlin.Net.Process.Traversal.Strategy.Decoration;
+using Gremlin.Net.Structure;
 
 namespace Gremlin.Net.Driver.Remote
 {
@@ -49,6 +49,10 @@ namespace Gremlin.Net.Driver.Remote
                     {Tokens.ArgsEvalTimeout, "scriptEvaluationTimeout", Tokens.ArgsBatchSize, 
                      Tokens.RequestId, Tokens.ArgsUserAgent};
 
+        private readonly string _sessionId;
+        private string Processor => IsSessionBound ? Tokens.ProcessorSession : Tokens.ProcessorTraversal;
+        public bool IsSessionBound => _sessionId != null;
+
         /// <summary>
         ///     Initializes a new <see cref="IRemoteConnection" /> using "g" as the default remote TraversalSource name.
         /// </summary>
@@ -91,6 +95,12 @@ namespace Gremlin.Net.Driver.Remote
             _traversalSource = traversalSource ?? throw new ArgumentNullException(nameof(traversalSource));
         }
 
+        private DriverRemoteConnection(IGremlinClient client, string traversalSource, Guid sessionId)
+            : this(client, traversalSource)
+        {
+            _sessionId = sessionId.ToString();
+        }
+
         /// <summary>
         ///     Submits <see cref="Bytecode" /> for evaluation to a remote Gremlin Server.
         /// </summary>
@@ -107,11 +117,16 @@ namespace Gremlin.Net.Driver.Remote
         {
             var requestMsg =
                 RequestMessage.Build(Tokens.OpsBytecode)
-                    .Processor(Tokens.ProcessorTraversal)
+                    .Processor(Processor)
                     .OverrideRequestId(requestid)
                     .AddArgument(Tokens.ArgsGremlin, bytecode)
                     .AddArgument(Tokens.ArgsAliases, new Dictionary<string, string> {{"g", _traversalSource}});
 
+            if (IsSessionBound)
+            {
+                requestMsg.AddArgument(Tokens.ArgsSession, _sessionId);
+            }
+
             var optionsStrategyInst = bytecode.SourceInstructions.Find(
                 s => s.OperatorName == "withStrategies" && s.Arguments[0] is OptionsStrategy);
             if (optionsStrategyInst != null)
@@ -128,6 +143,22 @@ namespace Gremlin.Net.Driver.Remote
             
             return await _client.SubmitAsync<Traverser>(requestMsg.Create()).ConfigureAwait(false);
         }
+        
+        public ITransaction Tx(GraphTraversalSource g)
+        {
+            var session = new DriverRemoteConnection(_client, _traversalSource, Guid.NewGuid());
+            return new DriverRemoteTransaction(session, g);
+        }
+
+        public async Task CommitAsync()
+        {
+            await SubmitAsync<object, object>(GraphOp.Commit).ConfigureAwait(false);
+        }
+        
+        public async Task RollbackAsync()
+        {
+            await SubmitAsync<object, object>(GraphOp.Rollback).ConfigureAwait(false);
+        }
 
         /// <inheritdoc />
         public void Dispose()
diff --git a/gremlin-dotnet/src/Gremlin.Net/Driver/Remote/DriverRemoteTransaction.cs b/gremlin-dotnet/src/Gremlin.Net/Driver/Remote/DriverRemoteTransaction.cs
new file mode 100644
index 0000000..f3be21b
--- /dev/null
+++ b/gremlin-dotnet/src/Gremlin.Net/Driver/Remote/DriverRemoteTransaction.cs
@@ -0,0 +1,69 @@
+#region License
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#endregion
+
+using System;
+using System.Threading.Tasks;
+using Gremlin.Net.Process.Traversal;
+using Gremlin.Net.Structure;
+
+namespace Gremlin.Net.Driver.Remote
+{
+    public class DriverRemoteTransaction : ITransaction, IDisposable
+    {
+        private readonly DriverRemoteConnection _sessionBasedConnection;
+        private GraphTraversalSource _g;
+
+        public DriverRemoteTransaction(DriverRemoteConnection connection, GraphTraversalSource g)
+        {
+            _sessionBasedConnection = connection;
+            _g = g;
+        }
+
+        public GraphTraversalSource Begin()
+        {
+            if (_g.IsSessionBound)
+            {
+                throw new InvalidOperationException("Transaction already started on this object");
+            }
+            _g = new GraphTraversalSource(_g.TraversalStrategies, _g.Bytecode, _sessionBasedConnection);
+            return _g;
+        }
+
+        public async Task CommitAsync()
+        {
+            await _sessionBasedConnection.CommitAsync().ConfigureAwait(false);
+            Dispose();
+        }
+
+        public async Task RollbackAsync()
+        {
+            await _sessionBasedConnection.RollbackAsync().ConfigureAwait(false);
+            Dispose();
+        }
+
+        public void Dispose()
+        {
+            _sessionBasedConnection.Dispose();
+        }
+    }
+}
\ No newline at end of file
diff --git a/gremlin-dotnet/src/Gremlin.Net/Gremlin.Net.csproj b/gremlin-dotnet/src/Gremlin.Net/Gremlin.Net.csproj
index 0f963bb..f9e3fc5 100644
--- a/gremlin-dotnet/src/Gremlin.Net/Gremlin.Net.csproj
+++ b/gremlin-dotnet/src/Gremlin.Net/Gremlin.Net.csproj
@@ -19,8 +19,9 @@ limitations under the License.
 
   <PropertyGroup Label="Build">
     <TargetFramework>netstandard2.0</TargetFramework>
-    <TreatWarningsAsErrors>true</TreatWarningsAsErrors>
-    <GenerateDocumentationFile>true</GenerateDocumentationFile>
+<!--    <TreatWarningsAsErrors>true</TreatWarningsAsErrors>-->
+<!--    <GenerateDocumentationFile>true</GenerateDocumentationFile>-->
+    <LangVersion>8</LangVersion>
   </PropertyGroup>
 
   <PropertyGroup Label="Package">
diff --git a/gremlin-dotnet/src/Gremlin.Net/Process/Remote/IRemoteConnection.cs b/gremlin-dotnet/src/Gremlin.Net/Process/Remote/IRemoteConnection.cs
index 5393bcb..9502f5d 100644
--- a/gremlin-dotnet/src/Gremlin.Net/Process/Remote/IRemoteConnection.cs
+++ b/gremlin-dotnet/src/Gremlin.Net/Process/Remote/IRemoteConnection.cs
@@ -23,6 +23,7 @@
 
 using System.Threading.Tasks;
 using Gremlin.Net.Process.Traversal;
+using Gremlin.Net.Structure;
 
 namespace Gremlin.Net.Process.Remote
 {
@@ -38,5 +39,10 @@ namespace Gremlin.Net.Process.Remote
         /// <param name="bytecode">The <see cref="Bytecode" /> to send.</param>
         /// <returns>The <see cref="ITraversal" /> with the results and optional side-effects.</returns>
         Task<ITraversal<S, E>> SubmitAsync<S, E>(Bytecode bytecode);
+        
+        ITransaction Tx(GraphTraversalSource graphTraversalSource);
+        bool IsSessionBound { get; }
+        Task CommitAsync();
+        Task RollbackAsync();
     }
 }
\ No newline at end of file
diff --git a/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/Bytecode.cs b/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/Bytecode.cs
index 7149e8b..cd53a1c 100644
--- a/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/Bytecode.cs
+++ b/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/Bytecode.cs
@@ -38,7 +38,7 @@ namespace Gremlin.Net.Process.Traversal
     /// </remarks>
     public class Bytecode
     {
-        private static readonly object[] EmptyArray = new object[0];
+        private static readonly object[] EmptyArray = Array.Empty<object>();
 
         /// <summary>
         ///     Initializes a new instance of the <see cref="Bytecode" /> class.
diff --git a/gremlin-dotnet/src/Gremlin.Net/Process/Remote/IRemoteConnection.cs b/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/GraphOp.cs
similarity index 55%
copy from gremlin-dotnet/src/Gremlin.Net/Process/Remote/IRemoteConnection.cs
copy to gremlin-dotnet/src/Gremlin.Net/Process/Traversal/GraphOp.cs
index 5393bcb..b938541 100644
--- a/gremlin-dotnet/src/Gremlin.Net/Process/Remote/IRemoteConnection.cs
+++ b/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/GraphOp.cs
@@ -21,22 +21,18 @@
 
 #endregion
 
-using System.Threading.Tasks;
-using Gremlin.Net.Process.Traversal;
-
-namespace Gremlin.Net.Process.Remote
+namespace Gremlin.Net.Process.Traversal
 {
-    /// <summary>
-    ///     A simple abstraction of a "connection" to a "server".
-    /// </summary>
-    public interface IRemoteConnection
+    public static class GraphOp
     {
-        /// <summary>
-        ///     Submits <see cref="ITraversal" /> <see cref="Bytecode" /> to a server and returns a
-        ///     <see cref="ITraversal" />.
-        /// </summary>
-        /// <param name="bytecode">The <see cref="Bytecode" /> to send.</param>
-        /// <returns>The <see cref="ITraversal" /> with the results and optional side-effects.</returns>
-        Task<ITraversal<S, E>> SubmitAsync<S, E>(Bytecode bytecode);
+        public static Bytecode Commit { get; } = CreateGraphOp("tx", "commit");
+        public static Bytecode Rollback { get; } = CreateGraphOp("tx", "rollback");
+
+        private static Bytecode CreateGraphOp(string name, object value)
+        {
+            var bytecode = new Bytecode();
+            bytecode.AddSource(name, value);
+            return bytecode;
+        }
     }
 }
\ No newline at end of file
diff --git a/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/GraphTraversalSource.cs b/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/GraphTraversalSource.cs
index db4c521..1dfa4f4 100644
--- a/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/GraphTraversalSource.cs
+++ b/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/GraphTraversalSource.cs
@@ -24,6 +24,7 @@
 using System;
 using System.Collections.Generic;
 using System.Linq;
+using Gremlin.Net.Driver.Remote;
 using Gremlin.Net.Process.Remote;
 using Gremlin.Net.Process.Traversal.Strategy.Decoration;
 using Gremlin.Net.Structure;
@@ -38,6 +39,10 @@ namespace Gremlin.Net.Process.Traversal
     /// </summary>
     public class GraphTraversalSource
     {
+        private readonly IRemoteConnection _connection;
+
+        public bool IsSessionBound => _connection is { IsSessionBound: true };
+        
         /// <summary>
         ///     Gets or sets the traversal strategies associated with this graph traversal source.
         /// </summary>
@@ -71,6 +76,15 @@ namespace Gremlin.Net.Process.Traversal
             Bytecode = bytecode;
         }
 
+        public GraphTraversalSource(ICollection<ITraversalStrategy> traversalStrategies, Bytecode bytecode,
+            IRemoteConnection connection)
+            : this(traversalStrategies.Where(strategy => strategy.GetType() != typeof(RemoteStrategy)).ToList(),
+                bytecode)
+        {
+            _connection = connection;
+            TraversalStrategies.Add(new RemoteStrategy(connection));
+        }
+
         public GraphTraversalSource With(string key)
         {
             return With(key, true);
@@ -243,12 +257,19 @@ namespace Gremlin.Net.Process.Traversal
         ///     <see cref="GraphTraversal{SType, EType}" />.
         /// </param>
         /// <returns>A <see cref="GraphTraversalSource" /> configured to use the provided <see cref="IRemoteConnection" />.</returns>
-        public GraphTraversalSource WithRemote(IRemoteConnection remoteConnection)
+        public GraphTraversalSource WithRemote(IRemoteConnection remoteConnection) =>
+            new GraphTraversalSource(new List<ITraversalStrategy>(TraversalStrategies),
+                new Bytecode(Bytecode), remoteConnection);
+
+        public ITransaction Tx()
         {
-            var source = new GraphTraversalSource(new List<ITraversalStrategy>(TraversalStrategies),
-                new Bytecode(Bytecode));
-            source.TraversalStrategies.Add(new RemoteStrategy(remoteConnection));
-            return source;
+            // you can't do g.tx().begin().tx() - no child transactions
+            if (IsSessionBound)
+            {
+                throw new InvalidOperationException(
+                    "This GraphTraversalSource is already bound to a transaction - child transactions are not supported");
+            }
+            return _connection.Tx(this);
         }
 
         /// <summary>
@@ -367,7 +388,6 @@ namespace Gremlin.Net.Process.Traversal
                 traversal.Bytecode.AddStep("io", file);
             return traversal;
         }
-
     }
     
 #pragma warning restore 1591
diff --git a/gremlin-dotnet/src/Gremlin.Net/Process/Remote/IRemoteConnection.cs b/gremlin-dotnet/src/Gremlin.Net/Structure/ITransaction.cs
similarity index 59%
copy from gremlin-dotnet/src/Gremlin.Net/Process/Remote/IRemoteConnection.cs
copy to gremlin-dotnet/src/Gremlin.Net/Structure/ITransaction.cs
index 5393bcb..f3dc77e 100644
--- a/gremlin-dotnet/src/Gremlin.Net/Process/Remote/IRemoteConnection.cs
+++ b/gremlin-dotnet/src/Gremlin.Net/Structure/ITransaction.cs
@@ -24,19 +24,12 @@
 using System.Threading.Tasks;
 using Gremlin.Net.Process.Traversal;
 
-namespace Gremlin.Net.Process.Remote
+namespace Gremlin.Net.Structure
 {
-    /// <summary>
-    ///     A simple abstraction of a "connection" to a "server".
-    /// </summary>
-    public interface IRemoteConnection
+    public interface ITransaction
     {
-        /// <summary>
-        ///     Submits <see cref="ITraversal" /> <see cref="Bytecode" /> to a server and returns a
-        ///     <see cref="ITraversal" />.
-        /// </summary>
-        /// <param name="bytecode">The <see cref="Bytecode" /> to send.</param>
-        /// <returns>The <see cref="ITraversal" /> with the results and optional side-effects.</returns>
-        Task<ITraversal<S, E>> SubmitAsync<S, E>(Bytecode bytecode);
+        GraphTraversalSource Begin();
+        Task CommitAsync();
+        Task RollbackAsync();
     }
 }
\ No newline at end of file
diff --git a/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Process/Traversal/DriverRemoteConnection/GraphTraversalModificationTests.cs b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Process/Traversal/DriverRemoteConnection/GraphTraversalModificationTests.cs
new file mode 100644
index 0000000..341cde0
--- /dev/null
+++ b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Process/Traversal/DriverRemoteConnection/GraphTraversalModificationTests.cs
@@ -0,0 +1,91 @@
+#region License
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#endregion
+
+using System;
+using System.Threading.Tasks;
+using Gremlin.Net.Process.Remote;
+using Gremlin.Net.Process.Traversal;
+using Xunit;
+
+namespace Gremlin.Net.IntegrationTest.Process.Traversal.DriverRemoteConnection
+{
+    public class GraphTraversalModificationTests : IDisposable
+    {
+        private readonly IRemoteConnection _connection = new RemoteConnectionFactory().CreateRemoteConnection("g");
+
+        [Fact]
+        public async Task ShouldSupportRemoteTransactionsCommit()
+        {
+            var g = AnonymousTraversalSource.Traversal().WithRemote(_connection);
+            var tx = g.Tx();
+            var gtx = tx.Begin();
+            await gtx.AddV("person").Property("name", "florian").Promise(t => t.Iterate()).ConfigureAwait(false);
+            await gtx.AddV("person").Property("name", "josh").Promise(t => t.Iterate()).ConfigureAwait(false);
+            
+            // Assert within the transaction
+            var count = await gtx.V().Count().Promise(t => t.Next()).ConfigureAwait(false);
+            Assert.Equal(2, count);
+            
+            // Now commit changes to test outside of the transaction
+            await tx.CommitAsync().ConfigureAwait(false);
+
+            count = await g.V().Count().Promise(t => t.Next()).ConfigureAwait(false);
+            Assert.Equal(2, count);
+            
+            g.V().Count().Next();
+        }
+        
+        [Fact]
+        public async Task ShouldSupportRemoteTransactionsRollback()
+        {
+            var g = AnonymousTraversalSource.Traversal().WithRemote(_connection);
+            var tx = g.Tx();
+            var gtx = tx.Begin();
+            await gtx.AddV("person").Property("name", "florian").Promise(t => t.Iterate()).ConfigureAwait(false);
+            await gtx.AddV("person").Property("name", "josh").Promise(t => t.Iterate()).ConfigureAwait(false);
+            
+            // Assert within the transaction
+            var count = await gtx.V().Count().Promise(t => t.Next()).ConfigureAwait(false);
+            Assert.Equal(2, count);
+            
+            // Now rollback changes to test outside of the transaction
+            await tx.RollbackAsync().ConfigureAwait(false);
+
+            count = await g.V().Count().Promise(t => t.Next()).ConfigureAwait(false);
+            Assert.Equal(2, count);
+            
+            g.V().Count().Next();
+        }
+
+        public void Dispose()
+        {
+            EmptyGraph();
+        }
+
+        private void EmptyGraph()
+        {
+            var g = AnonymousTraversalSource.Traversal().WithRemote(_connection);
+            g.V().Drop().Iterate();
+        }
+    }
+}
\ No newline at end of file
diff --git a/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Process/Traversal/DriverRemoteConnection/GraphTraversalTests.cs b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Process/Traversal/DriverRemoteConnection/GraphTraversalTests.cs
index 89597a3..b0347bc 100644
--- a/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Process/Traversal/DriverRemoteConnection/GraphTraversalTests.cs
+++ b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Process/Traversal/DriverRemoteConnection/GraphTraversalTests.cs
@@ -247,4 +247,4 @@ namespace Gremlin.Net.IntegrationTest.Process.Traversal.DriverRemoteConnection
             Assert.Equal(6, count);
         }
     }
-}
+}
\ No newline at end of file
diff --git a/gremlin-dotnet/test/Gremlin.Net.UnitTest/Driver/Remote/DriverRemoteTransactionTests.cs b/gremlin-dotnet/test/Gremlin.Net.UnitTest/Driver/Remote/DriverRemoteTransactionTests.cs
new file mode 100644
index 0000000..51f2fc3
--- /dev/null
+++ b/gremlin-dotnet/test/Gremlin.Net.UnitTest/Driver/Remote/DriverRemoteTransactionTests.cs
@@ -0,0 +1,57 @@
+#region License
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#endregion
+
+using System;
+using Gremlin.Net.Driver;
+using Gremlin.Net.Driver.Remote;
+using Gremlin.Net.Process.Traversal;
+using Moq;
+using Xunit;
+
+namespace Gremlin.Net.UnitTest.Driver.Remote
+{
+    public class DriverRemoteTransactionTests
+    {
+        [Fact]
+        public void ShouldNotAllowBeginMoreThanOnce()
+        {
+            var g = AnonymousTraversalSource.Traversal()
+                .WithRemote(new DriverRemoteConnection(Mock.Of<IGremlinClient>()));
+            var tx = g.Tx();
+            tx.Begin();
+
+            Assert.Throws<InvalidOperationException>(() => tx.Begin());
+        }
+        
+        [Fact]
+        public void ShouldNotSupportChildTransactions()
+        {
+            var g = AnonymousTraversalSource.Traversal()
+                .WithRemote(new DriverRemoteConnection(Mock.Of<IGremlinClient>()));
+            var tx = g.Tx();
+            
+            var gtx = tx.Begin();
+            Assert.Throws<InvalidOperationException>(() => gtx.Tx());
+        }
+    }
+}
\ No newline at end of file