import retry from 'async/retry' ; import Bluebird from 'bluebird' ; import { Transaction } from 'sequelize' ; import { Model } from 'sequelize-typescript' ; import { sequelizeTypescript } from '@server/initializers/database' ; import { logger } from './logger' ; function retryTransactionWrapper < T , A , B , C , D > ( functionToRetry , arg1 , arg2 , arg3 , arg4 ) ; function retryTransactionWrapper < T , A , B , C > ( functionToRetry , arg1 , arg2 , arg3 ) ; function retryTransactionWrapper < T , A , B > ( functionToRetry , arg1 , arg2 ) ; function retryTransactionWrapper < T , A > ( functionToRetry , arg1 ) ; function retryTransactionWrapper < T > ( functionToRetry ) ; function retryTransactionWrapper < T > ( functionToRetry , ... args ) { return transactionRetryer < T > ( callback => { functionToRetry . apply ( null , args ) . then ( ( result ) => callback ( null , result ) ) . catch ( err => callback ( err ) ) ; } ) . catch ( err => { logger . error ( `Cannot execute ${ functionToRetry . name } with many retries.` , { err } ) ; throw err ; } ) ; } function transactionRetryer < T > ( func ) { return new Promise < T > ( ( res , rej ) => { retry ( { times : 5 , errorFilter : err => { const willRetry = ( err . name === 'SequelizeDatabaseError' ) ; logger . debug ( 'Maybe retrying the transaction function.' , { willRetry , err , tags : [ 'sql' , 'retry' ] } ) ; return willRetry ; } } , func , ( err , data ) => err ? rej ( err ) : res ( data ) ) ; } ) ; } function updateInstanceWithAnother < M , T extends U , U extends Model < M > > ( instanceToUpdate , baseInstance ) { const obj = baseInstance . toJSON ( ) ; for ( const key of Object . keys ( obj ) ) { instanceToUpdate [ key ] = obj [ key ] ; } } function resetSequelizeInstance ( instance , savedFields ) { Object . keys ( savedFields ) . forEach ( key => { instance [ key ] = savedFields [ key ] ; } ) ; } function filterNonExistingModels < T extends { hasSameUniqueKeysThan ( other ) : boolean ; } > ( fromDatabase , newModels ) { return fromDatabase . filter ( f => ! newModels . find ( newModel => newModel . hasSameUniqueKeysThan ( f ) ) ) ; } function deleteAllModels < T extends Pick < Model , 'destroy' > > ( models , transaction ) { return Promise . all ( models . map ( f => f . destroy ( { transaction } ) ) ) ; } function runInReadCommittedTransaction < T > ( fn ) { const options = { isolationLevel : Transaction . ISOLATION_LEVELS . READ_COMMITTED } ; return sequelizeTypescript . transaction ( options , t => fn ( t ) ) ; } function afterCommitIfTransaction ( t , fn ) { if ( t ) return t . afterCommit ( ( ) => fn ( ) ) ; return fn ( ) ; } export { resetSequelizeInstance , retryTransactionWrapper , transactionRetryer , updateInstanceWithAnother , afterCommitIfTransaction , filterNonExistingModels , deleteAllModels , runInReadCommittedTransaction } ;