@@ -65,6 +65,7 @@ export type MarQSOptions = {
65
65
queuePriorityStrategy : MarQSQueuePriorityStrategy ;
66
66
envQueuePriorityStrategy : MarQSQueuePriorityStrategy ;
67
67
visibilityTimeoutStrategy : VisibilityTimeoutStrategy ;
68
+ maximumNackCount : number ;
68
69
enableRebalancing ?: boolean ;
69
70
verbose ?: boolean ;
70
71
subscriber ?: MessageQueueSubscriber ;
@@ -299,7 +300,6 @@ export class MarQS {
299
300
const messageData = await this . #callDequeueMessage( {
300
301
messageQueue,
301
302
parentQueue,
302
- visibilityQueue : constants . MESSAGE_VISIBILITY_TIMEOUT_QUEUE ,
303
303
concurrencyLimitKey : this . keys . concurrencyLimitKeyFromQueue ( messageQueue ) ,
304
304
currentConcurrencyKey : this . keys . currentConcurrencyKeyFromQueue ( messageQueue ) ,
305
305
envConcurrencyLimitKey : this . keys . envConcurrencyLimitKeyFromQueue ( messageQueue ) ,
@@ -335,7 +335,6 @@ export class MarQS {
335
335
parentQueue,
336
336
messageKey : this . keys . messageKey ( messageData . messageId ) ,
337
337
messageQueue : messageQueue ,
338
- visibilityQueue : constants . MESSAGE_VISIBILITY_TIMEOUT_QUEUE ,
339
338
concurrencyKey : this . keys . currentConcurrencyKeyFromQueue ( messageQueue ) ,
340
339
envConcurrencyKey : this . keys . envCurrentConcurrencyKeyFromQueue ( messageQueue ) ,
341
340
orgConcurrencyKey : this . keys . orgCurrentConcurrencyKeyFromQueue ( messageQueue ) ,
@@ -409,7 +408,6 @@ export class MarQS {
409
408
const messageData = await this . #callDequeueMessage( {
410
409
messageQueue,
411
410
parentQueue,
412
- visibilityQueue : constants . MESSAGE_VISIBILITY_TIMEOUT_QUEUE ,
413
411
concurrencyLimitKey : this . keys . concurrencyLimitKeyFromQueue ( messageQueue ) ,
414
412
currentConcurrencyKey : this . keys . currentConcurrencyKeyFromQueue ( messageQueue ) ,
415
413
envConcurrencyLimitKey : this . keys . envConcurrencyLimitKeyFromQueue ( messageQueue ) ,
@@ -496,7 +494,6 @@ export class MarQS {
496
494
parentQueue : message . parentQueue ,
497
495
messageKey : this . keys . messageKey ( messageId ) ,
498
496
messageQueue : message . queue ,
499
- visibilityQueue : constants . MESSAGE_VISIBILITY_TIMEOUT_QUEUE ,
500
497
concurrencyKey : this . keys . currentConcurrencyKeyFromQueue ( message . queue ) ,
501
498
envConcurrencyKey : this . keys . envCurrentConcurrencyKeyFromQueue ( message . queue ) ,
502
499
orgConcurrencyKey : this . keys . orgCurrentConcurrencyKeyFromQueue ( message . queue ) ,
@@ -565,7 +562,6 @@ export class MarQS {
565
562
parentQueue : oldMessage . parentQueue ,
566
563
messageKey : this . keys . messageKey ( messageId ) ,
567
564
messageQueue : oldMessage . queue ,
568
- visibilityQueue : constants . MESSAGE_VISIBILITY_TIMEOUT_QUEUE ,
569
565
concurrencyKey : this . keys . currentConcurrencyKeyFromQueue ( oldMessage . queue ) ,
570
566
envConcurrencyKey : this . keys . envCurrentConcurrencyKeyFromQueue ( oldMessage . queue ) ,
571
567
orgConcurrencyKey : this . keys . orgCurrentConcurrencyKeyFromQueue ( oldMessage . queue ) ,
@@ -717,7 +713,7 @@ export class MarQS {
717
713
const message = await this . readMessage ( messageId ) ;
718
714
719
715
if ( ! message ) {
720
- logger . log ( `[${ this . name } ].nackMessage() message not found` , {
716
+ logger . debug ( `[${ this . name } ].nackMessage() message not found` , {
721
717
messageId,
722
718
retryAt,
723
719
updates,
@@ -726,6 +722,25 @@ export class MarQS {
726
722
return ;
727
723
}
728
724
725
+ const nackCount = await this . #getNackCount( messageId ) ;
726
+
727
+ span . setAttribute ( "nack_count" , nackCount ) ;
728
+
729
+ if ( nackCount >= this . options . maximumNackCount ) {
730
+ logger . debug ( `[${ this . name } ].nackMessage() maximum nack count reached` , {
731
+ messageId,
732
+ retryAt,
733
+ updates,
734
+ service : this . name ,
735
+ } ) ;
736
+
737
+ span . setAttribute ( "maximum_nack_count_reached" , true ) ;
738
+
739
+ // If we have reached the maximum nack count, we will ack the message
740
+ await this . acknowledgeMessage ( messageId , "maximum nack count reached" ) ;
741
+ return ;
742
+ }
743
+
729
744
span . setAttributes ( {
730
745
[ SemanticAttributes . QUEUE ] : message . queue ,
731
746
[ SemanticAttributes . MESSAGE_ID ] : message . messageId ,
@@ -746,7 +761,7 @@ export class MarQS {
746
761
concurrencyKey : this . keys . currentConcurrencyKeyFromQueue ( message . queue ) ,
747
762
envConcurrencyKey : this . keys . envCurrentConcurrencyKeyFromQueue ( message . queue ) ,
748
763
orgConcurrencyKey : this . keys . orgCurrentConcurrencyKeyFromQueue ( message . queue ) ,
749
- visibilityQueue : constants . MESSAGE_VISIBILITY_TIMEOUT_QUEUE ,
764
+ nackCounterKey : this . keys . nackCounterKey ( messageId ) ,
750
765
messageId,
751
766
messageScore : retryAt ,
752
767
} ) ;
@@ -764,6 +779,12 @@ export class MarQS {
764
779
) ;
765
780
}
766
781
782
+ async #getNackCount( messageId : string ) : Promise < number > {
783
+ const result = await this . redis . get ( this . keys . nackCounterKey ( messageId ) ) ;
784
+
785
+ return result ? Number ( result ) : 0 ;
786
+ }
787
+
767
788
// This should increment by the number of seconds, but with a max value of Date.now() + visibilityTimeoutInMs
768
789
public async heartbeatMessage ( messageId : string ) {
769
790
await this . options . visibilityTimeoutStrategy . heartbeat ( messageId , this . visibilityTimeoutInMs ) ;
@@ -1234,7 +1255,6 @@ export class MarQS {
1234
1255
async #callDequeueMessage( {
1235
1256
messageQueue,
1236
1257
parentQueue,
1237
- visibilityQueue,
1238
1258
concurrencyLimitKey,
1239
1259
envConcurrencyLimitKey,
1240
1260
orgConcurrencyLimitKey,
@@ -1244,7 +1264,6 @@ export class MarQS {
1244
1264
} : {
1245
1265
messageQueue : string ;
1246
1266
parentQueue : string ;
1247
- visibilityQueue : string ;
1248
1267
concurrencyLimitKey : string ;
1249
1268
envConcurrencyLimitKey : string ;
1250
1269
orgConcurrencyLimitKey : string ;
@@ -1303,7 +1322,6 @@ export class MarQS {
1303
1322
parentQueue,
1304
1323
messageKey,
1305
1324
messageQueue,
1306
- visibilityQueue,
1307
1325
concurrencyKey,
1308
1326
envConcurrencyKey,
1309
1327
orgConcurrencyKey,
@@ -1312,7 +1330,6 @@ export class MarQS {
1312
1330
parentQueue : string ;
1313
1331
messageKey : string ;
1314
1332
messageQueue : string ;
1315
- visibilityQueue : string ;
1316
1333
concurrencyKey : string ;
1317
1334
envConcurrencyKey : string ;
1318
1335
orgConcurrencyKey : string ;
@@ -1321,7 +1338,6 @@ export class MarQS {
1321
1338
logger . debug ( "Calling acknowledgeMessage" , {
1322
1339
messageKey,
1323
1340
messageQueue,
1324
- visibilityQueue,
1325
1341
concurrencyKey,
1326
1342
envConcurrencyKey,
1327
1343
orgConcurrencyKey,
@@ -1334,7 +1350,6 @@ export class MarQS {
1334
1350
parentQueue ,
1335
1351
messageKey ,
1336
1352
messageQueue ,
1337
- visibilityQueue ,
1338
1353
concurrencyKey ,
1339
1354
envConcurrencyKey ,
1340
1355
orgConcurrencyKey ,
@@ -1351,7 +1366,7 @@ export class MarQS {
1351
1366
concurrencyKey,
1352
1367
envConcurrencyKey,
1353
1368
orgConcurrencyKey,
1354
- visibilityQueue ,
1369
+ nackCounterKey ,
1355
1370
messageId,
1356
1371
messageScore,
1357
1372
} : {
@@ -1361,7 +1376,7 @@ export class MarQS {
1361
1376
concurrencyKey : string ;
1362
1377
envConcurrencyKey : string ;
1363
1378
orgConcurrencyKey : string ;
1364
- visibilityQueue : string ;
1379
+ nackCounterKey : string ;
1365
1380
messageId : string ;
1366
1381
messageScore : number ;
1367
1382
} ) {
@@ -1372,7 +1387,7 @@ export class MarQS {
1372
1387
concurrencyKey,
1373
1388
envConcurrencyKey,
1374
1389
orgConcurrencyKey,
1375
- visibilityQueue ,
1390
+ nackCounterKey ,
1376
1391
messageId,
1377
1392
messageScore,
1378
1393
service : this . name ,
@@ -1385,8 +1400,8 @@ export class MarQS {
1385
1400
concurrencyKey ,
1386
1401
envConcurrencyKey ,
1387
1402
orgConcurrencyKey ,
1388
- visibilityQueue ,
1389
1403
this . keys . envQueueKeyFromQueue ( messageQueue ) ,
1404
+ nackCounterKey ,
1390
1405
messageQueue ,
1391
1406
messageId ,
1392
1407
String ( Date . now ( ) ) ,
@@ -1604,17 +1619,16 @@ redis.call('SET', messageKey, messageData, 'GET')
1604
1619
} ) ;
1605
1620
1606
1621
this . redis . defineCommand ( "acknowledgeMessage" , {
1607
- numberOfKeys : 8 ,
1622
+ numberOfKeys : 7 ,
1608
1623
lua : `
1609
- -- Keys: parentQueue, messageKey, messageQueue, visibilityQueue, concurrencyKey, envCurrentConcurrencyKey, orgCurrentConcurrencyKey
1624
+ -- Keys: parentQueue, messageKey, messageQueue, concurrencyKey, envCurrentConcurrencyKey, orgCurrentConcurrencyKey
1610
1625
local parentQueue = KEYS[1]
1611
1626
local messageKey = KEYS[2]
1612
1627
local messageQueue = KEYS[3]
1613
- local visibilityQueue = KEYS[4]
1614
- local concurrencyKey = KEYS[5]
1615
- local envCurrentConcurrencyKey = KEYS[6]
1616
- local orgCurrentConcurrencyKey = KEYS[7]
1617
- local envQueueKey = KEYS[8]
1628
+ local concurrencyKey = KEYS[4]
1629
+ local envCurrentConcurrencyKey = KEYS[5]
1630
+ local orgCurrentConcurrencyKey = KEYS[6]
1631
+ local envQueueKey = KEYS[7]
1618
1632
1619
1633
-- Args: messageId, messageQueueName
1620
1634
local messageId = ARGV[1]
@@ -1637,9 +1651,6 @@ else
1637
1651
redis.call('ZADD', parentQueue, earliestMessage[2], messageQueueName)
1638
1652
end
1639
1653
1640
- -- Remove the message from the timeout queue (deprecated, will eventually remove this)
1641
- redis.call('ZREM', visibilityQueue, messageId)
1642
-
1643
1654
-- Update the concurrency keys
1644
1655
redis.call('SREM', concurrencyKey, messageId)
1645
1656
redis.call('SREM', envCurrentConcurrencyKey, messageId)
@@ -1650,15 +1661,14 @@ redis.call('SREM', orgCurrentConcurrencyKey, messageId)
1650
1661
this . redis . defineCommand ( "nackMessage" , {
1651
1662
numberOfKeys : 8 ,
1652
1663
lua : `
1653
- -- Keys: childQueueKey, parentQueueKey, visibilityQueue, concurrencyKey, envConcurrencyKey, orgConcurrencyKey, messageId
1654
1664
local messageKey = KEYS[1]
1655
1665
local childQueueKey = KEYS[2]
1656
1666
local parentQueueKey = KEYS[3]
1657
1667
local concurrencyKey = KEYS[4]
1658
1668
local envConcurrencyKey = KEYS[5]
1659
1669
local orgConcurrencyKey = KEYS[6]
1660
- local visibilityQueue = KEYS[7]
1661
- local envQueueKey = KEYS[8]
1670
+ local envQueueKey = KEYS[7]
1671
+ local nackCounterKey = KEYS[8]
1662
1672
1663
1673
-- Args: childQueueName, messageId, currentTime, messageScore
1664
1674
local childQueueName = ARGV[1]
@@ -1671,20 +1681,16 @@ redis.call('SREM', concurrencyKey, messageId)
1671
1681
redis.call('SREM', envConcurrencyKey, messageId)
1672
1682
redis.call('SREM', orgConcurrencyKey, messageId)
1673
1683
1674
- -- Check to see if the message is still in the visibilityQueue
1675
- local messageVisibility = tonumber(redis.call('ZSCORE', visibilityQueue, messageId)) or 0
1676
-
1677
- if messageVisibility > 0 then
1678
- -- Remove the message from the timeout queue (deprecated, will eventually remove this)
1679
- redis.call('ZREM', visibilityQueue, messageId)
1680
- end
1681
-
1682
1684
-- Enqueue the message into the queue
1683
1685
redis.call('ZADD', childQueueKey, messageScore, messageId)
1684
1686
1685
1687
-- Enqueue the message into the env queue
1686
1688
redis.call('ZADD', envQueueKey, messageScore, messageId)
1687
1689
1690
+ -- Increment the nack counter with an expiry of 30 days
1691
+ redis.call('INCR', nackCounterKey)
1692
+ redis.call('EXPIRE', nackCounterKey, 2592000)
1693
+
1688
1694
-- Rebalance the parent queue
1689
1695
local earliestMessage = redis.call('ZRANGE', childQueueKey, 0, 0, 'WITHSCORES')
1690
1696
if #earliestMessage == 0 then
@@ -1713,36 +1719,6 @@ redis.call('SREM', orgCurrentConcurrencyKey, messageId)
1713
1719
` ,
1714
1720
} ) ;
1715
1721
1716
- this . redis . defineCommand ( "heartbeatMessage" , {
1717
- numberOfKeys : 1 ,
1718
- lua : `
1719
- -- Keys: visibilityQueue
1720
- local visibilityQueue = KEYS[1]
1721
-
1722
- -- Args: messageId, milliseconds, maxVisibilityTimeout
1723
- local messageId = ARGV[1]
1724
- local milliseconds = tonumber(ARGV[2])
1725
- local maxVisibilityTimeout = tonumber(ARGV[3])
1726
-
1727
- -- Get the current visibility timeout
1728
- local zscoreResult = redis.call('ZSCORE', visibilityQueue, messageId)
1729
-
1730
- -- If there's no currentVisibilityTimeout, return and do not execute ZADD
1731
- if zscoreResult == false then
1732
- return
1733
- end
1734
-
1735
- local currentVisibilityTimeout = tonumber(zscoreResult)
1736
-
1737
-
1738
- -- Calculate the new visibility timeout
1739
- local newVisibilityTimeout = math.min(currentVisibilityTimeout + milliseconds * 1000, maxVisibilityTimeout)
1740
-
1741
- -- Update the visibility timeout
1742
- redis.call('ZADD', visibilityQueue, newVisibilityTimeout, messageId)
1743
- ` ,
1744
- } ) ;
1745
-
1746
1722
this . redis . defineCommand ( "calculateQueueCurrentConcurrencies" , {
1747
1723
numberOfKeys : 3 ,
1748
1724
lua : `
@@ -1854,7 +1830,6 @@ declare module "ioredis" {
1854
1830
parentQueue : string ,
1855
1831
messageKey : string ,
1856
1832
messageQueue : string ,
1857
- visibilityQueue : string ,
1858
1833
concurrencyKey : string ,
1859
1834
envConcurrencyKey : string ,
1860
1835
orgConcurrencyKey : string ,
@@ -1871,8 +1846,8 @@ declare module "ioredis" {
1871
1846
concurrencyKey : string ,
1872
1847
envConcurrencyKey : string ,
1873
1848
orgConcurrencyKey : string ,
1874
- visibilityQueue : string ,
1875
1849
envQueueKey : string ,
1850
+ nackCounterKey : string ,
1876
1851
childQueueName : string ,
1877
1852
messageId : string ,
1878
1853
currentTime : string ,
@@ -1888,14 +1863,6 @@ declare module "ioredis" {
1888
1863
callback ?: Callback < void >
1889
1864
) : Result < void , Context > ;
1890
1865
1891
- heartbeatMessage (
1892
- visibilityQueue : string ,
1893
- messageId : string ,
1894
- milliseconds : string ,
1895
- maxVisibilityTimeout : string ,
1896
- callback ?: Callback < void >
1897
- ) : Result < void , Context > ;
1898
-
1899
1866
updateGlobalConcurrencyLimits (
1900
1867
envConcurrencyLimitKey : string ,
1901
1868
orgConcurrencyLimitKey : string ,
@@ -1953,6 +1920,7 @@ function getMarQSClient() {
1953
1920
defaultOrgConcurrency : env . DEFAULT_ORG_EXECUTION_CONCURRENCY_LIMIT ,
1954
1921
visibilityTimeoutInMs : env . MARQS_VISIBILITY_TIMEOUT_MS ,
1955
1922
enableRebalancing : ! env . MARQS_DISABLE_REBALANCING ,
1923
+ maximumNackCount : env . MARQS_MAXIMUM_NACK_COUNT ,
1956
1924
subscriber : concurrencyTracker ,
1957
1925
} ) ;
1958
1926
} else {
0 commit comments