多个多对一上的 App Engine JDO 事务

发布于 2024-09-24 06:26:33 字数 784 浏览 6 评论 0 原文

我有一个简单的域模型,如下

驱动程序 - key(string)、run-count、unique-track-count

Track - key(string)、run-count、unique-driver-count、best-time

Run - key(?) 、 driver-key、track-key、time、boolean-driver-update、boolean-track-updated

我需要能够在同一事务中更新 Run 和 Driver;以及同一事务中的运行和跟踪(显然是为了确保我不会两次更新统计数据,或者错过增量计数器)

现在我尝试分配作为运行键,一个由驱动程序组成的键- key/track-key/run-key(string)

这将让我在一个事务中更新 Run 实体和 Driver 实体。

但是,如果我尝试一起更新“运行”和“跟踪”实体,它会抱怨它无法在多个组上进行事务处理。它说它在交易中同时拥有司机和卡车,但它不能在两者上运行......

tx.begin();

run = pmf.getObjectById(Run.class, runKey);
track = pmf.getObjectById(Track.class, trackKey);
//This is where it fails;

incrementCounters();
updateUpdatedFlags();
tx.commit();

奇怪的是,当我做类似的事情来更新运行和司机时,它工作正常。

关于如何映射我的域模型以实现相同的功能有什么建议吗?

I have a simple domain model as follows

Driver - key(string), run-count, unique-track-count

Track - key(string), run-count, unique-driver-count, best-time

Run - key(?), driver-key, track-key, time, boolean-driver-update, boolean-track-updated

I need to be able to update a Run and a Driver in the same transaction; as well as a Run and a Track in the same transaction (obviously to make sure i don't update the statistics twice, or miss out on an increment counter)

Now I have tried assigning as run key, a key made up of driver-key/track-key/run-key(string)

This will let me update in one transaction the Run entity and the Driver entity.

But if I try updating the Run and Track entities together, it will complain that it cannot transact over multiple groups. It says that it has both the Driver and the Truck in the transaction and it can't operate on both...

tx.begin();

run = pmf.getObjectById(Run.class, runKey);
track = pmf.getObjectById(Track.class, trackKey);
//This is where it fails;

incrementCounters();
updateUpdatedFlags();
tx.commit();

Strangely enough when I do a similar thing to update Run and Driver it works fine.

Any suggestions on how else I can map my domain model to achieve the same functionality?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

帅的被狗咬 2024-10-01 06:26:34

对于 Google App Engine,所有数据存储区操作都必须在实体上进行在同一个实体组中。这是因为您的数据通常存储在多个表中,而 Google App Engine 无法跨多个表进行事务。

拥有一对一和一对多关系的实体 自动位于同一实体组中。因此,如果一个实体包含对另一个实体或实体集合的引用,您可以在同一事务中读取或写入这两个实体。对于没有所有者关系的实体,您可以创建具有显式实体组父级的实体。

您可以将所有对象放在同一个实体组中,但如果太多用户尝试同时修改实体组中的对象,则可能会出现一些争用。如果每个对象都在其自己的实体组中,则您无法执行任何有意义的事务。你想做一些介于两者之间的事情。

一种解决方案是将 Track 和 Run 放在同一实体组中。您可以通过让 Track 包含运行列表来实现此目的(如果您这样做,则 Track 可能不需要运行计数、唯一驱动程序计数和最佳时间;可以在需要时计算它们)。如果您不希望 Track 具有运行列表,则可以使用 无主一对多关系,并指定运行的实体组父级作为其轨道(请参阅 此页面)。无论哪种方式,如果运行与其轨道位于同一实体组中,则您可以执行涉及运行及其部分/全部轨道的事务。

对于许多大型系统,不是使用事务来实现一致性,而是通过进行幂等< /a>.例如,如果 Driver 和 Run 不在同一实体组中,您可以通过首先执行查询来获取过去某个日期之前的所有运行计数来更新 Driver 的运行计数,然后在事务中,使用新的计数和上次计算的日期更新驱动程序。

请记住,在使用日期时,机器可能会出现某种时钟漂移,这就是为什么我建议过去使用日期。

With Google App Engine, all of the datastore operations must be on entities in the same entity group. This is because your data is usually stored across multiple tables, and Google App Engine cannot do transactions across multiple tables.

Entities with owned one-to-one and one-to-many relationships are automatically in the same entity group. So if an entity contains a reference to another entity, or a collection of entities, you can read or write to both in the same transactions. For entities that don't have an owner relationship, you can create an entity with an explicit entity group parent.

You could put all of the objects in the same entity group, but you might get some contention if too many users are trying to modify objects in an entity group at the same time. If every object is in its own entity group, you can't do any meaningful transactions. You want to do something in between.

One solution is to have Track and Run in the same entity group. You could do this by having Track contain a List of Runs (if you do this, then Track might not need run-count, unique-driver-count and best-time; they could be computed when needed). If you do not want Track to have a List of Runs, you can use an unowned one-to-many relationship and specify the entity group parent of the Run be its Track (see "Creating Entities With Entity Groups" on this page). Either way, if a Run is in the same entity group as its track, you could do transactions that involve a Run and some/all of its Tracks.

For many large systems, instead of using transactions for consistency, changes are done by making operations that are idempotent. For instance, if Driver and Run were not in the same entity group, you could update the run-count for a Driver by first doing a query to get the count of all runs before some date in the past, then, in a transaction, update the Driver with the new count and the date when it was last computed.

Keep in mind when using dates that machines can have some kind of a clock drift, which is why I suggested using a date in the past.

葬﹪忆之殇 2024-10-01 06:26:34

我想我找到了一个横向但仍然干净的解决方案,它在我的域模型中仍然有意义。

领域模型略有变化如下:

驱动程序 - 键(字符串 ID)、驱动程序统计信息 - 例如。 id="迈克尔",运行=17

曲目 - key(string-id)、曲目统计 - 例如。 id="蒙扎",bestTime=157

RunData - 键(字符串 ID)、统计数据 - 例如。 id="Michael-Monza-20101010",时间=148

TrackRun - key(Track/string-id), track-stats-updated - 例如。 id =“蒙扎/迈克尔-蒙扎-20101010”,track-stats-updated = false

DriverRun - 密钥(驱动程序/字符串 ID)、驱动程序统计信息更新 - 例如。 id="Michael/Michael-Monza-20101010",driver-stats-updated=true

我现在可以立即或在我自己的时间内使用运行的统计数据自动(即精确)更新赛道的统计数据。 (与驱动程序/运行统计数据相同)。

所以基本上我必须以非常规的关系方式稍微扩展一下我对问题建模的方式。你怎么认为?

I think I found a lateral but still clean solution which still makes sense in my domain model.

The domain model changes slightly as follows:

Driver - key(string-id), driver-stats - ex. id="Michael", runs=17

Track - key(string-id), track-stats - ex. id="Monza", bestTime=157

RunData - key(string-id), stat-data - ex. id="Michael-Monza-20101010", time=148

TrackRun - key(Track/string-id), track-stats-updated - ex. id="Monza/Michael-Monza-20101010", track-stats-updated=false

DriverRun - key(Driver/string-id), driver-stats-updated - ex. id="Michael/Michael-Monza-20101010", driver-stats-updated=true

I can now update atomically (i.e. precisely) the statistics of a Track with the statistics from a Run, immediately or in my own time. (And same with the Driver / Run statistics).

So basically I have to expand a little bit the way I model my problem, in a non-conventional relational way. What do you think?

篱下浅笙歌 2024-10-01 06:26:34

意识到这已经晚了,但是..

您见过这种银行账户转账方法吗?
http://blog.notdot.net/2009/9/Distributed -Transactions-on-App-Engine

,则稍后再拾取碎片。

在我看来,您可以通过将增量计数器作为 IncrementEntity 分成两个步骤来执行类似的操作并对其进行处理,如果事务失败等 博客:

  1. 在一笔交易中,扣除所需的
    来自付款账户的金额,以及
    创建一个 Transfer 子实体到
    记录下来,指定接收
    “目标”字段中的帐户,以及
    将“其他”字段留空
    现在。
  2. 在第二笔交易中,添加
    所需金额到收款人
    帐户,并创建一个 Transfer child
    实体来记录这一点,指定
    “目标”字段中的付款帐户,
    以及创建于的 Transfer 实体
    “其他”字段中的步骤 1。
  3. 最后,
    更新创建的传输实体
    第 1 步,将“其他”字段设置为
    我们在第 2 步中创建的传输。

博客中有 Python 代码示例,但应该很容易适应

realize this is late, but..

Have you seen this method for Bank Account transfers?
http://blog.notdot.net/2009/9/Distributed-Transactions-on-App-Engine

It seems to me that you could do something similar by breaking out your increment counters into two steps as a IncrementEntity and process that, picking up the pieces later if a transaction fails etc.

From the blog:

  1. In a transaction, deduct the required
    amount from the paying account, and
    create a Transfer child entity to
    record this, specifying the receiving
    account in the 'target' field, and
    leaving the 'other' field blank for
    now.
  2. In a second transaction, add the
    required amount to the receiving
    account, and create a Transfer child
    entity to record this, specifying the
    paying account in the 'target' field,
    and the Transfer entity created in
    step 1 in the 'other' field.
  3. Finally,
    update the Transfer entity created in
    step 1, setting the 'other' field to
    the Transfer we created in step 2.

The blog has code examples in Python, but is should be easy to adapt

马蹄踏│碎落叶 2024-10-01 06:26:34

关于此主题有一个有趣的 google io 会议 http://www.google.com/events/io/2010/sessions/high-throughput-data-pipelines-appengine.html

我想您可以更新运行统计信息,然后触发两个任务来更新驱动程序和单独的轨道。

There's an interesting google io session on this topic http://www.google.com/events/io/2010/sessions/high-throughput-data-pipelines-appengine.html

I guess you could update the Run stats and then fire two tasks to update the Driver and the Track individually.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文