X-Git-Url: https://git.immae.eu/?a=blobdiff_plain;f=server%2Fhelpers%2Fdatabase-utils.ts;h=aedcc5e6409d89861462352f8dfc92f151d3ef95;hb=fb3c9e2bf5b45d6d283cea4d55cc0d49eb58e3cb;hp=e174dc3e960594ee7e6e2412ec09b5f6d9f9bd02;hpb=ad0997adfb9e1e3b1ff54338d7558cf7b18440ea;p=github%2FChocobozzz%2FPeerTube.git diff --git a/server/helpers/database-utils.ts b/server/helpers/database-utils.ts index e174dc3e9..aedcc5e64 100644 --- a/server/helpers/database-utils.ts +++ b/server/helpers/database-utils.ts @@ -1,45 +1,123 @@ -// TODO: import from ES6 when retry typing file will include errorFilter function -import * as retry from 'async/retry' -import * as Promise from 'bluebird' - +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' -type RetryTransactionWrapperOptions = { errorMessage: string, arguments?: any[] } -function retryTransactionWrapper (functionToRetry: (... args) => Promise, options: RetryTransactionWrapperOptions) { - const args = options.arguments ? options.arguments : [] +function retryTransactionWrapper ( + functionToRetry: (arg1: A, arg2: B, arg3: C, arg4: D) => Promise, + arg1: A, + arg2: B, + arg3: C, + arg4: D, +): Promise + +function retryTransactionWrapper ( + functionToRetry: (arg1: A, arg2: B, arg3: C) => Promise, + arg1: A, + arg2: B, + arg3: C +): Promise + +function retryTransactionWrapper ( + functionToRetry: (arg1: A, arg2: B) => Promise, + arg1: A, + arg2: B +): Promise + +function retryTransactionWrapper ( + functionToRetry: (arg1: A) => Promise, + arg1: A +): Promise - return transactionRetryer( - function (callback) { - functionToRetry.apply(this, args) - .then(result => callback(null, result)) +function retryTransactionWrapper ( + functionToRetry: () => Promise | Bluebird +): Promise + +function retryTransactionWrapper ( + functionToRetry: (...args: any[]) => Promise, + ...args: any[] +): Promise { + return transactionRetryer(callback => { + functionToRetry.apply(null, args) + .then((result: T) => callback(null, result)) .catch(err => callback(err)) - } - ) + }) .catch(err => { - // Do not throw the error, continue the process - logger.error(options.errorMessage, err) + logger.error(`Cannot execute ${functionToRetry.name} with many retries.`, { err }) + throw err }) } -function transactionRetryer (func: Function) { - return new Promise((res, rej) => { - retry({ - times: 5, +function transactionRetryer (func: (err: any, data: T) => any) { + return new Promise((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 > (instanceToUpdate: T, baseInstance: U) { + const obj = baseInstance.toJSON() + + for (const key of Object.keys(obj)) { + instanceToUpdate[key] = obj[key] + } +} - errorFilter: function (err) { - const willRetry = (err.name === 'SequelizeDatabaseError') - logger.debug('Maybe retrying the transaction function.', { willRetry }) - return willRetry - } - }, func, function (err) { - err ? rej(err) : res() - }) +function resetSequelizeInstance (instance: Model, savedFields: object) { + Object.keys(savedFields).forEach(key => { + instance[key] = savedFields[key] }) } +function filterNonExistingModels ( + fromDatabase: T[], + newModels: T[] +) { + return fromDatabase.filter(f => !newModels.find(newModel => newModel.hasSameUniqueKeysThan(f))) +} + +function deleteAllModels > (models: T[], transaction: Transaction) { + return Promise.all(models.map(f => f.destroy({ transaction }))) +} + +// --------------------------------------------------------------------------- + +function runInReadCommittedTransaction (fn: (t: Transaction) => Promise) { + const options = { isolationLevel: Transaction.ISOLATION_LEVELS.READ_COMMITTED } + + return sequelizeTypescript.transaction(options, t => fn(t)) +} + +function afterCommitIfTransaction (t: Transaction, fn: Function) { + if (t) return t.afterCommit(() => fn()) + + return fn() +} + // --------------------------------------------------------------------------- export { + resetSequelizeInstance, retryTransactionWrapper, - transactionRetryer + transactionRetryer, + updateInstanceWithAnother, + afterCommitIfTransaction, + filterNonExistingModels, + deleteAllModels, + runInReadCommittedTransaction }