diff --git a/Documentation/dev-guide/api_reference_v3.md b/Documentation/dev-guide/api_reference_v3.md index e2725850625..eecd5a3ce15 100644 --- a/Documentation/dev-guide/api_reference_v3.md +++ b/Documentation/dev-guide/api_reference_v3.md @@ -45,6 +45,7 @@ This is a generated documentation. Please read the proto files for more. | Method | Request Type | Response Type | Description | | ------ | ------------ | ------------- | ----------- | | Range | RangeRequest | RangeResponse | Range gets the keys in the range from the key-value store. | +| RangeStream | RangeRequest | RangeResponse | RangeStream gets the keys in the range stream from the key-value store. | | Put | PutRequest | PutResponse | Put puts the given key into the key-value store. A put request increments the revision of the key-value store and generates one event in the event history. | | DeleteRange | DeleteRangeRequest | DeleteRangeResponse | DeleteRange deletes the given range from the key-value store. A delete request increments the revision of the key-value store and generates a delete event in the event history for every deleted key. | | Txn | TxnRequest | TxnResponse | Txn processes multiple requests in a single transaction. A txn request increments the revision of the key-value store and generates events with the same revision for every completed request. It is not allowed to modify the same key several times within one txn. | @@ -805,6 +806,8 @@ Empty field. | kvs | kvs is the list of key-value pairs matched by the range request. kvs is empty when count is requested. | (slice of) mvccpb.KeyValue | | more | more indicates if there are more keys to return in the requested range. | bool | | count | count is set to the number of keys within the range when requested. | int64 | +| sub_count | | int64 | +| total_size | | int64 | diff --git a/Documentation/dev-guide/apispec/swagger/rpc.swagger.json b/Documentation/dev-guide/apispec/swagger/rpc.swagger.json index cea9c9d6ff0..bc739bbbcf6 100644 --- a/Documentation/dev-guide/apispec/swagger/rpc.swagger.json +++ b/Documentation/dev-guide/apispec/swagger/rpc.swagger.json @@ -968,6 +968,48 @@ } } }, + "/v3/kv/rangestream": { + "post": { + "tags": [ + "KV" + ], + "summary": "RangeStream gets the keys in the range stream from the key-value store.", + "operationId": "KV_RangeStream", + "parameters": [ + { + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/etcdserverpbRangeRequest" + } + } + ], + "responses": { + "200": { + "description": "A successful response.(streaming responses)", + "schema": { + "type": "object", + "title": "Stream result of etcdserverpbRangeResponse", + "properties": { + "error": { + "$ref": "#/definitions/runtimeStreamError" + }, + "result": { + "$ref": "#/definitions/etcdserverpbRangeResponse" + } + } + } + }, + "default": { + "description": "An unexpected error response", + "schema": { + "$ref": "#/definitions/runtimeError" + } + } + } + } + }, "/v3/kv/txn": { "post": { "tags": [ @@ -2632,6 +2674,14 @@ "description": "more indicates if there are more keys to return in the requested range.", "type": "boolean", "format": "boolean" + }, + "sub_count": { + "type": "string", + "format": "int64" + }, + "total_size": { + "type": "string", + "format": "int64" } } }, diff --git a/api/etcdserverpb/gw/rpc.pb.gw.go b/api/etcdserverpb/gw/rpc.pb.gw.go index 2fca126af85..ff01bbab244 100644 --- a/api/etcdserverpb/gw/rpc.pb.gw.go +++ b/api/etcdserverpb/gw/rpc.pb.gw.go @@ -66,6 +66,31 @@ func local_request_KV_Range_0(ctx context.Context, marshaler runtime.Marshaler, } +func request_KV_RangeStream_0(ctx context.Context, marshaler runtime.Marshaler, client etcdserverpb.KVClient, req *http.Request, pathParams map[string]string) (etcdserverpb.KV_RangeStreamClient, runtime.ServerMetadata, error) { + var protoReq etcdserverpb.RangeRequest + var metadata runtime.ServerMetadata + + newReader, berr := utilities.IOReaderFactory(req.Body) + if berr != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) + } + if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + stream, err := client.RangeStream(ctx, &protoReq) + if err != nil { + return nil, metadata, err + } + header, err := stream.Header() + if err != nil { + return nil, metadata, err + } + metadata.HeaderMD = header + return stream, metadata, nil + +} + func request_KV_Put_0(ctx context.Context, marshaler runtime.Marshaler, client etcdserverpb.KVClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { var protoReq etcdserverpb.PutRequest var metadata runtime.ServerMetadata @@ -1580,6 +1605,13 @@ func RegisterKVHandlerServer(ctx context.Context, mux *runtime.ServeMux, server }) + mux.Handle("POST", pattern_KV_RangeStream_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + err := status.Error(codes.Unimplemented, "streaming calls are not yet supported in the in-process transport") + _, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + }) + mux.Handle("POST", pattern_KV_Put_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() @@ -2502,6 +2534,26 @@ func RegisterKVHandlerClient(ctx context.Context, mux *runtime.ServeMux, client }) + mux.Handle("POST", pattern_KV_RangeStream_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_KV_RangeStream_0(rctx, inboundMarshaler, client, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_KV_RangeStream_0(ctx, mux, outboundMarshaler, w, req, func() (proto.Message, error) { return resp.Recv() }, mux.GetForwardResponseOptions()...) + + }) + mux.Handle("POST", pattern_KV_Put_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() @@ -2588,6 +2640,8 @@ func RegisterKVHandlerClient(ctx context.Context, mux *runtime.ServeMux, client var ( pattern_KV_Range_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"v3", "kv", "range"}, "", runtime.AssumeColonVerbOpt(true))) + pattern_KV_RangeStream_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"v3", "kv", "rangestream"}, "", runtime.AssumeColonVerbOpt(true))) + pattern_KV_Put_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"v3", "kv", "put"}, "", runtime.AssumeColonVerbOpt(true))) pattern_KV_DeleteRange_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"v3", "kv", "deleterange"}, "", runtime.AssumeColonVerbOpt(true))) @@ -2600,6 +2654,8 @@ var ( var ( forward_KV_Range_0 = runtime.ForwardResponseMessage + forward_KV_RangeStream_0 = runtime.ForwardResponseStream + forward_KV_Put_0 = runtime.ForwardResponseMessage forward_KV_DeleteRange_0 = runtime.ForwardResponseMessage diff --git a/api/etcdserverpb/rpc.pb.go b/api/etcdserverpb/rpc.pb.go index f0942040a09..a94262417a2 100644 --- a/api/etcdserverpb/rpc.pb.go +++ b/api/etcdserverpb/rpc.pb.go @@ -528,6 +528,8 @@ type RangeResponse struct { More bool `protobuf:"varint,3,opt,name=more,proto3" json:"more,omitempty"` // count is set to the number of keys within the range when requested. Count int64 `protobuf:"varint,4,opt,name=count,proto3" json:"count,omitempty"` + SubCount int64 `protobuf:"varint,101,opt,name=sub_count,json=subCount,proto3" json:"sub_count,omitempty"` + TotalSize int64 `protobuf:"varint,102,opt,name=total_size,json=totalSize,proto3" json:"total_size,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -594,6 +596,20 @@ func (m *RangeResponse) GetCount() int64 { return 0 } +func (m *RangeResponse) GetSubCount() int64 { + if m != nil { + return m.SubCount + } + return 0 +} + +func (m *RangeResponse) GetTotalSize() int64 { + if m != nil { + return m.TotalSize + } + return 0 +} + type PutRequest struct { // key is the key, in bytes, to put into the key-value store. Key []byte `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"` @@ -6154,264 +6170,268 @@ func init() { func init() { proto.RegisterFile("rpc.proto", fileDescriptor_77a6da22d6a3feb1) } var fileDescriptor_77a6da22d6a3feb1 = []byte{ - // 4107 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x5b, 0x5b, 0x73, 0x1b, 0xc9, - 0x75, 0xe6, 0x00, 0xc4, 0xed, 0xe0, 0x42, 0xb0, 0x79, 0x11, 0x84, 0x95, 0x28, 0x6e, 0x6b, 0xa5, - 0xe5, 0x4a, 0xbb, 0xc4, 0x9a, 0xb6, 0xb3, 0x55, 0x4a, 0xe2, 0x18, 0x22, 0xb1, 0x12, 0x97, 0x14, - 0xc9, 0x1d, 0x42, 0xda, 0x4b, 0xb9, 0xc2, 0x1a, 0x02, 0x2d, 0x72, 0x42, 0x60, 0x06, 0x9e, 0x19, - 0x40, 0xe4, 0xe6, 0xe2, 0x94, 0xcb, 0x71, 0x25, 0xaf, 0x76, 0x55, 0x2a, 0x79, 0x48, 0x5e, 0x52, - 0x29, 0x97, 0x1f, 0xfc, 0x9c, 0xbf, 0x90, 0xa7, 0x5c, 0x2a, 0x7f, 0x20, 0xb5, 0xf1, 0x4b, 0xf2, - 0x23, 0x52, 0xae, 0xbe, 0xcd, 0xf4, 0xdc, 0x40, 0xd9, 0xd8, 0xdd, 0x17, 0x11, 0x7d, 0xfa, 0xf4, - 0xf9, 0x4e, 0x9f, 0xee, 0x3e, 0xe7, 0xf4, 0xe9, 0x11, 0x94, 0x9c, 0x51, 0x6f, 0x73, 0xe4, 0xd8, - 0x9e, 0x8d, 0x2a, 0xc4, 0xeb, 0xf5, 0x5d, 0xe2, 0x4c, 0x88, 0x33, 0x3a, 0x6d, 0x2e, 0x9f, 0xd9, - 0x67, 0x36, 0xeb, 0x68, 0xd1, 0x5f, 0x9c, 0xa7, 0xd9, 0xa0, 0x3c, 0x2d, 0x63, 0x64, 0xb6, 0x86, - 0x93, 0x5e, 0x6f, 0x74, 0xda, 0xba, 0x98, 0x88, 0x9e, 0xa6, 0xdf, 0x63, 0x8c, 0xbd, 0xf3, 0xd1, - 0x29, 0xfb, 0x23, 0xfa, 0x6e, 0x9d, 0xd9, 0xf6, 0xd9, 0x80, 0xf0, 0x5e, 0xcb, 0xb2, 0x3d, 0xc3, - 0x33, 0x6d, 0xcb, 0xe5, 0xbd, 0xf8, 0xaf, 0x34, 0xa8, 0xe9, 0xc4, 0x1d, 0xd9, 0x96, 0x4b, 0x9e, - 0x12, 0xa3, 0x4f, 0x1c, 0x74, 0x1b, 0xa0, 0x37, 0x18, 0xbb, 0x1e, 0x71, 0x4e, 0xcc, 0x7e, 0x43, - 0x5b, 0xd7, 0x36, 0xe6, 0xf5, 0x92, 0xa0, 0xec, 0xf6, 0xd1, 0x1b, 0x50, 0x1a, 0x92, 0xe1, 0x29, - 0xef, 0xcd, 0xb0, 0xde, 0x22, 0x27, 0xec, 0xf6, 0x51, 0x13, 0x8a, 0x0e, 0x99, 0x98, 0xae, 0x69, - 0x5b, 0x8d, 0xec, 0xba, 0xb6, 0x91, 0xd5, 0xfd, 0x36, 0x1d, 0xe8, 0x18, 0x2f, 0xbd, 0x13, 0x8f, - 0x38, 0xc3, 0xc6, 0x3c, 0x1f, 0x48, 0x09, 0x5d, 0xe2, 0x0c, 0xf1, 0x4f, 0x72, 0x50, 0xd1, 0x0d, - 0xeb, 0x8c, 0xe8, 0xe4, 0x87, 0x63, 0xe2, 0x7a, 0xa8, 0x0e, 0xd9, 0x0b, 0x72, 0xc5, 0xe0, 0x2b, - 0x3a, 0xfd, 0xc9, 0xc7, 0x5b, 0x67, 0xe4, 0x84, 0x58, 0x1c, 0xb8, 0x42, 0xc7, 0x5b, 0x67, 0xa4, - 0x63, 0xf5, 0xd1, 0x32, 0xe4, 0x06, 0xe6, 0xd0, 0xf4, 0x04, 0x2a, 0x6f, 0x84, 0xd4, 0x99, 0x8f, - 0xa8, 0xb3, 0x0d, 0xe0, 0xda, 0x8e, 0x77, 0x62, 0x3b, 0x7d, 0xe2, 0x34, 0x72, 0xeb, 0xda, 0x46, - 0x6d, 0xeb, 0xad, 0x4d, 0x75, 0x19, 0x36, 0x55, 0x85, 0x36, 0x8f, 0x6d, 0xc7, 0x3b, 0xa4, 0xbc, - 0x7a, 0xc9, 0x95, 0x3f, 0xd1, 0x87, 0x50, 0x66, 0x42, 0x3c, 0xc3, 0x39, 0x23, 0x5e, 0x23, 0xcf, - 0xa4, 0xdc, 0xbb, 0x46, 0x4a, 0x97, 0x31, 0xeb, 0x0c, 0x9e, 0xff, 0x46, 0x18, 0x2a, 0x2e, 0x71, - 0x4c, 0x63, 0x60, 0x7e, 0x61, 0x9c, 0x0e, 0x48, 0xa3, 0xb0, 0xae, 0x6d, 0x14, 0xf5, 0x10, 0x8d, - 0xce, 0xff, 0x82, 0x5c, 0xb9, 0x27, 0xb6, 0x35, 0xb8, 0x6a, 0x14, 0x19, 0x43, 0x91, 0x12, 0x0e, - 0xad, 0xc1, 0x15, 0x5b, 0x34, 0x7b, 0x6c, 0x79, 0xbc, 0xb7, 0xc4, 0x7a, 0x4b, 0x8c, 0xc2, 0xba, - 0x37, 0xa0, 0x3e, 0x34, 0xad, 0x93, 0xa1, 0xdd, 0x3f, 0xf1, 0x0d, 0x02, 0xcc, 0x20, 0xb5, 0xa1, - 0x69, 0x3d, 0xb3, 0xfb, 0xba, 0x34, 0x0b, 0xe5, 0x34, 0x2e, 0xc3, 0x9c, 0x65, 0xc1, 0x69, 0x5c, - 0xaa, 0x9c, 0x9b, 0xb0, 0x44, 0x65, 0xf6, 0x1c, 0x62, 0x78, 0x24, 0x60, 0xae, 0x30, 0xe6, 0xc5, - 0xa1, 0x69, 0x6d, 0xb3, 0x9e, 0x10, 0xbf, 0x71, 0x19, 0xe3, 0xaf, 0x0a, 0x7e, 0xe3, 0x32, 0xcc, - 0x8f, 0x37, 0xa1, 0xe4, 0xdb, 0x1c, 0x15, 0x61, 0xfe, 0xe0, 0xf0, 0xa0, 0x53, 0x9f, 0x43, 0x00, - 0xf9, 0xf6, 0xf1, 0x76, 0xe7, 0x60, 0xa7, 0xae, 0xa1, 0x32, 0x14, 0x76, 0x3a, 0xbc, 0x91, 0xc1, - 0x8f, 0x01, 0x02, 0xeb, 0xa2, 0x02, 0x64, 0xf7, 0x3a, 0x9f, 0xd5, 0xe7, 0x28, 0xcf, 0x8b, 0x8e, - 0x7e, 0xbc, 0x7b, 0x78, 0x50, 0xd7, 0xe8, 0xe0, 0x6d, 0xbd, 0xd3, 0xee, 0x76, 0xea, 0x19, 0xca, - 0xf1, 0xec, 0x70, 0xa7, 0x9e, 0x45, 0x25, 0xc8, 0xbd, 0x68, 0xef, 0x3f, 0xef, 0xd4, 0xe7, 0xf1, - 0xcf, 0x35, 0xa8, 0x8a, 0xf5, 0xe2, 0x67, 0x02, 0x7d, 0x07, 0xf2, 0xe7, 0xec, 0x5c, 0xb0, 0xad, - 0x58, 0xde, 0xba, 0x15, 0x59, 0xdc, 0xd0, 0xd9, 0xd1, 0x05, 0x2f, 0xc2, 0x90, 0xbd, 0x98, 0xb8, - 0x8d, 0xcc, 0x7a, 0x76, 0xa3, 0xbc, 0x55, 0xdf, 0xe4, 0xe7, 0x75, 0x73, 0x8f, 0x5c, 0xbd, 0x30, - 0x06, 0x63, 0xa2, 0xd3, 0x4e, 0x84, 0x60, 0x7e, 0x68, 0x3b, 0x84, 0xed, 0xd8, 0xa2, 0xce, 0x7e, - 0xd3, 0x6d, 0xcc, 0x16, 0x4d, 0xec, 0x56, 0xde, 0xc0, 0xbf, 0xd4, 0x00, 0x8e, 0xc6, 0x5e, 0xfa, - 0xd1, 0x58, 0x86, 0xdc, 0x84, 0x0a, 0x16, 0xc7, 0x82, 0x37, 0xd8, 0x99, 0x20, 0x86, 0x4b, 0xfc, - 0x33, 0x41, 0x1b, 0xe8, 0x06, 0x14, 0x46, 0x0e, 0x99, 0x9c, 0x5c, 0x4c, 0x18, 0x48, 0x51, 0xcf, - 0xd3, 0xe6, 0xde, 0x04, 0xbd, 0x09, 0x15, 0xf3, 0xcc, 0xb2, 0x1d, 0x72, 0xc2, 0x65, 0xe5, 0x58, - 0x6f, 0x99, 0xd3, 0x98, 0xde, 0x0a, 0x0b, 0x17, 0x9c, 0x57, 0x59, 0xf6, 0x29, 0x09, 0x5b, 0x50, - 0x66, 0xaa, 0xce, 0x64, 0xbe, 0x77, 0x02, 0x1d, 0x33, 0x6c, 0x58, 0xdc, 0x84, 0x42, 0x6b, 0xfc, - 0x03, 0x40, 0x3b, 0x64, 0x40, 0x3c, 0x32, 0x8b, 0xf7, 0x50, 0x6c, 0x92, 0x55, 0x6d, 0x82, 0x7f, - 0xa6, 0xc1, 0x52, 0x48, 0xfc, 0x4c, 0xd3, 0x6a, 0x40, 0xa1, 0xcf, 0x84, 0x71, 0x0d, 0xb2, 0xba, - 0x6c, 0xa2, 0x87, 0x50, 0x14, 0x0a, 0xb8, 0x8d, 0x6c, 0xca, 0xa6, 0x29, 0x70, 0x9d, 0x5c, 0xfc, - 0xcb, 0x0c, 0x94, 0xc4, 0x44, 0x0f, 0x47, 0xa8, 0x0d, 0x55, 0x87, 0x37, 0x4e, 0xd8, 0x7c, 0x84, - 0x46, 0xcd, 0x74, 0x27, 0xf4, 0x74, 0x4e, 0xaf, 0x88, 0x21, 0x8c, 0x8c, 0x7e, 0x1f, 0xca, 0x52, - 0xc4, 0x68, 0xec, 0x09, 0x93, 0x37, 0xc2, 0x02, 0x82, 0xfd, 0xf7, 0x74, 0x4e, 0x07, 0xc1, 0x7e, - 0x34, 0xf6, 0x50, 0x17, 0x96, 0xe5, 0x60, 0x3e, 0x1b, 0xa1, 0x46, 0x96, 0x49, 0x59, 0x0f, 0x4b, - 0x89, 0x2f, 0xd5, 0xd3, 0x39, 0x1d, 0x89, 0xf1, 0x4a, 0xa7, 0xaa, 0x92, 0x77, 0xc9, 0x9d, 0x77, - 0x4c, 0xa5, 0xee, 0xa5, 0x15, 0x57, 0xa9, 0x7b, 0x69, 0x3d, 0x2e, 0x41, 0x41, 0xb4, 0xf0, 0xbf, - 0x64, 0x00, 0xe4, 0x6a, 0x1c, 0x8e, 0xd0, 0x0e, 0xd4, 0x1c, 0xd1, 0x0a, 0x59, 0xeb, 0x8d, 0x44, - 0x6b, 0x89, 0x45, 0x9c, 0xd3, 0xab, 0x72, 0x10, 0x57, 0xee, 0x7b, 0x50, 0xf1, 0xa5, 0x04, 0x06, - 0xbb, 0x99, 0x60, 0x30, 0x5f, 0x42, 0x59, 0x0e, 0xa0, 0x26, 0xfb, 0x04, 0x56, 0xfc, 0xf1, 0x09, - 0x36, 0x7b, 0x73, 0x8a, 0xcd, 0x7c, 0x81, 0x4b, 0x52, 0x82, 0x6a, 0x35, 0x55, 0xb1, 0xc0, 0x6c, - 0x37, 0x13, 0xcc, 0x16, 0x57, 0x8c, 0x1a, 0x0e, 0x68, 0xbc, 0xe4, 0x4d, 0xfc, 0xbf, 0x59, 0x28, - 0x6c, 0xdb, 0xc3, 0x91, 0xe1, 0xd0, 0xd5, 0xc8, 0x3b, 0xc4, 0x1d, 0x0f, 0x3c, 0x66, 0xae, 0xda, - 0xd6, 0xdd, 0xb0, 0x44, 0xc1, 0x26, 0xff, 0xea, 0x8c, 0x55, 0x17, 0x43, 0xe8, 0x60, 0x11, 0x1e, - 0x33, 0xaf, 0x31, 0x58, 0x04, 0x47, 0x31, 0x44, 0x1e, 0xe4, 0x6c, 0x70, 0x90, 0x9b, 0x50, 0x98, - 0x10, 0x27, 0x08, 0xe9, 0x4f, 0xe7, 0x74, 0x49, 0x40, 0xef, 0xc0, 0x42, 0x34, 0xbc, 0xe4, 0x04, - 0x4f, 0xad, 0x17, 0x8e, 0x46, 0x77, 0xa1, 0x12, 0x8a, 0x71, 0x79, 0xc1, 0x57, 0x1e, 0x2a, 0x21, - 0x6e, 0x55, 0xfa, 0x55, 0x1a, 0x8f, 0x2b, 0x4f, 0xe7, 0xa4, 0x67, 0x5d, 0x95, 0x9e, 0xb5, 0x28, - 0x46, 0x09, 0xdf, 0x1a, 0x72, 0x32, 0xdf, 0x0f, 0x3b, 0x19, 0xfc, 0x7d, 0xa8, 0x86, 0x0c, 0x44, - 0xe3, 0x4e, 0xe7, 0xe3, 0xe7, 0xed, 0x7d, 0x1e, 0xa4, 0x9e, 0xb0, 0xb8, 0xa4, 0xd7, 0x35, 0x1a, - 0xeb, 0xf6, 0x3b, 0xc7, 0xc7, 0xf5, 0x0c, 0xaa, 0x42, 0xe9, 0xe0, 0xb0, 0x7b, 0xc2, 0xb9, 0xb2, - 0xf8, 0x89, 0x2f, 0x41, 0x04, 0x39, 0x25, 0xb6, 0xcd, 0x29, 0xb1, 0x4d, 0x93, 0xb1, 0x2d, 0x13, - 0xc4, 0x36, 0x16, 0xe6, 0xf6, 0x3b, 0xed, 0xe3, 0x4e, 0x7d, 0xfe, 0x71, 0x0d, 0x2a, 0xdc, 0xbe, - 0x27, 0x63, 0x8b, 0x86, 0xda, 0x7f, 0xd2, 0x00, 0x82, 0xd3, 0x84, 0x5a, 0x50, 0xe8, 0x71, 0x9c, - 0x86, 0xc6, 0x9c, 0xd1, 0x4a, 0xe2, 0x92, 0xe9, 0x92, 0x0b, 0x7d, 0x0b, 0x0a, 0xee, 0xb8, 0xd7, - 0x23, 0xae, 0x0c, 0x79, 0x37, 0xa2, 0xfe, 0x50, 0x78, 0x2b, 0x5d, 0xf2, 0xd1, 0x21, 0x2f, 0x0d, - 0x73, 0x30, 0x66, 0x01, 0x70, 0xfa, 0x10, 0xc1, 0x87, 0xff, 0x5e, 0x83, 0xb2, 0xb2, 0x79, 0x7f, - 0x47, 0x27, 0x7c, 0x0b, 0x4a, 0x4c, 0x07, 0xd2, 0x17, 0x6e, 0xb8, 0xa8, 0x07, 0x04, 0xf4, 0x7b, - 0x50, 0x92, 0x27, 0x40, 0x7a, 0xe2, 0x46, 0xb2, 0xd8, 0xc3, 0x91, 0x1e, 0xb0, 0xe2, 0x3d, 0x58, - 0x64, 0x56, 0xe9, 0xd1, 0xe4, 0x5a, 0xda, 0x51, 0x4d, 0x3f, 0xb5, 0x48, 0xfa, 0xd9, 0x84, 0xe2, - 0xe8, 0xfc, 0xca, 0x35, 0x7b, 0xc6, 0x40, 0x68, 0xe1, 0xb7, 0xf1, 0x47, 0x80, 0x54, 0x61, 0xb3, - 0x4c, 0x17, 0x57, 0xa1, 0xfc, 0xd4, 0x70, 0xcf, 0x85, 0x4a, 0xf8, 0x21, 0x54, 0x69, 0x73, 0xef, - 0xc5, 0x6b, 0xe8, 0xc8, 0x2e, 0x07, 0x92, 0x7b, 0x26, 0x9b, 0x23, 0x98, 0x3f, 0x37, 0xdc, 0x73, - 0x36, 0xd1, 0xaa, 0xce, 0x7e, 0xa3, 0x77, 0xa0, 0xde, 0xe3, 0x93, 0x3c, 0x89, 0x5c, 0x19, 0x16, - 0x04, 0xdd, 0xcf, 0x04, 0x3f, 0x85, 0x0a, 0x9f, 0xc3, 0x57, 0xad, 0x04, 0x5e, 0x84, 0x85, 0x63, - 0xcb, 0x18, 0xb9, 0xe7, 0xb6, 0x8c, 0x6e, 0x74, 0xd2, 0xf5, 0x80, 0x36, 0x13, 0xe2, 0xdb, 0xb0, - 0xe0, 0x90, 0xa1, 0x61, 0x5a, 0xa6, 0x75, 0x76, 0x72, 0x7a, 0xe5, 0x11, 0x57, 0x5c, 0x98, 0x6a, - 0x3e, 0xf9, 0x31, 0xa5, 0x52, 0xd5, 0x4e, 0x07, 0xf6, 0xa9, 0x70, 0x73, 0xec, 0x37, 0xfe, 0x69, - 0x06, 0x2a, 0x9f, 0x18, 0x5e, 0x4f, 0x2e, 0x1d, 0xda, 0x85, 0x9a, 0xef, 0xdc, 0x18, 0x45, 0xe8, - 0x12, 0x09, 0xb1, 0x6c, 0x8c, 0x4c, 0xa5, 0x65, 0x74, 0xac, 0xf6, 0x54, 0x02, 0x13, 0x65, 0x58, - 0x3d, 0x32, 0xf0, 0x45, 0x65, 0xd2, 0x45, 0x31, 0x46, 0x55, 0x94, 0x4a, 0x40, 0x87, 0x50, 0x1f, - 0x39, 0xf6, 0x99, 0x43, 0x5c, 0xd7, 0x17, 0xc6, 0xc3, 0x18, 0x4e, 0x10, 0x76, 0x24, 0x58, 0x03, - 0x71, 0x0b, 0xa3, 0x30, 0xe9, 0xf1, 0x42, 0x90, 0xcf, 0x70, 0xe7, 0xf4, 0x9f, 0x19, 0x40, 0xf1, - 0x49, 0xfd, 0xb6, 0x29, 0xde, 0x3d, 0xa8, 0xb9, 0x9e, 0xe1, 0xc4, 0x36, 0x5b, 0x95, 0x51, 0x7d, - 0x8f, 0xff, 0x36, 0xf8, 0x0a, 0x9d, 0x58, 0xb6, 0x67, 0xbe, 0xbc, 0x12, 0x59, 0x72, 0x4d, 0x92, - 0x0f, 0x18, 0x15, 0x75, 0xa0, 0xf0, 0xd2, 0x1c, 0x78, 0xc4, 0x71, 0x1b, 0xb9, 0xf5, 0xec, 0x46, - 0x6d, 0xeb, 0xe1, 0x75, 0xcb, 0xb0, 0xf9, 0x21, 0xe3, 0xef, 0x5e, 0x8d, 0x88, 0x2e, 0xc7, 0xaa, - 0x99, 0x67, 0x3e, 0x94, 0x8d, 0xdf, 0x84, 0xe2, 0x2b, 0x2a, 0x82, 0xde, 0xb2, 0x0b, 0x3c, 0x59, - 0x64, 0x6d, 0x7e, 0xc9, 0x7e, 0xe9, 0x18, 0x67, 0x43, 0x62, 0x79, 0xf2, 0x1e, 0x28, 0xdb, 0xf8, - 0x1e, 0x40, 0x00, 0x43, 0x5d, 0xfe, 0xc1, 0xe1, 0xd1, 0xf3, 0x6e, 0x7d, 0x0e, 0x55, 0xa0, 0x78, - 0x70, 0xb8, 0xd3, 0xd9, 0xef, 0xd0, 0xf8, 0x80, 0x5b, 0xd2, 0xa4, 0xa1, 0xb5, 0x54, 0x31, 0xb5, - 0x10, 0x26, 0x5e, 0x85, 0xe5, 0xa4, 0x05, 0xa4, 0xb9, 0x68, 0x55, 0xec, 0xd2, 0x99, 0x8e, 0x8a, - 0x0a, 0x9d, 0x09, 0x4f, 0xb7, 0x01, 0x05, 0xbe, 0x7b, 0xfb, 0x22, 0x39, 0x97, 0x4d, 0x6a, 0x08, - 0xbe, 0x19, 0x49, 0x5f, 0xac, 0x92, 0xdf, 0x4e, 0x74, 0x2f, 0xb9, 0x44, 0xf7, 0x82, 0xee, 0x42, - 0xd5, 0x3f, 0x0d, 0x86, 0x2b, 0x72, 0x81, 0x92, 0x5e, 0x91, 0x1b, 0x9d, 0xd2, 0x42, 0x46, 0x2f, - 0x84, 0x8d, 0x8e, 0xee, 0x41, 0x9e, 0x4c, 0x88, 0xe5, 0xb9, 0x8d, 0x32, 0x8b, 0x18, 0x55, 0x99, - 0xbb, 0x77, 0x28, 0x55, 0x17, 0x9d, 0xf8, 0xbb, 0xb0, 0xc8, 0xee, 0x48, 0x4f, 0x1c, 0xc3, 0x52, - 0x2f, 0x73, 0xdd, 0xee, 0xbe, 0x30, 0x37, 0xfd, 0x89, 0x6a, 0x90, 0xd9, 0xdd, 0x11, 0x46, 0xc8, - 0xec, 0xee, 0xe0, 0x1f, 0x6b, 0x80, 0xd4, 0x71, 0x33, 0xd9, 0x39, 0x22, 0x5c, 0xc2, 0x67, 0x03, - 0xf8, 0x65, 0xc8, 0x11, 0xc7, 0xb1, 0x1d, 0x66, 0xd1, 0x92, 0xce, 0x1b, 0xf8, 0x2d, 0xa1, 0x83, - 0x4e, 0x26, 0xf6, 0x85, 0x7f, 0x06, 0xb9, 0x34, 0xcd, 0x57, 0x75, 0x0f, 0x96, 0x42, 0x5c, 0x33, - 0x45, 0xae, 0x0f, 0x61, 0x81, 0x09, 0xdb, 0x3e, 0x27, 0xbd, 0x8b, 0x91, 0x6d, 0x5a, 0x31, 0x3c, - 0xba, 0x72, 0x81, 0x83, 0xa5, 0xf3, 0xe0, 0x13, 0xab, 0xf8, 0xc4, 0x6e, 0x77, 0x1f, 0x7f, 0x06, - 0xab, 0x11, 0x39, 0x52, 0xfd, 0x3f, 0x82, 0x72, 0xcf, 0x27, 0xba, 0x22, 0xd7, 0xb9, 0x1d, 0x56, - 0x2e, 0x3a, 0x54, 0x1d, 0x81, 0x0f, 0xe1, 0x46, 0x4c, 0xf4, 0x4c, 0x73, 0x7e, 0x1b, 0x56, 0x98, - 0xc0, 0x3d, 0x42, 0x46, 0xed, 0x81, 0x39, 0x49, 0xb5, 0xf4, 0x48, 0x4c, 0x4a, 0x61, 0xfc, 0x7a, - 0xf7, 0x05, 0xfe, 0x03, 0x81, 0xd8, 0x35, 0x87, 0xa4, 0x6b, 0xef, 0xa7, 0xeb, 0x46, 0xa3, 0xd9, - 0x05, 0xb9, 0x72, 0x45, 0x5a, 0xc3, 0x7e, 0xe3, 0x7f, 0xd6, 0x84, 0xa9, 0xd4, 0xe1, 0x5f, 0xf3, - 0x4e, 0x5e, 0x03, 0x38, 0xa3, 0x47, 0x86, 0xf4, 0x69, 0x07, 0xaf, 0xa8, 0x28, 0x14, 0x5f, 0x4f, - 0xea, 0xbf, 0x2b, 0x42, 0xcf, 0x65, 0xb1, 0xcf, 0xd9, 0x3f, 0xbe, 0x97, 0xbb, 0x0d, 0x65, 0x46, - 0x38, 0xf6, 0x0c, 0x6f, 0xec, 0xc6, 0x16, 0xe3, 0x2f, 0xc4, 0xb6, 0x97, 0x83, 0x66, 0x9a, 0xd7, - 0xb7, 0x20, 0xcf, 0x2e, 0x13, 0x32, 0x95, 0xbe, 0x99, 0xb0, 0x1f, 0xb9, 0x1e, 0xba, 0x60, 0xc4, - 0x3f, 0xd5, 0x20, 0xff, 0x8c, 0x95, 0x60, 0x15, 0xd5, 0xe6, 0xe5, 0x5a, 0x58, 0xc6, 0x90, 0x17, - 0x86, 0x4a, 0x3a, 0xfb, 0xcd, 0x52, 0x4f, 0x42, 0x9c, 0xe7, 0xfa, 0x3e, 0x4f, 0x71, 0x4b, 0xba, - 0xdf, 0xa6, 0x36, 0xeb, 0x0d, 0x4c, 0x62, 0x79, 0xac, 0x77, 0x9e, 0xf5, 0x2a, 0x14, 0x9a, 0x3d, - 0x9b, 0xee, 0x3e, 0x31, 0x1c, 0x4b, 0x14, 0x4d, 0x8b, 0x7a, 0x40, 0xc0, 0xfb, 0x50, 0xe7, 0x7a, - 0xb4, 0xfb, 0x7d, 0x25, 0xc1, 0xf4, 0xd1, 0xb4, 0x08, 0x5a, 0x48, 0x5a, 0x26, 0x2a, 0xed, 0x17, - 0x1a, 0x2c, 0x2a, 0xe2, 0x66, 0xb2, 0xea, 0xbb, 0x90, 0xe7, 0x45, 0x6a, 0x91, 0xe9, 0x2c, 0x87, - 0x47, 0x71, 0x18, 0x5d, 0xf0, 0xa0, 0x4d, 0x28, 0xf0, 0x5f, 0xf2, 0x0e, 0x90, 0xcc, 0x2e, 0x99, - 0xf0, 0x3d, 0x58, 0x12, 0x24, 0x32, 0xb4, 0x93, 0x0e, 0x06, 0x5b, 0x0c, 0xfc, 0x67, 0xb0, 0x1c, - 0x66, 0x9b, 0x69, 0x4a, 0x8a, 0x92, 0x99, 0xd7, 0x51, 0xb2, 0x2d, 0x95, 0x7c, 0x3e, 0xea, 0x2b, - 0x79, 0x54, 0x74, 0xc7, 0xa8, 0xeb, 0x95, 0x09, 0xaf, 0x57, 0x30, 0x01, 0x29, 0xe2, 0x1b, 0x9d, - 0xc0, 0x07, 0x72, 0x3b, 0xec, 0x9b, 0xae, 0xef, 0xc3, 0x31, 0x54, 0x06, 0xa6, 0x45, 0x0c, 0x47, - 0x54, 0xce, 0x35, 0x5e, 0x39, 0x57, 0x69, 0xf8, 0x0b, 0x40, 0xea, 0xc0, 0x6f, 0x54, 0xe9, 0xfb, - 0xd2, 0x64, 0x47, 0x8e, 0x3d, 0xb4, 0x53, 0xcd, 0x8e, 0xff, 0x1c, 0x56, 0x22, 0x7c, 0xdf, 0xa8, - 0x9a, 0x4b, 0xb0, 0xb8, 0x43, 0x64, 0x42, 0x23, 0xdd, 0xde, 0x47, 0x80, 0x54, 0xe2, 0x4c, 0x91, - 0xad, 0x05, 0x8b, 0xcf, 0xec, 0x09, 0x75, 0x91, 0x94, 0x1a, 0xf8, 0x06, 0x5e, 0x87, 0xf0, 0x4d, - 0xe1, 0xb7, 0x29, 0xb8, 0x3a, 0x60, 0x26, 0xf0, 0x7f, 0xd7, 0xa0, 0xd2, 0x1e, 0x18, 0xce, 0x50, - 0x02, 0x7f, 0x0f, 0xf2, 0xfc, 0x76, 0x2d, 0x0a, 0x5a, 0xf7, 0xc3, 0x62, 0x54, 0x5e, 0xde, 0x68, - 0xf3, 0xbb, 0xb8, 0x18, 0x45, 0x15, 0x17, 0x6f, 0x5e, 0x3b, 0x91, 0x37, 0xb0, 0x1d, 0xf4, 0x1e, - 0xe4, 0x0c, 0x3a, 0x84, 0x85, 0xa2, 0x5a, 0xb4, 0xae, 0xc1, 0xa4, 0xb1, 0x3b, 0x00, 0xe7, 0xc2, - 0xdf, 0x81, 0xb2, 0x82, 0x80, 0x0a, 0x90, 0x7d, 0xd2, 0x11, 0x09, 0x7b, 0x7b, 0xbb, 0xbb, 0xfb, - 0x82, 0x17, 0x74, 0x6a, 0x00, 0x3b, 0x1d, 0xbf, 0x9d, 0xc1, 0x9f, 0x8a, 0x51, 0xc2, 0xed, 0xab, - 0xfa, 0x68, 0x69, 0xfa, 0x64, 0x5e, 0x4b, 0x9f, 0x4b, 0xa8, 0x8a, 0xe9, 0xcf, 0x1a, 0xc6, 0x98, - 0xbc, 0x94, 0x30, 0xa6, 0x28, 0xaf, 0x0b, 0x46, 0xfc, 0x2b, 0x0d, 0xea, 0x3b, 0xf6, 0x2b, 0xeb, - 0xcc, 0x31, 0xfa, 0xfe, 0x39, 0xf9, 0x30, 0xb2, 0x52, 0x9b, 0x91, 0xe2, 0x68, 0x84, 0x3f, 0x20, - 0x44, 0x56, 0xac, 0x11, 0x94, 0x0d, 0x79, 0x2c, 0x94, 0x4d, 0xfc, 0x01, 0x2c, 0x44, 0x06, 0x51, - 0xdb, 0xbf, 0x68, 0xef, 0xef, 0xee, 0x50, 0x5b, 0xb3, 0xc2, 0x5a, 0xe7, 0xa0, 0xfd, 0x78, 0xbf, - 0x23, 0x1e, 0x90, 0xda, 0x07, 0xdb, 0x9d, 0xfd, 0x7a, 0x06, 0xf7, 0x60, 0x51, 0x81, 0x9f, 0xf5, - 0x65, 0x20, 0x45, 0xbb, 0x05, 0xa8, 0x8a, 0x68, 0x2f, 0x0e, 0xe5, 0xbf, 0x65, 0xa0, 0x26, 0x29, - 0x5f, 0x0f, 0x26, 0x5a, 0x85, 0x7c, 0xff, 0xf4, 0xd8, 0xfc, 0x42, 0xbe, 0x1c, 0x89, 0x16, 0xa5, - 0x0f, 0x38, 0x0e, 0x7f, 0xbe, 0x15, 0x2d, 0x1a, 0xc6, 0x1d, 0xe3, 0xa5, 0xb7, 0x6b, 0xf5, 0xc9, - 0x25, 0x4b, 0x0a, 0xe6, 0xf5, 0x80, 0xc0, 0x2a, 0x4c, 0xe2, 0x99, 0x97, 0xdd, 0xac, 0x94, 0x67, - 0x5f, 0xf4, 0x00, 0xea, 0xf4, 0x77, 0x7b, 0x34, 0x1a, 0x98, 0xa4, 0xcf, 0x05, 0x14, 0x18, 0x4f, - 0x8c, 0x4e, 0xd1, 0xd9, 0x5d, 0xc4, 0x6d, 0x14, 0x59, 0x58, 0x12, 0x2d, 0xb4, 0x0e, 0x65, 0xae, - 0xdf, 0xae, 0xf5, 0xdc, 0x25, 0xec, 0xed, 0x33, 0xab, 0xab, 0xa4, 0x70, 0x9a, 0x01, 0xd1, 0x34, - 0x63, 0x09, 0x16, 0xdb, 0x63, 0xef, 0xbc, 0x63, 0xd1, 0x58, 0x21, 0xad, 0xbc, 0x0c, 0x88, 0x12, - 0x77, 0x4c, 0x57, 0xa5, 0x0a, 0xd6, 0xf0, 0x82, 0x74, 0x60, 0x89, 0x12, 0x89, 0xe5, 0x99, 0x3d, - 0x25, 0xae, 0xca, 0xcc, 0x4b, 0x8b, 0x64, 0x5e, 0x86, 0xeb, 0xbe, 0xb2, 0x9d, 0xbe, 0xb0, 0xb9, - 0xdf, 0xc6, 0xff, 0xa8, 0x71, 0xc8, 0xe7, 0x6e, 0x28, 0x7d, 0xfa, 0x2d, 0xc5, 0xa0, 0xf7, 0xa1, - 0x60, 0x8f, 0xd8, 0x0b, 0xbf, 0x28, 0xc3, 0xac, 0x6e, 0xf2, 0x6f, 0x02, 0x36, 0x85, 0xe0, 0x43, - 0xde, 0xab, 0x4b, 0x36, 0x74, 0x1f, 0x6a, 0xe7, 0x86, 0x7b, 0x4e, 0xfa, 0x47, 0x52, 0x26, 0xbf, - 0xf9, 0x45, 0xa8, 0x78, 0x23, 0xd0, 0xef, 0x09, 0xf1, 0xa6, 0xe8, 0x87, 0x1f, 0xc2, 0x8a, 0xe4, - 0x14, 0xaf, 0x13, 0x53, 0x98, 0x5f, 0xc1, 0x6d, 0xc9, 0xbc, 0x7d, 0x6e, 0x58, 0x67, 0x44, 0x02, - 0xfe, 0xae, 0x16, 0x88, 0xcf, 0x27, 0x9b, 0x38, 0x9f, 0xc7, 0xd0, 0xf0, 0xe7, 0xc3, 0x6e, 0xd6, - 0xf6, 0x40, 0x55, 0x74, 0xec, 0x8a, 0xf3, 0x54, 0xd2, 0xd9, 0x6f, 0x4a, 0x73, 0xec, 0x81, 0x9f, - 0x4a, 0xd3, 0xdf, 0x78, 0x1b, 0x6e, 0x4a, 0x19, 0xe2, 0xce, 0x1b, 0x16, 0x12, 0x53, 0x3c, 0x49, - 0x88, 0x30, 0x2c, 0x1d, 0x3a, 0x7d, 0xe1, 0x55, 0xce, 0xf0, 0x12, 0x30, 0x99, 0x9a, 0x22, 0x73, - 0x85, 0x6f, 0x4a, 0xaa, 0x98, 0x92, 0x2d, 0x49, 0x32, 0x15, 0xa0, 0x92, 0xc5, 0x82, 0x51, 0x72, - 0x6c, 0xc1, 0x62, 0xa2, 0x7f, 0x00, 0x6b, 0xbe, 0x12, 0xd4, 0x6e, 0x47, 0xc4, 0x19, 0x9a, 0xae, - 0xab, 0xd4, 0xbd, 0x93, 0x26, 0x7e, 0x1f, 0xe6, 0x47, 0x44, 0x04, 0xa1, 0xf2, 0x16, 0x92, 0x9b, - 0x52, 0x19, 0xcc, 0xfa, 0x71, 0x1f, 0xee, 0x48, 0xe9, 0xdc, 0xa2, 0x89, 0xe2, 0xa3, 0x4a, 0xc9, - 0x6a, 0x60, 0x26, 0xa5, 0x1a, 0x98, 0x8d, 0xbc, 0xc5, 0x7c, 0xc4, 0x0d, 0x29, 0xcf, 0xfc, 0x4c, - 0xc9, 0xc5, 0x1e, 0xb7, 0xa9, 0xef, 0x2a, 0x66, 0x12, 0xf6, 0xd7, 0xc2, 0x0b, 0x7c, 0x55, 0x1e, - 0x9e, 0xb0, 0x19, 0xca, 0x87, 0x0e, 0xd9, 0xa4, 0x59, 0x33, 0x5d, 0x00, 0x5d, 0xad, 0x85, 0xce, - 0xeb, 0x21, 0x1a, 0x3e, 0x85, 0xe5, 0xb0, 0x5f, 0x9b, 0x49, 0x97, 0x65, 0xc8, 0x79, 0xf6, 0x05, - 0x91, 0xb1, 0x86, 0x37, 0xa4, 0xed, 0x7c, 0x9f, 0x37, 0x93, 0xed, 0x8c, 0x40, 0x18, 0x3b, 0x1d, - 0xb3, 0xea, 0x4b, 0x37, 0x96, 0xbc, 0x03, 0xf1, 0x06, 0x3e, 0x80, 0xd5, 0xa8, 0x67, 0x9b, 0x49, - 0xe5, 0x17, 0xfc, 0x2c, 0x25, 0x39, 0xbf, 0x99, 0xe4, 0x7e, 0x1c, 0xf8, 0x25, 0xc5, 0xb7, 0xcd, - 0x24, 0x52, 0x87, 0x66, 0x92, 0xab, 0xfb, 0x2a, 0x8e, 0x8e, 0xef, 0xf9, 0x66, 0x12, 0xe6, 0x06, - 0xc2, 0x66, 0x5f, 0xfe, 0xc0, 0x5d, 0x65, 0xa7, 0xba, 0x2b, 0x71, 0x48, 0x02, 0x87, 0xfa, 0x35, - 0x6c, 0x3a, 0x81, 0x11, 0xf8, 0xf2, 0x59, 0x31, 0x68, 0x38, 0xf3, 0x31, 0x58, 0x43, 0x6e, 0x6c, - 0x35, 0x02, 0xcc, 0xb4, 0x18, 0x9f, 0x04, 0x6e, 0x3c, 0x16, 0x24, 0x66, 0x12, 0xfc, 0x29, 0xac, - 0xa7, 0xc7, 0x87, 0x59, 0x24, 0x3f, 0x68, 0x41, 0xc9, 0xbf, 0x0c, 0x29, 0xdf, 0x9b, 0x95, 0xa1, - 0x70, 0x70, 0x78, 0x7c, 0xd4, 0xde, 0xee, 0xf0, 0x0f, 0xce, 0xb6, 0x0f, 0x75, 0xfd, 0xf9, 0x51, - 0xb7, 0x9e, 0xd9, 0xfa, 0x75, 0x16, 0x32, 0x7b, 0x2f, 0xd0, 0x67, 0x90, 0xe3, 0x5f, 0x5f, 0x4c, - 0xf9, 0xe4, 0xa6, 0x39, 0xed, 0x03, 0x13, 0x7c, 0xe3, 0xc7, 0xff, 0xf5, 0xeb, 0x9f, 0x67, 0x16, - 0x71, 0xa5, 0x35, 0xf9, 0x76, 0xeb, 0x62, 0xd2, 0x62, 0x61, 0xea, 0x91, 0xf6, 0x00, 0x7d, 0x0c, - 0xd9, 0xa3, 0xb1, 0x87, 0x52, 0x3f, 0xc5, 0x69, 0xa6, 0x7f, 0x73, 0x82, 0x57, 0x98, 0xd0, 0x05, - 0x0c, 0x42, 0xe8, 0x68, 0xec, 0x51, 0x91, 0x3f, 0x84, 0xb2, 0xfa, 0xc5, 0xc8, 0xb5, 0xdf, 0xe7, - 0x34, 0xaf, 0xff, 0x1a, 0x05, 0xdf, 0x66, 0x50, 0x37, 0x30, 0x12, 0x50, 0xfc, 0x9b, 0x16, 0x75, - 0x16, 0xdd, 0x4b, 0x0b, 0xa5, 0x7e, 0xbd, 0xd3, 0x4c, 0xff, 0x40, 0x25, 0x36, 0x0b, 0xef, 0xd2, - 0xa2, 0x22, 0xff, 0x44, 0x7c, 0x9b, 0xd2, 0xf3, 0xd0, 0x9d, 0x84, 0x6f, 0x13, 0xd4, 0x57, 0xf8, - 0xe6, 0x7a, 0x3a, 0x83, 0x00, 0xb9, 0xc5, 0x40, 0x56, 0xf1, 0xa2, 0x00, 0xe9, 0xf9, 0x2c, 0x8f, - 0xb4, 0x07, 0x5b, 0x3d, 0xc8, 0xb1, 0x17, 0x2e, 0xf4, 0xb9, 0xfc, 0xd1, 0x4c, 0x78, 0xea, 0x4b, - 0x59, 0xe8, 0xd0, 0xdb, 0x18, 0x5e, 0x66, 0x40, 0x35, 0x5c, 0xa2, 0x40, 0xec, 0x7d, 0xeb, 0x91, - 0xf6, 0x60, 0x43, 0x7b, 0x5f, 0xdb, 0xfa, 0x55, 0x0e, 0x72, 0xac, 0xb4, 0x8b, 0x2e, 0x00, 0x82, - 0xd7, 0x9e, 0xe8, 0xec, 0x62, 0xef, 0x47, 0xd1, 0xd9, 0xc5, 0x1f, 0x8a, 0x70, 0x93, 0x81, 0x2e, - 0xe3, 0x05, 0x0a, 0xca, 0x2a, 0xc6, 0x2d, 0x56, 0x04, 0xa7, 0x76, 0xfc, 0x1b, 0x4d, 0x54, 0xb6, - 0xf9, 0x59, 0x42, 0x49, 0xd2, 0x42, 0x4f, 0x3e, 0xd1, 0xed, 0x90, 0xf0, 0xdc, 0x83, 0xbf, 0xcb, - 0x00, 0x5b, 0xb8, 0x1e, 0x00, 0x3a, 0x8c, 0xe3, 0x91, 0xf6, 0xe0, 0xf3, 0x06, 0x5e, 0x12, 0x56, - 0x8e, 0xf4, 0xa0, 0x1f, 0x41, 0x2d, 0xfc, 0xa4, 0x81, 0xee, 0x26, 0x60, 0x45, 0x5f, 0x46, 0x9a, - 0x6f, 0x4d, 0x67, 0x12, 0x3a, 0xad, 0x31, 0x9d, 0x04, 0x38, 0x47, 0xbe, 0x20, 0x64, 0x64, 0x50, - 0x26, 0xb1, 0x06, 0xe8, 0x1f, 0x34, 0xf1, 0xe2, 0x14, 0xbc, 0x51, 0xa0, 0x24, 0xe9, 0xb1, 0x17, - 0x90, 0xe6, 0xbd, 0x6b, 0xb8, 0x84, 0x12, 0x7f, 0xc8, 0x94, 0xf8, 0x00, 0x2f, 0x07, 0x4a, 0x78, - 0xe6, 0x90, 0x78, 0xb6, 0xd0, 0xe2, 0xf3, 0x5b, 0xf8, 0x46, 0xc8, 0x38, 0xa1, 0xde, 0x60, 0xb1, - 0xf8, 0x3b, 0x43, 0xe2, 0x62, 0x85, 0xde, 0x2d, 0x12, 0x17, 0x2b, 0xfc, 0x48, 0x91, 0xb4, 0x58, - 0xfc, 0x55, 0x21, 0x69, 0xb1, 0xfc, 0x9e, 0xad, 0xff, 0x9b, 0x87, 0xc2, 0x36, 0xff, 0x26, 0x1c, - 0xd9, 0x50, 0xf2, 0xcb, 0xf4, 0x68, 0x2d, 0xa9, 0xce, 0x18, 0x5c, 0x6b, 0x9a, 0x77, 0x52, 0xfb, - 0x85, 0x42, 0x6f, 0x32, 0x85, 0xde, 0xc0, 0xab, 0x14, 0x59, 0x7c, 0x76, 0xde, 0xe2, 0xc5, 0xac, - 0x96, 0xd1, 0xef, 0x53, 0x43, 0xfc, 0x29, 0x54, 0xd4, 0x3a, 0x3a, 0x7a, 0x33, 0xb1, 0xb6, 0xa9, - 0x96, 0xe2, 0x9b, 0x78, 0x1a, 0x8b, 0x40, 0x7e, 0x8b, 0x21, 0xaf, 0xe1, 0x9b, 0x09, 0xc8, 0x0e, - 0x63, 0x0d, 0x81, 0xf3, 0x1a, 0x78, 0x32, 0x78, 0xa8, 0xc4, 0x9e, 0x0c, 0x1e, 0x2e, 0xa1, 0x4f, - 0x05, 0x1f, 0x33, 0x56, 0x0a, 0xee, 0x02, 0x04, 0x95, 0x6c, 0x94, 0x68, 0x4b, 0xe5, 0x5e, 0x17, - 0x75, 0x0e, 0xf1, 0x22, 0x38, 0xc6, 0x0c, 0x56, 0xec, 0xbb, 0x08, 0xec, 0xc0, 0x74, 0x3d, 0x7e, - 0x30, 0xab, 0xa1, 0xd2, 0x34, 0x4a, 0x9c, 0x4f, 0xb8, 0xbe, 0xdd, 0xbc, 0x3b, 0x95, 0x47, 0xa0, - 0xdf, 0x63, 0xe8, 0x77, 0x70, 0x33, 0x01, 0x7d, 0xc4, 0x79, 0xe9, 0x66, 0xfb, 0xff, 0x3c, 0x94, - 0x9f, 0x19, 0xa6, 0xe5, 0x11, 0xcb, 0xb0, 0x7a, 0x04, 0x9d, 0x42, 0x8e, 0x45, 0xea, 0xa8, 0x23, - 0x56, 0xcb, 0xb6, 0x51, 0x47, 0x1c, 0xaa, 0x69, 0xe2, 0x75, 0x06, 0xdc, 0xc4, 0x2b, 0x14, 0x78, - 0x18, 0x88, 0x6e, 0xb1, 0x52, 0x24, 0x9d, 0xf4, 0x4b, 0xc8, 0x8b, 0xd7, 0xbe, 0x88, 0xa0, 0x50, - 0xf1, 0xa7, 0x79, 0x2b, 0xb9, 0x33, 0x69, 0x2f, 0xab, 0x30, 0x2e, 0xe3, 0xa3, 0x38, 0x13, 0x80, - 0xa0, 0xc6, 0x1e, 0x5d, 0xd1, 0x58, 0x49, 0xbe, 0xb9, 0x9e, 0xce, 0x90, 0x64, 0x53, 0x15, 0xb3, - 0xef, 0xf3, 0x52, 0xdc, 0x3f, 0x86, 0xf9, 0xa7, 0x86, 0x7b, 0x8e, 0x22, 0xb1, 0x57, 0xf9, 0x56, - 0xac, 0xd9, 0x4c, 0xea, 0x12, 0x28, 0x77, 0x18, 0xca, 0x4d, 0xee, 0xca, 0x54, 0x94, 0x73, 0xc3, - 0xa5, 0x41, 0x0d, 0xf5, 0x21, 0xcf, 0x3f, 0x1d, 0x8b, 0xda, 0x2f, 0xf4, 0xf9, 0x59, 0xd4, 0x7e, - 0xe1, 0xaf, 0xcd, 0xae, 0x47, 0x19, 0x41, 0x51, 0x7e, 0xab, 0x85, 0x22, 0x0f, 0xf7, 0x91, 0xef, - 0xba, 0x9a, 0x6b, 0x69, 0xdd, 0x02, 0xeb, 0x2e, 0xc3, 0xba, 0x8d, 0x1b, 0xb1, 0xb5, 0x12, 0x9c, - 0x8f, 0xb4, 0x07, 0xef, 0x6b, 0xe8, 0x47, 0x00, 0xc1, 0xb3, 0x44, 0xec, 0x04, 0x46, 0x5f, 0x38, - 0x62, 0x27, 0x30, 0xf6, 0xa2, 0x81, 0x37, 0x19, 0xee, 0x06, 0xbe, 0x1b, 0xc5, 0xf5, 0x1c, 0xc3, - 0x72, 0x5f, 0x12, 0xe7, 0x3d, 0x5e, 0x65, 0x75, 0xcf, 0xcd, 0x11, 0x9d, 0xb2, 0x03, 0x25, 0xbf, - 0xea, 0x1c, 0xf5, 0xb6, 0xd1, 0x6a, 0x78, 0xd4, 0xdb, 0xc6, 0xca, 0xd5, 0x61, 0xb7, 0x13, 0xda, - 0x2d, 0x92, 0x95, 0x1e, 0xc0, 0x5f, 0xd4, 0x61, 0x9e, 0x66, 0xdd, 0x34, 0x39, 0x09, 0xea, 0x26, - 0xd1, 0xd9, 0xc7, 0xaa, 0xa8, 0xd1, 0xd9, 0xc7, 0x4b, 0x2e, 0xe1, 0xe4, 0x84, 0x5e, 0xb2, 0x5a, - 0xbc, 0x44, 0x41, 0x67, 0x6a, 0x43, 0x59, 0x29, 0xac, 0xa0, 0x04, 0x61, 0xe1, 0xf2, 0x6c, 0x34, - 0xdc, 0x25, 0x54, 0x65, 0xf0, 0x1b, 0x0c, 0x6f, 0x85, 0x87, 0x3b, 0x86, 0xd7, 0xe7, 0x1c, 0x14, - 0x50, 0xcc, 0x4e, 0x9c, 0xfb, 0x84, 0xd9, 0x85, 0xcf, 0xfe, 0x7a, 0x3a, 0x43, 0xea, 0xec, 0x82, - 0x83, 0xff, 0x0a, 0x2a, 0x6a, 0x79, 0x05, 0x25, 0x28, 0x1f, 0x29, 0x29, 0x47, 0xe3, 0x48, 0x52, - 0x75, 0x26, 0xec, 0xd9, 0x18, 0xa4, 0xa1, 0xb0, 0x51, 0xe0, 0x01, 0x14, 0x44, 0xbd, 0x25, 0xc9, - 0xa4, 0xe1, 0xf2, 0x73, 0x92, 0x49, 0x23, 0xc5, 0x9a, 0x70, 0xf6, 0xcc, 0x10, 0xe9, 0x95, 0x52, - 0xc6, 0x6a, 0x81, 0xf6, 0x84, 0x78, 0x69, 0x68, 0x41, 0x25, 0x33, 0x0d, 0x4d, 0xb9, 0xce, 0xa7, - 0xa1, 0x9d, 0x11, 0x4f, 0xf8, 0x03, 0x79, 0x4d, 0x46, 0x29, 0xc2, 0xd4, 0xf8, 0x88, 0xa7, 0xb1, - 0x24, 0x5d, 0x6e, 0x02, 0x40, 0x19, 0x1c, 0x2f, 0x01, 0x82, 0x6a, 0x50, 0x34, 0x63, 0x4d, 0xac, - 0x82, 0x47, 0x33, 0xd6, 0xe4, 0x82, 0x52, 0xd8, 0xf7, 0x05, 0xb8, 0xfc, 0x6e, 0x45, 0x91, 0x7f, - 0xa6, 0x01, 0x8a, 0x17, 0x8e, 0xd0, 0xc3, 0x64, 0xe9, 0x89, 0xb5, 0xf5, 0xe6, 0xbb, 0xaf, 0xc7, - 0x9c, 0x14, 0xce, 0x02, 0x95, 0x7a, 0x8c, 0x7b, 0xf4, 0x8a, 0x2a, 0xf5, 0x97, 0x1a, 0x54, 0x43, - 0x55, 0x27, 0x74, 0x3f, 0x65, 0x4d, 0x23, 0x25, 0xf7, 0xe6, 0xdb, 0xd7, 0xf2, 0x25, 0xa5, 0xf2, - 0xca, 0x0e, 0x90, 0x77, 0x9a, 0x9f, 0x68, 0x50, 0x0b, 0x57, 0xa9, 0x50, 0x8a, 0xec, 0x58, 0xc9, - 0xbe, 0xb9, 0x71, 0x3d, 0xe3, 0xf4, 0xe5, 0x09, 0xae, 0x33, 0x03, 0x28, 0x88, 0xba, 0x56, 0xd2, - 0xc6, 0x0f, 0x17, 0xfb, 0x93, 0x36, 0x7e, 0xa4, 0x28, 0x96, 0xb0, 0xf1, 0x1d, 0x7b, 0x40, 0x94, - 0x63, 0x26, 0x0a, 0x5f, 0x69, 0x68, 0xd3, 0x8f, 0x59, 0xa4, 0x6a, 0x96, 0x86, 0x16, 0x1c, 0x33, - 0x59, 0xf1, 0x42, 0x29, 0xc2, 0xae, 0x39, 0x66, 0xd1, 0x82, 0x59, 0xc2, 0x31, 0x63, 0x80, 0xca, - 0x31, 0x0b, 0x6a, 0x53, 0x49, 0xc7, 0x2c, 0xf6, 0x76, 0x91, 0x74, 0xcc, 0xe2, 0xe5, 0xad, 0x84, - 0x75, 0x64, 0xb8, 0xa1, 0x63, 0xb6, 0x94, 0x50, 0xc6, 0x42, 0xef, 0xa6, 0x18, 0x31, 0xf1, 0x49, - 0xa4, 0xf9, 0xde, 0x6b, 0x72, 0xa7, 0xee, 0x71, 0x6e, 0x7e, 0xb9, 0xc7, 0xff, 0x56, 0x83, 0xe5, - 0xa4, 0x12, 0x18, 0x4a, 0xc1, 0x49, 0x79, 0x4a, 0x69, 0x6e, 0xbe, 0x2e, 0xfb, 0x74, 0x6b, 0xf9, - 0xbb, 0xfe, 0x71, 0xfd, 0x5f, 0xbf, 0x5c, 0xd3, 0xfe, 0xe3, 0xcb, 0x35, 0xed, 0xbf, 0xbf, 0x5c, - 0xd3, 0xfe, 0xee, 0x7f, 0xd6, 0xe6, 0x4e, 0xf3, 0xec, 0x3f, 0x1a, 0x7f, 0xfb, 0x37, 0x01, 0x00, - 0x00, 0xff, 0xff, 0xee, 0x4f, 0x63, 0x90, 0xed, 0x3c, 0x00, 0x00, + // 4167 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x5b, 0xdd, 0x73, 0x1b, 0x47, + 0x72, 0xe7, 0x02, 0x24, 0x40, 0x34, 0x3e, 0x08, 0x0e, 0x3f, 0x04, 0xc1, 0x12, 0x45, 0x8f, 0x2c, + 0x99, 0x96, 0x6c, 0xc2, 0xc7, 0xbb, 0x8b, 0xab, 0x94, 0xe4, 0x72, 0x10, 0x09, 0x4b, 0x34, 0x29, + 0x92, 0x5e, 0x42, 0xf2, 0x47, 0x5d, 0x05, 0xb5, 0x04, 0x86, 0xe0, 0x86, 0xc0, 0x2e, 0x6e, 0x77, + 0x01, 0x91, 0xca, 0xc7, 0xa5, 0xae, 0x2e, 0x57, 0xc9, 0xeb, 0xa5, 0x2a, 0x95, 0x3c, 0x24, 0x2f, + 0xa9, 0xd4, 0xd5, 0x3d, 0xdc, 0x73, 0xfe, 0x85, 0xbc, 0x24, 0x97, 0x54, 0xfe, 0x81, 0x94, 0x93, + 0x97, 0xe4, 0x8f, 0x48, 0x5d, 0xcd, 0xd7, 0xee, 0xec, 0x17, 0x29, 0x1b, 0xb6, 0x5f, 0x44, 0x4c, + 0x4f, 0x4f, 0xff, 0x7a, 0x7a, 0x66, 0xba, 0x7b, 0x7a, 0x56, 0x50, 0x70, 0x46, 0xdd, 0xcd, 0x91, + 0x63, 0x7b, 0x36, 0x2a, 0x11, 0xaf, 0xdb, 0x73, 0x89, 0x33, 0x21, 0xce, 0xe8, 0xa4, 0xbe, 0xdc, + 0xb7, 0xfb, 0x36, 0xeb, 0x68, 0xd0, 0x5f, 0x9c, 0xa7, 0x5e, 0xa3, 0x3c, 0x0d, 0x63, 0x64, 0x36, + 0x86, 0x93, 0x6e, 0x77, 0x74, 0xd2, 0x38, 0x9f, 0x88, 0x9e, 0xba, 0xdf, 0x63, 0x8c, 0xbd, 0xb3, + 0xd1, 0x09, 0xfb, 0x23, 0xfa, 0x6e, 0xf5, 0x6d, 0xbb, 0x3f, 0x20, 0xbc, 0xd7, 0xb2, 0x6c, 0xcf, + 0xf0, 0x4c, 0xdb, 0x72, 0x79, 0x2f, 0xfe, 0x0b, 0x0d, 0x2a, 0x3a, 0x71, 0x47, 0xb6, 0xe5, 0x92, + 0xa7, 0xc4, 0xe8, 0x11, 0x07, 0xdd, 0x06, 0xe8, 0x0e, 0xc6, 0xae, 0x47, 0x9c, 0x8e, 0xd9, 0xab, + 0x69, 0xeb, 0xda, 0xc6, 0xac, 0x5e, 0x10, 0x94, 0xdd, 0x1e, 0x7a, 0x03, 0x0a, 0x43, 0x32, 0x3c, + 0xe1, 0xbd, 0x19, 0xd6, 0x3b, 0xcf, 0x09, 0xbb, 0x3d, 0x54, 0x87, 0x79, 0x87, 0x4c, 0x4c, 0xd7, + 0xb4, 0xad, 0x5a, 0x76, 0x5d, 0xdb, 0xc8, 0xea, 0x7e, 0x9b, 0x0e, 0x74, 0x8c, 0x53, 0xaf, 0xe3, + 0x11, 0x67, 0x58, 0x9b, 0xe5, 0x03, 0x29, 0xa1, 0x4d, 0x9c, 0x21, 0xfe, 0xd9, 0x1c, 0x94, 0x74, + 0xc3, 0xea, 0x13, 0x9d, 0xfc, 0x78, 0x4c, 0x5c, 0x0f, 0x55, 0x21, 0x7b, 0x4e, 0x2e, 0x19, 0x7c, + 0x49, 0xa7, 0x3f, 0xf9, 0x78, 0xab, 0x4f, 0x3a, 0xc4, 0xe2, 0xc0, 0x25, 0x3a, 0xde, 0xea, 0x93, + 0x96, 0xd5, 0x43, 0xcb, 0x30, 0x37, 0x30, 0x87, 0xa6, 0x27, 0x50, 0x79, 0x23, 0xa4, 0xce, 0x6c, + 0x44, 0x9d, 0x6d, 0x00, 0xd7, 0x76, 0xbc, 0x8e, 0xed, 0xf4, 0x88, 0x53, 0x9b, 0x5b, 0xd7, 0x36, + 0x2a, 0x5b, 0x6f, 0x6d, 0xaa, 0xcb, 0xb0, 0xa9, 0x2a, 0xb4, 0x79, 0x6c, 0x3b, 0xde, 0x21, 0xe5, + 0xd5, 0x0b, 0xae, 0xfc, 0x89, 0x3e, 0x84, 0x22, 0x13, 0xe2, 0x19, 0x4e, 0x9f, 0x78, 0xb5, 0x1c, + 0x93, 0x72, 0xef, 0x1a, 0x29, 0x6d, 0xc6, 0xac, 0x33, 0x78, 0xfe, 0x1b, 0x61, 0x28, 0xb9, 0xc4, + 0x31, 0x8d, 0x81, 0xf9, 0xca, 0x38, 0x19, 0x90, 0x5a, 0x7e, 0x5d, 0xdb, 0x98, 0xd7, 0x43, 0x34, + 0x3a, 0xff, 0x73, 0x72, 0xe9, 0x76, 0x6c, 0x6b, 0x70, 0x59, 0x9b, 0x67, 0x0c, 0xf3, 0x94, 0x70, + 0x68, 0x0d, 0x2e, 0xd9, 0xa2, 0xd9, 0x63, 0xcb, 0xe3, 0xbd, 0x05, 0xd6, 0x5b, 0x60, 0x14, 0xd6, + 0xbd, 0x01, 0xd5, 0xa1, 0x69, 0x75, 0x86, 0x76, 0xaf, 0xe3, 0x1b, 0x04, 0x98, 0x41, 0x2a, 0x43, + 0xd3, 0x7a, 0x66, 0xf7, 0x74, 0x69, 0x16, 0xca, 0x69, 0x5c, 0x84, 0x39, 0x8b, 0x82, 0xd3, 0xb8, + 0x50, 0x39, 0x37, 0x61, 0x89, 0xca, 0xec, 0x3a, 0xc4, 0xf0, 0x48, 0xc0, 0x5c, 0x62, 0xcc, 0x8b, + 0x43, 0xd3, 0xda, 0x66, 0x3d, 0x21, 0x7e, 0xe3, 0x22, 0xc6, 0x5f, 0x16, 0xfc, 0xc6, 0x45, 0x98, + 0x1f, 0x6f, 0x42, 0xc1, 0xb7, 0x39, 0x9a, 0x87, 0xd9, 0x83, 0xc3, 0x83, 0x56, 0x75, 0x06, 0x01, + 0xe4, 0x9a, 0xc7, 0xdb, 0xad, 0x83, 0x9d, 0xaa, 0x86, 0x8a, 0x90, 0xdf, 0x69, 0xf1, 0x46, 0x06, + 0x3f, 0x06, 0x08, 0xac, 0x8b, 0xf2, 0x90, 0xdd, 0x6b, 0x7d, 0x56, 0x9d, 0xa1, 0x3c, 0x2f, 0x5a, + 0xfa, 0xf1, 0xee, 0xe1, 0x41, 0x55, 0xa3, 0x83, 0xb7, 0xf5, 0x56, 0xb3, 0xdd, 0xaa, 0x66, 0x28, + 0xc7, 0xb3, 0xc3, 0x9d, 0x6a, 0x16, 0x15, 0x60, 0xee, 0x45, 0x73, 0xff, 0x79, 0xab, 0x3a, 0x8b, + 0x7f, 0xa3, 0x41, 0x59, 0xac, 0x17, 0x3f, 0x13, 0xe8, 0x7b, 0x90, 0x3b, 0x63, 0xe7, 0x82, 0x6d, + 0xc5, 0xe2, 0xd6, 0xad, 0xc8, 0xe2, 0x86, 0xce, 0x8e, 0x2e, 0x78, 0x11, 0x86, 0xec, 0xf9, 0xc4, + 0xad, 0x65, 0xd6, 0xb3, 0x1b, 0xc5, 0xad, 0xea, 0x26, 0x3f, 0xaf, 0x9b, 0x7b, 0xe4, 0xf2, 0x85, + 0x31, 0x18, 0x13, 0x9d, 0x76, 0x22, 0x04, 0xb3, 0x43, 0xdb, 0x21, 0x6c, 0xc7, 0xce, 0xeb, 0xec, + 0x37, 0xdd, 0xc6, 0x6c, 0xd1, 0xc4, 0x6e, 0xe5, 0x0d, 0xba, 0xf2, 0xee, 0xf8, 0xa4, 0xc3, 0x7b, + 0x08, 0xdf, 0xc7, 0xee, 0xf8, 0x64, 0x9b, 0x75, 0xde, 0x06, 0xf0, 0x6c, 0xcf, 0x18, 0x74, 0x5c, + 0xf3, 0x15, 0xa9, 0x9d, 0xb2, 0xde, 0x02, 0xa3, 0x1c, 0x9b, 0xaf, 0x08, 0xfe, 0x95, 0x06, 0x70, + 0x34, 0xf6, 0xd2, 0x8f, 0xd5, 0x32, 0xcc, 0x4d, 0xa8, 0x52, 0xe2, 0x48, 0xf1, 0x06, 0x3b, 0x4f, + 0xc4, 0x70, 0x89, 0x7f, 0x9e, 0x68, 0x03, 0xdd, 0x80, 0xfc, 0xc8, 0x21, 0x93, 0xce, 0xf9, 0x84, + 0x29, 0x38, 0xaf, 0xe7, 0x68, 0x73, 0x6f, 0x82, 0xde, 0x84, 0x92, 0xd9, 0xb7, 0x6c, 0x87, 0x74, + 0xb8, 0xac, 0x39, 0xd6, 0x5b, 0xe4, 0x34, 0x36, 0x67, 0x85, 0x85, 0x0b, 0xce, 0xa9, 0x2c, 0xfb, + 0x94, 0x84, 0x2d, 0x28, 0x32, 0x55, 0xa7, 0x32, 0xfd, 0x3b, 0x81, 0x8e, 0x19, 0x36, 0x2c, 0x6e, + 0x7e, 0xa1, 0x35, 0xfe, 0x11, 0xa0, 0x1d, 0x32, 0x20, 0x1e, 0x99, 0xc6, 0xf3, 0x28, 0x36, 0xc9, + 0xaa, 0x36, 0xc1, 0xbf, 0xd0, 0x60, 0x29, 0x24, 0x7e, 0xaa, 0x69, 0xd5, 0x20, 0xdf, 0x63, 0xc2, + 0xb8, 0x06, 0x59, 0x5d, 0x36, 0xd1, 0x43, 0x98, 0x17, 0x0a, 0xb8, 0xb5, 0x6c, 0xca, 0x86, 0xcb, + 0x73, 0x9d, 0x5c, 0xfc, 0xab, 0x0c, 0x14, 0xc4, 0x44, 0x0f, 0x47, 0xa8, 0x09, 0x65, 0x87, 0x37, + 0x3a, 0x6c, 0x3e, 0x42, 0xa3, 0x7a, 0xba, 0x03, 0x7b, 0x3a, 0xa3, 0x97, 0xc4, 0x10, 0x46, 0x46, + 0xbf, 0x0b, 0x45, 0x29, 0x62, 0x34, 0xf6, 0x84, 0xc9, 0x6b, 0x61, 0x01, 0xc1, 0xfe, 0x7b, 0x3a, + 0xa3, 0x83, 0x60, 0x3f, 0x1a, 0x7b, 0xa8, 0x0d, 0xcb, 0x72, 0x30, 0x9f, 0x8d, 0x50, 0x23, 0xcb, + 0xa4, 0xac, 0x87, 0xa5, 0xc4, 0x97, 0xea, 0xe9, 0x8c, 0x8e, 0xc4, 0x78, 0xa5, 0x53, 0x55, 0xc9, + 0xbb, 0xe0, 0x8e, 0x3f, 0xa6, 0x52, 0xfb, 0xc2, 0x8a, 0xab, 0xd4, 0xbe, 0xb0, 0x1e, 0x17, 0x20, + 0x2f, 0x5a, 0xf8, 0x9f, 0x33, 0x00, 0x72, 0x35, 0x0e, 0x47, 0x68, 0x07, 0x2a, 0x8e, 0x68, 0x85, + 0xac, 0xf5, 0x46, 0xa2, 0xb5, 0xc4, 0x22, 0xce, 0xe8, 0x65, 0x39, 0x88, 0x2b, 0xf7, 0x03, 0x28, + 0xf9, 0x52, 0x02, 0x83, 0xdd, 0x4c, 0x30, 0x98, 0x2f, 0xa1, 0x28, 0x07, 0x50, 0x93, 0x7d, 0x02, + 0x2b, 0xfe, 0xf8, 0x04, 0x9b, 0xbd, 0x79, 0x85, 0xcd, 0x7c, 0x81, 0x4b, 0x52, 0x82, 0x6a, 0x35, + 0x55, 0xb1, 0xc0, 0x6c, 0x37, 0x13, 0xcc, 0x16, 0x57, 0x8c, 0x1a, 0x0e, 0x68, 0xac, 0xe5, 0x4d, + 0xfc, 0xbf, 0x59, 0xc8, 0x6f, 0xdb, 0xc3, 0x91, 0xe1, 0xd0, 0xd5, 0xc8, 0x39, 0xc4, 0x1d, 0x0f, + 0x3c, 0x66, 0xae, 0xca, 0xd6, 0xdd, 0xb0, 0x44, 0xc1, 0x26, 0xff, 0xea, 0x8c, 0x55, 0x17, 0x43, + 0xe8, 0x60, 0x11, 0x5a, 0x33, 0xaf, 0x31, 0x58, 0x04, 0x56, 0x31, 0x44, 0x1e, 0xe4, 0x6c, 0x70, + 0x90, 0xeb, 0x90, 0x9f, 0x10, 0x27, 0x48, 0x07, 0x9e, 0xce, 0xe8, 0x92, 0x80, 0xde, 0x81, 0x85, + 0x68, 0x68, 0x9a, 0x13, 0x3c, 0x95, 0x6e, 0x38, 0x92, 0xdd, 0x85, 0x52, 0x28, 0x3e, 0xe6, 0x04, + 0x5f, 0x71, 0xa8, 0x84, 0xc7, 0x55, 0xe9, 0x57, 0x69, 0x2c, 0x2f, 0x3d, 0x9d, 0x91, 0x9e, 0x75, + 0x55, 0x7a, 0xd6, 0x79, 0x31, 0x4a, 0xf8, 0xd6, 0x90, 0x93, 0xf9, 0x61, 0xd8, 0xc9, 0xe0, 0x1f, + 0x42, 0x39, 0x64, 0x20, 0x1a, 0xb3, 0x5a, 0x1f, 0x3f, 0x6f, 0xee, 0xf3, 0x00, 0xf7, 0x84, 0xc5, + 0x34, 0xbd, 0xaa, 0xd1, 0x38, 0xb9, 0xdf, 0x3a, 0x3e, 0xae, 0x66, 0x50, 0x19, 0x0a, 0x07, 0x87, + 0xed, 0x0e, 0xe7, 0xca, 0xe2, 0x27, 0xbe, 0x04, 0x11, 0x20, 0x95, 0xb8, 0x38, 0xa3, 0xc4, 0x45, + 0x4d, 0xc6, 0xc5, 0x4c, 0x10, 0x17, 0x59, 0x88, 0xdc, 0x6f, 0x35, 0x8f, 0x5b, 0xd5, 0xd9, 0xc7, + 0x15, 0x28, 0x71, 0xfb, 0x76, 0xc6, 0x16, 0x0d, 0xd3, 0xff, 0xa8, 0x01, 0x04, 0xa7, 0x09, 0x35, + 0x20, 0xdf, 0xe5, 0x38, 0x35, 0x8d, 0x39, 0xa3, 0x95, 0xc4, 0x25, 0xd3, 0x25, 0x17, 0xfa, 0x0e, + 0xe4, 0xdd, 0x71, 0xb7, 0x4b, 0x5c, 0x19, 0x2e, 0x6f, 0x44, 0xfd, 0xa1, 0xf0, 0x56, 0xba, 0xe4, + 0xa3, 0x43, 0x4e, 0x0d, 0x73, 0x30, 0x66, 0xc1, 0xf3, 0xea, 0x21, 0x82, 0x0f, 0xff, 0x9d, 0x06, + 0x45, 0x65, 0xf3, 0x7e, 0x45, 0x27, 0x7c, 0x8b, 0x06, 0xe2, 0x6e, 0x97, 0x90, 0x9e, 0x70, 0xc3, + 0xf3, 0x7a, 0x40, 0x40, 0xbf, 0x03, 0x05, 0x79, 0x02, 0xa4, 0x27, 0xae, 0x25, 0x8b, 0x3d, 0x1c, + 0xe9, 0x01, 0x2b, 0xde, 0x83, 0x45, 0x66, 0x95, 0x2e, 0x4d, 0xcc, 0xa5, 0x1d, 0xd5, 0xd4, 0x55, + 0x8b, 0xa4, 0xae, 0x75, 0x98, 0x1f, 0x9d, 0x5d, 0xba, 0x66, 0xd7, 0x18, 0x08, 0x2d, 0xfc, 0x36, + 0xfe, 0x08, 0x90, 0x2a, 0x6c, 0x9a, 0xe9, 0xe2, 0x32, 0x14, 0x9f, 0x1a, 0xee, 0x99, 0x50, 0x09, + 0x3f, 0x84, 0x32, 0x6d, 0xee, 0xbd, 0x78, 0x0d, 0x1d, 0xd9, 0xc5, 0x42, 0x72, 0x4f, 0x65, 0x73, + 0x04, 0xb3, 0x67, 0x86, 0x7b, 0xc6, 0x26, 0x5a, 0xd6, 0xd9, 0x6f, 0xf4, 0x0e, 0x54, 0xbb, 0x7c, + 0x92, 0x9d, 0xc8, 0x75, 0x63, 0x41, 0xd0, 0xfd, 0x2c, 0xf2, 0x53, 0x28, 0xf1, 0x39, 0x7c, 0xdd, + 0x4a, 0xe0, 0x45, 0x58, 0x38, 0xb6, 0x8c, 0x91, 0x7b, 0x66, 0xcb, 0xe8, 0x46, 0x27, 0x5d, 0x0d, + 0x68, 0x53, 0x21, 0xbe, 0x0d, 0x0b, 0x0e, 0x19, 0x1a, 0xa6, 0x65, 0x5a, 0xfd, 0xce, 0xc9, 0xa5, + 0x47, 0x5c, 0x71, 0xd9, 0xaa, 0xf8, 0xe4, 0xc7, 0x94, 0x4a, 0x55, 0x3b, 0x19, 0xd8, 0x27, 0xc2, + 0xcd, 0xb1, 0xdf, 0xf8, 0xe7, 0x19, 0x28, 0x7d, 0x62, 0x78, 0x5d, 0xb9, 0x74, 0x68, 0x17, 0x2a, + 0xbe, 0x73, 0x63, 0x14, 0xa1, 0x4b, 0x24, 0xc4, 0xb2, 0x31, 0x32, 0x0d, 0x97, 0xd1, 0xb1, 0xdc, + 0x55, 0x09, 0x4c, 0x94, 0x61, 0x75, 0xc9, 0xc0, 0x17, 0x95, 0x49, 0x17, 0xc5, 0x18, 0x55, 0x51, + 0x2a, 0x01, 0x1d, 0x42, 0x75, 0xe4, 0xd8, 0x7d, 0x87, 0xb8, 0xae, 0x2f, 0x8c, 0x87, 0x31, 0x9c, + 0x20, 0xec, 0x48, 0xb0, 0x06, 0xe2, 0x16, 0x46, 0x61, 0xd2, 0xe3, 0x85, 0x20, 0x9f, 0xe1, 0xce, + 0xe9, 0x3f, 0x32, 0x80, 0xe2, 0x93, 0xfa, 0xb2, 0x29, 0xde, 0x3d, 0xa8, 0xb8, 0x9e, 0xe1, 0xc4, + 0x36, 0x5b, 0x99, 0x51, 0x7d, 0x8f, 0xff, 0x36, 0xf8, 0x0a, 0x75, 0x2c, 0xdb, 0x33, 0x4f, 0x2f, + 0x45, 0x96, 0x5c, 0x91, 0xe4, 0x03, 0x46, 0x45, 0x2d, 0xc8, 0x9f, 0x9a, 0x03, 0x8f, 0x38, 0x6e, + 0x6d, 0x6e, 0x3d, 0xbb, 0x51, 0xd9, 0x7a, 0x78, 0xdd, 0x32, 0x6c, 0x7e, 0xc8, 0xf8, 0xdb, 0x97, + 0x23, 0xa2, 0xcb, 0xb1, 0x6a, 0xe6, 0x99, 0x0b, 0x65, 0xe3, 0x37, 0x61, 0xfe, 0x25, 0x15, 0x41, + 0x6f, 0xe8, 0x79, 0x9e, 0x2c, 0xb2, 0x36, 0xbf, 0xa0, 0x9f, 0x3a, 0x46, 0x7f, 0x48, 0x2c, 0x4f, + 0xde, 0x21, 0x65, 0x1b, 0xdf, 0x03, 0x08, 0x60, 0xa8, 0xcb, 0x3f, 0x38, 0x3c, 0x7a, 0xde, 0xae, + 0xce, 0xa0, 0x12, 0xcc, 0x1f, 0x1c, 0xee, 0xb4, 0xf6, 0x5b, 0x34, 0x3e, 0xe0, 0x86, 0x34, 0x69, + 0x68, 0x2d, 0x55, 0x4c, 0x2d, 0x84, 0x89, 0x57, 0x61, 0x39, 0x69, 0x01, 0x69, 0x2e, 0x5a, 0x16, + 0xbb, 0x74, 0xaa, 0xa3, 0xa2, 0x42, 0x67, 0xc2, 0xd3, 0xad, 0x41, 0x9e, 0xef, 0xde, 0x9e, 0x48, + 0xce, 0x65, 0x93, 0x1a, 0x82, 0x6f, 0x46, 0xd2, 0x13, 0xab, 0xe4, 0xb7, 0x13, 0xdd, 0xcb, 0x5c, + 0xa2, 0x7b, 0x41, 0x77, 0xa1, 0xec, 0x9f, 0x06, 0xc3, 0x15, 0xb9, 0x40, 0x41, 0x2f, 0xc9, 0x8d, + 0x4e, 0x69, 0x21, 0xa3, 0xe7, 0xc3, 0x46, 0x47, 0xf7, 0x20, 0x47, 0x26, 0xc4, 0xf2, 0xdc, 0x5a, + 0x91, 0x45, 0x8c, 0xb2, 0xcc, 0xdd, 0x5b, 0x94, 0xaa, 0x8b, 0x4e, 0xfc, 0x7d, 0x58, 0x64, 0x77, + 0xa4, 0x27, 0x8e, 0x61, 0xa9, 0x97, 0xb9, 0x76, 0x7b, 0x5f, 0x98, 0x9b, 0xfe, 0x44, 0x15, 0xc8, + 0xec, 0xee, 0x08, 0x23, 0x64, 0x76, 0x77, 0xf0, 0x4f, 0x35, 0x40, 0xea, 0xb8, 0xa9, 0xec, 0x1c, + 0x11, 0x2e, 0xe1, 0xb3, 0x01, 0xfc, 0x32, 0xcc, 0x11, 0xc7, 0xb1, 0x1d, 0x66, 0xd1, 0x82, 0xce, + 0x1b, 0xf8, 0x2d, 0xa1, 0x83, 0x4e, 0x26, 0xf6, 0xb9, 0x7f, 0x06, 0xb9, 0x34, 0xcd, 0x57, 0x75, + 0x0f, 0x96, 0x42, 0x5c, 0x53, 0x45, 0xae, 0x0f, 0x61, 0x81, 0x09, 0xdb, 0x3e, 0x23, 0xdd, 0xf3, + 0x91, 0x6d, 0x5a, 0x31, 0x3c, 0xba, 0x72, 0x81, 0x83, 0xa5, 0xf3, 0xe0, 0x13, 0x2b, 0xf9, 0xc4, + 0x76, 0x7b, 0x1f, 0x7f, 0x06, 0xab, 0x11, 0x39, 0x52, 0xfd, 0x3f, 0x80, 0x62, 0xd7, 0x27, 0xba, + 0x22, 0xd7, 0xb9, 0x1d, 0x56, 0x2e, 0x3a, 0x54, 0x1d, 0x81, 0x0f, 0xe1, 0x46, 0x4c, 0xf4, 0x54, + 0x73, 0x7e, 0x1b, 0x56, 0x98, 0xc0, 0x3d, 0x42, 0x46, 0xcd, 0x81, 0x39, 0x49, 0xb5, 0xf4, 0x48, + 0x4c, 0x4a, 0x61, 0xfc, 0x66, 0xf7, 0x05, 0xfe, 0x3d, 0x81, 0xd8, 0x36, 0x87, 0xa4, 0x6d, 0xef, + 0xa7, 0xeb, 0x46, 0xa3, 0xd9, 0x39, 0xb9, 0x74, 0x45, 0x5a, 0xc3, 0x7e, 0xe3, 0x7f, 0xd2, 0x84, + 0xa9, 0xd4, 0xe1, 0xdf, 0xf0, 0x4e, 0x5e, 0x03, 0xe8, 0xd3, 0x23, 0x43, 0x7a, 0xb4, 0x83, 0x57, + 0x63, 0x14, 0x8a, 0xaf, 0x27, 0xf5, 0xdf, 0x25, 0xa1, 0xe7, 0xb2, 0xd8, 0xe7, 0xec, 0x1f, 0xdf, + 0xcb, 0xdd, 0x86, 0x22, 0x23, 0x1c, 0x7b, 0x86, 0x37, 0x76, 0x63, 0x8b, 0xf1, 0x67, 0x62, 0xdb, + 0xcb, 0x41, 0x53, 0xcd, 0xeb, 0x3b, 0x90, 0x63, 0x97, 0x09, 0x99, 0x4a, 0xdf, 0x4c, 0xd8, 0x8f, + 0x5c, 0x0f, 0x5d, 0x30, 0xe2, 0x9f, 0x6b, 0x90, 0x7b, 0xc6, 0xca, 0xb7, 0x8a, 0x6a, 0xb3, 0x72, + 0x2d, 0x2c, 0x63, 0xc8, 0x0b, 0x43, 0x05, 0x9d, 0xfd, 0x66, 0xa9, 0x27, 0x21, 0xce, 0x73, 0x7d, + 0x9f, 0xa7, 0xb8, 0x05, 0xdd, 0x6f, 0x53, 0x9b, 0x75, 0x07, 0x26, 0xb1, 0x3c, 0xd6, 0x3b, 0xcb, + 0x7a, 0x15, 0x0a, 0xcd, 0x9e, 0x4d, 0x77, 0x9f, 0x18, 0x8e, 0x25, 0x0a, 0xae, 0xf3, 0x7a, 0x40, + 0xc0, 0xfb, 0x50, 0xe5, 0x7a, 0x34, 0x7b, 0x3d, 0x25, 0xc1, 0xf4, 0xd1, 0xb4, 0x08, 0x5a, 0x48, + 0x5a, 0x26, 0x2a, 0xed, 0x97, 0x1a, 0x2c, 0x2a, 0xe2, 0xa6, 0xb2, 0xea, 0xbb, 0x90, 0xe3, 0x05, + 0x6e, 0x91, 0xe9, 0x2c, 0x87, 0x47, 0x71, 0x18, 0x5d, 0xf0, 0xa0, 0x4d, 0xc8, 0xf3, 0x5f, 0xf2, + 0x0e, 0x90, 0xcc, 0x2e, 0x99, 0xf0, 0x3d, 0x58, 0x12, 0x24, 0x32, 0xb4, 0x93, 0x0e, 0x06, 0x5b, + 0x0c, 0xfc, 0x27, 0xb0, 0x1c, 0x66, 0x9b, 0x6a, 0x4a, 0x8a, 0x92, 0x99, 0xd7, 0x51, 0xb2, 0x29, + 0x95, 0x7c, 0x3e, 0xea, 0x29, 0x79, 0x54, 0x74, 0xc7, 0xa8, 0xeb, 0x95, 0x09, 0xaf, 0x57, 0x30, + 0x01, 0x29, 0xe2, 0x5b, 0x9d, 0xc0, 0x07, 0x72, 0x3b, 0xec, 0x9b, 0xae, 0xef, 0xc3, 0x31, 0x94, + 0x06, 0xa6, 0x45, 0x0c, 0x47, 0x54, 0xdd, 0x35, 0x5e, 0x75, 0x57, 0x69, 0xf8, 0x15, 0x20, 0x75, + 0xe0, 0xb7, 0xaa, 0xf4, 0x7d, 0x69, 0xb2, 0x23, 0xc7, 0x1e, 0xda, 0xa9, 0x66, 0xc7, 0x7f, 0x0a, + 0x2b, 0x11, 0xbe, 0x6f, 0x55, 0xcd, 0x25, 0x58, 0xdc, 0x21, 0x32, 0xa1, 0x91, 0x6e, 0xef, 0x23, + 0x40, 0x2a, 0x71, 0xaa, 0xc8, 0xd6, 0x80, 0xc5, 0x67, 0xf6, 0x84, 0xba, 0x48, 0x4a, 0x0d, 0x7c, + 0x03, 0xaf, 0x43, 0xf8, 0xa6, 0xf0, 0xdb, 0x14, 0x5c, 0x1d, 0x30, 0x15, 0xf8, 0x6f, 0x34, 0x28, + 0x35, 0x07, 0x86, 0x33, 0x94, 0xc0, 0x3f, 0x80, 0x1c, 0xbf, 0x5d, 0x8b, 0x82, 0xd6, 0xfd, 0xb0, + 0x18, 0x95, 0x97, 0x37, 0x9a, 0xfc, 0x2e, 0x2e, 0x46, 0x51, 0xc5, 0xc5, 0x7b, 0xd9, 0x4e, 0xe4, + 0xfd, 0x6c, 0x07, 0xbd, 0x07, 0x73, 0x06, 0x1d, 0xc2, 0x42, 0x51, 0x25, 0x5a, 0xd7, 0x60, 0xd2, + 0xd8, 0x1d, 0x80, 0x73, 0xe1, 0xef, 0x41, 0x51, 0x41, 0x40, 0x79, 0xc8, 0x3e, 0x69, 0x89, 0x84, + 0xbd, 0xb9, 0xdd, 0xde, 0x7d, 0xc1, 0x0b, 0x3a, 0x15, 0x80, 0x9d, 0x96, 0xdf, 0xce, 0xe0, 0x4f, + 0xc5, 0x28, 0xe1, 0xf6, 0x55, 0x7d, 0xb4, 0x34, 0x7d, 0x32, 0xaf, 0xa5, 0xcf, 0x05, 0x94, 0xc5, + 0xf4, 0xa7, 0x0d, 0x63, 0x4c, 0x5e, 0x4a, 0x18, 0x53, 0x94, 0xd7, 0x05, 0x23, 0xfe, 0xb5, 0x06, + 0xd5, 0x1d, 0xfb, 0xa5, 0xd5, 0x77, 0x8c, 0x9e, 0x7f, 0x4e, 0x3e, 0x8c, 0xac, 0xd4, 0x66, 0xa4, + 0x38, 0x1a, 0xe1, 0x0f, 0x08, 0x91, 0x15, 0xab, 0x05, 0x65, 0x43, 0x1e, 0x0b, 0x65, 0x13, 0x7f, + 0x00, 0x0b, 0x91, 0x41, 0xd4, 0xf6, 0x2f, 0x9a, 0xfb, 0xbb, 0x3b, 0xd4, 0xd6, 0xac, 0xb0, 0xd6, + 0x3a, 0x68, 0x3e, 0xde, 0x6f, 0x89, 0xc7, 0xa7, 0xe6, 0xc1, 0x76, 0x6b, 0xbf, 0x9a, 0xc1, 0x5d, + 0x58, 0x54, 0xe0, 0xa7, 0x7d, 0x19, 0x48, 0xd1, 0x6e, 0x01, 0xca, 0x22, 0xda, 0x8b, 0x43, 0xf9, + 0x6f, 0x19, 0xa8, 0x48, 0xca, 0x37, 0x83, 0x89, 0x56, 0x21, 0xd7, 0x3b, 0x39, 0x36, 0x5f, 0xc9, + 0x97, 0x23, 0xd1, 0xa2, 0xf4, 0x01, 0xc7, 0xe1, 0x4f, 0xbf, 0xa2, 0x45, 0xc3, 0xb8, 0x63, 0x9c, + 0x7a, 0xbb, 0x56, 0x8f, 0x5c, 0xb0, 0xa4, 0x60, 0x56, 0x0f, 0x08, 0xac, 0xc2, 0x24, 0x9e, 0x88, + 0xd9, 0xcd, 0x4a, 0x79, 0x32, 0x46, 0x0f, 0xa0, 0x4a, 0x7f, 0x37, 0x47, 0xa3, 0x81, 0x49, 0x7a, + 0x5c, 0x40, 0x9e, 0xf1, 0xc4, 0xe8, 0x14, 0x9d, 0xdd, 0x45, 0xdc, 0xda, 0x3c, 0x0b, 0x4b, 0xa2, + 0x85, 0xd6, 0xa1, 0xc8, 0xf5, 0xdb, 0xb5, 0x9e, 0xbb, 0x84, 0xbd, 0x9b, 0x66, 0x75, 0x95, 0x14, + 0x4e, 0x33, 0x20, 0x9a, 0x66, 0x2c, 0xc1, 0x62, 0x73, 0xec, 0x9d, 0xb5, 0x2c, 0x1a, 0x2b, 0xa4, + 0x95, 0x97, 0x01, 0x51, 0xe2, 0x8e, 0xe9, 0xaa, 0x54, 0xc1, 0x1a, 0x5e, 0x90, 0x16, 0x2c, 0x51, + 0x22, 0xb1, 0x3c, 0xb3, 0xab, 0xc4, 0x55, 0x99, 0x79, 0x69, 0x91, 0xcc, 0xcb, 0x70, 0xdd, 0x97, + 0xb6, 0xd3, 0x13, 0x36, 0xf7, 0xdb, 0xf8, 0x1f, 0x34, 0x0e, 0xf9, 0xdc, 0x0d, 0xa5, 0x4f, 0x5f, + 0x52, 0x0c, 0x7a, 0x1f, 0xf2, 0xf6, 0x88, 0x7d, 0x1d, 0x20, 0xca, 0x30, 0xab, 0x9b, 0xfc, 0x7b, + 0x82, 0x4d, 0x21, 0xf8, 0x90, 0xf7, 0xea, 0x92, 0x0d, 0xdd, 0x87, 0xca, 0x99, 0xe1, 0x9e, 0x91, + 0xde, 0x91, 0x94, 0xc9, 0x6f, 0x7e, 0x11, 0x2a, 0xde, 0x08, 0xf4, 0x7b, 0x42, 0xbc, 0x2b, 0xf4, + 0xc3, 0x0f, 0x61, 0x45, 0x72, 0x8a, 0xd7, 0x89, 0x2b, 0x98, 0x5f, 0xc2, 0x6d, 0xc9, 0xbc, 0x7d, + 0x66, 0x58, 0x7d, 0x22, 0x01, 0xbf, 0xaa, 0x05, 0xe2, 0xf3, 0xc9, 0x26, 0xce, 0xe7, 0x31, 0xd4, + 0xfc, 0xf9, 0xb0, 0x9b, 0xb5, 0x3d, 0x50, 0x15, 0x1d, 0xbb, 0xe2, 0x3c, 0x15, 0x74, 0xf6, 0x9b, + 0xd2, 0x1c, 0x7b, 0xe0, 0xa7, 0xd2, 0xf4, 0x37, 0xde, 0x86, 0x9b, 0x52, 0x86, 0xb8, 0xf3, 0x86, + 0x85, 0xc4, 0x14, 0x4f, 0x12, 0x22, 0x0c, 0x4b, 0x87, 0x5e, 0xbd, 0xf0, 0x2a, 0x67, 0x78, 0x09, + 0x98, 0x4c, 0x4d, 0x91, 0xb9, 0xc2, 0x37, 0x25, 0x55, 0x4c, 0xc9, 0x96, 0x24, 0x99, 0x0a, 0x50, + 0xc9, 0x62, 0xc1, 0x28, 0x39, 0xb6, 0x60, 0x31, 0xd1, 0x3f, 0x82, 0x35, 0x5f, 0x09, 0x6a, 0xb7, + 0x23, 0xe2, 0x0c, 0x4d, 0xd7, 0x55, 0xea, 0xde, 0x49, 0x13, 0xbf, 0x0f, 0xb3, 0x23, 0x22, 0x82, + 0x50, 0x71, 0x0b, 0xc9, 0x4d, 0xa9, 0x0c, 0x66, 0xfd, 0xb8, 0x07, 0x77, 0xa4, 0x74, 0x6e, 0xd1, + 0x44, 0xf1, 0x51, 0xa5, 0x64, 0x35, 0x30, 0x93, 0x52, 0x0d, 0xcc, 0x46, 0xde, 0x62, 0x3e, 0xe2, + 0x86, 0x94, 0x67, 0x7e, 0xaa, 0xe4, 0x62, 0x8f, 0xdb, 0xd4, 0x77, 0x15, 0x53, 0x09, 0xfb, 0x4b, + 0xe1, 0x05, 0xbe, 0x2e, 0x0f, 0x4f, 0xd8, 0x0c, 0xe5, 0x43, 0x87, 0x6c, 0xd2, 0xac, 0x99, 0x2e, + 0x80, 0xae, 0xd6, 0x42, 0x67, 0xf5, 0x10, 0x0d, 0x9f, 0xc0, 0x72, 0xd8, 0xaf, 0x4d, 0xa5, 0xcb, + 0x32, 0xcc, 0x79, 0xf6, 0x39, 0x91, 0xb1, 0x86, 0x37, 0xa4, 0xed, 0x7c, 0x9f, 0x37, 0x95, 0xed, + 0x8c, 0x40, 0x18, 0x3b, 0x1d, 0xd3, 0xea, 0x4b, 0x37, 0x96, 0xbc, 0x03, 0xf1, 0x06, 0x3e, 0x80, + 0xd5, 0xa8, 0x67, 0x9b, 0x4a, 0xe5, 0x17, 0xfc, 0x2c, 0x25, 0x39, 0xbf, 0xa9, 0xe4, 0x7e, 0x1c, + 0xf8, 0x25, 0xc5, 0xb7, 0x4d, 0x25, 0x52, 0x87, 0x7a, 0x92, 0xab, 0xfb, 0x3a, 0x8e, 0x8e, 0xef, + 0xf9, 0xa6, 0x12, 0xe6, 0x06, 0xc2, 0xa6, 0x5f, 0xfe, 0xc0, 0x5d, 0x65, 0xaf, 0x74, 0x57, 0xe2, + 0x90, 0x04, 0x0e, 0xf5, 0x1b, 0xd8, 0x74, 0x02, 0x23, 0xf0, 0xe5, 0xd3, 0x62, 0xd0, 0x70, 0xe6, + 0x63, 0xb0, 0x86, 0xdc, 0xd8, 0x6a, 0x04, 0x98, 0x6a, 0x31, 0x3e, 0x09, 0xdc, 0x78, 0x2c, 0x48, + 0x4c, 0x25, 0xf8, 0x53, 0x58, 0x4f, 0x8f, 0x0f, 0xd3, 0x48, 0x7e, 0xd0, 0x80, 0x82, 0x7f, 0x19, + 0x52, 0xbe, 0x55, 0x2b, 0x42, 0xfe, 0xe0, 0xf0, 0xf8, 0xa8, 0xb9, 0xdd, 0xe2, 0x1f, 0xab, 0x6d, + 0x1f, 0xea, 0xfa, 0xf3, 0xa3, 0x76, 0x35, 0xb3, 0xf5, 0xaf, 0xb3, 0x90, 0xd9, 0x7b, 0x81, 0x3e, + 0x83, 0x39, 0xfe, 0xf5, 0xc5, 0x15, 0x9f, 0xdc, 0xd4, 0xaf, 0xfa, 0xc0, 0x04, 0xdf, 0xf8, 0xe9, + 0x7f, 0xfe, 0xcf, 0x5f, 0x67, 0x16, 0x71, 0xa9, 0x31, 0xf9, 0x6e, 0xe3, 0x7c, 0xd2, 0x60, 0x61, + 0xea, 0x91, 0xf6, 0x00, 0xf5, 0xa1, 0xc8, 0x38, 0x8f, 0x3d, 0x87, 0x18, 0xc3, 0xaf, 0x0e, 0x70, + 0x9b, 0x01, 0xdc, 0xc0, 0x48, 0x05, 0x70, 0x99, 0xd0, 0x47, 0xda, 0x83, 0xf7, 0x35, 0xf4, 0x31, + 0x64, 0x8f, 0xc6, 0x1e, 0x4a, 0xfd, 0xe6, 0xa7, 0x9e, 0xfe, 0x71, 0x0b, 0x5e, 0x61, 0xc2, 0x17, + 0x30, 0x08, 0xe1, 0xa3, 0xb1, 0x47, 0x75, 0xff, 0x31, 0x14, 0xd5, 0x4f, 0x53, 0xae, 0xfd, 0x10, + 0xa8, 0x7e, 0xfd, 0x67, 0x2f, 0xb1, 0x79, 0xf0, 0x8f, 0x67, 0x7c, 0x73, 0x7d, 0x0c, 0xd9, 0xf6, + 0x85, 0x85, 0x52, 0x3f, 0x13, 0xaa, 0xa7, 0x7f, 0x09, 0x13, 0x9b, 0x85, 0x77, 0x61, 0x51, 0x91, + 0x7f, 0x24, 0x3e, 0x82, 0xe9, 0x7a, 0xe8, 0x4e, 0xc2, 0x47, 0x10, 0xea, 0x73, 0x7f, 0x7d, 0x3d, + 0x9d, 0x41, 0x80, 0xdc, 0x62, 0x20, 0xab, 0x78, 0x51, 0x80, 0x74, 0x7d, 0x96, 0x47, 0xda, 0x83, + 0xad, 0x2e, 0xcc, 0xb1, 0xa7, 0x34, 0xf4, 0xb9, 0xfc, 0x51, 0x4f, 0x78, 0x53, 0x4c, 0x59, 0xf0, + 0xd0, 0x23, 0x1c, 0x5e, 0x66, 0x40, 0x15, 0x5c, 0xa0, 0x40, 0xec, 0x21, 0xed, 0x91, 0xf6, 0x60, + 0x43, 0x7b, 0x5f, 0xdb, 0xfa, 0xf5, 0x1c, 0xcc, 0xb1, 0x1a, 0x32, 0x3a, 0x07, 0x08, 0x9e, 0x95, + 0xa2, 0xb3, 0x8b, 0x3d, 0x54, 0x45, 0x67, 0x17, 0x7f, 0x91, 0xc2, 0x75, 0x06, 0xba, 0x8c, 0x17, + 0x28, 0x28, 0x2b, 0x4d, 0x37, 0x58, 0xb5, 0x9d, 0xda, 0xf1, 0xaf, 0x34, 0x51, 0x42, 0xe7, 0x87, + 0x16, 0x25, 0x49, 0x0b, 0xbd, 0x2d, 0x45, 0xb7, 0x43, 0xc2, 0xbb, 0x12, 0xfe, 0x3e, 0x03, 0x6c, + 0xe0, 0x6a, 0x00, 0xe8, 0x30, 0x8e, 0x47, 0xda, 0x83, 0xcf, 0x6b, 0x78, 0x49, 0x58, 0x39, 0xd2, + 0x83, 0x7e, 0x02, 0x95, 0xf0, 0xdb, 0x09, 0xba, 0x9b, 0x80, 0x15, 0x7d, 0x82, 0xa9, 0xbf, 0x75, + 0x35, 0x93, 0xd0, 0x69, 0x8d, 0xe9, 0x24, 0xc0, 0x39, 0xf2, 0x39, 0x21, 0x23, 0x83, 0x32, 0x89, + 0x35, 0x40, 0x7f, 0xaf, 0x89, 0xa7, 0xad, 0xe0, 0x31, 0x04, 0x25, 0x49, 0x8f, 0x3d, 0xb5, 0xd4, + 0xef, 0x5d, 0xc3, 0x25, 0x94, 0xf8, 0x7d, 0xa6, 0xc4, 0x07, 0x78, 0x39, 0x50, 0xc2, 0x33, 0x87, + 0xc4, 0xb3, 0x85, 0x16, 0x9f, 0xdf, 0xc2, 0x37, 0x42, 0xc6, 0x09, 0xf5, 0x06, 0x8b, 0xc5, 0x1f, + 0x34, 0x12, 0x17, 0x2b, 0xf4, 0x40, 0x92, 0xb8, 0x58, 0xe1, 0xd7, 0x90, 0xa4, 0xc5, 0xe2, 0xcf, + 0x17, 0x49, 0x8b, 0xe5, 0xf7, 0x6c, 0xfd, 0xdf, 0x2c, 0xe4, 0xb7, 0xf9, 0x87, 0xeb, 0xc8, 0x86, + 0x82, 0xff, 0x1e, 0x80, 0xd6, 0x92, 0x0a, 0x9a, 0xc1, 0xfd, 0xa9, 0x7e, 0x27, 0xb5, 0x5f, 0x28, + 0xf4, 0x26, 0x53, 0xe8, 0x0d, 0xbc, 0x4a, 0x91, 0xc5, 0xb7, 0xf1, 0x0d, 0x5e, 0x35, 0x6b, 0x18, + 0xbd, 0x1e, 0x35, 0xc4, 0x1f, 0x43, 0x49, 0x2d, 0xd8, 0xa3, 0x37, 0x13, 0x8b, 0xa8, 0x6a, 0xcd, + 0xbf, 0x8e, 0xaf, 0x62, 0x11, 0xc8, 0x6f, 0x31, 0xe4, 0x35, 0x7c, 0x33, 0x01, 0xd9, 0x61, 0xac, + 0x21, 0x70, 0x5e, 0x6c, 0x4f, 0x06, 0x0f, 0xd5, 0xf2, 0x93, 0xc1, 0xc3, 0xb5, 0xfa, 0x2b, 0xc1, + 0xc7, 0x8c, 0x95, 0x82, 0xbb, 0x00, 0x41, 0xc9, 0x1c, 0x25, 0xda, 0x52, 0xb9, 0x40, 0x46, 0x9d, + 0x43, 0xbc, 0xda, 0x8e, 0x31, 0x83, 0x15, 0xfb, 0x2e, 0x02, 0x3b, 0x30, 0x5d, 0x8f, 0x1f, 0xcc, + 0x72, 0xa8, 0x06, 0x8e, 0x12, 0xe7, 0x13, 0x2e, 0xa4, 0xd7, 0xef, 0x5e, 0xc9, 0x23, 0xd0, 0xef, + 0x31, 0xf4, 0x3b, 0xb8, 0x9e, 0x80, 0x3e, 0xe2, 0xbc, 0x74, 0xb3, 0xfd, 0x7f, 0x0e, 0x8a, 0xcf, + 0x0c, 0xd3, 0xf2, 0x88, 0x65, 0x58, 0x5d, 0x82, 0x4e, 0x60, 0x8e, 0xa5, 0x04, 0x51, 0x47, 0xac, + 0xd6, 0x87, 0xa3, 0x8e, 0x38, 0x54, 0x3c, 0xc5, 0xeb, 0x0c, 0xb8, 0x8e, 0x57, 0x28, 0xf0, 0x30, + 0x10, 0xdd, 0x60, 0x35, 0x4f, 0x3a, 0xe9, 0x53, 0xc8, 0x89, 0x67, 0xc5, 0x88, 0xa0, 0x50, 0x95, + 0xa9, 0x7e, 0x2b, 0xb9, 0x33, 0x69, 0x2f, 0xab, 0x30, 0x2e, 0xe3, 0xa3, 0x38, 0x13, 0x80, 0xa0, + 0x98, 0x1f, 0x5d, 0xd1, 0x58, 0xed, 0xbf, 0xbe, 0x9e, 0xce, 0x90, 0x64, 0x53, 0x15, 0xb3, 0xe7, + 0xf3, 0x52, 0xdc, 0x3f, 0x84, 0xd9, 0xa7, 0x86, 0x7b, 0x86, 0x22, 0xb1, 0x57, 0xf9, 0x28, 0xad, + 0x5e, 0x4f, 0xea, 0x12, 0x28, 0x77, 0x18, 0xca, 0x4d, 0xee, 0xca, 0x54, 0x94, 0x33, 0xc3, 0xa5, + 0x41, 0x0d, 0xf5, 0x20, 0xc7, 0xbf, 0x51, 0x8b, 0xda, 0x2f, 0xf4, 0x9d, 0x5b, 0xd4, 0x7e, 0xe1, + 0xcf, 0xda, 0xae, 0x47, 0x19, 0xc1, 0xbc, 0xfc, 0x28, 0x0c, 0x45, 0xbe, 0x10, 0x88, 0x7c, 0x40, + 0x56, 0x5f, 0x4b, 0xeb, 0x16, 0x58, 0x77, 0x19, 0xd6, 0x6d, 0x5c, 0x8b, 0xad, 0x95, 0xe0, 0xe4, + 0x29, 0xd9, 0x4f, 0x00, 0x82, 0xf7, 0x8f, 0xd8, 0x09, 0x8c, 0x3e, 0xa5, 0xc4, 0x4e, 0x60, 0xec, + 0xe9, 0x04, 0x6f, 0x32, 0xdc, 0x0d, 0x7c, 0x37, 0x8a, 0xeb, 0x39, 0x86, 0xe5, 0x9e, 0x12, 0xe7, + 0x3d, 0x5e, 0xce, 0x75, 0xcf, 0xcc, 0x11, 0x9d, 0xb2, 0x03, 0x05, 0xbf, 0xbc, 0x1d, 0xf5, 0xb6, + 0xd1, 0xb2, 0x7b, 0xd4, 0xdb, 0xc6, 0xea, 0xe2, 0x61, 0xb7, 0x13, 0xda, 0x2d, 0x92, 0x95, 0x1e, + 0xc0, 0x5f, 0x56, 0x61, 0x96, 0xa6, 0xf7, 0x34, 0x39, 0x09, 0x0a, 0x34, 0xd1, 0xd9, 0xc7, 0xca, + 0xb5, 0xd1, 0xd9, 0xc7, 0x6b, 0x3b, 0xe1, 0xe4, 0x84, 0xde, 0xe6, 0x1a, 0xbc, 0x16, 0x42, 0x67, + 0x6a, 0x43, 0x51, 0xa9, 0xe0, 0xa0, 0x04, 0x61, 0xe1, 0x3a, 0x70, 0x34, 0xdc, 0x25, 0x94, 0x7f, + 0xf0, 0x1b, 0x0c, 0x6f, 0x85, 0x87, 0x3b, 0x86, 0xd7, 0xe3, 0x1c, 0x14, 0x50, 0xcc, 0x4e, 0x9c, + 0xfb, 0x84, 0xd9, 0x85, 0xcf, 0xfe, 0x7a, 0x3a, 0x43, 0xea, 0xec, 0x82, 0x83, 0xff, 0x12, 0x4a, + 0x6a, 0x1d, 0x07, 0x25, 0x28, 0x1f, 0xa9, 0x5d, 0x47, 0xe3, 0x48, 0x52, 0x19, 0x28, 0xec, 0xd9, + 0x18, 0xa4, 0xa1, 0xb0, 0x51, 0xe0, 0x01, 0xe4, 0x45, 0x61, 0x27, 0xc9, 0xa4, 0xe1, 0x3a, 0x77, + 0x92, 0x49, 0x23, 0x55, 0xa1, 0x70, 0xf6, 0xcc, 0x10, 0xe9, 0xdd, 0x55, 0xc6, 0x6a, 0x81, 0xf6, + 0x84, 0x78, 0x69, 0x68, 0x41, 0xc9, 0x34, 0x0d, 0x4d, 0xa9, 0x1b, 0xa4, 0xa1, 0xf5, 0x89, 0x27, + 0xfc, 0x81, 0xbc, 0x8f, 0xa3, 0x14, 0x61, 0x6a, 0x7c, 0xc4, 0x57, 0xb1, 0x24, 0x5d, 0x6e, 0x02, + 0x40, 0x19, 0x1c, 0x2f, 0x00, 0x82, 0xb2, 0x53, 0x34, 0x63, 0x4d, 0x2c, 0xb7, 0x47, 0x33, 0xd6, + 0xe4, 0xca, 0x55, 0xd8, 0xf7, 0x05, 0xb8, 0xfc, 0x6e, 0x45, 0x91, 0x7f, 0xa1, 0x01, 0x8a, 0x57, + 0xa8, 0xd0, 0xc3, 0x64, 0xe9, 0x89, 0x45, 0xfc, 0xfa, 0xbb, 0xaf, 0xc7, 0x9c, 0x14, 0xce, 0x02, + 0x95, 0xba, 0x8c, 0x7b, 0xf4, 0x92, 0x2a, 0xf5, 0xe7, 0x1a, 0x94, 0x43, 0xe5, 0x2d, 0x74, 0x3f, + 0x65, 0x4d, 0x23, 0xb5, 0xfd, 0xfa, 0xdb, 0xd7, 0xf2, 0x25, 0xa5, 0xf2, 0xca, 0x0e, 0x90, 0x77, + 0x9a, 0x9f, 0x69, 0x50, 0x09, 0x97, 0xc3, 0x50, 0x8a, 0xec, 0xd8, 0xdb, 0x40, 0x7d, 0xe3, 0x7a, + 0xc6, 0xab, 0x97, 0x27, 0xb8, 0xce, 0x0c, 0x20, 0x2f, 0x0a, 0x68, 0x49, 0x1b, 0x3f, 0xfc, 0xaa, + 0x90, 0xb4, 0xf1, 0x23, 0xd5, 0xb7, 0x84, 0x8d, 0xef, 0xd8, 0x03, 0xa2, 0x1c, 0x33, 0x51, 0x61, + 0x4b, 0x43, 0xbb, 0xfa, 0x98, 0x45, 0xca, 0x73, 0x69, 0x68, 0xc1, 0x31, 0x93, 0xa5, 0x35, 0x94, + 0x22, 0xec, 0x9a, 0x63, 0x16, 0xad, 0xcc, 0x25, 0x1c, 0x33, 0x06, 0xa8, 0x1c, 0xb3, 0xa0, 0x08, + 0x96, 0x74, 0xcc, 0x62, 0x8f, 0x24, 0x49, 0xc7, 0x2c, 0x5e, 0x47, 0x4b, 0x58, 0x47, 0x86, 0x1b, + 0x3a, 0x66, 0x4b, 0x09, 0xf5, 0x32, 0xf4, 0x6e, 0x8a, 0x11, 0x13, 0xdf, 0x5e, 0xea, 0xef, 0xbd, + 0x26, 0x77, 0xea, 0x1e, 0xe7, 0xe6, 0x97, 0x7b, 0xfc, 0x6f, 0x34, 0x58, 0x4e, 0xaa, 0xb5, 0xa1, + 0x14, 0x9c, 0x94, 0x37, 0x9b, 0xfa, 0xe6, 0xeb, 0xb2, 0x5f, 0x6d, 0x2d, 0x7f, 0xd7, 0x3f, 0xae, + 0xfe, 0xcb, 0x17, 0x6b, 0xda, 0xbf, 0x7f, 0xb1, 0xa6, 0xfd, 0xd7, 0x17, 0x6b, 0xda, 0xdf, 0xfe, + 0xf7, 0xda, 0xcc, 0x49, 0x8e, 0xfd, 0x6f, 0xe8, 0xef, 0xfe, 0x36, 0x00, 0x00, 0xff, 0xff, 0x47, + 0x65, 0xfe, 0x91, 0x92, 0x3d, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -6428,6 +6448,8 @@ const _ = grpc.SupportPackageIsVersion4 type KVClient interface { // Range gets the keys in the range from the key-value store. Range(ctx context.Context, in *RangeRequest, opts ...grpc.CallOption) (*RangeResponse, error) + // RangeStream gets the keys in the range stream from the key-value store. + RangeStream(ctx context.Context, in *RangeRequest, opts ...grpc.CallOption) (KV_RangeStreamClient, error) // Put puts the given key into the key-value store. // A put request increments the revision of the key-value store // and generates one event in the event history. @@ -6464,6 +6486,38 @@ func (c *kVClient) Range(ctx context.Context, in *RangeRequest, opts ...grpc.Cal return out, nil } +func (c *kVClient) RangeStream(ctx context.Context, in *RangeRequest, opts ...grpc.CallOption) (KV_RangeStreamClient, error) { + stream, err := c.cc.NewStream(ctx, &_KV_serviceDesc.Streams[0], "/etcdserverpb.KV/RangeStream", opts...) + if err != nil { + return nil, err + } + x := &kVRangeStreamClient{stream} + if err := x.ClientStream.SendMsg(in); err != nil { + return nil, err + } + if err := x.ClientStream.CloseSend(); err != nil { + return nil, err + } + return x, nil +} + +type KV_RangeStreamClient interface { + Recv() (*RangeResponse, error) + grpc.ClientStream +} + +type kVRangeStreamClient struct { + grpc.ClientStream +} + +func (x *kVRangeStreamClient) Recv() (*RangeResponse, error) { + m := new(RangeResponse) + if err := x.ClientStream.RecvMsg(m); err != nil { + return nil, err + } + return m, nil +} + func (c *kVClient) Put(ctx context.Context, in *PutRequest, opts ...grpc.CallOption) (*PutResponse, error) { out := new(PutResponse) err := c.cc.Invoke(ctx, "/etcdserverpb.KV/Put", in, out, opts...) @@ -6504,6 +6558,8 @@ func (c *kVClient) Compact(ctx context.Context, in *CompactionRequest, opts ...g type KVServer interface { // Range gets the keys in the range from the key-value store. Range(context.Context, *RangeRequest) (*RangeResponse, error) + // RangeStream gets the keys in the range stream from the key-value store. + RangeStream(*RangeRequest, KV_RangeStreamServer) error // Put puts the given key into the key-value store. // A put request increments the revision of the key-value store // and generates one event in the event history. @@ -6530,6 +6586,9 @@ type UnimplementedKVServer struct { func (*UnimplementedKVServer) Range(ctx context.Context, req *RangeRequest) (*RangeResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method Range not implemented") } +func (*UnimplementedKVServer) RangeStream(req *RangeRequest, srv KV_RangeStreamServer) error { + return status.Errorf(codes.Unimplemented, "method RangeStream not implemented") +} func (*UnimplementedKVServer) Put(ctx context.Context, req *PutRequest) (*PutResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method Put not implemented") } @@ -6565,6 +6624,27 @@ func _KV_Range_Handler(srv interface{}, ctx context.Context, dec func(interface{ return interceptor(ctx, in, info, handler) } +func _KV_RangeStream_Handler(srv interface{}, stream grpc.ServerStream) error { + m := new(RangeRequest) + if err := stream.RecvMsg(m); err != nil { + return err + } + return srv.(KVServer).RangeStream(m, &kVRangeStreamServer{stream}) +} + +type KV_RangeStreamServer interface { + Send(*RangeResponse) error + grpc.ServerStream +} + +type kVRangeStreamServer struct { + grpc.ServerStream +} + +func (x *kVRangeStreamServer) Send(m *RangeResponse) error { + return x.ServerStream.SendMsg(m) +} + func _KV_Put_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(PutRequest) if err := dec(in); err != nil { @@ -6662,7 +6742,13 @@ var _KV_serviceDesc = grpc.ServiceDesc{ Handler: _KV_Compact_Handler, }, }, - Streams: []grpc.StreamDesc{}, + Streams: []grpc.StreamDesc{ + { + StreamName: "RangeStream", + Handler: _KV_RangeStream_Handler, + ServerStreams: true, + }, + }, Metadata: "rpc.proto", } @@ -8515,6 +8601,20 @@ func (m *RangeResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } + if m.TotalSize != 0 { + i = encodeVarintRpc(dAtA, i, uint64(m.TotalSize)) + i-- + dAtA[i] = 0x6 + i-- + dAtA[i] = 0xb0 + } + if m.SubCount != 0 { + i = encodeVarintRpc(dAtA, i, uint64(m.SubCount)) + i-- + dAtA[i] = 0x6 + i-- + dAtA[i] = 0xa8 + } if m.Count != 0 { i = encodeVarintRpc(dAtA, i, uint64(m.Count)) i-- @@ -13014,6 +13114,12 @@ func (m *RangeResponse) Size() (n int) { if m.Count != 0 { n += 1 + sovRpc(uint64(m.Count)) } + if m.SubCount != 0 { + n += 2 + sovRpc(uint64(m.SubCount)) + } + if m.TotalSize != 0 { + n += 2 + sovRpc(uint64(m.TotalSize)) + } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } @@ -15601,6 +15707,44 @@ func (m *RangeResponse) Unmarshal(dAtA []byte) error { break } } + case 101: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field SubCount", wireType) + } + m.SubCount = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowRpc + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.SubCount |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 102: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field TotalSize", wireType) + } + m.TotalSize = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowRpc + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.TotalSize |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } default: iNdEx = preIndex skippy, err := skipRpc(dAtA[iNdEx:]) diff --git a/api/etcdserverpb/rpc.proto b/api/etcdserverpb/rpc.proto index e63d4057bfb..d6944e17d11 100644 --- a/api/etcdserverpb/rpc.proto +++ b/api/etcdserverpb/rpc.proto @@ -20,6 +20,14 @@ service KV { }; } + // RangeStream gets the keys in the range stream from the key-value store. + rpc RangeStream(RangeRequest) returns (stream RangeResponse) { + option (google.api.http) = { + post: "/v3/kv/rangestream" + body: "*" + }; + } + // Put puts the given key into the key-value store. // A put request increments the revision of the key-value store // and generates one event in the event history. @@ -475,6 +483,10 @@ message RangeResponse { bool more = 3; // count is set to the number of keys within the range when requested. int64 count = 4; + + int64 sub_count = 101; + + int64 total_size = 102; } message PutRequest { diff --git a/client/mock/mockserver/mockserver.go b/client/mock/mockserver/mockserver.go index 21a8b013b40..80135291296 100644 --- a/client/mock/mockserver/mockserver.go +++ b/client/mock/mockserver/mockserver.go @@ -171,6 +171,10 @@ func (m *mockKVServer) Range(context.Context, *pb.RangeRequest) (*pb.RangeRespon return &pb.RangeResponse{}, nil } +func (m *mockKVServer) RangeStream(r *pb.RangeRequest, rss pb.KV_RangeStreamServer) error { + return nil +} + func (m *mockKVServer) Put(context.Context, *pb.PutRequest) (*pb.PutResponse, error) { return &pb.PutResponse{}, nil } diff --git a/clientv3/kv.go b/clientv3/kv.go index 5e9fb7d4589..4ce72d495e2 100644 --- a/clientv3/kv.go +++ b/clientv3/kv.go @@ -16,18 +16,23 @@ package clientv3 import ( "context" + "go.uber.org/zap" + "io" + "time" pb "go.etcd.io/etcd/api/v3/etcdserverpb" + v3rpc "go.etcd.io/etcd/api/v3/v3rpc/rpctypes" "google.golang.org/grpc" ) type ( - CompactResponse pb.CompactionResponse - PutResponse pb.PutResponse - GetResponse pb.RangeResponse - DeleteResponse pb.DeleteRangeResponse - TxnResponse pb.TxnResponse + CompactResponse pb.CompactionResponse + PutResponse pb.PutResponse + GetResponse pb.RangeResponse + GetStreamResponse pb.RangeResponse + DeleteResponse pb.DeleteRangeResponse + TxnResponse pb.TxnResponse ) type KV interface { @@ -47,6 +52,8 @@ type KV interface { // When passed WithSort(), the keys will be sorted. Get(ctx context.Context, key string, opts ...OpOption) (*GetResponse, error) + GetStream(ctx context.Context, key string, opts ...OpOption) (*GetStreamResponse, error) + // Delete deletes a key, or optionally using WithRange(end), [key, end). Delete(ctx context.Context, key string, opts ...OpOption) (*DeleteResponse, error) @@ -65,16 +72,18 @@ type KV interface { } type OpResponse struct { - put *PutResponse - get *GetResponse - del *DeleteResponse - txn *TxnResponse + put *PutResponse + get *GetResponse + getStream *GetStreamResponse + del *DeleteResponse + txn *TxnResponse } -func (op OpResponse) Put() *PutResponse { return op.put } -func (op OpResponse) Get() *GetResponse { return op.get } -func (op OpResponse) Del() *DeleteResponse { return op.del } -func (op OpResponse) Txn() *TxnResponse { return op.txn } +func (op OpResponse) Put() *PutResponse { return op.put } +func (op OpResponse) Get() *GetResponse { return op.get } +func (op OpResponse) GetStream() *GetStreamResponse { return op.getStream } +func (op OpResponse) Del() *DeleteResponse { return op.del } +func (op OpResponse) Txn() *TxnResponse { return op.txn } func (resp *PutResponse) OpResponse() OpResponse { return OpResponse{put: resp} @@ -82,6 +91,9 @@ func (resp *PutResponse) OpResponse() OpResponse { func (resp *GetResponse) OpResponse() OpResponse { return OpResponse{get: resp} } +func (resp *GetStreamResponse) OpResponse() OpResponse { + return OpResponse{getStream: resp} +} func (resp *DeleteResponse) OpResponse() OpResponse { return OpResponse{del: resp} } @@ -92,10 +104,11 @@ func (resp *TxnResponse) OpResponse() OpResponse { type kv struct { remote pb.KVClient callOpts []grpc.CallOption + lg *zap.Logger } func NewKV(c *Client) KV { - api := &kv{remote: RetryKVClient(c)} + api := &kv{remote: RetryKVClient(c), lg: c.lg} if c != nil { api.callOpts = c.callOpts } @@ -103,7 +116,11 @@ func NewKV(c *Client) KV { } func NewKVFromKVClient(remote pb.KVClient, c *Client) KV { - api := &kv{remote: remote} + var lg *zap.Logger + if c != nil { + lg = c.lg + } + api := &kv{remote: remote, lg: lg} if c != nil { api.callOpts = c.callOpts } @@ -120,6 +137,11 @@ func (kv *kv) Get(ctx context.Context, key string, opts ...OpOption) (*GetRespon return r.get, toErr(ctx, err) } +func (kv *kv) GetStream(ctx context.Context, key string, opts ...OpOption) (*GetStreamResponse, error) { + r, err := kv.Do(ctx, OpGetStream(key, opts...)) + return r.getStream, toErr(ctx, err) +} + func (kv *kv) Delete(ctx context.Context, key string, opts ...OpOption) (*DeleteResponse, error) { r, err := kv.Do(ctx, OpDelete(key, opts...)) return r.del, toErr(ctx, err) @@ -150,6 +172,14 @@ func (kv *kv) Do(ctx context.Context, op Op) (OpResponse, error) { if err == nil { return OpResponse{get: (*GetResponse)(resp)}, nil } + case tRangeStream: + var rangeStreamClient pb.KV_RangeStreamClient + var resp *pb.RangeResponse + rangeStreamClient, err = kv.openRangeStreamClient(ctx, op.toRangeStreamRequest(), kv.callOpts...) + resp, err = kv.serveRangeStream(ctx, rangeStreamClient) + if err == nil { + return OpResponse{getStream: (*GetStreamResponse)(resp)}, nil + } case tPut: var resp *pb.PutResponse r := &pb.PutRequest{Key: op.key, Value: op.val, Lease: int64(op.leaseID), PrevKv: op.prevKV, IgnoreValue: op.ignoreValue, IgnoreLease: op.ignoreLease} @@ -175,3 +205,111 @@ func (kv *kv) Do(ctx context.Context, op Op) (OpResponse, error) { } return OpResponse{}, toErr(ctx, err) } + +// openRangeStreamClient retries opening a rangeStream client until success or halt. +// manually retry in case "rsc==nil && err==nil" +// TODO: remove FailFast=false +func (kv *kv) openRangeStreamClient(ctx context.Context, in *pb.RangeRequest, opts ...grpc.CallOption) (rsc pb.KV_RangeStreamClient, err error) { + backoff := time.Millisecond + for { + select { + case <-ctx.Done(): + if err == nil { + return nil, ctx.Err() + } + return nil, err + default: + } + if rsc, err = kv.remote.RangeStream(ctx, in, opts...); rsc != nil && err == nil { + break + } + if isHaltErr(ctx, err) { + return nil, v3rpc.Error(err) + } + if isUnavailableErr(ctx, err) { + // retry, but backoff + if backoff < maxBackoff { + // 25% backoff factor + backoff = backoff + backoff/4 + if backoff > maxBackoff { + backoff = maxBackoff + } + } + time.Sleep(backoff) + } + } + return rsc, nil +} + +func (kv *kv) serveRangeStream(ctx context.Context, rsc pb.KV_RangeStreamClient) (*pb.RangeResponse, error) { + rspC := make(chan *pb.RangeResponse) + errC := make(chan error) + + mainRSP := &pb.RangeResponse{} + mainRSP.Header = &pb.ResponseHeader{} + + go kv.handleRangeStream(ctx, rsc, rspC, errC) + +Loop: + for { + select { + case subRsp := <-rspC: + if subRsp == nil { + break Loop + } + + mainRSP.Kvs = append(mainRSP.Kvs, subRsp.Kvs...) + mainRSP.Count = subRsp.Count + mainRSP.Header = subRsp.Header + case err := <-errC: + return nil, err + case <-ctx.Done(): + return nil, ctx.Err() + } + } + + return mainRSP, nil +} + +func (kv *kv) handleRangeStream(ctx context.Context, rsc pb.KV_RangeStreamClient, rspC chan *pb.RangeResponse, errC chan error) { + defer func() { + if err := recover(); err != nil { + switch e := err.(type) { + case error: + kv.lg.Error("kv handleRangeStream() panic error", zap.Error(e)) + } + } + }() + + defer func() { + close(rspC) + close(errC) + }() + + for { + resp, err := rsc.Recv() + if err != nil { + if err == io.EOF { + select { + case rspC <- nil: + case <-ctx.Done(): + return + } + break + } + + select { + case errC <- err: + case <-ctx.Done(): + } + return + } + + select { + case rspC <- resp: + case <-ctx.Done(): + return + } + } + return +} diff --git a/clientv3/leasing/cache.go b/clientv3/leasing/cache.go index d2683a54900..dd776da9bb3 100644 --- a/clientv3/leasing/cache.go +++ b/clientv3/leasing/cache.go @@ -136,6 +136,18 @@ func (lc *leaseCache) Add(key string, resp *v3.GetResponse, op v3.Op) *v3.GetRes return ret } +func (lc *leaseCache) AddStream(key string, resp *v3.GetStreamResponse, op v3.Op) *v3.GetStreamResponse { + lk := &leaseKey{(*v3.GetResponse)(resp), resp.Header.Revision, closedCh} + lc.mu.Lock() + if lc.header == nil || lc.header.Revision < resp.Header.Revision { + lc.header = resp.Header + } + lc.entries[key] = lk + ret := lk.getStream(op) + lc.mu.Unlock() + return ret +} + func (lc *leaseCache) Update(key, val []byte, respHeader *v3pb.ResponseHeader) { li := lc.entries[string(key)] if li == nil { @@ -216,6 +228,27 @@ func (lc *leaseCache) Get(ctx context.Context, op v3.Op) (*v3.GetResponse, bool) return ret, true } +func (lc *leaseCache) GetStream(ctx context.Context, op v3.Op) (*v3.GetStreamResponse, bool) { + if isBadOp(op) { + return nil, false + } + key := string(op.KeyBytes()) + li, wc := lc.notify(key) + if li == nil { + return nil, true + } + select { + case <-wc: + case <-ctx.Done(): + return nil, true + } + lc.mu.RLock() + lk := *li + ret := lk.getStream(op) + lc.mu.RUnlock() + return ret, true +} + func (lk *leaseKey) get(op v3.Op) *v3.GetResponse { ret := *lk.response ret.Header = copyHeader(ret.Header) @@ -239,6 +272,27 @@ func (lk *leaseKey) get(op v3.Op) *v3.GetResponse { return &ret } +func (lk *leaseKey) getStream(op v3.Op) *v3.GetStreamResponse { + ret := *lk.response + ret.Header = copyHeader(ret.Header) + empty := len(ret.Kvs) == 0 + if empty { + ret.Kvs = nil + } else { + kv := *ret.Kvs[0] + kv.Key = make([]byte, len(kv.Key)) + copy(kv.Key, ret.Kvs[0].Key) + if !op.IsKeysOnly() { + kv.Value = make([]byte, len(kv.Value)) + copy(kv.Value, ret.Kvs[0].Value) + } + ret.Kvs = []*mvccpb.KeyValue{&kv} + } + + retNew := (v3.GetStreamResponse)(ret) + return &retNew +} + func (lc *leaseCache) notify(key string) (*leaseKey, <-chan struct{}) { lc.mu.RLock() defer lc.mu.RUnlock() diff --git a/clientv3/leasing/kv.go b/clientv3/leasing/kv.go index d7bf95fe88f..e2c2d089277 100644 --- a/clientv3/leasing/kv.go +++ b/clientv3/leasing/kv.go @@ -86,6 +86,10 @@ func (lkv *leasingKV) Get(ctx context.Context, key string, opts ...v3.OpOption) return lkv.get(ctx, v3.OpGet(key, opts...)) } +func (lkv *leasingKV) GetStream(ctx context.Context, key string, opts ...v3.OpOption) (*v3.GetStreamResponse, error) { + return lkv.getStream(ctx, v3.OpGetStream(key, opts...)) +} + func (lkv *leasingKV) Put(ctx context.Context, key, val string, opts ...v3.OpOption) (*v3.PutResponse, error) { return lkv.put(ctx, v3.OpPut(key, val, opts...)) } @@ -99,6 +103,9 @@ func (lkv *leasingKV) Do(ctx context.Context, op v3.Op) (v3.OpResponse, error) { case op.IsGet(): resp, err := lkv.get(ctx, op) return resp.OpResponse(), err + case op.IsGetStream(): + resp, err := lkv.getStream(ctx, op) + return resp.OpResponse(), err case op.IsPut(): resp, err := lkv.put(ctx, op) return resp.OpResponse(), err @@ -292,6 +299,41 @@ func (lkv *leasingKV) acquire(ctx context.Context, key string, op v3.Op) (*v3.Tx return nil, ctx.Err() } +func (lkv *leasingKV) acquireStream(ctx context.Context, key string, op v3.Op) (*v3.TxnResponse, error) { + for ctx.Err() == nil { + if err := lkv.waitSession(ctx); err != nil { + return nil, err + } + lcmp := v3.Cmp{Key: []byte(key), Target: pb.Compare_LEASE} + resp, err := lkv.kv.Txn(ctx).If( + v3.Compare(v3.CreateRevision(lkv.pfx+key), "=", 0), + v3.Compare(lcmp, "=", 0)). + Then( + op, + v3.OpPut(lkv.pfx+key, "", v3.WithLease(lkv.leaseID()))). + Else( + op, + v3.OpGetStream(lkv.pfx+key), + ).Commit() + if err == nil { + if !resp.Succeeded { + kvs := resp.Responses[1].GetResponseRange().Kvs + // if txn failed since already owner, lease is acquired + resp.Succeeded = len(kvs) > 0 && v3.LeaseID(kvs[0].Lease) == lkv.leaseID() + } + return resp, nil + } + // retry if transient error + if _, ok := err.(rpctypes.EtcdError); ok { + return nil, err + } + if ev, ok := status.FromError(err); ok && ev.Code() != codes.Unavailable { + return nil, err + } + } + return nil, ctx.Err() +} + func (lkv *leasingKV) get(ctx context.Context, op v3.Op) (*v3.GetResponse, error) { do := func() (*v3.GetResponse, error) { r, err := lkv.kv.Do(ctx, op) @@ -331,6 +373,45 @@ func (lkv *leasingKV) get(ctx context.Context, op v3.Op) (*v3.GetResponse, error return getResp, nil } +func (lkv *leasingKV) getStream(ctx context.Context, op v3.Op) (*v3.GetStreamResponse, error) { + do := func() (*v3.GetStreamResponse, error) { + r, err := lkv.kv.Do(ctx, op) + return r.GetStream(), err + } + if !lkv.readySession() { + return do() + } + + if resp, ok := lkv.leases.GetStream(ctx, op); resp != nil { + return resp, nil + } else if !ok || op.IsSerializable() { + // must be handled by server or can skip linearization + return do() + } + + key := string(op.KeyBytes()) + if !lkv.leases.MayAcquire(key) { + resp, err := lkv.kv.Do(ctx, op) + return resp.GetStream(), err + } + + resp, err := lkv.acquireStream(ctx, key, v3.OpGetStream(key)) + if err != nil { + return nil, err + } + getResp := (*v3.GetStreamResponse)(resp.Responses[0].GetResponseRange()) + getResp.Header = resp.Header + if resp.Succeeded { + getResp = lkv.leases.AddStream(key, getResp, op) + lkv.wg.Add(1) + go func() { + defer lkv.wg.Done() + lkv.monitorLease(ctx, key, resp.Header.Revision) + }() + } + return getResp, nil +} + func (lkv *leasingKV) deleteRangeRPC(ctx context.Context, maxLeaseRev int64, key, end string) (*v3.DeleteResponse, error) { lkey, lend := lkv.pfx+key, lkv.pfx+end resp, err := lkv.kv.Txn(ctx).If( diff --git a/clientv3/namespace/kv.go b/clientv3/namespace/kv.go index 35fe5858a48..ac4fd385c97 100644 --- a/clientv3/namespace/kv.go +++ b/clientv3/namespace/kv.go @@ -60,6 +60,19 @@ func (kv *kvPrefix) Get(ctx context.Context, key string, opts ...clientv3.OpOpti return get, nil } +func (kv *kvPrefix) GetStream(ctx context.Context, key string, opts ...clientv3.OpOption) (*clientv3.GetStreamResponse, error) { + if len(key) == 0 { + return nil, rpctypes.ErrEmptyKey + } + r, err := kv.KV.Do(ctx, kv.prefixOp(clientv3.OpGetStream(key, opts...))) + if err != nil { + return nil, err + } + get := r.GetStream() + kv.unprefixGetStreamResponse(get) + return get, nil +} + func (kv *kvPrefix) Delete(ctx context.Context, key string, opts ...clientv3.OpOption) (*clientv3.DeleteResponse, error) { if len(key) == 0 && !(clientv3.IsOptsWithFromKey(opts) || clientv3.IsOptsWithPrefix(opts)) { return nil, rpctypes.ErrEmptyKey @@ -84,6 +97,8 @@ func (kv *kvPrefix) Do(ctx context.Context, op clientv3.Op) (clientv3.OpResponse switch { case r.Get() != nil: kv.unprefixGetResponse(r.Get()) + case r.GetStream() != nil: + kv.unprefixGetStreamResponse(r.GetStream()) case r.Put() != nil: kv.unprefixPutResponse(r.Put()) case r.Del() != nil: @@ -144,6 +159,12 @@ func (kv *kvPrefix) unprefixGetResponse(resp *clientv3.GetResponse) { } } +func (kv *kvPrefix) unprefixGetStreamResponse(resp *clientv3.GetStreamResponse) { + for i := range resp.Kvs { + resp.Kvs[i].Key = resp.Kvs[i].Key[len(kv.pfx):] + } +} + func (kv *kvPrefix) unprefixPutResponse(resp *clientv3.PutResponse) { if resp.PrevKv != nil { resp.PrevKv.Key = resp.PrevKv.Key[len(kv.pfx):] diff --git a/clientv3/op.go b/clientv3/op.go index bd0f1f2f213..64f01fadfba 100644 --- a/clientv3/op.go +++ b/clientv3/op.go @@ -24,6 +24,7 @@ const ( tPut tDeleteRange tTxn + tRangeStream ) var noPrefixEnd = []byte{0} @@ -109,6 +110,8 @@ func (op Op) IsPut() bool { return op.t == tPut } // IsGet returns true iff the operation is a Get. func (op Op) IsGet() bool { return op.t == tRange } +func (op Op) IsGetStream() bool { return op.t == tRangeStream } + // IsDelete returns true iff the operation is a Delete. func (op Op) IsDelete() bool { return op.t == tDeleteRange } @@ -166,6 +169,21 @@ func (op Op) toRangeRequest() *pb.RangeRequest { return r } +func (op Op) toRangeStreamRequest() *pb.RangeRequest { + if op.t != tRangeStream { + panic("op.t != tRangeStream") + } + r := &pb.RangeRequest{ + Key: op.key, + RangeEnd: op.end, + Limit: op.limit, + Revision: op.rev, + Serializable: op.serializable, + KeysOnly: op.keysOnly, + } + return r +} + func (op Op) toTxnRequest() *pb.TxnRequest { thenOps := make([]*pb.RequestOp, len(op.thenOps)) for i, tOp := range op.thenOps { @@ -186,6 +204,8 @@ func (op Op) toRequestOp() *pb.RequestOp { switch op.t { case tRange: return &pb.RequestOp{Request: &pb.RequestOp_RequestRange{RequestRange: op.toRangeRequest()}} + case tRangeStream: + return &pb.RequestOp{Request: &pb.RequestOp_RequestRange{RequestRange: op.toRangeStreamRequest()}} case tPut: r := &pb.PutRequest{Key: op.key, Value: op.val, Lease: int64(op.leaseID), PrevKv: op.prevKV, IgnoreValue: op.ignoreValue, IgnoreLease: op.ignoreLease} return &pb.RequestOp{Request: &pb.RequestOp_RequestPut{RequestPut: r}} @@ -213,7 +233,7 @@ func (op Op) isWrite() bool { } return false } - return op.t != tRange + return op.t != tRange && op.t != tRangeStream } // OpGet returns "get" operation based on given key and operation options. @@ -227,6 +247,16 @@ func OpGet(key string, opts ...OpOption) Op { return ret } +func OpGetStream(key string, opts ...OpOption) Op { + // WithPrefix and WithFromKey are not supported together + if IsOptsWithPrefix(opts) && IsOptsWithFromKey(opts) { + panic("`WithPrefix` and `WithFromKey` cannot be set at the same time, choose one") + } + ret := Op{t: tRangeStream, key: []byte(key)} + ret.applyOpts(opts) + return ret +} + // OpDelete returns "delete" operation based on given key and operation options. func OpDelete(key string, opts ...OpOption) Op { // WithPrefix and WithFromKey are not supported together diff --git a/clientv3/ordering/kv.go b/clientv3/ordering/kv.go index d62f2c8911c..84de337476d 100644 --- a/clientv3/ordering/kv.go +++ b/clientv3/ordering/kv.go @@ -75,6 +75,32 @@ func (kv *kvOrdering) Get(ctx context.Context, key string, opts ...clientv3.OpOp } } +func (kv *kvOrdering) GetStream(ctx context.Context, key string, opts ...clientv3.OpOption) (*clientv3.GetStreamResponse, error) { + // prevRev is stored in a local variable in order to record the prevRev + // at the beginning of the Get operation, because concurrent + // access to kvOrdering could change the prevRev field in the + // middle of the Get operation. + prevRev := kv.getPrevRev() + op := clientv3.OpGetStream(key, opts...) + for { + r, err := kv.KV.Do(ctx, op) + if err != nil { + return nil, err + } + resp := r.GetStream() + if resp.Header.Revision == prevRev { + return resp, nil + } else if resp.Header.Revision > prevRev { + kv.setPrevRev(resp.Header.Revision) + return resp, nil + } + err = kv.orderViolationFunc(op, r, prevRev) + if err != nil { + return nil, err + } + } +} + func (kv *kvOrdering) Txn(ctx context.Context) clientv3.Txn { return &txnOrdering{ kv.KV.Txn(ctx), diff --git a/clientv3/retry.go b/clientv3/retry.go index 69ecc631471..98272795272 100644 --- a/clientv3/retry.go +++ b/clientv3/retry.go @@ -105,6 +105,10 @@ func (rkv *retryKVClient) Range(ctx context.Context, in *pb.RangeRequest, opts . return rkv.kc.Range(ctx, in, append(opts, withRetryPolicy(repeatable))...) } +func (rkv *retryKVClient) RangeStream(ctx context.Context, in *pb.RangeRequest, opts ...grpc.CallOption) (pb.KV_RangeStreamClient, error) { + return rkv.kc.RangeStream(ctx, in, append(opts, withRetryPolicy(repeatable))...) +} + func (rkv *retryKVClient) Put(ctx context.Context, in *pb.PutRequest, opts ...grpc.CallOption) (resp *pb.PutResponse, err error) { return rkv.kc.Put(ctx, in, opts...) } diff --git a/embed/config.go b/embed/config.go index 58665bbed9d..a36ee185a31 100644 --- a/embed/config.go +++ b/embed/config.go @@ -37,6 +37,7 @@ import ( "go.etcd.io/etcd/v3/etcdserver/api/v3compactor" bolt "go.etcd.io/bbolt" + "go.uber.org/multierr" "go.uber.org/zap" "go.uber.org/zap/zapcore" "golang.org/x/crypto/bcrypt" @@ -614,7 +615,7 @@ func (cfg *Config) PeerURLsMapAndToken(which string) (urlsmap types.URLsMap, tok case cfg.DNSCluster != "": clusterStrs, cerr := cfg.GetDNSClusterNames() lg := cfg.logger - if cerr != nil { + if len(clusterStrs) == 0 && cerr != nil { lg.Warn("failed to resolve during SRV discovery", zap.Error(cerr)) return nil, "", cerr } @@ -672,7 +673,7 @@ func (cfg *Config) GetDNSClusterNames() ([]string, error) { ) defaultHTTPClusterStrs, httpCerr := srv.GetCluster("http", "etcd-server"+serviceNameSuffix, cfg.Name, cfg.DNSCluster, cfg.APUrls) - if httpCerr != nil { + if httpCerr == nil { clusterStrs = append(clusterStrs, defaultHTTPClusterStrs...) } lg.Info( @@ -686,7 +687,7 @@ func (cfg *Config) GetDNSClusterNames() ([]string, error) { zap.Error(httpCerr), ) - return clusterStrs, cerr + return clusterStrs, multierr.Combine(cerr, httpCerr) } func (cfg Config) InitialClusterFromName(name string) (ret string) { diff --git a/etcdctl/ctlv3/command/getstream_command.go b/etcdctl/ctlv3/command/getstream_command.go new file mode 100644 index 00000000000..dbc724b56ec --- /dev/null +++ b/etcdctl/ctlv3/command/getstream_command.go @@ -0,0 +1,124 @@ +// Copyright 2015 The etcd Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package command + +import ( + "fmt" + + "github.com/spf13/cobra" + "go.etcd.io/etcd/v3/clientv3" +) + +var ( + getStreamConsistency string + getStreamLimit int64 + getStreamPrefix bool + getStreamFromKey bool + getStreamRev int64 + getStreamKeysOnly bool + printStreamValueOnly bool +) + +// NewGetStreamCommand returns the cobra command for "getstream". +func NewGetStreamCommand() *cobra.Command { + cmd := &cobra.Command{ + Use: "getstream [options] [range_end]", + Short: "Gets the key or a range of keys by stream", + Run: getStreamCommandFunc, + } + + cmd.Flags().StringVar(&getStreamConsistency, "consistency", "l", "Linearizable(l) or Serializable(s)") + cmd.Flags().Int64Var(&getStreamLimit, "limit", 0, "Maximum number of results") + cmd.Flags().BoolVar(&getStreamPrefix, "prefix", false, "Get keys with matching prefix") + cmd.Flags().BoolVar(&getStreamFromKey, "from-key", false, "Get keys that are greater than or equal to the given key using byte compare") + cmd.Flags().Int64Var(&getStreamRev, "rev", 0, "Specify the kv revision") + cmd.Flags().BoolVar(&getStreamKeysOnly, "keys-only", false, "Get only the keys") + cmd.Flags().BoolVar(&printStreamValueOnly, "print-value-only", false, `Only write values when using the "simple" output format`) + return cmd +} + +// getStreamCommandFunc executes the "getstream" command. +func getStreamCommandFunc(cmd *cobra.Command, args []string) { + key, opts := getGetStreamOp(args) + ctx, cancel := commandCtx(cmd) + resp, err := mustClientFromCmd(cmd).GetStream(ctx, key, opts...) + cancel() + if err != nil { + ExitWithError(ExitError, err) + } + + if printStreamValueOnly { + dp, simple := (display).(*simplePrinter) + if !simple { + ExitWithError(ExitBadArgs, fmt.Errorf("print-value-only is only for `--write-out=simple`")) + } + dp.valueOnly = true + } + display.GetStream(*resp) +} + +func getGetStreamOp(args []string) (string, []clientv3.OpOption) { + if len(args) == 0 { + ExitWithError(ExitBadArgs, fmt.Errorf("get command needs one argument as key and an optional argument as range_end")) + } + + if getStreamPrefix && getStreamFromKey { + ExitWithError(ExitBadArgs, fmt.Errorf("`--prefix` and `--from-key` cannot be set at the same time, choose one")) + } + + opts := []clientv3.OpOption{} + switch getStreamConsistency { + case "s": + opts = append(opts, clientv3.WithSerializable()) + case "l": + default: + ExitWithError(ExitBadFeature, fmt.Errorf("unknown consistency flag %q", getStreamConsistency)) + } + + key := args[0] + if len(args) > 1 { + if getStreamPrefix || getStreamFromKey { + ExitWithError(ExitBadArgs, fmt.Errorf("too many arguments, only accept one argument when `--prefix` or `--from-key` is set")) + } + opts = append(opts, clientv3.WithRange(args[1])) + } + + opts = append(opts, clientv3.WithLimit(getStreamLimit)) + if getStreamRev > 0 { + opts = append(opts, clientv3.WithRev(getStreamRev)) + } + + if getStreamPrefix { + if len(key) == 0 { + key = "\x00" + opts = append(opts, clientv3.WithFromKey()) + } else { + opts = append(opts, clientv3.WithPrefix()) + } + } + + if getStreamFromKey { + if len(key) == 0 { + key = "\x00" + } + opts = append(opts, clientv3.WithFromKey()) + } + + if getStreamKeysOnly { + opts = append(opts, clientv3.WithKeysOnly()) + } + + return key, opts +} diff --git a/etcdctl/ctlv3/command/printer.go b/etcdctl/ctlv3/command/printer.go index fab26eb0d9a..ec72c14ee78 100644 --- a/etcdctl/ctlv3/command/printer.go +++ b/etcdctl/ctlv3/command/printer.go @@ -29,6 +29,7 @@ import ( type printer interface { Del(v3.DeleteResponse) Get(v3.GetResponse) + GetStream(v3.GetStreamResponse) Put(v3.PutResponse) Txn(v3.TxnResponse) Watch(v3.WatchResponse) @@ -92,11 +93,12 @@ type printerRPC struct { p func(interface{}) } -func (p *printerRPC) Del(r v3.DeleteResponse) { p.p((*pb.DeleteRangeResponse)(&r)) } -func (p *printerRPC) Get(r v3.GetResponse) { p.p((*pb.RangeResponse)(&r)) } -func (p *printerRPC) Put(r v3.PutResponse) { p.p((*pb.PutResponse)(&r)) } -func (p *printerRPC) Txn(r v3.TxnResponse) { p.p((*pb.TxnResponse)(&r)) } -func (p *printerRPC) Watch(r v3.WatchResponse) { p.p(&r) } +func (p *printerRPC) Del(r v3.DeleteResponse) { p.p((*pb.DeleteRangeResponse)(&r)) } +func (p *printerRPC) Get(r v3.GetResponse) { p.p((*pb.RangeResponse)(&r)) } +func (p *printerRPC) GetStream(r v3.GetStreamResponse) { p.p((*pb.RangeResponse)(&r)) } +func (p *printerRPC) Put(r v3.PutResponse) { p.p((*pb.PutResponse)(&r)) } +func (p *printerRPC) Txn(r v3.TxnResponse) { p.p((*pb.TxnResponse)(&r)) } +func (p *printerRPC) Watch(r v3.WatchResponse) { p.p(&r) } func (p *printerRPC) Grant(r v3.LeaseGrantResponse) { p.p(r) } func (p *printerRPC) Revoke(id v3.LeaseID, r v3.LeaseRevokeResponse) { p.p(r) } diff --git a/etcdctl/ctlv3/command/printer_fields.go b/etcdctl/ctlv3/command/printer_fields.go index c52e029823b..7b07be127f1 100644 --- a/etcdctl/ctlv3/command/printer_fields.go +++ b/etcdctl/ctlv3/command/printer_fields.go @@ -58,6 +58,14 @@ func (p *fieldsPrinter) Get(r v3.GetResponse) { fmt.Println(`"Count" :`, r.Count) } +func (p *fieldsPrinter) GetStream(r v3.GetStreamResponse) { + p.hdr(r.Header) + for _, kv := range r.Kvs { + p.kv("", kv) + } + fmt.Println(`"Count" :`, r.Count) +} + func (p *fieldsPrinter) Put(r v3.PutResponse) { p.hdr(r.Header) if r.PrevKv != nil { diff --git a/etcdctl/ctlv3/command/printer_simple.go b/etcdctl/ctlv3/command/printer_simple.go index e9a529008fc..5531267502e 100644 --- a/etcdctl/ctlv3/command/printer_simple.go +++ b/etcdctl/ctlv3/command/printer_simple.go @@ -43,6 +43,12 @@ func (s *simplePrinter) Get(resp v3.GetResponse) { } } +func (s *simplePrinter) GetStream(resp v3.GetStreamResponse) { + for _, kv := range resp.Kvs { + printKV(s.isHex, s.valueOnly, kv) + } +} + func (s *simplePrinter) Put(r v3.PutResponse) { fmt.Println("OK") if r.PrevKv != nil { diff --git a/etcdctl/ctlv3/ctl.go b/etcdctl/ctlv3/ctl.go index e16e8e3c287..1a0a7f7065f 100644 --- a/etcdctl/ctlv3/ctl.go +++ b/etcdctl/ctlv3/ctl.go @@ -71,6 +71,7 @@ func init() { rootCmd.AddCommand( command.NewGetCommand(), + command.NewGetStreamCommand(), command.NewPutCommand(), command.NewDelCommand(), command.NewTxnCommand(), diff --git a/etcdserver/api/v3rpc/key.go b/etcdserver/api/v3rpc/key.go index ef71d83e8c4..f5bae312607 100644 --- a/etcdserver/api/v3rpc/key.go +++ b/etcdserver/api/v3rpc/key.go @@ -17,6 +17,7 @@ package v3rpc import ( "context" + "go.uber.org/zap" pb "go.etcd.io/etcd/api/v3/etcdserverpb" "go.etcd.io/etcd/api/v3/v3rpc/rpctypes" @@ -32,10 +33,12 @@ type kvServer struct { // Txn.Success can have at most 128 operations, // and Txn.Failure can have at most 128 operations. maxTxnOps uint + + lg *zap.Logger } func NewKVServer(s *etcdserver.EtcdServer) pb.KVServer { - return &kvServer{hdr: newHeader(s), kv: s, maxTxnOps: s.Cfg.MaxTxnOps} + return &kvServer{hdr: newHeader(s), kv: s, maxTxnOps: s.Cfg.MaxTxnOps, lg: s.Cfg.Logger} } func (s *kvServer) Range(ctx context.Context, r *pb.RangeRequest) (*pb.RangeResponse, error) { @@ -52,6 +55,64 @@ func (s *kvServer) Range(ctx context.Context, r *pb.RangeRequest) (*pb.RangeResp return resp, nil } +func (s *kvServer) RangeStream(r *pb.RangeRequest, rss pb.KV_RangeStreamServer) error { + defer func() { + if err := recover(); err != nil { + switch e := err.(type) { + case error: + s.lg.Error( + "kvServer RangeStream() panic error", zap.Error(e)) + } + } + }() + + if err := checkRangeRequest(r); err != nil { + return err + } + + respC := make(chan *pb.RangeResponse) + errC := make(chan error) + + go func() { + err := s.kv.RangeStream(rss.Context(), r, respC, errC) + if err != nil { + s.lg.Error("EtcdServer RangeStream error", zap.Error(togRPCError(err))) + } + }() + +Loop: + for { + select { + case resp := <-respC: + if resp == nil { + break Loop + } + if resp.Kvs == nil || len(resp.Kvs) == 0 { + break Loop + } + + s.hdr.fill(resp.Header) + + serr := rss.Send(resp) + if serr != nil { + if isClientCtxErr(rss.Context().Err(), serr) { + s.lg.Debug("failed to send range stream response to gRPC stream", zap.Error(serr)) + } else { + s.lg.Warn("failed to send range stream response to gRPC stream", zap.Error(serr)) + streamFailures.WithLabelValues("send", "rangeStream").Inc() + } + return nil + } + case err := <-errC: + return err + case <-rss.Context().Done(): + return rss.Context().Err() + } + } + + return nil +} + func (s *kvServer) Put(ctx context.Context, r *pb.PutRequest) (*pb.PutResponse, error) { if err := checkPutRequest(r); err != nil { return nil, err diff --git a/etcdserver/apply.go b/etcdserver/apply.go index 208dea1f13a..526739b1d09 100644 --- a/etcdserver/apply.go +++ b/etcdserver/apply.go @@ -64,6 +64,7 @@ type applierV3 interface { Put(ctx context.Context, txn mvcc.TxnWrite, p *pb.PutRequest) (*pb.PutResponse, *traceutil.Trace, error) Range(ctx context.Context, txn mvcc.TxnRead, r *pb.RangeRequest) (*pb.RangeResponse, error) + RangeStream(ctx context.Context, txn mvcc.TxnRead, r *pb.RangeRequest, rspC chan *pb.RangeResponse, errC chan error) (*pb.RangeResponse, error) DeleteRange(txn mvcc.TxnWrite, dr *pb.DeleteRangeRequest) (*pb.DeleteRangeResponse, error) Txn(ctx context.Context, rt *pb.TxnRequest) (*pb.TxnResponse, *traceutil.Trace, error) Compaction(compaction *pb.CompactionRequest) (*pb.CompactionResponse, <-chan struct{}, *traceutil.Trace, error) @@ -385,6 +386,105 @@ func (a *applierV3backend) Range(ctx context.Context, txn mvcc.TxnRead, r *pb.Ra return resp, nil } +func (a *applierV3backend) RangeStream(ctx context.Context, txn mvcc.TxnRead, r *pb.RangeRequest, rspC chan *pb.RangeResponse, errC chan error) (*pb.RangeResponse, error) { + defer func() { + if err := recover(); err != nil { + switch e := err.(type) { + case error: + a.s.getLogger().Error( + "applierV3backend RangeStream() panic error", zap.Error(e)) + } + } + }() + + defer close(rspC) + defer close(errC) + + trace := traceutil.Get(ctx) + + lg := a.s.getLogger() + + resp := &pb.RangeResponse{} + resp.Header = &pb.ResponseHeader{} + streamC := make(chan *mvcc.RangeResult) + + var err error + + if txn == nil { + txn = a.s.kv.Read(trace) + defer txn.End() + } + + limit := r.Limit + ro := mvcc.RangeOptions{ + Limit: limit, + Rev: r.Revision, + } + + go func() { + err = txn.RangeStream(r.Key, mkGteRange(r.RangeEnd), ro, streamC) + if err != nil { + lg.Error("storeTxnRead RangeStream error", zap.Error(err)) + } + }() + +Loop: + for { + select { + case rr := <-streamC: + if rr == nil { + select { + case rspC <- nil: + case <-ctx.Done(): + return nil, ctx.Err() + } + break Loop + } + + if rr.Err != nil { + select { + case errC <- rr.Err: + return nil, rr.Err + case <-ctx.Done(): + return nil, ctx.Err() + } + } + + subResp := &pb.RangeResponse{} + subResp.Header = &pb.ResponseHeader{} + + subResp.Header.Revision = rr.Rev + subResp.SubCount = int64(rr.Count) + + resp.Count += int64(rr.Count) + subResp.Count = resp.Count + subResp.Kvs = make([]*mvccpb.KeyValue, len(rr.KVs)) + for i := range rr.KVs { + if r.KeysOnly { + rr.KVs[i].Value = nil + } + subResp.Kvs[i] = &rr.KVs[i] + } + // resp TotalSize just use monitor long time range stream + resp.TotalSize += int64(proto.Size(subResp)) + // resp Header.Revision just use monitor long time range stream + resp.Header.Revision = subResp.Header.Revision + + select { + case rspC <- subResp: + case <-ctx.Done(): + return nil, ctx.Err() + } + + case <-ctx.Done(): + return nil, ctx.Err() + } + } + + trace.Step("assemble the response") + return resp, nil +} + func (a *applierV3backend) Txn(ctx context.Context, rt *pb.TxnRequest) (*pb.TxnResponse, *traceutil.Trace, error) { trace := traceutil.Get(ctx) if trace.IsEmpty() { diff --git a/etcdserver/apply_auth.go b/etcdserver/apply_auth.go index bc211221435..620c5e98352 100644 --- a/etcdserver/apply_auth.go +++ b/etcdserver/apply_auth.go @@ -92,6 +92,13 @@ func (aa *authApplierV3) Range(ctx context.Context, txn mvcc.TxnRead, r *pb.Rang return aa.applierV3.Range(ctx, txn, r) } +func (aa *authApplierV3) RangeStream(ctx context.Context, txn mvcc.TxnRead, r *pb.RangeRequest, rspC chan *pb.RangeResponse, errC chan error) (*pb.RangeResponse, error) { + if err := aa.as.IsRangePermitted(&aa.authInfo, r.Key, r.RangeEnd); err != nil { + return nil, err + } + return aa.applierV3.RangeStream(ctx, txn, r, rspC, errC) +} + func (aa *authApplierV3) DeleteRange(txn mvcc.TxnWrite, r *pb.DeleteRangeRequest) (*pb.DeleteRangeResponse, error) { if err := aa.as.IsDeleteRangePermitted(&aa.authInfo, r.Key, r.RangeEnd); err != nil { return nil, err diff --git a/etcdserver/corrupt.go b/etcdserver/corrupt.go index f8c7542527c..d84c21a1377 100644 --- a/etcdserver/corrupt.go +++ b/etcdserver/corrupt.go @@ -317,6 +317,10 @@ func (a *applierV3Corrupt) Range(ctx context.Context, txn mvcc.TxnRead, p *pb.Ra return nil, ErrCorrupt } +func (a *applierV3Corrupt) RangeStream(ctx context.Context, txn mvcc.TxnRead, r *pb.RangeRequest, rspC chan *pb.RangeResponse, errC chan error) (*pb.RangeResponse, error) { + return nil, ErrCorrupt +} + func (a *applierV3Corrupt) DeleteRange(txn mvcc.TxnWrite, p *pb.DeleteRangeRequest) (*pb.DeleteRangeResponse, error) { return nil, ErrCorrupt } diff --git a/etcdserver/util.go b/etcdserver/util.go index 78f8278f415..ea16675c181 100644 --- a/etcdserver/util.go +++ b/etcdserver/util.go @@ -152,6 +152,14 @@ func warnOfExpensiveReadOnlyRangeRequest(lg *zap.Logger, now time.Time, reqStrin warnOfExpensiveGenericRequest(lg, now, reqStringer, "read-only range ", resp, err) } +func warnOfExpensiveReadOnlyRangeStreamRequest(lg *zap.Logger, now time.Time, reqStringer fmt.Stringer, rangeResponse *pb.RangeResponse, err error) { + var resp string + if !isNil(rangeResponse) { + resp = fmt.Sprintf("range_stream_response_total_count:%d, size:%d", rangeResponse.GetCount(), rangeResponse.GetTotalSize()) + } + warnOfExpensiveGenericRequest(lg, now, reqStringer, "read-only rangeStream ", resp, err) +} + func warnOfExpensiveGenericRequest(lg *zap.Logger, now time.Time, reqStringer fmt.Stringer, prefix string, resp string, err error) { d := time.Since(now) if d > warnApplyDuration { diff --git a/etcdserver/v3_server.go b/etcdserver/v3_server.go index 7a904dc1c1e..7e56996ebd9 100644 --- a/etcdserver/v3_server.go +++ b/etcdserver/v3_server.go @@ -48,6 +48,7 @@ const ( type RaftKV interface { Range(ctx context.Context, r *pb.RangeRequest) (*pb.RangeResponse, error) + RangeStream(ctx context.Context, r *pb.RangeRequest, rspC chan *pb.RangeResponse, errC chan error) error Put(ctx context.Context, r *pb.PutRequest) (*pb.PutResponse, error) DeleteRange(ctx context.Context, r *pb.DeleteRangeRequest) (*pb.DeleteRangeResponse, error) Txn(ctx context.Context, r *pb.TxnRequest) (*pb.TxnResponse, error) @@ -131,6 +132,47 @@ func (s *EtcdServer) Range(ctx context.Context, r *pb.RangeRequest) (*pb.RangeRe return resp, err } +func (s *EtcdServer) RangeStream(ctx context.Context, r *pb.RangeRequest, rspC chan *pb.RangeResponse, errC chan error) error { + trace := traceutil.New("rangeStream", + s.getLogger(), + traceutil.Field{Key: "range_begin", Value: string(r.Key)}, + traceutil.Field{Key: "range_end", Value: string(r.RangeEnd)}, + ) + + ctx = context.WithValue(ctx, traceutil.TraceKey, trace) + + var resp *pb.RangeResponse + var err error + defer func(start time.Time) { + warnOfExpensiveReadOnlyRangeStreamRequest(s.getLogger(), start, r, resp, err) + if resp != nil { + trace.AddField( + traceutil.Field{Key: "response_total_count", Value: resp.GetCount()}, + traceutil.Field{Key: "response_revision", Value: resp.Header.Revision}, + ) + } + trace.LogIfLong(traceThreshold) + }(time.Now()) + + if !r.Serializable { + err = s.linearizableReadNotify(ctx) + trace.Step("agreement among raft nodes before linearized reading") + if err != nil { + return err + } + } + chk := func(ai *auth.AuthInfo) error { + return s.authStore.IsRangePermitted(ai, r.Key, r.RangeEnd) + } + + get := func() { resp, err = s.applyV3Base.RangeStream(ctx, nil, r, rspC, errC) } + if serr := s.doSerialize(ctx, chk, get); serr != nil { + err = serr + return err + } + return err +} + func (s *EtcdServer) Put(ctx context.Context, r *pb.PutRequest) (*pb.PutResponse, error) { ctx = context.WithValue(ctx, traceutil.StartTimeKey, time.Now()) resp, err := s.raftRequest(ctx, pb.InternalRaftRequest{Put: r}) diff --git a/go.mod b/go.mod index 07ab0075a72..158ba1f0a47 100644 --- a/go.mod +++ b/go.mod @@ -41,6 +41,7 @@ require ( go.etcd.io/etcd/api/v3 v3.0.0-20201012212543-0b95e8cef14a go.etcd.io/etcd/client/v2 v2.0.0-00010101000000-000000000000 go.etcd.io/etcd/pkg/v3 v3.0.0-00010101000000-000000000000 + go.uber.org/multierr v1.6.0 go.uber.org/zap v1.16.0 golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0 golang.org/x/lint v0.0.0-20200302205851-738671d3881b // indirect diff --git a/mvcc/kv.go b/mvcc/kv.go index c9737911a42..a0cdb764d17 100644 --- a/mvcc/kv.go +++ b/mvcc/kv.go @@ -31,6 +31,7 @@ type RangeResult struct { KVs []mvccpb.KeyValue Rev int64 Count int + Err error } type ReadView interface { @@ -51,6 +52,8 @@ type ReadView interface { // Limit limits the number of keys returned. // If the required rev is compacted, ErrCompacted will be returned. Range(key, end []byte, ro RangeOptions) (r *RangeResult, err error) + + RangeStream(key, end []byte, ro RangeOptions, streamC chan *RangeResult) (err error) } // TxnRead represents a read-only transaction with operations that will not diff --git a/mvcc/kv_view.go b/mvcc/kv_view.go index b38297f1feb..a5ffbd12ef1 100644 --- a/mvcc/kv_view.go +++ b/mvcc/kv_view.go @@ -39,6 +39,12 @@ func (rv *readView) Range(key, end []byte, ro RangeOptions) (r *RangeResult, err return tr.Range(key, end, ro) } +func (rv *readView) RangeStream(key, end []byte, ro RangeOptions, streamC chan *RangeResult) (err error) { + tr := rv.kv.Read(traceutil.TODO()) + defer tr.End() + return tr.RangeStream(key, end, ro, streamC) +} + type writeView struct{ kv KV } func (wv *writeView) DeleteRange(key, end []byte) (n, rev int64) { diff --git a/mvcc/kvstore_txn.go b/mvcc/kvstore_txn.go index 8b2e465aa58..040e08ab8fb 100644 --- a/mvcc/kvstore_txn.go +++ b/mvcc/kvstore_txn.go @@ -22,6 +22,8 @@ import ( "go.uber.org/zap" ) +const RangeStreamBatch int = 1000 + type storeTxnRead struct { s *store tx backend.ReadTx @@ -51,6 +53,10 @@ func (tr *storeTxnRead) Range(key, end []byte, ro RangeOptions) (r *RangeResult, return tr.rangeKeys(key, end, tr.Rev(), ro) } +func (tr *storeTxnRead) RangeStream(key, end []byte, ro RangeOptions, streamC chan *RangeResult) (err error) { + return tr.rangeStreamKeys(key, end, tr.Rev(), ro, streamC) +} + func (tr *storeTxnRead) End() { tr.tx.RUnlock() // RUnlock signals the end of concurrentReadTx. tr.s.mu.RUnlock() @@ -87,6 +93,14 @@ func (tw *storeTxnWrite) Range(key, end []byte, ro RangeOptions) (r *RangeResult return tw.rangeKeys(key, end, rev, ro) } +func (tw *storeTxnWrite) RangeStream(key, end []byte, ro RangeOptions, streamC chan *RangeResult) (err error) { + rev := tw.beginRev + if len(tw.changes) > 0 { + rev++ + } + return tw.rangeStreamKeys(key, end, rev, ro, streamC) +} + func (tw *storeTxnWrite) DeleteRange(key, end []byte) (int64, int64) { if n := tw.deleteRange(key, end); n != 0 || len(tw.changes) > 0 { return n, tw.beginRev + 1 @@ -164,6 +178,91 @@ func (tr *storeTxnRead) rangeKeys(key, end []byte, curRev int64, ro RangeOptions return &RangeResult{KVs: kvs, Count: len(revpairs), Rev: curRev}, nil } +func (tr *storeTxnRead) rangeStreamKeys(key, end []byte, curRev int64, ro RangeOptions, streamC chan *RangeResult) error { + defer func() { + if err := recover(); err != nil { + switch e := err.(type) { + case error: + tr.s.lg.Error( + "storeTxnRead rangeStreamKeys() panic error", zap.Error(e)) + } + } + }() + + defer close(streamC) + + rev := ro.Rev + if rev > curRev { + streamC <- &RangeResult{KVs: nil, Count: -1, Rev: curRev, Err: ErrFutureRev} + return ErrFutureRev + } + if rev <= 0 { + rev = curRev + } + if rev < tr.s.compactMainRev { + streamC <- &RangeResult{KVs: nil, Count: -1, Rev: 0, Err: ErrCompacted} + return ErrCompacted + } + revpairs := tr.s.kvindex.Revisions(key, end, rev, int(ro.Limit)) + tr.trace.Step("range keys from in-memory index tree") + if len(revpairs) == 0 { + streamC <- &RangeResult{KVs: nil, Count: 0, Rev: curRev} + streamC <- nil + return nil + } + + limit := int(ro.Limit) + if limit <= 0 || limit > len(revpairs) { + limit = len(revpairs) + } + + var kvsLen int + totalRevpairsCount := len(revpairs) + + if limit <= RangeStreamBatch { + kvsLen = limit + } else { + kvsLen = RangeStreamBatch + limit -= RangeStreamBatch + } + kvs := make([]mvccpb.KeyValue, kvsLen) + + revBytes := newRevBytes() + for i, revpair := range revpairs { + revToBytes(revpair, revBytes) + _, vs := tr.tx.UnsafeRange(keyBucketName, revBytes, nil, 0) + if len(vs) != 1 { + tr.s.lg.Fatal( + "range failed to find revision pair", + zap.Int64("revision-main", revpair.main), + zap.Int64("revision-sub", revpair.sub), + ) + } + if err := kvs[i%RangeStreamBatch].Unmarshal(vs[0]); err != nil { + tr.s.lg.Fatal( + "failed to unmarshal mvccpb.KeyValue", + zap.Error(err), + ) + } + if (i+1)%RangeStreamBatch == 0 && (i+1) != totalRevpairsCount { + streamC <- &RangeResult{KVs: kvs, Count: RangeStreamBatch, Rev: curRev} + + if limit <= RangeStreamBatch { + kvsLen = limit + } else { + kvsLen = RangeStreamBatch + limit -= RangeStreamBatch + } + kvs = make([]mvccpb.KeyValue, kvsLen) + } + } + + tr.trace.Step("range keys from bolt db") + streamC <- &RangeResult{KVs: kvs, Count: kvsLen, Rev: curRev} + streamC <- nil + return nil +} + func (tw *storeTxnWrite) put(key, value []byte, leaseID lease.LeaseID) { rev := tw.beginRev + 1 c := rev diff --git a/mvcc/metrics_txn.go b/mvcc/metrics_txn.go index 89f9297ec43..2837d97cd04 100644 --- a/mvcc/metrics_txn.go +++ b/mvcc/metrics_txn.go @@ -18,18 +18,19 @@ import "go.etcd.io/etcd/v3/lease" type metricsTxnWrite struct { TxnWrite - ranges uint - puts uint - deletes uint - putSize int64 + ranges uint + puts uint + deletes uint + putSize int64 + rangeStreams uint } func newMetricsTxnRead(tr TxnRead) TxnRead { - return &metricsTxnWrite{&txnReadWrite{tr}, 0, 0, 0, 0} + return &metricsTxnWrite{&txnReadWrite{tr}, 0, 0, 0, 0, 0} } func newMetricsTxnWrite(tw TxnWrite) TxnWrite { - return &metricsTxnWrite{tw, 0, 0, 0, 0} + return &metricsTxnWrite{tw, 0, 0, 0, 0, 0} } func (tw *metricsTxnWrite) Range(key, end []byte, ro RangeOptions) (*RangeResult, error) { @@ -37,6 +38,11 @@ func (tw *metricsTxnWrite) Range(key, end []byte, ro RangeOptions) (*RangeResult return tw.TxnWrite.Range(key, end, ro) } +func (tw *metricsTxnWrite) RangeStream(key, end []byte, ro RangeOptions, streamC chan *RangeResult) (err error) { + tw.rangeStreams++ + return tw.TxnWrite.RangeStream(key, end, ro, streamC) +} + func (tw *metricsTxnWrite) DeleteRange(key, end []byte) (n, rev int64) { tw.deletes++ return tw.TxnWrite.DeleteRange(key, end) diff --git a/proxy/grpcproxy/adapter/kv_client_adapter.go b/proxy/grpcproxy/adapter/kv_client_adapter.go index ddb6ada4732..47a35df1307 100644 --- a/proxy/grpcproxy/adapter/kv_client_adapter.go +++ b/proxy/grpcproxy/adapter/kv_client_adapter.go @@ -32,6 +32,13 @@ func (s *kvs2kvc) Range(ctx context.Context, in *pb.RangeRequest, opts ...grpc.C return s.kvs.Range(ctx, in) } +func (s *kvs2kvc) RangeStream(ctx context.Context, in *pb.RangeRequest, opts ...grpc.CallOption) (pb.KV_RangeStreamClient, error) { + cs := newPipeStream(ctx, func(ss chanServerStream) error { + return s.kvs.RangeStream(in, (&rs2rcServerStream{ss})) + }) + return &rs2rcClientStream{cs}, nil +} + func (s *kvs2kvc) Put(ctx context.Context, in *pb.PutRequest, opts ...grpc.CallOption) (*pb.PutResponse, error) { return s.kvs.Put(ctx, in) } @@ -47,3 +54,29 @@ func (s *kvs2kvc) Txn(ctx context.Context, in *pb.TxnRequest, opts ...grpc.CallO func (s *kvs2kvc) Compact(ctx context.Context, in *pb.CompactionRequest, opts ...grpc.CallOption) (*pb.CompactionResponse, error) { return s.kvs.Compact(ctx, in) } + +type rs2rcClientStream struct{ chanClientStream } + +type rs2rcServerStream struct{ chanServerStream } + +func (s *rs2rcClientStream) Send(wr *pb.RangeRequest) error { + return s.SendMsg(wr) +} +func (s *rs2rcClientStream) Recv() (*pb.RangeResponse, error) { + var v interface{} + if err := s.RecvMsg(&v); err != nil { + return nil, err + } + return v.(*pb.RangeResponse), nil +} + +func (s *rs2rcServerStream) Send(wr *pb.RangeResponse) error { + return s.SendMsg(wr) +} +func (s *rs2rcServerStream) Recv() (*pb.RangeRequest, error) { + var v interface{} + if err := s.RecvMsg(&v); err != nil { + return nil, err + } + return v.(*pb.RangeRequest), nil +} diff --git a/proxy/grpcproxy/kv.go b/proxy/grpcproxy/kv.go index 6fbbedf0017..0f342470420 100644 --- a/proxy/grpcproxy/kv.go +++ b/proxy/grpcproxy/kv.go @@ -22,6 +22,8 @@ import ( "go.etcd.io/etcd/v3/proxy/grpcproxy/cache" ) +const RangeStreamBatch int = 1000 + type kvProxy struct { kv clientv3.KV cache cache.Cache @@ -67,6 +69,81 @@ func (p *kvProxy) Range(ctx context.Context, r *pb.RangeRequest) (*pb.RangeRespo return gresp, nil } +func (p *kvProxy) RangeStream(r *pb.RangeRequest, rss pb.KV_RangeStreamServer) error { + + sendFunc := func(resp *pb.RangeResponse) (err error) { + subRSP := &pb.RangeResponse{} + subRSP.Header = &pb.ResponseHeader{} + + if resp.Kvs == nil || len(resp.Kvs) == 0 { + err = rss.Send(subRSP) + if err != nil { + return err + } + return nil + } + + start := int64(0) + end := int64(RangeStreamBatch) + for { + if end > resp.Count { + end = resp.Count + } + subRSP.Kvs = resp.Kvs[start:end] + subRSP.Count = subRSP.Count + subRSP.Header = subRSP.Header + err = rss.Send(subRSP) + if err != nil { + return err + } + if resp.Count <= end { + break + } + + start = end + end += int64(RangeStreamBatch) + } + return nil + } + + if r.Serializable { + resp, err := p.cache.Get(r) + switch err { + case nil: + cacheHits.Inc() + err := sendFunc(resp) + if err != nil { + return err + } + return nil + case cache.ErrCompacted: + cacheHits.Inc() + return err + } + + cachedMisses.Inc() + } + + resp, err := p.kv.Do(rss.Context(), RangeStreamRequestToOp(r)) + if err != nil { + return err + } + + // cache linearizable as serializable + req := *r + req.Serializable = true + gresp := (*pb.RangeResponse)(resp.GetStream()) + err = sendFunc(gresp) + if err != nil { + return err + } + + p.cache.Add(&req, gresp) + cacheKeys.Set(float64(p.cache.Size())) + + return nil +} + func (p *kvProxy) Put(ctx context.Context, r *pb.PutRequest) (*pb.PutResponse, error) { p.cache.Invalidate(r.Key, nil) cacheKeys.Set(float64(p.cache.Size())) @@ -189,6 +266,23 @@ func RangeRequestToOp(r *pb.RangeRequest) clientv3.Op { return clientv3.OpGet(string(r.Key), opts...) } +func RangeStreamRequestToOp(r *pb.RangeRequest) clientv3.Op { + opts := []clientv3.OpOption{} + if len(r.RangeEnd) != 0 { + opts = append(opts, clientv3.WithRange(string(r.RangeEnd))) + } + opts = append(opts, clientv3.WithRev(r.Revision)) + opts = append(opts, clientv3.WithLimit(r.Limit)) + if r.KeysOnly { + opts = append(opts, clientv3.WithKeysOnly()) + } + if r.Serializable { + opts = append(opts, clientv3.WithSerializable()) + } + + return clientv3.OpGetStream(string(r.Key), opts...) +} + func PutRequestToOp(r *pb.PutRequest) clientv3.Op { opts := []clientv3.OpOption{} opts = append(opts, clientv3.WithLease(clientv3.LeaseID(r.Lease)))