mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 07:10:43 +00:00
fix(sessions): enforce maintenance by default and prune on load to prevent gateway OOM
Co-authored-by: bobrenze-bot <bobrenze-ops@gmail.com>
This commit is contained in:
committed by
Peter Steinberger
parent
b5f25de352
commit
6a21962552
@@ -7,6 +7,7 @@ Docs: https://docs.openclaw.ai
|
||||
### Changes
|
||||
|
||||
- Models/costs: support tiered model pricing from cached catalogs and configured models, and include bundled Moonshot Kimi K2.6/K2.5 cost estimates for token-usage reports. (#67605) Thanks @sliverp.
|
||||
- Sessions/Maintenance: enforce the built-in entry cap and age prune by default, and prune oversized stores at load time so accumulated cron/executor session backlogs cannot OOM the gateway before the write path runs. (#69404) Thanks @bobrenze-bot.
|
||||
- Plugins/tests: reuse plugin loader alias and Jiti config resolution across repeated same-context loads, reducing import-heavy test overhead. (#69316) Thanks @amknight.
|
||||
- Cron: split runtime execution state into `jobs-state.json` so `jobs.json` stays stable for git-tracked job definitions. (#63105) Thanks @Feelw00.
|
||||
- Agents/compaction: send opt-in start and completion notices during context compaction. (#67830) Thanks @feniix.
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import fs from "node:fs";
|
||||
import { createSubsystemLogger } from "../../logging/subsystem.js";
|
||||
import { normalizeSessionDeliveryFields } from "../../utils/delivery-context.shared.js";
|
||||
import { getFileStatSnapshot } from "../cache-utils.js";
|
||||
import {
|
||||
@@ -8,12 +9,15 @@ import {
|
||||
writeSessionStoreCache,
|
||||
} from "./store-cache.js";
|
||||
import { applySessionStoreMigrations } from "./store-migrations.js";
|
||||
import { capEntryCount, pruneStaleEntries, resolveMaintenanceConfig } from "./store-maintenance.js";
|
||||
import { normalizeSessionRuntimeModelFields, type SessionEntry } from "./types.js";
|
||||
|
||||
export type LoadSessionStoreOptions = {
|
||||
skipCache?: boolean;
|
||||
};
|
||||
|
||||
const log = createSubsystemLogger("sessions/store");
|
||||
|
||||
function isSessionStoreRecord(value: unknown): value is Record<string, SessionEntry> {
|
||||
return !!value && typeof value === "object" && !Array.isArray(value);
|
||||
}
|
||||
@@ -118,6 +122,25 @@ export function loadSessionStore(
|
||||
|
||||
applySessionStoreMigrations(store);
|
||||
normalizeSessionStore(store);
|
||||
const maintenance = resolveMaintenanceConfig();
|
||||
if (maintenance.mode === "enforce" && Object.keys(store).length > maintenance.maxEntries) {
|
||||
const beforeCount = Object.keys(store).length;
|
||||
const pruned = pruneStaleEntries(store, maintenance.pruneAfterMs, { log: false });
|
||||
const capped = capEntryCount(store, maintenance.maxEntries, { log: false });
|
||||
const afterCount = Object.keys(store).length;
|
||||
if (pruned > 0 || capped > 0) {
|
||||
serializedFromDisk = undefined;
|
||||
setSerializedSessionStore(storePath, undefined);
|
||||
log.info("applied load-time maintenance to oversized session store", {
|
||||
storePath,
|
||||
before: beforeCount,
|
||||
after: afterCount,
|
||||
pruned,
|
||||
capped,
|
||||
maxEntries: maintenance.maxEntries,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (!opts.skipCache && isSessionStoreCacheEnabled()) {
|
||||
writeSessionStoreCache({
|
||||
|
||||
@@ -13,7 +13,7 @@ const log = createSubsystemLogger("sessions/store");
|
||||
const DEFAULT_SESSION_PRUNE_AFTER_MS = 30 * 24 * 60 * 60 * 1000;
|
||||
const DEFAULT_SESSION_MAX_ENTRIES = 500;
|
||||
const DEFAULT_SESSION_ROTATE_BYTES = 10_485_760; // 10 MB
|
||||
const DEFAULT_SESSION_MAINTENANCE_MODE: SessionMaintenanceMode = "warn";
|
||||
const DEFAULT_SESSION_MAINTENANCE_MODE: SessionMaintenanceMode = "enforce";
|
||||
const DEFAULT_SESSION_DISK_BUDGET_HIGH_WATER_RATIO = 0.8;
|
||||
|
||||
export type SessionMaintenanceWarning = {
|
||||
|
||||
Reference in New Issue
Block a user