2010-03-31 4 views
2
  • Скажите, что вы получаете набор записей из хранилища данных (что-то вроде: select * from MyClass where reserved = 'false').Атомная секция Google App Engine?

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

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

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

(btw - я мог бы использовать «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); 

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

Ужасное решение, не так ли?

+4

Нет, это не попало.Общий принцип называется «оптимистичная блокировка», и он более эффективен, чем взаимное исключение («пессимистическая блокировка») в ситуациях, когда утверждение достаточно редко. Это также более удобно для массово распределенных реализаций. –

+0

это ужасно, если у вас много клиентов, которые пытаются зарезервировать комнату одновременно и перед каждым резервом вам нужно выполнить выбор! – bach

+0

не говоря уже о том, что после выбора вы держите ВСЕ незаслуженные номера, поэтому любой другой запрос клиента в это время вызовет исключение ... Механизм блокировки - это не мое изобретение ... это тривиально и необходимо ... – bach

ответ

4

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

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

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

+0

Надеюсь, это очевидно, что эта операция (захват CurrentUser в транзакции) будет хуже, чем просто выполнить первоначальную операцию обновления в транзакции в первую очередь ... если нет, прочитав этот комментарий, я надеюсь, что это будет :) –

+0

Спасибо за ваши ответы. - Где написано, что синхронизация не может использоваться в GAE? - Я не ищу «атомарного» в смысле БД, но в смысле языков программирования. . На самом деле мне не нужно больше, чем 1 JVM, запускающее мое приложение, так как это локальное приложение. – bach

+0

используя цикл while во всех клиентах, где внутри этого цикла есть вызов в хранилище данных, будет есть все мои DB/CPU qouata, не так ли? – bach

0

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

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 
      } 
     } 
     } 
+0

ОК, я думаю, я прибил его! – bach