Repository navigation
Conversation
…-v1.2.6 # Conflicts: # App/backend/src/services/agent-source-service.ts # App/backend/src/services/tests/agent-source-service.test.ts
| }); | ||
| active.add(task); | ||
| } | ||
| if (remaining <= 0 || !this.deps.memoryAddEnabled() || this.stopping || failedDrain || execution.signal?.aborted || |
There was a problem hiding this comment.
更新仍要求向量任务在第二轮执行的测试断言
问题: 当前 head 的 npm run smoke:memory-layer:test 为 2 项通过、1 项失败,错误为 expected [] to include 'embedding';指定 base 同一命令为 3 项通过。这是旧测试没有适配新设计,不是已证实的向量生成或检索故障。
原因: 旧测试要求第一次调用 worker 执行摘要,第二次调用返回向量任务。新设计在预算尚有剩余时连续补位,摘要产生的向量任务已经在第一次调用中执行完,第二次返回空是正常结果。使用真实 SQLite、MemoryService 和模拟模型的补充验证确认,摘要、向量生成及最终检索均正常。
建议做法: 只调整测试及相关断言,不修改产品调度逻辑。不再要求向量任务固定出现在第二轮,保留摘要成功、向量成功、记忆可读取并可检索的完整验证,并确认后续调用不会重复处理已完成任务。更新后重跑该测试及相关 smoke 入口,后续脚本原有的基线失败单独记录。
| const paused = this.deps.memoryBudgetPaused(); | ||
| const filters = JSON.stringify([retryOnly, paused, this.deps.memoryBudgetModelSources(), this.summaryJobsHeld(), | ||
| this.deps.configurationFingerprint?.()]); | ||
| if (!scan.more || scanVersion !== admissionVersion || scanFilters !== filters || |
There was a problem hiding this comment.
无关资源释放反复重置扫描,导致可运行任务长期得不到调度
关联位置:
- 当前文件 581 行的资源释放订阅;
- worker-concurrency.ts (line 327) 的释放通知。
问题: 大量历史任务需要的模型服务并发名额已满时,后面的任务即使使用其他可用资源,也可能长期轮不到执行。
使用真实 SQLite、WorkerRunner、并发控制器及受控业务替身的测试中,20,000 条受阻任务后的可运行任务,在无关请求持续完成的 15 秒内始终没有开始;停止干扰后又等待约 4.20 秒才执行。换成使用独立 Provider 的演化任务仍复现。无关请求降到每秒一次、持续 5 秒时,目标也要等到约 8.22 秒才开始。有限测试证明了干扰期间没有进展;相同干扰持续存在时,代码路径可能持续阻止任务被调度。
原因: 当前已经能解析任务需要哪些资源,也会检查 Memmy 自己维护的资源占用和并发上限。问题不在于缺少资源感知,而在于释放通知没有告诉扫描器具体哪项资源发生变化。
任何资源释放都可能增加 admissionVersion,随后清除整个扫描进度。每次最多检查 64 个候选,无关请求不断结束就会让扫描反复回到队首。15 秒内累计检查 64,512 次,却始终没有越过前 192 条。Aging 只能帮助已经读到的候选,无法解决这个问题。诊断性保留扫描进度后,干扰仍在持续,任务约 4.02 秒就开始了。
建议做法:
保留五类加权轮询、同类可运行 FIFO、并发限制和先取得许可再领取任务的设计,调整共用的扫描及资源通知逻辑:
- 保留向后检查的进度,无关资源释放不能使整个扫描归零。
- 记录阻止任务运行的具体资源;资源状态未变化时,避免反复检查已确认受阻的任务。
- 相关资源释放后重新考虑这些任务,保留原 FIFO 位置,不能移到队尾或永久跳过。
- 全局槽位、Provider、Embedding、同目标互斥等限制采用一致规则;真正的配置、分类或可运行条件变化仍需正确重新检查。
回归应验证:持续干扰期间可运行任务有进展;原资源释放后旧任务恢复;多资源任务不突破并发上限;开启 Aging 后仍正常。诊断性取消重置只证明根因,不能代替完整修复。
| ${queue === "main" ? "q.job_type, q.payload_json" : "'embedding_retry' AS job_type, '{}' AS payload_json"}, | ||
| m.agent_id, m.memory_layer, m.tags_json, | ||
| json_extract(m.properties_json, '$.internal_info.plugin_algorithm') AS plugin_algorithm, | ||
| json_extract(m.properties_json, '$.internal_info.capture_origin') AS capture_origin |
There was a problem hiding this comment.
分类已有效仍在每次补位时扫描大量积压,显著增加调度开销
关联位置:
- fair-worker-queue.ts (line 215) 的重复调用;
- queue-scheduling-schema.ts (line 46) 的分类失效触发条件。
问题: 即便所有任务的分类已经有效,调度器仍重复进行昂贵检查。使用立即完成的业务替身,固定只处理前 1,000 条,初始队列为 1,000/10,000/50,000 条时,耗时分别为 1.53/3.07/10.68 秒。
50,000 条场景没有一条分类需要更新,分类查询仍调用 278 次、耗时 7.54 秒,占总耗时约 71%;事件循环延迟 p99 为 121.90 毫秒,影响同进程处理新任务及其他请求的及时性。
原因: 分类检查放在每次领取切片的路径中,没有变化也会反复执行。SQL 先通过子查询找出大量可运行任务,再判断分类版本是否过期;maxRows=64 只限制返回或更新条数,不能限制子查询的扫描规模。此外,普通记忆属性更新也可能使关联任务被标记为需要重新检查分类,触发范围过宽。
这不是独立的分类守护进程,而是繁忙队列反复进入同一检查路径。隔离测试加入“没有过期分类”的前置判断后,总耗时降到 3.10 秒,领取的 1,000 条任务及顺序完全相同,支持这段检查是主要额外成本。
建议做法:
将分类维护放到明确事件上:
- 新任务创建时确定并保存分类。
- 软件升级且分类规则版本确实变化时,对相关旧任务进行分批、可恢复的补齐。
- 用户主动操作需要复用并提升已有任务待遇时,定向更新该任务。
- 普通补位使用已保存的有效分类,普通记忆属性更新不应宽泛地使关联任务重新分类。
同步核对主队列、向量重试、去重合并及旧数据写入入口。确有缺失或过期分类时,只处理这些记录,不能遗漏,也不能恢复为每次全队列扫描。
同一个未完成任务被复用时,应保留已获得的较高执行待遇。例如,历史任务因用户主动重试而提升后,后续普通扫描不能将其降回历史导入类,首次报告待遇也应保留。应按明确的业务提升规则合并,不能把轮询权重直接当作优先级,也不能将待遇永久继承给以后新建的独立任务。这是修复约束,不是另一个已经完整复现的缺陷。
回归应覆盖不同积压规模下的同预算成本、升级后旧分类补齐、主队列和向量重试不遗漏,以及“后台创建→用户主动提升→后台再次复用”不降级且保留 FIFO 位置。上述性能数据衡量的是调度开销,不能直接外推为真实云模型吞吐。
| mainSeen: [], retrySeen: [], mainDone: false, retryDone: false, agingSkippedLanes: [] }; | ||
| let inspected = 0; | ||
| const inspectLimit = scan ? FAIR_SCAN_CANDIDATE_LIMIT : Number.POSITIVE_INFINITY; | ||
| const saveScan = (more: boolean) => { |
There was a problem hiding this comment.
受阻任务仍被反复遍历,持续到来的实时任务等待过长(部分解决)
1. 问题是什么
大量历史任务因模型资源没有空闲名额而受阻时,调度器反复遍历这些任务,拖慢了使用其他空闲资源的实时任务。这是上轮 CR-002 尚未彻底修复的部分。
复审 head 的受控测试中,50,000 条历史任务保持受阻,每 100 毫秒加入一条执行耗时为 1 毫秒的实时任务;5 秒共加入 49 条,到第 7 秒只完成 15 条,34 条尚未开始。已开始任务平均等待 2.18 秒、最长 4.65 秒。相同输入、仅 1 条受阻历史任务时,49 条全部完成,最长等待 9.49 毫秒。测试使用真实 SQLite 和调度器,业务及模型调用为替身。
2. 已经修了什么
已经按具体阻塞资源保存等待任务,并在相关资源释放后重新纳入候选。无关资源释放不再清空正在向后扫描的进度;已知受阻的任务也不再反复解析资源、申请许可。上轮“扫描总被打断,无法找到后面的可运行任务”的路径已修复。
3. 还有什么没修
扫描位置没有跨正常补位完整保留:本次槽位填满或扫描结束时,saveScan(false) 仍会丢弃扫描位置,下一次补位从队首重新读取。虽然随后能认出“这条任务已经受阻”并跳过,但分页读取和逐条选择仍然重复发生。只增加计数器的复跑记录了 202,496 次命中已知受阻记录的重复检查;现有只检查资源解析次数的测试无法发现这部分开销。
4. 应该怎么修
沿用现有加权轮询、可运行 FIFO、并发限制和资源等待队列,完善共用扫描逻辑与 WorkerRunner 的等待状态。分别保存“扫描到哪里”“是否立即继续扫描”“是否仍有任务等待资源”,使用现有稳定入队序号保存各类别、各队列的扫描位置;正常补位或扫描结束时,不要无条件从头重扫。
资源释放后,让对应等待任务按原入队顺序重新参与调度,并重新检查全部执行条件、取得全部许可后才领取任务。如果还受另一项资源阻挡,就继续等待该资源,同时允许后面的可运行任务被考虑;资源释放不等于直接执行,也不能让一个仍受阻的队首卡住其他可运行任务。
新任务入队后,应能从已扫描范围之后发现它;已完成或删除的任务应从等待记录中清除或在重新检查时识别失效。较早任务到期、失败退避结束、租约过期,以及配置或分类变化时,应重新检查受影响任务,必要时重建扫描状态;服务重启后也应重新发现待执行任务。因此,保留扫描位置不是“永远只扫一次”,也不要求因正常入队、出队新增周期性全量扫描。不能通过永久跳过旧任务或提前结束后台处理来减少重扫。
回归应覆盖 10,000/50,000 条稳定受阻积压中持续加入使用空闲资源的实时任务,核对等待时间、未开始数量和候选分页/检查次数,并保留资源恢复后的原 FIFO、多资源再次受阻、新增/删除/到期任务、配置变化、有限预算和停止后的许可释放验证。
问题描述
历史导入、启动摘要修复和全量向量重建会产生大量后台任务。原调度依赖严格优先级,向量重试仅使用普通任务领取后的剩余名额,可能导致反思、向量重试等任务长时间无法执行。批量预领取及等待整批完成的执行方式,也会延长新任务的等待时间。
主要修改
队列调度
统一并发控制和连续补位
自动处理、手动执行、重试和扫描共用并发控制。任务取得全部许可后才领取租约;任务完成后立即补位,不等待整批慢任务。
limit=100表示单次执行预算,不代表同时运行或提前领取 100 个任务。总并发配置为 1 时保留后台执行能力,避免预留规则使后台永远无法执行。租约和失败恢复
运行观测
记录各类任务等待时间、完成情况、并发占用、模型请求、租约恢复及准入拒绝原因,并增加后台占用和实时预留容量指标。
联调兼容修复
审核反馈修复
最新验证(2026-10-27)
最终提交:
9673ccf015ef8b8e30d576ff0301b4f9761e33a2。已通过 merge 同步upstream/v1.2.6(0074a33b31a909b7964b8c56254945467eb9cfdc)。上游新增的导入状态测试已按实际租约过期和退避到期条件适配,保留“不误报处理中任务”的断言,并验证有效租约不可被重启回收。npm run lint、npm run typecheck、完整npm test全部通过。包内持续入队测试:真实 SQLite 和 WorkerRunner,模型与业务执行使用受控替身;每 100 毫秒加入一条实时任务,共 49 条,每条模拟执行 1 毫秒。
三组均无未开始的实时任务;首次发现结束后的额外 10 次空闲唤醒没有重复读取或检查积压候选。这些数值衡量本机受控调度行为,不代表真实模型吞吐量。
安装包 Memory buildId:
144b8912daea74babceb58b80f54eb6a6c6cf517db179f415921254e55e26b97。DMG SHA256:
11d28ab02080210bbe4431d3884c7c5689010d2e3088b41a73ca7379a396238a。版本沿用 Desktop 1.2.4、Memory 2.1.7、schema 13;分支名与应用版本号不同。
验证范围