Атомный раздел Google App Engine? - PullRequest
2 голосов
/ 01 апреля 2010
  • Скажем, вы извлекаете набор записей из хранилища данных (что-то вроде: выберите * из MyClass, где зарезервировано = 'false').

  • как я могу убедиться, что другой пользователь не установил зарезервированное значение по-прежнему false?

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

Любое решение, которое мне не хватает - трудно поверить, что в этой среде нет способа выполнить атомарную операцию.

(кстати, я мог бы использовать 'syncronize' внутри сервлета, но я думаю, что это недопустимо, поскольку нет способа гарантировать, что существует только один экземпляр объекта сервлета, не так ли? То же самое относится и к решению статических переменных)

Есть идеи, как решить?

(вот решение Google:

http://code.google.com/appengine/docs/java/datastore/transactions.html#Entity_Groups

посмотрите на:

Key k = KeyFactory.createKey("Employee", "k12345");
Employee e = pm.getObjectById(Employee.class, k);
e.counter += 1;
pm.makePersistent(e);

'Для этого требуется транзакция, поскольку значение может быть обновлено другим пользователем после того, как этот код извлечет объект, но до того, как он сохранит измененный объект. Без транзакции запрос пользователя будет использовать значение счетчика до обновления другого пользователя, а сохранение перезапишет новое значение. При транзакции приложению сообщается об обновлении другого пользователя. Если объект обновляется во время транзакции, транзакция завершается с ошибкой. Приложение может повторить транзакцию, чтобы использовать новые данные

Ужасное решение, не правда ли?

Ответы [ 2 ]

4 голосов
/ 01 апреля 2010

Вы правы, что не можете использовать синхронизацию или статическую переменную.

Вы ошибаетесь в том, что в среде App Engine невозможно выполнить атомарное действие. (Посмотрите, что атомарный означает здесь ). Когда вы делаете транзакцию, она атомарна - либо все происходит, либо ничего не происходит. Похоже, что вы хотите, это какой-то глобальный механизм блокировки. В мире СУБД это может быть что-то вроде «выбрать для обновления» или установка уровня изоляции транзакции для сериализованных транзакций. Ни один из этих типов опций не является очень масштабируемым. Или, как вы сказали бы, они оба ужасные решения:)

Если вам действительно нужна глобальная блокировка в движке приложения, вы можете сделать это, но это будет ужасно и серьезно ухудшит масштабируемость. Все, что вам нужно сделать, это создать некую сущность CurrentUser, в которой вы сохраните имя текущего пользователя, который имеет глобальную блокировку. Прежде чем позволить пользователю что-либо делать, вам необходимо сначала убедиться, что ни один пользователь не указан в списке CurrentUser, а затем записать ключ этого пользователя в объект CurrentUser. Чек и запись должны быть в транзакции. Таким образом, только один пользователь будет «Текущим» и поэтому будет иметь глобальную блокировку.

0 голосов
/ 01 апреля 2010

Вы имеете в виду, как это:

   public void func(Data data2) {

        String query = "select from " + objectA.class.getName()
                + " where reserved == false";
        List<objectA> Table = (List<objectA>) pm.newQuery(
                query).execute();

        for (objectA row : Table)
        {
            Data data1 = row.getData1();
            row.setWeight(JUtils.CalcWeight(data1, data2));
        }

        Collections.sort(Table, new objectA.SortByWeight());

        int retries = 0;
        int NUM_RETRIES = 10;
        for (int i = 0; i < Table.size() ; i++)
        {   
            retries++;      
            pm.currentTransaction().begin(); //    <---- BEGIN
            ObjectA obj = pm.getObjectById(Table.get(i).class, Table.get(i).getKey());
            if (obj .getReserved() == false)   // <--- CHECK if still reserved
                obj.setReserved(true);
            else
                break;      

            try
            {
                pm.currentTransaction().commit();
                break;
            } 
            catch (JDOCanRetryException ex)
            {
                if (j == (NUM_RETRIES - 1))
                {
                    throw ex;
                }
                i--; //so we retry again on the same object
            }
        }
        }
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...