公告介绍最快速的MongoDB 8.0!了解更多 >>介绍最快速的MongoDB 8.0!>>
MONGODB 系统警报

Node.js v22.7.0 运行时缺陷可能导致数据编码问题

2024年8月22日,Node.js v22.7.0 发布了buffer.write 的不正确优化,这可能导致字符串使用 ISO-8859-1 而不是 UTF-8 编码。

尽管此问题不直接存在于 MongoDB 的任何产品中,但 Node.js v22.8.0 将禁用 buffer.write 的快速 API (将禁用)。使用 MongoDB Node.js 驱动的开发者可能会在 Node.js v22.7.0 中遇到潜在的数据完整性问题。

只有以下条件都成立时,此问题才会出现

  • 正在使用 Node.js v22.7.0
  • 正在将文档写入 MongoDB
  • 这些文档包含某些字符(例如:é 或 )这些字符可以使用 ISO-8859-1 编码,但不能使用 ASCII。因此,中文/日文文本不会受到影响,因为它不能使用 ISO-8859-1 进行编码

截至2024年9月3日,Node.js v22.8.0 已发布,并包含修复 Node.js v22.7.0 中存在的 UTF-8 编码问题的补丁。

减轻问题的影响

为了避免由于 Node.js 运行时中的此错误而导致的潜在数据完整性问题,建议完全**不使用**Node.js v22.7.0。

MongoDB 建议仅在文档中记录与生产环境兼容的 Node.js 运行时版本。截至编写时,Node.js v22.x 不被视为与 MongoDB Node.js 驱动程序兼容的运行时。

了解问题

为了说明这种情况是如何发生的,请考虑以下重现

import { MongoClient } from "mongodb";

const client = new MongoClient("mongodb://:27017/test");
const value = 'bébé';

async function run() {
  try {
    console.log(`Running Node ${process.versions.node}`);
    const coll = client.db("test").collection("foo");
    await coll.drop();

    let i = 0;
    while (Buffer.from(value).length === 6 && i < 20000) { i++ }

    await coll.insertOne({ _id: 1, message: value });
    const doc = await coll.findOne({ _id: 1 });
    console.log(`Found doc ${JSON.stringify(doc)}`);
  } finally {
    await client.close();
  }
}
run().catch(console.dir);

当使用 Node.js 的先前版本运行时,Buffer 长度会在 20K 次迭代中一致评估,然后将文档插入 MongoDB 集合并成功检索。

Running Node 22.6.0
Found doc {"_id":1,"message":"bébé"}

但是,当使用 Node.js v22.7.0 运行相同的重现时,可能会生成无效的 UTF-8 字符串数据,然后将其插入 MongoDB 集合,导致后续检索尝试失败。

Running Node 22.7.0
BSONError: Invalid UTF-8 string in BSON document
    at parseUtf8 (/Users/alex/temp/test-node/node_modules/bson/lib/bson.cjs:148:19)
    at Object.toUTF8 (/Users/alex/temp/test-node/node_modules/bson/lib/bson.cjs:273:21)
    ... 6 lines matching cause stack trace ...
    at process.processTicksAndRejections (node:internal/process/task_queues:105:5)
    at async Collection.findOne (/Users/alex/temp/test-node/node_modules/mongodb/lib/collection.js:274:21) {
  [cause]: TypeError: The encoded data was not valid for encoding utf-8
      at TextDecoder.decode (node:internal/encoding:443:16)
      at parseUtf8 (/Users/alex/temp/test-node/node_modules/bson/lib/bson.cjs:145:37)
      at Object.toUTF8 (/Users/alex/temp/test-node/node_modules/bson/lib/bson.cjs:273:21)
      at deserializeObject (/Users/alex/temp/test-node/node_modules/bson/lib/bson.cjs:2952:31)
      at internalDeserialize (/Users/alex/temp/test-node/node_modules/bson/lib/bson.cjs:2863:12)
      at Object.deserialize (/Users/alex/temp/test-node/node_modules/bson/lib/bson.cjs:4335:12)
      at OnDemandDocument.toObject (/Users/alex/temp/test-node/node_modules/mongodb/lib/cmap/wire_protocol/on_demand/document.js:208:28)
      at CursorResponse.shift (/Users/alex/temp/test-node/node_modules/mongodb/lib/cmap/wire_protocol/responses.js:207:35)
      at FindCursor.next (/Users/alex/temp/test-node/node_modules/mongodb/lib/cursor/abstract_cursor.js:222:41)
      at process.processTicksAndRejections (node:internal/process/task_queues:105:5) {
    code: 'ERR_ENCODING_INVALID_ENCODED_DATA'
  }
}

尽管 MongoDB 的 Node.js 驱动程序支持 UTF-8 验证,但该功能适用于从 MongoDB 服务器接收到的 BSON 字符串的解码。由于 Node.js v22.7.0 中的错误发生在将字符串编码为 UTF-8 时,因此无效数据仍可以序列化为 BSON 并写入数据库。

请注意,如果您在macOS上通过homebrew安装了mongosh,底层Node.js运行时可能是Node.js v22.7.0,因为homebrew默认自动升级到最新的Node.js版本。如果从这些mongosh实例向数据库写入任何数据,并且这些数据包含非ASCII字符,则可能发生此编码问题。

识别活动集群中的数据完整性问题

MongoDB的商业支持团队维护副本集一致性和修复工具,可在数据损坏的情况下使用。

要确定您的数据是否受到影响,可以使用以下方式使用validate.js脚本来验证7.0或更高版本的数据

curl -O "https://raw.githubusercontent.com/mongodb/support-tools/master/replset-consistency/validate.js" && mongosh "mongodb://:27017/test" validate.js --eval "validateFull=true" 2>&1 | tee results.json | grep -v "warnings\":\[\]"

如果输出包含类似于以下内容的条目,则在那些集合中检测到无效的BSON文档。

{"ns":"test.foo","uuid":{"$binary":{"base64":"b4//q+dUQrKN5LAQha+FIg==","subType":"04"}},"nInvalidDocuments":0,"nNonCompliantDocuments":1,"nrecords":1,"nIndexes":1,"keysPerIndex":{"_id_":1},"indexDetails":{"_id_":{"valid":true}},"valid":true,"repaired":false,"warnings":["Detected one or more documents in this collection not conformant to BSON specifications. For more info, see logs with log id 6825900"],"errors":[],"extraIndexEntries":[],"missingIndexEntries":[],"corruptRecords":[],"ok":1,"$clusterTime":{"clusterTime":{"$timestamp":{"t":1724865657,"i":1}},"signature":{"hash":{"$binary":{"base64":"AAAAAAAAAAAAAAAAAAAAAAAAAAA=","subType":"00"}},"keyId":0}},"operationTime":{"$timestamp":{"t":1724865657,"i":1}}}

此失败会在mongod.log中按以下方式记录,可以通过grep日志来识别

{"t":{"$date":"2024-08-30T04:37:46.769+00:00"},"s":"W", "c":"STORAGE", "id":6825900, "ctx":"conn7","msg":"Document is not conformant to BSON specifications","attr":{"recordId":"1","reason":{"code":378,"codeName":"NonConformantBSON","errmsg":"Found string that doesn't follow UTF-8 encoding. in element with field name 'validString' in object with _id: ObjectId('66d1221af66b999c954cd518')"}}}

对于7.0版本之前的发布,可以利用脚本来识别由于Node.js问题而错误编码的包含字符串的文档。一个利用PyMongo的Python脚本示例(detection.py)可用于4.2.25、4.4.29、5.0.28和6.0.16,其输出包括_id、数据库和集合。

> python3 detection.py
Document with _id 66d53e17cd331a1c16e58d1b in myProject.documents needs fixing
Document with _id 66d54857211816027147a245 in myProject.documents needs fixing
Document with _id 66d54857211816027147a246 in myProject.documents needs fixing
Document with _id 66d54857211816027147a247 in myProject.documents needs fixing
Document with _id 66d54857211816027147a248 in myProject.documents needs fixing
Document with _id 66d54857211816027147a249 in myProject.documents needs fixing

运行检测脚本会生成一个名为to_fix.csv的CSV文件,其中包含上述相同的信息

collection,_id
myProject.documents,"{""$oid"": ""66d53e17cd331a1c16e58d1b""}"
myProject.documents,"{""$oid"": ""66d54857211816027147a245""}"
myProject.documents,"{""$oid"": ""66d54857211816027147a246""}"
myProject.documents,"{""$oid"": ""66d54857211816027147a247""}"
myProject.documents,"{""$oid"": ""66d54857211816027147a248""}"
myProject.documents,"{""$oid"": ""66d54857211816027147a249""}"

对备份的影响

如果备份使用了错误的编码,则将来恢复这些备份将包含此问题。在从该时间段恢复备份时,必须执行检测和修复程序,以确保修复无效编码。

修复

运行detection.py脚本会生成一个to_fix.csv文件,然后可以使用该文件手动修复问题或运行fix.py脚本。建议您将脚本用作示例并使其适用于您的环境。

如果您担心此问题的影響,我们建议您将数据与任何其他记录交叉引用,以帮助验证数据完整性。有关任何进一步的问题,请创建支持案例或与Atlas支持团队开始聊天

© . All rights reserved.