-
Notifications
You must be signed in to change notification settings - Fork 1.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Service Bus] Fix credit management based on the buffer capacity for the receivers #14060
Changes from 116 commits
0e7de1c
dd940e6
76eb13e
a7191fe
e0ca3f5
bf789d0
fd9d20a
ecca8b0
b19575a
9a26c5e
fddbd28
a209104
144b904
e7d7a82
67bd418
26a30e1
ceedf58
d1c16ad
8f25785
fe6c0c2
a4b867b
ad2ef9b
f36fc9e
420af6d
eebb4fe
68b1ee7
1ac91f3
aa6b359
478a7ae
629de9a
9f2fd9b
db35c49
6a5fd66
55138fa
418a4f8
38a5fff
1d0203b
8324a30
a38830c
8090238
814765e
cb3975c
88bb497
99841cc
b782d56
2c048b3
0fc6f9b
fddc414
00f6b4b
93f6a97
7293fa4
6188a4e
d7e914a
65630c8
42e1b8f
ffd2b79
eea0cff
8994c2d
4bbbfbf
b72b80d
d67a552
a4c93f5
ae3109d
4a1b205
b692c80
25d465b
906f9f7
7192d04
f385a24
a466bbd
5fb5562
b63d88a
9634b74
f30bba5
e53e93d
270573d
741ada6
a91db19
1298c16
1711ded
7820768
ca98f09
50f1217
6cbebff
9bf59cb
d0ba78e
dbd1097
c25ef93
11b8206
8bbc2d9
d366486
c6ca65b
34fdc32
1f75e73
4415097
9416b33
3f3def6
97b5c8b
e1bc672
f66789e
71780be
516d270
bb07f09
db05be0
3969549
69be170
1d6a152
e71dfdd
d0c9426
a2f75c5
0e9091c
7647e3d
27e0480
eebffb3
7cad41d
716f7b1
931b9fd
b551bf8
7c9136c
f1332bf
4d26b7f
fce9703
afc9760
2d5800d
02710aa
fd07e8c
5295fe0
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,8 +3,11 @@ | |
|
||
import { Delivery, ReceiverOptions, Source } from "rhea-promise"; | ||
import { translateServiceBusError } from "../serviceBusError"; | ||
import { receiverLogger } from "../log"; | ||
import { logger, receiverLogger } from "../log"; | ||
import { ReceiveMode } from "../models"; | ||
import { Receiver } from "rhea-promise"; | ||
import { OnError } from "./messageReceiver"; | ||
import { StreamingReceiverHelper } from "./streamingReceiverHelper"; | ||
|
||
/** | ||
* @internal | ||
|
@@ -103,3 +106,132 @@ export function createReceiverOptions( | |
|
||
return rcvrOptions; | ||
} | ||
|
||
export const UnsettledMessagesLimitExceededError = | ||
HarshaNalluru marked this conversation as resolved.
Show resolved
Hide resolved
|
||
"Failed to fetch new messages as the limit for unsettled messages is reached. Please settle received messages using settlement methods(such as `completeMessage()`) on the receiver to receive the next message."; | ||
|
||
/** | ||
* Returns the number of empty/filled slots in the Circular buffer of incoming deliveries | ||
* based on the capacity and size of the buffer. | ||
* | ||
* @internal | ||
*/ | ||
export function incomingBufferProperties( | ||
receiver: Pick<Receiver, "session"> | undefined | ||
): { | ||
numberOfEmptySlots: number; // 2048(total) - filled | ||
numberOfFilledSlots: number; // number of unsettled messages | ||
} { | ||
const incomingDeliveries = receiver?.session?.incoming?.deliveries; | ||
let numberOfEmptySlots = 0; | ||
if (incomingDeliveries && incomingDeliveries.capacity - 1 > incomingDeliveries.size) { | ||
// Exmpty slots should have been `incomingDeliveries.capacity - 1 - incomingDeliveries.size`. Why -1? | ||
// - If the number of slots is set to (capacity - size), | ||
// the number of unsettled messages that can be held in the buffer would equal to the "capacity". | ||
// At that limiting point, service doesn't respond to the drain request for unpartitioned queues. | ||
// Service team is tracking the issue. | ||
// -1 allows us to not fill up the buffer entirely, it fills up to 2047 if the capacity is 2048 | ||
numberOfEmptySlots = incomingDeliveries.capacity - 1 - incomingDeliveries.size; | ||
} | ||
return { | ||
numberOfEmptySlots, | ||
numberOfFilledSlots: incomingDeliveries ? incomingDeliveries.size : 0 | ||
}; | ||
} | ||
|
||
/** | ||
* Provides helper methods to manage the credits on the link for the | ||
* streaming messages scenarios. | ||
* (Used by both sessions(MessageSession) and non-sessions(StreamingReceiver)) | ||
* | ||
* @internal | ||
*/ | ||
export class StreamingReceiverCreditManager { | ||
constructor( | ||
private _getCurrentReceiver: () => { receiver: Receiver | undefined; logPrefix: string }, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why do we allow receiver to be undefined? Is there a scenario where that is actually intended? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not actually. Instead of passing the StreamingReceiverHelper, I've now merged my StreamingReceiverCreditManager into the existing StreamingReceiverHelper. Let me know if that looks fine. |
||
private streamingReceiverHelper: StreamingReceiverHelper, | ||
private receiveMode: ReceiveMode, | ||
private maxConcurrentCalls: number | ||
) {} | ||
|
||
addCreditsInit() { | ||
HarshaNalluru marked this conversation as resolved.
Show resolved
Hide resolved
|
||
const { receiver, logPrefix } = this._getCurrentReceiver(); | ||
const emptySlots = incomingBufferProperties(receiver).numberOfEmptySlots; | ||
const creditsToAdd = | ||
this.receiveMode === "peekLock" | ||
? Math.min(this.maxConcurrentCalls, emptySlots) | ||
: this.maxConcurrentCalls; | ||
this.streamingReceiverHelper.addCredit(creditsToAdd); | ||
logger.verbose( | ||
`${logPrefix} creditManager: added ${creditsToAdd} credits (initial); total credits = ${receiver?.credit}` | ||
); | ||
} | ||
/** | ||
* Upon receiving a new message, this method is to be called in the streaming receiver logic to add credits to receive more messages. | ||
* | ||
* @internal | ||
*/ | ||
onReceive(_notifyError: OnError | undefined) { | ||
HarshaNalluru marked this conversation as resolved.
Show resolved
Hide resolved
|
||
const { receiver, logPrefix } = this._getCurrentReceiver(); | ||
if (this.receiveMode === "receiveAndDelete") { | ||
this.streamingReceiverHelper.addCredit(1); | ||
logger.verbose( | ||
`${logPrefix} creditManager: added 1 credits upon receiving a message; total credits = ${receiver?.credit}` | ||
); | ||
return; | ||
} | ||
|
||
const { numberOfEmptySlots } = incomingBufferProperties(receiver); | ||
if (receiver && numberOfEmptySlots > 0) { | ||
const possibleMaxCredits = Math.min(this.maxConcurrentCalls, numberOfEmptySlots); | ||
if (possibleMaxCredits > receiver.credit) { | ||
const creditsToAdd = possibleMaxCredits - receiver.credit; | ||
this.streamingReceiverHelper.addCredit(creditsToAdd); | ||
logger.verbose( | ||
`${logPrefix} creditManager: added ${creditsToAdd} credits upon receiving a message; total credits = ${receiver?.credit}` | ||
); | ||
} | ||
return; | ||
} | ||
|
||
// if (receiver) { | ||
// No empty slots left, so notify the user with an error | ||
// Commented out because we want to be consistent with other languages | ||
// notifyError?.({ | ||
// error: new ServiceBusError( | ||
// UnsettledMessagesLimitExceededError, | ||
// "UnsettledMessagesLimitExceeded" as ServiceBusErrorCode | ||
// ), | ||
// errorSource: "receive", | ||
// entityPath: this.entityPath, | ||
// fullyQualifiedNamespace: this.fullyQualifiedNamespace | ||
// }); | ||
// } else { | ||
// receiver is not defined | ||
// SessionLockLost for sessions/onAMQPError for non-sessions will be notified in one of the listeners - nothing to do here | ||
// } | ||
HarshaNalluru marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
|
||
/** | ||
* Meant to be called after a message is settled with the receive link. | ||
* Replenishes the number of credits on the link based on the maxConcurrentCalls and the numberOfEmptySlots to receive more messages. | ||
* | ||
* @internal | ||
*/ | ||
postProcessing() { | ||
const { receiver, logPrefix } = this._getCurrentReceiver(); | ||
const { numberOfEmptySlots } = incomingBufferProperties(receiver); | ||
if (this.receiveMode === "peekLock") { | ||
if (receiver && numberOfEmptySlots > 0) { | ||
const possibleMaxCredits = Math.min(this.maxConcurrentCalls, numberOfEmptySlots); | ||
if (possibleMaxCredits > receiver.credit) { | ||
const creditsToAdd = possibleMaxCredits - receiver.credit; | ||
this.streamingReceiverHelper.addCredit(creditsToAdd); | ||
logger.verbose( | ||
`${logPrefix} creditManager: added ${creditsToAdd} credits after message settlement; total credits = ${receiver?.credit}` | ||
); | ||
} | ||
} | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wondering why this changelog entry was removed. Was there a merge conflict?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh no, I'll revert. Thanks! 🙂