@@ -30,6 +30,105 @@ const LOCKS_TERMINATED_ERROR =
3030 'Neo.TransientError.Transaction.LockClientStopped'
3131const OOM_ERROR = 'Neo.DatabaseError.General.OutOfMemoryError'
3232
33+ // Not exactly integration tests but annoyingly slow for being a unit tests.
34+ describe ( '#integration TransactionExecutor' , ( ) => {
35+ let originalTimeout
36+
37+ beforeEach ( ( ) => {
38+ originalTimeout = jasmine . DEFAULT_TIMEOUT_INTERVAL
39+ jasmine . DEFAULT_TIMEOUT_INTERVAL = 30000
40+ } )
41+
42+ afterEach ( ( ) => {
43+ jasmine . DEFAULT_TIMEOUT_INTERVAL = originalTimeout
44+ } )
45+
46+ it ( 'should retry until database error happens' , async ( ) => {
47+ await testNoRetryOnUnknownError (
48+ [
49+ SERVICE_UNAVAILABLE ,
50+ SERVICE_UNAVAILABLE ,
51+ TRANSIENT_ERROR_2 ,
52+ SESSION_EXPIRED ,
53+ UNKNOWN_ERROR ,
54+ SESSION_EXPIRED
55+ ] ,
56+ 5
57+ )
58+ } )
59+
60+ it ( 'should stop retrying when time expires' , async ( ) => {
61+ let clock
62+ const usedTransactions = [ ]
63+ try {
64+ const executor = new TransactionExecutor ( )
65+ const realWork = transactionWork (
66+ [
67+ SERVICE_UNAVAILABLE ,
68+ SESSION_EXPIRED ,
69+ TRANSIENT_ERROR_1 ,
70+ TRANSIENT_ERROR_2
71+ ] ,
72+ 42
73+ )
74+
75+ await executor . execute ( transactionCreator ( ) , tx => {
76+ expect ( tx ) . toBeDefined ( )
77+ usedTransactions . push ( tx )
78+ if ( usedTransactions . length === 3 ) {
79+ const currentTime = Date . now ( )
80+ clock = lolex . install ( )
81+ clock . setSystemTime ( currentTime + 30001 ) // move `Date.now()` call forward by 30 seconds
82+ }
83+ return realWork ( )
84+ } )
85+
86+ expect ( false ) . toBeTruthy ( 'should have thrown an exception' )
87+ } catch ( error ) {
88+ expect ( usedTransactions . length ) . toEqual ( 3 )
89+ expectAllTransactionsToBeClosed ( usedTransactions )
90+ expect ( error . code ) . toEqual ( TRANSIENT_ERROR_1 )
91+ } finally {
92+ if ( clock ) {
93+ clock . uninstall ( )
94+ }
95+ }
96+ } )
97+
98+ it ( 'should cancel in-flight timeouts when closed' , async ( ) => {
99+ const fakeSetTimeout = setTimeoutMock . install ( )
100+ try {
101+ const executor = new TransactionExecutor ( )
102+ // do not execute setTimeout callbacks
103+ fakeSetTimeout . pause ( )
104+
105+ executor . execute ( transactionCreator ( [ SERVICE_UNAVAILABLE ] ) , ( ) =>
106+ Promise . resolve ( 42 )
107+ )
108+ executor . execute ( transactionCreator ( [ TRANSIENT_ERROR_1 ] ) , ( ) =>
109+ Promise . resolve ( 4242 )
110+ )
111+ executor . execute ( transactionCreator ( [ SESSION_EXPIRED ] ) , ( ) =>
112+ Promise . resolve ( 424242 )
113+ )
114+
115+ await new Promise ( ( resolve , reject ) => {
116+ fakeSetTimeout . setTimeoutOriginal ( ( ) => {
117+ try {
118+ executor . close ( )
119+ expect ( fakeSetTimeout . clearedTimeouts . length ) . toEqual ( 3 )
120+ resolve ( )
121+ } catch ( error ) {
122+ reject ( error )
123+ }
124+ } , 1000 )
125+ } )
126+ } finally {
127+ fakeSetTimeout . uninstall ( )
128+ }
129+ } )
130+ } )
131+
33132describe ( '#unit TransactionExecutor' , ( ) => {
34133 let originalTimeout
35134
@@ -97,44 +196,6 @@ describe('#unit TransactionExecutor', () => {
97196 ) . toBeRejectedWith ( error )
98197 } )
99198
100- it ( 'should stop retrying when time expires' , async ( ) => {
101- let clock
102- const usedTransactions = [ ]
103- try {
104- const executor = new TransactionExecutor ( )
105- const realWork = transactionWork (
106- [
107- SERVICE_UNAVAILABLE ,
108- SESSION_EXPIRED ,
109- TRANSIENT_ERROR_1 ,
110- TRANSIENT_ERROR_2
111- ] ,
112- 42
113- )
114-
115- await executor . execute ( transactionCreator ( ) , tx => {
116- expect ( tx ) . toBeDefined ( )
117- usedTransactions . push ( tx )
118- if ( usedTransactions . length === 3 ) {
119- const currentTime = Date . now ( )
120- clock = lolex . install ( )
121- clock . setSystemTime ( currentTime + 30001 ) // move `Date.now()` call forward by 30 seconds
122- }
123- return realWork ( )
124- } )
125-
126- expect ( false ) . toBeTruthy ( 'should have thrown an exception' )
127- } catch ( error ) {
128- expect ( usedTransactions . length ) . toEqual ( 3 )
129- expectAllTransactionsToBeClosed ( usedTransactions )
130- expect ( error . code ) . toEqual ( TRANSIENT_ERROR_1 )
131- } finally {
132- if ( clock ) {
133- clock . uninstall ( )
134- }
135- }
136- } )
137-
138199 it ( 'should retry when given transaction creator throws once' , async ( ) => {
139200 await testRetryWhenTransactionCreatorFails ( [ SERVICE_UNAVAILABLE ] )
140201 } )
@@ -190,20 +251,6 @@ describe('#unit TransactionExecutor', () => {
190251 ] )
191252 } )
192253
193- it ( 'should retry until database error happens' , async ( ) => {
194- await testNoRetryOnUnknownError (
195- [
196- SERVICE_UNAVAILABLE ,
197- SERVICE_UNAVAILABLE ,
198- TRANSIENT_ERROR_2 ,
199- SESSION_EXPIRED ,
200- UNKNOWN_ERROR ,
201- SESSION_EXPIRED
202- ] ,
203- 5
204- )
205- } )
206-
207254 it ( 'should retry when transaction work throws and rollback fails' , async ( ) => {
208255 await testRetryWhenTransactionWorkThrowsAndRollbackFails (
209256 [
@@ -216,39 +263,6 @@ describe('#unit TransactionExecutor', () => {
216263 )
217264 } )
218265
219- it ( 'should cancel in-flight timeouts when closed' , async ( ) => {
220- const fakeSetTimeout = setTimeoutMock . install ( )
221- try {
222- const executor = new TransactionExecutor ( )
223- // do not execute setTimeout callbacks
224- fakeSetTimeout . pause ( )
225-
226- executor . execute ( transactionCreator ( [ SERVICE_UNAVAILABLE ] ) , ( ) =>
227- Promise . resolve ( 42 )
228- )
229- executor . execute ( transactionCreator ( [ TRANSIENT_ERROR_1 ] ) , ( ) =>
230- Promise . resolve ( 4242 )
231- )
232- executor . execute ( transactionCreator ( [ SESSION_EXPIRED ] ) , ( ) =>
233- Promise . resolve ( 424242 )
234- )
235-
236- await new Promise ( ( resolve , reject ) => {
237- fakeSetTimeout . setTimeoutOriginal ( ( ) => {
238- try {
239- executor . close ( )
240- expect ( fakeSetTimeout . clearedTimeouts . length ) . toEqual ( 3 )
241- resolve ( )
242- } catch ( error ) {
243- reject ( error )
244- }
245- } , 1000 )
246- } )
247- } finally {
248- fakeSetTimeout . uninstall ( )
249- }
250- } )
251-
252266 it ( 'should allow zero max retry time' , ( ) => {
253267 const executor = new TransactionExecutor ( 0 )
254268 expect ( executor . _maxRetryTimeMs ) . toEqual ( 0 )
@@ -396,35 +410,35 @@ describe('#unit TransactionExecutor', () => {
396410 fakeSetTimeout . uninstall ( )
397411 }
398412 }
413+ } )
399414
400- async function testNoRetryOnUnknownError (
401- errorCodes ,
402- expectedWorkInvocationCount
403- ) {
404- const executor = new TransactionExecutor ( )
405- const usedTransactions = [ ]
406- const realWork = transactionWork ( errorCodes , 42 )
407-
408- try {
409- await executor . execute ( transactionCreator ( ) , tx => {
410- expect ( tx ) . toBeDefined ( )
411- usedTransactions . push ( tx )
412- return realWork ( )
413- } )
414- } catch ( error ) {
415- expect ( usedTransactions . length ) . toEqual ( expectedWorkInvocationCount )
416- expectAllTransactionsToBeClosed ( usedTransactions )
417- if ( errorCodes . length === 1 ) {
418- expect ( error . code ) . toEqual ( errorCodes [ 0 ] )
419- } else {
420- expect ( error . code ) . toEqual ( errorCodes [ expectedWorkInvocationCount - 1 ] )
421- }
422- return
415+ async function testNoRetryOnUnknownError (
416+ errorCodes ,
417+ expectedWorkInvocationCount
418+ ) {
419+ const executor = new TransactionExecutor ( )
420+ const usedTransactions = [ ]
421+ const realWork = transactionWork ( errorCodes , 42 )
422+
423+ try {
424+ await executor . execute ( transactionCreator ( ) , tx => {
425+ expect ( tx ) . toBeDefined ( )
426+ usedTransactions . push ( tx )
427+ return realWork ( )
428+ } )
429+ } catch ( error ) {
430+ expect ( usedTransactions . length ) . toEqual ( expectedWorkInvocationCount )
431+ expectAllTransactionsToBeClosed ( usedTransactions )
432+ if ( errorCodes . length === 1 ) {
433+ expect ( error . code ) . toEqual ( errorCodes [ 0 ] )
434+ } else {
435+ expect ( error . code ) . toEqual ( errorCodes [ expectedWorkInvocationCount - 1 ] )
423436 }
424-
425- expect ( false ) . toBeTruthy ( 'exception expected' )
437+ return
426438 }
427- } )
439+
440+ expect ( false ) . toBeTruthy ( 'exception expected' )
441+ }
428442
429443function transactionCreator ( commitErrorCodes , rollbackErrorCodes ) {
430444 const remainingCommitErrorCodes = ( commitErrorCodes || [ ] ) . slice ( ) . reverse ( )
0 commit comments