From dc1130cf17d9aaba41ab6ae7c832d6b6c72d4954 Mon Sep 17 00:00:00 2001 From: Emma Haruka Iwao <yuryu@google.com> Date: Sat, 23 Jul 2022 13:22:01 -0700 Subject: [PATCH] feat: chunk count checking This patch supports check count checking when committing. The client can optionally set chunk_count when uploading and if set, the server will check if the uploaded chunk count matches the value. It returns FailedPrecondition if it doesn't. --- api/open_saves.pb.go | 528 +++++++++++--------- api/open_saves.proto | 15 +- docs/reference.md | 5 +- internal/app/collector/collector_test.go | 18 +- internal/app/server/open_saves.go | 4 +- internal/app/server/open_saves_test.go | 89 ++++ internal/pkg/metadb/blobref/blobref.go | 25 +- internal/pkg/metadb/blobref/blobref_test.go | 62 ++- internal/pkg/metadb/metadb.go | 4 + internal/pkg/metadb/metadb_test.go | 21 +- 10 files changed, 480 insertions(+), 291 deletions(-) diff --git a/api/open_saves.pb.go b/api/open_saves.pb.go index 6a0c42de..d73c7c9f 100644 --- a/api/open_saves.pb.go +++ b/api/open_saves.pb.go @@ -1625,6 +1625,10 @@ type BlobMetadata struct { Crc32C uint32 `protobuf:"varint,6,opt,name=crc32c,proto3" json:"crc32c,omitempty"` // has_crc32c indicates if crc32c is present. HasCrc32C bool `protobuf:"varint,7,opt,name=has_crc32c,json=hasCrc32c,proto3" json:"has_crc32c,omitempty"` + // chunked is set true if the attached blob is chunked, otherwise false (read only). + Chunked bool `protobuf:"varint,8,opt,name=chunked,proto3" json:"chunked,omitempty"` + // Number of chunks (read only). + ChunkCount int64 `protobuf:"varint,9,opt,name=chunk_count,json=chunkCount,proto3" json:"chunk_count,omitempty"` } func (x *BlobMetadata) Reset() { @@ -1708,6 +1712,20 @@ func (x *BlobMetadata) GetHasCrc32C() bool { return false } +func (x *BlobMetadata) GetChunked() bool { + if x != nil { + return x.Chunked + } + return false +} + +func (x *BlobMetadata) GetChunkCount() int64 { + if x != nil { + return x.ChunkCount + } + return 0 +} + type CreateChunkedBlobRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -1719,6 +1737,10 @@ type CreateChunkedBlobRequest struct { RecordKey string `protobuf:"bytes,2,opt,name=record_key,json=recordKey,proto3" json:"record_key,omitempty"` // Size of each chunk ChunkSize int64 `protobuf:"varint,3,opt,name=chunk_size,json=chunkSize,proto3" json:"chunk_size,omitempty"` + // Expected number of chunks. + // When set to non-zero, the server checks if it has received the exact number of + // chunks when CommitChunkedUpload is called. + ChunkCount int64 `protobuf:"varint,4,opt,name=chunk_count,json=chunkCount,proto3" json:"chunk_count,omitempty"` } func (x *CreateChunkedBlobRequest) Reset() { @@ -1774,6 +1796,13 @@ func (x *CreateChunkedBlobRequest) GetChunkSize() int64 { return 0 } +func (x *CreateChunkedBlobRequest) GetChunkCount() int64 { + if x != nil { + return x.ChunkCount + } + return 0 +} + type CreateChunkedBlobResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -1914,7 +1943,7 @@ type ChunkMetadata struct { // session_id is the ID of a chunk upload session. Not used for downloads. SessionId string `protobuf:"bytes,1,opt,name=session_id,json=sessionId,proto3" json:"session_id,omitempty"` - // number is the number + // number is the number of the chunk. Number int64 `protobuf:"varint,2,opt,name=number,proto3" json:"number,omitempty"` // size is a byte size of the chunk. Size int64 `protobuf:"varint,3,opt,name=size,proto3" json:"size,omitempty"` @@ -3167,7 +3196,7 @@ var file_open_saves_proto_rawDesc = []byte{ 0x64, 0x61, 0x74, 0x61, 0x48, 0x00, 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x1a, 0x0a, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x00, 0x52, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x42, 0x09, 0x0a, 0x07, - 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0xcc, 0x01, 0x0a, 0x0c, 0x42, 0x6c, 0x6f, 0x62, + 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x87, 0x02, 0x0a, 0x0c, 0x42, 0x6c, 0x6f, 0x62, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x1b, 0x0a, 0x09, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x4b, 0x65, 0x79, 0x12, 0x1d, 0x0a, 0x0a, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x5f, @@ -3180,272 +3209,277 @@ var file_open_saves_proto_rawDesc = []byte{ 0x16, 0x0a, 0x06, 0x63, 0x72, 0x63, 0x33, 0x32, 0x63, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x06, 0x63, 0x72, 0x63, 0x33, 0x32, 0x63, 0x12, 0x1d, 0x0a, 0x0a, 0x68, 0x61, 0x73, 0x5f, 0x63, 0x72, 0x63, 0x33, 0x32, 0x63, 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x68, 0x61, 0x73, - 0x43, 0x72, 0x63, 0x33, 0x32, 0x63, 0x22, 0x75, 0x0a, 0x18, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, - 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x65, 0x64, 0x42, 0x6c, 0x6f, 0x62, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x4b, 0x65, 0x79, 0x12, - 0x1d, 0x0a, 0x0a, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x09, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x4b, 0x65, 0x79, 0x12, 0x1d, - 0x0a, 0x0a, 0x63, 0x68, 0x75, 0x6e, 0x6b, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x03, 0x20, 0x01, - 0x28, 0x03, 0x52, 0x09, 0x63, 0x68, 0x75, 0x6e, 0x6b, 0x53, 0x69, 0x7a, 0x65, 0x22, 0x3a, 0x0a, - 0x19, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x65, 0x64, 0x42, 0x6c, - 0x6f, 0x62, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x65, - 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, - 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x22, 0x73, 0x0a, 0x12, 0x55, 0x70, 0x6c, - 0x6f, 0x61, 0x64, 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, - 0x36, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x18, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x73, 0x61, 0x76, 0x65, 0x73, 0x2e, 0x43, 0x68, - 0x75, 0x6e, 0x6b, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x48, 0x00, 0x52, 0x08, 0x6d, - 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x1a, 0x0a, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, - 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x00, 0x52, 0x07, 0x63, 0x6f, 0x6e, 0x74, - 0x65, 0x6e, 0x74, 0x42, 0x09, 0x0a, 0x07, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0xc8, - 0x01, 0x0a, 0x0d, 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, - 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, - 0x16, 0x0a, 0x06, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, - 0x06, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x18, - 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x12, 0x23, 0x0a, 0x04, 0x68, - 0x69, 0x6e, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x6f, 0x70, 0x65, 0x6e, - 0x73, 0x61, 0x76, 0x65, 0x73, 0x2e, 0x48, 0x69, 0x6e, 0x74, 0x52, 0x04, 0x68, 0x69, 0x6e, 0x74, - 0x12, 0x10, 0x0a, 0x03, 0x6d, 0x64, 0x35, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x03, 0x6d, - 0x64, 0x35, 0x12, 0x16, 0x0a, 0x06, 0x63, 0x72, 0x63, 0x33, 0x32, 0x63, 0x18, 0x06, 0x20, 0x01, - 0x28, 0x0d, 0x52, 0x06, 0x63, 0x72, 0x63, 0x33, 0x32, 0x63, 0x12, 0x1d, 0x0a, 0x0a, 0x68, 0x61, - 0x73, 0x5f, 0x63, 0x72, 0x63, 0x33, 0x32, 0x63, 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, - 0x68, 0x61, 0x73, 0x43, 0x72, 0x63, 0x33, 0x32, 0x63, 0x22, 0x60, 0x0a, 0x1a, 0x43, 0x6f, 0x6d, - 0x6d, 0x69, 0x74, 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x65, 0x64, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x65, 0x73, 0x73, 0x69, + 0x43, 0x72, 0x63, 0x33, 0x32, 0x63, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x68, 0x75, 0x6e, 0x6b, 0x65, + 0x64, 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x63, 0x68, 0x75, 0x6e, 0x6b, 0x65, 0x64, + 0x12, 0x1f, 0x0a, 0x0b, 0x63, 0x68, 0x75, 0x6e, 0x6b, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, + 0x09, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a, 0x63, 0x68, 0x75, 0x6e, 0x6b, 0x43, 0x6f, 0x75, 0x6e, + 0x74, 0x22, 0x96, 0x01, 0x0a, 0x18, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x43, 0x68, 0x75, 0x6e, + 0x6b, 0x65, 0x64, 0x42, 0x6c, 0x6f, 0x62, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, + 0x0a, 0x09, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x08, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x4b, 0x65, 0x79, 0x12, 0x1d, 0x0a, 0x0a, 0x72, + 0x65, 0x63, 0x6f, 0x72, 0x64, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x09, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x4b, 0x65, 0x79, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x68, + 0x75, 0x6e, 0x6b, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, + 0x63, 0x68, 0x75, 0x6e, 0x6b, 0x53, 0x69, 0x7a, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x63, 0x68, 0x75, + 0x6e, 0x6b, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a, + 0x63, 0x68, 0x75, 0x6e, 0x6b, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0x3a, 0x0a, 0x19, 0x43, 0x72, + 0x65, 0x61, 0x74, 0x65, 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x65, 0x64, 0x42, 0x6c, 0x6f, 0x62, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x73, 0x65, 0x73, - 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x23, 0x0a, 0x04, 0x68, 0x69, 0x6e, 0x74, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x73, 0x61, 0x76, 0x65, 0x73, - 0x2e, 0x48, 0x69, 0x6e, 0x74, 0x52, 0x04, 0x68, 0x69, 0x6e, 0x74, 0x22, 0x3a, 0x0a, 0x19, 0x41, - 0x62, 0x6f, 0x72, 0x74, 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x65, 0x64, 0x55, 0x70, 0x6c, 0x6f, 0x61, - 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x65, 0x73, 0x73, - 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x73, 0x65, - 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x22, 0x71, 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x42, 0x6c, - 0x6f, 0x62, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x73, 0x74, 0x6f, - 0x72, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x73, 0x74, - 0x6f, 0x72, 0x65, 0x4b, 0x65, 0x79, 0x12, 0x1d, 0x0a, 0x0a, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, - 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x72, 0x65, 0x63, 0x6f, - 0x72, 0x64, 0x4b, 0x65, 0x79, 0x12, 0x23, 0x0a, 0x04, 0x68, 0x69, 0x6e, 0x74, 0x18, 0x03, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x73, 0x61, 0x76, 0x65, 0x73, 0x2e, - 0x48, 0x69, 0x6e, 0x74, 0x52, 0x04, 0x68, 0x69, 0x6e, 0x74, 0x22, 0x70, 0x0a, 0x0f, 0x47, 0x65, - 0x74, 0x42, 0x6c, 0x6f, 0x62, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x35, 0x0a, - 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x17, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x73, 0x61, 0x76, 0x65, 0x73, 0x2e, 0x42, 0x6c, 0x6f, 0x62, + 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x22, 0x73, 0x0a, 0x12, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, + 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x36, 0x0a, 0x08, + 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, + 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x73, 0x61, 0x76, 0x65, 0x73, 0x2e, 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x48, 0x00, 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x1a, 0x0a, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x00, 0x52, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, - 0x42, 0x0a, 0x0a, 0x08, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x99, 0x01, 0x0a, - 0x13, 0x47, 0x65, 0x74, 0x42, 0x6c, 0x6f, 0x62, 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x5f, 0x6b, 0x65, - 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x4b, 0x65, - 0x79, 0x12, 0x1d, 0x0a, 0x0a, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x5f, 0x6b, 0x65, 0x79, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x4b, 0x65, 0x79, - 0x12, 0x21, 0x0a, 0x0c, 0x63, 0x68, 0x75, 0x6e, 0x6b, 0x5f, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, - 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0b, 0x63, 0x68, 0x75, 0x6e, 0x6b, 0x4e, 0x75, 0x6d, - 0x62, 0x65, 0x72, 0x12, 0x23, 0x0a, 0x04, 0x68, 0x69, 0x6e, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, + 0x42, 0x09, 0x0a, 0x07, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0xc8, 0x01, 0x0a, 0x0d, + 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x1d, 0x0a, + 0x0a, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x09, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x16, 0x0a, 0x06, + 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x6e, 0x75, + 0x6d, 0x62, 0x65, 0x72, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x03, 0x52, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x12, 0x23, 0x0a, 0x04, 0x68, 0x69, 0x6e, 0x74, + 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x73, 0x61, 0x76, + 0x65, 0x73, 0x2e, 0x48, 0x69, 0x6e, 0x74, 0x52, 0x04, 0x68, 0x69, 0x6e, 0x74, 0x12, 0x10, 0x0a, + 0x03, 0x6d, 0x64, 0x35, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x03, 0x6d, 0x64, 0x35, 0x12, + 0x16, 0x0a, 0x06, 0x63, 0x72, 0x63, 0x33, 0x32, 0x63, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0d, 0x52, + 0x06, 0x63, 0x72, 0x63, 0x33, 0x32, 0x63, 0x12, 0x1d, 0x0a, 0x0a, 0x68, 0x61, 0x73, 0x5f, 0x63, + 0x72, 0x63, 0x33, 0x32, 0x63, 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x68, 0x61, 0x73, + 0x43, 0x72, 0x63, 0x33, 0x32, 0x63, 0x22, 0x60, 0x0a, 0x1a, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, + 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x65, 0x64, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, + 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, + 0x6e, 0x49, 0x64, 0x12, 0x23, 0x0a, 0x04, 0x68, 0x69, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x73, 0x61, 0x76, 0x65, 0x73, 0x2e, 0x48, 0x69, - 0x6e, 0x74, 0x52, 0x04, 0x68, 0x69, 0x6e, 0x74, 0x22, 0x76, 0x0a, 0x14, 0x47, 0x65, 0x74, 0x42, - 0x6c, 0x6f, 0x62, 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x12, 0x36, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x73, 0x61, 0x76, 0x65, 0x73, 0x2e, 0x43, - 0x68, 0x75, 0x6e, 0x6b, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x48, 0x00, 0x52, 0x08, - 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x1a, 0x0a, 0x07, 0x63, 0x6f, 0x6e, 0x74, - 0x65, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x00, 0x52, 0x07, 0x63, 0x6f, 0x6e, - 0x74, 0x65, 0x6e, 0x74, 0x42, 0x0a, 0x0a, 0x08, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x22, 0x74, 0x0a, 0x11, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x42, 0x6c, 0x6f, 0x62, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x5f, 0x6b, - 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x4b, - 0x65, 0x79, 0x12, 0x1d, 0x0a, 0x0a, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x5f, 0x6b, 0x65, 0x79, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x4b, 0x65, - 0x79, 0x12, 0x23, 0x0a, 0x04, 0x68, 0x69, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x0f, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x73, 0x61, 0x76, 0x65, 0x73, 0x2e, 0x48, 0x69, 0x6e, 0x74, - 0x52, 0x04, 0x68, 0x69, 0x6e, 0x74, 0x22, 0x21, 0x0a, 0x0b, 0x50, 0x69, 0x6e, 0x67, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x69, 0x6e, 0x67, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x04, 0x70, 0x69, 0x6e, 0x67, 0x22, 0x22, 0x0a, 0x0c, 0x50, 0x69, 0x6e, - 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x6f, 0x6e, - 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x70, 0x6f, 0x6e, 0x67, 0x22, 0xfa, 0x01, - 0x0a, 0x15, 0x43, 0x6f, 0x6d, 0x70, 0x61, 0x72, 0x65, 0x41, 0x6e, 0x64, 0x53, 0x77, 0x61, 0x70, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x73, 0x74, 0x6f, 0x72, 0x65, - 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x73, 0x74, 0x6f, 0x72, - 0x65, 0x4b, 0x65, 0x79, 0x12, 0x1d, 0x0a, 0x0a, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x5f, 0x6b, - 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, - 0x4b, 0x65, 0x79, 0x12, 0x23, 0x0a, 0x0d, 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x79, 0x5f, - 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x70, 0x72, 0x6f, 0x70, - 0x65, 0x72, 0x74, 0x79, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x29, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, - 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x73, 0x61, - 0x76, 0x65, 0x73, 0x2e, 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x79, 0x52, 0x05, 0x76, 0x61, - 0x6c, 0x75, 0x65, 0x12, 0x30, 0x0a, 0x09, 0x6f, 0x6c, 0x64, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, - 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x73, 0x61, 0x76, - 0x65, 0x73, 0x2e, 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x79, 0x52, 0x08, 0x6f, 0x6c, 0x64, - 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x23, 0x0a, 0x04, 0x68, 0x69, 0x6e, 0x74, 0x18, 0x06, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x73, 0x61, 0x76, 0x65, 0x73, 0x2e, - 0x48, 0x69, 0x6e, 0x74, 0x52, 0x04, 0x68, 0x69, 0x6e, 0x74, 0x22, 0x5d, 0x0a, 0x16, 0x43, 0x6f, - 0x6d, 0x70, 0x61, 0x72, 0x65, 0x41, 0x6e, 0x64, 0x53, 0x77, 0x61, 0x70, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x12, 0x29, - 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, - 0x6f, 0x70, 0x65, 0x6e, 0x73, 0x61, 0x76, 0x65, 0x73, 0x2e, 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, - 0x74, 0x79, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0xae, 0x01, 0x0a, 0x10, 0x41, 0x74, - 0x6f, 0x6d, 0x69, 0x63, 0x49, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, - 0x0a, 0x09, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x08, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x4b, 0x65, 0x79, 0x12, 0x1d, 0x0a, 0x0a, 0x72, - 0x65, 0x63, 0x6f, 0x72, 0x64, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x09, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x4b, 0x65, 0x79, 0x12, 0x23, 0x0a, 0x0d, 0x70, 0x72, - 0x6f, 0x70, 0x65, 0x72, 0x74, 0x79, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x0c, 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x79, 0x4e, 0x61, 0x6d, 0x65, 0x12, - 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, - 0x76, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x23, 0x0a, 0x04, 0x68, 0x69, 0x6e, 0x74, 0x18, 0x05, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x73, 0x61, 0x76, 0x65, 0x73, 0x2e, - 0x48, 0x69, 0x6e, 0x74, 0x52, 0x04, 0x68, 0x69, 0x6e, 0x74, 0x22, 0x43, 0x0a, 0x11, 0x41, 0x74, - 0x6f, 0x6d, 0x69, 0x63, 0x49, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, - 0x18, 0x0a, 0x07, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, - 0x52, 0x07, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, - 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, - 0xda, 0x01, 0x0a, 0x10, 0x41, 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x49, 0x6e, 0x63, 0x52, 0x65, 0x71, + 0x6e, 0x74, 0x52, 0x04, 0x68, 0x69, 0x6e, 0x74, 0x22, 0x3a, 0x0a, 0x19, 0x41, 0x62, 0x6f, 0x72, + 0x74, 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x65, 0x64, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, + 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x73, 0x65, 0x73, 0x73, 0x69, + 0x6f, 0x6e, 0x49, 0x64, 0x22, 0x71, 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x42, 0x6c, 0x6f, 0x62, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x5f, + 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x73, 0x74, 0x6f, 0x72, 0x65, + 0x4b, 0x65, 0x79, 0x12, 0x1d, 0x0a, 0x0a, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x5f, 0x6b, 0x65, + 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x4b, + 0x65, 0x79, 0x12, 0x23, 0x0a, 0x04, 0x68, 0x69, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x0f, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x73, 0x61, 0x76, 0x65, 0x73, 0x2e, 0x48, 0x69, 0x6e, + 0x74, 0x52, 0x04, 0x68, 0x69, 0x6e, 0x74, 0x22, 0x70, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x42, 0x6c, + 0x6f, 0x62, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x35, 0x0a, 0x08, 0x6d, 0x65, + 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x6f, + 0x70, 0x65, 0x6e, 0x73, 0x61, 0x76, 0x65, 0x73, 0x2e, 0x42, 0x6c, 0x6f, 0x62, 0x4d, 0x65, 0x74, + 0x61, 0x64, 0x61, 0x74, 0x61, 0x48, 0x00, 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, + 0x61, 0x12, 0x1a, 0x0a, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x0c, 0x48, 0x00, 0x52, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x42, 0x0a, 0x0a, + 0x08, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x99, 0x01, 0x0a, 0x13, 0x47, 0x65, + 0x74, 0x42, 0x6c, 0x6f, 0x62, 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x4b, 0x65, 0x79, 0x12, 0x1d, + 0x0a, 0x0a, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x09, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x4b, 0x65, 0x79, 0x12, 0x21, 0x0a, + 0x0c, 0x63, 0x68, 0x75, 0x6e, 0x6b, 0x5f, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x03, 0x52, 0x0b, 0x63, 0x68, 0x75, 0x6e, 0x6b, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, + 0x12, 0x23, 0x0a, 0x04, 0x68, 0x69, 0x6e, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, + 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x73, 0x61, 0x76, 0x65, 0x73, 0x2e, 0x48, 0x69, 0x6e, 0x74, 0x52, + 0x04, 0x68, 0x69, 0x6e, 0x74, 0x22, 0x76, 0x0a, 0x14, 0x47, 0x65, 0x74, 0x42, 0x6c, 0x6f, 0x62, + 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x36, 0x0a, + 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x18, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x73, 0x61, 0x76, 0x65, 0x73, 0x2e, 0x43, 0x68, 0x75, 0x6e, + 0x6b, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x48, 0x00, 0x52, 0x08, 0x6d, 0x65, 0x74, + 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x1a, 0x0a, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x00, 0x52, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, + 0x74, 0x42, 0x0a, 0x0a, 0x08, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x74, 0x0a, + 0x11, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x42, 0x6c, 0x6f, 0x62, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x4b, 0x65, 0x79, 0x12, + 0x1d, 0x0a, 0x0a, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x09, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x4b, 0x65, 0x79, 0x12, 0x23, + 0x0a, 0x04, 0x68, 0x69, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x6f, + 0x70, 0x65, 0x6e, 0x73, 0x61, 0x76, 0x65, 0x73, 0x2e, 0x48, 0x69, 0x6e, 0x74, 0x52, 0x04, 0x68, + 0x69, 0x6e, 0x74, 0x22, 0x21, 0x0a, 0x0b, 0x50, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x69, 0x6e, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x04, 0x70, 0x69, 0x6e, 0x67, 0x22, 0x22, 0x0a, 0x0c, 0x50, 0x69, 0x6e, 0x67, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x6f, 0x6e, 0x67, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x70, 0x6f, 0x6e, 0x67, 0x22, 0xfa, 0x01, 0x0a, 0x15, 0x43, + 0x6f, 0x6d, 0x70, 0x61, 0x72, 0x65, 0x41, 0x6e, 0x64, 0x53, 0x77, 0x61, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x4b, 0x65, 0x79, 0x12, 0x1d, 0x0a, 0x0a, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x4b, 0x65, 0x79, 0x12, 0x23, 0x0a, 0x0d, 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x79, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, - 0x79, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x6c, 0x6f, 0x77, 0x65, 0x72, 0x5f, 0x62, - 0x6f, 0x75, 0x6e, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a, 0x6c, 0x6f, 0x77, 0x65, - 0x72, 0x42, 0x6f, 0x75, 0x6e, 0x64, 0x12, 0x1f, 0x0a, 0x0b, 0x75, 0x70, 0x70, 0x65, 0x72, 0x5f, - 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a, 0x75, 0x70, 0x70, - 0x65, 0x72, 0x42, 0x6f, 0x75, 0x6e, 0x64, 0x12, 0x23, 0x0a, 0x04, 0x68, 0x69, 0x6e, 0x74, 0x18, - 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x73, 0x61, 0x76, 0x65, - 0x73, 0x2e, 0x48, 0x69, 0x6e, 0x74, 0x52, 0x04, 0x68, 0x69, 0x6e, 0x74, 0x2a, 0x5b, 0x0a, 0x0e, - 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x12, 0x09, - 0x0a, 0x05, 0x45, 0x51, 0x55, 0x41, 0x4c, 0x10, 0x00, 0x12, 0x0b, 0x0a, 0x07, 0x47, 0x52, 0x45, - 0x41, 0x54, 0x45, 0x52, 0x10, 0x01, 0x12, 0x08, 0x0a, 0x04, 0x4c, 0x45, 0x53, 0x53, 0x10, 0x02, - 0x12, 0x14, 0x0a, 0x10, 0x47, 0x52, 0x45, 0x41, 0x54, 0x45, 0x52, 0x5f, 0x4f, 0x52, 0x5f, 0x45, - 0x51, 0x55, 0x41, 0x4c, 0x10, 0x03, 0x12, 0x11, 0x0a, 0x0d, 0x4c, 0x45, 0x53, 0x53, 0x5f, 0x4f, - 0x52, 0x5f, 0x45, 0x51, 0x55, 0x41, 0x4c, 0x10, 0x04, 0x32, 0xfc, 0x0e, 0x0a, 0x09, 0x4f, 0x70, - 0x65, 0x6e, 0x53, 0x61, 0x76, 0x65, 0x73, 0x12, 0x40, 0x0a, 0x0b, 0x43, 0x72, 0x65, 0x61, 0x74, - 0x65, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x12, 0x1d, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x73, 0x61, 0x76, - 0x65, 0x73, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x10, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x73, 0x61, 0x76, 0x65, - 0x73, 0x2e, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x22, 0x00, 0x12, 0x3a, 0x0a, 0x08, 0x47, 0x65, 0x74, - 0x53, 0x74, 0x6f, 0x72, 0x65, 0x12, 0x1a, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x73, 0x61, 0x76, 0x65, - 0x73, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x10, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x73, 0x61, 0x76, 0x65, 0x73, 0x2e, 0x53, 0x74, - 0x6f, 0x72, 0x65, 0x22, 0x00, 0x12, 0x4b, 0x0a, 0x0a, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x74, 0x6f, - 0x72, 0x65, 0x73, 0x12, 0x1c, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x73, 0x61, 0x76, 0x65, 0x73, 0x2e, - 0x4c, 0x69, 0x73, 0x74, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x1d, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x73, 0x61, 0x76, 0x65, 0x73, 0x2e, 0x4c, 0x69, - 0x73, 0x74, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x22, 0x00, 0x12, 0x46, 0x0a, 0x0b, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x53, 0x74, 0x6f, 0x72, - 0x65, 0x12, 0x1d, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x73, 0x61, 0x76, 0x65, 0x73, 0x2e, 0x44, 0x65, - 0x6c, 0x65, 0x74, 0x65, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, - 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x12, 0x43, 0x0a, 0x0c, 0x43, 0x72, - 0x65, 0x61, 0x74, 0x65, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x12, 0x1e, 0x2e, 0x6f, 0x70, 0x65, - 0x6e, 0x73, 0x61, 0x76, 0x65, 0x73, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x65, 0x63, - 0x6f, 0x72, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x11, 0x2e, 0x6f, 0x70, 0x65, - 0x6e, 0x73, 0x61, 0x76, 0x65, 0x73, 0x2e, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x22, 0x00, 0x12, - 0x3d, 0x0a, 0x09, 0x47, 0x65, 0x74, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x12, 0x1b, 0x2e, 0x6f, - 0x70, 0x65, 0x6e, 0x73, 0x61, 0x76, 0x65, 0x73, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x65, 0x63, 0x6f, - 0x72, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x11, 0x2e, 0x6f, 0x70, 0x65, 0x6e, - 0x73, 0x61, 0x76, 0x65, 0x73, 0x2e, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x22, 0x00, 0x12, 0x51, - 0x0a, 0x0c, 0x51, 0x75, 0x65, 0x72, 0x79, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x73, 0x12, 0x1e, - 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x73, 0x61, 0x76, 0x65, 0x73, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, - 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, - 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x73, 0x61, 0x76, 0x65, 0x73, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, - 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, - 0x00, 0x12, 0x43, 0x0a, 0x0c, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x63, 0x6f, 0x72, - 0x64, 0x12, 0x1e, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x73, 0x61, 0x76, 0x65, 0x73, 0x2e, 0x55, 0x70, - 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x11, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x73, 0x61, 0x76, 0x65, 0x73, 0x2e, 0x52, 0x65, - 0x63, 0x6f, 0x72, 0x64, 0x22, 0x00, 0x12, 0x48, 0x0a, 0x0c, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, - 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x12, 0x1e, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x73, 0x61, 0x76, - 0x65, 0x73, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, - 0x12, 0x47, 0x0a, 0x0a, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x42, 0x6c, 0x6f, 0x62, 0x12, 0x1c, - 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x73, 0x61, 0x76, 0x65, 0x73, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, - 0x65, 0x42, 0x6c, 0x6f, 0x62, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x6f, - 0x70, 0x65, 0x6e, 0x73, 0x61, 0x76, 0x65, 0x73, 0x2e, 0x42, 0x6c, 0x6f, 0x62, 0x4d, 0x65, 0x74, - 0x61, 0x64, 0x61, 0x74, 0x61, 0x22, 0x00, 0x28, 0x01, 0x12, 0x60, 0x0a, 0x11, 0x43, 0x72, 0x65, - 0x61, 0x74, 0x65, 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x65, 0x64, 0x42, 0x6c, 0x6f, 0x62, 0x12, 0x23, - 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x73, 0x61, 0x76, 0x65, 0x73, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, - 0x65, 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x65, 0x64, 0x42, 0x6c, 0x6f, 0x62, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x24, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x73, 0x61, 0x76, 0x65, 0x73, 0x2e, - 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x65, 0x64, 0x42, 0x6c, 0x6f, - 0x62, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x4a, 0x0a, 0x0b, 0x55, - 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x12, 0x1d, 0x2e, 0x6f, 0x70, 0x65, - 0x6e, 0x73, 0x61, 0x76, 0x65, 0x73, 0x2e, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x43, 0x68, 0x75, - 0x6e, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x6f, 0x70, 0x65, 0x6e, - 0x73, 0x61, 0x76, 0x65, 0x73, 0x2e, 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x4d, 0x65, 0x74, 0x61, 0x64, - 0x61, 0x74, 0x61, 0x22, 0x00, 0x28, 0x01, 0x12, 0x57, 0x0a, 0x13, 0x43, 0x6f, 0x6d, 0x6d, 0x69, - 0x74, 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x65, 0x64, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x12, 0x25, - 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x73, 0x61, 0x76, 0x65, 0x73, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x69, - 0x74, 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x65, 0x64, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x73, 0x61, 0x76, 0x65, - 0x73, 0x2e, 0x42, 0x6c, 0x6f, 0x62, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x22, 0x00, - 0x12, 0x54, 0x0a, 0x12, 0x41, 0x62, 0x6f, 0x72, 0x74, 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x65, 0x64, - 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x12, 0x24, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x73, 0x61, 0x76, - 0x65, 0x73, 0x2e, 0x41, 0x62, 0x6f, 0x72, 0x74, 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x65, 0x64, 0x55, - 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, - 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, - 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x12, 0x44, 0x0a, 0x07, 0x47, 0x65, 0x74, 0x42, 0x6c, 0x6f, - 0x62, 0x12, 0x19, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x73, 0x61, 0x76, 0x65, 0x73, 0x2e, 0x47, 0x65, - 0x74, 0x42, 0x6c, 0x6f, 0x62, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x6f, - 0x70, 0x65, 0x6e, 0x73, 0x61, 0x76, 0x65, 0x73, 0x2e, 0x47, 0x65, 0x74, 0x42, 0x6c, 0x6f, 0x62, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x30, 0x01, 0x12, 0x53, 0x0a, 0x0c, - 0x47, 0x65, 0x74, 0x42, 0x6c, 0x6f, 0x62, 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x12, 0x1e, 0x2e, 0x6f, - 0x70, 0x65, 0x6e, 0x73, 0x61, 0x76, 0x65, 0x73, 0x2e, 0x47, 0x65, 0x74, 0x42, 0x6c, 0x6f, 0x62, - 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x6f, - 0x70, 0x65, 0x6e, 0x73, 0x61, 0x76, 0x65, 0x73, 0x2e, 0x47, 0x65, 0x74, 0x42, 0x6c, 0x6f, 0x62, - 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x30, - 0x01, 0x12, 0x44, 0x0a, 0x0a, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x42, 0x6c, 0x6f, 0x62, 0x12, - 0x1c, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x73, 0x61, 0x76, 0x65, 0x73, 0x2e, 0x44, 0x65, 0x6c, 0x65, - 0x74, 0x65, 0x42, 0x6c, 0x6f, 0x62, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, + 0x79, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x29, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x04, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x73, 0x61, 0x76, 0x65, 0x73, + 0x2e, 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x79, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, + 0x12, 0x30, 0x0a, 0x09, 0x6f, 0x6c, 0x64, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x05, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x73, 0x61, 0x76, 0x65, 0x73, 0x2e, + 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x79, 0x52, 0x08, 0x6f, 0x6c, 0x64, 0x56, 0x61, 0x6c, + 0x75, 0x65, 0x12, 0x23, 0x0a, 0x04, 0x68, 0x69, 0x6e, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x0f, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x73, 0x61, 0x76, 0x65, 0x73, 0x2e, 0x48, 0x69, 0x6e, + 0x74, 0x52, 0x04, 0x68, 0x69, 0x6e, 0x74, 0x22, 0x5d, 0x0a, 0x16, 0x43, 0x6f, 0x6d, 0x70, 0x61, + 0x72, 0x65, 0x41, 0x6e, 0x64, 0x53, 0x77, 0x61, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 0x18, 0x0a, 0x07, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x08, 0x52, 0x07, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x12, 0x29, 0x0a, 0x05, 0x76, + 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x6f, 0x70, 0x65, + 0x6e, 0x73, 0x61, 0x76, 0x65, 0x73, 0x2e, 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x79, 0x52, + 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0xae, 0x01, 0x0a, 0x10, 0x41, 0x74, 0x6f, 0x6d, 0x69, + 0x63, 0x49, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x73, + 0x74, 0x6f, 0x72, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, + 0x73, 0x74, 0x6f, 0x72, 0x65, 0x4b, 0x65, 0x79, 0x12, 0x1d, 0x0a, 0x0a, 0x72, 0x65, 0x63, 0x6f, + 0x72, 0x64, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x72, 0x65, + 0x63, 0x6f, 0x72, 0x64, 0x4b, 0x65, 0x79, 0x12, 0x23, 0x0a, 0x0d, 0x70, 0x72, 0x6f, 0x70, 0x65, + 0x72, 0x74, 0x79, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, + 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x79, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x14, 0x0a, 0x05, + 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x76, 0x61, 0x6c, + 0x75, 0x65, 0x12, 0x23, 0x0a, 0x04, 0x68, 0x69, 0x6e, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x0f, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x73, 0x61, 0x76, 0x65, 0x73, 0x2e, 0x48, 0x69, 0x6e, + 0x74, 0x52, 0x04, 0x68, 0x69, 0x6e, 0x74, 0x22, 0x43, 0x0a, 0x11, 0x41, 0x74, 0x6f, 0x6d, 0x69, + 0x63, 0x49, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07, + 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x75, + 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0xda, 0x01, 0x0a, + 0x10, 0x41, 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x49, 0x6e, 0x63, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x4b, 0x65, 0x79, 0x12, 0x1d, + 0x0a, 0x0a, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x09, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x4b, 0x65, 0x79, 0x12, 0x23, 0x0a, + 0x0d, 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x79, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x79, 0x4e, 0x61, + 0x6d, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x6c, 0x6f, 0x77, 0x65, 0x72, 0x5f, 0x62, 0x6f, 0x75, 0x6e, + 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a, 0x6c, 0x6f, 0x77, 0x65, 0x72, 0x42, 0x6f, + 0x75, 0x6e, 0x64, 0x12, 0x1f, 0x0a, 0x0b, 0x75, 0x70, 0x70, 0x65, 0x72, 0x5f, 0x62, 0x6f, 0x75, + 0x6e, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a, 0x75, 0x70, 0x70, 0x65, 0x72, 0x42, + 0x6f, 0x75, 0x6e, 0x64, 0x12, 0x23, 0x0a, 0x04, 0x68, 0x69, 0x6e, 0x74, 0x18, 0x06, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x73, 0x61, 0x76, 0x65, 0x73, 0x2e, 0x48, + 0x69, 0x6e, 0x74, 0x52, 0x04, 0x68, 0x69, 0x6e, 0x74, 0x2a, 0x5b, 0x0a, 0x0e, 0x46, 0x69, 0x6c, + 0x74, 0x65, 0x72, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x12, 0x09, 0x0a, 0x05, 0x45, + 0x51, 0x55, 0x41, 0x4c, 0x10, 0x00, 0x12, 0x0b, 0x0a, 0x07, 0x47, 0x52, 0x45, 0x41, 0x54, 0x45, + 0x52, 0x10, 0x01, 0x12, 0x08, 0x0a, 0x04, 0x4c, 0x45, 0x53, 0x53, 0x10, 0x02, 0x12, 0x14, 0x0a, + 0x10, 0x47, 0x52, 0x45, 0x41, 0x54, 0x45, 0x52, 0x5f, 0x4f, 0x52, 0x5f, 0x45, 0x51, 0x55, 0x41, + 0x4c, 0x10, 0x03, 0x12, 0x11, 0x0a, 0x0d, 0x4c, 0x45, 0x53, 0x53, 0x5f, 0x4f, 0x52, 0x5f, 0x45, + 0x51, 0x55, 0x41, 0x4c, 0x10, 0x04, 0x32, 0xfc, 0x0e, 0x0a, 0x09, 0x4f, 0x70, 0x65, 0x6e, 0x53, + 0x61, 0x76, 0x65, 0x73, 0x12, 0x40, 0x0a, 0x0b, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x53, 0x74, + 0x6f, 0x72, 0x65, 0x12, 0x1d, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x73, 0x61, 0x76, 0x65, 0x73, 0x2e, + 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x10, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x73, 0x61, 0x76, 0x65, 0x73, 0x2e, 0x53, + 0x74, 0x6f, 0x72, 0x65, 0x22, 0x00, 0x12, 0x3a, 0x0a, 0x08, 0x47, 0x65, 0x74, 0x53, 0x74, 0x6f, + 0x72, 0x65, 0x12, 0x1a, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x73, 0x61, 0x76, 0x65, 0x73, 0x2e, 0x47, + 0x65, 0x74, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x10, + 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x73, 0x61, 0x76, 0x65, 0x73, 0x2e, 0x53, 0x74, 0x6f, 0x72, 0x65, + 0x22, 0x00, 0x12, 0x4b, 0x0a, 0x0a, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x73, + 0x12, 0x1c, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x73, 0x61, 0x76, 0x65, 0x73, 0x2e, 0x4c, 0x69, 0x73, + 0x74, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, + 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x73, 0x61, 0x76, 0x65, 0x73, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x53, + 0x74, 0x6f, 0x72, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, + 0x46, 0x0a, 0x0b, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x12, 0x1d, + 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x73, 0x61, 0x76, 0x65, 0x73, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, + 0x65, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, - 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x12, 0x39, 0x0a, 0x04, 0x50, 0x69, 0x6e, 0x67, 0x12, - 0x16, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x73, 0x61, 0x76, 0x65, 0x73, 0x2e, 0x50, 0x69, 0x6e, 0x67, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x73, 0x61, - 0x76, 0x65, 0x73, 0x2e, 0x50, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x22, 0x00, 0x12, 0x57, 0x0a, 0x0e, 0x43, 0x6f, 0x6d, 0x70, 0x61, 0x72, 0x65, 0x41, 0x6e, 0x64, - 0x53, 0x77, 0x61, 0x70, 0x12, 0x20, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x73, 0x61, 0x76, 0x65, 0x73, - 0x2e, 0x43, 0x6f, 0x6d, 0x70, 0x61, 0x72, 0x65, 0x41, 0x6e, 0x64, 0x53, 0x77, 0x61, 0x70, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x73, 0x61, 0x76, - 0x65, 0x73, 0x2e, 0x43, 0x6f, 0x6d, 0x70, 0x61, 0x72, 0x65, 0x41, 0x6e, 0x64, 0x53, 0x77, 0x61, - 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x57, 0x0a, 0x18, 0x43, - 0x6f, 0x6d, 0x70, 0x61, 0x72, 0x65, 0x41, 0x6e, 0x64, 0x53, 0x77, 0x61, 0x70, 0x47, 0x72, 0x65, - 0x61, 0x74, 0x65, 0x72, 0x49, 0x6e, 0x74, 0x12, 0x1b, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x73, 0x61, - 0x76, 0x65, 0x73, 0x2e, 0x41, 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x49, 0x6e, 0x74, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x73, 0x61, 0x76, 0x65, 0x73, - 0x2e, 0x41, 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x49, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x22, 0x00, 0x12, 0x54, 0x0a, 0x15, 0x43, 0x6f, 0x6d, 0x70, 0x61, 0x72, 0x65, 0x41, - 0x6e, 0x64, 0x53, 0x77, 0x61, 0x70, 0x4c, 0x65, 0x73, 0x73, 0x49, 0x6e, 0x74, 0x12, 0x1b, 0x2e, - 0x6f, 0x70, 0x65, 0x6e, 0x73, 0x61, 0x76, 0x65, 0x73, 0x2e, 0x41, 0x74, 0x6f, 0x6d, 0x69, 0x63, - 0x49, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x6f, 0x70, 0x65, - 0x6e, 0x73, 0x61, 0x76, 0x65, 0x73, 0x2e, 0x41, 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x49, 0x6e, 0x74, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x4b, 0x0a, 0x0c, 0x41, 0x74, - 0x6f, 0x6d, 0x69, 0x63, 0x41, 0x64, 0x64, 0x49, 0x6e, 0x74, 0x12, 0x1b, 0x2e, 0x6f, 0x70, 0x65, + 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x12, 0x43, 0x0a, 0x0c, 0x43, 0x72, 0x65, 0x61, 0x74, + 0x65, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x12, 0x1e, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x73, 0x61, + 0x76, 0x65, 0x73, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x11, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x73, 0x61, + 0x76, 0x65, 0x73, 0x2e, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x22, 0x00, 0x12, 0x3d, 0x0a, 0x09, + 0x47, 0x65, 0x74, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x12, 0x1b, 0x2e, 0x6f, 0x70, 0x65, 0x6e, + 0x73, 0x61, 0x76, 0x65, 0x73, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x11, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x73, 0x61, 0x76, + 0x65, 0x73, 0x2e, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x22, 0x00, 0x12, 0x51, 0x0a, 0x0c, 0x51, + 0x75, 0x65, 0x72, 0x79, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x73, 0x12, 0x1e, 0x2e, 0x6f, 0x70, + 0x65, 0x6e, 0x73, 0x61, 0x76, 0x65, 0x73, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x52, 0x65, 0x63, + 0x6f, 0x72, 0x64, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x6f, 0x70, + 0x65, 0x6e, 0x73, 0x61, 0x76, 0x65, 0x73, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x52, 0x65, 0x63, + 0x6f, 0x72, 0x64, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x43, + 0x0a, 0x0c, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x12, 0x1e, + 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x73, 0x61, 0x76, 0x65, 0x73, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, + 0x65, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x11, + 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x73, 0x61, 0x76, 0x65, 0x73, 0x2e, 0x52, 0x65, 0x63, 0x6f, 0x72, + 0x64, 0x22, 0x00, 0x12, 0x48, 0x0a, 0x0c, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x63, + 0x6f, 0x72, 0x64, 0x12, 0x1e, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x73, 0x61, 0x76, 0x65, 0x73, 0x2e, + 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x12, 0x47, 0x0a, + 0x0a, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x42, 0x6c, 0x6f, 0x62, 0x12, 0x1c, 0x2e, 0x6f, 0x70, + 0x65, 0x6e, 0x73, 0x61, 0x76, 0x65, 0x73, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x42, 0x6c, + 0x6f, 0x62, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x6f, 0x70, 0x65, 0x6e, + 0x73, 0x61, 0x76, 0x65, 0x73, 0x2e, 0x42, 0x6c, 0x6f, 0x62, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, + 0x74, 0x61, 0x22, 0x00, 0x28, 0x01, 0x12, 0x60, 0x0a, 0x11, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, + 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x65, 0x64, 0x42, 0x6c, 0x6f, 0x62, 0x12, 0x23, 0x2e, 0x6f, 0x70, + 0x65, 0x6e, 0x73, 0x61, 0x76, 0x65, 0x73, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x43, 0x68, + 0x75, 0x6e, 0x6b, 0x65, 0x64, 0x42, 0x6c, 0x6f, 0x62, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x24, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x73, 0x61, 0x76, 0x65, 0x73, 0x2e, 0x43, 0x72, 0x65, + 0x61, 0x74, 0x65, 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x65, 0x64, 0x42, 0x6c, 0x6f, 0x62, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x4a, 0x0a, 0x0b, 0x55, 0x70, 0x6c, 0x6f, + 0x61, 0x64, 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x12, 0x1d, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x73, 0x61, + 0x76, 0x65, 0x73, 0x2e, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x73, 0x61, 0x76, + 0x65, 0x73, 0x2e, 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, + 0x22, 0x00, 0x28, 0x01, 0x12, 0x57, 0x0a, 0x13, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x43, 0x68, + 0x75, 0x6e, 0x6b, 0x65, 0x64, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x12, 0x25, 0x2e, 0x6f, 0x70, + 0x65, 0x6e, 0x73, 0x61, 0x76, 0x65, 0x73, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x43, 0x68, + 0x75, 0x6e, 0x6b, 0x65, 0x64, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x73, 0x61, 0x76, 0x65, 0x73, 0x2e, 0x42, + 0x6c, 0x6f, 0x62, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x22, 0x00, 0x12, 0x54, 0x0a, + 0x12, 0x41, 0x62, 0x6f, 0x72, 0x74, 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x65, 0x64, 0x55, 0x70, 0x6c, + 0x6f, 0x61, 0x64, 0x12, 0x24, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x73, 0x61, 0x76, 0x65, 0x73, 0x2e, + 0x41, 0x62, 0x6f, 0x72, 0x74, 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x65, 0x64, 0x55, 0x70, 0x6c, 0x6f, + 0x61, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, + 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, + 0x79, 0x22, 0x00, 0x12, 0x44, 0x0a, 0x07, 0x47, 0x65, 0x74, 0x42, 0x6c, 0x6f, 0x62, 0x12, 0x19, + 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x73, 0x61, 0x76, 0x65, 0x73, 0x2e, 0x47, 0x65, 0x74, 0x42, 0x6c, + 0x6f, 0x62, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x6f, 0x70, 0x65, 0x6e, + 0x73, 0x61, 0x76, 0x65, 0x73, 0x2e, 0x47, 0x65, 0x74, 0x42, 0x6c, 0x6f, 0x62, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x30, 0x01, 0x12, 0x53, 0x0a, 0x0c, 0x47, 0x65, 0x74, + 0x42, 0x6c, 0x6f, 0x62, 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x12, 0x1e, 0x2e, 0x6f, 0x70, 0x65, 0x6e, + 0x73, 0x61, 0x76, 0x65, 0x73, 0x2e, 0x47, 0x65, 0x74, 0x42, 0x6c, 0x6f, 0x62, 0x43, 0x68, 0x75, + 0x6e, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x6f, 0x70, 0x65, 0x6e, + 0x73, 0x61, 0x76, 0x65, 0x73, 0x2e, 0x47, 0x65, 0x74, 0x42, 0x6c, 0x6f, 0x62, 0x43, 0x68, 0x75, + 0x6e, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x30, 0x01, 0x12, 0x44, + 0x0a, 0x0a, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x42, 0x6c, 0x6f, 0x62, 0x12, 0x1c, 0x2e, 0x6f, + 0x70, 0x65, 0x6e, 0x73, 0x61, 0x76, 0x65, 0x73, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x42, + 0x6c, 0x6f, 0x62, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, + 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, + 0x74, 0x79, 0x22, 0x00, 0x12, 0x39, 0x0a, 0x04, 0x50, 0x69, 0x6e, 0x67, 0x12, 0x16, 0x2e, 0x6f, + 0x70, 0x65, 0x6e, 0x73, 0x61, 0x76, 0x65, 0x73, 0x2e, 0x50, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x73, 0x61, 0x76, 0x65, 0x73, + 0x2e, 0x50, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, + 0x57, 0x0a, 0x0e, 0x43, 0x6f, 0x6d, 0x70, 0x61, 0x72, 0x65, 0x41, 0x6e, 0x64, 0x53, 0x77, 0x61, + 0x70, 0x12, 0x20, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x73, 0x61, 0x76, 0x65, 0x73, 0x2e, 0x43, 0x6f, + 0x6d, 0x70, 0x61, 0x72, 0x65, 0x41, 0x6e, 0x64, 0x53, 0x77, 0x61, 0x70, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x73, 0x61, 0x76, 0x65, 0x73, 0x2e, + 0x43, 0x6f, 0x6d, 0x70, 0x61, 0x72, 0x65, 0x41, 0x6e, 0x64, 0x53, 0x77, 0x61, 0x70, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x57, 0x0a, 0x18, 0x43, 0x6f, 0x6d, 0x70, + 0x61, 0x72, 0x65, 0x41, 0x6e, 0x64, 0x53, 0x77, 0x61, 0x70, 0x47, 0x72, 0x65, 0x61, 0x74, 0x65, + 0x72, 0x49, 0x6e, 0x74, 0x12, 0x1b, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x73, 0x61, 0x76, 0x65, 0x73, + 0x2e, 0x41, 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x49, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x1c, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x73, 0x61, 0x76, 0x65, 0x73, 0x2e, 0x41, 0x74, + 0x6f, 0x6d, 0x69, 0x63, 0x49, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, + 0x00, 0x12, 0x54, 0x0a, 0x15, 0x43, 0x6f, 0x6d, 0x70, 0x61, 0x72, 0x65, 0x41, 0x6e, 0x64, 0x53, + 0x77, 0x61, 0x70, 0x4c, 0x65, 0x73, 0x73, 0x49, 0x6e, 0x74, 0x12, 0x1b, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x73, 0x61, 0x76, 0x65, 0x73, 0x2e, 0x41, 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x49, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x73, 0x61, 0x76, 0x65, 0x73, 0x2e, 0x41, 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x49, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x4b, 0x0a, 0x0c, 0x41, 0x74, 0x6f, 0x6d, 0x69, - 0x63, 0x53, 0x75, 0x62, 0x49, 0x6e, 0x74, 0x12, 0x1b, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x73, 0x61, + 0x63, 0x41, 0x64, 0x64, 0x49, 0x6e, 0x74, 0x12, 0x1b, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x73, 0x61, 0x76, 0x65, 0x73, 0x2e, 0x41, 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x49, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x73, 0x61, 0x76, 0x65, 0x73, 0x2e, 0x41, 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x49, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x22, 0x00, 0x12, 0x48, 0x0a, 0x09, 0x41, 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x49, 0x6e, - 0x63, 0x12, 0x1b, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x73, 0x61, 0x76, 0x65, 0x73, 0x2e, 0x41, 0x74, - 0x6f, 0x6d, 0x69, 0x63, 0x49, 0x6e, 0x63, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, + 0x73, 0x65, 0x22, 0x00, 0x12, 0x4b, 0x0a, 0x0c, 0x41, 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x53, 0x75, + 0x62, 0x49, 0x6e, 0x74, 0x12, 0x1b, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x73, 0x61, 0x76, 0x65, 0x73, + 0x2e, 0x41, 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x49, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x1c, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x73, 0x61, 0x76, 0x65, 0x73, 0x2e, 0x41, 0x74, + 0x6f, 0x6d, 0x69, 0x63, 0x49, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, + 0x00, 0x12, 0x48, 0x0a, 0x09, 0x41, 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x49, 0x6e, 0x63, 0x12, 0x1b, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x73, 0x61, 0x76, 0x65, 0x73, 0x2e, 0x41, 0x74, 0x6f, 0x6d, 0x69, - 0x63, 0x49, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x48, - 0x0a, 0x09, 0x41, 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x44, 0x65, 0x63, 0x12, 0x1b, 0x2e, 0x6f, 0x70, + 0x63, 0x49, 0x6e, 0x63, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x73, 0x61, 0x76, 0x65, 0x73, 0x2e, 0x41, 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x49, 0x6e, - 0x63, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x73, - 0x61, 0x76, 0x65, 0x73, 0x2e, 0x41, 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x49, 0x6e, 0x74, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, 0x30, 0x5a, 0x2e, 0x67, 0x69, 0x74, 0x68, - 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x66, 0x6f, 0x72, - 0x67, 0x61, 0x6d, 0x65, 0x73, 0x2f, 0x6f, 0x70, 0x65, 0x6e, 0x2d, 0x73, 0x61, 0x76, 0x65, 0x73, - 0x3b, 0x6f, 0x70, 0x65, 0x6e, 0x73, 0x61, 0x76, 0x65, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x33, + 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x48, 0x0a, 0x09, 0x41, + 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x44, 0x65, 0x63, 0x12, 0x1b, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x73, + 0x61, 0x76, 0x65, 0x73, 0x2e, 0x41, 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x49, 0x6e, 0x63, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x73, 0x61, 0x76, 0x65, + 0x73, 0x2e, 0x41, 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x49, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, 0x30, 0x5a, 0x2e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, + 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x66, 0x6f, 0x72, 0x67, 0x61, 0x6d, + 0x65, 0x73, 0x2f, 0x6f, 0x70, 0x65, 0x6e, 0x2d, 0x73, 0x61, 0x76, 0x65, 0x73, 0x3b, 0x6f, 0x70, + 0x65, 0x6e, 0x73, 0x61, 0x76, 0x65, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/api/open_saves.proto b/api/open_saves.proto index 647016c0..3034e9c1 100644 --- a/api/open_saves.proto +++ b/api/open_saves.proto @@ -317,7 +317,6 @@ message DeleteStoreRequest { string key = 1; } - message CreateRecordRequest { // The key of the store to create the record into. string store_key = 1; @@ -343,7 +342,6 @@ message GetRecordRequest { // QueryRecordsRequest contains conditions to search particular records. // Multiple conditions are AND'ed together. message QueryRecordsRequest { - // store_key is the primary key of the store. // Optional and the method queries all stores when omitted. string store_key = 1; @@ -498,6 +496,12 @@ message BlobMetadata { // has_crc32c indicates if crc32c is present. bool has_crc32c = 7; + + // chunked is set true if the attached blob is chunked, otherwise false (read only). + bool chunked = 8; + + // Number of chunks (read only). + int64 chunk_count = 9; } message CreateChunkedBlobRequest { @@ -509,6 +513,11 @@ message CreateChunkedBlobRequest { // Size of each chunk int64 chunk_size = 3; + + // Expected number of chunks. + // When set to non-zero, the server checks if it has received the exact number of + // chunks when CommitChunkedUpload is called. + int64 chunk_count = 4; } message CreateChunkedBlobResponse { @@ -533,7 +542,7 @@ message ChunkMetadata { // session_id is the ID of a chunk upload session. Not used for downloads. string session_id = 1; - // number is the number + // number is the number of the chunk. int64 number = 2; // size is a byte size of the chunk. diff --git a/docs/reference.md b/docs/reference.md index ee5e01f0..db3e0999 100644 --- a/docs/reference.md +++ b/docs/reference.md @@ -149,6 +149,8 @@ when creating (uploading) a new blob object. | md5 | [bytes](#bytes) | | md5 is the MD5 hash of the blob content. If supplied for uploads, the server validates the content using the hash value. For downloads, the server returns the stored hash value of the blob content. The length of the hash value is 0 (not present) or 16 (present) bytes. | | crc32c | [uint32](#uint32) | | crc32c is the CRC32C checksum of the blob content. Specifically, it uses the Castagnoli polynomial. https://pkg.go.dev/hash/crc32#pkg-constants If supplied for uploads, the server validates the content using the checksum. For downloads, the server returns the checksum of the blob content. Open Saves provides both MD5 and CRC32C because CRC32C is often used by Cloud object storage services. | | has_crc32c | [bool](#bool) | | has_crc32c indicates if crc32c is present. | +| chunked | [bool](#bool) | | chunked is set true if the attached blob is chunked, otherwise false (read only). | +| chunk_count | [int64](#int64) | | Number of chunks (read only). | @@ -164,7 +166,7 @@ when creating (uploading) a new blob object. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | session_id | [string](#string) | | session_id is the ID of a chunk upload session. Not used for downloads. | -| number | [int64](#int64) | | number is the number | +| number | [int64](#int64) | | number is the number of the chunk. | | size | [int64](#int64) | | size is a byte size of the chunk. | | hint | [Hint](#opensaves-Hint) | | Performance hints (write only). | | md5 | [bytes](#bytes) | | md5 is the MD5 hash of the chunk content. If supplied for uploads, the server validates the content using the hash value. For downloads, the server returns the stored hash value of the chunk content. The length of the hash value is 0 (not present) or 16 (present) bytes. | @@ -258,6 +260,7 @@ should contain content. | store_key | [string](#string) | | store_key is the key of the store that the record belongs to. | | record_key | [string](#string) | | record_key is the key of the record the new blob object belongs to. | | chunk_size | [int64](#int64) | | Size of each chunk | +| chunk_count | [int64](#int64) | | Expected number of chunks. When set to non-zero, the server checks if it has received the exact number of chunks when CommitChunkedUpload is called. | diff --git a/internal/app/collector/collector_test.go b/internal/app/collector/collector_test.go index b1d7da85..95305939 100644 --- a/internal/app/collector/collector_test.go +++ b/internal/app/collector/collector_test.go @@ -147,13 +147,13 @@ func TestCollector_DeletesBlobs(t *testing.T) { collector := newTestCollector(ctx, t) store := setupTestStore(ctx, t, collector) record := setupTestRecord(ctx, t, collector, store.Key) - const numBlobRefs = 5 - blobRefs := make([]*blobref.BlobRef, 0, numBlobRefs) + const blobRefCount = 5 + blobRefs := make([]*blobref.BlobRef, 0, blobRefCount) // 0 and 2 are old, to be deleted // 1 and 3 have the applicable statuses but new // 4 is still initializing - for i := 0; i < numBlobRefs; i++ { + for i := 0; i < blobRefCount; i++ { blobRef := blobref.NewBlobRef(0, store.Key, record.Key) blobRef.Timestamps.CreatedAt = collector.cfg.Before blobRef.Timestamps.UpdatedAt = collector.cfg.Before @@ -197,10 +197,10 @@ func TestCollector_DeletesUnlinkedBlobRefs(t *testing.T) { collector := newTestCollector(ctx, t) store := setupTestStore(ctx, t, collector) record := setupTestRecord(ctx, t, collector, store.Key) - const numBlobRefs = 3 - blobRefs := make([]*blobref.BlobRef, 0, numBlobRefs) + const blobRefCount = 3 + blobRefs := make([]*blobref.BlobRef, 0, blobRefCount) ds := newDatastoreClient(ctx, t) - for i := 0; i < numBlobRefs; i++ { + for i := 0; i < blobRefCount; i++ { blobRef := blobref.NewBlobRef(0, store.Key, record.Key) blobRef.Fail() // Fail() updates Timestamps so needs to come here. blobRef.Timestamps.CreatedAt = collector.cfg.Before.Add(-1 * time.Second) @@ -226,15 +226,15 @@ func TestCollector_DeletesUnlinkedBlobRefs(t *testing.T) { } func TestCollector_DeletesChunkedBlobs(t *testing.T) { + const chunkRefCount = 5 + ctx := context.Background() collector := newTestCollector(ctx, t) store := setupTestStore(ctx, t, collector) record := setupTestRecord(ctx, t, collector, store.Key) - blob := blobref.NewChunkedBlobRef(store.Key, record.Key) + blob := blobref.NewChunkedBlobRef(store.Key, record.Key, chunkRefCount) ds := newDatastoreClient(ctx, t) setupTestBlobRef(ctx, t, ds, blob) - - const chunkRefCount = 5 chunks := make([]*chunkref.ChunkRef, 0, chunkRefCount) // 0 and 2 are old, to be deleted diff --git a/internal/app/server/open_saves.go b/internal/app/server/open_saves.go index ff0de59d..5fbc5472 100644 --- a/internal/app/server/open_saves.go +++ b/internal/app/server/open_saves.go @@ -490,7 +490,7 @@ func (s *openSavesServer) Ping(ctx context.Context, req *pb.PingRequest) (*pb.Pi } func (s *openSavesServer) CreateChunkedBlob(ctx context.Context, req *pb.CreateChunkedBlobRequest) (*pb.CreateChunkedBlobResponse, error) { - b := blobref.NewChunkedBlobRef(req.GetStoreKey(), req.GetRecordKey()) + b := blobref.NewChunkedBlobRef(req.GetStoreKey(), req.GetRecordKey(), req.GetChunkCount()) b, err := s.metaDB.InsertBlobRef(ctx, b) if err != nil { log.Errorf("CreateChunkedBlob failed for store (%v), record (%v): %v", req.GetStoreKey(), req.GetRecordKey(), err) @@ -609,7 +609,7 @@ func (s *openSavesServer) CommitChunkedUpload(ctx context.Context, req *pb.Commi log.Errorf("Cannot retrieve chunked blob metadata for session (%v): %v", blobKey, err) return nil, err } - record, blob, err := s.metaDB.PromoteBlobRefToCurrent(ctx, blob) + record, _, err := s.metaDB.PromoteBlobRefToCurrent(ctx, blob) if err != nil { log.Errorf("PromoteBlobRefToCurrent failed for object %v: %v", blob.ObjectPath(), err) // Do not delete the blob object here. Leave it to the garbage collector. diff --git a/internal/app/server/open_saves_test.go b/internal/app/server/open_saves_test.go index 271f3c85..cdc13421 100644 --- a/internal/app/server/open_saves_test.go +++ b/internal/app/server/open_saves_test.go @@ -1557,6 +1557,95 @@ func TestOpenSaves_UploadChunkedBlob(t *testing.T) { SessionId: sessionId, }); assert.NoError(t, err) { assert.Equal(t, int64(len(testChunk)*chunkCount), meta.Size) + assert.True(t, meta.Chunked) + assert.Equal(t, int64(chunkCount), meta.ChunkCount) + assert.False(t, meta.HasCrc32C) + assert.Empty(t, meta.Md5) + assert.Equal(t, store.Key, meta.StoreKey) + assert.Equal(t, record.Key, meta.RecordKey) + } + + // Check if the metadata is reflected to the record as well. + if updatedRecord, err := client.GetRecord(ctx, &pb.GetRecordRequest{ + StoreKey: store.Key, Key: record.Key, + }); assert.NoError(t, err) { + if assert.NotNil(t, updatedRecord) { + assert.Equal(t, int64(len(testChunk)*chunkCount), updatedRecord.BlobSize) + assert.Equal(t, int64(chunkCount), updatedRecord.ChunkCount) + assert.True(t, updatedRecord.Chunked) + assert.True(t, record.GetCreatedAt().AsTime().Equal(updatedRecord.GetCreatedAt().AsTime())) + assert.True(t, beforeCreateChunk.Before(updatedRecord.GetUpdatedAt().AsTime())) + assert.NotEqual(t, record.Signature, updatedRecord.Signature) + } + } + + for i := 0; i < chunkCount; i++ { + verifyChunk(ctx, t, client, store.Key, record.Key, sessionId, int64(i), testChunk) + } + + // Deletion test + if _, err := client.DeleteBlob(ctx, &pb.DeleteBlobRequest{ + StoreKey: store.Key, + RecordKey: record.Key, + }); err != nil { + t.Errorf("DeleteBlob failed: %v", err) + } + verifyBlob(ctx, t, client, store.Key, record.Key, make([]byte, 0)) +} + +func TestOpenSaves_UploadChunkedBlobWithChunkCount(t *testing.T) { + ctx := context.Background() + _, listener := getOpenSavesServer(ctx, t, "gcp") + _, client := getTestClient(ctx, t, listener) + store := &pb.Store{Key: uuid.NewString()} + setupTestStore(ctx, t, client, store) + record := &pb.Record{Key: uuid.NewString()} + record = setupTestRecord(ctx, t, client, store.Key, record) + + const chunkSize = 1025 + const chunkCount = 4 + testChunk := make([]byte, chunkSize) + for i := 0; i < chunkSize; i++ { + testChunk[i] = byte(i % 256) + } + + beforeCreateChunk := time.Now() + var sessionId string + + if res, err := client.CreateChunkedBlob(ctx, &pb.CreateChunkedBlobRequest{ + StoreKey: store.Key, + RecordKey: record.Key, + ChunkSize: chunkSize, + ChunkCount: chunkCount, + }); assert.NoError(t, err) { + if assert.NotNil(t, res) { + _, err := uuid.Parse(res.SessionId) + assert.NoError(t, err) + sessionId = res.SessionId + } + } + t.Cleanup(func() { + client.DeleteBlob(ctx, &pb.DeleteBlobRequest{StoreKey: store.Key, RecordKey: record.Key}) + }) + + for i := 0; i < chunkCount-1; i++ { + uploadChunk(ctx, t, client, sessionId, int64(i), testChunk) + // UploadChunk shouldn't update Signature. + if actual, err := client.GetRecord(ctx, &pb.GetRecordRequest{StoreKey: store.Key, Key: record.Key}); assert.NoError(t, err) { + assert.Equal(t, record.Signature, actual.Signature) + } + b, err := client.CommitChunkedUpload(ctx, &pb.CommitChunkedUploadRequest{SessionId: sessionId}) + assert.Nil(t, b) + assert.Equal(t, codes.FailedPrecondition, status.Code(err)) + } + uploadChunk(ctx, t, client, sessionId, chunkCount-1, testChunk) + + if meta, err := client.CommitChunkedUpload(ctx, &pb.CommitChunkedUploadRequest{ + SessionId: sessionId, + }); assert.NoError(t, err) { + assert.Equal(t, int64(len(testChunk)*chunkCount), meta.Size) + assert.True(t, meta.Chunked) + assert.Equal(t, int64(chunkCount), meta.ChunkCount) assert.False(t, meta.HasCrc32C) assert.Empty(t, meta.Md5) assert.Equal(t, store.Key, meta.StoreKey) diff --git a/internal/pkg/metadb/blobref/blobref.go b/internal/pkg/metadb/blobref/blobref.go index 1f38f89b..67d11424 100644 --- a/internal/pkg/metadb/blobref/blobref.go +++ b/internal/pkg/metadb/blobref/blobref.go @@ -38,6 +38,10 @@ type BlobRef struct { RecordKey string // Chunked is whether the BlobRef is chunked or not. Chunked bool + // ChunkCount is the number of chunks that should be associated to the BlobRef. + // It is set by either the client when starting a chunk upload or + // the server when committing a chunked upload. + ChunkCount int64 // Checksums have checksums for each blob object associated with the BlobRef entity. // Record.{MD5,CRC32C} must be used for inline blobs, and @@ -95,12 +99,13 @@ func NewBlobRef(size int64, storeKey, recordKey string) *BlobRef { } } -// NewChunkedBlobRef creates a new BlobRef object with Size and Chunked -// set 0 and true, respectively. +// NewChunkedBlobRef creates a new BlobRef object with Size, Chunked, and +// ChunkCount set to 0, true, and chunkCount respectively. // Other behaviors are the same as NewBlobRef -func NewChunkedBlobRef(storeKey, recordKey string) *BlobRef { +func NewChunkedBlobRef(storeKey, recordKey string, chunkCount int64) *BlobRef { b := NewBlobRef(0, storeKey, recordKey) b.Chunked = true + b.ChunkCount = chunkCount return b } @@ -112,11 +117,13 @@ func (b *BlobRef) ObjectPath() string { // ToProto returns a BlobMetadata representation of the object. func (b *BlobRef) ToProto() *pb.BlobMetadata { return &pb.BlobMetadata{ - StoreKey: b.StoreKey, - RecordKey: b.RecordKey, - Size: b.Size, - Md5: b.MD5, - Crc32C: b.GetCRC32C(), - HasCrc32C: b.HasCRC32C, + StoreKey: b.StoreKey, + RecordKey: b.RecordKey, + Size: b.Size, + Md5: b.MD5, + Chunked: b.Chunked, + ChunkCount: b.ChunkCount, + Crc32C: b.GetCRC32C(), + HasCrc32C: b.HasCRC32C, } } diff --git a/internal/pkg/metadb/blobref/blobref_test.go b/internal/pkg/metadb/blobref/blobref_test.go index 4d161b2c..ea876e85 100644 --- a/internal/pkg/metadb/blobref/blobref_test.go +++ b/internal/pkg/metadb/blobref/blobref_test.go @@ -24,6 +24,22 @@ import ( "github.com/stretchr/testify/assert" ) +func TestBlobRef_New(t *testing.T) { + const ( + store = "store" + record = "record" + chunkCount = int64(42) + ) + + b := NewChunkedBlobRef(store, record, chunkCount) + if assert.NotNil(t, b) { + assert.Equal(t, store, b.StoreKey) + assert.Equal(t, record, b.RecordKey) + assert.NotEqual(t, uuid.Nil, b.Key) + assert.Equal(t, chunkCount, b.ChunkCount) + } +} + func TestBlobRef_LoadKey(t *testing.T) { key := uuid.MustParse("d13c289c-8845-485f-b582-c87342d5dade") blob := new(BlobRef) @@ -37,6 +53,7 @@ func TestBlobRef_Save(t *testing.T) { objectName = "object name" store = "store" record = "record" + chunkCount = int64(42) ) blob := BlobRef{ @@ -44,6 +61,7 @@ func TestBlobRef_Save(t *testing.T) { Status: StatusInitializing, StoreKey: store, RecordKey: record, + ChunkCount: chunkCount, Checksums: checksumstest.RandomChecksums(t), Timestamps: timestamps.New(), } @@ -69,14 +87,18 @@ func TestBlobRef_Save(t *testing.T) { Name: "Chunked", Value: false, }, + { + Name: "ChunkCount", + Value: chunkCount, + }, } actual, err := blob.Save() assert.NoError(t, err) if assert.NotNil(t, actual) { assert.Equal(t, expected, actual[:len(expected)]) - if assert.Equal(t, 9, len(actual)) { - checksumstest.AssertPropertyListMatch(t, blob.Checksums, actual[5:8]) - assert.Equal(t, "Timestamps", actual[8].Name) + if assert.Equal(t, len(expected)+3+1, len(actual)) { + checksumstest.AssertPropertyListMatch(t, blob.Checksums, actual[len(expected):len(expected)+3]) + assert.Equal(t, "Timestamps", actual[len(expected)+3].Name) } } } @@ -87,6 +109,7 @@ func TestBlobRef_Load(t *testing.T) { objectName = "object name" store = "store" record = "record" + chunkCount = int64(551) ) properties := []datastore.Property{ { @@ -105,13 +128,23 @@ func TestBlobRef_Load(t *testing.T) { Name: "RecordKey", Value: record, }, + { + Name: "Chunked", + Value: true, + }, + { + Name: "ChunkCount", + Value: chunkCount, + }, } expected := &BlobRef{ - Size: 123, - Status: StatusReady, - StoreKey: store, - RecordKey: record, - Checksums: checksumstest.RandomChecksums(t), + Size: size, + Status: StatusReady, + StoreKey: store, + RecordKey: record, + Chunked: true, + ChunkCount: chunkCount, + Checksums: checksumstest.RandomChecksums(t), } properties = append(properties, checksumstest.ChecksumsToProperties(t, expected.Checksums)...) actual := new(BlobRef) @@ -129,18 +162,23 @@ func TestBlobRef_GetObjectPath(t *testing.T) { func TestBlobRef_ToProto(t *testing.T) { const ( - size = int64(123) - store = "store" - record = "record" + size = int64(123) + store = "store" + record = "record" + chunkCount = int64(42) ) - b := NewBlobRef(size, store, record) + b := NewChunkedBlobRef(store, record, chunkCount) b.Checksums = checksumstest.RandomChecksums(t) + b.Size = size + b.ChunkCount = chunkCount proto := b.ToProto() if assert.NotNil(t, proto) { assert.Equal(t, b.StoreKey, proto.GetStoreKey()) assert.Equal(t, b.RecordKey, proto.GetRecordKey()) assert.Equal(t, b.Size, proto.GetSize()) + assert.True(t, b.Chunked) + assert.Equal(t, b.ChunkCount, chunkCount) checksumstest.AssertProtoEqual(t, b.Checksums, proto) } } diff --git a/internal/pkg/metadb/metadb.go b/internal/pkg/metadb/metadb.go index 3ed652d8..c3fa4004 100644 --- a/internal/pkg/metadb/metadb.go +++ b/internal/pkg/metadb/metadb.go @@ -510,6 +510,10 @@ func (m *MetaDB) PromoteBlobRefToCurrent(ctx context.Context, blob *blobref.Blob if err != nil { return err } + if blob.ChunkCount != 0 && blob.ChunkCount != count { + return status.Errorf(codes.FailedPrecondition, "expected chunk count doesn't match: expected (%v), actual (%v)", blob.ChunkCount, count) + } + blob.ChunkCount = count record.ChunkCount = count blob.Size = size } else { diff --git a/internal/pkg/metadb/metadb_test.go b/internal/pkg/metadb/metadb_test.go index 7bd59758..6dfed063 100644 --- a/internal/pkg/metadb/metadb_test.go +++ b/internal/pkg/metadb/metadb_test.go @@ -513,7 +513,7 @@ func TestMetaDB_SimpleCreateGetDeleteBlobRef(t *testing.T) { assert.Nil(t, promoRecord.Blob) assert.Equal(t, blobKey, promoRecord.ExternalBlob) assert.Equal(t, blob.Size, promoRecord.BlobSize) - assert.Zero(t, promoRecord.Chunked) + assert.False(t, promoRecord.Chunked) assert.Zero(t, promoRecord.ChunkCount) assert.True(t, beforePromo.Before(promoRecord.Timestamps.UpdatedAt)) assert.NotEqual(t, record.Timestamps.Signature, promoRecord.Timestamps.Signature) @@ -521,6 +521,8 @@ func TestMetaDB_SimpleCreateGetDeleteBlobRef(t *testing.T) { if assert.NotNil(t, promoBlob) { assert.Equal(t, blobKey, promoBlob.Key) assert.Equal(t, blobref.StatusReady, promoBlob.Status) + assert.False(t, promoBlob.Chunked) + assert.Zero(t, promoBlob.ChunkCount) assert.True(t, beforePromo.Before(promoBlob.Timestamps.UpdatedAt)) assert.NotEqual(t, origSig, promoBlob.Timestamps.Signature) } @@ -547,7 +549,7 @@ func TestMetaDB_SimpleCreateGetDeleteBlobRef(t *testing.T) { assert.Equal(t, uuid.Nil, delPendRecord.ExternalBlob) assert.Empty(t, delPendRecord.Blob) assert.Zero(t, delPendRecord.BlobSize) - assert.Zero(t, delPendRecord.Chunked) + assert.False(t, delPendRecord.Chunked) assert.Zero(t, delPendRecord.ChunkCount) } if assert.NotNil(t, delPendBlob) { @@ -932,7 +934,7 @@ func TestMetaDB_DeleteRecordWithNonExistentBlobRef(t *testing.T) { func TestMetaDB_SimpleCreateGetDeleteChunkedBlob(t *testing.T) { const ( testChunkCount = 3 - testChunKSize = int32(42) + testChunkSize = int32(42) ) ctx := context.Background() @@ -951,7 +953,7 @@ func TestMetaDB_SimpleCreateGetDeleteChunkedBlob(t *testing.T) { // Mark the chunks ready for _, chunk := range chunks { - chunk.Size = testChunKSize + chunk.Size = testChunkSize now := time.Now() if err := metaDB.MarkChunkRefReady(ctx, chunk); err != nil { t.Fatalf("MarkChunkRefReady failed for chunk (%v): %v", chunk.Key, err) @@ -970,21 +972,24 @@ func TestMetaDB_SimpleCreateGetDeleteChunkedBlob(t *testing.T) { t.Fatalf("PromoteBlobRefToCurrent failed: %v", err) } if assert.NotNil(t, record) { - assert.EqualValues(t, testChunKSize*testChunkCount, record.BlobSize) + assert.EqualValues(t, testChunkSize*testChunkCount, record.BlobSize) assert.EqualValues(t, testChunkCount, record.ChunkCount) assert.True(t, record.Chunked) } if assert.NotNil(t, blobRetrieved) { - assert.EqualValues(t, testChunKSize*testChunkCount, blobRetrieved.Size) + assert.EqualValues(t, testChunkSize*testChunkCount, blobRetrieved.Size) + assert.True(t, blobRetrieved.Chunked) + assert.Equal(t, int64(testChunkCount), blobRetrieved.ChunkCount) } // Verify blob and chunk metadata if blobRetrieved, err := metaDB.GetCurrentBlobRef(ctx, store.Key, record.Key); err != nil { t.Errorf("GetCurrentBlobRef failed: %v", err) } else { - assert.EqualValues(t, testChunKSize*testChunkCount, blobRetrieved.Size) + assert.EqualValues(t, testChunkSize*testChunkCount, blobRetrieved.Size) assert.Equal(t, blob.Key, blobRetrieved.Key) assert.True(t, blobRetrieved.Chunked) + assert.Equal(t, int64(testChunkCount), blobRetrieved.ChunkCount) } for i := 0; i < testChunkCount; i++ { got, err := metaDB.FindChunkRefByNumber(ctx, store.Key, record.Key, int32(i)) @@ -1041,7 +1046,7 @@ func TestMetaDB_MarkUncommittedBlobForDeletion(t *testing.T) { assert.NoError(t, metaDB.MarkUncommittedBlobForDeletion(ctx, blob.Key)) // Should fail if blob is live - blob = blobref.NewChunkedBlobRef(store.Key, record.Key) + blob = blobref.NewChunkedBlobRef(store.Key, record.Key, 0) setupTestBlobRef(ctx, t, metaDB, blob) _, _, err := metaDB.PromoteBlobRefToCurrent(ctx, blob) if assert.NoError(t, err) {