Простая операция хранилища данных Google App Engine занимает слишком много времени: вызывает исключение DeadlineExceededException / DatastoreTimeoutException

#java #google-app-engine

#java #google-app-engine

Вопрос:

У меня есть довольно простое Java-приложение App Engine, в котором есть учетные записи, заказы и элементы заказа — ничего сумасшедшего.

Только за последние 12 часов я начал получать исключения из некоторого довольно простого кода, который добавляет заказы в учетные записи, а затем сохраняет их.

Я создал тривиальный тестовый сервлет для репликации проблемы, он выглядит следующим образом:

 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 через минуту или две. В процессе используется слишком большая загрузка процессорного времени.

Я бы оценил, что в учетной записи будет менее 2000 дочерних элементов Orders, в каждом заказе будет 1-3 дочерних элемента OrderItem.

Мой вопрос в том, почему это внезапно начало давать сбой, если это сработало, чтобы добавить 1000 заказов, которые уже есть. И я пропустил что-то важное? нужно ли добавлять индексы? действительно ли хранилище данных может быть таким медленным? Я злоупотребляю этим, не должен ли я иметь дочерние отношения с таким количеством дочерних элементов — возможно, набор ключей был бы лучшим подходом?

Ответ №1:

На одном из слайдов Google App Engine упоминается, что для свойства list производительность хранилища данных будет низкой, если номер элемента больше «500». Этот слайд очень старый, я не уверен, что ограничение осталось прежним, но это может быть проблемой в вашем случае. И имеет смысл не хранить слишком много элементов в свойстве list. На этом слайде пользователю предлагается использовать класс extend для этой ситуации. Например:

 class Orders:
     Account account    // the account these order belong to.
     List<Order> orders // the orders, limit to 500 items
  

если у вас более 500 заказов, просто добавьте другой экземпляр Orders.

Ответ №2:

Я прекратил использовать GetOrders () в пользу интерфейса запроса, подобного этому:

     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 Group здесь:
http://groups.google.com/group/google-appengine-java/browse_thread/thread/6011d08025398254 #

Кроме того, предложение lucemia сработало бы, но я думаю, что управлять кодом сложнее.