Pergunta

Gostaria de acabar com todos os dados para um tipo específico no Google App Engine. O que é melhor maneira de fazer isso? Eu escrevi um script de exclusão (corte), mas desde há muito dados é tempo limite está fora depois de algumas centenas de registros.

Foi útil?

Solução

O resposta oficial do Google é que você tem para apagar em pedaços espalhados por várias solicitações. Você pode usar AJAX, meta refresh , ou solicitar o seu URL a partir de um roteiro até que não haja entidades esquerda .

Outras dicas

Atualmente, estou excluindo as entidades pela sua chave, e parece ser mais 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

a partir do terminal, eu corro onda -N http: // ...

Você agora pode usar o armazenamento de dados de administração para isso: https://developers.google.com/appengine / docs / adminconsole / datastoreadmin # Deleting_Entities_in_Bulk

Se eu fosse uma pessoa paranóica, eu diria que o Google App Engine (GAE) não tornou mais fácil para nós para remover dados se quisermos. Vou pular discussão sobre tamanhos de índice e como eles se traduzem um 6 GB de dados para 35 GB de armazenamento (sendo cobrado por). Isso é outra história, mas eles têm maneiras de contornar isso -. Número limite de propriedades para criar índice em (índices gerados automaticamente) et cetera

A razão pela qual eu decidi escrever este post é que eu preciso "nuke" todos os meus tipos em uma caixa de areia. Eu li sobre isso e finalmente veio com 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<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");

 }

}

Eu tenho mais de 6 milhões de discos. Isso é muito. Eu não tenho nenhuma idéia do que o custo será de excluir os registros (talvez mais econômico para não excluí-los). Outra alternativa seria a de solicitar uma exclusão para o aplicativo inteiro (sandbox). Mas isso não é realista na maioria dos casos.

Eu decidi ir com grupos menores de registros (em consulta fácil). Eu sei que eu poderia ir para 500 entidades, mas depois comecei a receber muito elevadas taxas de insucesso (re função de exclusão).

O meu pedido de equipa GAE:. Por favor, adicionar um recurso para excluir todas as entidades de um tipo em uma única transação

Tente usar App Engine Console então você não tem sequer para implantar qualquer código especial

Provavelmente o hack foi algo como isto:

# 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 você disse, se há dados suficientes, você está indo para bater o tempo limite do pedido antes que ele chegue através de todos os registros. Você teria que voltar a invocar esse pedido várias vezes de fora para garantir que todos os dados foram apagados; bastante fácil de fazer, mas dificilmente ideal.

O console de administração não parece oferecer qualquer ajuda, como (a partir de minha própria experiência com ele), parece para permitir que apenas entidades de um determinado tipo coletados e, em seguida, eliminado em uma base página por página.

Ao testar, eu tive que limpar meu banco de dados na inicialização para se livrar de dados existentes.

Eu inferir a partir disso que o Google opera no princípio de que disco é barato, e por isso os dados são normalmente órfãos (índices para dados redundantes substituído), em vez de excluído. Dado que há uma quantidade fixa de dados disponíveis para cada aplicativo no momento (0,5 GB), isso não é de muita ajuda para usuários do motor não-Google App.

Eu tentei db.delete (resultados) e App Engine Console, e nenhum deles parece estar a trabalhar para mim. remover manualmente as entradas do Visualizador de Dados (aumento de limite de até 200) não quer trabalhar desde que eu ter carregado mais de 10000 entradas. Acabei escrevendo este script

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)  

O truque era incluir redirecionamento no html em vez de usar self.redirect. Estou pronto para esperar durante a noite para se livrar de todos os dados na minha mesa. Felizmente, a equipe GAE, será mais fácil para soltar tabelas no futuro.

A maneira mais rápida e eficiente de lidar com massa de exclusão no armazenamento de dados é usando o novo mapeador API anunciou na última Google I / o .

Se o seu idioma de escolha é Python , você só tem para registrar o seu mapeador em um mapreduce.yaml arquivo e definir uma função como esta:

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

Na Java você deve ter um olhar para este artigo que sugere uma função assim:

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

Uma dica. Eu sugiro que você começa a conhecer o remote_api para estes tipos de usos (exclusão em massa , modificador, etc.). Mas, mesmo com a api remota, tamanho do lote pode ser limitado a algumas centenas de cada vez.

Infelizmente, não há nenhuma maneira de fazer facilmente um volume excluir. Sua melhor aposta é a de escrever um script que exclui um número razoável de entradas por invocação, e então chamá-lo repetidamente - por exemplo, por ter o seu roteiro de exclusão retornar um redirecionamento 302 sempre que há mais dados para excluir, em seguida, buscar-lo com "wget ??- -Max-redirect = 10000" (ou algum outro número grande).

Com Django, configuração url:

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

vista de configuração

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

Em seguida, execute no PowerShell:

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

Se você estiver usando Java / JPA você pode fazer algo como isto:

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

info Java / JDO pode ser encontrada aqui: http: / /code.google.com/appengine/docs/java/datastore/queriesandindexes.html#Delete_By_Query

Sim, você pode: Ir para armazenamento de dados de administração e, em seguida, selecione o tipo de entidade que deseja excluir e clique em Excluir. Mapreduce vai cuidar de apagar!

Em um dev servidor , cd uma lata para o diretório de seu aplicativo, em seguida, executá-lo como este:

dev_appserver.py --clear_datastore=yes .

Se o fizer, irá iniciar o aplicativo e limpar o armazenamento de dados. Se você já tem outra instância em execução, o aplicativo não será capaz de se ligar ao IP necessário e, portanto, deixar de começar ... e para limpar seu armazenamento de dados.

Você pode usar as filas de tarefas para eliminar pedaços de digamos 100 objetos. Excluindo objetos na mostra GAE quão limitada as capacidades de administrador estão em GAE. Você tem que trabalhar com lotes de 1000 entidades ou menos. Você pode usar a ferramenta bulkloader que trabalha com CSV de mas a documentação não java capa. Eu estou usando GAE Java e minha estratégia para eliminações envolve ter 2 servlets, uma para fazer o fato de exclusão e outro para carregar as filas de tarefas. Quando eu quero fazer uma exclusão, eu executar o servlet fila de carregamento, ele carrega as filas e, em seguida, GAE vai para o trabalho de executar todas as tarefas na fila.

Como fazê-lo: Criar um servlet que exclui um pequeno número de objetos. Adicione o servlet para suas filas de tarefas. Vá para casa ou trabalhar em outra coisa;) Verifique o armazenamento de dados de vez em quando ...

Eu tenho um armazenamento de dados com cerca de 5000 objetos que i purgar toda semana e leva cerca de 6 horas para limpar, então eu executar a tarefa na sexta-feira noite. Eu uso a mesma técnica para aumentar a massa carregar meus dados que passa a ser cerca de 5000 objetos, com cerca de uma dúzia de propriedades.

Isso funcionou para mim:

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)

Obrigado todos os caras, eu tenho o que eu preciso. : D
Isso pode ser útil se você tiver lotes db modelos para apagar, você pode despachar-lo em seu terminal. E também, você pode gerenciar a lista de exclusão em DB_MODEL_LIST si mesmo.
Db_1 de exclusão:

python bulkdel.py 10 DB_1

Apagar todos DB:

python bulkdel.py 11

Aqui está o arquivo 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 )

E aqui está a versão modificada do 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

E, claro, você deve mapear o link para modelo em um arquivo (como main.py no GAE),;)
No caso de algumas caras como eu precisa-lo em detalhe, aqui é 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 excluir todas as entidades em um determinado tipo no Google App Engine você só precisa fazer o seguinte:

from google.cloud import datastore

query = datastore.Client().query(kind = <KIND>)
results = query.fetch()
for result in results:
    datastore.Client().delete(result.key)

Em javascript, o seguinte irá apagar todas as entradas para a página:

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

dado que você está no admin-página (... / _ ah / admin) com as entidades que você deseja excluir.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top