Skip to content

Commit ab928e3

Browse files
committed
fix(oauth): follower last-chance read after poll deadline
1 parent 0c96964 commit ab928e3

2 files changed

Lines changed: 26 additions & 0 deletions

File tree

apps/sim/lib/concurrency/__tests__/leader-lock.test.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,28 @@ describe('withLeaderLock', () => {
117117
expect(onFollower.mock.calls.length).toBeGreaterThanOrEqual(2)
118118
})
119119

120+
it('follower does a final read after timeout to catch a just-finished leader', async () => {
121+
redisConfigMockFns.mockAcquireLock.mockResolvedValueOnce(false)
122+
123+
let polls = 0
124+
const onFollower = vi.fn(async () => {
125+
polls += 1
126+
// Return null during the poll loop, value on the post-deadline read.
127+
if (polls <= 2) return null
128+
return 'late-leader'
129+
})
130+
131+
const result = await withLeaderLock<string>({
132+
key: 'k',
133+
pollIntervalMs: 5,
134+
maxWaitMs: 12,
135+
onLeader: async () => 'should-not-run',
136+
onFollower,
137+
})
138+
139+
expect(result).toBe('late-leader')
140+
})
141+
120142
it('follower returns null after timeout', async () => {
121143
redisConfigMockFns.mockAcquireLock.mockResolvedValueOnce(false)
122144

apps/sim/lib/concurrency/leader-lock.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,10 @@ export async function withLeaderLock<T>(opts: LeaderLockOptions<T>): Promise<T |
6464
if (value !== null) return value
6565
}
6666

67+
// The leader may have persisted between our final poll and now; one last check.
68+
const lastChance = await onFollower()
69+
if (lastChance !== null) return lastChance
70+
6771
logger.warn('Follower timed out waiting for leader', { key, maxWaitMs })
6872
return null
6973
}

0 commit comments

Comments
 (0)