在 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.