Sandbox 存储性能优化:NAS 选型与本地盘构建
一个新项目还没有开始修改代码,准备过程就先等了六七秒。拆开日志以后发现,其中接近一半时间花在初始 Git 推送上。
2026 年 4 月 6 日,测试环境两次新项目准备的平均耗时是 6.66 秒,负责推送的 push_remote 步骤平均用了 2.90 秒,占约 43.6%。沿着这次推送继续看,Gitea 服务端的仓库目录挂在 NAS 上,文件存储由此进入了排查范围。
后面我们又测了不同文件系统上的依赖安装和构建,遇到了“包已经缓存,安装仍然很慢”的问题。这篇文章从 Git push 开始,沿着这些测量与实现,讲到 NAS 选型,以及本地盘上的依赖和编译。
1. 先从一次 Git push 看起
1.1. 新项目为什么先要推送代码
当时的新项目初始化流程,会先把模板解压到 Sandbox,创建本地 Git 仓库并提交初始代码,再推送到 Gitea。Gitea 是保存项目 Git 仓库的服务,后续可以从这里取得已经提交的版本。只有这套准备流程完成,项目才能继续使用。
我们在预热流程,也就是 warmup 中记录了各步骤耗时。下面两条是 2026 年 4 月 6 日测试环境的成功样本,均为新项目的模板初始化路径,时间单位为秒。
| 样本 | warmup 总耗时 | init_git | push_remote | 推送步骤占比 |
|---|---|---|---|---|
| 第一次 | 6.437 | 0.050 | 2.935 | 45.6% |
| 第二次 | 6.890 | 0.047 | 2.873 | 41.7% |
| 平均 | 6.663 | 0.049 | 2.904 | 43.6% |
占比按未四舍五入的原始时间计算,平均行使用两次推送耗时之和除以两次总耗时之和。这是两个现场样本,不代表长期平均延迟。
init_git 包含建仓、配置提交身份、git add 和 git commit,这两次只花了约 50 毫秒和 47 毫秒。随后的 push_remote 则用了约 2.9 秒,等待主要集中在后一步。
这两个步骤都从沙箱代理服务发起远程执行开始计时,直到脚本返回。push_remote 执行初始代码推送,包含远程执行开销、Git 客户端处理、网络和服务端等待;前一步的本地提交不算在里面。因此,不能把它直接当作纯网络上传时间,也不能拿新项目首次提交的结果代表任意大小仓库的提交与推送。
1.2. 推送在等待什么
Git push 会向远端发送它尚未拥有的对象,并请求更新分支引用。服务端需要接收相关对象,检查这次更新是否允许,再让目标分支指向新的提交。Git push、服务端 receive-pack
因此,客户端在等待推送结果时,等待的内容还包括服务端对仓库的处理。如果仓库保存在网络文件系统上,这条路径就涉及服务端的文件操作。
当时检查 Gitea 部署配置,确认 /data 挂载了 NAS,其中保存 Gitea 的仓库数据。这条线索把存储纳入了排查,但还不能把那 2.90 秒全部归因于 NAS:我们没有在这两个样本中继续测出网络、客户端和服务端 I/O 各占多少。
接下来需要区分两个存储位置:Gitea 保存已经提交的 Git 版本,Sandbox 工作区保存当前正在修改的工程文件。 它们都可以使用 NAS,但访问发生在不同位置,优化其中一处,不会自动改变另一处。
1.3. 工程工作区也需要持久保存
Agent 在 Sandbox 中修改代码、安装依赖,再执行构建。Sandbox 是承载这些操作的隔离执行环境,里面有项目目录、Node.js 和构建工具。用户下一次回来继续修改时,执行任务的容器可能已经换了一个。
源码需要跨实例保存。例如,Agent 给游戏增加了暂停菜单,修改了 src/menu.ts,还增加了一个依赖。容器回收以后,这次修改和对应的依赖版本都需要保留下来。
后来的 NAS 工作区方案,把网络文件存储通过 NFS 协议挂载到 Sandbox 中。挂载以后,程序仍然通过普通目录访问它。新容器挂载同一个项目目录,就可以继续处理原来的工程,包括尚未提交到 Git 的修改。
这个布局很直接:源码、package.json、锁文件、node_modules 和 dist 都在同一个工程目录里,现有工具基本不用调整。持久化的问题解决了,安装和构建也随之使用了网络文件系统。
读取一个依赖文件,可能先要查找目录项、确认权限、读取文件属性,然后才是内容。客户端会缓存一部分信息;缓存未命中或需要向服务端确认时,这些操作就要经过网络。对于包含大量小文件的 node_modules,文件总量不大,也可能有很多次访问。
1.4. 第一个方案:换一种文件系统
既然安装和构建都在 NAS 上,首先可以尝试改善这条存储路径。这样不需要调整工程目录,也不用让包管理器适应新的布局。
阿里云的 NAS 有多种规格。按目前的产品划分,通用型包含容量型、高级型和性能型,极速型又分标准型与高级型。各类产品提供的容量、时延、吞吐和 IOPS 规格不同,选型需要结合项目的实际操作。通用型 NAS、极速型 NAS
| 类型 | 主要特点 | 对这个问题需要核对什么 |
|---|---|---|
| 通用容量型 NAS | 面向容量与成本较敏感的共享文件场景 | 小文件时延、实际使用容量对应的吞吐 |
| 通用高级型、性能型 NAS | 提供不同的时延、吞吐和 IOPS 规格 | 单个项目能否利用这些能力,安装与构建是否改善 |
| 极速型 NAS | 面向较低时延和较高 IOPS 的文件访问 | 当前地域、规格和计算平台的挂载支持 |
| CPFS | 并行文件存储,面向较大规模的并发 I/O | 客户端、协议服务、网络配置和接入条件 |
CPFS 也进入过我们的测试范围。不过,文件系统的性能只是选型的一部分,还要看 Sandbox 能否接入。当时 Sandbox 使用函数计算 FC,可以配置 NAS 挂载;CPFS 实验则需要先创建协议服务与导出目录,再在 ECS 上安装 CPFS-NFS 客户端并挂载导出的路径。接通 ECS,并不等于可以直接把 FC 的 NAS 地址换成 CPFS。FC 存储选型、CPFS-NFS 挂载说明
接下来需要回答的,就是换存储到底能改善多少。连续读写大文件的吞吐,不能直接换算成安装几万个依赖文件需要的时间,因此我们拿已有的前端模板执行安装和构建。
2. 换了文件系统,构建快了多少
2026 年 6 月 12 日,我们用四种前端模板做了两轮测试,表中分别记为 2D、3D、Empty3D(空的 3D 工程)、Canvas。以下时间均为秒。
这一轮测的是工程目录中的 npm install 和 npm run build,与前面的初始 Git 推送是不同负载。现有记录没有 Git push 在各文件系统上的同口径对照,下面的数据只用于比较安装和构建。命令也保留当时的 npm 口径,不改写成后来的 pnpm 生产流程。
2.1. 第一轮:容量型反而安装得更快
NAS 测试使用新加坡区域的 ecs.c9i.xlarge,4 核 8 GiB,Node.js 22.16.0、npm 10.9.2。每个项目先安装依赖,再连续构建三次。CPFS 随后补跑,也使用 Node.js 22.16.0、npm 10.9.2。
下面是第一轮记录。这轮 NAS 测试共用了 ECS 本地的 npm 缓存,性能型先跑,容量型后跑,安装条件并不完全一致。
| 项目 | 文件系统 | 安装 | 首次 build | 三次 build 中位数 | 安装+首次 build |
|---|---|---|---|---|---|
| 2D | NAS 性能型 | 29.924 | 5.840 | 4.022 | 35.764 |
| 2D | NAS 容量型 | 16.911 | 5.442 | 3.845 | 22.353 |
| 2D | CPFS | 22.881 | 3.804 | 3.507 | 26.685 |
| 3D | NAS 性能型 | 14.341 | 2.880 | 1.993 | 17.221 |
| 3D | NAS 容量型 | 8.875 | 2.718 | 1.928 | 11.593 |
| 3D | CPFS | 11.105 | 1.938 | 1.784 | 13.043 |
| Empty3D | NAS 性能型 | 5.723 | 1.442 | 1.259 | 7.165 |
| Empty3D | NAS 容量型 | 5.473 | 1.422 | 1.203 | 6.895 |
| Empty3D | CPFS | 3.080 | 1.190 | 1.140 | 4.270 |
| Canvas | NAS 性能型 | 1.301 | 1.298 | 0.982 | 2.599 |
| Canvas | NAS 容量型 | 1.260 | 1.438 | 0.972 | 2.698 |
| Canvas | CPFS | 0.852 | 0.725 | 0.620 | 1.577 |
2D 的结果最明显:容量型安装用了 16.911 秒,性能型用了 29.924 秒。检查测试过程后,发现 npm 缓存保存在 ECS 的 /root/.npm,后测的一组能够复用前面下载过的内容。
因此,这两行数字不能直接证明容量型更快。缓存是已经确认的干扰因素,但没有单独控制它做实验,也无法判断这约 13 秒的差异有多少由缓存造成。CPFS 的补跑同样没有满足后来要求的完整隔离条件。
构建数据也需要分开看。两个 NAS 的中位数比较接近,例如 3D 是 1.993 秒和 1.928 秒,Canvas 是 0.982 秒和 0.972 秒。每组只有三次连续执行,还不足以作稳定的性能排名;首次 build 紧接着安装,也不等于清空文件缓存后的冷读测试。
这轮先暴露出来的是测法的问题。要继续比较文件系统,得让每一组从独立环境开始。
2.2. 第二轮:隔离环境后再测
第二轮改成“文件系统类型 × 项目”各用一台新 ECS、一套新文件系统,避免共用 npm 缓存。规格也调整为更接近当时 Sandbox 的 2 核 4 GiB:ecs.c9i.large、Ubuntu 20.04、Node.js 22.22.3、npm 10.9.8,NAS 使用 NFSv3 挂载。
CPU 和软件版本与第一轮不同,所以这一轮内部比较,不能拿两轮数字计算升级收益。
| 项目 | 文件系统 | 安装 | build 1 / 2 / 3 | build 中位数 | 安装+首次 build |
|---|---|---|---|---|---|
| 2D | NAS 容量型 | 31.93 | 5.66 / 5.05 / 4.94 | 5.05 | 37.59 |
| 2D | NAS 性能型 | 39.64 | 5.69 / 7.50 / 5.15 | 5.69 | 45.33 |
| 3D | NAS 容量型 | 21.73 | 2.91 / 2.56 / 2.59 | 2.59 | 24.64 |
| 3D | NAS 性能型 | 21.63 | 2.84 / 2.51 / 2.46 | 2.51 | 24.47 |
| Canvas | NAS 容量型 | 1.44 | 1.28 / 0.96 / 0.92 | 0.96 | 2.72 |
| Canvas | NAS 性能型 | 1.16 | 1.16 / 0.89 / 0.87 | 0.89 | 2.32 |
有些组合没有跑完:Empty3D 在两种 NAS 上都遇到 npm install 的 ERESOLVE 依赖解析错误,项目本身声明使用 pnpm,直接以 npm 安装并不完全符合原来的依赖约束;CPFS 独立组合则停在资源与挂载准备阶段。它们没有有效构建时间,也不能用第一轮的 CPFS 数字补上。
极速型 NAS 没有参加这两轮测试。全部阶段记录,包括三次 build 和失败退出码,保留在测试数据 CSV 中。
2.3. 安装仍然占了大头
这六组成功结果,没有显示出换成性能型 NAS 后各个项目都明显变快。更值得继续追的是时间分布:2D 在容量型上的安装占“安装+首次构建”的约 85%,3D 对应约 88%。
即使只看 2D,把 5.66 秒的编译继续压缩,前面的 31.93 秒安装仍然存在。要缩短用户等待的时间,需要继续拆解依赖准备。
安装依赖会下载包,因此一个直接的方案是预热包缓存,尽量离线安装。我们后来的镜像已经这样做了,但工作区中的 node_modules 仍在 NAS 上。接下来的问题变成了:包内容已经在本地,安装还需要做哪些文件操作?
3. 包已经缓存了,安装还在做什么
3.1. 缓存与依赖树是两个位置
安装大致包含读取包与锁文件、获取包内容、建立项目依赖树,以及执行安装脚本几个过程。缓存能够复用包内容,项目自己的依赖目录仍然需要准备。
以 pnpm 为例,需要区分三个位置:
- 内容存储 store:保存可以复用的包文件;
- 项目的
node_modules/.pnpm:项目使用的依赖文件和目录布局; node_modules中的链接与入口:让程序按照依赖关系找到模块。
pnpm 使用内容存储和链接组织依赖。建立和读取项目依赖树时,仍然要操作项目目录;store 与项目分属不同文件系统时,也不能假定跨文件系统硬链接总能成立。pnpm 依赖目录结构
8 月检查构建链路时,镜像里的预热 store 已经在本地盘,安装器也支持优先离线安装。但 node_modules 和 .pnpm 还在 NAS。包可以不重新下载,目录创建、文件复制、链接与属性检查仍可能经过网络文件系统。
构建阶段也会反复查找模块、读取 package.json、解析源码,再生成临时文件和 dist。依赖留在 NAS 上时,读取依赖的成本会继续影响构建,只调整输出目录还不够。
3.2. 连查看目录也变慢了
2026 年 8 月 8 日的一次排查,进一步把范围指向了文件访问。工作区确认是 NFSv3 挂载,现场记录中,find 用了约 117 秒,git status 约 166 秒,部分目录扫描超过 60 秒。
这些命令不负责把源码编译成产物。它们也变慢,说明需要检查安装和编译之外共同使用的文件系统路径。
约 42 分钟的现场累计统计中,有下面这些 NFS 请求:
| 操作 | 次数 | 含义 |
|---|---|---|
SETATTR | 42,110 | 设置权限、时间等文件属性 |
LOOKUP | 22,871 | 查找目录中的文件或子目录 |
ACCESS | 21,662 | 检查访问权限 |
CREATE | 11,362 | 创建文件 |
WRITE | 11,583 | 写入文件内容 |
这些是多个操作累计的计数,不是一次安装的调用次数。不过,它们解释了为什么不能只看传输带宽:查目录、检查权限、创建文件,每一步都可能有等待。依赖文件多了,即使单个文件很小,这些步骤也要反复执行。
现场同时存在外部包下载慢的问题,手工执行的 pnpm 命令还曾把 store 建在 NAS 上。下载、缓存和项目目录需要分别检查,已有证据不足以把所有等待都算在 NAS 上。但对于已经确认留在 NAS 的依赖树,可以提出一个更具体的改法:把它移到本地。
4. 先把 node_modules 搬到本地
4.1. 哪些文件可以不持久保存
源码、素材和依赖约束需要保留;node_modules 则可以根据 package.json、锁文件和工具版本重新生成。它被频繁读写,却不一定需要和源码使用同一种存储。
因此,可以让 NAS 继续保存工程,把 node_modules 放到 Sandbox 本地盘。工作区里的 node_modules 变成软链接,工具访问这个路径时,实际读取本地目录。
本地盘的选择还取决于部署环境:有独立 SSD 时,可以把依赖目录放在 SSD 对应的挂载点;只有容器本地临时盘时,也可以验证同样的分工。两者的容量、性能和生命周期需要按实际环境确认,目录叫 /tmp 并不能说明底层一定是 SSD。
4.2. 实验结果:安装改善大,构建改善小
我们在 8 月评审过《低权限环境下 node_modules 本地快盘托管方案》,附录保留了一组 2026 年 6 月的实验。它先在本地目录安装依赖,再通过软链接让工作区使用这份 node_modules。
原实验中,/workspace 和 /scratch 是同一个 NFSv3 文件系统,/tmp 是约 9.9 GiB 的容器本地 overlay,没有独立本地 SSD。下面比较的是 NFS 路径与容器本地路径。
| 操作与规模 | NFS 直接使用 | 本地目录+软链接 | 两者耗时比 |
|---|---|---|---|
| 安装 express 等,70 多个依赖 | 5.3 秒 | 1.3 秒 | 4.08 |
| 安装 91 个依赖,9,164 个文件 | 54.4 秒 | 7.5 秒 | 7.25 |
| 安装 431 个依赖,22,302 个文件 | 166 秒 | 31 秒 | 5.35 |
| 431 个依赖项目的 Vite build | 6.05 秒 | 4.29 秒 | 1.41 |
在 431 个依赖的记录里,安装从 166 秒降到 31 秒;Vite build 从 6.05 秒降到 4.29 秒。移动依赖目录影响了两部分,安装阶段的变化更大。构建还包含解析、转换、压缩等 CPU 工作,不能用安装的耗时比代表整个发布流程的收益。
这份附录没有保留完整的 CPU 规格、缓存控制和重复轮数,属于方案验证资料,也不是前面几个模板的同口径复测。它提供了继续实现本地依赖目录的依据,生产项目的具体收益还需要重新测量。原记录见方案文档的实机验证附录,revision 7。
4.3. 软链接又被安装器删掉了
有了这组结果,接下来的工作是把目录布局接入现有工具。最先遇到的限制,就在依赖安装本身。
假设目录是这样的:
/workspace/game/node_modules
→ /local/game/project/node_modules
软链接保存目标路径,但包管理器不一定保留它。补充实验中的 npm 11 在安装的 reify 阶段发现 node_modules 是软链接,会移除它,再在原位置创建目录。于是,看起来已经搬到本地的依赖,又写回了 NFS。npm ci 也明确包含先清理既有 node_modules 的行为。npm ci 的行为说明
要保留本地布局,安装命令应当直接在本地工程目录执行,成功以后再由平台建立工作区链接。处理已有依赖目录前,还需要确认它属于可重建数据,并停止正在使用它的进程。
构建工具有另一类限制:部分框架检查工程根目录与依赖的实际路径,外置依赖可能触发“超出文件系统根目录”的错误。只挪 node_modules,源码和依赖仍分处两个工程根,这套结构并不一定适合所有工具。
因此,下一步把源码副本也放进本地,让源码、依赖和构建命令位于同一个工程根。NAS 继续保留原工程,本地副本负责执行这一轮构建。
5. 在本地准备一个完整工程
5.1. 两份目录如何分工
本地工程副本需要源码,但不负责替代 NAS 保存用户修改。每次构建前,从 NAS 同步本轮输入;依赖安装、临时文件和编译都在本地完成;构建成功后,再把产物交回持久层。
| 内容 | 主要保存位置 | 用途 |
|---|---|---|
源码、素材、package.json、锁文件 | NAS | 保存用户修改和下一次安装的依赖约束 |
| Git 元数据、项目运行记录 | NAS | 供提交、恢复和查询使用 |
| 本轮源码与配置的副本 | 本地工程目录 | 提供一致的构建输入与相对路径 |
| npm cache、pnpm store | 本地盘或镜像内的预热目录 | 复用包内容,缺失时可以重新获取 |
node_modules、编译缓存、构建中的 dist | 本地工程目录 | 承接安装与编译中的反复读写 |
成功后的 dist | 回写 NAS,再进入原发布流程 | 交给上传与版本管理 |
同步时排除 NAS 上的旧 node_modules、旧 dist 和无关运行目录,避免把大量可重建文件复制回来。源码中的删除也要同步,否则已经从 NAS 删除的旧文件,可能继续留在本地参与编译。
如果一个 Sandbox 承载多个项目,本地目录还要按项目隔离,防止它们修改同一份依赖。整个本地副本的生命周期跟随当前实例,实例回收后可以重建。
5.2. 一个 npm 项目怎样执行
下面用 npm 展示最基本的过程。假设项目已有有效的 package-lock.json,构建期间没有并发修改,输出目录是 dist。本地目录每次新建,暂不考虑缓存复用。
workspace=/workspace/game
local_root=$(mktemp -d /tmp/game-build.XXXXXX)
local_project="$local_root/project"
mkdir -p "$local_project"
# 同步构建输入,跳过可以重新生成的内容。
rsync -a \
--exclude='/.git' \
--exclude='/node_modules' \
--exclude='/dist' \
--exclude='/.combos' \
"$workspace/" "$local_project/"
# 安装和构建都从本地工程根开始。
(
set -e
cd "$local_project"
npm ci --cache "$local_root/npm-cache"
npm run build
)
产物出现在 $local_project/dist。平台检查命令退出状态与产物后,再把它交回持久层。这个示例说明目录关系,没有包含并发锁、缓存复用和发布过程。
已有项目使用 pnpm 时,应保留原来的锁文件与版本。采用本地目录,并不需要改用 npm 重新解析依赖。我们实际的主路径使用 sandbox-pnpm-install,先尝试离线安装,缺包时按策略联网补齐。
monorepo、本地 file: 依赖,以及会访问相邻目录的安装脚本,还要求同步完整的依赖范围。复制工程时需要保持这些相对关系,不能只带走一份 package.json。
5.3. 接入原来的构建入口
实现时,我们把本地 I/O 接入了依赖安装和构建两个统一入口,分别由 sandbox-pnpm-install 和 sandbox-build 负责,已有的持久工作区与后续发布流程继续使用。
依赖准备先检查本地目录是否可用、空间是否足够,再同步输入和安装。成功后,将工作区的 node_modules 链接到本地依赖目录。构建入口核对依赖状态,刷新源码,然后从本地工程根执行原来的 build.sh。
部分旧项目的 build.sh 会再次执行 npm ci 或 pnpm install。统一入口已经准备过依赖,如果脚本再来一遍,就会重复安装。因此,当时为这次构建进程加入命令包装,跳过约定中的重复安装命令;run build 等其他调用继续交给原来的包管理器。这项兼容处理限定在已有构建协议内。
编译成功后,先把本地 dist 复制到 NAS 的暂存目录。复制完成,再将它重命名为本次产物目录,后续上传、Git 提交和版本记录继续走原流程。
这里要分别处理编译失败和回写失败。编译失败,本轮没有可上传的结果;回写失败,即使本地编译已经完成,也还没有把产物交给发布流程。构建入口会清除旧输出,防止上传器在本轮失败后误用上一次的 dist。
暂存目录让消费者不会读到复制了一半的产物,但“编译、上传、版本发布”仍然是多个步骤,并没有组成一个文件系统事务。最终预览还要等后续发布完成。
5.4. 哪些修改需要写回
本地副本带来了一个新的边界:在本地改了文件,不代表用户的持久工程已经变化。
普通构建根据现有锁文件准备依赖。用户要求增加或删除依赖时,package.json 与锁文件的改动需要保存到 NAS,成为下次恢复的输入;只更新本地 node_modules,换一个实例就会丢失这次变化。
因此,修改依赖要作为单独的操作处理,检查并写回对应文件;写回前还应确认原文件没有被其他操作更新。构建脚本如果会生成需要长期保存的源码,也要约定这些文件的写回范围,不能将整个本地副本直接覆盖回 NAS。
到这里,安装和编译已经有了统一的本地执行路径,NAS 上保留的是持久输入与完成后的产物。但还有一个条件需要处理:本地目录只属于当前实例。用户下一次继续工作时,NAS 上的成功记录可能还在,本地文件却已经不在了。
6. 实例换了,依赖怎样恢复
6.1. 成功记录与真实目录
假设原实例已经完成依赖安装,NAS 保存了成功标记。原实例被回收后,新实例挂载同一份 NAS,可以看到旧记录,但没有原实例的本地依赖。
软链接也可能保存在 NAS 上。容器更换后,链接文件仍在,它指向的本地目录却不存在。只检查链接或“安装成功”状态,都会漏掉这个情况。
实际实现判断依赖是否就绪时,会一起核对记录是否属于当前实例、package.json 与锁文件的摘要、本地依赖目录、pnpm 布局标记,以及软链接是否指向当前目录。条件不满足,就重新建立本地副本、安装依赖、修复链接,再更新当前实例的记录。
Node.js 与包管理器版本也需要固定。如果以后把依赖缓存扩展到跨实例共享,还要考虑操作系统、CPU 架构、Node ABI 等兼容条件,尤其是包含原生扩展的包。当前实例内的一份成功记录,不能直接扩展成跨机器复用的依据。
6.2. 恢复过程还会并发执行
异步预热和用户触发的构建可能同时准备依赖。如果只给安装加锁,两个进程仍可能在前面的同步阶段相互干扰:一个正在准备本地布局,另一个同时删除或替换其中的文件。
为此,锁需要覆盖共享目录的准备过程,让源码同步、依赖安装和状态更新按照约定执行。
本地环境不可用时,还需要回到原来的 NAS 工作区。回退不能只切换工作目录:如果 node_modules 仍是指向已消失本地目录的链接,继续安装可能出现 ENOTDIR。因此,回退时还要处理失效链接,让依赖重新安装到可用的工作区目录中。
| 情况 | 处理方式 |
|---|---|
| 本地目录不可写、空间不足或布局不兼容 | 记录原因,按策略回到工作区路径 |
| 同一实例已因本地环境问题回退 | 保留该实例的回退结果,避免反复尝试同一条失败路径 |
| TypeScript 报错、源码语法错误 | 返回编译错误,由调用方修复源码 |
| 实例已重建 | 重新检查目录和依赖,允许新环境再次尝试本地模式 |
长时间保留实例时,还要限制缓存占用。检查容量也需要关注可用 inode:剩余字节足够,并不保证还能创建大量文件。
7. 下一步:重新测量完整构建
回看已有数据,可以确认两件事:当时测过的容量型与性能型 NAS 没有给所有模板带来一致的明显改善;补充实验把依赖放到本地后,安装耗时变化远大于构建耗时变化。沿着这个方向,我们把本地执行接入了统一安装、构建与恢复流程。
不过,现有测量还缺少最终生产链路在同一条件下的完整前后对照。前面 166 秒降到 31 秒的数字,不能直接作为这套完整方案的上线收益。
本地副本减少了安装与编译中的网络文件操作,也增加了源码同步和产物回写。因此,下一轮计时需要覆盖:等待执行、同步输入、依赖准备、实际编译、产物回写,以及后续上传和验证。否则,单看本地 build 变快,仍然无法判断用户少等了多久。
测试场景也要分开:新实例首次构建、同一实例重复构建、只修改源码、修改依赖,以及实例丢失后的恢复。每组固定代码、锁文件、工具版本和计算规格,记录缓存条件,核对最终发布的产物。
这样才能继续回答下一个问题:依赖和编译移到本地以后,整条链路中最长的等待又落在了哪里。