bigquery: cache schema inference

This will speed up struct uploading.

Change-Id: I73266ebd1003ab0536b89270d5e65a2aa0e694a6
Reviewed-on: https://code-review.googlesource.com/9832
Reviewed-by: Sarah Adams <shadams@google.com>
diff --git a/bigquery/schema.go b/bigquery/schema.go
index e202d9c..4d0da28 100644
--- a/bigquery/schema.go
+++ b/bigquery/schema.go
@@ -19,6 +19,8 @@
 	"fmt"
 	"reflect"
 
+	"cloud.google.com/go/internal/atomiccache"
+
 	bq "google.golang.org/api/bigquery/v2"
 )
 
@@ -141,9 +143,21 @@
 	return inferSchemaReflect(reflect.TypeOf(st))
 }
 
-func inferSchemaReflect(t reflect.Type) (Schema, error) {
-	return inferStruct(t, map[reflect.Type]bool{})
+var schemaCache atomiccache.Cache
+
+type cacheVal struct {
+	schema Schema
+	err    error
 }
+
+func inferSchemaReflect(t reflect.Type) (Schema, error) {
+	cv := schemaCache.Get(t, func() interface{} {
+		s, err := inferStruct(t, map[reflect.Type]bool{})
+		return cacheVal{s, err}
+	}).(cacheVal)
+	return cv.schema, cv.err
+}
+
 func inferStruct(t reflect.Type, seen map[reflect.Type]bool) (Schema, error) {
 	if seen[t] {
 		return nil, fmt.Errorf("bigquery: schema inference for recursive type %s", t)
diff --git a/bigquery/uploader.go b/bigquery/uploader.go
index a0ed0ae..2328118 100644
--- a/bigquery/uploader.go
+++ b/bigquery/uploader.go
@@ -122,7 +122,6 @@
 	if v.Kind() != reflect.Struct {
 		return nil, false, nil
 	}
-	// TODO(jba): cache schema inference to speed this up.
 	schema, err := inferSchemaReflect(v.Type())
 	if err != nil {
 		return nil, false, err