如果最近的事务引发了一个特殊的"事件,那么事务性db.get()是否可能返回过时的结果?例外?

时间:2013-06-08 10:41:10

标签: google-app-engine google-cloud-datastore

这是在appengine transaction docs ...

  

注意:如果您的应用程序在提交事务时收到异常,则并不总是意味着事务失败。在已提交事务并最终成功应用事务的情况下,您可以收到Timeout,TransactionFailedError或InternalError异常......

考虑以下情况

  1. 我在事务中更新实体A
  2. 交易操作导致上述特殊“异常”,其中交易已经提交并最终将被应用
  3. 我在第二步之后或几乎与第二步同时在另一笔交易中运行db.get(entity_a_key_goes_here)
  4. 如果上面的第3步返回None,我会通过将密钥设置为entity_a_key_goes_heredb.put()来创建该实体(步骤3和此步骤在同一事务中运行)。
  5. 我的问题:

    上面第3步的事务性 db.get()操作是否有可能返回过时值(或者不是第1步中设置的更新值)?事务性db.get()操作是否保证即使在它之前发生“怪异”事务异常也会返回最新鲜的结果?

3 个答案:

答案 0 :(得分:1)

我认为在这种情况下第3步的结果将是一个陈旧的价值。在这种情况下,“最新鲜”的结果可能不是步骤1中的实体。

我的建议是在检索值时使用memcache(当memcache未命中时回退到db.get()调用),并在更新实体时更新memcache。使用实体密钥作为内存缓存密钥。实际上,这就是ndb自动运行的方式。

答案 1 :(得分:1)

获取并不是真正的“交易”;如果事务只包含读取,则提交事务实际上不会执行任何操作。在事务中执行读取只保证一件事:如果在应用事务中的任何写入时,返回的读取值不再是最新值,则事务将中止且不会写作将会发生。

因此,允许发生以下事件序列:

  1. 启动交易1。
  2. 在事务1内部,您读取实体A并返回状态A1。
  3. 在事务1内部,您将实体A从状态A1更新为状态A2。
  4. 提交事务1时,它会因上述异常之一而失败。
  5. 启动交易2。
  6. 在事务2内部,您读取实体A并返回状态A1。
  7. 事务1应用于后台的数据存储区,将A从状态A1更改为状态A2。
  8. 结束事务2(没有提交,因为没有写入)。
  9. 但是,这一系列事件是不同的:

    1. 启动交易1。
    2. 在事务1内部,您读取实体A并返回状态A1。
    3. 在事务1内部,您将实体A从状态A1更新为状态A2。
    4. 提交事务1时,它会因上述异常之一而失败。
    5. 启动交易2。
    6. 在事务2内部,您读取实体A并返回状态A1。
    7. 在事务2内部,您将实体A从状态A1更新为状态A3。
    8. 事务1应用于后台的数据存储区,将A从状态A1更改为状态A2。
    9. 交易2现在将无法成功提交,因为它即将应用的写入是基于实体A的过时版本。它将失败并且A将保留在状态A2中。
    10. 因此,在您的第4步添加到问题中,您处于第二个事件序列中。即使实体确实存在,步骤3中的get也可能返回None,但在后一种写入成功的情况下 可能:事务已过期,因此无法提交。事务将被重试,第二次,get将返回步骤1中写入的对象,这就是你想要的。

      所以非常简短的答案是:是的,它可以返回一个陈旧的值,但是如果结果是陈旧的,那么保证在同一个事务中对该实体的后续写入将失败,所以这实际上不应该导致问题。

答案 2 :(得分:0)

根据我在“ndb”上阅读的文档,我了解如果您在“交易”(get())中运行put()@ndb.transactional,那么您就不会得到过时的数据。 'ndb'将提供更新的数据或两者都失败。

交易失败或成功。与其他dbms一样,ndb也维护'journal'。

希望这有帮助。