mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-06-21 06:11:45 +00:00
fix: prevent cold-path hang when Dexie write transactions stall after external IDB clear
When DevTools "Clear site data" deletes the IDB while the tab is open, Dexie receives a versionchange event and closes its connection. On reopen, read transactions work (toArray completes after ~400ms), but write transactions can stall indefinitely, causing the cold-path 'await refresh' to never resolve. Two changes: - Make upsertTrip calls fire-and-forget in the IIFE so network data is returned immediately without blocking on potentially-stuck IDB writes. - Add a 2-second timeout to the initial offlineDb.trips.toArray() call so that if the read also stalls, the cold path falls through to the network fetch. - Reduce the outer dashboard timeout from 12s to 5s now that the inner path cannot stall for more than ~2s + network RTT.
This commit is contained in:
@@ -744,7 +744,11 @@ export default function DashboardPage(): React.ReactElement {
|
||||
const loadTrips = async () => {
|
||||
setIsLoading(true)
|
||||
try {
|
||||
const { trips, archivedTrips, refresh } = await tripRepo.list()
|
||||
const listOrTimeout = Promise.race([
|
||||
tripRepo.list(),
|
||||
new Promise<never>((_, reject) => setTimeout(() => reject(new Error('trips-load-timeout')), 5_000)),
|
||||
])
|
||||
const { trips, archivedTrips, refresh } = await listOrTimeout
|
||||
setTrips(sortTrips(trips))
|
||||
setArchivedTrips(sortTrips(archivedTrips))
|
||||
setIsLoading(false)
|
||||
|
||||
@@ -8,7 +8,12 @@ type TripRefresh = Promise<{ trip: Trip } | null>
|
||||
|
||||
export const tripRepo = {
|
||||
async list(): Promise<{ trips: Trip[]; archivedTrips: Trip[]; refresh: TripsRefresh }> {
|
||||
const all = await offlineDb.trips.toArray()
|
||||
// 2-second guard: if Dexie is in a bad state (e.g. externally deleted while tab
|
||||
// was open), toArray() may hang. Fall back to the cold/network path.
|
||||
const all = await Promise.race([
|
||||
offlineDb.trips.toArray(),
|
||||
new Promise<Trip[]>(resolve => setTimeout(() => resolve([]), 2000)),
|
||||
])
|
||||
|
||||
const refresh: TripsRefresh = (async () => {
|
||||
try {
|
||||
@@ -16,6 +21,8 @@ export const tripRepo = {
|
||||
tripsApi.list(),
|
||||
tripsApi.list({ archived: 1 }),
|
||||
])
|
||||
// Fire-and-forget IDB writes: returning data immediately unblocks the cold
|
||||
// path even when Dexie write transactions stall after an external DB clear.
|
||||
Promise.all([
|
||||
...active.trips.map(t => upsertTrip(t)),
|
||||
...archived.trips.map(t => upsertTrip(t)),
|
||||
@@ -36,11 +43,6 @@ export const tripRepo = {
|
||||
|
||||
const fresh = await refresh
|
||||
if (!fresh) return { trips: [], archivedTrips: [], refresh: Promise.resolve(null) }
|
||||
// Await upserts on cold path so next mount reads from IDB instead of hitting network again
|
||||
await Promise.all([
|
||||
...fresh.trips.map(t => upsertTrip(t)),
|
||||
...fresh.archivedTrips.map(t => upsertTrip(t)),
|
||||
]).catch(() => {})
|
||||
return { ...fresh, refresh: Promise.resolve(null) }
|
||||
},
|
||||
|
||||
|
||||
Reference in New Issue
Block a user