MongoDB目前在内部维护10种编程语言Drivers,其中包括我目前是产品经理的Ruby驱动程序。此外,我们还有一个community maintained drivers库,它是使用Abiaoqian构建的,我们的工程师维护和发布。
我注意到,这些社区驱动程序之一 - Haskell driver-正在遇到一个问题,即查询不再从MongoDB Atlas群集中返回其应用程序的结果。
。尽管我从未与Haskell合作,但在加入团队之前,我在Technical Services工作,为客户提供了通过我们的驾驶员遇到申请问题的客户的支持。这似乎是一个有趣的问题,我们希望为开发人员社区解决,所以我想分享带我们解决问题并最终实现解决方案的诊断旅程。
概述
当Adrien首次报道issue #131 on GitHub时,最初的评估是他们的应用程序可以成功连接到MongoDB Atlas群集并写下新内容,但是当试图回读这些结果时,结果集总是空的。发生这种情况突然导致现有的应用程序和工作量破裂,但是没有引入新的代码,这可能是罪魁祸首。
由于我不熟悉Haskell Adrien,请提供Dockerized reproduction,我可以用来针对自己的地图集簇测试这个问题。此复制将将3个文档写入一个集合,然后尝试回读3个文档。要开始测试,我设置了M10群集并进行了几次测试。
Failures:
src/Lib.hs:101:33:
1) Reads Ensures reads work
expected: 3
but got: 9
每次我进行测试时,都会失败,但是“确保阅读工作”中的文档数量不断增加。我正在测试的群集是一个专用的群集,但是Mongodb Atlas还提供了免费和共享的层群集,因此为了完成测试,我接下来配置了M0并重新运行测试。
Failures:
src/Lib.hs:101:33:
1) Reads Ensures reads work
expected: 3
but got: 0
无论我对我的M0进行了多少次测试(也测试了M2和M5),结果始终为0。
只是为了确保这不是我用脚本测试的较大问题,该脚本使用Ruby驱动程序与M0群集一起验证行为没有重现在那里:
require 'bundler/inline'
gemfile do
source 'https://rubygems.org'
gem 'mongo'
end
client = Mongo::Client.new('mongodb+srv://....mongodb.net/test')
collection = client[:foo]
collection.drop
puts "Found #{collection.find.to_a.length} documents"
# => Found 0 documents
collection.insert_many([].fill({ "bar": "baz" },0,3))
puts "Found #{collection.find.to_a.length} documents"
# => Found 3 documents
该脚本将产生预期的结果,这进一步指出了Atlas方面的潜在问题,该问题是特定于自由和共享层簇的特定问题。
mongodb atlas强加了一些limitations on free and shared tier簇,在某些情况下,这些簇是由应用程序和基础基础架构之间的代理层强制执行的。
分析
现在,该问题已经缩小,与云运营工程师合作,在开发环境中创建一个孤立的M2群集,我们为koude0和koude1日志组件增加了log verbosity。
有了这些信息,当我们下载节点的日志时,我们的测试是针对的,我们应该能够获得更多有关执行的内容以及可能失败的信息。
// Test #1
{"t":{"$date":"2022-08-18T11:19:16.985+00:00"},"s":"D2", "c":"COMMAND", "id":5578800, "ctx":"conn24194","msg":"Deprecated operation requested. The client driver may require an upgrade in order to ensure compatibility with future server versions. For more details see https://dochub.mongodb.org/core/legacy-opcode-compatibility","attr":{"op":"query","clientInfo":{"driver":{"name":"mongo-go-driver","version":"v1.7.2+prerelease"},"os":{"type":"linux","architecture":"arm64"},"platform":"go1.18.2","application":{"name":"Atlas Proxy v20220824.0.0.1660656950"}}}}
{"t":{"$date":"2022-08-18T11:19:16.986+00:00"},"s":"D2", "c":"QUERY", "id":20914, "ctx":"conn24194","msg":"Running query","attr":{"query":"ns: 62fe1f7d37518e1c32149694_haskell.test123 query: { comment: { AtlasProxyAppName: \"\", AtlasProxyClientMetadata: {} } } sort: {} projection: {}"}}
{"t":{"$date":"2022-08-18T11:19:16.986+00:00"},"s":"D5", "c":"QUERY", "id":20917, "ctx":"conn24194","msg":"Not caching executor but returning results","attr":{"numResults":0}}
基于日志分析,我们不仅可以验证该问题的存在,而且还可以影响Haskell驱动程序的这些操作:
正在运行不屑一顾的操作
MongoDB在内部和外部发送/接收消息时(通过驱动程序)在发送/接收消息时使用wire protocol。最初存在许多opcodes,但从mongodb 5.0开始,其中大多数被弃用了koude2。
在MongoDB 3.6之前,当koude2 was introduced包含现有Opcodes时,通过koude4执行了查询操作,Haskell驱动程序显然仍在用于查询执行。
请注意,尽管OP_QUERY
已弃用,但仍将在MongoDB的版本中支持它(5.0),因此不是造成此问题的原因。
日志确认没有查询
返回结果在默认级别上,如果Database Profiler超过100ms的慢速查询阈值(koude7),则仅将查询输出到mongod
日志。我们正在进行的测试可能会在10毫秒以下完成,这阻止了任何有用的记录。
{"t":{"$date":"2022-08-18T11:19:16.986+00:00"},"s":"D5", "c":"QUERY", "id":20917, "ctx":"conn24194","msg":"Not caching executor but returning results","attr":{"numResults":0}}
一旦日志级别提高,显然正在执行问题的操作,但没有返回任何结果。
日志突出显示查询本身的问题
随着日志级别的增加,但是QUERY
组件日志清楚地显示出不仅没有结果,而且发送到服务器的查询 shape 与我们的预期不匹配:
{"t":{"$date":"2022-08-18T11:19:16.986+00:00"},"s":"D2", "c":"QUERY", "id":20914, "ctx":"conn24194","msg":"Running query","attr":{"query":"ns: 62fe1f7d37518e1c32149694_haskell.test123 query: { comment: { AtlasProxyAppName: \"\", AtlasProxyClientMetadata: {} } } sort: {} projection: {}"}}
看来,查询的过滤器(我们期望为空的)实际上是为comment: { AtlasProxyAppName: "", AtlasProxyClientMetadata: {} }
过滤。由于作为此测试的一部分创建的示例文档都不符合这些条件,因此查询返回了0个结果。
发现
从我们的日志分析中,似乎正在重写我们的操作,以附加具有{ AtlasProxyAppName: "", AtlasProxyClientMetadata: {} }
值的comment
字段的过滤条件。由于comment
在MongoDB命令的上下文中具有特定的含义。
从MongoDB 4.4, a koude12 option was added to all database commands开始(请参阅SERVER-29794)。
这不是与koude16 meta operator相混淆的,该koude16 meta operator可用于传播元数据以查询日志。
ATLAS团队推出了一项功能(发布的2022-06-22
),该功能将利用这些注释来改善免费/共享簇中的koude18输出。正如所有“官方” MongoDB司机使用OP_MSG
与现代MongoDB群集进行通信,当对此功能进行测试时,没有问题。
不幸的是,仍然使用OP_QUERY
进行查询的驾驶员由于元数据注释在滤镜中的注入而不是上面的一个级别而受到负面影响,就像OP_MSG
一样。
现在可以验证该问题,如果检测到OP_QUERY
而不是不正确地应用comment
选项,则引入了其他逻辑来使用$comment
Meta运算符。
结果
在Haskell社区的协助下,我们能够识别和解决MongoDB Atlas的免费和共享层的缺陷。该解决方案的修复是在2022-09-21的版本8ed75a4810@v20220914
中发布的,任何使用社区维护的Haskell驱动程序的Haskell应用程序都应该开始按预期工作,而无需额外干预。
我们非常感谢开发商社区将时间和精力投入到像MongoDB驾驶员一样强大的东西时所做的投资,并希望确保我们尽可能地提供帮助。