Skip to content

Commit 33886a7

Browse files
durrandariakp
andauthored
fix(NODE-3116): reschedule unreliable async interval first (#3006)
* fix(NODE-3116): reschedule unreliable async interval first Co-authored-by: Daria Pardue <[email protected]>
1 parent f696909 commit 33886a7

File tree

2 files changed

+287
-128
lines changed

2 files changed

+287
-128
lines changed

src/utils.ts

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -954,7 +954,7 @@ export function makeInterruptibleAsyncInterval(
954954
): InterruptibleAsyncInterval {
955955
let timerId: NodeJS.Timeout | undefined;
956956
let lastCallTime: number;
957-
let lastWakeTime: number;
957+
let cannotBeExpedited = false;
958958
let stopped = false;
959959

960960
options = options ?? {};
@@ -965,34 +965,34 @@ export function makeInterruptibleAsyncInterval(
965965

966966
function wake() {
967967
const currentTime = clock();
968-
const timeSinceLastWake = currentTime - lastWakeTime;
969-
const timeSinceLastCall = currentTime - lastCallTime;
970-
const timeUntilNextCall = interval - timeSinceLastCall;
971-
lastWakeTime = currentTime;
968+
const nextScheduledCallTime = lastCallTime + interval;
969+
const timeUntilNextCall = nextScheduledCallTime - currentTime;
972970

973971
// For the streaming protocol: there is nothing obviously stopping this
974972
// interval from being woken up again while we are waiting "infinitely"
975973
// for `fn` to be called again`. Since the function effectively
976974
// never completes, the `timeUntilNextCall` will continue to grow
977975
// negatively unbounded, so it will never trigger a reschedule here.
978976

977+
// This is possible in virtualized environments like AWS Lambda where our
978+
// clock is unreliable. In these cases the timer is "running" but never
979+
// actually completes, so we want to execute immediately and then attempt
980+
// to reschedule.
981+
if (timeUntilNextCall < 0) {
982+
executeAndReschedule();
983+
return;
984+
}
985+
979986
// debounce multiple calls to wake within the `minInterval`
980-
if (timeSinceLastWake < minInterval) {
987+
if (cannotBeExpedited) {
981988
return;
982989
}
983990

984991
// reschedule a call as soon as possible, ensuring the call never happens
985992
// faster than the `minInterval`
986993
if (timeUntilNextCall > minInterval) {
987994
reschedule(minInterval);
988-
}
989-
990-
// This is possible in virtualized environments like AWS Lambda where our
991-
// clock is unreliable. In these cases the timer is "running" but never
992-
// actually completes, so we want to execute immediately and then attempt
993-
// to reschedule.
994-
if (timeUntilNextCall < 0) {
995-
executeAndReschedule();
995+
cannotBeExpedited = true;
996996
}
997997
}
998998

@@ -1004,7 +1004,7 @@ export function makeInterruptibleAsyncInterval(
10041004
}
10051005

10061006
lastCallTime = 0;
1007-
lastWakeTime = 0;
1007+
cannotBeExpedited = false;
10081008
}
10091009

10101010
function reschedule(ms?: number) {
@@ -1017,7 +1017,7 @@ export function makeInterruptibleAsyncInterval(
10171017
}
10181018

10191019
function executeAndReschedule() {
1020-
lastWakeTime = 0;
1020+
cannotBeExpedited = false;
10211021
lastCallTime = clock();
10221022

10231023
fn(err => {

0 commit comments

Comments
 (0)