使用Liquibase和Git的推荐工作流是什么?

15

最近我们开始使用Liquibase。虽然还没有发生,但我们想象了两个开发人员向共享的Git仓库提交更改日志文件时会发生什么。

如何解决或避免合并冲突?为了扩大这个问题:

在与Git结合使用Liquibase时,推荐的工作流程是什么?

示例场景:
- Michael更改了“customer”表中的列。
- Jacob更改了“account”表中的列。
因此,两个开发人员都向同一个更改日志文件changelog.xml添加了<changeSet>

编辑 :

正如评论所说,情景并不那么激动人心。假设Jacob是最后一个推送他的代码的人。他必须先拉取。收到警告有冲突需要解决。他通过保留Michael和自己的代码部分来解决冲突。使用Liquibase更新数据库没有问题。

高级示例场景:
-Michael将“客户”表中“名称”列的名称更改为“first_name”,提交并推送。
-Jacob将“客户”表中“名称”列的名称更改为“last_name”并提交。
-Jacob在拉取Michael的代码时遇到合并冲突。
-Jacob和Michael讨论了冲突,并同意应该是“last_name”,Jacob提交并推送。
-Michael拉取解决的冲突并运行Liquibase更新。他收到一个错误:column "name" does not exist


在这种情况下,您看到了什么问题?只要更改集不更改相同的内容,那么就应该没问题。请注意,changeSet 的唯一键是文件名 + 作者 + id。 - Puce
4个回答

8
在我的公司,我们使用liquibase的方式可以避免出现这些情况。基本上,您为每个更改创建一个单独的liquibase文件。我们根据JIRA票据命名文件以及一些描述性文本来命名这些文件。我们将这些文件放入版本系统的文件夹中;如果下一个发布版本是1.22,则在我们开始进行数据库更改时,就会创建该文件夹,并将每个liquibase文件与一个update.xml脚本放在其中。该update.xml文件最终成为唯一可能发生冲突的地方,而且它们很容易解决。
举个例子,这是src/main/liquibase文件夹:
├── install                        
│   ├── projectauthor.xml          
│   ├── project_obspriorities.xml  
│   ├── project_priorities.xml     
│   ├── project_udv.xml            
│   ├── project.xml                
│   ├── roles.xml                  
│   ├── scan.xml                   
│   ├── (the other table definitions in the system go here)
│
├── install.xml                 <-- this reads all the files in ./install
│
├── local.properties            <--
├── prod.properties             <--  these are database credentials (boo, hiss)  
├── staging.properties          <-- 
├── test.properties             <--  
│
├── update.xml                  <-- reads each version/master.xml file     
│
├── v1.16
│   ├── 2013-06-06_EVL-2240.xml
│   ├── 2013-07-01_EVL-2286-remove-invalid-name-characters.xml
│   ├── 2013-07-02_defer-coauthor-projectauthor-unique-constraint.xml
│   └── master.xml
├── v1.17
│   ├── 2013-07-19_EVL-2295.xml
│   ├── 2013-09-11_EVL-2370_otf-mosaicking.xml
│   └── master.xml
├── v1.18
│   ├── 2014-05-05_EVL-2326-remove-prerequisite-construct.xml
│   ├── 2014-06-03_EVL-2750_fix-p-band-polarizations.xml
│   └── master.xml

安装.xml文件只是一堆文件包含:
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
                   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                   xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-2.0.xsd">

    <include file="src/main/liquibase/project/install/projectauthor.xml"/>
    <include file="src/main/liquibase/project/install/project_obspriorities.xml"/>
    ...
</databaseChangeLog>

更新文件update.xml也是同样的故事:
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
                   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                   xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-2.0.xsd">
    <include file="src/main/liquibase/project/v1.18/master.xml"/>
</databaseChangeLog>

我不太喜欢的工作流程方面是,install/*.xml 应该创建与当前版本之前完全相同的数据库,但我们通常会忘记这样做。

无论如何,这种方法将使您免受许多合并困难的困扰。我们正在使用 Subversion,采用这种方法没有任何合并困难。


我并不完全理解这个工作流程。为什么install/*.xml文件应该创建与当前版本之前完全相同的数据库?如果是这样的话,那么当你安装应用程序1.18版本时,在1.18版本之前的更新.xml文件中就不应该包含master.xml文件。 - Bart Weber
省略了用于安装新数据库和升级数据库的Shell脚本,这些脚本指示Liquibase使用特定的文件。 - Daniel Lyons
@Barthelomeus,我认为我们的工作流程并不完美,但我确信这里的基本思想是正确的:将数据库更改制作成它们自己的文件。如果您需要添加一个列到表Y以完成功能X,您可以创建一个名为feature X的Liquibase文件,该文件添加该列(而不是编辑表Y创建脚本)。 - Daniel Lyons
另外,你怎么知道你的更改最终会在哪个版本中出现?如果它在拉取请求(分支)中以便包含在未来的发布中,你会将它的数据库更新归属于哪个版本? - Bradley

7
通常情况下,需要手动处理一些边缘情况,但这些情况并不经常发生。Git通常可以很好地处理文本级别的合并更改,因此合并后的文件将包含两个changeSet,一个在另一个之后。
由于Liquibase通过id/author/filename跟踪changeSet,所以Jacob的changeSet最终在Michaels之前并不重要。当两个开发人员运行最终的changeSet时,Liquibase将仅运行另一个开发人员的changeSet,因为他们的changeSet已被标记为已运行,而另一个changeSet没有被标记为已运行。对于所有其他环境,两个changeSet都将运行。
您的高级案例遇到问题,因为两个开发人员正在进行相互矛盾的更改。如果两个开发人员删除了一列或添加了一个具有相同名称的新列,则也可能遇到类似的问题。有时冲突的changeSet来自于合并的两个不同功能分支,而不仅仅是一个开发人员与另一个开发人员之间的问题。合并的changeSet本身没有问题,问题在于新的changelog不正确。这不是一个git问题,而是一个逻辑问题。
实际上,这种类型的冲突很少发生,因为不同的开发人员和不同的分支通常在代码库的不同区域上工作,并且当存在潜在冲突时,他们通过沟通和计划来处理它。
如果您遇到冲突,则有几种解决方法。通常(就像在您的示例中)通过删除不正确或重复的changeSet来处理,但也可以通过创建一个组合两者的全新changeSet来处理。无论哪种情况,都需要处理已运行“错误” changeSet的数据库。如何最好地处理取决于运行了多少个系统。
如果只有一个开发人员,有时最简单的方法是仅运行liquibase changeLogSync,将新的changeSet标记为已运行,并手动在数据库中进行更改。如果糟糕的changeSet最近被运行,他们甚至可以运行liquibase rollbackCount X来回滚他们的错误更改,然后删除changeSet,然后运行liquibase update 如果存在多个冲突和/或多个运行了问题changeSet的系统,最简单的方法通常是使用<preConditions onFail="MARK_RAN"><changeSetExecuted id=....></preConditions> 标签。您可以删除错误的changeSet并添加一个新的changeSet,仅在旧的changeSet被执行时才运行,并将数据库放回到后续changeSet所期望的状态。在您的示例中,它将first_name重命名为name,以便name到last_name的changeSet可以正常工作。

4
TL;DR: 在 .gitattributes 文件中添加 /path/to/changelogfile.xml merge=union
我看到过一些关于使用单独文件的讨论。在liquibase中,有两种方法可以使用单独文件:
1. 使用includeAll标签,并将新文件持续推送到该文件夹中。 2. 在主文件中使用include标签来指定每个单独文件的顺序。
两种方法都会导致问题:(1) includeAll 只会选择按字母顺序排列的文件名,这在每次提交新更改时执行 changelog 时效果很好,但如果您想部署到全新文件上,它将失败。除非您有一个命名策略来指定顺序。但是,在 Michael 和 Jacob 同时工作的情况下,这仍然是一个问题:他们可能会使用相同的名称,从而导致冲突。(2)Michael 和 Jacob 都需要编辑主文件的最后几行。换句话说:合并冲突!
实际解决方案不是更改您的 liquibase 策略。只需保持迄今为止所遵循的相同策略即可。这里问题的根源是源代码控制和合并机制。liquibase changelog 文件本质上是一份历史记录。将其视为发布说明文件,其中开发人员只需将他们的工作添加到文件的最后一行即可。在这种情况下,无论两个开发人员是否添加了最后的两行,它们都应该去掉,而不管顺序如何。因此,解决方法是在您的 git 项目的根文件夹中创建一个 .gitattributes 文件(如果您还没有该文件),并将以下行添加到其中: /path/to/changelogfile.xml merge=union 这仍然不是完美的解决方案。为了解决高级示例场景,我想不出任何自动策略或工作流程。开发人员应该在接触相同表格(或相同的数据库对象)时进行沟通。这种情况不仅是“文件”冲突,而且也是语义冲突。这时就需要一个好的 CI。提交和合并代码的开发人员会得到错误提示,并需要联系赢得比赛的第一个人。
请注意,也许您的 Git 门户不支持合并策略,因此在拉取请求(合并请求)上仍然会出现合并冲突。但是当您看到它并尝试合并回来或重新基于它以解决冲突时,冲突已经解决。我知道 GitLab 支持它。不确定 Github 或 BitBucket 是否支持。

0

我不知道liquidbase,但我认为错误出在“Jacob和Michael讨论了冲突并同意必须是'last_name',然后Jacob提交并推送”的步骤。

更改日志必须匹配要执行的操作(或已在数据库上执行的操作)。即不能删除已推送的更改。

因此,正确的解决方案是name->first_name AND first_name->last_name。


我同意你所说的:“changelog必须与要执行的操作(或已经在数据库上执行的操作)相匹配。即你不能删除已推送的更改。” 但是当Jacob保留两个changesets的时候,就会出现列“name”不再存在的事实。因此,Jacob无法执行Michael的changeset。对于Michael来说情况也是如此。当Jacob添加一个changeset将列“last_name”更改回“name”,以及另一个changeset将Michael的“first_name”更改为“last_name”时,Michael也会遇到同样的问题。最终,Michael在他这一侧也会遇到同样的麻烦。 - Bart Weber

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接