yzprofile's Notebook

All Posts| Note| Books| About

网上针对 Erlang 可以热升级的特性是各种吹捧,真的有那么好用吗?

在一个关于 Erlang 的邮件列表里有一段关于热升级的讨论:

[erlang-questions] Best practice to hot update .hrl

Q:

OTP 设计原则里针对如何做发布讲了很多。但是没见针对如何升级 `.hrl` 的说明。
举个例子,如何处理下列的情况:

1. 添加一个新的 Record
2. 从 Record 里添加/删除一个元素
3. 宏变了

A:

`.hrl` 文件里的定义是在预编译阶段直接插入 `.erl` 文件里的。
(我不认为 Erlang 的运行时定义了什么是"头文件",如果我错了,请指出我的错误)

所以最简单的做法就是重新编译所有依赖了更新后头文件的模块,之后 Reload 它们。

A:

1. 你需要先找出所有依赖更新后头文件的 `.erl`文件
2. 找出所有和这些 `.erl` 文件相关的进程
3. a. 挂起进程;b. 更新数据结构;c. 恢复进程

A:

头文件是直接被编译器插入 `.erl` 文件中的。
重新编译/加载所有的 `·.erl` 文件这种操作方式可以覆盖大部分简单的场景。
比如说没有进程在使用老版本的 Record/Macro。

这也是为啥所有的 `long-lived` 进程都需要使用 gen_server,
你可以使用 `code_change` 这个回调函数来对 `state` 进行转换,
但是无论你怎么做,牵扯到升级 Record 的动态更新操作起来都很傻逼。
所以,在这种情况下,有些地方最好还是使用 `proplist` 或者 `dict` 会更好一些(但是会影响性能)。


每个 Record 实际上都是一个 tuple

如果你改变了 Record 的定义,那么有可能会有个老版本的 Record 的实例在系统中正在被处理
(比如说:mnesia, 在其他节点,在处理中),你将不能使用新的 Record 访问到它的任何字段。

字典倒是没有这个问题。

最后,除非你的模块可以处理每一个版本的 Record,并且每个版本 Record 的长度都不同,
不过这个解决办法简直傻逼极了。

嗯,这也是我们在生产环境中遇到的尴尬,小时候读书少别人说热升级真是好!我们也说“好!”。

现实世界里,每一次真正意义上的发布还是乖乖的停掉服务,处理好每一处变更,之后再发布吧。

热升级也就留给修修 Bug 什么的。

(终归原因还是我比较懒,以及线上升级不停服务的需求不强烈)….

EOF

hava fun :)