storage: use 60s context timeout on App Engine

Change-Id: Iaa40f3c6bf50b7351ca38ae3184431180ed71036
Reviewed-on: https://go-review.googlesource.com/35876
Reviewed-by: Russ Cox <rsc@golang.org>
diff --git a/storage/app/appengine.go b/storage/app/appengine.go
index b9ff1e6..9e4018c 100644
--- a/storage/app/appengine.go
+++ b/storage/app/appengine.go
@@ -19,4 +19,5 @@
 	return appengine.NewContext(r)
 }
 
+var infof = log.Infof
 var errorf = log.Errorf
diff --git a/storage/app/local.go b/storage/app/local.go
index 4a9cf43..e283567 100644
--- a/storage/app/local.go
+++ b/storage/app/local.go
@@ -18,6 +18,8 @@
 	return r.Context()
 }
 
-func errorf(_ context.Context, format string, args ...interface{}) {
+func infof(_ context.Context, format string, args ...interface{}) {
 	log.Printf(format, args...)
 }
+
+var errorf = infof
diff --git a/storage/app/upload.go b/storage/app/upload.go
index 0a48b84..aa983b3 100644
--- a/storage/app/upload.go
+++ b/storage/app/upload.go
@@ -165,16 +165,19 @@
 }
 
 func (a *App) indexFile(ctx context.Context, upload *db.Upload, p io.Reader, meta map[string]string) (err error) {
-	fw, err := a.FS.NewWriter(ctx, fmt.Sprintf("uploads/%s.txt", meta["upload-part"]), meta)
+	path := fmt.Sprintf("uploads/%s.txt", meta["upload-part"])
+	fw, err := a.FS.NewWriter(ctx, path, meta)
 	if err != nil {
 		return err
 	}
 	defer func() {
+		start := time.Now()
 		if err != nil {
 			fw.CloseWithError(err)
 		} else {
 			err = fw.Close()
 		}
+		infof(ctx, "Close(%q) took %.2f seconds", path, time.Since(start).Seconds())
 	}()
 	var keys []string
 	for k := range meta {
diff --git a/storage/appengine/app.go b/storage/appengine/app.go
index ca69ecf..bb8b3f6 100644
--- a/storage/appengine/app.go
+++ b/storage/appengine/app.go
@@ -10,8 +10,10 @@
 	"log"
 	"net/http"
 	"os"
+	"time"
 
 	_ "github.com/go-sql-driver/mysql"
+	"golang.org/x/net/context"
 	"golang.org/x/perf/storage/app"
 	"golang.org/x/perf/storage/db"
 	"golang.org/x/perf/storage/fs/gcs"
@@ -69,6 +71,13 @@
 // be supplied in /upload responses.
 func appHandler(w http.ResponseWriter, r *http.Request) {
 	ctx := appengine.NewContext(r)
+	// App Engine does not return a context with a deadline set,
+	// even though the request does have a deadline. urlfetch uses
+	// a 5s default timeout if the context does not have a
+	// deadline, so explicitly set a deadline to match the App
+	// Engine timeout.
+	ctx, cancel := context.WithTimeout(ctx, 60*time.Second)
+	defer cancel()
 	// GCS clients need to be constructed with an AppEngine
 	// context, so we can't actually make the App until the
 	// request comes in.