Я хотел бы стереть все данные определенного типа в Google App Engine. Что лучший способ сделать это? Я написал сценарий удаления (взломать), но поскольку данных так много, истекло время ожидания после нескольких сотен записей.






Предположительно ваш взлом был примерно таким:
# Deleting all messages older than "earliest_date"
q = db.GqlQuery("SELECT * FROM Message WHERE create_date < :1", earliest_date)
results = q.fetch(1000)
while results:
db.delete(results)
results = q.fetch(1000, len(results))
Как вы говорите, если данных достаточно, вы достигнете тайм-аута запроса, прежде чем он пройдёт через все записи. Вам придется повторно вызывать этот запрос несколько раз извне, чтобы убедиться, что все данные были стерты; Достаточно легко сделать, но вряд ли идеально.
Консоль администратора, похоже, не предлагает никакой помощи, поскольку (по моему собственному опыту с ней), похоже, она разрешает перечисление только сущностей определенного типа, а затем удаление постранично.
При тестировании мне пришлось очистить мою базу данных при запуске, чтобы избавиться от существующих данных.
Из этого я мог бы сделать вывод, что Google работает по принципу дешевизны диска, и поэтому данные обычно теряются (индексы для избыточных данных заменяются), а не удаляются. Учитывая, что в настоящий момент для каждого приложения доступен фиксированный объем данных (0,5 ГБ), это не очень поможет пользователям, не использующим Google App Engine.
К сожалению, нет возможности легко выполнить массовое удаление. Лучше всего написать сценарий, который удаляет разумное количество записей за один вызов, а затем вызывает его несколько раз - например, заставляя ваш сценарий удаления возвращать перенаправление 302 всякий раз, когда есть другие данные для удаления, а затем извлекать его с помощью «wget - -max-redirect = 10000 "(или другое большое число).
302 редиректа не будут работать, GAE ограничивает количество перенаправлений на запрос (а также совокупное время, которое занимает запрос).
Слишком поздно для этого комментария на три года, но это неправда и никогда не было: App Engine не ограничивает ни то, ни другое; браузеры ограничивают общее количество перенаправлений, которым они будут следовать. App Engine не суммирует время, затраченное на несколько запросов.
Конечно, мой первоначальный ответ теперь устарел, учитывая наличие mapreduce и консоли администратора хранилища данных.
официальный ответ от Google заключается в том, что вам нужно удалять кусками, распределенными по нескольким запросам. Вы можете использовать AJAX, мета обновление или запросить свой URL из скрипта, пока не останется никаких сущностей.
Попробуйте использовать Консоль App Engine, тогда вам даже не придется развертывать какой-либо специальный код
Нет кнопки (AFAIK) для удаления ВСЕГО определенного типа.
Я пробовал db.delete (results) и App Engine Console, и, похоже, ни один из них у меня не работает. Удаление записей из Data Viewer вручную (увеличенный лимит до 200) тоже не помогло, так как я загрузил более 10000 записей. Я закончил писать этот скрипт
from google.appengine.ext import db
from google.appengine.ext import webapp
from google.appengine.ext.webapp.util import run_wsgi_app
import wsgiref.handlers
from mainPage import YourData #replace this with your data
class CleanTable(webapp.RequestHandler):
def get(self, param):
txt = self.request.get('table')
q = db.GqlQuery("SELECT * FROM "+txt)
results = q.fetch(10)
self.response.headers['Content-Type'] = 'text/plain'
#replace yourapp and YouData your app info below.
self.response.out.write("""
<html>
<meta HTTP-EQUIV = "REFRESH" content = "5; url=http://yourapp.appspot.com/cleanTable?table=YourData">
<body>""")
try:
for i in range(10):
db.delete(results)
results = q.fetch(10, len(results))
self.response.out.write("<p>10 removed</p>")
self.response.out.write("""
</body>
</html>""")
except Exception, ints:
self.response.out.write(str(inst))
def main():
application = webapp.WSGIApplication([
('/cleanTable(.*)', CleanTable),
])
wsgiref.handlers.CGIHandler().run(application)
Уловка заключалась в том, чтобы включить перенаправление в html вместо использования self.redirect. Я готов подождать ночь, чтобы избавиться от всех данных в моей таблице. Надеюсь, команда GAE упростит удаление таблиц в будущем.
Я хочу адаптировать ваше решение для удаления всех элементов одного типа, с date раньше, чем с определенным Date. Я обнаружил несколько опечаток в вашем решении, но меня беспокоят другие опечатки, т.к. я не совсем понимаю код. Например, inst против ints, как устанавливается первое значение txt? Какова цель 5? Так что, если вы обнаружили другие опечатки, укажите их. Мой главный вопрос: похоже, вы удаляете 100 или меньше записей; можно ли заменить for на while db.delete(results):, добавив break в конец предложения except, или это нарушит код?
В настоящее время я удаляю объекты по их ключу, и, похоже, это быстрее.
from google.appengine.ext import db
class bulkdelete(webapp.RequestHandler):
def get(self):
self.response.headers['Content-Type'] = 'text/plain'
try:
while True:
q = db.GqlQuery("SELECT __key__ FROM MyModel")
assert q.count()
db.delete(q.fetch(200))
time.sleep(0.5)
except Exception, e:
self.response.out.write(repr(e)+'\n')
pass
с терминала запускаю curl -N http: // ...
Для меня он возвращает «Произошла ошибка сервера. Обратитесь к администратору». вместо исключения, но работает :)
Приведенный выше код прерывает цикл, вызывая AssertionError и перехватывая его. Это довольно некрасиво! Должен просто сломаться, если q.count () равен 0
Один совет. Я предлагаю вам ознакомиться с remote_api для этих типов использования (массовое удаление, изменение и т. д.). Но даже с удаленным API размер пакета может быть ограничен несколькими сотнями за раз.
Вау ... это полезно, а я даже не слышал об этом. Это то, что я получаю, работая в основном с Java API, в котором нет ничего похожего на remote_api.
Если бы я был параноиком, я бы сказал, что Google App Engine (GAE) не упростил нам удаление данных, если мы этого хотим. Я пропущу обсуждение размеров индексов и того, как они преобразуют 6 ГБ данных в 35 ГБ хранилища (за которые взимается плата). Это другая история, но у них есть способы обойти это - ограничить количество свойств для создания индекса (автоматически сгенерированные индексы) и так далее.
Причина, по которой я решил написать этот пост, заключается в том, что мне нужно «уничтожить» все мои виды в песочнице. Я читал об этом и, наконец, придумал такой код:
package com.intillium.formshnuker;
import java.io.IOException;
import java.util.ArrayList;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.google.appengine.api.datastore.Key;
import com.google.appengine.api.datastore.Query;
import com.google.appengine.api.datastore.Entity;
import com.google.appengine.api.datastore.FetchOptions;
import com.google.appengine.api.datastore.DatastoreService;
import com.google.appengine.api.datastore.DatastoreServiceFactory;
import com.google.appengine.api.labs.taskqueue.QueueFactory;
import com.google.appengine.api.labs.taskqueue.TaskOptions.Method;
import static com.google.appengine.api.labs.taskqueue.TaskOptions.Builder.url;
@SuppressWarnings("serial")
public class FormsnukerServlet extends HttpServlet {
public void doGet(final HttpServletRequest request, final HttpServletResponse response) throws IOException {
response.setContentType("text/plain");
final String kind = request.getParameter("kind");
final String passcode = request.getParameter("passcode");
if (kind == null) {
throw new NullPointerException();
}
if (passcode == null) {
throw new NullPointerException();
}
if (!passcode.equals("LONGSECRETCODE")) {
response.getWriter().println("BAD PASSCODE!");
return;
}
System.err.println("*** deleting entities form " + kind);
final long start = System.currentTimeMillis();
int deleted_count = 0;
boolean is_finished = false;
final DatastoreService dss = DatastoreServiceFactory.getDatastoreService();
while (System.currentTimeMillis() - start < 16384) {
final Query query = new Query(kind);
query.setKeysOnly();
final ArrayList<Key> keys = new ArrayList<Key>();
for (final Entity entity: dss.prepare(query).asIterable(FetchOptions.Builder.withLimit(128))) {
keys.add(entity.getKey());
}
keys.trimToSize();
if (keys.size() == 0) {
is_finished = true;
break;
}
while (System.currentTimeMillis() - start < 16384) {
try {
dss.delete(keys);
deleted_count += keys.size();
break;
} catch (Throwable ignore) {
continue;
}
}
}
System.err.println("*** deleted " + deleted_count + " entities form " + kind);
if (is_finished) {
System.err.println("*** deletion job for " + kind + " is completed.");
} else {
final int taskcount;
final String tcs = request.getParameter("taskcount");
if (tcs == null) {
taskcount = 0;
} else {
taskcount = Integer.parseInt(tcs) + 1;
}
QueueFactory.getDefaultQueue().add(
url("/formsnuker?kind = " + kind + "&passcode=LONGSECRETCODE&taskcount = " + taskcount).method(Method.GET));
System.err.println("*** deletion task # " + taskcount + " for " + kind + " is queued.");
}
response.getWriter().println("OK");
}
}
У меня более 6 миллионов записей. Это много. Я понятия не имею, сколько будет стоить удаление записей (может быть, более экономично не удалять их). Другой альтернативой может быть запрос на удаление всего приложения (песочницы). Но в большинстве случаев это нереально.
Я решил использовать небольшие группы записей (в простом запросе). Я знаю, что могу использовать 500 объектов, но затем я начал получать очень высокие показатели отказов (функция повторного удаления).
Мой запрос от команды GAE: добавьте функцию для удаления всех объектов одного типа за одну транзакцию.
Вы можете использовать очереди задач для удаления фрагментов, например, из 100 объектов. Удаление объектов в GAE показывает, насколько ограничены возможности администратора в GAE. Вы должны работать с пакетами по 1000 или меньше объектов. Вы можете использовать инструмент массовой загрузки, который работает с CSV, но документация не охватывает java. Я использую GAE Java, и моя стратегия удаления включает в себя 2 сервлета, один для фактического удаления, а другой для загрузки очередей задач. Когда я хочу выполнить удаление, я запускаю сервлет загрузки очереди, он загружает очереди, а затем GAE приступает к работе, выполняя все задачи в очереди.
Как это сделать: Создайте сервлет, который удаляет небольшое количество объектов. Добавьте сервлет в очереди задач. Иди домой или займись чем-нибудь другим;) Проверяйте хранилище данных время от времени ...
У меня есть хранилище данных с примерно 5000 объектами, которые я очищаю каждую неделю, и на очистку уходит около 6 часов, поэтому я запускаю задачу в пятницу вечером. Я использую тот же метод для массовой загрузки своих данных, которые составляют около 5000 объектов с примерно дюжиной свойств.
С помощью django настройте URL-адрес:
url(r'^Model/bdelete/$', v.bulk_delete_models, {'model':'ModelKind'}),
Просмотр установки
def bulk_delete_models(request, model):
import time
limit = request.GET['limit'] or 200
start = time.clock()
set = db.GqlQuery("SELECT __key__ FROM %s" % model).fetch(int(limit))
count = len(set)
db.delete(set)
return HttpResponse("Deleted %s %s in %s" % (count,model,(time.clock() - start)))
Затем запустите в PowerShell:
$client = new-object System.Net.WebClient
$client.DownloadString("http://your-app.com/Model/bdelete/?limit=400")
Это сработало для меня:
class ClearHandler(webapp.RequestHandler):
def get(self):
self.response.headers['Content-Type'] = 'text/plain'
q = db.GqlQuery("SELECT * FROM SomeModel")
self.response.out.write("deleting...")
db.delete(q)
Самый быстрый и эффективный способ обработки массового удаления в Datastore - использовать новый mapper API, объявленный в последнем Google I / O.
Если выбранный вами язык - Python, вам просто нужно зарегистрировать свой маппер в файле mapreduce.yaml и определить такую функцию:
from mapreduce import operation as op
def process(entity):
yield op.db.Delete(entity)
На Ява вы должны взглянуть на Эта статья, который предлагает такую функцию:
@Override
public void map(Key key, Entity value, Context context) {
log.info("Adding key to deletion pool: " + key);
DatastoreMutationPool mutationPool = this.getAppEngineContext(context)
.getMutationPool();
mutationPool.delete(value.getKey());
}
Я использую этот подход, хотя он очень интенсивно использует ЦП (не ЦП хранилища данных, ЦП общего назначения).
Если вы используете Java / JPA, вы можете сделать что-то вроде этого:
em = EntityManagerFactoryUtils.getTransactionalEntityManager(entityManagerFactory)
Query q = em.createQuery("delete from Table t");
int number = q.executeUpdate();
Информацию о Java / JDO можно найти здесь: http://code.google.com/appengine/docs/java/datastore/queriesandindexes.html#Delete_By_Query
Спасибо всем, ребята, я получил то, что мне нужно. : D
Это может быть полезно, если у вас есть много моделей БД для удаления, вы можете отправить их в свой терминал. Кроме того, вы можете самостоятельно управлять списком удаления в DB_MODEL_LIST.
Удалить DB_1:
python bulkdel.py 10 DB_1
Удалить всю БД:
python bulkdel.py 11
Вот файл bulkdel.py:
import sys, os
URL = 'http://localhost:8080'
DB_MODEL_LIST = ['DB_1', 'DB_2', 'DB_3']
# Delete Model
if sys.argv[1] == '10' :
command = 'curl %s/clear_db?model=%s' % ( URL, sys.argv[2] )
os.system( command )
# Delete All DB Models
if sys.argv[1] == '11' :
for model in DB_MODEL_LIST :
command = 'curl %s/clear_db?model=%s' % ( URL, model )
os.system( command )
А вот и модифицированная версия кода Александра Фьори.
from google.appengine.ext import db
class DBDelete( webapp.RequestHandler ):
def get( self ):
self.response.headers['Content-Type'] = 'text/plain'
db_model = self.request.get('model')
sql = 'SELECT __key__ FROM %s' % db_model
try:
while True:
q = db.GqlQuery( sql )
assert q.count()
db.delete( q.fetch(200) )
time.sleep(0.5)
except Exception, e:
self.response.out.write( repr(e)+'\n' )
pass
И, конечно же, вы должны отобразить ссылку на модель в файле (например, main.py в GAE),;)
Если некоторым ребятам вроде меня это нужно подробно, вот часть main.py:
from google.appengine.ext import webapp
import utility # DBDelete was defined in utility.py
application = webapp.WSGIApplication([('/clear_db',utility.DBDelete ),('/',views.MainPage )],debug = True)
Теперь вы можете использовать для этого администратора хранилища данных: https://developers.google.com/appengine/docs/adminconsole/datastoreadmin#Deleting_Entities_in_Bulk
Сообщение в блоге, предупреждающее вас о неэффективности администратора хранилища данных: marram.posterous.com/…
приведенная выше ссылка мертва, но я могу добавить, что массовое удаление сущностей у меня не работает в данный момент (она проходит через движения и выдает успешно удаленное сообщение, но все сущности остаются).
Да, ты можешь: Перейдите в Администратор хранилища данных, затем выберите тип объекта, который вы хотите удалить, и нажмите Удалить. Mapreduce позаботится об удалении!
В javascript все записи на странице удаляются следующим образом:
document.getElementById("allkeys").checked=true;
checkAllEntities();
document.getElementById("delete_button").setAttribute("onclick","");
document.getElementById("delete_button").click();
учитывая, что вы находитесь на странице администратора (... / _ ah / admin) с объектами, которые хотите удалить.
Нет, я думаю, это удаляет все записи. Это код javascript, который вы можете запустить в консоли браузера, чтобы отправлять правильные http-запросы на страницы администратора appengine.
Ответ Герберта - это предлагаемый способ незначительной автоматизации браузера, чтобы установить флажки и нажать кнопку удаления. Если бы нужно было настроить проект автоматизации браузера с помощью Selenium или чего-то подобного, то это было бы законным, но мучительно медленным решением для удаления всех объектов через экран администратора, поскольку он удаляет не более 20 объектов за раз.
Что лалала ... лямбда говорит.
Для тех, кто отрицает мой ответ, предоставьте адекватную информацию о том, как его улучшить. В то время, когда я писал ответ, в панели администратора Google App Engine не было возможности удалить все записи одного объекта. Поскольку я не хотел создавать в своем исходном коде действие для таких административных действий, я написал хакерский код javascript, чтобы сделать это через панель администратора. Это некрасиво, но ИМХО единственный способ выполнить массовое удаление без изменения самого приложения (поскольку удаление в bluk может быть просто случайным). Так что, пожалуйста, поясните, что не так в этой идее.
На сервер разработки можно записать cd в каталог своего приложения, а затем запустить его следующим образом:
dev_appserver.py --clear_datastore=yes .
Это запустит приложение и очистит хранилище данных. Если у вас уже запущен другой экземпляр, приложение не сможет выполнить привязку к необходимому IP-адресу и, следовательно, не сможет запуститься ... и очистить хранилище данных.
Чтобы удалить все объекты данного типа в Google App Engine, вам нужно сделать только следующее:
from google.cloud import datastore
query = datastore.Client().query(kind = <KIND>)
results = query.fetch()
for result in results:
datastore.Client().delete(result.key)
это боль в шее