feat(offline): track structured failure details during caching
Replace the failure counter with a list of typed failures (fetch, build, save, readme, unknown) carrying path, sha, message, and affected doc ids so callers can surface diagnostics instead of just a count.
This commit is contained in:
@@ -11,6 +11,26 @@ import { useUserRepoStore } from "@/modules/repo/store/userRepo.store"
|
|||||||
const CONCURRENCY = 8
|
const CONCURRENCY = 8
|
||||||
const BULK_FLUSH_SIZE = 50
|
const BULK_FLUSH_SIZE = 50
|
||||||
|
|
||||||
|
export type CacheFailureKind =
|
||||||
|
| "fetch"
|
||||||
|
| "build"
|
||||||
|
| "save"
|
||||||
|
| "readme"
|
||||||
|
| "unknown"
|
||||||
|
|
||||||
|
export type CacheFailure = {
|
||||||
|
kind: CacheFailureKind
|
||||||
|
path?: string
|
||||||
|
sha?: string
|
||||||
|
message?: string
|
||||||
|
docIds?: string[]
|
||||||
|
}
|
||||||
|
|
||||||
|
const describeError = (error: unknown): string => {
|
||||||
|
if (error instanceof Error) return error.message
|
||||||
|
return String(error)
|
||||||
|
}
|
||||||
|
|
||||||
const runWithConcurrency = async <T>(
|
const runWithConcurrency = async <T>(
|
||||||
items: T[],
|
items: T[],
|
||||||
limit: number,
|
limit: number,
|
||||||
@@ -33,7 +53,13 @@ export const useOfflineNotes = () => {
|
|||||||
const totalOfNotes = computed(() => store.files.length)
|
const totalOfNotes = computed(() => store.files.length)
|
||||||
|
|
||||||
const noteCompleted = ref(0)
|
const noteCompleted = ref(0)
|
||||||
const failedNotes = ref(0)
|
const failures = ref<CacheFailure[]>([])
|
||||||
|
const failedNotes = computed(() => failures.value.length)
|
||||||
|
|
||||||
|
const recordFailure = (failure: CacheFailure) => {
|
||||||
|
failures.value.push(failure)
|
||||||
|
console.warn("[offline-cache] failure", failure)
|
||||||
|
}
|
||||||
|
|
||||||
const cacheAllNotes = async () => {
|
const cacheAllNotes = async () => {
|
||||||
const isInitialized = store.user && store.repo && totalOfNotes.value > 0
|
const isInitialized = store.user && store.repo && totalOfNotes.value > 0
|
||||||
@@ -56,13 +82,23 @@ export const useOfflineNotes = () => {
|
|||||||
)
|
)
|
||||||
|
|
||||||
noteCompleted.value = store.files.length - filesToFetch.length
|
noteCompleted.value = store.files.length - filesToFetch.length
|
||||||
failedNotes.value = 0
|
failures.value = []
|
||||||
|
|
||||||
const pendingDocs: Note[] = []
|
const pendingDocs: Note[] = []
|
||||||
const flush = async () => {
|
const flush = async () => {
|
||||||
if (!pendingDocs.length) return
|
if (!pendingDocs.length) return
|
||||||
const batch = pendingDocs.splice(0, pendingDocs.length)
|
const batch = pendingDocs.splice(0, pendingDocs.length)
|
||||||
|
try {
|
||||||
await data.bulkUpdate(batch)
|
await data.bulkUpdate(batch)
|
||||||
|
} catch (error) {
|
||||||
|
recordFailure({
|
||||||
|
kind: "save",
|
||||||
|
message: describeError(error),
|
||||||
|
docIds: batch
|
||||||
|
.map((doc) => doc._id)
|
||||||
|
.filter((id): id is string => Boolean(id))
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const fetchWork = runWithConcurrency(
|
const fetchWork = runWithConcurrency(
|
||||||
@@ -75,22 +111,45 @@ export const useOfflineNotes = () => {
|
|||||||
store.repo,
|
store.repo,
|
||||||
file.sha!
|
file.sha!
|
||||||
)
|
)
|
||||||
if (!content) {
|
if (content === null) {
|
||||||
failedNotes.value++
|
recordFailure({
|
||||||
|
kind: "fetch",
|
||||||
|
path: file.path,
|
||||||
|
sha: file.sha!
|
||||||
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
pendingDocs.push(...buildNoteDocs(file.sha!, file.path, content))
|
let docs: Note[]
|
||||||
|
try {
|
||||||
|
docs = buildNoteDocs(file.sha!, file.path, content)
|
||||||
|
} catch (error) {
|
||||||
|
recordFailure({
|
||||||
|
kind: "build",
|
||||||
|
path: file.path,
|
||||||
|
sha: file.sha!,
|
||||||
|
message: describeError(error)
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
pendingDocs.push(...docs)
|
||||||
if (pendingDocs.length >= BULK_FLUSH_SIZE) {
|
if (pendingDocs.length >= BULK_FLUSH_SIZE) {
|
||||||
await flush()
|
await flush()
|
||||||
}
|
}
|
||||||
|
} catch (error) {
|
||||||
|
recordFailure({
|
||||||
|
kind: "unknown",
|
||||||
|
path: file.path,
|
||||||
|
sha: file.sha,
|
||||||
|
message: describeError(error)
|
||||||
|
})
|
||||||
} finally {
|
} finally {
|
||||||
noteCompleted.value++
|
noteCompleted.value++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
const readmeWork = getMainReadme(store.user, store.repo).catch(() => {
|
const readmeWork = getMainReadme(store.user, store.repo).catch((error) => {
|
||||||
failedNotes.value++
|
recordFailure({ kind: "readme", message: describeError(error) })
|
||||||
})
|
})
|
||||||
|
|
||||||
await Promise.all([fetchWork, readmeWork])
|
await Promise.all([fetchWork, readmeWork])
|
||||||
@@ -105,6 +164,7 @@ export const useOfflineNotes = () => {
|
|||||||
isLoading,
|
isLoading,
|
||||||
totalOfNotes,
|
totalOfNotes,
|
||||||
noteCompleted,
|
noteCompleted,
|
||||||
failedNotes
|
failedNotes,
|
||||||
|
failures
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user