Eliminar todos los datos de un tipo en Google App Engine

Me gustaría borrar todos los datos de un tipo específico en el motor de aplicaciones de Google. ¿Cuál es la mejor manera de hacer esto? Escribí un script de eliminación (pirateo), pero dado que hay tantos datos, se agotan los tiempos de espera después de unos cientos de registros.

La respuesta oficial de Google es que tiene que eliminar en trozos repartidos en múltiples solicitudes. Puede usar AJAX, meta actualización o solicitar su URL desde un script hasta que no quede ninguna entidad.

Actualmente estoy eliminando las entidades por su clave, y parece ser más rápido.

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 

Desde la terminal, corro curl -N http: // …

Ahora puede usar el Administrador del almacén de datos para eso: https://developers.google.com/appengine/docs/adminconsole/datastoreadmin#Deleting_Entities_in_Bulk

Si fuera una persona paranoica, diría que Google App Engine (GAE) no nos ha facilitado la eliminación de datos si queremos. Voy a omitir la discusión sobre el tamaño del índice y cómo se traducen 6 GB de datos a 35 GB de almacenamiento (se facturará). Esa es otra historia, pero tienen formas de solucionarlo: limitar el número de propiedades para crear índices en (índices generados automáticamente), etc.

La razón por la que decidí escribir este post es que necesito “atacar” a todos mis Kinds en una caja de arena. Lo leí y finalmente encontré este código:

 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 keys = new ArrayList(); 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"); } } 

Tengo más de 6 millones de discos. Eso es mucho. No tengo idea de lo que costará eliminar los registros (tal vez sea más económico no eliminarlos). Otra alternativa sería solicitar una eliminación para toda la aplicación (sandbox). Pero eso no es realista en la mayoría de los casos.

Decidí ir con grupos más pequeños de registros (en consulta fácil). Sé que podría apostar por 500 entidades, pero luego comencé a recibir tasas muy altas de fallas (volver a eliminar la función).

Mi solicitud del equipo de GAE: agregue una función para eliminar todas las entidades de un tipo en una sola transacción.

Intente usar la consola de App Engine, entonces ni siquiera tiene que implementar ningún código especial

Presumiblemente tu hack fue algo como esto:

 # 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)) 

Como usted dice, si hay suficientes datos, va a alcanzar el tiempo de espera de la solicitud antes de que pase por todos los registros. Tendría que volver a invocar esta solicitud varias veces desde el exterior para asegurarse de que todos los datos se hayan borrado; Bastante fácil de hacer, pero difícilmente ideal.

La consola de administración no parece ofrecer ninguna ayuda, ya que (por mi propia experiencia con ella), parece que solo permite que las entidades de un tipo determinado se enumeren y luego se eliminen página por página.

Durante las pruebas, tuve que purgar mi base de datos en el inicio para deshacerme de los datos existentes.

De esto deduciría que Google opera según el principio de que el disco es barato y, por lo tanto, los datos suelen quedar huérfanos (los índices se reemplazan con los datos redundantes), en lugar de eliminarse. Dado que hay una cantidad fija de datos disponibles para cada aplicación en este momento (0.5 GB), eso no es de mucha ayuda para los usuarios que no son de Google App Engine.

He intentado db.delete (resultados) y la consola de App Engine, y ninguno de ellos parece estar funcionando para mí. La eliminación manual de las entradas del Visor de datos (límite aumentado hasta 200) tampoco funcionó ya que he cargado más de 10000 entradas. Terminé de escribir este guion

 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("""   """) try: for i in range(10): db.delete(results) results = q.fetch(10, len(results)) self.response.out.write("

10 removed

") self.response.out.write(""" """) except Exception, ints: self.response.out.write(str(inst)) def main(): application = webapp.WSGIApplication([ ('/cleanTable(.*)', CleanTable), ]) wsgiref.handlers.CGIHandler().run(application)

El truco fue incluir la redirección en html en lugar de usar self.redirect. Estoy listo para esperar toda la noche para deshacerme de todos los datos de mi mesa. Con suerte, el equipo de GAE hará que sea más fácil quitar las mesas en el futuro.

La forma más rápida y eficiente de manejar la eliminación masiva en el almacén de datos es mediante el uso de la nueva API de mapeador anunciada en la última versión de Google I / O.

Si su idioma de elección es Python , solo tiene que registrar su asignador en un archivo mapreduce.yaml y definir una función como esta:

 from mapreduce import operation as op def process(entity): yield op.db.Delete(entity) 

En Java , deberías echarle un vistazo a este artículo que sugiere una función como esta:

 @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()); } 

Un consejo. Le sugiero que conozca el remote_api para estos tipos de usos (eliminación masiva, modificación, etc.). Pero, incluso con el api remoto, el tamaño del lote puede limitarse a unos pocos cientos a la vez.

Desafortunadamente, no hay manera de hacer fácilmente una eliminación masiva. Su mejor opción es escribir un script que elimine un número razonable de entradas por invocación, y luego invocarlo repetidamente, por ejemplo, haciendo que su script de eliminación devuelva un redireccionamiento 302 cada vez que haya más datos para eliminar y luego con “wget ​​- -max-redirect = 10000 “(o algún otro número grande).

Con django, configurar url:

 url(r'^Model/bdelete/$', v.bulk_delete_models, {'model':'ModelKind'}), 

Vista de configuración

 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))) 

A continuación, ejecute en PowerShell:

 $client = new-object System.Net.WebClient $client.DownloadString("http://your-app.com/Model/bdelete/?limit=400") 

Si está utilizando Java / JPA, puede hacer algo como esto:

  em = EntityManagerFactoryUtils.getTransactionalEntityManager(entityManagerFactory) Query q = em.createQuery("delete from Table t"); int number = q.executeUpdate(); 

La información de Java / JDO se puede encontrar aquí: http://code.google.com/appengine/docs/java/datastore/queriesandindexes.html#Delete_By_Query

Sí, puede: Ir a Admin del almacén de datos, seleccionar el tipo de entidad que desea eliminar y hacer clic en Eliminar. Mapreduce se encargará de eliminar!

En un servidor de desarrollo , uno puede copiar al directorio de su aplicación y luego ejecutarlo así:

 dev_appserver.py --clear_datastore=yes . 

Al hacerlo, se iniciará la aplicación y se borrará el almacén de datos. Si ya tiene otra instancia en ejecución, la aplicación no podrá vincularse a la IP necesaria y, por lo tanto, no podrá iniciarse … y borrar su almacén de datos.

Puede utilizar las colas de tareas para eliminar fragmentos de, por ejemplo, 100 objetos. Eliminar objetos en GAE muestra cuán limitadas están las capacidades de administración en GAE. Tienes que trabajar con lotes en 1000 entidades o menos. Puede usar la herramienta de carga masiva que funciona con csv, pero la documentación no cubre java. Estoy usando GAE Java y mi estrategia para las eliminaciones implica tener 2 servlets, uno para eliminar realmente y otro para cargar las colas de tareas. Cuando quiero hacer una eliminación, ejecuto el servlet de carga de la cola, carga las colas y luego GAE se pone a trabajar ejecutando todas las tareas en la cola.

Cómo hacerlo: Cree un servlet que elimine una pequeña cantidad de objetos. Agregue el servlet a sus colas de tareas. Ir a casa o trabajar en otra cosa;) Revise el almacén de datos de vez en cuando …

Tengo un almacén de datos con aproximadamente 5000 objetos que purgo cada semana y se necesitan aproximadamente 6 horas para limpiar, así que ejecuto la tarea el viernes por la noche. Utilizo la misma técnica para cargar de forma masiva mis datos, que son aproximadamente 5000 objetos, con aproximadamente una docena de propiedades.

Esto funcionó para mí:

 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) 

Gracias a todos, chicos, tengo lo que necesito. :RE
Esto puede ser útil si tiene muchos modelos de db para eliminar, puede enviarlo en su terminal. Y también, puede administrar la lista de eliminación en DB_MODEL_LIST usted mismo.
Eliminar DB_1:

 python bulkdel.py 10 DB_1 

Eliminar todos los DB:

 python bulkdel.py 11 

Aquí está el archivo 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 ) 

Y aquí está la versión modificada del código de alexandre fiori.

 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 

Y, por supuesto, debe asignar el enlace al modelo en un archivo (como main.py en GAE);
En caso de que algunos tipos como yo lo necesiten en detalle, aquí es parte de 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) 

Para eliminar todas las entidades de un tipo determinado en Google App Engine, solo tiene que hacer lo siguiente:

 from google.cloud import datastore query = datastore.Client().query(kind = ) results = query.fetch() for result in results: datastore.Client().delete(result.key) 

En javascript, lo siguiente eliminará todas las entradas en la página:

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

dado que está en la página de administración (… / _ ah / admin) con las entidades que desea eliminar.