database-utils.ts 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123
  1. import retry from 'async/retry'
  2. import Bluebird from 'bluebird'
  3. import { Transaction } from 'sequelize'
  4. import { Model } from 'sequelize-typescript'
  5. import { sequelizeTypescript } from '@server/initializers/database'
  6. import { logger } from './logger'
  7. function retryTransactionWrapper <T, A, B, C, D> (
  8. functionToRetry: (arg1: A, arg2: B, arg3: C, arg4: D) => Promise<T>,
  9. arg1: A,
  10. arg2: B,
  11. arg3: C,
  12. arg4: D,
  13. ): Promise<T>
  14. function retryTransactionWrapper <T, A, B, C> (
  15. functionToRetry: (arg1: A, arg2: B, arg3: C) => Promise<T>,
  16. arg1: A,
  17. arg2: B,
  18. arg3: C
  19. ): Promise<T>
  20. function retryTransactionWrapper <T, A, B> (
  21. functionToRetry: (arg1: A, arg2: B) => Promise<T>,
  22. arg1: A,
  23. arg2: B
  24. ): Promise<T>
  25. function retryTransactionWrapper <T, A> (
  26. functionToRetry: (arg1: A) => Promise<T>,
  27. arg1: A
  28. ): Promise<T>
  29. function retryTransactionWrapper <T> (
  30. functionToRetry: () => Promise<T> | Bluebird<T>
  31. ): Promise<T>
  32. function retryTransactionWrapper <T> (
  33. functionToRetry: (...args: any[]) => Promise<T>,
  34. ...args: any[]
  35. ): Promise<T> {
  36. return transactionRetryer<T>(callback => {
  37. functionToRetry.apply(null, args)
  38. .then((result: T) => callback(null, result))
  39. .catch(err => callback(err))
  40. })
  41. .catch(err => {
  42. logger.warn(`Cannot execute ${functionToRetry.name} with many retries.`, { err })
  43. throw err
  44. })
  45. }
  46. function transactionRetryer <T> (func: (err: any, data: T) => any) {
  47. return new Promise<T>((res, rej) => {
  48. retry(
  49. {
  50. times: 5,
  51. errorFilter: err => {
  52. const willRetry = (err.name === 'SequelizeDatabaseError')
  53. logger.debug('Maybe retrying the transaction function.', { willRetry, err, tags: [ 'sql', 'retry' ] })
  54. return willRetry
  55. }
  56. },
  57. func,
  58. (err, data) => err ? rej(err) : res(data)
  59. )
  60. })
  61. }
  62. // ---------------------------------------------------------------------------
  63. function updateInstanceWithAnother <M, T extends U, U extends Model<M>> (instanceToUpdate: T, baseInstance: U) {
  64. const obj = baseInstance.toJSON()
  65. for (const key of Object.keys(obj)) {
  66. instanceToUpdate[key] = obj[key]
  67. }
  68. }
  69. function resetSequelizeInstance (instance: Model<any>, savedFields: object) {
  70. Object.keys(savedFields).forEach(key => {
  71. instance[key] = savedFields[key]
  72. })
  73. }
  74. function filterNonExistingModels <T extends { hasSameUniqueKeysThan (other: T): boolean }> (
  75. fromDatabase: T[],
  76. newModels: T[]
  77. ) {
  78. return fromDatabase.filter(f => !newModels.find(newModel => newModel.hasSameUniqueKeysThan(f)))
  79. }
  80. function deleteAllModels <T extends Pick<Model, 'destroy'>> (models: T[], transaction: Transaction) {
  81. return Promise.all(models.map(f => f.destroy({ transaction })))
  82. }
  83. // ---------------------------------------------------------------------------
  84. function runInReadCommittedTransaction <T> (fn: (t: Transaction) => Promise<T>) {
  85. const options = { isolationLevel: Transaction.ISOLATION_LEVELS.READ_COMMITTED }
  86. return sequelizeTypescript.transaction(options, t => fn(t))
  87. }
  88. function afterCommitIfTransaction (t: Transaction, fn: Function) {
  89. if (t) return t.afterCommit(() => fn())
  90. return fn()
  91. }
  92. // ---------------------------------------------------------------------------
  93. export {
  94. resetSequelizeInstance,
  95. retryTransactionWrapper,
  96. transactionRetryer,
  97. updateInstanceWithAnother,
  98. afterCommitIfTransaction,
  99. filterNonExistingModels,
  100. deleteAllModels,
  101. runInReadCommittedTransaction
  102. }