45 votes

Supprimer toutes les données d'un genre dans Google App Engine

Je voudrais effacer toutes les données pour un type spécifique dans Google App Engine. Quelle est la meilleur moyen de le faire ? J'ai écrit un script delete script (hack), mais comme il y a tellement de données c'est timeout après quelques centaines d'enregistrements.

0 votes

C'est une douleur dans le cou

28voto

Je supprime actuellement les entités par leur clé, ce qui semble plus rapide.

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

depuis le terminal, je lance curl -N http://...

0 votes

Pour moi, il renvoie "Une erreur de serveur s'est produite. Please contact the administrator" au lieu d'une exception, mais ça marche :)

1 votes

Le code ci-dessus interrompt la boucle en déclenchant une erreur d'assertion (AssertionError) et en la rattrapant. C'est plutôt moche ! Il faudrait simplement interrompre la boucle si q.count() est égal à 0

23voto

Pieter Herroelen Points 1573

0 votes

Un article de blog vous mettant en garde contre l'inefficacité de Datastore Admin : marram.posterous.com/

0 votes

Le lien ci-dessus est mort, mais je peux ajouter que la suppression en masse des entités ne fonctionne pas pour moi pour le moment (elle passe par les mouvements et donne un message de suppression réussie, mais toutes les entités restent).

10voto

babakm Points 91

Si j'étais paranoïaque, je dirais que Google App Engine (GAE) ne nous a pas facilité la tâche pour supprimer les données si nous le souhaitons. Je vais passer sous silence la taille des index et la façon dont ils transforment 6 Go de données en 35 Go de stockage (facturés). C'est une autre histoire, mais ils ont des moyens de contourner ce problème - limiter le nombre de propriétés sur lesquelles créer un index (index générés automatiquement), etc.

La raison pour laquelle j'ai décidé d'écrire ce post est que j'ai besoin de "nuke" tous mes Kinds dans un bac à sable. J'ai lu sur le sujet et j'ai finalement trouvé ce code :

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

 }

}

J'ai plus de 6 millions d'enregistrements. C'est beaucoup. Je n'ai aucune idée du coût de la suppression des enregistrements (il est peut-être plus économique de ne pas les supprimer). Une autre solution serait de demander la suppression de l'ensemble de l'application (sandbox). Mais ce n'est pas réaliste dans la plupart des cas.

J'ai décidé d'opter pour des groupes d'enregistrements plus petits (dans une requête facile). Je sais que je pourrais aller jusqu'à 500 entités, mais je commencerais alors à recevoir des taux d'échec très élevés (fonction de suppression).

Je demande à l'équipe GAE d'ajouter une fonctionnalité permettant de supprimer toutes les entités d'un même type en une seule transaction.

9voto

Jason Etheridge Points 3879

Je suppose que votre piratage était quelque chose comme ça :

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

Comme vous l'avez dit, s'il y a suffisamment de données, vous allez atteindre le délai d'attente de la requête avant qu'elle n'atteigne tous les enregistrements. Il faudrait relancer cette requête plusieurs fois de l'extérieur pour s'assurer que toutes les données ont été effacées ; c'est assez facile à faire, mais pas idéal.

La console d'administration ne semble pas offrir d'aide, car (d'après ma propre expérience avec elle), elle semble seulement permettre aux entités d'un type donné d'être listées et ensuite supprimées page par page.

Lors des tests, j'ai dû purger ma base de données au démarrage pour me débarrasser des données existantes.

J'en déduis que Google fonctionne selon le principe que le disque est bon marché, et que les données sont généralement rendues orphelines (les index de données redondantes sont remplacés), plutôt que supprimées. Étant donné qu'une quantité fixe de données est actuellement disponible pour chaque application (0,5 Go), cela n'aide pas beaucoup les utilisateurs qui ne font pas partie de Google App Engine.

9voto

Sam Points 3315

Essayez d'utiliser Console du moteur d'application alors vous n'avez même pas à déployer un code spécial

0 votes

Il n'existe pas (AFAIK) de bouton permettant de supprimer TOUTES les données d'un certain type.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X