简单的Google App Engine数据存储区操作耗时太长:导致DeadlineExceededException / DatastoreTimeoutException

时间:2011-05-17 05:31:26

标签: java google-app-engine

我有一个相当简单的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个订单。我错过了一些重要的东西吗?我需要添加索引吗?数据存储真的会这么慢吗?我是否滥用它,如果我没有与这么多孩子建立孩子关系 - 也许一组钥匙会是更好的方法吗?

2 个答案:

答案 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的建议可行,但我认为管理代码比较棘手。