Skip to content

Commit 6978372

Browse files
Handle concurrent order taking: refactor PerOrderIdMutex, move it to util/index.ts for more clean reusability [1/2] (#823)
* Move PerOrderIdMutex to util * Code formatting
1 parent 6f5b06e commit 6978372

4 files changed

Lines changed: 51 additions & 39 deletions

File tree

jobs/cancel_orders.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,14 @@ import { HasTelegram } from '../bot/start';
22
import { User, Order } from '../models';
33
import { cancelShowHoldInvoice, cancelAddInvoice } from '../bot/commands';
44
import * as messages from '../bot/messages';
5-
import { getUserI18nContext, holdInvoiceExpirationInSecs } from '../util';
5+
import {
6+
getUserI18nContext,
7+
holdInvoiceExpirationInSecs,
8+
PerOrderIdMutex,
9+
} from '../util';
610
import { logger } from '../logger';
711
import { CommunityContext } from '../bot/modules/community/communityContext';
812
import * as OrderEvents from '../bot/modules/events/orders';
9-
import { PerOrderIdMutex } from '../ln/subscribe_invoice';
1013

1114
const cancelOrders = async (bot: HasTelegram) => {
1215
try {

jobs/check_hold_invoice_expired.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
import { HasTelegram } from '../bot/start';
22
import { Order, User, Dispute } from '../models';
3-
import { holdInvoiceExpirationInSecs, getUserI18nContext } from '../util';
3+
import {
4+
holdInvoiceExpirationInSecs,
5+
getUserI18nContext,
6+
PerOrderIdMutex,
7+
} from '../util';
48
import { logger } from '../logger';
59
import { cancelHoldInvoice } from '../ln';
6-
import { PerOrderIdMutex } from '../ln/subscribe_invoice';
710
import * as OrderEvents from '../bot/modules/events/orders';
811
import * as messages from '../bot/messages';
912

ln/subscribe_invoice.ts

Lines changed: 7 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -6,48 +6,20 @@ import { getInfo } from './info';
66
import lnd from './connect';
77
import * as messages from '../bot/messages';
88
import * as ordersActions from '../bot/ordersActions';
9-
import { getUserI18nContext, getEmojiRate, decimalRound } from '../util';
9+
import {
10+
getUserI18nContext,
11+
getEmojiRate,
12+
decimalRound,
13+
PerOrderIdMutex,
14+
} from '../util';
1015
import { logger } from '../logger';
1116
import { HasTelegram } from '../bot/start';
1217
import { IOrder } from '../models/order';
13-
import { Mutex } from 'async-mutex';
14-
15-
type LockCountedMutex = {
16-
lockCount: number;
17-
mutex: Mutex;
18-
};
1918

2019
// Track pending reconnects to prevent duplicate resubscriptions
2120
// when both 'error' and 'end' events fire for the same invoice
2221
const pendingReconnects: Set<string> = new Set();
2322

24-
class PerOrderIdMutex {
25-
mutexes: Map<string, LockCountedMutex> = new Map();
26-
27-
async runExclusive(orderId: string, callback: () => Promise<any>) {
28-
let mtx: LockCountedMutex;
29-
if (!this.mutexes.has(orderId)) {
30-
mtx = { lockCount: 1, mutex: new Mutex() };
31-
this.mutexes.set(orderId, mtx);
32-
} else {
33-
mtx = this.mutexes.get(orderId)!;
34-
mtx.lockCount++;
35-
}
36-
let ret: any;
37-
try {
38-
ret = await mtx.mutex.runExclusive(callback);
39-
} finally {
40-
mtx.lockCount--;
41-
if (mtx.lockCount == 0) {
42-
this.mutexes.delete(orderId);
43-
}
44-
}
45-
return ret;
46-
}
47-
48-
static instance = new PerOrderIdMutex();
49-
}
50-
5123
const subscribeInvoice = async (
5224
bot: HasTelegram,
5325
id: string,
@@ -307,4 +279,4 @@ const payHoldInvoice = async (bot: HasTelegram, order: IOrder) => {
307279
}
308280
};
309281

310-
export { subscribeInvoice, payHoldInvoice, PerOrderIdMutex };
282+
export { subscribeInvoice, payHoldInvoice };

util/index.ts

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { I18nContext } from '@grammyjs/i18n';
2+
import { Mutex } from 'async-mutex';
23
import { ICommunity, IOrderChannel } from '../models/community';
34
import { IOrder } from '../models/order';
45
import { UserDocument } from '../models/user';
@@ -604,6 +605,38 @@ const generateQRWithImage = async (request: string, randomImage: string) => {
604605
return canvas.toBuffer();
605606
};
606607

608+
type LockCountedMutex = {
609+
lockCount: number;
610+
mutex: Mutex;
611+
};
612+
613+
class PerOrderIdMutex {
614+
mutexes: Map<string, LockCountedMutex> = new Map();
615+
616+
async runExclusive(orderId: string, callback: () => Promise<any>) {
617+
let mtx: LockCountedMutex;
618+
if (!this.mutexes.has(orderId)) {
619+
mtx = { lockCount: 1, mutex: new Mutex() };
620+
this.mutexes.set(orderId, mtx);
621+
} else {
622+
mtx = this.mutexes.get(orderId)!;
623+
mtx.lockCount++;
624+
}
625+
let ret: any;
626+
try {
627+
ret = await mtx.mutex.runExclusive(callback);
628+
} finally {
629+
mtx.lockCount--;
630+
if (mtx.lockCount == 0) {
631+
this.mutexes.delete(orderId);
632+
}
633+
}
634+
return ret;
635+
}
636+
637+
static instance = new PerOrderIdMutex();
638+
}
639+
607640
export {
608641
isIso4217,
609642
plural,
@@ -637,4 +670,5 @@ export {
637670
isOrderCreator,
638671
generateRandomImage,
639672
generateQRWithImage,
673+
PerOrderIdMutex,
640674
};

0 commit comments

Comments
 (0)