关键修复: 不再直读WAL活库(会撞checkpoint报malformed), 改VACUUM INTO每5分钟一致性快照; 前端数据加载失败显式提示+重试; 看板显示快照时间
This commit is contained in:
@@ -45,8 +45,34 @@ udb.exec(`CREATE TABLE IF NOT EXISTS users (
|
||||
)`);
|
||||
const userCount = () => udb.prepare('SELECT COUNT(*) c FROM users').get().c;
|
||||
|
||||
// ---------- 招聘库(只读) ----------
|
||||
const rdb = new Database(RECRUITMENT_DB, { readonly: true });
|
||||
// ---------- 招聘库(读快照,不直读活库) ----------
|
||||
// 活库是 WAL 模式且被 Hermes 持续写入,直接单文件挂载读会在 checkpoint 期间报
|
||||
// "database disk image is malformed"。改为定期 VACUUM INTO 生成一致性快照再读。
|
||||
const RECRUITMENT_SRC = process.env.RECRUITMENT_SRC || '';
|
||||
const SNAP_PATH = RECRUITMENT_DB; // 读取目标=快照文件
|
||||
let snapshotAt = null;
|
||||
function makeSnapshot() {
|
||||
if (!RECRUITMENT_SRC) return; // 未配置源=直接读单文件模式(仅测试)
|
||||
const tmp = SNAP_PATH + '.tmp';
|
||||
try {
|
||||
const src = new Database(RECRUITMENT_SRC, { readonly: true });
|
||||
fs.rmSync(tmp, { force: true });
|
||||
src.exec(`VACUUM INTO '${tmp}'`);
|
||||
src.close();
|
||||
fs.renameSync(tmp, SNAP_PATH);
|
||||
snapshotAt = new Date().toISOString();
|
||||
console.log(`[snapshot] ok ${snapshotAt}`);
|
||||
} catch (e) {
|
||||
console.log(`[snapshot] failed(继续用旧快照): ${String(e.message).slice(0, 120)}`);
|
||||
}
|
||||
}
|
||||
if (RECRUITMENT_SRC) {
|
||||
makeSnapshot(); // 启动先做一次
|
||||
setInterval(makeSnapshot, 5 * 60 * 1000).unref(); // 每5分钟刷新
|
||||
} else {
|
||||
snapshotAt = fs.existsSync(SNAP_PATH) ? fs.statSync(SNAP_PATH).mtime.toISOString() : null;
|
||||
}
|
||||
const rdb = new Database(SNAP_PATH, { readonly: true });
|
||||
|
||||
// ---------- 会话(内存态,重启需重登) ----------
|
||||
const sessions = new Map(); // sid -> { uid, role, expires }
|
||||
@@ -122,6 +148,7 @@ function requireSuper(req, res, next) {
|
||||
app.get('/api/stats', requireAuth, (req, res) => {
|
||||
const today = new Date(Date.now() + 8 * 3600e3).toISOString().slice(0, 10);
|
||||
res.json({
|
||||
snapshotAt,
|
||||
total: rdb.prepare('SELECT COUNT(*) c FROM candidates').get().c,
|
||||
today: rdb.prepare("SELECT COUNT(*) c FROM candidates WHERE scraped_at >= ?").get(today + 'T00:00').c,
|
||||
greetedTotal: rdb.prepare('SELECT COUNT(*) c FROM candidates WHERE greeted = 1').get().c,
|
||||
|
||||
Reference in New Issue
Block a user