我有一个相当简单的App Engine Java应用程序,它有账户,订单和OrderItems - 没什么好疯的。
就在最近12个小时内,我开始从一些相当直接的代码中抛出异常,这些代码会向帐户添加订单然后保存它们。
我创建了一个简单的测试servlet来复制问题,它看起来像这样:
public void doGet(HttpServletRequest req, HttpServletResponse resp)
throws IOException {
String key = req.getParameter("key");
PersistenceManager pm = PMF.get().getPersistenceManager();
Account account = pm.getObjectById(Account.class, KeyFactory.stringToKey(key));
Order order = new Order();
order.setExternalOrderID("ASHLEY-TESTING");
Item item = new Item();
item.setSku("ASHLEY-WIDGET-A");
item.setQuantity(2);
Item item2 = new Item();
item2.setSku("ASHLEY-WIDGET-B");
item2.setQuantity(2);
order.addItem(item);
order.addItem(item2);
account.addOrder(order);
order.setAccount(account);
pm.makePersistent(order);
pm.close();
}
addOrder is implemented as a pretty standard lazy init:
public void addOrder(Order order) {
if (getOrders() == null) {
setOrders(new ArrayList<Order>());
}
getOrders().add(order);
}
The relevant parts of the entities:
@PersistenceCapable
public class Account {
//...
@Persistent(mappedBy="account")
@javax.jdo.annotations.Order(extensions = @Extension(vendorName="datanucleus", key="list-ordering", value="orderDate desc"))
private List<Order> orders;
//...
}
and the Order has an account field:
@Persistent
private Account account;
此代码在account.addOrder()
行失败。如果它直接在浏览器中运行,则会因DeadlineExceededException(30秒后)而失败,如果我将其排队以通过任务队列运行,则会在一两分钟后因DatastoreTimeoutException而失败。它在此过程中使用卡车负载的CPU时间。
我估计账户下面会有少于2000个订单子项,每个订单包含1-3个OrderItem子项。
我的问题是,为什么这会突然开始失败,它已经努力添加已经在那里的1000个订单。我错过了一些重要的东西吗?我需要添加索引吗?数据存储真的会这么慢吗?我是否滥用它,如果我没有与这么多孩子建立孩子关系 - 也许一组钥匙会是更好的方法吗?
答案 0 :(得分:0)
在其中一个谷歌应用引擎幻灯片中,它提到对于列表属性,当项目编号大于“500”时,数据存储区性能会很差。这张幻灯片很旧,我不确定现在的限制是否仍然相同,但这可能是你的情况的问题。并且在list属性中不存储太多项是有意义的。在该幻灯片中,它建议用户使用扩展类来处理这种情况。例如:
class Orders:
Account account // the account these order belong to.
List<Order> orders // the orders, limit to 500 items
如果订单超过500个,只需添加另一个订单实例。
答案 1 :(得分:0)
我已经停止使用getOrders()来支持Query接口,如下所示:
Query query = pm.newQuery(Order.class);
query.setFilter("account == accountKey");
query.declareParameters(Key.class.getName() + " accountKey");
query.setOrdering("orderDate desc");
query.setRange(0, numOrders);
为了向帐户添加订单,我只需在新订单上设置父属性并保留它,GAE处理创建密钥并将其与父级关联。
order.setAccount(account);
pm.makePersistent(order);
我从GAE / J小组那里得到了一些提示: http://groups.google.com/group/google-appengine-java/browse_thread/thread/6011d08025398254#
此外,lucemia的建议可行,但我认为管理代码比较棘手。