在 firebase 中,为什么事务不像在管理员 api 中那样在云函数中工作?
In firebase, why don't transactions work in cloud functions like they do in the admin api?
我有现有的管理 api 代码,为了测试目的我已将其简化为此代码(有效):
admin.database().ref('/dropbox').on('child_added', function (childSnap) {
let item, itemRef = childSnap.ref;
console.log(`Item: ${JSON.stringify(childSnap.val())} at ${childSnap.key}`);
console.log(`Item ref: ${itemRef.toString()}`);
itemRef.transaction(function (value) {
console.log(`Value: ${JSON.stringify(value)}`);
if (value) {
item = value;
return null;
}
}).then(function (resolution) {
console.log(`Txn resolution: ${resolution.committed ? 'committed' : 'NOT-COMMITTED'}`);
if (resolution.committed) {
// process item
console.log(`Process: ${JSON.stringify(item)}`);
} else {
// assume that item must have been removed by someone else
}
}).catch(function (err) {
console.log(`Txn error: ${JSON.stringify(err, null, 2)}`);
});
});
当我运行:
firebase database:push /dropbox <<<'{"test":"abc123"}'
控制台输出为:
Item: {"test":"abc123"} at -KgTpp3FzgbLUrMNofNZ
Item ref: https://cloud-function-txn-test.firebaseio.com/dropbox/-KgTpp3FzgbLUrMNofNZ
Value: {"test":"abc123"}
Txn resolution: committed
Process: {"test":"abc123"}
我一直在尝试将我的代码和这个示例移动到云函数中。我意识到 .on('child_added', f) 和 .onWrite(f) 对现有数据的处理方式不同,但我无法让事务代码正常工作。传递给我的交易函数的参数总是空的。
作为云函数(这不起作用):
exports.receiveAndRemove = functions.database.ref('/dropbox/{entryId}').onWrite(function (event) {
if (!event.data.exists()) {
return;
}
let item, itemRef = event.data.adminRef;
console.log(`Item: ${JSON.stringify(event.data.val())} at ${event.data.key}`);
console.log(`Item ref: ${itemRef.toString()}`);
itemRef.transaction(function (value) {
console.log(`Value: ${JSON.stringify(value)}`);
if (value) {
item = value;
return null;
}
}).then(function (resolution) {
console.log(`Txn resolution: ${resolution.committed ? 'committed' : 'NOT-COMMITTED'}`);
if (resolution.committed) {
// process item
console.log(`Process: ${JSON.stringify(item)}`);
} else {
// bad to assume here that item must have been removed by someone else
}
}).catch(function (err) {
console.log(`Txn error: ${JSON.stringify(err, null, 2)}`);
});
});
由于某种原因,交易从未删除该项目。日志输出:
2017-03-30T10:51:19.387565284Z D receiveAndRemove: Function execution started
2017-03-30T10:51:19.395Z I receiveAndRemove: Item: {"test":"abc123"} at -KgTpp3FzgbLUrMNofNZ
2017-03-30T10:51:19.395Z I receiveAndRemove: Item ref: https://cloud-function-txn-test.firebaseio.com/dropbox/-KgTpp3FzgbLUrMNofNZ
2017-03-30T10:51:19.396Z I receiveAndRemove: Value: null
2017-03-30T10:51:19.396Z I receiveAndRemove: Txn resolution: NOT-COMMITTED
2017-03-30T10:51:19.418446269Z D receiveAndRemove: Function execution took 32 ms, finished with status: 'ok'
当然,云函数无法删除该项目,并且由于事务未提交删除,因此也不会处理该项目。我希望两者都会发生,并且我希望即使节点服务器版本为 运行ning,此代码也能正常工作。无论云中有多少实例 运行ning and/or 我的服务器。
我遗漏的云功能是否存在细微差别?是不是我对事务的处理不正确,或者对云函数不起作用?
完整来源:https://github.com/mscalora/cloud-function-txn-test.git
这里的问题是在交易值为null
的场景下,你returnundefined
,取消了交易。您实际上需要处理值为 null
的情况,因为 Firebase 可能会传递该值。这样做的原因是深入了解 Firebase 事务的工作原理。
在第一个示例中,您在执行事务的节点上有一个本地侦听器。这意味着您具有存储在本地缓存中的该节点的确切值。在第二个示例中,您拥有节点的值,但本地没有该节点的实际侦听器。该值来自 Cloud Functions 本身,不存储在本地。因此,当您进行交易时,Firebase 将立即尝试使用 "guess" 的值,该值始终以 null
开始。一旦从服务器得知该值不是 null
,事务将重试,服务器将告诉它新值是什么。然后,事务将重试。但是,因为您不处理 null
案例,而只是 return undefined
,交易被取消。
我不认为你真的需要交易来完成你想要做的事情。您可以在两个代码示例中获取 item
的值,而无需进行交易。例如,以下是您可以如何更新您的 Cloud Functions 示例:
exports.receiveAndRemove = functions.database.ref('/dropbox/{entryId}').onWrite(function (event) {
if (!event.data.exists()) {
return;
}
let item = event.data.val();
let itemRef = event.data.adminRef;
console.log(`Item: ${JSON.stringify(item)} at ${event.data.key}`);
console.log(`Item ref: ${itemRef.toString()}`);
return itemRef.remove();
});
据我了解,云函数是无状态的,因此没有本地缓存。如果在一段时间后不再使用该方法,它总是会删除该实例。这就是为什么它总是 returns null.
我有现有的管理 api 代码,为了测试目的我已将其简化为此代码(有效):
admin.database().ref('/dropbox').on('child_added', function (childSnap) {
let item, itemRef = childSnap.ref;
console.log(`Item: ${JSON.stringify(childSnap.val())} at ${childSnap.key}`);
console.log(`Item ref: ${itemRef.toString()}`);
itemRef.transaction(function (value) {
console.log(`Value: ${JSON.stringify(value)}`);
if (value) {
item = value;
return null;
}
}).then(function (resolution) {
console.log(`Txn resolution: ${resolution.committed ? 'committed' : 'NOT-COMMITTED'}`);
if (resolution.committed) {
// process item
console.log(`Process: ${JSON.stringify(item)}`);
} else {
// assume that item must have been removed by someone else
}
}).catch(function (err) {
console.log(`Txn error: ${JSON.stringify(err, null, 2)}`);
});
});
当我运行:
firebase database:push /dropbox <<<'{"test":"abc123"}'
控制台输出为:
Item: {"test":"abc123"} at -KgTpp3FzgbLUrMNofNZ
Item ref: https://cloud-function-txn-test.firebaseio.com/dropbox/-KgTpp3FzgbLUrMNofNZ
Value: {"test":"abc123"}
Txn resolution: committed
Process: {"test":"abc123"}
我一直在尝试将我的代码和这个示例移动到云函数中。我意识到 .on('child_added', f) 和 .onWrite(f) 对现有数据的处理方式不同,但我无法让事务代码正常工作。传递给我的交易函数的参数总是空的。
作为云函数(这不起作用):
exports.receiveAndRemove = functions.database.ref('/dropbox/{entryId}').onWrite(function (event) {
if (!event.data.exists()) {
return;
}
let item, itemRef = event.data.adminRef;
console.log(`Item: ${JSON.stringify(event.data.val())} at ${event.data.key}`);
console.log(`Item ref: ${itemRef.toString()}`);
itemRef.transaction(function (value) {
console.log(`Value: ${JSON.stringify(value)}`);
if (value) {
item = value;
return null;
}
}).then(function (resolution) {
console.log(`Txn resolution: ${resolution.committed ? 'committed' : 'NOT-COMMITTED'}`);
if (resolution.committed) {
// process item
console.log(`Process: ${JSON.stringify(item)}`);
} else {
// bad to assume here that item must have been removed by someone else
}
}).catch(function (err) {
console.log(`Txn error: ${JSON.stringify(err, null, 2)}`);
});
});
由于某种原因,交易从未删除该项目。日志输出:
2017-03-30T10:51:19.387565284Z D receiveAndRemove: Function execution started
2017-03-30T10:51:19.395Z I receiveAndRemove: Item: {"test":"abc123"} at -KgTpp3FzgbLUrMNofNZ
2017-03-30T10:51:19.395Z I receiveAndRemove: Item ref: https://cloud-function-txn-test.firebaseio.com/dropbox/-KgTpp3FzgbLUrMNofNZ
2017-03-30T10:51:19.396Z I receiveAndRemove: Value: null
2017-03-30T10:51:19.396Z I receiveAndRemove: Txn resolution: NOT-COMMITTED
2017-03-30T10:51:19.418446269Z D receiveAndRemove: Function execution took 32 ms, finished with status: 'ok'
当然,云函数无法删除该项目,并且由于事务未提交删除,因此也不会处理该项目。我希望两者都会发生,并且我希望即使节点服务器版本为 运行ning,此代码也能正常工作。无论云中有多少实例 运行ning and/or 我的服务器。
我遗漏的云功能是否存在细微差别?是不是我对事务的处理不正确,或者对云函数不起作用?
完整来源:https://github.com/mscalora/cloud-function-txn-test.git
这里的问题是在交易值为null
的场景下,你returnundefined
,取消了交易。您实际上需要处理值为 null
的情况,因为 Firebase 可能会传递该值。这样做的原因是深入了解 Firebase 事务的工作原理。
在第一个示例中,您在执行事务的节点上有一个本地侦听器。这意味着您具有存储在本地缓存中的该节点的确切值。在第二个示例中,您拥有节点的值,但本地没有该节点的实际侦听器。该值来自 Cloud Functions 本身,不存储在本地。因此,当您进行交易时,Firebase 将立即尝试使用 "guess" 的值,该值始终以 null
开始。一旦从服务器得知该值不是 null
,事务将重试,服务器将告诉它新值是什么。然后,事务将重试。但是,因为您不处理 null
案例,而只是 return undefined
,交易被取消。
我不认为你真的需要交易来完成你想要做的事情。您可以在两个代码示例中获取 item
的值,而无需进行交易。例如,以下是您可以如何更新您的 Cloud Functions 示例:
exports.receiveAndRemove = functions.database.ref('/dropbox/{entryId}').onWrite(function (event) {
if (!event.data.exists()) {
return;
}
let item = event.data.val();
let itemRef = event.data.adminRef;
console.log(`Item: ${JSON.stringify(item)} at ${event.data.key}`);
console.log(`Item ref: ${itemRef.toString()}`);
return itemRef.remove();
});
据我了解,云函数是无状态的,因此没有本地缓存。如果在一段时间后不再使用该方法,它总是会删除该实例。这就是为什么它总是 returns null.