dsh-large-proj-perf 1.3.3 → 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +43 -0
- package/README.md +3 -3
- package/docs/COMPATIBILITY.md +72 -0
- package/lib/patches/zero-copy-fork.js +9 -2
- package/lib/runtime.js +1 -1
- package/lib/warmup.js +13 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -9,6 +9,49 @@
|
|
|
9
9
|
|
|
10
10
|
## [Unreleased]
|
|
11
11
|
|
|
12
|
+
## [1.4.0] — 2026-11-06
|
|
13
|
+
|
|
14
|
+
面向 **dsh 0.2.2-rc.1** 的兼容版本。这是 0.2.x 线上的实质变更版本(上一版
|
|
15
|
+
0.2.1-rc.3→rc.4→rc.5 均为纯版本刷新,`dsh-session` 源码零变化)。
|
|
16
|
+
|
|
17
|
+
### Fixed
|
|
18
|
+
|
|
19
|
+
- **零拷贝 fork 的 `parentSession` 与原生分歧**:0.2.2 起原生 `fork()` 不再给子会话
|
|
20
|
+
写 `parentSession`(0.1.7–0.2.1 均写)。插件原本无条件写入,导致同一 dsh 版本内
|
|
21
|
+
「走零拷贝」与「回退原生」产出的 header 不同,下游按该字段识别分支会话的逻辑
|
|
22
|
+
时灵时不灵。现按原生源码探测(`nativeSetsParentSession`),两代都与原生一致。
|
|
23
|
+
- **预热对 fork 子会话的缓存回填在 0.2.2 静默失效**:原判断
|
|
24
|
+
`header.parentSession !== undefined` 在 0.2.2 下恒假。改为
|
|
25
|
+
`parentSession !== undefined || inheritedEventCount > 0`,覆盖 fork 与 resume
|
|
26
|
+
两类带继承前缀的会话。
|
|
27
|
+
- **预热遍历已释放的投影单元**:0.2.2 起 `registrations` 保留 `refs === 0` 的 key
|
|
28
|
+
但不再被 drive/snapshot/restore 读取,预热它们纯属浪费。现跳过这类条目
|
|
29
|
+
(老版本无该字段,按活跃处理)。
|
|
30
|
+
|
|
31
|
+
### Changed
|
|
32
|
+
|
|
33
|
+
- 已知版本列表加入 `0.2.1-rc.4`、`0.2.1-rc.5`、`0.2.2-rc.1`;支持范围上限更新为
|
|
34
|
+
`0.2.2-rc.1`。
|
|
35
|
+
- 测试适配 0.2.2 的投影 API:`register()` 变异步且读 `ctx.fiber`,
|
|
36
|
+
`drive()` 会广播 `eventListeners`;e2e 脚手架的 mock ctx 相应补齐。
|
|
37
|
+
- `fork` 相关断言从「写死 `parentSession`」改为「与同版本原生行为一致」,
|
|
38
|
+
避免上游行为变化时误报。
|
|
39
|
+
|
|
40
|
+
### Compatibility notes(0.2.2 的三处收紧)
|
|
41
|
+
|
|
42
|
+
详见 `docs/COMPATIBILITY.md`:
|
|
43
|
+
|
|
44
|
+
1. **append 生命周期**:store 拥有的 Session 在 `session/created` 派发完成前禁止
|
|
45
|
+
append;`prepare()` 后处于只读态,`announce()` 成功才解锁。本插件的零拷贝 fork
|
|
46
|
+
走 `create()`(prepare→enter→announce 同步完成),不受影响。
|
|
47
|
+
2. **`parentSession` 移除**:见上。
|
|
48
|
+
3. **投影读路径重构**:`snapshot()` 不再全量物化,只计算被请求的 key;新增
|
|
49
|
+
`refs === 0` 语义、`summaryKeys()`、`onEventChanged()`。本插件不调用
|
|
50
|
+
`register()`,预热写入的 cell 仍被 `snapshot()` 采用(实测零冷折叠)。
|
|
51
|
+
|
|
52
|
+
另:`dsh-app-boot` 新增 `dsh.bundle.platforms` 平台门禁(可选声明)。本插件为纯
|
|
53
|
+
JS、无平台限定,保持不声明。
|
|
54
|
+
|
|
12
55
|
## [1.3.3] — 2026-10-24
|
|
13
56
|
|
|
14
57
|
面向 **dsh 0.2.1-rc.3** 的兼容版本。rc.3 是**纯版本刷新**:17 个关键包
|
package/README.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# dsh-large-proj-perf
|
|
2
2
|
|
|
3
|
-
[]()
|
|
4
|
+
[]()
|
|
5
5
|
[](LICENSE)
|
|
6
6
|
|
|
7
7
|
DSH(DeepSeek Harness)大会话性能插件。消除 fork、历史会话加载、超大会话落盘对
|
|
@@ -28,7 +28,7 @@ DSH(DeepSeek Harness)大会话性能插件。消除 fork、历史会话加
|
|
|
28
28
|
|
|
29
29
|
## 安装
|
|
30
30
|
|
|
31
|
-
要求 Node **≥ 22.15.0**(`node:zlib` 的 zstd 接口),dsh `0.1.0-rc.6` ~ `0.2.
|
|
31
|
+
要求 Node **≥ 22.15.0**(`node:zlib` 的 zstd 接口),dsh `0.1.0-rc.6` ~ `0.2.2-rc.1`。
|
|
32
32
|
|
|
33
33
|
```sh
|
|
34
34
|
# 从 GitHub 安装(推荐)
|
package/docs/COMPATIBILITY.md
CHANGED
|
@@ -21,6 +21,78 @@
|
|
|
21
21
|
| 0.2.1-rc.1 | **门禁新增 `engines.dsh` 且独占生效**;jsonl 后端改为 cordis 插件形态(只导出 `name`/`apply`/`Config`);persistence 改为 `ctx.effect` 资源生命周期 + `listConcurrency` 限流;`dsh-session` 新增 `appendPluginRecord`/`pluginRecordOf`;投影 `register()` 直接返回 effect。**fork 通道与四个补丁点源码零变化** |
|
|
22
22
|
| 0.2.1-rc.2 | **纯版本刷新**:13 个关键包 rc.1→rc.2 逐字节相同。唯一实质变更在 `dsh-web-app/cordis.patch.yml`(新增 `ui-deepseek-model-label-1017` 条目),不触及任何作用点 |
|
|
23
23
|
| 0.2.1-rc.3 | **纯版本刷新**:17 个关键包 rc.2→rc.3 逐字节相同(含 `dsh-session`、三个 persistence/projection 包、`dsh-app-boot`、`dsh-web-app`、`dsh-plugin-manager`、`dsh-session-format-*`、`dsh-working-directory`、`dsh-agent-preset-registry` 等);依赖集 86 项无增删;305 个已装包全部对齐 rc.3。插件侧仅需登记版本 |
|
|
24
|
+
| 0.2.1-rc.4 / rc.5 | `dsh-session` 源码零变化(rc.3 起逐字节相同)。插件侧仅需登记版本 |
|
|
25
|
+
| 0.2.2-rc.1 | **破坏性变更(已适配)**,见下节「0.2.2 的三处收紧」 |
|
|
26
|
+
|
|
27
|
+
## 0.2.2-rc.1 的三处收紧
|
|
28
|
+
|
|
29
|
+
0.2.2 是 0.2.x 线的实质变更版本,三处都影响了本插件:
|
|
30
|
+
|
|
31
|
+
### 1. append 生命周期收紧(`dsh-session`)
|
|
32
|
+
|
|
33
|
+
store 拥有的 Session 在 **`session/created` 派发完成前禁止 append**:
|
|
34
|
+
|
|
35
|
+
```js
|
|
36
|
+
publicationEntry() {
|
|
37
|
+
const entry = attachments.get(this)
|
|
38
|
+
if (entry === null) throw new Error(`session "${this.id}" cannot append outside a SessionStore`)
|
|
39
|
+
if (entry !== void 0 && entry.announcement !== 'complete')
|
|
40
|
+
throw new Error(`session "${this.id}" cannot append before session/created completes`)
|
|
41
|
+
...
|
|
42
|
+
}
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
`prepare()` / `create()` 现在会 `attachments.set(session, null)`——未进入 store 的
|
|
46
|
+
store-owned 会话处于「只读」状态;`enter()` 后是 `pending`,`announce()` 成功才
|
|
47
|
+
转 `complete`;`announce()` 抛错则转 `failed` 且不可重试。
|
|
48
|
+
|
|
49
|
+
**对本插件**:零拷贝 fork 走 `create(childId, {...})`,其内部 `prepare → enter →
|
|
50
|
+
announce` 是同一条同步链路,announce 在 `create` 返回前完成,因此不受影响。
|
|
51
|
+
但任何「先 `prepare`、跨 await 再 `announce`」的写法都会失败。
|
|
52
|
+
|
|
53
|
+
### 2. 原生 fork 不再写 `parentSession`
|
|
54
|
+
|
|
55
|
+
```js
|
|
56
|
+
// 0.1.7 – 0.2.1
|
|
57
|
+
meta: { cwd, parentSession: liveSource.id, isSeeded: true }
|
|
58
|
+
// 0.2.2+
|
|
59
|
+
meta: { cwd, isSeeded: true }
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
字段本身在 header 类型里仍存在,只是原生 fork 不再自动设置。
|
|
63
|
+
|
|
64
|
+
**对本插件的影响与修复**:
|
|
65
|
+
|
|
66
|
+
- `lib/patches/zero-copy-fork.js` 原本无条件写 `parentSession`,导致同一版本内
|
|
67
|
+
「走零拷贝」与「回退原生」产出的 header 不同。现改为按原生源码探测:
|
|
68
|
+
`nativeSetsParentSession = String(originalFork).includes('parentSession: liveSource.id')`,
|
|
69
|
+
两代都产出与原生一致的结果。
|
|
70
|
+
- `lib/warmup.js` 原本用 `header.parentSession !== undefined` 判断 fork 子会话以
|
|
71
|
+
触发缓存回填;0.2.2 下该判断恒假,回填会静默失效。现改为
|
|
72
|
+
`parentSession !== undefined || inheritedEventCount > 0`,覆盖两代。
|
|
73
|
+
|
|
74
|
+
### 3. 投影注册与读路径重构(`dsh-session-projection`)
|
|
75
|
+
|
|
76
|
+
| 变化 | 说明 |
|
|
77
|
+
|---|---|
|
|
78
|
+
| `register()` 变异步 | 返回 thenable + disposer;内部 `acquire()` 走 async,读 `ctx.fiber.assertActive()` |
|
|
79
|
+
| 新增 `refs === 0` 语义 | 最后一个使用者释放后 key 保留在 `registrations` 里但**不参与** drive / snapshot / restore / checkpoint |
|
|
80
|
+
| `snapshot()` 不再全量物化 | 原实现先 `materializeCells(session)` 再读;现在只 `cellFor()` 被请求的 key,Host-only 单元不再被附带折叠 |
|
|
81
|
+
| 新增 `summaryKeys()` / `onEventChanged()` | 客户端摘要 key 集合与事件级变更订阅;`wire.summary !== false` 决定是否进摘要 |
|
|
82
|
+
| `cachedSnapshot()` 标记废弃 | 只返回别处已算过的值,不得用于决策 |
|
|
83
|
+
|
|
84
|
+
**对本插件**:我们**不调用** `registry.register()`(只用内部 `registrations` /
|
|
85
|
+
`cells` / `buildCell`),所以异步化不影响。预热写入 `registration.cells` 的 cell
|
|
86
|
+
仍被 `snapshot()` 采用(实测零冷折叠),语义有效。但需注意:
|
|
87
|
+
|
|
88
|
+
- 预热循环应跳过 `registration.refs === 0` 的条目(已释放的单元不再被读取);
|
|
89
|
+
- `snapshot(session, keys)` 传 keys 时只算所选单元,预热价值集中在「被请求的 key」。
|
|
90
|
+
|
|
91
|
+
### 4. 新增平台门禁(`dsh-app-boot`)
|
|
92
|
+
|
|
93
|
+
`dsh.bundle.platforms` 是新增的可选声明(Node 平台名数组)。声明后,运行平台不在
|
|
94
|
+
列表内即抛 `UnsupportedBundlePlatformError` 并跳过该 bundle。本插件未声明——它是
|
|
95
|
+
纯 JS、无平台限定,保持不声明即可在所有平台加载。
|
|
24
96
|
|
|
25
97
|
## 0.1.3-alpha.2 的关键变化
|
|
26
98
|
|
|
@@ -73,6 +73,13 @@ export function install(pc) {
|
|
|
73
73
|
|
|
74
74
|
const protocolLabel = forkSeedProtocol ? '0.1.7 forkSeed' : newProtocol ? '0.1.3 eventState' : oldProtocol ? 'alpha.5 seedSource' : 'unknown'
|
|
75
75
|
|
|
76
|
+
// 上游 fork 是否给子会话写 parentSession。0.2.2 起原生 fork 不再设置该
|
|
77
|
+
// 字段(0.1.7–0.2.1 均设置),补丁必须与同版本原生行为一致——否则同一个
|
|
78
|
+
// 上游版本会因是否走零拷贝而产出不同的 header,下游按 parentSession 识别
|
|
79
|
+
// 分支会话的逻辑(含本插件 warmup 的 fork 缓存回填)就会时灵时不灵。
|
|
80
|
+
const forkSource = String(originalFork)
|
|
81
|
+
const nativeSetsParentSession = forkSource.includes('parentSession: liveSource.id')
|
|
82
|
+
|
|
76
83
|
const patchedFork = function (source, boundary, childSessionId) {
|
|
77
84
|
const capable = typeof this._resolveForkSource === 'function'
|
|
78
85
|
&& typeof this.prepare === 'function'
|
|
@@ -139,7 +146,7 @@ export function install(pc) {
|
|
|
139
146
|
inheritedEventCount: sessionLogOffset(cut),
|
|
140
147
|
meta: {
|
|
141
148
|
...(liveSource.header.cwd !== void 0 ? { cwd: liveSource.header.cwd } : {}),
|
|
142
|
-
parentSession: liveSource.id,
|
|
149
|
+
...(nativeSetsParentSession ? { parentSession: liveSource.id } : {}),
|
|
143
150
|
isSeeded: true,
|
|
144
151
|
},
|
|
145
152
|
seedSource: 'persistence',
|
|
@@ -162,7 +169,7 @@ export function install(pc) {
|
|
|
162
169
|
id: childId,
|
|
163
170
|
createdAt: Date.now(),
|
|
164
171
|
...(liveSource.header.cwd !== void 0 ? { cwd: liveSource.header.cwd } : {}),
|
|
165
|
-
parentSession: liveSource.id,
|
|
172
|
+
...(nativeSetsParentSession ? { parentSession: liveSource.id } : {}),
|
|
166
173
|
isSeeded: true,
|
|
167
174
|
},
|
|
168
175
|
eventState: 'shared-frozen',
|
package/lib/runtime.js
CHANGED
|
@@ -10,7 +10,7 @@ export const TAG = '[dsh-perf]'
|
|
|
10
10
|
|
|
11
11
|
// 插件开发/验证过的 dsh 版本。运行时探针检测到列表外版本时打告警,并提示跑
|
|
12
12
|
// tests/verify_compat.mjs 确认补丁特征是否仍匹配(特征校验会自动跳过,但会静默失效)。
|
|
13
|
-
export const KNOWN_DSH_VERSIONS = ['0.1.0-rc.6', '0.1.0-rc.7', '0.1.0-rc.8', '0.1.1-rc.1', '0.1.1-rc.2', '0.1.2-alpha.5', '0.1.2-rc.1', '0.1.3-alpha.2', '0.1.5-alpha.1', '0.1.5-rc.1', '0.1.5-rc.2', '0.1.7-rc.1', '0.1.7-rc.2', '0.2.0-rc.2', '0.2.1-rc.1', '0.2.1-rc.2', '0.2.1-rc.3']
|
|
13
|
+
export const KNOWN_DSH_VERSIONS = ['0.1.0-rc.6', '0.1.0-rc.7', '0.1.0-rc.8', '0.1.1-rc.1', '0.1.1-rc.2', '0.1.2-alpha.5', '0.1.2-rc.1', '0.1.3-alpha.2', '0.1.5-alpha.1', '0.1.5-rc.1', '0.1.5-rc.2', '0.1.7-rc.1', '0.1.7-rc.2', '0.2.0-rc.2', '0.2.1-rc.1', '0.2.1-rc.2', '0.2.1-rc.3', '0.2.1-rc.4', '0.2.1-rc.5', '0.2.2-rc.1']
|
|
14
14
|
|
|
15
15
|
// cordis 环境下对缺失服务调用 ctx.get 可能抛错——统一安全取服务
|
|
16
16
|
export const safeGet = (ctx, name) => {
|
package/lib/warmup.js
CHANGED
|
@@ -109,6 +109,10 @@ export function createWarmup(pc) {
|
|
|
109
109
|
let baseSeq = -1
|
|
110
110
|
|
|
111
111
|
for (const [key, registration] of [...registrations.entries()]) {
|
|
112
|
+
// 0.2.2 起 registrations 会保留已释放(refs === 0)的 key:它们不再被
|
|
113
|
+
// drive / snapshot / restore 读取,预热它们纯属浪费。老版本没有 refs
|
|
114
|
+
// 字段(undefined),按「活跃」处理。
|
|
115
|
+
if (registration.refs === 0) continue
|
|
112
116
|
const outcome = await foldUnit(registry, session, key, registration, events, checkpointRows)
|
|
113
117
|
if (outcome.status === 'abort') return
|
|
114
118
|
baseSeq = Math.max(baseSeq, outcome.baseSeq)
|
|
@@ -118,7 +122,15 @@ export function createWarmup(pc) {
|
|
|
118
122
|
if (foldedUnits > 0) {
|
|
119
123
|
stats.warmed += 1
|
|
120
124
|
recordWarm({ t: Date.now(), id: sessionId, events: events.length, units: foldedUnits, baseSeq, ms: performance.now() - t0 })
|
|
121
|
-
|
|
125
|
+
// fork 子会话预热后回填缓存行,否则被放弃时永远没有缓存行。
|
|
126
|
+
//
|
|
127
|
+
// 识别方式:0.1.7–0.2.1 的原生 fork 在 header 写 parentSession;0.2.2 起
|
|
128
|
+
// 原生 fork 不再写该字段,只剩 `isSeeded`。两者都覆盖:isSeeded 表示
|
|
129
|
+
// 会话带继承前缀(fork / resume),这类会话正是"被放弃后重开要走
|
|
130
|
+
// readFrom(0) 全量读"的高危对象。
|
|
131
|
+
if (session.header?.parentSession !== void 0 || session.inheritedEventCount > 0) {
|
|
132
|
+
await backfillForkCacheRow(sessionId, events.length)
|
|
133
|
+
}
|
|
122
134
|
}
|
|
123
135
|
}
|
|
124
136
|
|
package/package.json
CHANGED