ニジカ投稿局 https://tv.nizika.tv
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

122 lines
3.3 KiB

  1. import retry from 'async/retry.js'
  2. import Bluebird from 'bluebird'
  3. import { Transaction } from 'sequelize'
  4. import { Model } from 'sequelize-typescript'
  5. import { sequelizeTypescript } from '@server/initializers/database.js'
  6. import { logger } from './logger.js'
  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. function saveInTransactionWithRetries <T extends Pick<Model, 'save'>> (model: T) {
  63. return retryTransactionWrapper(() => {
  64. return sequelizeTypescript.transaction(async transaction => {
  65. await model.save({ transaction })
  66. })
  67. })
  68. }
  69. // ---------------------------------------------------------------------------
  70. function resetSequelizeInstance <T> (instance: Model<T>) {
  71. return instance.reload()
  72. }
  73. function filterNonExistingModels <T extends { hasSameUniqueKeysThan (other: T): boolean }> (
  74. fromDatabase: T[],
  75. newModels: T[]
  76. ) {
  77. return fromDatabase.filter(f => !newModels.find(newModel => newModel.hasSameUniqueKeysThan(f)))
  78. }
  79. function deleteAllModels <T extends Pick<Model, 'destroy'>> (models: T[], transaction: Transaction) {
  80. return Promise.all(models.map(f => f.destroy({ transaction })))
  81. }
  82. // ---------------------------------------------------------------------------
  83. function runInReadCommittedTransaction <T> (fn: (t: Transaction) => Promise<T>) {
  84. const options = { isolationLevel: Transaction.ISOLATION_LEVELS.READ_COMMITTED }
  85. return sequelizeTypescript.transaction(options, t => fn(t))
  86. }
  87. function afterCommitIfTransaction (t: Transaction, fn: Function) {
  88. if (t) return t.afterCommit(() => fn())
  89. return fn()
  90. }
  91. // ---------------------------------------------------------------------------
  92. export {
  93. resetSequelizeInstance,
  94. retryTransactionWrapper,
  95. transactionRetryer,
  96. saveInTransactionWithRetries,
  97. afterCommitIfTransaction,
  98. filterNonExistingModels,
  99. deleteAllModels,
  100. runInReadCommittedTransaction
  101. }