@push.rocks/smartmta 9.4.0 → 9.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/changelog.md +9 -0
- package/dist_rust/mailer-bin_linux_amd64 +0 -0
- package/dist_rust/mailer-bin_linux_arm64 +0 -0
- package/dist_ts/00_commitinfo_data.js +1 -1
- package/dist_ts/mail/delivery/classes.delivery.queue.d.ts +9 -0
- package/dist_ts/mail/delivery/classes.delivery.queue.js +61 -2
- package/dist_ts/mail/delivery/classes.delivery.system.d.ts +5 -0
- package/dist_ts/mail/delivery/classes.delivery.system.js +64 -22
- package/package.json +1 -1
- package/readme.md +2 -0
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/mail/delivery/classes.delivery.queue.ts +64 -1
- package/ts/mail/delivery/classes.delivery.system.ts +66 -25
|
@@ -148,6 +148,10 @@ export class MultiModeDeliverySystem extends EventEmitter {
|
|
|
148
148
|
private emailServer?: UnifiedEmailServer;
|
|
149
149
|
private bounceDrainTimer?: NodeJS.Timeout;
|
|
150
150
|
private bounceDrainPromise?: Promise<void>;
|
|
151
|
+
private readonly queueItemsReadyHandler = (items: IQueueItem[]): Promise<void> =>
|
|
152
|
+
this.processItems(items);
|
|
153
|
+
private queueListenerRegistered = false;
|
|
154
|
+
private lifecycleGeneration = 0;
|
|
151
155
|
private pendingSmtpAuthenticationUpdates = new Map<string, {
|
|
152
156
|
itemId: string;
|
|
153
157
|
transactionId: string;
|
|
@@ -225,8 +229,18 @@ export class MultiModeDeliverySystem extends EventEmitter {
|
|
|
225
229
|
}
|
|
226
230
|
};
|
|
227
231
|
|
|
228
|
-
|
|
229
|
-
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
private registerQueueListener(): void {
|
|
235
|
+
if (this.queueListenerRegistered) return;
|
|
236
|
+
this.queue.on('itemsReady', this.queueItemsReadyHandler);
|
|
237
|
+
this.queueListenerRegistered = true;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
private unregisterQueueListener(): void {
|
|
241
|
+
if (!this.queueListenerRegistered) return;
|
|
242
|
+
this.queue.off('itemsReady', this.queueItemsReadyHandler);
|
|
243
|
+
this.queueListenerRegistered = false;
|
|
230
244
|
}
|
|
231
245
|
|
|
232
246
|
private createEmailForRecipients(email: Email, recipients: string[]): Email {
|
|
@@ -510,14 +524,33 @@ export class MultiModeDeliverySystem extends EventEmitter {
|
|
|
510
524
|
return;
|
|
511
525
|
}
|
|
512
526
|
|
|
527
|
+
const lifecycleGeneration = ++this.lifecycleGeneration;
|
|
513
528
|
this.running = true;
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
529
|
+
this.registerQueueListener();
|
|
530
|
+
try {
|
|
531
|
+
await this.drainBounceOutbox();
|
|
532
|
+
if (!this.running || lifecycleGeneration !== this.lifecycleGeneration) return;
|
|
533
|
+
|
|
534
|
+
this.bounceDrainTimer = setInterval(() => {
|
|
535
|
+
void this.drainBounceOutbox();
|
|
536
|
+
}, 30_000);
|
|
537
|
+
|
|
538
|
+
await emitSafely(this, 'started');
|
|
539
|
+
if (!this.running || lifecycleGeneration !== this.lifecycleGeneration) return;
|
|
540
|
+
|
|
541
|
+
this.queue.wake();
|
|
542
|
+
logger.log('info', 'MultiModeDeliverySystem started successfully');
|
|
543
|
+
} catch (error) {
|
|
544
|
+
if (lifecycleGeneration === this.lifecycleGeneration) {
|
|
545
|
+
this.running = false;
|
|
546
|
+
this.unregisterQueueListener();
|
|
547
|
+
if (this.bounceDrainTimer) {
|
|
548
|
+
clearInterval(this.bounceDrainTimer);
|
|
549
|
+
this.bounceDrainTimer = undefined;
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
throw error;
|
|
553
|
+
}
|
|
521
554
|
}
|
|
522
555
|
|
|
523
556
|
/**
|
|
@@ -527,11 +560,15 @@ export class MultiModeDeliverySystem extends EventEmitter {
|
|
|
527
560
|
logger.log('info', 'Stopping MultiModeDeliverySystem');
|
|
528
561
|
|
|
529
562
|
if (!this.running) {
|
|
563
|
+
this.unregisterQueueListener();
|
|
530
564
|
logger.log('warn', 'MultiModeDeliverySystem is already stopped');
|
|
531
565
|
return;
|
|
532
566
|
}
|
|
567
|
+
|
|
568
|
+
const lifecycleGeneration = ++this.lifecycleGeneration;
|
|
533
569
|
|
|
534
570
|
this.running = false;
|
|
571
|
+
this.unregisterQueueListener();
|
|
535
572
|
if (this.bounceDrainTimer) {
|
|
536
573
|
clearInterval(this.bounceDrainTimer);
|
|
537
574
|
this.bounceDrainTimer = undefined;
|
|
@@ -542,6 +579,10 @@ export class MultiModeDeliverySystem extends EventEmitter {
|
|
|
542
579
|
clearTimeout(this.throttleResetTimer);
|
|
543
580
|
this.throttleResetTimer = null;
|
|
544
581
|
}
|
|
582
|
+
this.throttled = false;
|
|
583
|
+
this.rateLimitCounter = 0;
|
|
584
|
+
this.rateLimitLastCheck = Date.now();
|
|
585
|
+
this.stats.rateLimiting.currentRate = 0;
|
|
545
586
|
|
|
546
587
|
// Wait for active deliveries to complete
|
|
547
588
|
if (this.activeDeliveries.size > 0) {
|
|
@@ -566,6 +607,8 @@ export class MultiModeDeliverySystem extends EventEmitter {
|
|
|
566
607
|
}
|
|
567
608
|
|
|
568
609
|
await this.bounceDrainPromise;
|
|
610
|
+
if (lifecycleGeneration !== this.lifecycleGeneration) return;
|
|
611
|
+
|
|
569
612
|
await emitSafely(this, 'stopped');
|
|
570
613
|
logger.log('info', 'MultiModeDeliverySystem stopped successfully');
|
|
571
614
|
}
|
|
@@ -585,17 +628,15 @@ export class MultiModeDeliverySystem extends EventEmitter {
|
|
|
585
628
|
return;
|
|
586
629
|
}
|
|
587
630
|
|
|
588
|
-
// Check rate limiting
|
|
589
|
-
if (this.checkRateLimit()) {
|
|
590
|
-
logger.log('debug', 'Rate limit exceeded, throttling deliveries');
|
|
591
|
-
return;
|
|
592
|
-
}
|
|
593
|
-
|
|
594
631
|
let startedDeliveries = 0;
|
|
595
632
|
|
|
596
633
|
for (const item of items) {
|
|
597
634
|
if (this.activeDeliveries.size >= this.options.concurrentDeliveries) break;
|
|
598
635
|
if (this.activeDeliveries.has(item.id)) continue;
|
|
636
|
+
if (this.checkRateLimit()) {
|
|
637
|
+
logger.log('debug', 'Rate limit exceeded, throttling deliveries');
|
|
638
|
+
break;
|
|
639
|
+
}
|
|
599
640
|
|
|
600
641
|
// Reserve synchronously before the first await so overlapping queue events
|
|
601
642
|
// cannot dispatch the same item while its durable claim is in flight.
|
|
@@ -818,6 +859,7 @@ export class MultiModeDeliverySystem extends EventEmitter {
|
|
|
818
859
|
|
|
819
860
|
// Update statistics
|
|
820
861
|
await emitSafely(this, 'statsUpdated', this.stats);
|
|
862
|
+
this.queue.wake();
|
|
821
863
|
}
|
|
822
864
|
}
|
|
823
865
|
|
|
@@ -1403,7 +1445,6 @@ export class MultiModeDeliverySystem extends EventEmitter {
|
|
|
1403
1445
|
this.rateLimitCounter = 0;
|
|
1404
1446
|
this.throttled = false;
|
|
1405
1447
|
this.stats.rateLimiting.currentRate = 0;
|
|
1406
|
-
return false;
|
|
1407
1448
|
}
|
|
1408
1449
|
|
|
1409
1450
|
// Check if we're already throttled
|
|
@@ -1411,15 +1452,9 @@ export class MultiModeDeliverySystem extends EventEmitter {
|
|
|
1411
1452
|
return true;
|
|
1412
1453
|
}
|
|
1413
1454
|
|
|
1414
|
-
//
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
// Calculate current rate (emails per minute)
|
|
1418
|
-
const rate = (this.rateLimitCounter / elapsed) * 60000;
|
|
1419
|
-
this.stats.rateLimiting.currentRate = rate;
|
|
1420
|
-
|
|
1421
|
-
// Check if rate limit is exceeded
|
|
1422
|
-
if (rate > this.options.globalRateLimit) {
|
|
1455
|
+
// Use a fixed one-minute window. Extrapolating the first delivery from a
|
|
1456
|
+
// few elapsed milliseconds falsely throttles every freshly started queue.
|
|
1457
|
+
if (this.rateLimitCounter >= this.options.globalRateLimit) {
|
|
1423
1458
|
this.throttled = true;
|
|
1424
1459
|
this.stats.rateLimiting.throttled++;
|
|
1425
1460
|
|
|
@@ -1434,10 +1469,16 @@ export class MultiModeDeliverySystem extends EventEmitter {
|
|
|
1434
1469
|
this.rateLimitLastCheck = Date.now();
|
|
1435
1470
|
this.rateLimitCounter = 0;
|
|
1436
1471
|
this.stats.rateLimiting.currentRate = 0;
|
|
1472
|
+
if (this.running) {
|
|
1473
|
+
this.queue.wake();
|
|
1474
|
+
}
|
|
1437
1475
|
}, resetDelay);
|
|
1438
1476
|
|
|
1439
1477
|
return true;
|
|
1440
1478
|
}
|
|
1479
|
+
|
|
1480
|
+
this.rateLimitCounter++;
|
|
1481
|
+
this.stats.rateLimiting.currentRate = this.rateLimitCounter;
|
|
1441
1482
|
|
|
1442
1483
|
return false;
|
|
1443
1484
|
}
|