我有一个简单的域模型,如下
驱动程序 - 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?
发布评论
评论(4)
对于 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.
我想我找到了一个横向但仍然干净的解决方案,它在我的域模型中仍然有意义。
领域模型略有变化如下:
我现在可以立即或在我自己的时间内使用运行的统计数据自动(即精确)更新赛道的统计数据。 (与驱动程序/运行统计数据相同)。
所以基本上我必须以非常规的关系方式稍微扩展一下我对问题建模的方式。你怎么认为?
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:
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?
意识到这已经晚了,但是..
您见过这种银行账户转账方法吗?
http://blog.notdot.net/2009/9/Distributed -Transactions-on-App-Engine
,则稍后再拾取碎片。
在我看来,您可以通过将增量计数器作为 IncrementEntity 分成两个步骤来执行类似的操作并对其进行处理,如果事务失败等 博客:
博客中有 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:
The blog has code examples in Python, but is should be easy to adapt
关于此主题有一个有趣的 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.