Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 55 additions & 0 deletions docs/queue-priority-strategy.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# Request Queue Priority Strategy

## Overview

The `RequestQueue` service (`src/services/api/requestQueue.ts`) manages offline requests that fail due to network errors. It persists requests to AsyncStorage, supports priority levels, and batches similar requests during sync.

## Priority Levels

| Priority | Value | Use Case |
|----------|-------|----------|
| critical | 0 | Auth tokens, payments, enrollments |
| high | 1 | Course progress, quiz submissions |
| normal | 2 | Profile updates, content likes |
| low | 3 | Analytics events, read-ahead fetches |

The queue is sorted by priority then FIFO within each priority level.

## Persistence

- All queued requests are stored in AsyncStorage under `@teachlink_request_queue`
- Queue metrics are persisted under `@teachlink_queue_metrics`
- On app restart, `resume()` restores pending requests from storage

## Batch Processing

During sync, requests with the same method + endpoint are grouped:

- **PUT/PATCH**: Payloads are merged into a single request
- **GET**: Executed in parallel via `Promise.allSettled`
- **POST/DELETE**: Processed individually within the batch

## Analytics

Queue events are tracked via `mobileAnalyticsService.trackEvent()`:
- `request_queued` — when a request enters the queue
- `request_dequeued` — when a request succeeds
- `queue_batch_synced` — when a batch merge succeeds
- `queue_resumed` — on app restart with pending requests

## Usage

```ts
import { requestQueue } from '../services/api/requestQueue';

// Add with priority
await requestQueue.addToQueue(config, 'high');

// Check status
const status = await requestQueue.getQueueStatus();

// Monitor from hook
import { usePendingRequests } from '../hooks/usePendingRequests';
const count = usePendingRequests();
const { pendingCount, byPriority } = usePendingRequests('critical');
```
42 changes: 31 additions & 11 deletions src/hooks/usePendingRequests.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,41 @@
import { useEffect, useState } from 'react';
import requestQueue from '../services/api/requestQueue';
import requestQueue, { RequestPriority } from '../services/api/requestQueue';

/**
* Hook to get the number of pending offline requests
*/
export function usePendingRequests() {
export function usePendingRequests(priority?: RequestPriority) {
const [pendingCount, setPendingCount] = useState(0);
const [byPriority, setByPriority] = useState<
Record<RequestPriority, number>
>({
critical: 0,
high: 0,
normal: 0,
low: 0,
});

useEffect(() => {
// Get initial count
requestQueue.getPendingCount().then(setPendingCount);
const update = async () => {
if (priority) {
const counts = await requestQueue.getPendingByPriority();
setByPriority(counts);
setPendingCount(counts[priority]);
} else {
const count = await requestQueue.getPendingCount();
setPendingCount(count);
}
};

// Listen for changes
const unsubscribe = requestQueue.onPendingCountChange(setPendingCount);
update();

const unsubscribe = requestQueue.onPendingCountChange(() => {
update();
});

return unsubscribe;
}, []);
}, [priority]);

if (priority) {
return { pendingCount, byPriority };
}

return pendingCount;
}
}
Loading