Garbage collection cleans up a collection while one of its transactions is still persisting
Les mainteneurs répondent en général sous 1 jour
@Nabil372 y travaille déjà.
Depuis le 24/10/2026.
Évaluation
- Difficulté
- 3/5
- Temps estimé
- 1-2 jours
- Accessibilité débutants
- 35/100
- Type d'issue
- Bug
- Clarté
- Clairement spécifiée
- Activité
- Active
- Stack technique
- typescript
- Domaine
- database
Piste de recherche
Start with packages/db/src/collection/lifecycle.ts and trace how canGarbageCollect() determines whether cleanup can proceed. Then inspect the transaction lifecycle and existing generated-history tests mentioned in the issue. Done means a persisting transaction keeps the collection and its optimistic state alive until it settles or rolls back, while explicit cleanup() retains its current behavior.
Rédigé par le modèle d'indexation à partir du texte de l'issue.
Description
- I've validated the bug against the latest version of DB packages
(Checked on main and on 0.15.2.)
Describe the bug
A collection with no subscribers is garbage collected after gcTime even when one of its transactions is still persisting. The optimistic row disappears while the mutation handler is still running. With the Electric collection, cleanup also rejects the handler's pending awaitTxId with StreamAbortedError, so the write fails even though the server accepted it.
We use TanStack DB with Electric in production in web apps and in Expo apps on iOS and Android, and this is easy to hit on mobile. A screen inserts a row and closes; the handler is waiting on a slow request, or on a txid that arrives late because the app went to the background. Once gcTime passes, the write is lost from the client's point of view.
canGarbageCollect() in packages/db/src/collection/lifecycle.ts checks for a cleanup in progress, active subscribers, and a pending preload, but not for persisting transactions.
To Reproduce
import { createCollection } from '@tanstack/db'
let release!: () => void
const handlerDone = new Promise<void>((resolve) => (release = resolve))
const collection = createCollection<{ id: string }, string>({
id: 'gc-during-persist',
getKey: (row) => row.id,
gcTime: 10,
startSync: true,
sync: {
sync: ({ markReady }) => {
markReady()
return () => {}
},
},
onInsert: async () => {
await handlerDone
},
})
const subscription = collection.subscribeChanges(() => {})
const tx = collection.insert({ id: 'a' })
subscription.unsubscribe()
await new Promise((resolve) => setTimeout(resolve, 2000))
console.log(collection.status, collection.has('a'), tx.state)
// cleaned-up false persisting
release()
Expected behavior
While a transaction that touches the collection is persisting, the collection is not garbage collected, so its optimistic state and any awaitTxId or awaitMatch wait survive until the transaction settles. GC can start its timer once the transaction completes or rolls back. An explicit cleanup() would keep its current behavior.
In production we work around it by holding the collection with a subscription for the length of each write, using subscribeChanges(() => {}, { includeInitialState: false, deferAcquisition: true }) so the hold does not count as a reader. deferAcquisition is marked @internal, so we would rather not depend on it.
If holding a collection while its transaction persists is not the intended behavior, a public way to hold a collection for the length of a write would cover the same need.
I have a fix with a generated-history test and will open a pull request for it.
Desktop: Node 24, jsdom (Vitest). Smartphone: React Native (Expo), iOS and Android, where screens close and apps background while writes are in flight.
- Langage dominant
- TypeScript
- Étoiles
- 4k
- Forks
- 277
- Merge moyen
- 17 h 40 min
- PR mergées (30 j)
- 223
Préparer son environnement
- Aucun Dockerfile ni fichier Docker Compose
- Propose un modèle de pull request
- Lire le guide de contribution
Par où commencer
- Lisez l'issue en entier, puis le guide de contribution du projet.
- Signalez en commentaire que vous la prenez — cela évite que deux personnes fassent le même travail.
- Forkez le dépôt et travaillez sur une branche.
- Ouvrez une pull request qui référence le numéro de l'issue.
Autres issues de TanStack/db
-
Difficulté 2/5 1-3 heures Accessibilité débutants 78/100
Les mainteneurs répondent en général sous 1 jour
-
Difficulté 2/5 1-3 heures Accessibilité débutants 78/100
Les mainteneurs répondent en général sous 1 jour
-
Difficulté 2/5 1-3 heures Accessibilité débutants 82/100
Les mainteneurs répondent en général sous 1 jour
-
persistedCollectionOptions without a schema can't be passed to createCollection (follow-up to #1452)Ouverte
Difficulté 3/5 1-2 jours Accessibilité débutants 68/100
Les mainteneurs répondent en général sous 1 jour
-
Electric: scoped move-in snapshots can prematurely resolve awaitTxId and remove optimistic rowsOuverte
Difficulté 4/5 3-5 jours Accessibilité débutants 38/100
Les mainteneurs répondent en général sous 1 jour
Toutes les issues de TanStack/db
Issues similaires
-
bug
Difficulté 2/5 1-3 heures Accessibilité débutants 68/100
outline/outline#14348 · 1 commentaire ·
Les mainteneurs répondent en général sous 1 jour
-
bug needs-triage
Difficulté 2/5 1-3 heures Accessibilité débutants 68/100
Les mainteneurs répondent en général sous 1 jour
-
[Bug]: Agent import retains quotes in .env API keys followed by inline commentsPeut-être pris @yxr-2025 l’a pris aujourd’hui. Ouverte
Difficulté 2/5 1-3 heures Accessibilité débutants 82/100
elie222/rakazo#2322 · 1 commentaire ·
Les mainteneurs répondent en général sous 1 jour
-
bug
Difficulté 2/5 1-3 heures Accessibilité débutants 76/100
awslabs/visual-asset-management-system#804 ·
Les mainteneurs répondent en général sous 1 jour
-
用户目录有单引号时,热加载时YAML解析崩溃Ouvertebug
Difficulté 2/5 1-3 heures Accessibilité débutants 68/100
dsh-market/dsh-market#1027 · 1 commentaire ·
Les mainteneurs répondent en général sous 1 jour