作者 | 何蔚
一、背景
在这个数字化时代,企业的复杂业务逻辑运转需要依赖复杂的业务服务来完成。这些业务服务通常会经历变更、拆分、合并和上云等过程,最终与一些商业软件和云平台深度融合。
以之前服务过的客户为例,他们的系统多年来一直在.Net生态和Azure云上运行,并与微软系数据库系统进行绑定。但是,随着市场的变化,客户想要摆脱对单一商业软件和云平台的依赖,以便在续约谈判中争取更多优惠,而不是被厂商随意操纵。他们面临的其中一个挑战是必须将数据库系统迁移到PostgreSQL,以节省许可费用并迁移到更优惠的云平台。
二、技术挑战
在过去十几年中,该客户在SQL Server积累了大量的用户数据、系统数据,业务代码和测试代码也是面向SQL Server和SQL Server Compact(SQL CE)编写的。我们为客户梳理出如下的技术挑战:
- T-SQL转换
- 自动化测试数据的迁移
- 高效加载测试数据
三、T-SQL转换
T-SQL转换的具体策略需要从以下几个角度来综合考量:
- 交付计划
- T-SQL的形态
- T-SQL的数量
1.交付计划
业务侧的用户数据是否迭代迁移、开发侧的代码能否迭代修改,将会直接决定T-SQL转换的交付计划,也会决定有几种方言的SQL会同时存在。
以我们的客户为例,各个产品线十多年的代码混杂在一起,难以清晰拆分。此外,用户数据量庞大,迁移至新数据库系统需要耗费数月时间。因此,我们采取了一次性交付代码的策略,并同时支持对两种数据库系统(多方言SQL)的访问。
2.T-SQL的形态
以我们的客户为例,T-SQL以两种形态存在于代码库中:
- XML资源文件(resx)中的完整T-SQL
- 代码逻辑中的T-SQL片段
为了实现多方言SQL的切换并根据用户数据动态访问不同的数据库系统,我们基于.Net的XML资源文件设计了以下流程。
在客户已有上下文和开发流程下,这个T-SQL改写流程具有以下优点:
- 采用客户开发人员熟悉的XML资源文件机制,降低理解和推广的成本。
- 不引入额外的工具库即可达到切换SQL方言的功能,减少了改造的隐形成本,如升级老旧的库、框架带来的连锁升级问题。
- Resx文件之间的单向覆盖,减少了需维护SQL的总数量,同时方便扩展至其它方言SQL。
- 对原始SQL文件不做改动,从而避免对运行中的业务造成影响。
- 运行时的SQL方言由用户数据动态决定,待用户数据全部迁移后,原始T-SQL和原始Embeded T-SQL可以直接删除,无须再修改代码。
3.T-SQL的数量
如果SQL的总数量较少,可以考虑手动改写,因为开发自动化工具不一定划算。
在我们的案例中,需要在一个交付周期内转换超过600个SQL,长度甚至达到数十行,如果手动改写不仅费时,而且容易出错。因此,我们团队为客户量身定制了转换工具,集成了第三方开源库JOOQ。该工具可以直接读取资源文件中的SQL语句,自动逐条转换,并生成PostgreSQL版的资源文件。开发人员将代码中的SQL整理到资源文件后,使用该工具转换SQL的平均速度可以达到每条1-2秒。
特别强调,在企业中使用第三方开源库和框架,必须根据开源许可证确认其允许商业使用。否则,将会给企业带来法律风险。
四、自动化测试数据的迁移
完善的自动化测试是一张安全网,帮助企业第一时间发现破坏性修改。当SQL从一种方言转换到另一种方言之后,基于旧数据库系统运行的测试,对于新方言SQL就不再适用。为多种数据库系统而维护几套业务逻辑完全相同的测试,会极大增加测试的维护成本。而且随着时间的推移,多套测试数据将会变得不再完全一致。
想要将同一套测试运行在两种不同的数据库系统上面,并且只维护一套测试数据,可以尝试下面的方法:
- 定下测试数据的单一来源 (SSOT)。
- 开发或者使用一个命令行工具,配合流水线自动转换测试数据文件。
- 改造已有的自动化测试,可以通过参数决定使用哪种数据库文件运行自动化测试。
- 配合流水线在新数据库系统上运行已有全部测试用例。
五、高效加载测试数据
为了避免因数据更改导致的测试随机失败,集成测试和端到端测必须清理/恢复被修改的测试数据。对于像 SQL CE 这样的文件型数据库系统,每个测试套件复制数据文件的时间成本是可以接受的。但是,对于像 PostgreSQL 这样的服务器数据库系统,每个测试套件导入数据文件的时间成本比简单复制文件更长,累积成本变得不可接受。
1.使用模板数据库
为了加速测试,我们在PostgreSQL上采用模板数据库(Template Database)。同时把数据文件的Hash片段作为Database的名字,测试框架代码就能判断这份数据文件是否已经被导入过。倘若已导入,则跳过导入步骤,直接在PostgreSQL内复制一份数据库供测试使用。
更进一步,对于只做查询的测试用例,甚至可以跳过复制数据库,在“模板数据库”上直接运行测试用例,这样能进一步减少准备数据的时间开销。缺点就是需要谨慎维护“只读”测试用例,避免混入会修改数据的测试用例。
2.回收存储空间
随着测试的运行,废弃的测试数据会占用越来越多的存储空间。采取什么样的方法进行清理,可以依据测试数据库系统是统一维护,还是安装在测试Agent上来决定。
针对统一维护的测试数据库系统,可以创建一条夜间运行流水线去清除特定名称的数据库。也可以让每个测试集在测试完成时删除各自用过的数据库。
针对安装在测试Agent上的测试数据库系统,可以创建CronJob来清除数据库。如果测试Agent是早上自动创建、晚上自动销毁的虚拟机,则无须引入清理步骤。
六、写在最后
更换大型系统所使用的数据库系统,注定不是简单的事情。不仅要考虑框架、代码等具体的技术、基础设施,还要考虑测试、甚至企业部门之间的配合等诸多方面。具体的策略、步骤、任务数量多少,都是由企业和系统所处情况来决定的。