Google App Engine: эффективные большие удаления (около 90000 в день)

У меня есть приложение, в котором есть только одна модель с двумя StringProperties.

Первоначальное количество сущностей составляет около 100 миллионов (я загружу их с помощью массового загрузчика).

Каждые 24 часа я должен удалять около 70000 сущностей и добавлять 100000 сущностей. У меня вопрос: как лучше всего удалить эти объекты?

Есть ли способ избежать извлечения объекта перед его удалением? Мне не удалось найти способ сделать что-то вроде:

DELETE from xxx WHERE foo1 IN ('bar1', 'bar2', 'bar3', ...)

Я понимаю, что движок приложения предлагает предложение IN (хотя и с максимальной длиной 30 (из-за максимального количества отдельных запросов на запрос GQL 1)), но мне это все еще кажется странным, потому что мне придется получить объекты x, а затем удалить их снова (делая два вызова RPC для каждого объекта).

Примечание: если объект не найден, его следует игнорировать.

РЕДАКТИРОВАТЬ: добавлена ​​информация о проблеме

Эти сущности - просто домены. Первая строка - это SLD, а вторая - TLD (без поддоменов). Приложение можно использовать для выполнения запроса, подобного следующему: http: // [...] /available/stackoverflow.com. Приложение вернет объект json True / False.

Почему у меня так много сущностей? Потому что хранилище данных содержит все зарегистрированные домены (на данный момент .com). Я не могу выполнять запросы Whois в каждом случае из-за TOS и задержки. Поэтому я сначала заполняю хранилище данных целым файлом зоны, а затем ежедневно добавляю / удаляю зарегистрированные / удаленные домены ... Проблема в том, что это довольно большие количества, и мне нужно найти способ снизить затраты и добавлять / удалять 2 * ~ 100000 доменов в день.

Примечание: вычисления практически не выполняются, поскольку запрос доступности просто проверяет, существует ли домен в хранилище данных!

1: 'Для любого один запрос GQL. ' (http://code.google.com/appengine/docs/python/datastore/gqlreference.html)


person o1iver    schedule 07.08.2011    source источник


Ответы (2)


Если вы этого еще не сделали, вы должны использовать для этого key_names.

Вам понадобится что-то вроде модели:

class UnavailableDomain(db.Model):
    pass

Затем вы заполните свое хранилище данных следующим образом:

UnavailableDomain.get_or_insert(key_name='stackoverflow.com')
UnavailableDomain.get_or_insert(key_name='google.com')

Затем вы запросите доступные домены с помощью чего-то вроде:

is_available = UnavailableDomain.get_by_key_name('stackoverflow.com') is None

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

free_domains = ['stackoverflow.com', 'monkey.com']
db.delete(db.Key.from_path('UnavailableDomain', name) for name in free_domains)

Я бы по-прежнему рекомендовал группировать удаления примерно по 200 на RPC, если ваш список free_domains действительно большой.

person Chris Farmiloe    schedule 07.08.2011
comment
И это должно иметь лучшую производительность / наименьшие затраты в этом случае? - person o1iver; 07.08.2011
comment
Выполнение операций с хранилищем данных напрямую с помощью ключей настолько дешево, насколько это возможно, а группирование операций - лучший способ снизить нагрузку на ЦП при выполнении нескольких действий. Вы захотите поиграть с размером пакетов, которые нужно удалить за раз, чтобы удалить столько ключей, не превышая 1 МБ на ограничение RPC и не достигая каких-либо тайм-аутов. Также стоит отметить, что в ближайшем будущем цены не будут зависеть от ЦП. - person Chris Farmiloe; 07.08.2011
comment
Ах. Это хорошие новости с учетом новых цен. И да, я поиграю с размером, хотя, если не считать начального списка, он не слишком большой (около 1 МБ на 50000 доменов). Спасибо! - person o1iver; 07.08.2011
comment
Почему-то пока не работает. Я получаю сообщение об ошибке «Невозможно работать с разными группами сущностей в транзакции». Я предполагаю, что это связано с тем фактом, что все эти объекты являются корневыми объектами и, следовательно, не являются частью одной и той же группы. Означает ли это, что я не могу использовать такие ключи в пакетном режиме (без предоставления им всем родителя)? - person o1iver; 07.08.2011
comment
вы используете их в транзакции? Если так ... не надо :) - person Chris Farmiloe; 07.08.2011
comment
Я думаю, что get_or_insert () (из вашего примера, я не использовал это раньше) по умолчанию является транзакционной операцией ... Итак, я думаю, вопрос в том, могу ли я все еще делать это с помощью ключей? Казалось бы, если бы я хотел использовать ключи, мне пришлось бы перебрать транзакции ИЛИ создать общего родителя (вероятно, не очень хорошая идея). Что вы думаете? - person o1iver; 07.08.2011
comment
Ограничение API в 1 МБ больше не является проблемой для этого - SDK автоматически разбивает большие пакеты на более мелкие, чтобы избежать слишком больших RPC для вас. - person Nick Johnson; 08.08.2011
comment
@ o1iver get_or_insert действительно транзакционная функция. Однако, поскольку у вас нет данных по каждому объекту, он вам не нужен - вы можете просто перезаписать существующий объект, если он существует. Отбросьте get_or_insert и просто создайте объект с помощью UnavailableDomain(key_name='stackoverflow.com'). Однако удивительно, что вы получаете эту ошибку - вы пытаетесь использовать get_or_insert из другой транзакции? - person Nick Johnson; 08.08.2011
comment
@nick: Спасибо за два очка (и, кстати, отличный блог :-)). Я не использую get_or_insert «внутри другой транзакции», но из того, что я мог видеть, google/appengine/ext/db/__init__.py get_or_insert вызывает сам run_in_transaction(txn). Но я не буду использовать get_or_insert и просто передам объект прямо в db.put(). - person o1iver; 08.08.2011

рассматривали ли вы библиотеку appengine-mapreduce. Он поставляется с библиотекой конвейеров, и вы можете использовать их для:

  • Создайте конвейер для общей задачи, который вы будете запускать через cron каждые 24 часа.
  • «Общий» конвейер запустит средство сопоставления, которое фильтрует ваши объекты и дает операции удаления.
  • после того, как средство сопоставления удаления завершится, «общий» конвейер может вызвать конвейер «импорта», чтобы запустить часть создания вашей сущности.
  • Затем api конвейера может отправить вам электронное письмо, чтобы сообщить о его статусе
person Chris Farmiloe    schedule 07.08.2011
comment
Я только что быстро взглянул на библиотеку конвейера (не знал, что она существует, поэтому спасибо), дело в том, что я думаю, что это немного перебор. Я никогда не выполняю никаких вычислений с этими объектами (они требуются только для того, чтобы я мог проверить, существует ли он). Не изучив подробно библиотеку: разве мне все равно не придется делать RPC хранилища данных при фильтрации? Я просто хочу минимизировать время процессора ... - person o1iver; 07.08.2011
comment
Может быть, тогда вам нужно объяснить немного больше о вашем процессе, поскольку похоже, что вы должны что-то делать с ключами / key_names. - person Chris Farmiloe; 07.08.2011
comment
Я добавил дополнительную информацию о моем текущем сценарии использования выше! Спасибо за помощь! - person o1iver; 07.08.2011