Skip to content

Commit bdd56d0

Browse files
committed
v2 / Tests: clean up open txs; constraints never hang now!
1 parent 70127a5 commit bdd56d0

File tree

2 files changed

+48
-37
lines changed

2 files changed

+48
-37
lines changed

test-new/constraints._coffee

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -118,13 +118,6 @@ describe 'Constraints', ->
118118
violateConstraint _
119119

120120
it 'should support creating constraint', (_) ->
121-
# TODO: This can randomly take a *very* long time.
122-
# (Why? We're using a new and unique property. Maybe we should be
123-
# using a new and unique label too?)
124-
# So for now, we 10x the Mocha timeout.
125-
@timeout 10 * @timeout()
126-
console.log 'Creating constraint. This could take a while...'
127-
128121
constraint = DB.createConstraint
129122
label: TEST_LABEL
130123
property: TEST_PROP

test-new/transactions._coffee

Lines changed: 48 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,37 @@ neo4j = require '../'
1616

1717
[TEST_NODE_A, TEST_NODE_B, TEST_REL] = []
1818

19+
OPEN_TXS = []
20+
1921

2022
## HELPERS
2123

24+
# To help with cleanup (see below):
25+
beginTx = ->
26+
tx = DB.beginTransaction()
27+
OPEN_TXS.push tx
28+
tx
29+
30+
# NOTE: Open transactions can cause Neo4j queries & requests to hang,
31+
# e.g. because a transaction has a lock, or when creating constraints.
32+
# The default transaction expiry time of 60 seconds is also far too long.
33+
# So we track all transactions we create (see above), and this method can be
34+
# called to clean those transactions up whenever needed.
35+
# It *should* be called at the end at least (see test near end of suite below).
36+
cleanupTxs = (_) ->
37+
while tx = OPEN_TXS.pop()
38+
switch tx.state
39+
when tx.STATE_COMMITTED, tx.STATE_ROLLED_BACK, tx.STATE_EXPIRED
40+
continue
41+
when tx.STATE_PENDING
42+
throw new Error 'Unexpected: transaction state is pending!
43+
Maybe a test timed out mid-request?'
44+
when tx.STATE_OPEN
45+
tx.rollback _
46+
expect(tx.state).to.equal tx.STATE_ROLLED_BACK
47+
else
48+
throw new Error "Unrecognized tx state! #{tx.state}"
49+
2250
# Calls the given asynchronous function with a placeholder callback, and
2351
# immediately returns a "future" that can be called with a real callback.
2452
# TODO: Achieve this with Streamline futures once we upgrade to 1.0.
@@ -62,14 +90,14 @@ expectTxErrorRolledBack = (tx, _) ->
6290
describe 'Transactions', ->
6391

6492
it 'should support simple queries', (_) ->
65-
tx = DB.beginTransaction()
93+
tx = beginTx()
6694

6795
[{foo}] = tx.cypher 'RETURN "bar" AS foo', _
6896

6997
expect(foo).to.equal 'bar'
7098

7199
it 'should convey pending state, and reject concurrent requests', (done) ->
72-
tx = DB.beginTransaction()
100+
tx = beginTx()
73101
expect(tx.state).to.equal tx.STATE_OPEN
74102

75103
fn = ->
@@ -89,7 +117,7 @@ describe 'Transactions', ->
89117
fixtures.createTestGraph module, 2, _
90118

91119
it 'should isolate effects', (_) ->
92-
tx = DB.beginTransaction()
120+
tx = beginTx()
93121

94122
# NOTE: It's important for us to create something new here, rather than
95123
# modify something existing. Otherwise, since we don't explicitly
@@ -139,7 +167,7 @@ describe 'Transactions', ->
139167
expect(results).to.be.empty()
140168

141169
it 'should support committing, and reject subsequent requests', (_) ->
142-
tx = DB.beginTransaction()
170+
tx = beginTx()
143171

144172
[{nodeA}] = tx.cypher
145173
query: '''
@@ -173,14 +201,14 @@ describe 'Transactions', ->
173201
expect(nodeA.properties.test).to.equal 'committing'
174202

175203
it 'should support committing before any queries', (_) ->
176-
tx = DB.beginTransaction()
204+
tx = beginTx()
177205
expect(tx.state).to.equal tx.STATE_OPEN
178206

179207
tx.commit _
180208
expect(tx.state).to.equal tx.STATE_COMMITTED
181209

182210
it 'should support auto-committing', (_) ->
183-
tx = DB.beginTransaction()
211+
tx = beginTx()
184212

185213
# Rather than test auto-committing on the first query, which doesn't
186214
# actually create a new transaction, auto-commit on the second.
@@ -234,7 +262,7 @@ describe 'Transactions', ->
234262
expect(nodeA.properties.i).to.equal 2
235263

236264
it 'should support rolling back, and reject subsequent requests', (_) ->
237-
tx = DB.beginTransaction()
265+
tx = beginTx()
238266

239267
[{nodeA}] = tx.cypher
240268
query: '''
@@ -268,7 +296,7 @@ describe 'Transactions', ->
268296
expect(nodeA.properties.test).to.not.equal 'rolling back'
269297

270298
it 'should support rolling back before any queries', (_) ->
271-
tx = DB.beginTransaction()
299+
tx = beginTx()
272300
expect(tx.state).to.equal tx.STATE_OPEN
273301

274302
tx.rollback _
@@ -277,7 +305,7 @@ describe 'Transactions', ->
277305
# NOTE: Skipping this test by default, because it's slow (we have to pause
278306
# one second; see note within) and not really a mission-critical feature.
279307
it.skip 'should support renewing (slow)', (_) ->
280-
tx = DB.beginTransaction()
308+
tx = beginTx()
281309

282310
[{nodeA}] = tx.cypher
283311
query: '''
@@ -320,12 +348,6 @@ describe 'Transactions', ->
320348
expect(tx.expiresIn).to.be.greaterThan 0
321349
expect(tx.expiresIn).to.equal tx.expiresAt - new Date
322350

323-
# To prevent Neo4j from hanging at the end waiting for this transaction
324-
# to commit or expire (since it touches the existing graph, and our last
325-
# step is to delete the existing graph), roll this transaction back.
326-
tx.rollback _
327-
expect(tx.state).to.equal tx.STATE_ROLLED_BACK
328-
329351
# We also ensure that renewing didn't cause the transaction to commit.
330352
[{nodeA}] = DB.cypher
331353
query: '''
@@ -339,7 +361,7 @@ describe 'Transactions', ->
339361
expect(nodeA.properties.test).to.not.equal 'renewing'
340362

341363
it 'should properly handle (fatal) client errors', (_) ->
342-
tx = DB.beginTransaction()
364+
tx = beginTx()
343365

344366
[{nodeA}] = tx.cypher
345367
query: '''
@@ -399,8 +421,8 @@ describe 'Transactions', ->
399421
# We can do this by having two separate transactions take locks on the
400422
# same two nodes, across two queries, but in opposite order.
401423
# (Taking a lock on a node just means writing to the node.)
402-
tx1 = DB.beginTransaction()
403-
tx2 = DB.beginTransaction()
424+
tx1 = beginTx()
425+
tx2 = beginTx()
404426

405427
[[{nodeA}], [{nodeB}]] = flows.collect _, [
406428
defer tx1.cypher.bind tx1,
@@ -481,15 +503,8 @@ describe 'Transactions', ->
481503
expect(nodeB.properties.test).to.not.equal 'transient errors'
482504
expect(nodeB.properties.tx).to.equal 1
483505

484-
# To prevent Neo4j from hanging at the end waiting for this transaction
485-
# to commit or expire (since it touches the existing graph, and our last
486-
# step is to delete the existing graph), roll this transaction back.
487-
expect(tx1.state).to.equal tx1.STATE_OPEN
488-
tx1.rollback _
489-
expect(tx1.state).to.equal tx1.STATE_ROLLED_BACK
490-
491506
it 'should properly handle (fatal) database errors', (_) ->
492-
tx = DB.beginTransaction()
507+
tx = beginTx()
493508

494509
# Important: don't auto-commit in the first query, because that doesn't
495510
# let us test that a transaction gets *returned* and *then* rolled back.
@@ -541,7 +556,7 @@ describe 'Transactions', ->
541556
expect(nodeA.properties.test).to.not.equal 'database errors'
542557

543558
it 'should properly handle (fatal) errors during commit', (_) ->
544-
tx = DB.beginTransaction()
559+
tx = beginTx()
545560

546561
# Important: don't auto-commit in the first query, because that doesn't
547562
# let us test that a transaction gets *returned* and *then* rolled back.
@@ -581,7 +596,7 @@ describe 'Transactions', ->
581596
expect(tx.state).to.equal tx.STATE_ROLLED_BACK
582597

583598
it 'should properly handle (fatal) errors on the first query', (_) ->
584-
tx = DB.beginTransaction()
599+
tx = beginTx()
585600
expect(tx.state).to.equal tx.STATE_OPEN
586601

587602
# For precision, implementing this step without Streamline.
@@ -599,7 +614,7 @@ describe 'Transactions', ->
599614

600615
it 'should properly handle (fatal) errors
601616
on an auto-commit first query', (_) ->
602-
tx = DB.beginTransaction()
617+
tx = beginTx()
603618
expect(tx.state).to.equal tx.STATE_OPEN
604619

605620
# For precision, implementing this step without Streamline.
@@ -618,7 +633,7 @@ describe 'Transactions', ->
618633
expect(tx.state).to.equal tx.STATE_ROLLED_BACK
619634

620635
it 'should properly handle (fatal) errors with batching', (_) ->
621-
tx = DB.beginTransaction()
636+
tx = beginTx()
622637

623638
results = tx.cypher [
624639
query: '''
@@ -709,5 +724,8 @@ describe 'Transactions', ->
709724

710725
it 'should support streaming (TODO)'
711726

727+
it '(clean up open txs)', (_) ->
728+
cleanupTxs _
729+
712730
it '(delete test graph)', (_) ->
713731
fixtures.deleteTestGraph module, _

0 commit comments

Comments
 (0)