diff --git a/go/gen/proto/dolt/services/remotesapi/v1alpha1/chunkstore.pb.go b/go/gen/proto/dolt/services/remotesapi/v1alpha1/chunkstore.pb.go index 4fb6fcd3d98..1f2b6e24774 100644 --- a/go/gen/proto/dolt/services/remotesapi/v1alpha1/chunkstore.pb.go +++ b/go/gen/proto/dolt/services/remotesapi/v1alpha1/chunkstore.pb.go @@ -402,9 +402,11 @@ type RangeChunk struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Hash []byte `protobuf:"bytes,1,opt,name=hash,proto3" json:"hash,omitempty"` - Offset uint64 `protobuf:"varint,2,opt,name=offset,proto3" json:"offset,omitempty"` - Length uint32 `protobuf:"varint,3,opt,name=length,proto3" json:"length,omitempty"` + Hash []byte `protobuf:"bytes,1,opt,name=hash,proto3" json:"hash,omitempty"` + Offset uint64 `protobuf:"varint,2,opt,name=offset,proto3" json:"offset,omitempty"` + Length uint32 `protobuf:"varint,3,opt,name=length,proto3" json:"length,omitempty"` + DictionaryOffset uint64 `protobuf:"varint,4,opt,name=dictionary_offset,json=dictionaryOffset,proto3" json:"dictionary_offset,omitempty"` + DictionaryLength uint32 `protobuf:"varint,5,opt,name=dictionary_length,json=dictionaryLength,proto3" json:"dictionary_length,omitempty"` } func (x *RangeChunk) Reset() { @@ -460,6 +462,20 @@ func (x *RangeChunk) GetLength() uint32 { return 0 } +func (x *RangeChunk) GetDictionaryOffset() uint64 { + if x != nil { + return x.DictionaryOffset + } + return 0 +} + +func (x *RangeChunk) GetDictionaryLength() uint32 { + if x != nil { + return x.DictionaryLength + } + return 0 +} + type HttpGetRange struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -2233,418 +2249,424 @@ var file_dolt_services_remotesapi_v1alpha1_chunkstore_proto_rawDesc = []byte{ 0x74, 0x70, 0x47, 0x65, 0x74, 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x6c, 0x12, 0x16, 0x0a, 0x06, 0x68, 0x61, 0x73, 0x68, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x06, 0x68, 0x61, - 0x73, 0x68, 0x65, 0x73, 0x22, 0x50, 0x0a, 0x0a, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x43, 0x68, 0x75, - 0x6e, 0x6b, 0x12, 0x12, 0x0a, 0x04, 0x68, 0x61, 0x73, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, - 0x52, 0x04, 0x68, 0x61, 0x73, 0x68, 0x12, 0x16, 0x0a, 0x06, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x12, 0x16, - 0x0a, 0x06, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x06, - 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x22, 0x67, 0x0a, 0x0c, 0x48, 0x74, 0x74, 0x70, 0x47, 0x65, - 0x74, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, 0x6c, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x6c, 0x12, 0x45, 0x0a, 0x06, 0x72, 0x61, 0x6e, 0x67, - 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2d, 0x2e, 0x64, 0x6f, 0x6c, 0x74, 0x2e, - 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x73, - 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x52, 0x61, 0x6e, - 0x67, 0x65, 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x52, 0x06, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x22, - 0xe9, 0x02, 0x0a, 0x0b, 0x44, 0x6f, 0x77, 0x6e, 0x6c, 0x6f, 0x61, 0x64, 0x4c, 0x6f, 0x63, 0x12, - 0x4c, 0x0a, 0x08, 0x68, 0x74, 0x74, 0x70, 0x5f, 0x67, 0x65, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x2f, 0x2e, 0x64, 0x6f, 0x6c, 0x74, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, - 0x73, 0x2e, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x73, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x61, - 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x48, 0x74, 0x74, 0x70, 0x47, 0x65, 0x74, 0x43, 0x68, 0x75, - 0x6e, 0x6b, 0x48, 0x00, 0x52, 0x07, 0x68, 0x74, 0x74, 0x70, 0x47, 0x65, 0x74, 0x12, 0x57, 0x0a, - 0x0e, 0x68, 0x74, 0x74, 0x70, 0x5f, 0x67, 0x65, 0x74, 0x5f, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2f, 0x2e, 0x64, 0x6f, 0x6c, 0x74, 0x2e, 0x73, 0x65, 0x72, - 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x73, 0x61, 0x70, 0x69, - 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x48, 0x74, 0x74, 0x70, 0x47, 0x65, - 0x74, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x48, 0x00, 0x52, 0x0c, 0x68, 0x74, 0x74, 0x70, 0x47, 0x65, - 0x74, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x3f, 0x0a, 0x0d, 0x72, 0x65, 0x66, 0x72, 0x65, 0x73, - 0x68, 0x5f, 0x61, 0x66, 0x74, 0x65, 0x72, 0x18, 0x03, 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, 0x0c, 0x72, 0x65, 0x66, 0x72, 0x65, - 0x73, 0x68, 0x41, 0x66, 0x74, 0x65, 0x72, 0x12, 0x66, 0x0a, 0x0f, 0x72, 0x65, 0x66, 0x72, 0x65, - 0x73, 0x68, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x3d, 0x2e, 0x64, 0x6f, 0x6c, 0x74, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, + 0x73, 0x68, 0x65, 0x73, 0x22, 0xaa, 0x01, 0x0a, 0x0a, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x43, 0x68, + 0x75, 0x6e, 0x6b, 0x12, 0x12, 0x0a, 0x04, 0x68, 0x61, 0x73, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x0c, 0x52, 0x04, 0x68, 0x61, 0x73, 0x68, 0x12, 0x16, 0x0a, 0x06, 0x6f, 0x66, 0x66, 0x73, 0x65, + 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x12, + 0x16, 0x0a, 0x06, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, + 0x06, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x12, 0x2b, 0x0a, 0x11, 0x64, 0x69, 0x63, 0x74, 0x69, + 0x6f, 0x6e, 0x61, 0x72, 0x79, 0x5f, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x18, 0x04, 0x20, 0x01, + 0x28, 0x04, 0x52, 0x10, 0x64, 0x69, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x72, 0x79, 0x4f, 0x66, + 0x66, 0x73, 0x65, 0x74, 0x12, 0x2b, 0x0a, 0x11, 0x64, 0x69, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x61, + 0x72, 0x79, 0x5f, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, 0x52, + 0x10, 0x64, 0x69, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x72, 0x79, 0x4c, 0x65, 0x6e, 0x67, 0x74, + 0x68, 0x22, 0x67, 0x0a, 0x0c, 0x48, 0x74, 0x74, 0x70, 0x47, 0x65, 0x74, 0x52, 0x61, 0x6e, 0x67, + 0x65, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, + 0x75, 0x72, 0x6c, 0x12, 0x45, 0x0a, 0x06, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x18, 0x02, 0x20, + 0x03, 0x28, 0x0b, 0x32, 0x2d, 0x2e, 0x64, 0x6f, 0x6c, 0x74, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, + 0x63, 0x65, 0x73, 0x2e, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x73, 0x61, 0x70, 0x69, 0x2e, 0x76, + 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x43, 0x68, 0x75, + 0x6e, 0x6b, 0x52, 0x06, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x22, 0xe9, 0x02, 0x0a, 0x0b, 0x44, + 0x6f, 0x77, 0x6e, 0x6c, 0x6f, 0x61, 0x64, 0x4c, 0x6f, 0x63, 0x12, 0x4c, 0x0a, 0x08, 0x68, 0x74, + 0x74, 0x70, 0x5f, 0x67, 0x65, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2f, 0x2e, 0x64, + 0x6f, 0x6c, 0x74, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x72, 0x65, 0x6d, + 0x6f, 0x74, 0x65, 0x73, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, + 0x2e, 0x48, 0x74, 0x74, 0x70, 0x47, 0x65, 0x74, 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x48, 0x00, 0x52, + 0x07, 0x68, 0x74, 0x74, 0x70, 0x47, 0x65, 0x74, 0x12, 0x57, 0x0a, 0x0e, 0x68, 0x74, 0x74, 0x70, + 0x5f, 0x67, 0x65, 0x74, 0x5f, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x2f, 0x2e, 0x64, 0x6f, 0x6c, 0x74, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x73, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x61, 0x6c, - 0x70, 0x68, 0x61, 0x31, 0x2e, 0x52, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x54, 0x61, 0x62, 0x6c, - 0x65, 0x46, 0x69, 0x6c, 0x65, 0x55, 0x72, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, - 0x0e, 0x72, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x42, - 0x0a, 0x0a, 0x08, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x25, 0x0a, 0x11, 0x48, - 0x74, 0x74, 0x70, 0x50, 0x6f, 0x73, 0x74, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x46, 0x69, 0x6c, 0x65, - 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, - 0x72, 0x6c, 0x22, 0x94, 0x01, 0x0a, 0x09, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x4c, 0x6f, 0x63, - 0x12, 0x26, 0x0a, 0x0f, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x68, - 0x61, 0x73, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0d, 0x74, 0x61, 0x62, 0x6c, 0x65, - 0x46, 0x69, 0x6c, 0x65, 0x48, 0x61, 0x73, 0x68, 0x12, 0x53, 0x0a, 0x09, 0x68, 0x74, 0x74, 0x70, - 0x5f, 0x70, 0x6f, 0x73, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x34, 0x2e, 0x64, 0x6f, + 0x70, 0x68, 0x61, 0x31, 0x2e, 0x48, 0x74, 0x74, 0x70, 0x47, 0x65, 0x74, 0x52, 0x61, 0x6e, 0x67, + 0x65, 0x48, 0x00, 0x52, 0x0c, 0x68, 0x74, 0x74, 0x70, 0x47, 0x65, 0x74, 0x52, 0x61, 0x6e, 0x67, + 0x65, 0x12, 0x3f, 0x0a, 0x0d, 0x72, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x5f, 0x61, 0x66, 0x74, + 0x65, 0x72, 0x18, 0x03, 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, 0x0c, 0x72, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x41, 0x66, 0x74, + 0x65, 0x72, 0x12, 0x66, 0x0a, 0x0f, 0x72, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x5f, 0x72, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x3d, 0x2e, 0x64, 0x6f, 0x6c, 0x74, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x73, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, - 0x48, 0x74, 0x74, 0x70, 0x50, 0x6f, 0x73, 0x74, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x46, 0x69, 0x6c, - 0x65, 0x48, 0x00, 0x52, 0x08, 0x68, 0x74, 0x74, 0x70, 0x50, 0x6f, 0x73, 0x74, 0x42, 0x0a, 0x0a, - 0x08, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0xbb, 0x01, 0x0a, 0x16, 0x47, 0x65, - 0x74, 0x44, 0x6f, 0x77, 0x6e, 0x6c, 0x6f, 0x61, 0x64, 0x4c, 0x6f, 0x63, 0x73, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x12, 0x42, 0x0a, 0x07, 0x72, 0x65, 0x70, 0x6f, 0x5f, 0x69, 0x64, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x64, 0x6f, 0x6c, 0x74, 0x2e, 0x73, 0x65, 0x72, - 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x73, 0x61, 0x70, 0x69, - 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x52, 0x65, 0x70, 0x6f, 0x49, 0x64, - 0x52, 0x06, 0x72, 0x65, 0x70, 0x6f, 0x49, 0x64, 0x12, 0x21, 0x0a, 0x0c, 0x63, 0x68, 0x75, 0x6e, - 0x6b, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x0b, - 0x63, 0x68, 0x75, 0x6e, 0x6b, 0x48, 0x61, 0x73, 0x68, 0x65, 0x73, 0x12, 0x1d, 0x0a, 0x0a, 0x72, - 0x65, 0x70, 0x6f, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x09, 0x72, 0x65, 0x70, 0x6f, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x1b, 0x0a, 0x09, 0x72, 0x65, - 0x70, 0x6f, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x72, - 0x65, 0x70, 0x6f, 0x50, 0x61, 0x74, 0x68, 0x22, 0x7c, 0x0a, 0x17, 0x47, 0x65, 0x74, 0x44, 0x6f, - 0x77, 0x6e, 0x6c, 0x6f, 0x61, 0x64, 0x4c, 0x6f, 0x63, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x12, 0x42, 0x0a, 0x04, 0x6c, 0x6f, 0x63, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, - 0x32, 0x2e, 0x2e, 0x64, 0x6f, 0x6c, 0x74, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, - 0x2e, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x73, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x61, 0x6c, - 0x70, 0x68, 0x61, 0x31, 0x2e, 0x44, 0x6f, 0x77, 0x6e, 0x6c, 0x6f, 0x61, 0x64, 0x4c, 0x6f, 0x63, - 0x52, 0x04, 0x6c, 0x6f, 0x63, 0x73, 0x12, 0x1d, 0x0a, 0x0a, 0x72, 0x65, 0x70, 0x6f, 0x5f, 0x74, - 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x72, 0x65, 0x70, 0x6f, - 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x22, 0x8b, 0x01, 0x0a, 0x10, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x46, - 0x69, 0x6c, 0x65, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x02, 0x69, 0x64, 0x12, 0x25, 0x0a, 0x0e, 0x63, 0x6f, - 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x5f, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x04, 0x52, 0x0d, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x4c, 0x65, 0x6e, 0x67, 0x74, - 0x68, 0x12, 0x21, 0x0a, 0x0c, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x5f, 0x68, 0x61, 0x73, - 0x68, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0b, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, - 0x48, 0x61, 0x73, 0x68, 0x12, 0x1d, 0x0a, 0x0a, 0x6e, 0x75, 0x6d, 0x5f, 0x63, 0x68, 0x75, 0x6e, - 0x6b, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x6e, 0x75, 0x6d, 0x43, 0x68, 0x75, - 0x6e, 0x6b, 0x73, 0x22, 0xa9, 0x02, 0x0a, 0x14, 0x47, 0x65, 0x74, 0x55, 0x70, 0x6c, 0x6f, 0x61, - 0x64, 0x4c, 0x6f, 0x63, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x42, 0x0a, 0x07, - 0x72, 0x65, 0x70, 0x6f, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x29, 0x2e, - 0x64, 0x6f, 0x6c, 0x74, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x72, 0x65, - 0x6d, 0x6f, 0x74, 0x65, 0x73, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, - 0x31, 0x2e, 0x52, 0x65, 0x70, 0x6f, 0x49, 0x64, 0x52, 0x06, 0x72, 0x65, 0x70, 0x6f, 0x49, 0x64, - 0x12, 0x2e, 0x0a, 0x11, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x68, - 0x61, 0x73, 0x68, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0c, 0x42, 0x02, 0x18, 0x01, 0x52, - 0x0f, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x46, 0x69, 0x6c, 0x65, 0x48, 0x61, 0x73, 0x68, 0x65, 0x73, - 0x12, 0x61, 0x0a, 0x12, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x64, - 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x33, 0x2e, 0x64, - 0x6f, 0x6c, 0x74, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x72, 0x65, 0x6d, - 0x6f, 0x74, 0x65, 0x73, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, - 0x2e, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x46, 0x69, 0x6c, 0x65, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, - 0x73, 0x52, 0x10, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x46, 0x69, 0x6c, 0x65, 0x44, 0x65, 0x74, 0x61, - 0x69, 0x6c, 0x73, 0x12, 0x1d, 0x0a, 0x0a, 0x72, 0x65, 0x70, 0x6f, 0x5f, 0x74, 0x6f, 0x6b, 0x65, - 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x72, 0x65, 0x70, 0x6f, 0x54, 0x6f, 0x6b, - 0x65, 0x6e, 0x12, 0x1b, 0x0a, 0x09, 0x72, 0x65, 0x70, 0x6f, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, - 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x72, 0x65, 0x70, 0x6f, 0x50, 0x61, 0x74, 0x68, 0x22, - 0x78, 0x0a, 0x15, 0x47, 0x65, 0x74, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x4c, 0x6f, 0x63, 0x73, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x40, 0x0a, 0x04, 0x6c, 0x6f, 0x63, 0x73, - 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2c, 0x2e, 0x64, 0x6f, 0x6c, 0x74, 0x2e, 0x73, 0x65, + 0x52, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x46, 0x69, 0x6c, 0x65, + 0x55, 0x72, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, 0x0e, 0x72, 0x65, 0x66, 0x72, + 0x65, 0x73, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x42, 0x0a, 0x0a, 0x08, 0x6c, 0x6f, + 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x25, 0x0a, 0x11, 0x48, 0x74, 0x74, 0x70, 0x50, 0x6f, + 0x73, 0x74, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x46, 0x69, 0x6c, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x75, + 0x72, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x6c, 0x22, 0x94, 0x01, + 0x0a, 0x09, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x4c, 0x6f, 0x63, 0x12, 0x26, 0x0a, 0x0f, 0x74, + 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0d, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x46, 0x69, 0x6c, 0x65, 0x48, + 0x61, 0x73, 0x68, 0x12, 0x53, 0x0a, 0x09, 0x68, 0x74, 0x74, 0x70, 0x5f, 0x70, 0x6f, 0x73, 0x74, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x34, 0x2e, 0x64, 0x6f, 0x6c, 0x74, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x73, 0x61, 0x70, - 0x69, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x55, 0x70, 0x6c, 0x6f, 0x61, - 0x64, 0x4c, 0x6f, 0x63, 0x52, 0x04, 0x6c, 0x6f, 0x63, 0x73, 0x12, 0x1d, 0x0a, 0x0a, 0x72, 0x65, + 0x69, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x48, 0x74, 0x74, 0x70, 0x50, + 0x6f, 0x73, 0x74, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x46, 0x69, 0x6c, 0x65, 0x48, 0x00, 0x52, 0x08, + 0x68, 0x74, 0x74, 0x70, 0x50, 0x6f, 0x73, 0x74, 0x42, 0x0a, 0x0a, 0x08, 0x6c, 0x6f, 0x63, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x22, 0xbb, 0x01, 0x0a, 0x16, 0x47, 0x65, 0x74, 0x44, 0x6f, 0x77, 0x6e, + 0x6c, 0x6f, 0x61, 0x64, 0x4c, 0x6f, 0x63, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, + 0x42, 0x0a, 0x07, 0x72, 0x65, 0x70, 0x6f, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x29, 0x2e, 0x64, 0x6f, 0x6c, 0x74, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, + 0x2e, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x73, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x61, 0x6c, + 0x70, 0x68, 0x61, 0x31, 0x2e, 0x52, 0x65, 0x70, 0x6f, 0x49, 0x64, 0x52, 0x06, 0x72, 0x65, 0x70, + 0x6f, 0x49, 0x64, 0x12, 0x21, 0x0a, 0x0c, 0x63, 0x68, 0x75, 0x6e, 0x6b, 0x5f, 0x68, 0x61, 0x73, + 0x68, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x0b, 0x63, 0x68, 0x75, 0x6e, 0x6b, + 0x48, 0x61, 0x73, 0x68, 0x65, 0x73, 0x12, 0x1d, 0x0a, 0x0a, 0x72, 0x65, 0x70, 0x6f, 0x5f, 0x74, + 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x72, 0x65, 0x70, 0x6f, + 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x1b, 0x0a, 0x09, 0x72, 0x65, 0x70, 0x6f, 0x5f, 0x70, 0x61, + 0x74, 0x68, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x72, 0x65, 0x70, 0x6f, 0x50, 0x61, + 0x74, 0x68, 0x22, 0x7c, 0x0a, 0x17, 0x47, 0x65, 0x74, 0x44, 0x6f, 0x77, 0x6e, 0x6c, 0x6f, 0x61, + 0x64, 0x4c, 0x6f, 0x63, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x42, 0x0a, + 0x04, 0x6c, 0x6f, 0x63, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2e, 0x2e, 0x64, 0x6f, + 0x6c, 0x74, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x72, 0x65, 0x6d, 0x6f, + 0x74, 0x65, 0x73, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, + 0x44, 0x6f, 0x77, 0x6e, 0x6c, 0x6f, 0x61, 0x64, 0x4c, 0x6f, 0x63, 0x52, 0x04, 0x6c, 0x6f, 0x63, + 0x73, 0x12, 0x1d, 0x0a, 0x0a, 0x72, 0x65, 0x70, 0x6f, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x72, 0x65, 0x70, 0x6f, 0x54, 0x6f, 0x6b, 0x65, 0x6e, + 0x22, 0x8b, 0x01, 0x0a, 0x10, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x46, 0x69, 0x6c, 0x65, 0x44, 0x65, + 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x0c, 0x52, 0x02, 0x69, 0x64, 0x12, 0x25, 0x0a, 0x0e, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, + 0x5f, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0d, 0x63, + 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x12, 0x21, 0x0a, 0x0c, + 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x0c, 0x52, 0x0b, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x48, 0x61, 0x73, 0x68, 0x12, + 0x1d, 0x0a, 0x0a, 0x6e, 0x75, 0x6d, 0x5f, 0x63, 0x68, 0x75, 0x6e, 0x6b, 0x73, 0x18, 0x04, 0x20, + 0x01, 0x28, 0x04, 0x52, 0x09, 0x6e, 0x75, 0x6d, 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x73, 0x22, 0xa9, + 0x02, 0x0a, 0x14, 0x47, 0x65, 0x74, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x4c, 0x6f, 0x63, 0x73, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x42, 0x0a, 0x07, 0x72, 0x65, 0x70, 0x6f, 0x5f, + 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x64, 0x6f, 0x6c, 0x74, 0x2e, + 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x73, + 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x52, 0x65, 0x70, + 0x6f, 0x49, 0x64, 0x52, 0x06, 0x72, 0x65, 0x70, 0x6f, 0x49, 0x64, 0x12, 0x2e, 0x0a, 0x11, 0x74, + 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x65, 0x73, + 0x18, 0x02, 0x20, 0x03, 0x28, 0x0c, 0x42, 0x02, 0x18, 0x01, 0x52, 0x0f, 0x74, 0x61, 0x62, 0x6c, + 0x65, 0x46, 0x69, 0x6c, 0x65, 0x48, 0x61, 0x73, 0x68, 0x65, 0x73, 0x12, 0x61, 0x0a, 0x12, 0x74, + 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, + 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x33, 0x2e, 0x64, 0x6f, 0x6c, 0x74, 0x2e, 0x73, + 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x73, 0x61, + 0x70, 0x69, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x54, 0x61, 0x62, 0x6c, + 0x65, 0x46, 0x69, 0x6c, 0x65, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x52, 0x10, 0x74, 0x61, + 0x62, 0x6c, 0x65, 0x46, 0x69, 0x6c, 0x65, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x1d, + 0x0a, 0x0a, 0x72, 0x65, 0x70, 0x6f, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x04, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x09, 0x72, 0x65, 0x70, 0x6f, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x1b, 0x0a, + 0x09, 0x72, 0x65, 0x70, 0x6f, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x08, 0x72, 0x65, 0x70, 0x6f, 0x50, 0x61, 0x74, 0x68, 0x22, 0x78, 0x0a, 0x15, 0x47, 0x65, + 0x74, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x4c, 0x6f, 0x63, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x40, 0x0a, 0x04, 0x6c, 0x6f, 0x63, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, + 0x0b, 0x32, 0x2c, 0x2e, 0x64, 0x6f, 0x6c, 0x74, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, + 0x73, 0x2e, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x73, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x61, + 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x4c, 0x6f, 0x63, 0x52, + 0x04, 0x6c, 0x6f, 0x63, 0x73, 0x12, 0x1d, 0x0a, 0x0a, 0x72, 0x65, 0x70, 0x6f, 0x5f, 0x74, 0x6f, + 0x6b, 0x65, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x72, 0x65, 0x70, 0x6f, 0x54, + 0x6f, 0x6b, 0x65, 0x6e, 0x22, 0x8f, 0x01, 0x0a, 0x0d, 0x52, 0x65, 0x62, 0x61, 0x73, 0x65, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x42, 0x0a, 0x07, 0x72, 0x65, 0x70, 0x6f, 0x5f, 0x69, + 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x64, 0x6f, 0x6c, 0x74, 0x2e, 0x73, + 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x73, 0x61, + 0x70, 0x69, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x52, 0x65, 0x70, 0x6f, + 0x49, 0x64, 0x52, 0x06, 0x72, 0x65, 0x70, 0x6f, 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x72, 0x65, 0x70, 0x6f, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, - 0x72, 0x65, 0x70, 0x6f, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x22, 0x8f, 0x01, 0x0a, 0x0d, 0x52, 0x65, - 0x62, 0x61, 0x73, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x42, 0x0a, 0x07, 0x72, - 0x65, 0x70, 0x6f, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x64, - 0x6f, 0x6c, 0x74, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x72, 0x65, 0x6d, - 0x6f, 0x74, 0x65, 0x73, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, - 0x2e, 0x52, 0x65, 0x70, 0x6f, 0x49, 0x64, 0x52, 0x06, 0x72, 0x65, 0x70, 0x6f, 0x49, 0x64, 0x12, - 0x1d, 0x0a, 0x0a, 0x72, 0x65, 0x70, 0x6f, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x09, 0x72, 0x65, 0x70, 0x6f, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x1b, - 0x0a, 0x09, 0x72, 0x65, 0x70, 0x6f, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x03, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x08, 0x72, 0x65, 0x70, 0x6f, 0x50, 0x61, 0x74, 0x68, 0x22, 0x2f, 0x0a, 0x0e, 0x52, - 0x65, 0x62, 0x61, 0x73, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1d, 0x0a, - 0x0a, 0x72, 0x65, 0x70, 0x6f, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x09, 0x72, 0x65, 0x70, 0x6f, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x22, 0x8d, 0x01, 0x0a, - 0x0b, 0x52, 0x6f, 0x6f, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x42, 0x0a, 0x07, + 0x72, 0x65, 0x70, 0x6f, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x1b, 0x0a, 0x09, 0x72, 0x65, 0x70, + 0x6f, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x72, 0x65, + 0x70, 0x6f, 0x50, 0x61, 0x74, 0x68, 0x22, 0x2f, 0x0a, 0x0e, 0x52, 0x65, 0x62, 0x61, 0x73, 0x65, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x72, 0x65, 0x70, 0x6f, + 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x72, 0x65, + 0x70, 0x6f, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x22, 0x8d, 0x01, 0x0a, 0x0b, 0x52, 0x6f, 0x6f, 0x74, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x42, 0x0a, 0x07, 0x72, 0x65, 0x70, 0x6f, 0x5f, + 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x64, 0x6f, 0x6c, 0x74, 0x2e, + 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x73, + 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x52, 0x65, 0x70, + 0x6f, 0x49, 0x64, 0x52, 0x06, 0x72, 0x65, 0x70, 0x6f, 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x72, + 0x65, 0x70, 0x6f, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x09, 0x72, 0x65, 0x70, 0x6f, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x1b, 0x0a, 0x09, 0x72, 0x65, + 0x70, 0x6f, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x72, + 0x65, 0x70, 0x6f, 0x50, 0x61, 0x74, 0x68, 0x22, 0x4a, 0x0a, 0x0c, 0x52, 0x6f, 0x6f, 0x74, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x72, 0x6f, 0x6f, 0x74, 0x5f, + 0x68, 0x61, 0x73, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x72, 0x6f, 0x6f, 0x74, + 0x48, 0x61, 0x73, 0x68, 0x12, 0x1d, 0x0a, 0x0a, 0x72, 0x65, 0x70, 0x6f, 0x5f, 0x74, 0x6f, 0x6b, + 0x65, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x72, 0x65, 0x70, 0x6f, 0x54, 0x6f, + 0x6b, 0x65, 0x6e, 0x22, 0x45, 0x0a, 0x0e, 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x54, 0x61, 0x62, 0x6c, + 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x12, 0x0a, 0x04, 0x68, 0x61, 0x73, 0x68, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x0c, 0x52, 0x04, 0x68, 0x61, 0x73, 0x68, 0x12, 0x1f, 0x0a, 0x0b, 0x63, 0x68, 0x75, + 0x6e, 0x6b, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0a, + 0x63, 0x68, 0x75, 0x6e, 0x6b, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0xde, 0x02, 0x0a, 0x0d, 0x43, + 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x42, 0x0a, 0x07, 0x72, 0x65, 0x70, 0x6f, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x64, 0x6f, 0x6c, 0x74, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x73, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x52, 0x65, 0x70, 0x6f, 0x49, 0x64, 0x52, 0x06, 0x72, 0x65, 0x70, 0x6f, 0x49, 0x64, - 0x12, 0x1d, 0x0a, 0x0a, 0x72, 0x65, 0x70, 0x6f, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x72, 0x65, 0x70, 0x6f, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, - 0x1b, 0x0a, 0x09, 0x72, 0x65, 0x70, 0x6f, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x03, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x08, 0x72, 0x65, 0x70, 0x6f, 0x50, 0x61, 0x74, 0x68, 0x22, 0x4a, 0x0a, 0x0c, - 0x52, 0x6f, 0x6f, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1b, 0x0a, 0x09, - 0x72, 0x6f, 0x6f, 0x74, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, - 0x08, 0x72, 0x6f, 0x6f, 0x74, 0x48, 0x61, 0x73, 0x68, 0x12, 0x1d, 0x0a, 0x0a, 0x72, 0x65, 0x70, - 0x6f, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x72, - 0x65, 0x70, 0x6f, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x22, 0x45, 0x0a, 0x0e, 0x43, 0x68, 0x75, 0x6e, - 0x6b, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x12, 0x0a, 0x04, 0x68, 0x61, - 0x73, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x68, 0x61, 0x73, 0x68, 0x12, 0x1f, - 0x0a, 0x0b, 0x63, 0x68, 0x75, 0x6e, 0x6b, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x0d, 0x52, 0x0a, 0x63, 0x68, 0x75, 0x6e, 0x6b, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x22, - 0xde, 0x02, 0x0a, 0x0d, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x12, 0x42, 0x0a, 0x07, 0x72, 0x65, 0x70, 0x6f, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x64, 0x6f, 0x6c, 0x74, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, - 0x65, 0x73, 0x2e, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x73, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, - 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x52, 0x65, 0x70, 0x6f, 0x49, 0x64, 0x52, 0x06, 0x72, - 0x65, 0x70, 0x6f, 0x49, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x12, - 0x12, 0x0a, 0x04, 0x6c, 0x61, 0x73, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x6c, - 0x61, 0x73, 0x74, 0x12, 0x5b, 0x0a, 0x10, 0x63, 0x68, 0x75, 0x6e, 0x6b, 0x5f, 0x74, 0x61, 0x62, - 0x6c, 0x65, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x31, 0x2e, - 0x64, 0x6f, 0x6c, 0x74, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x72, 0x65, - 0x6d, 0x6f, 0x74, 0x65, 0x73, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, - 0x31, 0x2e, 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x49, 0x6e, 0x66, 0x6f, - 0x52, 0x0e, 0x63, 0x68, 0x75, 0x6e, 0x6b, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x49, 0x6e, 0x66, 0x6f, - 0x12, 0x61, 0x0a, 0x12, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x5f, 0x72, 0x65, 0x70, 0x6f, 0x5f, - 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x33, 0x2e, 0x64, - 0x6f, 0x6c, 0x74, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x72, 0x65, 0x6d, - 0x6f, 0x74, 0x65, 0x73, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, - 0x2e, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x70, 0x6f, 0x46, 0x6f, 0x72, 0x6d, 0x61, - 0x74, 0x52, 0x10, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x70, 0x6f, 0x46, 0x6f, 0x72, - 0x6d, 0x61, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x72, 0x65, 0x70, 0x6f, 0x5f, 0x70, 0x61, 0x74, 0x68, - 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x72, 0x65, 0x70, 0x6f, 0x50, 0x61, 0x74, 0x68, - 0x22, 0x2a, 0x0a, 0x0e, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x08, 0x52, 0x07, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x22, 0xfb, 0x01, 0x0a, - 0x16, 0x47, 0x65, 0x74, 0x52, 0x65, 0x70, 0x6f, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x42, 0x0a, 0x07, 0x72, 0x65, 0x70, 0x6f, 0x5f, - 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x64, 0x6f, 0x6c, 0x74, 0x2e, + 0x12, 0x18, 0x0a, 0x07, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x0c, 0x52, 0x07, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6c, 0x61, + 0x73, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x6c, 0x61, 0x73, 0x74, 0x12, 0x5b, + 0x0a, 0x10, 0x63, 0x68, 0x75, 0x6e, 0x6b, 0x5f, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x69, 0x6e, + 0x66, 0x6f, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x31, 0x2e, 0x64, 0x6f, 0x6c, 0x74, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x73, - 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x52, 0x65, 0x70, - 0x6f, 0x49, 0x64, 0x52, 0x06, 0x72, 0x65, 0x70, 0x6f, 0x49, 0x64, 0x12, 0x61, 0x0a, 0x12, 0x63, + 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x43, 0x68, 0x75, + 0x6e, 0x6b, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x0e, 0x63, 0x68, 0x75, + 0x6e, 0x6b, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x61, 0x0a, 0x12, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x5f, 0x72, 0x65, 0x70, 0x6f, 0x5f, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x33, 0x2e, 0x64, 0x6f, 0x6c, 0x74, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x73, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x70, 0x6f, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x52, 0x10, 0x63, 0x6c, - 0x69, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x70, 0x6f, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x12, 0x1d, - 0x0a, 0x0a, 0x72, 0x65, 0x70, 0x6f, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x09, 0x72, 0x65, 0x70, 0x6f, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x1b, 0x0a, - 0x09, 0x72, 0x65, 0x70, 0x6f, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x08, 0x72, 0x65, 0x70, 0x6f, 0x50, 0x61, 0x74, 0x68, 0x22, 0x92, 0x02, 0x0a, 0x17, 0x47, - 0x65, 0x74, 0x52, 0x65, 0x70, 0x6f, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x6e, 0x62, 0x66, 0x5f, 0x76, 0x65, - 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x6e, 0x62, 0x66, - 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x1f, 0x0a, 0x0b, 0x6e, 0x62, 0x73, 0x5f, 0x76, - 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x6e, 0x62, - 0x73, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x21, 0x0a, 0x0c, 0x73, 0x74, 0x6f, 0x72, - 0x61, 0x67, 0x65, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, - 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x53, 0x69, 0x7a, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x72, - 0x65, 0x70, 0x6f, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x09, 0x72, 0x65, 0x70, 0x6f, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x73, 0x0a, 0x18, 0x70, 0x75, - 0x73, 0x68, 0x5f, 0x63, 0x6f, 0x6e, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x5f, 0x63, - 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x39, 0x2e, 0x64, - 0x6f, 0x6c, 0x74, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x72, 0x65, 0x6d, - 0x6f, 0x74, 0x65, 0x73, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, - 0x2e, 0x50, 0x75, 0x73, 0x68, 0x43, 0x6f, 0x6e, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, - 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x52, 0x16, 0x70, 0x75, 0x73, 0x68, 0x43, 0x6f, 0x6e, - 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x22, - 0x54, 0x0a, 0x10, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x70, 0x6f, 0x46, 0x6f, 0x72, - 0x6d, 0x61, 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x6e, 0x62, 0x66, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, - 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x6e, 0x62, 0x66, 0x56, 0x65, 0x72, - 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x1f, 0x0a, 0x0b, 0x6e, 0x62, 0x73, 0x5f, 0x76, 0x65, 0x72, 0x73, - 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x6e, 0x62, 0x73, 0x56, 0x65, - 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0xc0, 0x01, 0x0a, 0x15, 0x4c, 0x69, 0x73, 0x74, 0x54, 0x61, - 0x62, 0x6c, 0x65, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, - 0x42, 0x0a, 0x07, 0x72, 0x65, 0x70, 0x6f, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x29, 0x2e, 0x64, 0x6f, 0x6c, 0x74, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, - 0x2e, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x73, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x61, 0x6c, - 0x70, 0x68, 0x61, 0x31, 0x2e, 0x52, 0x65, 0x70, 0x6f, 0x49, 0x64, 0x52, 0x06, 0x72, 0x65, 0x70, - 0x6f, 0x49, 0x64, 0x12, 0x27, 0x0a, 0x0d, 0x61, 0x70, 0x70, 0x65, 0x6e, 0x64, 0x69, 0x78, 0x5f, - 0x6f, 0x6e, 0x6c, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x42, 0x02, 0x18, 0x01, 0x52, 0x0c, - 0x61, 0x70, 0x70, 0x65, 0x6e, 0x64, 0x69, 0x78, 0x4f, 0x6e, 0x6c, 0x79, 0x12, 0x1d, 0x0a, 0x0a, - 0x72, 0x65, 0x70, 0x6f, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x09, 0x72, 0x65, 0x70, 0x6f, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x1b, 0x0a, 0x09, 0x72, - 0x65, 0x70, 0x6f, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, - 0x72, 0x65, 0x70, 0x6f, 0x50, 0x61, 0x74, 0x68, 0x22, 0x82, 0x02, 0x0a, 0x0d, 0x54, 0x61, 0x62, - 0x6c, 0x65, 0x46, 0x69, 0x6c, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x17, 0x0a, 0x07, 0x66, 0x69, - 0x6c, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x66, 0x69, 0x6c, - 0x65, 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x6e, 0x75, 0x6d, 0x5f, 0x63, 0x68, 0x75, 0x6e, 0x6b, - 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x6e, 0x75, 0x6d, 0x43, 0x68, 0x75, 0x6e, - 0x6b, 0x73, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x03, 0x75, 0x72, 0x6c, 0x12, 0x3f, 0x0a, 0x0d, 0x72, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x5f, - 0x61, 0x66, 0x74, 0x65, 0x72, 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, 0x0c, 0x72, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, - 0x41, 0x66, 0x74, 0x65, 0x72, 0x12, 0x66, 0x0a, 0x0f, 0x72, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, - 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x3d, - 0x2e, 0x64, 0x6f, 0x6c, 0x74, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x72, - 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x73, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, - 0x61, 0x31, 0x2e, 0x52, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x46, - 0x69, 0x6c, 0x65, 0x55, 0x72, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, 0x0e, 0x72, - 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0xb5, 0x01, - 0x0a, 0x1a, 0x52, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x46, 0x69, - 0x6c, 0x65, 0x55, 0x72, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x42, 0x0a, 0x07, - 0x72, 0x65, 0x70, 0x6f, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x29, 0x2e, - 0x64, 0x6f, 0x6c, 0x74, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x72, 0x65, - 0x6d, 0x6f, 0x74, 0x65, 0x73, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, - 0x31, 0x2e, 0x52, 0x65, 0x70, 0x6f, 0x49, 0x64, 0x52, 0x06, 0x72, 0x65, 0x70, 0x6f, 0x49, 0x64, - 0x12, 0x17, 0x0a, 0x07, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x06, 0x66, 0x69, 0x6c, 0x65, 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x72, 0x65, 0x70, - 0x6f, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x72, + 0x69, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x70, 0x6f, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x12, 0x1b, + 0x0a, 0x09, 0x72, 0x65, 0x70, 0x6f, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x05, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x08, 0x72, 0x65, 0x70, 0x6f, 0x50, 0x61, 0x74, 0x68, 0x22, 0x2a, 0x0a, 0x0e, 0x43, + 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x18, 0x0a, + 0x07, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, + 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x22, 0xfb, 0x01, 0x0a, 0x16, 0x47, 0x65, 0x74, 0x52, + 0x65, 0x70, 0x6f, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x12, 0x42, 0x0a, 0x07, 0x72, 0x65, 0x70, 0x6f, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x64, 0x6f, 0x6c, 0x74, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, + 0x63, 0x65, 0x73, 0x2e, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x73, 0x61, 0x70, 0x69, 0x2e, 0x76, + 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x52, 0x65, 0x70, 0x6f, 0x49, 0x64, 0x52, 0x06, + 0x72, 0x65, 0x70, 0x6f, 0x49, 0x64, 0x12, 0x61, 0x0a, 0x12, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, + 0x5f, 0x72, 0x65, 0x70, 0x6f, 0x5f, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x18, 0x0e, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x33, 0x2e, 0x64, 0x6f, 0x6c, 0x74, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, + 0x65, 0x73, 0x2e, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x73, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, + 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x70, + 0x6f, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x52, 0x10, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x52, + 0x65, 0x70, 0x6f, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x72, 0x65, 0x70, + 0x6f, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x72, 0x65, 0x70, 0x6f, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x1b, 0x0a, 0x09, 0x72, 0x65, 0x70, 0x6f, - 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x72, 0x65, 0x70, - 0x6f, 0x50, 0x61, 0x74, 0x68, 0x22, 0x8f, 0x01, 0x0a, 0x1b, 0x52, 0x65, 0x66, 0x72, 0x65, 0x73, - 0x68, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x46, 0x69, 0x6c, 0x65, 0x55, 0x72, 0x6c, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, 0x6c, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x6c, 0x12, 0x3f, 0x0a, 0x0d, 0x72, 0x65, 0x66, 0x72, 0x65, - 0x73, 0x68, 0x5f, 0x61, 0x66, 0x74, 0x65, 0x72, 0x18, 0x02, 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, 0x0c, 0x72, 0x65, 0x66, 0x72, - 0x65, 0x73, 0x68, 0x41, 0x66, 0x74, 0x65, 0x72, 0x12, 0x1d, 0x0a, 0x0a, 0x72, 0x65, 0x70, 0x6f, - 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x72, 0x65, - 0x70, 0x6f, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x22, 0x99, 0x02, 0x0a, 0x16, 0x4c, 0x69, 0x73, 0x74, - 0x54, 0x61, 0x62, 0x6c, 0x65, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x72, 0x6f, 0x6f, 0x74, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x72, 0x6f, 0x6f, 0x74, 0x48, 0x61, 0x73, 0x68, 0x12, - 0x58, 0x0a, 0x0f, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x69, 0x6e, - 0x66, 0x6f, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x30, 0x2e, 0x64, 0x6f, 0x6c, 0x74, 0x2e, - 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x73, - 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x54, 0x61, 0x62, - 0x6c, 0x65, 0x46, 0x69, 0x6c, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x0d, 0x74, 0x61, 0x62, 0x6c, - 0x65, 0x46, 0x69, 0x6c, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x69, 0x0a, 0x18, 0x61, 0x70, 0x70, - 0x65, 0x6e, 0x64, 0x69, 0x78, 0x5f, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x66, 0x69, 0x6c, 0x65, - 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x30, 0x2e, 0x64, 0x6f, + 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x72, 0x65, 0x70, + 0x6f, 0x50, 0x61, 0x74, 0x68, 0x22, 0x92, 0x02, 0x0a, 0x17, 0x47, 0x65, 0x74, 0x52, 0x65, 0x70, + 0x6f, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x6e, 0x62, 0x66, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x6e, 0x62, 0x66, 0x56, 0x65, 0x72, 0x73, 0x69, + 0x6f, 0x6e, 0x12, 0x1f, 0x0a, 0x0b, 0x6e, 0x62, 0x73, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, + 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x6e, 0x62, 0x73, 0x56, 0x65, 0x72, 0x73, + 0x69, 0x6f, 0x6e, 0x12, 0x21, 0x0a, 0x0c, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x5f, 0x73, + 0x69, 0x7a, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, 0x73, 0x74, 0x6f, 0x72, 0x61, + 0x67, 0x65, 0x53, 0x69, 0x7a, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x72, 0x65, 0x70, 0x6f, 0x5f, 0x74, + 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x72, 0x65, 0x70, 0x6f, + 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x73, 0x0a, 0x18, 0x70, 0x75, 0x73, 0x68, 0x5f, 0x63, 0x6f, + 0x6e, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, + 0x6c, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x39, 0x2e, 0x64, 0x6f, 0x6c, 0x74, 0x2e, 0x73, + 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x73, 0x61, + 0x70, 0x69, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x50, 0x75, 0x73, 0x68, + 0x43, 0x6f, 0x6e, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x43, 0x6f, 0x6e, 0x74, 0x72, + 0x6f, 0x6c, 0x52, 0x16, 0x70, 0x75, 0x73, 0x68, 0x43, 0x6f, 0x6e, 0x63, 0x75, 0x72, 0x72, 0x65, + 0x6e, 0x63, 0x79, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x22, 0x54, 0x0a, 0x10, 0x43, 0x6c, + 0x69, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x70, 0x6f, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x12, 0x1f, + 0x0a, 0x0b, 0x6e, 0x62, 0x66, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x0a, 0x6e, 0x62, 0x66, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, + 0x1f, 0x0a, 0x0b, 0x6e, 0x62, 0x73, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x6e, 0x62, 0x73, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, + 0x22, 0xc0, 0x01, 0x0a, 0x15, 0x4c, 0x69, 0x73, 0x74, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x46, 0x69, + 0x6c, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x42, 0x0a, 0x07, 0x72, 0x65, + 0x70, 0x6f, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x64, 0x6f, 0x6c, 0x74, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x73, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, - 0x54, 0x61, 0x62, 0x6c, 0x65, 0x46, 0x69, 0x6c, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x15, 0x61, - 0x70, 0x70, 0x65, 0x6e, 0x64, 0x69, 0x78, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x46, 0x69, 0x6c, 0x65, - 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x1d, 0x0a, 0x0a, 0x72, 0x65, 0x70, 0x6f, 0x5f, 0x74, 0x6f, 0x6b, - 0x65, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x72, 0x65, 0x70, 0x6f, 0x54, 0x6f, - 0x6b, 0x65, 0x6e, 0x22, 0xba, 0x03, 0x0a, 0x14, 0x41, 0x64, 0x64, 0x54, 0x61, 0x62, 0x6c, 0x65, - 0x46, 0x69, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x42, 0x0a, 0x07, - 0x72, 0x65, 0x70, 0x6f, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x29, 0x2e, - 0x64, 0x6f, 0x6c, 0x74, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x72, 0x65, - 0x6d, 0x6f, 0x74, 0x65, 0x73, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, - 0x31, 0x2e, 0x52, 0x65, 0x70, 0x6f, 0x49, 0x64, 0x52, 0x06, 0x72, 0x65, 0x70, 0x6f, 0x49, 0x64, - 0x12, 0x61, 0x0a, 0x12, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x5f, 0x72, 0x65, 0x70, 0x6f, 0x5f, - 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x33, 0x2e, 0x64, - 0x6f, 0x6c, 0x74, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x72, 0x65, 0x6d, - 0x6f, 0x74, 0x65, 0x73, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, - 0x2e, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x70, 0x6f, 0x46, 0x6f, 0x72, 0x6d, 0x61, - 0x74, 0x52, 0x10, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x70, 0x6f, 0x46, 0x6f, 0x72, - 0x6d, 0x61, 0x74, 0x12, 0x5b, 0x0a, 0x10, 0x63, 0x68, 0x75, 0x6e, 0x6b, 0x5f, 0x74, 0x61, 0x62, - 0x6c, 0x65, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x31, 0x2e, - 0x64, 0x6f, 0x6c, 0x74, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x72, 0x65, - 0x6d, 0x6f, 0x74, 0x65, 0x73, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, - 0x31, 0x2e, 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x49, 0x6e, 0x66, 0x6f, - 0x52, 0x0e, 0x63, 0x68, 0x75, 0x6e, 0x6b, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x49, 0x6e, 0x66, 0x6f, - 0x12, 0x62, 0x0a, 0x0f, 0x61, 0x70, 0x70, 0x65, 0x6e, 0x64, 0x69, 0x78, 0x5f, 0x6f, 0x70, 0x74, - 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x39, 0x2e, 0x64, 0x6f, 0x6c, 0x74, + 0x52, 0x65, 0x70, 0x6f, 0x49, 0x64, 0x52, 0x06, 0x72, 0x65, 0x70, 0x6f, 0x49, 0x64, 0x12, 0x27, + 0x0a, 0x0d, 0x61, 0x70, 0x70, 0x65, 0x6e, 0x64, 0x69, 0x78, 0x5f, 0x6f, 0x6e, 0x6c, 0x79, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x08, 0x42, 0x02, 0x18, 0x01, 0x52, 0x0c, 0x61, 0x70, 0x70, 0x65, 0x6e, + 0x64, 0x69, 0x78, 0x4f, 0x6e, 0x6c, 0x79, 0x12, 0x1d, 0x0a, 0x0a, 0x72, 0x65, 0x70, 0x6f, 0x5f, + 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x72, 0x65, 0x70, + 0x6f, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x1b, 0x0a, 0x09, 0x72, 0x65, 0x70, 0x6f, 0x5f, 0x70, + 0x61, 0x74, 0x68, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x72, 0x65, 0x70, 0x6f, 0x50, + 0x61, 0x74, 0x68, 0x22, 0x82, 0x02, 0x0a, 0x0d, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x46, 0x69, 0x6c, + 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x17, 0x0a, 0x07, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x69, 0x64, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x66, 0x69, 0x6c, 0x65, 0x49, 0x64, 0x12, 0x1d, + 0x0a, 0x0a, 0x6e, 0x75, 0x6d, 0x5f, 0x63, 0x68, 0x75, 0x6e, 0x6b, 0x73, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x0d, 0x52, 0x09, 0x6e, 0x75, 0x6d, 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x73, 0x12, 0x10, 0x0a, + 0x03, 0x75, 0x72, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x6c, 0x12, + 0x3f, 0x0a, 0x0d, 0x72, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x5f, 0x61, 0x66, 0x74, 0x65, 0x72, + 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, 0x0c, 0x72, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x41, 0x66, 0x74, 0x65, 0x72, + 0x12, 0x66, 0x0a, 0x0f, 0x72, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x5f, 0x72, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x3d, 0x2e, 0x64, 0x6f, 0x6c, 0x74, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, - 0x73, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x4d, 0x61, - 0x6e, 0x69, 0x66, 0x65, 0x73, 0x74, 0x41, 0x70, 0x70, 0x65, 0x6e, 0x64, 0x69, 0x78, 0x4f, 0x70, - 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0e, 0x61, 0x70, 0x70, 0x65, 0x6e, 0x64, 0x69, 0x78, 0x4f, 0x70, - 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1d, 0x0a, 0x0a, 0x72, 0x65, 0x70, 0x6f, 0x5f, 0x74, 0x6f, 0x6b, - 0x65, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x72, 0x65, 0x70, 0x6f, 0x54, 0x6f, + 0x73, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x52, 0x65, + 0x66, 0x72, 0x65, 0x73, 0x68, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x46, 0x69, 0x6c, 0x65, 0x55, 0x72, + 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, 0x0e, 0x72, 0x65, 0x66, 0x72, 0x65, 0x73, + 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0xb5, 0x01, 0x0a, 0x1a, 0x52, 0x65, 0x66, + 0x72, 0x65, 0x73, 0x68, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x46, 0x69, 0x6c, 0x65, 0x55, 0x72, 0x6c, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x42, 0x0a, 0x07, 0x72, 0x65, 0x70, 0x6f, 0x5f, + 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x64, 0x6f, 0x6c, 0x74, 0x2e, + 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x73, + 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x52, 0x65, 0x70, + 0x6f, 0x49, 0x64, 0x52, 0x06, 0x72, 0x65, 0x70, 0x6f, 0x49, 0x64, 0x12, 0x17, 0x0a, 0x07, 0x66, + 0x69, 0x6c, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x66, 0x69, + 0x6c, 0x65, 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x72, 0x65, 0x70, 0x6f, 0x5f, 0x74, 0x6f, 0x6b, + 0x65, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x72, 0x65, 0x70, 0x6f, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x1b, 0x0a, 0x09, 0x72, 0x65, 0x70, 0x6f, 0x5f, 0x70, 0x61, 0x74, 0x68, - 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x72, 0x65, 0x70, 0x6f, 0x50, 0x61, 0x74, 0x68, - 0x22, 0x50, 0x0a, 0x15, 0x41, 0x64, 0x64, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x46, 0x69, 0x6c, 0x65, - 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x75, 0x63, - 0x63, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x73, 0x75, 0x63, 0x63, - 0x65, 0x73, 0x73, 0x12, 0x1d, 0x0a, 0x0a, 0x72, 0x65, 0x70, 0x6f, 0x5f, 0x74, 0x6f, 0x6b, 0x65, - 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x72, 0x65, 0x70, 0x6f, 0x54, 0x6f, 0x6b, - 0x65, 0x6e, 0x2a, 0xa4, 0x01, 0x0a, 0x16, 0x50, 0x75, 0x73, 0x68, 0x43, 0x6f, 0x6e, 0x63, 0x75, - 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x12, 0x28, 0x0a, - 0x24, 0x50, 0x55, 0x53, 0x48, 0x5f, 0x43, 0x4f, 0x4e, 0x43, 0x55, 0x52, 0x52, 0x45, 0x4e, 0x43, - 0x59, 0x5f, 0x43, 0x4f, 0x4e, 0x54, 0x52, 0x4f, 0x4c, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, - 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x2f, 0x0a, 0x2b, 0x50, 0x55, 0x53, 0x48, 0x5f, - 0x43, 0x4f, 0x4e, 0x43, 0x55, 0x52, 0x52, 0x45, 0x4e, 0x43, 0x59, 0x5f, 0x43, 0x4f, 0x4e, 0x54, - 0x52, 0x4f, 0x4c, 0x5f, 0x49, 0x47, 0x4e, 0x4f, 0x52, 0x45, 0x5f, 0x57, 0x4f, 0x52, 0x4b, 0x49, - 0x4e, 0x47, 0x5f, 0x53, 0x45, 0x54, 0x10, 0x01, 0x12, 0x2f, 0x0a, 0x2b, 0x50, 0x55, 0x53, 0x48, + 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x72, 0x65, 0x70, 0x6f, 0x50, 0x61, 0x74, 0x68, + 0x22, 0x8f, 0x01, 0x0a, 0x1b, 0x52, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x54, 0x61, 0x62, 0x6c, + 0x65, 0x46, 0x69, 0x6c, 0x65, 0x55, 0x72, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, + 0x72, 0x6c, 0x12, 0x3f, 0x0a, 0x0d, 0x72, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x5f, 0x61, 0x66, + 0x74, 0x65, 0x72, 0x18, 0x02, 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, 0x0c, 0x72, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x41, 0x66, + 0x74, 0x65, 0x72, 0x12, 0x1d, 0x0a, 0x0a, 0x72, 0x65, 0x70, 0x6f, 0x5f, 0x74, 0x6f, 0x6b, 0x65, + 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x72, 0x65, 0x70, 0x6f, 0x54, 0x6f, 0x6b, + 0x65, 0x6e, 0x22, 0x99, 0x02, 0x0a, 0x16, 0x4c, 0x69, 0x73, 0x74, 0x54, 0x61, 0x62, 0x6c, 0x65, + 0x46, 0x69, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1b, 0x0a, + 0x09, 0x72, 0x6f, 0x6f, 0x74, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, + 0x52, 0x08, 0x72, 0x6f, 0x6f, 0x74, 0x48, 0x61, 0x73, 0x68, 0x12, 0x58, 0x0a, 0x0f, 0x74, 0x61, + 0x62, 0x6c, 0x65, 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x02, 0x20, + 0x03, 0x28, 0x0b, 0x32, 0x30, 0x2e, 0x64, 0x6f, 0x6c, 0x74, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, + 0x63, 0x65, 0x73, 0x2e, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x73, 0x61, 0x70, 0x69, 0x2e, 0x76, + 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x46, 0x69, 0x6c, + 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x0d, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x46, 0x69, 0x6c, 0x65, + 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x69, 0x0a, 0x18, 0x61, 0x70, 0x70, 0x65, 0x6e, 0x64, 0x69, 0x78, + 0x5f, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x69, 0x6e, 0x66, 0x6f, + 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x30, 0x2e, 0x64, 0x6f, 0x6c, 0x74, 0x2e, 0x73, 0x65, + 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x73, 0x61, 0x70, + 0x69, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x54, 0x61, 0x62, 0x6c, 0x65, + 0x46, 0x69, 0x6c, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x15, 0x61, 0x70, 0x70, 0x65, 0x6e, 0x64, + 0x69, 0x78, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x46, 0x69, 0x6c, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x12, + 0x1d, 0x0a, 0x0a, 0x72, 0x65, 0x70, 0x6f, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x04, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x09, 0x72, 0x65, 0x70, 0x6f, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x22, 0xba, + 0x03, 0x0a, 0x14, 0x41, 0x64, 0x64, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x46, 0x69, 0x6c, 0x65, 0x73, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x42, 0x0a, 0x07, 0x72, 0x65, 0x70, 0x6f, 0x5f, + 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x64, 0x6f, 0x6c, 0x74, 0x2e, + 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x73, + 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x52, 0x65, 0x70, + 0x6f, 0x49, 0x64, 0x52, 0x06, 0x72, 0x65, 0x70, 0x6f, 0x49, 0x64, 0x12, 0x61, 0x0a, 0x12, 0x63, + 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x5f, 0x72, 0x65, 0x70, 0x6f, 0x5f, 0x66, 0x6f, 0x72, 0x6d, 0x61, + 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x33, 0x2e, 0x64, 0x6f, 0x6c, 0x74, 0x2e, 0x73, + 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x73, 0x61, + 0x70, 0x69, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x43, 0x6c, 0x69, 0x65, + 0x6e, 0x74, 0x52, 0x65, 0x70, 0x6f, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x52, 0x10, 0x63, 0x6c, + 0x69, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x70, 0x6f, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x12, 0x5b, + 0x0a, 0x10, 0x63, 0x68, 0x75, 0x6e, 0x6b, 0x5f, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x69, 0x6e, + 0x66, 0x6f, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x31, 0x2e, 0x64, 0x6f, 0x6c, 0x74, 0x2e, + 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x73, + 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x43, 0x68, 0x75, + 0x6e, 0x6b, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x0e, 0x63, 0x68, 0x75, + 0x6e, 0x6b, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x62, 0x0a, 0x0f, 0x61, + 0x70, 0x70, 0x65, 0x6e, 0x64, 0x69, 0x78, 0x5f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x04, + 0x20, 0x01, 0x28, 0x0e, 0x32, 0x39, 0x2e, 0x64, 0x6f, 0x6c, 0x74, 0x2e, 0x73, 0x65, 0x72, 0x76, + 0x69, 0x63, 0x65, 0x73, 0x2e, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x73, 0x61, 0x70, 0x69, 0x2e, + 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x4d, 0x61, 0x6e, 0x69, 0x66, 0x65, 0x73, + 0x74, 0x41, 0x70, 0x70, 0x65, 0x6e, 0x64, 0x69, 0x78, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x52, + 0x0e, 0x61, 0x70, 0x70, 0x65, 0x6e, 0x64, 0x69, 0x78, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, + 0x1d, 0x0a, 0x0a, 0x72, 0x65, 0x70, 0x6f, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x05, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x09, 0x72, 0x65, 0x70, 0x6f, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x1b, + 0x0a, 0x09, 0x72, 0x65, 0x70, 0x6f, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x06, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x08, 0x72, 0x65, 0x70, 0x6f, 0x50, 0x61, 0x74, 0x68, 0x22, 0x50, 0x0a, 0x15, 0x41, + 0x64, 0x64, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x12, 0x1d, + 0x0a, 0x0a, 0x72, 0x65, 0x70, 0x6f, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x09, 0x72, 0x65, 0x70, 0x6f, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x2a, 0xa4, 0x01, + 0x0a, 0x16, 0x50, 0x75, 0x73, 0x68, 0x43, 0x6f, 0x6e, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, + 0x79, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x12, 0x28, 0x0a, 0x24, 0x50, 0x55, 0x53, 0x48, 0x5f, 0x43, 0x4f, 0x4e, 0x43, 0x55, 0x52, 0x52, 0x45, 0x4e, 0x43, 0x59, 0x5f, 0x43, 0x4f, 0x4e, - 0x54, 0x52, 0x4f, 0x4c, 0x5f, 0x41, 0x53, 0x53, 0x45, 0x52, 0x54, 0x5f, 0x57, 0x4f, 0x52, 0x4b, - 0x49, 0x4e, 0x47, 0x5f, 0x53, 0x45, 0x54, 0x10, 0x02, 0x2a, 0x89, 0x01, 0x0a, 0x16, 0x4d, 0x61, - 0x6e, 0x69, 0x66, 0x65, 0x73, 0x74, 0x41, 0x70, 0x70, 0x65, 0x6e, 0x64, 0x69, 0x78, 0x4f, 0x70, - 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x28, 0x0a, 0x24, 0x4d, 0x41, 0x4e, 0x49, 0x46, 0x45, 0x53, 0x54, - 0x5f, 0x41, 0x50, 0x50, 0x45, 0x4e, 0x44, 0x49, 0x58, 0x5f, 0x4f, 0x50, 0x54, 0x49, 0x4f, 0x4e, - 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x20, - 0x0a, 0x1c, 0x4d, 0x41, 0x4e, 0x49, 0x46, 0x45, 0x53, 0x54, 0x5f, 0x41, 0x50, 0x50, 0x45, 0x4e, - 0x44, 0x49, 0x58, 0x5f, 0x4f, 0x50, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x53, 0x45, 0x54, 0x10, 0x01, - 0x12, 0x23, 0x0a, 0x1f, 0x4d, 0x41, 0x4e, 0x49, 0x46, 0x45, 0x53, 0x54, 0x5f, 0x41, 0x50, 0x50, - 0x45, 0x4e, 0x44, 0x49, 0x58, 0x5f, 0x4f, 0x50, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x41, 0x50, 0x50, - 0x45, 0x4e, 0x44, 0x10, 0x02, 0x32, 0xb2, 0x0b, 0x0a, 0x11, 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x53, - 0x74, 0x6f, 0x72, 0x65, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x88, 0x01, 0x0a, 0x0f, - 0x47, 0x65, 0x74, 0x52, 0x65, 0x70, 0x6f, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, - 0x39, 0x2e, 0x64, 0x6f, 0x6c, 0x74, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, - 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x73, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, - 0x68, 0x61, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x65, 0x70, 0x6f, 0x4d, 0x65, 0x74, 0x61, 0x64, - 0x61, 0x74, 0x61, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x3a, 0x2e, 0x64, 0x6f, 0x6c, + 0x54, 0x52, 0x4f, 0x4c, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, + 0x10, 0x00, 0x12, 0x2f, 0x0a, 0x2b, 0x50, 0x55, 0x53, 0x48, 0x5f, 0x43, 0x4f, 0x4e, 0x43, 0x55, + 0x52, 0x52, 0x45, 0x4e, 0x43, 0x59, 0x5f, 0x43, 0x4f, 0x4e, 0x54, 0x52, 0x4f, 0x4c, 0x5f, 0x49, + 0x47, 0x4e, 0x4f, 0x52, 0x45, 0x5f, 0x57, 0x4f, 0x52, 0x4b, 0x49, 0x4e, 0x47, 0x5f, 0x53, 0x45, + 0x54, 0x10, 0x01, 0x12, 0x2f, 0x0a, 0x2b, 0x50, 0x55, 0x53, 0x48, 0x5f, 0x43, 0x4f, 0x4e, 0x43, + 0x55, 0x52, 0x52, 0x45, 0x4e, 0x43, 0x59, 0x5f, 0x43, 0x4f, 0x4e, 0x54, 0x52, 0x4f, 0x4c, 0x5f, + 0x41, 0x53, 0x53, 0x45, 0x52, 0x54, 0x5f, 0x57, 0x4f, 0x52, 0x4b, 0x49, 0x4e, 0x47, 0x5f, 0x53, + 0x45, 0x54, 0x10, 0x02, 0x2a, 0x89, 0x01, 0x0a, 0x16, 0x4d, 0x61, 0x6e, 0x69, 0x66, 0x65, 0x73, + 0x74, 0x41, 0x70, 0x70, 0x65, 0x6e, 0x64, 0x69, 0x78, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, + 0x28, 0x0a, 0x24, 0x4d, 0x41, 0x4e, 0x49, 0x46, 0x45, 0x53, 0x54, 0x5f, 0x41, 0x50, 0x50, 0x45, + 0x4e, 0x44, 0x49, 0x58, 0x5f, 0x4f, 0x50, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x55, 0x4e, 0x53, 0x50, + 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x20, 0x0a, 0x1c, 0x4d, 0x41, 0x4e, + 0x49, 0x46, 0x45, 0x53, 0x54, 0x5f, 0x41, 0x50, 0x50, 0x45, 0x4e, 0x44, 0x49, 0x58, 0x5f, 0x4f, + 0x50, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x53, 0x45, 0x54, 0x10, 0x01, 0x12, 0x23, 0x0a, 0x1f, 0x4d, + 0x41, 0x4e, 0x49, 0x46, 0x45, 0x53, 0x54, 0x5f, 0x41, 0x50, 0x50, 0x45, 0x4e, 0x44, 0x49, 0x58, + 0x5f, 0x4f, 0x50, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x41, 0x50, 0x50, 0x45, 0x4e, 0x44, 0x10, 0x02, + 0x32, 0xb2, 0x0b, 0x0a, 0x11, 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x53, + 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x88, 0x01, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x52, 0x65, + 0x70, 0x6f, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x39, 0x2e, 0x64, 0x6f, 0x6c, 0x74, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x73, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x65, 0x70, 0x6f, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x76, 0x0a, 0x09, 0x48, 0x61, 0x73, 0x43, 0x68, 0x75, - 0x6e, 0x6b, 0x73, 0x12, 0x33, 0x2e, 0x64, 0x6f, 0x6c, 0x74, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x3a, 0x2e, 0x64, 0x6f, 0x6c, 0x74, 0x2e, 0x73, 0x65, 0x72, + 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x73, 0x61, 0x70, 0x69, + 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x65, 0x70, + 0x6f, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 0x76, 0x0a, 0x09, 0x48, 0x61, 0x73, 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x73, 0x12, 0x33, + 0x2e, 0x64, 0x6f, 0x6c, 0x74, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x72, + 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x73, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, + 0x61, 0x31, 0x2e, 0x48, 0x61, 0x73, 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x73, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x34, 0x2e, 0x64, 0x6f, 0x6c, 0x74, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x73, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x48, 0x61, 0x73, 0x43, 0x68, 0x75, 0x6e, 0x6b, - 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x34, 0x2e, 0x64, 0x6f, 0x6c, 0x74, 0x2e, - 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x73, - 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x48, 0x61, 0x73, - 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x8d, - 0x01, 0x0a, 0x14, 0x47, 0x65, 0x74, 0x44, 0x6f, 0x77, 0x6e, 0x6c, 0x6f, 0x61, 0x64, 0x4c, 0x6f, - 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x39, 0x2e, 0x64, 0x6f, 0x6c, 0x74, 0x2e, 0x73, - 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x73, 0x61, - 0x70, 0x69, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x44, - 0x6f, 0x77, 0x6e, 0x6c, 0x6f, 0x61, 0x64, 0x4c, 0x6f, 0x63, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x3a, 0x2e, 0x64, 0x6f, 0x6c, 0x74, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, + 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x8d, 0x01, 0x0a, 0x14, 0x47, 0x65, + 0x74, 0x44, 0x6f, 0x77, 0x6e, 0x6c, 0x6f, 0x61, 0x64, 0x4c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x73, 0x12, 0x39, 0x2e, 0x64, 0x6f, 0x6c, 0x74, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x73, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x44, 0x6f, 0x77, 0x6e, 0x6c, 0x6f, - 0x61, 0x64, 0x4c, 0x6f, 0x63, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x94, - 0x01, 0x0a, 0x17, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x44, 0x6f, 0x77, 0x6e, 0x6c, 0x6f, 0x61, - 0x64, 0x4c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x39, 0x2e, 0x64, 0x6f, 0x6c, - 0x74, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x72, 0x65, 0x6d, 0x6f, 0x74, - 0x65, 0x73, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x47, - 0x65, 0x74, 0x44, 0x6f, 0x77, 0x6e, 0x6c, 0x6f, 0x61, 0x64, 0x4c, 0x6f, 0x63, 0x73, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x3a, 0x2e, 0x64, 0x6f, 0x6c, 0x74, 0x2e, 0x73, 0x65, 0x72, + 0x61, 0x64, 0x4c, 0x6f, 0x63, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x3a, 0x2e, + 0x64, 0x6f, 0x6c, 0x74, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x72, 0x65, + 0x6d, 0x6f, 0x74, 0x65, 0x73, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, + 0x31, 0x2e, 0x47, 0x65, 0x74, 0x44, 0x6f, 0x77, 0x6e, 0x6c, 0x6f, 0x61, 0x64, 0x4c, 0x6f, 0x63, + 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x94, 0x01, 0x0a, 0x17, 0x53, 0x74, + 0x72, 0x65, 0x61, 0x6d, 0x44, 0x6f, 0x77, 0x6e, 0x6c, 0x6f, 0x61, 0x64, 0x4c, 0x6f, 0x63, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x39, 0x2e, 0x64, 0x6f, 0x6c, 0x74, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x73, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x44, 0x6f, 0x77, - 0x6e, 0x6c, 0x6f, 0x61, 0x64, 0x4c, 0x6f, 0x63, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x28, 0x01, 0x30, 0x01, 0x12, 0x87, 0x01, 0x0a, 0x12, 0x47, 0x65, 0x74, 0x55, 0x70, 0x6c, - 0x6f, 0x61, 0x64, 0x4c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x37, 0x2e, 0x64, + 0x6e, 0x6c, 0x6f, 0x61, 0x64, 0x4c, 0x6f, 0x63, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x3a, 0x2e, 0x64, 0x6f, 0x6c, 0x74, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, + 0x2e, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x73, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x61, 0x6c, + 0x70, 0x68, 0x61, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x44, 0x6f, 0x77, 0x6e, 0x6c, 0x6f, 0x61, 0x64, + 0x4c, 0x6f, 0x63, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x28, 0x01, 0x30, 0x01, + 0x12, 0x87, 0x01, 0x0a, 0x12, 0x47, 0x65, 0x74, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x4c, 0x6f, + 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x37, 0x2e, 0x64, 0x6f, 0x6c, 0x74, 0x2e, 0x73, + 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x73, 0x61, + 0x70, 0x69, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x55, + 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x4c, 0x6f, 0x63, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x38, 0x2e, 0x64, 0x6f, 0x6c, 0x74, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, + 0x2e, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x73, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x61, 0x6c, + 0x70, 0x68, 0x61, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x4c, 0x6f, + 0x63, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x6d, 0x0a, 0x06, 0x52, 0x65, + 0x62, 0x61, 0x73, 0x65, 0x12, 0x30, 0x2e, 0x64, 0x6f, 0x6c, 0x74, 0x2e, 0x73, 0x65, 0x72, 0x76, + 0x69, 0x63, 0x65, 0x73, 0x2e, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x73, 0x61, 0x70, 0x69, 0x2e, + 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x52, 0x65, 0x62, 0x61, 0x73, 0x65, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x31, 0x2e, 0x64, 0x6f, 0x6c, 0x74, 0x2e, 0x73, 0x65, + 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x73, 0x61, 0x70, + 0x69, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x52, 0x65, 0x62, 0x61, 0x73, + 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x67, 0x0a, 0x04, 0x52, 0x6f, 0x6f, + 0x74, 0x12, 0x2e, 0x2e, 0x64, 0x6f, 0x6c, 0x74, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, + 0x73, 0x2e, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x73, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x61, + 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x52, 0x6f, 0x6f, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x2f, 0x2e, 0x64, 0x6f, 0x6c, 0x74, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, + 0x73, 0x2e, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x73, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x61, + 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x52, 0x6f, 0x6f, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x12, 0x6d, 0x0a, 0x06, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x12, 0x30, 0x2e, 0x64, 0x6f, 0x6c, 0x74, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x73, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, - 0x2e, 0x47, 0x65, 0x74, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x4c, 0x6f, 0x63, 0x73, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x38, 0x2e, 0x64, 0x6f, 0x6c, 0x74, 0x2e, 0x73, 0x65, 0x72, - 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x73, 0x61, 0x70, 0x69, - 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x55, 0x70, 0x6c, - 0x6f, 0x61, 0x64, 0x4c, 0x6f, 0x63, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, - 0x6d, 0x0a, 0x06, 0x52, 0x65, 0x62, 0x61, 0x73, 0x65, 0x12, 0x30, 0x2e, 0x64, 0x6f, 0x6c, 0x74, - 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, - 0x73, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x52, 0x65, - 0x62, 0x61, 0x73, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x31, 0x2e, 0x64, 0x6f, - 0x6c, 0x74, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x72, 0x65, 0x6d, 0x6f, - 0x74, 0x65, 0x73, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, - 0x52, 0x65, 0x62, 0x61, 0x73, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x67, - 0x0a, 0x04, 0x52, 0x6f, 0x6f, 0x74, 0x12, 0x2e, 0x2e, 0x64, 0x6f, 0x6c, 0x74, 0x2e, 0x73, 0x65, - 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x73, 0x61, 0x70, - 0x69, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x52, 0x6f, 0x6f, 0x74, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2f, 0x2e, 0x64, 0x6f, 0x6c, 0x74, 0x2e, 0x73, 0x65, - 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x73, 0x61, 0x70, - 0x69, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x52, 0x6f, 0x6f, 0x74, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x6d, 0x0a, 0x06, 0x43, 0x6f, 0x6d, 0x6d, 0x69, - 0x74, 0x12, 0x30, 0x2e, 0x64, 0x6f, 0x6c, 0x74, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, + 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x31, + 0x2e, 0x64, 0x6f, 0x6c, 0x74, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x72, + 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x73, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, + 0x61, 0x31, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 0x85, 0x01, 0x0a, 0x0e, 0x4c, 0x69, 0x73, 0x74, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x46, + 0x69, 0x6c, 0x65, 0x73, 0x12, 0x38, 0x2e, 0x64, 0x6f, 0x6c, 0x74, 0x2e, 0x73, 0x65, 0x72, 0x76, + 0x69, 0x63, 0x65, 0x73, 0x2e, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x73, 0x61, 0x70, 0x69, 0x2e, + 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x54, 0x61, 0x62, + 0x6c, 0x65, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x39, + 0x2e, 0x64, 0x6f, 0x6c, 0x74, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x72, + 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x73, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, + 0x61, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x46, 0x69, 0x6c, 0x65, + 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x94, 0x01, 0x0a, 0x13, 0x52, 0x65, + 0x66, 0x72, 0x65, 0x73, 0x68, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x46, 0x69, 0x6c, 0x65, 0x55, 0x72, + 0x6c, 0x12, 0x3d, 0x2e, 0x64, 0x6f, 0x6c, 0x74, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x73, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x61, - 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x31, 0x2e, 0x64, 0x6f, 0x6c, 0x74, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, - 0x63, 0x65, 0x73, 0x2e, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x73, 0x61, 0x70, 0x69, 0x2e, 0x76, - 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x85, 0x01, 0x0a, 0x0e, 0x4c, 0x69, 0x73, 0x74, 0x54, - 0x61, 0x62, 0x6c, 0x65, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x12, 0x38, 0x2e, 0x64, 0x6f, 0x6c, 0x74, - 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, - 0x73, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x4c, 0x69, - 0x73, 0x74, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x39, 0x2e, 0x64, 0x6f, 0x6c, 0x74, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, - 0x63, 0x65, 0x73, 0x2e, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x73, 0x61, 0x70, 0x69, 0x2e, 0x76, - 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x54, 0x61, 0x62, 0x6c, - 0x65, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x94, - 0x01, 0x0a, 0x13, 0x52, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x46, - 0x69, 0x6c, 0x65, 0x55, 0x72, 0x6c, 0x12, 0x3d, 0x2e, 0x64, 0x6f, 0x6c, 0x74, 0x2e, 0x73, 0x65, - 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x73, 0x61, 0x70, - 0x69, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x52, 0x65, 0x66, 0x72, 0x65, - 0x73, 0x68, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x46, 0x69, 0x6c, 0x65, 0x55, 0x72, 0x6c, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x3e, 0x2e, 0x64, 0x6f, 0x6c, 0x74, 0x2e, 0x73, 0x65, 0x72, - 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x73, 0x61, 0x70, 0x69, - 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x52, 0x65, 0x66, 0x72, 0x65, 0x73, - 0x68, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x46, 0x69, 0x6c, 0x65, 0x55, 0x72, 0x6c, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x82, 0x01, 0x0a, 0x0d, 0x41, 0x64, 0x64, 0x54, 0x61, 0x62, - 0x6c, 0x65, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x12, 0x37, 0x2e, 0x64, 0x6f, 0x6c, 0x74, 0x2e, 0x73, - 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x73, 0x61, - 0x70, 0x69, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x41, 0x64, 0x64, 0x54, - 0x61, 0x62, 0x6c, 0x65, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x38, 0x2e, 0x64, 0x6f, 0x6c, 0x74, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, + 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x52, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x54, 0x61, 0x62, + 0x6c, 0x65, 0x46, 0x69, 0x6c, 0x65, 0x55, 0x72, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x3e, 0x2e, 0x64, 0x6f, 0x6c, 0x74, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x73, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x61, 0x6c, - 0x70, 0x68, 0x61, 0x31, 0x2e, 0x41, 0x64, 0x64, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x46, 0x69, 0x6c, - 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x53, 0x5a, 0x51, 0x67, 0x69, - 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x64, 0x6f, 0x6c, 0x74, 0x68, 0x75, 0x62, - 0x2f, 0x64, 0x6f, 0x6c, 0x74, 0x2f, 0x67, 0x6f, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x2f, 0x64, 0x6f, 0x6c, 0x74, 0x2f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, - 0x2f, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x73, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x61, 0x6c, - 0x70, 0x68, 0x61, 0x31, 0x3b, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x73, 0x61, 0x70, 0x69, 0x62, - 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x70, 0x68, 0x61, 0x31, 0x2e, 0x52, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x54, 0x61, 0x62, 0x6c, + 0x65, 0x46, 0x69, 0x6c, 0x65, 0x55, 0x72, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x12, 0x82, 0x01, 0x0a, 0x0d, 0x41, 0x64, 0x64, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x46, 0x69, 0x6c, + 0x65, 0x73, 0x12, 0x37, 0x2e, 0x64, 0x6f, 0x6c, 0x74, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, + 0x65, 0x73, 0x2e, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x73, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, + 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x41, 0x64, 0x64, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x46, + 0x69, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x38, 0x2e, 0x64, 0x6f, + 0x6c, 0x74, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x72, 0x65, 0x6d, 0x6f, + 0x74, 0x65, 0x73, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, + 0x41, 0x64, 0x64, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x53, 0x5a, 0x51, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, + 0x63, 0x6f, 0x6d, 0x2f, 0x64, 0x6f, 0x6c, 0x74, 0x68, 0x75, 0x62, 0x2f, 0x64, 0x6f, 0x6c, 0x74, + 0x2f, 0x67, 0x6f, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x64, 0x6f, + 0x6c, 0x74, 0x2f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2f, 0x72, 0x65, 0x6d, 0x6f, + 0x74, 0x65, 0x73, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x3b, + 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x73, 0x61, 0x70, 0x69, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x33, } var ( diff --git a/go/libraries/doltcore/remotesrv/grpc.go b/go/libraries/doltcore/remotesrv/grpc.go index d158f47e05b..89f53780bc5 100644 --- a/go/libraries/doltcore/remotesrv/grpc.go +++ b/go/libraries/doltcore/remotesrv/grpc.go @@ -295,7 +295,12 @@ func (rs *RemoteChunkStore) StreamDownloadLocations(stream remotesapi.ChunkStore var ranges []*remotesapi.RangeChunk for h, r := range hashToRange { hCpy := h - ranges = append(ranges, &remotesapi.RangeChunk{Hash: hCpy[:], Offset: r.Offset, Length: r.Length}) + ranges = append(ranges, &remotesapi.RangeChunk{ + Hash: hCpy[:], + Offset: r.Offset, + Length: r.Length, + DictionaryOffset: r.DictOffset, + DictionaryLength: r.DictLength}) } url := rs.getDownloadUrl(md, prefix+"/"+loc) diff --git a/go/libraries/doltcore/remotesrv/http.go b/go/libraries/doltcore/remotesrv/http.go index eedbf926148..0b89d43fb24 100644 --- a/go/libraries/doltcore/remotesrv/http.go +++ b/go/libraries/doltcore/remotesrv/http.go @@ -29,6 +29,7 @@ import ( "strconv" "strings" + "github.com/dolthub/dolt/go/store/nbs" "github.com/sirupsen/logrus" "google.golang.org/grpc/metadata" "google.golang.org/grpc/peer" @@ -94,12 +95,18 @@ func (fh filehandler) ServeHTTP(respWr http.ResponseWriter, req *http.Request) { respWr.WriteHeader(http.StatusBadRequest) return } - _, ok := hash.MaybeParse(path[i+1:]) + + fileName := path[i+1:] + if strings.HasSuffix(fileName, nbs.ArchiveFileSuffix) { + fileName = fileName[:len(fileName)-len(nbs.ArchiveFileSuffix)] + } + _, ok := hash.MaybeParse(fileName) if !ok { - logger.WithField("last_path_component", path[i+1:]).Warn("bad request with unparseable last path component") + logger.WithField("last_path_component", fileName).Warn("bad request with unparseable last path component") respWr.WriteHeader(http.StatusBadRequest) return } + abs, err := fh.fs.Abs(path) if err != nil { logger.WithError(err).Error("could not get absolute path") diff --git a/go/libraries/doltcore/remotesrv/server.go b/go/libraries/doltcore/remotesrv/server.go index 46b7ec5182f..6b451a63109 100644 --- a/go/libraries/doltcore/remotesrv/server.go +++ b/go/libraries/doltcore/remotesrv/server.go @@ -17,7 +17,6 @@ package remotesrv import ( "context" "crypto/tls" - "errors" "net" "net/http" "strings" @@ -29,7 +28,6 @@ import ( "google.golang.org/grpc" remotesapi "github.com/dolthub/dolt/go/gen/proto/dolt/services/remotesapi/v1alpha1" - "github.com/dolthub/dolt/go/libraries/doltcore/env" "github.com/dolthub/dolt/go/libraries/utils/filesys" ) @@ -80,14 +78,6 @@ func NewServer(args ServerArgs) (*Server, error) { args.Logger = logrus.NewEntry(logrus.StandardLogger()) } - storageMetadata, err := env.GetMultiEnvStorageMetadata(args.FS) - if err != nil { - return nil, err - } - if storageMetadata.ArchiveFilesPresent() { - return nil, errors.New("archive files present. Please run `dolt archive --revert` before running the server.") - } - s := new(Server) s.stopChan = make(chan struct{}) diff --git a/go/libraries/doltcore/remotestorage/chunk_cache.go b/go/libraries/doltcore/remotestorage/chunk_cache.go index 0412b20a0ab..0d1e968844d 100644 --- a/go/libraries/doltcore/remotestorage/chunk_cache.go +++ b/go/libraries/doltcore/remotestorage/chunk_cache.go @@ -22,20 +22,20 @@ import ( // ChunkCache is an interface used for caching chunks type ChunkCache interface { // Put puts a slice of chunks into the cache. - Put(c []nbs.CompressedChunk) bool + Put(c []nbs.ToChunker) bool // Get gets a map of hash to chunk for a set of hashes. In the event that a chunk is not in the cache, chunks.Empty. // is put in it's place - Get(h hash.HashSet) map[hash.Hash]nbs.CompressedChunk + Get(h hash.HashSet) map[hash.Hash]nbs.ToChunker // Has takes a set of hashes and returns the set of hashes that the cache currently does not have in it. Has(h hash.HashSet) (absent hash.HashSet) // PutChunk puts a single chunk in the cache. true returns in the event that the chunk was cached successfully // and false is returned if that chunk is already is the cache. - PutChunk(chunk nbs.CompressedChunk) bool + PutChunk(chunk nbs.ToChunker) bool // GetAndClearChunksToFlush gets a map of hash to chunk which includes all the chunks that were put in the cache // between the last time GetAndClearChunksToFlush was called and now. - GetAndClearChunksToFlush() map[hash.Hash]nbs.CompressedChunk + GetAndClearChunksToFlush() map[hash.Hash]nbs.ToChunker } diff --git a/go/libraries/doltcore/remotestorage/chunk_fetcher.go b/go/libraries/doltcore/remotestorage/chunk_fetcher.go index 51bd4bac614..88b9dba7d17 100644 --- a/go/libraries/doltcore/remotestorage/chunk_fetcher.go +++ b/go/libraries/doltcore/remotestorage/chunk_fetcher.go @@ -18,9 +18,12 @@ import ( "context" "errors" "io" + "sync" "sync/atomic" "time" + "github.com/cenkalti/backoff/v4" + "github.com/dolthub/gozstd" "golang.org/x/sync/errgroup" "google.golang.org/grpc" @@ -30,6 +33,8 @@ import ( "github.com/dolthub/dolt/go/libraries/doltcore/remotestorage/internal/reliable" "github.com/dolthub/dolt/go/store/hash" "github.com/dolthub/dolt/go/store/nbs" + + "github.com/hashicorp/golang-lru/v2" ) // A remotestorage.ChunkFetcher is a pipelined chunk fetcher for fetching a @@ -48,8 +53,14 @@ type ChunkFetcher struct { eg *errgroup.Group egCtx context.Context + // toGetCh is the channel used to request chunks. This will be initially given a root, + // and as refs are found, they will be added to the channel for workers to batch and request. toGetCh chan hash.HashSet - resCh chan nbs.CompressedChunk + + // resCh is the results channel for the fetcher. It is used both to return + // chunks themselves, and to indicate which chunks were requested but missing + // by having a Hash, but are empty. + resCh chan nbs.ToChunker abortCh chan struct{} stats StatsRecorder @@ -62,14 +73,21 @@ const ( reliableCallDeliverRespTimeout = 15 * time.Second ) +var globalDictCache *dictionaryCache +var once sync.Once + func NewChunkFetcher(ctx context.Context, dcs *DoltChunkStore) *ChunkFetcher { + once.Do(func() { + globalDictCache = NewDictionaryCache(dcs.csClient) + }) + eg, ctx := errgroup.WithContext(ctx) ret := &ChunkFetcher{ eg: eg, egCtx: ctx, toGetCh: make(chan hash.HashSet), - resCh: make(chan nbs.CompressedChunk), + resCh: make(chan nbs.ToChunker), abortCh: make(chan struct{}), stats: StatsFactory(), @@ -123,7 +141,7 @@ func (f *ChunkFetcher) CloseSend() error { // by |Get|. Returns |io.EOF| after |CloseSend| is called and all requested // chunks have been successfully received. Returns an error if this // |ChunkFetcher| is terminally failed or if the supplied |ctx| is |Done|. -func (f *ChunkFetcher) Recv(ctx context.Context) (nbs.CompressedChunk, error) { +func (f *ChunkFetcher) Recv(ctx context.Context) (nbs.ToChunker, error) { select { case <-ctx.Done(): return nbs.CompressedChunk{}, context.Cause(ctx) @@ -219,7 +237,7 @@ func fetcherHashSetToGetDlLocsReqsThread(ctx context.Context, reqCh chan hash.Ha // delivered in |reqCh|, and they will be delivered in order. // // This function handles backoff and retries for the underlying streaming RPC. -func fetcherRPCDownloadLocsThread(ctx context.Context, reqCh chan *remotesapi.GetDownloadLocsRequest, resCh chan []*remotesapi.DownloadLoc, client remotesapi.ChunkStoreServiceClient, storeRepoToken func(string), missingChunkCh chan nbs.CompressedChunk, host string) error { +func fetcherRPCDownloadLocsThread(ctx context.Context, reqCh chan *remotesapi.GetDownloadLocsRequest, resCh chan []*remotesapi.DownloadLoc, client remotesapi.ChunkStoreServiceClient, storeRepoToken func(string), missingChunkCh chan nbs.ToChunker, host string) error { stream, err := reliable.MakeCall[*remotesapi.GetDownloadLocsRequest, *remotesapi.GetDownloadLocsResponse]( ctx, reliable.CallOptions[*remotesapi.GetDownloadLocsRequest, *remotesapi.GetDownloadLocsResponse]{ @@ -362,7 +380,7 @@ func (d downloads) Add(resp *remotesapi.DownloadLoc) { d.refreshes[path] = refresh } for _, r := range gr.Ranges { - d.ranges.Insert(gr.Url, r.Hash, r.Offset, r.Length) + d.ranges.Insert(gr.Url, r.Hash[:], r.Offset, r.Length, r.DictionaryOffset, r.DictionaryLength) } } @@ -371,9 +389,11 @@ func toGetRange(rs []*ranges.GetRange) *GetRange { for _, r := range rs { ret.Url = r.Url ret.Ranges = append(ret.Ranges, &remotesapi.RangeChunk{ - Hash: r.Hash, - Offset: r.Offset, - Length: r.Length, + Hash: r.Hash, + Offset: r.Offset, + Length: r.Length, + DictionaryOffset: r.DictionaryOffset, + DictionaryLength: r.DictionaryLength, }) } return ret @@ -527,7 +547,7 @@ func (cc *ConcurrencyControl) Run(ctx context.Context, done <-chan struct{}, ss } } -func fetcherDownloadURLThreads(ctx context.Context, fetchReqCh chan fetchReq, doneCh chan struct{}, chunkCh chan nbs.CompressedChunk, client remotesapi.ChunkStoreServiceClient, stats StatsRecorder, fetcher HTTPFetcher, params NetworkRequestParams) error { +func fetcherDownloadURLThreads(ctx context.Context, fetchReqCh chan fetchReq, doneCh chan struct{}, chunkCh chan nbs.ToChunker, client remotesapi.ChunkStoreServiceClient, stats StatsRecorder, fetcher HTTPFetcher, params NetworkRequestParams) error { eg, ctx := errgroup.WithContext(ctx) cc := &ConcurrencyControl{ MaxConcurrency: params.MaximumConcurrentDownloads, @@ -559,7 +579,7 @@ func fetcherDownloadURLThreads(ctx context.Context, fetchReqCh chan fetchReq, do return nil } -func fetcherDownloadURLThread(ctx context.Context, fetchReqCh chan fetchReq, doneCh <-chan struct{}, chunkCh chan nbs.CompressedChunk, client remotesapi.ChunkStoreServiceClient, stats StatsRecorder, health reliable.HealthRecorder, fetcher HTTPFetcher, params NetworkRequestParams) error { +func fetcherDownloadURLThread(ctx context.Context, fetchReqCh chan fetchReq, doneCh <-chan struct{}, chunkCh chan nbs.ToChunker, client remotesapi.ChunkStoreServiceClient, stats StatsRecorder, health reliable.HealthRecorder, fetcher HTTPFetcher, params NetworkRequestParams) error { respCh := make(chan fetchResp) cancelCh := make(chan struct{}) for { @@ -587,3 +607,144 @@ func fetcherDownloadURLThread(ctx context.Context, fetchReqCh chan fetchReq, don } } } + +// dictionaryCache caches dictionaries for the chunks in an archive store. When we fetch from a database with an archive, +// we get back the path/offset/length of the dictionary for each chunk. These, by definition, are repeatedly used +// and we don't want to request the same dictionary multiple times. +// +// Currently (feb '25), archives generally have only a default dictionary, so this is kind of overkill. Mainly planning +// for the future when chunk grouping is the default and we could have thousands of dictionaries. +type dictionaryCache struct { + cache *lru.TwoQueueCache[DictionaryKey, *gozstd.DDict] + pending sync.Map + client remotesapi.ChunkStoreServiceClient + dlds downloads +} + +// DictionaryKey is the a globaly unique identifier for an archive dictionary. +type DictionaryKey struct { + // This is the short url to the resource, not including the query parameters - which are provided by the + // locationRefresher. + path string + off uint64 + len uint32 +} + +func NewDictionaryCache(client remotesapi.ChunkStoreServiceClient) *dictionaryCache { + c, err := lru.New2Q[DictionaryKey, *gozstd.DDict](1024) + if err != nil { + panic(err) + } + + return &dictionaryCache{ + cache: c, + client: client, + dlds: newDownloads(), + } +} + +func (dc *dictionaryCache) get(rang *GetRange, idx int, stats StatsRecorder, recorder reliable.HealthRecorder) (*gozstd.DDict, error) { + path := rang.ResourcePath() + off := rang.Ranges[idx].DictionaryOffset + ln := rang.Ranges[idx].DictionaryLength + + key := DictionaryKey{path, off, ln} + if dict, ok := dc.cache.Get(key); ok { + return dict, nil + } + + // Check for an in-flight request. Default dictionary will be requested many times, so we want to avoid + // making multiple requests for the same resource. + if ch, loaded := dc.pending.LoadOrStore(key, make(chan struct{})); loaded { + // There's an ongoing fetch, wait for its completion + <-ch.(chan struct{}) + if dict, ok := dc.cache.Get(key); ok { + return dict, nil + } + return nil, errors.New("failed to fetch dictionary due to in-flight request") + } + // When update is done, regardless of success or failure, we need to unblock anyone waiting. + defer func() { + if ch, found := dc.pending.LoadAndDelete(key); found { + close(ch.(chan *gozstd.DDict)) + } + }() + + // Fetch the dictionary + ddict, err := dc.fetchDictionary(rang, idx, stats, recorder) + if err != nil { + return nil, err + } + + // Store the dictionary in the cache + dc.cache.Add(key, ddict) + + return ddict, nil +} + +func (dc *dictionaryCache) fetchDictionary(rang *GetRange, idx int, stats StatsRecorder, recorder reliable.HealthRecorder) (*gozstd.DDict, error) { + path := rang.ResourcePath() + off := rang.Ranges[idx].DictionaryOffset + ln := rang.Ranges[idx].DictionaryLength + + ctx := context.Background() + pathToUrl := dc.dlds.refreshes[path] + if pathToUrl == nil { + // We manually construct the RangeChunk and DownloadLoc in this case because we are retrieving the dictionary span. + // We'll make a single span request, and consume the entire response to create the dictionary. + sRang := &remotesapi.HttpGetRange{} + sRang.Url = rang.Url + sRang.Ranges = append(sRang.Ranges, &remotesapi.RangeChunk{Offset: off, Length: ln}) + rang := &remotesapi.DownloadLoc_HttpGetRange{HttpGetRange: sRang} + dl := &remotesapi.DownloadLoc{Location: rang} + + refresh := new(locationRefresh) + refresh.Add(dl) + dc.dlds.refreshes[path] = refresh + pathToUrl = refresh + } + + urlF := func(lastError error) (string, error) { + earl, err := pathToUrl.GetURL(ctx, lastError, dc.client) + if err != nil { + return "", err + } + if earl == "" { + earl = path + } + return earl, nil + } + + resp := reliable.StreamingRangeDownload(ctx, reliable.StreamingRangeRequest{ + Fetcher: globalHttpFetcher, + Offset: off, + Length: uint64(ln), + UrlFact: urlF, + Stats: stats, + Health: recorder, + BackOffFact: func(ctx context.Context) backoff.BackOff { + return downloadBackOff(ctx, 3) // params.DownloadRetryCount) + }, + Throughput: reliable.MinimumThroughputCheck{ + CheckInterval: defaultRequestParams.ThroughputMinimumCheckInterval, + BytesPerCheck: defaultRequestParams.ThroughputMinimumBytesPerCheck, + NumIntervals: defaultRequestParams.ThroughputMinimumNumIntervals, + }, + RespHeadersTimeout: defaultRequestParams.RespHeadersTimeout, + }) + defer resp.Close() + + buf := make([]byte, ln) + _, err := io.ReadFull(resp.Body, buf) + if err != nil { + return nil, err + } + + // Dictionaries are compressed, but with vanilla zstd, so there is no dictionary. + rawDict, err := gozstd.Decompress(nil, buf) + if err != nil { + return nil, err + } + + return gozstd.NewDDict(rawDict) +} diff --git a/go/libraries/doltcore/remotestorage/chunk_store.go b/go/libraries/doltcore/remotestorage/chunk_store.go index c0ca1777995..1a50d149176 100644 --- a/go/libraries/doltcore/remotestorage/chunk_store.go +++ b/go/libraries/doltcore/remotestorage/chunk_store.go @@ -32,6 +32,7 @@ import ( "time" "github.com/cenkalti/backoff/v4" + "github.com/dolthub/gozstd" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/trace" @@ -317,7 +318,7 @@ func (dcs *DoltChunkStore) Get(ctx context.Context, h hash.Hash) (chunks.Chunk, func (dcs *DoltChunkStore) GetMany(ctx context.Context, hashes hash.HashSet, found func(context.Context, *chunks.Chunk)) error { ae := atomicerr.New() decompressedSize := uint64(0) - err := dcs.GetManyCompressed(ctx, hashes, func(ctx context.Context, cc nbs.CompressedChunk) { + err := dcs.GetManyCompressed(ctx, hashes, func(ctx context.Context, cc nbs.ToChunker) { if ae.IsSet() { return } @@ -340,7 +341,7 @@ func (dcs *DoltChunkStore) GetMany(ctx context.Context, hashes hash.HashSet, fou // GetMany gets the Chunks with |hashes| from the store. On return, |foundChunks| will have been fully sent all chunks // which have been found. Any non-present chunks will silently be ignored. -func (dcs *DoltChunkStore) GetManyCompressed(ctx context.Context, hashes hash.HashSet, found func(context.Context, nbs.CompressedChunk)) error { +func (dcs *DoltChunkStore) GetManyCompressed(ctx context.Context, hashes hash.HashSet, found func(context.Context, nbs.ToChunker)) error { ctx, span := tracer.Start(ctx, "remotestorage.GetManyCompressed") defer span.End() @@ -353,7 +354,7 @@ func (dcs *DoltChunkStore) GetManyCompressed(ctx context.Context, hashes hash.Ha for h := range hashes { c := hashToChunk[h] - if c.IsEmpty() { + if c == nil || c.IsEmpty() { notCached = append(notCached, h) } else { found(ctx, c) @@ -371,6 +372,8 @@ func (dcs *DoltChunkStore) GetManyCompressed(ctx context.Context, hashes hash.Ha return nil } +// GetRange is structurally the same as remotesapi.HttpGetRange, but with added functions. Instances of GetRange +// don't get sent over the wire, so it is not necessary to use the remotesapi, just convenient. type GetRange remotesapi.HttpGetRange func (gr *GetRange) ResourcePath() string { @@ -432,10 +435,11 @@ func sortRangesBySize(ranges []*GetRange) { type resourcePathToUrlFunc func(ctx context.Context, lastError error, resourcePath string) (url string, err error) -func (gr *GetRange) GetDownloadFunc(ctx context.Context, stats StatsRecorder, health reliable.HealthRecorder, fetcher HTTPFetcher, params NetworkRequestParams, chunkChan chan nbs.CompressedChunk, pathToUrl resourcePathToUrlFunc) func() error { +func (gr *GetRange) GetDownloadFunc(ctx context.Context, stats StatsRecorder, health reliable.HealthRecorder, fetcher HTTPFetcher, params NetworkRequestParams, chunkChan chan nbs.ToChunker, pathToUrl resourcePathToUrlFunc) func() error { if len(gr.Ranges) == 0 { return func() error { return nil } } + return func() error { urlF := func(lastError error) (string, error) { url, err := pathToUrl(ctx, lastError, gr.ResourcePath()) @@ -466,9 +470,9 @@ func (gr *GetRange) GetDownloadFunc(ctx context.Context, stats StatsRecorder, he RespHeadersTimeout: params.RespHeadersTimeout, }) defer resp.Close() - reader := &RangeChunkReader{GetRange: gr, Reader: resp.Body} + reader := &RangeChunkReader{Path: gr.ResourcePath(), GetRange: gr, Reader: resp.Body} for { - cc, err := reader.ReadChunk() + cc, err := reader.ReadChunk(stats, health) if errors.Is(err, io.EOF) { return nil } @@ -484,14 +488,56 @@ func (gr *GetRange) GetDownloadFunc(ctx context.Context, stats StatsRecorder, he } } +type ArchiveToChunker struct { + h hash.Hash + dictionary *gozstd.DDict + chunkData []byte +} + +func (a ArchiveToChunker) Hash() hash.Hash { + return a.h +} + +func (a ArchiveToChunker) ToChunk() (chunks.Chunk, error) { + dict := a.dictionary + data := a.chunkData + rawChunk, err := gozstd.DecompressDict(nil, data, dict) + // NM4 - calculate chunk addr for safety while testing. + newChunk := chunks.NewChunk(rawChunk) + + if newChunk.Hash() != a.h { + panic("Hash Mismatch!!") + } + + return newChunk, err +} + +func (a ArchiveToChunker) FullCompressedChunkLen() uint32 { + //TODO Not sure what the right impl for this is.... NM4. + return uint32(len(a.chunkData)) // + dictionary??? +} + +func (a ArchiveToChunker) IsEmpty() bool { + //TODO implement me + return len(a.chunkData) == 0 +} + +func (a ArchiveToChunker) IsGhost() bool { + // archives are never ghosts. They are only instantiated when the chunk is found. + return false +} + +var _ nbs.ToChunker = (*ArchiveToChunker)(nil) + type RangeChunkReader struct { + Path string GetRange *GetRange Reader io.Reader i int skip int } -func (r *RangeChunkReader) ReadChunk() (nbs.CompressedChunk, error) { +func (r *RangeChunkReader) ReadChunk(stats StatsRecorder, health reliable.HealthRecorder) (nbs.ToChunker, error) { if r.skip > 0 { _, err := io.CopyN(io.Discard, r.Reader, int64(r.skip)) if err != nil { @@ -499,21 +545,36 @@ func (r *RangeChunkReader) ReadChunk() (nbs.CompressedChunk, error) { } r.skip = 0 } - if r.i >= len(r.GetRange.Ranges) { + + idx := r.i + r.i += 1 + + if idx >= len(r.GetRange.Ranges) { return nbs.CompressedChunk{}, io.EOF } - if r.i < len(r.GetRange.Ranges)-1 { - r.skip = int(r.GetRange.GapBetween(r.i, r.i+1)) + if idx < len(r.GetRange.Ranges)-1 { + r.skip = int(r.GetRange.GapBetween(idx, idx+1)) } - l := r.GetRange.Ranges[r.i].Length - h := hash.New(r.GetRange.Ranges[r.i].Hash) - r.i += 1 + + rang := r.GetRange.Ranges[idx] + l := rang.Length + h := hash.New(rang.Hash) + buf := make([]byte, l) _, err := io.ReadFull(r.Reader, buf) if err != nil { return nbs.CompressedChunk{}, err } else { - return nbs.NewCompressedChunk(h, buf) + if rang.DictionaryLength == 0 { + return nbs.NewCompressedChunk(h, buf) + } else { + dict, err := globalDictCache.get(r.GetRange, idx, stats, health) + if err != nil { + return nbs.CompressedChunk{}, err + } + + return ArchiveToChunker{h: h, dictionary: dict, chunkData: buf}, nil + } } } @@ -574,7 +635,7 @@ type RepoRequest interface { SetRepoPath(string) } -func (dcs *DoltChunkStore) readChunksAndCache(ctx context.Context, hashes []hash.Hash, found func(context.Context, nbs.CompressedChunk)) (err error) { +func (dcs *DoltChunkStore) readChunksAndCache(ctx context.Context, hashes []hash.Hash, found func(context.Context, nbs.ToChunker)) (err error) { toSend := hash.NewHashSet(hashes...) fetcher := dcs.ChunkFetcher(ctx) @@ -603,7 +664,7 @@ func (dcs *DoltChunkStore) readChunksAndCache(ctx context.Context, hashes []hash return err } // Don't forward on empty/not found chunks. - if len(cc.CompressedData) > 0 { + if !cc.IsEmpty() { if dcs.cache.PutChunk(cc) { return ErrCacheCapacityExceeded } @@ -644,7 +705,7 @@ func (dcs *DoltChunkStore) HasMany(ctx context.Context, hashes hash.HashSet) (ha hashSl, byteSl := HashSetToSlices(notCached) absent := make(hash.HashSet) - var found []nbs.CompressedChunk + var found []nbs.ToChunker var err error batchItr(len(hashSl), maxHasManyBatchSize, func(st, end int) (stop bool) { @@ -738,7 +799,7 @@ func (dcs *DoltChunkStore) Put(ctx context.Context, c chunks.Chunk, getAddrs chu } cc := nbs.ChunkToCompressedChunk(c) - if dcs.cache.Put([]nbs.CompressedChunk{cc}) { + if dcs.cache.Put([]nbs.ToChunker{cc}) { return ErrCacheCapacityExceeded } return nil diff --git a/go/libraries/doltcore/remotestorage/internal/ranges/ranges.go b/go/libraries/doltcore/remotestorage/internal/ranges/ranges.go index de1600bd0a4..9e3ebe16236 100644 --- a/go/libraries/doltcore/remotestorage/internal/ranges/ranges.go +++ b/go/libraries/doltcore/remotestorage/internal/ranges/ranges.go @@ -33,6 +33,11 @@ type GetRange struct { Offset uint64 Length uint32 Region *Region + + // Archive file format requires the url/dictionary offset/length to be carried through to fully resolve the chunk. + // This information is not used withing the range calculations at all, as the range is not related to the chunk content. + DictionaryOffset uint64 + DictionaryLength uint32 } // A |Region| represents a continuous range of bytes within in a Url. @@ -145,12 +150,14 @@ func (t *Tree) Len() int { return t.t.Len() } -func (t *Tree) Insert(url string, hash []byte, offset uint64, length uint32) { +func (t *Tree) Insert(url string, hash []byte, offset uint64, length uint32, dictOffset uint64, dictLength uint32) { ins := &GetRange{ - Url: t.intern(url), - Hash: hash, - Offset: offset, - Length: length, + Url: t.intern(url), + Hash: hash, + Offset: offset, + Length: length, + DictionaryOffset: dictOffset, + DictionaryLength: dictLength, } t.t.ReplaceOrInsert(ins) diff --git a/go/libraries/doltcore/remotestorage/internal/ranges/ranges_test.go b/go/libraries/doltcore/remotestorage/internal/ranges/ranges_test.go index 9ec6b04e338..1f36ec296fb 100644 --- a/go/libraries/doltcore/remotestorage/internal/ranges/ranges_test.go +++ b/go/libraries/doltcore/remotestorage/internal/ranges/ranges_test.go @@ -77,11 +77,11 @@ func TestTree(t *testing.T) { tree := NewTree(8 * 1024) // Insert 1KB ranges every 16 KB. for i, j := 0, 0; i < 16; i, j = i+1, j+16*1024 { - tree.Insert("A", []byte{}, uint64(j), 1024) + tree.Insert("A", []byte{}, uint64(j), 1024, 0, 0) } // Insert 1KB ranges every 16 KB, offset by 8KB. for i := 15*16*1024 + 8*1024; i >= 0; i -= 16 * 1024 { - tree.Insert("A", []byte{}, uint64(i), 1024) + tree.Insert("A", []byte{}, uint64(i), 1024, 0, 0) } assertTree(t, tree) }) @@ -89,11 +89,11 @@ func TestTree(t *testing.T) { tree := NewTree(8 * 1024) // Insert 1KB ranges every 16 KB, offset by 8KB. for i := 15*16*1024 + 8*1024; i >= 0; i -= 16 * 1024 { - tree.Insert("A", []byte{}, uint64(i), 1024) + tree.Insert("A", []byte{}, uint64(i), 1024, 0, 0) } // Insert 1KB ranges every 16 KB. for i, j := 0, 0; i < 16; i, j = i+1, j+16*1024 { - tree.Insert("A", []byte{}, uint64(j), 1024) + tree.Insert("A", []byte{}, uint64(j), 1024, 0, 0) } assertTree(t, tree) }) @@ -111,7 +111,7 @@ func TestTree(t *testing.T) { }) tree := NewTree(8 * 1024) for _, offset := range entries { - tree.Insert("A", []byte{}, offset, 1024) + tree.Insert("A", []byte{}, offset, 1024, 0, 0) } assertTree(t, tree) } @@ -126,7 +126,7 @@ func TestTree(t *testing.T) { "B", "A", "9", "8", } for i, j := 0, 0; i < 16; i, j = i+1, j+1024 { - tree.Insert(files[i], []byte{}, uint64(j), 1024) + tree.Insert(files[i], []byte{}, uint64(j), 1024, 0, 0) } assert.Equal(t, 16, tree.regions.Len()) assert.Equal(t, 16, tree.t.Len()) @@ -134,17 +134,17 @@ func TestTree(t *testing.T) { t.Run("MergeInMiddle", func(t *testing.T) { tree := NewTree(8 * 1024) // 1KB chunk at byte 0 - tree.Insert("A", []byte{}, 0, 1024) + tree.Insert("A", []byte{}, 0, 1024, 0, 0) // 1KB chunk at byte 16KB - tree.Insert("A", []byte{}, 16384, 1024) + tree.Insert("A", []byte{}, 16384, 1024, 0, 0) assert.Equal(t, 2, tree.regions.Len()) assert.Equal(t, 2, tree.t.Len()) // 1KB chunk at byte 8KB - tree.Insert("A", []byte{}, 8192, 1024) + tree.Insert("A", []byte{}, 8192, 1024, 0, 0) assert.Equal(t, 1, tree.regions.Len()) assert.Equal(t, 3, tree.t.Len()) - tree.Insert("A", []byte{}, 4096, 1024) - tree.Insert("A", []byte{}, 12228, 1024) + tree.Insert("A", []byte{}, 4096, 1024, 0, 0) + tree.Insert("A", []byte{}, 12228, 1024, 0, 0) assert.Equal(t, 1, tree.regions.Len()) assert.Equal(t, 5, tree.t.Len()) e, _ := tree.t.Min() @@ -184,7 +184,7 @@ func TestTree(t *testing.T) { t.Run("InsertAscending", func(t *testing.T) { tree := NewTree(4 * 1024) for _, e := range entries { - tree.Insert(e.url, []byte{e.id}, e.offset, e.length) + tree.Insert(e.url, []byte{e.id}, e.offset, e.length, 0, 0) } assertTree(t, tree) }) @@ -192,7 +192,7 @@ func TestTree(t *testing.T) { tree := NewTree(4 * 1024) for i := len(entries) - 1; i >= 0; i-- { e := entries[i] - tree.Insert(e.url, []byte{e.id}, e.offset, e.length) + tree.Insert(e.url, []byte{e.id}, e.offset, e.length, 0, 0) } assertTree(t, tree) }) @@ -205,7 +205,7 @@ func TestTree(t *testing.T) { }) tree := NewTree(4 * 1024) for _, e := range entries { - tree.Insert(e.url, []byte{e.id}, e.offset, e.length) + tree.Insert(e.url, []byte{e.id}, e.offset, e.length, 0, 0) } assertTree(t, tree) } diff --git a/go/libraries/doltcore/remotestorage/map_chunk_cache.go b/go/libraries/doltcore/remotestorage/map_chunk_cache.go index ea5dad323ef..64a0fcf6e6e 100644 --- a/go/libraries/doltcore/remotestorage/map_chunk_cache.go +++ b/go/libraries/doltcore/remotestorage/map_chunk_cache.go @@ -24,16 +24,16 @@ import ( // mapChunkCache is a ChunkCache implementation that stores everything in an in memory map. type mapChunkCache struct { mu *sync.Mutex - hashToChunk map[hash.Hash]nbs.CompressedChunk - toFlush map[hash.Hash]nbs.CompressedChunk + hashToChunk map[hash.Hash]nbs.ToChunker + toFlush map[hash.Hash]nbs.ToChunker cm CapacityMonitor } func newMapChunkCache() *mapChunkCache { return &mapChunkCache{ &sync.Mutex{}, - make(map[hash.Hash]nbs.CompressedChunk), - make(map[hash.Hash]nbs.CompressedChunk), + make(map[hash.Hash]nbs.ToChunker), + make(map[hash.Hash]nbs.ToChunker), NewUncappedCapacityMonitor(), } } @@ -42,14 +42,14 @@ func newMapChunkCache() *mapChunkCache { func NewMapChunkCacheWithMaxCapacity(maxCapacity int64) *mapChunkCache { return &mapChunkCache{ &sync.Mutex{}, - make(map[hash.Hash]nbs.CompressedChunk), - make(map[hash.Hash]nbs.CompressedChunk), + make(map[hash.Hash]nbs.ToChunker), + make(map[hash.Hash]nbs.ToChunker), NewFixedCapacityMonitor(maxCapacity), } } // Put puts a slice of chunks into the cache. -func (mcc *mapChunkCache) Put(chnks []nbs.CompressedChunk) bool { +func (mcc *mapChunkCache) Put(chnks []nbs.ToChunker) bool { mcc.mu.Lock() defer mcc.mu.Unlock() @@ -63,7 +63,7 @@ func (mcc *mapChunkCache) Put(chnks []nbs.CompressedChunk) bool { } } - if mcc.cm.CapacityExceeded(len(c.FullCompressedChunk)) { + if mcc.cm.CapacityExceeded(int(c.FullCompressedChunkLen())) { return true } @@ -79,8 +79,8 @@ func (mcc *mapChunkCache) Put(chnks []nbs.CompressedChunk) bool { // Get gets a map of hash to chunk for a set of hashes. In the event that a chunk is not in the cache, chunks.Empty. // is put in it's place -func (mcc *mapChunkCache) Get(hashes hash.HashSet) map[hash.Hash]nbs.CompressedChunk { - hashToChunk := make(map[hash.Hash]nbs.CompressedChunk) +func (mcc *mapChunkCache) Get(hashes hash.HashSet) map[hash.Hash]nbs.ToChunker { + hashToChunk := make(map[hash.Hash]nbs.ToChunker) mcc.mu.Lock() defer mcc.mu.Unlock() @@ -112,13 +112,13 @@ func (mcc *mapChunkCache) Has(hashes hash.HashSet) (absent hash.HashSet) { return absent } -func (mcc *mapChunkCache) PutChunk(ch nbs.CompressedChunk) bool { +func (mcc *mapChunkCache) PutChunk(ch nbs.ToChunker) bool { mcc.mu.Lock() defer mcc.mu.Unlock() h := ch.Hash() if existing, ok := mcc.hashToChunk[h]; !ok || existing.IsEmpty() { - if mcc.cm.CapacityExceeded(len(ch.FullCompressedChunk)) { + if mcc.cm.CapacityExceeded(int(ch.FullCompressedChunkLen())) { return true } mcc.hashToChunk[h] = ch @@ -130,8 +130,8 @@ func (mcc *mapChunkCache) PutChunk(ch nbs.CompressedChunk) bool { // GetAndClearChunksToFlush gets a map of hash to chunk which includes all the chunks that were put in the cache // between the last time GetAndClearChunksToFlush was called and now. -func (mcc *mapChunkCache) GetAndClearChunksToFlush() map[hash.Hash]nbs.CompressedChunk { - newToFlush := make(map[hash.Hash]nbs.CompressedChunk) +func (mcc *mapChunkCache) GetAndClearChunksToFlush() map[hash.Hash]nbs.ToChunker { + newToFlush := make(map[hash.Hash]nbs.ToChunker) mcc.mu.Lock() defer mcc.mu.Unlock() diff --git a/go/libraries/doltcore/remotestorage/map_chunk_cache_test.go b/go/libraries/doltcore/remotestorage/map_chunk_cache_test.go index 6944eb98c35..8cb5e118d1c 100644 --- a/go/libraries/doltcore/remotestorage/map_chunk_cache_test.go +++ b/go/libraries/doltcore/remotestorage/map_chunk_cache_test.go @@ -27,8 +27,8 @@ import ( "github.com/dolthub/dolt/go/store/nbs" ) -func genRandomChunks(rng *rand.Rand, n int) (hash.HashSet, []nbs.CompressedChunk) { - chks := make([]nbs.CompressedChunk, n) +func genRandomChunks(rng *rand.Rand, n int) (hash.HashSet, []nbs.ToChunker) { + chks := make([]nbs.ToChunker, n) hashes := make(hash.HashSet) for i := 0; i < n; i++ { size := int(rng.Int31n(99) + 1) @@ -88,7 +88,7 @@ func TestMapChunkCache(t *testing.T) { toFlush = mapChunkCache.GetAndClearChunksToFlush() - expected := map[hash.Hash]nbs.CompressedChunk{moreChks[0].Hash(): moreChks[0]} + expected := map[hash.Hash]nbs.ToChunker{moreChks[0].Hash(): moreChks[0]} eq := reflect.DeepEqual(toFlush, expected) assert.True(t, eq, "Missing or unexpected chunks to flush (seed %d)", seed) } diff --git a/go/libraries/doltcore/remotestorage/noop_chunk_cache.go b/go/libraries/doltcore/remotestorage/noop_chunk_cache.go index 8edf589c31b..6b86aa11188 100644 --- a/go/libraries/doltcore/remotestorage/noop_chunk_cache.go +++ b/go/libraries/doltcore/remotestorage/noop_chunk_cache.go @@ -29,22 +29,22 @@ var noopChunkCache = &noopChunkCacheImpl{} type noopChunkCacheImpl struct { } -func (*noopChunkCacheImpl) Put(chnks []nbs.CompressedChunk) bool { +func (*noopChunkCacheImpl) Put(chnks []nbs.ToChunker) bool { return false } -func (*noopChunkCacheImpl) Get(hashes hash.HashSet) map[hash.Hash]nbs.CompressedChunk { - return make(map[hash.Hash]nbs.CompressedChunk) +func (*noopChunkCacheImpl) Get(hashes hash.HashSet) map[hash.Hash]nbs.ToChunker { + return make(map[hash.Hash]nbs.ToChunker) } func (*noopChunkCacheImpl) Has(hashes hash.HashSet) (absent hash.HashSet) { return hashes } -func (*noopChunkCacheImpl) PutChunk(ch nbs.CompressedChunk) bool { +func (*noopChunkCacheImpl) PutChunk(ch nbs.ToChunker) bool { return false } -func (*noopChunkCacheImpl) GetAndClearChunksToFlush() map[hash.Hash]nbs.CompressedChunk { +func (*noopChunkCacheImpl) GetAndClearChunksToFlush() map[hash.Hash]nbs.ToChunker { panic("noopChunkCache does not support GetAndClearChunksToFlush().") } diff --git a/go/store/cmd/noms/noms_manifest.go b/go/store/cmd/noms/noms_manifest.go index 4ece88cb030..1f979ef64c3 100644 --- a/go/store/cmd/noms/noms_manifest.go +++ b/go/store/cmd/noms/noms_manifest.go @@ -103,7 +103,7 @@ func runManifest(ctx context.Context, args []string) int { nbsFiles := make([]NbsFile, numSpecs) for i := 0; i < numSpecs; i++ { tableSpecInfo := manifest.GetTableSpecInfo(i) - path := filepath.Join(spec.DatabaseName, tableSpecInfo.GetName()) + path := filepath.Join(spec.DatabaseName, tableSpecInfo.GetFileName()) fileInfo, err := os.Stat(path) nbsFiles[i] = NbsFile{tableSpecInfo, fileInfo, err} } @@ -130,7 +130,7 @@ func runManifest(ctx context.Context, args []string) int { fmt.Println(" referenced nbs files:") for _, nbsFile := range nbsFiles { - name := nbsFile.manifestSpec.GetName() + name := nbsFile.manifestSpec.GetFileName() chunkCnt := nbsFile.manifestSpec.GetChunkCount() sizeStr := nbsFile.sizeStr() existsStr := nbsFile.fileInfoErr == nil diff --git a/go/store/datas/pull/clone.go b/go/store/datas/pull/clone.go index 9cad9f5e514..3695914b085 100644 --- a/go/store/datas/pull/clone.go +++ b/go/store/datas/pull/clone.go @@ -19,8 +19,10 @@ import ( "errors" "fmt" "io" + "strings" "github.com/cenkalti/backoff/v4" + "github.com/dolthub/dolt/go/store/nbs" "golang.org/x/sync/errgroup" "golang.org/x/sync/semaphore" @@ -81,9 +83,14 @@ func mapTableFiles(tblFiles []chunks.TableFile) ([]string, map[string]chunks.Tab fileIDtoNumChunks := make(map[string]int) for i, tblFile := range tblFiles { - fileIDtoTblFile[tblFile.FileID()] = tblFile - fileIds[i] = tblFile.FileID() - fileIDtoNumChunks[tblFile.FileID()] = tblFile.NumChunks() + fileId := tblFile.FileID() + if strings.HasSuffix(fileId, nbs.ArchiveFileSuffix) { + fileId = fileId[:len(fileId)-len(nbs.ArchiveFileSuffix)] + } + + fileIDtoTblFile[fileId] = tblFile + fileIds[i] = fileId + fileIDtoNumChunks[fileId] = tblFile.NumChunks() } return fileIds, fileIDtoTblFile, fileIDtoNumChunks diff --git a/go/store/datas/pull/pull_chunk_fetcher.go b/go/store/datas/pull/pull_chunk_fetcher.go index 2e0d58a0f87..3d2c6d556aa 100644 --- a/go/store/datas/pull/pull_chunk_fetcher.go +++ b/go/store/datas/pull/pull_chunk_fetcher.go @@ -26,7 +26,7 @@ import ( ) type GetManyer interface { - GetManyCompressed(ctx context.Context, hashes hash.HashSet, found func(context.Context, nbs.CompressedChunk)) error + GetManyCompressed(ctx context.Context, hashes hash.HashSet, found func(context.Context, nbs.ToChunker)) error } type ChunkFetcherable interface { @@ -52,7 +52,7 @@ type PullChunkFetcher struct { batchCh chan hash.HashSet doneCh chan struct{} - resCh chan nbs.CompressedChunk + resCh chan nbs.ToChunker } func NewPullChunkFetcher(ctx context.Context, getter GetManyer) *PullChunkFetcher { @@ -63,7 +63,7 @@ func NewPullChunkFetcher(ctx context.Context, getter GetManyer) *PullChunkFetche getter: getter, batchCh: make(chan hash.HashSet), doneCh: make(chan struct{}), - resCh: make(chan nbs.CompressedChunk), + resCh: make(chan nbs.ToChunker), } ret.eg.Go(func() error { return ret.fetcherThread(func() { @@ -86,9 +86,9 @@ func (f *PullChunkFetcher) fetcherThread(finalize func()) error { missing := batch.Copy() // Blocking get, no concurrency, only one fetcher. - err := f.getter.GetManyCompressed(f.ctx, batch, func(ctx context.Context, chk nbs.CompressedChunk) { + err := f.getter.GetManyCompressed(f.ctx, batch, func(ctx context.Context, chk nbs.ToChunker) { mu.Lock() - missing.Remove(chk.H) + missing.Remove(chk.Hash()) mu.Unlock() select { case <-ctx.Done(): @@ -139,7 +139,7 @@ func (f *PullChunkFetcher) Close() error { return f.eg.Wait() } -func (f *PullChunkFetcher) Recv(ctx context.Context) (nbs.CompressedChunk, error) { +func (f *PullChunkFetcher) Recv(ctx context.Context) (nbs.ToChunker, error) { select { case res, ok := <-f.resCh: if !ok { diff --git a/go/store/datas/pull/pull_chunk_fetcher_test.go b/go/store/datas/pull/pull_chunk_fetcher_test.go index 2fa3bd4e187..f2975efb679 100644 --- a/go/store/datas/pull/pull_chunk_fetcher_test.go +++ b/go/store/datas/pull/pull_chunk_fetcher_test.go @@ -70,8 +70,12 @@ func TestPullChunkFetcher(t *testing.T) { defer wg.Done() cmp, err := f.Recv(context.Background()) assert.NoError(t, err) - assert.Equal(t, cmp.H, gm.C.H) - assert.Equal(t, cmp.FullCompressedChunk, gm.C.FullCompressedChunk) + assert.Equal(t, cmp.Hash(), gm.C.H) + + cc, ok := cmp.(nbs.CompressedChunk) + assert.True(t, ok) + + assert.Equal(t, cc.FullCompressedChunk, gm.C.FullCompressedChunk) _, err = f.Recv(context.Background()) assert.ErrorIs(t, err, io.EOF) assert.NoError(t, f.Close()) @@ -92,8 +96,11 @@ func TestPullChunkFetcher(t *testing.T) { defer wg.Done() cmp, err := f.Recv(context.Background()) assert.NoError(t, err) - assert.Equal(t, cmp.H, h) - assert.Nil(t, cmp.FullCompressedChunk) + assert.Equal(t, cmp.Hash(), h) + + cc, ok := cmp.(nbs.CompressedChunk) + assert.True(t, ok) + assert.Nil(t, cc.FullCompressedChunk) _, err = f.Recv(context.Background()) assert.ErrorIs(t, err, io.EOF) assert.NoError(t, f.Close()) @@ -136,7 +143,7 @@ func TestPullChunkFetcher(t *testing.T) { type emptyGetManyer struct { } -func (emptyGetManyer) GetManyCompressed(ctx context.Context, hashes hash.HashSet, found func(context.Context, nbs.CompressedChunk)) error { +func (emptyGetManyer) GetManyCompressed(ctx context.Context, hashes hash.HashSet, found func(context.Context, nbs.ToChunker)) error { return nil } @@ -144,7 +151,7 @@ type deliveringGetManyer struct { C nbs.CompressedChunk } -func (d deliveringGetManyer) GetManyCompressed(ctx context.Context, hashes hash.HashSet, found func(context.Context, nbs.CompressedChunk)) error { +func (d deliveringGetManyer) GetManyCompressed(ctx context.Context, hashes hash.HashSet, found func(context.Context, nbs.ToChunker)) error { for _ = range hashes { found(ctx, d.C) } @@ -155,7 +162,7 @@ type blockingGetManyer struct { block chan struct{} } -func (b blockingGetManyer) GetManyCompressed(ctx context.Context, hashes hash.HashSet, found func(context.Context, nbs.CompressedChunk)) error { +func (b blockingGetManyer) GetManyCompressed(ctx context.Context, hashes hash.HashSet, found func(context.Context, nbs.ToChunker)) error { <-b.block return nil } @@ -165,6 +172,6 @@ type errorGetManyer struct { var getManyerErr = fmt.Errorf("always return an error") -func (errorGetManyer) GetManyCompressed(ctx context.Context, hashes hash.HashSet, found func(context.Context, nbs.CompressedChunk)) error { +func (errorGetManyer) GetManyCompressed(ctx context.Context, hashes hash.HashSet, found func(context.Context, nbs.ToChunker)) error { return getManyerErr } diff --git a/go/store/datas/pull/puller.go b/go/store/datas/pull/puller.go index 9401b58d7e6..53beec0adbd 100644 --- a/go/store/datas/pull/puller.go +++ b/go/store/datas/pull/puller.go @@ -28,6 +28,7 @@ import ( "sync/atomic" "time" + "github.com/dolthub/dolt/go/libraries/doltcore/remotestorage" "golang.org/x/sync/errgroup" "github.com/dolthub/dolt/go/libraries/doltcore/dconfig" @@ -346,11 +347,11 @@ func (p *Puller) Pull(ctx context.Context) error { if cChk.IsGhost() { return fmt.Errorf("attempted to push or pull ghost chunk: %w", nbs.ErrGhostChunkRequested) } - if len(cChk.FullCompressedChunk) == 0 { + if cChk.FullCompressedChunkLen() == 0 { return errors.New("failed to get all chunks.") } - atomic.AddUint64(&p.stats.fetchedSourceBytes, uint64(len(cChk.FullCompressedChunk))) + atomic.AddUint64(&p.stats.fetchedSourceBytes, uint64(cChk.FullCompressedChunkLen())) atomic.AddUint64(&p.stats.fetchedSourceChunks, uint64(1)) chnk, err := cChk.ToChunk() @@ -366,9 +367,19 @@ func (p *Puller) Pull(ctx context.Context) error { } tracker.TickProcessed() - err = p.wr.AddCompressedChunk(ctx, cChk) - if err != nil { - return err + if compressedChunk, ok := cChk.(nbs.CompressedChunk); ok { + err = p.wr.AddCompressedChunk(ctx, compressedChunk) + if err != nil { + return err + } + } else if _, ok := cChk.(remotestorage.ArchiveToChunker); ok { + // NM4 - Until we can write quickly to archives..... + cc := nbs.ChunkToCompressedChunk(chnk) + + err = p.wr.AddCompressedChunk(ctx, cc) + if err != nil { + return err + } } } }) diff --git a/go/store/nbs/archive.go b/go/store/nbs/archive.go index 650b45a0c96..98692571fd4 100644 --- a/go/store/nbs/archive.go +++ b/go/store/nbs/archive.go @@ -27,7 +27,7 @@ Chunks from the Archive. ByteSpans are arbitrary offset/lengths into the file which store (1) zstd dictionary data, and (2) compressed chunk data. Each Chunk is stored as a pair of ByteSpans (dict,data). Dictionary ByteSpans can (should) be used by multiple Chunks, so there are more ByteSpans than Chunks. The Index is used to map Chunks to ByteSpan pairs. These pairs are -called ChunkRefs, and were store them as [uint32,uint32] on disk. This allows us to quickly find the ByteSpans for a +called ChunkRefs, and we store them as [uint32,uint32] on disk. This allows us to quickly find the ByteSpans for a given Chunk with minimal processing at load time. A Dolt Archive file follows the following format: @@ -162,7 +162,7 @@ const ( archiveCheckSumSize + 1 + // version byte archiveFileSigSize - archiveFileSuffix = ".darc" + ArchiveFileSuffix = ".darc" ) /* diff --git a/go/store/nbs/archive_build.go b/go/store/nbs/archive_build.go index 349bf239d11..6f1e7c1a804 100644 --- a/go/store/nbs/archive_build.go +++ b/go/store/nbs/archive_build.go @@ -57,7 +57,7 @@ func UnArchive(ctx context.Context, cs chunks.ChunkStore, smd StorageMetadata, p return err } if exists { - // We have a fast path to follow because oritinal table file is still on disk. + // We have a fast path to follow because original table file is still on disk. swapMap[arc.hash()] = orginTfId } else { // We don't have the original table file id, so we have to create a new one. @@ -99,8 +99,8 @@ func UnArchive(ctx context.Context, cs chunks.ChunkStore, smd StorageMetadata, p specs, err := gs.oldGen.tables.toSpecs() newSpecs := make([]tableSpec, 0, len(specs)) for _, spec := range specs { - if newSpec, exists := swapMap[spec.name]; exists { - newSpecs = append(newSpecs, tableSpec{newSpec, spec.chunkCount}) + if newSpec, exists := swapMap[spec.hash]; exists { + newSpecs = append(newSpecs, tableSpec{TypeNoms, newSpec, spec.chunkCount}) } else { newSpecs = append(newSpecs, spec) } @@ -169,8 +169,8 @@ func BuildArchive(ctx context.Context, cs chunks.ChunkStore, dagGroups *ChunkRel specs, err := gs.oldGen.tables.toSpecs() newSpecs := make([]tableSpec, 0, len(specs)) for _, spec := range specs { - if newSpec, exists := swapMap[spec.name]; exists { - newSpecs = append(newSpecs, tableSpec{newSpec, spec.chunkCount}) + if newSpec, exists := swapMap[spec.hash]; exists { + newSpecs = append(newSpecs, tableSpec{TypeArchive, newSpec, spec.chunkCount}) } else { newSpecs = append(newSpecs, spec) } diff --git a/go/store/nbs/archive_chunk_source.go b/go/store/nbs/archive_chunk_source.go index 3d3a59ec4d6..66307ada81a 100644 --- a/go/store/nbs/archive_chunk_source.go +++ b/go/store/nbs/archive_chunk_source.go @@ -36,7 +36,7 @@ type archiveChunkSource struct { var _ chunkSource = &archiveChunkSource{} func newArchiveChunkSource(ctx context.Context, dir string, h hash.Hash, chunkCount uint32, q MemoryQuotaProvider) (archiveChunkSource, error) { - archiveFile := filepath.Join(dir, h.String()+archiveFileSuffix) + archiveFile := filepath.Join(dir, h.String()+ArchiveFileSuffix) file, size, err := openReader(archiveFile) if err != nil { @@ -141,6 +141,10 @@ func (acs archiveChunkSource) hash() hash.Hash { return acs.aRdr.footer.hash } +func (acs archiveChunkSource) name() string { + return acs.hash().String() + ArchiveFileSuffix +} + func (acs archiveChunkSource) currentSize() uint64 { return acs.aRdr.footer.fileSize } @@ -167,12 +171,38 @@ func (acs archiveChunkSource) clone() (chunkSource, error) { return archiveChunkSource{acs.file, rdr}, nil } -func (acs archiveChunkSource) getRecordRanges(_ context.Context, _ []getRecord, _ keeperF) (map[hash.Hash]Range, gcBehavior, error) { - return nil, gcBehavior_Continue, errors.New("Archive chunk source does not support getRecordRanges") +func (acs archiveChunkSource) getRecordRanges(_ context.Context, requests []getRecord, keeper keeperF) (map[hash.Hash]Range, gcBehavior, error) { + result := make(map[hash.Hash]Range, len(requests)) + for _, req := range requests { + hAddr := *req.a + idx := acs.aRdr.search(hAddr) + if idx < 0 { + // Chunk not found. + continue + } + if keeper != nil && keeper(hAddr) { + return nil, gcBehavior_Block, nil + } + + dictId, dataId := acs.aRdr.getChunkRef(idx) + dataSpan := acs.aRdr.getByteSpanByID(dataId) + dictSpan := acs.aRdr.getByteSpanByID(dictId) + + rng := Range{ + Offset: dataSpan.offset, + Length: uint32(dataSpan.length), + DictOffset: dictSpan.offset, + DictLength: uint32(dictSpan.length), + } + + result[hAddr] = rng + } + return result, gcBehavior_Continue, nil } -func (acs archiveChunkSource) getManyCompressed(ctx context.Context, eg *errgroup.Group, reqs []getRecord, found func(context.Context, CompressedChunk), keeper keeperF, stats *Stats) (bool, gcBehavior, error) { +func (acs archiveChunkSource) getManyCompressed(ctx context.Context, eg *errgroup.Group, reqs []getRecord, found func(context.Context, ToChunker), keeper keeperF, stats *Stats) (bool, gcBehavior, error) { return acs.getMany(ctx, eg, reqs, func(ctx context.Context, chk *chunks.Chunk) { + // NM4 - UPDATE. this is def wrong. found(ctx, ChunkToCompressedChunk(*chk)) }, keeper, stats) } diff --git a/go/store/nbs/archive_test.go b/go/store/nbs/archive_test.go index 4de745e6a5c..ac0c1c70eb3 100644 --- a/go/store/nbs/archive_test.go +++ b/go/store/nbs/archive_test.go @@ -676,7 +676,7 @@ func (tcs *testChunkSource) getMany(ctx context.Context, eg *errgroup.Group, req panic("never used") } -func (tcs *testChunkSource) getManyCompressed(ctx context.Context, eg *errgroup.Group, reqs []getRecord, found func(context.Context, CompressedChunk), keeper keeperF, stats *Stats) (bool, gcBehavior, error) { +func (tcs *testChunkSource) getManyCompressed(ctx context.Context, eg *errgroup.Group, reqs []getRecord, found func(context.Context, ToChunker), keeper keeperF, stats *Stats) (bool, gcBehavior, error) { panic("never used") } @@ -696,6 +696,10 @@ func (tcs *testChunkSource) hash() hash.Hash { panic("never used") } +func (tcs *testChunkSource) name() string { + panic("never used") +} + func (tcs *testChunkSource) reader(ctx context.Context) (io.ReadCloser, uint64, error) { panic("never used") } diff --git a/go/store/nbs/archive_writer.go b/go/store/nbs/archive_writer.go index 0223cf33c51..d555287af9b 100644 --- a/go/store/nbs/archive_writer.go +++ b/go/store/nbs/archive_writer.go @@ -426,7 +426,7 @@ func (aw *archiveWriter) genFileName(path string) (string, error) { return "", err } - fileName := fmt.Sprintf("%s%s", h.String(), archiveFileSuffix) + fileName := fmt.Sprintf("%s%s", h.String(), ArchiveFileSuffix) fullPath := filepath.Join(path, fileName) return fullPath, nil } diff --git a/go/store/nbs/chunk_fetcher.go b/go/store/nbs/chunk_fetcher.go index 2be1a0aa78d..8b4aae88a45 100644 --- a/go/store/nbs/chunk_fetcher.go +++ b/go/store/nbs/chunk_fetcher.go @@ -46,7 +46,7 @@ type ChunkFetcher interface { CloseSend() error - Recv(context.Context) (CompressedChunk, error) + Recv(context.Context) (ToChunker, error) Close() error } diff --git a/go/store/nbs/chunk_source_adapter.go b/go/store/nbs/chunk_source_adapter.go index f86d7527b43..013351ef6c5 100644 --- a/go/store/nbs/chunk_source_adapter.go +++ b/go/store/nbs/chunk_source_adapter.go @@ -29,6 +29,10 @@ func (csa chunkSourceAdapter) hash() hash.Hash { return csa.h } +func (csa chunkSourceAdapter) name() string { + return csa.h.String() +} + func newReaderFromIndexData(ctx context.Context, q MemoryQuotaProvider, idxData []byte, name hash.Hash, tra tableReaderAt, blockSize uint64) (cs chunkSource, err error) { index, err := parseTableIndexByCopy(ctx, idxData, q) if err != nil { diff --git a/go/store/nbs/cmp_chunk_table_writer.go b/go/store/nbs/cmp_chunk_table_writer.go index 28cdde95630..64112ab19d1 100644 --- a/go/store/nbs/cmp_chunk_table_writer.go +++ b/go/store/nbs/cmp_chunk_table_writer.go @@ -75,17 +75,22 @@ func (tw *CmpChunkTableWriter) GetMD5() []byte { } // AddCmpChunk adds a compressed chunk -func (tw *CmpChunkTableWriter) AddCmpChunk(c CompressedChunk) error { - if c.IsGhost() { +func (tw *CmpChunkTableWriter) AddCmpChunk(tc ToChunker) error { + if tc.IsGhost() { // Ghost chunks cannot be written to a table file. They should // always be filtered by the write processes before landing // here. return ErrGhostChunkRequested } - if len(c.CompressedData) == 0 { + if tc.IsEmpty() { panic("NBS blocks cannot be zero length") } + c, ok := tc.(CompressedChunk) + if !ok { + panic("Require a CompressedChunk") // NM4 + } + uncmpLen, err := snappy.DecodedLen(c.CompressedData) if err != nil { diff --git a/go/store/nbs/cmp_chunk_table_writer_test.go b/go/store/nbs/cmp_chunk_table_writer_test.go index 6f0508b2e8e..592ca683e45 100644 --- a/go/store/nbs/cmp_chunk_table_writer_test.go +++ b/go/store/nbs/cmp_chunk_table_writer_test.go @@ -48,10 +48,10 @@ func TestCmpChunkTableWriter(t *testing.T) { } reqs := toGetRecords(hashes) - found := make([]CompressedChunk, 0) + found := make([]ToChunker, 0) eg, egCtx := errgroup.WithContext(ctx) - _, _, err = tr.getManyCompressed(egCtx, eg, reqs, func(ctx context.Context, c CompressedChunk) { found = append(found, c) }, nil, &Stats{}) + _, _, err = tr.getManyCompressed(egCtx, eg, reqs, func(ctx context.Context, c ToChunker) { found = append(found, c) }, nil, &Stats{}) require.NoError(t, err) require.NoError(t, eg.Wait()) diff --git a/go/store/nbs/conjoiner.go b/go/store/nbs/conjoiner.go index ad2a12ac61f..a5590752cd3 100644 --- a/go/store/nbs/conjoiner.go +++ b/go/store/nbs/conjoiner.go @@ -162,7 +162,7 @@ func conjoin(ctx context.Context, s conjoinStrategy, upstream manifestContents, return upstream, func() {}, nil } for i := range upstream.appendix { - if upstream.appendix[i].name != appendixSpecs[i].name { + if upstream.appendix[i].hash != appendixSpecs[i].hash { return upstream, func() {}, nil } } @@ -176,19 +176,19 @@ func conjoin(ctx context.Context, s conjoinStrategy, upstream manifestContents, conjoineeSet := map[hash.Hash]struct{}{} upstreamNames := map[hash.Hash]struct{}{} for _, spec := range upstream.specs { - upstreamNames[spec.name] = struct{}{} + upstreamNames[spec.hash] = struct{}{} } for _, c := range conjoinees { - if _, present := upstreamNames[c.name]; !present { + if _, present := upstreamNames[c.hash]; !present { return upstream, func() {}, nil // Bail! } - conjoineeSet[c.name] = struct{}{} + conjoineeSet[c.hash] = struct{}{} } // Filter conjoinees out of upstream.specs to generate new set of keepers keepers = make([]tableSpec, 0, len(upstream.specs)-len(conjoinees)) for _, spec := range upstream.specs { - if _, present := conjoineeSet[spec.name]; !present { + if _, present := conjoineeSet[spec.hash]; !present { keepers = append(keepers, spec) } } @@ -202,7 +202,7 @@ func conjoinTables(ctx context.Context, conjoinees []tableSpec, p tablePersister for idx := range conjoinees { i, spec := idx, conjoinees[idx] eg.Go(func() (err error) { - toConjoin[i], err = p.Open(ectx, spec.name, spec.chunkCount, stats) + toConjoin[i], err = p.Open(ectx, spec.hash, spec.chunkCount, stats) return }) } @@ -240,7 +240,7 @@ func conjoinTables(ctx context.Context, conjoinees []tableSpec, p tablePersister if err != nil { return tableSpec{}, nil, err } - return tableSpec{h, cnt}, cleanup, nil + return tableSpec{TypeNoms, h, cnt}, cleanup, nil } func toSpecs(srcs chunkSources) ([]tableSpec, error) { @@ -258,7 +258,7 @@ func toSpecs(srcs chunkSources) ([]tableSpec, error) { if err != nil { return nil, err } - specs[i] = tableSpec{h, cnt} + specs[i] = tableSpec{TypeNoms, h, cnt} } return specs, nil diff --git a/go/store/nbs/conjoiner_test.go b/go/store/nbs/conjoiner_test.go index a9f64aa2220..9b2480ec0e2 100644 --- a/go/store/nbs/conjoiner_test.go +++ b/go/store/nbs/conjoiner_test.go @@ -42,7 +42,7 @@ func (ts tableSpecsByAscendingCount) Len() int { return len(ts) } func (ts tableSpecsByAscendingCount) Less(i, j int) bool { tsI, tsJ := ts[i], ts[j] if tsI.chunkCount == tsJ.chunkCount { - return bytes.Compare(tsI.name[:], tsJ.name[:]) < 0 + return bytes.Compare(tsI.hash[:], tsJ.hash[:]) < 0 } return tsI.chunkCount < tsJ.chunkCount } @@ -76,7 +76,7 @@ func makeTestSrcs(t *testing.T, tableSizes []uint32, p tablePersister) (srcs chu // Makes a tableSet with len(tableSizes) upstream tables containing tableSizes[N] unique chunks func makeTestTableSpecs(t *testing.T, tableSizes []uint32, p tablePersister) (specs []tableSpec) { for _, src := range makeTestSrcs(t, tableSizes, p) { - specs = append(specs, tableSpec{src.hash(), mustUint32(src.count())}) + specs = append(specs, tableSpec{typeNoms, src.hash(), mustUint32(src.count())}) err := src.close() require.NoError(t, err) } @@ -134,7 +134,7 @@ func testConjoin(t *testing.T, factory func(t *testing.T) tablePersister) { assertContainAll := func(t *testing.T, p tablePersister, expect, actual []tableSpec) { open := func(specs []tableSpec) (sources chunkSources) { for _, sp := range specs { - cs, err := p.Open(context.Background(), sp.name, sp.chunkCount, stats) + cs, err := p.Open(context.Background(), sp.hash, sp.chunkCount, stats) if err != nil { require.NoError(t, err) } @@ -183,7 +183,7 @@ func testConjoin(t *testing.T, factory func(t *testing.T) tablePersister) { src, _, err := p.Persist(context.Background(), mt, nil, nil, &Stats{}) require.NoError(t, err) defer src.close() - return tableSpec{src.hash(), mustUint32(src.count())} + return tableSpec{typeNoms, src.hash(), mustUint32(src.count())} } tc := []struct { diff --git a/go/store/nbs/dynamo_manifest_test.go b/go/store/nbs/dynamo_manifest_test.go index e682c35c7be..1c1ba194156 100644 --- a/go/store/nbs/dynamo_manifest_test.go +++ b/go/store/nbs/dynamo_manifest_test.go @@ -68,13 +68,13 @@ func TestDynamoManifestParseIfExists(t *testing.T) { assert.Equal(newLock, contents.lock) assert.Equal(newRoot, contents.root) if assert.Len(contents.appendix, 1) { - assert.Equal(tableName.String(), contents.specs[0].name.String()) + assert.Equal(tableName.String(), contents.specs[0].hash.String()) assert.Equal(uint32(0), contents.specs[0].chunkCount) - assert.Equal(tableName.String(), contents.appendix[0].name.String()) + assert.Equal(tableName.String(), contents.appendix[0].hash.String()) assert.Equal(uint32(0), contents.appendix[0].chunkCount) } if assert.Len(contents.specs, 2) { - assert.Equal(tableName.String(), contents.specs[1].name.String()) + assert.Equal(tableName.String(), contents.specs[1].hash.String()) assert.Equal(uint32(0), contents.specs[1].chunkCount) } } @@ -109,7 +109,7 @@ func TestDynamoManifestUpdate(t *testing.T) { stats := &Stats{} // First, test winning the race against another process. - contents := makeContents("locker", "nuroot", []tableSpec{{computeAddr([]byte("a")), 3}}, nil) + contents := makeContents("locker", "nuroot", []tableSpec{{typeNoms, computeAddr([]byte("a")), 3}}, nil) upstream, err := mm.Update(context.Background(), hash.Hash{}, contents, stats, func() error { // This should fail to get the lock, and therefore _not_ clobber the manifest. So the Update should succeed. lock := computeAddr([]byte("nolock")) @@ -145,7 +145,7 @@ func TestDynamoManifestUpdate(t *testing.T) { require.NoError(t, err) assert.Equal(jerkLock, upstream.lock) assert.Equal(rejected.root, upstream.root) - assert.Equal([]tableSpec{{tableName, 1}}, upstream.specs) + assert.Equal([]tableSpec{{typeNoms, tableName, 1}}, upstream.specs) } func TestDynamoManifestUpdateAppendix(t *testing.T) { @@ -155,11 +155,11 @@ func TestDynamoManifestUpdateAppendix(t *testing.T) { // First, test winning the race against another process. specs := []tableSpec{ - {computeAddr([]byte("app-a")), 3}, - {computeAddr([]byte("a")), 3}, + {typeNoms, computeAddr([]byte("app-a")), 3}, + {typeNoms, computeAddr([]byte("a")), 3}, } - app := []tableSpec{{computeAddr([]byte("app-a")), 3}} + app := []tableSpec{{typeNoms, computeAddr([]byte("app-a")), 3}} contents := makeContents("locker", "nuroot", specs, app) upstream, err := mm.Update(context.Background(), hash.Hash{}, contents, stats, func() error { @@ -204,8 +204,8 @@ func TestDynamoManifestUpdateAppendix(t *testing.T) { require.NoError(t, err) assert.Equal(jerkLock, upstream.lock) assert.Equal(rejected.root, upstream.root) - assert.Equal([]tableSpec{{appTableName, 1}, {tableName, 1}}, upstream.specs) - assert.Equal([]tableSpec{{appTableName, 1}}, upstream.appendix) + assert.Equal([]tableSpec{{typeNoms, appTableName, 1}, {typeNoms, tableName, 1}}, upstream.specs) + assert.Equal([]tableSpec{{typeNoms, appTableName, 1}}, upstream.appendix) } func TestDynamoManifestCaching(t *testing.T) { @@ -231,7 +231,7 @@ func TestDynamoManifestCaching(t *testing.T) { // When failing the optimistic lock, we should hit persistent storage. reads = ddb.NumGets() - contents := makeContents("lock2", "nuroot", []tableSpec{{computeAddr([]byte("a")), 3}}, nil) + contents := makeContents("lock2", "nuroot", []tableSpec{{typeNoms, computeAddr([]byte("a")), 3}}, nil) upstream, err := mm.Update(context.Background(), hash.Hash{}, contents, stats, nil) require.NoError(t, err) assert.NotEqual(contents.lock, upstream.lock) diff --git a/go/store/nbs/empty_chunk_source.go b/go/store/nbs/empty_chunk_source.go index 60b7f953d21..a341251ff7e 100644 --- a/go/store/nbs/empty_chunk_source.go +++ b/go/store/nbs/empty_chunk_source.go @@ -50,7 +50,7 @@ func (ecs emptyChunkSource) getMany(ctx context.Context, eg *errgroup.Group, req return true, gcBehavior_Continue, nil } -func (ecs emptyChunkSource) getManyCompressed(ctx context.Context, eg *errgroup.Group, reqs []getRecord, found func(context.Context, CompressedChunk), keeper keeperF, stats *Stats) (bool, gcBehavior, error) { +func (ecs emptyChunkSource) getManyCompressed(ctx context.Context, eg *errgroup.Group, reqs []getRecord, found func(context.Context, ToChunker), keeper keeperF, stats *Stats) (bool, gcBehavior, error) { return true, gcBehavior_Continue, nil } @@ -66,6 +66,10 @@ func (ecs emptyChunkSource) hash() hash.Hash { return hash.Hash{} } +func (ecs emptyChunkSource) name() string { + return ecs.hash().String() +} + func (ecs emptyChunkSource) index() (tableIndex, error) { return onHeapTableIndex{}, nil } diff --git a/go/store/nbs/file_manifest_test.go b/go/store/nbs/file_manifest_test.go index 9cb5d51830f..d4cad682dfd 100644 --- a/go/store/nbs/file_manifest_test.go +++ b/go/store/nbs/file_manifest_test.go @@ -73,7 +73,7 @@ func TestFileManifestLoadIfExists(t *testing.T) { assert.Equal(jerk, upstream.lock) assert.Equal(newRoot, upstream.root) if assert.Len(upstream.specs, 1) { - assert.Equal(tableName.String(), upstream.specs[0].name.String()) + assert.Equal(tableName.String(), upstream.specs[0].hash.String()) assert.Equal(uint32(0), upstream.specs[0].chunkCount) } } @@ -134,7 +134,7 @@ func TestFileManifestUpdate(t *testing.T) { nbfVers: constants.FormatLD1String, lock: computeAddr([]byte("locker")), root: hash.Of([]byte("new root")), - specs: []tableSpec{{computeAddr([]byte("a")), 3}}, + specs: []tableSpec{{typeNoms, computeAddr([]byte("a")), 3}}, } upstream, err := fm.Update(context.Background(), hash.Hash{}, contents, stats, func() error { // This should fail to get the lock, and therefore _not_ clobber the manifest. So the Update should succeed. @@ -177,7 +177,7 @@ func TestFileManifestUpdate(t *testing.T) { require.NoError(t, err) assert.Equal(jerkLock, upstream.lock) assert.Equal(contents2.root, upstream.root) - assert.Equal([]tableSpec{{tableName, 1}}, upstream.specs) + assert.Equal([]tableSpec{{typeNoms, tableName, 1}}, upstream.specs) } // tryClobberManifest simulates another process trying to access dir/manifestFileName concurrently. To avoid deadlock, it does a non-blocking lock of dir/lockFileName. If it can get the lock, it clobbers the manifest. diff --git a/go/store/nbs/file_table_reader.go b/go/store/nbs/file_table_reader.go index a95c2109c32..d808b81563e 100644 --- a/go/store/nbs/file_table_reader.go +++ b/go/store/nbs/file_table_reader.go @@ -54,7 +54,7 @@ func tableFileExists(ctx context.Context, dir string, h hash.Hash) (bool, error) } func archiveFileExists(ctx context.Context, dir string, h hash.Hash) (bool, error) { - darc := fmt.Sprintf("%s%s", h.String(), archiveFileSuffix) + darc := fmt.Sprintf("%s%s", h.String(), ArchiveFileSuffix) path := filepath.Join(dir, darc) _, err := os.Stat(path) @@ -169,6 +169,10 @@ func (ftr *fileTableReader) hash() hash.Hash { return ftr.h } +func (ftr *fileTableReader) name() string { + return ftr.h.String() +} + func (ftr *fileTableReader) Close() error { return ftr.tableReader.close() } diff --git a/go/store/nbs/gc_copier.go b/go/store/nbs/gc_copier.go index 44c02df8ef7..203588b98e6 100644 --- a/go/store/nbs/gc_copier.go +++ b/go/store/nbs/gc_copier.go @@ -56,7 +56,7 @@ func newGarbageCollectionCopier(tfp tableFilePersister) (*gcCopier, error) { return &gcCopier{writer, tfp}, nil } -func (gcc *gcCopier) addChunk(ctx context.Context, c CompressedChunk) error { +func (gcc *gcCopier) addChunk(ctx context.Context, c ToChunker) error { return gcc.writer.AddCmpChunk(c) } @@ -94,7 +94,7 @@ func (gcc *gcCopier) copyTablesToDir(ctx context.Context) (ts []tableSpec, err e if exists { return []tableSpec{ { - name: addr, + hash: addr, chunkCount: uint32(gcc.writer.ChunkCount()), }, }, nil @@ -106,7 +106,7 @@ func (gcc *gcCopier) copyTablesToDir(ctx context.Context) (ts []tableSpec, err e if err == nil { return []tableSpec{ { - name: addr, + hash: addr, chunkCount: uint32(gcc.writer.ChunkCount()), }, }, nil @@ -128,7 +128,7 @@ func (gcc *gcCopier) copyTablesToDir(ctx context.Context) (ts []tableSpec, err e return []tableSpec{ { - name: addr, + hash: addr, chunkCount: uint32(gcc.writer.ChunkCount()), }, }, nil diff --git a/go/store/nbs/generational_chunk_store.go b/go/store/nbs/generational_chunk_store.go index c858ab683b0..380ffd8cb3d 100644 --- a/go/store/nbs/generational_chunk_store.go +++ b/go/store/nbs/generational_chunk_store.go @@ -28,6 +28,7 @@ import ( ) var _ chunks.ChunkStore = (*GenerationalNBS)(nil) +var _ chunks.GenerationalCS = (*GenerationalNBS)(nil) var _ chunks.TableFileStore = (*GenerationalNBS)(nil) var _ chunks.GenerationalCS = (*GenerationalNBS)(nil) var _ chunks.ChunkStoreGarbageCollector = (*GenerationalNBS)(nil) @@ -144,14 +145,14 @@ func (gcs *GenerationalNBS) GetMany(ctx context.Context, hashes hash.HashSet, fo return gcs.ghostGen.GetMany(ctx, notFound, found) } -func (gcs *GenerationalNBS) GetManyCompressed(ctx context.Context, hashes hash.HashSet, found func(context.Context, CompressedChunk)) error { +func (gcs *GenerationalNBS) GetManyCompressed(ctx context.Context, hashes hash.HashSet, found func(context.Context, ToChunker)) error { return gcs.getManyCompressed(ctx, hashes, found, gcDependencyMode_TakeDependency) } -func (gcs *GenerationalNBS) getManyCompressed(ctx context.Context, hashes hash.HashSet, found func(context.Context, CompressedChunk), gcDepMode gcDependencyMode) error { +func (gcs *GenerationalNBS) getManyCompressed(ctx context.Context, hashes hash.HashSet, found func(context.Context, ToChunker), gcDepMode gcDependencyMode) error { var mu sync.Mutex notInOldGen := hashes.Copy() - err := gcs.oldGen.getManyCompressed(ctx, hashes, func(ctx context.Context, chunk CompressedChunk) { + err := gcs.oldGen.getManyCompressed(ctx, hashes, func(ctx context.Context, chunk ToChunker) { mu.Lock() delete(notInOldGen, chunk.Hash()) mu.Unlock() @@ -165,7 +166,7 @@ func (gcs *GenerationalNBS) getManyCompressed(ctx context.Context, hashes hash.H } notFound := notInOldGen.Copy() - err = gcs.newGen.getManyCompressed(ctx, notInOldGen, func(ctx context.Context, chunk CompressedChunk) { + err = gcs.newGen.getManyCompressed(ctx, notInOldGen, func(ctx context.Context, chunk ToChunker) { mu.Lock() delete(notFound, chunk.Hash()) mu.Unlock() @@ -458,7 +459,7 @@ func (gcs *GenerationalNBS) GetChunkLocationsWithPaths(ctx context.Context, hash return res, nil } -func (gcs *GenerationalNBS) GetChunkLocations(ctx context.Context, hashes hash.HashSet) (map[hash.Hash]map[hash.Hash]Range, error) { +func (gcs *GenerationalNBS) GetChunkLocations(ctx context.Context, hashes hash.HashSet) (map[string]map[hash.Hash]Range, error) { res, err := gcs.newGen.GetChunkLocations(ctx, hashes) if err != nil { return nil, err diff --git a/go/store/nbs/ghost_store.go b/go/store/nbs/ghost_store.go index 38fdf5247c4..b6d86ee04c5 100644 --- a/go/store/nbs/ghost_store.go +++ b/go/store/nbs/ghost_store.go @@ -90,11 +90,11 @@ func (g GhostBlockStore) GetMany(ctx context.Context, hashes hash.HashSet, found return nil } -func (g GhostBlockStore) GetManyCompressed(ctx context.Context, hashes hash.HashSet, found func(context.Context, CompressedChunk)) error { +func (g GhostBlockStore) GetManyCompressed(ctx context.Context, hashes hash.HashSet, found func(context.Context, ToChunker)) error { return g.getManyCompressed(ctx, hashes, found, gcDependencyMode_TakeDependency) } -func (g GhostBlockStore) getManyCompressed(ctx context.Context, hashes hash.HashSet, found func(context.Context, CompressedChunk), gcDepMode gcDependencyMode) error { +func (g GhostBlockStore) getManyCompressed(ctx context.Context, hashes hash.HashSet, found func(context.Context, ToChunker), gcDepMode gcDependencyMode) error { for h := range hashes { if g.skippedRefs.Has(h) { found(ctx, NewGhostCompressedChunk(h)) diff --git a/go/store/nbs/ghost_store_test.go b/go/store/nbs/ghost_store_test.go index e6fc6146f7e..aa347b1a6bf 100644 --- a/go/store/nbs/ghost_store_test.go +++ b/go/store/nbs/ghost_store_test.go @@ -74,8 +74,8 @@ func TestGhostBlockStore(t *testing.T) { require.Equal(t, ghost, got[0].Hash()) }) t.Run("GetManyCompressed", func(t *testing.T) { - var got []CompressedChunk - err := bs.GetManyCompressed(ctx, hash.NewHashSet(absent, ghost), func(_ context.Context, c CompressedChunk) { + var got []ToChunker + err := bs.GetManyCompressed(ctx, hash.NewHashSet(absent, ghost), func(_ context.Context, c ToChunker) { got = append(got, c) }) require.NoError(t, err) diff --git a/go/store/nbs/journal.go b/go/store/nbs/journal.go index dc8deff8e19..a07229102c2 100644 --- a/go/store/nbs/journal.go +++ b/go/store/nbs/journal.go @@ -503,7 +503,7 @@ func (c journalConjoiner) chooseConjoinees(upstream []tableSpec) (conjoinees, ke var stash tableSpec // don't conjoin journal pruned := make([]tableSpec, 0, len(upstream)) for _, ts := range upstream { - if isJournalAddr(ts.name) { + if isJournalAddr(ts.hash) { stash = ts } else { pruned = append(pruned, ts) @@ -513,7 +513,7 @@ func (c journalConjoiner) chooseConjoinees(upstream []tableSpec) (conjoinees, ke if err != nil { return nil, nil, err } - if !hash.Hash(stash.name).IsEmpty() { + if !hash.Hash(stash.hash).IsEmpty() { keepers = append(keepers, stash) } return @@ -646,7 +646,7 @@ func (jm *journalManifest) Close() (err error) { func containsJournalSpec(specs []tableSpec) (ok bool) { for _, spec := range specs { - if spec.name == journalAddr { + if spec.hash == journalAddr { ok = true break } diff --git a/go/store/nbs/journal_chunk_source.go b/go/store/nbs/journal_chunk_source.go index 10a5548b0c8..7d55f93f068 100644 --- a/go/store/nbs/journal_chunk_source.go +++ b/go/store/nbs/journal_chunk_source.go @@ -96,7 +96,7 @@ type journalRecord struct { } func (s journalChunkSource) getMany(ctx context.Context, eg *errgroup.Group, reqs []getRecord, found func(context.Context, *chunks.Chunk), keeper keeperF, stats *Stats) (bool, gcBehavior, error) { - return s.getManyCompressed(ctx, eg, reqs, func(ctx context.Context, cc CompressedChunk) { + return s.getManyCompressed(ctx, eg, reqs, func(ctx context.Context, cc ToChunker) { ch, err := cc.ToChunk() if err != nil { eg.Go(func() error { @@ -115,7 +115,7 @@ func (s journalChunkSource) getMany(ctx context.Context, eg *errgroup.Group, req // and then (4) asynchronously perform reads. We release the journal read // lock after returning when all reads are completed, which can be after the // function returns. -func (s journalChunkSource) getManyCompressed(ctx context.Context, eg *errgroup.Group, reqs []getRecord, found func(context.Context, CompressedChunk), keeper keeperF, stats *Stats) (bool, gcBehavior, error) { +func (s journalChunkSource) getManyCompressed(ctx context.Context, eg *errgroup.Group, reqs []getRecord, found func(context.Context, ToChunker), keeper keeperF, stats *Stats) (bool, gcBehavior, error) { defer trace.StartRegion(ctx, "journalChunkSource.getManyCompressed").End() var remaining bool @@ -182,6 +182,10 @@ func (s journalChunkSource) hash() hash.Hash { return journalAddr } +func (s journalChunkSource) name() string { + return s.hash().String() +} + // reader implements chunkSource. func (s journalChunkSource) reader(ctx context.Context) (io.ReadCloser, uint64, error) { rdr, sz, err := s.journal.snapshot(ctx) @@ -298,10 +302,10 @@ func equalSpecs(left, right []tableSpec) bool { } l := make(map[hash.Hash]struct{}, len(left)) for _, s := range left { - l[s.name] = struct{}{} + l[s.hash] = struct{}{} } for _, s := range right { - if _, ok := l[s.name]; !ok { + if _, ok := l[s.hash]; !ok { return false } } diff --git a/go/store/nbs/manifest.go b/go/store/nbs/manifest.go index f125353849d..69dc8b4b335 100644 --- a/go/store/nbs/manifest.go +++ b/go/store/nbs/manifest.go @@ -177,7 +177,7 @@ func (mc manifestContents) removeAppendixSpecs() (manifestContents, []tableSpec) filtered := make([]tableSpec, 0) removed := make([]tableSpec, 0) for _, s := range mc.specs { - if _, ok := appendixSet[s.name]; ok { + if _, ok := appendixSet[s.hash]; ok { removed = append(removed, s) } else { filtered = append(filtered, s) @@ -204,7 +204,7 @@ func (mc manifestContents) getAppendixSet() (ss map[hash.Hash]struct{}) { func toSpecSet(specs []tableSpec) (ss map[hash.Hash]struct{}) { ss = make(map[hash.Hash]struct{}, len(specs)) for _, ts := range specs { - ss[ts.name] = struct{}{} + ss[ts.hash] = struct{}{} } return ss } @@ -212,7 +212,7 @@ func toSpecSet(specs []tableSpec) (ss map[hash.Hash]struct{}) { func (mc manifestContents) size() (size uint64) { size += uint64(len(mc.nbfVers)) + hash.ByteLen + hash.ByteLen for _, sp := range mc.specs { - size += uint64(len(sp.name)) + uint32Size // for sp.chunkCount + size += uint64(len(sp.hash)) + uint32Size // for sp.chunkCount } return } @@ -439,27 +439,47 @@ func (mm manifestManager) Name() string { // TableSpecInfo is an interface for retrieving data from a tableSpec outside of this package type TableSpecInfo interface { - GetName() string + GetFileName() string GetChunkCount() uint32 + GetFileType() TableFileFormat } +type TableFileFormat int + +const ( + TypeNoms TableFileFormat = iota + TypeArchive +) + type tableSpec struct { - name hash.Hash + fileType TableFileFormat + hash hash.Hash chunkCount uint32 } -func (ts tableSpec) GetName() string { - return ts.name.String() +func (ts tableSpec) GetFileName() string { + switch ts.fileType { + case TypeNoms: + return ts.hash.String() + case TypeArchive: + return ts.hash.String() + ArchiveFileSuffix + default: + panic(fmt.Sprintf("runtime error: unknown table file type: %d", ts.fileType)) + } } func (ts tableSpec) GetChunkCount() uint32 { return ts.chunkCount } +func (ts tableSpec) GetFileType() TableFileFormat { + return ts.fileType +} + func tableSpecsToMap(specs []tableSpec) map[string]int { m := make(map[string]int) for _, spec := range specs { - m[spec.name.String()] = int(spec.chunkCount) + m[spec.hash.String()] = int(spec.chunkCount) } return m @@ -470,7 +490,7 @@ func parseSpecs(tableInfo []string) ([]tableSpec, error) { for i := range specs { var err error var ok bool - specs[i].name, ok = hash.MaybeParse(tableInfo[2*i]) + specs[i].hash, ok = hash.MaybeParse(tableInfo[2*i]) if !ok { return nil, fmt.Errorf("invalid table file name: %s", tableInfo[2*i]) } @@ -490,7 +510,7 @@ func parseSpecs(tableInfo []string) ([]tableSpec, error) { func formatSpecs(specs []tableSpec, tableInfo []string) { d.Chk.True(len(tableInfo) == 2*len(specs)) for i, t := range specs { - tableInfo[2*i] = t.name.String() + tableInfo[2*i] = t.hash.String() tableInfo[2*i+1] = strconv.FormatUint(uint64(t.chunkCount), 10) } } @@ -505,11 +525,11 @@ func generateLockHash(root hash.Hash, specs []tableSpec, appendix []tableSpec, e blockHash := sha512.New() blockHash.Write(root[:]) for _, spec := range appendix { - blockHash.Write(spec.name[:]) + blockHash.Write(spec.hash[:]) } blockHash.Write([]byte{0}) for _, spec := range specs { - blockHash.Write(spec.name[:]) + blockHash.Write(spec.hash[:]) } if len(extra) > 0 { blockHash.Write([]byte{0}) diff --git a/go/store/nbs/mem_table.go b/go/store/nbs/mem_table.go index 1fd8c0ffcda..ada8b669612 100644 --- a/go/store/nbs/mem_table.go +++ b/go/store/nbs/mem_table.go @@ -193,7 +193,7 @@ func (mt *memTable) getMany(ctx context.Context, eg *errgroup.Group, reqs []getR return remaining, gcBehavior_Continue, nil } -func (mt *memTable) getManyCompressed(ctx context.Context, eg *errgroup.Group, reqs []getRecord, found func(context.Context, CompressedChunk), keeper keeperF, stats *Stats) (bool, gcBehavior, error) { +func (mt *memTable) getManyCompressed(ctx context.Context, eg *errgroup.Group, reqs []getRecord, found func(context.Context, ToChunker), keeper keeperF, stats *Stats) (bool, gcBehavior, error) { var remaining bool for i, r := range reqs { data := mt.chunks[*r.a] diff --git a/go/store/nbs/mem_table_test.go b/go/store/nbs/mem_table_test.go index 42a1888a98f..3e97be099c0 100644 --- a/go/store/nbs/mem_table_test.go +++ b/go/store/nbs/mem_table_test.go @@ -306,7 +306,7 @@ func (crg chunkReaderGroup) getMany(ctx context.Context, eg *errgroup.Group, req return true, gcBehavior_Continue, nil } -func (crg chunkReaderGroup) getManyCompressed(ctx context.Context, eg *errgroup.Group, reqs []getRecord, found func(context.Context, CompressedChunk), keeper keeperF, stats *Stats) (bool, gcBehavior, error) { +func (crg chunkReaderGroup) getManyCompressed(ctx context.Context, eg *errgroup.Group, reqs []getRecord, found func(context.Context, ToChunker), keeper keeperF, stats *Stats) (bool, gcBehavior, error) { for _, haver := range crg { remaining, gcb, err := haver.getManyCompressed(ctx, eg, reqs, found, keeper, stats) if err != nil { diff --git a/go/store/nbs/metadata.go b/go/store/nbs/metadata.go index 6f443efb0b5..4c6852b89db 100644 --- a/go/store/nbs/metadata.go +++ b/go/store/nbs/metadata.go @@ -22,34 +22,31 @@ import ( "github.com/dolthub/dolt/go/store/hash" ) -type StorageType int - -const ( - Journal StorageType = iota - TableFileNewGen - TableFileOldGen - Archive -) - type ArchiveMetadata struct { originalTableFileId string } type StorageArtifact struct { - id hash.Hash - path string - storageType StorageType + // ID of the storage artifact. This is uses in the manifest to identify the artifact, but it is not the file name. + // as archives has a suffix. + id hash.Hash + // path to the storage artifact. + path string + // storageType is the type of the storage artifact. + storageType TableFileFormat + // arcMetadata is additional metadata for archive files. it is only set for storageType == TypeArchive. arcMetadata *ArchiveMetadata } type StorageMetadata struct { + // root is the path to storage. Specifically, it contains a .dolt directory. root string artifacts []StorageArtifact } func (sm *StorageMetadata) ArchiveFilesPresent() bool { for _, artifact := range sm.artifacts { - if artifact.storageType == Archive { + if artifact.storageType == TypeArchive { return true } } @@ -60,7 +57,7 @@ func (sm *StorageMetadata) ArchiveFilesPresent() bool { func (sm *StorageMetadata) RevertMap() map[hash.Hash]hash.Hash { revertMap := make(map[hash.Hash]hash.Hash) for _, artifact := range sm.artifacts { - if artifact.storageType == Archive { + if artifact.storageType == TypeArchive { md := artifact.arcMetadata revertMap[artifact.id] = hash.Parse(md.originalTableFileId) } @@ -68,6 +65,8 @@ func (sm *StorageMetadata) RevertMap() map[hash.Hash]hash.Hash { return revertMap } +// oldGenTableExists returns true if the table file exists in the oldgen directory. This is a file system check for +// a table file we have no record of, but may be useful in the process of reverting an archive operation. func (sm *StorageMetadata) oldGenTableExists(id hash.Hash) (bool, error) { path := filepath.Join(sm.root, ".dolt", "noms", "oldgen", id.String()) _, err := os.Stat(path) @@ -88,20 +87,9 @@ func GetStorageMetadata(path string) (StorageMetadata, error) { return StorageMetadata{}, err } - // TODO: new gen and journal information in storage metadata will be useful in the future. - // newGen := filepath.Join(path, ".dolt", "noms") - // newgenManifest := filepath.Join(newGen, "manifest") - - oldgen := filepath.Join(path, ".dolt", "noms", "oldgen") - oldgenManifest := filepath.Join(oldgen, "manifest") - - // If there is not oldgen manifest, then GC has never been run. Which is fine. We just don't have any oldgen. - if _, err := os.Stat(oldgenManifest); err != nil { - return StorageMetadata{}, nil - } - - // create a io.Reader for the manifest file - manifestReader, err := os.Open(oldgenManifest) + newGen := filepath.Join(path, ".dolt", "noms") + newgenManifest := filepath.Join(newGen, "manifest") + manifestReader, err := os.Open(newgenManifest) if err != nil { return StorageMetadata{}, err } @@ -116,53 +104,71 @@ func GetStorageMetadata(path string) (StorageMetadata, error) { // for each table in the manifest, get the table spec for i := 0; i < manifest.NumTableSpecs(); i++ { tableSpecInfo := manifest.GetTableSpecInfo(i) + artifact, err := buildArtifact(tableSpecInfo, newGen) + if err != nil { + return StorageMetadata{}, err + } + artifacts = append(artifacts, artifact) + } - // If the oldgen/name exists, it's not an archive. If it exists with a .darc suffix, then it's an archive. - tfName := tableSpecInfo.GetName() - fullPath := filepath.Join(oldgen, tfName) - _, err := os.Stat(fullPath) - if err == nil { - // exists. Not an archive. - artifacts = append(artifacts, StorageArtifact{ - id: hash.Parse(tfName), - path: fullPath, - storageType: TableFileOldGen, - }) - } else if os.IsNotExist(err) { - arcName := tfName + ".darc" - arcPath := filepath.Join(oldgen, arcName) - _, err := os.Stat(arcPath) - if err == nil { - // reader for the path. State. call - reader, fileSize, err := openReader(arcPath) - if err != nil { - return StorageMetadata{}, err - } - - arcMetadata, err := newArchiveMetadata(reader, fileSize) - if err != nil { - return StorageMetadata{}, err - } - - artifacts = append(artifacts, StorageArtifact{ - id: hash.Parse(tfName), - path: arcPath, - storageType: Archive, - arcMetadata: arcMetadata, - }) - } else { - // any error is bad here. If the files don't exist, then the manifest is no good. - return StorageMetadata{}, err - } - } else { - // some other error. + oldgen := filepath.Join(newGen, "oldgen") + oldgenManifest := filepath.Join(oldgen, "manifest") + + // If there is no oldgen manifest, then GC has never been run. Which is fine. We just don't have any oldgen. + if _, err := os.Stat(oldgenManifest); err != nil { + return StorageMetadata{path, artifacts}, nil + } + + manifestReader, err = os.Open(oldgenManifest) + if err != nil { + return StorageMetadata{}, err + } + + for i := 0; i < manifest.NumTableSpecs(); i++ { + tableSpecInfo := manifest.GetTableSpecInfo(i) + + artifact, err := buildArtifact(tableSpecInfo, oldgen) + if err != nil { return StorageMetadata{}, err } + artifacts = append(artifacts, artifact) } return StorageMetadata{path, artifacts}, nil } +func buildArtifact(info TableSpecInfo, genPath string) (StorageArtifact, error) { + tfName := info.GetFileName() + fullPath := filepath.Join(genPath, tfName) + + if info.GetFileType() == TypeNoms { + return StorageArtifact{ + id: hash.Parse(tfName), + path: fullPath, + storageType: TypeNoms, + }, nil + } else if info.GetFileType() == TypeArchive { + reader, fileSize, err := openReader(fullPath) + if err != nil { + return StorageArtifact{}, err + } + + arcMetadata, err := newArchiveMetadata(reader, fileSize) + if err != nil { + return StorageArtifact{}, err + } + + return StorageArtifact{ + id: hash.Parse(tfName), + path: fullPath, + storageType: TypeArchive, + arcMetadata: arcMetadata, + }, nil + } else { + return StorageArtifact{}, ErrUnsupportedTableFileFormat + } +} + func validateDir(path string) error { info, err := os.Stat(path) diff --git a/go/store/nbs/nbs_metrics_wrapper.go b/go/store/nbs/nbs_metrics_wrapper.go index 36b262075b7..1da178f8843 100644 --- a/go/store/nbs/nbs_metrics_wrapper.go +++ b/go/store/nbs/nbs_metrics_wrapper.go @@ -99,7 +99,7 @@ func (nbsMW *NBSMetricWrapper) PruneTableFiles(ctx context.Context) error { // GetManyCompressed gets the compressed Chunks with |hashes| from the store. On return, // |found| will have been fully sent all chunks which have been // found. Any non-present chunks will silently be ignored. -func (nbsMW *NBSMetricWrapper) GetManyCompressed(ctx context.Context, hashes hash.HashSet, found func(context.Context, CompressedChunk)) error { +func (nbsMW *NBSMetricWrapper) GetManyCompressed(ctx context.Context, hashes hash.HashSet, found func(context.Context, ToChunker)) error { atomic.AddInt32(&nbsMW.TotalChunkGets, int32(len(hashes))) return nbsMW.nbs.GetManyCompressed(ctx, hashes, found) } diff --git a/go/store/nbs/root_tracker_test.go b/go/store/nbs/root_tracker_test.go index 37d82483743..e8fff9ad62b 100644 --- a/go/store/nbs/root_tracker_test.go +++ b/go/store/nbs/root_tracker_test.go @@ -404,7 +404,7 @@ func interloperWrite(fm *fakeManifest, p tablePersister, rootChunk []byte, chunk return hash.Hash{}, nil, err } - fm.set(constants.FormatLD1String, newLock, newRoot, []tableSpec{{src.hash(), uint32(len(chunks) + 1)}}, nil) + fm.set(constants.FormatLD1String, newLock, newRoot, []tableSpec{{typeNoms, src.hash(), uint32(len(chunks) + 1)}}, nil) if err = src.close(); err != nil { return [20]byte{}, nil, err diff --git a/go/store/nbs/store.go b/go/store/nbs/store.go index e49daa85956..ab16bb1b1b5 100644 --- a/go/store/nbs/store.go +++ b/go/store/nbs/store.go @@ -68,7 +68,6 @@ const ( defaultMaxTables = 256 defaultManifestCacheSize = 1 << 23 // 8MB - preflushChunkCount = 8 ) var ( @@ -86,7 +85,7 @@ func makeGlobalCaches() { type NBSCompressedChunkStore interface { chunks.ChunkStore - GetManyCompressed(context.Context, hash.HashSet, func(context.Context, CompressedChunk)) error + GetManyCompressed(context.Context, hash.HashSet, func(context.Context, ToChunker)) error } type gcDependencyMode int @@ -97,7 +96,7 @@ const ( ) type CompressedChunkStoreForGC interface { - getManyCompressed(context.Context, hash.HashSet, func(context.Context, CompressedChunk), gcDependencyMode) error + getManyCompressed(context.Context, hash.HashSet, func(context.Context, ToChunker), gcDependencyMode) error } type NomsBlockStore struct { @@ -144,8 +143,10 @@ var _ chunks.ChunkStoreGarbageCollector = &NomsBlockStore{} const hasCacheSize = 100000 type Range struct { - Offset uint64 - Length uint32 + Offset uint64 + Length uint32 + DictOffset uint64 + DictLength uint32 } // ChunkJournal returns the ChunkJournal in use by this NomsBlockStore, or nil if no ChunkJournal is being used. @@ -163,13 +164,13 @@ func (nbs *NomsBlockStore) GetChunkLocationsWithPaths(ctx context.Context, hashe } toret := make(map[string]map[hash.Hash]Range, len(locs)) for k, v := range locs { - toret[k.String()] = v + toret[k] = v } return toret, nil } -func (nbs *NomsBlockStore) GetChunkLocations(ctx context.Context, hashes hash.HashSet) (map[hash.Hash]map[hash.Hash]Range, error) { - fn := func(css chunkSourceSet, gr []getRecord, ranges map[hash.Hash]map[hash.Hash]Range, keeper keeperF) (gcBehavior, error) { +func (nbs *NomsBlockStore) GetChunkLocations(ctx context.Context, hashes hash.HashSet) (map[string]map[hash.Hash]Range, error) { + fn := func(css chunkSourceSet, gr []getRecord, ranges map[string]map[hash.Hash]Range, keeper keeperF) (gcBehavior, error) { for _, cs := range css { rng, gcb, err := cs.getRecordRanges(ctx, gr, keeper) if err != nil { @@ -178,8 +179,11 @@ func (nbs *NomsBlockStore) GetChunkLocations(ctx context.Context, hashes hash.Ha if gcb != gcBehavior_Continue { return gcb, nil } + if len(rng) == 0 { + continue + } - h := hash.Hash(cs.hash()) + h := cs.name() if m, ok := ranges[h]; ok { for k, v := range rng { m[k] = v @@ -197,7 +201,7 @@ func (nbs *NomsBlockStore) GetChunkLocations(ctx context.Context, hashes hash.Ha nbs.mu.Unlock() gr := toGetRecords(hashes) - ranges := make(map[hash.Hash]map[hash.Hash]Range) + ranges := make(map[string]map[hash.Hash]Range) gcb, err := fn(tables.upstream, gr, ranges, keeper) if needsContinue, err := nbs.handleUnlockedRead(ctx, gcb, endRead, err); err != nil { @@ -317,15 +321,15 @@ func (nbs *NomsBlockStore) updateManifestAddFiles(ctx context.Context, updates m if appendixOption == nil { if _, ok := currSpecs[h]; !ok { hasWork = true - contents.specs = append(contents.specs, tableSpec{h, count}) + contents.specs = append(contents.specs, tableSpec{TypeNoms, h, count}) } } else if *appendixOption == ManifestAppendixOption_Set { hasWork = true - appendixSpecs = append(appendixSpecs, tableSpec{h, count}) + appendixSpecs = append(appendixSpecs, tableSpec{TypeNoms, h, count}) } else if *appendixOption == ManifestAppendixOption_Append { if _, ok := currAppendixSpecs[h]; !ok { hasWork = true - appendixSpecs = append(appendixSpecs, tableSpec{h, count}) + appendixSpecs = append(appendixSpecs, tableSpec{TypeNoms, h, count}) } } else { return manifestContents{}, ErrUnsupportedManifestAppendixOption @@ -465,12 +469,12 @@ func OverwriteStoreManifest(ctx context.Context, store *NomsBlockStore, root has } // Appendix table files should come first in specs for h, c := range appendixTableFiles { - s := tableSpec{name: h, chunkCount: c} + s := tableSpec{fileType: TypeNoms, hash: h, chunkCount: c} contents.appendix = append(contents.appendix, s) contents.specs = append(contents.specs, s) } for h, c := range tableFiles { - s := tableSpec{name: h, chunkCount: c} + s := tableSpec{fileType: TypeNoms, hash: h, chunkCount: c} contents.specs = append(contents.specs, s) } contents.lock = generateLockHash(contents.root, contents.specs, contents.appendix, nil) @@ -896,11 +900,11 @@ func (nbs *NomsBlockStore) GetMany(ctx context.Context, hashes hash.HashSet, fou ) } -func (nbs *NomsBlockStore) GetManyCompressed(ctx context.Context, hashes hash.HashSet, found func(context.Context, CompressedChunk)) error { +func (nbs *NomsBlockStore) GetManyCompressed(ctx context.Context, hashes hash.HashSet, found func(context.Context, ToChunker)) error { return nbs.getManyCompressed(ctx, hashes, found, gcDependencyMode_TakeDependency) } -func (nbs *NomsBlockStore) getManyCompressed(ctx context.Context, hashes hash.HashSet, found func(context.Context, CompressedChunk), gcDepMode gcDependencyMode) error { +func (nbs *NomsBlockStore) getManyCompressed(ctx context.Context, hashes hash.HashSet, found func(context.Context, ToChunker), gcDepMode gcDependencyMode) error { ctx, span := tracer.Start(ctx, "nbs.GetManyCompressed", trace.WithAttributes(attribute.Int("num_hashes", len(hashes)))) defer span.End() return nbs.getManyWithFunc(ctx, hashes, gcDepMode, @@ -1399,7 +1403,7 @@ func (nbs *NomsBlockStore) updateManifest(ctx context.Context, current, last has filtered := make([]tableSpec, 0, len(specs)) for _, s := range specs { - if _, present := appendixSet[s.name]; !present { + if _, present := appendixSet[s.hash]; !present { filtered = append(filtered, s) } } @@ -1488,7 +1492,7 @@ func (tf tableFile) LocationPrefix() string { // FileID gets the id of the file func (tf tableFile) FileID() string { - return tf.info.GetName() + return tf.info.GetFileName() } // NumChunks returns the number of chunks in a table file @@ -1546,10 +1550,15 @@ func getTableFiles(css map[hash.Hash]chunkSource, contents manifestContents, num } for i := 0; i < numSpecs; i++ { info := specFunc(contents, i) - cs, ok := css[info.name] + cs, ok := css[info.hash] if !ok { return nil, ErrSpecWithoutChunkSource } + + if _, ok := cs.(archiveChunkSource); ok { + info.fileType = TypeArchive + } + tableFiles = append(tableFiles, newTableFile(cs, info)) } return tableFiles, nil @@ -1844,25 +1853,25 @@ func (i *markAndSweeper) SaveHashes(ctx context.Context, hashes []hash.Hash) err found := 0 var addErr error - err = i.src.getManyCompressed(ctx, toVisit, func(ctx context.Context, cc CompressedChunk) { + err = i.src.getManyCompressed(ctx, toVisit, func(ctx context.Context, tc ToChunker) { mu.Lock() defer mu.Unlock() if addErr != nil { return } found += 1 - if cc.IsGhost() { + if tc.IsGhost() { // Ghost chunks encountered on the walk can be left alone --- they // do not bring their dependencies, and because of how generational // store works, they will still be ghost chunks // in the store after the GC is finished. return } - addErr = i.gcc.addChunk(ctx, cc) + addErr = i.gcc.addChunk(ctx, tc) if addErr != nil { return } - c, err := cc.ToChunk() + c, err := tc.ToChunk() if err != nil { addErr = err return @@ -1917,7 +1926,7 @@ func (gcf gcFinalizer) AddChunksToStore(ctx context.Context) (chunks.HasManyFunc fileIdToNumChunks := tableSpecsToMap(gcf.specs) var addrs []hash.Hash for _, spec := range gcf.specs { - addrs = append(addrs, spec.name) + addrs = append(addrs, spec.hash) } f := func(ctx context.Context, hashes hash.HashSet) (hash.HashSet, error) { return gcf.nbs.hasManyInSources(addrs, hashes) diff --git a/go/store/nbs/table.go b/go/store/nbs/table.go index 234163be86e..4bc7f9acaba 100644 --- a/go/store/nbs/table.go +++ b/go/store/nbs/table.go @@ -187,7 +187,7 @@ type extractRecord struct { err error } -// Returned by read methods that take a |keeperFunc|, this lets a +// Returned by read methods that take a |keeperF|, this lets a // caller know whether the operation was successful or if it needs to // be retried. It may need to be retried if a GC is in progress but // the dependencies indicated by the operation cannot be added to the @@ -202,6 +202,10 @@ const ( gcBehavior_Block = true ) +// keeperF is a function that takes a hash.Hash and returns true if the hash is used by the GC system to indicate +// that the chunk requested may not be present in the future, and therefore |gcBehavior_Block| should be returned. This +// is used to allow read/write ops to the store by non-GC processes while GC is underway. The |keeperF| may be nil, +// in which case GC is not underway. If it's non-nil, and return false, it's ok to proceed with the operation (|gcBehavior_Continue|) type keeperF func(hash.Hash) bool type chunkReader interface { @@ -221,7 +225,7 @@ type chunkReader interface { // getManyCompressed sets getRecord.found to true, and calls |found| for each present getRecord query. // It returns true if any getRecord query was not found in this chunkReader. - getManyCompressed(ctx context.Context, eg *errgroup.Group, reqs []getRecord, found func(context.Context, CompressedChunk), keeper keeperF, stats *Stats) (bool, gcBehavior, error) + getManyCompressed(ctx context.Context, eg *errgroup.Group, reqs []getRecord, found func(context.Context, ToChunker), keeper keeperF, stats *Stats) (bool, gcBehavior, error) // count returns the chunk count for this chunkReader. count() (uint32, error) @@ -239,6 +243,10 @@ type chunkSource interface { // hash returns the hash address of this chunkSource. hash() hash.Hash + // name is the on disk short name for this chunkSource. Classically, this was a hash. Having files + // with suffixes (eg darc) was useful. + name() string + // opens a Reader to the first byte of the chunkData segment of this table. reader(context.Context) (io.ReadCloser, uint64, error) diff --git a/go/store/nbs/table_reader.go b/go/store/nbs/table_reader.go index de66ffd27bd..f3456ae58fc 100644 --- a/go/store/nbs/table_reader.go +++ b/go/store/nbs/table_reader.go @@ -38,6 +38,14 @@ import ( // Do not read more than 128MB at a time. const maxReadSize = 128 * 1024 * 1024 +type ToChunker interface { + Hash() hash.Hash + ToChunk() (chunks.Chunk, error) + FullCompressedChunkLen() uint32 + IsEmpty() bool + IsGhost() bool +} + // CompressedChunk represents a chunk of data in a table file which is still compressed via snappy. type CompressedChunk struct { // H is the hash of the chunk @@ -53,6 +61,8 @@ type CompressedChunk struct { ghost bool } +var _ ToChunker = CompressedChunk{} + // NewCompressedChunk creates a CompressedChunk func NewCompressedChunk(h hash.Hash, buff []byte) (CompressedChunk, error) { dataLen := uint64(len(buff)) - checksumSize @@ -114,6 +124,10 @@ func (cmp CompressedChunk) CompressedSize() int { return len(cmp.CompressedData) } +func (cmp CompressedChunk) FullCompressedChunkLen() uint32 { + return uint32(len(cmp.FullCompressedChunk)) +} + var EmptyCompressedChunk CompressedChunk func init() { @@ -323,10 +337,10 @@ var _ chunkReader = tableReader{} func (tr tableReader) readCompressedAtOffsets( ctx context.Context, rb readBatch, - found func(context.Context, CompressedChunk), + found func(context.Context, ToChunker), stats *Stats, ) error { - return tr.readAtOffsetsWithCB(ctx, rb, stats, func(ctx context.Context, cmp CompressedChunk) error { + return tr.readAtOffsetsWithCB(ctx, rb, stats, func(ctx context.Context, cmp ToChunker) error { found(ctx, cmp) return nil }) @@ -338,7 +352,7 @@ func (tr tableReader) readAtOffsets( found func(context.Context, *chunks.Chunk), stats *Stats, ) error { - return tr.readAtOffsetsWithCB(ctx, rb, stats, func(ctx context.Context, cmp CompressedChunk) error { + return tr.readAtOffsetsWithCB(ctx, rb, stats, func(ctx context.Context, cmp ToChunker) error { chk, err := cmp.ToChunk() if err != nil { @@ -354,7 +368,7 @@ func (tr tableReader) readAtOffsetsWithCB( ctx context.Context, rb readBatch, stats *Stats, - cb func(ctx context.Context, cmp CompressedChunk) error, + cb func(ctx context.Context, cmp ToChunker) error, ) error { readLength := rb.End() - rb.Start() buff := make([]byte, readLength) @@ -405,7 +419,7 @@ func (tr tableReader) getMany( err = tr.getManyAtOffsets(ctx, eg, offsetRecords, found, stats) return remaining, gcBehavior_Continue, err } -func (tr tableReader) getManyCompressed(ctx context.Context, eg *errgroup.Group, reqs []getRecord, found func(context.Context, CompressedChunk), keeper keeperF, stats *Stats) (bool, gcBehavior, error) { +func (tr tableReader) getManyCompressed(ctx context.Context, eg *errgroup.Group, reqs []getRecord, found func(context.Context, ToChunker), keeper keeperF, stats *Stats) (bool, gcBehavior, error) { // Pass #1: Iterate over |reqs| and |tr.prefixes| (both sorted by address) and build the set // of table locations which must be read in order to satisfy the getMany operation. offsetRecords, remaining, gcb, err := tr.findOffsets(reqs, keeper) @@ -419,7 +433,7 @@ func (tr tableReader) getManyCompressed(ctx context.Context, eg *errgroup.Group, return remaining, gcBehavior_Continue, err } -func (tr tableReader) getManyCompressedAtOffsets(ctx context.Context, eg *errgroup.Group, offsetRecords offsetRecSlice, found func(context.Context, CompressedChunk), stats *Stats) error { +func (tr tableReader) getManyCompressedAtOffsets(ctx context.Context, eg *errgroup.Group, offsetRecords offsetRecSlice, found func(context.Context, ToChunker), stats *Stats) error { return tr.getManyAtOffsetsWithReadFunc(ctx, eg, offsetRecords, stats, func( ctx context.Context, rb readBatch, diff --git a/go/store/nbs/table_set.go b/go/store/nbs/table_set.go index 88fd92de587..59bec668932 100644 --- a/go/store/nbs/table_set.go +++ b/go/store/nbs/table_set.go @@ -228,7 +228,7 @@ func (ts tableSet) getMany(ctx context.Context, eg *errgroup.Group, reqs []getRe return f(ts.upstream) } -func (ts tableSet) getManyCompressed(ctx context.Context, eg *errgroup.Group, reqs []getRecord, found func(context.Context, CompressedChunk), keeper keeperF, stats *Stats) (bool, gcBehavior, error) { +func (ts tableSet) getManyCompressed(ctx context.Context, eg *errgroup.Group, reqs []getRecord, found func(context.Context, ToChunker), keeper keeperF, stats *Stats) (bool, gcBehavior, error) { f := func(css chunkSourceSet) (bool, gcBehavior, error) { for _, haver := range css { remaining, gcb, err := haver.getManyCompressed(ctx, eg, reqs, found, keeper, stats) @@ -433,10 +433,10 @@ func (ts tableSet) rebase(ctx context.Context, specs []tableSpec, stats *Stats) specs = make([]tableSpec, 0, len(orig)) seen := map[hash.Hash]struct{}{} for _, spec := range orig { - if _, ok := seen[spec.name]; ok { + if _, ok := seen[spec.hash]; ok { continue } - seen[spec.name] = struct{}{} + seen[spec.hash] = struct{}{} // keep specs in order to play nicely with // manifest appendix optimization specs = append(specs, spec) @@ -464,7 +464,7 @@ func (ts tableSet) rebase(ctx context.Context, specs []tableSpec, stats *Stats) upstream := make(chunkSourceSet, len(specs)) for _, s := range specs { // clone tables that we have already opened - if cs, ok := ts.upstream[s.name]; ok { + if cs, ok := ts.upstream[s.hash]; ok { cl, err := cs.clone() if err != nil { _ = eg.Wait() @@ -482,7 +482,7 @@ func (ts tableSet) rebase(ctx context.Context, specs []tableSpec, stats *Stats) // open missing tables in parallel spec := s eg.Go(func() error { - cs, err := ts.p.Open(ctx, spec.name, spec.chunkCount, stats) // NM4 - spec.name is the tf/arch name. + cs, err := ts.p.Open(ctx, spec.hash, spec.chunkCount, stats) if err != nil { return err } @@ -521,8 +521,11 @@ func (ts tableSet) toSpecs() ([]tableSpec, error) { if err != nil { return nil, err } else if cnt > 0 { - h := src.hash() - tableSpecs = append(tableSpecs, tableSpec{h, cnt}) + if _, ok := src.(archiveChunkSource); ok { + tableSpecs = append(tableSpecs, tableSpec{TypeArchive, src.hash(), cnt}) + } else { + tableSpecs = append(tableSpecs, tableSpec{TypeNoms, src.hash(), cnt}) + } } } for _, src := range ts.upstream { @@ -532,11 +535,14 @@ func (ts tableSet) toSpecs() ([]tableSpec, error) { } else if cnt <= 0 { return nil, errors.New("no upstream chunks") } - h := src.hash() - tableSpecs = append(tableSpecs, tableSpec{h, cnt}) + if _, ok := src.(archiveChunkSource); ok { + tableSpecs = append(tableSpecs, tableSpec{TypeArchive, src.hash(), cnt}) + } else { + tableSpecs = append(tableSpecs, tableSpec{TypeNoms, src.hash(), cnt}) + } } sort.Slice(tableSpecs, func(i, j int) bool { - return bytes.Compare(tableSpecs[i].name[:], tableSpecs[j].name[:]) < 0 + return bytes.Compare(tableSpecs[i].hash[:], tableSpecs[j].hash[:]) < 0 }) return tableSpecs, nil } diff --git a/go/store/nbs/table_set_test.go b/go/store/nbs/table_set_test.go index e1cfcef3dad..cc19f34fe45 100644 --- a/go/store/nbs/table_set_test.go +++ b/go/store/nbs/table_set_test.go @@ -238,7 +238,7 @@ func TestTableSetClosesOpenedChunkSourcesOnErr(t *testing.T) { p.sourcesToFail[a] = true } once = false - specs = append(specs, tableSpec{a, 1}) + specs = append(specs, tableSpec{typeNoms, a, 1}) } ts := newTableSet(p, q) diff --git a/integration-tests/bats/archive-test-repo/noms/8p5e2m6skovfdjlh4jg3llr8sfvu384l b/integration-tests/bats/archive-test-repo/noms/8p5e2m6skovfdjlh4jg3llr8sfvu384l new file mode 100644 index 00000000000..aa14b7ce0eb Binary files /dev/null and b/integration-tests/bats/archive-test-repo/noms/8p5e2m6skovfdjlh4jg3llr8sfvu384l differ diff --git a/integration-tests/bats/archive-test-repo/noms/LOCK b/integration-tests/bats/archive-test-repo/noms/LOCK new file mode 100644 index 00000000000..e69de29bb2d diff --git a/integration-tests/bats/archive-test-repo/noms/manifest b/integration-tests/bats/archive-test-repo/noms/manifest new file mode 100644 index 00000000000..924d8f97c52 --- /dev/null +++ b/integration-tests/bats/archive-test-repo/noms/manifest @@ -0,0 +1 @@ +5:__DOLT__:6eseiohaelofp485e95ti1pgqe5qrseg:att0kn4lt0uqce5mdqf01ni9ediahoo4:6eseiohaelofp485e95ti1pgqe5qrseg:8p5e2m6skovfdjlh4jg3llr8sfvu384l:2 \ No newline at end of file diff --git a/integration-tests/bats/archive-test-repo/noms/oldgen/29o8a3uevcpr15tilcemb3s438edmoog.darc b/integration-tests/bats/archive-test-repo/noms/oldgen/29o8a3uevcpr15tilcemb3s438edmoog.darc new file mode 100644 index 00000000000..1b938008b80 Binary files /dev/null and b/integration-tests/bats/archive-test-repo/noms/oldgen/29o8a3uevcpr15tilcemb3s438edmoog.darc differ diff --git a/integration-tests/bats/archive-test-repo/noms/oldgen/LOCK b/integration-tests/bats/archive-test-repo/noms/oldgen/LOCK new file mode 100644 index 00000000000..e69de29bb2d diff --git a/integration-tests/bats/archive-test-repo/noms/oldgen/dnu4lr5j8sstbj5usbld7alsnuj5nf23.darc b/integration-tests/bats/archive-test-repo/noms/oldgen/dnu4lr5j8sstbj5usbld7alsnuj5nf23.darc new file mode 100644 index 00000000000..c13a757ab41 Binary files /dev/null and b/integration-tests/bats/archive-test-repo/noms/oldgen/dnu4lr5j8sstbj5usbld7alsnuj5nf23.darc differ diff --git a/integration-tests/bats/archive-test-repo/noms/oldgen/manifest b/integration-tests/bats/archive-test-repo/noms/oldgen/manifest new file mode 100644 index 00000000000..95b6efd291c --- /dev/null +++ b/integration-tests/bats/archive-test-repo/noms/oldgen/manifest @@ -0,0 +1 @@ +5:__DOLT__:c3qnrebcud1nuibb18lmitlc442ql6me:00000000000000000000000000000000:c3qnrebcud1nuibb18lmitlc442ql6me:29o8a3uevcpr15tilcemb3s438edmoog:125:dnu4lr5j8sstbj5usbld7alsnuj5nf23:139 \ No newline at end of file diff --git a/integration-tests/bats/archive-test-repo/repo_state.json b/integration-tests/bats/archive-test-repo/repo_state.json new file mode 100755 index 00000000000..32f2d6197d5 --- /dev/null +++ b/integration-tests/bats/archive-test-repo/repo_state.json @@ -0,0 +1,6 @@ +{ + "head": "refs/heads/main", + "remotes": {}, + "backups": {}, + "branches": {} +} \ No newline at end of file diff --git a/integration-tests/bats/archive.bats b/integration-tests/bats/archive.bats index 0e31e7b0aea..32100da5d30 100755 --- a/integration-tests/bats/archive.bats +++ b/integration-tests/bats/archive.bats @@ -11,6 +11,12 @@ setup() { } teardown() { + if [ -n "$remotesrv_pid" ]; then + kill "$remotesrv_pid" + wait "$remotesrv_pid" || : + remotesrv_pid="" + fi + assert_feature_version teardown_common } @@ -109,19 +115,6 @@ mutations_and_gc_statement() { [ "$files" -eq "2" ] } -@test "archive: archive with remotesrv no go" { - dolt sql -q "$(mutations_and_gc_statement)" - dolt archive - - run dolt sql-server --remotesapi-port=12321 - [ "$status" -eq 1 ] - [[ "$output" =~ "archive files present" ]] || false - - run remotesrv --repo-mode - [ "$status" -eq 1 ] - [[ "$output" =~ "archive files present" ]] || false -} - @test "archive: archive --revert (fast)" { dolt sql -q "$(mutations_and_gc_statement)" dolt archive @@ -143,19 +136,128 @@ mutations_and_gc_statement() { [ "$commits" -eq "66" ] } -@test "archive: archive backup no go" { - dolt sql -q "$(mutations_and_gc_statement)" - dolt archive +@test "archive: can clone archived repository" { + mkdir -p remote/.dolt + mkdir cloned - dolt backup add bac1 file://../bac1 - run dolt backup sync bac1 + # Copy the archive test repo to remote directory + cp -R $BATS_TEST_DIRNAME/archive-test-repo/* remote/.dolt + cd remote - [ "$status" -eq 1 ] - [[ "$output" =~ "archive files present" ]] || false + port=$( definePORT ) - # currently the cli and stored procedures are different code paths. - run dolt sql -q "call dolt_backup('sync', 'bac1')" - [ "$status" -eq 1 ] - # NM4 - TODO. This message is cryptic, but plumbing the error through is awkward. - [[ "$output" =~ "Archive chunk source" ]] || false + remotesrv --http-port $port --grpc-port $port --repo-mode & + remotesrv_pid=$! + [[ "$remotesrv_pid" -gt 0 ]] || false + + cd ../cloned + run dolt clone http://localhost:$port/test-org/test-repo repo1 + [ "$status" -eq 0 ] + cd repo1 + + # Verify we can read data + run dolt sql -q 'select sum(i) from tbl;' + [[ "$status" -eq 0 ]] || false + [[ "$output" =~ "138075" ]] || false # i = 1 - 525, sum is 138075 + + kill $remotesrv_pid + wait $remotesrv_pid || : + remotesrv_pid="" + + ## The above test is the setup for the next test - so we'll stick both in here. + ## This tests cloning from a clone. Archive files are generally in oldgen, but not the case with a fresh clone. + cd ../../ + mkdir clone2 + + cd cloned/repo1 # start the server using the clone from above. + port=$( definePORT ) + remotesrv --http-port $port --grpc-port $port --repo-mode & + remotesrv_pid=$! + [[ "$remotesrv_pid" -gt 0 ]] || false + + cd ../../clone2 + run dolt clone http://localhost:$port/test-org/test-repo repo2 + [ "$status" -eq 0 ] + cd repo2 + + run dolt sql -q 'select sum(i) from tbl;' + [[ "$status" -eq 0 ]] || false + [[ "$output" =~ "138075" ]] || false # i = 1 - 525, sum is 138075 +} + +@test "archive: can clone respiratory with mixed types" { + mkdir -p remote/.dolt + mkdir cloned + + # Copy the archive test repo to remote directory + cp -R $BATS_TEST_DIRNAME/archive-test-repo/* remote/.dolt + cd remote + + # Insert data (commits automatically), but don't gc/archive yet. Want to make sure we can still clone it. + dolt sql -q "$(insert_statement)" + + port=$( definePORT ) + + remotesrv --http-port $port --grpc-port $port --repo-mode & + remotesrv_pid=$! + [[ "$remotesrv_pid" -gt 0 ]] || false + + cd ../cloned + run dolt clone http://localhost:$port/test-org/test-repo repo1 + [ "$status" -eq 0 ] + cd repo1 + + # verify new data is there. + run dolt sql -q 'select sum(i) from tbl;' + [[ "$status" -eq 0 ]] || false + + [[ "$output" =~ "151525" ]] || false # i = 1 - 550, sum is 151525 +} + +@test "archive: can fetch chunks from an archived repo" { + mkdir -p remote/.dolt + mkdir cloned + + # Copy the archive test repo to remote directory + cp -R $BATS_TEST_DIRNAME/archive-test-repo/* remote/.dolt + cd remote + + port=$( definePORT ) + + remotesrv --http-port $port --grpc-port $port --repo-mode & + remotesrv_pid=$! + [[ "$remotesrv_pid" -gt 0 ]] || false + + cd ../cloned + dolt clone http://localhost:$port/test-org/test-repo repo1 + # Fetch when there are no changes. + cd repo1 + dolt fetch + + ## update the remote repo directly. Need to run the archive command when the server is stopped. + ## This will result in achived files on the remote, which we will need to read chunks from when we fetch. + cd ../../remote + kill $remotesrv_pid + wait $remotesrv_pid || : + remotesrv_pid="" + dolt sql -q "$(mutations_and_gc_statement)" + dolt archive + + remotesrv --http-port $port --grpc-port $port --repo-mode & + remotesrv_pid=$! + [[ "$remotesrv_pid" -gt 0 ]] || false + + cd ../cloned/repo1 + + run dolt fetch + [ "$status" -eq 0 ] + + run dolt status + [ "$status" -eq 0 ] + + [[ "$output" =~ "Your branch is behind 'origin/main' by 20 commits, and can be fast-forwarded" ]] || false + + # Verify the repo has integrity. + dolt fsck } + diff --git a/proto/dolt/services/remotesapi/v1alpha1/chunkstore.proto b/proto/dolt/services/remotesapi/v1alpha1/chunkstore.proto index 519bd616b03..04ff5804567 100644 --- a/proto/dolt/services/remotesapi/v1alpha1/chunkstore.proto +++ b/proto/dolt/services/remotesapi/v1alpha1/chunkstore.proto @@ -89,6 +89,8 @@ message RangeChunk { bytes hash = 1; uint64 offset = 2; uint32 length = 3; + uint64 dictionary_offset = 4; + uint32 dictionary_length = 5; } message HttpGetRange {