0

Наше приложение GAE создает локальную копию реляционной базы данных другого сайта в NDB. Существует 4 типа сущностей: пользователь, таблица, строка, поле. Каждый пользователь имеет кучу таблиц, каждая таблица имеет ряд строк, каждая строка содержит кучу полей.Проблема разбора хранилища GAE

SomeUser> SomeTable> Arow> Afield

Таким образом, каждый пользователь становится один объект группы. Мне нужна функция, где я могу очистить все таблицы (и их строки) для определенного пользователя. Каков правильный способ удаления всех таблиц и всех строк, при этом избегая ограничительного предела в ~ 5 операций в секунду.

Текущий код получает TransactionFailedError из-за разногласий в группе Entity. (подробно, что я упускать из виду, что мы хотим, чтобы удалить таблицы с «сервис» атрибута установлено определенное значение)

def delete_tables_for_service(user, service): 
    tables = Tables.query(Tables.service == service, ancestor=user.key).fetch(keys_only=True) 
    for table in tables: 
     keys = [] 
     keys += Fields.query(ancestor=table).fetch(keys_only=True) 
     keys += TableRows.query(ancestor=table).fetch(keys_only=True) 
     keys.append(table) 
     ndb.delete_multi(keys) 
+1

Похоже, что вы удаляете каждый объект таблицы (и связанные дочерние объекты) отдельно. Не видя контекста в вашем коде (и не знакомого с транзакциями на Python), я не могу определить, является ли это транзакцией для каждой таблицы. Вы пытались переместить свой 'ndb.delete_multi (keys)' вне цикла 'for', чтобы он удалял все таблицы? В противном случае, для пользователя с 10 таблицами, похоже, вы пытаетесь выполнить 10 транзакций. – tx802

ответ

1

Если все объекты, которые вы удаляете, находятся в одной группе сущностей, попробуйте удалить их все за одну транзакцию. Без явной транзакции каждое удаление происходит в своей собственной транзакции, и все транзакции должны выстраиваться в линию (посредством конфликтов и попыток) для изменения группы объектов.

+0

Это исправление, которое я подозреваю, будет работать, но семантически это не имеет смысла для меня, так как мне не нужно, чтобы одна строка зависела от удаления другого. Если один из них проходит, это должно быть совершено. Объединение их в транзакцию, чтобы обойти конкурентный предел, похоже на взлома. – Neil

+0

Лучше визуализировать, что «строка» в хранилище данных фактически является группой сущностей, а не сущностью. Со всеми объектами в одной группе вы фактически изменяете одну строку несколько раз. Внесение всего этого в транзакцию просто делает все групповые обновления сразу, что вы намерены в этом случае. –

+0

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

0

Вы уверены, что это утверждение на основе, или, возможно, потому, что приведенный выше код выполняется в транзакция? Быстрое исправление может быть, чтобы увеличить число повторных попыток и включить кросс-групповых операций для этого метода:

@ndb.transactional(retries=5, xg=True) 

Вы можете прочитать больше об этом здесь: https://developers.google.com/appengine/docs/python/ndb/transactions. Если это не преступник, возможно, подумайте о том, чтобы отложить или запустить удаление асинхронно, чтобы они выполнялись со временем и в меньших партиях. Трюк с NDB заключается в том, чтобы делать небольшие всплески работы регулярно, по сравнению с большим количеством работы нечасто. Вот один из способов превратить этот код в асинхронную единицу работы:

def delete_tables_for_service(user, service): 
    tables = Tables.query(Tables.service == service, ancestor=user.key).fetch(keys_only=True) 
    for table in tables: 
     # Delete fields 
     fields_keys = Fields.query(ancestor=table).fetch(keys_only=True) 
     ndb.delete_multi_async(fields_keys) 

     # Delete table rows 
     table_rows_keys = TableRows.query(ancestor=table).fetch(keys_only=True) 
     ndb.delete_multi_async(table_rows_keys) 

     # Finally delete table itself 
     ndb.delete_async(table.key) 

Если вы хотите получить больше контроля над удалениями, повторными попытки, неудачами, вы можете либо использовать очереди задач, или просто использовать библиотеку Defer (https://developers.google.com/appengine/articles/deferred):

  1. Turn отложила в вашем app.yaml
  2. Изменить вызовы на ndb.delete_multi по отложенному:

    def delete_tables_for_service(user, service): 
        tables = Tables.query(Tables.service == service, ancestor=user.key).fetch(keys_only=True) 
        for table in tables: 
         keys = [] 
         keys += Fields.query(ancestor=table).fetch(keys_only=True) 
         keys += TableRows.query(ancestor=table).fetch(keys_only=True) 
         keys.append(table) 
         deferred.defer(_deferred_delete_tables_for_keys, keys) 
    
    def _deferred_delete_tables_for_keys(keys): 
        ndb.delete_multi(keys) 
    
Смежные вопросы