Beispiel #1
0
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)
}
Beispiel #2
0
// 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
}
Beispiel #3
0
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
				}
	  	}
	  }
	  ***/
}
Beispiel #4
0
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)
}