web-dev-qa-db-ja.com

Google App Engineで種類のすべてのデータを削除する

Google App Engineで特定の種類のすべてのデータを消去したいのですが。これを行う最良の方法は何ですか?削除スクリプト(ハック)を作成しましたが、データが非常に多いため、数百レコード後にタイムアウトが発生します。

44
user8548

Googleの 公式回答 は、複数のリクエストにまたがるチャンクで削除する必要があることです。 AJAX、 meta refresh を使用するか、エンティティがなくなるまでスクリプトからURLをリクエストできます。

私は現在、エンティティをキーで削除していますが、より高速のようです。

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:// ...を実行します.

27
alexandre fiori

これでDatastore管理を使用できるようになります。 https://developers.google.com/appengine/docs/adminconsole/datastoreadmin#Deleting_Entities_in_Bulk

23

私が偏執狂の人なら、Google App Engine(GAE)でデータを簡単に削除することはできませんでした。インデックスのサイズと、それらが6 GBのデータを35 GBのストレージ(課金対象)に変換する方法についての説明はスキップします。それは別の話ですが、それらを回避する方法はあります-インデックスを作成するプロパティの数を制限します(自動生成されたインデックス)など。

私がこの投稿を書くことにしたのは、サンドボックスですべての種類を「核にする」必要があるからです。私はそれについて読んで、ついにこのコードを思いつきました:

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

 }

}

600万件を超えるレコードがあります。それは沢山。レコードを削除するのにかかるコストはわかりません(レコードを削除しない方が経済的かもしれません)。別の代替方法は、アプリケーション全体(サンドボックス)の削除を要求することです。しかし、ほとんどの場合それは現実的ではありません。

私は(簡単なクエリで)小さなレコードグループを使用することにしました。 500エンティティを処理できることはわかっていますが、その後、非常に高い失敗率(機能の削除)を受け取り始めました。

GAEチームからのリクエスト:1つのトランザクションで種類のすべてのエンティティを削除する機能を追加してください。

10
babakm

おそらくあなたのハックは次のようなものでした:

# 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 GB)ことを考えると、それはGoogle App Engine以外のユーザーにはあまり役立ちません。

9
Jason Etheridge

App Engine Console を使用してみてください。特別なコードをデプロイする必要すらありません。

9
Sam

私はdb.delete(results)とApp Engineコンソールを試しましたが、どれも私のために働いていないようです。アップロードしたエントリが10000を超えているため、Data Viewerから手動でエントリを削除する(上限が200に増加)ことも機能しませんでした。このスクリプトの作成を終了しました

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)  

コツは、self.redirectを使用する代わりに、htmlにリダイレクトを含めることでした。テーブルのすべてのデータを削除するために、一晩待つ準備ができています。うまくいけば、GAEチームは将来、テーブルを簡単に削除できるようになります。

7
Sargis

Datastoreで一括削除を処理する最も速く効率的な方法は、最新の Google I/O で発表された新しい mapper API を使用することです。

選択した言語が Python の場合、マッパーをmapreduce.yamlファイルに登録して関数を定義するだけですこのような:

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

Java では、次のような関数を提案する この記事 を確認する必要があります。

@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

1つのヒント。これらのタイプの使用(一括削除、変更など)の remote_api を理解することをお勧めします。ただし、リモートAPIを使用する場合でも、バッチサイズは一度に数百に制限される可能性があります。

4
RyanW

残念ながら、一括削除を簡単に行う方法はありません。最善の策は、呼び出しごとに妥当な数のエントリを削除するスクリプトを記述し、それを繰り返し呼び出すことです。たとえば、削除するデータが増えるたびに削除スクリプトが302リダイレクトを返し、それを「wget- -max-redirect = 10000 "(または他の大きな数値)。

3
Nick Johnson

はい:データストア管理に移動し、削除するエンティティタイプを選択して、[削除]をクリックします。 Mapreduceが削除を処理します!

1
David

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

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

1
bebeastie

dev server では、彼のアプリのディレクトリにcdして、次のように実行できます。

dev_appserver.py --clear_datastore=yes .

これにより、アプリが起動し、データストアがクリアされます。すでに別のインスタンスが実行されている場合、アプリは必要なIPにバインドできないため、起動に失敗し、データストアをクリアできません。

1
hamx0r

これは私のために働きました:

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

タスクキューを使用して、たとえば100個のオブジェクトのチャンクを削除できます。 GAEでオブジェクトを削除すると、GAEでの管理機能の制限がわかります。 1000エンティティ以下のバッチで作業する必要があります。 csvで動作するバルクローダーツールを使用できますが、ドキュメントにはJavaは含まれていません。私はGAE Javaを使用しており、削除の戦略には、実際に削除を行うためのサーブレットと、タスクキューをロードするためのサーブレットの2つが含まれます。削除を実行する場合は、キューを実行しますサーブレットをロードすると、キューがロードされ、GAEはキュー内のすべてのタスクを実行します。

方法:少数のオブジェクトを削除するサーブレットを作成します。サーブレットをタスクキューに追加します。家に帰るか、他の作業をします;)データストアを時々チェックしてください...

毎週約5000個のオブジェクトをパージするデータストアがあり、クリーンアップに約6時間かかるため、金曜日の夜にタスクを実行します。同じテクニックを使用して、たまたま約5000個のオブジェクトと約12個のプロパティを持つデータを一括読み込みします。

0
Iggy

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

みんなありがとう、私は必要なものを手に入れた。 :D
これは、削除するdbモデルがたくさんある場合に便利です。ターミナルでディスパッチできます。また、DB_MODEL_LISTで削除リストを自分で管理することもできます。
DB_1を削除:

python bulkdel.py 10 DB_1

すべてのDBを削除:

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

そしてもちろん、リンクをファイル内のモデル(GAEのmain.pyなど)にマップする必要があります;)
私のような一部の人が詳細にそれを必要とする場合に備えて、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