index.js 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100
  1. import { Db } from '../index';
  2. import { CollectionReference } from '../collection';
  3. import { ERRORS } from '../const/code';
  4. const START = 'database.startTransaction';
  5. const COMMIT = 'database.commitTransaction';
  6. const ABORT = 'database.abortTransaction';
  7. export class Transaction {
  8. constructor(db) {
  9. this._db = db;
  10. this._request = new Db.reqClass(this._db.config);
  11. this.aborted = false;
  12. this.commited = false;
  13. this.inited = false;
  14. }
  15. async init() {
  16. const res = await this._request.send(START);
  17. if (res.code) {
  18. throw res;
  19. }
  20. this.inited = true;
  21. this._id = res.transactionId;
  22. }
  23. collection(collName) {
  24. if (!collName) {
  25. throw new Error('Collection name is required');
  26. }
  27. return new CollectionReference(this._db, collName, {}, this._id);
  28. }
  29. getTransactionId() {
  30. return this._id;
  31. }
  32. getRequestMethod() {
  33. return this._request;
  34. }
  35. async commit() {
  36. const param = {
  37. transactionId: this._id
  38. };
  39. const res = await this._request.send(COMMIT, param);
  40. if (res.code)
  41. throw res;
  42. this.commited = true;
  43. return res;
  44. }
  45. async rollback(customRollbackRes) {
  46. const param = {
  47. transactionId: this._id
  48. };
  49. const res = await this._request.send(ABORT, param);
  50. if (res.code)
  51. throw res;
  52. this.aborted = true;
  53. this.abortReason = customRollbackRes;
  54. return res;
  55. }
  56. }
  57. export async function startTransaction() {
  58. const transaction = new Transaction(this);
  59. await transaction.init();
  60. return transaction;
  61. }
  62. export async function runTransaction(callback, times = 3) {
  63. let transaction;
  64. try {
  65. transaction = new Transaction(this);
  66. await transaction.init();
  67. const callbackRes = await callback(transaction);
  68. if (transaction.aborted === true) {
  69. throw transaction.abortReason;
  70. }
  71. await transaction.commit();
  72. return callbackRes;
  73. }
  74. catch (error) {
  75. if (transaction.inited === false) {
  76. throw error;
  77. }
  78. const throwWithRollback = async (error) => {
  79. if (!transaction.aborted && !transaction.commited) {
  80. try {
  81. await transaction.rollback();
  82. }
  83. catch (err) {
  84. }
  85. throw error;
  86. }
  87. if (transaction.aborted === true) {
  88. throw transaction.abortReason;
  89. }
  90. throw error;
  91. };
  92. if (times <= 0) {
  93. await throwWithRollback(error);
  94. }
  95. if (error && error.code === ERRORS.DATABASE_TRANSACTION_CONFLICT.code) {
  96. return await runTransaction.bind(this)(callback, --times);
  97. }
  98. await throwWithRollback(error);
  99. }
  100. }