[英]Firebase Realtime DB multi-document update failing
我需要更新实时数据库中的许多文档(30 万+)。 我已经使用 Firebase Admin SDK 编写了一个 js 代码来执行此操作。 当文档较少(~3000)时,此代码可以正常工作。 但是当存在大量文档(使用 14k 测试)且 js 堆出 memory 时会失败。
我的js代码:-
const firebaseAuth = require('./firebase/firebase.admin')
async function main() {
await firebaseAuth.firebaseAuthInit();
let db = firebaseAuth.admin.database();
let usersRef = db.ref("users");
return usersRef.once("value").then(async function (snapshot) {
let updates = [];
let usersCount = 0;
snapshot.forEach(async function (userSnapshot) {
let user = userSnapshot.val();
let userKey = userSnapshot.key;
if (user.hasOwnProperty('Settings')) {
//let promise = await usersRef.child(userKey).get();
let promise = usersRef.child(userKey).update({
'Settings': null
}).then(
console.log(`Removed Settings attribute for user: ${userKey}`)
)
updates.push(promise);
}
usersCount++;
})
let settingRemoved = (await Promise.all(updates)).length;
console.log(`Numbers of Users: ${usersCount}. Total no of User Settings Deleted: ${settingRemoved}`)
}, function (error) {
console.error(error);
}).then(function () {
process.exit()
})
}
main()
我得到的错误是:
<--- Last few GCs --->
[683:0x5cea240] 76471 ms: Scavenge 2037.5 (2062.5) -> 2035.2 (2063.3) MB, 11.3 / 0.0 ms (average mu = 0.185, current mu = 0.148) allocation failure
[683:0x5cea240] 76484 ms: Scavenge 2038.4 (2063.3) -> 2036.1 (2064.0) MB, 6.1 / 0.0 ms (average mu = 0.185, current mu = 0.148) allocation failure
[683:0x5cea240] 76507 ms: Scavenge 2039.0 (2064.0) -> 2036.9 (2072.8) MB, 7.8 / 0.0 ms (average mu = 0.185, current mu = 0.148) allocation failure
<--- JS stacktrace --->
FATAL ERROR: Ineffective mark-compacts near heap limit Allocation failed - JavaScript heap out of memory
1: 0xb200e0 node::Abort() [node]
2: 0xa3c157 node::FatalError(char const*, char const*) [node]
3: 0xd083ae v8::Utils::ReportOOMFailure(v8::internal::Isolate*, char const*, bool) [node]
4: 0xd08727 v8::internal::V8::FatalProcessOutOfMemory(v8::internal::Isolate*, char const*, bool) [node]
5: 0xee9785 [node]
6: 0xeea2cc [node]
7: 0xef8269 v8::internal::Heap::CollectGarbage(v8::internal::AllocationSpace, v8::internal::GarbageCollectionReason, v8::GCCallbackFlags) [node]
8: 0xefb535 v8::internal::Heap::HandleGCRequest() [node]
9: 0xe8fc17 v8::internal::StackGuard::HandleInterrupts() [node]
10: 0x12364e2 v8::internal::Runtime_StackGuardWithGap(int, unsigned long*, v8::internal::Isolate*) [node]
11: 0x1640839 [node]
Aborted (core dumped)
该问题可能是由于将所有承诺推送到单个数组而导致的,这会导致堆 memory 溢出。
有什么办法可以避免这种情况吗? 我找不到任何方法可以分批或按顺序执行这些操作(但是,如果 300k 记录的运行时间超过 5 分钟,则按顺序可能不可行)。
节点版本:16.4
但是当存在大量文档(使用 14k 测试)且 js 堆出 memory 时会失败。
这是预期的行为,因为 14k 代表大量数据,实际上并不适合 memory。 有两种方法可以解决这种情况。
第一个是仅按需更新数据。 假设您有一个特定页面要显示一些数据,其中一个字段不应该再存在,另一个应该更新一些内容。 现在,您应该仅在用户打开页面时检查这些字段。 因此,如果第一个字段没有被删除而另一个字段没有被更新,则执行删除和更新操作,然后显示数据,否则只显示数据。 这样,您将仅在实际需要时更新数据。 因此,您很可能永远不会更新所有这 30 万多个节点,因为并非所有用户都会访问该屏幕。
第二种解决方案是创建一个云 Function 并通过执行多个同时更新来开始更新所有数据。 但在开始之前,请尝试检查实时数据库限制。
除了 Alex 建议的两种方法之外,第三种选择是分块更新Users
。 假设您决定一次处理 100 个用户:
ref.orderByKey().limitToFirst(100)
加载前 100 个用户。ref.orderByKey().startAfter(theLastKey).limitToFirst(100)
加载接下来的 100 个用户。或者,由于您要删除一个节点,您还可以使用它来过滤您仍需要更新的子节点。 这样流程就变成了:
ref.orderByChild("Settings").limitToFirst(100)
加载前 100 个用户。 除了更简单之外,这还有一个优点,即您不会读取已经没有Settings
属性的节点。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.