From 7e5d50ccd467aae58fbb639ee5a067b0e164a669 Mon Sep 17 00:00:00 2001 From: Tobias Schottdorf Date: Wed, 3 Oct 2018 17:33:29 +0200 Subject: [PATCH 1/5] roachtest: print output on failure in election test This was hiding the output if the invocation itself failed, which is when you wanted it most. Release note: None --- pkg/cmd/roachtest/election.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/cmd/roachtest/election.go b/pkg/cmd/roachtest/election.go index e3e9860d0ea7..058c8e52e2c2 100644 --- a/pkg/cmd/roachtest/election.go +++ b/pkg/cmd/roachtest/election.go @@ -64,7 +64,7 @@ SET TRACING = off; SHOW TRACE FOR SESSION; "`) if err != nil { - t.Fatal(err) + t.Fatalf("%s\n\n%s", buf, err) } duration = timeutil.Since(start) c.l.Printf("post-restart, query took %s\n", duration) From 2ef8fa4cbc792da3b456050cff41568ba8a0ae20 Mon Sep 17 00:00:00 2001 From: Tobias Schottdorf Date: Thu, 4 Oct 2018 11:02:40 +0200 Subject: [PATCH 2/5] kv: improve a trace event about descriptor eviction Release note: None --- pkg/kv/dist_sender.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/kv/dist_sender.go b/pkg/kv/dist_sender.go index 63160285396a..736e169d470d 100644 --- a/pkg/kv/dist_sender.go +++ b/pkg/kv/dist_sender.go @@ -1114,7 +1114,7 @@ func (ds *DistSender) sendPartialBatch( // they're all down, or we're using an out-of-date range // descriptor. Invalidate the cache and try again with the new // metadata. - log.Event(ctx, "evicting range descriptor on send error and backoff for re-lookup") + log.Eventf(ctx, "evicting range descriptor on %T and backoff for re-lookup: %+v", tErr, desc) if err := evictToken.Evict(ctx); err != nil { return response{pErr: roachpb.NewError(err)} } From 8ff44cedcf54e8f8df3d659b379b9e47d227b5e5 Mon Sep 17 00:00:00 2001 From: Tobias Schottdorf Date: Thu, 4 Oct 2018 11:03:46 +0200 Subject: [PATCH 3/5] storage: report optional StoreID with RangeNotFoundError Release note: None --- pkg/kv/dist_sender.go | 2 +- pkg/kv/dist_sender_test.go | 2 +- pkg/roachpb/errors.go | 12 +- pkg/roachpb/errors.pb.go | 369 ++++++++++++++++++++----------------- pkg/roachpb/errors.proto | 3 + pkg/server/status.go | 2 +- pkg/storage/replica.go | 6 +- pkg/storage/store.go | 7 +- pkg/storage/stores.go | 2 +- pkg/storage/stores_test.go | 2 +- 10 files changed, 223 insertions(+), 184 deletions(-) diff --git a/pkg/kv/dist_sender.go b/pkg/kv/dist_sender.go index 736e169d470d..214bec3dc3fb 100644 --- a/pkg/kv/dist_sender.go +++ b/pkg/kv/dist_sender.go @@ -1364,7 +1364,7 @@ func (ds *DistSender) sendToReplicas( // cached RangeDescriptor and re-send. if replicas.FindReplica(lh.StoreID) == -1 { // Replace NotLeaseHolderError with RangeNotFoundError. - br.Error = roachpb.NewError(roachpb.NewRangeNotFoundError(rangeID)) + br.Error = roachpb.NewError(roachpb.NewRangeNotFoundError(rangeID, curReplica.StoreID)) propagateError = true } else { // Move the new lease holder to the head of the queue for the next retry. diff --git a/pkg/kv/dist_sender_test.go b/pkg/kv/dist_sender_test.go index 4f421dfc3513..3c48939e3f36 100644 --- a/pkg/kv/dist_sender_test.go +++ b/pkg/kv/dist_sender_test.go @@ -953,7 +953,7 @@ func TestEvictCacheOnUnknownLeaseHolder(t *testing.T) { case 0, 1: err = &roachpb.NotLeaseHolderError{LeaseHolder: &roachpb.ReplicaDescriptor{NodeID: 99, StoreID: 999}} case 2: - err = roachpb.NewRangeNotFoundError(0) + err = roachpb.NewRangeNotFoundError(0, 0) default: return args.CreateReply(), nil } diff --git a/pkg/roachpb/errors.go b/pkg/roachpb/errors.go index 917941386fa2..bd2905d20f67 100644 --- a/pkg/roachpb/errors.go +++ b/pkg/roachpb/errors.go @@ -241,10 +241,12 @@ func (s *SendError) message(_ *Error) string { var _ ErrorDetailInterface = &SendError{} -// NewRangeNotFoundError initializes a new RangeNotFoundError. -func NewRangeNotFoundError(rangeID RangeID) *RangeNotFoundError { +// NewRangeNotFoundError initializes a new RangeNotFoundError for the given RangeID and, optionally, +// a StoreID. +func NewRangeNotFoundError(rangeID RangeID, storeID StoreID) *RangeNotFoundError { return &RangeNotFoundError{ RangeID: rangeID, + StoreID: storeID, } } @@ -253,7 +255,11 @@ func (e *RangeNotFoundError) Error() string { } func (e *RangeNotFoundError) message(_ *Error) string { - return fmt.Sprintf("r%d was not found", e.RangeID) + msg := fmt.Sprintf("r%d was not found", e.RangeID) + if e.StoreID != 0 { + msg += fmt.Sprintf(" on s%d", e.StoreID) + } + return msg } var _ ErrorDetailInterface = &RangeNotFoundError{} diff --git a/pkg/roachpb/errors.pb.go b/pkg/roachpb/errors.pb.go index c04713ba39eb..9bdaeb5d6217 100644 --- a/pkg/roachpb/errors.pb.go +++ b/pkg/roachpb/errors.pb.go @@ -356,6 +356,8 @@ func (*UnsupportedRequestError) Descriptor() ([]byte, []int) { return fileDescri // which is not hosted on this store. type RangeNotFoundError struct { RangeID RangeID `protobuf:"varint,1,opt,name=range_id,json=rangeId,casttype=RangeID" json:"range_id"` + // store_id is nonzero only if the error originated on a Store. + StoreID StoreID `protobuf:"varint,2,opt,name=store_id,json=storeId,casttype=StoreID" json:"store_id"` } func (m *RangeNotFoundError) Reset() { *m = RangeNotFoundError{} } @@ -1887,6 +1889,9 @@ func (this *RangeNotFoundError) Equal(that interface{}) bool { if this.RangeID != that1.RangeID { return false } + if this.StoreID != that1.StoreID { + return false + } return true } func (this *RangeKeyMismatchError) Equal(that interface{}) bool { @@ -3437,6 +3442,9 @@ func (m *RangeNotFoundError) MarshalTo(dAtA []byte) (int, error) { dAtA[i] = 0x8 i++ i = encodeVarintErrors(dAtA, i, uint64(m.RangeID)) + dAtA[i] = 0x10 + i++ + i = encodeVarintErrors(dAtA, i, uint64(m.StoreID)) return i, nil } @@ -4760,6 +4768,7 @@ func (m *RangeNotFoundError) Size() (n int) { var l int _ = l n += 1 + sovErrors(uint64(m.RangeID)) + n += 1 + sovErrors(uint64(m.StoreID)) return n } @@ -5670,6 +5679,25 @@ func (m *RangeNotFoundError) Unmarshal(dAtA []byte) error { break } } + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field StoreID", wireType) + } + m.StoreID = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowErrors + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.StoreID |= (StoreID(b) & 0x7F) << shift + if b < 0x80 { + break + } + } default: iNdEx = preIndex skippy, err := skipErrors(dAtA[iNdEx:]) @@ -9481,178 +9509,179 @@ var ( func init() { proto.RegisterFile("roachpb/errors.proto", fileDescriptorErrors) } var fileDescriptorErrors = []byte{ - // 2760 bytes of a gzipped FileDescriptorProto + // 2771 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x58, 0xcd, 0x73, 0xdb, 0xc6, - 0x15, 0x27, 0x45, 0x4a, 0x94, 0x9e, 0xbe, 0xa0, 0xb5, 0x22, 0xc3, 0x72, 0x4c, 0xda, 0x72, 0x3e, + 0x15, 0x27, 0x24, 0x4a, 0x94, 0x9e, 0xbe, 0xa0, 0xb5, 0x22, 0xc3, 0x72, 0x4c, 0xd9, 0x72, 0x3e, 0x9c, 0x74, 0x22, 0x75, 0x9c, 0x7a, 0x3a, 0x71, 0x93, 0x03, 0x3f, 0x20, 0x11, 0x12, 0x3f, 0x14, 0x90, 0x8a, 0xe3, 0xa4, 0x1d, 0x0c, 0x44, 0xac, 0x28, 0xc4, 0x24, 0xc0, 0x2e, 0x00, 0x8b, 0xba, - 0xf5, 0xd8, 0x5b, 0xdb, 0x5b, 0x6f, 0xcd, 0x4c, 0x7b, 0xe9, 0xf4, 0xda, 0xc9, 0xa5, 0xff, 0x40, + 0xf5, 0x98, 0x5b, 0xdb, 0x5b, 0x6f, 0xcd, 0x4c, 0x7b, 0xe9, 0xf4, 0xda, 0xc9, 0xa5, 0xff, 0x40, 0x8e, 0x3d, 0x66, 0x3a, 0x13, 0x4f, 0xeb, 0x5e, 0x3a, 0xd3, 0xff, 0xc0, 0xa7, 0xce, 0x7e, 0x00, - 0x04, 0x49, 0x80, 0x61, 0x7d, 0x22, 0xf1, 0xde, 0xdb, 0xdf, 0xbe, 0x7d, 0x6f, 0xf7, 0xed, 0xef, - 0x2d, 0x6c, 0x13, 0xc7, 0xe8, 0x5c, 0x0e, 0xce, 0x0f, 0x30, 0x21, 0x0e, 0x71, 0xf7, 0x07, 0xc4, - 0xf1, 0x1c, 0xb4, 0xd5, 0x71, 0x3a, 0xcf, 0x98, 0x66, 0x5f, 0xe8, 0x77, 0x51, 0x60, 0x68, 0x1a, - 0x9e, 0xc1, 0xcd, 0x76, 0x77, 0x02, 0x59, 0x1f, 0x7b, 0x46, 0x44, 0x7e, 0xdf, 0xf5, 0x1c, 0x62, - 0x74, 0xf1, 0x01, 0xb6, 0xbb, 0x96, 0x1d, 0xfc, 0x50, 0xbb, 0xe7, 0x9d, 0xce, 0x87, 0xc2, 0x48, - 0xf6, 0x3d, 0xab, 0x77, 0x70, 0xd9, 0xeb, 0x1c, 0x78, 0x56, 0x1f, 0xbb, 0x9e, 0xd1, 0x1f, 0x08, - 0xcd, 0x76, 0xd7, 0xe9, 0x3a, 0xec, 0xef, 0x01, 0xfd, 0xc7, 0xa5, 0x7b, 0xdf, 0x2c, 0xc0, 0x8d, - 0x86, 0xe3, 0xd5, 0xb0, 0xe1, 0xe2, 0xaa, 0xd3, 0x33, 0x31, 0x51, 0xa8, 0xcb, 0xa8, 0x02, 0x39, - 0x82, 0x07, 0x3d, 0xab, 0x63, 0xc8, 0xe9, 0xbb, 0xe9, 0x07, 0xab, 0x0f, 0xdf, 0xda, 0x9f, 0xf2, - 0x7e, 0x5f, 0xe3, 0x16, 0x15, 0xec, 0x76, 0x88, 0x35, 0xf0, 0x1c, 0x52, 0xca, 0x7e, 0xfb, 0xa2, - 0x90, 0xd2, 0x82, 0xa1, 0xe8, 0x08, 0xd6, 0x7a, 0x14, 0x59, 0xbf, 0x64, 0xd0, 0xf2, 0xc2, 0xfc, - 0x50, 0xda, 0x6a, 0x6f, 0xe4, 0x13, 0x7a, 0x04, 0xcb, 0xc4, 0xb0, 0xbb, 0x58, 0xb7, 0x4c, 0x39, - 0x73, 0x37, 0xfd, 0x20, 0x53, 0xda, 0xa5, 0x33, 0xbd, 0x7c, 0x51, 0xc8, 0x69, 0x54, 0xae, 0x56, - 0x5e, 0x8d, 0xfe, 0x6a, 0x39, 0x66, 0xab, 0x9a, 0x68, 0x1f, 0x16, 0x19, 0x8a, 0x9c, 0x65, 0x13, - 0xcb, 0x31, 0x13, 0xb3, 0x95, 0x6b, 0xdc, 0x0c, 0xdd, 0x07, 0xe8, 0xf8, 0xae, 0xe7, 0xf4, 0xf5, - 0xbe, 0xdb, 0x95, 0x17, 0xef, 0xa6, 0x1f, 0xac, 0x88, 0x25, 0xad, 0x70, 0x79, 0xdd, 0xed, 0x3e, - 0xce, 0xfe, 0xe7, 0xeb, 0x42, 0x7a, 0xef, 0x4d, 0xd8, 0x6e, 0x38, 0x26, 0x3e, 0xb3, 0x8d, 0xe7, - 0x86, 0xd5, 0x33, 0xce, 0x7b, 0x98, 0x05, 0x4e, 0x68, 0x0b, 0x70, 0xf3, 0xcc, 0x76, 0xfd, 0xc1, - 0xc0, 0x21, 0x1e, 0x36, 0x35, 0xfc, 0x4b, 0x1f, 0xbb, 0x5e, 0xd4, 0xe0, 0x53, 0x40, 0xcc, 0xdb, - 0x86, 0xe3, 0x1d, 0x3a, 0xbe, 0x6d, 0xf2, 0xa8, 0x47, 0x97, 0x99, 0x9e, 0x7b, 0x99, 0x02, 0xf2, - 0x0f, 0x0b, 0xf0, 0x06, 0x53, 0x9d, 0xe0, 0xeb, 0xba, 0xe5, 0xf6, 0x0d, 0xaf, 0x73, 0xc9, 0x61, - 0x3f, 0x84, 0x2d, 0xc2, 0x5d, 0xd0, 0x5d, 0xcf, 0x20, 0x9e, 0xfe, 0x0c, 0x5f, 0x33, 0xfc, 0xb5, - 0x52, 0xee, 0xd5, 0x8b, 0x42, 0xe6, 0x04, 0x5f, 0x6b, 0x9b, 0xc2, 0xa2, 0x45, 0x0d, 0x4e, 0xf0, - 0x35, 0x3a, 0x80, 0x40, 0xa4, 0x63, 0xdb, 0x64, 0x43, 0x16, 0xc6, 0x87, 0xac, 0x0b, 0xbd, 0x62, - 0x9b, 0x74, 0x40, 0x1d, 0xa4, 0xbe, 0x98, 0x16, 0x9b, 0x3a, 0xf3, 0x8d, 0xe5, 0x6a, 0xf5, 0xe1, - 0x5e, 0x5c, 0xc2, 0xa9, 0x3e, 0x92, 0xee, 0xcd, 0xd1, 0x58, 0xa6, 0x42, 0x27, 0xb0, 0xe9, 0xfa, - 0xdd, 0x2e, 0x76, 0xbd, 0x10, 0x2d, 0x3b, 0x37, 0xda, 0x46, 0x38, 0x94, 0x69, 0x44, 0x84, 0xfe, - 0xbb, 0x00, 0x7b, 0x1a, 0x36, 0xcc, 0x27, 0x96, 0x77, 0x69, 0xd9, 0x67, 0x76, 0x07, 0x13, 0xcf, - 0xb0, 0x6c, 0xef, 0x5a, 0xb5, 0x3d, 0x4c, 0x9e, 0x1b, 0x3d, 0x1e, 0xae, 0x63, 0xd8, 0x20, 0xd8, - 0x30, 0xf5, 0xf0, 0x04, 0x89, 0x23, 0x70, 0x27, 0x32, 0x31, 0x3d, 0x66, 0xfb, 0x97, 0xbd, 0xce, - 0x7e, 0x3b, 0x30, 0x12, 0x1b, 0x65, 0x9d, 0x0e, 0x0d, 0x85, 0x48, 0x03, 0x84, 0x87, 0x96, 0xeb, - 0x59, 0x76, 0x37, 0x82, 0xb7, 0x30, 0x3f, 0xde, 0x56, 0x30, 0x7c, 0x84, 0x59, 0x82, 0xf5, 0xbe, - 0x31, 0x8c, 0xc0, 0x65, 0xe6, 0x80, 0xd3, 0xd6, 0xfa, 0xc6, 0x70, 0x84, 0xf1, 0x25, 0xdc, 0x70, - 0xce, 0x5d, 0x4c, 0x9e, 0xe3, 0xc8, 0x3a, 0x5d, 0x39, 0x7b, 0x37, 0x93, 0x70, 0x40, 0x9b, 0xc2, - 0x7a, 0xd2, 0x3f, 0xe4, 0x4c, 0x2a, 0x5c, 0x11, 0xed, 0xaf, 0xe0, 0x66, 0x9b, 0x18, 0xb6, 0x6b, - 0x74, 0x3c, 0xcb, 0xb1, 0x8b, 0xe7, 0xec, 0x28, 0xf0, 0x08, 0xab, 0xb0, 0x44, 0xb0, 0xe1, 0x3a, - 0x36, 0x8b, 0xec, 0xc6, 0xc3, 0x1f, 0xc5, 0x4c, 0x38, 0x3d, 0x56, 0x63, 0x43, 0xc4, 0xbc, 0x02, - 0x40, 0xcc, 0x65, 0xc0, 0x76, 0xc4, 0xfe, 0xd4, 0x77, 0xc5, 0xce, 0x2f, 0x03, 0x0c, 0x7c, 0xf7, - 0x12, 0x63, 0xdd, 0x1b, 0xda, 0x22, 0x8d, 0xf9, 0xd9, 0x93, 0x05, 0x07, 0x9e, 0x8f, 0x6b, 0x0f, - 0x83, 0x29, 0x2e, 0xe0, 0x8d, 0x88, 0x95, 0x86, 0x3d, 0x72, 0xcd, 0xe7, 0x38, 0x9a, 0x58, 0xcc, - 0x7b, 0xb3, 0xf1, 0xd9, 0xc8, 0x19, 0x4b, 0xf9, 0x2e, 0x0d, 0x3b, 0x11, 0xf3, 0x96, 0x67, 0x78, - 0xbe, 0xcb, 0x67, 0xda, 0x81, 0x0c, 0xad, 0x4b, 0xe9, 0x48, 0x5d, 0xa2, 0x02, 0xd4, 0x08, 0x3d, - 0x58, 0x60, 0x1e, 0xfc, 0x78, 0xb6, 0x07, 0x11, 0xc8, 0xfd, 0x38, 0x47, 0xf6, 0x4e, 0x61, 0x89, - 0xcb, 0x11, 0x82, 0x0d, 0x4d, 0x29, 0xb6, 0x9a, 0x0d, 0xfd, 0xac, 0x71, 0xd2, 0x68, 0x3e, 0x69, - 0x48, 0x29, 0x24, 0xc3, 0xb6, 0x90, 0xb5, 0x3f, 0x6f, 0xe8, 0x8d, 0x66, 0x5b, 0x3f, 0x6c, 0x9e, - 0x35, 0x2a, 0x52, 0x7a, 0x42, 0x53, 0x6e, 0xd6, 0xeb, 0x6a, 0xbb, 0xad, 0x54, 0xa4, 0x05, 0xb1, - 0xb4, 0xa7, 0x20, 0x3d, 0x21, 0x96, 0x87, 0xe9, 0x71, 0xb3, 0x79, 0x39, 0x44, 0x1f, 0x41, 0xce, - 0x62, 0x9f, 0xae, 0x9c, 0x66, 0x9b, 0xef, 0x56, 0x8c, 0xf3, 0x7c, 0x40, 0x70, 0xbb, 0x08, 0x7b, - 0x0e, 0x7a, 0x9c, 0x5d, 0x5e, 0x90, 0x32, 0x7b, 0x7f, 0x49, 0x0b, 0xec, 0xb6, 0xe3, 0x34, 0x7b, - 0x62, 0x9b, 0x15, 0x61, 0xe5, 0xb5, 0xce, 0xf0, 0x68, 0x14, 0x6a, 0x80, 0x64, 0x74, 0x3c, 0xdf, - 0xe8, 0xbd, 0xde, 0xe9, 0xdd, 0xe4, 0x83, 0x43, 0xb1, 0x08, 0xc4, 0x2e, 0xa0, 0xe6, 0x80, 0xde, - 0x0a, 0x16, 0xc1, 0x6e, 0x7b, 0x68, 0x47, 0x6f, 0x86, 0xa7, 0xb0, 0x5d, 0x76, 0x6c, 0xd3, 0xa2, - 0x99, 0x3a, 0x34, 0xac, 0x5e, 0x70, 0x66, 0x7e, 0x06, 0x6b, 0xc2, 0x93, 0xe7, 0x46, 0xcf, 0xc7, - 0x62, 0x3d, 0x71, 0x57, 0xda, 0x67, 0x54, 0xaf, 0xad, 0x72, 0x6b, 0xf6, 0x21, 0xa0, 0xff, 0x9a, - 0x06, 0xc4, 0xef, 0x3b, 0xfc, 0x15, 0xee, 0x84, 0xa7, 0x31, 0x0f, 0xb9, 0x3e, 0x76, 0x5d, 0xa3, - 0x8b, 0xc7, 0xb6, 0x56, 0x20, 0x44, 0x1f, 0xc3, 0x8a, 0xa8, 0xf4, 0xd8, 0x14, 0x8b, 0x4f, 0xbc, - 0x49, 0x83, 0x08, 0x86, 0x03, 0xd0, 0x63, 0x58, 0x0e, 0x4a, 0x98, 0x28, 0x54, 0x3f, 0x34, 0x38, - 0xb4, 0x17, 0x6e, 0xff, 0x14, 0x56, 0x5a, 0xd8, 0x9e, 0xcf, 0xd9, 0xb1, 0x4d, 0x71, 0x05, 0xdb, - 0xc5, 0xfe, 0xb9, 0xd5, 0xf5, 0x1d, 0xdf, 0xd5, 0xb0, 0xeb, 0xf7, 0xbc, 0xf9, 0x16, 0xfc, 0x11, - 0xac, 0x5e, 0x11, 0x63, 0x30, 0xc0, 0xa6, 0x8e, 0x09, 0x99, 0xb1, 0x64, 0x06, 0xa7, 0x81, 0x30, - 0x56, 0x48, 0x90, 0xc3, 0x3b, 0xf4, 0x26, 0xbe, 0xf0, 0x8e, 0x88, 0xe3, 0x0f, 0x2a, 0xb8, 0x87, - 0x83, 0x50, 0x0b, 0x35, 0x86, 0x1d, 0xc1, 0x77, 0xca, 0x0e, 0x21, 0xfe, 0x80, 0xa6, 0x9a, 0x7b, - 0x76, 0x0f, 0x56, 0x18, 0x65, 0xd4, 0x27, 0xcf, 0xf9, 0x32, 0x13, 0xd7, 0xdd, 0x2e, 0xda, 0x83, - 0x95, 0x01, 0x71, 0x3a, 0xd8, 0x75, 0x45, 0x36, 0x96, 0xc3, 0x8a, 0x15, 0x88, 0xc3, 0x9d, 0x84, - 0xc4, 0x34, 0xd1, 0x43, 0xf1, 0x09, 0x80, 0xa0, 0x67, 0x01, 0xcb, 0x58, 0x2c, 0xe5, 0x05, 0xcb, - 0x58, 0x11, 0xf6, 0x8c, 0x67, 0x8c, 0x3e, 0x68, 0x3a, 0xf9, 0x5f, 0x73, 0x44, 0x5f, 0x5a, 0x9e, - 0x43, 0xa6, 0xe9, 0x0b, 0xe5, 0xa8, 0x71, 0xf4, 0x85, 0x59, 0x73, 0xfa, 0x22, 0xfe, 0x6a, 0x39, - 0x66, 0x1b, 0x42, 0xd6, 0x29, 0x65, 0xba, 0x34, 0x6c, 0xb3, 0x47, 0x2b, 0xbd, 0x47, 0xae, 0x43, - 0x4e, 0x85, 0x1e, 0x42, 0x76, 0xa0, 0x10, 0x32, 0x63, 0xcb, 0x33, 0x3b, 0x11, 0x07, 0x66, 0xbb, - 0xf7, 0x7d, 0x1a, 0xe4, 0xea, 0x04, 0x5a, 0x70, 0xd2, 0x12, 0x0b, 0xe9, 0x97, 0xb0, 0xe4, 0x0d, - 0x6d, 0xea, 0x3e, 0xa7, 0x3a, 0x15, 0xaa, 0xfa, 0xc7, 0x8b, 0xc2, 0x87, 0x5d, 0xcb, 0xbb, 0xf4, - 0xcf, 0xf7, 0x3b, 0x4e, 0xff, 0x20, 0x9c, 0xdc, 0x3c, 0x1f, 0xfd, 0x3f, 0x18, 0x3c, 0xeb, 0x1e, - 0x30, 0xea, 0xed, 0xfb, 0x96, 0xb9, 0x7f, 0x76, 0xa6, 0x56, 0x5e, 0xbe, 0x28, 0x2c, 0xb6, 0x87, - 0xb6, 0x5a, 0xd1, 0x16, 0xbd, 0xa1, 0xad, 0x9a, 0xe8, 0x10, 0x56, 0xbd, 0x51, 0x11, 0x16, 0x67, - 0x61, 0xbe, 0xcb, 0x28, 0x3a, 0x50, 0x84, 0xeb, 0x5d, 0x28, 0xb4, 0x87, 0x76, 0xb1, 0x47, 0xe9, - 0xc6, 0xb5, 0x62, 0x77, 0x1c, 0x9f, 0x72, 0x18, 0xb1, 0xcf, 0xa2, 0x9b, 0xed, 0x77, 0x69, 0xd8, - 0xa6, 0xf5, 0xb3, 0x8b, 0x49, 0xf3, 0x39, 0x26, 0x17, 0x3d, 0xe7, 0x8a, 0x07, 0xe1, 0x16, 0x64, - 0x62, 0x78, 0x20, 0x95, 0xa1, 0xf7, 0x60, 0xbd, 0xe3, 0x13, 0x82, 0x6d, 0x4f, 0x14, 0x9b, 0x05, - 0x96, 0x4d, 0xee, 0xcc, 0x9a, 0x50, 0xb1, 0xca, 0x82, 0x3e, 0x80, 0x4d, 0xcb, 0xee, 0x10, 0xdc, - 0x1f, 0x19, 0x67, 0x22, 0xc6, 0x1b, 0xa1, 0x32, 0x5a, 0x88, 0xea, 0xb0, 0x55, 0xb7, 0x86, 0xd8, - 0x6c, 0xf9, 0x1d, 0xba, 0x63, 0x83, 0x2c, 0xe7, 0xc4, 0x41, 0xfa, 0xa1, 0x44, 0x6b, 0x81, 0xa1, - 0x80, 0xfb, 0x73, 0x1a, 0x6e, 0x97, 0x28, 0x77, 0x1c, 0x95, 0x5f, 0x7c, 0xe1, 0x10, 0x7c, 0x54, - 0x0e, 0xef, 0x81, 0xf6, 0x6b, 0xdd, 0x03, 0x23, 0xbe, 0x44, 0x21, 0x2e, 0x09, 0x76, 0x69, 0x23, - 0xf3, 0xff, 0x5c, 0x00, 0xa3, 0x51, 0xc2, 0xd7, 0xcf, 0x01, 0xf1, 0xdb, 0xac, 0x6e, 0xb9, 0xae, - 0x65, 0x77, 0xb9, 0x87, 0x1f, 0xc3, 0xda, 0x15, 0x71, 0xec, 0xae, 0xce, 0xef, 0x36, 0xe1, 0x64, - 0xf2, 0x55, 0xa8, 0xad, 0x32, 0x73, 0xfe, 0x31, 0xea, 0x48, 0xea, 0x98, 0x74, 0xb1, 0x6a, 0x9f, - 0x12, 0xa7, 0x4b, 0x82, 0xb8, 0x0a, 0xed, 0xab, 0x34, 0xdc, 0x60, 0x5c, 0xf8, 0x10, 0x8b, 0x13, - 0xc1, 0x67, 0x3e, 0x99, 0x60, 0x2f, 0x1f, 0x24, 0xb1, 0xeb, 0xf1, 0x71, 0xf1, 0xc4, 0xe1, 0x37, - 0xe9, 0x90, 0x39, 0xec, 0xc2, 0x8e, 0xe0, 0x02, 0x9a, 0x72, 0x5a, 0x53, 0xcb, 0x45, 0x5d, 0x53, - 0xea, 0xcd, 0xcf, 0x94, 0x8a, 0x94, 0x42, 0x3b, 0x80, 0x02, 0x5d, 0xb1, 0x71, 0xa4, 0xe8, 0xad, - 0xd3, 0x9a, 0xda, 0x96, 0xd2, 0xe8, 0x26, 0xdc, 0x18, 0x93, 0xd7, 0x15, 0xed, 0x88, 0xd2, 0x87, - 0x08, 0xb1, 0xd0, 0x8a, 0x87, 0x6d, 0xbd, 0xd5, 0x28, 0x9e, 0xb6, 0xaa, 0xcd, 0xb6, 0x94, 0x41, - 0x79, 0xd8, 0x15, 0x9a, 0x5a, 0xf3, 0x48, 0x2d, 0x17, 0x6b, 0x7a, 0xf3, 0xb4, 0xa5, 0xd7, 0xd5, - 0x56, 0x4b, 0x6d, 0x1c, 0x49, 0x59, 0xb1, 0xf8, 0x3f, 0x6d, 0xc3, 0x2a, 0x73, 0xbb, 0x82, 0x3d, - 0xc3, 0xea, 0x21, 0x0d, 0x24, 0xdb, 0xf1, 0xf4, 0xb1, 0xde, 0x94, 0x87, 0xfc, 0x9d, 0x98, 0xe5, - 0xc7, 0xf4, 0xc7, 0xd5, 0x94, 0xb6, 0x61, 0x8f, 0x89, 0x51, 0x13, 0x36, 0x79, 0xef, 0x46, 0x91, - 0x2f, 0x68, 0x51, 0x14, 0xfb, 0xe4, 0xed, 0xa4, 0x88, 0x8e, 0x15, 0xcf, 0x2a, 0x6d, 0x1d, 0xa2, - 0x52, 0xf4, 0x39, 0x20, 0x0e, 0xf8, 0x0c, 0x5f, 0xeb, 0x41, 0x77, 0x24, 0xca, 0xc6, 0x83, 0x24, - 0xcc, 0xc9, 0xde, 0xaf, 0x9a, 0xd2, 0x24, 0x32, 0xa1, 0x40, 0xbf, 0x4a, 0xc3, 0x5d, 0xd6, 0xe1, - 0x5c, 0xb1, 0x46, 0x48, 0xf7, 0x47, 0x9d, 0x10, 0xdb, 0x80, 0xb4, 0x15, 0x12, 0xcd, 0xd6, 0xa3, - 0xd8, 0x5e, 0xfd, 0x87, 0x5a, 0xa8, 0x6a, 0x4a, 0xbb, 0x43, 0x66, 0x59, 0xa1, 0x5f, 0xc0, 0x8d, - 0x48, 0x4d, 0xd3, 0x0d, 0xce, 0xf0, 0x59, 0xcb, 0xbd, 0xfa, 0xf0, 0xfd, 0xb9, 0xda, 0x81, 0x60, - 0x26, 0xe4, 0x4d, 0xa9, 0x50, 0x1b, 0xa4, 0x28, 0x3c, 0xe5, 0xf2, 0xf2, 0x12, 0xc3, 0x7e, 0x77, - 0x36, 0x76, 0xd8, 0x3a, 0x54, 0x53, 0xda, 0xa6, 0x37, 0x2e, 0x47, 0x4f, 0x60, 0x2b, 0x8a, 0x4a, - 0xe8, 0x69, 0x90, 0x73, 0x89, 0x09, 0x89, 0x6d, 0x17, 0x68, 0x42, 0xbc, 0x09, 0x05, 0xfa, 0x02, - 0xa2, 0x8b, 0xa0, 0x4d, 0xba, 0xe7, 0xbb, 0xf2, 0x32, 0x43, 0x7e, 0x6f, 0x6e, 0x32, 0x5f, 0x4d, - 0x69, 0x51, 0xff, 0xb8, 0x06, 0x55, 0x69, 0x69, 0xb1, 0x3c, 0x1c, 0x94, 0x96, 0x15, 0x86, 0x7a, - 0x3f, 0x06, 0x75, 0x92, 0x9b, 0x57, 0x53, 0xb4, 0xcc, 0x84, 0x32, 0xa4, 0xc2, 0x3a, 0x47, 0xf2, - 0x1c, 0x47, 0xa7, 0x75, 0x10, 0x66, 0x43, 0x45, 0x58, 0x47, 0x08, 0xc5, 0x65, 0xf4, 0xb0, 0x38, - 0x03, 0x9d, 0x08, 0x06, 0xcc, 0x9a, 0xb3, 0xd5, 0xc4, 0xc3, 0x32, 0x4d, 0x95, 0xe9, 0x61, 0x71, - 0xa2, 0x52, 0x9a, 0xf0, 0x4e, 0xc0, 0x9a, 0xf5, 0x0b, 0x46, 0x9b, 0xe5, 0xb5, 0xc4, 0x84, 0xc7, - 0x11, 0x6c, 0x9a, 0xf0, 0xce, 0xb8, 0x1c, 0x35, 0x60, 0x83, 0xd7, 0x08, 0x22, 0x08, 0xb3, 0xbc, - 0x9e, 0xe8, 0xe5, 0x34, 0xb1, 0xa6, 0x5e, 0xf6, 0xa2, 0x52, 0xea, 0xa5, 0xed, 0x98, 0x58, 0xf7, - 0x47, 0xaf, 0x46, 0xf2, 0x46, 0xa2, 0x97, 0x71, 0xef, 0x4b, 0xd4, 0x4b, 0x7b, 0x5c, 0x4e, 0xe9, - 0x91, 0x8b, 0x6d, 0x53, 0xde, 0x64, 0x48, 0x6f, 0xc6, 0x20, 0x85, 0xf4, 0xb9, 0x9a, 0xd2, 0x98, - 0x2d, 0x2f, 0x2e, 0x17, 0x9e, 0xde, 0xa5, 0x14, 0x55, 0x37, 0x39, 0x47, 0x95, 0xa5, 0x19, 0xc5, - 0x25, 0x86, 0xce, 0xf2, 0xe2, 0x32, 0xae, 0xa0, 0x7b, 0x39, 0xe0, 0x97, 0x9d, 0x90, 0xdd, 0xca, - 0x5b, 0x89, 0x7b, 0x39, 0x9e, 0x09, 0xd3, 0xbd, 0x4c, 0x26, 0x35, 0xac, 0xc6, 0x0a, 0xec, 0x60, - 0x0f, 0xa2, 0xe4, 0x1a, 0x3b, 0xc5, 0x7d, 0x59, 0x8d, 0x8d, 0x4a, 0x69, 0x42, 0x8c, 0xa0, 0x43, - 0xd0, 0x09, 0x6b, 0x11, 0xe4, 0xdd, 0xc4, 0x84, 0xc4, 0x35, 0x13, 0x34, 0x21, 0xc6, 0xb8, 0x9c, - 0xba, 0xc9, 0x79, 0xf0, 0xe8, 0x2a, 0xb8, 0x9d, 0xe8, 0xe6, 0x34, 0x8f, 0xa6, 0x6e, 0xba, 0x51, - 0x29, 0xea, 0xc1, 0x6d, 0xc1, 0x8c, 0x79, 0xd1, 0xa1, 0x69, 0xa7, 0x87, 0x46, 0x67, 0x5d, 0x81, - 0xfc, 0x26, 0x03, 0x8f, 0x7b, 0x44, 0x49, 0x62, 0xc0, 0xd5, 0x94, 0x26, 0x5f, 0x26, 0xb1, 0xe3, - 0x36, 0x48, 0x16, 0x27, 0x8c, 0xba, 0x23, 0x18, 0xa3, 0x5c, 0x48, 0x0c, 0x4a, 0x1c, 0xb7, 0xa4, - 0x41, 0xb1, 0xc6, 0xe5, 0xb4, 0xe2, 0xfb, 0xa3, 0x27, 0x51, 0x5d, 0x34, 0x88, 0xf2, 0xdd, 0xc4, - 0x8a, 0x9f, 0xf0, 0x80, 0x4a, 0x2b, 0xbe, 0x3f, 0xa5, 0x42, 0x27, 0xb0, 0xde, 0xa7, 0x94, 0x52, - 0x77, 0x39, 0xa7, 0x94, 0xef, 0x25, 0xbe, 0x35, 0x4f, 0x51, 0xcf, 0x6a, 0x4a, 0x5b, 0xeb, 0x47, - 0x84, 0xe8, 0x4b, 0x90, 0xc2, 0x76, 0x5f, 0x3f, 0x67, 0x5c, 0x52, 0xde, 0x63, 0x78, 0xfb, 0x31, - 0x78, 0x33, 0xa8, 0x27, 0xbb, 0x45, 0xc6, 0x35, 0xe8, 0x0a, 0xee, 0xd0, 0xd4, 0x19, 0x9c, 0xba, - 0xeb, 0x78, 0xc4, 0xdd, 0x45, 0x3a, 0xef, 0xb3, 0x99, 0x1e, 0xc6, 0xd5, 0xfd, 0xd9, 0x8c, 0xbf, - 0x9a, 0xd2, 0x76, 0xbd, 0x44, 0x13, 0x5a, 0xcd, 0xf8, 0x1d, 0x40, 0xd9, 0x04, 0xe5, 0x9e, 0xf2, - 0x5b, 0x89, 0xbb, 0x72, 0x9a, 0xa3, 0xd2, 0x5d, 0x69, 0x45, 0xa5, 0xe8, 0x0c, 0xb6, 0xfa, 0x94, - 0x70, 0xea, 0x96, 0xad, 0x0f, 0x04, 0xe5, 0x94, 0xdf, 0x4e, 0xdc, 0x28, 0x71, 0xe4, 0x94, 0xc6, - 0xa7, 0x3f, 0x2e, 0x47, 0x9f, 0x0a, 0x22, 0x75, 0x81, 0x83, 0xed, 0x2e, 0xbf, 0x93, 0xc8, 0xcd, - 0x62, 0xa8, 0x29, 0xe5, 0x66, 0x21, 0x00, 0x13, 0x73, 0x16, 0x58, 0xca, 0xc1, 0x22, 0x6b, 0x50, - 0x8e, 0xb3, 0xcb, 0x3b, 0xd2, 0xcd, 0xe3, 0xec, 0xf2, 0x2d, 0x69, 0xf7, 0x38, 0xbb, 0x7c, 0x47, - 0xca, 0x1f, 0x67, 0x97, 0xf3, 0x52, 0x61, 0xef, 0x80, 0xb1, 0xc4, 0x53, 0xc7, 0x65, 0x77, 0x00, - 0xda, 0x85, 0x45, 0xcb, 0x36, 0xf1, 0x50, 0x34, 0xc9, 0x9c, 0xea, 0x72, 0x91, 0xe0, 0x95, 0xdf, - 0x64, 0x60, 0x71, 0xbe, 0x27, 0x85, 0x9f, 0x8f, 0xf3, 0x1d, 0x82, 0xd9, 0x43, 0x3c, 0x63, 0x73, - 0x1b, 0xb1, 0x09, 0x18, 0x23, 0x0f, 0xcc, 0x38, 0x78, 0x70, 0xf5, 0xa6, 0x34, 0xa8, 0x0c, 0xeb, - 0xbe, 0x8d, 0x87, 0x03, 0xc7, 0xc5, 0x26, 0xbb, 0x4c, 0xb3, 0xf3, 0x34, 0x97, 0xda, 0x5a, 0x38, - 0x88, 0x5e, 0xa1, 0x07, 0xb0, 0xea, 0x10, 0xab, 0x6b, 0xd9, 0x3a, 0xbd, 0x60, 0x18, 0x15, 0x5b, - 0x2c, 0x6d, 0xd0, 0x39, 0x5f, 0xbd, 0x28, 0x2c, 0xd1, 0xcb, 0x48, 0xad, 0x68, 0xc0, 0x4d, 0xe8, - 0x17, 0xfa, 0x18, 0x96, 0x4c, 0xc6, 0xa7, 0x05, 0xb5, 0xca, 0x27, 0xf5, 0x6b, 0x9c, 0x75, 0x07, - 0xbd, 0x02, 0x1f, 0x83, 0x7e, 0x12, 0x44, 0x37, 0x37, 0x6b, 0x70, 0x90, 0x0c, 0x11, 0x77, 0xf4, - 0x08, 0x32, 0xb6, 0x73, 0x25, 0xa8, 0xd1, 0x5c, 0x1d, 0x18, 0xb5, 0x7f, 0xbc, 0xfc, 0xfb, 0xaf, - 0x0b, 0xa9, 0xd1, 0xcb, 0xd0, 0xfb, 0xdf, 0x2f, 0x80, 0x9c, 0xf4, 0xc0, 0x4c, 0xbb, 0x8d, 0x62, - 0xa9, 0xa9, 0xb5, 0xf5, 0xa9, 0xa7, 0xcf, 0xb7, 0xe1, 0xde, 0x98, 0x86, 0x7d, 0x28, 0x15, 0x5d, - 0x53, 0xca, 0x4d, 0xad, 0x12, 0xbe, 0x83, 0xe6, 0x61, 0x77, 0xcc, 0xac, 0xa4, 0x1c, 0xa9, 0x0d, - 0xbd, 0xdd, 0x6c, 0xea, 0xcd, 0x1a, 0x6d, 0x67, 0x26, 0xf5, 0xe5, 0x9a, 0xaa, 0x34, 0xe8, 0xd7, - 0xb1, 0x52, 0xa6, 0x4d, 0x4d, 0x01, 0x6e, 0x8f, 0xe9, 0x4f, 0xcf, 0x5a, 0x55, 0x45, 0x0b, 0x66, - 0x93, 0xb2, 0xe8, 0x36, 0xdc, 0x9c, 0xf6, 0x43, 0x6f, 0x9d, 0x16, 0x1b, 0xd2, 0x22, 0x2a, 0xc2, - 0x27, 0xe3, 0xca, 0x9a, 0xa6, 0x14, 0x2b, 0x4f, 0x47, 0xef, 0xb1, 0x7a, 0x53, 0xd3, 0xb5, 0x66, - 0xad, 0xa6, 0x54, 0xf4, 0x52, 0xb1, 0x7c, 0xa2, 0x9f, 0x36, 0x5b, 0x2d, 0xb5, 0x54, 0x53, 0x58, - 0xa7, 0x56, 0x7c, 0x2a, 0x2d, 0xa1, 0x8f, 0xe0, 0xd1, 0x18, 0x44, 0x5b, 0xad, 0x2b, 0xad, 0x76, - 0xb1, 0x7e, 0xaa, 0x97, 0x8b, 0xe5, 0xaa, 0x22, 0x3c, 0x55, 0x2a, 0x53, 0x43, 0x73, 0xbb, 0xd9, - 0x5f, 0xff, 0x31, 0x9f, 0x7a, 0xff, 0x6f, 0xe3, 0x8f, 0xd8, 0x91, 0x37, 0x6f, 0xde, 0xcb, 0xb5, - 0xb5, 0xa7, 0xd3, 0xd1, 0x65, 0xed, 0x1f, 0xd5, 0x3c, 0xd1, 0xd4, 0xb6, 0x12, 0xc6, 0x2b, 0xcd, - 0xfb, 0x45, 0xaa, 0xa8, 0x28, 0x35, 0xa5, 0xad, 0xf0, 0xee, 0x50, 0x5a, 0x18, 0xc9, 0x5b, 0x8a, - 0xa6, 0x16, 0x6b, 0xea, 0x17, 0xc5, 0x52, 0x4d, 0x91, 0x32, 0xe8, 0x16, 0xbc, 0xc1, 0xe5, 0x93, - 0xee, 0x65, 0xd1, 0x1d, 0xb8, 0xc5, 0x55, 0xc5, 0xd6, 0xd3, 0x46, 0x59, 0xcc, 0x74, 0x58, 0x54, - 0x6b, 0x67, 0x9a, 0x22, 0x2d, 0x0a, 0xef, 0x1f, 0x03, 0x9a, 0x3e, 0x7e, 0x68, 0x19, 0xb2, 0x8d, - 0x66, 0x43, 0x91, 0x52, 0x68, 0x15, 0x72, 0x34, 0x70, 0xcd, 0xc3, 0x43, 0x29, 0x8d, 0xd6, 0x61, - 0x45, 0xad, 0xd7, 0x95, 0x8a, 0x5a, 0x6c, 0x2b, 0xd2, 0x42, 0xe9, 0xde, 0xb7, 0xff, 0xca, 0xa7, - 0xbe, 0x7d, 0x99, 0x4f, 0xff, 0xfd, 0x65, 0x3e, 0xfd, 0xdd, 0xcb, 0x7c, 0xfa, 0x9f, 0x2f, 0xf3, - 0xe9, 0xdf, 0xfe, 0x3b, 0x9f, 0xfa, 0x22, 0x27, 0xb6, 0xf5, 0xff, 0x02, 0x00, 0x00, 0xff, 0xff, - 0xdd, 0x21, 0x2a, 0xdf, 0x1e, 0x1e, 0x00, 0x00, + 0x04, 0x49, 0x80, 0x61, 0x72, 0x22, 0xf1, 0xbe, 0xf6, 0xed, 0xdb, 0xb7, 0xef, 0xfd, 0xde, 0xc2, + 0x16, 0x71, 0xcd, 0xf6, 0x65, 0xff, 0xfc, 0x00, 0x13, 0xe2, 0x12, 0x6f, 0xbf, 0x4f, 0x5c, 0xdf, + 0x45, 0x9b, 0x6d, 0xb7, 0xfd, 0x8c, 0x71, 0xf6, 0x05, 0x7f, 0x07, 0x85, 0x82, 0x96, 0xe9, 0x9b, + 0x5c, 0x6c, 0x67, 0x3b, 0xa4, 0xf5, 0xb0, 0x6f, 0xc6, 0xe8, 0xf7, 0x3d, 0xdf, 0x25, 0x66, 0x07, + 0x1f, 0x60, 0xa7, 0x63, 0x3b, 0xe1, 0x0f, 0x95, 0x7b, 0xde, 0x6e, 0xbf, 0x2f, 0x84, 0x94, 0xc0, + 0xb7, 0xbb, 0x07, 0x97, 0xdd, 0xf6, 0x81, 0x6f, 0xf7, 0xb0, 0xe7, 0x9b, 0xbd, 0xbe, 0xe0, 0x6c, + 0x75, 0xdc, 0x8e, 0xcb, 0xfe, 0x1e, 0xd0, 0x7f, 0x9c, 0xba, 0xf7, 0xf5, 0x1c, 0xdc, 0xa8, 0xbb, + 0x7e, 0x15, 0x9b, 0x1e, 0xae, 0xb8, 0x5d, 0x0b, 0x13, 0x95, 0xba, 0x8c, 0xca, 0x90, 0x23, 0xb8, + 0xdf, 0xb5, 0xdb, 0xa6, 0x22, 0xdd, 0x95, 0x1e, 0xac, 0x3c, 0x7c, 0x63, 0x7f, 0xc2, 0xfb, 0x7d, + 0x9d, 0x4b, 0x94, 0xb1, 0xd7, 0x26, 0x76, 0xdf, 0x77, 0x49, 0x31, 0xfb, 0xcd, 0x8b, 0xdd, 0x8c, + 0x1e, 0xaa, 0xa2, 0x23, 0x58, 0xed, 0x52, 0xcb, 0xc6, 0x25, 0x33, 0xad, 0xcc, 0xcd, 0x6e, 0x4a, + 0x5f, 0xe9, 0x0e, 0x7d, 0x42, 0x8f, 0x60, 0x89, 0x98, 0x4e, 0x07, 0x1b, 0xb6, 0xa5, 0xcc, 0xdf, + 0x95, 0x1e, 0xcc, 0x17, 0x77, 0xe8, 0x4a, 0x2f, 0x5f, 0xec, 0xe6, 0x74, 0x4a, 0xd7, 0xca, 0xaf, + 0x86, 0x7f, 0xf5, 0x1c, 0x93, 0xd5, 0x2c, 0xb4, 0x0f, 0x0b, 0xcc, 0x8a, 0x92, 0x65, 0x0b, 0x2b, + 0x09, 0x0b, 0xb3, 0x9d, 0xeb, 0x5c, 0x0c, 0xdd, 0x07, 0x68, 0x07, 0x9e, 0xef, 0xf6, 0x8c, 0x9e, + 0xd7, 0x51, 0x16, 0xee, 0x4a, 0x0f, 0x96, 0xc5, 0x96, 0x96, 0x39, 0xbd, 0xe6, 0x75, 0x1e, 0x67, + 0xff, 0xfb, 0xd5, 0xae, 0xb4, 0xf7, 0x3a, 0x6c, 0xd5, 0x5d, 0x0b, 0x9f, 0x39, 0xe6, 0x73, 0xd3, + 0xee, 0x9a, 0xe7, 0x5d, 0xcc, 0x02, 0x27, 0xb8, 0xbb, 0x70, 0xf3, 0xcc, 0xf1, 0x82, 0x7e, 0xdf, + 0x25, 0x3e, 0xb6, 0x74, 0xfc, 0xeb, 0x00, 0x7b, 0x7e, 0x5c, 0xe0, 0x4b, 0x09, 0x10, 0x73, 0xb7, + 0xee, 0xfa, 0x87, 0x6e, 0xe0, 0x58, 0x3c, 0xec, 0xf1, 0x7d, 0x4a, 0xb3, 0xef, 0xf3, 0x11, 0x2c, + 0xd1, 0xe4, 0x60, 0x6a, 0x73, 0xa3, 0x6a, 0x4d, 0x4a, 0xe7, 0x6a, 0xe2, 0xaf, 0x9e, 0x63, 0xb2, + 0x9a, 0x25, 0x5c, 0xf9, 0xe3, 0x1c, 0xbc, 0xc6, 0x2c, 0x9e, 0xe0, 0xeb, 0x9a, 0xed, 0xf5, 0x4c, + 0xbf, 0x7d, 0xc9, 0xbd, 0x79, 0x1f, 0x36, 0x09, 0x77, 0xdd, 0xf0, 0x7c, 0x93, 0xf8, 0xc6, 0x33, + 0x7c, 0xcd, 0xdc, 0x5a, 0x2d, 0xe6, 0x5e, 0xbd, 0xd8, 0x9d, 0x3f, 0xc1, 0xd7, 0xfa, 0x86, 0x90, + 0x68, 0x52, 0x81, 0x13, 0x7c, 0x8d, 0x0e, 0x20, 0x24, 0x19, 0xd8, 0xb1, 0x98, 0xca, 0xdc, 0xa8, + 0xca, 0x9a, 0xe0, 0xab, 0x8e, 0x45, 0x15, 0x6a, 0x20, 0xf7, 0xc4, 0xb2, 0xd8, 0x32, 0xd8, 0x96, + 0xd8, 0x19, 0xaf, 0x3c, 0xdc, 0x4b, 0x4a, 0x14, 0xca, 0x8f, 0xa5, 0xc9, 0xc6, 0x50, 0x97, 0xb1, + 0xd0, 0x09, 0x6c, 0x78, 0x41, 0xa7, 0x83, 0x3d, 0x3f, 0xb2, 0x96, 0x9d, 0xd9, 0xda, 0x7a, 0xa4, + 0xca, 0x38, 0x22, 0x42, 0xff, 0x9b, 0x83, 0x3d, 0x1d, 0x9b, 0xd6, 0x13, 0xdb, 0xbf, 0xb4, 0x9d, + 0x33, 0xa7, 0x8d, 0x89, 0x6f, 0xda, 0x8e, 0x7f, 0xad, 0x39, 0x3e, 0x26, 0xcf, 0xcd, 0x2e, 0x0f, + 0xd7, 0x31, 0xac, 0x13, 0x6c, 0x5a, 0x46, 0x74, 0xf3, 0xc4, 0xd5, 0xb9, 0x13, 0x5b, 0x98, 0x5e, + 0xcf, 0xfd, 0xcb, 0x6e, 0x7b, 0xbf, 0x15, 0x0a, 0x89, 0x04, 0x5b, 0xa3, 0xaa, 0x11, 0x11, 0xe9, + 0x80, 0xf0, 0xc0, 0xf6, 0x7c, 0xdb, 0xe9, 0xc4, 0xec, 0xcd, 0xcd, 0x6e, 0x6f, 0x33, 0x54, 0x1f, + 0xda, 0x2c, 0xc2, 0x5a, 0xcf, 0x1c, 0xc4, 0xcc, 0xcd, 0xcf, 0x60, 0x4e, 0x5f, 0xed, 0x99, 0x83, + 0xa1, 0x8d, 0xcf, 0xe1, 0x86, 0x7b, 0xee, 0x61, 0xf2, 0x1c, 0xc7, 0xf6, 0xe9, 0x29, 0xd9, 0xbb, + 0xf3, 0x29, 0x17, 0xbb, 0x21, 0xa4, 0xc7, 0xfd, 0x43, 0xee, 0x38, 0xc3, 0x13, 0xd1, 0xfe, 0x02, + 0x6e, 0xb6, 0x88, 0xe9, 0x78, 0x66, 0xdb, 0xb7, 0x5d, 0xa7, 0x70, 0xce, 0xae, 0x10, 0x8f, 0xb0, + 0x06, 0x8b, 0x04, 0x9b, 0x9e, 0xeb, 0xb0, 0xc8, 0xae, 0x3f, 0xfc, 0x49, 0xc2, 0x82, 0x93, 0xba, + 0x3a, 0x53, 0x11, 0xeb, 0x0a, 0x03, 0x62, 0x2d, 0x13, 0xb6, 0x62, 0xf2, 0xa7, 0x81, 0x27, 0x32, + 0xbf, 0x04, 0xd0, 0x0f, 0xbc, 0x4b, 0x8c, 0x0d, 0x7f, 0xe0, 0x88, 0x63, 0xcc, 0x4f, 0x5f, 0x2c, + 0x2c, 0x14, 0x5c, 0xaf, 0x35, 0x08, 0x97, 0xb8, 0x80, 0xd7, 0x62, 0x52, 0x3a, 0xf6, 0xc9, 0x35, + 0x5f, 0xe3, 0x68, 0x6c, 0x33, 0xef, 0x4c, 0xb7, 0xcf, 0x34, 0xa7, 0x6c, 0xe5, 0x5b, 0x09, 0xb6, + 0x63, 0xe2, 0x4d, 0xdf, 0xf4, 0x03, 0x8f, 0xaf, 0xb4, 0x0d, 0xf3, 0xb4, 0x9e, 0x49, 0xb1, 0x7a, + 0x46, 0x09, 0xa8, 0x1e, 0x79, 0x30, 0xc7, 0x3c, 0xf8, 0xe9, 0x74, 0x0f, 0x62, 0x26, 0xf7, 0x93, + 0x1c, 0xd9, 0x3b, 0x85, 0x45, 0x4e, 0x47, 0x08, 0xd6, 0x75, 0xb5, 0xd0, 0x6c, 0xd4, 0x8d, 0xb3, + 0xfa, 0x49, 0xbd, 0xf1, 0xa4, 0x2e, 0x67, 0x90, 0x02, 0x5b, 0x82, 0xd6, 0xfa, 0xb4, 0x6e, 0xd4, + 0x1b, 0x2d, 0xe3, 0xb0, 0x71, 0x56, 0x2f, 0xcb, 0xd2, 0x18, 0xa7, 0xd4, 0xa8, 0xd5, 0xb4, 0x56, + 0x4b, 0x2d, 0xcb, 0x73, 0x62, 0x6b, 0x4f, 0x41, 0x7e, 0x42, 0x6c, 0x1f, 0xd3, 0xeb, 0xe6, 0xf0, + 0x32, 0x8a, 0x3e, 0x80, 0x9c, 0xcd, 0x3e, 0x3d, 0x45, 0x62, 0xc9, 0x77, 0x2b, 0xc1, 0x79, 0xae, + 0x10, 0x76, 0x25, 0x21, 0xcf, 0x8d, 0x1e, 0x67, 0x97, 0xe6, 0xe4, 0xf9, 0xbd, 0xbf, 0x4a, 0xc2, + 0x76, 0xcb, 0x75, 0x1b, 0x5d, 0x91, 0x66, 0x05, 0x58, 0xfe, 0x51, 0x77, 0x78, 0xa8, 0x85, 0xea, + 0x20, 0x9b, 0x6d, 0x3f, 0x30, 0xbb, 0x3f, 0xee, 0xf6, 0x6e, 0x70, 0xe5, 0x88, 0x2c, 0x02, 0xb1, + 0x03, 0xa8, 0xd1, 0xa7, 0xdd, 0xc4, 0x26, 0xd8, 0x6b, 0x0d, 0x9c, 0x78, 0x47, 0x79, 0x0a, 0x5b, + 0x25, 0xd7, 0xb1, 0x6c, 0x7a, 0x52, 0x87, 0xa6, 0xdd, 0x0d, 0xef, 0xcc, 0x2f, 0x60, 0x55, 0x78, + 0xf2, 0xdc, 0xec, 0x06, 0x58, 0xec, 0x27, 0xa9, 0x15, 0x7e, 0x42, 0xf9, 0xfa, 0x0a, 0x97, 0x66, + 0x1f, 0xc2, 0xf4, 0xdf, 0x24, 0x40, 0xbc, 0x4f, 0xe2, 0x2f, 0x70, 0x3b, 0xba, 0x8d, 0x79, 0xc8, + 0xf5, 0xb0, 0xe7, 0x99, 0x1d, 0x3c, 0x92, 0x5a, 0x21, 0x11, 0x7d, 0x08, 0xcb, 0xa2, 0xd2, 0x63, + 0x4b, 0x6c, 0x3e, 0xb5, 0x03, 0x87, 0x11, 0x8c, 0x14, 0xd0, 0x63, 0x58, 0x0a, 0x4b, 0x98, 0x28, + 0x54, 0xdf, 0xa7, 0x1c, 0xc9, 0x0b, 0xb7, 0x7f, 0x0e, 0xcb, 0x4d, 0xec, 0xcc, 0xe6, 0xec, 0x48, + 0x52, 0x5c, 0xc1, 0x56, 0xa1, 0x77, 0x6e, 0x77, 0x02, 0x37, 0xf0, 0x74, 0xec, 0x05, 0x5d, 0x7f, + 0xb6, 0x0d, 0x7f, 0x00, 0x2b, 0x57, 0xc4, 0xec, 0xf7, 0xb1, 0x65, 0x60, 0x42, 0xa6, 0x6c, 0x99, + 0x99, 0xd3, 0x41, 0x08, 0xab, 0x24, 0x3c, 0xc3, 0x3b, 0xb4, 0x13, 0x5f, 0xf8, 0x47, 0xc4, 0x0d, + 0xfa, 0x65, 0xdc, 0xc5, 0x61, 0xa8, 0x05, 0x1b, 0xc3, 0xb6, 0xc0, 0x49, 0x25, 0x97, 0x90, 0xa0, + 0x4f, 0x8f, 0x9a, 0x7b, 0x76, 0x0f, 0x96, 0x19, 0xd4, 0x34, 0xc6, 0xef, 0xf9, 0x12, 0x23, 0xd7, + 0xbc, 0x0e, 0xda, 0x83, 0xe5, 0x3e, 0x71, 0xdb, 0xd8, 0xf3, 0xc4, 0x69, 0x2c, 0x45, 0x15, 0x2b, + 0x24, 0x47, 0x99, 0x84, 0xc4, 0x32, 0xf1, 0x4b, 0xf1, 0x11, 0x80, 0x80, 0x75, 0x21, 0x38, 0x59, + 0x28, 0xe6, 0x05, 0xca, 0x58, 0x16, 0xf2, 0x0c, 0x67, 0x0c, 0x3f, 0xe8, 0x71, 0xf2, 0xbf, 0xa1, + 0xe9, 0x8f, 0x01, 0x31, 0x14, 0x32, 0x81, 0x7a, 0x22, 0xf8, 0x22, 0xfd, 0x50, 0xf8, 0x52, 0xa3, + 0x50, 0xeb, 0xd2, 0x74, 0xac, 0x2e, 0xad, 0xf4, 0x3e, 0xb9, 0x8e, 0xb0, 0x18, 0x7a, 0x08, 0xd9, + 0xbe, 0x4a, 0xc8, 0x94, 0x94, 0x67, 0x72, 0x22, 0x0e, 0x4c, 0x76, 0xef, 0x3b, 0x09, 0x94, 0xca, + 0x98, 0xb5, 0xf0, 0xa6, 0xa5, 0x16, 0xd2, 0xcf, 0x61, 0xd1, 0x1f, 0x38, 0x21, 0xfa, 0x5a, 0x2d, + 0x96, 0x29, 0xeb, 0x9f, 0x2f, 0x76, 0xdf, 0xef, 0xd8, 0xfe, 0x65, 0x70, 0xbe, 0xdf, 0x76, 0x7b, + 0x07, 0xd1, 0xe2, 0xd6, 0xf9, 0xf0, 0xff, 0x41, 0xff, 0x59, 0xe7, 0x80, 0x41, 0xf6, 0x20, 0xb0, + 0xad, 0xfd, 0xb3, 0x33, 0xad, 0xfc, 0xf2, 0xc5, 0xee, 0x42, 0x6b, 0xe0, 0x68, 0x65, 0x7d, 0xc1, + 0x1f, 0x38, 0x9a, 0x85, 0x0e, 0x61, 0xc5, 0x1f, 0x16, 0x61, 0x71, 0x17, 0x66, 0x6b, 0x46, 0x71, + 0x45, 0x11, 0xae, 0xb7, 0x61, 0xb7, 0x35, 0x70, 0x0a, 0x5d, 0x0a, 0x37, 0xae, 0x55, 0xa7, 0xed, + 0x06, 0x14, 0xc3, 0x88, 0x3c, 0x8b, 0x27, 0xdb, 0xef, 0x25, 0xd8, 0xa2, 0xf5, 0xb3, 0x83, 0x49, + 0xe3, 0x39, 0x26, 0x17, 0x5d, 0xf7, 0x8a, 0x07, 0xe1, 0x16, 0xcc, 0x27, 0xe0, 0x40, 0x4a, 0x43, + 0xef, 0xc0, 0x5a, 0x3b, 0x20, 0x04, 0x3b, 0xbe, 0x28, 0x36, 0x1c, 0x8c, 0x72, 0x67, 0x56, 0x05, + 0x8b, 0x55, 0x16, 0xf4, 0x1e, 0x6c, 0xd8, 0x4e, 0x9b, 0xe0, 0xde, 0x50, 0x78, 0x3e, 0x26, 0xbc, + 0x1e, 0x31, 0xe3, 0x85, 0xa8, 0x06, 0x9b, 0x35, 0x7b, 0x80, 0xad, 0x66, 0xd0, 0xa6, 0x19, 0x1b, + 0x9e, 0x72, 0x4e, 0x5c, 0xa4, 0xef, 0x3b, 0x68, 0x3d, 0x14, 0x14, 0xe6, 0xfe, 0x22, 0xc1, 0xed, + 0x22, 0xc5, 0x8e, 0xc3, 0xf2, 0x8b, 0x2f, 0x5c, 0x82, 0x8f, 0x4a, 0x51, 0x1f, 0x68, 0xfd, 0xa8, + 0x3e, 0x30, 0xc4, 0x4b, 0xd4, 0xc4, 0x25, 0xc1, 0x1e, 0x1d, 0x80, 0x7e, 0x48, 0x03, 0x18, 0x6a, + 0x09, 0x5f, 0x3f, 0x05, 0xc4, 0xbb, 0x59, 0xcd, 0xf6, 0x3c, 0xdb, 0xe9, 0x70, 0x0f, 0x3f, 0x84, + 0xd5, 0x2b, 0xe2, 0x3a, 0x1d, 0x83, 0xf7, 0x36, 0xe1, 0x64, 0x7a, 0x2b, 0xd4, 0x57, 0x98, 0x38, + 0xff, 0x18, 0x4e, 0x32, 0x35, 0x4c, 0x3a, 0x58, 0x73, 0x4e, 0x89, 0xdb, 0x21, 0x61, 0x5c, 0x05, + 0xf7, 0x95, 0x04, 0x37, 0x18, 0x16, 0x3e, 0xc4, 0xe2, 0x46, 0xf0, 0x95, 0x4f, 0xc6, 0xd0, 0xcb, + 0x7b, 0x69, 0xe8, 0x7a, 0x54, 0x2f, 0x19, 0x38, 0xfc, 0x56, 0x8a, 0x90, 0xc3, 0x0e, 0x6c, 0x0b, + 0x2c, 0xa0, 0xab, 0xa7, 0x55, 0xad, 0x54, 0x30, 0x74, 0xb5, 0xd6, 0xf8, 0x44, 0x2d, 0xcb, 0x19, + 0xb4, 0x0d, 0x28, 0xe4, 0x15, 0xea, 0x47, 0xaa, 0xd1, 0x3c, 0xad, 0x6a, 0x2d, 0x59, 0x42, 0x37, + 0xe1, 0xc6, 0x08, 0xbd, 0xa6, 0xea, 0x47, 0x14, 0x3e, 0xc4, 0x80, 0x85, 0x5e, 0x38, 0x6c, 0x19, + 0xcd, 0x7a, 0xe1, 0xb4, 0x59, 0x69, 0xb4, 0xe4, 0x79, 0x94, 0x87, 0x1d, 0xc1, 0xa9, 0x36, 0x8e, + 0xb4, 0x52, 0xa1, 0x6a, 0x34, 0x4e, 0x9b, 0x46, 0x4d, 0x6b, 0x36, 0xb5, 0xfa, 0x91, 0x9c, 0x15, + 0x9b, 0xff, 0xf3, 0x16, 0xac, 0x30, 0xb7, 0xcb, 0xd8, 0x37, 0xed, 0x2e, 0xd2, 0x41, 0x76, 0x5c, + 0xdf, 0x18, 0x99, 0x69, 0x79, 0xc8, 0xdf, 0x4a, 0xd8, 0x7e, 0xc2, 0x5c, 0x5d, 0xc9, 0xe8, 0xeb, + 0xce, 0x08, 0x19, 0x35, 0x60, 0x83, 0x8f, 0x7c, 0xd4, 0xf2, 0x05, 0x2d, 0x8a, 0x22, 0x4f, 0xde, + 0x4c, 0x8b, 0xe8, 0x48, 0xf1, 0xac, 0xd0, 0xd1, 0x21, 0x4e, 0x45, 0x9f, 0x02, 0xe2, 0x06, 0x9f, + 0xe1, 0x6b, 0x23, 0x9c, 0x8e, 0x44, 0xd9, 0x78, 0x90, 0x66, 0x73, 0x7c, 0xf6, 0xab, 0x64, 0x74, + 0x99, 0x8c, 0x31, 0xd0, 0x6f, 0x24, 0xb8, 0xcb, 0x26, 0x9c, 0x2b, 0x36, 0x08, 0x19, 0xc1, 0x70, + 0x12, 0x62, 0x09, 0x48, 0x47, 0x21, 0x31, 0x6c, 0x3d, 0x4a, 0x9c, 0xf1, 0xbf, 0x6f, 0x84, 0xaa, + 0x64, 0xf4, 0x3b, 0x64, 0x9a, 0x14, 0xfa, 0x15, 0xdc, 0x88, 0xd5, 0x34, 0xc3, 0xe4, 0x08, 0x9f, + 0x8d, 0xea, 0x2b, 0x0f, 0xdf, 0x9d, 0x69, 0x1c, 0x08, 0x57, 0x42, 0xfe, 0x04, 0x0b, 0xb5, 0x40, + 0x8e, 0x9b, 0xa7, 0x58, 0x5e, 0x59, 0x64, 0xb6, 0xdf, 0x9e, 0x6e, 0x3b, 0x1a, 0x1d, 0x2a, 0x19, + 0x7d, 0xc3, 0x1f, 0xa5, 0xa3, 0x27, 0xb0, 0x19, 0xb7, 0x4a, 0xe8, 0x6d, 0x50, 0x72, 0xa9, 0x07, + 0x92, 0x38, 0x2e, 0xd0, 0x03, 0xf1, 0xc7, 0x18, 0xe8, 0x33, 0x88, 0x6f, 0x82, 0x0e, 0xe9, 0x7e, + 0xe0, 0x29, 0x4b, 0xcc, 0xf2, 0x3b, 0x33, 0x83, 0xf9, 0x4a, 0x46, 0x8f, 0xfb, 0xc7, 0x39, 0xa8, + 0x42, 0x4b, 0x8b, 0xed, 0xe3, 0xb0, 0xb4, 0x2c, 0x33, 0xab, 0xf7, 0x13, 0xac, 0x8e, 0x63, 0xf3, + 0x4a, 0x86, 0x96, 0x99, 0x88, 0x86, 0x34, 0x58, 0xe3, 0x96, 0x7c, 0xd7, 0x35, 0x68, 0x1d, 0x84, + 0xe9, 0xa6, 0x62, 0xa8, 0x23, 0x32, 0xc5, 0x69, 0xf4, 0xb2, 0xb8, 0x7d, 0x83, 0x08, 0x04, 0xcc, + 0x86, 0xb3, 0x95, 0xd4, 0xcb, 0x32, 0x09, 0x95, 0xe9, 0x65, 0x71, 0xe3, 0x54, 0x7a, 0xe0, 0xed, + 0x10, 0x35, 0x1b, 0x17, 0x0c, 0x36, 0x2b, 0xab, 0xa9, 0x07, 0x9e, 0x04, 0xb0, 0xe9, 0x81, 0xb7, + 0x47, 0xe9, 0xa8, 0x0e, 0xeb, 0xbc, 0x46, 0x10, 0x01, 0x98, 0x95, 0xb5, 0x54, 0x2f, 0x27, 0x81, + 0x35, 0xf5, 0xb2, 0x1b, 0xa7, 0x52, 0x2f, 0x1d, 0xd7, 0xc2, 0x46, 0x30, 0x7c, 0x6d, 0x52, 0xd6, + 0x53, 0xbd, 0x4c, 0x7a, 0x97, 0xa2, 0x5e, 0x3a, 0xa3, 0x74, 0x0a, 0x8f, 0x3c, 0xec, 0x58, 0xca, + 0x06, 0xb3, 0xf4, 0x7a, 0x82, 0xa5, 0x08, 0x3e, 0x57, 0x32, 0x3a, 0x93, 0xe5, 0xc5, 0xe5, 0xc2, + 0x37, 0x3a, 0x14, 0xa2, 0x1a, 0x16, 0xc7, 0xa8, 0x8a, 0x3c, 0xa5, 0xb8, 0x24, 0xc0, 0x59, 0x5e, + 0x5c, 0x46, 0x19, 0x34, 0x97, 0x43, 0x7c, 0xd9, 0x8e, 0xd0, 0xad, 0xb2, 0x99, 0x9a, 0xcb, 0xc9, + 0x48, 0x98, 0xe6, 0x32, 0x19, 0xe7, 0xb0, 0x1a, 0x2b, 0x6c, 0x87, 0x39, 0x88, 0xd2, 0x6b, 0xec, + 0x04, 0xf6, 0x65, 0x35, 0x36, 0x4e, 0xa5, 0x07, 0x62, 0x86, 0x13, 0x82, 0x41, 0xd8, 0x88, 0xa0, + 0xec, 0xa4, 0x1e, 0x48, 0xd2, 0x30, 0x41, 0x0f, 0xc4, 0x1c, 0xa5, 0x53, 0x37, 0x39, 0x0e, 0x1e, + 0xb6, 0x82, 0xdb, 0xa9, 0x6e, 0x4e, 0xe2, 0x68, 0xea, 0xa6, 0x17, 0xa7, 0xa2, 0x2e, 0xdc, 0x16, + 0xc8, 0x98, 0x17, 0x1d, 0x7a, 0xec, 0xf4, 0xd2, 0x18, 0x6c, 0x2a, 0x50, 0x5e, 0x67, 0xc6, 0x93, + 0x1e, 0x51, 0xd2, 0x10, 0x70, 0x25, 0xa3, 0x2b, 0x97, 0x69, 0xe8, 0xb8, 0x05, 0xb2, 0xcd, 0x01, + 0xa3, 0xe1, 0x0a, 0xc4, 0xa8, 0xec, 0xa6, 0x06, 0x25, 0x09, 0x5b, 0xd2, 0xa0, 0xd8, 0xa3, 0x74, + 0x5a, 0xf1, 0x83, 0xe1, 0x53, 0xaa, 0x21, 0x06, 0x44, 0xe5, 0x6e, 0x6a, 0xc5, 0x4f, 0x79, 0x78, + 0xa5, 0x15, 0x3f, 0x98, 0x60, 0xa1, 0x13, 0x58, 0xeb, 0x51, 0x48, 0x69, 0x78, 0x1c, 0x53, 0x2a, + 0xf7, 0x52, 0xdf, 0xa8, 0x27, 0xa0, 0x67, 0x25, 0xa3, 0xaf, 0xf6, 0x62, 0x44, 0xf4, 0x39, 0xc8, + 0xd1, 0xb8, 0x6f, 0x9c, 0x33, 0x2c, 0xa9, 0xec, 0x31, 0x7b, 0xfb, 0x09, 0xf6, 0xa6, 0x40, 0x4f, + 0xd6, 0x45, 0x46, 0x39, 0xe8, 0x0a, 0xee, 0xd0, 0xa3, 0x33, 0x39, 0x74, 0x37, 0xf0, 0x10, 0xbb, + 0x8b, 0xe3, 0xbc, 0xcf, 0x56, 0x7a, 0x98, 0x54, 0xf7, 0xa7, 0x23, 0xfe, 0x4a, 0x46, 0xdf, 0xf1, + 0x53, 0x45, 0x68, 0x35, 0xe3, 0x3d, 0x80, 0xa2, 0x09, 0x8a, 0x3d, 0x95, 0x37, 0x52, 0xb3, 0x72, + 0x12, 0xa3, 0xd2, 0xac, 0xb4, 0xe3, 0x54, 0x74, 0x06, 0x9b, 0x3d, 0x0a, 0x38, 0x0d, 0xdb, 0x31, + 0xfa, 0x02, 0x72, 0x2a, 0x6f, 0xa6, 0x26, 0x4a, 0x12, 0x38, 0xa5, 0xf1, 0xe9, 0x8d, 0xd2, 0xd1, + 0xc7, 0x02, 0x48, 0x5d, 0xe0, 0x30, 0xdd, 0x95, 0xb7, 0x52, 0xb1, 0x59, 0x02, 0x34, 0xa5, 0xd8, + 0x2c, 0x32, 0xc0, 0xc8, 0x1c, 0x05, 0x16, 0x73, 0xb0, 0xc0, 0x06, 0x94, 0xe3, 0xec, 0xd2, 0xb6, + 0x7c, 0xf3, 0x38, 0xbb, 0x74, 0x4b, 0xde, 0x39, 0xce, 0x2e, 0xdd, 0x91, 0xf3, 0xc7, 0xd9, 0xa5, + 0xbc, 0xbc, 0xbb, 0x77, 0xc0, 0x50, 0xe2, 0xa9, 0xeb, 0xb1, 0x1e, 0x80, 0x76, 0x60, 0xc1, 0x76, + 0x2c, 0x3c, 0x10, 0x43, 0x32, 0x87, 0xba, 0x9c, 0x24, 0x70, 0xe5, 0xd7, 0xf3, 0xb0, 0x30, 0xdb, + 0x93, 0xc2, 0x2f, 0x47, 0xf1, 0x0e, 0xc1, 0xec, 0x21, 0x9e, 0xa1, 0xb9, 0xf5, 0xc4, 0x03, 0x18, + 0x01, 0x0f, 0x4c, 0x38, 0x7c, 0x70, 0xf5, 0x27, 0x38, 0xa8, 0x04, 0x6b, 0x81, 0x83, 0x07, 0x7d, + 0xd7, 0xc3, 0x16, 0x6b, 0xa6, 0xd9, 0x59, 0x86, 0x4b, 0x7d, 0x35, 0x52, 0xa2, 0x2d, 0xf4, 0x00, + 0x56, 0x5c, 0x62, 0x77, 0x6c, 0xc7, 0xa0, 0x0d, 0x86, 0x41, 0xb1, 0x85, 0xe2, 0x3a, 0x5d, 0xf3, + 0xd5, 0x8b, 0xdd, 0x45, 0xda, 0x8c, 0xb4, 0xb2, 0x0e, 0x5c, 0x84, 0x7e, 0xa1, 0x0f, 0x61, 0xd1, + 0x62, 0x78, 0x5a, 0x40, 0xab, 0x7c, 0xda, 0xbc, 0xc6, 0x51, 0x77, 0x38, 0x2b, 0x70, 0x1d, 0xf4, + 0xb3, 0x30, 0xba, 0xb9, 0x69, 0xca, 0xe1, 0x61, 0x88, 0xb8, 0xa3, 0x47, 0x30, 0xef, 0xb8, 0x57, + 0x02, 0x1a, 0xcd, 0x34, 0x81, 0x51, 0xf9, 0xc7, 0x4b, 0x7f, 0xf8, 0x6a, 0x37, 0x33, 0x7c, 0x19, + 0x7a, 0xf7, 0xbb, 0x39, 0x50, 0xd2, 0x1e, 0x98, 0xe9, 0xb4, 0x51, 0x28, 0x36, 0xf4, 0x96, 0x31, + 0xf1, 0xf4, 0xf9, 0x26, 0xdc, 0x1b, 0xe1, 0xb0, 0x0f, 0xb5, 0x6c, 0xe8, 0x6a, 0xa9, 0xa1, 0x97, + 0xa3, 0x77, 0xd0, 0x3c, 0xec, 0x8c, 0x88, 0x15, 0xd5, 0x23, 0xad, 0x6e, 0xb4, 0x1a, 0x0d, 0xa3, + 0x51, 0xa5, 0xe3, 0xcc, 0x38, 0xbf, 0x54, 0xd5, 0xd4, 0x3a, 0xfd, 0x3a, 0x56, 0x4b, 0x74, 0xa8, + 0xd9, 0x85, 0xdb, 0x23, 0xfc, 0xd3, 0xb3, 0x66, 0x45, 0xd5, 0xc3, 0xd5, 0xe4, 0x2c, 0xba, 0x0d, + 0x37, 0x27, 0xfd, 0x30, 0x9a, 0xa7, 0x85, 0xba, 0xbc, 0x80, 0x0a, 0xf0, 0xd1, 0x28, 0xb3, 0xaa, + 0xab, 0x85, 0xf2, 0xd3, 0xe1, 0x7b, 0xac, 0xd1, 0xd0, 0x0d, 0xbd, 0x51, 0xad, 0xaa, 0x65, 0xa3, + 0x58, 0x28, 0x9d, 0x18, 0xa7, 0x8d, 0x66, 0x53, 0x2b, 0x56, 0x55, 0x36, 0xa9, 0x15, 0x9e, 0xca, + 0x8b, 0xe8, 0x03, 0x78, 0x34, 0x62, 0xa2, 0xa5, 0xd5, 0xd4, 0x66, 0xab, 0x50, 0x3b, 0x35, 0x4a, + 0x85, 0x52, 0x45, 0x15, 0x9e, 0xaa, 0xe5, 0x09, 0xd5, 0xdc, 0x4e, 0xf6, 0xcb, 0x3f, 0xe5, 0x33, + 0xef, 0xfe, 0x7d, 0xf4, 0x11, 0x3b, 0xf6, 0xe6, 0xcd, 0x67, 0xb9, 0x96, 0xfe, 0x74, 0x32, 0xba, + 0x6c, 0xfc, 0xa3, 0x9c, 0x27, 0xba, 0xd6, 0x52, 0xa3, 0x78, 0x49, 0x7c, 0x5e, 0xa4, 0x8c, 0xb2, + 0x5a, 0x55, 0x5b, 0x2a, 0x9f, 0x0e, 0xe5, 0xb9, 0x21, 0xbd, 0xa9, 0xea, 0x5a, 0xa1, 0xaa, 0x7d, + 0x56, 0x28, 0x56, 0x55, 0x79, 0x1e, 0xdd, 0x82, 0xd7, 0x38, 0x7d, 0xdc, 0xbd, 0x2c, 0xba, 0x03, + 0xb7, 0x38, 0xab, 0xd0, 0x7c, 0x5a, 0x2f, 0x89, 0x95, 0x0e, 0x0b, 0x5a, 0xf5, 0x4c, 0x57, 0xe5, + 0x05, 0xe1, 0xfd, 0x63, 0x40, 0x93, 0xd7, 0x0f, 0x2d, 0x41, 0xb6, 0xde, 0xa8, 0xab, 0x72, 0x06, + 0xad, 0x40, 0x8e, 0x06, 0xae, 0x71, 0x78, 0x28, 0x4b, 0x68, 0x0d, 0x96, 0xb5, 0x5a, 0x4d, 0x2d, + 0x6b, 0x85, 0x96, 0x2a, 0xcf, 0x15, 0xef, 0x7d, 0xf3, 0xef, 0x7c, 0xe6, 0x9b, 0x97, 0x79, 0xe9, + 0x1f, 0x2f, 0xf3, 0xd2, 0xb7, 0x2f, 0xf3, 0xd2, 0xbf, 0x5e, 0xe6, 0xa5, 0xdf, 0xfd, 0x27, 0x9f, + 0xf9, 0x2c, 0x27, 0xd2, 0xfa, 0xff, 0x01, 0x00, 0x00, 0xff, 0xff, 0x82, 0x01, 0xe3, 0xfb, 0x56, + 0x1e, 0x00, 0x00, } diff --git a/pkg/roachpb/errors.proto b/pkg/roachpb/errors.proto index 5f12e08c0fd1..7ecc967ce823 100644 --- a/pkg/roachpb/errors.proto +++ b/pkg/roachpb/errors.proto @@ -71,6 +71,9 @@ message RangeNotFoundError { optional int64 range_id = 1 [(gogoproto.nullable) = false, (gogoproto.customname) = "RangeID", (gogoproto.casttype) = "RangeID"]; + // store_id is nonzero only if the error originated on a Store. + optional int64 store_id = 2 [(gogoproto.nullable) = false, + (gogoproto.customname) = "StoreID", (gogoproto.casttype) = "StoreID"]; } // A RangeKeyMismatchError indicates that a command was sent to a diff --git a/pkg/server/status.go b/pkg/server/status.go index b78784681ede..de42e0913836 100644 --- a/pkg/server/status.go +++ b/pkg/server/status.go @@ -1333,7 +1333,7 @@ func (s *statusServer) CommandQueue( } if replica == nil { - return nil, roachpb.NewRangeNotFoundError(rangeID) + return nil, roachpb.NewRangeNotFoundError(rangeID, 0) } return &serverpb.CommandQueueResponse{ diff --git a/pkg/storage/replica.go b/pkg/storage/replica.go index 64b0542d09a6..8bdec913c3fd 100644 --- a/pkg/storage/replica.go +++ b/pkg/storage/replica.go @@ -1772,7 +1772,7 @@ func (r *Replica) getReplicaDescriptorRLocked() (roachpb.ReplicaDescriptor, erro if ok { return repDesc, nil } - return roachpb.ReplicaDescriptor{}, roachpb.NewRangeNotFoundError(r.RangeID) + return roachpb.ReplicaDescriptor{}, roachpb.NewRangeNotFoundError(r.RangeID, r.store.StoreID()) } func (r *Replica) getMergeCompleteCh() chan struct{} { @@ -2047,7 +2047,7 @@ func (r *Replica) sendWithRangeID( if _, ok := pErr.GetDetail().(*roachpb.RaftGroupDeletedError); ok { // This error needs to be converted appropriately so that // clients will retry. - pErr = roachpb.NewError(roachpb.NewRangeNotFoundError(r.RangeID)) + pErr = roachpb.NewError(roachpb.NewRangeNotFoundError(r.RangeID, r.store.StoreID())) } log.Eventf(ctx, "replica.Send got error: %s", pErr) } else { @@ -2990,7 +2990,7 @@ func (r *Replica) maybeWatchForMerge(ctx context.Context) error { // The merge committed but the left-hand replica on this store hasn't // subsumed this replica yet. Mark this replica as destroyed so it // doesn't serve requests when we close the mergeCompleteCh below. - r.mu.destroyStatus.Set(roachpb.NewRangeNotFoundError(r.RangeID), destroyReasonMergePending) + r.mu.destroyStatus.Set(roachpb.NewRangeNotFoundError(r.RangeID, r.store.StoreID()), destroyReasonMergePending) } // Unblock pending requests. If the merge committed, the requests will // notice that the replica has been destroyed and return an appropriate diff --git a/pkg/storage/store.go b/pkg/storage/store.go index 77cec15910c9..bc5707cba169 100644 --- a/pkg/storage/store.go +++ b/pkg/storage/store.go @@ -2126,7 +2126,7 @@ func (s *Store) GetReplica(rangeID roachpb.RangeID) (*Replica, error) { if value, ok := s.mu.replicas.Load(int64(rangeID)); ok { return (*Replica)(value), nil } - return nil, roachpb.NewRangeNotFoundError(rangeID) + return nil, roachpb.NewRangeNotFoundError(rangeID, s.StoreID()) } // LookupReplica looks up the replica that contains the specified key. It @@ -2772,7 +2772,7 @@ func (s *Store) removeReplicaImpl( rep.mu.Lock() rep.cancelPendingCommandsLocked() rep.mu.internalRaftGroup = nil - rep.mu.destroyStatus.Set(roachpb.NewRangeNotFoundError(rep.RangeID), destroyReasonRemoved) + rep.mu.destroyStatus.Set(roachpb.NewRangeNotFoundError(rep.RangeID, rep.store.StoreID()), destroyReasonRemoved) rep.mu.Unlock() rep.readOnlyCmdMu.Unlock() @@ -3793,7 +3793,8 @@ func (s *Store) HandleRaftResponse(ctx context.Context, resp *RaftMessageRespons // could be re-added with a higher replicaID, in which this error is // cleared in setReplicaIDRaftMuLockedMuLocked. if repl.mu.destroyStatus.IsAlive() { - repl.mu.destroyStatus.Set(roachpb.NewRangeNotFoundError(repl.RangeID), destroyReasonRemovalPending) + storeID := repl.store.StoreID() + repl.mu.destroyStatus.Set(roachpb.NewRangeNotFoundError(repl.RangeID, storeID), destroyReasonRemovalPending) } repl.mu.Unlock() diff --git a/pkg/storage/stores.go b/pkg/storage/stores.go index 30f21ec25e38..a24cda41a255 100644 --- a/pkg/storage/stores.go +++ b/pkg/storage/stores.go @@ -157,7 +157,7 @@ func (ls *Stores) GetReplicaForRangeID(rangeID roachpb.RangeID) (*Replica, error return nil, err } if replica == nil { - return nil, roachpb.NewRangeNotFoundError(rangeID) + return nil, roachpb.NewRangeNotFoundError(rangeID, 0) } return replica, nil } diff --git a/pkg/storage/stores_test.go b/pkg/storage/stores_test.go index 6c0db7c52152..bfe8b8b39b30 100644 --- a/pkg/storage/stores_test.go +++ b/pkg/storage/stores_test.go @@ -173,7 +173,7 @@ func TestStoresGetReplicaForRangeID(t *testing.T) { if replica2 != nil { t.Fatalf("expected replica to be nil; was %v", replica2) } - expectedError := roachpb.NewRangeNotFoundError(rangeID2) + expectedError := roachpb.NewRangeNotFoundError(rangeID2, 0) if err2.Error() != expectedError.Error() { t.Fatalf("expected err to be %v; was %v", expectedError, err2) } From 4f0d25a77c54c10f03ea583aa3622379833a458d Mon Sep 17 00:00:00 2001 From: Tobias Schottdorf Date: Mon, 8 Oct 2018 15:02:49 +0200 Subject: [PATCH 4/5] kv: try next replica on RangeNotFoundError Previously, if a Batch RPC came back with a RangeNotFoundError, we would immediately stop trying to send to more replicas, evict the range descriptor, and start a new attempt after a back-off. This new attempt could end up using the same replica, so if the RangeNotFoundError persisted for some amount of time, so would the unsuccessful retries for requests to it as DistSender doesn't aggressively shuffle the replicas. It turns out that there are such situations, and the election-after-restart roachtest spuriously hit one of them: 1. new replica receives a preemptive snapshot and the ConfChange 2. cluster restarts 3. now the new replica is in this state until the range wakes up, which may not happen for some time. 4. the first request to the range runs into the above problem @nvanbenschoten: I think there is an issue to be filed about the tendency of DistSender to get stuck in unfortunate configurations. Fixes #30613. Release note (bug fix): Avoid repeatedly trying a replica that was found to be in the process of being added. --- pkg/kv/dist_sender.go | 35 +++++++++++++--- pkg/kv/dist_sender_test.go | 84 ++++++++++++++++++++++++++++++++++++++ pkg/storage/store_test.go | 5 --- 3 files changed, 113 insertions(+), 11 deletions(-) diff --git a/pkg/kv/dist_sender.go b/pkg/kv/dist_sender.go index 214bec3dc3fb..9d5979faee72 100644 --- a/pkg/kv/dist_sender.go +++ b/pkg/kv/dist_sender.go @@ -1114,7 +1114,7 @@ func (ds *DistSender) sendPartialBatch( // they're all down, or we're using an out-of-date range // descriptor. Invalidate the cache and try again with the new // metadata. - log.Eventf(ctx, "evicting range descriptor on %T and backoff for re-lookup: %+v", tErr, desc) + log.VEventf(ctx, 1, "evicting range descriptor on %T and backoff for re-lookup: %+v", tErr, desc) if err := evictToken.Evict(ctx); err != nil { return response{pErr: roachpb.NewError(err)} } @@ -1346,6 +1346,10 @@ func (ds *DistSender) sendToReplicas( } } } else { + // NB: This section of code may have unfortunate performance implications. If we + // exit the below type switch with propagateError remaining at `false`, we'll try + // more replicas. That may succeed and future requests might do the same thing over + // and over again, adding needless round-trips to the earlier replicas. propagateError := false switch tErr := br.Error.GetDetail().(type) { case nil: @@ -1353,18 +1357,37 @@ func (ds *DistSender) sendToReplicas( case *roachpb.StoreNotFoundError, *roachpb.NodeUnavailableError: // These errors are likely to be unique to the replica that reported // them, so no action is required before the next retry. + case *roachpb.RangeNotFoundError: + // The store we routed to doesn't have this replica. This can happen when + // our descriptor is outright outdated, but it can also be caused by a + // replica that has just been added but needs a snapshot to be caught up. + // + // Evict this replica from the lease holder cache, if applicable, and try + // the next replica. It is important that we do the latter, for the next + // retry might otherwise try the same replica again (assuming the replica is + // still in the descriptor), looping endlessly until the replica is caught + // up (which may never happen if the target range is dormant). + if tErr.StoreID != 0 { + cachedStoreID, found := ds.leaseHolderCache.Lookup(ctx, tErr.RangeID) + evicting := found && cachedStoreID == tErr.StoreID + if evicting { + log.Eventf(ctx, "evicting leaseholder s%d for r%d after RangeNotFoundError", tErr.StoreID, tErr.RangeID) + ds.leaseHolderCache.Update(ctx, tErr.RangeID, 0 /* evict */) + } + + } case *roachpb.NotLeaseHolderError: ds.metrics.NotLeaseHolderErrCount.Inc(1) if lh := tErr.LeaseHolder; lh != nil { // If the replica we contacted knows the new lease holder, update the cache. ds.leaseHolderCache.Update(ctx, rangeID, lh.StoreID) - // If the implicated leaseholder is not a known replica, - // return a RangeNotFoundError to signal eviction of the - // cached RangeDescriptor and re-send. + // If the implicated leaseholder is not a known replica, return a SendError + // to signal eviction of the cached RangeDescriptor and re-send. if replicas.FindReplica(lh.StoreID) == -1 { - // Replace NotLeaseHolderError with RangeNotFoundError. - br.Error = roachpb.NewError(roachpb.NewRangeNotFoundError(rangeID, curReplica.StoreID)) + br.Error = roachpb.NewError(roachpb.NewSendError(fmt.Sprintf( + "leaseholder s%d (via %+v) not in cached replicas %v", lh.StoreID, curReplica, replicas, + ))) propagateError = true } else { // Move the new lease holder to the head of the queue for the next retry. diff --git a/pkg/kv/dist_sender_test.go b/pkg/kv/dist_sender_test.go index 3c48939e3f36..ae9e3eec2028 100644 --- a/pkg/kv/dist_sender_test.go +++ b/pkg/kv/dist_sender_test.go @@ -1284,6 +1284,90 @@ func TestSendRPCRetry(t *testing.T) { } } +// This test reproduces the main problem in: +// https://github.com/cockroachdb/cockroach/issues/30613. +// by verifying that if a RangeNotFoundError is returned from a Replica, +// the next Replica is tried. +func TestSendRPCRangeNotFoundError(t *testing.T) { + defer leaktest.AfterTest(t)() + stopper := stop.NewStopper() + defer stopper.Stop(context.TODO()) + + g, clock := makeGossip(t, stopper) + if err := g.SetNodeDescriptor(&roachpb.NodeDescriptor{NodeID: 1}); err != nil { + t.Fatal(err) + } + + // Fill RangeDescriptor with three replicas. + var descriptor = roachpb.RangeDescriptor{ + RangeID: 1, + StartKey: roachpb.RKey("a"), + EndKey: roachpb.RKey("z"), + } + for i := 1; i <= 3; i++ { + addr := util.MakeUnresolvedAddr("tcp", fmt.Sprintf("node%d", i)) + nd := &roachpb.NodeDescriptor{ + NodeID: roachpb.NodeID(i), + Address: util.MakeUnresolvedAddr(addr.Network(), addr.String()), + } + if err := g.AddInfoProto(gossip.MakeNodeIDKey(roachpb.NodeID(i)), nd, time.Hour); err != nil { + t.Fatal(err) + } + + descriptor.Replicas = append(descriptor.Replicas, roachpb.ReplicaDescriptor{ + NodeID: roachpb.NodeID(i), + StoreID: roachpb.StoreID(i), + ReplicaID: roachpb.ReplicaID(i), + }) + } + descDB := mockRangeDescriptorDBForDescs( + testMetaRangeDescriptor, + descriptor, + ) + + seen := map[roachpb.ReplicaID]struct{}{} + var ds *DistSender + var testFn simpleSendFn = func( + _ context.Context, + _ SendOptions, + _ ReplicaSlice, + ba roachpb.BatchRequest, + ) (*roachpb.BatchResponse, error) { + br := ba.CreateReply() + if _, ok := seen[ba.Replica.ReplicaID]; ok { + br.Error = roachpb.NewErrorf("visited replica %+v twice", ba.Replica) + return br, nil + } + seen[ba.Replica.ReplicaID] = struct{}{} + if len(seen) <= 2 { + if len(seen) == 1 { + // Add to the leaseholder cache to verify that the response evicts it. + ds.leaseHolderCache.Update(context.Background(), ba.RangeID, ba.Replica.StoreID) + } + br.Error = roachpb.NewError(roachpb.NewRangeNotFoundError(ba.RangeID, ba.Replica.StoreID)) + return br, nil + } + return br, nil + } + cfg := DistSenderConfig{ + AmbientCtx: log.AmbientContext{Tracer: tracing.NewTracer()}, + Clock: clock, + TestingKnobs: ClientTestingKnobs{ + TransportFactory: adaptSimpleTransport(testFn), + }, + RangeDescriptorDB: descDB, + } + ds = NewDistSender(cfg, g) + get := roachpb.NewGet(roachpb.Key("b")) + _, err := client.SendWrapped(context.Background(), ds, get) + if err != nil { + t.Fatal(err) + } + if storeID, found := ds.leaseHolderCache.Lookup(context.Background(), roachpb.RangeID(1)); found { + t.Fatalf("unexpected cached leaseholder s%d", storeID) + } +} + // TestGetNodeDescriptor checks that the Node descriptor automatically gets // looked up from Gossip. func TestGetNodeDescriptor(t *testing.T) { diff --git a/pkg/storage/store_test.go b/pkg/storage/store_test.go index 45bfcd70f018..a6a6c31a39ca 100644 --- a/pkg/storage/store_test.go +++ b/pkg/storage/store_test.go @@ -382,11 +382,6 @@ func TestStoreInitAndBootstrap(t *testing.T) { t.Fatalf("unable to read store ident: %s", err) } - // Try to get 1st range--non-existent. - if _, err := store.GetReplica(1); err == nil { - t.Error("expected error fetching non-existent range") - } - // Bootstrap first range. if err := store.BootstrapRange(nil, cfg.Settings.Version.ServerVersion); err != nil { t.Errorf("failure to create first range: %s", err) From 857b9c08eb8c0bf97b1be438a363f69516c83887 Mon Sep 17 00:00:00 2001 From: Tobias Schottdorf Date: Tue, 9 Oct 2018 16:54:54 +0200 Subject: [PATCH 5/5] kv: cache leaseholder on successful response Whenever a successful response is received from an RPC that we know has to contact the leaseholder to succeed, update the leaseholder cache. The immediate motivation for this is to be able to land the preceding commits, which greatly exacerbated (as in, added a much faster failure mode to) ``` make stress PKG=./pkg/sql/logictest TESTS=TestPlannerLogic/5node-dist/distsql_interleaved_join ``` However, the change is one we've wanted to make for a while; our caching and in particular the eviction of leaseholders has been deficient essentially ever since it was first introduced. Touches #31068. Release note: None --- pkg/kv/dist_sender.go | 60 ++++++++++++------- pkg/kv/dist_sender_test.go | 26 +++++--- pkg/kv/send_test.go | 2 +- pkg/roachpb/batch.go | 7 +++ .../testdata/logic_test/explain_analyze_plans | 24 ++++++++ .../opt/exec/execbuilder/testdata/lookup_join | 7 +++ 6 files changed, 94 insertions(+), 32 deletions(-) diff --git a/pkg/kv/dist_sender.go b/pkg/kv/dist_sender.go index 9d5979faee72..4a0d1214859f 100644 --- a/pkg/kv/dist_sender.go +++ b/pkg/kv/dist_sender.go @@ -372,7 +372,11 @@ func (ds *DistSender) getNodeDescriptor() *roachpb.NodeDescriptor { // The replicas are assumed to be ordered by preference, with closer // ones (i.e. expected lowest latency) first. func (ds *DistSender) sendRPC( - ctx context.Context, rangeID roachpb.RangeID, replicas ReplicaSlice, ba roachpb.BatchRequest, + ctx context.Context, + rangeID roachpb.RangeID, + replicas ReplicaSlice, + ba roachpb.BatchRequest, + cachedLeaseHolder roachpb.ReplicaDescriptor, ) (*roachpb.BatchResponse, error) { if len(replicas) == 0 { return nil, roachpb.NewSendError( @@ -384,7 +388,15 @@ func (ds *DistSender) sendRPC( tracing.AnnotateTrace() defer tracing.AnnotateTrace() - return ds.sendToReplicas(ctx, SendOptions{metrics: &ds.metrics}, rangeID, replicas, ba, ds.nodeDialer) + return ds.sendToReplicas( + ctx, + SendOptions{metrics: &ds.metrics}, + rangeID, + replicas, + ba, + ds.nodeDialer, + cachedLeaseHolder, + ) } // CountRanges returns the number of ranges that encompass the given key span. @@ -435,16 +447,16 @@ func (ds *DistSender) sendSingleRange( // If this request needs to go to a lease holder and we know who that is, move // it to the front. - var knowLeaseholder bool - if !ba.IsReadOnly() || ba.ReadConsistency.RequiresReadLease() { + var cachedLeaseHolder roachpb.ReplicaDescriptor + if ba.RequiresLeaseHolder() { if storeID, ok := ds.leaseHolderCache.Lookup(ctx, desc.RangeID); ok { if i := replicas.FindReplica(storeID); i >= 0 { replicas.MoveToFront(i) - knowLeaseholder = true + cachedLeaseHolder = replicas[0].ReplicaDescriptor } } } - if !knowLeaseholder { + if (cachedLeaseHolder == roachpb.ReplicaDescriptor{}) { // Rearrange the replicas so that they're ordered in expectation of // request latency. var latencyFn LatencyFunc @@ -454,7 +466,7 @@ func (ds *DistSender) sendSingleRange( replicas.OptimizeReplicaOrder(ds.getNodeDescriptor(), latencyFn) } - br, err := ds.sendRPC(ctx, desc.RangeID, replicas, ba) + br, err := ds.sendRPC(ctx, desc.RangeID, replicas, ba, cachedLeaseHolder) if err != nil { log.VErrEvent(ctx, 2, err.Error()) return nil, roachpb.NewError(err) @@ -1109,7 +1121,7 @@ func (ds *DistSender) sendPartialBatch( // row and the range descriptor hasn't changed, return the error // to our caller. switch tErr := pErr.GetDetail().(type) { - case *roachpb.SendError, *roachpb.RangeNotFoundError: + case *roachpb.SendError: // We've tried all the replicas without success. Either // they're all down, or we're using an out-of-date range // descriptor. Invalidate the cache and try again with the new @@ -1286,6 +1298,7 @@ func (ds *DistSender) sendToReplicas( replicas ReplicaSlice, ba roachpb.BatchRequest, nodeDialer *nodedialer.Dialer, + cachedLeaseHolder roachpb.ReplicaDescriptor, ) (*roachpb.BatchResponse, error) { var ambiguousError error var haveCommit bool @@ -1353,6 +1366,14 @@ func (ds *DistSender) sendToReplicas( propagateError := false switch tErr := br.Error.GetDetail().(type) { case nil: + // When a request that we know could only succeed on the leaseholder comes + // back as successful, make sure the leaseholder cache reflects this + // replica. In steady state, this is almost always the case, and so we + // gate the update on whether the response comes from a node that we didn't + // know held the lease. + if cachedLeaseHolder != curReplica && ba.RequiresLeaseHolder() { + ds.leaseHolderCache.Update(ctx, rangeID, curReplica.StoreID) + } return br, nil case *roachpb.StoreNotFoundError, *roachpb.NodeUnavailableError: // These errors are likely to be unique to the replica that reported @@ -1362,25 +1383,18 @@ func (ds *DistSender) sendToReplicas( // our descriptor is outright outdated, but it can also be caused by a // replica that has just been added but needs a snapshot to be caught up. // - // Evict this replica from the lease holder cache, if applicable, and try - // the next replica. It is important that we do the latter, for the next - // retry might otherwise try the same replica again (assuming the replica is - // still in the descriptor), looping endlessly until the replica is caught - // up (which may never happen if the target range is dormant). - if tErr.StoreID != 0 { - cachedStoreID, found := ds.leaseHolderCache.Lookup(ctx, tErr.RangeID) - evicting := found && cachedStoreID == tErr.StoreID - if evicting { - log.Eventf(ctx, "evicting leaseholder s%d for r%d after RangeNotFoundError", tErr.StoreID, tErr.RangeID) - ds.leaseHolderCache.Update(ctx, tErr.RangeID, 0 /* evict */) - } - - } + // We'll try other replicas which typically gives us the leaseholder, either + // via the NotLeaseHolderError or nil error paths, both of which update the + // leaseholder cache. case *roachpb.NotLeaseHolderError: ds.metrics.NotLeaseHolderErrCount.Inc(1) if lh := tErr.LeaseHolder; lh != nil { - // If the replica we contacted knows the new lease holder, update the cache. + // Update the leaseholder cache. Naively this would also happen when the + // next RPC comes back, but we don't want to wait out the additional RPC + // latency. ds.leaseHolderCache.Update(ctx, rangeID, lh.StoreID) + // Avoid an extra update to the leaseholder cache if the next RPC succeeds. + cachedLeaseHolder = *lh // If the implicated leaseholder is not a known replica, return a SendError // to signal eviction of the cached RangeDescriptor and re-send. diff --git a/pkg/kv/dist_sender_test.go b/pkg/kv/dist_sender_test.go index ae9e3eec2028..890c9ba35356 100644 --- a/pkg/kv/dist_sender_test.go +++ b/pkg/kv/dist_sender_test.go @@ -541,14 +541,18 @@ func TestRetryOnNotLeaseHolderError(t *testing.T) { _ ReplicaSlice, args roachpb.BatchRequest, ) (*roachpb.BatchResponse, error) { + reply := &roachpb.BatchResponse{} if first { - reply := &roachpb.BatchResponse{} reply.Error = roachpb.NewError( &roachpb.NotLeaseHolderError{LeaseHolder: &leaseHolder}) first = false return reply, nil } - return args.CreateReply(), nil + // Return an error to avoid activating a code path that would + // populate the leaseholder cache from the successful response. + // That's not what this test wants to test. + reply.Error = roachpb.NewErrorf("boom") + return reply, nil } cfg := DistSenderConfig{ @@ -563,8 +567,8 @@ func TestRetryOnNotLeaseHolderError(t *testing.T) { ds := NewDistSender(cfg, g) v := roachpb.MakeValueFromString("value") put := roachpb.NewPut(roachpb.Key("a"), v) - if _, err := client.SendWrapped(context.Background(), ds, put); err != nil { - t.Errorf("put encountered error: %s", err) + if _, pErr := client.SendWrapped(context.Background(), ds, put); !testutils.IsPError(pErr, "boom") { + t.Fatalf("unexpected error: %v", pErr) } if first { t.Errorf("The command did not retry") @@ -660,8 +664,10 @@ func TestDistSenderDownNodeEvictLeaseholder(t *testing.T) { t.Errorf("contacted n1: %t, contacted n2: %t", contacted1, contacted2) } - if storeID, ok := ds.LeaseHolderCache().Lookup(ctx, roachpb.RangeID(1)); ok { - t.Fatalf("expected no lease holder for r1, but got s%d", storeID) + if storeID, ok := ds.LeaseHolderCache().Lookup(ctx, roachpb.RangeID(1)); !ok { + t.Fatalf("expected new leaseholder to be cached") + } else if exp := roachpb.StoreID(2); storeID != exp { + t.Fatalf("expected lease holder for r1 to be cached as s%d, but got s%d", exp, storeID) } } @@ -1326,6 +1332,7 @@ func TestSendRPCRangeNotFoundError(t *testing.T) { ) seen := map[roachpb.ReplicaID]struct{}{} + var leaseholderStoreID roachpb.StoreID var ds *DistSender var testFn simpleSendFn = func( _ context.Context, @@ -1347,6 +1354,7 @@ func TestSendRPCRangeNotFoundError(t *testing.T) { br.Error = roachpb.NewError(roachpb.NewRangeNotFoundError(ba.RangeID, ba.Replica.StoreID)) return br, nil } + leaseholderStoreID = ba.Replica.StoreID return br, nil } cfg := DistSenderConfig{ @@ -1363,8 +1371,10 @@ func TestSendRPCRangeNotFoundError(t *testing.T) { if err != nil { t.Fatal(err) } - if storeID, found := ds.leaseHolderCache.Lookup(context.Background(), roachpb.RangeID(1)); found { - t.Fatalf("unexpected cached leaseholder s%d", storeID) + if storeID, found := ds.leaseHolderCache.Lookup(context.Background(), roachpb.RangeID(1)); !found { + t.Fatal("expected a cached leaseholder") + } else if storeID != leaseholderStoreID { + t.Fatalf("unexpected cached leaseholder s%d, expected s%d", storeID, leaseholderStoreID) } } diff --git a/pkg/kv/send_test.go b/pkg/kv/send_test.go index 3b3c4d2c3d01..8c177bd7dc9b 100644 --- a/pkg/kv/send_test.go +++ b/pkg/kv/send_test.go @@ -281,5 +281,5 @@ func sendBatch( TransportFactory: transportFactory, }, }, nil) - return ds.sendToReplicas(ctx, SendOptions{metrics: &ds.metrics}, 0, makeReplicas(addrs...), roachpb.BatchRequest{}, nodeDialer) + return ds.sendToReplicas(ctx, SendOptions{metrics: &ds.metrics}, 0, makeReplicas(addrs...), roachpb.BatchRequest{}, nodeDialer, roachpb.ReplicaDescriptor{}) } diff --git a/pkg/roachpb/batch.go b/pkg/roachpb/batch.go index 7b45b43884ab..181f36741933 100644 --- a/pkg/roachpb/batch.go +++ b/pkg/roachpb/batch.go @@ -101,6 +101,13 @@ func (ba *BatchRequest) IsReadOnly() bool { return len(ba.Requests) > 0 && !ba.hasFlag(isWrite|isAdmin) } +// RequiresLeaseHolder returns true if the request can only be served by the +// leaseholders of the ranges it addresses. +func (ba *BatchRequest) RequiresLeaseHolder() bool { + return !ba.IsReadOnly() || ba.Header.ReadConsistency.RequiresReadLease() + +} + // IsReverse returns true iff the BatchRequest contains a reverse request. func (ba *BatchRequest) IsReverse() bool { return ba.hasFlag(isReverse) diff --git a/pkg/sql/logictest/testdata/logic_test/explain_analyze_plans b/pkg/sql/logictest/testdata/logic_test/explain_analyze_plans index 648b04b2082a..02dbc0a5f99a 100644 --- a/pkg/sql/logictest/testdata/logic_test/explain_analyze_plans +++ b/pkg/sql/logictest/testdata/logic_test/explain_analyze_plans @@ -35,6 +35,30 @@ ALTER TABLE kw EXPERIMENTAL_RELOCATE SELECT ARRAY[i], i FROM generate_series(1, # Verify that EXPLAIN ANALYZE (DISTSQL) annotates plans with collected # statistics. +# Verify data placement. +query TTITI colnames +SHOW EXPERIMENTAL_RANGES FROM TABLE kv +---- +start_key end_key range_id replicas lease_holder +NULL /1 1 {1} 1 +/1 /2 2 {1} 1 +/2 /3 3 {2} 2 +/3 /4 4 {3} 3 +/4 /5 5 {4} 4 +/5 NULL 6 {5} 5 + +# Verify data placement. +query TTITI colnames +SHOW EXPERIMENTAL_RANGES FROM TABLE kw +---- +start_key end_key range_id replicas lease_holder +NULL /1 6 {5} 5 +/1 /2 7 {1} 1 +/2 /3 8 {2} 2 +/3 /4 9 {3} 3 +/4 /5 10 {4} 4 +/5 NULL 11 {5} 5 + # This query verifies stat collection for the tableReader, mergeJoiner, and # aggregator. query T diff --git a/pkg/sql/opt/exec/execbuilder/testdata/lookup_join b/pkg/sql/opt/exec/execbuilder/testdata/lookup_join index f677354b6abe..ed6fe275f31a 100644 --- a/pkg/sql/opt/exec/execbuilder/testdata/lookup_join +++ b/pkg/sql/opt/exec/execbuilder/testdata/lookup_join @@ -211,6 +211,13 @@ distinct · · (name) · table books@primary · · · spans ALL · · +# Verify data placement. +query TTITI colnames +SHOW EXPERIMENTAL_RANGES FROM TABLE books +---- +start_key end_key range_id replicas lease_holder +NULL NULL 10 {5} 5 + query T SELECT url FROM [EXPLAIN (DISTSQL) SELECT DISTINCT authors.name FROM books AS b1, books2 AS b2, authors WHERE b1.title = b2.title AND authors.book = b1.title AND b1.shelf <> b2.shelf] ----