Skip to content

Commit fe3cb82

Browse files
committed
docs(llmdoc): record Lua VM pool bounded-overflow divergence as accepted (#91)
Issue #91 flagged that pine-go (warm + sync.Pool with STW reclaim), pine-java (unbounded ConcurrentLinkedQueue), and pine-cpp (unbounded vector + unique_ptr) have materially non-equivalent pool overflow / GC reachability semantics. Decision after fresh end-to-end measurement on 2026-06-24: accept the divergence rather than build cross-language near-equivalents. Rationale, archived in dag-engine.md alongside the other "known runtime ops risks" so future audits hit the archive directly: - Metric layer (5-tuple borrow/return/create/reuse/active) is already byte-equal across runtimes, locked by H6/M10 cross-validate - Calibrated end-to-end three-fixture × three-runtime rerun shows Java and C++ are not penalised by their unbounded pools (Java 123-127 QPS, Go 121-122, cpp 225-234) - No portable approximation of Go's "sync.Pool cleared on STW" exists in Java (SoftReference triggers differently and is JVM-only) or C++ (no GC; would need a jthread + idle-TTL background shrink). Issue #91 Option C falls in the same "isolated win, end-to-end invisible" bucket as #119 outputPool — perf-evolution-roadmap calibration fact 2 - Production has no data-driven OOM report tying back to either pool Re-open trigger documented inline: production data showing Java/cpp pool RSS growing monotonically over long-lived runs. Cross-references: - lua-backend.md gets a one-line pointer at the warm/sync.Pool section noting the divergence is pine-go specific and pointing to the archive - index.md description for dag-engine.md mentions the accepted-divergence archive so the index is still a discoverable map
1 parent e6f5982 commit fe3cb82

3 files changed

Lines changed: 20 additions & 1 deletion

File tree

llmdoc/architecture/dag-engine.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -622,6 +622,23 @@ Server 现在是 struct-based 设计:`Server` 结构体封装所有可变状
622622
- **Lua baseline global 恢复不完整 — 已修复**:Lua pool 现在在每个 state 借出时记录该 state 的基线快照,并在 Return 时执行完整 baseline restoration,而不只是删除新增 global key。这样即使请求代码覆盖了已有全局变量,也会在归还时恢复,避免跨请求污染。
623623
- **`data_parallel` 能力门由引擎加载期强制执行 — 已修复**`pine-go/pine.go``validateDataParallel` 现在在 `data_parallel > 1` 时要求算子实例实现 `ConcurrentSafe`。Apple 编译器不再维护并发安全 blocklist,只保留结构性校验(Transform + 无 `common_output`),从而把能力判定收敛到 Go 的单一事实源。`transform_normalize` 因依赖全集语义而保持未实现 `ConcurrentSafe`
624624

625+
### 接受的跨引擎设计差异
626+
627+
不是所有跨引擎差异都需要对齐。下列差异经过审计后判定为**接受的设计选择**——指标层与可观测语义对等,端到端行为持平,跨语言对齐成本不与回报匹配。文档化让下游审计直接命中而非反复独立发现(教训:`llmdoc/memory/reflections/audit-extensibility-blindspot.md`)。
628+
629+
- **Lua VM pool 上限 / GC 回收语义不对等(issue #91**:三引擎 Lua VM pool 在"pool 上限触发 + 溢出实例 GC 回收"维度实质不对等:
630+
- **pine-go**`warm []*State``minIdle=100`,强引用)+ `sync.Pool` 溢出层;溢出实例由 Go runtime 在 STW GC 中清空,常驻上界 = `minIdle + in-flight`
631+
- **pine-java**`ConcurrentLinkedQueue<Globals>``TransformByLua.java`),**无上限**,强引用永不 GC
632+
- **pine-cpp**`vector<LuaVM*> free_vms_` + `vector<unique_ptr> all_vms_``lua_pool.hpp`),**无上限**,进程生命周期持有
633+
634+
接受差异的理由:
635+
1. **指标层对等**:5 元组计数器(`borrow_count` / `reuse_count` / `create_count` / `return_count` / `active_count`)三引擎已锁字节级一致(H6/M10 cross-validate)
636+
2. **端到端持平**:2026-06-24 calibrated 三 fixture × 三引擎复测,Java(LuaJC, 无上限 pool) 123-127 QPS、Go(wangshu, 双层 pool) 121-122 QPS、cpp(LuaJIT, 无上限 pool) 225-234 QPS——Java 和 cpp 都**没有因为"无上限 pool"在端到端被惩罚**;常驻内存上限差异在 calibrated 形状下不可观测
637+
3. **跨语言机制无可移植近似**:Go 的 "sync.Pool 在 STW GC 清空"行为在 Java(SoftReference 触发条件不同 + 仅 Java 可行)和 C++(无 GC,需要 jthread + idle TTL 后台 shrink)都只能近似,不能严格等价。issue #91 的方案 C(warm/cold + idle TTL)端到端预期落 noise 带,与 #119 outputPool 同属"隔离层胜出但端到端不可见"的 perf-evolution-roadmap 校准事实 2 范畴
638+
4. **生产无 OOM 痛点**:长时间运行下 Java/cpp pool 累积致内存压力的数据驱动报告未出现
639+
640+
重启对齐的触发条件:生产出现 Java/cpp pool 长时间运行 RSS 单调爬升 / OOM 数据。届时走 issue #91 方案 C 路径(warm/cold + idle TTL + per-引擎适配机制)。
641+
625642
HTTP 路由先由 `http.ServeMux` 注册内部端点,再由内置 `httpMetricsMiddleware``pine-go/pkg/server/http_metrics.go`)作为最内层中间件包装,最后由 `server.Config.Middlewares []func(http.Handler) http.Handler` 在启动 `ListenAndServe` 前按切片顺序从外到内包装整个 handler 链。也就是说,`Middlewares[0]` 最先看到请求、最后看到响应;`nil` 或空切片时行为与旧版一致。内置 HTTP 指标中间件记录 `pine_http_requests_total``pine_http_request_duration_seconds`,且将未知路径归一化为 `_other` 以防止高基数标签。该注入点位于 server 边界层,不参与 Engine 编译、DAG 推导或配置热加载逻辑,因此业务侧可叠加访问日志、认证、限流等横切能力,而不必自行重写 Pineapple 的 reload / shutdown 框架。
626643

627644
此分离很重要:DAG 执行仅依赖请求上下文和编译后的 plan,不依赖服务器特定逻辑。

llmdoc/index.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212

1313
## architecture/
1414

15-
- `llmdoc/architecture/dag-engine.md` — 核心引擎架构:配置编译流水线、DAG 推导规则(三标记 + auto-inject 模型:ConsumesRowSet/MutatesRowSet/AdditiveWritesRowSet 标记与 item 字段自动注入)、调度模型、DataFrame 语义(含 InputFieldSpec 三态模型:Nullable/Strict/Defaulted)、算子类型约束、行集依赖行为,以及引擎级 option / 根级配置注入(含 debug nullable 三态继承)、Server struct 生命周期与 context 传播、服务端 reload 集成与 HTTP middleware 包装边界、双通道运行时观测、ExecutionError/PanicError 因果链(三运行时 cause chain parity)、资源数据型(snapshot 导出)/句柄型(borrow 借用,如 redis_connection)区分、Pine-Java 完整功能对等描述。
15+
- `llmdoc/architecture/dag-engine.md` — 核心引擎架构:配置编译流水线、DAG 推导规则(三标记 + auto-inject 模型:ConsumesRowSet/MutatesRowSet/AdditiveWritesRowSet 标记与 item 字段自动注入)、调度模型、DataFrame 语义(含 InputFieldSpec 三态模型:Nullable/Strict/Defaulted)、算子类型约束、行集依赖行为,以及引擎级 option / 根级配置注入(含 debug nullable 三态继承)、Server struct 生命周期与 context 传播、服务端 reload 集成与 HTTP middleware 包装边界、双通道运行时观测、ExecutionError/PanicError 因果链(三运行时 cause chain parity)、资源数据型(snapshot 导出)/句柄型(borrow 借用,如 redis_connection)区分、Pine-Java 完整功能对等描述、接受的跨引擎设计差异归档(如 issue #91 Lua VM pool 上限/GC 回收语义不对等:指标层 5 元组对等 + 端到端 calibrated 持平 + 生产无 OOM 痛点 + 跨语言机制无可移植近似 → 接受差异,重启触发条件为生产 RSS 单调爬升数据)
1616
- `llmdoc/architecture/apple-compiler.md` — Python DSL 架构:Flow 声明 API、SubFlow 契约声明与编译期强制(`common_input`/`common_output`/`item_input`/`item_output` 在 issue #78 落地为 subtree-scoped 字段覆盖 + 死代码校验,未声明契约的 SubFlow 自动继承外层;`required_resources` 沿用 issue #37 校验)、编译流水线(含 step 8b `_validate_subflow_contracts`)、校验规则(含 `validate_write_without_read``AdditiveWritesRowSet` 算子的同字段豁免,issue #72)、控制流降级(含 `_rename_field` Lua `_G[]` 语法处理)、资源声明处理、根级配置字段扩展路径(如 `storage_mode``log_prefix``debug`),以及 row-set 标记三元组(`consumes_row_set` / `mutates_row_set` / `additive_writes_row_set`)通过 `apple_generated/markers.py` 表填充 `OpCall`、True-OR widen 合并语义、`_apply``_add_op` 的刻意非对称(仅 `consumes_row_set` 暴露给 DSL 调用点)。
1717
- `llmdoc/architecture/pine-cpp-runtime.md` — Pine-C++ 运行时架构:作为标杆运行时的定位、错误/fixture parity 契约、CLI 与 HTTP 入口(含 HTTP/1.1 keep-alive / read-header-timeout / idle-timeout / max-body-size / middleware / graceful shutdown / 客户端断连取消 eventfd 零延迟唤醒)、codegen 入口(`-schema-json` schema 导出 + `-output` 发射完整 Apple DSL 产物集与 Go/Java 字节级一致 + `-doc-dir` 发射算子文档 markdown 与 pine-go byte-equal、`OperatorSchema.metadata` 字段显式声明、`format_g` 对 |d| > LLONG_MAX 的 UB 守卫与 Ryu/Grisu 路由点、ResourceSchema 全局注册表与 `reset_resource_schema_registry`/`reset_all_resource_registries` 拆分语义)、`metrics::Provider` 与 `resource::Manager` 对等(`ResourceValue` 数据 `Variant` XOR 句柄 `shared_ptr<void>` 双通道,数据型走 `snapshot()`、句柄型走 `borrow()`,RAII 拆除)、Frame 多态基类 + ColumnFrame/RowFrame 双物理实现(C++23,per-call 锁形态与 Go/Java 对齐、`pine::SharedMutex` 备件)、Column 类型层级、`PINE_REGISTER_OPERATOR_T` 注册模型、ValidateOutput 类型约束、NaN/Inf 校验、PanicError stacktrace、外部 stop_token 取消、ready-queue DAG 调度器(双隔离线程池 + in-degree 原子追踪)、observe_log/pine-debug 日志、Redis client 失败收敛与 SIGPIPE 守卫(`MSG_NOSIGNAL` + AUTH/SELECT close fd)+ per-command 指标 `run_command<T>` 模板(错误类型分层 known follow-up)。
1818

llmdoc/reference/lua-backend.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,8 @@ issue #8 反馈闭环:边界双拷贝(state.go:557 + wangshu.go:371,每调
8686

8787
内存上界 = `minIdle + 当前 in-flight 实例数`,与 GC 周期 / 进程 uptime 无关。
8888

89+
> 注:双层 warm/sync.Pool 是 pine-go 专属——pine-java 用无上限 `ConcurrentLinkedQueue<Globals>`、pine-cpp 用无上限 `vector<LuaVM*>` 强引用。三引擎"pool 上限 / GC 回收语义"不对等是**接受的设计选择**(issue #91),指标层 5 元组对等 + 端到端 calibrated 持平 + 生产无 OOM 痛点;详见 `llmdoc/architecture/dag-engine.md` 「接受的跨引擎设计差异」节。
90+
8991
### Baseline 重置契约(借后必须)
9092

9193
借后必须执行 baseline 重置,把脚本运行期写入的全局变量复位回 pre-warm 时的快照——script-level 全局漏到下次借用属契约违反:

0 commit comments

Comments
 (0)