You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

VSCode Less语言服务扩展生产环境文件扫描卡顿求助

VSCode Less语言服务扩展生产环境扫描卡顿排查分析

问题背景

生产环境下,vscode-less扩展完成文件扫描并输出日志「scaning file done」耗时近10分钟,但调试环境运行正常。控制台日志显示扫描过程仅打印了少量文件路径,与实际项目文件数量不符。

核心代码片段

stream.on('file', (stat) => {
    const entry = makeEntryFile(stat.path, stat.ctime);
    // Return Cache if it exists and not outdated
    const cached = cache.get(entry.filepath);
    if (cached && cached.ctime.getTime() >= entry.ctime.getTime()) {
        listOfPromises.push(cached);
        return;
    }
    listOfPromises.push(makeSymbolsForDocument(cache, entry, settings));
});
stream.on('error', (err) => {
    if (settings.showErrors) {
        reject(err);
    }
});
stream.on('end', () => __awaiter(this, void 0, void 0, function* () {
    let projectSymbols = [];
    let importedSymbols = [];
    try {
        projectSymbols = yield Promise.all(listOfPromises);
        // on production, it takes almost ten minute to get the log done.
        (0, server_1.getConnection)().console.log('scaning file done')

        if (settings.scanImportedFiles) {
            importedSymbols = yield scannerImportedFiles(cache, projectSymbols, settings);
        }
    }
    catch (err) {
        if (settings.showErrors) {
            reject(err);
        }
    }
    resolve(projectSymbols.concat(importedSymbols));
}));

function makeSymbolsForDocument(cache, entry, settings) {
  return (0, fs_1.readFile)(entry.filepath).then((data) => {
    if (cache.keys().length % 50 == 0) {
        (0, server_1.getConnection)().console.log('scaning file ' + entry.filepath)
    }

    const doc = vscode_languageserver_1.TextDocument.create(entry.filepath, 'less', 1, data);
    const { symbols } = (0, parser_1.parseDocument)(doc, null, settings);
    symbols.ctime = entry.ctime;
    cache.set(entry.filepath, symbols);
    return symbols;
  });
}

控制台日志

scaning file businessapplication/invoice/splitInvoiceModal.less

scaning file platform/flow/workflowdrawing/ruletabs/designatedPosts.less

scaning file platform/expenseclaim/editor/component/expenseRecord/compositeAmount.less

scaning file done

可能的卡顿原因及排查方案

1. 无限制并发导致资源耗尽

代码中通过Promise.all一次性处理所有文件的读取和解析任务,生产环境文件量极大时,会瞬间占用大量CPU、IO资源,导致任务排队阻塞。调试环境因文件量小或调试器隐性限制并发,所以无此问题。

  • 排查:添加并发控制,使用类似p-limit的库限制同时执行的任务数(比如每次处理20个文件),避免一次性启动过多异步操作。
  • 验证:修改代码加入并发限制后,重新打包生产环境扩展,测试扫描耗时是否下降。

2. 缓存校验逻辑失效

缓存校验的时间比较cached.ctime.getTime() >= entry.ctime.getTime()可能存在精度问题:生产环境文件系统的时间戳精度(如毫秒级)与调试环境不同,导致大量缓存被误判为过期,所有文件被迫重新解析,大幅增加耗时。

  • 排查:在代码中添加缓存命中/未命中的统计日志,对比生产与调试环境的命中率;调整时间比较逻辑,允许1秒以内的时间差,避免精度问题导致的缓存失效。

3. 文件读取IO性能瓶颈

生产环境若使用网络存储(如NAS)或低性能磁盘,fs.readFile的异步读取延迟会被放大,大量文件的IO延迟叠加导致总耗时剧增。调试环境通常使用本地高速磁盘,所以表现正常。

  • 排查:在makeSymbolsForDocument中添加每个文件读取+解析的耗时统计,定位是否是IO环节拖慢速度;优化错误处理,补充readFile的错误捕获,避免因个别文件读取失败导致整体任务阻塞。

4. 同步日志输出阻塞任务

生产环境中,server_1.getConnection().console.log是同步IO操作,当文件数量极大时,累计的日志输出会阻塞异步任务执行。当前每处理50个文件输出一次日志,若文件总量上万,日志IO的开销不可忽视。

  • 排查:临时注释所有日志输出代码,重新打包测试扫描耗时;若耗时明显下降,将日志输出改为异步操作,或进一步降低输出频率(如每200个文件输出一次)。

5. 生产环境编译优化异常

调试环境使用未压缩代码,而生产环境代码经过webpack打包压缩,可能存在编译优化导致的逻辑异常:比如__awaiter编译错误、parseDocument函数被错误优化导致性能下降。

  • 排查:将生产环境的编译产物拿到本地调试,复现卡顿问题;对比调试与生产代码的编译差异,检查是否有函数逻辑被篡改或性能关键代码被压缩降级。

内容的提问来源于stack exchange,提问作者Jack Hu

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.07 13:35:13