Import changes for goma server

  - 786216f2f72f949629c33852ff63ce33510edc89 fix for publish_to_external_repo
  - a87d59872a64457efea7713ce74bbf603d06f70b Revert "remoteexec: use command.working_directory for non...
  - 255f0ece2468fea4ee16ac6369e4f1a6ead0a48f remoteexec: batch fetch for gomaInputReader
  - 0c2338e24694223b24f59eb000053d72d35da37a [toolchain config] Add feature to load config in parallel
  - 7f2dee79b5ef5ec7e22de9486d85804988eed280 remoteexec: fix -fdebug-compilation-dir handling
  - 895eb17049240200e8127400d5f970681627256f remoteexec: rename cwd agnostic to relocatable
  - 13745f68074d375d7d5482468651a5ae8c568e15 httprpc: increase timeout if ctx is timed-out
  - 1bc05465af290e200d74928bf8c4255bc6aff93b Add flag to disable server TLS cert verification
  - 23912419bd78500dcbac8f943f3d569c48372414 remoteexec: use slice for gcc.go:pathFlags
  - c6ab6f4278cac422bdc7be517db9f81e09faa673 Add OWNERS.
  - 50502850a7d3e81152502461412ce998de72f8e3 remoteexec: handle some feature flag for cwd agnotstic ch...
  - 3ff07d3135db5523b89bfaa26c55909c68a01939 use go 1.14.1
  - 44e354a7a5bd9b23efbae063d0e44f8b22ac672a roll cloud.google.com/go v0.54.0 to v0.55.0
  - 5a98cf13e6ca1aed3959be039fbcced7170fa0af cas: reduce memory usage in separateBlobsByByteLimit
  - e4a1b73baa5f5385231d531ad20e576200d73a4b handle additional arguments when determining cwdAgnostic
  - 36a3e8cb4b2d3fc149448a577978612e55e159bc roll cloud.google.com/go/pubsub v1.3.0 to v1.3.1
  - b1abc4046e85064e14f4bcbfddf26f8d6177388e roll github.com/golang/protobuf v1.3.4 to v1.3.5
  - 8b839a89b144b1f3bc57d918cb3263ff43fc0e9f roll google.golang.org/grpc v1.27.1 to v1.28.0
  - 6f8d3ef24a2ec5017ca3bd4f1b593de86155ee1b roll cloud.google.com/go/pubsub v1.2.0 v1.3.0
  - d07964413edc7f99b6261310a35629abd5da7727 roll cloud.google.com/go v0.53.0 to v0.54.0
  - a4ccdb5b9835cfdea026468e28ea64b7823759dc mixer: return StatusServiceUnavailable if token expired.
  - e96fb73af507d39679747135f97af39a881e500b roll google.golang.org/api v0.19.0 to v0.20.0
  - b12d8580773d1afc61377a2c0b03506d7f2f2b69 Revert "use go 1.14"
  - 9df336f29c934d537cff0943f148cb229ef7ab3a roll github.com/golang/protobuf v1.3.3 to v1.3.4
  - 6c161b3c40e4245d64004b93e01e9a8414e634a9 roll google.golang.org/api v0.18.0 to v0.19.0
  - 42578364e8b8d5d0fd7ef596519f5f59c0826fa2 use go 1.14
  - 8634f6c6448ff45c89957574cf16451da5a750e3 remoteexec: gcc treats hmap as non-cwd agnostic
  - 786f644e76df655907aaca52afd79ce8cd91f7cf remoteexec: Reduce size of ExecResp to < resp msg limit
  - d1f6c3a1d14263ba83326299b0c1f7ef5c2adefd roll google.golang.org/api v0.17.0 to v0.18.0
  - 3b38a0e70eaadfb67da9fb87d2e3d0b3c7161bda proto: Add new FileBlob::BlobType: FILE_REF
  - 2171e844c06091f753b761e7cdcf93175d1182ab remoteexec: Factor out function to create chunked file blob
  - d6272703df65f15e24f4e611aba25ad0075e259f remoteexec: call LookupFile() one blob at a time.
  - 82052d721336f525e5bb503271f2352f893665fb Record RBE's execution duration into the ExecResp
  - b9935b06dec093a18e48e50d4bdc5e8c16257e0d Update goma_data.proto
  - 9b0b387d5ac4503f35195432c0042c0f5150a2fb remoteexec: Add unit test for gomaOutput.toFileBlob
  - fd8b69d736133b33fdf72eadcb5a9ae50f6f259b Make gomaOutput.outputDirectory concurrent
  - 1a1b7a1311160ae5648046b71bba6cb61fb7b0ef remoteexec: Refactor gomaOutput.traverseTree
  - 6ab056629bc7313832979576de60080e2a800424 remoteexec: add unit test for gomaOutput.outputFile
  - a8a95cf0b9e52dee9040ed1c36eaa41e4ee1f206 remoteexec: Add unit test for traverseTree
  - acec3c2918f16f710f0af6339d522e594f4a3e19 remoteexec: Change order of traverseTree and outputDirect...
  - 5ce3ce9359a7caa980c32bb843d88ea44338cf96 remoteexec: Add helper funcs for gomaoutput_test
  - 0a042edfd6d76d70f3a267e253d9b69736667f6e remoteexec: use command.working_directory for non-Win
  - db9890e479d82b71656e815a775679f612dcdf4b roll cloud.google.com/go v0.52.0 to v0.53.0
  - b239f112ff79a0b5996495c559efbd67fae343d5 use go 1.13.8
  - a181e5a6449ebac79dee577677d459795c207f63 Revert "remoteexec: use command.working_directory"
  - b56b40cfbff6469c81475b097fc10ed93ae89450 remoteexec: use command.working_directory
  - 8c46d9ac9e240a8c270f8069b26beb55c3bbb702 roll contrib.go.opencensus.io/exporter/stackdriver v0.12....
  - 6f6f5357fc33e969a378cd868562bee9bebf59da roll google.golang.org/api v0.16.0 to v0.17.0
  - 6550b010d6604a95939b795712120645d6e72e28 roll google.golang.org/grpc v1.27.0 to v1.27.1
  - 686ecc2d45a88a4e2ae978151964e481da5d8955 roll cloud.google.com/go/pubsub v1.1.0 to v1.2.0
  - 6becb26481d8fc6c30486a8888e4033cd485248a roll google.golang.org/api v0.15.0 to v0.16.0
  - 36a475cc07e1159a3016a1cff12e08a9fbde5cda roll go.opencensus.io v0.22.2 to v0.22.3
  - aa9c9a7b99f14aa427da8a2365f8073991b92da8 roll github.com/grpc-ecosystem/go-grpc-middleware v1.1.0 ...

GitOrigin-RevId: 786216f2f72f949629c33852ff63ce33510edc89
Change-Id: Iefea5aecd28687323d5d6d6e69f5b66e3d601113
diff --git a/OWNERS b/OWNERS
new file mode 100644
index 0000000..d06a3c8
--- /dev/null
+++ b/OWNERS
@@ -0,0 +1,9 @@
+sque@chromium.org
+sque@google.com
+tikuta@chromium.org
+tikuta@google.com
+ukai@chromium.org
+ukai@google.com
+yekuang@google.com
+yyanagisawa@chromium.org
+yyanagisawa@google.com
diff --git a/backend/mixer.go b/backend/mixer.go
index 7419c14..cb59991 100644
--- a/backend/mixer.go
+++ b/backend/mixer.go
@@ -11,6 +11,7 @@
 	"net/http"
 	"net/url"
 
+	"go.chromium.org/goma/server/auth"
 	"go.chromium.org/goma/server/auth/enduser"
 	"go.chromium.org/goma/server/log"
 	pb "go.chromium.org/goma/server/proto/backend"
@@ -148,6 +149,11 @@
 			ctx, err = m.Auth.Auth(ctx, req)
 			if err != nil {
 				code := http.StatusUnauthorized
+				// Making the client to retry with the refreshed token instead of throttling.
+				// Please see (b/150189886) for details.
+				if err == auth.ErrExpired {
+					code = http.StatusServiceUnavailable
+				}
 				http.Error(w, "The request requires user authentication", code)
 				logger.Errorf("auth error %s: %d %s: %v", req.URL.Path, code, http.StatusText(code), err)
 				return
diff --git a/cipd_manifest.txt b/cipd_manifest.txt
index 613b227..35bc87a 100644
--- a/cipd_manifest.txt
+++ b/cipd_manifest.txt
@@ -13,7 +13,7 @@
 # https://chrome-infra-packages.appspot.com/
 
 # go
-infra/3pp/tools/go/${platform} version:1.13.7
+infra/3pp/tools/go/${platform} version:1.14.1
 
 # protoc
 # If the version you want is missing, please follow the instruction in:
diff --git a/cipd_manifest.versions b/cipd_manifest.versions
index e72fe3a..1fdc1a1 100644
--- a/cipd_manifest.versions
+++ b/cipd_manifest.versions
@@ -2,8 +2,8 @@
 # Do not modify manually. All changes will be overwritten.
 
 infra/3pp/tools/go/linux-amd64
-	version:1.13.7
-	BSP3G-zh4nYyRapTOCGWJvMD1O9I7NwkBnxBnsJStNUC
+	version:1.14.1
+	pqblas-RwD-1Ths7vq8uC2SkNfYXUe8l9Y5ygcpDmdwC
 
 infra/tools/protoc/linux-amd64
 	protobuf_version:v3.7.0
diff --git a/cmd/exec_server/main.go b/cmd/exec_server/main.go
index 906aae8..a9fc086 100644
--- a/cmd/exec_server/main.go
+++ b/cmd/exec_server/main.go
@@ -67,6 +67,7 @@
 	remoteexecAddr       = flag.String("remoteexec-addr", "", "use remoteexec API endpoint")
 	remoteInstancePrefix = flag.String("remote-instance-prefix", "", "remote instance name path prefix.")
 	cmdFilesBucket       = flag.String("cmd-files-bucket", "", "cloud storage bucket for command binary files")
+	fetchConfigParallel  = flag.Bool("fetch-config-parallel", false, "fetch toolchain configs in parallel")
 
 	// Needed for b/120582303, but will be deprecated by b/80508682.
 	fileLookupConcurrency = flag.Int("file-lookup-concurrency", 20, "concurrency to look up files from file-server")
@@ -130,11 +131,16 @@
 }
 
 func configureByLoader(ctx context.Context, loader *command.ConfigMapLoader, inventory *exec.Inventory) (string, error) {
+	logger := log.FromContext(ctx)
+	start := time.Now()
 	resp, err := loader.Load(ctx)
+	logger.Infof("loader.Load finished in %s", time.Since(start))
 	if err != nil {
 		return "", err
 	}
+	start = time.Now()
 	err = inventory.Configure(ctx, resp)
+	logger.Infof("inventory.Configure finished in %s", time.Since(start))
 	if err != nil {
 		return "", err
 	}
@@ -196,7 +202,8 @@
 	cs.loader = &command.ConfigMapLoader{
 		ConfigMap: cs.configmap,
 		ConfigLoader: command.ConfigLoader{
-			StorageClient: stiface.AdaptClient(gsclient),
+			StorageClient:  stiface.AdaptClient(gsclient),
+			EnableParallel: *fetchConfigParallel,
 		},
 	}
 	return cs, nil
@@ -352,6 +359,7 @@
 		*fileLookupConcurrency = 1
 	}
 	casBlobLookupConcurrency := 20
+	outputFileConcurrency := 20
 	re := &remoteexec.Adapter{
 		InstancePrefix: *remoteInstancePrefix,
 		ExecTimeout:    15 * time.Minute,
@@ -366,6 +374,7 @@
 		},
 		FileLookupSema:    make(chan struct{}, *fileLookupConcurrency),
 		CASBlobLookupSema: make(chan struct{}, casBlobLookupConcurrency),
+		OutputFileSema:    make(chan struct{}, outputFileConcurrency),
 	}
 
 	if *cmdFilesBucket == "" {
diff --git a/command/configmap.go b/command/configmap.go
index 695e446..eb3df35 100644
--- a/command/configmap.go
+++ b/command/configmap.go
@@ -12,6 +12,7 @@
 	"io/ioutil"
 	"math/rand"
 	"path"
+	"runtime"
 	"sort"
 	"strings"
 	"time"
@@ -23,6 +24,7 @@
 	"github.com/googleapis/google-cloud-go-testing/storage/stiface"
 	"go.opencensus.io/stats"
 	"go.opencensus.io/stats/view"
+	"golang.org/x/sync/errgroup"
 	"google.golang.org/api/iterator"
 
 	"go.chromium.org/goma/server/log"
@@ -383,7 +385,8 @@
 
 // ConfigLoader loads toolchain_config from cloud storage.
 type ConfigLoader struct {
-	StorageClient stiface.Client
+	StorageClient  stiface.Client
+	EnableParallel bool
 
 	// for test
 	versionID func() string
@@ -492,6 +495,7 @@
 // It sets rc.ServiceAddr  as target addr.
 func (c *ConfigLoader) Load(ctx context.Context, uri string, rc *cmdpb.RuntimeConfig) ([]*cmdpb.Config, error) {
 	platform := &cmdpb.RemoteexecPlatform{}
+	parallel := c.EnableParallel
 	for _, p := range rc.Platform.GetProperties() {
 		platform.Properties = append(platform.Properties, &cmdpb.RemoteexecPlatform_Property{
 			Name:  p.Name,
@@ -500,7 +504,7 @@
 	}
 	platform.HasNsjail = rc.GetPlatformRuntimeConfig().GetHasNsjail()
 
-	confs, err := loadConfigs(ctx, c.StorageClient, uri, rc, platform)
+	confs, err := loadConfigs(ctx, c.StorageClient, uri, rc, platform, parallel)
 	if err != nil {
 		return nil, err
 	}
@@ -671,7 +675,7 @@
 	return nil
 }
 
-func loadConfigs(ctx context.Context, client stiface.Client, uri string, rc *cmdpb.RuntimeConfig, platform *cmdpb.RemoteexecPlatform) ([]*cmdpb.Config, error) {
+func loadConfigs(ctx context.Context, client stiface.Client, uri string, rc *cmdpb.RuntimeConfig, platform *cmdpb.RemoteexecPlatform, parallel bool) ([]*cmdpb.Config, error) {
 	logger := log.FromContext(ctx)
 	bucket, obj, err := splitGCSPath(uri)
 	if err != nil {
@@ -689,7 +693,10 @@
 	// pagination?
 	var confs []*cmdpb.Config
 	logger.Infof("load from %s", bucket)
+	start := time.Now()
+	var attrsList []*storage.ObjectAttrs
 	for {
+		// iter does not have an API to read all, so just iterate everything.
 		attrs, err := iter.Next()
 		if err == iterator.Done {
 			break
@@ -697,8 +704,8 @@
 		if err != nil {
 			return nil, err
 		}
-		err = checkPrebuilt(rc, attrs.Name)
-		if err != nil {
+		// Some string ops, no need to be paralleled.
+		if err := checkPrebuilt(rc, attrs.Name); err != nil {
 			logger.Infof("prebuilt %s: %v", attrs.Name, err)
 			continue
 		}
@@ -707,46 +714,76 @@
 			logger.Infof("ignore %s", attrs.Name)
 			continue
 		}
-		d, err := loadDescriptor(ctx, client, bucket, attrs.Name)
-		if err != nil {
-			return nil, err
-		}
-		ts, err := ptypes.TimestampProto(attrs.Updated)
-		if err != nil {
-			return nil, err
-		}
-
-		// check descriptor.
-		err = checkSelector(rc, d.Selector)
-		if err != nil {
-			logger.Errorf("selector in %s/%s: %v", bucket, attrs.Name, err)
+		attrsList = append(attrsList, attrs)
+	}
+	logger.Infof("iterate over %s took %v", bucket, time.Since(start))
+	start = time.Now()
+	concurrent := 1
+	if parallel {
+		// Limit concurrent requests to NumCPU * 4.
+		concurrent = runtime.NumCPU() * 4
+	}
+	// The ordering of the output should be guaranteed
+	// as unit tests using proto.Equal.
+	var eg errgroup.Group
+	confList := make([]*cmdpb.Config, len(attrsList))
+	sema := make(chan struct{}, concurrent)
+	for i := range attrsList {
+		i := i
+		sema <- struct{}{}
+		eg.Go(func() error {
+			// Limit number of goroutines.
+			defer func() { <-sema }()
+			attrs := attrsList[i]
+			d, err := loadDescriptor(ctx, client, bucket, attrs.Name)
+			if err != nil {
+				return err
+			}
+			ts, err := ptypes.TimestampProto(attrs.Updated)
+			if err != nil {
+				return err
+			}
+			if err = checkSelector(rc, d.Selector); err != nil {
+				logger.Errorf("selector in %s/%s: %v", bucket, attrs.Name, err)
+				return nil
+			}
+			if d.Setup == nil {
+				logger.Errorf("no setup in %s/%s", bucket, attrs.Name)
+				return nil
+			}
+			if d.Setup.PathType == cmdpb.CmdDescriptor_UNKNOWN_PATH_TYPE {
+				logger.Errorf("unknown path type in %s/%s", bucket, attrs.Name)
+				return nil
+			}
+			// TODO: fix config definition.
+			// BuildInfo is used for key for cache key.
+			//  include cmd_server hash etc?
+			// BuildInfo.Timestamp is used for dedup in exec_server.
+			confList[i] = &cmdpb.Config{
+				Target: &cmdpb.Target{
+					Addr: rc.ServiceAddr,
+				},
+				BuildInfo: &cmdpb.BuildInfo{
+					Timestamp: ts,
+				},
+				CmdDescriptor:      d,
+				RemoteexecPlatform: platform,
+			}
+			return nil
+		})
+	}
+	if err := eg.Wait(); err != nil {
+		return nil, err
+	}
+	for i := range attrsList {
+		attrs := attrsList[i]
+		conf := confList[i]
+		if conf == nil {
 			continue
 		}
-		if d.Setup == nil {
-			logger.Errorf("no setup in %s/%s", bucket, attrs.Name)
-			continue
-		}
-		if d.Setup.PathType == cmdpb.CmdDescriptor_UNKNOWN_PATH_TYPE {
-			logger.Errorf("unknown path type in %s/%s", bucket, attrs.Name)
-			continue
-		}
-		// TODO: fix config definition.
-		// BuildInfo is used for key for cache key.
-		//  include cmd_server hash etc?
-		// BuildInfo.Timestamp is used for dedup in exec_server.
-		conf := &cmdpb.Config{
-			Target: &cmdpb.Target{
-				Addr: rc.ServiceAddr,
-			},
-			BuildInfo: &cmdpb.BuildInfo{
-				Timestamp: ts,
-			},
-			CmdDescriptor:      d,
-			RemoteexecPlatform: platform,
-		}
 		confs = append(confs, conf)
-		logger.Infof("%s/%s: %s", bucket, attrs.Name, d.GetSelector())
+		logger.Infof("%s/%s: %s", bucket, attrs.Name, conf.CmdDescriptor.GetSelector())
 	}
-	logger.Infof("loaded from %s: %d configs", bucket, len(confs))
+	logger.Infof("loaded from %s: %d configs using %v", bucket, len(confs), time.Since(start))
 	return confs, nil
 }
diff --git a/go.mod b/go.mod
index 4084ef5..75b7140 100644
--- a/go.mod
+++ b/go.mod
@@ -3,28 +3,28 @@
 go 1.12
 
 require (
-	cloud.google.com/go v0.52.0
-	cloud.google.com/go/pubsub v1.1.0
-	cloud.google.com/go/storage v1.5.0
-	contrib.go.opencensus.io/exporter/stackdriver v0.12.9
+	cloud.google.com/go v0.55.0
+	cloud.google.com/go/pubsub v1.3.1
+	cloud.google.com/go/storage v1.6.0
+	contrib.go.opencensus.io/exporter/stackdriver v0.13.0
 	github.com/bazelbuild/remote-apis v0.0.0-20191104140458-e77c4eb2ca48
 	github.com/bazelbuild/remote-apis-sdks v0.0.0-20200117155253-d02017f96d3b
 	github.com/fsnotify/fsnotify v1.4.7
-	github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7
-	github.com/golang/protobuf v1.3.3
+	github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e
+	github.com/golang/protobuf v1.3.5
 	github.com/gomodule/redigo v2.0.0+incompatible
 	github.com/google/go-cmp v0.4.0
 	github.com/google/uuid v1.1.1
 	github.com/googleapis/gax-go/v2 v2.0.5
 	github.com/googleapis/google-cloud-go-testing v0.0.0-20190904031503-2d24dde44ba5
-	github.com/grpc-ecosystem/go-grpc-middleware v1.1.0
-	go.opencensus.io v0.22.2
+	github.com/grpc-ecosystem/go-grpc-middleware v1.2.0
+	go.opencensus.io v0.22.3
 	go.uber.org/zap v1.10.0
 	golang.org/x/build v0.0.0-20191031202223-0706ea4fce0c
-	golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa
+	golang.org/x/net v0.0.0-20200301022130-244492dfa37a
 	golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d
-	golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e
-	google.golang.org/api v0.15.0
-	google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba
-	google.golang.org/grpc v1.27.0
+	golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a
+	google.golang.org/api v0.20.0
+	google.golang.org/genproto v0.0.0-20200317114155-1f3552e48f24
+	google.golang.org/grpc v1.28.0
 )
diff --git a/go.sum b/go.sum
index f9b193a..9b356ad 100644
--- a/go.sum
+++ b/go.sum
@@ -14,22 +14,40 @@
 cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To=
 cloud.google.com/go v0.52.0 h1:GGslhk/BU052LPlnI1vpp3fcbUs+hQ3E+Doti/3/vF8=
 cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4=
+cloud.google.com/go v0.53.0 h1:MZQCQQaRwOrAcuKjiHWHrgKykt4fZyuwF2dtiG3fGW8=
+cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M=
+cloud.google.com/go v0.54.0 h1:3ithwDMr7/3vpAMXiH+ZQnYbuIsh+OPhUPMFC9enmn0=
+cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc=
+cloud.google.com/go v0.55.0 h1:eoz/lYxKSL4CNAiaUJ0ZfD1J3bfMYbU5B3rwM1C1EIU=
+cloud.google.com/go v0.55.0/go.mod h1:ZHmoY+/lIMNkN2+fBmuTiqZ4inFhvQad8ft7MT8IV5Y=
 cloud.google.com/go/bigquery v1.0.1 h1:hL+ycaJpVE9M7nLoiXb/Pn10ENE2u+oddxbD8uu0ZVU=
 cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
 cloud.google.com/go/bigquery v1.3.0 h1:sAbMqjY1PEQKZBWfbu6Y6bsupJ9c4QdHnzg/VvYTLcE=
 cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=
+cloud.google.com/go/bigquery v1.4.0 h1:xE3CPsOgttP4ACBePh79zTKALtXwn/Edhcr16R5hMWU=
+cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=
+cloud.google.com/go/bigquery v1.5.0 h1:K2NyuHRuv15ku6eUpe0DQk5ZykPMnSOnvuVf6IHcjaE=
+cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg=
 cloud.google.com/go/datastore v1.0.0 h1:Kt+gOPPp2LEPWp8CSfxhsM8ik9CcyE/gYu+0r+RnZvM=
 cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
+cloud.google.com/go/datastore v1.1.0 h1:/May9ojXjRkPBNVrq+oWLqmWCkr4OU5uRY29bu0mRyQ=
+cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=
 cloud.google.com/go/pubsub v1.0.1 h1:W9tAK3E57P75u0XLLR82LZyw8VpAnhmyTOxW9qzmyj8=
 cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
 cloud.google.com/go/pubsub v1.1.0 h1:9/vpR43S4aJaROxqQHQ3nH9lfyKKV0dC3vOmnw8ebQQ=
 cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=
+cloud.google.com/go/pubsub v1.2.0 h1:Lpy6hKgdcl7a3WGSfJIFmxmcdjSpP6OmBEfcOv1Y680=
+cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA=
+cloud.google.com/go/pubsub v1.3.1 h1:ukjixP1wl0LpnZ6LWtZJ0mX5tBmjp1f8Sqer8Z2OMUU=
+cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU=
 cloud.google.com/go/storage v1.0.0 h1:VV2nUM3wwLLGh9lSABFgZMjInyUbJeaRSE64WuAIQ+4=
 cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
 cloud.google.com/go/storage v1.5.0 h1:RPUcBvDeYgQFMfQu1eBMq6piD1SXmLH+vK3qjewZPus=
 cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos=
-contrib.go.opencensus.io/exporter/stackdriver v0.12.9 h1:ZRVpDigsb+nVI/yps/NLDOYzYjFFmm3OCsBhmYocxR0=
-contrib.go.opencensus.io/exporter/stackdriver v0.12.9/go.mod h1:XyyafDnFOsqoxHJgTFycKZMrRUrPThLh2iYTJF6uoO0=
+cloud.google.com/go/storage v1.6.0 h1:UDpwYIwla4jHGzZJaEJYx1tOejbgSoNqsAfHAUYe2r8=
+cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=
+contrib.go.opencensus.io/exporter/stackdriver v0.13.0 h1:Jaz7WbqjtfoCPa1KbfisCX+P5aM3DizEY9pQMU0oAQo=
+contrib.go.opencensus.io/exporter/stackdriver v0.13.0/go.mod h1:z2tyTZtPmQ2HvWH4cOmVDgtY+1lomfKdbLnkJvZdc8c=
 dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
 github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
 github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
@@ -54,6 +72,7 @@
 github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
 github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
 github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
+github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
 github.com/coreos/go-systemd v0.0.0-20181012123002-c6f51f82210d/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
 github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
 github.com/cznic/cc v0.0.0-20181122101902-d673e9b70d4d/go.mod h1:m3fD/V+XTB35Kh9zw6dzjMY+We0Q7PMf6LLIC4vuG9k=
@@ -71,7 +90,9 @@
 github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/disintegration/gift v1.2.1/go.mod h1:Jh2i7f7Q2BM7Ezno3PhfezbR1xpUg9dUg3/RlKGr4HI=
 github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M=
+github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
 github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
+github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
 github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
 github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
 github.com/fogleman/gg v1.3.0/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k=
@@ -81,6 +102,10 @@
 github.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
 github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
 github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
+github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
+github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
+github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
+github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
 github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=
 github.com/godbus/dbus v4.1.0+incompatible/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw=
 github.com/gogo/protobuf v1.2.1 h1:/s5zKNz0uPFCZ5hddgPdo2TK2TVrUNMn0OOX8/aZMTE=
@@ -92,11 +117,19 @@
 github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
 github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7 h1:5ZkaAPbicIKTF2I64qf5Fh8Aa83Q/dnOafMYV0OMwjA=
 github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
+github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e h1:1r7pUrabqp18hOBcwBwiTsbnFeTZHV9eER/QT5JVZxY=
+github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
 github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
 github.com/golang/mock v1.2.0 h1:28o5sBqPkBsMGnC6b4MvE2TzSr5/AT4c/1fLqVGIwlk=
 github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
 github.com/golang/mock v1.3.1 h1:qGJ6qTW+x6xX/my+8YUVl4WNpX9B7+/l2tRsHGZ7f2s=
 github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
+github.com/golang/mock v1.4.0 h1:Rd1kQnQu0Hq3qvJppYSG0HtP+f5LPPUiDswTLiEegLg=
+github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
+github.com/golang/mock v1.4.1 h1:ocYkMQY5RrXTYgXl7ICpV0IXwlEQGwKIsery4gyXa1U=
+github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
+github.com/golang/mock v1.4.3 h1:GV+pQPG/EUUbkh47niozDcADz6go/dUwhVzdUQHIVRw=
+github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
 github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
 github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg=
 github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
@@ -104,6 +137,10 @@
 github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
 github.com/golang/protobuf v1.3.3 h1:gyjaxf+svBWX08ZjK86iN9geUJF0H6gp2IRKX6Nf6/I=
 github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
+github.com/golang/protobuf v1.3.4 h1:87PNWwrRvUSnqS4dlcBU/ftvOIBep4sYuBLlh6rX2wk=
+github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
+github.com/golang/protobuf v1.3.5 h1:F768QJ1E9tib+q5Sc8MkdJi1RxLTbRcTf8LJV56aRls=
+github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk=
 github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
 github.com/gomodule/redigo v2.0.0+incompatible h1:K/R+8tc58AaqLkqG2Ol3Qk+DR/TlNuhuh457pBFPtt0=
 github.com/gomodule/redigo v2.0.0+incompatible/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4=
@@ -135,6 +172,10 @@
 github.com/google/pprof v0.0.0-20191028172815-5e965273ee43/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
 github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc h1:DLpL8pWq0v4JYoRpEhDfsJhhJyGKCcQM2WPW2TJs31c=
 github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
+github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12 h1:TgXhFz35pKlZuUz1pNlOKk1UCSXPpuUIc144Wd7SxCA=
+github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
+github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3 h1:SRgJV+IoxM5MKyFdlSUeNy6/ycRUF2yBAKdAQswoHUk=
+github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
 github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
 github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
 github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY=
@@ -148,8 +189,8 @@
 github.com/googleapis/google-cloud-go-testing v0.0.0-20190904031503-2d24dde44ba5 h1:xOo+ADhpvq/M2z8t7xMswzeaSa5oPDRNjSildIGfF5U=
 github.com/googleapis/google-cloud-go-testing v0.0.0-20190904031503-2d24dde44ba5/go.mod h1:xRZIzjtWZ0v0RQc6ob2jLaRvHJu8rShcPkTGPCStK04=
 github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
-github.com/grpc-ecosystem/go-grpc-middleware v1.1.0 h1:THDBEeQ9xZ8JEaCLyLQqXMMdRqNr0QAUJTIkQAUtFjg=
-github.com/grpc-ecosystem/go-grpc-middleware v1.1.0/go.mod h1:f5nM7jw/oeRSadq3xCzHAvxcr8HZnzsqU6ILg/0NiiE=
+github.com/grpc-ecosystem/go-grpc-middleware v1.2.0 h1:0IKlLyQ3Hs9nDaiK5cSHAGmcQEIC8l2Ts1u6x5Dfrqg=
+github.com/grpc-ecosystem/go-grpc-middleware v1.2.0/go.mod h1:mJzapYve32yjrKlk9GbyCZHuPgZsrbyIbyKhSzOpg6s=
 github.com/hashicorp/golang-lru v0.5.0 h1:CL2msUPvZTLb5O648aiLNJw3hnBxN2+1Jq8rCOH9wdo=
 github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
 github.com/hashicorp/golang-lru v0.5.1 h1:0hERBMJE1eitiLkihrMvRVBYAkpHzc/J3QdDN+dAcgU=
@@ -169,6 +210,7 @@
 github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
 github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk=
 github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
+github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
 github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
 github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
 github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
@@ -208,6 +250,7 @@
 github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
 github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ=
 github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA=
+github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
 go.opencensus.io v0.21.0 h1:mU6zScU4U1YAFPHEHYk+3JC4SY7JxgkqS10ZOSyksNg=
 go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
 go.opencensus.io v0.22.0 h1:C9hSCOW830chIVkdja34wa6Ky+IzWllkUinR+BtRZd4=
@@ -216,6 +259,8 @@
 go.opencensus.io v0.22.1/go.mod h1:Ap50jQcDJrx6rB6VgeeFPtuPIf3wMRvRfrfYDO6+BmA=
 go.opencensus.io v0.22.2 h1:75k/FF0Q2YM8QYo07VPddOLBslDt1MZOdEslOHvmzAs=
 go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
+go.opencensus.io v0.22.3 h1:8sGtKOrtQqkN1bp2AtX+misvLIlOmsEsNd+9NIcPEm8=
+go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
 go.uber.org/atomic v1.4.0 h1:cxzIVoETapQEqDhQu3QfnvXAV4AlzcvUCxkVUFw3+EU=
 go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
 go.uber.org/multierr v1.1.0 h1:HoEmRHQPVSqub6w2z2d2EOVs2fjyFRGyofhKuyDq0QI=
@@ -243,6 +288,11 @@
 golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
 golang.org/x/exp v0.0.0-20191227195350-da58074b4299 h1:zQpM52jfKHG6II1ISZY1ZcpygvuSFZpLwfluuF89XOg=
 golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
+golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a h1:7Wlg8L54In96HTWOaI4sreLJ6qfyGuvSau5el3fK41Y=
+golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
+golang.org/x/exp v0.0.0-20200207192155-f17229e696bd h1:zkO/Lhoka23X63N9OSzpSeROEUQ5ODw47tM3YWjygbs=
+golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
+golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
 golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
 golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
 golang.org/x/image v0.0.0-20190910094157-69e4b8554b2a/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
@@ -258,12 +308,19 @@
 golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
 golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f h1:J5lckAjkw6qYlOZNj90mLYNTEKDvWeuc1yieZ8qUzUE=
 golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs=
+golang.org/x/lint v0.0.0-20200130185559-910be7a94367 h1:0IiAsCRByjO2QjX7ZPkw5oU9x+n1YqRL802rjC0c3Aw=
+golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
+golang.org/x/lint v0.0.0-20200302205851-738671d3881b h1:Wh+f8QHJXR411sJR8/vRBTZ7YapZaRvUcLFFJhusH0k=
+golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
 golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
 golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
 golang.org/x/mobile v0.0.0-20191031020345-0945064e013a/go.mod h1:p895TfNkDgPEmEQrNiOtIl3j98d/tGU95djDj7NfyjQ=
 golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
 golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
 golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
+golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
+golang.org/x/mod v0.2.0 h1:KU7oHjnv3XNWfa5COkzUifxZmxp1TyI7ImMXqFxLwvQ=
+golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
 golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -278,13 +335,18 @@
 golang.org/x/net v0.0.0-20190620200207-3b0461eec859 h1:R/3boaszxrf1GEUWTVDzSKVwLmSJpwZ1yqXm8j0v2QI=
 golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
-golang.org/x/net v0.0.0-20190912160710-24e19bdeb0f2 h1:4dVFTC832rPn4pomLSz1vA+are2+dU19w1H8OngV7nc=
-golang.org/x/net v0.0.0-20190912160710-24e19bdeb0f2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 golang.org/x/net v0.0.0-20191101175033-0deb6923b6d9/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553 h1:efeOvDhwQ29Dj3SdAV/MJf8oukgn+8D8WgaCaRMchF8=
 golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa h1:F+8P+gmewFQYRk6JoLQLwjBCTu3mcIURZfNkVweuRKA=
 golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20200202094626-16171245cfb2 h1:CCH4IOTTfewWjGOlSp+zGcjutRKlBEZQ6wTn8ozI/nI=
+golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20200301022130-244492dfa37a h1:GuSPYbZzB5/dcLNCwLQLsg3obCJtX9IJhpXkvY7kzk0=
+golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
 golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421 h1:Wo7BWFiOk0QRFMLYMqJGFMd9CgUAcGx7V+qEg/h5IBI=
 golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@@ -305,6 +367,8 @@
 golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e h1:vcxGaoTs7kV8m5Np9uUNQin4BrLOthgV7252N8V+FwY=
 golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a h1:WXEvlFVvvGxCJLG6REjsT03iWnKLEWinaScsxF2Vm2o=
+golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a h1:1BGLXjeY4akVXGgbC9HugT3Jv3hCI0z56oJR5vAMgBU=
@@ -319,14 +383,24 @@
 golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0 h1:HyfiK1WMnHj5FXFXatD+Qs1A/xC2Run6RzeW1SyHxpc=
 golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20190912141932-bc967efca4b8 h1:41hwlulw1prEMBxLQSlMSux1zxJf07B3WPsdjJlKZxE=
-golang.org/x/sys v0.0.0-20190912141932-bc967efca4b8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20191104094858-e8c54fb511f6/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8 h1:JA8d3MPx/IToSyXZG/RhwYEtfrKO1Fxrqe8KrkiLXKM=
 golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20200113162924-86b910548bc1 h1:gZpLHxUX5BdYLA08Lj4YCJNN/jk7KtquiArPoeX0WvA=
 golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5 h1:LfCXLvNmTYH9kEmVgqbnsWfruoXZIrh4YBgqVHtDvw0=
+golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4 h1:sfkvUWPNGwSV+8/fNqctR5lS2AqCSqYwXdrjCxp/dXo=
+golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527 h1:uYVVQ9WP/Ds2ROhcaGPeIdVq0RIXVLwsHlnvJ+cT1So=
+golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200317113312-5766fd39f98d h1:62ap6LNOjDU6uGmKXHJbSfciMoV+FeI1sRXx/pLDL44=
+golang.org/x/sys v0.0.0-20200317113312-5766fd39f98d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2 h1:z99zHgr7hKfrUcX/KsoJk5FJfjTceCKIp96+biqP4To=
 golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@@ -353,17 +427,35 @@
 golang.org/x/tools v0.0.0-20190909214602-067311248421/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
 golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
 golang.org/x/tools v0.0.0-20190927191325-030b2cf1153e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20191010075000-0337d82405ff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
 golang.org/x/tools v0.0.0-20191010171213-8abd42400456/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
 golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
 golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
 golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2 h1:EtTFh6h4SAKemS+CURDMTDIANuduG5zKEXShyy18bGA=
 golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
 golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
 golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
 golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4 h1:Toz2IK7k8rbltAXwNAxKcn9OzqyNfMUhUNjz3sL0NMk=
 golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
 golang.org/x/tools v0.0.0-20200117161641-43d50277825c h1:2EA2K0k9bcvvEDlqD8xdlOhCOqq+O/p9Voqi4x9W1YU=
 golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74 h1:KW20qMcLRWuIgjdCpHFJbVZA7zsDKtFXPNcm7/eI5ZA=
+golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56 h1:DFtSed2q3HtNuVazwVDZ4nSRS/JrZEig0gz2BY4VNrg=
+golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb h1:iKlO7ROJc6SttHKlxzwGytRtBUqX4VARrNTgP2YLX5M=
+golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
+golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d h1:3K34ovZAOnVaUPxanr0j4ghTZTPTA0CnXvjCl+5lZqk=
+golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
+golang.org/x/tools v0.0.0-20200317043434-63da46f3035e h1:8ogAbHWoJTPepnVbNRqXLOpzMkl0rtRsM7crbflc4XM=
+golang.org/x/tools v0.0.0-20200317043434-63da46f3035e/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=
 golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
@@ -381,6 +473,12 @@
 google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
 google.golang.org/api v0.15.0 h1:yzlyyDW/J0w8yNFJIhiAJy4kq74S+1DOLdawELNxFMA=
 google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
+google.golang.org/api v0.17.0 h1:0q95w+VuFtv4PAx4PZVQdBMmYbaCHbnfKaEiDIcVyag=
+google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
+google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
+google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
+google.golang.org/api v0.20.0 h1:jz2KixHX7EcCPiQrySzPdnYT7DbINAypCqKZ1Z7GM40=
+google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
 google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
 google.golang.org/appengine v1.4.0 h1:/wp5JvzpHIxhs/dumFmF7BXTf3Z+dd4uXta4kVyO508=
 google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
@@ -415,6 +513,19 @@
 google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
 google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba h1:pRj9OXZbwNtbtZtOB4dLwfK4u+EVRMvP+e9zKkg2grM=
 google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
+google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
+google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90 h1:7THRSvPuzF1bql5kyFzX0JM0vpGhwuhskgJrJsbZ80Y=
+google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA=
+google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce h1:1mbrb1tUU+Zmt5C94IGKADBTJZjZXAd+BubWi7r9EiI=
+google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
+google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
+google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
+google.golang.org/genproto v0.0.0-20200305110556-506484158171 h1:xes2Q2k+d/+YNXVw0FpZkIDJiaux4OVrRKXRAzH6A0U=
+google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
+google.golang.org/genproto v0.0.0-20200312145019-da6875a35672 h1:jiDSspVssiikoRPFHT6pYrL+CL6/yIc3b9AuHO/4xik=
+google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
+google.golang.org/genproto v0.0.0-20200317114155-1f3552e48f24 h1:IGPykv426z7LZSVPlaPufOyphngM4at5uZ7x5alaFvE=
+google.golang.org/genproto v0.0.0-20200317114155-1f3552e48f24/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
 google.golang.org/grpc v1.19.0 h1:cfg4PD8YEdSFnm7qLV4++93WcmhH2nIUhMjhdCvl3j8=
 google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
 google.golang.org/grpc v1.20.1 h1:Hz2g2wirWK7H0qIIhGIqRGTuMwTE8HEKFnDZZ7lm9NU=
@@ -424,10 +535,15 @@
 google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
 google.golang.org/grpc v1.23.1 h1:q4XQuHFC6I28BKZpo6IYyb3mNO+l7lSOxRuYTCiDfXk=
 google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
+google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
 google.golang.org/grpc v1.26.0 h1:2dTRdpdFEEhJYQD8EMLB61nnrzSCTbG38PhqdhvOltg=
 google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
 google.golang.org/grpc v1.27.0 h1:rRYRFMVgRv6E0D70Skyfsr28tDXIuuPZyWGMPdMcnXg=
 google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
+google.golang.org/grpc v1.27.1 h1:zvIju4sqAGvwKspUQOhwnpcqSbzi7/H6QomNNjTL4sk=
+google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
+google.golang.org/grpc v1.28.0 h1:bO/TA4OxCOummhSf10siHuG7vJOiwh7SpRpFZDkOgl4=
+google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60=
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
 gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
@@ -448,4 +564,8 @@
 honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
 honnef.co/go/tools v0.0.1-2019.2.3 h1:3JgtbtFHMiCmsznwGVTUWbgGov+pVqnlf1dEJTNAXeM=
 honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
+honnef.co/go/tools v0.0.1-2020.1.3 h1:sXmLre5bzIR6ypkjXCDI3jHPssRhc8KD/Ome589sc3U=
+honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
 rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
+rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
+rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
diff --git a/httprpc/server.go b/httprpc/server.go
index 838431b..6372086 100644
--- a/httprpc/server.go
+++ b/httprpc/server.go
@@ -370,13 +370,16 @@
 				authOK = true
 			}
 			resp, err = h(ctx, req)
+			if err != nil {
+				logger.Warnf("handler error %v; ctx.Err()=%v", err, ctx.Err())
+			}
 			if opt.Auth != nil && status.Code(err) == codes.Unauthenticated {
 				logger.Warnf("retry for unauthenticated %v", err)
 				return rpc.RetriableError{
 					Err: err,
 				}
 			}
-			if status.Code(err) == codes.DeadlineExceeded && pctx.Err() == nil {
+			if ((err != nil && ctx.Err() == context.DeadlineExceeded) || status.Code(err) == codes.DeadlineExceeded) && pctx.Err() == nil {
 				// api call is timed out, but caller's context is not.
 				// it would happen
 				// a) timeout was short; api call actually needs more time.
diff --git a/proto/api/goma_data.pb.go b/proto/api/goma_data.pb.go
index 8073e79..d4f5816 100644
--- a/proto/api/goma_data.pb.go
+++ b/proto/api/goma_data.pb.go
@@ -6,6 +6,7 @@
 import (
 	fmt "fmt"
 	proto "github.com/golang/protobuf/proto"
+	timestamp "github.com/golang/protobuf/ptypes/timestamp"
 	math "math"
 )
 
@@ -27,6 +28,8 @@
 	FileBlob_FILE             FileBlob_BlobType = 1
 	FileBlob_FILE_META        FileBlob_BlobType = 2
 	FileBlob_FILE_CHUNK       FileBlob_BlobType = 3
+	// ARCHIVE = 4;
+	FileBlob_FILE_REF FileBlob_BlobType = 5
 )
 
 var FileBlob_BlobType_name = map[int32]string{
@@ -34,6 +37,7 @@
 	1: "FILE",
 	2: "FILE_META",
 	3: "FILE_CHUNK",
+	5: "FILE_REF",
 }
 
 var FileBlob_BlobType_value = map[string]int32{
@@ -41,6 +45,7 @@
 	"FILE":             1,
 	"FILE_META":        2,
 	"FILE_CHUNK":       3,
+	"FILE_REF":         5,
 }
 
 func (x FileBlob_BlobType) Enum() *FileBlob_BlobType {
@@ -340,15 +345,21 @@
 
 // hash_key = sha256(serialized FileBlob)
 //
-// for small file (< 2MB)
+// for small file (< 2MB) embedded
 //   blob_type=FILE, !has_offset(), has_content()
 //                   has_file_size(), hash_key_size() == 0
+//
 // for large file
 //   blob_type=FILE_META, !has_offset(), !has_content()
 //                   has_file_size(), hash_key_size() > 0
 //  for each hash_key(i)
 //     blob_type=FILE_CHUNK, has_offset(), has_content(),
 //                   has_file_size(), hash_key_size() == 0
+//
+// for small file (< 2MB), stored as blob_type=FILE in file server with
+// key=hash_key[0]
+//   blob_type=FILE_REF, !hash_offset(), !has_content()
+//                   has_file_size(), hash_key_size() == 1
 type FileBlob struct {
 	BlobType *FileBlob_BlobType `protobuf:"varint,1,req,name=blob_type,json=blobType,enum=devtools_goma.FileBlob_BlobType" json:"blob_type,omitempty"`
 	// for blob_type=FILE_CHUNK
@@ -883,7 +894,7 @@
 	// e.g. "os":"linux".
 	//
 	// Inspired from swarming bot dimensions.
-	// https://cs.chromium.org/chromium/infra/luci/appengine/swarming/proto/bots.proto
+	// https://chromium.googlesource.com/infra/luci/luci-py/+/effbcfcafa96e0840189a98ac485586ba2c2ceb6/appengine/swarming/proto/config/bots.proto
 	Dimensions []string `protobuf:"bytes,10,rep,name=dimensions" json:"dimensions,omitempty"`
 	// Requester's path style.
 	PathStyle            *RequesterInfo_PathStyle `protobuf:"varint,11,opt,name=path_style,json=pathStyle,enum=devtools_goma.RequesterInfo_PathStyle" json:"path_style,omitempty"`
@@ -1344,53 +1355,58 @@
 	return nil
 }
 
-type MultiExecReq struct {
-	Req                  []*ExecReq     `protobuf:"bytes,1,rep,name=req" json:"req,omitempty"`
-	RequesterInfo        *RequesterInfo `protobuf:"bytes,10,opt,name=requester_info,json=requesterInfo" json:"requester_info,omitempty"`
-	XXX_NoUnkeyedLiteral struct{}       `json:"-"`
-	XXX_unrecognized     []byte         `json:"-"`
-	XXX_sizecache        int32          `json:"-"`
+// Stats of a single RBE execution. This is a subset of
+// https://github.com/bazelbuild/remote-apis/blob/178b756a22d441d8d06873a70bcd0ef01d876467/build/bazel/remote/execution/v2/remote_execution.proto#L789-L819
+type ExecutionStats struct {
+	// When the worker started executing the action command.
+	ExecutionStartTimestamp *timestamp.Timestamp `protobuf:"bytes,1,opt,name=execution_start_timestamp,json=executionStartTimestamp" json:"execution_start_timestamp,omitempty"`
+	// When the worker completed executing the action command.
+	ExecutionCompletedTimestamp *timestamp.Timestamp `protobuf:"bytes,2,opt,name=execution_completed_timestamp,json=executionCompletedTimestamp" json:"execution_completed_timestamp,omitempty"`
+	XXX_NoUnkeyedLiteral        struct{}             `json:"-"`
+	XXX_unrecognized            []byte               `json:"-"`
+	XXX_sizecache               int32                `json:"-"`
 }
 
-func (m *MultiExecReq) Reset()         { *m = MultiExecReq{} }
-func (m *MultiExecReq) String() string { return proto.CompactTextString(m) }
-func (*MultiExecReq) ProtoMessage()    {}
-func (*MultiExecReq) Descriptor() ([]byte, []int) {
+func (m *ExecutionStats) Reset()         { *m = ExecutionStats{} }
+func (m *ExecutionStats) String() string { return proto.CompactTextString(m) }
+func (*ExecutionStats) ProtoMessage()    {}
+func (*ExecutionStats) Descriptor() ([]byte, []int) {
 	return fileDescriptor_8016d97e57513e7e, []int{8}
 }
 
-func (m *MultiExecReq) XXX_Unmarshal(b []byte) error {
-	return xxx_messageInfo_MultiExecReq.Unmarshal(m, b)
+func (m *ExecutionStats) XXX_Unmarshal(b []byte) error {
+	return xxx_messageInfo_ExecutionStats.Unmarshal(m, b)
 }
-func (m *MultiExecReq) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
-	return xxx_messageInfo_MultiExecReq.Marshal(b, m, deterministic)
+func (m *ExecutionStats) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	return xxx_messageInfo_ExecutionStats.Marshal(b, m, deterministic)
 }
-func (m *MultiExecReq) XXX_Merge(src proto.Message) {
-	xxx_messageInfo_MultiExecReq.Merge(m, src)
+func (m *ExecutionStats) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_ExecutionStats.Merge(m, src)
 }
-func (m *MultiExecReq) XXX_Size() int {
-	return xxx_messageInfo_MultiExecReq.Size(m)
+func (m *ExecutionStats) XXX_Size() int {
+	return xxx_messageInfo_ExecutionStats.Size(m)
 }
-func (m *MultiExecReq) XXX_DiscardUnknown() {
-	xxx_messageInfo_MultiExecReq.DiscardUnknown(m)
+func (m *ExecutionStats) XXX_DiscardUnknown() {
+	xxx_messageInfo_ExecutionStats.DiscardUnknown(m)
 }
 
-var xxx_messageInfo_MultiExecReq proto.InternalMessageInfo
+var xxx_messageInfo_ExecutionStats proto.InternalMessageInfo
 
-func (m *MultiExecReq) GetReq() []*ExecReq {
+func (m *ExecutionStats) GetExecutionStartTimestamp() *timestamp.Timestamp {
 	if m != nil {
-		return m.Req
+		return m.ExecutionStartTimestamp
 	}
 	return nil
 }
 
-func (m *MultiExecReq) GetRequesterInfo() *RequesterInfo {
+func (m *ExecutionStats) GetExecutionCompletedTimestamp() *timestamp.Timestamp {
 	if m != nil {
-		return m.RequesterInfo
+		return m.ExecutionCompletedTimestamp
 	}
 	return nil
 }
 
+// NEXT ID TO USE: 82
 type ExecResp struct {
 	Result                                      *ExecResult                    `protobuf:"bytes,1,opt,name=result" json:"result,omitempty"`
 	Error                                       *ExecResp_ExecError            `protobuf:"varint,2,opt,name=error,enum=devtools_goma.ExecResp_ExecError,def=0" json:"error,omitempty"`
@@ -1428,9 +1444,11 @@
 	CompilerProxyLocalRun            *bool    `protobuf:"varint,75,opt,name=compiler_proxy_local_run,json=compilerProxyLocalRun" json:"compiler_proxy_local_run,omitempty"`
 	CompilerProxyLocalKilled         *bool    `protobuf:"varint,76,opt,name=compiler_proxy_local_killed,json=compilerProxyLocalKilled" json:"compiler_proxy_local_killed,omitempty"`
 	CompilerProxyExecRequestRetry    *int32   `protobuf:"varint,80,opt,name=compiler_proxy_exec_request_retry,json=compilerProxyExecRequestRetry" json:"compiler_proxy_exec_request_retry,omitempty"`
-	XXX_NoUnkeyedLiteral             struct{} `json:"-"`
-	XXX_unrecognized                 []byte   `json:"-"`
-	XXX_sizecache                    int32    `json:"-"`
+	// Execution stats collected from RBE
+	ExecutionStats       *ExecutionStats `protobuf:"bytes,81,opt,name=execution_stats,json=executionStats" json:"execution_stats,omitempty"`
+	XXX_NoUnkeyedLiteral struct{}        `json:"-"`
+	XXX_unrecognized     []byte          `json:"-"`
+	XXX_sizecache        int32           `json:"-"`
 }
 
 func (m *ExecResp) Reset()         { *m = ExecResp{} }
@@ -1688,88 +1706,9 @@
 	return 0
 }
 
-type MultiExecResp struct {
-	Response             []*MultiExecResp_Response `protobuf:"group,1,rep,name=Response,json=response" json:"response,omitempty"`
-	XXX_NoUnkeyedLiteral struct{}                  `json:"-"`
-	XXX_unrecognized     []byte                    `json:"-"`
-	XXX_sizecache        int32                     `json:"-"`
-}
-
-func (m *MultiExecResp) Reset()         { *m = MultiExecResp{} }
-func (m *MultiExecResp) String() string { return proto.CompactTextString(m) }
-func (*MultiExecResp) ProtoMessage()    {}
-func (*MultiExecResp) Descriptor() ([]byte, []int) {
-	return fileDescriptor_8016d97e57513e7e, []int{10}
-}
-
-func (m *MultiExecResp) XXX_Unmarshal(b []byte) error {
-	return xxx_messageInfo_MultiExecResp.Unmarshal(m, b)
-}
-func (m *MultiExecResp) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
-	return xxx_messageInfo_MultiExecResp.Marshal(b, m, deterministic)
-}
-func (m *MultiExecResp) XXX_Merge(src proto.Message) {
-	xxx_messageInfo_MultiExecResp.Merge(m, src)
-}
-func (m *MultiExecResp) XXX_Size() int {
-	return xxx_messageInfo_MultiExecResp.Size(m)
-}
-func (m *MultiExecResp) XXX_DiscardUnknown() {
-	xxx_messageInfo_MultiExecResp.DiscardUnknown(m)
-}
-
-var xxx_messageInfo_MultiExecResp proto.InternalMessageInfo
-
-func (m *MultiExecResp) GetResponse() []*MultiExecResp_Response {
+func (m *ExecResp) GetExecutionStats() *ExecutionStats {
 	if m != nil {
-		return m.Response
-	}
-	return nil
-}
-
-type MultiExecResp_Response struct {
-	ResponseCode         *int32    `protobuf:"varint,2,opt,name=response_code,json=responseCode" json:"response_code,omitempty"`
-	Resp                 *ExecResp `protobuf:"bytes,3,opt,name=resp" json:"resp,omitempty"`
-	XXX_NoUnkeyedLiteral struct{}  `json:"-"`
-	XXX_unrecognized     []byte    `json:"-"`
-	XXX_sizecache        int32     `json:"-"`
-}
-
-func (m *MultiExecResp_Response) Reset()         { *m = MultiExecResp_Response{} }
-func (m *MultiExecResp_Response) String() string { return proto.CompactTextString(m) }
-func (*MultiExecResp_Response) ProtoMessage()    {}
-func (*MultiExecResp_Response) Descriptor() ([]byte, []int) {
-	return fileDescriptor_8016d97e57513e7e, []int{10, 0}
-}
-
-func (m *MultiExecResp_Response) XXX_Unmarshal(b []byte) error {
-	return xxx_messageInfo_MultiExecResp_Response.Unmarshal(m, b)
-}
-func (m *MultiExecResp_Response) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
-	return xxx_messageInfo_MultiExecResp_Response.Marshal(b, m, deterministic)
-}
-func (m *MultiExecResp_Response) XXX_Merge(src proto.Message) {
-	xxx_messageInfo_MultiExecResp_Response.Merge(m, src)
-}
-func (m *MultiExecResp_Response) XXX_Size() int {
-	return xxx_messageInfo_MultiExecResp_Response.Size(m)
-}
-func (m *MultiExecResp_Response) XXX_DiscardUnknown() {
-	xxx_messageInfo_MultiExecResp_Response.DiscardUnknown(m)
-}
-
-var xxx_messageInfo_MultiExecResp_Response proto.InternalMessageInfo
-
-func (m *MultiExecResp_Response) GetResponseCode() int32 {
-	if m != nil && m.ResponseCode != nil {
-		return *m.ResponseCode
-	}
-	return 0
-}
-
-func (m *MultiExecResp_Response) GetResp() *ExecResp {
-	if m != nil {
-		return m.Resp
+		return m.ExecutionStats
 	}
 	return nil
 }
@@ -1786,7 +1725,7 @@
 func (m *StoreFileReq) String() string { return proto.CompactTextString(m) }
 func (*StoreFileReq) ProtoMessage()    {}
 func (*StoreFileReq) Descriptor() ([]byte, []int) {
-	return fileDescriptor_8016d97e57513e7e, []int{11}
+	return fileDescriptor_8016d97e57513e7e, []int{10}
 }
 
 func (m *StoreFileReq) XXX_Unmarshal(b []byte) error {
@@ -1832,7 +1771,7 @@
 func (m *StoreFileResp) String() string { return proto.CompactTextString(m) }
 func (*StoreFileResp) ProtoMessage()    {}
 func (*StoreFileResp) Descriptor() ([]byte, []int) {
-	return fileDescriptor_8016d97e57513e7e, []int{12}
+	return fileDescriptor_8016d97e57513e7e, []int{11}
 }
 
 func (m *StoreFileResp) XXX_Unmarshal(b []byte) error {
@@ -1872,7 +1811,7 @@
 func (m *LookupFileReq) String() string { return proto.CompactTextString(m) }
 func (*LookupFileReq) ProtoMessage()    {}
 func (*LookupFileReq) Descriptor() ([]byte, []int) {
-	return fileDescriptor_8016d97e57513e7e, []int{13}
+	return fileDescriptor_8016d97e57513e7e, []int{12}
 }
 
 func (m *LookupFileReq) XXX_Unmarshal(b []byte) error {
@@ -1918,7 +1857,7 @@
 func (m *LookupFileResp) String() string { return proto.CompactTextString(m) }
 func (*LookupFileResp) ProtoMessage()    {}
 func (*LookupFileResp) Descriptor() ([]byte, []int) {
-	return fileDescriptor_8016d97e57513e7e, []int{14}
+	return fileDescriptor_8016d97e57513e7e, []int{13}
 }
 
 func (m *LookupFileResp) XXX_Unmarshal(b []byte) error {
@@ -1956,7 +1895,7 @@
 func (m *EmptyMessage) String() string { return proto.CompactTextString(m) }
 func (*EmptyMessage) ProtoMessage()    {}
 func (*EmptyMessage) Descriptor() ([]byte, []int) {
-	return fileDescriptor_8016d97e57513e7e, []int{15}
+	return fileDescriptor_8016d97e57513e7e, []int{14}
 }
 
 func (m *EmptyMessage) XXX_Unmarshal(b []byte) error {
@@ -1988,7 +1927,7 @@
 func (m *HttpPortResponse) String() string { return proto.CompactTextString(m) }
 func (*HttpPortResponse) ProtoMessage()    {}
 func (*HttpPortResponse) Descriptor() ([]byte, []int) {
-	return fileDescriptor_8016d97e57513e7e, []int{16}
+	return fileDescriptor_8016d97e57513e7e, []int{15}
 }
 
 func (m *HttpPortResponse) XXX_Unmarshal(b []byte) error {
@@ -2034,10 +1973,8 @@
 	proto.RegisterType((*RequesterEnv)(nil), "devtools_goma.RequesterEnv")
 	proto.RegisterType((*ExecReq)(nil), "devtools_goma.ExecReq")
 	proto.RegisterType((*ExecReq_Input)(nil), "devtools_goma.ExecReq.Input")
-	proto.RegisterType((*MultiExecReq)(nil), "devtools_goma.MultiExecReq")
+	proto.RegisterType((*ExecutionStats)(nil), "devtools_goma.ExecutionStats")
 	proto.RegisterType((*ExecResp)(nil), "devtools_goma.ExecResp")
-	proto.RegisterType((*MultiExecResp)(nil), "devtools_goma.MultiExecResp")
-	proto.RegisterType((*MultiExecResp_Response)(nil), "devtools_goma.MultiExecResp.Response")
 	proto.RegisterType((*StoreFileReq)(nil), "devtools_goma.StoreFileReq")
 	proto.RegisterType((*StoreFileResp)(nil), "devtools_goma.StoreFileResp")
 	proto.RegisterType((*LookupFileReq)(nil), "devtools_goma.LookupFileReq")
@@ -2046,164 +1983,168 @@
 	proto.RegisterType((*HttpPortResponse)(nil), "devtools_goma.HttpPortResponse")
 }
 
-func init() { proto.RegisterFile("api/goma_data.proto", fileDescriptor_8016d97e57513e7e) }
+func init() {
+	proto.RegisterFile("api/goma_data.proto", fileDescriptor_8016d97e57513e7e)
+}
 
 var fileDescriptor_8016d97e57513e7e = []byte{
-	// 2482 bytes of a gzipped FileDescriptorProto
-	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x58, 0xfd, 0x6e, 0x1b, 0xc7,
-	0x11, 0x37, 0xbf, 0x24, 0x72, 0xf8, 0x21, 0x6a, 0xad, 0xd8, 0x67, 0xd9, 0x4e, 0x64, 0x2a, 0x4e,
-	0x54, 0xbb, 0x56, 0x6b, 0x25, 0x69, 0x12, 0x25, 0x6a, 0x43, 0x51, 0x94, 0x45, 0x8b, 0x22, 0xd9,
-	0xa5, 0x18, 0x37, 0x40, 0x81, 0xc3, 0xf1, 0x6e, 0x29, 0x1d, 0x74, 0xbc, 0x3b, 0xed, 0x1d, 0x15,
-	0x31, 0x40, 0x80, 0xbe, 0x43, 0xff, 0xea, 0x5b, 0x14, 0x05, 0xfa, 0x1c, 0x7d, 0x8d, 0x02, 0x7d,
-	0x87, 0xb6, 0xd8, 0xd9, 0x3d, 0x7e, 0x89, 0x94, 0x6d, 0x20, 0xfa, 0x43, 0xd8, 0x9d, 0x99, 0xdf,
-	0xec, 0xec, 0x7c, 0xed, 0x1c, 0xe1, 0xae, 0xe1, 0xdb, 0xbf, 0x39, 0xf3, 0xfa, 0x86, 0x6e, 0x19,
-	0xa1, 0xb1, 0xed, 0x73, 0x2f, 0xf4, 0x48, 0xde, 0x62, 0x57, 0xa1, 0xe7, 0x39, 0x81, 0x2e, 0x38,
-	0xa5, 0xff, 0xc6, 0x20, 0x7d, 0x68, 0x3b, 0x6c, 0xdf, 0xf1, 0xba, 0x64, 0x0f, 0x32, 0x5d, 0xc7,
-	0xeb, 0xea, 0xe1, 0xd0, 0x67, 0x5a, 0x6c, 0x23, 0xbe, 0x55, 0xd8, 0xd9, 0xd8, 0x9e, 0x92, 0xdf,
-	0x8e, 0x64, 0xb7, 0xc5, 0xbf, 0xd3, 0xa1, 0xcf, 0x68, 0xba, 0xab, 0x56, 0xe4, 0x1e, 0x2c, 0x79,
-	0xbd, 0x5e, 0xc0, 0x42, 0x0d, 0x36, 0x62, 0x5b, 0x09, 0xaa, 0x76, 0x44, 0x83, 0x65, 0xd3, 0x73,
-	0x43, 0xe6, 0x86, 0x5a, 0x76, 0x23, 0xb6, 0x95, 0xa3, 0xd1, 0x96, 0x3c, 0x84, 0x4c, 0xcf, 0x76,
-	0x98, 0x1e, 0xd8, 0x3f, 0x31, 0x6d, 0x0d, 0x41, 0x69, 0x41, 0x68, 0xdb, 0x3f, 0x31, 0xf2, 0x00,
-	0xd2, 0xe7, 0x46, 0x70, 0xae, 0x5f, 0xb0, 0xa1, 0xf6, 0xc1, 0x46, 0x62, 0x2b, 0x43, 0x97, 0xc5,
-	0xfe, 0x98, 0x0d, 0x4b, 0x35, 0x48, 0x47, 0xe7, 0x93, 0x35, 0x28, 0x1e, 0xd6, 0xea, 0x55, 0xbd,
-	0xd3, 0x68, 0xb7, 0xaa, 0x95, 0xda, 0x61, 0xad, 0x7a, 0x50, 0xbc, 0x43, 0xd2, 0x90, 0x14, 0xd4,
-	0x62, 0x8c, 0xe4, 0x21, 0x83, 0xfc, 0x93, 0xea, 0x69, 0xb9, 0x18, 0x27, 0x05, 0x00, 0xdc, 0x56,
-	0x8e, 0x3a, 0x8d, 0xe3, 0x62, 0xa2, 0xf4, 0x9f, 0x04, 0x64, 0x2b, 0x5e, 0xbf, 0x6f, 0xb8, 0x56,
-	0xdb, 0x67, 0x26, 0x21, 0x90, 0x74, 0x8d, 0xbe, 0xb8, 0x7e, 0x6c, 0x2b, 0x43, 0x71, 0x2d, 0x2e,
-	0x70, 0xc5, 0x78, 0x60, 0x7b, 0xae, 0x16, 0x47, 0x72, 0xb4, 0x15, 0x57, 0x0e, 0x0d, 0x7e, 0xc6,
-	0x42, 0x2d, 0x81, 0x0c, 0xb5, 0x13, 0x5a, 0xf0, 0x4e, 0x79, 0xbc, 0x13, 0xae, 0xc9, 0x47, 0x90,
-	0xed, 0xda, 0xae, 0xc1, 0x87, 0xba, 0xb8, 0x86, 0x96, 0x44, 0x57, 0x80, 0x24, 0x1d, 0x19, 0xc1,
-	0x39, 0xf9, 0x14, 0x56, 0xd8, 0xe5, 0xc0, 0xbe, 0x32, 0x1c, 0xe6, 0x86, 0x52, 0x28, 0xbb, 0x91,
-	0xd8, 0xca, 0xd1, 0xc2, 0x98, 0x8c, 0x82, 0x9b, 0x90, 0xb7, 0x58, 0x68, 0xd8, 0x0e, 0xb3, 0x74,
-	0xdb, 0xed, 0x79, 0x5a, 0x0a, 0x0f, 0xcf, 0x45, 0xc4, 0x9a, 0xdb, 0xf3, 0xc8, 0x36, 0xdc, 0x75,
-	0x3c, 0xd3, 0x70, 0x74, 0xd3, 0xeb, 0xfb, 0xb6, 0xc3, 0xb8, 0xee, 0x1b, 0xe1, 0xb9, 0xb6, 0x84,
-	0xa2, 0xab, 0xc8, 0xaa, 0x28, 0x4e, 0xcb, 0x08, 0xcf, 0x85, 0x7c, 0x30, 0x0c, 0x42, 0xd6, 0xd7,
-	0x6d, 0xd7, 0x74, 0x06, 0x16, 0x93, 0xf2, 0xcb, 0xe8, 0xf9, 0x55, 0xc9, 0xaa, 0x49, 0x0e, 0xca,
-	0x7f, 0x01, 0xf7, 0xcd, 0xeb, 0x6b, 0x7d, 0x1e, 0x26, 0x8d, 0x98, 0x35, 0xf3, 0xfa, 0xba, 0x7d,
-	0x03, 0xb6, 0x03, 0x1f, 0x28, 0x48, 0x8f, 0x1b, 0x7d, 0xf6, 0xa3, 0xc7, 0x2f, 0x24, 0x28, 0x83,
-	0x20, 0x65, 0xc3, 0x61, 0xc4, 0x9b, 0x31, 0xcd, 0xb1, 0xbb, 0x5c, 0x78, 0x10, 0x11, 0xb9, 0x49,
-	0xd3, 0xea, 0x92, 0x83, 0xf2, 0x98, 0x70, 0xfd, 0xbe, 0x48, 0x38, 0x90, 0xf1, 0x52, 0xdb, 0xd2,
-	0x0f, 0x50, 0x68, 0x0f, 0xba, 0x3e, 0xf7, 0xce, 0xb8, 0xd1, 0x8f, 0xe2, 0x8d, 0xca, 0x54, 0xbc,
-	0xc5, 0x7a, 0x36, 0x52, 0x32, 0xe6, 0x93, 0x91, 0x8a, 0xc2, 0x9b, 0x18, 0x87, 0xb7, 0xf4, 0xd7,
-	0x18, 0xe4, 0x4f, 0x3d, 0xcf, 0x31, 0xcf, 0x0d, 0xdb, 0x5d, 0xa8, 0x9a, 0x40, 0x72, 0x42, 0x27,
-	0xae, 0xe7, 0x69, 0x13, 0x21, 0xb6, 0x03, 0x9d, 0x5d, 0x33, 0x73, 0x10, 0x1a, 0x5d, 0x87, 0x61,
-	0xba, 0xa4, 0x69, 0xce, 0x0e, 0xaa, 0x23, 0x1a, 0x79, 0x02, 0xb9, 0x60, 0xd8, 0x77, 0x6c, 0x57,
-	0xb9, 0x50, 0xa6, 0x41, 0x56, 0xd1, 0x84, 0x2b, 0x4a, 0xff, 0x48, 0x00, 0x08, 0x04, 0x65, 0xc1,
-	0xc0, 0x09, 0xc9, 0x26, 0x64, 0xd9, 0xb5, 0x1d, 0xea, 0x41, 0x68, 0x84, 0x83, 0x00, 0x6b, 0x3c,
-	0xb5, 0x1b, 0x7f, 0xf1, 0x92, 0x82, 0x20, 0xb7, 0x91, 0x2a, 0xce, 0x0e, 0x42, 0xcb, 0x1b, 0x84,
-	0x7a, 0x77, 0xd0, 0xeb, 0x31, 0x8e, 0xc6, 0xe6, 0x68, 0x4e, 0x12, 0xf7, 0x91, 0xa6, 0x84, 0x18,
-	0xe7, 0x91, 0x50, 0x62, 0x24, 0xc4, 0x38, 0x57, 0x42, 0x7b, 0x90, 0x33, 0x65, 0x6d, 0xe9, 0x81,
-	0xcf, 0x4c, 0xbc, 0x44, 0x76, 0x67, 0x7d, 0xa6, 0xa7, 0x4c, 0x94, 0x1f, 0xcd, 0x9a, 0x13, 0xb5,
-	0xb8, 0x07, 0x10, 0x8c, 0xa2, 0xa5, 0xa5, 0x36, 0x12, 0x5b, 0xd9, 0x9d, 0xc7, 0x33, 0xe0, 0xe9,
-	0x70, 0xd2, 0x09, 0x00, 0xf9, 0x0a, 0x96, 0xbc, 0x41, 0xe8, 0x0f, 0x44, 0x16, 0x24, 0xb6, 0xe0,
-	0x46, 0x2f, 0x1b, 0xfb, 0x65, 0xbb, 0x89, 0x72, 0x54, 0xc9, 0xaf, 0xff, 0x0c, 0x4b, 0x92, 0x42,
-	0xd6, 0x01, 0x1b, 0x12, 0xb6, 0x84, 0x2c, 0xba, 0x77, 0xb4, 0x27, 0xcf, 0x21, 0x29, 0x7a, 0x9f,
-	0x96, 0xc3, 0x5b, 0xdd, 0x5f, 0xd0, 0x29, 0x29, 0x0a, 0x91, 0x67, 0xb3, 0x01, 0x15, 0xad, 0x21,
-	0xbd, 0x9b, 0xea, 0x19, 0x4e, 0xc0, 0xa6, 0xe3, 0x5a, 0xfa, 0x57, 0x02, 0xf2, 0x94, 0x5d, 0x0e,
-	0x58, 0x10, 0x32, 0x8e, 0xc5, 0x4c, 0x20, 0x69, 0x58, 0x16, 0x8f, 0x52, 0x49, 0xac, 0x85, 0x69,
-	0x83, 0x80, 0x71, 0x34, 0x4d, 0xa6, 0xd3, 0x68, 0x4f, 0x9e, 0xc1, 0xea, 0xb8, 0xec, 0xb9, 0x77,
-	0x3d, 0xd4, 0x6d, 0x4b, 0xb5, 0xa8, 0x95, 0x88, 0xd1, 0x12, 0xf4, 0x9a, 0x45, 0x4a, 0x90, 0x35,
-	0x7c, 0x5b, 0x8f, 0x3a, 0x9c, 0x88, 0x51, 0x6a, 0x37, 0xb6, 0x43, 0xc1, 0xf0, 0xed, 0xef, 0x55,
-	0x9f, 0x2b, 0x42, 0xc2, 0xb7, 0x2d, 0x4c, 0xb0, 0x14, 0x15, 0x4b, 0xb2, 0x06, 0x29, 0xce, 0x42,
-	0x3e, 0xd4, 0x96, 0x91, 0x26, 0x37, 0x22, 0x2b, 0xf0, 0xc1, 0xe1, 0xec, 0xca, 0x46, 0x6d, 0x69,
-	0xd9, 0x99, 0x04, 0x91, 0x2a, 0x9a, 0x68, 0xec, 0xdd, 0x81, 0xed, 0x58, 0xc2, 0xa6, 0x8c, 0xac,
-	0x4f, 0xdc, 0xd7, 0x2c, 0xf2, 0x21, 0x80, 0x65, 0xf7, 0x99, 0x2b, 0xe4, 0x02, 0x0c, 0x5b, 0x86,
-	0x4e, 0x50, 0x48, 0x15, 0x40, 0x64, 0xba, 0x1e, 0x84, 0x43, 0x47, 0x06, 0xa4, 0xb0, 0xf3, 0xc9,
-	0x8c, 0xe3, 0xa7, 0x3c, 0xb7, 0x2d, 0xaa, 0xa0, 0x2d, 0xa4, 0x69, 0xc6, 0x8f, 0x96, 0xa5, 0xa7,
-	0x50, 0x78, 0xe5, 0xf5, 0x8d, 0xf2, 0xf8, 0x82, 0x77, 0x61, 0xa5, 0xd2, 0xa1, 0xb4, 0xda, 0x38,
-	0xd5, 0xbf, 0xaf, 0xd2, 0x76, 0xad, 0xd9, 0x28, 0xc6, 0x4b, 0xfb, 0x90, 0x19, 0xc1, 0xc9, 0x2a,
-	0xe4, 0x3b, 0x8d, 0xe3, 0x46, 0xf3, 0x4d, 0x43, 0x6f, 0x9f, 0xfe, 0x50, 0xaf, 0x16, 0xef, 0x90,
-	0x15, 0xc8, 0xb6, 0x9a, 0xed, 0xda, 0x9f, 0x14, 0x21, 0x26, 0x64, 0xde, 0xd4, 0x1a, 0x07, 0xcd,
-	0x37, 0x6d, 0x45, 0x8a, 0xbf, 0x4e, 0xa6, 0x97, 0x8a, 0xcb, 0xa5, 0xbf, 0xc5, 0x21, 0x37, 0xb2,
-	0xab, 0xea, 0x5e, 0x89, 0x16, 0x23, 0x8c, 0x35, 0x4d, 0x59, 0xb9, 0xbf, 0x92, 0x2d, 0x46, 0x92,
-	0xb0, 0x87, 0x3d, 0x06, 0x90, 0xed, 0x1b, 0xf9, 0xcf, 0x90, 0x9f, 0x41, 0x0a, 0xb2, 0xd7, 0x20,
-	0x35, 0xe8, 0x1b, 0xc1, 0x85, 0xf6, 0x5c, 0xba, 0x1f, 0x37, 0xc2, 0xfd, 0x57, 0x8c, 0xdb, 0xbd,
-	0xa1, 0xae, 0x12, 0x7f, 0x47, 0x76, 0x0d, 0x49, 0x54, 0x29, 0xfd, 0x10, 0x32, 0x83, 0x80, 0xe9,
-	0xa8, 0x4b, 0xfb, 0x0c, 0x05, 0x44, 0xe2, 0xd4, 0xc5, 0x1e, 0xf3, 0xdd, 0x70, 0x9c, 0xae, 0x61,
-	0x5e, 0x68, 0x9f, 0x4b, 0x5e, 0xb4, 0x27, 0x4f, 0xa1, 0xa0, 0xb4, 0xab, 0x22, 0xd5, 0xbe, 0x40,
-	0xb3, 0xd4, 0x99, 0xaa, 0x8c, 0x45, 0xb7, 0x8e, 0x20, 0xba, 0xed, 0xfa, 0x83, 0x50, 0x17, 0x15,
-	0xa3, 0x7d, 0x2b, 0xbb, 0x75, 0xc4, 0xaa, 0x09, 0x8e, 0xa8, 0x91, 0xd2, 0xbf, 0x97, 0x61, 0x59,
-	0x96, 0xe2, 0xe5, 0x8d, 0x86, 0x21, 0x1a, 0xd4, 0x7b, 0x34, 0x8c, 0x22, 0x24, 0x0c, 0x7e, 0xa6,
-	0xc5, 0xf1, 0x28, 0xb1, 0x14, 0x14, 0xe6, 0x5e, 0x69, 0x09, 0x49, 0x61, 0xee, 0x95, 0xa0, 0x98,
-	0x3f, 0x5a, 0x98, 0xe6, 0x19, 0x2a, 0x96, 0x64, 0x07, 0x52, 0x68, 0xa7, 0x6a, 0x13, 0x8f, 0xe6,
-	0xb6, 0x89, 0xcb, 0x6d, 0xb4, 0x98, 0x4a, 0x51, 0xd1, 0x7a, 0xd9, 0xb5, 0x6f, 0xb8, 0x16, 0xb3,
-	0x74, 0x71, 0x64, 0x01, 0x0f, 0xc8, 0x46, 0xb4, 0x32, 0x3f, 0x9b, 0xe9, 0x5e, 0x2b, 0xef, 0xdb,
-	0xbd, 0x2a, 0x50, 0xe0, 0x51, 0xc6, 0xc8, 0x57, 0xfe, 0x43, 0xec, 0x33, 0x8f, 0x6e, 0x4b, 0x77,
-	0x9a, 0xe7, 0x53, 0x7d, 0xe3, 0x0d, 0xe4, 0x4c, 0xc3, 0x3c, 0x67, 0xba, 0xef, 0x39, 0xb6, 0x39,
-	0xd4, 0x3e, 0xc2, 0x8a, 0x29, 0x2d, 0xb8, 0x61, 0x45, 0x88, 0xb6, 0x50, 0x72, 0xb7, 0x58, 0x6f,
-	0x36, 0x8f, 0x3b, 0x2d, 0xbd, 0xdc, 0x38, 0xd0, 0xdb, 0xa7, 0x4d, 0x5a, 0xa5, 0x59, 0x73, 0xcc,
-	0x26, 0xdf, 0xc1, 0xf8, 0x24, 0x5d, 0x78, 0x78, 0x03, 0x8d, 0x7b, 0xb8, 0xc8, 0xb8, 0xaa, 0x7b,
-	0x45, 0x73, 0x7c, 0xb2, 0x02, 0x36, 0x21, 0x7f, 0xce, 0x78, 0x9f, 0x85, 0xb6, 0xa9, 0xf7, 0x3d,
-	0x8b, 0x69, 0x4f, 0x64, 0xae, 0x46, 0xc4, 0x13, 0xcf, 0x12, 0xc3, 0x5d, 0x2a, 0xe4, 0x86, 0xc9,
-	0xb4, 0x12, 0x32, 0xe5, 0x46, 0xcc, 0x10, 0xec, 0xda, 0x67, 0x66, 0xc8, 0x2c, 0x95, 0xe8, 0x98,
-	0x62, 0x81, 0xb6, 0x29, 0x67, 0x88, 0x88, 0x29, 0x13, 0x5e, 0x24, 0x59, 0x40, 0x7e, 0x0b, 0x6b,
-	0xb3, 0x18, 0xcb, 0xe6, 0x81, 0xf6, 0x31, 0x42, 0xc8, 0x34, 0xe4, 0xc0, 0xe6, 0x01, 0x79, 0x01,
-	0x24, 0x8c, 0xde, 0xf3, 0x68, 0xbe, 0xb1, 0xb4, 0xa7, 0x68, 0xc8, 0xea, 0x88, 0xa3, 0x66, 0x1b,
-	0x8b, 0x54, 0x61, 0x65, 0x2c, 0x2e, 0x92, 0x37, 0xd0, 0x3e, 0xc1, 0x98, 0xcf, 0x06, 0x6c, 0x6a,
-	0x48, 0xa0, 0x85, 0x70, 0x72, 0x1b, 0xac, 0x5f, 0x42, 0x0a, 0x13, 0xed, 0xd6, 0x97, 0x67, 0x72,
-	0x34, 0xce, 0x6d, 0xc4, 0x27, 0x46, 0x63, 0xf2, 0x72, 0x3c, 0x6c, 0xe7, 0x6f, 0x7f, 0x97, 0x22,
-	0xb9, 0x52, 0x17, 0xb2, 0x13, 0x91, 0x17, 0x03, 0xf5, 0x6c, 0xec, 0x8b, 0x31, 0xd1, 0xeb, 0x14,
-	0xb5, 0xd9, 0xa8, 0xff, 0x20, 0x07, 0x69, 0xe4, 0xc9, 0x7d, 0x82, 0x3c, 0x02, 0x6d, 0x16, 0xa6,
-	0xb7, 0x3b, 0x95, 0x4a, 0xb5, 0xdd, 0x2e, 0x26, 0x5f, 0x27, 0xd3, 0x66, 0xd1, 0x2a, 0xfd, 0x0c,
-	0xb9, 0x93, 0x81, 0x13, 0xda, 0x51, 0xb9, 0x6f, 0x41, 0x82, 0xb3, 0x4b, 0x2d, 0x86, 0x7e, 0xba,
-	0x37, 0x3f, 0x2b, 0xa9, 0x10, 0x99, 0x53, 0x0d, 0xf0, 0xde, 0xd5, 0x50, 0xfa, 0x27, 0x81, 0xb4,
-	0x7a, 0xf4, 0x7d, 0xf2, 0x12, 0x96, 0x38, 0x3e, 0xfe, 0xf8, 0xa8, 0x66, 0x77, 0x1e, 0x2c, 0x9c,
-	0x0e, 0xa8, 0x12, 0x24, 0xdf, 0x40, 0x8a, 0x71, 0xee, 0xc9, 0x81, 0xa8, 0xb0, 0xf3, 0x64, 0x3e,
-	0xc2, 0xc7, 0x45, 0x55, 0x08, 0xee, 0xc6, 0x9b, 0xc7, 0x54, 0x62, 0x88, 0x03, 0xf7, 0xbb, 0x86,
-	0xa5, 0x2b, 0x8b, 0x74, 0xce, 0x8c, 0xc0, 0x73, 0x75, 0x53, 0x64, 0x7e, 0x02, 0xd5, 0xfd, 0x7a,
-	0x91, 0xba, 0x7d, 0xc3, 0x52, 0xd7, 0xa2, 0x08, 0xaa, 0x78, 0x16, 0xdb, 0x5d, 0x56, 0x8f, 0x11,
-	0x5d, 0xeb, 0xce, 0x61, 0x8b, 0xea, 0xea, 0xdb, 0x41, 0x60, 0xbb, 0x67, 0xb2, 0x07, 0xe3, 0x97,
-	0x44, 0x86, 0xe6, 0x14, 0x51, 0xa6, 0xd8, 0x53, 0x28, 0x44, 0x42, 0xd2, 0x1c, 0xec, 0x52, 0x19,
-	0x1a, 0x41, 0xa5, 0x3e, 0xa1, 0x0b, 0xaf, 0xa0, 0xf7, 0x59, 0x10, 0x18, 0x67, 0x4c, 0x0d, 0xde,
-	0x39, 0x24, 0x9e, 0x48, 0x1a, 0xd1, 0xe1, 0x59, 0xcf, 0xe3, 0x26, 0xd3, 0x83, 0xd0, 0xe3, 0x6c,
-	0xb2, 0x2c, 0xf5, 0x9e, 0xc7, 0xf5, 0x81, 0xdb, 0x37, 0x42, 0xf3, 0x9c, 0x59, 0x72, 0xa4, 0x9e,
-	0x1a, 0x7e, 0x3e, 0x46, 0x60, 0x5b, 0xe0, 0xc6, 0x15, 0x7b, 0xe8, 0xf1, 0x4e, 0x84, 0xc1, 0x99,
-	0x9b, 0xc1, 0x8b, 0x77, 0x3b, 0x20, 0x1a, 0x64, 0x0a, 0x93, 0x67, 0x7c, 0xfa, 0xb6, 0x33, 0xa2,
-	0x41, 0xa0, 0x0f, 0x2f, 0xdf, 0xed, 0x98, 0x71, 0xab, 0x0e, 0xb4, 0xe2, 0xe4, 0x51, 0xcf, 0xdf,
-	0x76, 0xd4, 0xb8, 0xe9, 0x07, 0xe2, 0x31, 0x96, 0x0d, 0x5a, 0x7e, 0xe5, 0x62, 0x99, 0x23, 0x41,
-	0xd4, 0xf2, 0x77, 0x11, 0xf3, 0xdc, 0x0e, 0xb5, 0x87, 0x98, 0x24, 0x9b, 0x8b, 0x92, 0x04, 0x2b,
-	0xb8, 0xed, 0x0d, 0xb8, 0xc9, 0x94, 0x86, 0x23, 0x3b, 0x24, 0x7b, 0xf0, 0x70, 0x5c, 0x36, 0x37,
-	0x27, 0xc2, 0x75, 0x3c, 0x50, 0x1b, 0x89, 0x54, 0x66, 0x46, 0xc3, 0x6d, 0xb8, 0x3b, 0x03, 0x0a,
-	0xed, 0x3e, 0xc3, 0xa9, 0x22, 0x46, 0x57, 0xa7, 0x06, 0xc9, 0x53, 0xbb, 0xcf, 0x48, 0x1d, 0x36,
-	0x67, 0x0f, 0x89, 0xbe, 0x0b, 0x39, 0xf3, 0xb9, 0x67, 0x4a, 0xfc, 0x67, 0x88, 0xff, 0x68, 0x7a,
-	0x10, 0x55, 0xdf, 0x88, 0x52, 0x0e, 0xb5, 0x35, 0xe0, 0xe3, 0x05, 0xda, 0x44, 0x38, 0x1c, 0xcf,
-	0xb0, 0xa4, 0xba, 0xcf, 0x51, 0xdd, 0xc6, 0x3c, 0x75, 0x87, 0x4a, 0x10, 0xf5, 0xfd, 0x1e, 0x1e,
-	0xcd, 0xe8, 0xe3, 0xbe, 0xa9, 0x9b, 0x86, 0xe3, 0x48, 0x3d, 0x5f, 0xa0, 0x1e, 0x6d, 0x4a, 0x0f,
-	0xf5, 0xcd, 0x8a, 0xe1, 0x38, 0x88, 0x3f, 0x82, 0x27, 0x33, 0x78, 0x4c, 0x0b, 0xce, 0x02, 0xdf,
-	0x73, 0x03, 0x26, 0x95, 0xfc, 0x0e, 0x95, 0x3c, 0x9e, 0x52, 0x22, 0xac, 0xa0, 0x4a, 0x0a, 0x35,
-	0x7d, 0x07, 0x8f, 0xe7, 0x58, 0x22, 0x87, 0x62, 0xd4, 0xf2, 0x25, 0x6a, 0x79, 0x30, 0x6b, 0xca,
-	0xbe, 0x90, 0xb8, 0xe5, 0x2e, 0x01, 0x73, 0x95, 0x82, 0xaf, 0xe6, 0xdf, 0xa5, 0xcd, 0xdc, 0xdb,
-	0xf0, 0x3f, 0x1a, 0x76, 0x28, 0xf1, 0x5f, 0xcf, 0xc7, 0xbf, 0x31, 0xec, 0xf0, 0x16, 0x3c, 0x67,
-	0xe6, 0x95, 0xc4, 0xef, 0xce, 0xc7, 0x53, 0x66, 0x5e, 0xdd, 0xe2, 0x01, 0xdf, 0xe0, 0x91, 0x1f,
-	0xbf, 0x99, 0xef, 0x81, 0x96, 0x90, 0x58, 0x10, 0x0d, 0x35, 0x2f, 0x33, 0xd7, 0x12, 0x1d, 0x0d,
-	0xb5, 0x7c, 0x3b, 0x27, 0x1a, 0x38, 0xe8, 0xb6, 0xa4, 0xd4, 0x02, 0x5b, 0xa4, 0x26, 0x3e, 0x70,
-	0xa5, 0x96, 0xbd, 0x39, 0xb6, 0xa0, 0x16, 0x3a, 0x70, 0x51, 0xc3, 0x1f, 0x6e, 0x78, 0x03, 0xbf,
-	0x82, 0x7a, 0xb6, 0x6b, 0x07, 0xe7, 0xcc, 0xd2, 0x0e, 0x71, 0x68, 0x98, 0x56, 0x20, 0x3e, 0x40,
-	0x0e, 0x95, 0xc0, 0x1c, 0x13, 0x50, 0xc1, 0xb8, 0xfa, 0x5f, 0x2d, 0xd0, 0x50, 0x99, 0xa8, 0xf4,
-	0x79, 0x1a, 0x8c, 0xae, 0xc7, 0x43, 0x66, 0x69, 0x47, 0x88, 0xd7, 0x6e, 0xe0, 0xcb, 0x92, 0x4f,
-	0xbe, 0x86, 0x07, 0xf3, 0xe0, 0xf2, 0xb9, 0xab, 0x21, 0xf8, 0xde, 0x0d, 0x30, 0xbe, 0x71, 0xa4,
-	0xbc, 0xc0, 0x7d, 0xa3, 0xdb, 0xbf, 0x46, 0xf8, 0xfa, 0x4d, 0xf7, 0x8d, 0xae, 0xff, 0x25, 0x68,
-	0x8b, 0x22, 0xa0, 0x1d, 0x23, 0xfa, 0x83, 0xb9, 0xce, 0x9f, 0x73, 0x6b, 0x09, 0xbc, 0xb0, 0x1d,
-	0x87, 0x59, 0x5a, 0x7d, 0xce, 0xad, 0x11, 0x7b, 0x8c, 0xfc, 0x39, 0x39, 0x24, 0x3e, 0xd0, 0x27,
-	0xde, 0x68, 0xf1, 0x81, 0xdb, 0xc2, 0x2f, 0xac, 0xe9, 0x1c, 0x52, 0xb3, 0x89, 0x7c, 0x74, 0x43,
-	0x3e, 0x2c, 0xbd, 0x80, 0xcc, 0xe8, 0xd5, 0x27, 0x4b, 0x10, 0x6f, 0x1e, 0x17, 0xef, 0x10, 0x0d,
-	0xb2, 0xfb, 0xe5, 0x03, 0x9d, 0x56, 0xff, 0xd8, 0xa9, 0xb6, 0x4f, 0x8b, 0xff, 0x8b, 0xfe, 0x62,
-	0xa5, 0x0a, 0xac, 0xcd, 0x7b, 0xd5, 0x49, 0x16, 0xa2, 0x77, 0xbd, 0x78, 0x87, 0x7c, 0x08, 0xeb,
-	0x9d, 0x46, 0xbb, 0xd3, 0x6a, 0x35, 0xe9, 0x69, 0xf5, 0x40, 0xaf, 0x34, 0x4f, 0x5a, 0xb5, 0x7a,
-	0x95, 0xea, 0x87, 0xf5, 0xf2, 0xab, 0x76, 0x31, 0x56, 0xea, 0xa8, 0xb9, 0x4d, 0x76, 0x7d, 0x92,
-	0x83, 0x74, 0xa3, 0xa9, 0x57, 0xca, 0x95, 0x23, 0xf1, 0x6d, 0x9a, 0x87, 0xcc, 0x49, 0xf5, 0x44,
-	0x6d, 0xf1, 0xcb, 0x54, 0x8c, 0x64, 0xe5, 0x57, 0x55, 0x45, 0x8a, 0x93, 0x7b, 0x40, 0xea, 0xcd,
-	0x4a, 0xb9, 0xae, 0x37, 0x3b, 0xa7, 0xad, 0xce, 0xa9, 0xa2, 0x27, 0x5e, 0x27, 0xd3, 0xf7, 0x8a,
-	0xf7, 0x5f, 0x27, 0xd3, 0xf7, 0x8b, 0x9a, 0x1a, 0xdb, 0xfe, 0x1e, 0x83, 0xfc, 0xc4, 0xdc, 0x16,
-	0xf8, 0xa4, 0x0c, 0xe9, 0xa8, 0xed, 0xe1, 0xf4, 0x06, 0x3b, 0x4f, 0x67, 0x1e, 0xa6, 0x29, 0xf9,
-	0xed, 0xa8, 0xfb, 0xd1, 0x11, 0x6c, 0xfd, 0xcf, 0x90, 0x8e, 0xa8, 0x62, 0xc2, 0x18, 0x75, 0x51,
-	0x9c, 0x88, 0xe2, 0xe8, 0xf3, 0x5c, 0x44, 0x44, 0xdf, 0x3c, 0x87, 0xa4, 0xd8, 0xe3, 0xb4, 0x74,
-	0x73, 0xac, 0x8d, 0x8e, 0xa2, 0x28, 0x54, 0xfa, 0x4b, 0x0c, 0x72, 0xf8, 0x04, 0xcb, 0xde, 0x7b,
-	0x39, 0xfa, 0xb1, 0x46, 0xce, 0x9a, 0x6f, 0xf9, 0xb1, 0xe6, 0x17, 0x99, 0x36, 0x9f, 0x41, 0x7e,
-	0xc2, 0x82, 0xc0, 0x9f, 0x9a, 0xda, 0x63, 0xd3, 0x3f, 0x68, 0x7b, 0x90, 0xaf, 0x7b, 0xde, 0xc5,
-	0xc0, 0x8f, 0xcc, 0x5d, 0x2c, 0xfb, 0xcb, 0x18, 0xb7, 0x07, 0x85, 0xc9, 0x03, 0x03, 0x7f, 0xe4,
-	0xa0, 0xf8, 0x3b, 0x38, 0xa8, 0x54, 0x80, 0x5c, 0xb5, 0xef, 0x87, 0x43, 0x35, 0xfd, 0x95, 0x3e,
-	0x81, 0xe2, 0x51, 0x18, 0xfa, 0x2d, 0x8f, 0x87, 0xa3, 0xa0, 0x12, 0x48, 0xfa, 0x1e, 0x0f, 0xe5,
-	0x8f, 0x8c, 0x14, 0xd7, 0xff, 0x0f, 0x00, 0x00, 0xff, 0xff, 0x2d, 0xeb, 0x43, 0x58, 0x93, 0x18,
-	0x00, 0x00,
+	// 2516 bytes of a gzipped FileDescriptorProto
+	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x58, 0xdd, 0x52, 0x23, 0xc7,
+	0xf5, 0x5f, 0x49, 0x08, 0xa4, 0xa3, 0x0f, 0x44, 0x2f, 0x5e, 0x66, 0x61, 0xf1, 0xb2, 0xc2, 0x6b,
+	0xf3, 0x5f, 0xff, 0x17, 0x67, 0x71, 0x1c, 0xdb, 0xd8, 0x24, 0x06, 0x21, 0x16, 0x2d, 0x42, 0x92,
+	0x5b, 0xc8, 0xc4, 0x37, 0x99, 0x1a, 0xcd, 0xb4, 0x60, 0x8a, 0xd1, 0xcc, 0xd0, 0x33, 0x62, 0x91,
+	0xab, 0x52, 0x95, 0x07, 0xc8, 0x5d, 0xae, 0xf2, 0x1a, 0x79, 0x8a, 0xdc, 0xe5, 0x35, 0x52, 0x95,
+	0x67, 0x48, 0x25, 0xd5, 0xa7, 0x7b, 0xf4, 0x85, 0xc4, 0xee, 0x56, 0x85, 0x0b, 0x6a, 0xfa, 0x7c,
+	0xf5, 0xe9, 0xf3, 0xd5, 0xbf, 0x16, 0x3c, 0x34, 0x7c, 0xfb, 0x8b, 0x0b, 0xaf, 0x6b, 0xe8, 0x96,
+	0x11, 0x1a, 0xdb, 0x3e, 0xf7, 0x42, 0x8f, 0xe4, 0x2c, 0x76, 0x13, 0x7a, 0x9e, 0x13, 0xe8, 0x82,
+	0xb3, 0xfa, 0xf4, 0xc2, 0xf3, 0x2e, 0x1c, 0xf6, 0x05, 0x32, 0xdb, 0xbd, 0xce, 0x17, 0xa1, 0xdd,
+	0x65, 0x41, 0x68, 0x74, 0x7d, 0x29, 0x5f, 0xfc, 0x73, 0x1c, 0x52, 0x47, 0xb6, 0xc3, 0x0e, 0x1c,
+	0xaf, 0x4d, 0xf6, 0x20, 0xdd, 0x76, 0xbc, 0xb6, 0x1e, 0xf6, 0x7d, 0xa6, 0xc5, 0x36, 0xe2, 0x5b,
+	0xf9, 0x9d, 0x8d, 0xed, 0x31, 0x83, 0xdb, 0x91, 0xec, 0xb6, 0xf8, 0x77, 0xd6, 0xf7, 0x19, 0x4d,
+	0xb5, 0xd5, 0x17, 0x79, 0x04, 0xf3, 0x5e, 0xa7, 0x13, 0xb0, 0x50, 0x83, 0x8d, 0xd8, 0x56, 0x82,
+	0xaa, 0x15, 0xd1, 0x60, 0xc1, 0xf4, 0xdc, 0x90, 0xb9, 0xa1, 0x96, 0xd9, 0x88, 0x6d, 0x65, 0x69,
+	0xb4, 0x24, 0x6b, 0x90, 0xee, 0xd8, 0x0e, 0xd3, 0x03, 0xfb, 0x17, 0xa6, 0x2d, 0xa3, 0x52, 0x4a,
+	0x10, 0x9a, 0xf6, 0x2f, 0x8c, 0x3c, 0x86, 0xd4, 0xa5, 0x11, 0x5c, 0xea, 0x57, 0xac, 0xaf, 0x7d,
+	0xb4, 0x91, 0xd8, 0x4a, 0xd3, 0x05, 0xb1, 0x3e, 0x61, 0xfd, 0xe2, 0x39, 0xa4, 0xa2, 0xfd, 0xc9,
+	0x32, 0x14, 0x8e, 0x2a, 0xd5, 0xb2, 0xde, 0xaa, 0x35, 0x1b, 0xe5, 0x52, 0xe5, 0xa8, 0x52, 0x3e,
+	0x2c, 0x3c, 0x20, 0x29, 0x98, 0x13, 0xd4, 0x42, 0x8c, 0xe4, 0x20, 0x8d, 0xfc, 0xd3, 0xf2, 0xd9,
+	0x7e, 0x21, 0x4e, 0xf2, 0x00, 0xb8, 0x2c, 0x1d, 0xb7, 0x6a, 0x27, 0x85, 0x04, 0xc9, 0x42, 0x0a,
+	0xd7, 0xb4, 0x7c, 0x54, 0x48, 0x16, 0xff, 0x95, 0x80, 0x4c, 0xc9, 0xeb, 0x76, 0x0d, 0xd7, 0x6a,
+	0xfa, 0xcc, 0x24, 0x04, 0xe6, 0x5c, 0xa3, 0x2b, 0x82, 0x11, 0xdb, 0x4a, 0x53, 0xfc, 0x16, 0xc7,
+	0xb9, 0x61, 0x3c, 0xb0, 0x3d, 0x57, 0x8b, 0x23, 0x39, 0x5a, 0x8a, 0x00, 0x84, 0x06, 0xbf, 0x60,
+	0xa1, 0x96, 0x40, 0x86, 0x5a, 0x09, 0x2b, 0x78, 0xc2, 0x1c, 0x9e, 0x10, 0xbf, 0xc9, 0x53, 0xc8,
+	0xb4, 0x6d, 0xd7, 0xe0, 0x7d, 0x5d, 0x1c, 0x4a, 0x9b, 0xc3, 0xc0, 0x80, 0x24, 0x1d, 0x1b, 0xc1,
+	0x25, 0xf9, 0x0c, 0x16, 0xd9, 0x75, 0xcf, 0xbe, 0x31, 0x1c, 0xe6, 0x86, 0x52, 0x28, 0xb3, 0x91,
+	0xd8, 0xca, 0xd2, 0xfc, 0x90, 0x8c, 0x82, 0x9b, 0x90, 0xb3, 0x58, 0x68, 0xd8, 0x0e, 0xb3, 0x74,
+	0xdb, 0xed, 0x78, 0x5a, 0x12, 0x37, 0xcf, 0x46, 0xc4, 0x8a, 0xdb, 0xf1, 0xc8, 0x36, 0x3c, 0x74,
+	0x3c, 0xd3, 0x70, 0x74, 0xd3, 0xeb, 0xfa, 0xb6, 0xc3, 0xb8, 0xee, 0x1b, 0xe1, 0xa5, 0x36, 0x8f,
+	0xa2, 0x4b, 0xc8, 0x2a, 0x29, 0x4e, 0xc3, 0x08, 0x2f, 0x85, 0x7c, 0xd0, 0x0f, 0x42, 0xd6, 0xd5,
+	0x6d, 0xd7, 0x74, 0x7a, 0x16, 0x93, 0xf2, 0x0b, 0x98, 0x87, 0x25, 0xc9, 0xaa, 0x48, 0x0e, 0xca,
+	0x7f, 0x05, 0x2b, 0xe6, 0xed, 0xad, 0x3e, 0x4d, 0x27, 0x85, 0x3a, 0xcb, 0xe6, 0xed, 0x6d, 0xf3,
+	0x8e, 0xda, 0x0e, 0x7c, 0xa4, 0x54, 0x3a, 0xdc, 0xe8, 0xb2, 0xb7, 0x1e, 0xbf, 0x92, 0x4a, 0x69,
+	0x54, 0x52, 0x3e, 0x1c, 0x45, 0xbc, 0x09, 0xd7, 0x1c, 0xbb, 0xcd, 0x45, 0x04, 0x51, 0x23, 0x3b,
+	0xea, 0x5a, 0x55, 0x72, 0x50, 0x1e, 0xcb, 0xaf, 0xdb, 0x15, 0xe5, 0x07, 0x32, 0x5f, 0x6a, 0x59,
+	0xfc, 0x19, 0xf2, 0xcd, 0x5e, 0xdb, 0xe7, 0xde, 0x05, 0x37, 0xba, 0x51, 0xbe, 0xd1, 0x98, 0xca,
+	0xb7, 0xf8, 0x9e, 0xcc, 0x94, 0xcc, 0xf9, 0x68, 0xa6, 0xa2, 0xf4, 0x26, 0x86, 0xe9, 0x2d, 0xfe,
+	0x25, 0x06, 0xb9, 0x33, 0xcf, 0x73, 0xcc, 0x4b, 0xc3, 0x76, 0x67, 0x9a, 0x26, 0x30, 0x37, 0x62,
+	0x13, 0xbf, 0xa7, 0x59, 0x13, 0x29, 0xb6, 0x03, 0x9d, 0xdd, 0x32, 0xb3, 0x17, 0x1a, 0x6d, 0x87,
+	0x61, 0xb9, 0xa4, 0x68, 0xd6, 0x0e, 0xca, 0x03, 0x1a, 0x79, 0x06, 0xd9, 0xa0, 0xdf, 0x75, 0x6c,
+	0x57, 0x85, 0x50, 0x96, 0x41, 0x46, 0xd1, 0x44, 0x28, 0x8a, 0x7f, 0x4b, 0x00, 0x08, 0x0d, 0xca,
+	0x82, 0x9e, 0x13, 0x92, 0x4d, 0xc8, 0xb0, 0x5b, 0x3b, 0xd4, 0x83, 0xd0, 0x08, 0x7b, 0x01, 0x76,
+	0x7c, 0x72, 0x37, 0xfe, 0xf2, 0x15, 0x05, 0x41, 0x6e, 0x22, 0x55, 0xec, 0x1d, 0x84, 0x96, 0xd7,
+	0x0b, 0xf5, 0x76, 0xaf, 0xd3, 0x61, 0x1c, 0x9d, 0xcd, 0xd2, 0xac, 0x24, 0x1e, 0x20, 0x4d, 0x09,
+	0x31, 0xce, 0x23, 0xa1, 0xc4, 0x40, 0x88, 0x71, 0xae, 0x84, 0xf6, 0x20, 0x6b, 0xca, 0xde, 0xd2,
+	0x03, 0x9f, 0x99, 0x78, 0x88, 0xcc, 0xce, 0xea, 0xc4, 0x84, 0x19, 0x69, 0x3f, 0x9a, 0x31, 0x47,
+	0x7a, 0x71, 0x0f, 0x20, 0x18, 0x64, 0x4b, 0x4b, 0x6e, 0x24, 0xb6, 0x32, 0x3b, 0xeb, 0x13, 0xca,
+	0xe3, 0xe9, 0xa4, 0x23, 0x0a, 0xe4, 0x1b, 0x98, 0xf7, 0x7a, 0xa1, 0xdf, 0x13, 0x55, 0x90, 0xd8,
+	0x82, 0x3b, 0x93, 0x6d, 0x18, 0x97, 0xed, 0x3a, 0xca, 0x51, 0x25, 0xbf, 0xfa, 0x47, 0x98, 0x97,
+	0x14, 0xb2, 0x0a, 0x38, 0x9e, 0x70, 0x24, 0x64, 0x30, 0xbc, 0x83, 0x35, 0xf9, 0x1c, 0xe6, 0xc4,
+	0x24, 0xd4, 0xb2, 0x78, 0xaa, 0x95, 0x19, 0x73, 0x93, 0xa2, 0x10, 0x79, 0x31, 0x99, 0x50, 0x31,
+	0x1a, 0x52, 0xbb, 0xc9, 0x8e, 0xe1, 0x04, 0x6c, 0x3c, 0xaf, 0xc5, 0x7f, 0x24, 0x20, 0x47, 0xd9,
+	0x75, 0x8f, 0x05, 0x21, 0xe3, 0xd8, 0xcc, 0x04, 0xe6, 0x0c, 0xcb, 0xe2, 0x51, 0x29, 0x89, 0x6f,
+	0xe1, 0x5a, 0x2f, 0x60, 0x1c, 0x5d, 0x93, 0xe5, 0x34, 0x58, 0x93, 0x17, 0xb0, 0x34, 0x6c, 0x7b,
+	0xee, 0xdd, 0xf6, 0x75, 0xdb, 0x52, 0x23, 0x6a, 0x31, 0x62, 0x34, 0x04, 0xbd, 0x62, 0x91, 0x22,
+	0x64, 0x0c, 0xdf, 0xd6, 0xa3, 0x09, 0x27, 0x72, 0x94, 0xdc, 0x8d, 0xed, 0x50, 0x30, 0x7c, 0xfb,
+	0x27, 0x35, 0xe7, 0x0a, 0x90, 0xf0, 0x6d, 0x0b, 0x0b, 0x2c, 0x49, 0xc5, 0x27, 0x59, 0x86, 0x24,
+	0x67, 0x21, 0xef, 0x6b, 0x0b, 0x48, 0x93, 0x0b, 0x51, 0x15, 0x78, 0x3f, 0x71, 0x76, 0x63, 0xa3,
+	0xb5, 0x94, 0x9c, 0x4c, 0x82, 0x48, 0x15, 0x4d, 0x8c, 0xf9, 0x76, 0xcf, 0x76, 0x2c, 0xe1, 0x53,
+	0x5a, 0xf6, 0x27, 0xae, 0x2b, 0x16, 0xf9, 0x18, 0xc0, 0xb2, 0xbb, 0xcc, 0x15, 0x72, 0x01, 0xa6,
+	0x2d, 0x4d, 0x47, 0x28, 0xa4, 0x0c, 0x20, 0x2a, 0x5d, 0x0f, 0xc2, 0xbe, 0x23, 0x13, 0x92, 0xdf,
+	0xf9, 0x74, 0x22, 0xf0, 0x63, 0x91, 0xdb, 0x16, 0x5d, 0xd0, 0x14, 0xd2, 0x34, 0xed, 0x47, 0x9f,
+	0xc5, 0xe7, 0x90, 0x7f, 0xed, 0x75, 0x8d, 0xfd, 0xe1, 0x01, 0x1f, 0xc2, 0x62, 0xa9, 0x45, 0x69,
+	0xb9, 0x76, 0xa6, 0xff, 0x54, 0xa6, 0xcd, 0x4a, 0xbd, 0x56, 0x88, 0x17, 0x0f, 0x20, 0x3d, 0x50,
+	0x27, 0x4b, 0x90, 0x6b, 0xd5, 0x4e, 0x6a, 0xf5, 0xf3, 0x9a, 0xde, 0x3c, 0xfb, 0xb9, 0x5a, 0x2e,
+	0x3c, 0x20, 0x8b, 0x90, 0x69, 0xd4, 0x9b, 0x95, 0xdf, 0x2b, 0x42, 0x4c, 0xc8, 0x9c, 0x57, 0x6a,
+	0x87, 0xf5, 0xf3, 0xa6, 0x22, 0xc5, 0xdf, 0xcc, 0xa5, 0xe6, 0x0b, 0x0b, 0xc5, 0xbf, 0xc6, 0x21,
+	0x3b, 0xf0, 0xab, 0xec, 0xde, 0x88, 0x11, 0x23, 0x9c, 0x35, 0x4d, 0xd9, 0xb9, 0xff, 0x27, 0x47,
+	0x8c, 0x24, 0xe1, 0x0c, 0x5b, 0x07, 0x90, 0xe3, 0x1b, 0xf9, 0x2f, 0x90, 0x9f, 0x46, 0x0a, 0xb2,
+	0x97, 0x21, 0xd9, 0xeb, 0x1a, 0xc1, 0x95, 0xf6, 0xb9, 0x0c, 0x3f, 0x2e, 0x44, 0xf8, 0x6f, 0x18,
+	0xb7, 0x3b, 0x7d, 0x5d, 0x15, 0xfe, 0x8e, 0x9c, 0x1a, 0x92, 0xa8, 0x4a, 0x7a, 0x0d, 0xd2, 0xbd,
+	0x80, 0xe9, 0x68, 0x4b, 0xfb, 0x12, 0x05, 0x44, 0xe1, 0x54, 0xc5, 0x1a, 0xeb, 0xdd, 0x70, 0x9c,
+	0xb6, 0x61, 0x5e, 0x69, 0xbf, 0x96, 0xbc, 0x68, 0x4d, 0x9e, 0x43, 0x5e, 0x59, 0x57, 0x4d, 0xaa,
+	0x7d, 0x85, 0x6e, 0xa9, 0x3d, 0x55, 0x1b, 0x8b, 0x69, 0x1d, 0xa9, 0xe8, 0xb6, 0xeb, 0xf7, 0x42,
+	0x5d, 0x74, 0x8c, 0xf6, 0xbd, 0x9c, 0xd6, 0x11, 0xab, 0x22, 0x38, 0xa2, 0x47, 0x8a, 0xff, 0x5c,
+	0x80, 0x05, 0xd9, 0x8a, 0xd7, 0x77, 0x06, 0x86, 0x18, 0x50, 0x1f, 0x30, 0x30, 0x0a, 0x90, 0x30,
+	0xf8, 0x85, 0x16, 0xc7, 0xad, 0xc4, 0xa7, 0xa0, 0x30, 0xf7, 0x46, 0x4b, 0x48, 0x0a, 0x73, 0x6f,
+	0x04, 0xc5, 0x7c, 0x6b, 0x61, 0x99, 0xa7, 0xa9, 0xf8, 0x24, 0x3b, 0x90, 0x44, 0x3f, 0xd5, 0x98,
+	0x78, 0x32, 0x75, 0x4c, 0x5c, 0x6f, 0xa3, 0xc7, 0x54, 0x8a, 0x8a, 0xd1, 0xcb, 0x6e, 0x7d, 0xc3,
+	0xb5, 0x98, 0xa5, 0x8b, 0x2d, 0xf3, 0xb8, 0x41, 0x26, 0xa2, 0xed, 0xf3, 0x8b, 0x89, 0xe9, 0xb5,
+	0xf8, 0xa1, 0xd3, 0xab, 0x04, 0x79, 0x1e, 0x55, 0x8c, 0xbc, 0xe5, 0x3f, 0xc6, 0x39, 0xf3, 0xe4,
+	0xbe, 0x72, 0xa7, 0x39, 0x3e, 0x36, 0x37, 0xce, 0x21, 0x6b, 0x1a, 0xe6, 0x25, 0xd3, 0x7d, 0xcf,
+	0xb1, 0xcd, 0xbe, 0xf6, 0x14, 0x3b, 0xa6, 0x38, 0xe3, 0x84, 0x25, 0x21, 0xda, 0x40, 0xc9, 0xdd,
+	0x42, 0xb5, 0x5e, 0x3f, 0x69, 0x35, 0xf4, 0xfd, 0xda, 0xa1, 0xde, 0x3c, 0xab, 0xd3, 0x32, 0xcd,
+	0x98, 0x43, 0x36, 0xf9, 0x01, 0x86, 0x3b, 0xe9, 0x22, 0xc2, 0x1b, 0xe8, 0xdc, 0xda, 0x2c, 0xe7,
+	0xca, 0xee, 0x0d, 0xcd, 0xf2, 0xd1, 0x0e, 0xd8, 0x84, 0xdc, 0x25, 0xe3, 0x5d, 0x16, 0xda, 0xa6,
+	0xde, 0xf5, 0x2c, 0xa6, 0x3d, 0x93, 0xb5, 0x1a, 0x11, 0x4f, 0x3d, 0x4b, 0x40, 0xbd, 0x64, 0xc8,
+	0x0d, 0x93, 0x69, 0x45, 0x64, 0xca, 0x85, 0xc0, 0x10, 0xec, 0xd6, 0x67, 0x66, 0xc8, 0x2c, 0x55,
+	0xe8, 0x58, 0x62, 0x81, 0xb6, 0x29, 0x31, 0x44, 0xc4, 0x94, 0x05, 0x2f, 0x8a, 0x2c, 0x20, 0xbf,
+	0x82, 0xe5, 0x49, 0x1d, 0xcb, 0xe6, 0x81, 0xf6, 0x09, 0xaa, 0x90, 0x71, 0x95, 0x43, 0x9b, 0x07,
+	0xe4, 0x25, 0x90, 0x30, 0xba, 0xcf, 0x23, 0x7c, 0x63, 0x69, 0xcf, 0xd1, 0x91, 0xa5, 0x01, 0x47,
+	0x61, 0x1b, 0x8b, 0x94, 0x61, 0x71, 0x28, 0x2e, 0x8a, 0x37, 0xd0, 0x3e, 0xc5, 0x9c, 0x4f, 0x26,
+	0x6c, 0x0c, 0x24, 0xd0, 0x7c, 0x38, 0xba, 0x0c, 0x56, 0xaf, 0x21, 0x89, 0x85, 0x76, 0xef, 0xcd,
+	0x33, 0x0a, 0x94, 0xb3, 0x1b, 0xf1, 0x11, 0xa0, 0x4c, 0x5e, 0x0d, 0xa1, 0x77, 0xee, 0xfe, 0x7b,
+	0x29, 0x92, 0x2b, 0xb6, 0x21, 0x33, 0x92, 0x79, 0x01, 0xaf, 0x27, 0x73, 0x5f, 0x88, 0x89, 0x59,
+	0xa7, 0xa8, 0xf5, 0x5a, 0xf5, 0x67, 0x09, 0xab, 0x91, 0x27, 0xd7, 0x09, 0xf2, 0x04, 0xb4, 0x49,
+	0x35, 0xbd, 0xd9, 0x2a, 0x95, 0xca, 0xcd, 0x66, 0x61, 0xee, 0xcd, 0x5c, 0xca, 0x2c, 0x58, 0xc5,
+	0xbf, 0xc7, 0x20, 0x2f, 0xef, 0x39, 0xdb, 0x73, 0x05, 0xda, 0x08, 0xc8, 0x4f, 0xf0, 0x98, 0x45,
+	0x14, 0x01, 0x4b, 0x78, 0xa8, 0x0f, 0x5e, 0x2c, 0x78, 0xdd, 0x89, 0xf6, 0x97, 0x6f, 0x9a, 0xed,
+	0xe8, 0x4d, 0xb3, 0x7d, 0x16, 0x49, 0xd0, 0x15, 0x36, 0x62, 0x8e, 0x87, 0x03, 0x06, 0xf9, 0x03,
+	0xac, 0x0f, 0xed, 0x8a, 0x2b, 0xcf, 0x61, 0x22, 0xf5, 0x43, 0xdb, 0xf1, 0x77, 0xda, 0x5e, 0x1b,
+	0x18, 0x28, 0x45, 0xfa, 0x03, 0x66, 0xf1, 0xdf, 0x04, 0x52, 0x0a, 0x40, 0xf8, 0xe4, 0x15, 0xcc,
+	0x73, 0x04, 0x12, 0xca, 0xe3, 0xc7, 0x33, 0x91, 0x06, 0x55, 0x82, 0xe4, 0x3b, 0x48, 0x32, 0xce,
+	0x3d, 0x09, 0xae, 0xf2, 0x3b, 0xcf, 0xa6, 0x6b, 0xf8, 0xf8, 0x51, 0x16, 0x82, 0xbb, 0xf1, 0xfa,
+	0x09, 0x95, 0x3a, 0xc4, 0x81, 0x95, 0xb6, 0x61, 0xe9, 0xaa, 0x9f, 0x74, 0xce, 0x8c, 0x00, 0x4f,
+	0x69, 0x49, 0x10, 0x99, 0xdf, 0xf9, 0xff, 0x59, 0xe6, 0x0e, 0x0c, 0x4b, 0xf5, 0x24, 0x45, 0xa5,
+	0x92, 0x67, 0xb1, 0xdd, 0x05, 0x75, 0xb1, 0xd1, 0xe5, 0xf6, 0x14, 0xb6, 0xe8, 0xd4, 0xae, 0x1d,
+	0x04, 0xb6, 0x7b, 0x21, 0xe7, 0x39, 0xbe, 0x4a, 0xd2, 0x34, 0xab, 0x88, 0xb2, 0x5c, 0x9f, 0x43,
+	0x3e, 0x12, 0x92, 0xee, 0xe0, 0xc4, 0x4b, 0xd3, 0x48, 0x55, 0xda, 0x13, 0xb6, 0xf0, 0x08, 0x7a,
+	0x97, 0x05, 0x81, 0x71, 0xc1, 0x14, 0x88, 0xcf, 0x22, 0xf1, 0x54, 0xd2, 0x88, 0x0e, 0x2f, 0x3a,
+	0x1e, 0x37, 0x99, 0x1e, 0x84, 0x1e, 0x67, 0xa3, 0x2d, 0xae, 0x77, 0x3c, 0xae, 0xf7, 0xdc, 0xae,
+	0x11, 0x9a, 0x97, 0xcc, 0x92, 0xf0, 0x7c, 0x0c, 0x48, 0x7d, 0x82, 0x8a, 0x4d, 0xa1, 0x37, 0xec,
+	0xfe, 0x23, 0x8f, 0xb7, 0x22, 0x1d, 0xc4, 0xef, 0x0c, 0x5e, 0xbe, 0xdf, 0x06, 0x11, 0x28, 0xca,
+	0x8f, 0xee, 0xf1, 0xd9, 0xbb, 0xf6, 0x88, 0x40, 0x45, 0x17, 0x5e, 0xbd, 0xdf, 0x36, 0xc3, 0xb1,
+	0x1f, 0x68, 0x85, 0xd1, 0xad, 0x3e, 0x7f, 0xd7, 0x56, 0xc3, 0x0b, 0x24, 0x10, 0x17, 0xbb, 0x1c,
+	0xf6, 0xf2, 0xfd, 0x8c, 0x23, 0x03, 0x09, 0x62, 0x2e, 0xfc, 0x10, 0x31, 0x2f, 0xed, 0x50, 0x5b,
+	0xc3, 0x22, 0xd9, 0x9c, 0x55, 0x24, 0x38, 0x0d, 0x9a, 0x5e, 0x8f, 0x9b, 0x4c, 0x59, 0x38, 0xb6,
+	0x43, 0xb2, 0x07, 0x6b, 0xc3, 0x91, 0x7f, 0x17, 0x5d, 0xae, 0xe2, 0x86, 0xda, 0x40, 0xa4, 0x34,
+	0x01, 0x33, 0xb7, 0xe1, 0xe1, 0x84, 0x92, 0xe8, 0x45, 0x44, 0x28, 0x31, 0xba, 0x34, 0x06, 0x4a,
+	0x45, 0x97, 0x91, 0x2a, 0x6c, 0x4e, 0x6e, 0x12, 0xbd, 0x31, 0x39, 0xf3, 0xb9, 0x67, 0x4a, 0xfd,
+	0x2f, 0x51, 0xff, 0xe9, 0x38, 0xa8, 0x55, 0xef, 0x4d, 0x29, 0x87, 0xd6, 0x6a, 0xf0, 0xc9, 0x0c,
+	0x6b, 0x22, 0x1d, 0x8e, 0x67, 0xc8, 0xd1, 0x80, 0x98, 0x27, 0x46, 0x37, 0xa6, 0x99, 0x3b, 0x52,
+	0x82, 0x68, 0xef, 0xb7, 0xf0, 0x64, 0xc2, 0x1e, 0xf7, 0x4d, 0xdd, 0x34, 0x1c, 0x47, 0xda, 0xf9,
+	0x0a, 0xed, 0x68, 0x63, 0x76, 0xa8, 0x6f, 0x96, 0x0c, 0xc7, 0x41, 0xfd, 0x63, 0x78, 0x36, 0xa1,
+	0x8f, 0x65, 0xc1, 0x59, 0xe0, 0x7b, 0x6e, 0xc0, 0xa4, 0x91, 0xdf, 0xa0, 0x91, 0xf5, 0x31, 0x23,
+	0xc2, 0x0b, 0xaa, 0xa4, 0xd0, 0xd2, 0x0f, 0xb0, 0x3e, 0xc5, 0x13, 0x09, 0xb0, 0xd1, 0xca, 0xd7,
+	0x68, 0xe5, 0xf1, 0xa4, 0x2b, 0x07, 0x42, 0xe2, 0x9e, 0xb3, 0x04, 0xcc, 0x55, 0x06, 0xbe, 0x99,
+	0x7e, 0x96, 0x26, 0x73, 0xef, 0xd3, 0x7f, 0x6b, 0xd8, 0x72, 0x94, 0x6b, 0xdf, 0x4e, 0xd7, 0x3f,
+	0x37, 0xec, 0xf0, 0x1e, 0x7d, 0xce, 0xcc, 0x1b, 0xa9, 0xbf, 0x3b, 0x5d, 0x9f, 0x32, 0xf3, 0xe6,
+	0x9e, 0x08, 0xf8, 0x06, 0x8f, 0xe2, 0xf8, 0xdd, 0xf4, 0x08, 0x34, 0x84, 0xc4, 0x8c, 0x6c, 0x28,
+	0xec, 0xcd, 0x5c, 0x4b, 0x4c, 0x34, 0xb4, 0xf2, 0xfd, 0x94, 0x6c, 0x20, 0x68, 0x6e, 0x48, 0xa9,
+	0x19, 0xbe, 0x48, 0x4b, 0xbc, 0xe7, 0x4a, 0x2b, 0x7b, 0x53, 0x7c, 0x41, 0x2b, 0xb4, 0xe7, 0xa2,
+	0x85, 0xdf, 0xdd, 0x89, 0x06, 0xbe, 0xa8, 0x3a, 0xb6, 0x6b, 0x07, 0x97, 0xcc, 0xd2, 0x8e, 0x10,
+	0x80, 0x8c, 0x1b, 0x10, 0x8f, 0x99, 0x23, 0x25, 0x30, 0xc5, 0x05, 0x34, 0x30, 0xec, 0xfe, 0xd7,
+	0x33, 0x2c, 0x94, 0x46, 0x3a, 0x7d, 0x9a, 0x05, 0xa3, 0xed, 0xf1, 0x90, 0x59, 0xda, 0x31, 0xea,
+	0x6b, 0x77, 0xf4, 0xf7, 0x25, 0x9f, 0x7c, 0x0b, 0x8f, 0xa7, 0xa9, 0xcb, 0xeb, 0xae, 0x82, 0xca,
+	0x8f, 0xee, 0x28, 0xe3, 0x1d, 0x47, 0xf6, 0x67, 0x84, 0x6f, 0x70, 0xfa, 0x37, 0xa8, 0xbe, 0x7a,
+	0x37, 0x7c, 0x83, 0xe3, 0x7f, 0x0d, 0xda, 0xac, 0x0c, 0x68, 0x27, 0xa8, 0xfd, 0xd1, 0xd4, 0xe0,
+	0x4f, 0x39, 0xb5, 0x54, 0xbc, 0xb2, 0x1d, 0x87, 0x59, 0x5a, 0x75, 0xca, 0xa9, 0x51, 0xf7, 0x04,
+	0xf9, 0x53, 0x6a, 0x48, 0xc0, 0x87, 0x91, 0x3b, 0x5a, 0x3c, 0x96, 0x1b, 0xf8, 0x5a, 0x1b, 0xaf,
+	0x21, 0x85, 0xbe, 0xe5, 0xa5, 0x2b, 0x1e, 0xd1, 0x47, 0xb0, 0x38, 0x06, 0x89, 0xc2, 0x40, 0xfb,
+	0x11, 0x61, 0xc5, 0xfa, 0x94, 0x81, 0x3d, 0x84, 0x52, 0x34, 0xcf, 0xc6, 0xd6, 0xc5, 0x97, 0x90,
+	0x1e, 0xa0, 0x07, 0x32, 0x0f, 0xf1, 0xfa, 0x49, 0xe1, 0x01, 0xd1, 0x20, 0x73, 0xb0, 0x7f, 0xa8,
+	0xd3, 0xf2, 0x8f, 0xad, 0x72, 0xf3, 0xac, 0xf0, 0x9f, 0xe8, 0x2f, 0x56, 0x2c, 0xc1, 0xf2, 0x34,
+	0x74, 0x40, 0x32, 0x10, 0xe1, 0x83, 0xc2, 0x03, 0xf2, 0x31, 0xac, 0xb6, 0x6a, 0xcd, 0x56, 0xa3,
+	0x51, 0xa7, 0x67, 0xe5, 0x43, 0xbd, 0x54, 0x3f, 0x6d, 0x54, 0xaa, 0x65, 0xaa, 0x1f, 0x55, 0xf7,
+	0x5f, 0x37, 0x0b, 0xb1, 0x62, 0x4b, 0x61, 0x49, 0x79, 0x7b, 0x90, 0x2c, 0xa4, 0x6a, 0x75, 0xbd,
+	0xb4, 0x5f, 0x3a, 0x16, 0xef, 0xe5, 0x1c, 0xa4, 0x4f, 0xcb, 0xa7, 0x6a, 0x89, 0xaf, 0x65, 0x01,
+	0x13, 0xf7, 0x5f, 0x97, 0x15, 0x29, 0x4e, 0x1e, 0x01, 0xa9, 0xd6, 0x4b, 0xfb, 0x55, 0xbd, 0xde,
+	0x3a, 0x6b, 0xb4, 0xce, 0x14, 0x3d, 0xf1, 0x66, 0x2e, 0xf5, 0xa8, 0xb0, 0xf2, 0x66, 0x2e, 0xb5,
+	0x52, 0xd0, 0x14, 0x94, 0xfc, 0x53, 0x0c, 0xb2, 0x78, 0x2f, 0xca, 0x81, 0x78, 0x3d, 0xf8, 0x35,
+	0x26, 0x86, 0xa0, 0xfb, 0x1d, 0xbf, 0xc6, 0xdc, 0x7d, 0x5c, 0xc1, 0x07, 0x3f, 0xae, 0x8a, 0x2f,
+	0x20, 0x37, 0xe2, 0x41, 0xe0, 0x8f, 0xc1, 0xf2, 0xd8, 0xf8, 0xef, 0xd7, 0x1e, 0xe4, 0xaa, 0x9e,
+	0x77, 0xd5, 0xf3, 0x23, 0x77, 0x67, 0xcb, 0xfe, 0x6f, 0x9c, 0xdb, 0x83, 0xfc, 0xe8, 0x86, 0x81,
+	0x3f, 0x08, 0x50, 0xfc, 0x3d, 0x02, 0x54, 0xcc, 0x43, 0xb6, 0xdc, 0xf5, 0xc3, 0xbe, 0x82, 0x64,
+	0xc5, 0x4f, 0xa1, 0x70, 0x1c, 0x86, 0x7e, 0xc3, 0xe3, 0x61, 0x74, 0xfb, 0xe0, 0xef, 0x9b, 0x1e,
+	0x0f, 0xe5, 0xaf, 0x88, 0x14, 0xbf, 0xff, 0x1b, 0x00, 0x00, 0xff, 0xff, 0x6a, 0xbb, 0xbc, 0x18,
+	0xa3, 0x18, 0x00, 0x00,
 }
diff --git a/proto/api/goma_data.proto b/proto/api/goma_data.proto
index d7e0345..296bd72 100644
--- a/proto/api/goma_data.proto
+++ b/proto/api/goma_data.proto
@@ -1,6 +1,4 @@
-// Copyright 2010 The Goma Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
+// Copyright 2010 Google LLC. All Rights Reserved.
 
 //
 // new proto definitions for goma v2
@@ -8,21 +6,29 @@
 
 syntax = "proto2";
 
+import "google/protobuf/timestamp.proto";
+
 package devtools_goma;
 
 // persistent data
 
 // hash_key = sha256(serialized FileBlob)
 //
-// for small file (< 2MB)
+// for small file (< 2MB) embedded
 //   blob_type=FILE, !has_offset(), has_content()
 //                   has_file_size(), hash_key_size() == 0
+//
 // for large file
 //   blob_type=FILE_META, !has_offset(), !has_content()
 //                   has_file_size(), hash_key_size() > 0
 //  for each hash_key(i)
 //     blob_type=FILE_CHUNK, has_offset(), has_content(),
 //                   has_file_size(), hash_key_size() == 0
+//
+// for small file (< 2MB), stored as blob_type=FILE in file server with
+// key=hash_key[0]
+//   blob_type=FILE_REF, !hash_offset(), !has_content()
+//                   has_file_size(), hash_key_size() == 1
 message FileBlob {
   enum BlobType {
     FILE_UNSPECIFIED = 0;
@@ -30,6 +36,7 @@
     FILE_META = 2;
     FILE_CHUNK = 3;
     // ARCHIVE = 4;
+    FILE_REF = 5;
   }
   required BlobType blob_type = 1;
 
@@ -183,7 +190,7 @@
   // e.g. "os":"linux".
   //
   // Inspired from swarming bot dimensions.
-  // https://cs.chromium.org/chromium/infra/luci/appengine/swarming/proto/bots.proto
+  // https://chromium.googlesource.com/infra/luci/luci-py/+/effbcfcafa96e0840189a98ac485586ba2c2ceb6/appengine/swarming/proto/config/bots.proto
   repeated string dimensions = 10;
 
   // Implementation Note: `WINDOWS` is defined on Win, so we have to add
@@ -295,12 +302,17 @@
   reserved 99;
 }
 
-message MultiExecReq {
-  repeated ExecReq req = 1;
+// Stats of a single RBE execution. This is a subset of
+// https://github.com/bazelbuild/remote-apis/blob/178b756a22d441d8d06873a70bcd0ef01d876467/build/bazel/remote/execution/v2/remote_execution.proto#L789-L819
+message ExecutionStats {
+  // When the worker started executing the action command.
+  optional google.protobuf.Timestamp execution_start_timestamp = 1;
 
-  optional RequesterInfo requester_info = 10;
+  // When the worker completed executing the action command.
+  optional google.protobuf.Timestamp execution_completed_timestamp = 2;
 }
 
+// NEXT ID TO USE: 82
 message ExecResp {
   enum ExecError {
     OK = 0;
@@ -368,18 +380,12 @@
   optional bool compiler_proxy_local_killed = 76;
 
   optional int32 compiler_proxy_exec_request_retry = 80;
-
+  // Execution stats collected from RBE
+  optional ExecutionStats execution_stats = 81;
   // 99 was used in experimental phase.
   reserved 99;
 }
 
-message MultiExecResp {
-  repeated group Response = 1 {
-    optional int32 response_code = 2;
-    optional ExecResp resp = 3;
-  }
-}
-
 // FileService Interface
 
 message StoreFileReq {
diff --git a/proto/api/goma_log.pb.go b/proto/api/goma_log.pb.go
index 63981d9..b420986 100644
--- a/proto/api/goma_log.pb.go
+++ b/proto/api/goma_log.pb.go
@@ -1597,7 +1597,9 @@
 	proto.RegisterType((*OSInfo_MacInfo)(nil), "devtools_goma.OSInfo.MacInfo")
 }
 
-func init() { proto.RegisterFile("api/goma_log.proto", fileDescriptor_663523c6ed595628) }
+func init() {
+	proto.RegisterFile("api/goma_log.proto", fileDescriptor_663523c6ed595628)
+}
 
 var fileDescriptor_663523c6ed595628 = []byte{
 	// 2573 bytes of a gzipped FileDescriptorProto
diff --git a/proto/auth/acl.pb.go b/proto/auth/acl.pb.go
index 82706d1..f027435 100644
--- a/proto/auth/acl.pb.go
+++ b/proto/auth/acl.pb.go
@@ -164,7 +164,9 @@
 	proto.RegisterType((*ACL)(nil), "auth.ACL")
 }
 
-func init() { proto.RegisterFile("auth/acl.proto", fileDescriptor_91b80568fa5a4647) }
+func init() {
+	proto.RegisterFile("auth/acl.proto", fileDescriptor_91b80568fa5a4647)
+}
 
 var fileDescriptor_91b80568fa5a4647 = []byte{
 	// 220 bytes of a gzipped FileDescriptorProto
diff --git a/proto/auth/auth.pb.go b/proto/auth/auth.pb.go
index eeaf347..1bc2bd5 100644
--- a/proto/auth/auth.pb.go
+++ b/proto/auth/auth.pb.go
@@ -196,7 +196,9 @@
 	proto.RegisterType((*AuthResp)(nil), "auth.AuthResp")
 }
 
-func init() { proto.RegisterFile("auth/auth.proto", fileDescriptor_712ec48c1eaf43a2) }
+func init() {
+	proto.RegisterFile("auth/auth.proto", fileDescriptor_712ec48c1eaf43a2)
+}
 
 var fileDescriptor_712ec48c1eaf43a2 = []byte{
 	// 293 bytes of a gzipped FileDescriptorProto
diff --git a/proto/auth/auth_service.pb.go b/proto/auth/auth_service.pb.go
index 3adfe19..b7db01d 100644
--- a/proto/auth/auth_service.pb.go
+++ b/proto/auth/auth_service.pb.go
@@ -24,7 +24,9 @@
 // proto package needs to be updated.
 const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
 
-func init() { proto.RegisterFile("auth/auth_service.proto", fileDescriptor_eea8da14b90d2639) }
+func init() {
+	proto.RegisterFile("auth/auth_service.proto", fileDescriptor_eea8da14b90d2639)
+}
 
 var fileDescriptor_eea8da14b90d2639 = []byte{
 	// 96 bytes of a gzipped FileDescriptorProto
diff --git a/proto/auth/authdb.pb.go b/proto/auth/authdb.pb.go
index e75f053..1791606 100644
--- a/proto/auth/authdb.pb.go
+++ b/proto/auth/authdb.pb.go
@@ -111,7 +111,9 @@
 	proto.RegisterType((*CheckMembershipResp)(nil), "auth.CheckMembershipResp")
 }
 
-func init() { proto.RegisterFile("auth/authdb.proto", fileDescriptor_555942e2cbf784d7) }
+func init() {
+	proto.RegisterFile("auth/authdb.proto", fileDescriptor_555942e2cbf784d7)
+}
 
 var fileDescriptor_555942e2cbf784d7 = []byte{
 	// 129 bytes of a gzipped FileDescriptorProto
diff --git a/proto/auth/authdb_service.pb.go b/proto/auth/authdb_service.pb.go
index 30bb448..6d1a7a3 100644
--- a/proto/auth/authdb_service.pb.go
+++ b/proto/auth/authdb_service.pb.go
@@ -24,7 +24,9 @@
 // proto package needs to be updated.
 const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
 
-func init() { proto.RegisterFile("auth/authdb_service.proto", fileDescriptor_2b553d7f571c1da7) }
+func init() {
+	proto.RegisterFile("auth/authdb_service.proto", fileDescriptor_2b553d7f571c1da7)
+}
 
 var fileDescriptor_2b553d7f571c1da7 = []byte{
 	// 115 bytes of a gzipped FileDescriptorProto
diff --git a/proto/backend/backend.pb.go b/proto/backend/backend.pb.go
index a904729..5acfb10 100644
--- a/proto/backend/backend.pb.go
+++ b/proto/backend/backend.pb.go
@@ -503,7 +503,9 @@
 	proto.RegisterType((*BackendConfig)(nil), "backend.BackendConfig")
 }
 
-func init() { proto.RegisterFile("backend/backend.proto", fileDescriptor_b81549028379a959) }
+func init() {
+	proto.RegisterFile("backend/backend.proto", fileDescriptor_b81549028379a959)
+}
 
 var fileDescriptor_b81549028379a959 = []byte{
 	// 466 bytes of a gzipped FileDescriptorProto
diff --git a/proto/cache/cache.pb.go b/proto/cache/cache.pb.go
index 3f08425..4cdcc43 100644
--- a/proto/cache/cache.pb.go
+++ b/proto/cache/cache.pb.go
@@ -247,7 +247,9 @@
 	proto.RegisterType((*PutResp)(nil), "cache.PutResp")
 }
 
-func init() { proto.RegisterFile("cache/cache.proto", fileDescriptor_dd209d76f5b70ea3) }
+func init() {
+	proto.RegisterFile("cache/cache.proto", fileDescriptor_dd209d76f5b70ea3)
+}
 
 var fileDescriptor_dd209d76f5b70ea3 = []byte{
 	// 203 bytes of a gzipped FileDescriptorProto
diff --git a/proto/cache/cache_service.pb.go b/proto/cache/cache_service.pb.go
index 5987fcb..7ac0a25 100644
--- a/proto/cache/cache_service.pb.go
+++ b/proto/cache/cache_service.pb.go
@@ -24,7 +24,9 @@
 // proto package needs to be updated.
 const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
 
-func init() { proto.RegisterFile("cache/cache_service.proto", fileDescriptor_7933906ce719ddba) }
+func init() {
+	proto.RegisterFile("cache/cache_service.proto", fileDescriptor_7933906ce719ddba)
+}
 
 var fileDescriptor_7933906ce719ddba = []byte{
 	// 112 bytes of a gzipped FileDescriptorProto
diff --git a/proto/command/command.pb.go b/proto/command/command.pb.go
index 48447c6..d24030d 100644
--- a/proto/command/command.pb.go
+++ b/proto/command/command.pb.go
@@ -1127,7 +1127,9 @@
 	proto.RegisterType((*ConfigResp)(nil), "command.ConfigResp")
 }
 
-func init() { proto.RegisterFile("command/command.proto", fileDescriptor_ce77433d29a243f3) }
+func init() {
+	proto.RegisterFile("command/command.proto", fileDescriptor_ce77433d29a243f3)
+}
 
 var fileDescriptor_ce77433d29a243f3 = []byte{
 	// 1215 bytes of a gzipped FileDescriptorProto
diff --git a/proto/command/command_service.pb.go b/proto/command/command_service.pb.go
index 49b757c..41558fa 100644
--- a/proto/command/command_service.pb.go
+++ b/proto/command/command_service.pb.go
@@ -20,7 +20,9 @@
 // proto package needs to be updated.
 const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
 
-func init() { proto.RegisterFile("command/command_service.proto", fileDescriptor_927f43bcc3a37434) }
+func init() {
+	proto.RegisterFile("command/command_service.proto", fileDescriptor_927f43bcc3a37434)
+}
 
 var fileDescriptor_927f43bcc3a37434 = []byte{
 	// 58 bytes of a gzipped FileDescriptorProto
diff --git a/proto/command/package_opts.pb.go b/proto/command/package_opts.pb.go
index e921881..11ec41b 100644
--- a/proto/command/package_opts.pb.go
+++ b/proto/command/package_opts.pb.go
@@ -76,7 +76,9 @@
 	proto.RegisterType((*PackageOpts)(nil), "command.PackageOpts")
 }
 
-func init() { proto.RegisterFile("command/package_opts.proto", fileDescriptor_290ec2fb95a58d08) }
+func init() {
+	proto.RegisterFile("command/package_opts.proto", fileDescriptor_290ec2fb95a58d08)
+}
 
 var fileDescriptor_290ec2fb95a58d08 = []byte{
 	// 169 bytes of a gzipped FileDescriptorProto
diff --git a/proto/command/setup.pb.go b/proto/command/setup.pb.go
index 2be2276..2beac56 100644
--- a/proto/command/setup.pb.go
+++ b/proto/command/setup.pb.go
@@ -297,7 +297,9 @@
 	proto.RegisterType((*Setup)(nil), "command.Setup")
 }
 
-func init() { proto.RegisterFile("command/setup.proto", fileDescriptor_b38a5ba41df99617) }
+func init() {
+	proto.RegisterFile("command/setup.proto", fileDescriptor_b38a5ba41df99617)
+}
 
 var fileDescriptor_b38a5ba41df99617 = []byte{
 	// 374 bytes of a gzipped FileDescriptorProto
diff --git a/proto/exec/exec_service.pb.go b/proto/exec/exec_service.pb.go
index c1d507d..3bbac5a 100644
--- a/proto/exec/exec_service.pb.go
+++ b/proto/exec/exec_service.pb.go
@@ -85,7 +85,9 @@
 	proto.RegisterEnum("devtools_goma.ExecServiceApplicationError", ExecServiceApplicationError_name, ExecServiceApplicationError_value)
 }
 
-func init() { proto.RegisterFile("exec/exec_service.proto", fileDescriptor_86aa5ae33157a9d0) }
+func init() {
+	proto.RegisterFile("exec/exec_service.proto", fileDescriptor_86aa5ae33157a9d0)
+}
 
 var fileDescriptor_86aa5ae33157a9d0 = []byte{
 	// 281 bytes of a gzipped FileDescriptorProto
diff --git a/proto/execlog/log_service.pb.go b/proto/execlog/log_service.pb.go
index f10d5ac..d8aa29d 100644
--- a/proto/execlog/log_service.pb.go
+++ b/proto/execlog/log_service.pb.go
@@ -25,7 +25,9 @@
 // proto package needs to be updated.
 const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
 
-func init() { proto.RegisterFile("execlog/log_service.proto", fileDescriptor_a47cf8dc6db25b78) }
+func init() {
+	proto.RegisterFile("execlog/log_service.proto", fileDescriptor_a47cf8dc6db25b78)
+}
 
 var fileDescriptor_a47cf8dc6db25b78 = []byte{
 	// 131 bytes of a gzipped FileDescriptorProto
diff --git a/proto/file/file_service.pb.go b/proto/file/file_service.pb.go
index b2b7902..5d6410d 100644
--- a/proto/file/file_service.pb.go
+++ b/proto/file/file_service.pb.go
@@ -25,7 +25,9 @@
 // proto package needs to be updated.
 const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
 
-func init() { proto.RegisterFile("file/file_service.proto", fileDescriptor_f713fbe7eb11f137) }
+func init() {
+	proto.RegisterFile("file/file_service.proto", fileDescriptor_f713fbe7eb11f137)
+}
 
 var fileDescriptor_f713fbe7eb11f137 = []byte{
 	// 153 bytes of a gzipped FileDescriptorProto
diff --git a/proto/google/protobuf/timestamp.proto b/proto/google/protobuf/timestamp.proto
index eafb3fa..cd35786 100644
--- a/proto/google/protobuf/timestamp.proto
+++ b/proto/google/protobuf/timestamp.proto
@@ -40,17 +40,19 @@
 option java_multiple_files = true;
 option objc_class_prefix = "GPB";
 
-// A Timestamp represents a point in time independent of any time zone
-// or calendar, represented as seconds and fractions of seconds at
-// nanosecond resolution in UTC Epoch time. It is encoded using the
-// Proleptic Gregorian Calendar which extends the Gregorian calendar
-// backwards to year one. It is encoded assuming all minutes are 60
-// seconds long, i.e. leap seconds are "smeared" so that no leap second
-// table is needed for interpretation. Range is from
-// 0001-01-01T00:00:00Z to 9999-12-31T23:59:59.999999999Z.
-// By restricting to that range, we ensure that we can convert to
-// and from  RFC 3339 date strings.
-// See [https://www.ietf.org/rfc/rfc3339.txt](https://www.ietf.org/rfc/rfc3339.txt).
+// A Timestamp represents a point in time independent of any time zone or local
+// calendar, encoded as a count of seconds and fractions of seconds at
+// nanosecond resolution. The count is relative to an epoch at UTC midnight on
+// January 1, 1970, in the proleptic Gregorian calendar which extends the
+// Gregorian calendar backwards to year one.
+//
+// All minutes are 60 seconds long. Leap seconds are "smeared" so that no leap
+// second table is needed for interpretation, using a [24-hour linear
+// smear](https://developers.google.com/time/smear).
+//
+// The range is from 0001-01-01T00:00:00Z to 9999-12-31T23:59:59.999999999Z. By
+// restricting to that range, we ensure that we can convert to and from [RFC
+// 3339](https://www.ietf.org/rfc/rfc3339.txt) date strings.
 //
 // # Examples
 //
@@ -111,17 +113,18 @@
 // 01:30 UTC on January 15, 2017.
 //
 // In JavaScript, one can convert a Date object to this format using the
-// standard [toISOString()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toISOString]
+// standard
+// [toISOString()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toISOString)
 // method. In Python, a standard `datetime.datetime` object can be converted
-// to this format using [`strftime`](https://docs.python.org/2/library/time.html#time.strftime)
-// with the time format spec '%Y-%m-%dT%H:%M:%S.%fZ'. Likewise, in Java, one
-// can use the Joda Time's [`ISODateTimeFormat.dateTime()`](
-// http://www.joda.org/joda-time/apidocs/org/joda/time/format/ISODateTimeFormat.html#dateTime--
+// to this format using
+// [`strftime`](https://docs.python.org/2/library/time.html#time.strftime) with
+// the time format spec '%Y-%m-%dT%H:%M:%S.%fZ'. Likewise, in Java, one can use
+// the Joda Time's [`ISODateTimeFormat.dateTime()`](
+// http://www.joda.org/joda-time/apidocs/org/joda/time/format/ISODateTimeFormat.html#dateTime%2D%2D
 // ) to obtain a formatter capable of generating timestamps in this format.
 //
 //
 message Timestamp {
-
   // Represents seconds of UTC time since Unix epoch
   // 1970-01-01T00:00:00Z. Must be from 0001-01-01T00:00:00Z to
   // 9999-12-31T23:59:59Z inclusive.
diff --git a/proto/nsjail/config.pb.go b/proto/nsjail/config.pb.go
index b7c0187..d2592c3 100644
--- a/proto/nsjail/config.pb.go
+++ b/proto/nsjail/config.pb.go
@@ -1270,7 +1270,9 @@
 	proto.RegisterType((*NsJailConfig)(nil), "nsjail.NsJailConfig")
 }
 
-func init() { proto.RegisterFile("nsjail/config.proto", fileDescriptor_82b7e3129c410694) }
+func init() {
+	proto.RegisterFile("nsjail/config.proto", fileDescriptor_82b7e3129c410694)
+}
 
 var fileDescriptor_82b7e3129c410694 = []byte{
 	// 2041 bytes of a gzipped FileDescriptorProto
diff --git a/proto/settings/settings.pb.go b/proto/settings/settings.pb.go
index bd07047..7abcf49 100644
--- a/proto/settings/settings.pb.go
+++ b/proto/settings/settings.pb.go
@@ -168,7 +168,9 @@
 	proto.RegisterType((*SettingsResp)(nil), "settings.SettingsResp")
 }
 
-func init() { proto.RegisterFile("settings/settings.proto", fileDescriptor_a4bfd59e429426d0) }
+func init() {
+	proto.RegisterFile("settings/settings.proto", fileDescriptor_a4bfd59e429426d0)
+}
 
 var fileDescriptor_a4bfd59e429426d0 = []byte{
 	// 208 bytes of a gzipped FileDescriptorProto
diff --git a/proto/settings/settings_service.pb.go b/proto/settings/settings_service.pb.go
index 38aa3f5..a51ab92 100644
--- a/proto/settings/settings_service.pb.go
+++ b/proto/settings/settings_service.pb.go
@@ -24,7 +24,9 @@
 // proto package needs to be updated.
 const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
 
-func init() { proto.RegisterFile("settings/settings_service.proto", fileDescriptor_f19be0f58c8239c5) }
+func init() {
+	proto.RegisterFile("settings/settings_service.proto", fileDescriptor_f19be0f58c8239c5)
+}
 
 var fileDescriptor_f19be0f58c8239c5 = []byte{
 	// 101 bytes of a gzipped FileDescriptorProto
diff --git a/remoteexec/adapter.go b/remoteexec/adapter.go
index e7d39d3..13280f9 100644
--- a/remoteexec/adapter.go
+++ b/remoteexec/adapter.go
@@ -74,6 +74,10 @@
 	// which calls Store.Get().
 	CASBlobLookupSema chan struct{}
 
+	// OutputFileSema specifies concurrency to download files from CAS and store in
+	// file server in gomaOutput.toFileBlob().
+	OutputFileSema chan struct{}
+
 	capMu        sync.Mutex
 	capabilities *rpb.ServerCapabilities
 }
diff --git a/remoteexec/adapter_test.go b/remoteexec/adapter_test.go
index 989d782..39bade9 100644
--- a/remoteexec/adapter_test.go
+++ b/remoteexec/adapter_test.go
@@ -949,12 +949,12 @@
 		}
 	}
 
-	if got, want := files["out/Release/run.sh"].digest, digest.Bytes("cwd-agnostic-wrapper-script", []byte(cwdAgnosticWrapperScript)).Digest(); !proto.Equal(got, want) {
+	if got, want := files["out/Release/run.sh"].digest, digest.Bytes("relocatable-wrapper-script", []byte(relocatableWrapperScript)).Digest(); !proto.Equal(got, want) {
 		t.Errorf("digest of out/Release/run.sh: %s != %s", got, want)
 	}
 }
 
-func TestAdaptorHandleArbitraryToolchainSupportNonCwdAgnostic(t *testing.T) {
+func TestAdaptorHandleArbitraryToolchainSupportNonRelocatable(t *testing.T) {
 	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
 	defer cancel()
 
@@ -1099,12 +1099,12 @@
 		want []*rpb.Platform_Property
 	}{
 		{
-			desc: "cwd agnostic",
+			desc: "relocatable",
 			args: nil,
 			want: nil,
 		},
 		{
-			desc: "non cwd agnostic",
+			desc: "non relocatable",
 			args: []string{"-g"},
 			want: []*rpb.Platform_Property{
 				{
diff --git a/remoteexec/cas/cas.go b/remoteexec/cas/cas.go
index e64cf46..fa3d91f 100644
--- a/remoteexec/cas/cas.go
+++ b/remoteexec/cas/cas.go
@@ -103,6 +103,43 @@
 	return fmt.Sprintf("missing %d blobs", len(e.Blobs))
 }
 
+type batchUpdateBlobsRequestPool struct {
+	mu        sync.Mutex
+	pool      sync.Pool // New is protected by mu
+	byteLimit int64
+}
+
+var batchReqPool batchUpdateBlobsRequestPool
+
+// Get gets dummy BachUpdateBlobsRequest to check serialized size.
+// maxSizeBytes is max size needed for data buffer.
+// byteLimit is limit that RBE server sets for batch total size bytes.
+// maxSizeBytes must not exceeds byteLimit.
+func (p *batchUpdateBlobsRequestPool) Get(instance string, maxSizeBytes, byteLimit int64) *rpb.BatchUpdateBlobsRequest {
+	p.mu.Lock()
+	if p.byteLimit < byteLimit {
+		p.byteLimit = byteLimit
+	}
+	if p.pool.New == nil {
+		p.pool.New = func() interface{} {
+			return make([]byte, 0, p.byteLimit)
+		}
+	}
+	buf := p.pool.Get().([]byte)
+	if int64(cap(buf)) < maxSizeBytes {
+		buf = make([]byte, 0, maxSizeBytes)
+	}
+	p.mu.Unlock()
+	return &rpb.BatchUpdateBlobsRequest{
+		InstanceName: instance,
+		Requests:     []*rpb.BatchUpdateBlobsRequest_Request{{Data: buf}},
+	}
+}
+
+func (p *batchUpdateBlobsRequestPool) Put(req *rpb.BatchUpdateBlobsRequest) {
+	p.pool.Put(req.Requests[0].Data)
+}
+
 func separateBlobsByByteLimit(blobs []*rpb.Digest, instance string, byteLimit int64) ([]*rpb.Digest, []*rpb.Digest) {
 	if len(blobs) == 0 {
 		return nil, nil
@@ -113,22 +150,28 @@
 	})
 
 	// Create dummy data to check protobuf size. To avoid redundant allocations, find the largest digest size.
+	// string/bytes will be [encoded <wire-type><tag>] [length] [content...]
+	// so no need to allocate more than byteLimit here.
+	// https://developers.google.com/protocol-buffers/docs/encoding#structure
+	// https://developers.google.com/protocol-buffers/docs/encoding#strings
 	maxSizeBytes := blobs[len(blobs)-1].SizeBytes
-	dummyReq := &rpb.BatchUpdateBlobsRequest{
-		InstanceName: instance,
-		Requests:     []*rpb.BatchUpdateBlobsRequest_Request{{Data: make([]byte, 1, maxSizeBytes)}},
+	if maxSizeBytes >= byteLimit {
+		maxSizeBytes = byteLimit
 	}
-
-	for i, blob := range blobs {
-		// Create dummy data to check protobuf size.
-		dummyReq.Requests[0].Digest = blob
-		dummyReq.Requests[0].Data = dummyReq.Requests[0].Data[:blob.SizeBytes]
-		if int64(proto.Size(dummyReq)) >= byteLimit {
-			return blobs[:i], blobs[i:]
+	dummyReq := batchReqPool.Get(instance, maxSizeBytes, byteLimit)
+	defer batchReqPool.Put(dummyReq)
+	i := sort.Search(len(blobs), func(i int) bool {
+		if blobs[i].SizeBytes >= byteLimit {
+			return true
 		}
+		dummyReq.Requests[0].Digest = blobs[i]
+		dummyReq.Requests[0].Data = dummyReq.Requests[0].Data[:blobs[i].SizeBytes]
+		return int64(proto.Size(dummyReq)) >= byteLimit
+	})
+	if i < len(blobs) {
+		return blobs[:i], blobs[i:]
 	}
-	// All blobs have protobuf size below `byteLimit`.
-	return blobs, []*rpb.Digest{}
+	return blobs, nil
 }
 
 func lookupBlobsInStore(ctx context.Context, blobs []*rpb.Digest, store *digest.Store, sema chan struct{}) ([]*rpb.BatchUpdateBlobsRequest_Request, []MissingBlob) {
@@ -245,11 +288,11 @@
 
 	// up to max_batch_total_size_bytes, use BatchUpdateBlobs.
 	// more than this, use bytestream.Write.
-	batchLimit := int64(DefaultBatchByteLimit)
+	byteLimit := int64(DefaultBatchByteLimit)
 	if c.CacheCapabilities != nil && c.CacheCapabilities.MaxBatchTotalSizeBytes > 0 {
-		batchLimit = c.CacheCapabilities.MaxBatchTotalSizeBytes
+		byteLimit = c.CacheCapabilities.MaxBatchTotalSizeBytes
 	}
-	smallBlobs, largeBlobs := separateBlobsByByteLimit(blobs, instance, batchLimit)
+	smallBlobs, largeBlobs := separateBlobsByByteLimit(blobs, instance, byteLimit)
 
 	logger.Infof("upload by batch %d out of %d", len(smallBlobs), len(blobs))
 	blobReqs, missingBlobs := lookupBlobsInStore(ctx, smallBlobs, c.Store, sema)
@@ -257,7 +300,7 @@
 		Blobs: missingBlobs,
 	}
 
-	batchReqs := createBatchUpdateBlobsRequests(blobReqs, instance, batchLimit)
+	batchReqs := createBatchUpdateBlobsRequests(blobReqs, instance, byteLimit)
 	for _, batchReq := range batchReqs {
 		uploaded := false
 		for !uploaded {
diff --git a/remoteexec/cas/cas_test.go b/remoteexec/cas/cas_test.go
index 43dcc05..f4c8cff 100644
--- a/remoteexec/cas/cas_test.go
+++ b/remoteexec/cas/cas_test.go
@@ -15,6 +15,7 @@
 	rpb "github.com/bazelbuild/remote-apis/build/bazel/remote/execution/v2"
 	"github.com/golang/protobuf/proto"
 	"github.com/google/go-cmp/cmp"
+	"github.com/google/go-cmp/cmp/cmpopts"
 
 	"go.chromium.org/goma/server/remoteexec/digest"
 )
@@ -59,7 +60,7 @@
 }
 
 func protoEqual(x, y interface{}) bool {
-	return cmp.Equal(x, y, cmp.Comparer(proto.Equal))
+	return cmp.Equal(x, y, cmp.Comparer(proto.Equal), cmpopts.EquateEmpty())
 }
 
 func TestMissing(t *testing.T) {
diff --git a/remoteexec/clang-cl.go b/remoteexec/clang-cl.go
index c9c2ca9..5e4ac29 100644
--- a/remoteexec/clang-cl.go
+++ b/remoteexec/clang-cl.go
@@ -10,12 +10,14 @@
 
 // TODO: share exec/clangcl.go ?
 
-// clangclCwdAgnostic checks if args will generate cwd-agnostic outputs
-// (files/stdout/stderr).
+// clangclRelocatableReq checks if the request (args, envs) uses relative
+// paths only and doesn't use flags that generates output including cwd,
+// so will generate cwd-agnostic outputs
+// (files/stdout/stderr will not include cwd dependent paths).
 //
 // TODO: implement it.
-func clangclCwdAgnostic(args, envs []string) error {
-	return errors.New("no cwd agnostic check for clang-cl")
+func clangclRelocatableReq(args, envs []string) error {
+	return errors.New("no relocatable check for clang-cl")
 }
 
 // clangclOutputs returns output files from clang-cl command line.
diff --git a/remoteexec/exec.go b/remoteexec/exec.go
index 034a35d..cb4f9e0 100644
--- a/remoteexec/exec.go
+++ b/remoteexec/exec.go
@@ -28,6 +28,7 @@
 	"google.golang.org/grpc/status"
 
 	"go.chromium.org/goma/server/command/descriptor"
+	"go.chromium.org/goma/server/exec"
 	"go.chromium.org/goma/server/log"
 	gomapb "go.chromium.org/goma/server/proto/api"
 	cmdpb "go.chromium.org/goma/server/proto/command"
@@ -612,7 +613,7 @@
 type wrapperType int
 
 const (
-	wrapperCwdAgnostic wrapperType = iota
+	wrapperRelocatable wrapperType = iota
 	wrapperBindMount
 	wrapperNsjailChroot
 	wrapperWin
@@ -620,8 +621,8 @@
 
 func (w wrapperType) String() string {
 	switch w {
-	case wrapperCwdAgnostic:
-		return "wrapper-cwd-agnostic"
+	case wrapperRelocatable:
+		return "wrapper-relocatable"
 	case wrapperBindMount:
 		return "wrapper-bind-mount"
 	case wrapperNsjailChroot:
@@ -668,7 +669,7 @@
 "$@"
 `
 
-	cwdAgnosticWrapperScript = `#!/bin/bash
+	relocatableWrapperScript = `#!/bin/bash
 set -e
 if [[ "$WORK_DIR" != "" ]]; then
   cd "${WORK_DIR}"
@@ -708,17 +709,17 @@
 	args := buildArgs(ctx, cmdConfig, argv0, r.gomaReq)
 	// TODO: only allow whitelisted envs.
 
-	wt := wrapperCwdAgnostic
+	wt := wrapperRelocatable
 	pathType := cmdConfig.GetCmdDescriptor().GetSetup().GetPathType()
 	switch pathType {
 	case cmdpb.CmdDescriptor_POSIX:
 		if r.needChroot {
 			wt = wrapperNsjailChroot
 		} else {
-			err = cwdAgnosticReq(ctx, cmdConfig, r.filepath, r.gomaReq.Arg, r.gomaReq.Env)
+			err = relocatableReq(ctx, cmdConfig, r.filepath, r.gomaReq.Arg, r.gomaReq.Env)
 			if err != nil {
 				wt = wrapperBindMount
-				logger.Infof("non cwd agnostic: %v", err)
+				logger.Infof("non relocatable: %v", err)
 			}
 		}
 	case cmdpb.CmdDescriptor_WINDOWS:
@@ -768,12 +769,12 @@
 				isExecutable: true,
 			},
 		}
-	case wrapperCwdAgnostic:
-		logger.Infof("run with chdir: cwd agnostic")
+	case wrapperRelocatable:
+		logger.Infof("run with chdir: relocatable")
 		for _, e := range r.gomaReq.Env {
 			if strings.HasPrefix(e, "PWD=") {
 				// PWD is usually absolute path.
-				// if cwd agnostic, then we should remove
+				// if relocatable, then we should remove
 				// PWD environment variable.
 				continue
 			}
@@ -782,7 +783,7 @@
 		files = []fileDesc{
 			{
 				name:         posixWrapperName,
-				data:         digest.Bytes("cwd-agnostic-wrapper-script", []byte(cwdAgnosticWrapperScript)),
+				data:         digest.Bytes("relocatable-wrapper-script", []byte(relocatableWrapperScript)),
 				isExecutable: true,
 			},
 		}
@@ -866,21 +867,21 @@
 	return append(args, fmt.Sprintf("--target=%s", target))
 }
 
-// cwdAgnosticReq checks args, envs is cwd agnostic, respecting cmdConfig.
-func cwdAgnosticReq(ctx context.Context, cmdConfig *cmdpb.Config, filepath clientFilePath, args, envs []string) error {
+// relocatableReq checks args, envs is relocatable, respecting cmdConfig.
+func relocatableReq(ctx context.Context, cmdConfig *cmdpb.Config, filepath clientFilePath, args, envs []string) error {
 	switch name := cmdConfig.GetCmdDescriptor().GetSelector().GetName(); name {
 	case "gcc", "g++", "clang", "clang++":
-		return gccCwdAgnostic(filepath, args, envs)
+		return gccRelocatableReq(filepath, args, envs)
 	case "clang-cl":
-		return clangclCwdAgnostic(args, envs)
+		return clangclRelocatableReq(args, envs)
 	case "javac":
-		// Currently, javac in Chromium is fully cwd agnostic. Simpler just to
-		// support only the cwd agnostic case and let it fail if the client passed
+		// Currently, javac in Chromium is fully relocatable. Simpler just to
+		// support only the relocatable case and let it fail if the client passed
 		// in invalid absolute paths.
 		return nil
 	default:
 		// "cl.exe", "clang-tidy"
-		return fmt.Errorf("no cwd agnostic check for %s", name)
+		return fmt.Errorf("no relocatable check for %s", name)
 	}
 }
 
@@ -1216,7 +1217,10 @@
 		timestampSub(ctx, md.GetInputFetchCompletedTimestamp(), md.GetInputFetchStartTimestamp()),
 		timestampSub(ctx, md.GetExecutionCompletedTimestamp(), md.GetExecutionStartTimestamp()),
 		timestampSub(ctx, md.GetOutputUploadCompletedTimestamp(), md.GetOutputUploadStartTimestamp()))
-
+	r.gomaResp.ExecutionStats = &gomapb.ExecutionStats{
+		ExecutionStartTimestamp:     md.GetExecutionStartTimestamp(),
+		ExecutionCompletedTimestamp: md.GetExecutionCompletedTimestamp(),
+	}
 	gout := gomaOutput{
 		gomaResp: r.gomaResp,
 		bs:       r.client.ByteStream(),
@@ -1278,11 +1282,23 @@
 			r.gomaResp.ErrorMessage = append(r.gomaResp.ErrorMessage, fmt.Sprintf("output path %s: %v", output.Path, err))
 			continue
 		}
-		gout.outputDirectory(ctx, r.filepath, fname, output)
+		gout.outputDirectory(ctx, r.filepath, fname, output, r.f.OutputFileSema)
 	}
 	if len(r.gomaResp.ErrorMessage) == 0 {
 		r.gomaResp.Result.ExitStatus = proto.Int32(eresp.Result.ExitCode)
 	}
+
+	sizeLimit := exec.DefaultMaxRespMsgSize
+	respSize := proto.Size(r.gomaResp)
+	if respSize > sizeLimit {
+		logger.Infof("gomaResp size=%d, limit=%d, using FileService for larger blobs.", respSize, sizeLimit)
+		if err := gout.reduceRespSize(ctx, sizeLimit, r.f.OutputFileSema); err != nil {
+			// Don't need to append any error messages to `r.gomaResp` because it won't be sent.
+			return nil, fmt.Errorf("failed to reduce resp size below limit=%d, %d -> %d: %v", sizeLimit, respSize, proto.Size(gout.gomaResp), err)
+		}
+		logger.Infof("gomaResp size reduced %d -> %d", respSize, proto.Size(gout.gomaResp))
+	}
+
 	return r.gomaResp, r.Err()
 }
 
diff --git a/remoteexec/gcc.go b/remoteexec/gcc.go
index 687b66d..4903faf 100644
--- a/remoteexec/gcc.go
+++ b/remoteexec/gcc.go
@@ -10,20 +10,37 @@
 	"strings"
 )
 
+var pathFlags = []string{
+	"--sysroot=",
+	"-B",
+	"-I",
+	"-fcrash-diagnostics-dir=",
+	"-fprofile-instr-use=",
+	"-fprofile-sample-use=",
+	"-fsanitize-blacklist=",
+	"-include=",
+	"-isystem",
+	"-o",
+	"-resource-dir=",
+}
+
 // TODO: share exec/gcc.go ?
 
-// gccCwdAgnostic checks if args will generate cwd-agnostic outputs
-// (files/stdout/stderr).
+// gccRelocatableReq checks if the request (args, envs) uses relative
+// paths only and doesn't use flags that generates output including cwd,
+// so will generate cwd-agnostic outputs
+// (files/stdout/stderr will not include cwd dependent paths).
 //
-// args will be cwd-agnostic if path used in arg is cwd relative.
+// The request will be relocatable if path used in arg is cwd relative.
 //
-// args will NOT generate cwd-agnostic output (.o), that is, generate
+// The request will NOT be relocatable, that is, generate
 // outputs that would contain absolute path names (DW_AT_comp_dir etc),
 // if
 //  debug build (-g* except -g0) -> DW_AT_comp_dir or other filepaths.
+//    this will be canceled by -fdebug-compilation-dir
 //  --pnacl-allow-translate  crbug.com/685461
 //
-// these flags would emit non cwd-agnostic output.
+// The following flags would NOT be relocatable
 //  absolute input filename (debug build)
 //      *.d file output will not be cwd-agnostic.
 //      DW_AT_name (debug build)
@@ -37,32 +54,66 @@
 //
 // ref:
 // https://docs.google.com/spreadsheets/d/1_-ZJhqy7WhSFYuZU2QkmQ4Ed9182bWfKg09EfBAkVf8/edit#gid=759603323
-func gccCwdAgnostic(filepath clientFilePath, args, envs []string) error {
+//
+// TODO: http://b/150662978 relocatableReq should check input and output file path too.
+func gccRelocatableReq(filepath clientFilePath, args, envs []string) error {
 	var debugFlags []string
+	debugCompilationDir := false
 	subArgs := map[string][]string{}
 	pathFlag := false
 	var subCmd string
+Loop:
 	for _, arg := range args {
-		switch {
-		case arg == "-fdebug-compilation-dir":
-			// We can stop checking the rest of the flags. When seeing
-			// "-fdebug-compilation-dir", we expect the result to be CWD agnostic.
-			//
-			// Note that this check applies to both GCC and Clang and returns nil
-			// immediately for the following cases:
-			// -xx -fdebug-compilation-dir . -yy ...                 <- GCC flag
-			// -xx -XClang -fdebug-compilation-dir -XClang . -yy ... <- Clang flag
-			//
-			// As a result, clangArgCwdAgnostic() doesn't need to check this again.
-			if subCmd == "" || subCmd == "clang" {
-				return nil
-			}
-			return errors.New("fdebug-compilation-dir not supported for " + subCmd)
-		case pathFlag:
+		if pathFlag {
 			if filepath.IsAbs(arg) {
 				return fmt.Errorf("abs path: %s", arg)
 			}
+			// TODO: When clang supports relative paths in hmap,
+			// instead check that hmap does not have abs paths.
+			if strings.HasSuffix(arg, ".hmap") {
+				return fmt.Errorf("hmap file: %s", arg)
+			}
 			pathFlag = false
+			continue
+		}
+		// TODO: When clang supports relative paths in hmap,
+		// instead check that hmap does not have abs paths.
+		if strings.HasPrefix(arg, "-I") && strings.HasSuffix(arg, ".hmap") {
+			return fmt.Errorf("hmap file: %s", arg)
+		}
+		for _, fp := range pathFlags {
+			if arg != fp && strings.HasPrefix(arg, fp) {
+
+				if filepath.IsAbs(arg[len(fp):]) {
+					return fmt.Errorf("abs path: %s", arg)
+				}
+				continue Loop
+			}
+		}
+		switch {
+		case arg == "-fdebug-compilation-dir":
+			// We can stop checking the rest of the flags.
+			// When seeing "-fdebug-compilation-dir",
+			// we could cancel non-cwd agnosticsness due to
+			// debug flags.
+			//
+			// Note that this check applies to both GCC and Clang
+			// -xx -fdebug-compilation-dir . -yy ...                 <- GCC flag
+			// -xx -Xclang -fdebug-compilation-dir -Xclang . -yy ... <- Clang flag
+			//
+			// As a result, clangArgRelocatableReq() doesn't need to check this again.
+			// The value of -fdebug-compilation-dir is used
+			// just for DW_AT_comp_dir, so no need to check it.
+			switch subCmd {
+			case "clang":
+				subArgs[subCmd] = append(subArgs[subCmd], arg)
+				fallthrough
+			case "":
+				debugCompilationDir = true
+
+				continue Loop
+			}
+			return errors.New("fdebug-compilation-dir not supported for " + subCmd)
 
 		case subCmd != "":
 			subArgs[subCmd] = append(subArgs[subCmd], arg)
@@ -120,34 +171,9 @@
 			pathFlag = true
 		case arg == "-MF":
 			pathFlag = true
-		case strings.HasPrefix(arg, "-o"):
-			if filepath.IsAbs(arg[len("-o"):]) {
-				return fmt.Errorf("abs path: %s", arg)
-			}
-		case strings.HasPrefix(arg, "-I") || strings.HasPrefix(arg, "-B"):
-			if filepath.IsAbs(arg[len("-I"):]) {
-				return fmt.Errorf("abs path: %s", arg)
-			}
-		case strings.HasPrefix(arg, "-isystem"):
-			if filepath.IsAbs(arg[len("-isystem"):]) {
-				return fmt.Errorf("abs path: %s", arg)
-			}
-		case strings.HasPrefix(arg, "-include="):
-			if filepath.IsAbs(arg[len("-include="):]) {
-				return fmt.Errorf("abs path: %s", arg)
-			}
-		case strings.HasPrefix(arg, "--sysroot="):
-			if filepath.IsAbs(arg[len("--sysroot="):]) {
-				return fmt.Errorf("abs path: %s", arg)
-			}
 		case arg == "-isysroot":
 			pathFlag = true
 
-		case strings.HasPrefix(arg, "-resource-dir="):
-			if filepath.IsAbs(arg[len("-resource-dir="):]) {
-				return fmt.Errorf("abs path: %s", arg)
-			}
-
 		case strings.HasPrefix(arg, "-"): // unknown flag?
 			return fmt.Errorf("unknown flag: %s", arg)
 
@@ -158,19 +184,19 @@
 		}
 	}
 
-	if len(debugFlags) > 0 {
+	if len(debugFlags) > 0 && !debugCompilationDir {
 		return fmt.Errorf("debug build: %q", debugFlags)
 	}
 	if len(subArgs) > 0 {
 		for cmd, args := range subArgs {
 			switch cmd {
 			case "clang":
-				err := clangArgCwdAgnostic(filepath, args)
+				err := clangArgRelocatable(filepath, args)
 				if err != nil {
 					return err
 				}
 			case "llvm":
-				err := llvmArgCwdAgnostic(filepath, args)
+				err := llvmArgRelocatable(filepath, args)
 				if err != nil {
 					return err
 				}
@@ -195,7 +221,7 @@
 	return nil
 }
 
-func clangArgCwdAgnostic(filepath clientFilePath, args []string) error {
+func clangArgRelocatable(filepath clientFilePath, args []string) error {
 	pathFlag := false
 	skipFlag := false
 	for _, arg := range args {
@@ -208,8 +234,8 @@
 		case skipFlag:
 			skipFlag = false
 
-		case arg == "-mllvm" || arg == "-add-plugin":
-			// TODO: pass llvmArgCwdAgnostic for -mllvm?
+		case arg == "-mllvm", arg == "-add-plugin", arg == "-fdebug-compilation-dir":
+			// TODO: pass llvmArgRelocatable for -mllvm?
 			skipFlag = true
 		case strings.HasPrefix(arg, "-plugin-arg-"):
 			skipFlag = true
@@ -222,7 +248,7 @@
 	return nil
 }
 
-func llvmArgCwdAgnostic(filepath clientFilePath, args []string) error {
+func llvmArgRelocatable(filepath clientFilePath, args []string) error {
 	for _, arg := range args {
 		switch {
 		case strings.HasPrefix(arg, "-asan-"):
diff --git a/remoteexec/gcc_test.go b/remoteexec/gcc_test.go
index de600e1..86e902d 100644
--- a/remoteexec/gcc_test.go
+++ b/remoteexec/gcc_test.go
@@ -12,7 +12,7 @@
 	"go.chromium.org/goma/server/command/descriptor/posixpath"
 )
 
-func TestGccCwdAgnostic(t *testing.T) {
+func TestGccRelocatableReq(t *testing.T) {
 
 	baseReleaseArgs := []string{
 		"../../third_party/llvm-build/Release+Asserts/bin/clang++",
@@ -78,7 +78,7 @@
 		desc        string
 		args        []string
 		envs        []string
-		cwdAgnostic bool
+		relocatable bool
 	}{
 		{
 			desc: "chromium base release",
@@ -86,7 +86,7 @@
 			envs: []string{
 				"PWD=/b/c/b/linux/src/out/Release",
 			},
-			cwdAgnostic: true,
+			relocatable: true,
 		},
 		{
 			desc: "chromium base debug",
@@ -136,58 +136,58 @@
 			envs: []string{
 				"PWD=/b/c/b/linux/src/out/Debug",
 			},
-			cwdAgnostic: false,
+			relocatable: false,
 		},
 		{
 			desc: "resource-dir relative",
 			args: append(append([]string{}, baseReleaseArgs...),
 				"-resource-dir=../../third_party/llvm-build-release+Asserts/bin/clang/10.0.0"),
-			cwdAgnostic: true,
+			relocatable: true,
 		},
 		{
 			desc: "resource-dir absolute",
 			args: append(append([]string{}, baseReleaseArgs...),
 				"-resource-dir=/b/c/b/linux/src/third_party/llvm-build-release+Asserts/bin/clang/10.0.0"),
-			cwdAgnostic: false,
+			relocatable: false,
 		},
 		{
 			desc: "isystem absolute",
 			args: append(append([]string{}, baseReleaseArgs...),
 				"-isystem/b/c/b/linux/src/build/linux/debian_sid_amd64-sysroot/usr/include/glib-2.0"),
-			cwdAgnostic: false,
+			relocatable: false,
 		},
 		{
 			desc: "include absolute",
-			args: append(append([]string {}, baseReleaseArgs...),
+			args: append(append([]string{}, baseReleaseArgs...),
 				"-include",
 				"/b/c/b/linux/header.h"),
-			cwdAgnostic: false,
+			relocatable: false,
 		},
 		{
 			desc: "include relative",
-			args: append(append([]string {}, baseReleaseArgs...),
+			args: append(append([]string{}, baseReleaseArgs...),
 				"-include",
 				"../b/c/b/linux/header.h"),
-			cwdAgnostic: true,
+			relocatable: true,
 		},
 		{
 			desc: "include= absolute",
-			args: append(append([]string {}, baseReleaseArgs...),
+			args: append(append([]string{}, baseReleaseArgs...),
 				"-include=/b/c/b/linux/header.h"),
-			cwdAgnostic: false,
+			relocatable: false,
 		},
 		{
 			desc: "include= relative",
-			args: append(append([]string {}, baseReleaseArgs...),
+			args: append(append([]string{}, baseReleaseArgs...),
 				"-include=../b/c/b/linux/header.h"),
-			cwdAgnostic: true,
+			relocatable: true,
 		},
 		{
 			desc: "sysroot absolute",
 			args: modifyArgs(baseReleaseArgs,
 				"--sysroot",
 				"--sysroot=/b/c/b/linux/build/linux/debian_sid_amd64-sysroot"),
-			cwdAgnostic: false,
+			relocatable: false,
 		},
 		{
 			desc: "llvm -asan option",
@@ -195,7 +195,7 @@
 			args: append(append([]string{}, baseReleaseArgs...),
 				"-mllvm",
 				"-asan-globals=0"),
-			cwdAgnostic: true,
+			relocatable: true,
 		},
 		{
 			desc: "llvm -regalloc option",
@@ -205,27 +205,89 @@
 				"-regalloc=pbqp",
 				"-mllvm",
 				"-pbqp-coalescing"),
-			cwdAgnostic: true,
+			relocatable: true,
+		},
+		{
+			desc: "headermap file",
+			// http://b/149448356#comment17
+			args: append(append([]string{}, baseReleaseArgs...),
+				"-Ifoo.hmap"),
+			relocatable: false,
+		},
+		{
+			desc: "headermap file 2",
+			// http://b/149448356#comment17
+			args: append(append([]string{}, baseReleaseArgs...),
+				[]string{"-I", "foo.hmap"}...),
+			relocatable: false,
 		},
 		{
 			desc: "Qunused-arguments",
-			args: append(append([]string {}, baseReleaseArgs...),
+			args: append(append([]string{}, baseReleaseArgs...),
 				"-Qunused-arguments"),
-			cwdAgnostic: true,
+			relocatable: true,
+		},
+		{
+			desc: "fprofile-instr-use relative",
+			args: append(append([]string{}, baseReleaseArgs...),
+				"-fprofile-instr-use=../../out/data/default.profdata"),
+			relocatable: true,
+		},
+		{
+			desc: "fprofile-instr-use absolute",
+			args: append(append([]string{}, baseReleaseArgs...),
+				"-fprofile-instr-use=/b/c/b/linux/src/out/data/default.profdataa"),
+			relocatable: false,
+		},
+		{
+			desc: "fprofile-sample-use relative",
+			args: append(append([]string{}, baseReleaseArgs...),
+				"-fprofile-sample-use=../../chrome/android/profiles/afdo.prof"),
+			relocatable: true,
+		},
+		{
+			desc: "fprofile-sample-use absolute",
+			args: append(append([]string{}, baseReleaseArgs...),
+				"-fprofile-sample-use=/b/c/b/linux/src/android/profiles/afdo.prof"),
+			relocatable: false,
+		},
+		{
+			desc: "fsatinitize-blacklist relative",
+			args: append(append([]string{}, baseReleaseArgs...),
+				"-fsanitize-blacklist=../../tools/cfi/blacklist.txt"),
+			relocatable: true,
+		},
+		{
+			desc: "fsanitize-blacklist absolute",
+			args: append(append([]string{}, baseReleaseArgs...),
+				"-fsanitize-blacklist=/b/c/b/linux/src/tools/cfi/blacklist.txt"),
+			relocatable: false,
+		},
+		{
+			desc: "fcrash-diagnostics-dir absolute",
+			args: append(append([]string{}, baseReleaseArgs...),
+				"-fcrash-diagnostics-dir=../../tools/clang/crashreports"),
+			relocatable: true,
+		},
+		{
+			desc: "fcrash-diagnostics-dir absolute",
+			args: append(append([]string{}, baseReleaseArgs...),
+				"-fcrash-diagnostics-dir=/b/c/b/linux/src/tools/clang/crashreports"),
+			relocatable: false,
 		},
 	} {
 		t.Run(tc.desc, func(t *testing.T) {
-			err := gccCwdAgnostic(posixpath.FilePath{}, tc.args, tc.envs)
-			if (err == nil) != tc.cwdAgnostic {
-				t.Errorf("gccCwdAgnostic(posixpath.FilePath, args, envs)=%v; cwdAgnostic=%t", err, tc.cwdAgnostic)
+			err := gccRelocatableReq(posixpath.FilePath{}, tc.args, tc.envs)
+			if (err == nil) != tc.relocatable {
+				t.Errorf("gccRelocatableReq(posixpath.FilePath, args, envs)=%v; relocatable=%t", err, tc.relocatable)
 			}
 		})
 	}
 }
 
-func TestGccCwdAgnosticForDebugCompilationDir(t *testing.T) {
+func TestGccRelocatableReqForDebugCompilationDir(t *testing.T) {
 	// Tests for supporting "-fdebug-compilation-dir", see b/135719929.
-	// We could have merged the cases here into TestGccCwdAgnostic, but decided
+	// We could have merged the cases here into TestGccRelocatableReq, but decided
 	// to separate them for clarity.
 
 	// Do not set "-g*" options in baseReleaseArgs!
@@ -240,14 +302,14 @@
 		desc        string
 		args        []string
 		envs        []string
-		cwdAgnostic bool
+		relocatable bool
 	}{
 		{
 			desc: "basic",
 			args: append(append([]string{}, baseReleaseArgs...),
 				"-fdebug-compilation-dir",
 				"."),
-			cwdAgnostic: true,
+			relocatable: true,
 		},
 		{
 			desc: "-Xclang",
@@ -256,7 +318,7 @@
 				"-fdebug-compilation-dir",
 				"-Xclang",
 				"."),
-			cwdAgnostic: true,
+			relocatable: true,
 		},
 		{
 			desc: "With -g* DBG options",
@@ -265,7 +327,7 @@
 				"-gsplit-dwarf",
 				"-fdebug-compilation-dir",
 				"."),
-			cwdAgnostic: true,
+			relocatable: true,
 		},
 		{
 			desc: "-Xclang with -g* DBG option",
@@ -276,7 +338,7 @@
 				"-fdebug-compilation-dir",
 				"-Xclang",
 				"."),
-			cwdAgnostic: true,
+			relocatable: true,
 		},
 		{
 			// Make sure the CWD agnostic still returns false if
@@ -285,7 +347,7 @@
 			args: append(append([]string{}, baseReleaseArgs...),
 				"-g2",
 				"-gsplit-dwarf"),
-			cwdAgnostic: false,
+			relocatable: false,
 		},
 		{
 			// "-fdebug-compilation-dir" is not supported as LLVM flags.
@@ -295,13 +357,21 @@
 				"-fdebug-compilation-dir",
 				"-mllvm",
 				"."),
-			cwdAgnostic: false,
+			relocatable: false,
+		},
+		{
+			desc: "input abs path",
+			args: append(append([]string{}, baseReleaseArgs...),
+				"-fdebug-compilation-dir",
+				".",
+				"-isystem/b/c/b/linux/src/build/linux/debian_sid_amd64-sysroot/usr/include/glib-2.0"),
+			relocatable: false,
 		},
 	} {
 		t.Run(tc.desc, func(t *testing.T) {
-			err := gccCwdAgnostic(posixpath.FilePath{}, tc.args, tc.envs)
-			if (err == nil) != tc.cwdAgnostic {
-				t.Errorf("gccCwdAgnostic(posixpath.FilePath, args, envs)=%v; cwdAgnostic=%t", err, tc.cwdAgnostic)
+			err := gccRelocatableReq(posixpath.FilePath{}, tc.args, tc.envs)
+			if (err == nil) != tc.relocatable {
+				t.Errorf("gccRelocatableReq(posixpath.FilePath, args, envs)=%v; relocatable=%t", err, tc.relocatable)
 			}
 		})
 	}
diff --git a/remoteexec/gomainput.go b/remoteexec/gomainput.go
index a2a2287..299767f 100644
--- a/remoteexec/gomainput.go
+++ b/remoteexec/gomainput.go
@@ -15,6 +15,7 @@
 	"google.golang.org/grpc/codes"
 	"google.golang.org/grpc/status"
 
+	"go.chromium.org/goma/server/file"
 	"go.chromium.org/goma/server/hash"
 	gomapb "go.chromium.org/goma/server/proto/api"
 	fpb "go.chromium.org/goma/server/proto/file"
@@ -83,28 +84,39 @@
 	return resp.HashKey, nil
 }
 
-func lookup(ctx context.Context, c lookupClient, hashKey string) (*gomapb.FileBlob, error) {
+func lookup(ctx context.Context, c lookupClient, hashKeys []string) ([]*gomapb.FileBlob, error) {
+	req := &gomapb.LookupFileReq{
+		HashKey:       hashKeys,
+		RequesterInfo: requesterInfo(ctx),
+	}
 	var resp *gomapb.LookupFileResp
 	var err error
 	err = rpc.Retry{}.Do(ctx, func() error {
-		resp, err = c.LookupFile(ctx, &gomapb.LookupFileReq{
-			HashKey: []string{
-				hashKey,
-			},
-			RequesterInfo: requesterInfo(ctx),
-		})
+		resp, err = c.LookupFile(ctx, req)
 		return err
 	})
 	if err != nil {
 		return nil, err
 	}
 	if len(resp.Blob) == 0 {
-		return nil, status.Errorf(codes.NotFound, "no blob for %s", hashKey)
+		return nil, status.Errorf(codes.NotFound, "no blob for %s", hashKeys)
 	}
-	if resp.Blob[0].GetBlobType() == gomapb.FileBlob_FILE_UNSPECIFIED {
-		return nil, status.Errorf(codes.NotFound, "missing blob for %s", hashKey)
+	if len(resp.Blob) != len(hashKeys) {
+		return nil, status.Errorf(codes.Internal, "request %d (%q), got %d", len(hashKeys), hashKeys, len(resp.Blob))
 	}
-	return resp.Blob[0], nil
+	var unspecified []string
+	var blobs []*gomapb.FileBlob
+	for i, blob := range resp.Blob {
+		if blob.GetBlobType() == gomapb.FileBlob_FILE_UNSPECIFIED {
+			unspecified = append(unspecified, fmt.Sprintf("%d:%q", i, hashKeys[i]))
+			continue
+		}
+		blobs = append(blobs, blob)
+	}
+	if len(unspecified) > 0 {
+		return nil, status.Errorf(codes.NotFound, "missing blob for %s", unspecified)
+	}
+	return blobs, nil
 }
 
 type gomaInputSource struct {
@@ -122,10 +134,11 @@
 	// TODO: release g.blob
 	if blob == nil {
 		var err error
-		blob, err = lookup(ctx, g.lookupClient, g.hashKey)
+		blobs, err := lookup(ctx, g.lookupClient, []string{g.hashKey})
 		if err != nil {
 			return nil, err
 		}
+		blob = blobs[0]
 	}
 	switch blob.GetBlobType() {
 	case gomapb.FileBlob_FILE:
@@ -138,6 +151,7 @@
 			lookupClient: g.lookupClient,
 			hashKey:      g.hashKey,
 			meta:         blob,
+			allocated:    make([]byte, gomaInputBatchSize*file.FileChunkSize),
 		}, nil
 
 	case gomapb.FileBlob_FILE_UNSPECIFIED:
@@ -146,13 +160,16 @@
 	return nil, status.Errorf(codes.Internal, "bad file_blob type: %s: %v", g.hashKey, blob.GetBlobType())
 }
 
+const gomaInputBatchSize = 5
+
 type gomaInputReader struct {
 	ctx          context.Context
 	lookupClient lookupClient
 	hashKey      string
 	meta         *gomapb.FileBlob
 	i            int    // next index of hash key in meta.
-	buf          []byte // points meta hash_key[i-1]'s Content.
+	buf          []byte // points meta hash_key[prev_i:i]'s Content.
+	allocated    []byte // allocated buffer for buf.
 }
 
 func (r *gomaInputReader) Read(buf []byte) (int, error) {
@@ -160,15 +177,31 @@
 		if len(r.meta.HashKey) == r.i {
 			return 0, io.EOF
 		}
-		blob, err := lookup(r.ctx, r.lookupClient, r.meta.HashKey[r.i])
+		j := r.i + gomaInputBatchSize
+		if j > len(r.meta.HashKey) {
+			j = len(r.meta.HashKey)
+		}
+		blobs, err := lookup(r.ctx, r.lookupClient, r.meta.HashKey[r.i:j])
 		if err != nil {
-			return 0, status.Errorf(status.Code(err), "lookup chunk in FILE_META %s %d %s: %v", r.hashKey, r.i, r.meta.HashKey[r.i], err)
+			return 0, status.Errorf(status.Code(err), "lookup chunk in FILE_META %s %d:%d %s: %v", r.hashKey, r.i, j, r.meta.HashKey[r.i:j], err)
 		}
-		if blob.GetBlobType() != gomapb.FileBlob_FILE_CHUNK {
-			return 0, status.Errorf(codes.Internal, "lookup chunk in FILE_META %s %d %s: not FILE_CHUNK %v", r.hashKey, r.i, r.meta.HashKey[r.i], blob.GetBlobType())
+		for i, blob := range blobs {
+			if blob.GetBlobType() != gomapb.FileBlob_FILE_CHUNK {
+				return 0, status.Errorf(codes.Internal, "lookup chunk in FILE_META %s %d %s: not FILE_CHUNK %v", r.hashKey, r.i+i, r.meta.HashKey[r.i+i], blob.GetBlobType())
+			}
 		}
-		r.i++
-		r.buf = blob.Content
+		b := r.allocated
+		pos := 0
+		i0 := r.i
+		r.i = j
+		for i, blob := range blobs {
+			n := copy(b[pos:], blob.Content)
+			if n < len(blob.Content) {
+				return 0, status.Errorf(codes.Internal, "goma input buffer shortage %d written, len(blob.Content)=%d for %s %d %s", n, len(blob.Content), r.hashKey, i0+i, r.meta.HashKey[i0+i])
+			}
+			pos += n
+		}
+		r.buf = b[:pos]
 	}
 	n := copy(buf, r.buf)
 	r.buf = r.buf[n:]
diff --git a/remoteexec/gomaoutput.go b/remoteexec/gomaoutput.go
index 4f687d7..bb5dd68 100644
--- a/remoteexec/gomaoutput.go
+++ b/remoteexec/gomaoutput.go
@@ -11,15 +11,18 @@
 	"fmt"
 	"io"
 	"os"
+	"sync"
 
 	rpb "github.com/bazelbuild/remote-apis/build/bazel/remote/execution/v2"
 
 	"github.com/golang/protobuf/proto"
 	"go.opencensus.io/trace"
+	"golang.org/x/sync/errgroup"
 	bpb "google.golang.org/genproto/googleapis/bytestream"
 	"google.golang.org/grpc/codes"
 	"google.golang.org/grpc/status"
 
+	"go.chromium.org/goma/server/file"
 	"go.chromium.org/goma/server/log"
 	gomapb "go.chromium.org/goma/server/proto/api"
 	fpb "go.chromium.org/goma/server/proto/file"
@@ -81,7 +84,7 @@
 	g.gomaResp.Result.StderrBuffer = buf.Bytes()
 }
 
-func (g gomaOutput) outputFile(ctx context.Context, fname string, output *rpb.OutputFile) {
+func (g gomaOutput) outputFileHelper(ctx context.Context, fname string, output *rpb.OutputFile) (*gomapb.ExecResult_Output, error) {
 	var blob *gomapb.FileBlob
 	err := rpc.Retry{}.Do(ctx, func() error {
 		var err error
@@ -96,14 +99,115 @@
 		default:
 			logger.Errorf("goma blob for %s: %v", output.Path, err)
 		}
-		g.gomaResp.ErrorMessage = append(g.gomaResp.ErrorMessage, fmt.Sprintf("goma blob for %s: %v", output.Path, status.Code(err)))
-		return
+		return nil, fmt.Errorf("goma blob for %s: %v", output.Path, status.Code(err))
 	}
-	g.gomaResp.Result.Output = append(g.gomaResp.Result.Output, &gomapb.ExecResult_Output{
+	return &gomapb.ExecResult_Output{
 		Filename:     proto.String(fname),
 		Blob:         blob,
 		IsExecutable: proto.Bool(output.IsExecutable),
-	})
+	}, nil
+}
+
+func (g gomaOutput) outputFile(ctx context.Context, fname string, output *rpb.OutputFile) {
+	result, err := g.outputFileHelper(ctx, fname, output)
+	if err != nil {
+		g.gomaResp.ErrorMessage = append(g.gomaResp.ErrorMessage, err.Error())
+		return
+	}
+	g.gomaResp.Result.Output = append(g.gomaResp.Result.Output, result)
+}
+
+func (g gomaOutput) outputFilesConcurrent(ctx context.Context, outputs []*rpb.OutputFile, sema chan struct{}) {
+	var wg sync.WaitGroup
+	results := make([]*gomapb.ExecResult_Output, len(outputs))
+	errs := make([]error, len(outputs))
+
+	for i := range outputs {
+		wg.Add(1)
+		go func(i int) {
+			defer wg.Done()
+			sema <- struct{}{}
+			defer func() {
+				<-sema
+			}()
+
+			output := outputs[i]
+			results[i], errs[i] = g.outputFileHelper(ctx, output.Path, output)
+		}(i)
+	}
+	wg.Wait()
+
+	for _, result := range results {
+		if result != nil {
+			g.gomaResp.Result.Output = append(g.gomaResp.Result.Output, result)
+		}
+	}
+	for _, err := range errs {
+		if err != nil {
+			g.gomaResp.ErrorMessage = append(g.gomaResp.ErrorMessage, err.Error())
+		}
+	}
+}
+
+func toChunkedFileBlob(ctx context.Context, rd io.Reader, size int64, fs fpb.FileServiceClient) (*gomapb.FileBlob, error) {
+	const bufsize = file.LargeFileThreshold
+	in := bufio.NewReaderSize(rd, bufsize)
+	blob := &gomapb.FileBlob{
+		BlobType: gomapb.FileBlob_FILE_META.Enum(),
+		FileSize: proto.Int64(size),
+	}
+	buf := make([]byte, bufsize)
+	var offset int64
+	eof := false
+	for offset < size && !eof {
+		remain := size - offset
+		if remain < bufsize {
+			buf = buf[:remain]
+		}
+		n, err := io.ReadFull(in, buf)
+		if err != nil && err != io.EOF {
+			return nil, err
+		}
+		eof = err == io.EOF
+		var resp *gomapb.StoreFileResp
+		err = rpc.Retry{}.Do(ctx, func() error {
+			resp, err = fs.StoreFile(ctx, &gomapb.StoreFileReq{
+				Blob: []*gomapb.FileBlob{
+					{
+						BlobType: gomapb.FileBlob_FILE_CHUNK.Enum(),
+						Offset:   proto.Int64(offset),
+						Content:  buf[:n],
+						FileSize: proto.Int64(size),
+					},
+				},
+				RequesterInfo: requesterInfo(ctx),
+			})
+			return err
+		})
+		if err != nil {
+			return nil, fmt.Errorf("store blob failed offset=%d: %v", offset, err)
+		}
+		for _, hashKey := range resp.HashKey {
+			if hashKey == "" {
+				return nil, fmt.Errorf("store blob failed offset=%d", offset)
+			}
+			blob.HashKey = append(blob.HashKey, hashKey)
+		}
+		offset += int64(n)
+	}
+	if size != offset {
+		return nil, fmt.Errorf("size mismatch: digest size=%d, store size=%d", size, offset)
+	}
+	// EOF is only returned when there is no more input available at the beginning of a read,
+	// not at the end of the final non-empty read.
+	n, err := in.Read(make([]byte, 1))
+	if n != 0 {
+		return nil, fmt.Errorf("more bytes were read past end: %d", n)
+	}
+	if err != io.EOF {
+		return nil, fmt.Errorf("could not confirm EOF: %v", err)
+	}
+	return blob, nil
 }
 
 func (g gomaOutput) toFileBlob(ctx context.Context, output *rpb.OutputFile) (*gomapb.FileBlob, error) {
@@ -112,9 +216,7 @@
 
 	logger := log.FromContext(ctx)
 
-	const fileBlobChunkThresholdBytes = 2 * 1024 * 1024
-
-	if output.Digest.SizeBytes < fileBlobChunkThresholdBytes {
+	if output.Digest.SizeBytes < file.LargeFileThreshold {
 		// for single FileBlob.
 		var buf bytes.Buffer
 		err := cas.DownloadDigest(ctx, g.bs, &buf, g.instance, output.Digest)
@@ -128,9 +230,7 @@
 		}, nil
 	}
 
-	var in io.Reader
 	casErrCh := make(chan error, 1)
-	const bufsize = fileBlobChunkThresholdBytes
 	rd, wr, err := os.Pipe()
 	if err != nil {
 		return nil, err
@@ -149,63 +249,50 @@
 		wr.Close()
 		casErrCh <- err
 	}()
-	in = bufio.NewReaderSize(rd, bufsize)
-	blob := &gomapb.FileBlob{
-		BlobType: gomapb.FileBlob_FILE_META.Enum(),
-		FileSize: proto.Int64(output.Digest.SizeBytes),
+
+	blob, err := toChunkedFileBlob(ctx, rd, output.Digest.SizeBytes, g.gomaFile)
+	if err != nil {
+		return nil, fmt.Errorf("Failed to convert %v to chunked FileBlob: %v", output, err)
 	}
-	buf := make([]byte, bufsize)
-	var offset int64
-	for offset < output.Digest.SizeBytes {
-		remain := output.Digest.SizeBytes - offset
-		if remain < bufsize {
-			buf = buf[:remain]
-		}
-		n, err := io.ReadFull(in, buf)
-		if err != nil && err != io.EOF {
-			return nil, err
-		}
-		eof := err == io.EOF
-		var resp *gomapb.StoreFileResp
-		err = rpc.Retry{}.Do(ctx, func() error {
-			resp, err = g.gomaFile.StoreFile(ctx, &gomapb.StoreFileReq{
-				Blob: []*gomapb.FileBlob{
-					{
-						BlobType: gomapb.FileBlob_FILE_CHUNK.Enum(),
-						Offset:   proto.Int64(offset),
-						Content:  buf[:n],
-						FileSize: proto.Int64(output.Digest.SizeBytes),
-					},
-				},
-				RequesterInfo: requesterInfo(ctx),
-			})
-			return err
-		})
-		if err != nil {
-			logger.Warnf("store blob failed offset=%d for %v: %v", offset, output, err)
-			return nil, err
-		}
-		for _, hashKey := range resp.HashKey {
-			if hashKey == "" {
-				return nil, fmt.Errorf("store blob failed offset=%d for %v", offset, output)
-			}
-			blob.HashKey = append(blob.HashKey, hashKey)
-		}
-		offset += int64(n)
-		if eof && offset < output.Digest.SizeBytes {
-			break
-		}
-	}
-	if err := <-casErrCh; err != nil {
+	if err = <-casErrCh; err != nil {
 		return nil, err
 	}
-	if output.Digest.SizeBytes != offset {
-		return nil, fmt.Errorf("size mismatch: digest size=%d, store size=%d", output.Digest.SizeBytes, offset)
-	}
 	return blob, nil
 }
 
-func (g gomaOutput) outputDirectory(ctx context.Context, filepath clientFilePath, dname string, output *rpb.OutputDirectory) {
+func traverseTree(ctx context.Context, filepath clientFilePath, dname string, dir *rpb.Directory, ds *digest.Store) []*rpb.OutputFile {
+	logger := log.FromContext(ctx)
+	var result []*rpb.OutputFile
+	for _, f := range dir.Files {
+		fname := filepath.Join(dname, f.Name)
+		result = append(result, &rpb.OutputFile{
+			Path:         fname,
+			Digest:       f.Digest,
+			IsExecutable: f.IsExecutable,
+		})
+	}
+	for _, d := range dir.Directories {
+		subdname := filepath.Join(dname, d.Name)
+		db, found := ds.Get(d.Digest)
+		if !found {
+			logger.Errorf("no dir for %s %s", subdname, d.Digest)
+			continue
+		}
+		subdir := &rpb.Directory{}
+		err := datasource.ReadProto(ctx, db, subdir)
+		if err != nil {
+			logger.Errorf("bad dir proto for %s %s: %v", subdname, d.Digest, err)
+			continue
+		}
+		result = append(result, traverseTree(ctx, filepath, subdname, subdir, ds)...)
+	}
+	if len(dir.Symlinks) > 0 {
+		logger.Errorf("symlinks exists in output dir %s: %q", dname, dir.Symlinks)
+	}
+	return result
+}
+
+func (g gomaOutput) outputDirectory(ctx context.Context, filepath clientFilePath, dname string, output *rpb.OutputDirectory, sema chan struct{}) {
 	logger := log.FromContext(ctx)
 	if output.TreeDigest == nil {
 		logger.Warnf("no tree digest in %s", dname)
@@ -238,35 +325,91 @@
 		}
 		ds.Set(d)
 	}
-	g.traverseTree(ctx, filepath, dname, tree.Root, ds)
+	outputFiles := traverseTree(ctx, filepath, dname, tree.Root, ds)
+	g.outputFilesConcurrent(ctx, outputFiles, sema)
 }
 
-func (g gomaOutput) traverseTree(ctx context.Context, filepath clientFilePath, dname string, dir *rpb.Directory, ds *digest.Store) {
-	logger := log.FromContext(ctx)
-	for _, f := range dir.Files {
-		fname := filepath.Join(dname, f.Name)
-		g.outputFile(ctx, fname, &rpb.OutputFile{
-			Path:         fname,
-			Digest:       f.Digest,
-			IsExecutable: f.IsExecutable,
+// reduceRespSize attempts to reduce the encoded size of `g.gomaResp` to under `byteLimit`.
+// It replaces all file blobs with blob_type=FILE (embedded data) with blob_type=FILE_REF
+// (`content` in FileServer, `blob_type`=FILE). With each replaced blob, the response loses
+// `sizeof(content)` bytes and gains `sizeof(hash_key)` bytes. This will result in a net
+// reduction in encoded size as long as `sizeof(content)` > `sizeof(hash_key)`.
+//
+// See description of FileBlob for more information:
+// https://chromium.googlesource.com/infra/goma/client/+/bd9711495c9357eead845f0ae2d4eef92494c6d5/lib/goma_data.proto#17
+func (g gomaOutput) reduceRespSize(ctx context.Context, byteLimit int, sema chan struct{}) error {
+	origSize := proto.Size(g.gomaResp)
+	if origSize <= byteLimit {
+		return nil
+	}
+
+	toStoredFileBlob := func(ctx context.Context, input []byte, fs fpb.FileServiceClient) (*gomapb.FileBlob, error) {
+		size := int64(len(input))
+		blob := &gomapb.FileBlob{
+			BlobType: gomapb.FileBlob_FILE_REF.Enum(),
+			FileSize: proto.Int64(size),
+		}
+		var resp *gomapb.StoreFileResp
+		var err error
+		err = rpc.Retry{}.Do(ctx, func() error {
+			blob := &gomapb.FileBlob{
+				BlobType: gomapb.FileBlob_FILE.Enum(),
+				Content:  input,
+				FileSize: proto.Int64(size),
+			}
+			resp, err = fs.StoreFile(ctx, &gomapb.StoreFileReq{
+				Blob:          []*gomapb.FileBlob{blob},
+				RequesterInfo: requesterInfo(ctx),
+			})
+			return err
+		})
+		if err != nil {
+			return nil, fmt.Errorf("store blob failed: %v", err)
+		}
+		if len(resp.HashKey) != 1 {
+			return nil, fmt.Errorf("store blob got len(resp.HashKey)=%d, want=1", len(resp.HashKey))
+		}
+		if resp.HashKey[0] == "" {
+			return nil, fmt.Errorf("store blob failed with empty hash key")
+		}
+		blob.HashKey = resp.HashKey
+		return blob, nil
+	}
+
+	output := g.gomaResp.Result.Output
+	eg, ctx := errgroup.WithContext(ctx)
+	// For simplicity, store all blobs in FileServer rather than worrying about which ones to
+	// store.
+	// TODO: We can optimize this later.
+	for _, out := range output {
+		out := out
+		blob := out.Blob
+		if blob.GetBlobType() != gomapb.FileBlob_FILE {
+			continue
+		}
+		eg.Go(func() error {
+			sema <- struct{}{}
+			defer func() {
+				<-sema
+			}()
+
+			newBlob, err := toStoredFileBlob(ctx, blob.Content, g.gomaFile)
+			if err != nil {
+				return err
+			}
+			out.Blob = newBlob
+			return nil
 		})
 	}
-	for _, d := range dir.Directories {
-		subdname := filepath.Join(dname, d.Name)
-		db, found := ds.Get(d.Digest)
-		if !found {
-			logger.Errorf("no dir for %s %s", subdname, d.Digest)
-			continue
-		}
-		subdir := &rpb.Directory{}
-		err := datasource.ReadProto(ctx, db, subdir)
-		if err != nil {
-			logger.Errorf("bad dir proto for %s %s: %v", subdname, d.Digest, err)
-			continue
-		}
-		g.traverseTree(ctx, filepath, subdname, subdir, ds)
+
+	if err := eg.Wait(); err != nil {
+		return err
 	}
-	if len(dir.Symlinks) > 0 {
-		logger.Errorf("symlinks exists in output dir %s: %q", dname, dir.Symlinks)
+
+	// The result could still be too big if there are many FILE_REF blobs.
+	newSize := proto.Size(g.gomaResp)
+	if newSize > byteLimit {
+		return fmt.Errorf("new resp size: got=%d, want<=%d", newSize, byteLimit)
 	}
+	return nil
 }
diff --git a/remoteexec/gomaoutput_test.go b/remoteexec/gomaoutput_test.go
index 4dc4681..10707a4 100644
--- a/remoteexec/gomaoutput_test.go
+++ b/remoteexec/gomaoutput_test.go
@@ -6,7 +6,9 @@
 
 import (
 	"context"
+	"fmt"
 	"path"
+	"strings"
 	"testing"
 
 	rpb "github.com/bazelbuild/remote-apis/build/bazel/remote/execution/v2"
@@ -19,6 +21,284 @@
 	"go.chromium.org/goma/server/remoteexec/digest"
 )
 
+type goutTestFile struct {
+	name string
+	data digest.Data
+	node *rpb.FileNode
+}
+
+func makeFileNode(name string) *goutTestFile {
+	// Contents are the same as the filename.
+	d := digest.Bytes(name, []byte(name))
+	return &goutTestFile{
+		name: name,
+		data: d,
+		node: &rpb.FileNode{
+			Name:   name,
+			Digest: d.Digest(),
+		},
+	}
+}
+
+func makeDirNode(t *testing.T, name string, dir *rpb.Directory) *rpb.DirectoryNode {
+	dirdata, err := digest.Proto(dir)
+	if err != nil {
+		t.Fatalf("dir %s: %v", name, err)
+	}
+	return &rpb.DirectoryNode{
+		Name:   name,
+		Digest: dirdata.Digest(),
+	}
+}
+
+func makeFileBlob(contents string) *gomapb.FileBlob {
+	return &gomapb.FileBlob{
+		BlobType: gomapb.FileBlob_FILE.Enum(),
+		Content:  []byte(contents),
+		FileSize: proto.Int64(int64(len(contents))),
+	}
+}
+
+func TestToFileBlob(t *testing.T) {
+	ctx := context.Background()
+
+	cluster := &fakeCluster{
+		rbe: newFakeRBE(),
+	}
+	err := cluster.setup(ctx, cluster.rbe.instancePrefix)
+	if err != nil {
+		t.Fatal(err)
+	}
+	defer cluster.teardown()
+
+	f1 := makeFileNode("file1")
+	f2 := makeFileNode("file2")
+	f3 := makeFileNode("file3")
+	cluster.rbe.cas.Set(f1.data)
+	cluster.rbe.cas.Set(f2.data)
+	cluster.rbe.cas.Set(f3.data)
+
+	gout := gomaOutput{
+		bs:       cluster.adapter.Client,
+		instance: path.Join(cluster.rbe.instancePrefix, "default_instance"),
+		gomaFile: cluster.adapter.GomaFile,
+	}
+
+	var blobs []*gomapb.FileBlob
+	for _, f := range []*goutTestFile{f1, f2, f3} {
+		blob, err := gout.toFileBlob(ctx, &rpb.OutputFile{
+			Path:   f.name,
+			Digest: f.node.Digest,
+		})
+		if err != nil {
+			t.Errorf("toFileBlob returned err: %v", err)
+			continue
+		}
+		blobs = append(blobs, blob)
+	}
+
+	var want []*gomapb.FileBlob
+	for _, f := range []*goutTestFile{f1, f2, f3} {
+		want = append(want, makeFileBlob(f.name))
+	}
+	if diff := cmp.Diff(want, blobs, cmp.Comparer(proto.Equal)); diff != "" {
+		t.Errorf("output diff -want +got:\n%s", diff)
+	}
+}
+
+func TestToFileBlobLarge(t *testing.T) {
+	ctx := context.Background()
+
+	cluster := &fakeCluster{
+		rbe: newFakeRBE(),
+	}
+	err := cluster.setup(ctx, cluster.rbe.instancePrefix)
+	if err != nil {
+		t.Fatal(err)
+	}
+	defer cluster.teardown()
+
+	gout := gomaOutput{
+		bs:       cluster.adapter.Client,
+		instance: path.Join(cluster.rbe.instancePrefix, "default_instance"),
+		gomaFile: cluster.adapter.GomaFile,
+	}
+
+	mibSize := int64(1024 * 1024)
+	bigData := digest.Bytes("fbig", make([]byte, 5*mibSize))
+	fBig := &goutTestFile{
+		name: "fbig",
+		data: bigData,
+		node: &rpb.FileNode{
+			Name:   "fbig",
+			Digest: bigData.Digest(),
+		},
+	}
+	cluster.rbe.cas.Set(fBig.data)
+
+	blob, err := gout.toFileBlob(ctx, &rpb.OutputFile{
+		Path:   fBig.name,
+		Digest: fBig.node.Digest,
+	})
+	if err != nil {
+		t.Errorf("toFileBlob returned err: %v", err)
+	}
+
+	var chunkBlobs []*gomapb.FileBlob
+	for i, hk := range blob.HashKey {
+		// Look up one blob at a time to avoid exceeding the 4MiB gRPC response size limit.
+		resp, err := gout.gomaFile.LookupFile(ctx, &gomapb.LookupFileReq{
+			HashKey: []string{hk},
+		})
+		if err != nil {
+			t.Errorf("gomaFile.LookupFile(HashKey[%d]=%s) returned err: %v", i, hk, err)
+			continue
+		}
+		if len(resp.Blob) != 1 {
+			t.Errorf("gomaFile.LookupFile(HashKey[%d]=%s) returned %d blobs, expected 1", i, hk, len(resp.Blob))
+			continue
+		}
+		chunkBlobs = append(chunkBlobs, resp.Blob[0])
+	}
+
+	wantBlob := &gomapb.FileBlob{
+		BlobType: gomapb.FileBlob_FILE_META.Enum(),
+		FileSize: proto.Int64(5 * mibSize),
+		HashKey:  blob.HashKey, // These don't need to be checked.
+	}
+	if diff := cmp.Diff(wantBlob, blob, cmp.Comparer(proto.Equal)); diff != "" {
+		t.Errorf("output diff -want +got:\n%s", diff)
+	}
+
+	wantChunkBlobs := []*gomapb.FileBlob{
+		{
+			BlobType: gomapb.FileBlob_FILE_CHUNK.Enum(),
+			Offset:   proto.Int64(0),
+			Content:  make([]byte, 2*mibSize),
+			FileSize: proto.Int64(5 * mibSize),
+		}, {
+			BlobType: gomapb.FileBlob_FILE_CHUNK.Enum(),
+			Offset:   proto.Int64(2 * mibSize),
+			Content:  make([]byte, 2*mibSize),
+			FileSize: proto.Int64(5 * mibSize),
+		}, {
+			BlobType: gomapb.FileBlob_FILE_CHUNK.Enum(),
+			Offset:   proto.Int64(4 * mibSize),
+			Content:  make([]byte, mibSize),
+			FileSize: proto.Int64(5 * mibSize),
+		},
+	}
+	if diff := cmp.Diff(wantChunkBlobs, chunkBlobs, cmp.Comparer(proto.Equal)); diff != "" {
+		t.Errorf("output diff -want +got:\n%s", diff)
+	}
+}
+
+func TestOutputFile(t *testing.T) {
+	ctx := context.Background()
+
+	cluster := &fakeCluster{
+		rbe: newFakeRBE(),
+	}
+	err := cluster.setup(ctx, cluster.rbe.instancePrefix)
+	if err != nil {
+		t.Fatal(err)
+	}
+	defer cluster.teardown()
+
+	f1 := makeFileNode("file1")
+	f2 := makeFileNode("file2")
+	f3 := makeFileNode("file3")
+	cluster.rbe.cas.Set(f1.data)
+	cluster.rbe.cas.Set(f2.data)
+	cluster.rbe.cas.Set(f3.data)
+
+	gout := gomaOutput{
+		gomaResp: &gomapb.ExecResp{
+			Result: &gomapb.ExecResult{},
+		},
+		bs:       cluster.adapter.Client,
+		instance: path.Join(cluster.rbe.instancePrefix, "default_instance"),
+		gomaFile: cluster.adapter.GomaFile,
+	}
+
+	for _, f := range []*goutTestFile{f1, f2, f3} {
+		gout.outputFile(ctx, f.name, &rpb.OutputFile{
+			Path:   f.name,
+			Digest: f.node.Digest,
+		})
+	}
+
+	if len(gout.gomaResp.ErrorMessage) > 0 {
+		t.Errorf("resp errorMessage %q; want no error", gout.gomaResp.ErrorMessage)
+	}
+	var want []*gomapb.ExecResult_Output
+	for _, f := range []*goutTestFile{f1, f2, f3} {
+		want = append(want, &gomapb.ExecResult_Output{
+			Filename:     proto.String(f.name),
+			Blob:         makeFileBlob(f.name),
+			IsExecutable: proto.Bool(false),
+		})
+	}
+	if diff := cmp.Diff(want, gout.gomaResp.Result.Output, cmp.Comparer(proto.Equal)); diff != "" {
+		t.Errorf("output diff -want +got:\n%s", diff)
+	}
+}
+
+func TestOutputFilesConcurrent(t *testing.T) {
+	ctx := context.Background()
+
+	cluster := &fakeCluster{
+		rbe: newFakeRBE(),
+	}
+	err := cluster.setup(ctx, cluster.rbe.instancePrefix)
+	if err != nil {
+		t.Fatal(err)
+	}
+	defer cluster.teardown()
+
+	var files []*goutTestFile
+	for i := 0; i < 1000; i++ {
+		f := makeFileNode(fmt.Sprintf("file%d", i))
+		files = append(files, f)
+		cluster.rbe.cas.Set(f.data)
+	}
+
+	gout := gomaOutput{
+		gomaResp: &gomapb.ExecResp{
+			Result: &gomapb.ExecResult{},
+		},
+		bs:       cluster.adapter.Client,
+		instance: path.Join(cluster.rbe.instancePrefix, "default_instance"),
+		gomaFile: cluster.adapter.GomaFile,
+	}
+
+	var outputFiles []*rpb.OutputFile
+	for _, f := range files {
+		outputFiles = append(outputFiles, &rpb.OutputFile{
+			Path:   f.name,
+			Digest: f.node.Digest,
+		})
+	}
+
+	sema := make(chan struct{}, 20)
+	gout.outputFilesConcurrent(ctx, outputFiles, sema)
+
+	if len(gout.gomaResp.ErrorMessage) > 0 {
+		t.Errorf("resp errorMessage %q; want no error", gout.gomaResp.ErrorMessage)
+	}
+	var want []*gomapb.ExecResult_Output
+	for _, f := range files {
+		want = append(want, &gomapb.ExecResult_Output{
+			Filename:     proto.String(f.name),
+			Blob:         makeFileBlob(f.name),
+			IsExecutable: proto.Bool(false),
+		})
+	}
+	if diff := cmp.Diff(want, gout.gomaResp.Result.Output, cmp.Comparer(proto.Equal)); diff != "" {
+		t.Errorf("output diff -want +got:\n%s", diff)
+	}
+}
+
 func TestOutputDirectory(t *testing.T) {
 	ctx := context.Background()
 
@@ -31,56 +311,27 @@
 	}
 	defer cluster.teardown()
 
-	f1 := digest.Bytes("file1", []byte("file1"))
-	f2 := digest.Bytes("file2", []byte("file2"))
-	f3 := digest.Bytes("file3", []byte("file3"))
-	cluster.rbe.cas.Set(f1)
-	cluster.rbe.cas.Set(f2)
-	cluster.rbe.cas.Set(f3)
+	f1 := makeFileNode("file1")
+	f2 := makeFileNode("file2")
+	f3 := makeFileNode("file3")
+	cluster.rbe.cas.Set(f1.data)
+	cluster.rbe.cas.Set(f2.data)
+	cluster.rbe.cas.Set(f3.data)
 	dir2 := &rpb.Directory{
-		Files: []*rpb.FileNode{
-			{
-				Name:   "file3",
-				Digest: f3.Digest(),
-			},
-		},
-	}
-	dir2data, err := digest.Proto(dir2)
-	if err != nil {
-		t.Fatalf("dir2: %v", err)
+		Files: []*rpb.FileNode{f3.node},
 	}
 	dir1 := &rpb.Directory{
-		Files: []*rpb.FileNode{
-			{
-				Name:   "file2",
-				Digest: f2.Digest(),
-			},
-		},
+		Files: []*rpb.FileNode{f2.node},
 		Directories: []*rpb.DirectoryNode{
-			{
-				Name:   "dir2",
-				Digest: dir2data.Digest(),
-			},
+			makeDirNode(t, "dir2", dir2),
 		},
 	}
-	dir1data, err := digest.Proto(dir1)
-	if err != nil {
-		t.Fatalf("dir1: %v", err)
-	}
 
 	td, err := cluster.rbe.setProto(ctx, &rpb.Tree{
 		Root: &rpb.Directory{
-			Files: []*rpb.FileNode{
-				{
-					Name:   "file1",
-					Digest: f1.Digest(),
-				},
-			},
+			Files: []*rpb.FileNode{f1.node},
 			Directories: []*rpb.DirectoryNode{
-				{
-					Name:   "dir1",
-					Digest: dir1data.Digest(),
-				},
+				makeDirNode(t, "dir1", dir1),
 			},
 		},
 		Children: []*rpb.Directory{
@@ -102,40 +353,29 @@
 		gomaFile: cluster.adapter.GomaFile,
 	}
 
+	sema := make(chan struct{}, 2)
 	gout.outputDirectory(ctx, filepath, "out", &rpb.OutputDirectory{
 		Path:       "out",
 		TreeDigest: td,
-	})
+	}, sema)
 
 	if len(gout.gomaResp.ErrorMessage) > 0 {
 		t.Errorf("resp errorMessage %q; want no error", gout.gomaResp.ErrorMessage)
 	}
 	want := []*gomapb.ExecResult_Output{
 		{
-			Filename: proto.String("out/file1"),
-			Blob: &gomapb.FileBlob{
-				BlobType: gomapb.FileBlob_FILE.Enum(),
-				Content:  []byte("file1"),
-				FileSize: proto.Int64(int64(len("file1"))),
-			},
+			Filename:     proto.String("out/file1"),
+			Blob:         makeFileBlob("file1"),
 			IsExecutable: proto.Bool(false),
 		},
 		{
-			Filename: proto.String("out/dir1/file2"),
-			Blob: &gomapb.FileBlob{
-				BlobType: gomapb.FileBlob_FILE.Enum(),
-				Content:  []byte("file2"),
-				FileSize: proto.Int64(int64(len("file2"))),
-			},
+			Filename:     proto.String("out/dir1/file2"),
+			Blob:         makeFileBlob("file2"),
 			IsExecutable: proto.Bool(false),
 		},
 		{
-			Filename: proto.String("out/dir1/dir2/file3"),
-			Blob: &gomapb.FileBlob{
-				BlobType: gomapb.FileBlob_FILE.Enum(),
-				Content:  []byte("file3"),
-				FileSize: proto.Int64(int64(len("file3"))),
-			},
+			Filename:     proto.String("out/dir1/dir2/file3"),
+			Blob:         makeFileBlob("file3"),
 			IsExecutable: proto.Bool(false),
 		},
 	}
@@ -143,3 +383,284 @@
 		t.Errorf("output diff -want +got:\n%s", diff)
 	}
 }
+
+func TestTraverseTree(t *testing.T) {
+	ctx := context.Background()
+
+	cluster := &fakeCluster{
+		rbe: newFakeRBE(),
+	}
+	err := cluster.setup(ctx, cluster.rbe.instancePrefix)
+	if err != nil {
+		t.Fatal(err)
+	}
+	defer cluster.teardown()
+
+	f1 := makeFileNode("file1")
+	f2 := makeFileNode("file2")
+	f3 := makeFileNode("file3")
+	cluster.rbe.cas.Set(f1.data)
+	cluster.rbe.cas.Set(f2.data)
+	cluster.rbe.cas.Set(f3.data)
+	dir2 := &rpb.Directory{
+		Files: []*rpb.FileNode{f3.node},
+	}
+	dir1 := &rpb.Directory{
+		Files: []*rpb.FileNode{f2.node},
+		Directories: []*rpb.DirectoryNode{
+			makeDirNode(t, "dir2", dir2),
+		},
+	}
+	tree := &rpb.Tree{
+		Root: &rpb.Directory{
+			Files: []*rpb.FileNode{f1.node},
+			Directories: []*rpb.DirectoryNode{
+				makeDirNode(t, "dir1", dir1),
+			},
+		},
+		Children: []*rpb.Directory{
+			dir1,
+			dir2,
+		},
+	}
+
+	ds := digest.NewStore()
+	for _, c := range tree.Children {
+		d, err := digest.Proto(c)
+		if err != nil {
+			t.Errorf("digest for children %s: %v", c, err)
+			continue
+		}
+		ds.Set(d)
+	}
+
+	var filepath posixpath.FilePath
+
+	result := traverseTree(ctx, filepath, "out", tree.Root, ds)
+
+	want := []*rpb.OutputFile{
+		{
+			Path:   "out/file1",
+			Digest: f1.node.Digest,
+		}, {
+			Path:   "out/dir1/file2",
+			Digest: f2.node.Digest,
+		}, {
+			Path:   "out/dir1/dir2/file3",
+			Digest: f3.node.Digest,
+		},
+	}
+	if diff := cmp.Diff(want, result, cmp.Comparer(proto.Equal)); diff != "" {
+		t.Errorf("output diff -want +got:\n%s", diff)
+	}
+}
+
+func TestReduceRespSize(t *testing.T) {
+	ctx := context.Background()
+
+	contents := []string{
+		strings.Repeat("###", 10),
+		strings.Repeat("###", 20),
+		strings.Repeat("###", 30),
+		strings.Repeat("###", 40),
+		strings.Repeat("###", 50),
+		strings.Repeat("###", 60),
+		strings.Repeat("###", 70),
+		strings.Repeat("###", 80),
+		strings.Repeat("###", 90),
+		strings.Repeat("###", 100),
+	}
+
+	var outputs []*gomapb.ExecResult_Output
+	for i, content := range contents {
+		outputs = append(outputs, &gomapb.ExecResult_Output{
+			Filename:     proto.String(fmt.Sprintf("file%d", i)),
+			Blob:         makeFileBlob(content),
+			IsExecutable: proto.Bool(false),
+		})
+	}
+	convertToStored := func(in *gomapb.ExecResult_Output) *gomapb.ExecResult_Output {
+		return &gomapb.ExecResult_Output{
+			Filename: in.Filename,
+			Blob: &gomapb.FileBlob{
+				BlobType: gomapb.FileBlob_FILE_REF.Enum(),
+				FileSize: proto.Int64(in.Blob.GetFileSize()),
+				// Hashkey will be checked separately.
+			},
+			IsExecutable: in.IsExecutable,
+		}
+	}
+
+	makeResp := func(outputs []*gomapb.ExecResult_Output) *gomapb.ExecResp {
+		return &gomapb.ExecResp{
+			Result: &gomapb.ExecResult{
+				ExitStatus:   proto.Int32(1234),
+				StdoutBuffer: []byte("Hello"),
+				StderrBuffer: []byte("Goodbye"),
+				Output:       outputs,
+			},
+		}
+	}
+	respSizeAllOutputs := proto.Size(makeResp(outputs))
+
+	metaOutput := &gomapb.ExecResult_Output{
+		Filename: proto.String("bigfile"),
+		Blob: &gomapb.FileBlob{
+			BlobType: gomapb.FileBlob_FILE_META.Enum(),
+			FileSize: proto.Int64(5 * 1024 * 1024),
+			HashKey:  []string{"0123", "4567", "89ab"},
+		},
+		IsExecutable: proto.Bool(false),
+	}
+
+	for _, tc := range []struct {
+		desc       string
+		output     []*gomapb.ExecResult_Output
+		byteLimit  int
+		wantOutput []*gomapb.ExecResult_Output
+		wantErr    bool
+	}{
+		{
+			desc:      "empty",
+			output:    []*gomapb.ExecResult_Output{},
+			byteLimit: 1,
+			wantErr:   true,
+		}, {
+			desc:       "no change in size",
+			output:     outputs,
+			byteLimit:  respSizeAllOutputs,
+			wantOutput: outputs,
+		}, {
+			desc:       "limit too high",
+			output:     outputs,
+			byteLimit:  respSizeAllOutputs * 2,
+			wantOutput: outputs,
+		}, {
+			desc:      "limit too low",
+			output:    outputs,
+			byteLimit: 1,
+			wantErr:   true,
+		}, {
+			desc: "blob smaller than hashkey",
+			output: []*gomapb.ExecResult_Output{
+				outputs[0],
+				outputs[1], // The largest blob has size 3 * 20 = 60, < hashkey size=64
+			},
+			byteLimit: proto.Size(makeResp([]*gomapb.ExecResult_Output{
+				outputs[0],
+				outputs[1],
+			})) - 1,
+			wantErr: true,
+		}, {
+			desc:      "remove all blobs",
+			byteLimit: respSizeAllOutputs - 600,
+			output:    outputs,
+			wantOutput: []*gomapb.ExecResult_Output{
+				convertToStored(outputs[0]),
+				convertToStored(outputs[1]),
+				convertToStored(outputs[2]),
+				convertToStored(outputs[3]),
+				convertToStored(outputs[4]),
+				convertToStored(outputs[5]),
+				convertToStored(outputs[6]),
+				convertToStored(outputs[7]),
+				convertToStored(outputs[8]),
+				convertToStored(outputs[9]),
+			},
+		}, {
+			desc:      "skip non-FILE",
+			byteLimit: respSizeAllOutputs - 600,
+			output: []*gomapb.ExecResult_Output{
+				outputs[0],
+				outputs[1],
+				outputs[2],
+				outputs[3],
+				outputs[4],
+				metaOutput,
+				outputs[5],
+				outputs[6],
+				outputs[7],
+				outputs[8],
+				outputs[9],
+			},
+			wantOutput: []*gomapb.ExecResult_Output{
+				convertToStored(outputs[0]),
+				convertToStored(outputs[1]),
+				convertToStored(outputs[2]),
+				convertToStored(outputs[3]),
+				convertToStored(outputs[4]),
+				metaOutput,
+				convertToStored(outputs[5]),
+				convertToStored(outputs[6]),
+				convertToStored(outputs[7]),
+				convertToStored(outputs[8]),
+				convertToStored(outputs[9]),
+			},
+		},
+	} {
+		t.Run(tc.desc, func(t *testing.T) {
+			cluster := &fakeCluster{}
+			err := cluster.setup(ctx, "")
+			if err != nil {
+				t.Fatal(err)
+			}
+			defer cluster.teardown()
+
+			gout := gomaOutput{
+				gomaFile: cluster.adapter.GomaFile,
+				// Avoid changing the original gomapb.ExecResult_Output values during test.
+				gomaResp: proto.Clone(makeResp(tc.output)).(*gomapb.ExecResp),
+			}
+			sema := make(chan struct{}, 3)
+			err = gout.reduceRespSize(ctx, tc.byteLimit, sema)
+			result := gout.gomaResp.Result
+
+			if err != nil && !tc.wantErr {
+				t.Errorf("got err=%v, want nil", err)
+				return
+			}
+			if tc.wantErr {
+				if err == nil {
+					t.Errorf("got err=nil, want !nil")
+				}
+				return
+			}
+
+			if len(result.Output) != len(tc.wantOutput) {
+				t.Errorf("got len(result.Output)=%d, want %d", len(result.Output), len(tc.wantOutput))
+				return
+			}
+			if proto.Size(result) > tc.byteLimit {
+				t.Errorf("got size=%d, want<=%d", proto.Size(result), tc.byteLimit)
+			}
+
+			// Since we don't have expected hash keys, check them separately and remove them from the result.
+			for i, resOut := range result.Output {
+				if resOut.Blob.GetBlobType() != gomapb.FileBlob_FILE_REF {
+					continue
+				}
+				hks := resOut.Blob.HashKey
+				if len(hks) != 1 {
+					t.Errorf("len(hks)=%d, want=1", len(hks))
+				}
+				resOut.Blob.HashKey = nil
+				resp, err := gout.gomaFile.LookupFile(ctx, &gomapb.LookupFileReq{HashKey: hks})
+				if err != nil {
+					t.Errorf("LookupFile failed: %v", err)
+				}
+				// Make sure the stored blob is the same as the original blob.
+				wantBlobs := []*gomapb.FileBlob{tc.output[i].Blob}
+				if !cmp.Equal(resp.Blob, wantBlobs, cmp.Comparer(proto.Equal)) {
+					t.Errorf("at i=%d, resp.Blob=%v, want=%v", i, resp.Blob[0], wantBlobs)
+				}
+			}
+
+			for i, resOut := range result.Output {
+				wantOut := tc.wantOutput[i]
+				if !cmp.Equal(resOut, wantOut, cmp.Comparer(proto.Equal)) {
+					t.Errorf("at i=%d, resOut=%v, want=%v", i, resOut, wantOut)
+				}
+			}
+		})
+	}
+}