Удалить все данные для вида в Google App Engine

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

это боль в шее

user784435 17.04.2013 15:41
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
45
1
28 689
19
Перейти к ответу Данный вопрос помечен как решенный

Ответы 19

Предположительно ваш взлом был примерно таким:

# 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 ограничивает количество перенаправлений на запрос (а также совокупное время, которое занимает запрос).

Alexander Kojevnikov 23.09.2008 06:40

Слишком поздно для этого комментария на три года, но это неправда и никогда не было: App Engine не ограничивает ни то, ни другое; браузеры ограничивают общее количество перенаправлений, которым они будут следовать. App Engine не суммирует время, затраченное на несколько запросов.

Nick Johnson 05.09.2011 08:21

Конечно, мой первоначальный ответ теперь устарел, учитывая наличие mapreduce и консоли администратора хранилища данных.

Nick Johnson 05.09.2011 08:21
Ответ принят как подходящий

официальный ответ от Google заключается в том, что вам нужно удалять кусками, распределенными по нескольким запросам. Вы можете использовать AJAX, мета обновление или запросить свой URL из скрипта, пока не останется никаких сущностей.

Попробуйте использовать Консоль App Engine, тогда вам даже не придется развертывать какой-либо специальный код

Нет кнопки (AFAIK) для удаления ВСЕГО определенного типа.

Jonny 02.04.2014 12:34

Я пробовал 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, или это нарушит код?

zerowords 06.10.2012 18:41

В настоящее время я удаляю объекты по их ключу, и, похоже, это быстрее.

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: // ...

Для меня он возвращает «Произошла ошибка сервера. Обратитесь к администратору». вместо исключения, но работает :)

Pavel Alexeev 29.03.2012 23:08

Приведенный выше код прерывает цикл, вызывая AssertionError и перехватывая его. Это довольно некрасиво! Должен просто сломаться, если q.count () равен 0

boxed 27.03.2013 17:58

Один совет. Я предлагаю вам ознакомиться с remote_api для этих типов использования (массовое удаление, изменение и т. д.). Но даже с удаленным API размер пакета может быть ограничен несколькими сотнями за раз.

Вау ... это полезно, а я даже не слышал об этом. Это то, что я получаю, работая в основном с Java API, в котором нет ничего похожего на remote_api.

mcherm 10.11.2009 05:26

Если бы я был параноиком, я бы сказал, что 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());
}

Я использую этот подход, хотя он очень интенсивно использует ЦП (не ЦП хранилища данных, ЦП общего назначения).

antony.trupe 06.01.2011 22:41

Если вы используете 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/…

Pieter Herroelen 27.12.2011 14:00

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

ljs.dev 03.09.2013 17:33

Да, ты можешь: Перейдите в Администратор хранилища данных, затем выберите тип объекта, который вы хотите удалить, и нажмите Удалить. Mapreduce позаботится об удалении!

В javascript все записи на странице удаляются следующим образом:

document.getElementById("allkeys").checked=true;
checkAllEntities();
document.getElementById("delete_button").setAttribute("onclick","");
document.getElementById("delete_button").click();

учитывая, что вы находитесь на странице администратора (... / _ ah / admin) с объектами, которые хотите удалить.

Нет, я думаю, это удаляет все записи. Это код javascript, который вы можете запустить в консоли браузера, чтобы отправлять правильные http-запросы на страницы администратора appengine.

Herbert 23.05.2013 16:29

Ответ Герберта - это предлагаемый способ незначительной автоматизации браузера, чтобы установить флажки и нажать кнопку удаления. Если бы нужно было настроить проект автоматизации браузера с помощью Selenium или чего-то подобного, то это было бы законным, но мучительно медленным решением для удаления всех объектов через экран администратора, поскольку он удаляет не более 20 объектов за раз.

ljs.dev 03.09.2013 17:47

Что лалала ... лямбда говорит.

Herbert 07.11.2013 22:25

Для тех, кто отрицает мой ответ, предоставьте адекватную информацию о том, как его улучшить. В то время, когда я писал ответ, в панели администратора Google App Engine не было возможности удалить все записи одного объекта. Поскольку я не хотел создавать в своем исходном коде действие для таких административных действий, я написал хакерский код javascript, чтобы сделать это через панель администратора. Это некрасиво, но ИМХО единственный способ выполнить массовое удаление без изменения самого приложения (поскольку удаление в bluk может быть просто случайным). Так что, пожалуйста, поясните, что не так в этой идее.

Herbert 26.03.2015 20:52

На сервер разработки можно записать 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)

Другие вопросы по теме