import * as Datastore from "@google-cloud/datastore" ; import { errorCodes } from "../enums/errorCodes" ; import { tsDatastoreOrm } from "../tsDatastoreOrm" ; import { ITransactionManagerParams } from "../types" ; import { timeout , updateStack } from "../utils" ; import { Session } from "./Session" ; type ITransactionResult < T > = { value : T ; hasCommitted : boolean ; totalRetry : number ; } ; export class TransactionManager { public readonly datastore : Datastore . Datastore ; public readonly maxRetry : number ; public readonly retryDelay : number ; public readonly readOnly : boolean ; constructor ( options ) { this . datastore = options . datastore ; this . maxRetry = options . maxRetry ; this . retryDelay = options . retryDelay ; this . readOnly = options . readOnly ; } public async start < T extends any > ( callback ) { let retry = 0 ; let value ; let totalRetry = 0 ; let hasCommitted = false ; const friendlyErrorStack = tsDatastoreOrm . getFriendlyErrorStack ( ) ; do { const transaction = this . datastore . transaction ( { readOnly : this . readOnly } ) ; const session = new Session ( { transaction } ) ; try { await session . run ( ) ; value = await callback ( session ) ; await session . commit ( ) ; hasCommitted = ! ( session . transaction . skipCommit ) ; break ; } catch ( err ) { hasCommitted = false ; try { await session . rollback ( ) ; } catch ( err ) { } if ( err . code === errorCodes . ABORTED ) { if ( retry < this . maxRetry ) { totalRetry = retry ; if ( this . retryDelay ) { await timeout ( this . retryDelay ) ; } continue ; } } throw Object . assign ( err , friendlyErrorStack && { stack : updateStack ( friendlyErrorStack , err ) } ) ; } } while ( retry ++ < this . maxRetry ) ; return { value , hasCommitted , totalRetry } ; } }