fix: 缓存项目存储轮询并降低频率

This commit is contained in:
JOJO 2025-12-16 16:35:58 +08:00
parent fc88a22272
commit e27bc62ca7
2 changed files with 50 additions and 1 deletions

View File

@ -52,6 +52,9 @@ const buildDefaultQuota = (): UsageQuota => ({
role: 'user'
});
const PROJECT_STORAGE_POLL_INTERVAL_MS = 60_000;
const PROJECT_STORAGE_POLL_INTERVAL_MAX_MS = 300_000;
export const useResourceStore = defineStore('resource', {
state: () => ({
tokenPanelCollapsed: true,
@ -75,6 +78,9 @@ export const useResourceStore = defineStore('resource', {
lastContainerSample: null as ContainerSample | null,
containerStatsTimer: null as ReturnType<typeof setInterval> | null,
projectStorageTimer: null as ReturnType<typeof setInterval> | null,
projectStorageInFlight: false,
projectStorageFailCount: 0,
projectStorageIntervalMs: PROJECT_STORAGE_POLL_INTERVAL_MS,
usageQuota: buildDefaultQuota(),
usageQuotaTimer: null as ReturnType<typeof setInterval> | null
}),
@ -225,6 +231,10 @@ export const useResourceStore = defineStore('resource', {
}
},
async pollProjectStorage() {
if (this.projectStorageInFlight) {
return;
}
this.projectStorageInFlight = true;
try {
const resp = await fetch('/api/project-storage');
if (!resp.ok) {
@ -236,9 +246,26 @@ export const useResourceStore = defineStore('resource', {
this.projectStorage.limit_bytes = data.data.limit_bytes ?? null;
this.projectStorage.limit_label = data.data.limit_label || '';
this.projectStorage.usage_percent = data.data.usage_percent ?? null;
this.projectStorageFailCount = 0;
if (this.projectStorageIntervalMs > PROJECT_STORAGE_POLL_INTERVAL_MS) {
this._restartProjectStorageTimer(PROJECT_STORAGE_POLL_INTERVAL_MS);
}
}
} catch (err) {
this.projectStorageFailCount += 1;
console.warn('获取存储信息失败:', err);
if (this.projectStorageFailCount >= 3) {
const slower = Math.min(
this.projectStorageIntervalMs * 2,
PROJECT_STORAGE_POLL_INTERVAL_MAX_MS
);
if (slower !== this.projectStorageIntervalMs) {
console.warn(`连续获取失败,存储轮询间隔调整为 ${Math.round(slower / 1000)}`);
this._restartProjectStorageTimer(slower);
}
}
} finally {
this.projectStorageInFlight = false;
}
},
startProjectStoragePolling() {
@ -248,13 +275,25 @@ export const useResourceStore = defineStore('resource', {
this.pollProjectStorage();
this.projectStorageTimer = setInterval(() => {
this.pollProjectStorage();
}, 5000);
}, this.projectStorageIntervalMs);
},
stopProjectStoragePolling() {
if (this.projectStorageTimer) {
clearInterval(this.projectStorageTimer);
this.projectStorageTimer = null;
}
this.projectStorageInFlight = false;
this.projectStorageFailCount = 0;
this.projectStorageIntervalMs = PROJECT_STORAGE_POLL_INTERVAL_MS;
},
_restartProjectStorageTimer(intervalMs: number) {
if (this.projectStorageTimer) {
clearInterval(this.projectStorageTimer);
}
this.projectStorageIntervalMs = Math.max(intervalMs, 10_000);
this.projectStorageTimer = setInterval(() => {
this.pollProjectStorage();
}, this.projectStorageIntervalMs);
},
normalizeUsageQuota(raw: any) {
const base = buildDefaultQuota();

View File

@ -172,6 +172,8 @@ CSRF_EXEMPT_PATHS = {"/api/csrf-token"}
FAILED_LOGIN_LIMIT = 5
FAILED_LOGIN_LOCK_SECONDS = 300
SOCKET_TOKEN_TTL_SECONDS = 45
PROJECT_STORAGE_CACHE: Dict[str, Dict[str, Any]] = {}
PROJECT_STORAGE_CACHE_TTL_SECONDS = float(os.environ.get("PROJECT_STORAGE_CACHE_TTL", "30"))
def sanitize_filename_preserve_unicode(filename: str) -> str:
@ -1147,6 +1149,10 @@ def get_container_status_api(terminal: WebTerminal, workspace: UserWorkspace, us
@with_terminal
def get_project_storage(terminal: WebTerminal, workspace: UserWorkspace, username: str):
"""获取项目目录占用情况,供前端轮询。"""
now = time.time()
cache_entry = PROJECT_STORAGE_CACHE.get(username)
if cache_entry and (now - cache_entry.get("ts", 0)) < PROJECT_STORAGE_CACHE_TTL_SECONDS:
return jsonify({"success": True, "data": cache_entry["data"]})
try:
file_manager = getattr(terminal, 'file_manager', None)
if not file_manager:
@ -1160,8 +1166,12 @@ def get_project_storage(terminal: WebTerminal, workspace: UserWorkspace, usernam
"limit_label": f"{PROJECT_MAX_STORAGE_MB}MB" if PROJECT_MAX_STORAGE_MB else "未限制",
"usage_percent": usage_percent
}
PROJECT_STORAGE_CACHE[username] = {"ts": now, "data": data}
return jsonify({"success": True, "data": data})
except Exception as exc:
stale = PROJECT_STORAGE_CACHE.get(username)
if stale:
return jsonify({"success": True, "data": stale.get("data"), "stale": True}), 200
return jsonify({"success": False, "error": str(exc)}), 500