是什么导致 MongoDB / GridFS MD5 哈希不匹配?
What causes a MongoDB / GridFS MD5 hash mismatch?
我正在对 MongoDB 的 GridFS 作为二进制存储进行一些审查,在我的测试过程中,我遇到了极少数情况下驱动程序抛出的异常(在我的情况下,Java) 当我验证存储的文件确实存储正确时。此验证是 Java 驱动程序将其刚刚存储的内容的计算 MD5 与 MongoDB 的 filemd5 命令返回的 MD5 进行比较的地方。
此异常已记录在案,我可以努力正确处理它,但是:
我的问题是为什么会发生这种情况(显然存在二进制不匹配,但是否有任何原因导致这种情况更频繁地发生)?
我能理解它是否发生在系统故障期间,但它似乎是随机发生的(尽管不是很频繁)。
终于有时间好好研究一下了,这就是我的发现!
默认情况下,mongo java 驱动程序为 mongodb 的任何和所有写入设置所谓的 NONE/UNACKNOWLEDGED 写入关注。这意味着写入 mongodb 是一个非常异步的过程。驱动程序说 "hey, store all this stuff when you have a chance" 和 mongo 最终会存储它,但是响应会立即返回给应用程序,因此它会继续执行线程。
Mongo 的 java 驱动程序的 GridFSFile 对象有一个名为 validate
的方法,可以在存储文件后调用。此方法使用 java 驱动程序在内存中计算的 MD5,因为流被分成块并发送到 mongodb,然后将其与 mongo' 的结果进行比较s filemd5 命令。当 mongo 发出一个 filemd5 请求时,它采用提供的 id 并根据存在的各种二进制数组块的内容计算 MD5,这些块引用提供的 id(它们一起表示存储在 GridFS 中的单个实体).
至于导致我看到驱动程序计算的 MD5 与从 mongo 返回的 MD5 之间频繁不匹配的原因,基本上默认写关注 NONE 它正在将控制权返回给应用程序然后立即执行 validate
/filemd5
命令,mongo 然后处理其内容的当前状态。 MongoDB 可能在所有内容实际写入内存之前就计算了 id 的 MD5,因此有时会返回错误的 MD5。
此外,这与在调试细节中看到的一致 - 一旦发生异常,我添加了日志记录来查询存储的文件并读取其内容,此时它始终具有正确的内容和正确的MD5.
解决方案是 mongo 驱动程序与 GridFS 的交互必须使用至少 ACKNOWLEDGED 的写关注,这意味着要写入的数据在返回之前至少 mongo 保存在内存中到应用程序。我已经为我的测试手动设置了它,并在应用程序启动时为 GridFS 集合启用它,因为它尚未作为 spring-data 中的配置选项公开。同样值得注意的是,新的 mongo-3.x java 驱动程序有一个默认的 ACKNOWLEDGED 写入问题,所以一旦我使用它,理论上我可以删除额外的引导程序。
至于为什么这在本地从未出现过,很可能我的机器具有 mongo 所需的可用内存和处理能力,以便在 validate
时足够快地存储内容调用时,一切都已经存在,因此返回的 MD5 是正确的。构建代理 运行 宁大量并发测试负载明显更重,资源可能更有限,因此 mongodb 并不总是在执行 filemd5 命令之前完成存储内容。
至于验证这确实是固定的,我之前看到 3-4 次测试(大约 2000 次)由于这个问题在每个构建上都失败了,我现在 运行 超过 15 套完整的构建代理上的测试,一次都没见过。
我用来更正此问题的实际代码:
private static final String GRID_FS_FILES_COLLECTION_NAME = "fs.files";
private static final String GRID_FS_CHUNKS_COLLECTION_NAME = "fs.chunks";
@Autowired
protected MongoOperations mongoOperations;
mongoOperations.getCollection(GRID_FS_FILES_COLLECTION_NAME).setWriteConcern(WriteConcern.ACKNOWLEDGED);
mongoOperations.getCollection(GRID_FS_CHUNKS_COLLECTION_NAME).setWriteConcern(WriteConcern.ACKNOWLEDGED);
我正在对 MongoDB 的 GridFS 作为二进制存储进行一些审查,在我的测试过程中,我遇到了极少数情况下驱动程序抛出的异常(在我的情况下,Java) 当我验证存储的文件确实存储正确时。此验证是 Java 驱动程序将其刚刚存储的内容的计算 MD5 与 MongoDB 的 filemd5 命令返回的 MD5 进行比较的地方。
此异常已记录在案,我可以努力正确处理它,但是:
我的问题是为什么会发生这种情况(显然存在二进制不匹配,但是否有任何原因导致这种情况更频繁地发生)?
我能理解它是否发生在系统故障期间,但它似乎是随机发生的(尽管不是很频繁)。
终于有时间好好研究一下了,这就是我的发现!
默认情况下,mongo java 驱动程序为 mongodb 的任何和所有写入设置所谓的 NONE/UNACKNOWLEDGED 写入关注。这意味着写入 mongodb 是一个非常异步的过程。驱动程序说 "hey, store all this stuff when you have a chance" 和 mongo 最终会存储它,但是响应会立即返回给应用程序,因此它会继续执行线程。
Mongo 的 java 驱动程序的 GridFSFile 对象有一个名为 validate
的方法,可以在存储文件后调用。此方法使用 java 驱动程序在内存中计算的 MD5,因为流被分成块并发送到 mongodb,然后将其与 mongo' 的结果进行比较s filemd5 命令。当 mongo 发出一个 filemd5 请求时,它采用提供的 id 并根据存在的各种二进制数组块的内容计算 MD5,这些块引用提供的 id(它们一起表示存储在 GridFS 中的单个实体).
至于导致我看到驱动程序计算的 MD5 与从 mongo 返回的 MD5 之间频繁不匹配的原因,基本上默认写关注 NONE 它正在将控制权返回给应用程序然后立即执行 validate
/filemd5
命令,mongo 然后处理其内容的当前状态。 MongoDB 可能在所有内容实际写入内存之前就计算了 id 的 MD5,因此有时会返回错误的 MD5。
此外,这与在调试细节中看到的一致 - 一旦发生异常,我添加了日志记录来查询存储的文件并读取其内容,此时它始终具有正确的内容和正确的MD5.
解决方案是 mongo 驱动程序与 GridFS 的交互必须使用至少 ACKNOWLEDGED 的写关注,这意味着要写入的数据在返回之前至少 mongo 保存在内存中到应用程序。我已经为我的测试手动设置了它,并在应用程序启动时为 GridFS 集合启用它,因为它尚未作为 spring-data 中的配置选项公开。同样值得注意的是,新的 mongo-3.x java 驱动程序有一个默认的 ACKNOWLEDGED 写入问题,所以一旦我使用它,理论上我可以删除额外的引导程序。
至于为什么这在本地从未出现过,很可能我的机器具有 mongo 所需的可用内存和处理能力,以便在 validate
时足够快地存储内容调用时,一切都已经存在,因此返回的 MD5 是正确的。构建代理 运行 宁大量并发测试负载明显更重,资源可能更有限,因此 mongodb 并不总是在执行 filemd5 命令之前完成存储内容。
至于验证这确实是固定的,我之前看到 3-4 次测试(大约 2000 次)由于这个问题在每个构建上都失败了,我现在 运行 超过 15 套完整的构建代理上的测试,一次都没见过。
我用来更正此问题的实际代码:
private static final String GRID_FS_FILES_COLLECTION_NAME = "fs.files";
private static final String GRID_FS_CHUNKS_COLLECTION_NAME = "fs.chunks";
@Autowired
protected MongoOperations mongoOperations;
mongoOperations.getCollection(GRID_FS_FILES_COLLECTION_NAME).setWriteConcern(WriteConcern.ACKNOWLEDGED);
mongoOperations.getCollection(GRID_FS_CHUNKS_COLLECTION_NAME).setWriteConcern(WriteConcern.ACKNOWLEDGED);