Skip to content

Commit 6745ed0

Browse files
KyleAMathewsclaude
andauthored
Clarify queueStrategy documentation (#1107)
* docs: clarify queueStrategy error handling behavior The previous documentation implied that "every mutation is guaranteed to persist" which is misleading. This clarifies that: - Every mutation is guaranteed to be ATTEMPTED (not dropped) - Failed mutations are NOT automatically retried - Failed mutations transition to "failed" state - Subsequent mutations continue processing (queue doesn't stop) - Each mutation is independent (no all-or-nothing semantics) Fixes #1071 * docs: add retry behavior documentation and examples TanStack DB does not automatically retry failed mutations - this is by design since retry strategies vary by use case. This commit: - Adds a "Retry Behavior" section explaining why auto-retry isn't built-in - Provides a simple retry helper example with exponential backoff - Adds a queue strategy-specific retry example for file uploads - References p-retry library for more sophisticated strategies This helps answer the question from #1071 about what happens when mutations fail and how to handle retries. * docs: remove duplicative retry example from queue strategy section Link to the Retry Behavior section instead of duplicating the example. * docs: revert manual edit to generated reference doc The queueStrategy.md reference doc is auto-generated from JSDoc in the source file - the source already has the updated documentation. * chore: add changeset for queue strategy docs clarification Co-Authored-By: Claude Opus 4.5 <[email protected]> * docs: address review feedback on punctuation and backoff description - Replace em-dash with period for clarity (Kevin's feedback) - Fix "exponential backoff" comment to "increasing delay" since the code uses linear backoff (Kyle's earlier feedback) Co-Authored-By: Claude Opus 4.5 <[email protected]> --------- Co-authored-by: Claude <[email protected]>
1 parent bdf9405 commit 6745ed0

3 files changed

Lines changed: 61 additions & 4 deletions

File tree

.changeset/clarify-queue-docs.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+
Clarify queueStrategy error handling behavior in documentation. Changed "guaranteed to persist" to "guaranteed to be attempted" and added explicit documentation about how failed mutations are handled (not retried, queue continues). Added new Retry Behavior section with example code for implementing custom retry logic.

docs/guides/mutations.md

Lines changed: 48 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1093,7 +1093,7 @@ function VolumeSlider() {
10931093

10941094
### Queue Strategy
10951095

1096-
The queue strategy creates a separate transaction for each mutation and processes them sequentially in order. Unlike debounce/throttle, **every mutation is guaranteed to persist**, making it ideal for workflows where you can't lose any operations.
1096+
The queue strategy creates a separate transaction for each mutation and processes them sequentially in order. Unlike debounce/throttle which may drop intermediate mutations, **every mutation is guaranteed to be attempted**, making it ideal for workflows where you can't skip any operations.
10971097

10981098
```tsx
10991099
import { usePacedMutations, queueStrategy } from "@tanstack/react-db"
@@ -1136,9 +1136,16 @@ function FileUploader() {
11361136
- Each mutation becomes its own transaction
11371137
- Processes sequentially in order (FIFO by default)
11381138
- Can configure to LIFO by setting `getItemsFrom: 'back'`
1139-
- All mutations guaranteed to persist
1139+
- All mutations guaranteed to be attempted (unlike debounce/throttle which may skip intermediate mutations)
11401140
- Waits for each transaction to complete before starting the next
11411141

1142+
**Error handling**:
1143+
- If a mutation fails, **it is not automatically retried** - the transaction transitions to "failed" state
1144+
- Failed mutations surface their error via `transaction.isPersisted.promise` (which will reject)
1145+
- **Subsequent mutations continue processing** - a single failure does not block the queue
1146+
- Each mutation is independent; there is no all-or-nothing transaction semantics across multiple mutations
1147+
- To implement retry logic, see [Retry Behavior](#retry-behavior)
1148+
11421149
### Choosing a Strategy
11431150

11441151
Use this guide to pick the right strategy for your use case:
@@ -1453,6 +1460,45 @@ If an error occurs: `pending` → `persisting` → `failed`
14531460

14541461
Failed transactions automatically rollback their optimistic state.
14551462

1463+
### Retry Behavior
1464+
1465+
**Important:** TanStack DB does not automatically retry failed mutations. If a mutation fails (network error, server error, etc.), the transaction transitions to `failed` state and the optimistic state is rolled back. This is by design. Automatic retry logic varies significantly based on your use case (idempotency requirements, error types, backoff strategies, etc.).
1466+
1467+
To implement retry logic, wrap your API calls in your `mutationFn`:
1468+
1469+
```typescript
1470+
// Simple retry helper
1471+
async function withRetry<T>(
1472+
fn: () => Promise<T>,
1473+
maxRetries = 3,
1474+
delay = 1000
1475+
): Promise<T> {
1476+
for (let attempt = 0; attempt < maxRetries; attempt++) {
1477+
try {
1478+
return await fn()
1479+
} catch (error) {
1480+
if (attempt === maxRetries - 1) throw error
1481+
await new Promise(resolve => setTimeout(resolve, delay * (attempt + 1)))
1482+
}
1483+
}
1484+
throw new Error('Unreachable')
1485+
}
1486+
1487+
// Use in your collection
1488+
const todoCollection = createCollection({
1489+
id: "todos",
1490+
onUpdate: async ({ transaction }) => {
1491+
const mutation = transaction.mutations[0]
1492+
// Retry up to 3 times with increasing delay
1493+
await withRetry(() =>
1494+
api.todos.update(mutation.original.id, mutation.changes)
1495+
)
1496+
},
1497+
})
1498+
```
1499+
1500+
For more sophisticated retry strategies, consider using a library like [p-retry](https://github.com/sindresorhus/p-retry) which supports exponential backoff, custom retry conditions, and abort signals.
1501+
14561502
## Handling Temporary IDs
14571503

14581504
When inserting new items into collections where the server generates the final ID, you'll need to handle the transition from temporary to real IDs carefully to avoid UI issues and operation failures.

packages/db/src/strategies/queueStrategy.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,15 @@ import type { Transaction } from '../transactions'
66
* Creates a queue strategy that processes all mutations in order with proper serialization.
77
*
88
* Unlike other strategies that may drop executions, queue ensures every
9-
* mutation is processed sequentially. Each transaction commit completes before
9+
* mutation is attempted sequentially. Each transaction commit completes before
1010
* the next one starts. Useful when data consistency is critical and
11-
* every operation must complete in order.
11+
* every operation must be attempted in order.
12+
*
13+
* **Error handling behavior:**
14+
* - If a mutation fails, it is NOT automatically retried - the transaction transitions to "failed" state
15+
* - Failed mutations surface their error via `transaction.isPersisted.promise` (which will reject)
16+
* - Subsequent mutations continue processing - a single failure does not block the queue
17+
* - Each mutation is independent; there is no all-or-nothing transaction semantics
1218
*
1319
* @param options - Configuration for queue behavior (FIFO/LIFO, timing, size limits)
1420
* @returns A queue strategy instance

0 commit comments

Comments
 (0)