func MatchIncomingBids(c appengine.Context, articleId bitwrk.ArticleId) error { incomingBids := make([]hotBid, 0, 16) if tasks, err := taskqueue.LeaseByTag(c, 200, "hotbids", 20, string(articleId)); err != nil { return err } else { defer func() { if err := taskqueue.DeleteMulti(c, tasks, "hotbids"); err != nil { c.Errorf("Couldn't delete from task queue: %v", err) } }() for index, task := range tasks { var hot hotBid if err := json.Unmarshal(task.Payload, &hot); err != nil { c.Errorf("Couldn't unmarshal task #%v: %v", index, err) } else { incomingBids = append(incomingBids, hot) } } } f := func(c appengine.Context) error { now := time.Now() if err := matchIncomingBids(c, now, articleId, incomingBids); err != nil { return err } else { return deleteExpiredHotBids(c, now, articleId) } } return datastore.RunInTransaction(c, f, nil) }
// sliceHandler fetches Tile tasks from the tileQueue, // generates and stores an image for each Tile, and - if all the // tiles have been generated - kicks off the zip task. func sliceHandler(c appengine.Context, w http.ResponseWriter, r *http.Request) *appError { tim := timer.New() tim.Point("process request") // Get Overlay from datastore and its Image from blobstore. k, o, err := getOverlay(r) if err != nil { return appErrorf(err, "overlay not found") } tim.Point("get Overlay") m, err := imageBlob(c, o.Image) if err != nil { return appErrorf(err, "could not get image") } tim.Point("get overlay Image") restart: const ( inFlight = 10 // tiles to process at once secPerTile = 2 // worst-case time to process one tile ) errc := make(chan error, o.Tiles/inFlight+1) errs := 0 count := 0 // Generate images for the provided tiles. for { tasks, err := taskqueue.LeaseByTag(c, inFlight, tileQueue, inFlight*secPerTile, k.Encode()) if err != nil { return appErrorf(err, "couldn't get more tasks") } if len(tasks) == 0 { // No more work to do. break } tiles := make([]*Tile, len(tasks)) keys := make([]*datastore.Key, len(tasks)) for i, task := range tasks { tile := new(Tile) err = json.Unmarshal(task.Payload, tile) if err != nil { panic(err) } err = slice(c, tile, o.Transform, m) if err != nil { panic(err) } tiles[i] = tile keys[i] = tile.Key(c, k) } // Store generated tiles in datastore while going back to // generate more. go func() { _, err = datastore.PutMulti(c, keys, tiles) if err != nil { errc <- err return } for _, task := range tasks { err = taskqueue.Delete(c, task, tileQueue) if err != nil { errc <- err return } } var ids []string for _, t := range tiles { ids = append(ids, t.String()) } send(c, k.Encode(), Message{Total: o.Tiles, IDs: ids}) count += len(tiles) errc <- nil }() errs++ } // Wait for all goroutines to finish. for i := 0; i < errs; i++ { if e := <-errc; err == nil && e != nil { err = e } } if err != nil { return appErrorf(err, "could not generate tiles") } tim.Pointf("generate and put %d tiles", count) // Start zip task if we're done. done, err := checkDone(c, k) if err != nil { return appErrorf(err, "could not check job status") } tim.Point("checkDone") // If we're not done we must have missed some tasks. // Wait a second and try it all over again. if !done { c.Infof("Not done; trying again") time.Sleep(1 * time.Second) goto restart } // Tell the client we're done. send(c, k.Encode(), Message{TilesDone: true}) c.Infof("%v", tim) return nil }
func StreamingInsertHandler(rw http.ResponseWriter, req *http.Request) { c := appengine.NewContext(req) // ログの種類一覧を取得する logInfos := model.GetBigQueries() for _, logInfo := range logInfos { //key := model.NewBigQueryInfoKey(c, logInfo) for { if isShuttingDown { c.Warningf("The instance is shutting down for some reason.") return } tasks, err := taskqueue.LeaseByTag(c, TASK_COUNT, QUEUE_NAME, LEASE_TIME, logInfo.LogID) if err != nil { c.Errorf("%s", err.Error()) return } taskSize := len(tasks) if taskSize > 0 { handlesMap := map[string][]*taskqueue.Task{} for _, task := range tasks { params := unmarshalPayload(task) registerDate := params.Date // TODO: LocationをAsia/Tokyoにズラした時刻を取得する方法が分からない。 registerYmd := registerDate.Format(YYYYMMDD) handles, ok := handlesMap[registerYmd] if !ok { handles = []*taskqueue.Task{} handlesMap[registerYmd] = handles } handlesMap[registerYmd] = append(handlesMap[registerYmd], task) // debug. ok. c.Infof("logID: %s task count: %d", logInfo.LogID, len(handlesMap[registerYmd])) } // 362行目相当 for registerYmd, handles := range handlesMap { // TODO: 下記テーブルのローテートは、ローテート無しでBigQueryへのInsert確認後に実装。 /* date := time.Parse(constants.YYYYMMDD, registerYmd) slashedYmd := date.Format(constants.DATE_FORMAT_SLASHED) if この日付のテーブルが有るか { BigQueryに新しくテーブルを作成 // DatastoreUtilityがない・・・後で池田さんに聞こう。 } */ // InsertAll用のデータ構築と、insertIDの有無を確認してpanic処理 rows := make([]*model.Task, len(handles)) for i, taskHandle := range handles { rows[i] = unmarshalPayload(taskHandle) if len(rows[i].InsertID) == 0 { panic(fmt.Sprintln("log key not found.")) } } c.Debugf("inserting! logID:%s, ymd:%s, rows:%d", logInfo.LogID, registerYmd, len(rows)) service := gaebigquery.NewService(c) res, err := service.TableData.InsertAll(logInfo.DatasetID, logInfo.TableID, rows) // insertAllでエラーが出たら、次のループへ。(ここはJavaと異なる) if err != nil { c.Errorf("%s", err.Error()) for _, ft := range handles { c.Debugf("modify task lease...") taskqueue.ModifyLease(c, ft, QUEUE_NAME, 5) } break } failures := make([]*taskqueue.Task, 0) if res.InsertErrors != nil && len(res.InsertErrors) > 0 { for _, insertErrors := range res.InsertErrors { // TableDataInsertAllResponseInsertErrors.Index は int64です。 // そのためzero valueの場合と、値が0である場合を区別できません。 // よってErrorsの有無で判別することにしました。 if insertErrors.Errors == nil { continue } for _, errProto := range insertErrors.Errors { errJson, err := json.Marshal(errProto) if err != nil { c.Warningf("json error: %s", err.Error()) } else { c.Debugf("%s", string(errJson)) } } failures = append(failures, handles[int(insertErrors.Index)]) } } succeeds := make([]*taskqueue.Task, 0) for _, taskHandle := range handles { ifIn := func() bool { for _, f := range failures { if taskHandle == f { return true } } return false }() if ifIn == false { succeeds = append(succeeds, taskHandle) } } c.Debugf("succeeds:%d", len(succeeds)) c.Debugf("failures:%d", len(failures)) if len(succeeds) > 0 { c.Debugf("delete tasks...") err = taskqueue.DeleteMulti(c, succeeds, QUEUE_NAME) if err != nil { multiError, ok := err.(appengine.MultiError) if ok { for _, err = range multiError { c.Errorf("%s", err.Error()) } } else { c.Errorf("%s", err.Error()) } } } for _, ft := range failures { c.Debugf("modify task lease...") taskqueue.ModifyLease(c, ft, QUEUE_NAME, 5) } } } // if BigQueryのテーブル作成でエラー吐いた { // break // } if len(tasks) < TASK_COUNT { break } } } /*** for(ログの種類) { cron自体のログの記録を取るエンティティを取得 // 必要? datastoreに保存してあるログの種類のBigQuery定義を取得する。 キューからタスクを取得 for { if isShuttingDown { // backendインスタンスが落ちる30秒前か否か map[string]string{"result":"shutdown"}を返す // どこに返してんの? } taskをleaseする。 if taskが有る { handlesMap := map[string][]*taskqueue.Task // keyは何だ? → 日付のようだ。 // illegalTasks関連の処理は今回要らないので除いてます。 for リースしたtask { タスクのパラメータを取得 タスクのpayloadから、タスクの登録日時を取得 取得した日時を基に、フォーマットとタイムゾーンを指定してDate型変数に変換 Date型の日時を文字列に変換 handles := handlesMap[日時文字列] if handlesが無かった(nil) { handlesMap[日時文字列] = make([]*taskqueue.Task) } handlesMap[日時文字列] = append(handlesMap[日時文字列], task) } for 登録日時, taskのslice := range handlesMap { // 362行目 handlesMapから日付ごとのTaskを取得 年/月/日のフォーマットに文字列変更 // TODO: 下記テーブルのローテートは、ローテート無しでBigQueryへのInsert確認後に実装。 if この日付のテーブルが有るか { BigQueryに新しくテーブルを作成 // DatastoreUtilityがない・・・後で池田さんに聞こう。 } インサートIDを入れるスライス変数 行を入れるスライス変数 for 個別タスク := range 日付ごとのTask { taskのパラメータを取得(payloadのこと) パラメータをスキーマに合わせたmapに変換 行を格納するスライスに追加 パラメータmapからInsertIDを取得 if InsertIDがnull { 例外を投げる(GoではpanicでもOK) } インサートIDのスライスに追加 } ログ出力 InsertAllの実行 // TODO: bigquery apiのエラー処理を細かにする 失敗したtask一覧を入れるスライス if エラーがあれば { for エラー一覧 { if エラーのインデックスがnull { continue } for エラーの中身 { エラーの中身をログに出力 } エラーのインデックスを基に、失敗したタスクを取得して、失敗したタスク一覧に追加 } } 成功したtask一覧用変数 for 日付ごとのTask { if 失敗したタスク一覧に入っていない { 成功したタスク一覧に追加する } } 成功したタスク数をログ出力 失敗したタスク数をログ出力 if 成功したタスク数 > 0 { ログにタスクを削除する旨を出力 成功したタスクをキューから削除 } for 失敗したタスク一覧 { 失敗したタスクはキューに戻す旨をログに出力 失敗したタスクをキューに戻す } } } if タスクの数が500件以下 { ログに次へと出力 break } } } ***/ }
func batchBulkUpdateUnique(c appengine.Context, w http.ResponseWriter, r *http.Request) { tasks, err := taskqueue.LeaseByTag(c, 100, "updateUniquePull", 3600, "") if err != nil { c.Errorf("Error leasing: %v", err) http.Error(w, err.Error(), 500) return } if len(tasks) == 0 { c.Infof("No tasks found") w.WriteHeader(204) return } uuid := tasks[0].Tag mckey := "ulock." + uuid item := memcache.Item{ Key: mckey, Expiration: time.Minute, } if memcache.Add(c, &item) == memcache.ErrNotStored { c.Errorf("Already processing %v, skipping", uuid) http.Error(w, "Already processing item", 503) return } c.Infof("Processing %v things for %v", len(tasks), uuid) uk := datastore.NewKey(c, "Unique", uuid, 0, nil) chu := make(chan uniqueFetch) go func() { rv := uniqueFetch{} rv.u, rv.err = loadUnique(c, uk) chu <- rv }() keys := []*datastore.Key{} obs := make([]oldLoader, len(tasks)) for i, t := range tasks { key, err := datastore.DecodeKey(string(t.Payload)) if err != nil { c.Errorf("Error decoding key: %s: %v", t.Payload, err) http.Error(w, err.Error(), 500) return } keys = append(keys, key) obs[i] = oldLoader{c: c, into: &Stats{}} } err = datastore.GetMulti(c, keys, obs) if err != nil { c.Errorf("Error grabbing all the stats: %v", err) http.Error(w, err.Error(), 500) return } uf := <-chu if uf.err != nil { c.Errorf("Error fetching unique: %v", err) http.Error(w, err.Error(), 500) return } cherr := make(chan error, 5) // We're probably safe enough to count these now go func() { cherr <- sharded_counter.IncrementBy(c, "unique_"+uuid, len(tasks)) }() for _, wst := range obs { st := wst.into.(*Stats) st.uncompress() err := uf.u.Update(*st) if err != nil { c.Warningf("Problem updating a unique: %v (continuing)", err) } } go func() { cherr <- uf.u.compress() _, err = datastore.Put(c, uk, &uf.u) if err != nil { c.Errorf("Error storing unique: %v", err) } cherr <- err }() go func() { memcache.Delete(c, mckey) cherr <- couchit(c, uk, url.Values{ "isnew": []string{fmt.Sprintf("%t", uf.u.isNew)}}) }() go func() { cherr <- taskqueue.DeleteMulti(c, tasks, "updateUniquePull") }() go func() { st, err := taskqueue.QueueStats(c, []string{"updateUniquePull", "bulkupdateunique"}, 0) if err != nil { c.Errorf("Error getting queue stats: %v", err) cherr <- err return } if st[0].Tasks > 5000 && st[1].Tasks < 1000 { c.Infof("There's more to go, resubmitting: %v", err) taskqueue.Add(c, taskqueue.NewPOSTTask("/submit/bulkUpdateUnique", url.Values{"times": []string{"100"}}), "default") } cherr <- nil }() err = anyErr(<-cherr, <-cherr, <-cherr, <-cherr, <-cherr) if err != nil { c.Errorf("Error in batch processing: %v", err) http.Error(w, err.Error(), 500) return } w.WriteHeader(204) }