dify/cli/src/cache/compat-store.ts
Stephen Zhou a84c2d36a3
style: format with vp fmt (#38803)
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-07-12 15:57:46 +00:00

68 lines
2.3 KiB
TypeScript

import type { Store } from '@/store/store'
import { CACHE_COMPAT, getCache } from '@/store/manager'
// How long a host stays "known compatible" before difyctl re-probes /_version.
export const COMPAT_TTL_MS = 60 * 60 * 1000
// Only *positive* (compatible) verdicts are cached — never "too old". A host that
// was too old is re-probed every time, so a just-upgraded server clears a previous
// block immediately instead of staying locked out for the whole TTL.
const COMPATIBLE_KEY = { key: 'compatible', default: {} as Record<string, string> } as const
export type CompatStore = {
readonly isFreshCompatible: (host: string, now?: Date) => boolean
readonly markCompatible: (host: string, now?: Date) => Promise<void>
}
export type CompatStoreOptions = {
readonly store?: Store
readonly now?: () => Date
readonly ttlMs?: number
}
export async function loadCompatStore(opts: CompatStoreOptions = {}): Promise<CompatStore> {
const store = opts.store ?? getCache(CACHE_COMPAT)
const ttlMs = opts.ttlMs ?? COMPAT_TTL_MS
const clock = opts.now ?? (() => new Date())
const memory = await readCompatible(store)
return {
isFreshCompatible: (host, now) => {
const last = memory.get(host)
if (last === undefined) return false
const elapsed = Math.max(0, (now ?? clock()).getTime() - last)
return elapsed < ttlMs
},
markCompatible: async (host, now) => {
const stamp = (now ?? clock()).getTime()
memory.set(host, stamp)
// Re-read disk inside the write cycle so concurrent processes touching
// different hosts don't clobber each other's stamps.
const onDisk = await readCompatible(store)
onDisk.set(host, stamp)
await writeCompatible(store, onDisk)
},
}
}
async function readCompatible(store: Store): Promise<Map<string, number>> {
const out = new Map<string, number>()
let raw: Record<string, string>
try {
raw = await store.get(COMPATIBLE_KEY)
} catch {
return out
}
for (const [host, iso] of Object.entries(raw)) {
const t = Date.parse(iso)
if (!Number.isNaN(t)) out.set(host, t)
}
return out
}
async function writeCompatible(store: Store, state: Map<string, number>): Promise<void> {
const compatible: Record<string, string> = {}
for (const [host, t] of state) compatible[host] = new Date(t).toISOString()
await store.set(COMPATIBLE_KEY, compatible)
}