claude-code-best/src/services/rawDump/queue.ts

143 lines
3.7 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* Raw Dump 任务队列
* 主进程只写队列,独立 batch worker 顺序消费
*
* 单一队列设计csc-workq-queue.jsonl
* - 队列常驻内存,进程启动时一次性加载,后续操作均在内存中进行
* - 任务入队/移除时写文件(其他操作仅更新内存)
* - 成功则移除彻底失败attemptCount >= MAX_ATTEMPTS也移除
* - 失败但未超限attemptCount++ 后写回文件,下次 batch 重试
*/
import { promises as fs } from 'node:fs'
import os from 'node:os'
import path from 'path'
const QUEUE_FILE = path.join(
os.homedir(),
'.claude',
'raw-dump',
'csc-work-queue.jsonl',
)
const LOCK_FILE = path.join(os.homedir(), '.claude', 'raw-dump', 'csc-work-queue.lock')
export const MAX_ATTEMPTS = 4 // 最多尝试次数
export interface QueueTask {
sessionID: string
messageID: string
directory: string
enqueuedAt: number
attemptCount: number
}
// ----------- 内存中的队列 -----------
let queue: QueueTask[] = []
let queueLoaded = false
/**
* 从磁盘加载队列到内存,只在进程启动时调用一次
*/
export async function loadQueue(): Promise<void> {
if (queueLoaded) return
try {
const text = await fs.readFile(QUEUE_FILE, 'utf-8')
queue = text
.split('\n')
.filter(Boolean)
.map(line => {
try {
return JSON.parse(line) as QueueTask
} catch {
return null
}
})
.filter((t): t is QueueTask => t !== null)
} catch {
queue = []
}
queueLoaded = true
}
/**
* 将内存中的队列同步写回磁盘(仅在入队/移除时调用)
*/
export async function flushQueue(): Promise<void> {
const lines = queue.map(t => JSON.stringify(t)).join('\n') + '\n'
await fs.writeFile(QUEUE_FILE, lines, 'utf-8')
}
/**
* 将任务追加到队列(内存 + 文件)
* 写入失败被静默吞掉,不影响主流程
*/
export function enqueue(task: Omit<QueueTask, 'enqueuedAt' | 'attemptCount'>): void {
const item: QueueTask = { ...task, enqueuedAt: Date.now(), attemptCount: 0 }
queue.push(item)
// 同步写文件,不阻塞主进程 event loop
fs.writeFile(QUEUE_FILE, JSON.stringify(item) + '\n', { flag: 'a', encoding: 'utf-8' }).catch(() => {})
}
/**
* 从内存队列中移除任务(成功或彻底失败时调用)
* @param key sessionID:messageID
*/
export function removeTask(key: string): void {
queue = queue.filter(t => `${t.sessionID}:${t.messageID}` !== key)
flushQueue().catch(() => {})
}
/**
* 消费队首任务(不移除,仅返回引用)
* 用于在处理前 peek失败时自行更新 attemptCount 再写回
*/
export function peekTask(): QueueTask | undefined {
return queue[0]
}
/**
* 获取当前内存队列快照
*/
export function getQueue(): QueueTask[] {
return [...queue]
}
/**
* 尝试获取 batch worker 运行锁
* - 读取锁文件中的 PID若进程仍存在则返回 false已有 worker 在运行)
* - 若锁文件不存在或持有进程已退出,则抢占锁
*/
export async function acquireLock(): Promise<boolean> {
try {
try {
const stat = await fs.readFile(LOCK_FILE, 'utf-8')
const pid = parseInt(stat, 10)
if (!isNaN(pid) && pid !== process.pid) {
try {
process.kill(pid, 0)
return false
} catch {
// 进程已退出,可以抢占锁
}
}
} catch {
// lock 文件不存在
}
await fs.writeFile(LOCK_FILE, String(process.pid), 'utf-8')
return true
} catch {
return false
}
}
/**
* 释放 batch worker 运行锁(清空锁文件内容)
*/
export async function releaseLock(): Promise<void> {
try {
await fs.writeFile(LOCK_FILE, '', 'utf-8')
} catch {
// ignore
}
}