flyway 跳过已预知且配置的脚本执行错误

文章讲述了如何在大型数据库升级中,使用Flyway社区版避免因执行时间过长导致的部署失败。通过配置允许的错误,捕获并忽略特定SQL错误,如表、字段冲突,确保后续脚本的执行。
摘要由CSDN通过智能技术生成
flyway 跳过已预知且配置的脚本执行错误
  • 前景: 在版本迭代中,过往的表结构可能无法支持新功能,则需要对表结构进行升级优化,拿某大型表为例,某次迭代中一定需要给该表增加一个字段,且该字段需要被设为索引,添加索引的需要数据库对现有数据进行计算维护索引数据结构, 而这张表的数据量是比较庞大的,所以这个升级的时间时比较长的.在服务部署中,flyway脚本执行时间过长可能会导致停机时间超过预期或超过部署等待时间导致被认定为部署失败而被杀掉.为了避免这几种情况的发生,会实现让运维同学将这部分脚本执行掉,但是在部署过程中因为脚本已执行导致报错,需要人为干预再重启,所以增加了该版本.通过提前配置某些允许的错误,在flyway脚本执行中能忽略这些问题,不影响后面未执行的脚本.

  • 调研过程: 通过flyway提供的文档配置后发现,该功能为收费功能,而我们使用的是社区版本,该功能智能自己做实现.

    • Flyway Pro Edition or Flyway Enterprise Edition upgrade required: errorOverrides is not supported by Flyway Community Edition.
  • 实现过程: 通过某段flyway报错信息总结出,flyway的异常是在 at org.flywaydb.core.internal.command.DbMigrate.doMigrateGroup(DbMigrate.java:370)抛出的,

    •  private void doMigrateGroup(LinkedHashMap<MigrationInfoImpl, Boolean> group, StopWatch stopWatch) {
              Context context = new Context() {
                  @Override
                  public Configuration getConfiguration() {
                      return configuration;
                  }
      
                  @Override
                  public java.sql.Connection getConnection() {
                      return connectionUserObjects.getJdbcConnection();
                  }
              };
      
              for (Map.Entry<MigrationInfoImpl, Boolean> entry : group.entrySet()) {
                  final MigrationInfoImpl migration = entry.getKey();
                  boolean isOutOfOrder = entry.getValue();
      
                  final String migrationText = toMigrationText(migration, isOutOfOrder);
      
                  stopWatch.start();
      
                  LOG.info("Migrating " + migrationText);
      
                  connectionUserObjects.restoreOriginalState();
                  connectionUserObjects.changeCurrentSchemaTo(schema);
      
                  try {
                      callbackExecutor.setMigrationInfo(migration);
                      callbackExecutor.onEachMigrateOrUndoEvent(Event.BEFORE_EACH_MIGRATE);
                      try {
                          migration.getResolvedMigration().getExecutor().execute(context);
                      } catch (FlywayException e) {
                          callbackExecutor.onEachMigrateOrUndoEvent(Event.AFTER_EACH_MIGRATE_ERROR);
                          // 抛出了执行异常
                          throw new FlywayMigrateException(migration, isOutOfOrder, e);
                      } catch (SQLException e) {
                          callbackExecutor.onEachMigrateOrUndoEvent(Event.AFTER_EACH_MIGRATE_ERROR);
                          throw new FlywayMigrateException(migration, isOutOfOrder, e);
                      }
      
                      LOG.debug("Successfully completed migration of " + migrationText);
                      callbackExecutor.onEachMigrateOrUndoEvent(Event.AFTER_EACH_MIGRATE);
                  } finally {
                      callbackExecutor.setMigrationInfo(null);
                  }
      
                  stopWatch.stop();
                  int executionTime = (int) stopWatch.getTotalTimeMillis();
      
                  schemaHistory.addAppliedMigration(migration.getVersion(), migration.getDescription(), migration.getType(),
                          migration.getScript(), migration.getResolvedMigration().getChecksum(), executionTime, true);
              }
          }
      
    • 那需要优化的点就是在这里了,然后去分析flyway 的错误码配置,发现是有一个state, 和一个errorCode 说明,flyway的异常类里面是有这两个信息的.而去看FlywayException是没有的,再找FlywayException的子类,FlywaySqlException,FlywaySqlScriptException. FlywaySqlScriptException中是有这两个信息的,分析报错发现这类信息是指sql自身没有问题,由于表冲突/字段冲突等会抛出,那就要针对这个异常去做捕获分析.

    • // 判断是否属于这个异常,是的话则判断是否在已配置的异常里
      if (e instanceof FlywaySqlScriptException) {
          if (isWarnException(e)) {
              return;
          }
      }
      
      /**
           * 处理警告异常 只针对警告信息作日志记录,并未实现像文档里可以Debug日志,Info日志.这些如果需要可以再开发,目前看是处			* 理警告信息足够了
           * @param e
           * @return
           */
          private boolean isWarnException(FlywayException e) {
              FlywaySqlScriptException e1 = (FlywaySqlScriptException) e;
              SQLException cause = (SQLException) e1.getCause();
              String sqlState = cause.getSQLState();
              int errorCode = cause.getErrorCode();
              String[] errorOverrides = configuration.getErrorOverrides();
              if (errorOverrides == null) {
                  return false;
              }
              for (String errorOverride : errorOverrides) {
                  ErrorOverride override = ErrorOverride.getFromString(errorOverride);
                  if (override.getState().equals(sqlState) && override.getErrorCode() == errorCode) {
                      if (override.getLevel().equals("W")) {
                          LOG.warn(e.getMessage(), e);
                          return true;
                      }
                  }
              }
              return false;
          }
      
    • 在FluentConfiguration中errorCodes也需要被释放,原先是抛出了不可用异常.

      • ClassicConfiguration

      • 	@Override
            public String[] getErrorOverrides() {
        
                return errorCodes;
            }
            
            public void setErrorOverrides(String... errorOverrides) {
        
                this.errorCodes = errorOverrides;
        
            }
            
           //  在需要将值赋上
              public void configure(Configuration configuration) {
         	      .......
                setErrorOverrides(configuration.getErrorOverrides());
                ......
            	}
        
    • 即可在代码中配置使用.

      • configure.errorOverrides("42S01:1050:W","42000:1061:W","42S21:1060:W");
        
      • 常用异常

        • 表已存在: 42S01:1050

          索引已存在: 42000:1061

          字段已存在: 42S21:1060

  • 5
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在进行单元测试时,如果依赖于flyway,你需要在项目的pom.xml文件中添加flyway的依赖项。具体的依赖项配置为: ```xml <!--引入flywaydb--> <dependency> <groupId>org.flywaydb</groupId> <artifactId>flyway-core</artifactId> </dependency> ``` 这样就可以在单元测试中使用flyway来进行数据库迁移的操作了。同时,在单元测试中使用flyway的好处包括: 1. 可以简单、直观、快速地测试某个功能是否正确。 2. 在打包之前,通过单元测试可以发现一些问题,因为在打包之前,必须保证所有的单元测试都通过。 3. 使用单元测试可以在不污染数据库的情况下测试功能,即在不对数据库进行任何改变的情况下进行测试。 然而,有时候在使用@SpringBootTest注解时可能会遇到问题,比如报错"Flyway Validate failed:migration checksum mismatch"。这是因为@SpringBootTest注解会启动整个应用的环境,包括flyway的自动配置,而你又重复了这个动作,导致出错。解决这个问题的方法是在配置文件中设置flyway.enable=false,禁止flyway的自动装配。 总之,配置flyway的单元测试需要添加flyway的依赖项,并注意@SpringBootTest注解可能会导致一些问题,需要进行适当的配置。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [微服务SpringCloud的单元测试+flyway配置](https://blog.csdn.net/qq_42378874/article/details/106588957)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT3_1"}}] [.reference_item style="max-width: 33.333333333333336%"] - *2* [MyBatis【MyBatis的增删改查操作与单元测试】](https://blog.csdn.net/m0_59735420/article/details/128160380)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT3_1"}}] [.reference_item style="max-width: 33.333333333333336%"] - *3* [使用mybatis+h2+flyway进行单元测试](https://blog.csdn.net/ljm_csdn/article/details/80500936)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT3_1"}}] [.reference_item style="max-width: 33.333333333333336%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值