在软件开发中,数据库的版本控制和迁移是一个复杂而关键的任务。在本文中,我们将详细介绍在IMBoy项目中如何应用erlang-pure-migrations这个工具来构建一个健壯的数据库迁移功能。
什么是 erlang-pure-migrations?
erlang-pure-migrations 是一个Erlang语言的强大而灵活的数据库迁移工具,它帮助开发者以纯函数的方式管理PostgreSQL或MySQL数据库的迁移;
它遵循Unix哲学,即“一切皆文件”,将数据库迁移脚本视为文件系统中的普通文件,它允许开发者使用纯SQL语句来编写迁移脚本,而无需编写任何Erlang代码;这不仅简化了迁移过程,而且提高了代码的可读性和可维护性。
https://github.com/bearmug/erlang-pure-migrations
IMBoy 项目中应用 erlang-pure-migrations
环境准备
首先,我们将erlang-pure-migrations作为依赖项添加到IMBoy项目的Makefile文件中,并确保所有开发和生产环境都包含了这个库。
DEPS += epgsql pooler pure_migrations
IMBoy使用的是PostgreSQL15+,链接数据库依赖了 epgsql;
为了安全,特别为 erlang-pure-migrations 新增了一个操作数据库的 super_account 账号,配置参考 ./config/sys.config 100行
, {super_account, #{
host => "localhost"
, username => "imboy_user"
, password => "123456"
, database => "imboy_v1"
, port => 5432
, ssl => false
, timeout => 4000
}}
通过配置 scripts_path 目录来指定脚本的存放目录
, {scripts_path, "./doc/postgresql/migrations"}
迁移脚本编写
迁移脚本以数字为前缀版本号、以.sql为文件后缀,版本号和其他部分用下划线 _ 分割,并且按照严格的顺序编号。
"<VersionNum>_<FileNameDescription>.sql"
例如:
00000000_config.sql 00000001_app_version.sql 00000002_app_ddl.sql ...
每个脚本文件中包含了数据库迁移所需的所有SQL语句。
- 以8位数字为前缀版本号(这是IMBoy项目的约定)
- 以
.sql为文件后缀
(imboy@127.0.0.1)2> string:to_integer("00000001_app_version.sql").
{1,"_app_version.sql"}
版本号前缀通过上面的代码转为int数据存在 int4(postgresql15是这样的)中,(int4数据大小范围为-2,147,483,648 to 2,147,483,647),所以版本号的最大值是 2,147,483,647
自动从0递增,相信不用考虑版本号不够的问题。
迁移流程
- 配置迁移路径:指定迁移脚本所在的文件夹路径。
- 编写迁移脚本:根据数据库变更编写SQL脚本。
- 执行迁移:使用
pure_migrations:migrate/3函数执行迁移。这个函数接受迁移路径、事务处理器和数据库查询执行处理器作为参数。
在IMBoy项目中,运行 imboy_db:migrate(). 即可()
IMBOYENV=local gmake run (imboy@127.0.0.1)1> imboy_db:migrate(). ok
以后打算添加一个 ./imboy migrate:run 的命令
事务和查询处理器
erlang-pure-migrations允许我们自定义事务和查询处理器。这意味着我们可以根据不同的数据库客户端库(如epgsql、pgsql等)来实现这些处理器,以确保迁移过程中的事务性和查询的正确执行。
IMBoy 用法如下 imboy_db:migrate():
% imboy_db:migrate().
migrate() ->
Conf = config_ds:env(super_account),
Path = config_ds:env(scripts_path),
{ok, Conn} = epgsql:connect(Conf),
MigrationCall =
pure_migrations:migrate(
Path,
fun(F) -> epgsql:with_transaction(Conn, fun(_) -> F() end) end,
fun(Q) ->
case epgsql:squery(Conn, Q) of
{ok, [{column, <<"version">>, _, _, _, _, _, _, _},
{column, <<"filename">>, _, _, _, _, _, _, _}], []} ->
[];
{ok, [{column, <<"version">>, _, _, _, _, _, _, _},
{column, <<"filename">>, _, _, _, _, _, _, _}], Data} ->
[{list_to_integer(binary_to_list(BinV)), binary_to_list(BinF)} || {BinV, BinF} <- Data];
{ok, [{column, <<"max">>, _, _, _, _, _, _, _}], [{null}]} ->
% It has to be -1 or it will get an error during initialization
-1;
{ok, [{column, <<"max">>, _, _, _, _, _, _, _}], [{N}]} ->
% The version number is stored in the int4 type and ranges from -2,147,483,648 to 2,147,483,647
list_to_integer(binary_to_list(N));
{ok, [
{column, <<"version">>, _, _, _, _, _},
{column, <<"filename">>, _, _, _, _, _}], Data} ->
[{list_to_integer(binary_to_list(BinV)), binary_to_list(BinF)} || {BinV, BinF} <- Data];
{ok, [{column, <<"max">>, _, _, _, _, _}], [{null}]} -> -1;
{ok, [{column, <<"max">>, _, _, _, _, _}], [{N}]} ->
list_to_integer(binary_to_list(N));
{ok, _, _} -> ok;
{ok, _} -> ok;
Default ->
% Match multiple SQL statements in a script
Res = priv_is_valid(Default),
% io:format("DefaultDefaultDefaultDefaultDefault ~p~n", [Default]),
case Res of
true->
ok;
_ ->
Default
end
end
end),
% ...
%% more preparation steps if needed
% ...
%% migration call
Res = MigrationCall(),
% imboy_log:debug(io:format("~p~n", [Res])),
ok = epgsql:close(Conn),
Res.
priv_is_valid(List) ->
lists:all(fun(E) ->
case E of
{ok, _} -> true;
{ok, _, _} -> true;
_ -> false
end
end, List).
更多其他,使用案例参考 https://github.com/bearmug/erlang-pure-migrations/tree/master/test
兼容性和集成
erlang-pure-migrations与多种Erlang“数据库客户端库”兼容,我们已经在IMBoy项目中成功集成了epgsql。
并且erlang-pure-migrations完全不依赖第三方库,代码变得非常轻量,并且与特定“数据库客户端库”分离,这为我们提供了灵活的选择,可以根据项目需求和团队熟悉度来选择合适的数据库客户端。
无副作用的迁移
-
erlang-pure-migrations采用了无副作用的编程范式,将所有副作用(如文件操作、数据库事务)推迟到程序的边缘。这使得迁移代码更加安全,可以在不同的环境和条件下重复执行,而不会引起副作用。 -
迁移逻辑是幂等的,可以使用相同的迁移脚本集针对同一个数据库执行多次。
-
同时迁移数据库也是事务安全的。
函数式编程抽象
erlang-pure-migrations使用了函数式编程中的一些抽象概念,如函数组合、functor应用和部分函数应用。这些抽象使得迁移逻辑更加清晰和灵活。
结语
通过在IMBoy项目中应用erlang-pure-migrations,我们成功构建了一个健壯、灵活且易于维护的数据库迁移功能。它不仅提高了开发效率,而且确保了数据库迁移的安全性和一致性。
通过这篇文章,我们希望能够帮助更多Erlang开发者了解并应用erlang-pure-migrations,以实现高效、安全的数据库迁移管理。
同时欢迎关注 IMBoy开源项目 https://gitee.com/imboy-pub
许可
IMBoy项目中应用的erlang-pure-migrations库遵循MIT许可协议,具体细节请参考LICENSE文件。


被折叠的 条评论
为什么被折叠?



