GAE обновляет разные поля одной и той же сущности

UserA и UserB изменяют objectA.filedA objectA.filedB соответственно и одновременно. Поскольку они не меняют одно и то же поле, можно подумать, что нет совпадений. Это правда? или реализация pm.makePersistnace() фактически перекрывает весь объект... полезно знать...

2 ответа

Это то, что вы предполагаете, происходит?

  1. Алиса возвращает объект. { a = 1, b = "Foo" }
  2. Боб забирает объект. { a = 1, b = "Foo" }
  3. Алиса изменяет объект, изменяя b. { a = 1, b = "Бар"}
  4. Боб изменяет объект, изменяя. { a = 2, b = "Foo" }
  5. Алиса сохраняет свою копию объекта. {a = 1, b = "Бар"}
  6. Боб сохраняет свою копию объекта. { a = 2, b = "Foo" }

Копия объекта Бобом перезапишет копию в хранилище данных, потому что он сохраняет весь свой объект, а не только набор измененных полей. Или, в общем, какой бы из них не сохранился в последний раз, весь их объект сохраняется в хранилище данных.

Это можно исправить, выполнив каждую из операций get-set-and-persist в транзакции. Транзакции App Engine не блокируют весь объект от извлечения или изменения локально, они просто предотвращают сохранение других пользователей. Так:

  1. Алиса получает объект в транзакции. { a = 1, b = "Foo" }
  2. Боб получает объект в транзакции. { a = 1, b = "Foo" }
  3. Алиса изменяет объект, изменяя b. { a = 1, b = "Бар"}
  4. Боб изменяет объект, изменяя. { a = 2, b = "Foo" }
  5. Алиса пытается сохранить объект, но не может, потому что Боб открыл его в транзакции. Будет сгенерировано исключение, которое Алиса поймает, завершив ее перевод и повторив попытку...
  6. Боб сохраняет объект без проблем, потому что Алиса завершила свою транзакцию {a = 2, b = "Foo"}
  7. Алиса повторяет свою транзакцию, получая снова. { a = 2, b = "Foo" }
  8. Алиса изменяет объект, изменяя b. { a = 2, b = "Бар"}
  9. Алиса сохраняет объект и работает, потому что ни у кого больше нет открытой транзакции. {a = 2, b = "Бар"}

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

Это называется оптимистической блокировкой.

Спасибо за Ваш ответ. Жаль, что реализация makePersistence() предназначена для записи целого объекта в хранилище данных, а не только в поля, которые были изменены. Этот факт фактически заставляет ЛЮБОЕ обновление общего объекта в GAE использовать транзакцию как правило. Более того - в таких случаях вы должны реализовать "механизм повторных попыток", поскольку в транзакции может возникнуть исключение.

Итак... обновление любого общего объекта в GAE должно ВСЕГДА иметь эти дополнения:

  • сделать это в рамках транзакции
  • реализовать механизм повтора

Большинство примеров Google на их сайте фактически не принимают это во внимание. Как будто они предполагают, что большинство приложений не будут использовать общие объекты

Например ( http://code.google.com/appengine/docs/java/datastore/creatinggettinganddeletingdata.html):

public void updateEmployeeTitle(User user, String newTitle) {
    PersistenceManager pm = PMF.get().getPersistenceManager();
    try {
        Employee e = pm.getObjectById(Employee.class, user.getEmail());
        if (titleChangeIsAuthorized(e, newTitle) {
            e.setTitle(newTitle);
        } else {
            throw new UnauthorizedTitleChangeException(e, newTitle);
        }
    } finally {
        pm.close();
    }
}

ИЛИ ЖЕ:

public void updateEmployeeTitle(Employee e, String newTitle) {
    if (titleChangeIsAuthorized(e, newTitle) {
        e.setTitle(newTitle);
        PersistenceManager pm = PMF.get().getPersistenceManager();
        try {
            pm.makePersistent(e);
        } finally {
            pm.close();
        }
    } else {
        throw new UnauthorizedTitleChangeException(e, newTitle);
    }
}