CloudDataStore_dalete

Cloud DatastoreのEntryを全件削除する方法が意外にも難しかったので整理しました。
基本的には、How to bulk delete entries in the App Engine's Datastoreを参考に内容をまとめますが、私のユースケースではDataflowを使ったパターンがうまく行ったので追加で紹介します。

1. そもそも削除する必要がありますか?

  • 保存しておくよりも安くなりますか?
  • 削除フラグを付けるなど「アーカイブ」しておくより良いアプローチですか?

Indexが複数貼られている場合、削除は高くなる傾向があります

2. 手動で削除

Datastoreの管理画面から全件削除できます。

gcp_1

3. シンプルなプログラムによる削除

件数が少ない場合は次のコードで削除できます。

>>> from google.appengine.ext import ndb
>>> ndb.delete_multi([k for k in Model.query().fetch(FETCH_SIZE, keys_only=True)])

4. 複数のバッチ実行による削除

ちょっと件数が多い程度であれば次のコードで削除できます。
コードは参考ページからそのまま持って来ているので未検証です。
少しだけ異なるが、実質的には同じアルゴリズムで試してみましたが、私のケースでは十分な速度が出ませんでした。

from google.appengine.ext import ndb
from google.appengine.api import taskqueue

class Task(webapp2.RequestHandler):
    def post(self): 
        entries = Entry.all(keys_only=True)

        bookmark = self.request.get('bookmark')
        if bookmark:
            cursor = 
                ndb.Cursor.from_websafe_string(bookmark)

        query = Entry.query()
        entries, next_cursor, more = query.fetch_page(
            1000, 
            keys_only=True, 
            start_cursor=cursor)

        ndb.delete_multi(entries)

        bookmark = None
        if more:
            bookmark = next_cursor.to_websafe_string()

        taskqueue.add(
            url='/task', 
            params={'bookmark': bookmark})

5. Map&Reduceによる削除

件数が多い場合は、Map&Reduceを利用して削除すると良いでしょう。
ただし、私の場合はAppEngineのなんちゃってMap&Reduceを利用したのですが、思ったほど速度が出ませんでした。
(チューニングにすれば早くなるとは思いますが、次に紹介するDataflowを利用した削除を実現した方が確実だと思います。)

参考までにコードを紹介します。
AppEngineのMap&Reduceライブラリは別途用意してください。

main.pyの一部

class Model(ndb.Model):
    ..フィールド定義は略..

def delete(entry):
    entry.key.delete()

mapreduce.yaml

mapreduce:
- name: Delete all entry
  mapper:
    handler: main.delete
    input_reader: mapreduce.input_readers.DatastoreInputReader
    params:
    - name: entity_kind
      default: main.Model
    - name: namespace
      default: ''
    - name: shard_count
      default: 4

app.yaml

runtime: python27
api_version: 1
threadsafe: true

handlers:
# the map reduce handler, this handles the inner calls of mapreduce
- url: /mapreduce(/.*)?
  script: mapreduce.main.APP
  login: admin

requirements.txt

GoogleAppEnginePipeline==1.9.22.1
Graphy==1.0.0

6. Dataflowによる削除

件数が多い場合でも問題なく動きます。
何よりコードが簡単なのでオススメです。

package your.package;

import com.google.cloud.dataflow.sdk.Pipeline;
import com.google.cloud.dataflow.sdk.io.datastore.DatastoreIO;
import com.google.cloud.dataflow.sdk.options.PipelineOptionsFactory;
import com.google.datastore.v1.Query;


/**
 * Deleting all entities in Cloud Datastore for writing Google Cloud Dataflow programs.
 *
 * <p>To run this example using managed resource in Google Cloud
 * Platform, you should specify the following command-line options:
 *   --project=
 *   --stagingLocation=
 *   --runner=BlockingDataflowPipelineRunner
 */
public class DeleteAllEntries {
    public static void main(String[] args) {
        Pipeline p = Pipeline.create(
                PipelineOptionsFactory.fromArgs(args).withValidation().create());

        // build query to specify the kind
        Query.Builder qb = Query.newBuilder();
        qb.addKindBuilder().setName("KIND");
        Query q = qb.build();

        p.apply(DatastoreIO.v1().read().withProjectId("PROJECT_ID").withQuery(q).withNamespace("NAMESPACE"))
        .apply(DatastoreIO.v1().deleteEntity().withProjectId("PROJECT_ID"));

        p.run();
    }
}

"KIND", "PROJECT_ID", "NAMESPACE"は自分のケースに合わせて変更してください。

Dataflowを利用すると、規模に合わせて勝手にワーカーを追加してくれるので、件数が増えても安心です。
gcp_2