Skip to content

Commit f9b741e

Browse files
KyleAMathewsclaude
andauthored
Fix garbage collection issue with Infinity setting (#1135)
* fix(db): handle gcTime Infinity to prevent immediate garbage collection When gcTime was set to Infinity, setTimeout(fn, Infinity) would coerce Infinity to 0 via JavaScript's ToInt32 conversion, causing immediate garbage collection instead of disabling it. This fix treats Infinity (and any non-finite values) the same as 0, effectively disabling automatic garbage collection when the user intends for data to never be collected. Fixes issue where collections in Electron apps would unexpectedly lose data when navigating back and forth with gcTime set to Infinity. * chore: add changeset for gcTime Infinity fix Co-Authored-By: Claude Opus 4.5 <[email protected]> * Handle negative gcTime values in GC timer Negative gcTime values like -1000 would pass through to setTimeout, which browsers treat as 0 (immediate execution). Now gcTime <= 0 disables GC, matching the intent of invalid timeout values. Co-Authored-By: Claude Opus 4.5 <[email protected]> --------- Co-authored-by: Claude <[email protected]>
1 parent 9204d76 commit f9b741e

3 files changed

Lines changed: 29 additions & 2 deletions

File tree

.changeset/fix-gc-infinity.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@tanstack/db': patch
3+
---
4+
5+
Fix `gcTime: Infinity` causing immediate garbage collection instead of disabling GC. JavaScript's `setTimeout` coerces `Infinity` to `0` via ToInt32, so we now explicitly check for non-finite values.

packages/db/src/collection/lifecycle.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -180,8 +180,10 @@ export class CollectionLifecycleManager<
180180

181181
const gcTime = this.config.gcTime ?? 300000 // 5 minutes default
182182

183-
// If gcTime is 0, GC is disabled
184-
if (gcTime === 0) {
183+
// If gcTime is 0, negative, or non-finite (Infinity, -Infinity, NaN), GC is disabled.
184+
// Note: setTimeout with Infinity coerces to 0 via ToInt32, causing immediate GC,
185+
// so we must explicitly check for non-finite values here.
186+
if (gcTime <= 0 || !Number.isFinite(gcTime)) {
185187
return
186188
}
187189

packages/db/tests/collection-lifecycle.test.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -360,6 +360,26 @@ describe(`Collection Lifecycle Management`, () => {
360360
expect(mockSetTimeout).not.toHaveBeenCalled()
361361
expect(collection.status).not.toBe(`cleaned-up`)
362362
})
363+
364+
it(`should disable GC when gcTime is Infinity`, () => {
365+
const collection = createCollection<{ id: string; name: string }>({
366+
id: `infinity-gc-test`,
367+
getKey: (item) => item.id,
368+
gcTime: Infinity, // Disabled GC via Infinity
369+
sync: {
370+
sync: () => {},
371+
},
372+
})
373+
374+
const subscription = collection.subscribeChanges(() => {})
375+
subscription.unsubscribe()
376+
377+
// Should not start any timer when gcTime is Infinity
378+
// Note: Without this fix, setTimeout(fn, Infinity) would coerce to 0,
379+
// causing immediate GC instead of never collecting
380+
expect(mockSetTimeout).not.toHaveBeenCalled()
381+
expect(collection.status).not.toBe(`cleaned-up`)
382+
})
363383
})
364384

365385
describe(`Manual Preload and Cleanup`, () => {

0 commit comments

Comments
 (0)