IT博客汇
  • 首页
  • 精华
  • 技术
  • 设计
  • 资讯
  • 扯淡
  • 权利声明
  • 登录 注册

    Solr复制bug一例:Unable to move index file from tempfile to indexfile

    kafka0102发表于 2010-11-24 15:37:28
    love 0

    22日下午3时多,收到搜索系统的报警邮件,错误日志如下:

    [2010-11-22 15:16:14][ERROR][pool-6-thread-1][SnapPuller.java(650)]Unable to move index file from: /indexpath/index.20101122031500/_21.frq to: /indexpath/index.20101122031000/_21.frq

    SnapPuller是Solr复制用到的一个类,我对它做了一些修改,所以把它挪到我的代码里了。报错的代码片断如下:

    private boolean copyAFile(final File tmpIdxDir, final File indexDir, final String fname, final List<String> copiedfiles) {
        final File indexFileInTmpDir = new File(tmpIdxDir, fname);
        final File indexFileInIndex = new File(indexDir, fname);
        final boolean success = indexFileInTmpDir.renameTo(indexFileInIndex);
        if (!success) {
          LOG.error("Unable to move index file from: " + indexFileInTmpDir
              + " to: " + indexFileInIndex);
          for (final String f : copiedfiles) {
            final File indexFile = new File(indexDir, f);
            if (indexFile.exists()) {
              indexFile.delete();
            }
          }
          delTree(tmpIdxDir);
          return false;
        }
        return true;
      }

    错误现象表明,indexFileInTmpDir.renameTo(indexFileInIndex)失败了。在这个复制过程中,这种失败最大的可能性就是indexFileInIndex所在的indexDir不存在。查看索引数据目录,确实没有找到index.20101122031000文件夹。不过,从库的index.properties文件却记录“index=index.20101122031000”,也就是说,当前提供查询服务的索引目录就是index.20101122031000。查看进程打开的句柄,进程确实保持着index.20101122031000目录下的索引文件句柄,但它们也确实被删除了。

    这里补充一下背景,搜索系统使用的是最新solr1.4.1版本,从库每5分钟同步一次主库。所以,当上面的错误出现以后,后续每5分钟就又会出现类似的错误,使得复制总是失败。Solr复制的部分原理是:从库默认的索引目录是index,在正常复制时,从库只是对该目录里的索引文件做修改(添加新的,删除旧的),但当从库索引提交点(IndexCommit)的generation大于主库时,会发生索引文件的全copy,也就是抛弃当前使用的索引目录,而是使用新生成的临时索引目录作为新的索引目录,并修改index.properties,临时目录的命名格式就是取当前的一时间戳(也就是bug所在,但到这里我还没发现这个bug)。Solr wiki也有提到,从库的IndexCommit.generation大于的bad case就是外界对从库做了不适当的提交。而在我的系统里,这种现象是由于重建索引造成的,系统重建索引没有使用DIH,而是在不影响更新时单独线程重建,建完后覆盖旧的索引,这造成重建后的索引的generation会比从库小,所以每次重建从库都会使用新的索引目录。

    回到这个问题上来,临时的解决办法就是重启从库,因为重启后从库读index.properties发现index.20101122031000并不存在,默认就会使用index目录的索引,而index目录的generation会和主库不一致,就产生全复制,短时间内恢复成最新的索引,但这样可能后续还会触发到bug。但问题并没有解决,我继续查看主从库的日志,再没有发现其他错误信息。而这个问题之前是没有出现的,所以可能是最近的什么操作引起的。而最近的操作就是老高22号凌晨重建了索引,我对照了重建完成时间,是凌晨3点多,而从库的index.20101122031000应该就是之后复制生成的。

    google之错误信息,找到https://issues.apache.org/jira/browse/SOLR-1822,报bug的人出现和我一样的错误!!不过他也不清楚索引目录怎么没了,他是建议发现目录没了就新建一个,这也是可以曲线解决问题的(我想了一下,这种方法应该不会产生句柄泄漏)。可问题是,好端端的一个索引目录怎么就没了。我继续查看从库复制相关的log,发现在下午3点10分之前都是有正常的复制log的。初想一下,复制过程中发生删除的地方也就是和过期的IndexCommit相关联的索引文件被删除,但也不会删除目录阿!!!莫不是被系统外界下的黑手?

    回家的地铁上就在琢磨,复制过程有删除目录的操作吗?有!!!复制的最后会删除临时目录!!!那会不会是临时目录和目标索引目录重名了,结果复制后把索引目录给删除了?回家再回看错误日志,这临时索引目录名称是:index.20101122031500,而出错当时是下午的15:15,而不是凌晨的03:15,查找创建临时索引目录的代码:

     private File createTempindexDir(final SolrCore core) {
        final String tmpIdxDirName = "index." + new SimpleDateFormat(SnapShooter.DATE_FMT).format(new Date());
        final File tmpIdxDir = new File(core.getDataDir(), tmpIdxDirName);
        tmpIdxDir.mkdirs();
        return tmpIdxDir;
      }

    SnapShooter.DATE_FMT的定义是:

      public static final String DATE_FMT = "yyyyMMddhhmmss";

    注意时间串格式中的小时是“hh”(范围是01-12)而不是“HH”(范围是00-23),这就造成从库如果在当天上午某个时间点全复制,而不幸的,在下午的相应时刻复制又来了,就使得临时索引目录和当前索引目录名称相同,而在复制的最后误删掉当前索引目录。从Bug效果来说,这绝对有破坏性,并且其真的是够低级的。
    我后来check了solr的trunk,发现这个bug已经被修复了,log显示是:

    r964312 | koji | 2010-07-15 14:32:22 +0800 (四, 2010-07-15) | 1 行
    SOLR-1995: Use proper format for SimpleDateFormat

    找到https://issues.apache.org/jira/browse/SOLR-1995,发现修复的初衷和我所出现的问题还不一样,它的破坏性也要小的多。话说又说回来,这个bug其实是很隐蔽的,触发的条件是:1)从库要在当天的上午某点进行全复制,如果使用DIH复制,这种情况是不会出现的,而我的系统的全复制通常是在下午,所以之前没出现该问题。2)下午的相应时刻也要发生复制,这要求复制间隔能被12小时整除(我的5分钟间隔中招了),并且当时主库的索引有更新变化(我的系统10多个库当时就有部分库就因为那个时间段没有更新而没有中招)。



沪ICP备19023445号-2号
友情链接