mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-06-22 06:41:46 +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 () => {
|
const loadTrips = async () => {
|
||||||
setIsLoading(true)
|
setIsLoading(true)
|
||||||
try {
|
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))
|
setTrips(sortTrips(trips))
|
||||||
setArchivedTrips(sortTrips(archivedTrips))
|
setArchivedTrips(sortTrips(archivedTrips))
|
||||||
setIsLoading(false)
|
setIsLoading(false)
|
||||||
|
|||||||
@@ -8,7 +8,12 @@ type TripRefresh = Promise<{ trip: Trip } | null>
|
|||||||
|
|
||||||
export const tripRepo = {
|
export const tripRepo = {
|
||||||
async list(): Promise<{ trips: Trip[]; archivedTrips: Trip[]; refresh: TripsRefresh }> {
|
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 () => {
|
const refresh: TripsRefresh = (async () => {
|
||||||
try {
|
try {
|
||||||
@@ -16,6 +21,8 @@ export const tripRepo = {
|
|||||||
tripsApi.list(),
|
tripsApi.list(),
|
||||||
tripsApi.list({ archived: 1 }),
|
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([
|
Promise.all([
|
||||||
...active.trips.map(t => upsertTrip(t)),
|
...active.trips.map(t => upsertTrip(t)),
|
||||||
...archived.trips.map(t => upsertTrip(t)),
|
...archived.trips.map(t => upsertTrip(t)),
|
||||||
@@ -36,11 +43,6 @@ export const tripRepo = {
|
|||||||
|
|
||||||
const fresh = await refresh
|
const fresh = await refresh
|
||||||
if (!fresh) return { trips: [], archivedTrips: [], refresh: Promise.resolve(null) }
|
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) }
|
return { ...fresh, refresh: Promise.resolve(null) }
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user