同事的 SNAPSHOT 更新了,我三天后才拉到:Maven 更新检查被 _remote.repositories 静默跳过

老炮踩坑录 · 番外篇 · 翻车现场系列

基于「基础企业档案」真实项目代码,复盘一次"同事 deploy 了新 SNAPSHOT,我本地怎么构建都是旧代码"的排查

关键词:SNAPSHOT · updatePolicy · maven-metadata.xml · -U · _remote.repositories

👋 欢迎阅读 🏠个人主页: 知守观

📘我的专栏: 老炮踩坑录

💻当前内容:Maven 更新踩坑

前言

番外篇讲了改完 common 模块忘了 install、打出来的包是旧逻辑的事。install 解决的是本地源码到本地仓库这一段。本地仓库再往上游走,还有一段路:同事的代码到我的本地仓库,中间隔着公司私服。

这一段我翻过车,翻了三天。

还是 base-archives 那个项目,还是 D:\Maven\repertory 那个仓库。时间线接着番外篇往后两周。

现象:他说 deploy 了,我这边没变

当时 common 模块有个 bug,行政区划编码在某种入参下处理不对,同事领了修复任务。下午四点多,他在群里说改完了,deploy 到私服了,版本号不变,还是 0.0.1-SNAPSHOT,让我本地验证一下。

我拉了他最新代码确认改动内容,然后构建我自己的工程,跑验证用例。

bug 还在。

同事的第一反应是自己没 deploy 成功,又 deploy 了一次,并截图发我,BUILD SUCCESS。我这边重新构建,结果 bug 还在。

到下班我也没搞明白。第二天我继续折腾,-U 也用了,还是没用。第三天上午我把本地仓库里 common 的目录整个删了,重新构建,新逻辑出来了。

问题解决了,但解决得莫名其妙。我把删除前备份的目录翻出来对比,才搞明白这三天卡在哪里。

排查:clean、强制更新、等,挨个试了一遍

mvn clean 和这件事没关系

第一天下班前,我执行得最多的命令是 mvn clean package。直觉上"没拿到新代码"就该 clean 一下。

clean 只清各模块的 target 目录,编译产物删掉重来。本地仓库在 D:\Maven\repertory,clean 根本不碰它------执行完 clean,本地仓库里的 jar 一个都不会少。

这么想的不止我一个,一起排查的同事当时也建议我"再 clean 一次试试"。

第二天:daily 策略该过期了,还是没变

当晚我回家想了下,SNAPSHOT 有个更新检查策略 updatePolicy,默认 daily,24 小时内不重复检查远程。第一天的现象用它解释得通:我早上构建过一次,检查过了,下午同事 deploy,还在 24 小时窗口内,不检查,继续用本地缓存。

那睡一觉到第二天,超过 24 小时,总该检查了吧。

第二天上午构建,bug 还在。

-U 强制更新,日志里什么动静都没有

不等了,上强制参数:

go 复制代码
mvn clean package -U -DskipTests

-U 即 --update-snapshots,跳过 updatePolicy 的时间窗口,每次都检查远程 SNAPSHOT 有没有更新。

构建成功。跑验证,还是旧行为,bug还在。

回头看构建日志,关于 common 这个依赖,一行下载记录都没有。正常情况下 -U 检测到远程有更新,日志里会有 Downloading 和 Downloaded。我这里连 metadata 的下载记录都没有,Maven 压根没去远程问。

到这一步可以确定:问题不在时间策略,在更新前面的某个环节,Maven 直接跳过了整个更新检查。

定位:本地仓库目录下的三个文件

去 D:\Maven\repertory\cn\xiaomayi\base-archives-common\0.0.1-SNAPSHOT 看,SNAPSHOT 目录下除了 jar 和 pom,还有几个平时注意不到的文件:

csharp 复制代码
maven-metadata-nexus.xml
maven-metadata-nexus.xml.sha1
resolver-status.properties
_remote.repositories
base-archives-common-0.0.1-SNAPSHOT.jar
base-archives-common-0.0.1-SNAPSHOT.pom

挨个打开看。

maven-metadata-nexus.xml 是远程私服的元数据缓存,记录这个 SNAPSHOT 当前最新版本的 timestamp 和 buildNumber。里面的时间戳是上周的------同事 deploy 的新版本信息根本没出现在这里,坐实了"Maven 没去远程访问过"。

resolver-status.properties 记录上次检查时间:

ini 复制代码
maven-metadata-nexus.xml.lastUpdated=1665478800000

daily 策略就靠它判断要不要重新检查。用 -U 之后这个时间戳刷新了,说明检查动作本身执行了,但检查完没去下载。

_remote.repositories 的内容是关键:

csharp 复制代码
base-archives-common-0.0.1-SNAPSHOT.jar>old-nexus=
base-archives-common-0.0.1-SNAPSHOT.pom>old-nexus=

先说下这个文件的格式。每行一条登记信息:文件名>仓库id=。> 只是个分隔符,没有特殊含义;= 右边固定为空。这两行读作:jar 和 pom 是从 id 为 old-nexus 的仓库下载的。本地 mvn install 进去的 artifact,仓库 id 的位置是空的,形如 xxx.jar>=。

我打开当时用的 settings.xml,里面 mirror 配置的 id 是 nexus。

对不上。

原因:更新检查之前,还有一道来源校验

_remote.repositories 是 Maven 3 引入的本地仓库管理机制。每个从远程下载的 artifact,都会在这个文件里记录它是从哪个仓库 id 下载的。本地 mvn install 进去的,> 后面是空的。

Maven 对一个本地已有的 artifact 做更新检查之前,先看这个文件:标记的来源仓库 id,在不在当前构建配置的仓库列表里。在,正常走更新流程。不在,Maven 认为这个 artifact 和当前配置的仓库对不上号,整个更新检查跳过------日志不打,错误不报,静默用旧的。

两周前公司迁移私服,老 Nexus 下线,换了新地址。我按 wiki 换了 settings.xml,新配置的仓库 id 从 old-nexus 改成了 nexus。本地仓库里之前下载的所有 artifact,_remote.repositories 里记的还是 old-nexus。

两边的账从那天起就对不上了。大部分依赖是 release 版本,不更新,不触发检查,相安无事。common 这个 SNAPSHOT 天天在变,第一个撞上。

完整的更新链路,按顺序是四步:

flowchart TD A[开始构建] --> B[步骤1: 检查 updatePolicy 和 resolver-status.properties] B -->|未过期且无 -U| C[使用本地缓存, 结束] B -->|已过期或 -U| D[步骤2: 检查 _remote.repositories 来源仓库 id] D -->|id 不在当前配置| E[静默跳过更新, 使用本地旧 jar, 结束] D -->|id 匹配| F[步骤3: 下载远程 maven-metadata.xml 对比 timestamp] F -->|远程无更新| G[使用本地缓存, 结束] F -->|远程有更新| H[步骤4: 下载新 jar] H --> I[构建成功, 使用新代码]

网上的文章基本只讲第一步和第三步。第二步出事的时候,-U 绕得过时间窗口,绕不过来源校验。

另外补一个相关场景:如果你本地对 common 执行过 mvn install,_remote.repositories 里 > 后面是空的本机标记,Maven 把它当本地产物,同样不检查远程更新。自己 install 过一次,把同事 deploy 的版本晾在一边,这种翻车方式也常见。

这套匹配规则的具体行为跟 Maven 版本有关,我在 3.6.3 上验证的是"静默跳过更新"。其他版本是拒绝使用还是报警告,我没逐个试过,不替它们下结论。

修复

彻底的办法:删掉本地仓库里对应的模块目录,重新下载。新下载的 artifact 会用当前的仓库 id 重新登记。

swift 复制代码
Remove-Item -Recurse -Force "D:\Maven\repertory\cn\xiaomayi\base-archives-common"

临时应急 :-llr(--legacy-local-repository),让 Maven 按 Maven 2 的老规则用本地仓库,忽略 _remote.repositories 的来源校验。

go 复制代码
mvn clean package -U -llr -DskipTests

这个参数适合救急,不适合长期用------来源校验本身防的是"仓库串号"的问题,长期绕开会把本地仓库搞乱。

还有一个教训层面的做法:迁移私服的时候,settings.xml 里仓库 id 保持不变,只改 url。id 一变,本地所有存量缓存的登记信息全部作废,坑就埋下了。

这个坑为什么难排查

全程没有任何报错。-U 执行了,BUILD SUCCESS,日志干净。Maven 跳过一个内部流程的时候没有任何提示,从输出上看,一切正常。

排查方向容易被第一天的时间差带偏。updatePolicy daily 是个众所周知的点,第一天的现象又正好符合它,第二天、第三天还用这个思路去解释,就越走越远。

_remote.repositories 这个文件几乎没人提。它藏在每个 artifact 目录里,纯文本,机制写在 Maven 源码里,官方文档里基本没提它。出问题之前,我不知道它的存在,出问题之后搜中文资料,讲它的文章也不多。

Maven 跳过一个内部流程的时候没有任何提示,从输出上看,一切正常。

自查清单

老炮点评

Maven 的缓存设计是层层"能不动就不动":时间窗口挡一次,来源校验挡一次,本地 install 再挡一次。每一层单独看都合理,叠在一起,"同事的代码三天到不了我手上"就是真实会发生的事。

排查缓存类问题,别满足于"换了个参数好了"或者"删了重下好了"。好了之后回头把机制搞清楚,下次再遇到不一样的症状,才不用又花三天。

上一篇番外篇讲了多模块没install导致打包还是旧代码,这篇讲SNAPSHOT拉不到,两篇都是Maven仓库机制的坑。


下期预告:《mirrorOf=* 把私服请求拦去了公网镜像》

settings.xml 里抄来一行 mirrorOf=*,所有仓库请求都被镜像接管,包括公司私服。本地能下载的依赖,CI 上 404;私服里明明有的包,Maven 偏偏去公网镜像找。

下期讲 mirror 的匹配规则,以及 ! 排除写法为什么一定要会。

如果本文对你有帮助,欢迎:

👍 点赞 | ⭐ 收藏 | 👤 关注| 💬 留言

我是老炮,Java 老兵,仍在一线。关注「Java老炮踩坑录」,看真实案例,少踩坑。

相关推荐
用户0942485680344 分钟前
第37章:JVM极端场景调优与生产故障复盘方法论
java·jvm
Wang's Blog1 小时前
Java 项目实战: 交友项目 - 项目模块划分与依赖关系
java·开发语言·交友
努力进修1 小时前
时序数据长期承载难题:从压缩、批量写入到数据组织优化
java·开发语言·数据库
斑鸠喳喳1 小时前
交换器 Exchanger
后端
霸道流氓气质1 小时前
MinIO 入门与实战:S3 兼容对象存储、Spring Boot 集成与 AI 应用文件管理
人工智能·spring boot·后端
我真是泰库辣1 小时前
我写了 300 行 Python,让代码替我在《三角洲行动》里吹口琴
后端
CaptainT1 小时前
先把例子跑起来:MySQL 环境与本文的示例数据
数据库·后端·mysql
Dovis(誓平步青云)1 小时前
总用电下降了,为什么家庭能源看板仍然提示异常
java·开发语言·数据库·人工智能·算法·电脑·能源
程序猿乐锅1 小时前
【从0-1学习Python | 第四篇】Python 字符串和元组
java·开发语言·笔记·后端·python·学习