datastore/app: switch more bug queries to paged queries

Update #1448
diff --git a/dashboard/app/api.go b/dashboard/app/api.go
index b488bb5..1baf0af 100644
--- a/dashboard/app/api.go
+++ b/dashboard/app/api.go
@@ -177,13 +177,12 @@
 	if err := json.Unmarshal(payload, req); err != nil {
 		return nil, fmt.Errorf("failed to unmarshal request: %v", err)
 	}
-	var bugs []*Bug
-	_, err := db.NewQuery("Bug").
-		Filter("Namespace=", ns).
-		Filter("Status<", BugStatusFixed).
-		GetAll(c, &bugs)
+	bugs, err := loadAllBugs(c, func(query *db.Query) *db.Query {
+		return query.Filter("Namespace=", ns).
+			Filter("Status<", BugStatusFixed)
+	})
 	if err != nil {
-		return nil, fmt.Errorf("failed to query bugs: %v", err)
+		return nil, err
 	}
 	m := make(map[string]bool)
 loop:
@@ -521,13 +520,12 @@
 
 func addCommitsToBugsInStatus(c context.Context, status int, ns, manager string, managers []string,
 	presentCommits map[string]bool, bugFixedBy map[string][]string) error {
-	var bugs []*Bug
-	_, err := db.NewQuery("Bug").
-		Filter("Namespace=", ns).
-		Filter("Status=", status).
-		GetAll(c, &bugs)
+	bugs, err := loadAllBugs(c, func(query *db.Query) *db.Query {
+		return query.Filter("Namespace=", ns).
+			Filter("Status=", status)
+	})
 	if err != nil {
-		return fmt.Errorf("failed to query bugs: %v", err)
+		return err
 	}
 	for _, bug := range bugs {
 		var fixCommits []string
diff --git a/dashboard/app/main.go b/dashboard/app/main.go
index d43f9d1..64fb6ff 100644
--- a/dashboard/app/main.go
+++ b/dashboard/app/main.go
@@ -527,7 +527,14 @@
 
 func fetchNamespaceBugs(c context.Context, accessLevel AccessLevel,
 	ns, manager string) ([]*uiBugGroup, int, error) {
-	bugs, err := loadAllBugs(c, ns, manager)
+	filter := func(query *db.Query) *db.Query {
+		query = query.Filter("Namespace=", ns)
+		if manager != "" {
+			query = query.Filter("HappenedOn=", manager)
+		}
+		return query
+	}
+	bugs, err := loadAllBugs(c, filter)
 	if err != nil {
 		return nil, 0, err
 	}
@@ -616,14 +623,15 @@
 
 func fetchTerminalBugs(c context.Context, accessLevel AccessLevel,
 	ns, manager string, typ *TerminalBug) (*uiBugGroup, error) {
-	var bugs []*Bug
-	query := db.NewQuery("Bug").
-		Filter("Namespace=", ns).
-		Filter("Status=", typ.Status)
-	if manager != "" {
-		query = query.Filter("HappenedOn=", manager)
-	}
-	if _, err := query.GetAll(c, &bugs); err != nil {
+	bugs, err := loadAllBugs(c, func(query *db.Query) *db.Query {
+		query = query.Filter("Namespace=", ns).
+			Filter("Status=", typ.Status)
+		if manager != "" {
+			query = query.Filter("HappenedOn=", manager)
+		}
+		return query
+	})
+	if err != nil {
 		return nil, err
 	}
 	state, err := loadReportingState(c)
diff --git a/dashboard/app/reporting.go b/dashboard/app/reporting.go
index 43067fe..417f3a9 100644
--- a/dashboard/app/reporting.go
+++ b/dashboard/app/reporting.go
@@ -44,10 +44,9 @@
 		log.Errorf(c, "%v", err)
 		return nil
 	}
-	var bugs []*Bug
-	_, err = db.NewQuery("Bug").
-		Filter("Status<", BugStatusFixed).
-		GetAll(c, &bugs)
+	bugs, err := loadAllBugs(c, func(query *db.Query) *db.Query {
+		return query.Filter("Status<", BugStatusFixed)
+	})
 	if err != nil {
 		log.Errorf(c, "%v", err)
 		return nil
@@ -152,10 +151,9 @@
 }
 
 func reportingPollNotifications(c context.Context, typ string) []*dashapi.BugNotification {
-	var bugs []*Bug
-	_, err := db.NewQuery("Bug").
-		Filter("Status<", BugStatusFixed).
-		GetAll(c, &bugs)
+	bugs, err := loadAllBugs(c, func(query *db.Query) *db.Query {
+		return query.Filter("Status<", BugStatusFixed)
+	})
 	if err != nil {
 		log.Errorf(c, "%v", err)
 		return nil
@@ -471,9 +469,9 @@
 	return repos
 }
 
-func loadAllBugs(c context.Context, ns, manager string) ([]*Bug, error) {
+func loadAllBugs(c context.Context, filter func(*db.Query) *db.Query) ([]*Bug, error) {
 	var bugs []*Bug
-	err := foreachBug(c, ns, manager, func(bug *Bug) error {
+	err := foreachBug(c, filter, func(bug *Bug, _ *db.Key) error {
 		bugs = append(bugs, bug)
 		return nil
 	})
@@ -483,41 +481,39 @@
 	return bugs, nil
 }
 
-func foreachBug(c context.Context, ns, manager string, fn func(bug *Bug) error) error {
+func foreachBug(c context.Context, filter func(*db.Query) *db.Query, fn func(bug *Bug, key *db.Key) error) error {
 	const batchSize = 1000
-	var cursor db.Cursor
-	for first := true; ; first = false {
+	var cursor *db.Cursor
+	for {
 		query := db.NewQuery("Bug").Limit(batchSize)
-		if ns != "" {
-			query = query.Filter("Namespace=", ns)
-			if manager != "" {
-				query = query.Filter("HappenedOn=", manager)
-			}
+		if filter != nil {
+			query = filter(query)
 		}
-		if !first {
-			query = query.Start(cursor)
+		if cursor != nil {
+			query = query.Start(*cursor)
 		}
 		iter := query.Run(c)
 		for i := 0; ; i++ {
 			bug := new(Bug)
-			_, err := iter.Next(bug)
+			key, err := iter.Next(bug)
 			if err == db.Done {
 				if i < batchSize {
 					return nil
 				}
-				cursor, err = iter.Cursor()
-				if err != nil {
-					return fmt.Errorf("cursor failed while fetching bugs: %v", err)
-				}
 				break
 			}
 			if err != nil {
 				return fmt.Errorf("failed to fetch bugs: %v", err)
 			}
-			if err := fn(bug); err != nil {
+			if err := fn(bug, key); err != nil {
 				return err
 			}
 		}
+		cur, err := iter.Cursor()
+		if err != nil {
+			return fmt.Errorf("cursor failed while fetching bugs: %v", err)
+		}
+		cursor = &cur
 	}
 }
 
@@ -528,7 +524,7 @@
 		idMap[id] = true
 	}
 	var closed []string
-	err := foreachBug(c, "", "", func(bug *Bug) error {
+	err := foreachBug(c, nil, func(bug *Bug, _ *db.Key) error {
 		for i := range bug.Reporting {
 			bugReporting := &bug.Reporting[i]
 			if !idMap[bugReporting.ID] {
diff --git a/dashboard/app/reporting_email.go b/dashboard/app/reporting_email.go
index 3b3df67..b88953b 100644
--- a/dashboard/app/reporting_email.go
+++ b/dashboard/app/reporting_email.go
@@ -111,7 +111,7 @@
 	reports := reportingPollBugs(c, emailType)
 	for _, rep := range reports {
 		if err := emailSendBugReport(c, rep); err != nil {
-			log.Errorf(c, "%v", err)
+			log.Errorf(c, "emailPollBugs: %v", err)
 		}
 	}
 	return nil
@@ -147,7 +147,7 @@
 	notifs := reportingPollNotifications(c, emailType)
 	for _, notif := range notifs {
 		if err := emailSendBugNotif(c, notif); err != nil {
-			log.Errorf(c, "%v", err)
+			log.Errorf(c, "emailPollNotifications: %v", err)
 		}
 	}
 	return nil
@@ -256,7 +256,7 @@
 func handleIncomingMail(w http.ResponseWriter, r *http.Request) {
 	c := appengine.NewContext(r)
 	if err := incomingMail(c, r); err != nil {
-		log.Errorf(c, "%v", err)
+		log.Errorf(c, "handleIncomingMail: %v", err)
 	}
 }