使用go迭代appengine上的大量实体

时间:2016-03-23 00:04:01

标签: google-app-engine go google-cloud-datastore

在app引擎上,我有大量特定类型的实体。 我想在每个实体上运行一个函数(例如编辑实体或复制它) 我会在任务队列中执行此操作,但任务队列限制为10分钟运行时间,并且每个函数调用都容易出现多种错误。这样做的最佳方式是什么?

1 个答案:

答案 0 :(得分:0)

这是我的解决方案,虽然我希望有人有更好的解决方案。我也想知道这是否容易发生叉炸弹,例如如果任务运行两次,它将引发两个迭代链..!我只是用它来迭代几十万个实体,尽管每个实体的操作都很昂贵。

首先,我创建一个taskqueue,用于一次在一个实体上运行每个单独的函数调用:

queue:
- name: entity-iter
  rate: 100/s
  max_concurrent_requests: 1
  retry_parameters:
    task_retry_limit: 3
    task_age_limit: 30m
    min_backoff_seconds: 200

然后我有一个迭代实体方法,在给定类型的情况下,将使用密钥在每个实体上调用延迟函数。

package sysadmin

import (
    "google.golang.org/appengine/datastore"
    "golang.org/x/net/context"
    "google.golang.org/appengine/log"
    "google.golang.org/appengine/delay"
    "google.golang.org/appengine/taskqueue"
)


func ForEachEntity(kind string, f *delay.Function) *delay.Function {
    var callWithNextKey *delay.Function // func(c context.Context, depth int, cursorString string) error
    callWithNextKey = delay.Func("something", func(c context.Context, depth int, cursorString string) error {
        q := datastore.NewQuery(kind).KeysOnly()
        if cursorString != "" {
            if curs, err := datastore.DecodeCursor(cursorString); err != nil {
                log.Errorf(c, "error decoding cursor %v", err)
                return err
            } else {
                q = q.Start(curs)
            }
        }
        it := q.Run(c)
        if key, err := it.Next(nil); err != nil {
            if err == datastore.Done {
                log.Infof(c, "Done %v", err)
                return nil
            }
            log.Errorf(c, "datastore error %v", err)
            return err
        } else {
            curs, _ := it.Cursor()
            if t, err := f.Task(key); err != nil {
                return err
            } else if _, err = taskqueue.Add(c, t, "entity-iter"); err != nil {
                log.Errorf(c, "error %v", err)
                return err
            }
            if depth - 1 > 0 {
                if err := callWithNextKey.Call(c, depth - 1, curs.String()); err != nil {
                    log.Errorf(c, "error2 %v", err)
                    return err
                }
            }
        }
        return nil
    })
    return callWithNextKey
}

示例用法:

var DoCopyCourse = delay.Func("something2", CopyCourse)

var DoCopyCourses = ForEachEntity("Course", DoCopyCourse)

func CopyCourses(c context.Context) {
    //sharedmodels.MakeMockCourses(c)
    DoCopyCourses.Call(c, 9999999, "")
}