diff --git a/internal/grpc/example/client/BUILD.bazel b/internal/grpc/example/client/BUILD.bazel index f4be2a8f713..a5f7a572640 100644 --- a/internal/grpc/example/client/BUILD.bazel +++ b/internal/grpc/example/client/BUILD.bazel @@ -2,15 +2,20 @@ load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library") go_library( name = "client_lib", - srcs = ["main.go"], + srcs = [ + "main.go", + "retry.go", + ], embedsrcs = ["sun.svg"], importpath = "github.com/sourcegraph/sourcegraph/internal/grpc/example/client", visibility = ["//visibility:private"], deps = [ "//internal/grpc/defaults", "//internal/grpc/example/weather/v1:weather", + "//internal/grpc/retry", "//internal/grpc/streamio", "@com_github_sourcegraph_log//:log", + "@org_golang_google_grpc//:go_default_library", "@org_golang_google_grpc//codes", "@org_golang_google_grpc//status", ], diff --git a/internal/grpc/example/client/main.go b/internal/grpc/example/client/main.go index c8b20f6da34..003c38e85e0 100644 --- a/internal/grpc/example/client/main.go +++ b/internal/grpc/example/client/main.go @@ -10,11 +10,13 @@ import ( "time" "github.com/sourcegraph/log" - "github.com/sourcegraph/sourcegraph/internal/grpc/defaults" - pb "github.com/sourcegraph/sourcegraph/internal/grpc/example/weather/v1" - "github.com/sourcegraph/sourcegraph/internal/grpc/streamio" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" + + "github.com/sourcegraph/sourcegraph/internal/grpc/defaults" + pb "github.com/sourcegraph/sourcegraph/internal/grpc/example/weather/v1" + "github.com/sourcegraph/sourcegraph/internal/grpc/retry" + "github.com/sourcegraph/sourcegraph/internal/grpc/streamio" ) // From https://commons.wikimedia.org/w/index.php?title=File:Sun-soleil.svg&oldid=456041378 @@ -40,13 +42,21 @@ func main() { defer conn.Close() client := pb.NewWeatherServiceClient(conn) + client = &automaticRetryClient{base: client} // Wrap the client with our automatic retry logic // Demonstrate Unary RPCs (single request, single response): Get weather for a specific location // // This example demonstrates a basic client server RPC, as well as error handling tactics. { + + // You can pass call options to each unique RPC invocation. + // In this example, we use this to demonstrate the automatic retry logic. + retryCallback := func(ctx context.Context, attempt uint, err error) { + logger.Info("The call to GetCurrentWeather for New York failed. Retrying...", log.Uint("attempt", attempt), log.Error(err)) + } + // Normal case - weather, err := client.GetCurrentWeather(context.Background(), &pb.GetCurrentWeatherRequest{Location: "New York"}) + weather, err := client.GetCurrentWeather(context.Background(), &pb.GetCurrentWeatherRequest{Location: "New York"}, retry.WithOnRetryCallback(retryCallback)) if err != nil { logger.Fatal("Could not get weather", log.Error(err)) } diff --git a/internal/grpc/example/client/retry.go b/internal/grpc/example/client/retry.go new file mode 100644 index 00000000000..c5f218a774f --- /dev/null +++ b/internal/grpc/example/client/retry.go @@ -0,0 +1,65 @@ +package main + +import ( + "context" + + "google.golang.org/grpc" + + "github.com/sourcegraph/sourcegraph/internal/grpc/defaults" + proto "github.com/sourcegraph/sourcegraph/internal/grpc/example/weather/v1" +) + +// automaticRetryClient is a convenience wrapper around a base proto.WeatherServiceClient that automatically retries +// idempotent ("safe") methods in accordance to the policy defined in internal/grpc/defaults.RetryPolicy. +// +// Read the implementation of this type for more details on what kinds of RPCs are automatically retried (and why). +// +// Callers are free to override the default retry behavior by proving their own grpc.CallOptions when invoking the RPC. +// (example: providing retry.WithMax(0) will disable retries even when invoking GetCurrentWeather - which is idempotent). +type automaticRetryClient struct { + base proto.WeatherServiceClient +} + +// Unsupported methods. + +func (a *automaticRetryClient) UploadWeatherData(ctx context.Context, opts ...grpc.CallOption) (proto.WeatherService_UploadWeatherDataClient, error) { + // UploadWeatherData is a client streaming method, which isn't supported by our automatic retry logic. + // Trying to use our automatic retry logic with this method immediately returns the following error (fail fast): + // + // code = Unimplemented desc = grpc_retry: cannot retry on ClientStreams, set grpc_retry.Disable() + return a.base.UploadWeatherData(ctx, opts...) +} + +func (a *automaticRetryClient) RealTimeWeather(ctx context.Context, opts ...grpc.CallOption) (proto.WeatherService_RealTimeWeatherClient, error) { + // RealTimeWeather is a bidirectional streaming method, which isn't supported by our automatic retry logic. + // Trying to use our automatic retry logic with this method immediately returns the following error (fail fast): + // + // code = Unimplemented desc = grpc_retry: cannot retry on ClientStreams, set grpc_retry.Disable() + return a.base.RealTimeWeather(ctx, opts...) +} + +// Non idempotent methods. + +func (a *automaticRetryClient) UploadWeatherPhoto(ctx context.Context, opts ...grpc.CallOption) (proto.WeatherService_UploadWeatherPhotoClient, error) { + // UploadWeatherPhoto's RPC documentation states that it is not idempotent because it doesn't do any deduplication. + return a.base.UploadWeatherPhoto(ctx, opts...) +} + +// Idempotent methods. + +func (a *automaticRetryClient) GetCurrentWeather(ctx context.Context, in *proto.GetCurrentWeatherRequest, opts ...grpc.CallOption) (*proto.GetCurrentWeatherResponse, error) { + opts = append(defaults.RetryPolicy, opts...) + return a.base.GetCurrentWeather(ctx, in, opts...) +} + +func (a *automaticRetryClient) SubscribeWeatherAlerts(ctx context.Context, in *proto.SubscribeWeatherAlertsRequest, opts ...grpc.CallOption) (proto.WeatherService_SubscribeWeatherAlertsClient, error) { + opts = append(defaults.RetryPolicy, opts...) + return a.base.SubscribeWeatherAlerts(ctx, in, opts...) +} + +func (a *automaticRetryClient) GetCurrentWeatherOld(ctx context.Context, in *proto.GetCurrentWeatherOldRequest, opts ...grpc.CallOption) (*proto.GetCurrentWeatherOldResponse, error) { + opts = append(defaults.RetryPolicy, opts...) + return a.base.GetCurrentWeatherOld(ctx, in, opts...) +} + +var _ proto.WeatherServiceClient = &automaticRetryClient{} diff --git a/internal/grpc/example/server/BUILD.bazel b/internal/grpc/example/server/BUILD.bazel index 134ed464b67..29ac6dffa4b 100644 --- a/internal/grpc/example/server/BUILD.bazel +++ b/internal/grpc/example/server/BUILD.bazel @@ -5,6 +5,7 @@ go_library( name = "server_lib", srcs = [ "conversion.go", + "faulty.go", "main.go", ], importpath = "github.com/sourcegraph/sourcegraph/internal/grpc/example/server", diff --git a/internal/grpc/example/server/faulty.go b/internal/grpc/example/server/faulty.go new file mode 100644 index 00000000000..5e5628cdf6f --- /dev/null +++ b/internal/grpc/example/server/faulty.go @@ -0,0 +1,51 @@ +package main + +import ( + "context" + "sync/atomic" + + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + + proto "github.com/sourcegraph/sourcegraph/internal/grpc/example/weather/v1" +) + +// flakyWeatherService is a wrapper around a WeatherServiceServer that simulates +// a flaky service by returning an error on the first call to GetCurrentWeather. +type flakyWeatherService struct { + getCurrentWeatherCallCounter atomic.Int64 + + base proto.WeatherServiceServer + proto.UnimplementedWeatherServiceServer +} + +func (f *flakyWeatherService) GetCurrentWeather(ctx context.Context, request *proto.GetCurrentWeatherRequest) (*proto.GetCurrentWeatherResponse, error) { + count := f.getCurrentWeatherCallCounter.Add(1) + if count == 1 { + return nil, status.Error(codes.Unavailable, "simulated service outage") + } + + return f.base.GetCurrentWeather(ctx, request) +} + +func (f *flakyWeatherService) SubscribeWeatherAlerts(request *proto.SubscribeWeatherAlertsRequest, server proto.WeatherService_SubscribeWeatherAlertsServer) error { + return f.base.SubscribeWeatherAlerts(request, server) +} + +func (f *flakyWeatherService) UploadWeatherData(server proto.WeatherService_UploadWeatherDataServer) error { + return f.base.UploadWeatherData(server) +} + +func (f *flakyWeatherService) RealTimeWeather(server proto.WeatherService_RealTimeWeatherServer) error { + return f.base.RealTimeWeather(server) +} + +func (f *flakyWeatherService) UploadWeatherPhoto(server proto.WeatherService_UploadWeatherPhotoServer) error { + return f.base.UploadWeatherPhoto(server) +} + +func (f *flakyWeatherService) GetCurrentWeatherOld(ctx context.Context, request *proto.GetCurrentWeatherOldRequest) (*proto.GetCurrentWeatherOldResponse, error) { + return f.base.GetCurrentWeatherOld(ctx, request) +} + +var _ proto.WeatherServiceServer = &flakyWeatherService{} diff --git a/internal/grpc/example/server/main.go b/internal/grpc/example/server/main.go index 22e27fc4845..e801085e0db 100644 --- a/internal/grpc/example/server/main.go +++ b/internal/grpc/example/server/main.go @@ -6,14 +6,16 @@ import ( "net" "github.com/sourcegraph/log" + "google.golang.org/grpc" + "github.com/sourcegraph/sourcegraph/internal/grpc/example/server/service" pb "github.com/sourcegraph/sourcegraph/internal/grpc/example/weather/v1" "github.com/sourcegraph/sourcegraph/internal/grpc/streamio" - "google.golang.org/grpc" - "github.com/sourcegraph/sourcegraph/lib/errors" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" + + "github.com/sourcegraph/sourcegraph/lib/errors" ) type weatherGRPCServer struct { @@ -194,7 +196,9 @@ func (w *weatherGRPCServer) UploadWeatherPhoto(stream pb.WeatherService_UploadWe w.logger.Info("Received screenshot metadata", log.String("sensorID", v.Metadata.GetSensorId()), log.String("filename", v.Metadata.GetFileName()), - log.String("location", v.Metadata.GetLocation())) + log.String("location", v.Metadata.GetLocation()), + log.Time("timestamp", v.Metadata.GetTimestamp().AsTime()), + ) default: return errors.Errorf("expected first message to be image metadata, instead got unexpected message type %T", v) } @@ -241,9 +245,13 @@ func main() { } s := grpc.NewServer() - pb.RegisterWeatherServiceServer(s, &weatherGRPCServer{ - logger: logger, - }) + flaky := &flakyWeatherService{ + base: &weatherGRPCServer{ + logger: logger, + }, + } + pb.RegisterWeatherServiceServer(s, flaky) + logger.Info("Server listening", log.String("address", lis.Addr().String())) if err := s.Serve(lis); err != nil { diff --git a/internal/grpc/example/weather/v1/BUILD.bazel b/internal/grpc/example/weather/v1/BUILD.bazel index be628362682..65859491c7f 100644 --- a/internal/grpc/example/weather/v1/BUILD.bazel +++ b/internal/grpc/example/weather/v1/BUILD.bazel @@ -13,6 +13,7 @@ proto_library( name = "grpc_example_weather_v1_proto", srcs = ["weather.proto"], visibility = ["//:__subpackages__"], + deps = ["@com_google_protobuf//:timestamp_proto"], ) go_proto_library( diff --git a/internal/grpc/example/weather/v1/weather.pb.go b/internal/grpc/example/weather/v1/weather.pb.go index 6e46be432a6..8ce485dc98c 100644 --- a/internal/grpc/example/weather/v1/weather.pb.go +++ b/internal/grpc/example/weather/v1/weather.pb.go @@ -9,6 +9,7 @@ package v1 import ( protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" + timestamppb "google.golang.org/protobuf/types/known/timestamppb" reflect "reflect" sync "sync" ) @@ -511,9 +512,10 @@ type UploadWeatherDataRequest struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - SensorId string `protobuf:"bytes,1,opt,name=sensor_id,json=sensorId,proto3" json:"sensor_id,omitempty"` - Temperature *Temperature `protobuf:"bytes,2,opt,name=temperature,proto3" json:"temperature,omitempty"` - Humidity float64 `protobuf:"fixed64,3,opt,name=humidity,proto3" json:"humidity,omitempty"` + SensorId string `protobuf:"bytes,1,opt,name=sensor_id,json=sensorId,proto3" json:"sensor_id,omitempty"` + Temperature *Temperature `protobuf:"bytes,2,opt,name=temperature,proto3" json:"temperature,omitempty"` + Humidity float64 `protobuf:"fixed64,3,opt,name=humidity,proto3" json:"humidity,omitempty"` + Timestamp *timestamppb.Timestamp `protobuf:"bytes,4,opt,name=timestamp,proto3" json:"timestamp,omitempty"` } func (x *UploadWeatherDataRequest) Reset() { @@ -569,6 +571,13 @@ func (x *UploadWeatherDataRequest) GetHumidity() float64 { return 0 } +func (x *UploadWeatherDataRequest) GetTimestamp() *timestamppb.Timestamp { + if x != nil { + return x.Timestamp + } + return nil +} + type UploadWeatherPhotoResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -825,9 +834,10 @@ type UploadWeatherPhotoRequest_Metadata struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Location string `protobuf:"bytes,1,opt,name=location,proto3" json:"location,omitempty"` - SensorId string `protobuf:"bytes,2,opt,name=sensor_id,json=sensorId,proto3" json:"sensor_id,omitempty"` - FileName string `protobuf:"bytes,3,opt,name=file_name,json=fileName,proto3" json:"file_name,omitempty"` + Location string `protobuf:"bytes,1,opt,name=location,proto3" json:"location,omitempty"` + SensorId string `protobuf:"bytes,2,opt,name=sensor_id,json=sensorId,proto3" json:"sensor_id,omitempty"` + FileName string `protobuf:"bytes,3,opt,name=file_name,json=fileName,proto3" json:"file_name,omitempty"` + Timestamp *timestamppb.Timestamp `protobuf:"bytes,4,opt,name=timestamp,proto3" json:"timestamp,omitempty"` } func (x *UploadWeatherPhotoRequest_Metadata) Reset() { @@ -883,6 +893,13 @@ func (x *UploadWeatherPhotoRequest_Metadata) GetFileName() string { return "" } +func (x *UploadWeatherPhotoRequest_Metadata) GetTimestamp() *timestamppb.Timestamp { + if x != nil { + return x.Timestamp + } + return nil +} + type UploadWeatherPhotoRequest_Payload struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -935,131 +952,141 @@ var File_weather_proto protoreflect.FileDescriptor var file_weather_proto_rawDesc = []byte{ 0x0a, 0x0d, 0x77, 0x65, 0x61, 0x74, 0x68, 0x65, 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x17, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2e, 0x77, 0x65, - 0x61, 0x74, 0x68, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x22, 0x36, 0x0a, 0x18, 0x47, 0x65, 0x74, 0x43, - 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x57, 0x65, 0x61, 0x74, 0x68, 0x65, 0x72, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x22, 0x85, 0x01, 0x0a, 0x19, 0x47, 0x65, 0x74, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x57, - 0x65, 0x61, 0x74, 0x68, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x20, - 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, - 0x12, 0x46, 0x0a, 0x0b, 0x74, 0x65, 0x6d, 0x70, 0x65, 0x72, 0x61, 0x74, 0x75, 0x72, 0x65, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x65, 0x78, 0x61, - 0x6d, 0x70, 0x6c, 0x65, 0x2e, 0x77, 0x65, 0x61, 0x74, 0x68, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, - 0x54, 0x65, 0x6d, 0x70, 0x65, 0x72, 0x61, 0x74, 0x75, 0x72, 0x65, 0x52, 0x0b, 0x74, 0x65, 0x6d, - 0x70, 0x65, 0x72, 0x61, 0x74, 0x75, 0x72, 0x65, 0x22, 0xb8, 0x01, 0x0a, 0x0b, 0x54, 0x65, 0x6d, - 0x70, 0x65, 0x72, 0x61, 0x74, 0x75, 0x72, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, - 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x01, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x3d, - 0x0a, 0x04, 0x75, 0x6e, 0x69, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x29, 0x2e, 0x67, - 0x72, 0x70, 0x63, 0x2e, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2e, 0x77, 0x65, 0x61, 0x74, - 0x68, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x65, 0x6d, 0x70, 0x65, 0x72, 0x61, 0x74, 0x75, - 0x72, 0x65, 0x2e, 0x55, 0x6e, 0x69, 0x74, 0x52, 0x04, 0x75, 0x6e, 0x69, 0x74, 0x22, 0x54, 0x0a, - 0x04, 0x55, 0x6e, 0x69, 0x74, 0x12, 0x14, 0x0a, 0x10, 0x55, 0x4e, 0x49, 0x54, 0x5f, 0x55, 0x4e, - 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x10, 0x0a, 0x0c, 0x55, - 0x4e, 0x49, 0x54, 0x5f, 0x43, 0x45, 0x4c, 0x53, 0x49, 0x55, 0x53, 0x10, 0x01, 0x12, 0x13, 0x0a, - 0x0f, 0x55, 0x4e, 0x49, 0x54, 0x5f, 0x46, 0x41, 0x48, 0x52, 0x45, 0x4e, 0x48, 0x45, 0x49, 0x54, - 0x10, 0x02, 0x12, 0x0f, 0x0a, 0x0b, 0x55, 0x4e, 0x49, 0x54, 0x5f, 0x4b, 0x45, 0x4c, 0x56, 0x49, - 0x4e, 0x10, 0x03, 0x22, 0x4b, 0x0a, 0x12, 0x53, 0x65, 0x6e, 0x73, 0x6f, 0x72, 0x4f, 0x66, 0x66, - 0x6c, 0x69, 0x6e, 0x65, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x1b, 0x0a, 0x09, 0x73, 0x65, 0x6e, - 0x73, 0x6f, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x73, 0x65, - 0x6e, 0x73, 0x6f, 0x72, 0x49, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, - 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, - 0x22, 0xda, 0x02, 0x0a, 0x19, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x57, 0x65, 0x61, 0x74, 0x68, - 0x65, 0x72, 0x50, 0x68, 0x6f, 0x74, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x59, - 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x3b, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2e, - 0x77, 0x65, 0x61, 0x74, 0x68, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x70, 0x6c, 0x6f, 0x61, - 0x64, 0x57, 0x65, 0x61, 0x74, 0x68, 0x65, 0x72, 0x50, 0x68, 0x6f, 0x74, 0x6f, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x2e, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x48, 0x00, 0x52, - 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x56, 0x0a, 0x07, 0x70, 0x61, 0x79, - 0x6c, 0x6f, 0x61, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x3a, 0x2e, 0x67, 0x72, 0x70, - 0x63, 0x2e, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2e, 0x77, 0x65, 0x61, 0x74, 0x68, 0x65, - 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x57, 0x65, 0x61, 0x74, 0x68, - 0x65, 0x72, 0x50, 0x68, 0x6f, 0x74, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x50, - 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x48, 0x00, 0x52, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, - 0x64, 0x1a, 0x60, 0x0a, 0x08, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x1a, 0x0a, - 0x08, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x08, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1b, 0x0a, 0x09, 0x73, 0x65, 0x6e, - 0x73, 0x6f, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x73, 0x65, - 0x6e, 0x73, 0x6f, 0x72, 0x49, 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x6e, - 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x66, 0x69, 0x6c, 0x65, 0x4e, - 0x61, 0x6d, 0x65, 0x1a, 0x1d, 0x0a, 0x07, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x12, 0x12, - 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x64, 0x61, - 0x74, 0x61, 0x42, 0x09, 0x0a, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x22, 0x35, 0x0a, - 0x19, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x57, 0x65, 0x61, 0x74, 0x68, 0x65, 0x72, 0x44, 0x61, - 0x74, 0x61, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, - 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, - 0x73, 0x61, 0x67, 0x65, 0x22, 0x37, 0x0a, 0x1d, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, - 0x65, 0x57, 0x65, 0x61, 0x74, 0x68, 0x65, 0x72, 0x41, 0x6c, 0x65, 0x72, 0x74, 0x73, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x65, 0x67, 0x69, 0x6f, 0x6e, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x72, 0x65, 0x67, 0x69, 0x6f, 0x6e, 0x22, 0x36, 0x0a, - 0x1e, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x57, 0x65, 0x61, 0x74, 0x68, 0x65, - 0x72, 0x41, 0x6c, 0x65, 0x72, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, - 0x14, 0x0a, 0x05, 0x61, 0x6c, 0x65, 0x72, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, - 0x61, 0x6c, 0x65, 0x72, 0x74, 0x22, 0x9b, 0x01, 0x0a, 0x18, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, - 0x57, 0x65, 0x61, 0x74, 0x68, 0x65, 0x72, 0x44, 0x61, 0x74, 0x61, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x73, 0x65, 0x6e, 0x73, 0x6f, 0x72, 0x5f, 0x69, 0x64, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x73, 0x65, 0x6e, 0x73, 0x6f, 0x72, 0x49, 0x64, 0x12, - 0x46, 0x0a, 0x0b, 0x74, 0x65, 0x6d, 0x70, 0x65, 0x72, 0x61, 0x74, 0x75, 0x72, 0x65, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x65, 0x78, 0x61, 0x6d, - 0x70, 0x6c, 0x65, 0x2e, 0x77, 0x65, 0x61, 0x74, 0x68, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x54, - 0x65, 0x6d, 0x70, 0x65, 0x72, 0x61, 0x74, 0x75, 0x72, 0x65, 0x52, 0x0b, 0x74, 0x65, 0x6d, 0x70, - 0x65, 0x72, 0x61, 0x74, 0x75, 0x72, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x68, 0x75, 0x6d, 0x69, 0x64, - 0x69, 0x74, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x01, 0x52, 0x08, 0x68, 0x75, 0x6d, 0x69, 0x64, - 0x69, 0x74, 0x79, 0x22, 0x36, 0x0a, 0x1a, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x57, 0x65, 0x61, - 0x74, 0x68, 0x65, 0x72, 0x50, 0x68, 0x6f, 0x74, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x34, 0x0a, 0x16, 0x52, - 0x65, 0x61, 0x6c, 0x54, 0x69, 0x6d, 0x65, 0x57, 0x65, 0x61, 0x74, 0x68, 0x65, 0x72, 0x52, 0x65, + 0x61, 0x74, 0x68, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x1a, 0x1f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, + 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, + 0x61, 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x36, 0x0a, 0x18, 0x47, 0x65, 0x74, + 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x57, 0x65, 0x61, 0x74, 0x68, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x22, 0x83, 0x01, 0x0a, 0x17, 0x52, 0x65, 0x61, 0x6c, 0x54, 0x69, 0x6d, 0x65, 0x57, 0x65, - 0x61, 0x74, 0x68, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x20, 0x0a, - 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, - 0x46, 0x0a, 0x0b, 0x74, 0x65, 0x6d, 0x70, 0x65, 0x72, 0x61, 0x74, 0x75, 0x72, 0x65, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x65, 0x78, 0x61, 0x6d, - 0x70, 0x6c, 0x65, 0x2e, 0x77, 0x65, 0x61, 0x74, 0x68, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x54, - 0x65, 0x6d, 0x70, 0x65, 0x72, 0x61, 0x74, 0x75, 0x72, 0x65, 0x52, 0x0b, 0x74, 0x65, 0x6d, 0x70, - 0x65, 0x72, 0x61, 0x74, 0x75, 0x72, 0x65, 0x22, 0x39, 0x0a, 0x1b, 0x47, 0x65, 0x74, 0x43, 0x75, - 0x72, 0x72, 0x65, 0x6e, 0x74, 0x57, 0x65, 0x61, 0x74, 0x68, 0x65, 0x72, 0x4f, 0x6c, 0x64, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x22, 0x88, 0x01, 0x0a, 0x1c, 0x47, 0x65, 0x74, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, - 0x74, 0x57, 0x65, 0x61, 0x74, 0x68, 0x65, 0x72, 0x4f, 0x6c, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x22, 0x85, 0x01, 0x0a, 0x19, 0x47, 0x65, 0x74, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, + 0x57, 0x65, 0x61, 0x74, 0x68, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, + 0x6e, 0x12, 0x46, 0x0a, 0x0b, 0x74, 0x65, 0x6d, 0x70, 0x65, 0x72, 0x61, 0x74, 0x75, 0x72, 0x65, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x65, 0x78, + 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2e, 0x77, 0x65, 0x61, 0x74, 0x68, 0x65, 0x72, 0x2e, 0x76, 0x31, + 0x2e, 0x54, 0x65, 0x6d, 0x70, 0x65, 0x72, 0x61, 0x74, 0x75, 0x72, 0x65, 0x52, 0x0b, 0x74, 0x65, + 0x6d, 0x70, 0x65, 0x72, 0x61, 0x74, 0x75, 0x72, 0x65, 0x22, 0xb8, 0x01, 0x0a, 0x0b, 0x54, 0x65, + 0x6d, 0x70, 0x65, 0x72, 0x61, 0x74, 0x75, 0x72, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, + 0x75, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x01, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x12, + 0x3d, 0x0a, 0x04, 0x75, 0x6e, 0x69, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x29, 0x2e, + 0x67, 0x72, 0x70, 0x63, 0x2e, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2e, 0x77, 0x65, 0x61, + 0x74, 0x68, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x65, 0x6d, 0x70, 0x65, 0x72, 0x61, 0x74, + 0x75, 0x72, 0x65, 0x2e, 0x55, 0x6e, 0x69, 0x74, 0x52, 0x04, 0x75, 0x6e, 0x69, 0x74, 0x22, 0x54, + 0x0a, 0x04, 0x55, 0x6e, 0x69, 0x74, 0x12, 0x14, 0x0a, 0x10, 0x55, 0x4e, 0x49, 0x54, 0x5f, 0x55, + 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x10, 0x0a, 0x0c, + 0x55, 0x4e, 0x49, 0x54, 0x5f, 0x43, 0x45, 0x4c, 0x53, 0x49, 0x55, 0x53, 0x10, 0x01, 0x12, 0x13, + 0x0a, 0x0f, 0x55, 0x4e, 0x49, 0x54, 0x5f, 0x46, 0x41, 0x48, 0x52, 0x45, 0x4e, 0x48, 0x45, 0x49, + 0x54, 0x10, 0x02, 0x12, 0x0f, 0x0a, 0x0b, 0x55, 0x4e, 0x49, 0x54, 0x5f, 0x4b, 0x45, 0x4c, 0x56, + 0x49, 0x4e, 0x10, 0x03, 0x22, 0x4b, 0x0a, 0x12, 0x53, 0x65, 0x6e, 0x73, 0x6f, 0x72, 0x4f, 0x66, + 0x66, 0x6c, 0x69, 0x6e, 0x65, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x1b, 0x0a, 0x09, 0x73, 0x65, + 0x6e, 0x73, 0x6f, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x73, + 0x65, 0x6e, 0x73, 0x6f, 0x72, 0x49, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, + 0x67, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, + 0x65, 0x22, 0x95, 0x03, 0x0a, 0x19, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x57, 0x65, 0x61, 0x74, + 0x68, 0x65, 0x72, 0x50, 0x68, 0x6f, 0x74, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, + 0x59, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x3b, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, + 0x2e, 0x77, 0x65, 0x61, 0x74, 0x68, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x70, 0x6c, 0x6f, + 0x61, 0x64, 0x57, 0x65, 0x61, 0x74, 0x68, 0x65, 0x72, 0x50, 0x68, 0x6f, 0x74, 0x6f, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x48, 0x00, + 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x56, 0x0a, 0x07, 0x70, 0x61, + 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x3a, 0x2e, 0x67, 0x72, + 0x70, 0x63, 0x2e, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2e, 0x77, 0x65, 0x61, 0x74, 0x68, + 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x57, 0x65, 0x61, 0x74, + 0x68, 0x65, 0x72, 0x50, 0x68, 0x6f, 0x74, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, + 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x48, 0x00, 0x52, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, + 0x61, 0x64, 0x1a, 0x9a, 0x01, 0x0a, 0x08, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, + 0x1a, 0x0a, 0x08, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x08, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1b, 0x0a, 0x09, 0x73, + 0x65, 0x6e, 0x73, 0x6f, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, + 0x73, 0x65, 0x6e, 0x73, 0x6f, 0x72, 0x49, 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x66, 0x69, 0x6c, 0x65, + 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x66, 0x69, 0x6c, + 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x38, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, + 0x6d, 0x70, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, + 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, + 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x1a, + 0x1d, 0x0a, 0x07, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x61, + 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x42, 0x09, + 0x0a, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x22, 0x35, 0x0a, 0x19, 0x55, 0x70, 0x6c, + 0x6f, 0x61, 0x64, 0x57, 0x65, 0x61, 0x74, 0x68, 0x65, 0x72, 0x44, 0x61, 0x74, 0x61, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, + 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, + 0x22, 0x37, 0x0a, 0x1d, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x57, 0x65, 0x61, + 0x74, 0x68, 0x65, 0x72, 0x41, 0x6c, 0x65, 0x72, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x65, 0x67, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x06, 0x72, 0x65, 0x67, 0x69, 0x6f, 0x6e, 0x22, 0x36, 0x0a, 0x1e, 0x53, 0x75, 0x62, + 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x57, 0x65, 0x61, 0x74, 0x68, 0x65, 0x72, 0x41, 0x6c, 0x65, + 0x72, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x61, + 0x6c, 0x65, 0x72, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x61, 0x6c, 0x65, 0x72, + 0x74, 0x22, 0xd5, 0x01, 0x0a, 0x18, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x57, 0x65, 0x61, 0x74, + 0x68, 0x65, 0x72, 0x44, 0x61, 0x74, 0x61, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, + 0x0a, 0x09, 0x73, 0x65, 0x6e, 0x73, 0x6f, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x08, 0x73, 0x65, 0x6e, 0x73, 0x6f, 0x72, 0x49, 0x64, 0x12, 0x46, 0x0a, 0x0b, 0x74, + 0x65, 0x6d, 0x70, 0x65, 0x72, 0x61, 0x74, 0x75, 0x72, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x24, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2e, + 0x77, 0x65, 0x61, 0x74, 0x68, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x65, 0x6d, 0x70, 0x65, + 0x72, 0x61, 0x74, 0x75, 0x72, 0x65, 0x52, 0x0b, 0x74, 0x65, 0x6d, 0x70, 0x65, 0x72, 0x61, 0x74, + 0x75, 0x72, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x68, 0x75, 0x6d, 0x69, 0x64, 0x69, 0x74, 0x79, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x01, 0x52, 0x08, 0x68, 0x75, 0x6d, 0x69, 0x64, 0x69, 0x74, 0x79, 0x12, + 0x38, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x04, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, + 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x22, 0x36, 0x0a, 0x1a, 0x55, 0x70, 0x6c, + 0x6f, 0x61, 0x64, 0x57, 0x65, 0x61, 0x74, 0x68, 0x65, 0x72, 0x50, 0x68, 0x6f, 0x74, 0x6f, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, + 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, + 0x65, 0x22, 0x34, 0x0a, 0x16, 0x52, 0x65, 0x61, 0x6c, 0x54, 0x69, 0x6d, 0x65, 0x57, 0x65, 0x61, + 0x74, 0x68, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x6c, + 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6c, + 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x83, 0x01, 0x0a, 0x17, 0x52, 0x65, 0x61, 0x6c, + 0x54, 0x69, 0x6d, 0x65, 0x57, 0x65, 0x61, 0x74, 0x68, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x46, 0x0a, 0x0b, 0x74, 0x65, 0x6d, 0x70, 0x65, 0x72, 0x61, 0x74, 0x75, 0x72, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2e, 0x77, 0x65, 0x61, 0x74, 0x68, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x65, 0x6d, 0x70, 0x65, 0x72, 0x61, 0x74, 0x75, 0x72, 0x65, - 0x52, 0x0b, 0x74, 0x65, 0x6d, 0x70, 0x65, 0x72, 0x61, 0x74, 0x75, 0x72, 0x65, 0x32, 0xa9, 0x06, - 0x0a, 0x0e, 0x57, 0x65, 0x61, 0x74, 0x68, 0x65, 0x72, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, - 0x12, 0x7c, 0x0a, 0x11, 0x47, 0x65, 0x74, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x57, 0x65, - 0x61, 0x74, 0x68, 0x65, 0x72, 0x12, 0x31, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x65, 0x78, 0x61, - 0x6d, 0x70, 0x6c, 0x65, 0x2e, 0x77, 0x65, 0x61, 0x74, 0x68, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, - 0x47, 0x65, 0x74, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x57, 0x65, 0x61, 0x74, 0x68, 0x65, - 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x32, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, - 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2e, 0x77, 0x65, 0x61, 0x74, 0x68, 0x65, 0x72, 0x2e, - 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x57, 0x65, 0x61, - 0x74, 0x68, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x8d, - 0x01, 0x0a, 0x16, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x57, 0x65, 0x61, 0x74, - 0x68, 0x65, 0x72, 0x41, 0x6c, 0x65, 0x72, 0x74, 0x73, 0x12, 0x36, 0x2e, 0x67, 0x72, 0x70, 0x63, - 0x2e, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2e, 0x77, 0x65, 0x61, 0x74, 0x68, 0x65, 0x72, - 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x57, 0x65, 0x61, - 0x74, 0x68, 0x65, 0x72, 0x41, 0x6c, 0x65, 0x72, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x37, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, - 0x2e, 0x77, 0x65, 0x61, 0x74, 0x68, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x75, 0x62, 0x73, + 0x52, 0x0b, 0x74, 0x65, 0x6d, 0x70, 0x65, 0x72, 0x61, 0x74, 0x75, 0x72, 0x65, 0x22, 0x39, 0x0a, + 0x1b, 0x47, 0x65, 0x74, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x57, 0x65, 0x61, 0x74, 0x68, + 0x65, 0x72, 0x4f, 0x6c, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, + 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, + 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x88, 0x01, 0x0a, 0x1c, 0x47, 0x65, 0x74, + 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x57, 0x65, 0x61, 0x74, 0x68, 0x65, 0x72, 0x4f, 0x6c, + 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, + 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, + 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x46, 0x0a, 0x0b, 0x74, + 0x65, 0x6d, 0x70, 0x65, 0x72, 0x61, 0x74, 0x75, 0x72, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x24, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2e, + 0x77, 0x65, 0x61, 0x74, 0x68, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x65, 0x6d, 0x70, 0x65, + 0x72, 0x61, 0x74, 0x75, 0x72, 0x65, 0x52, 0x0b, 0x74, 0x65, 0x6d, 0x70, 0x65, 0x72, 0x61, 0x74, + 0x75, 0x72, 0x65, 0x32, 0xb9, 0x06, 0x0a, 0x0e, 0x57, 0x65, 0x61, 0x74, 0x68, 0x65, 0x72, 0x53, + 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x7f, 0x0a, 0x11, 0x47, 0x65, 0x74, 0x43, 0x75, 0x72, + 0x72, 0x65, 0x6e, 0x74, 0x57, 0x65, 0x61, 0x74, 0x68, 0x65, 0x72, 0x12, 0x31, 0x2e, 0x67, 0x72, + 0x70, 0x63, 0x2e, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2e, 0x77, 0x65, 0x61, 0x74, 0x68, + 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, + 0x57, 0x65, 0x61, 0x74, 0x68, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x32, + 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2e, 0x77, 0x65, + 0x61, 0x74, 0x68, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x75, 0x72, 0x72, + 0x65, 0x6e, 0x74, 0x57, 0x65, 0x61, 0x74, 0x68, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x22, 0x03, 0x90, 0x02, 0x01, 0x12, 0x90, 0x01, 0x0a, 0x16, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x57, 0x65, 0x61, 0x74, 0x68, 0x65, 0x72, 0x41, 0x6c, 0x65, 0x72, - 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x30, 0x01, 0x12, 0x7e, - 0x0a, 0x11, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x57, 0x65, 0x61, 0x74, 0x68, 0x65, 0x72, 0x44, - 0x61, 0x74, 0x61, 0x12, 0x31, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x65, 0x78, 0x61, 0x6d, 0x70, + 0x74, 0x73, 0x12, 0x36, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, + 0x65, 0x2e, 0x77, 0x65, 0x61, 0x74, 0x68, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x75, 0x62, + 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x57, 0x65, 0x61, 0x74, 0x68, 0x65, 0x72, 0x41, 0x6c, 0x65, + 0x72, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x37, 0x2e, 0x67, 0x72, 0x70, + 0x63, 0x2e, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2e, 0x77, 0x65, 0x61, 0x74, 0x68, 0x65, + 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x57, 0x65, + 0x61, 0x74, 0x68, 0x65, 0x72, 0x41, 0x6c, 0x65, 0x72, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x22, 0x03, 0x90, 0x02, 0x01, 0x30, 0x01, 0x12, 0x81, 0x01, 0x0a, 0x11, 0x55, + 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x57, 0x65, 0x61, 0x74, 0x68, 0x65, 0x72, 0x44, 0x61, 0x74, 0x61, + 0x12, 0x31, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2e, + 0x77, 0x65, 0x61, 0x74, 0x68, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x70, 0x6c, 0x6f, 0x61, + 0x64, 0x57, 0x65, 0x61, 0x74, 0x68, 0x65, 0x72, 0x44, 0x61, 0x74, 0x61, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x32, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2e, 0x77, 0x65, 0x61, 0x74, 0x68, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x57, 0x65, 0x61, 0x74, 0x68, 0x65, 0x72, 0x44, 0x61, 0x74, 0x61, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x32, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x65, 0x78, - 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2e, 0x77, 0x65, 0x61, 0x74, 0x68, 0x65, 0x72, 0x2e, 0x76, 0x31, - 0x2e, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x57, 0x65, 0x61, 0x74, 0x68, 0x65, 0x72, 0x44, 0x61, - 0x74, 0x61, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x28, 0x01, 0x12, 0x7a, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x03, 0x90, 0x02, 0x02, 0x28, 0x01, 0x12, 0x7d, 0x0a, 0x0f, 0x52, 0x65, 0x61, 0x6c, 0x54, 0x69, 0x6d, 0x65, 0x57, 0x65, 0x61, 0x74, 0x68, 0x65, 0x72, 0x12, 0x2f, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2e, 0x77, 0x65, 0x61, 0x74, 0x68, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x61, 0x6c, @@ -1067,29 +1094,29 @@ var file_weather_proto_rawDesc = []byte{ 0x73, 0x74, 0x1a, 0x30, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2e, 0x77, 0x65, 0x61, 0x74, 0x68, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x61, 0x6c, 0x54, 0x69, 0x6d, 0x65, 0x57, 0x65, 0x61, 0x74, 0x68, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x28, 0x01, 0x30, 0x01, 0x12, 0x81, 0x01, 0x0a, 0x12, 0x55, + 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x03, 0x90, 0x02, 0x01, 0x28, 0x01, 0x30, 0x01, 0x12, 0x81, 0x01, + 0x0a, 0x12, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x57, 0x65, 0x61, 0x74, 0x68, 0x65, 0x72, 0x50, + 0x68, 0x6f, 0x74, 0x6f, 0x12, 0x32, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x65, 0x78, 0x61, 0x6d, + 0x70, 0x6c, 0x65, 0x2e, 0x77, 0x65, 0x61, 0x74, 0x68, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x57, 0x65, 0x61, 0x74, 0x68, 0x65, 0x72, 0x50, 0x68, 0x6f, 0x74, - 0x6f, 0x12, 0x32, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, - 0x2e, 0x77, 0x65, 0x61, 0x74, 0x68, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x70, 0x6c, 0x6f, - 0x61, 0x64, 0x57, 0x65, 0x61, 0x74, 0x68, 0x65, 0x72, 0x50, 0x68, 0x6f, 0x74, 0x6f, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x33, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x65, 0x78, 0x61, - 0x6d, 0x70, 0x6c, 0x65, 0x2e, 0x77, 0x65, 0x61, 0x74, 0x68, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, - 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x57, 0x65, 0x61, 0x74, 0x68, 0x65, 0x72, 0x50, 0x68, 0x6f, - 0x74, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x28, 0x01, 0x12, 0x88, - 0x01, 0x0a, 0x14, 0x47, 0x65, 0x74, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x57, 0x65, 0x61, - 0x74, 0x68, 0x65, 0x72, 0x4f, 0x6c, 0x64, 0x12, 0x34, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x65, - 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2e, 0x77, 0x65, 0x61, 0x74, 0x68, 0x65, 0x72, 0x2e, 0x76, - 0x31, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x57, 0x65, 0x61, 0x74, - 0x68, 0x65, 0x72, 0x4f, 0x6c, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x35, 0x2e, - 0x67, 0x72, 0x70, 0x63, 0x2e, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2e, 0x77, 0x65, 0x61, - 0x74, 0x68, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x75, 0x72, 0x72, 0x65, - 0x6e, 0x74, 0x57, 0x65, 0x61, 0x74, 0x68, 0x65, 0x72, 0x4f, 0x6c, 0x64, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x03, 0x88, 0x02, 0x01, 0x42, 0x45, 0x5a, 0x43, 0x67, 0x69, 0x74, - 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x67, 0x72, - 0x61, 0x70, 0x68, 0x2f, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x67, 0x72, 0x61, 0x70, 0x68, 0x2f, - 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x67, 0x72, 0x70, 0x63, 0x2f, 0x65, 0x78, - 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2f, 0x77, 0x65, 0x61, 0x74, 0x68, 0x65, 0x72, 0x2f, 0x76, 0x31, - 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x33, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, + 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2e, 0x77, 0x65, 0x61, 0x74, 0x68, 0x65, 0x72, 0x2e, + 0x76, 0x31, 0x2e, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x57, 0x65, 0x61, 0x74, 0x68, 0x65, 0x72, + 0x50, 0x68, 0x6f, 0x74, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x28, + 0x01, 0x12, 0x8b, 0x01, 0x0a, 0x14, 0x47, 0x65, 0x74, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, + 0x57, 0x65, 0x61, 0x74, 0x68, 0x65, 0x72, 0x4f, 0x6c, 0x64, 0x12, 0x34, 0x2e, 0x67, 0x72, 0x70, + 0x63, 0x2e, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2e, 0x77, 0x65, 0x61, 0x74, 0x68, 0x65, + 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x57, + 0x65, 0x61, 0x74, 0x68, 0x65, 0x72, 0x4f, 0x6c, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x35, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2e, + 0x77, 0x65, 0x61, 0x74, 0x68, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x75, + 0x72, 0x72, 0x65, 0x6e, 0x74, 0x57, 0x65, 0x61, 0x74, 0x68, 0x65, 0x72, 0x4f, 0x6c, 0x64, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x06, 0x88, 0x02, 0x01, 0x90, 0x02, 0x02, 0x42, + 0x45, 0x5a, 0x43, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x73, 0x6f, + 0x75, 0x72, 0x63, 0x65, 0x67, 0x72, 0x61, 0x70, 0x68, 0x2f, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, + 0x67, 0x72, 0x61, 0x70, 0x68, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x67, + 0x72, 0x70, 0x63, 0x2f, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2f, 0x77, 0x65, 0x61, 0x74, + 0x68, 0x65, 0x72, 0x2f, 0x76, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -1124,6 +1151,7 @@ var file_weather_proto_goTypes = []interface{}{ (*GetCurrentWeatherOldResponse)(nil), // 14: grpc.example.weather.v1.GetCurrentWeatherOldResponse (*UploadWeatherPhotoRequest_Metadata)(nil), // 15: grpc.example.weather.v1.UploadWeatherPhotoRequest.Metadata (*UploadWeatherPhotoRequest_Payload)(nil), // 16: grpc.example.weather.v1.UploadWeatherPhotoRequest.Payload + (*timestamppb.Timestamp)(nil), // 17: google.protobuf.Timestamp } var file_weather_proto_depIdxs = []int32{ 3, // 0: grpc.example.weather.v1.GetCurrentWeatherResponse.temperature:type_name -> grpc.example.weather.v1.Temperature @@ -1131,25 +1159,27 @@ var file_weather_proto_depIdxs = []int32{ 15, // 2: grpc.example.weather.v1.UploadWeatherPhotoRequest.metadata:type_name -> grpc.example.weather.v1.UploadWeatherPhotoRequest.Metadata 16, // 3: grpc.example.weather.v1.UploadWeatherPhotoRequest.payload:type_name -> grpc.example.weather.v1.UploadWeatherPhotoRequest.Payload 3, // 4: grpc.example.weather.v1.UploadWeatherDataRequest.temperature:type_name -> grpc.example.weather.v1.Temperature - 3, // 5: grpc.example.weather.v1.RealTimeWeatherResponse.temperature:type_name -> grpc.example.weather.v1.Temperature - 3, // 6: grpc.example.weather.v1.GetCurrentWeatherOldResponse.temperature:type_name -> grpc.example.weather.v1.Temperature - 1, // 7: grpc.example.weather.v1.WeatherService.GetCurrentWeather:input_type -> grpc.example.weather.v1.GetCurrentWeatherRequest - 7, // 8: grpc.example.weather.v1.WeatherService.SubscribeWeatherAlerts:input_type -> grpc.example.weather.v1.SubscribeWeatherAlertsRequest - 9, // 9: grpc.example.weather.v1.WeatherService.UploadWeatherData:input_type -> grpc.example.weather.v1.UploadWeatherDataRequest - 11, // 10: grpc.example.weather.v1.WeatherService.RealTimeWeather:input_type -> grpc.example.weather.v1.RealTimeWeatherRequest - 5, // 11: grpc.example.weather.v1.WeatherService.UploadWeatherPhoto:input_type -> grpc.example.weather.v1.UploadWeatherPhotoRequest - 13, // 12: grpc.example.weather.v1.WeatherService.GetCurrentWeatherOld:input_type -> grpc.example.weather.v1.GetCurrentWeatherOldRequest - 2, // 13: grpc.example.weather.v1.WeatherService.GetCurrentWeather:output_type -> grpc.example.weather.v1.GetCurrentWeatherResponse - 8, // 14: grpc.example.weather.v1.WeatherService.SubscribeWeatherAlerts:output_type -> grpc.example.weather.v1.SubscribeWeatherAlertsResponse - 6, // 15: grpc.example.weather.v1.WeatherService.UploadWeatherData:output_type -> grpc.example.weather.v1.UploadWeatherDataResponse - 12, // 16: grpc.example.weather.v1.WeatherService.RealTimeWeather:output_type -> grpc.example.weather.v1.RealTimeWeatherResponse - 10, // 17: grpc.example.weather.v1.WeatherService.UploadWeatherPhoto:output_type -> grpc.example.weather.v1.UploadWeatherPhotoResponse - 14, // 18: grpc.example.weather.v1.WeatherService.GetCurrentWeatherOld:output_type -> grpc.example.weather.v1.GetCurrentWeatherOldResponse - 13, // [13:19] is the sub-list for method output_type - 7, // [7:13] is the sub-list for method input_type - 7, // [7:7] is the sub-list for extension type_name - 7, // [7:7] is the sub-list for extension extendee - 0, // [0:7] is the sub-list for field type_name + 17, // 5: grpc.example.weather.v1.UploadWeatherDataRequest.timestamp:type_name -> google.protobuf.Timestamp + 3, // 6: grpc.example.weather.v1.RealTimeWeatherResponse.temperature:type_name -> grpc.example.weather.v1.Temperature + 3, // 7: grpc.example.weather.v1.GetCurrentWeatherOldResponse.temperature:type_name -> grpc.example.weather.v1.Temperature + 17, // 8: grpc.example.weather.v1.UploadWeatherPhotoRequest.Metadata.timestamp:type_name -> google.protobuf.Timestamp + 1, // 9: grpc.example.weather.v1.WeatherService.GetCurrentWeather:input_type -> grpc.example.weather.v1.GetCurrentWeatherRequest + 7, // 10: grpc.example.weather.v1.WeatherService.SubscribeWeatherAlerts:input_type -> grpc.example.weather.v1.SubscribeWeatherAlertsRequest + 9, // 11: grpc.example.weather.v1.WeatherService.UploadWeatherData:input_type -> grpc.example.weather.v1.UploadWeatherDataRequest + 11, // 12: grpc.example.weather.v1.WeatherService.RealTimeWeather:input_type -> grpc.example.weather.v1.RealTimeWeatherRequest + 5, // 13: grpc.example.weather.v1.WeatherService.UploadWeatherPhoto:input_type -> grpc.example.weather.v1.UploadWeatherPhotoRequest + 13, // 14: grpc.example.weather.v1.WeatherService.GetCurrentWeatherOld:input_type -> grpc.example.weather.v1.GetCurrentWeatherOldRequest + 2, // 15: grpc.example.weather.v1.WeatherService.GetCurrentWeather:output_type -> grpc.example.weather.v1.GetCurrentWeatherResponse + 8, // 16: grpc.example.weather.v1.WeatherService.SubscribeWeatherAlerts:output_type -> grpc.example.weather.v1.SubscribeWeatherAlertsResponse + 6, // 17: grpc.example.weather.v1.WeatherService.UploadWeatherData:output_type -> grpc.example.weather.v1.UploadWeatherDataResponse + 12, // 18: grpc.example.weather.v1.WeatherService.RealTimeWeather:output_type -> grpc.example.weather.v1.RealTimeWeatherResponse + 10, // 19: grpc.example.weather.v1.WeatherService.UploadWeatherPhoto:output_type -> grpc.example.weather.v1.UploadWeatherPhotoResponse + 14, // 20: grpc.example.weather.v1.WeatherService.GetCurrentWeatherOld:output_type -> grpc.example.weather.v1.GetCurrentWeatherOldResponse + 15, // [15:21] is the sub-list for method output_type + 9, // [9:15] is the sub-list for method input_type + 9, // [9:9] is the sub-list for extension type_name + 9, // [9:9] is the sub-list for extension extendee + 0, // [0:9] is the sub-list for field type_name } func init() { file_weather_proto_init() } diff --git a/internal/grpc/example/weather/v1/weather.proto b/internal/grpc/example/weather/v1/weather.proto index cc36c12cd14..135b955e72b 100644 --- a/internal/grpc/example/weather/v1/weather.proto +++ b/internal/grpc/example/weather/v1/weather.proto @@ -2,30 +2,59 @@ syntax = "proto3"; package grpc.example.weather.v1; +import "google/protobuf/timestamp.proto"; + option go_package = "github.com/sourcegraph/sourcegraph/internal/grpc/example/weather/v1"; // The weather service definition. service WeatherService { + // Each of the RPCs in this service documents its idempotency level if applicable. This is useful for client authors + // to know whether they can retry a failed RPC without causing side effects, or if they need to take more care. + // + // When thinking about the difference between IDEMPOTENT and NO_SIDE_EFFECTS, the following StackOverflow answer might be + // helpful: https://softwareengineering.stackexchange.com/a/411619 (quoted below). + + // + // > Safe means no side effects. Idempotent means any side effects must be the same, and therefore that it doesn't matter if they are repeated. + // > + // > Say I ask my daughter to check if the oven is preheated. That's a safe operation because it has no side effects. She is just looking. + // > + // > If I ask her to preheat the oven, that's an idempotent operation. If it's already preheating, she is just going to check and leave it in the same state. I can ask her 10 times to preheat the oven, and she will roll her eyes at my terrible memory, but the oven always ends up in the on state. + // > + // > If I ask her to flip the switch on the oven, that is not idempotent. Whether the oven ends up preheating depends on if it was already preheating. + // Unary RPC: Get current weather for a location. - rpc GetCurrentWeather(GetCurrentWeatherRequest) returns (GetCurrentWeatherResponse) {} + rpc GetCurrentWeather(GetCurrentWeatherRequest) returns (GetCurrentWeatherResponse) { + option idempotency_level = NO_SIDE_EFFECTS; + } // Server Streaming RPC: Subscribe to severe weather alerts in the provided region. - rpc SubscribeWeatherAlerts(SubscribeWeatherAlertsRequest) returns (stream SubscribeWeatherAlertsResponse) {} + rpc SubscribeWeatherAlerts(SubscribeWeatherAlertsRequest) returns (stream SubscribeWeatherAlertsResponse) { + option idempotency_level = NO_SIDE_EFFECTS; + } // Client Streaming RPC: Send continuous weather data from sensors. - rpc UploadWeatherData(stream UploadWeatherDataRequest) returns (UploadWeatherDataResponse) {} + rpc UploadWeatherData(stream UploadWeatherDataRequest) returns (UploadWeatherDataResponse) { + option idempotency_level = IDEMPOTENT; + } // Bidirectional Streaming RPC: Get real-time weather updates as the client moves around. - rpc RealTimeWeather(stream RealTimeWeatherRequest) returns (stream RealTimeWeatherResponse) {} + rpc RealTimeWeather(stream RealTimeWeatherRequest) returns (stream RealTimeWeatherResponse) { + option idempotency_level = NO_SIDE_EFFECTS; + } // Client Streaming RPC: Upload a photo of the current weather from a given sensor. - rpc UploadWeatherPhoto(stream UploadWeatherPhotoRequest) returns (UploadWeatherPhotoResponse) {} + rpc UploadWeatherPhoto(stream UploadWeatherPhotoRequest) returns (UploadWeatherPhotoResponse) { + // This RPC is not idempotent, as the server does not deduplicate the photo uploads. + } // Deprecated RPC: Get current weather for a location. rpc GetCurrentWeatherOld(GetCurrentWeatherOldRequest) returns (GetCurrentWeatherOldResponse) { option deprecated = true; // Mark the RPC as deprecated. Nobody should use this anymore. // When generating go code, the deprecated option will be translated to a deprecated comment. And gopls and various tools // will show a warning when using this RPC. + + option idempotency_level = IDEMPOTENT; } } @@ -63,6 +92,7 @@ message UploadWeatherPhotoRequest { string location = 1; string sensor_id = 2; string file_name = 3; + google.protobuf.Timestamp timestamp = 4; } message Payload { @@ -91,6 +121,7 @@ message UploadWeatherDataRequest { string sensor_id = 1; Temperature temperature = 2; double humidity = 3; + google.protobuf.Timestamp timestamp = 4; } message UploadWeatherPhotoResponse { diff --git a/nogo_config.json b/nogo_config.json index c9e8dd29d4e..f8f0bd4492c 100644 --- a/nogo_config.json +++ b/nogo_config.json @@ -13,6 +13,8 @@ "internal/batches/testing/.*": "ignore batches testing code", "internal/gitserver/mock\\.go$": "ignore deprecation warning of P4Exec", "internal/gitserver/retry\\.go$": "ignore deprecation warning of P4Exec", + "internal/grpc/example/client/retry\\.go$": "ignore deprecation warning of GetCurrentWeatherOld", + "internal/grpc/example/server/faulty\\.go$": "ignore deprecation warning of GetCurrentWeatherOld", "internal/database/dbmocks": "ignore generated database mocks" } },