Repository navigation
Commit 30f6e24
Performance [P1]: Single-flight async Azure token refresh (#198)
* perf: single-flight async Azure token refresh
AsyncAccessTokenManager.get_access_token() checked expiry and awaited
refresh_token() without any synchronization. Since the async DTS gRPC
interceptor awaits it on every RPC, a burst of concurrent grpc.aio calls
arriving while the token was missing or inside its renewal window would each
independently call the credential, producing a thundering herd of
Entra/credential requests, extra latency, and throttling pressure.
Mirror the double-checked locking already used by the synchronous
AccessTokenManager: re-check token validity after acquiring a refresh guard so
only one coroutine contacts the credential per refresh window while the rest
reuse the result.
The guard is an asyncio.Lock created lazily per running event loop (tracked in
a small map behind a threading.Lock), because an asyncio.Lock binds to the loop
it is first awaited on and this manager may outlive a loop or be shared across
loops. Locks for closed loops are pruned so the map cannot grow unbounded.
The cached-token fast path is unchanged and never touches the lock, so there is
no added per-RPC overhead. Public signatures, return values, the renewal margin
behavior, exception propagation, and logging are all preserved.
Adds unit tests covering concurrent cold start, concurrent refresh after
expiry, the cached-token path, credential failure propagation without
deadlock, and reuse across distinct event loops.
Fixes #192
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: bb08d0ed-484d-4015-b905-3e164f68678b
* refactor: hoist stale-loop comprehension into a named local
Address review feedback on the stale-lock cleanup in
AsyncAccessTokenManager._get_refresh_lock(). The list comprehension was
embedded directly in the `for` statement, which read awkwardly across the
wrapped line.
Hoist it into a named `stale_loops` local and iterate over that. This is purely
a readability change with no behavioural difference: the snapshot is still
materialized before mutating the mapping, which is what makes deleting during
iteration safe.
Note this was not a lint violation - flake8 passes on the file both before and
after the change.
Also align the async deferred-acquisition test with the naming and assertions
that PR #199 introduced for the synchronous manager, and assert explicitly that
constructing AsyncAccessTokenManager performs no credential call. The async
manager has always deferred its first acquisition, so the cold-start burst
flows through the same refresh guard as a later refresh; the test now states
that intent directly.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: bb08d0ed-484d-4015-b905-3e164f68678b
---------
Co-authored-by: Bernd Verst <beverst@microsoft.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: bb08d0ed-484d-4015-b905-3e164f68678b1 parent 0f316cc commit 30f6e24
3 files changed
Lines changed: 141 additions & 2 deletions
File tree
- durabletask-azuremanaged
- durabletask/azuremanaged/internal
- tests/durabletask-azuremanaged
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
14 | 14 | | |
15 | 15 | | |
16 | 16 | | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
17 | 20 | | |
18 | 21 | | |
19 | 22 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | 1 | | |
2 | 2 | | |
| 3 | + | |
3 | 4 | | |
4 | 5 | | |
5 | 6 | | |
| |||
71 | 72 | | |
72 | 73 | | |
73 | 74 | | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
74 | 98 | | |
75 | 99 | | |
76 | | - | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
77 | 106 | | |
78 | 107 | | |
79 | 108 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | 1 | | |
2 | 2 | | |
3 | 3 | | |
| 4 | + | |
4 | 5 | | |
5 | 6 | | |
6 | 7 | | |
| |||
13 | 14 | | |
14 | 15 | | |
15 | 16 | | |
16 | | - | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
17 | 21 | | |
18 | 22 | | |
19 | 23 | | |
| |||
230 | 234 | | |
231 | 235 | | |
232 | 236 | | |
| 237 | + | |
| 238 | + | |
| 239 | + | |
| 240 | + | |
| 241 | + | |
| 242 | + | |
| 243 | + | |
| 244 | + | |
| 245 | + | |
| 246 | + | |
| 247 | + | |
| 248 | + | |
| 249 | + | |
| 250 | + | |
| 251 | + | |
| 252 | + | |
| 253 | + | |
| 254 | + | |
| 255 | + | |
| 256 | + | |
| 257 | + | |
| 258 | + | |
| 259 | + | |
| 260 | + | |
| 261 | + | |
| 262 | + | |
| 263 | + | |
| 264 | + | |
| 265 | + | |
| 266 | + | |
| 267 | + | |
| 268 | + | |
| 269 | + | |
| 270 | + | |
| 271 | + | |
| 272 | + | |
| 273 | + | |
| 274 | + | |
| 275 | + | |
| 276 | + | |
| 277 | + | |
| 278 | + | |
| 279 | + | |
| 280 | + | |
| 281 | + | |
| 282 | + | |
| 283 | + | |
| 284 | + | |
| 285 | + | |
| 286 | + | |
| 287 | + | |
| 288 | + | |
| 289 | + | |
| 290 | + | |
| 291 | + | |
| 292 | + | |
| 293 | + | |
| 294 | + | |
| 295 | + | |
| 296 | + | |
| 297 | + | |
| 298 | + | |
| 299 | + | |
| 300 | + | |
| 301 | + | |
| 302 | + | |
| 303 | + | |
| 304 | + | |
| 305 | + | |
| 306 | + | |
| 307 | + | |
| 308 | + | |
| 309 | + | |
| 310 | + | |
| 311 | + | |
| 312 | + | |
| 313 | + | |
| 314 | + | |
| 315 | + | |
| 316 | + | |
| 317 | + | |
| 318 | + | |
| 319 | + | |
| 320 | + | |
| 321 | + | |
| 322 | + | |
| 323 | + | |
| 324 | + | |
| 325 | + | |
| 326 | + | |
| 327 | + | |
| 328 | + | |
| 329 | + | |
| 330 | + | |
| 331 | + | |
| 332 | + | |
| 333 | + | |
| 334 | + | |
| 335 | + | |
| 336 | + | |
| 337 | + | |
| 338 | + | |
| 339 | + | |
233 | 340 | | |
234 | 341 | | |
0 commit comments