My bad on wording the question, I meant is there a step (currently in .NET driver or coming to it in future) that I can use to either abort the transaction explicitly then close the session, or as an alternative make the transaction fail intentionally? Just to make sure we're on the same page I'm looking for something to rollback in the if statement below:
var sessionId = Guid.NewGuid().ToString();
using(var gremlinClient = new GremlinClient(new GremlinServer(endpoint, port), sessionId: sessionId))
{
var remoteConnection= new DriverRemoteConnection(gremlinClient);
var g = AnonymousTraversalSource.Traversal().WithRemote(remoteConnection);
// add vertices
// add/remove edges
var createsCycle = await g.V().As("a").Repeat(__.Both().SimplePath()).Emit(__.Loops().Is(P.Gt(1))).Both().Where(P.Eq("a")).Promise(t => t.HasNext());
if (createsCycle)
{
// abort or rollback
}
}
Neptune supports transactions with sessions as per their
doc:
The client starts a session transaction when it is initialized. All the queries that you run during the session form are committed only when you call client.close( ). Again, if a single query fails, or if you don't close the connection within the maximum session lifetime that Neptune supports, the session transaction fails, and all the queries in it are rolled back.
It says you can close the session and that will commit, but I was hoping there would be a step to rollback before closing.
Thanks for the suggestions, I wish there was a TinkerGraph for other languages, as well.
Since Neptune
guarantees no dirty-reads, so 1.a is not a problem. 1.b might become an issue, because Neptune does not support sideEffect step and variables, still I don't think my use case would create very large traversals. I wanted to go with 2 but it's like managing the transaction myself and I prefer to leave that to the database if possible.
So I'd rather to go with 1, if what I'm asking above could not be done.
To simplify the problem, let's say it's okay if the new vertices remain in the graph, however the edges must roll back, if cycle was found.
I need to clarify that requests to API are not necessarily edge creation, they could also include edge removal.
I tried to come up with a traversal that would modify->validate->rollback/commit and this is what I thought should work:
g.V(newEdgeSrcId0).addE(newEdgeLabel0).to(V(newEdgeDstId0)).as('newEdge')
...
.V(newEdgeSrcIdN).addE(newEdgeLabelN).to(V(newEdgeDstIdN)).as('newEdge')
.coalesce(
V().outE(edgeIdsList).drop(),
// look for cycles
V(newVerticesIds).as('a')
.repeat(both().simplePath())
.emit(loops().is(gt(1)))
.both().where(eq('a')).path()
.dedup().by(unfold().order().by(id).dedup().fold())
.as('cycle')
.where(count().is(gt(0)))
// rollback and return cycle
.V(deleteEdgeSrcId0).addE(deleteEdgeLabel0).to(V(deleteEdgeDstId0))
...
.V(deleteEdgeSrcIdN).addE(deleteEdgeLabelN).to(V(deleteEdgeDstIdN))
.coalesce(
V().outE().hasId(select(all, 'newEdge').unfold().id()).drop(),
select('cycle')
)
)
I tried the query for one edge creation, neither returned the cycle nor dropped the edge:
gremlin> g = TinkerFactory.createModern().traversal()
// break the existing cycle by removing edge 1->4
gremlin> g.E('8').drop()
gremlin> g.V('1').addE('test').to(V('4')).as('newEdge')
.V('1', '4').as('a').repeat(both().simplePath()).emit(loops().is(gt(1)))
.both().where(eq('a')).path()
.dedup().by(unfold().order().by(id).dedup().fold()).as('cycle')
.where(count().is(gt(0)))
.coalesce(V().outE().hasId(select(all, 'newEdge').unfold().id()).drop(),
select('cycle')))
just to see if I can get the cycle, which again returns nothing:
// reload graph and break existing cycle
gremlin> g.V('1').addE('test').to(V('4')).as('newEdge')
.V('1', '4').as('a').repeat(both().simplePath()).emit(loops().is(gt(1)))
.both().where(eq('a')).path()
.dedup().by(unfold().order().by(id).dedup().fold())
but if I run the cycle detection part afterwards, it will give the cycle:
gremlin> g.V('1', '4').as('a').repeat(both().simplePath()).emit(loops().is(gt(1)))
.both().where(eq('a')).path()
.dedup().by(unfold().order().by(id).dedup().fold())
==>[v[1],v[4],v[3],v[1]]
even dropping the created edge in the same traversal didn't work:
gremlin> g.V('1').addE('test').to(V('4')).as('newEdge').V().outE().hasId(select(all, 'newEdge').unfold().id()).drop()
gremlin> g.E()
==>e[7][1-knows->2]
==>e[8][1-knows->4]
==>e[9][1-created->3]
==>e[10][4-created->5]
==>e[11][4-created->3]
==>e[12][6-created->3]
==>e[13][1-test->4]
do you have any hints for fixing this?