it-swarm-eu.dev

Smažte všechna data určitého druhu v aplikaci Google App Engine

V aplikaci Google App Engine bych rád vymazal všechna data pro určitý druh. Jaký je nejlepší způsob, jak to udělat? Napsal jsem smazací skript (hack), ale protože je tolik dat, je po uplynutí několika stovek záznamů Časový limit. 

44
user8548

Oficiální odpověď od Googlu je to, že musíte smazat kousky rozložené na více požadavků. Můžete použít AJAX, meta refresh , nebo požádat o URL ze skriptu, dokud nezůstanou žádné entity.

6

V současné době odstraňuji entity jejich klíčem a zdá se, že je rychlejší.

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

z terminálu spustím curl -N http: // ...

27
alexandre fiori
23
Pieter Herroelen

Kdybych byl paranoidní člověk, řekl bych, že Google App Engine (GAE) nám nedovolil odstranit data, pokud chceme. Chystám se přeskočit diskusi o velikostech indexů a o tom, jak překládají 6 GB dat na 35 GB úložného prostoru (účtované za). To je další příběh, ale mají způsoby, jak to obejít - omezit počet vlastností pro vytvoření indexu na (automaticky generované indexy) et cetera.

Důvod, proč jsem se rozhodl napsat tento příspěvek je, že musím "nuke" všechny své druhy v karanténě. Četl jsem o tom a konečně přišel s tímto kódem:

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

 }

}

Mám přes 6 milionů záznamů. To je hodně. Nemám tušení, jaké budou náklady na vymazání záznamů (možná úspornější, než je odstranit). Další alternativou by bylo požádat o vymazání celé aplikace (pískoviště). Ale to není ve většině případů realistické.

Rozhodl jsem se jít s menšími skupinami záznamů (v jednoduchém dotazu). Vím, že jsem mohl jít na 500 entit, ale pak jsem začal přijímat velmi vysoké míry selhání (znovu smazat funkci).

Můj požadavek od týmu GAE: doplňte prosím funkci pro smazání všech entit určitého druhu v jedné transakci.

10
babakm

Váš hack byl pravděpodobně něco takového:

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

Jak říkáte, pokud je k dispozici dostatek dat, narazíte na časový limit žádosti, než se dostane přes všechny záznamy. Budete muset tento požadavek znovu zvenčí vyvolat z vnějšku, abyste zajistili vymazání všech dat; snadné, ale sotva ideální.

Zdá se, že administrátorská konzole nenabízí žádnou pomoc, protože (z mé vlastní zkušenosti s ní) se zdá, že pouze dovoluje, aby byly entity daného typu uvedeny a poté smazány na jednotlivých stránkách.

Když jsem testoval, musel jsem vyčistit databázi při startu, abych se zbavil existujících dat.

Z toho bych vyvodil, že Google funguje na principu, že disk je levný, a proto jsou data obvykle osamocená (indexy nahrazena nadbytečnými daty), spíše než smazána. Vzhledem k tomu, že pro každou aplikaci je v tuto chvíli k dispozici pevné množství dat (0,5 GB), není to pro uživatele, kteří nejsou členy aplikace Google Engine Engine, příliš nápomocno.

9
Jason Etheridge

Zkuste použít App Engine Console pak nemusíte ani nasadit žádný speciální kód

9
Sam

Zkoušel jsem db.delete (výsledky) a App Engine Console a zdá se, že žádný z nich pro mě nefunguje. Manuální odstranění položek z Prohlížeče dat (zvýšený limit až 200) nefungovalo ani proto, že jsem nahrál více než 10000 položek. Skončil jsem psaní tohoto skriptu 

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)  

Trik spočíval v zahrnutí přesměrování do html namísto použití self.redirect. Jsem připraven čekat přes noc, abych se zbavil všech údajů v tabulce. Doufejme, že tým GAE usnadní v budoucnu stoly. 

7
Sargis

Nejrychlejší a nejúčinnější způsob, jak zpracovat hromadné mazání na Datastore, je pomocí nového mapovacího API ohlášeného na nejnovější Google I/O .

Pokud je vaším jazykem volby Python , stačí, abyste svůj mapovač zaregistrovali v souboru mapreduce.yaml file a definovali takovou funkci:

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

Na Java měli byste se podívat na tento článek který navrhuje podobnou funkci:

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

Jeden tip. Doporučuji, abyste se seznámili s remote_api pro tyto typy použití (hromadné mazání, modifikace atd.). Ale i u vzdáleného api může být velikost dávky omezena na několik set najednou.

4
RyanW

Bohužel neexistuje způsob, jak snadno provést hromadné odstranění. Nejlepším řešením je napsat skript, který odstraní rozumný počet položek pro každé vyvolání, a pak jej opakovaně volejte - například tím, že váš skript pro odstranění vrátí přesměrování 302, kdykoli bude k dispozici více dat, které chcete smazat, a poté jej načtete „wget - -max-redirect = 10000 "(nebo jiné velké číslo).

3
Nick Johnson

Ano, můžete: Přejděte na Správce datových úložišť a poté vyberte Typ entity, kterou chcete odstranit, a klepněte na tlačítko Odstranit. Mapreduce se postará o smazání!

1
David

S Djangem: 

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

Nastavení zobrazení

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

Pak spusťte v powerhell:

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

Pokud používáte Java/JPA, můžete něco takového udělat:

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

Informace o Java/JDO naleznete zde: http://code.google.com/appengine/docs/Java/datastore/queriesandindexes.html#Delete_By_Query

1
bebeastie

Na serveru dev lze do adresáře aplikace vložit CD a pak jej spustit takto:

dev_appserver.py --clear_datastore=yes .

Tímto způsobem spustíte aplikaci a vymažete datový úložiště. Pokud již máte spuštěnou jinou instanci, aplikace se nebude moci vázat na potřebnou IP adresu, a proto se nepodaří spustit ... a smazat svůj datový úložiště. 

1
hamx0r

To fungovalo pro 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)
0
Timothy Jordan

Fronty úkolů můžete použít k odstranění kousků řekněme 100 objektů. Odstranění objektů v GAE ukazuje, jak omezené jsou možnosti správy v GAE. Musíte pracovat s dávkami na 1000 entit nebo méně. Můžete použít nástroj bulkloader, který pracuje s csv, ale dokumentace se nevztahuje na Javu. Používám GAE Java a moje strategie pro smazání zahrnuje 2 servlety, jeden pro provádění skutečně smazání a druhý pro načítání úlohy fronty. Když chci provést odstranění, spustím servlet načítání fronty, načte fronty a pak přejde do práce vykonávající všechny úkoly ve frontě.

Jak to udělat: Vytvořte servlet, který odstraní malý počet objektů. Přidejte servlet do svých front úloh. Jděte domů nebo pracujte na něčem jiném;) Podívejte se na datastore tak často ...

Mám datastore s asi 5000 objekty, které jsem očistit každý týden, a to trvá asi 6 hodin vyčistit, tak jsem spustit úkol v pátek v noci. Používám stejnou techniku ​​pro hromadné načítání dat, která se stane s být asi 5000 objektů, s asi tucet vlastností. 

0
Iggy

Chcete-li smazat všechny entity v daném druhu v aplikaci Google App Engine, stačí provést následující postup:

from google.cloud import datastore

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

Děkuji všem klukům, mám to, co potřebuju. : D
To může být užitečné, pokud máte spoustu modelů db k odstranění, můžete je odeslat ve vašem terminálu. A také můžete sami spravovat seznam odstranění v souboru DB_MODEL_LIST.
Smazat DB_1:

python bulkdel.py 10 DB_1

Smazat vše DB:

python bulkdel.py 11

Zde je soubor 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 )

A zde je upravená verze kódu 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

A samozřejmě byste měli namapovat odkaz na model do souboru (např. Main.py v GAE),;)
V případě, že to někteří kluci jako já potřebují podrobně, zde je část 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)
0
Kjuly