fix: add jitter, toasts, and cleanup to workspace token refresh#12276
fix: add jitter, toasts, and cleanup to workspace token refresh#12276christian-byrne wants to merge 1 commit into
Conversation
- Add jitter to retry delays to prevent thundering herd on backend recovery - Add toast notifications for retry states (retrying, degraded, expired) - Add AbortController for cleanup on destroy() and clearWorkspaceContext() - Preserve workspace context when token still valid but refresh fails - Update tests for new behavior (jitter-aware delays, context preservation) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
🎨 Storybook: ✅ Built — View Storybook |
🎭 Playwright: ✅ 1471 passed, 0 failed📊 Browser Reports
|
Codecov Report❌ Patch coverage is
@@ Coverage Diff @@
## fix/be-186-session-logout #12276 +/- ##
============================================================
Coverage ? 56.11%
============================================================
Files ? 1385
Lines ? 70933
Branches ? 19775
============================================================
Hits ? 39807
Misses ? 30596
Partials ? 530
Flags with carried forward coverage won't be shown. Click here to find out more.
🚀 New features to boost your workflow:
|
📦 Bundle Size
⚡ Performance Report
No baseline found — showing absolute values.
Raw data{
"timestamp": "2026-05-14T20:23:08.155Z",
"gitSha": "287902b7ec298ca41632911b9deea6536d5970fe",
"branch": "enhance/retry-utility-with-jitter",
"measurements": [
{
"name": "canvas-idle",
"durationMs": 2065.1249999999945,
"styleRecalcs": 8,
"styleRecalcDurationMs": 8.669999999999996,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 412.2260000000001,
"heapDeltaBytes": 23448104,
"heapUsedBytes": 72777860,
"domNodes": 16,
"jsHeapTotalBytes": 14680064,
"scriptDurationMs": 21.531000000000002,
"eventListeners": 4,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333332,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "canvas-idle",
"durationMs": 2059.1699999999946,
"styleRecalcs": 10,
"styleRecalcDurationMs": 9.274999999999999,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 430.706,
"heapDeltaBytes": 23793488,
"heapUsedBytes": 72734064,
"domNodes": 20,
"jsHeapTotalBytes": 14942208,
"scriptDurationMs": 30.154000000000003,
"eventListeners": 6,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "canvas-idle",
"durationMs": 2032.8139999999166,
"styleRecalcs": 8,
"styleRecalcDurationMs": 7.75,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 402.65099999999995,
"heapDeltaBytes": 23104576,
"heapUsedBytes": 71446740,
"domNodes": 16,
"jsHeapTotalBytes": 15728640,
"scriptDurationMs": 22.16100000000001,
"eventListeners": 4,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "canvas-mouse-sweep",
"durationMs": 1998.1149999999843,
"styleRecalcs": 77,
"styleRecalcDurationMs": 51.892,
"layouts": 12,
"layoutDurationMs": 4.97,
"taskDurationMs": 1019.8069999999999,
"heapDeltaBytes": 2010464,
"heapUsedBytes": 50811160,
"domNodes": -265,
"jsHeapTotalBytes": 15855616,
"scriptDurationMs": 141.184,
"eventListeners": -131,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.699999999999818
},
{
"name": "canvas-mouse-sweep",
"durationMs": 1902.4299999999812,
"styleRecalcs": 74,
"styleRecalcDurationMs": 41.228,
"layouts": 12,
"layoutDurationMs": 4.034000000000001,
"taskDurationMs": 855.972,
"heapDeltaBytes": 4827788,
"heapUsedBytes": 53119108,
"domNodes": -262,
"jsHeapTotalBytes": 15331328,
"scriptDurationMs": 127.36,
"eventListeners": -133,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333335,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "canvas-mouse-sweep",
"durationMs": 2067.3790000000736,
"styleRecalcs": 80,
"styleRecalcDurationMs": 45.152,
"layouts": 12,
"layoutDurationMs": 3.8429999999999995,
"taskDurationMs": 1066.203,
"heapDeltaBytes": 6361388,
"heapUsedBytes": 54721092,
"domNodes": -266,
"jsHeapTotalBytes": 16379904,
"scriptDurationMs": 136.974,
"eventListeners": -133,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "canvas-zoom-sweep",
"durationMs": 1730.2329999999984,
"styleRecalcs": 31,
"styleRecalcDurationMs": 19.654,
"layouts": 6,
"layoutDurationMs": 0.8099999999999997,
"taskDurationMs": 392.4920000000001,
"heapDeltaBytes": -80480,
"heapUsedBytes": 47925704,
"domNodes": 76,
"jsHeapTotalBytes": 15204352,
"scriptDurationMs": 29.781000000000002,
"eventListeners": 19,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "canvas-zoom-sweep",
"durationMs": 1718.7009999999532,
"styleRecalcs": 31,
"styleRecalcDurationMs": 17.673,
"layouts": 6,
"layoutDurationMs": 0.642,
"taskDurationMs": 315.35900000000004,
"heapDeltaBytes": 132716,
"heapUsedBytes": 48549216,
"domNodes": 76,
"jsHeapTotalBytes": 14942208,
"scriptDurationMs": 22.776000000000003,
"eventListeners": 19,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.699999999999818
},
{
"name": "canvas-zoom-sweep",
"durationMs": 1736.7080000000215,
"styleRecalcs": 32,
"styleRecalcDurationMs": 17.491,
"layouts": 6,
"layoutDurationMs": 0.6150000000000001,
"taskDurationMs": 318.009,
"heapDeltaBytes": 7871904,
"heapUsedBytes": 75794480,
"domNodes": 78,
"jsHeapTotalBytes": 18874368,
"scriptDurationMs": 27.118999999999996,
"eventListeners": 21,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66999999999998,
"p95FrameDurationMs": 16.799999999999272
},
{
"name": "dom-widget-clipping",
"durationMs": 589.6809999999846,
"styleRecalcs": 12,
"styleRecalcDurationMs": 8.908000000000003,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 389.864,
"heapDeltaBytes": -10589688,
"heapUsedBytes": 57412924,
"domNodes": 20,
"jsHeapTotalBytes": 20185088,
"scriptDurationMs": 70.049,
"eventListeners": 0,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000273
},
{
"name": "dom-widget-clipping",
"durationMs": 570.5600000000004,
"styleRecalcs": 10,
"styleRecalcDurationMs": 7.6679999999999975,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 363.55600000000004,
"heapDeltaBytes": 8498344,
"heapUsedBytes": 57201876,
"domNodes": 16,
"jsHeapTotalBytes": 16252928,
"scriptDurationMs": 62.132,
"eventListeners": 0,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000273
},
{
"name": "dom-widget-clipping",
"durationMs": 589.1050000000178,
"styleRecalcs": 13,
"styleRecalcDurationMs": 9.727,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 370.783,
"heapDeltaBytes": -10463108,
"heapUsedBytes": 57290184,
"domNodes": 22,
"jsHeapTotalBytes": 19660800,
"scriptDurationMs": 66.173,
"eventListeners": 0,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.669999999999998,
"p95FrameDurationMs": 16.700000000000273
},
{
"name": "large-graph-idle",
"durationMs": 2028.3939999999916,
"styleRecalcs": 8,
"styleRecalcDurationMs": 7.872999999999998,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 680.9419999999999,
"heapDeltaBytes": 9375388,
"heapUsedBytes": 68751756,
"domNodes": -263,
"jsHeapTotalBytes": 3756032,
"scriptDurationMs": 108.348,
"eventListeners": -131,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "large-graph-idle",
"durationMs": 2030.4520000000252,
"styleRecalcs": 8,
"styleRecalcDurationMs": 8.439000000000002,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 670.0670000000001,
"heapDeltaBytes": 3275688,
"heapUsedBytes": 61178880,
"domNodes": -263,
"jsHeapTotalBytes": 5009408,
"scriptDurationMs": 106.09299999999999,
"eventListeners": -129,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "large-graph-idle",
"durationMs": 2073.998999999958,
"styleRecalcs": 9,
"styleRecalcDurationMs": 8.888,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 599.795,
"heapDeltaBytes": 19568776,
"heapUsedBytes": 77951708,
"domNodes": -251,
"jsHeapTotalBytes": -233472,
"scriptDurationMs": 103.27,
"eventListeners": -129,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "large-graph-pan",
"durationMs": 2174.7839999999883,
"styleRecalcs": 68,
"styleRecalcDurationMs": 20.531999999999996,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 1227.3239999999998,
"heapDeltaBytes": 7313416,
"heapUsedBytes": 66807488,
"domNodes": -266,
"jsHeapTotalBytes": 1019904,
"scriptDurationMs": 410.71799999999996,
"eventListeners": -127,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.699999999999818
},
{
"name": "large-graph-pan",
"durationMs": 2194.272000000012,
"styleRecalcs": 69,
"styleRecalcDurationMs": 21.131,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 1219.3520000000003,
"heapDeltaBytes": 10368904,
"heapUsedBytes": 69927032,
"domNodes": -261,
"jsHeapTotalBytes": 233472,
"scriptDurationMs": 427.27899999999994,
"eventListeners": -127,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "large-graph-pan",
"durationMs": 2145.4390000000103,
"styleRecalcs": 68,
"styleRecalcDurationMs": 19.743999999999996,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 1080.664,
"heapDeltaBytes": 4240016,
"heapUsedBytes": 63465484,
"domNodes": -261,
"jsHeapTotalBytes": 757760,
"scriptDurationMs": 378.603,
"eventListeners": -127,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "large-graph-zoom",
"durationMs": 3277.290999999991,
"styleRecalcs": 64,
"styleRecalcDurationMs": 19.677,
"layouts": 60,
"layoutDurationMs": 8.996,
"taskDurationMs": 1534.005,
"heapDeltaBytes": 17249140,
"heapUsedBytes": 78196580,
"domNodes": -270,
"jsHeapTotalBytes": 1077248,
"scriptDurationMs": 574.129,
"eventListeners": -127,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333335,
"p95FrameDurationMs": 16.699999999999818
},
{
"name": "large-graph-zoom",
"durationMs": 3195.6630000000246,
"styleRecalcs": 64,
"styleRecalcDurationMs": 20.237000000000002,
"layouts": 60,
"layoutDurationMs": 8.51,
"taskDurationMs": 1408.625,
"heapDeltaBytes": 5281132,
"heapUsedBytes": 65776556,
"domNodes": -271,
"jsHeapTotalBytes": 4747264,
"scriptDurationMs": 513.3299999999999,
"eventListeners": -157,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "large-graph-zoom",
"durationMs": 3134.2069999999467,
"styleRecalcs": 65,
"styleRecalcDurationMs": 19.646000000000004,
"layouts": 60,
"layoutDurationMs": 8.78,
"taskDurationMs": 1378.565,
"heapDeltaBytes": 17005916,
"heapUsedBytes": 77211876,
"domNodes": -269,
"jsHeapTotalBytes": 815104,
"scriptDurationMs": 500.4820000000001,
"eventListeners": -125,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333335,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "minimap-idle",
"durationMs": 2014.7190000000137,
"styleRecalcs": 8,
"styleRecalcDurationMs": 8.511999999999999,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 714.159,
"heapDeltaBytes": 11217340,
"heapUsedBytes": 72673728,
"domNodes": -264,
"jsHeapTotalBytes": 4018176,
"scriptDurationMs": 106.593,
"eventListeners": -131,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "minimap-idle",
"durationMs": 2011.4970000000199,
"styleRecalcs": 8,
"styleRecalcDurationMs": 7.634000000000002,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 606.7289999999999,
"heapDeltaBytes": 10490768,
"heapUsedBytes": 70124140,
"domNodes": -265,
"jsHeapTotalBytes": 552960,
"scriptDurationMs": 99.92000000000002,
"eventListeners": -129,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333335,
"p95FrameDurationMs": 16.699999999999818
},
{
"name": "minimap-idle",
"durationMs": 2038.8749999999618,
"styleRecalcs": 8,
"styleRecalcDurationMs": 6.974999999999999,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 581.65,
"heapDeltaBytes": -10055068,
"heapUsedBytes": 50919288,
"domNodes": -262,
"jsHeapTotalBytes": 4018176,
"scriptDurationMs": 95.20200000000001,
"eventListeners": -129,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "subgraph-dom-widget-clipping",
"durationMs": 626.1339999999791,
"styleRecalcs": 47,
"styleRecalcDurationMs": 13.021,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 423.53,
"heapDeltaBytes": 9616968,
"heapUsedBytes": 57579364,
"domNodes": 20,
"jsHeapTotalBytes": 15204352,
"scriptDurationMs": 148.146,
"eventListeners": 6,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000273
},
{
"name": "subgraph-dom-widget-clipping",
"durationMs": 577.8450000000248,
"styleRecalcs": 48,
"styleRecalcDurationMs": 13.110000000000001,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 402.36799999999994,
"heapDeltaBytes": -9368128,
"heapUsedBytes": 58732088,
"domNodes": 22,
"jsHeapTotalBytes": 19660800,
"scriptDurationMs": 129.71200000000002,
"eventListeners": 8,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.670000000000012,
"p95FrameDurationMs": 16.700000000000273
},
{
"name": "subgraph-dom-widget-clipping",
"durationMs": 572.8490000000193,
"styleRecalcs": 47,
"styleRecalcDurationMs": 11.369,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 377.9509999999999,
"heapDeltaBytes": -12675820,
"heapUsedBytes": 53096360,
"domNodes": 20,
"jsHeapTotalBytes": 20541440,
"scriptDurationMs": 130.89600000000002,
"eventListeners": 8,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.669999999999998,
"p95FrameDurationMs": 16.799999999999727
},
{
"name": "subgraph-idle",
"durationMs": 2013.5709999999847,
"styleRecalcs": 11,
"styleRecalcDurationMs": 11.011,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 481.785,
"heapDeltaBytes": 23033768,
"heapUsedBytes": 71000908,
"domNodes": 21,
"jsHeapTotalBytes": 14680064,
"scriptDurationMs": 29.196999999999996,
"eventListeners": 4,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "subgraph-idle",
"durationMs": 1998.658999999975,
"styleRecalcs": 10,
"styleRecalcDurationMs": 10.224,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 443.41999999999996,
"heapDeltaBytes": 2886312,
"heapUsedBytes": 71524704,
"domNodes": 20,
"jsHeapTotalBytes": 18874368,
"scriptDurationMs": 21.553000000000008,
"eventListeners": 6,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "subgraph-idle",
"durationMs": 2004.2869999999766,
"styleRecalcs": 10,
"styleRecalcDurationMs": 8.794,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 382.38000000000005,
"heapDeltaBytes": 22314980,
"heapUsedBytes": 70675080,
"domNodes": 19,
"jsHeapTotalBytes": 15204352,
"scriptDurationMs": 18.216000000000005,
"eventListeners": 4,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "subgraph-mouse-sweep",
"durationMs": 2005.9489999999869,
"styleRecalcs": 84,
"styleRecalcDurationMs": 51.546,
"layouts": 16,
"layoutDurationMs": 4.997000000000001,
"taskDurationMs": 1045.241,
"heapDeltaBytes": 9171868,
"heapUsedBytes": 58788120,
"domNodes": -260,
"jsHeapTotalBytes": 13852672,
"scriptDurationMs": 107.659,
"eventListeners": -133,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "subgraph-mouse-sweep",
"durationMs": 1721.0440000000062,
"styleRecalcs": 76,
"styleRecalcDurationMs": 42.659000000000006,
"layouts": 16,
"layoutDurationMs": 4.922000000000001,
"taskDurationMs": 732.6429999999999,
"heapDeltaBytes": -5390148,
"heapUsedBytes": 62889384,
"domNodes": 63,
"jsHeapTotalBytes": 19660800,
"scriptDurationMs": 105.608,
"eventListeners": 4,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333332,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "subgraph-mouse-sweep",
"durationMs": 1698.8770000000386,
"styleRecalcs": 76,
"styleRecalcDurationMs": 40.43,
"layouts": 16,
"layoutDurationMs": 4.970000000000001,
"taskDurationMs": 696.875,
"heapDeltaBytes": 14867672,
"heapUsedBytes": 63954036,
"domNodes": 63,
"jsHeapTotalBytes": 15204352,
"scriptDurationMs": 102.15199999999999,
"eventListeners": 6,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "viewport-pan-sweep",
"durationMs": 8225.655000000017,
"styleRecalcs": 249,
"styleRecalcDurationMs": 65.16900000000001,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 4337.976000000001,
"heapDeltaBytes": 25873384,
"heapUsedBytes": 83829112,
"domNodes": -261,
"jsHeapTotalBytes": 3641344,
"scriptDurationMs": 1389.27,
"eventListeners": -113,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "viewport-pan-sweep",
"durationMs": 8315.219000000014,
"styleRecalcs": 250,
"styleRecalcDurationMs": 62.175999999999995,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 4282.89,
"heapDeltaBytes": 24670900,
"heapUsedBytes": 84325812,
"domNodes": -259,
"jsHeapTotalBytes": 9699328,
"scriptDurationMs": 1445.3519999999999,
"eventListeners": -113,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "viewport-pan-sweep",
"durationMs": 8178.869000000077,
"styleRecalcs": 249,
"styleRecalcDurationMs": 59.68899999999999,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 4015.7859999999996,
"heapDeltaBytes": 25933608,
"heapUsedBytes": 85081500,
"domNodes": -261,
"jsHeapTotalBytes": 12058624,
"scriptDurationMs": 1391.1370000000002,
"eventListeners": -113,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333332,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "vue-large-graph-idle",
"durationMs": 13855.42399999997,
"styleRecalcs": 0,
"styleRecalcDurationMs": 0,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 13817.687,
"heapDeltaBytes": -41524068,
"heapUsedBytes": 168476108,
"domNodes": -8329,
"jsHeapTotalBytes": 22343680,
"scriptDurationMs": 605.9369999999999,
"eventListeners": -16462,
"totalBlockingTimeMs": 0,
"frameDurationMs": 18.330000000000048,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "vue-large-graph-idle",
"durationMs": 13856.95899999996,
"styleRecalcs": 0,
"styleRecalcDurationMs": 0,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 13833.328,
"heapDeltaBytes": -26928364,
"heapUsedBytes": 171959072,
"domNodes": -8331,
"jsHeapTotalBytes": 24440832,
"scriptDurationMs": 673.0500000000001,
"eventListeners": -16462,
"totalBlockingTimeMs": 0,
"frameDurationMs": 17.223333333333237,
"p95FrameDurationMs": 16.799999999999272
},
{
"name": "vue-large-graph-idle",
"durationMs": 13383.418000000005,
"styleRecalcs": 0,
"styleRecalcDurationMs": 0,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 13367.151,
"heapDeltaBytes": -27755312,
"heapUsedBytes": 169312316,
"domNodes": -8331,
"jsHeapTotalBytes": 21295104,
"scriptDurationMs": 587.607,
"eventListeners": -16464,
"totalBlockingTimeMs": 0,
"frameDurationMs": 17.776666666666642,
"p95FrameDurationMs": 16.799999999999272
},
{
"name": "vue-large-graph-pan",
"durationMs": 15967.87599999999,
"styleRecalcs": 82,
"styleRecalcDurationMs": 21.857000000000014,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 15920.782000000003,
"heapDeltaBytes": -51449536,
"heapUsedBytes": 165055652,
"domNodes": -8331,
"jsHeapTotalBytes": -3346432,
"scriptDurationMs": 911.087,
"eventListeners": -16458,
"totalBlockingTimeMs": 2,
"frameDurationMs": 17.776666666666642,
"p95FrameDurationMs": 16.799999999999272
},
{
"name": "vue-large-graph-pan",
"durationMs": 16139.173000000028,
"styleRecalcs": 87,
"styleRecalcDurationMs": 21.926000000000002,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 16103.638999999997,
"heapDeltaBytes": -20016628,
"heapUsedBytes": 187992056,
"domNodes": -8329,
"jsHeapTotalBytes": 20946944,
"scriptDurationMs": 954.1809999999999,
"eventListeners": -16458,
"totalBlockingTimeMs": 6,
"frameDurationMs": 17.223333333333358,
"p95FrameDurationMs": 16.80000000000291
},
{
"name": "vue-large-graph-pan",
"durationMs": 15703.431000000022,
"styleRecalcs": 80,
"styleRecalcDurationMs": 19.571999999999978,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 15679.546,
"heapDeltaBytes": -52961392,
"heapUsedBytes": 158235400,
"domNodes": -8331,
"jsHeapTotalBytes": -6230016,
"scriptDurationMs": 851.4549999999998,
"eventListeners": -16490,
"totalBlockingTimeMs": 42,
"frameDurationMs": 17.776666666666642,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "workflow-execution",
"durationMs": 474.2119999999659,
"styleRecalcs": 21,
"styleRecalcDurationMs": 27.482,
"layouts": 5,
"layoutDurationMs": 1.7589999999999997,
"taskDurationMs": 131.10999999999999,
"heapDeltaBytes": 5333640,
"heapUsedBytes": 55281012,
"domNodes": 190,
"jsHeapTotalBytes": 262144,
"scriptDurationMs": 23.269000000000002,
"eventListeners": 69,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.663333333333338,
"p95FrameDurationMs": 16.700000000000273
},
{
"name": "workflow-execution",
"durationMs": 503.43500000008135,
"styleRecalcs": 14,
"styleRecalcDurationMs": 22.551000000000002,
"layouts": 5,
"layoutDurationMs": 1.4310000000000003,
"taskDurationMs": 165.61900000000003,
"heapDeltaBytes": -2338108,
"heapUsedBytes": 48936524,
"domNodes": -115,
"jsHeapTotalBytes": -1351680,
"scriptDurationMs": 25.485999999999997,
"eventListeners": -62,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000273
},
{
"name": "workflow-execution",
"durationMs": 135.02699999992274,
"styleRecalcs": 13,
"styleRecalcDurationMs": 20.322999999999997,
"layouts": 5,
"layoutDurationMs": 1.5590000000000002,
"taskDurationMs": 98.12499999999999,
"heapDeltaBytes": 3450844,
"heapUsedBytes": 54333552,
"domNodes": 148,
"jsHeapTotalBytes": 0,
"scriptDurationMs": 20.912999999999993,
"eventListeners": 37,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.800000000000182
}
]
} |
dante01yoon
left a comment
There was a problem hiding this comment.
Nice UX additions over #11109 (jitter, toasts, degraded-state preservation, cancellable refresh). A few things I'd like to resolve before approving:
issue:jitter is applied after the 60s cap inscheduleRefreshRetry, so the retry delay can exceed the cap by up to 5s.question:Math.random() * 5000jitter magnitude vs.remainingMs: whenremainingMsis small (e.g. ~11s), the retry can land within ~500ms of token expiry.question:AbortController.signalis never threaded into thefetchinswitchWorkspace, soabort()only short-circuits the next retry-loop iteration — it does not cancel the in-flight network call or prevent a late-arriving success from re-armingscheduleTokenRefreshafterdestroy().suggestion:toast emission per attempt can pile up during sustained outages, especially becausescheduleRefreshRetryre-spawns the whole retry cycle.
Details inline.
| remainingMs <= 10_000 | ||
| ? remainingMs | ||
| : Math.min(60_000, Math.floor(remainingMs / 2)) | ||
| : Math.min(60_000, Math.floor(remainingMs / 2)) + jitter |
There was a problem hiding this comment.
issue: jitter is added outside the Math.min(60_000, …) cap, so retryDelay can be up to 60_000 + jitter (~65s) once remainingMs / 2 >= 60_000. The intent of the cap ("don't sleep longer than 60s before re-attempting") is defeated by the additive jitter.
Could we move the jitter inside the min so the cap is honored, e.g.:
const retryDelay =
remainingMs <= 10_000
? remainingMs
: Math.min(60_000, Math.floor(remainingMs / 2) + jitter)|
|
||
| const remainingMs = workspaceTokenExpiresAt.value - Date.now() | ||
| // Add jitter to prevent thundering herd across browser tabs | ||
| const jitter = Math.random() * 5000 |
There was a problem hiding this comment.
question: 5s of additive jitter is large relative to small remainingMs windows. When remainingMs is just above the 10s short-circuit (say ~11s), the non-short-circuit branch gives floor(11_000/2) + up to 5_000 = up to 10.5s, leaving as little as ~500ms before the workspace token actually expires. Was 5_000ms picked deliberately, or would something proportional to remainingMs (or a smaller fixed jitter) be safer? Same question applies to the retry-loop jitter at line 378 (500ms there feels reasonable, just want to confirm the asymmetry is intentional).
| // Create AbortController for this refresh operation | ||
| abortCurrentRefresh() | ||
| const abortController = new AbortController() | ||
| currentRefreshAbort = abortController |
There was a problem hiding this comment.
question: the AbortController is allocated and stored, but abortController.signal is never passed to the fetch inside switchWorkspace. So abort():
- does not cancel the in-flight
/auth/tokenrequest, and - does not prevent a late-resolving successful
switchWorkspacefrom settingcurrentWorkspace/workspaceTokenand callingscheduleTokenRefresh(expiresAt)afterdestroy()orclearWorkspaceContext()has run.
The PR description says "Proper cleanup when context is cleared or component unmounts." For clearWorkspaceContext, refreshRequestId++ already covers the stale-write case before mutating state, but destroy() does not increment refreshRequestId, so a successful in-flight refresh after destroy can re-arm the timer and resurrect state. Is the intent to thread abortController.signal into the fetch (and treat AbortError as a no-op in the catch), or are you deliberately keeping abort as a loop-only guard? If the latter, the new abortController.signal.aborted check in the loop overlaps almost entirely with the existing capturedRequestId !== refreshRequestId check called from clearWorkspaceContext, so it might be worth either wiring the signal end-to-end or dropping the AbortController and bumping refreshRequestId inside destroy() instead.
| delay: Math.round(delay / 1000) | ||
| }), | ||
| life: delay + 2000 | ||
| }) |
There was a problem hiding this comment.
suggestion: every failed attempt adds a fresh "Reconnecting…" toast (3 per cycle, plus a final degraded/expired toast). Because scheduleRefreshRetry() will eventually trigger another refreshToken() (which restarts the whole 4-attempt cycle), a backend that's down for a few minutes can produce a long stream of warn toasts.
Would it make sense to either gate the retry toast to attempt === 0 (one "Reconnecting…" per refresh cycle) or reuse a single sticky toast that updates? Right now even a recoverable hiccup pops 3 stacked warn toasts before the success, which is noisier than the "toast notifications during retries" intent in the PR description suggests.
Summary
Enhancements to PR #11109 based on code review feedback:
Changes
workspaceAuthStore.ts: Add jitter to delays, toast notifications, AbortController cleanupmain.json: i18n keys for toast messagesuseWorkspaceAuth.test.ts: Update tests for jitter-aware delays and context preservation behaviorTest plan
pnpm test:unit- all 35 workspace auth tests passpnpm typecheck- no errorspnpm lint- no errorspnpm format:check- passes🤖 Generated with Claude Code
┆Issue is synchronized with this Notion page by Unito