在软件开发中,数据库的版本控制和迁移是一个复杂而关键的任务。在本文中,我们将详细介绍在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文件。