Merge pull request #9065 from gyuho/watch-id-2

*: allow user-provided watch ID to mvcc
This commit is contained in:
Gyuho Lee 2018-01-05 15:08:10 -08:00 committed by GitHub
commit 65464838ba
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 305 additions and 198 deletions

View File

@ -832,6 +832,7 @@ From google paxosdb paper: Our implementation hinges around a powerful primitive
| progress_notify | progress_notify is set so that the etcd server will periodically send a WatchResponse with no events to the new watcher if there are no recent events. It is useful when clients wish to recover a disconnected watcher starting from a recent known revision. The etcd server may decide how often it will send notifications based on current load. | bool | | progress_notify | progress_notify is set so that the etcd server will periodically send a WatchResponse with no events to the new watcher if there are no recent events. It is useful when clients wish to recover a disconnected watcher starting from a recent known revision. The etcd server may decide how often it will send notifications based on current load. | bool |
| filters | filters filter the events at server side before it sends back to the watcher. | (slice of) FilterType | | filters | filters filter the events at server side before it sends back to the watcher. | (slice of) FilterType |
| prev_kv | If prev_kv is set, created watcher gets the previous KV before the event happens. If the previous KV is already compacted, nothing will be returned. | bool | | prev_kv | If prev_kv is set, created watcher gets the previous KV before the event happens. If the previous KV is already compacted, nothing will be returned. | bool |
| watch_id | If watch_id is provided and non-zero, it will be assigned to this watcher. Since creating a watcher in etcd is not a synchronous operation, this can be used ensure that ordering is correct when creating multiple watchers on the same stream. Creating a watcher with an ID already in use on the stream will cause an error to be returned. | int64 |

View File

@ -2283,6 +2283,11 @@
"description": "start_revision is an optional revision to watch from (inclusive). No start_revision is \"now\".", "description": "start_revision is an optional revision to watch from (inclusive). No start_revision is \"now\".",
"type": "string", "type": "string",
"format": "int64" "format": "int64"
},
"watch_id": {
"description": "If watch_id is provided and non-zero, it will be assigned to this watcher.\nSince creating a watcher in etcd is not a synchronous operation,\nthis can be used ensure that ordering is correct when creating multiple\nwatchers on the same stream. Creating a watcher with an ID already in\nuse on the stream will cause an error to be returned.",
"type": "string",
"format": "int64"
} }
} }
}, },

View File

@ -367,7 +367,8 @@ func (w *watcher) closeStream(wgs *watchGrpcStream) {
} }
func (w *watchGrpcStream) addSubstream(resp *pb.WatchResponse, ws *watcherStream) { func (w *watchGrpcStream) addSubstream(resp *pb.WatchResponse, ws *watcherStream) {
if resp.WatchId == -1 { // check watch ID for backward compatibility (<= v3.3)
if resp.WatchId == -1 || (resp.Canceled && resp.CancelReason != "") {
// failed; no channel // failed; no channel
close(ws.recvc) close(ws.recvc)
return return
@ -453,6 +454,7 @@ func (w *watchGrpcStream) run() {
// Watch() requested // Watch() requested
case wreq := <-w.reqc: case wreq := <-w.reqc:
outc := make(chan WatchResponse, 1) outc := make(chan WatchResponse, 1)
// TODO: pass custom watch ID?
ws := &watcherStream{ ws := &watcherStream{
initReq: *wreq, initReq: *wreq,
id: -1, id: -1,
@ -553,6 +555,7 @@ func (w *watchGrpcStream) dispatchEvent(pbresp *pb.WatchResponse) bool {
for i, ev := range pbresp.Events { for i, ev := range pbresp.Events {
events[i] = (*Event)(ev) events[i] = (*Event)(ev)
} }
// TODO: return watch ID?
wr := &WatchResponse{ wr := &WatchResponse{
Header: *pbresp.Header, Header: *pbresp.Header,
Events: events, Events: events,

View File

@ -205,7 +205,7 @@ func (sws *serverWatchStream) recvLoop() error {
if !sws.isWatchPermitted(creq) { if !sws.isWatchPermitted(creq) {
wr := &pb.WatchResponse{ wr := &pb.WatchResponse{
Header: sws.newResponseHeader(sws.watchStream.Rev()), Header: sws.newResponseHeader(sws.watchStream.Rev()),
WatchId: -1, WatchId: creq.WatchId,
Canceled: true, Canceled: true,
Created: true, Created: true,
CancelReason: rpctypes.ErrGRPCPermissionDenied.Error(), CancelReason: rpctypes.ErrGRPCPermissionDenied.Error(),
@ -225,8 +225,8 @@ func (sws *serverWatchStream) recvLoop() error {
if rev == 0 { if rev == 0 {
rev = wsrev + 1 rev = wsrev + 1
} }
id := sws.watchStream.Watch(creq.Key, creq.RangeEnd, rev, filters...) id, err := sws.watchStream.Watch(mvcc.WatchID(creq.WatchId), creq.Key, creq.RangeEnd, rev, filters...)
if id != -1 { if err == nil {
sws.mu.Lock() sws.mu.Lock()
if creq.ProgressNotify { if creq.ProgressNotify {
sws.progress[id] = true sws.progress[id] = true
@ -240,7 +240,10 @@ func (sws *serverWatchStream) recvLoop() error {
Header: sws.newResponseHeader(wsrev), Header: sws.newResponseHeader(wsrev),
WatchId: int64(id), WatchId: int64(id),
Created: true, Created: true,
Canceled: id == -1, Canceled: err != nil,
}
if err != nil {
wr.CancelReason = err.Error()
} }
select { select {
case sws.ctrlStream <- wr: case sws.ctrlStream <- wr:

View File

@ -1620,6 +1620,12 @@ type WatchCreateRequest struct {
// If prev_kv is set, created watcher gets the previous KV before the event happens. // If prev_kv is set, created watcher gets the previous KV before the event happens.
// If the previous KV is already compacted, nothing will be returned. // If the previous KV is already compacted, nothing will be returned.
PrevKv bool `protobuf:"varint,6,opt,name=prev_kv,json=prevKv,proto3" json:"prev_kv,omitempty"` PrevKv bool `protobuf:"varint,6,opt,name=prev_kv,json=prevKv,proto3" json:"prev_kv,omitempty"`
// If watch_id is provided and non-zero, it will be assigned to this watcher.
// Since creating a watcher in etcd is not a synchronous operation,
// this can be used ensure that ordering is correct when creating multiple
// watchers on the same stream. Creating a watcher with an ID already in
// use on the stream will cause an error to be returned.
WatchId int64 `protobuf:"varint,7,opt,name=watch_id,json=watchId,proto3" json:"watch_id,omitempty"`
} }
func (m *WatchCreateRequest) Reset() { *m = WatchCreateRequest{} } func (m *WatchCreateRequest) Reset() { *m = WatchCreateRequest{} }
@ -1669,6 +1675,13 @@ func (m *WatchCreateRequest) GetPrevKv() bool {
return false return false
} }
func (m *WatchCreateRequest) GetWatchId() int64 {
if m != nil {
return m.WatchId
}
return 0
}
type WatchCancelRequest struct { type WatchCancelRequest struct {
// watch_id is the watcher id to cancel so that no more events are transmitted. // watch_id is the watcher id to cancel so that no more events are transmitted.
WatchId int64 `protobuf:"varint,1,opt,name=watch_id,json=watchId,proto3" json:"watch_id,omitempty"` WatchId int64 `protobuf:"varint,1,opt,name=watch_id,json=watchId,proto3" json:"watch_id,omitempty"`
@ -5919,6 +5932,11 @@ func (m *WatchCreateRequest) MarshalTo(dAtA []byte) (int, error) {
} }
i++ i++
} }
if m.WatchId != 0 {
dAtA[i] = 0x38
i++
i = encodeVarintRpc(dAtA, i, uint64(m.WatchId))
}
return i, nil return i, nil
} }
@ -8408,6 +8426,9 @@ func (m *WatchCreateRequest) Size() (n int) {
if m.PrevKv { if m.PrevKv {
n += 2 n += 2
} }
if m.WatchId != 0 {
n += 1 + sovRpc(uint64(m.WatchId))
}
return n return n
} }
@ -12309,6 +12330,25 @@ func (m *WatchCreateRequest) Unmarshal(dAtA []byte) error {
} }
} }
m.PrevKv = bool(v != 0) m.PrevKv = bool(v != 0)
case 7:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field WatchId", wireType)
}
m.WatchId = 0
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
m.WatchId |= (int64(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
default: default:
iNdEx = preIndex iNdEx = preIndex
skippy, err := skipRpc(dAtA[iNdEx:]) skippy, err := skipRpc(dAtA[iNdEx:])
@ -18431,13 +18471,13 @@ var (
func init() { proto.RegisterFile("rpc.proto", fileDescriptorRpc) } func init() { proto.RegisterFile("rpc.proto", fileDescriptorRpc) }
var fileDescriptorRpc = []byte{ var fileDescriptorRpc = []byte{
// 3669 bytes of a gzipped FileDescriptorProto // 3675 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x5b, 0x5b, 0x6f, 0x23, 0xc7, 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x5b, 0x5b, 0x6f, 0x23, 0xc7,
0x72, 0xd6, 0x90, 0x22, 0x29, 0x16, 0x2f, 0xe2, 0xb6, 0xb4, 0xbb, 0x14, 0x77, 0x57, 0xab, 0xed, 0x72, 0xd6, 0x90, 0x22, 0x29, 0x16, 0x2f, 0xe2, 0xb6, 0xb4, 0xbb, 0x14, 0x77, 0x57, 0xab, 0xed,
0xbd, 0x69, 0x2f, 0x16, 0x6d, 0xd9, 0xc9, 0xc3, 0x26, 0x30, 0xac, 0x95, 0xe8, 0x95, 0x2c, 0xad, 0xbd, 0x69, 0x2f, 0x16, 0x6d, 0xd9, 0xc9, 0xc3, 0x26, 0x30, 0xac, 0x95, 0xe8, 0x95, 0x2c, 0xad,
0x24, 0x8f, 0xa8, 0xb5, 0x03, 0x38, 0x11, 0x46, 0x64, 0x4b, 0x62, 0x44, 0xce, 0x30, 0x33, 0x43, 0x24, 0x8f, 0xa8, 0xb5, 0x03, 0x38, 0x11, 0x46, 0x64, 0x4b, 0x62, 0x44, 0xce, 0x30, 0x33, 0x43,
0xae, 0xb4, 0x31, 0x12, 0xc0, 0x71, 0x82, 0xbc, 0xe4, 0x25, 0x06, 0x82, 0xc4, 0xaf, 0x41, 0x60, 0xae, 0xb4, 0x31, 0x12, 0xc0, 0x71, 0x82, 0xbc, 0xe4, 0x25, 0x06, 0x82, 0x24, 0xaf, 0x41, 0x60,
0xf8, 0x07, 0x04, 0xf9, 0x0b, 0x41, 0x5e, 0x12, 0x20, 0x7f, 0xe0, 0xc0, 0xe7, 0xbc, 0x9c, 0x5f, 0xf8, 0x07, 0x18, 0xf9, 0x0b, 0x41, 0x5e, 0x12, 0x20, 0x7f, 0xe0, 0xc0, 0xe7, 0xbc, 0x9c, 0x5f,
0x70, 0x2e, 0x4f, 0x07, 0x7d, 0x9b, 0xe9, 0xb9, 0x51, 0xb2, 0x69, 0xfb, 0x45, 0x3b, 0x5d, 0x5d, 0x70, 0x2e, 0x4f, 0x07, 0x7d, 0x9b, 0xe9, 0xb9, 0x51, 0xb2, 0x69, 0xfb, 0x45, 0x3b, 0x5d, 0x5d,
0x5d, 0x55, 0x5d, 0xdd, 0x55, 0xd5, 0xfd, 0x35, 0x17, 0xf2, 0x76, 0xbf, 0xb5, 0xd4, 0xb7, 0x2d, 0x5d, 0x55, 0x5d, 0xdd, 0x55, 0xd5, 0xfd, 0x35, 0x17, 0xf2, 0x76, 0xbf, 0xb5, 0xd4, 0xb7, 0x2d,
0xd7, 0x42, 0x45, 0xe2, 0xb6, 0xda, 0x0e, 0xb1, 0x87, 0xc4, 0xee, 0x1f, 0xd6, 0x66, 0x8f, 0xad, 0xd7, 0x42, 0x45, 0xe2, 0xb6, 0xda, 0x0e, 0xb1, 0x87, 0xc4, 0xee, 0x1f, 0xd6, 0x66, 0x8f, 0xad,
@ -18473,7 +18513,7 @@ var fileDescriptorRpc = []byte{
0xf6, 0x84, 0xc5, 0x05, 0xdb, 0x8a, 0x85, 0xe5, 0x9b, 0xa1, 0xc5, 0x0d, 0xc4, 0x8e, 0x2e, 0x78, 0xf6, 0x84, 0xc5, 0x05, 0xdb, 0x8a, 0x85, 0xe5, 0x9b, 0xa1, 0xc5, 0x0d, 0xc4, 0x8e, 0x2e, 0x78,
0x11, 0x86, 0xf4, 0xe9, 0xd0, 0xa9, 0xa6, 0x16, 0xd2, 0x8b, 0x85, 0xe5, 0xca, 0x12, 0x0f, 0xd8, 0x11, 0x86, 0xf4, 0xe9, 0xd0, 0xa9, 0xa6, 0x16, 0xd2, 0x8b, 0x85, 0xe5, 0xca, 0x12, 0x0f, 0xd8,
0xa5, 0x4d, 0x72, 0xfe, 0xca, 0xe8, 0x0e, 0x88, 0x4e, 0x3b, 0x11, 0x82, 0xc9, 0x9e, 0x65, 0x13, 0xa5, 0x4d, 0x72, 0xfe, 0xca, 0xe8, 0x0e, 0x88, 0x4e, 0x3b, 0x11, 0x82, 0xc9, 0x9e, 0x65, 0x13,
0xb6, 0x63, 0xa7, 0x74, 0xf6, 0x4d, 0xb7, 0x31, 0x5b, 0x34, 0xb1, 0x5b, 0x79, 0x03, 0x7f, 0xab, 0xb6, 0x63, 0xa7, 0x74, 0xf6, 0x4d, 0xb7, 0x31, 0x5b, 0x34, 0xb1, 0x5b, 0x79, 0x03, 0x7f, 0xa3,
0x01, 0xec, 0x0e, 0xdc, 0xe4, 0xd0, 0x98, 0x85, 0xcc, 0x90, 0x0a, 0x16, 0x61, 0xc1, 0x1b, 0x2c, 0x01, 0xec, 0x0e, 0xdc, 0xe4, 0xd0, 0x98, 0x85, 0xcc, 0x90, 0x0a, 0x16, 0x61, 0xc1, 0x1b, 0x2c,
0x26, 0x88, 0xe1, 0x10, 0x2f, 0x26, 0x68, 0x03, 0x5d, 0x87, 0x5c, 0xdf, 0x26, 0xc3, 0x83, 0xd3, 0x26, 0x88, 0xe1, 0x10, 0x2f, 0x26, 0x68, 0x03, 0x5d, 0x87, 0x5c, 0xdf, 0x26, 0xc3, 0x83, 0xd3,
0x21, 0x53, 0x32, 0xa5, 0x67, 0x69, 0x73, 0x73, 0x88, 0xee, 0x40, 0xb1, 0x73, 0x6c, 0x5a, 0x36, 0x21, 0x53, 0x32, 0xa5, 0x67, 0x69, 0x73, 0x73, 0x88, 0xee, 0x40, 0xb1, 0x73, 0x6c, 0x5a, 0x36,
@ -18482,7 +18522,7 @@ var fileDescriptorRpc = []byte{
0x75, 0xa1, 0xb0, 0x1a, 0x7f, 0x06, 0x68, 0x8d, 0x74, 0x89, 0x4b, 0xc6, 0xc9, 0x1e, 0x8a, 0x4f, 0x75, 0xa1, 0xb0, 0x1a, 0x7f, 0x06, 0x68, 0x8d, 0x74, 0x89, 0x4b, 0xc6, 0xc9, 0x1e, 0x8a, 0x4f,
0xd2, 0xaa, 0x4f, 0xf0, 0x3f, 0x6b, 0x30, 0x13, 0x10, 0x3f, 0xd6, 0xb4, 0xaa, 0x90, 0x6b, 0x33, 0xd2, 0xaa, 0x4f, 0xf0, 0x3f, 0x6b, 0x30, 0x13, 0x10, 0x3f, 0xd6, 0xb4, 0xaa, 0x90, 0x6b, 0x33,
0x61, 0xdc, 0x82, 0xb4, 0x2e, 0x9b, 0xe8, 0x09, 0x4c, 0x09, 0x03, 0x9c, 0x6a, 0x3a, 0x61, 0xd3, 0x61, 0xdc, 0x82, 0xb4, 0x2e, 0x9b, 0xe8, 0x09, 0x4c, 0x09, 0x03, 0x9c, 0x6a, 0x3a, 0x61, 0xd3,
0xe4, 0xb8, 0x4d, 0x0e, 0xfe, 0x36, 0x05, 0x79, 0x31, 0xd1, 0x9d, 0x3e, 0x5a, 0x81, 0x92, 0xcd, 0xe4, 0xb8, 0x4d, 0x0e, 0xfe, 0x26, 0x05, 0x79, 0x31, 0xd1, 0x9d, 0x3e, 0x5a, 0x81, 0x92, 0xcd,
0x1b, 0x07, 0x6c, 0x3e, 0xc2, 0xa2, 0x5a, 0x72, 0x12, 0x5a, 0x9f, 0xd0, 0x8b, 0x62, 0x08, 0x23, 0x1b, 0x07, 0x6c, 0x3e, 0xc2, 0xa2, 0x5a, 0x72, 0x12, 0x5a, 0x9f, 0xd0, 0x8b, 0x62, 0x08, 0x23,
0xa3, 0x3f, 0x81, 0x82, 0x14, 0xd1, 0x1f, 0xb8, 0xc2, 0xe5, 0xd5, 0xa0, 0x00, 0x7f, 0xff, 0xad, 0xa3, 0x3f, 0x81, 0x82, 0x14, 0xd1, 0x1f, 0xb8, 0xc2, 0xe5, 0xd5, 0xa0, 0x00, 0x7f, 0xff, 0xad,
0x4f, 0xe8, 0x20, 0xd8, 0x77, 0x07, 0x2e, 0x6a, 0xc2, 0xac, 0x1c, 0xcc, 0x67, 0x23, 0xcc, 0x48, 0x4f, 0xe8, 0x20, 0xd8, 0x77, 0x07, 0x2e, 0x6a, 0xc2, 0xac, 0x1c, 0xcc, 0x67, 0x23, 0xcc, 0x48,
@ -18504,7 +18544,7 @@ var fileDescriptorRpc = []byte{
0xbd, 0xa2, 0xd1, 0x5a, 0xb7, 0xd5, 0xd8, 0xdb, 0xab, 0xa4, 0x50, 0x09, 0xf2, 0xdb, 0x3b, 0xcd, 0xbd, 0xa2, 0xd1, 0x5a, 0xb7, 0xd5, 0xd8, 0xdb, 0xab, 0xa4, 0x50, 0x09, 0xf2, 0xdb, 0x3b, 0xcd,
0x03, 0xce, 0x95, 0xc6, 0x2f, 0x3c, 0x09, 0xa2, 0xc8, 0x29, 0xb5, 0x6d, 0x42, 0xa9, 0x6d, 0x9a, 0x03, 0xce, 0x95, 0xc6, 0x2f, 0x3c, 0x09, 0xa2, 0xc8, 0x29, 0xb5, 0x6d, 0x42, 0xa9, 0x6d, 0x9a,
0xac, 0x6d, 0x29, 0xbf, 0xb6, 0xb1, 0x32, 0xb7, 0xd5, 0x58, 0xd9, 0x6b, 0x54, 0x26, 0x9f, 0x97, 0xac, 0x6d, 0x29, 0xbf, 0xb6, 0xb1, 0x32, 0xb7, 0xd5, 0x58, 0xd9, 0x6b, 0x54, 0x26, 0x9f, 0x97,
0xa1, 0xc8, 0xfd, 0x7b, 0x30, 0x30, 0x69, 0xa9, 0xfd, 0x77, 0x0d, 0xc0, 0x8f, 0x26, 0x54, 0x87, 0xa1, 0xc8, 0xfd, 0x7b, 0x30, 0x30, 0x69, 0xa9, 0xfd, 0x0f, 0x0d, 0xc0, 0x8f, 0x26, 0x54, 0x87,
0x5c, 0x8b, 0xeb, 0xa9, 0x6a, 0x2c, 0x19, 0x5d, 0x8d, 0x5d, 0x32, 0x5d, 0x72, 0xa1, 0x77, 0x20, 0x5c, 0x8b, 0xeb, 0xa9, 0x6a, 0x2c, 0x19, 0x5d, 0x8d, 0x5d, 0x32, 0x5d, 0x72, 0xa1, 0x77, 0x20,
0xe7, 0x0c, 0x5a, 0x2d, 0xe2, 0xc8, 0x92, 0x77, 0x3d, 0x9c, 0x0f, 0x45, 0xb6, 0xd2, 0x25, 0x1f, 0xe7, 0x0c, 0x5a, 0x2d, 0xe2, 0xc8, 0x92, 0x77, 0x3d, 0x9c, 0x0f, 0x45, 0xb6, 0xd2, 0x25, 0x1f,
0x1d, 0x72, 0x64, 0x74, 0xba, 0x03, 0x56, 0x00, 0x47, 0x0f, 0x11, 0x7c, 0xf8, 0xdf, 0x34, 0x28, 0x1d, 0x72, 0x64, 0x74, 0xba, 0x03, 0x56, 0x00, 0x47, 0x0f, 0x11, 0x7c, 0xf8, 0xdf, 0x34, 0x28,
@ -18519,147 +18559,147 @@ var fileDescriptorRpc = []byte{
0xaf, 0xc0, 0xf4, 0x9e, 0x69, 0xf4, 0x9d, 0x13, 0x4b, 0x56, 0x37, 0x3a, 0xe9, 0x8a, 0x4f, 0x1b, 0xaf, 0xc0, 0xf4, 0x9e, 0x69, 0xf4, 0x9d, 0x13, 0x4b, 0x56, 0x37, 0x3a, 0xe9, 0x8a, 0x4f, 0x1b,
0x4b, 0xe3, 0x43, 0x98, 0xb6, 0x49, 0xcf, 0xe8, 0x98, 0x1d, 0xf3, 0xf8, 0xe0, 0xf0, 0xdc, 0x25, 0x4b, 0xe3, 0x43, 0x98, 0xb6, 0x49, 0xcf, 0xe8, 0x98, 0x1d, 0xf3, 0xf8, 0xe0, 0xf0, 0xdc, 0x25,
0x8e, 0xb8, 0x30, 0x95, 0x3d, 0xf2, 0x73, 0x4a, 0xa5, 0xa6, 0x1d, 0x76, 0xad, 0x43, 0x91, 0xe6, 0x8e, 0xb8, 0x30, 0x95, 0x3d, 0xf2, 0x73, 0x4a, 0xa5, 0xa6, 0x1d, 0x76, 0xad, 0x43, 0x91, 0xe6,
0xd8, 0x37, 0xfe, 0x4f, 0x0d, 0x8a, 0x9f, 0x18, 0x6e, 0x4b, 0x2e, 0x1d, 0xda, 0x80, 0xb2, 0x97, 0xd8, 0x37, 0xfe, 0x56, 0x83, 0xe2, 0x27, 0x86, 0xdb, 0x92, 0x4b, 0x87, 0x36, 0xa0, 0xec, 0x25,
0xdc, 0x18, 0x45, 0xd8, 0x12, 0x2a, 0xb1, 0x6c, 0x8c, 0x3c, 0x4a, 0xcb, 0xea, 0x58, 0x6a, 0xa9, 0x37, 0x46, 0x11, 0xb6, 0x84, 0x4a, 0x2c, 0x1b, 0x23, 0x8f, 0xd2, 0xb2, 0x3a, 0x96, 0x5a, 0x2a,
0x04, 0x26, 0xca, 0x30, 0x5b, 0xa4, 0xeb, 0x89, 0x4a, 0x25, 0x8b, 0x62, 0x8c, 0xaa, 0x28, 0x95, 0x81, 0x89, 0x32, 0xcc, 0x16, 0xe9, 0x7a, 0xa2, 0x52, 0xc9, 0xa2, 0x18, 0xa3, 0x2a, 0x4a, 0x25,
0xf0, 0x7c, 0xda, 0x3f, 0x7e, 0xf0, 0x5c, 0xf2, 0x75, 0x0a, 0x50, 0xd4, 0x86, 0xef, 0x7b, 0x22, 0x3c, 0x9f, 0xf6, 0x8f, 0x1f, 0x3c, 0x97, 0x7c, 0x9b, 0x02, 0x14, 0xb5, 0xe1, 0xfb, 0x9e, 0xc8,
0xbb, 0x0f, 0x65, 0xc7, 0x35, 0xec, 0xc8, 0xde, 0x28, 0x31, 0xaa, 0x97, 0xa0, 0x1f, 0xc2, 0x74, 0xee, 0x43, 0xd9, 0x71, 0x0d, 0x3b, 0xb2, 0x37, 0x4a, 0x8c, 0xea, 0x25, 0xe8, 0x87, 0x30, 0xdd,
0xdf, 0xb6, 0x8e, 0x6d, 0xe2, 0x38, 0x07, 0xa6, 0xe5, 0x76, 0x8e, 0xce, 0xc5, 0xa1, 0xb6, 0x2c, 0xb7, 0xad, 0x63, 0x9b, 0x38, 0xce, 0x81, 0x69, 0xb9, 0x9d, 0xa3, 0x73, 0x71, 0xa8, 0x2d, 0x4b,
0xc9, 0xdb, 0x8c, 0x8a, 0x1a, 0x90, 0x3b, 0xea, 0x74, 0x5d, 0x62, 0x3b, 0xd5, 0xcc, 0x42, 0x7a, 0xf2, 0x36, 0xa3, 0xa2, 0x06, 0xe4, 0x8e, 0x3a, 0x5d, 0x97, 0xd8, 0x4e, 0x35, 0xb3, 0x90, 0x5e,
0xb1, 0xbc, 0xfc, 0xe4, 0x22, 0xaf, 0x2d, 0x7d, 0xc8, 0xf8, 0x9b, 0xe7, 0x7d, 0xa2, 0xcb, 0xb1, 0x2c, 0x2f, 0x3f, 0xb9, 0xc8, 0x6b, 0x4b, 0x1f, 0x32, 0xfe, 0xe6, 0x79, 0x9f, 0xe8, 0x72, 0xac,
0xea, 0x41, 0x31, 0x1b, 0x38, 0x28, 0xde, 0x07, 0xf0, 0xf9, 0x69, 0xaa, 0xdd, 0xde, 0xd9, 0xdd, 0x7a, 0x50, 0xcc, 0x06, 0x0e, 0xcf, 0x73, 0x30, 0xf5, 0x9a, 0x8a, 0xa0, 0x97, 0xe2, 0x1c, 0x3f,
0x6f, 0x56, 0x26, 0x50, 0x11, 0xa6, 0xb6, 0x77, 0xd6, 0x1a, 0x5b, 0x0d, 0x9a, 0x97, 0x71, 0x5d, 0xdb, 0xb1, 0xf6, 0x46, 0x1b, 0xdf, 0x07, 0xf0, 0x45, 0xd1, 0x2c, 0xbc, 0xbd, 0xb3, 0xbb, 0xdf,
0xfa, 0x46, 0xf5, 0x21, 0x9a, 0x83, 0xa9, 0xd7, 0x94, 0x2a, 0xef, 0xdb, 0x69, 0x3d, 0xc7, 0xda, 0xac, 0x4c, 0xa0, 0x22, 0x4c, 0x6d, 0xef, 0xac, 0x35, 0xb6, 0x1a, 0x34, 0x65, 0xe3, 0xba, 0x74,
0x1b, 0x6d, 0xfc, 0x4f, 0x29, 0x28, 0x89, 0x5d, 0x30, 0xd6, 0x56, 0x54, 0x55, 0xa4, 0x02, 0x2a, 0x9b, 0xea, 0xde, 0x80, 0x5c, 0x2d, 0x28, 0xf7, 0x9f, 0x52, 0x50, 0x12, 0x1b, 0x64, 0xac, 0x5d,
0xe8, 0xa9, 0x94, 0xef, 0x8e, 0xb6, 0x38, 0xfc, 0xca, 0x26, 0xcd, 0x0d, 0x7c, 0xb1, 0x49, 0x5b, 0xaa, 0xaa, 0x48, 0x05, 0x54, 0xd0, 0x03, 0x2b, 0xdf, 0x38, 0x6d, 0x71, 0x2e, 0x96, 0x4d, 0x9a,
0xb8, 0xd5, 0x6b, 0xc7, 0x86, 0x6f, 0x26, 0x36, 0x7c, 0xd1, 0x5d, 0x28, 0x79, 0xbb, 0xcd, 0x70, 0x36, 0xf8, 0x3e, 0x20, 0x6d, 0xe1, 0x71, 0xaf, 0x1d, 0x1b, 0xd9, 0x99, 0xd8, 0xc8, 0x46, 0x77,
0x44, 0xad, 0xcd, 0xeb, 0x45, 0xb9, 0x91, 0x28, 0x0d, 0xdd, 0x87, 0x2c, 0x19, 0x12, 0xd3, 0x75, 0xa1, 0xe4, 0x6d, 0x44, 0xc3, 0x11, 0x65, 0x38, 0xaf, 0x17, 0xe5, 0x1e, 0xa3, 0x34, 0x74, 0x1f,
0xaa, 0x05, 0x96, 0x75, 0x4b, 0xf2, 0xfc, 0xdb, 0xa0, 0x54, 0x5d, 0x74, 0xe2, 0x3f, 0x82, 0x2b, 0xb2, 0x64, 0x48, 0x4c, 0xd7, 0xa9, 0x16, 0x58, 0x42, 0x2e, 0xc9, 0xa3, 0x71, 0x83, 0x52, 0x75,
0xec, 0x9e, 0xf1, 0xc2, 0x36, 0x4c, 0xf5, 0x42, 0xd4, 0x6c, 0x6e, 0x09, 0xd7, 0xd1, 0x4f, 0x54, 0xd1, 0x89, 0xff, 0x08, 0xae, 0xb0, 0x2b, 0xc8, 0x0b, 0xdb, 0x30, 0xd5, 0xbb, 0x52, 0xb3, 0xb9,
0x86, 0xd4, 0xc6, 0x9a, 0x98, 0x68, 0x6a, 0x63, 0x0d, 0x7f, 0xa1, 0x01, 0x52, 0xc7, 0x8d, 0xe5, 0x25, 0x5c, 0x47, 0x3f, 0x51, 0x19, 0x52, 0x1b, 0x6b, 0x62, 0xa2, 0xa9, 0x8d, 0x35, 0xfc, 0x85,
0xcb, 0x90, 0x70, 0xa9, 0x3e, 0xed, 0xab, 0x9f, 0x85, 0x0c, 0xb1, 0x6d, 0xcb, 0x66, 0x5e, 0xcb, 0x06, 0x48, 0x1d, 0x37, 0x96, 0x2f, 0x43, 0xc2, 0xa5, 0xfa, 0xb4, 0xaf, 0x7e, 0x16, 0x32, 0xc4,
0xeb, 0xbc, 0x81, 0xef, 0x09, 0x1b, 0x74, 0x32, 0xb4, 0x4e, 0xbd, 0xc0, 0xe0, 0xd2, 0x34, 0xcf, 0xb6, 0x2d, 0x9b, 0x79, 0x2d, 0xaf, 0xf3, 0x06, 0xbe, 0x27, 0x6c, 0xd0, 0xc9, 0xd0, 0x3a, 0xf5,
0xd4, 0x4d, 0x98, 0x09, 0x70, 0x8d, 0x95, 0xfd, 0x1f, 0xc2, 0x55, 0x26, 0x6c, 0x93, 0x90, 0xfe, 0x62, 0x86, 0x4b, 0xd3, 0x3c, 0x53, 0x37, 0x61, 0x26, 0xc0, 0x35, 0x56, 0x61, 0x78, 0x08, 0x57,
0x4a, 0xb7, 0x33, 0x4c, 0xd4, 0xda, 0x87, 0x6b, 0x61, 0xc6, 0x9f, 0xd6, 0x47, 0xf8, 0x4f, 0x85, 0x99, 0xb0, 0x4d, 0x42, 0xfa, 0x2b, 0xdd, 0xce, 0x30, 0x51, 0x6b, 0x1f, 0xae, 0x85, 0x19, 0x7f,
0xc6, 0x66, 0xa7, 0x47, 0x9a, 0xd6, 0x56, 0xb2, 0x6d, 0x34, 0x3b, 0x9e, 0x92, 0x73, 0x47, 0x94, 0x5a, 0x1f, 0xe1, 0x3f, 0x15, 0x1a, 0x9b, 0x9d, 0x1e, 0x69, 0x5a, 0x5b, 0xc9, 0xb6, 0xd1, 0xc4,
0x49, 0xf6, 0x8d, 0xff, 0x43, 0x83, 0xeb, 0x91, 0xe1, 0x3f, 0xf1, 0xaa, 0xce, 0x03, 0x1c, 0xd3, 0x79, 0x4a, 0xce, 0x1d, 0x51, 0x41, 0xd9, 0x37, 0xfe, 0x4f, 0x0d, 0xae, 0x47, 0x86, 0xff, 0xc4,
0xed, 0x43, 0xda, 0xb4, 0x83, 0xdf, 0xd0, 0x15, 0x8a, 0x67, 0x27, 0x4d, 0x30, 0x45, 0x61, 0xe7, 0xab, 0x3a, 0x0f, 0x70, 0x4c, 0xb7, 0x0f, 0x69, 0xd3, 0x0e, 0x7e, 0x79, 0x57, 0x28, 0x9e, 0x9d,
0xac, 0x58, 0x73, 0xf6, 0xc7, 0x91, 0x35, 0xe6, 0x16, 0x14, 0x18, 0x61, 0xcf, 0x35, 0xdc, 0x81, 0x34, 0xf7, 0x14, 0x85, 0x9d, 0xb3, 0x62, 0xcd, 0xd9, 0x1f, 0x47, 0x96, 0x9f, 0x5b, 0x50, 0x60,
0x13, 0x59, 0x8c, 0xbf, 0x11, 0x5b, 0x40, 0x0e, 0x1a, 0x6b, 0x5e, 0xef, 0x40, 0x96, 0x1d, 0x4e, 0x84, 0x3d, 0xd7, 0x70, 0x07, 0x4e, 0x64, 0x31, 0xfe, 0x46, 0x6c, 0x01, 0x39, 0x68, 0xac, 0x79,
0xe5, 0xd1, 0x2c, 0x74, 0x1b, 0x50, 0xec, 0xd0, 0x05, 0x23, 0x3e, 0x81, 0xec, 0x4b, 0x86, 0xe8, 0xbd, 0x03, 0x59, 0x76, 0x6e, 0x95, 0xa7, 0xb6, 0xd0, 0x45, 0x41, 0xb1, 0x43, 0x17, 0x8c, 0xf8,
0x29, 0x96, 0x4d, 0xca, 0xa5, 0x30, 0x8d, 0x1e, 0xc7, 0x19, 0xf2, 0x3a, 0xfb, 0x66, 0x27, 0x19, 0x04, 0xb2, 0x2f, 0x19, 0xd8, 0xa7, 0x58, 0x36, 0x29, 0x97, 0xc2, 0x34, 0x7a, 0x1c, 0x82, 0xc8,
0x42, 0xec, 0x7d, 0x7d, 0x8b, 0x9f, 0x98, 0xf2, 0xba, 0xd7, 0xa6, 0x2e, 0x6b, 0x75, 0x3b, 0xc4, 0xeb, 0xec, 0x9b, 0x1d, 0x72, 0x08, 0xb1, 0xf7, 0xf5, 0x2d, 0x7e, 0x98, 0xca, 0xeb, 0x5e, 0x9b,
0x74, 0x59, 0xef, 0x24, 0xeb, 0x55, 0x28, 0x78, 0x09, 0x2a, 0x5c, 0xd3, 0x4a, 0xbb, 0xad, 0x9c, 0xba, 0xac, 0xd5, 0xed, 0x10, 0xd3, 0x65, 0xbd, 0x93, 0xac, 0x57, 0xa1, 0xe0, 0x25, 0xa8, 0x70,
0x48, 0x3c, 0x79, 0x5a, 0x50, 0x1e, 0xfe, 0x46, 0x83, 0x2b, 0xca, 0x80, 0xb1, 0x1c, 0xf3, 0x14, 0x4d, 0x2b, 0xed, 0xb6, 0x72, 0x58, 0xf1, 0xe4, 0x69, 0x41, 0x79, 0xf8, 0x6b, 0x0d, 0xae, 0x28,
0xb2, 0x1c, 0xb7, 0x14, 0xc5, 0x6f, 0x36, 0x38, 0x8a, 0xab, 0xd1, 0x05, 0x0f, 0x5a, 0x82, 0x1c, 0x03, 0xc6, 0x72, 0xcc, 0x53, 0xc8, 0x72, 0x48, 0x53, 0xd4, 0xc5, 0xd9, 0xe0, 0x28, 0xae, 0x46,
0xff, 0x92, 0xc7, 0xc2, 0x78, 0x76, 0xc9, 0x84, 0xef, 0xc3, 0x8c, 0x20, 0x91, 0x9e, 0x15, 0xb7, 0x17, 0x3c, 0x68, 0x09, 0x72, 0xfc, 0x4b, 0x9e, 0x18, 0xe3, 0xd9, 0x25, 0x13, 0xbe, 0x0f, 0x33,
0xb7, 0x99, 0x43, 0xf1, 0xe7, 0x30, 0x1b, 0x64, 0x1b, 0x6b, 0x4a, 0x8a, 0x91, 0xa9, 0xcb, 0x18, 0x82, 0x44, 0x7a, 0x56, 0xdc, 0xde, 0x66, 0x0e, 0xc5, 0x9f, 0xc3, 0x6c, 0x90, 0x6d, 0xac, 0x29,
0xb9, 0x22, 0x8d, 0xdc, 0xef, 0xb7, 0x95, 0x5a, 0x1d, 0x5e, 0x75, 0x75, 0x45, 0x52, 0xa1, 0x15, 0x29, 0x46, 0xa6, 0x2e, 0x63, 0xe4, 0x8a, 0x34, 0x72, 0xbf, 0xdf, 0x56, 0xca, 0x78, 0x78, 0xd5,
0xf1, 0x26, 0x20, 0x45, 0xfc, 0xac, 0x13, 0x98, 0x91, 0xdb, 0x61, 0xab, 0xe3, 0x78, 0x27, 0xb8, 0xd5, 0x15, 0x49, 0x85, 0x56, 0xc4, 0x9b, 0x80, 0x14, 0xf1, 0xb3, 0x4e, 0x60, 0x46, 0x6e, 0x87,
0x37, 0x80, 0x54, 0xe2, 0xcf, 0x6d, 0xd0, 0x1a, 0x39, 0xb2, 0x8d, 0xe3, 0x1e, 0xf1, 0xea, 0x13, 0xad, 0x8e, 0xe3, 0x1d, 0xee, 0xde, 0x00, 0x52, 0x89, 0x3f, 0xb7, 0x41, 0x6b, 0xe4, 0xc8, 0x36,
0x3d, 0xcf, 0xab, 0xc4, 0xb1, 0x32, 0x7a, 0x1d, 0xae, 0xbc, 0xb4, 0x86, 0x34, 0x35, 0x50, 0xaa, 0x8e, 0x7b, 0xc4, 0xab, 0x4f, 0xf4, 0xa8, 0xaf, 0x12, 0xc7, 0xca, 0xe8, 0x75, 0xb8, 0xf2, 0xd2,
0x1f, 0x32, 0xfc, 0x3e, 0xe7, 0x2d, 0x9b, 0xd7, 0xa6, 0xca, 0xd5, 0x01, 0x63, 0x29, 0xff, 0x5f, 0x1a, 0xd2, 0xd4, 0x40, 0xa9, 0x7e, 0xc8, 0xf0, 0xab, 0x9e, 0xb7, 0x6c, 0x5e, 0x9b, 0x2a, 0x57,
0x0d, 0x8a, 0x2b, 0x5d, 0xc3, 0xee, 0x49, 0xc5, 0xef, 0x43, 0x96, 0xdf, 0x52, 0x04, 0x30, 0xf0, 0x07, 0x8c, 0xa5, 0xfc, 0x7f, 0x35, 0x28, 0xae, 0x74, 0x0d, 0xbb, 0x27, 0x15, 0xbf, 0x0f, 0x59,
0x20, 0x28, 0x46, 0xe5, 0xe5, 0x8d, 0x15, 0x7e, 0xa7, 0x11, 0xa3, 0xa8, 0xe1, 0xe2, 0xed, 0x60, 0x7e, 0x81, 0x11, 0x98, 0xc1, 0x83, 0xa0, 0x18, 0x95, 0x97, 0x37, 0x56, 0xf8, 0x75, 0x47, 0x8c,
0x2d, 0xf4, 0x96, 0xb0, 0x86, 0xde, 0x82, 0x8c, 0x41, 0x87, 0xb0, 0x14, 0x5c, 0x0e, 0xdf, 0x0f, 0xa2, 0x86, 0x8b, 0x67, 0x85, 0xb5, 0xd0, 0x33, 0xc3, 0x1a, 0x7a, 0x0b, 0x32, 0x06, 0x1d, 0xc2,
0x99, 0x34, 0x76, 0x38, 0xe3, 0x5c, 0xf8, 0x3d, 0x28, 0x28, 0x1a, 0xe8, 0x0d, 0xf8, 0x45, 0x43, 0x52, 0x70, 0x39, 0x7c, 0x75, 0x64, 0xd2, 0xd8, 0xb9, 0x8d, 0x73, 0xe1, 0xf7, 0xa0, 0xa0, 0x68,
0x1c, 0xc0, 0x56, 0x56, 0x9b, 0x1b, 0xaf, 0xf8, 0xc5, 0xb8, 0x0c, 0xb0, 0xd6, 0xf0, 0xda, 0x29, 0xa0, 0x97, 0xe3, 0x17, 0x0d, 0x71, 0x00, 0x5b, 0x59, 0x6d, 0x6e, 0xbc, 0xe2, 0x77, 0xe6, 0x32,
0xfc, 0xa9, 0x18, 0x25, 0xf2, 0x9d, 0x6a, 0x8f, 0x96, 0x64, 0x4f, 0xea, 0x52, 0xf6, 0x9c, 0x41, 0xc0, 0x5a, 0xc3, 0x6b, 0xa7, 0xf0, 0xa7, 0x62, 0x94, 0xc8, 0x77, 0xaa, 0x3d, 0x5a, 0x92, 0x3d,
0x49, 0x4c, 0x7f, 0xdc, 0xf4, 0xcd, 0xe4, 0x25, 0xa4, 0x6f, 0xc5, 0x78, 0x5d, 0x30, 0xe2, 0x69, 0xa9, 0x4b, 0xd9, 0x73, 0x06, 0x25, 0x31, 0xfd, 0x71, 0xd3, 0x37, 0x93, 0x97, 0x90, 0xbe, 0x15,
0x28, 0x89, 0x84, 0x2e, 0xf6, 0xdf, 0xff, 0x68, 0x50, 0x96, 0x94, 0x71, 0x01, 0x4c, 0x89, 0xbd, 0xe3, 0x75, 0xc1, 0x88, 0xa7, 0xa1, 0x24, 0x12, 0xba, 0xd8, 0x7f, 0xff, 0xa3, 0x41, 0x59, 0x52,
0xf0, 0x0a, 0xe0, 0x21, 0x2f, 0xd7, 0x20, 0xdb, 0x3e, 0xdc, 0xeb, 0xbc, 0x91, 0x60, 0xb3, 0x68, 0xc6, 0xc5, 0x36, 0x25, 0x2c, 0xc3, 0x2b, 0x80, 0x07, 0xca, 0x5c, 0x83, 0x6c, 0xfb, 0x70, 0xaf,
0x51, 0x7a, 0x97, 0xeb, 0xe1, 0x2f, 0x3e, 0xa2, 0x45, 0x6f, 0xe1, 0xb6, 0x71, 0xe4, 0x6e, 0x98, 0xf3, 0x46, 0xe2, 0xd0, 0xa2, 0x45, 0xe9, 0x5d, 0xae, 0x87, 0x3f, 0x06, 0x89, 0x16, 0xbd, 0xa0,
0x6d, 0x72, 0xc6, 0xce, 0x8d, 0x93, 0xba, 0x4f, 0x60, 0x97, 0x52, 0xf1, 0x32, 0xc4, 0x0e, 0x8b, 0xdb, 0xc6, 0x91, 0xbb, 0x61, 0xb6, 0xc9, 0x19, 0x3b, 0x37, 0x4e, 0xea, 0x3e, 0x81, 0xdd, 0x57,
0xea, 0x4b, 0xd1, 0x0c, 0x5c, 0x59, 0x19, 0xb8, 0x27, 0x0d, 0xd3, 0x38, 0xec, 0xca, 0x8c, 0x45, 0xc5, 0xa3, 0x11, 0x3b, 0x2c, 0xaa, 0x8f, 0x48, 0x33, 0x70, 0x65, 0x65, 0xe0, 0x9e, 0x34, 0x4c,
0xcb, 0x2c, 0x25, 0xae, 0x75, 0x1c, 0x95, 0xda, 0x80, 0x19, 0x4a, 0x25, 0xa6, 0xdb, 0x69, 0x29, 0xe3, 0xb0, 0x2b, 0x33, 0x16, 0x2d, 0xb3, 0x94, 0xb8, 0xd6, 0x71, 0x54, 0x6a, 0x03, 0x66, 0x28,
0xe9, 0x4d, 0x16, 0x31, 0x2d, 0x54, 0xc4, 0x0c, 0xc7, 0x79, 0x6d, 0xd9, 0x6d, 0x31, 0x35, 0xaf, 0x95, 0x98, 0x6e, 0xa7, 0xa5, 0xa4, 0x37, 0x59, 0xc4, 0xb4, 0x50, 0x11, 0x33, 0x1c, 0xe7, 0xb5,
0x8d, 0xd7, 0xb8, 0xf0, 0x7d, 0x27, 0x50, 0xa6, 0xbe, 0xaf, 0x94, 0x45, 0x5f, 0xca, 0x0b, 0xe2, 0x65, 0xb7, 0xc5, 0xd4, 0xbc, 0x36, 0x5e, 0xe3, 0xc2, 0xf7, 0x9d, 0x40, 0x99, 0xfa, 0xbe, 0x52,
0x8e, 0x90, 0x82, 0x9f, 0xc0, 0x55, 0xc9, 0x29, 0xc0, 0xbd, 0x11, 0xcc, 0x3b, 0x70, 0x4b, 0x32, 0x16, 0x7d, 0x29, 0x2f, 0x88, 0x3b, 0x42, 0x0a, 0x7e, 0x02, 0x57, 0x25, 0xa7, 0xc0, 0xfd, 0x46,
0xaf, 0x9e, 0xd0, 0xdb, 0xd3, 0xae, 0x50, 0xf8, 0x43, 0xed, 0x7c, 0x0e, 0x55, 0xcf, 0x4e, 0x76, 0x30, 0xef, 0xc0, 0x2d, 0xc9, 0xbc, 0x7a, 0x42, 0x2f, 0x56, 0xbb, 0x42, 0xe1, 0x0f, 0xb5, 0xf3,
0x58, 0xb6, 0xba, 0xaa, 0x01, 0x03, 0x47, 0xec, 0x99, 0xbc, 0xce, 0xbe, 0x29, 0xcd, 0xb6, 0xba, 0x39, 0x54, 0x3d, 0x3b, 0xd9, 0x61, 0xd9, 0xea, 0xaa, 0x06, 0x0c, 0x1c, 0xb1, 0x67, 0xf2, 0x3a,
0xde, 0x91, 0x80, 0x7e, 0xe3, 0x55, 0x98, 0x93, 0x32, 0xc4, 0x31, 0x36, 0x28, 0x24, 0x62, 0x50, 0xfb, 0xa6, 0x34, 0xdb, 0xea, 0x7a, 0x47, 0x02, 0xfa, 0x8d, 0x57, 0x61, 0x4e, 0xca, 0x10, 0xc7,
0x9c, 0x10, 0xe1, 0x30, 0x3a, 0x74, 0xb4, 0xdb, 0x55, 0xce, 0xa0, 0x6b, 0x99, 0x4c, 0x4d, 0x91, 0xd8, 0xa0, 0x90, 0x88, 0x41, 0x71, 0x42, 0x84, 0xc3, 0xe8, 0xd0, 0xd1, 0x6e, 0x57, 0x39, 0x83,
0x79, 0x95, 0xef, 0x08, 0x6a, 0x98, 0x5a, 0x31, 0x04, 0x99, 0x0a, 0x50, 0xc9, 0x62, 0x21, 0x28, 0xae, 0x65, 0x32, 0x35, 0x45, 0xe6, 0x55, 0xbe, 0x23, 0xa8, 0x61, 0x6a, 0xc5, 0x10, 0x64, 0x2a,
0x39, 0xb2, 0x10, 0x11, 0xd1, 0x9f, 0xc1, 0xbc, 0x67, 0x04, 0xf5, 0xdb, 0x2e, 0xb1, 0x7b, 0x1d, 0x40, 0x25, 0x8b, 0x85, 0xa0, 0xe4, 0xc8, 0x42, 0x44, 0x44, 0x7f, 0x06, 0xf3, 0x9e, 0x11, 0xd4,
0xc7, 0x51, 0xe0, 0xa0, 0xb8, 0x89, 0x3f, 0x80, 0xc9, 0x3e, 0x11, 0x39, 0xa5, 0xb0, 0x8c, 0x96, 0x6f, 0xbb, 0xc4, 0xee, 0x75, 0x1c, 0x47, 0x41, 0x8a, 0xe2, 0x26, 0xfe, 0x00, 0x26, 0xfb, 0x44,
0xf8, 0xfb, 0xed, 0x92, 0x32, 0x98, 0xf5, 0xe3, 0x36, 0xdc, 0x96, 0xd2, 0xb9, 0x47, 0x63, 0xc5, 0xe4, 0x94, 0xc2, 0x32, 0x5a, 0xe2, 0x4f, 0xbb, 0x4b, 0xca, 0x60, 0xd6, 0x8f, 0xdb, 0x70, 0x5b,
0x87, 0x8d, 0x92, 0xb7, 0x6e, 0xee, 0xd6, 0xe8, 0xad, 0x3b, 0xcd, 0xd7, 0xde, 0x83, 0x28, 0x3f, 0x4a, 0xe7, 0x1e, 0x8d, 0x15, 0x1f, 0x36, 0x4a, 0x5e, 0xc8, 0xb9, 0x5b, 0xa3, 0x17, 0xf2, 0x34,
0xe2, 0x8e, 0x94, 0xb1, 0x35, 0x56, 0xad, 0xd8, 0xe4, 0x3e, 0xf5, 0x42, 0x72, 0x2c, 0x61, 0x87, 0x5f, 0x7b, 0x0f, 0xbd, 0xfc, 0x88, 0x3b, 0x52, 0xc6, 0xd6, 0x58, 0xb5, 0x62, 0x93, 0xfb, 0xd4,
0x30, 0x1b, 0x8c, 0xe4, 0xb1, 0xd2, 0xd8, 0x2c, 0x64, 0x5c, 0xeb, 0x94, 0xc8, 0x24, 0xc6, 0x1b, 0x0b, 0xc9, 0xb1, 0x84, 0x1d, 0xc2, 0x6c, 0x30, 0x92, 0xc7, 0x4a, 0x63, 0xb3, 0x90, 0x71, 0xad,
0xd2, 0x60, 0x2f, 0xcc, 0xc7, 0x32, 0xd8, 0xf0, 0x85, 0xb1, 0x2d, 0x39, 0xae, 0xbd, 0x74, 0x35, 0x53, 0x22, 0x93, 0x18, 0x6f, 0x48, 0x83, 0xbd, 0x30, 0x1f, 0xcb, 0x60, 0xc3, 0x17, 0xc6, 0xb6,
0xe5, 0xe1, 0x8b, 0x37, 0xf0, 0x36, 0x5c, 0x0b, 0xa7, 0x89, 0xb1, 0x4c, 0x7e, 0xc5, 0x37, 0x70, 0xe4, 0xb8, 0xf6, 0xd2, 0xd5, 0x94, 0x87, 0x2f, 0xde, 0xc0, 0xdb, 0x70, 0x2d, 0x9c, 0x26, 0xc6,
0x5c, 0x26, 0x19, 0x4b, 0xee, 0xc7, 0x7e, 0x32, 0x50, 0x12, 0xca, 0x58, 0x22, 0x75, 0xa8, 0xc5, 0x32, 0xf9, 0x15, 0xdf, 0xc0, 0x71, 0x99, 0x64, 0x2c, 0xb9, 0x1f, 0xfb, 0xc9, 0x40, 0x49, 0x28,
0xe5, 0x97, 0x1f, 0x63, 0xbf, 0x7a, 0xe9, 0x66, 0x2c, 0x61, 0x8e, 0x2f, 0x6c, 0xfc, 0xe5, 0xf7, 0x63, 0x89, 0xd4, 0xa1, 0x16, 0x97, 0x5f, 0x7e, 0x8c, 0xfd, 0xea, 0xa5, 0x9b, 0xb1, 0x84, 0x39,
0x73, 0x44, 0x7a, 0x64, 0x8e, 0x10, 0x41, 0xe2, 0x67, 0xb1, 0x9f, 0x60, 0xd3, 0x09, 0x1d, 0x7e, 0xbe, 0xb0, 0xf1, 0x97, 0xdf, 0xcf, 0x11, 0xe9, 0x91, 0x39, 0x42, 0x04, 0x89, 0x9f, 0xc5, 0x7e,
0x02, 0x1d, 0x57, 0x07, 0xad, 0x21, 0x9e, 0x0e, 0xd6, 0x90, 0x1b, 0x5b, 0x4d, 0xbb, 0x63, 0x2d, 0x82, 0x4d, 0x27, 0x74, 0xf8, 0x09, 0x74, 0x5c, 0x1d, 0xb4, 0x86, 0x78, 0x3a, 0x58, 0x43, 0x6e,
0xc6, 0x27, 0x7e, 0xee, 0x8c, 0x64, 0xe6, 0xb1, 0x04, 0x7f, 0x0a, 0x0b, 0xc9, 0x49, 0x79, 0x1c, 0x6c, 0x35, 0xed, 0x8e, 0xb5, 0x18, 0x9f, 0xf8, 0xb9, 0x33, 0x92, 0x99, 0xc7, 0x12, 0xfc, 0x29,
0xc9, 0x8f, 0xeb, 0x90, 0xf7, 0x0e, 0x94, 0xca, 0x6f, 0x1f, 0x0a, 0x90, 0xdb, 0xde, 0xd9, 0xdb, 0x2c, 0x24, 0x27, 0xe5, 0x71, 0x24, 0x3f, 0xae, 0x43, 0xde, 0x3b, 0x50, 0x2a, 0x3f, 0x8b, 0x28,
0x5d, 0x59, 0x6d, 0xf0, 0x1f, 0x3f, 0xac, 0xee, 0xe8, 0xfa, 0xfe, 0x6e, 0xb3, 0x92, 0x5a, 0xfe, 0x40, 0x6e, 0x7b, 0x67, 0x6f, 0x77, 0x65, 0xb5, 0xc1, 0x7f, 0x17, 0xb1, 0xba, 0xa3, 0xeb, 0xfb,
0x6d, 0x1a, 0x52, 0x9b, 0xaf, 0xd0, 0x9f, 0x43, 0x86, 0xbf, 0x04, 0x8e, 0x78, 0xfe, 0xad, 0x8d, 0xbb, 0xcd, 0x4a, 0x6a, 0xf9, 0xb7, 0x69, 0x48, 0x6d, 0xbe, 0x42, 0x7f, 0x0e, 0x19, 0xfe, 0x48,
0x7a, 0xec, 0xc4, 0x37, 0xbe, 0xf8, 0xff, 0x5f, 0x7d, 0x95, 0xba, 0x8a, 0x2b, 0xf5, 0xe1, 0xbb, 0x38, 0xe2, 0x65, 0xb8, 0x36, 0xea, 0x1d, 0x14, 0xdf, 0xf8, 0xe2, 0xff, 0x7f, 0xf5, 0x55, 0xea,
0x87, 0xc4, 0x35, 0xea, 0xa7, 0xc3, 0x3a, 0xab, 0x0f, 0xcf, 0xb4, 0xc7, 0x68, 0x1f, 0xd2, 0xbb, 0x2a, 0xae, 0xd4, 0x87, 0xef, 0x1e, 0x12, 0xd7, 0xa8, 0x9f, 0x0e, 0xeb, 0xac, 0x3e, 0x3c, 0xd3,
0x03, 0x17, 0x25, 0x3e, 0x0d, 0xd7, 0x92, 0xdf, 0x40, 0xf1, 0x1c, 0x13, 0x3c, 0x83, 0xcb, 0x8a, 0x1e, 0xa3, 0x7d, 0x48, 0xef, 0x0e, 0x5c, 0x94, 0xf8, 0x6a, 0x5c, 0x4b, 0x7e, 0x1e, 0xc5, 0x73,
0xe0, 0xfe, 0xc0, 0xa5, 0x62, 0x07, 0x50, 0x50, 0x5f, 0x31, 0x2f, 0x7c, 0x33, 0xae, 0x5d, 0xfc, 0x4c, 0xf0, 0x0c, 0x2e, 0x2b, 0x82, 0xfb, 0x03, 0x97, 0x8a, 0x1d, 0x40, 0x41, 0x7d, 0xe0, 0xbc,
0x42, 0x8a, 0xef, 0x30, 0x75, 0x37, 0xf0, 0x35, 0x45, 0x1d, 0x7f, 0x6b, 0x55, 0x67, 0xd3, 0x3c, 0xf0, 0x39, 0xb9, 0x76, 0xf1, 0xe3, 0x29, 0xbe, 0xc3, 0xd4, 0xdd, 0xc0, 0xd7, 0x14, 0x75, 0xfc,
0x33, 0x51, 0xe2, 0xab, 0x72, 0x2d, 0xf9, 0xe1, 0x34, 0x76, 0x36, 0xee, 0x99, 0x49, 0xc5, 0x9a, 0x19, 0x56, 0x9d, 0x4d, 0xf3, 0xcc, 0x44, 0x89, 0x0f, 0xce, 0xb5, 0xe4, 0x37, 0xd5, 0xd8, 0xd9,
0xe2, 0xdd, 0xb4, 0xe5, 0xa2, 0xdb, 0x31, 0xef, 0x66, 0xea, 0x0b, 0x51, 0x6d, 0x21, 0x99, 0x41, 0xb8, 0x67, 0x26, 0x15, 0x6b, 0x8a, 0x27, 0xd5, 0x96, 0x8b, 0x6e, 0xc7, 0x3c, 0xa9, 0xa9, 0x8f,
0x28, 0x5a, 0x60, 0x8a, 0x6a, 0xf8, 0xaa, 0xa2, 0xa8, 0xe5, 0xb1, 0x3d, 0xd3, 0x1e, 0x2f, 0x1f, 0x47, 0xb5, 0x85, 0x64, 0x06, 0xa1, 0x68, 0x81, 0x29, 0xaa, 0xe1, 0xab, 0x8a, 0xa2, 0x96, 0xc7,
0x43, 0x86, 0x21, 0xc4, 0xe8, 0x2f, 0xe4, 0x47, 0x2d, 0x06, 0xdb, 0x4e, 0x58, 0xfc, 0x00, 0xb6, 0xf6, 0x4c, 0x7b, 0xbc, 0x7c, 0x0c, 0x19, 0x86, 0x10, 0xa3, 0xbf, 0x90, 0x1f, 0xb5, 0x18, 0xd8,
0x8c, 0xab, 0x4c, 0x19, 0xc2, 0x25, 0xa9, 0x8c, 0x61, 0xc4, 0xcf, 0xb4, 0xc7, 0x8b, 0xda, 0xdb, 0x3b, 0x61, 0xf1, 0x03, 0xd8, 0x32, 0xae, 0x32, 0x65, 0x08, 0x97, 0xa4, 0x32, 0x86, 0x11, 0x3f,
0xda, 0xf2, 0x6f, 0x26, 0x21, 0xc3, 0xe0, 0x22, 0x64, 0x01, 0xf8, 0x68, 0x6a, 0x78, 0x96, 0x11, 0xd3, 0x1e, 0x2f, 0x6a, 0x6f, 0x6b, 0xcb, 0xbf, 0x99, 0x84, 0x0c, 0x83, 0x8b, 0x90, 0x05, 0xe0,
0x7c, 0x36, 0x3c, 0xcb, 0x28, 0x10, 0x8b, 0xe7, 0x99, 0xe2, 0x2a, 0x9e, 0x91, 0x8a, 0x19, 0x12, 0xa3, 0xa9, 0xe1, 0x59, 0x46, 0xf0, 0xd9, 0xf0, 0x2c, 0xa3, 0x40, 0x2c, 0x9e, 0x67, 0x8a, 0xab,
0x55, 0x67, 0xe0, 0x1a, 0xf5, 0xe9, 0x50, 0x00, 0x66, 0x3c, 0xcc, 0x50, 0x9c, 0xc0, 0x00, 0xaa, 0x78, 0x46, 0x2a, 0x66, 0x48, 0x54, 0x9d, 0x81, 0x6b, 0xd4, 0xa7, 0x43, 0x01, 0x98, 0xf1, 0x30,
0x1a, 0xde, 0x21, 0x31, 0x88, 0x2a, 0xc6, 0x4c, 0xe7, 0x4d, 0x7c, 0x5d, 0xf1, 0x2c, 0x57, 0x6b, 0x43, 0x71, 0x02, 0x03, 0xa8, 0x6a, 0x78, 0x87, 0xc4, 0x20, 0xaa, 0x18, 0x33, 0x9d, 0x37, 0xf1,
0x33, 0x46, 0xaa, 0xf7, 0xef, 0x34, 0x28, 0x07, 0x71, 0x51, 0x74, 0x37, 0x46, 0x72, 0x18, 0x5e, 0x75, 0xc5, 0xb3, 0x5c, 0xad, 0xcd, 0x18, 0xa9, 0xde, 0xbf, 0xd3, 0xa0, 0x1c, 0xc4, 0x45, 0xd1,
0xad, 0xdd, 0x1b, 0xcd, 0x94, 0x64, 0x01, 0x57, 0x7f, 0x4a, 0x48, 0xdf, 0xa0, 0x8c, 0xc2, 0xf1, 0xdd, 0x18, 0xc9, 0x61, 0x78, 0xb5, 0x76, 0x6f, 0x34, 0x53, 0x92, 0x05, 0x5c, 0xfd, 0x29, 0x21,
0xe8, 0x1f, 0x34, 0x98, 0x0e, 0x81, 0x9d, 0x28, 0x4e, 0x43, 0x04, 0x4a, 0xad, 0xdd, 0xbf, 0x80, 0x7d, 0x83, 0x32, 0x0a, 0xc7, 0xa3, 0x7f, 0xd0, 0x60, 0x3a, 0x04, 0x76, 0xa2, 0x38, 0x0d, 0x11,
0x4b, 0x18, 0xf2, 0x80, 0x19, 0xb2, 0x80, 0x6f, 0x44, 0x5c, 0xe1, 0x76, 0x7a, 0xc4, 0xb5, 0x84, 0x28, 0xb5, 0x76, 0xff, 0x02, 0x2e, 0x61, 0xc8, 0x03, 0x66, 0xc8, 0x02, 0xbe, 0x11, 0x71, 0x85,
0x31, 0xde, 0x32, 0x70, 0x60, 0x32, 0x76, 0x19, 0x02, 0x40, 0x67, 0xec, 0x32, 0x04, 0x51, 0xcd, 0xdb, 0xe9, 0x11, 0xd7, 0x12, 0xc6, 0x78, 0xcb, 0xc0, 0x81, 0xc9, 0xd8, 0x65, 0x08, 0x00, 0x9d,
0x11, 0xcb, 0xc0, 0xd1, 0x48, 0xba, 0xc5, 0x7f, 0x97, 0x86, 0xdc, 0x2a, 0xff, 0x05, 0x22, 0x72, 0xb1, 0xcb, 0x10, 0x44, 0x35, 0x47, 0x2c, 0x03, 0x47, 0x23, 0xe9, 0x16, 0xff, 0x5d, 0x1a, 0x72,
0x20, 0xef, 0x21, 0x80, 0x68, 0x3e, 0x0e, 0x8d, 0xf1, 0x6f, 0x0b, 0xb5, 0xdb, 0x89, 0xfd, 0x42, 0xab, 0xfc, 0xc7, 0x89, 0xc8, 0x81, 0xbc, 0x87, 0x00, 0xa2, 0xf9, 0x38, 0x34, 0xc6, 0xbf, 0x2d,
0xfb, 0x7d, 0xa6, 0xfd, 0x36, 0xae, 0x49, 0xed, 0xe2, 0x87, 0x8e, 0x75, 0x7e, 0xed, 0xaf, 0x1b, 0xd4, 0x6e, 0x27, 0xf6, 0x0b, 0xed, 0xf7, 0x99, 0xf6, 0xdb, 0xb8, 0x26, 0xb5, 0x8b, 0xdf, 0x40,
0xed, 0x36, 0x9d, 0xf8, 0xdf, 0x42, 0x51, 0x85, 0xe9, 0xd0, 0x9d, 0x58, 0x14, 0x48, 0x45, 0xfa, 0xd6, 0xf9, 0xb5, 0xbf, 0x6e, 0xb4, 0xdb, 0x74, 0xe2, 0x7f, 0x0b, 0x45, 0x15, 0xa6, 0x43, 0x77,
0x6a, 0x78, 0x14, 0x8b, 0xd0, 0xbe, 0xc8, 0xb4, 0x63, 0x7c, 0x2b, 0x41, 0xbb, 0xcd, 0xd8, 0x03, 0x62, 0x51, 0x20, 0x15, 0xe9, 0xab, 0xe1, 0x51, 0x2c, 0x42, 0xfb, 0x22, 0xd3, 0x8e, 0xf1, 0xad,
0x06, 0x70, 0x98, 0x2d, 0xde, 0x80, 0x00, 0x8a, 0x17, 0x6f, 0x40, 0x10, 0xa5, 0xbb, 0xd0, 0x80, 0x04, 0xed, 0x36, 0x63, 0x0f, 0x18, 0xc0, 0x61, 0xb6, 0x78, 0x03, 0x02, 0x28, 0x5e, 0xbc, 0x01,
0x01, 0x63, 0xa7, 0x06, 0xbc, 0x06, 0xf0, 0x41, 0x35, 0x14, 0xeb, 0x57, 0xe5, 0xea, 0x14, 0x0e, 0x41, 0x94, 0xee, 0x42, 0x03, 0x06, 0x8c, 0x9d, 0x1a, 0xf0, 0x1a, 0xc0, 0x07, 0xd5, 0x50, 0xac,
0xf9, 0x28, 0x1e, 0x17, 0xdd, 0x73, 0x21, 0xd5, 0xdd, 0x8e, 0x43, 0x43, 0x7f, 0xf9, 0x9b, 0x2c, 0x5f, 0x95, 0xab, 0x53, 0x38, 0xe4, 0xa3, 0x78, 0x5c, 0x74, 0xcf, 0x85, 0x54, 0x77, 0x3b, 0x0e,
0x14, 0x5e, 0x1a, 0x1d, 0xd3, 0x25, 0xa6, 0x61, 0xb6, 0x08, 0x3a, 0x82, 0x0c, 0x2b, 0x8d, 0xe1, 0x0d, 0xfd, 0xe5, 0xaf, 0xb3, 0x50, 0x78, 0x69, 0x74, 0x4c, 0x97, 0x98, 0x86, 0xd9, 0x22, 0xe8,
0x2c, 0xa7, 0x62, 0x4d, 0xe1, 0x2c, 0x17, 0x00, 0x62, 0xf0, 0x3d, 0xa6, 0x79, 0x1e, 0xcf, 0x49, 0x08, 0x32, 0xac, 0x34, 0x86, 0xb3, 0x9c, 0x8a, 0x35, 0x85, 0xb3, 0x5c, 0x00, 0x88, 0xc1, 0xf7,
0xcd, 0x3d, 0x5f, 0x7c, 0x9d, 0x61, 0x28, 0x74, 0xc2, 0x7f, 0x09, 0x59, 0x01, 0xcf, 0x87, 0x84, 0x98, 0xe6, 0x79, 0x3c, 0x27, 0x35, 0xf7, 0x7c, 0xf1, 0x75, 0x86, 0xa1, 0xd0, 0x09, 0xff, 0x25,
0x05, 0xb0, 0x95, 0xda, 0xcd, 0xf8, 0xce, 0xa4, 0xed, 0xa5, 0xaa, 0x72, 0x18, 0x2f, 0xd5, 0xf5, 0x64, 0x05, 0x3c, 0x1f, 0x12, 0x16, 0xc0, 0x56, 0x6a, 0x37, 0xe3, 0x3b, 0x93, 0xb6, 0x97, 0xaa,
0x06, 0xc0, 0x07, 0x08, 0xc3, 0xce, 0x8d, 0xe0, 0x89, 0xb5, 0x85, 0x64, 0x06, 0xa1, 0xf7, 0x11, 0xca, 0x61, 0xbc, 0x54, 0xd7, 0x1b, 0x00, 0x1f, 0x20, 0x0c, 0x3b, 0x37, 0x82, 0x27, 0xd6, 0x16,
0xd3, 0x7b, 0x17, 0xcf, 0xc7, 0xe9, 0x6d, 0x7b, 0xfc, 0x54, 0xf7, 0x21, 0x4c, 0xae, 0x1b, 0xce, 0x92, 0x19, 0x84, 0xde, 0x47, 0x4c, 0xef, 0x5d, 0x3c, 0x1f, 0xa7, 0xb7, 0xed, 0xf1, 0x53, 0xdd,
0x09, 0x0a, 0x15, 0x3b, 0xe5, 0x47, 0x03, 0xb5, 0x5a, 0x5c, 0x97, 0xd0, 0x74, 0x97, 0x69, 0xba, 0x87, 0x30, 0xb9, 0x6e, 0x38, 0x27, 0x28, 0x54, 0xec, 0x94, 0xdf, 0x13, 0xd4, 0x6a, 0x71, 0x5d,
0x85, 0xab, 0x71, 0x9a, 0x4e, 0x0c, 0x87, 0x56, 0x0f, 0x74, 0x02, 0x59, 0xfe, 0x3b, 0x82, 0xb0, 0x42, 0xd3, 0x5d, 0xa6, 0xe9, 0x16, 0xae, 0xc6, 0x69, 0x3a, 0x31, 0x1c, 0x5a, 0x3d, 0xd0, 0x09,
0x2f, 0x03, 0xbf, 0x45, 0x08, 0xfb, 0x32, 0xf8, 0xd3, 0x83, 0xcb, 0x69, 0x72, 0x61, 0x4a, 0x3e, 0x64, 0xf9, 0x4f, 0x0c, 0xc2, 0xbe, 0x0c, 0xfc, 0x4c, 0x21, 0xec, 0xcb, 0xe0, 0xaf, 0x12, 0x2e,
0xde, 0xa3, 0x5b, 0xa1, 0xa5, 0x09, 0x3e, 0xf4, 0xd7, 0xe6, 0x93, 0xba, 0x85, 0xbe, 0x87, 0x4c, 0xa7, 0xc9, 0x85, 0x29, 0xf9, 0xae, 0x8f, 0x6e, 0x85, 0x96, 0x26, 0xf8, 0x1b, 0x80, 0xda, 0x7c,
0xdf, 0x1d, 0x7c, 0x33, 0x76, 0xed, 0x04, 0xf7, 0x33, 0xed, 0xf1, 0xdb, 0x1a, 0x2d, 0x13, 0xe0, 0x52, 0xb7, 0xd0, 0xf7, 0x90, 0xe9, 0xbb, 0x83, 0x6f, 0xc6, 0xae, 0x9d, 0xe0, 0x7e, 0xa6, 0x3d,
0x83, 0xac, 0x91, 0xe8, 0x08, 0xe3, 0xb5, 0x91, 0xe8, 0x88, 0xe0, 0xb3, 0x78, 0x99, 0x29, 0x7f, 0x7e, 0x5b, 0xa3, 0x65, 0x02, 0x7c, 0x90, 0x35, 0x12, 0x1d, 0x61, 0xbc, 0x36, 0x12, 0x1d, 0x11,
0x8a, 0x1f, 0xc6, 0x29, 0x77, 0x6d, 0xc3, 0x74, 0x8e, 0x88, 0xfd, 0x16, 0x07, 0xd3, 0x9c, 0x93, 0x7c, 0x16, 0x2f, 0x33, 0xe5, 0x4f, 0xf1, 0xc3, 0x38, 0xe5, 0xae, 0x6d, 0x98, 0xce, 0x11, 0xb1,
0x4e, 0x9f, 0x46, 0xca, 0xef, 0xa7, 0x61, 0x92, 0x9e, 0x47, 0x69, 0x79, 0xf6, 0xaf, 0xf1, 0x61, 0xdf, 0xe2, 0x60, 0x9a, 0x73, 0xd2, 0xe9, 0xd3, 0x48, 0xf9, 0xfd, 0x34, 0x4c, 0xd2, 0xf3, 0x28,
0x6b, 0x22, 0xe0, 0x59, 0xd8, 0x9a, 0x28, 0x02, 0x10, 0x2d, 0xcf, 0xec, 0xb7, 0xe6, 0x84, 0x31, 0x2d, 0xcf, 0xfe, 0x35, 0x3e, 0x6c, 0x4d, 0x04, 0x3c, 0x0b, 0x5b, 0x13, 0x45, 0x00, 0xa2, 0xe5,
0x51, 0xaf, 0x3b, 0x50, 0x50, 0xee, 0xfa, 0x28, 0x46, 0x60, 0x10, 0x99, 0x0b, 0xd7, 0x85, 0x18, 0x99, 0xfd, 0x0c, 0x9d, 0x30, 0x26, 0xea, 0x75, 0x07, 0x0a, 0xca, 0x5d, 0x1f, 0xc5, 0x08, 0x0c,
0xa0, 0x00, 0xdf, 0x66, 0x3a, 0xe7, 0xf0, 0x6c, 0x40, 0x67, 0x9b, 0x73, 0x51, 0xa5, 0x7f, 0x0d, 0x22, 0x73, 0xe1, 0xba, 0x10, 0x03, 0x14, 0xe0, 0xdb, 0x4c, 0xe7, 0x1c, 0x9e, 0x0d, 0xe8, 0x6c,
0x45, 0x15, 0x13, 0x40, 0x31, 0x32, 0x43, 0xc8, 0x5f, 0x38, 0x25, 0xc6, 0x41, 0x0a, 0xd1, 0xec, 0x73, 0x2e, 0xaa, 0xf4, 0xaf, 0xa1, 0xa8, 0x62, 0x02, 0x28, 0x46, 0x66, 0x08, 0xf9, 0x0b, 0xa7,
0xe0, 0xfd, 0xae, 0x5e, 0xb2, 0x52, 0xe5, 0x7d, 0xc8, 0x09, 0xa0, 0x20, 0x6e, 0xb6, 0x41, 0xa8, 0xc4, 0x38, 0x48, 0x21, 0x9a, 0x1d, 0xbc, 0x9f, 0xdc, 0x4b, 0x56, 0xaa, 0xbc, 0x0f, 0x39, 0x01,
0x30, 0x6e, 0xb6, 0x21, 0x94, 0x21, 0x7a, 0xcc, 0x63, 0x5a, 0xe9, 0x7d, 0x48, 0x96, 0x20, 0xa1, 0x14, 0xc4, 0xcd, 0x36, 0x08, 0x15, 0xc6, 0xcd, 0x36, 0x84, 0x32, 0x44, 0x8f, 0x79, 0x4c, 0x2b,
0xf1, 0x05, 0x71, 0x93, 0x34, 0xfa, 0xd8, 0x57, 0x92, 0x46, 0xe5, 0x2e, 0x3a, 0x4a, 0xe3, 0x31, 0xbd, 0x0f, 0xc9, 0x12, 0x24, 0x34, 0xbe, 0x20, 0x6e, 0x92, 0x46, 0x1f, 0xfb, 0x4a, 0xd2, 0xa8,
0x71, 0x45, 0x2c, 0xc9, 0x7b, 0x1e, 0x4a, 0x10, 0xa8, 0xa6, 0x7c, 0x3c, 0x8a, 0x25, 0xe9, 0x54, 0xdc, 0x45, 0x47, 0x69, 0x3c, 0x26, 0xae, 0x88, 0x25, 0x79, 0xcf, 0x43, 0x09, 0x02, 0xd5, 0x94,
0xee, 0x2b, 0x15, 0xf9, 0x1e, 0x7d, 0x0e, 0xe0, 0x43, 0x1a, 0xe1, 0xd3, 0x56, 0x2c, 0x2e, 0x1a, 0x8f, 0x47, 0xb1, 0x24, 0x9d, 0xca, 0x7d, 0xa5, 0x22, 0xdf, 0xa3, 0xcf, 0x01, 0x7c, 0x48, 0x23,
0x3e, 0x6d, 0xc5, 0xa3, 0x22, 0xd1, 0xfc, 0xe1, 0xeb, 0xe6, 0x17, 0x03, 0xaa, 0xfd, 0x5f, 0x34, 0x7c, 0xda, 0x8a, 0xc5, 0x45, 0xc3, 0xa7, 0xad, 0x78, 0x54, 0x24, 0x9a, 0x3f, 0x7c, 0xdd, 0xfc,
0x40, 0x51, 0x04, 0x04, 0x3d, 0x89, 0xd7, 0x10, 0x8b, 0xb8, 0xd6, 0x9e, 0x5e, 0x8e, 0x39, 0xa9, 0x62, 0x40, 0xb5, 0xff, 0x8b, 0x06, 0x28, 0x8a, 0x80, 0xa0, 0x27, 0xf1, 0x1a, 0x62, 0x11, 0xd7,
0x44, 0xf8, 0x66, 0xb5, 0xd8, 0x88, 0xfe, 0x6b, 0x6a, 0xd8, 0x97, 0x1a, 0x94, 0x02, 0x10, 0x0a, 0xda, 0xd3, 0xcb, 0x31, 0x27, 0x95, 0x08, 0xdf, 0xac, 0x16, 0x1b, 0xd1, 0x7f, 0x4d, 0x0d, 0xfb,
0x7a, 0x90, 0xb0, 0xc6, 0x21, 0xd0, 0xb6, 0xf6, 0xf0, 0x42, 0xbe, 0xa4, 0x93, 0x98, 0xb2, 0x23, 0x52, 0x83, 0x52, 0x00, 0x42, 0x41, 0x0f, 0x12, 0xd6, 0x38, 0x04, 0xda, 0xd6, 0x1e, 0x5e, 0xc8,
0xe4, 0x41, 0xfc, 0x1f, 0x35, 0x28, 0x07, 0x61, 0x17, 0x94, 0x20, 0x3f, 0x02, 0xfc, 0xd6, 0x16, 0x97, 0x74, 0x12, 0x53, 0x76, 0x84, 0x3c, 0x88, 0xff, 0xa3, 0x06, 0xe5, 0x20, 0xec, 0x82, 0x12,
0x2f, 0x66, 0xbc, 0x78, 0xa9, 0xfc, 0xb3, 0x79, 0x1f, 0x72, 0x02, 0xac, 0x89, 0x0b, 0x88, 0x20, 0xe4, 0x47, 0x80, 0xdf, 0xda, 0xe2, 0xc5, 0x8c, 0x17, 0x2f, 0x95, 0x7f, 0x36, 0xef, 0x43, 0x4e,
0x6c, 0x1c, 0x17, 0x10, 0x21, 0xa4, 0x27, 0x21, 0x20, 0x6c, 0xab, 0x4b, 0x94, 0x10, 0x14, 0x88, 0x80, 0x35, 0x71, 0x01, 0x11, 0x84, 0x8d, 0xe3, 0x02, 0x22, 0x84, 0xf4, 0x24, 0x04, 0x84, 0x6d,
0x4e, 0x92, 0xc6, 0xd1, 0x21, 0x18, 0x82, 0x83, 0x46, 0x69, 0xf4, 0x43, 0x50, 0xc2, 0x39, 0x28, 0x75, 0x89, 0x12, 0x82, 0x02, 0xd1, 0x49, 0xd2, 0x38, 0x3a, 0x04, 0x43, 0x70, 0xd0, 0x28, 0x8d,
0x41, 0xe0, 0x05, 0x21, 0x18, 0x46, 0x83, 0x12, 0x42, 0x90, 0x29, 0x55, 0x42, 0xd0, 0x07, 0x5f, 0x7e, 0x08, 0x4a, 0x38, 0x07, 0x25, 0x08, 0xbc, 0x20, 0x04, 0xc3, 0x68, 0x50, 0x42, 0x08, 0x32,
0xe2, 0x42, 0x30, 0x82, 0x88, 0xc7, 0x85, 0x60, 0x14, 0xbf, 0x49, 0x58, 0x57, 0xa6, 0x3b, 0x10, 0xa5, 0x4a, 0x08, 0xfa, 0xe0, 0x4b, 0x5c, 0x08, 0x46, 0x10, 0xf1, 0xb8, 0x10, 0x8c, 0xe2, 0x37,
0x82, 0x33, 0x31, 0x58, 0x0d, 0x7a, 0x9a, 0xe0, 0xd0, 0x58, 0xb0, 0xbd, 0xf6, 0xd6, 0x25, 0xb9, 0x09, 0xeb, 0xca, 0x74, 0x07, 0x42, 0x70, 0x26, 0x06, 0xab, 0x41, 0x4f, 0x13, 0x1c, 0x1a, 0x0b,
0x47, 0xee, 0x7d, 0xbe, 0x14, 0x72, 0xef, 0x7f, 0xad, 0xc1, 0x6c, 0x1c, 0xd6, 0x83, 0x12, 0x74, 0xb6, 0xd7, 0xde, 0xba, 0x24, 0xf7, 0xc8, 0xbd, 0xcf, 0x97, 0x42, 0xee, 0xfd, 0x7f, 0xd7, 0x60,
0x25, 0x00, 0xf5, 0xb5, 0xa5, 0xcb, 0xb2, 0x5f, 0xec, 0x35, 0x2f, 0x1a, 0x9e, 0x57, 0xfe, 0xfb, 0x36, 0x0e, 0xeb, 0x41, 0x09, 0xba, 0x12, 0x80, 0xfa, 0xda, 0xd2, 0x65, 0xd9, 0x2f, 0xf6, 0x9a,
0xbb, 0x79, 0xed, 0xff, 0xbe, 0x9b, 0xd7, 0x7e, 0xf1, 0xdd, 0xbc, 0xf6, 0xaf, 0xbf, 0x9c, 0x9f, 0x17, 0x0d, 0xcf, 0x2b, 0xff, 0xfd, 0xdd, 0xbc, 0xf6, 0x7f, 0xdf, 0xcd, 0x6b, 0xbf, 0xf8, 0x6e,
0x38, 0xcc, 0xb2, 0xff, 0xe1, 0xf5, 0xee, 0x1f, 0x02, 0x00, 0x00, 0xff, 0xff, 0x74, 0x55, 0x61, 0x5e, 0xfb, 0xd7, 0x5f, 0xce, 0x4f, 0x1c, 0x66, 0xd9, 0x7f, 0xfe, 0x7a, 0xf7, 0x0f, 0x01, 0x00,
0xe6, 0x68, 0x36, 0x00, 0x00, 0x00, 0xff, 0xff, 0xb4, 0xfd, 0xab, 0xe6, 0x83, 0x36, 0x00, 0x00,
} }

View File

@ -671,6 +671,13 @@ message WatchCreateRequest {
// If prev_kv is set, created watcher gets the previous KV before the event happens. // If prev_kv is set, created watcher gets the previous KV before the event happens.
// If the previous KV is already compacted, nothing will be returned. // If the previous KV is already compacted, nothing will be returned.
bool prev_kv = 6; bool prev_kv = 6;
// If watch_id is provided and non-zero, it will be assigned to this watcher.
// Since creating a watcher in etcd is not a synchronous operation,
// this can be used ensure that ordering is correct when creating multiple
// watchers on the same stream. Creating a watcher with an ID already in
// use on the stream will cause an error to be returned.
int64 watch_id = 7;
} }
message WatchCancelRequest { message WatchCancelRequest {

View File

@ -716,7 +716,7 @@ func TestWatchableKVWatch(t *testing.T) {
w := s.NewWatchStream() w := s.NewWatchStream()
defer w.Close() defer w.Close()
wid := w.Watch([]byte("foo"), []byte("fop"), 0) wid, _ := w.Watch(0, []byte("foo"), []byte("fop"), 0)
wev := []mvccpb.Event{ wev := []mvccpb.Event{
{Type: mvccpb.PUT, {Type: mvccpb.PUT,
@ -783,7 +783,7 @@ func TestWatchableKVWatch(t *testing.T) {
} }
w = s.NewWatchStream() w = s.NewWatchStream()
wid = w.Watch([]byte("foo1"), []byte("foo2"), 3) wid, _ = w.Watch(0, []byte("foo1"), []byte("foo2"), 3)
select { select {
case resp := <-w.Chan(): case resp := <-w.Chan():

View File

@ -78,7 +78,7 @@ func BenchmarkWatchableStoreWatchSyncPut(b *testing.B) {
watchIDs := make([]WatchID, b.N) watchIDs := make([]WatchID, b.N)
for i := range watchIDs { for i := range watchIDs {
// non-0 value to keep watchers in unsynced // non-0 value to keep watchers in unsynced
watchIDs[i] = w.Watch(k, nil, 1) watchIDs[i], _ = w.Watch(0, k, nil, 1)
} }
b.ResetTimer() b.ResetTimer()
@ -142,7 +142,7 @@ func BenchmarkWatchableStoreUnsyncedCancel(b *testing.B) {
watchIDs := make([]WatchID, watcherN) watchIDs := make([]WatchID, watcherN)
for i := 0; i < watcherN; i++ { for i := 0; i < watcherN; i++ {
// non-0 value to keep watchers in unsynced // non-0 value to keep watchers in unsynced
watchIDs[i] = w.Watch(testKey, nil, 1) watchIDs[i], _ = w.Watch(0, testKey, nil, 1)
} }
// random-cancel N watchers to make it not biased towards // random-cancel N watchers to make it not biased towards
@ -182,7 +182,7 @@ func BenchmarkWatchableStoreSyncedCancel(b *testing.B) {
watchIDs := make([]WatchID, watcherN) watchIDs := make([]WatchID, watcherN)
for i := 0; i < watcherN; i++ { for i := 0; i < watcherN; i++ {
// 0 for startRev to keep watchers in synced // 0 for startRev to keep watchers in synced
watchIDs[i] = w.Watch(testKey, nil, 0) watchIDs[i], _ = w.Watch(0, testKey, nil, 0)
} }
// randomly cancel watchers to make it not biased towards // randomly cancel watchers to make it not biased towards

View File

@ -42,7 +42,7 @@ func TestWatch(t *testing.T) {
s.Put(testKey, testValue, lease.NoLease) s.Put(testKey, testValue, lease.NoLease)
w := s.NewWatchStream() w := s.NewWatchStream()
w.Watch(testKey, nil, 0) w.Watch(0, testKey, nil, 0)
if !s.synced.contains(string(testKey)) { if !s.synced.contains(string(testKey)) {
// the key must have had an entry in synced // the key must have had an entry in synced
@ -63,7 +63,7 @@ func TestNewWatcherCancel(t *testing.T) {
s.Put(testKey, testValue, lease.NoLease) s.Put(testKey, testValue, lease.NoLease)
w := s.NewWatchStream() w := s.NewWatchStream()
wt := w.Watch(testKey, nil, 0) wt, _ := w.Watch(0, testKey, nil, 0)
if err := w.Cancel(wt); err != nil { if err := w.Cancel(wt); err != nil {
t.Error(err) t.Error(err)
@ -114,7 +114,7 @@ func TestCancelUnsynced(t *testing.T) {
watchIDs := make([]WatchID, watcherN) watchIDs := make([]WatchID, watcherN)
for i := 0; i < watcherN; i++ { for i := 0; i < watcherN; i++ {
// use 1 to keep watchers in unsynced // use 1 to keep watchers in unsynced
watchIDs[i] = w.Watch(testKey, nil, 1) watchIDs[i], _ = w.Watch(0, testKey, nil, 1)
} }
for _, idx := range watchIDs { for _, idx := range watchIDs {
@ -160,7 +160,7 @@ func TestSyncWatchers(t *testing.T) {
for i := 0; i < watcherN; i++ { for i := 0; i < watcherN; i++ {
// specify rev as 1 to keep watchers in unsynced // specify rev as 1 to keep watchers in unsynced
w.Watch(testKey, nil, 1) w.Watch(0, testKey, nil, 1)
} }
// Before running s.syncWatchers() synced should be empty because we manually // Before running s.syncWatchers() synced should be empty because we manually
@ -242,7 +242,7 @@ func TestWatchCompacted(t *testing.T) {
} }
w := s.NewWatchStream() w := s.NewWatchStream()
wt := w.Watch(testKey, nil, compactRev-1) wt, _ := w.Watch(0, testKey, nil, compactRev-1)
select { select {
case resp := <-w.Chan(): case resp := <-w.Chan():
@ -271,7 +271,7 @@ func TestWatchFutureRev(t *testing.T) {
w := s.NewWatchStream() w := s.NewWatchStream()
wrev := int64(10) wrev := int64(10)
w.Watch(testKey, nil, wrev) w.Watch(0, testKey, nil, wrev)
for i := 0; i < 10; i++ { for i := 0; i < 10; i++ {
rev := s.Put(testKey, testValue, lease.NoLease) rev := s.Put(testKey, testValue, lease.NoLease)
@ -310,7 +310,7 @@ func TestWatchRestore(t *testing.T) {
defer cleanup(newStore, newBackend, newPath) defer cleanup(newStore, newBackend, newPath)
w := newStore.NewWatchStream() w := newStore.NewWatchStream()
w.Watch(testKey, nil, rev-1) w.Watch(0, testKey, nil, rev-1)
newStore.Restore(b) newStore.Restore(b)
select { select {
@ -349,7 +349,7 @@ func TestWatchBatchUnsynced(t *testing.T) {
} }
w := s.NewWatchStream() w := s.NewWatchStream()
w.Watch(v, nil, 1) w.Watch(0, v, nil, 1)
for i := 0; i < batches; i++ { for i := 0; i < batches; i++ {
if resp := <-w.Chan(); len(resp.Events) != watchBatchMaxRevs { if resp := <-w.Chan(); len(resp.Events) != watchBatchMaxRevs {
t.Fatalf("len(events) = %d, want %d", len(resp.Events), watchBatchMaxRevs) t.Fatalf("len(events) = %d, want %d", len(resp.Events), watchBatchMaxRevs)
@ -485,7 +485,7 @@ func TestWatchVictims(t *testing.T) {
for i := 0; i < numWatches; i++ { for i := 0; i < numWatches; i++ {
go func() { go func() {
w := s.NewWatchStream() w := s.NewWatchStream()
w.Watch(testKey, nil, 1) w.Watch(0, testKey, nil, 1)
defer func() { defer func() {
w.Close() w.Close()
wg.Done() wg.Done()
@ -561,7 +561,7 @@ func TestStressWatchCancelClose(t *testing.T) {
w := s.NewWatchStream() w := s.NewWatchStream()
ids := make([]WatchID, 10) ids := make([]WatchID, 10)
for i := range ids { for i := range ids {
ids[i] = w.Watch(testKey, nil, 0) ids[i], _ = w.Watch(0, testKey, nil, 0)
} }
<-readyc <-readyc
wg.Add(1 + len(ids)/2) wg.Add(1 + len(ids)/2)

View File

@ -22,8 +22,14 @@ import (
"github.com/coreos/etcd/mvcc/mvccpb" "github.com/coreos/etcd/mvcc/mvccpb"
) )
// AutoWatchID is the watcher ID passed in WatchStream.Watch when no
// user-provided ID is available. If pass, an ID will automatically be assigned.
const AutoWatchID WatchID = 0
var ( var (
ErrWatcherNotExist = errors.New("mvcc: watcher does not exist") ErrWatcherNotExist = errors.New("mvcc: watcher does not exist")
ErrEmptyWatcherRange = errors.New("mvcc: watcher range is empty")
ErrWatcherDuplicateID = errors.New("mvcc: duplicate watch ID provided on the WatchStream")
) )
type WatchID int64 type WatchID int64
@ -36,12 +42,13 @@ type WatchStream interface {
// happened on the given key or range [key, end) from the given startRev. // happened on the given key or range [key, end) from the given startRev.
// //
// The whole event history can be watched unless compacted. // The whole event history can be watched unless compacted.
// If `startRev` <=0, watch observes events after currentRev. // If "startRev" <=0, watch observes events after currentRev.
// //
// The returned `id` is the ID of this watcher. It appears as WatchID // The returned "id" is the ID of this watcher. It appears as WatchID
// in events that are sent to the created watcher through stream channel. // in events that are sent to the created watcher through stream channel.
// // The watch ID is used when it's not equal to AutoWatchID. Otherwise,
Watch(key, end []byte, startRev int64, fcs ...FilterFunc) WatchID // an auto-generated watch ID is returned.
Watch(id WatchID, key, end []byte, startRev int64, fcs ...FilterFunc) (WatchID, error)
// Chan returns a chan. All watch response will be sent to the returned chan. // Chan returns a chan. All watch response will be sent to the returned chan.
Chan() <-chan WatchResponse Chan() <-chan WatchResponse
@ -98,28 +105,34 @@ type watchStream struct {
} }
// Watch creates a new watcher in the stream and returns its WatchID. // Watch creates a new watcher in the stream and returns its WatchID.
// TODO: return error if ws is closed? func (ws *watchStream) Watch(id WatchID, key, end []byte, startRev int64, fcs ...FilterFunc) (WatchID, error) {
func (ws *watchStream) Watch(key, end []byte, startRev int64, fcs ...FilterFunc) WatchID {
// prevent wrong range where key >= end lexicographically // prevent wrong range where key >= end lexicographically
// watch request with 'WithFromKey' has empty-byte range end // watch request with 'WithFromKey' has empty-byte range end
if len(end) != 0 && bytes.Compare(key, end) != -1 { if len(end) != 0 && bytes.Compare(key, end) != -1 {
return -1 return -1, ErrEmptyWatcherRange
} }
ws.mu.Lock() ws.mu.Lock()
defer ws.mu.Unlock() defer ws.mu.Unlock()
if ws.closed { if ws.closed {
return -1 return -1, ErrEmptyWatcherRange
} }
id := ws.nextID if id == AutoWatchID {
ws.nextID++ for ws.watchers[ws.nextID] != nil {
ws.nextID++
}
id = ws.nextID
ws.nextID++
} else if _, ok := ws.watchers[id]; ok {
return -1, ErrWatcherDuplicateID
}
w, c := ws.watchable.watch(key, end, startRev, id, ws.ch, fcs...) w, c := ws.watchable.watch(key, end, startRev, id, ws.ch, fcs...)
ws.cancels[id] = c ws.cancels[id] = c
ws.watchers[id] = w ws.watchers[id] = w
return id return id, nil
} }
func (ws *watchStream) Chan() <-chan WatchResponse { func (ws *watchStream) Chan() <-chan WatchResponse {

View File

@ -33,6 +33,6 @@ func BenchmarkKVWatcherMemoryUsage(b *testing.B) {
b.ReportAllocs() b.ReportAllocs()
b.StartTimer() b.StartTimer()
for i := 0; i < b.N; i++ { for i := 0; i < b.N; i++ {
w.Watch([]byte(fmt.Sprint("foo", i)), nil, 0) w.Watch(0, []byte(fmt.Sprint("foo", i)), nil, 0)
} }
} }

View File

@ -40,7 +40,7 @@ func TestWatcherWatchID(t *testing.T) {
idm := make(map[WatchID]struct{}) idm := make(map[WatchID]struct{})
for i := 0; i < 10; i++ { for i := 0; i < 10; i++ {
id := w.Watch([]byte("foo"), nil, 0) id, _ := w.Watch(0, []byte("foo"), nil, 0)
if _, ok := idm[id]; ok { if _, ok := idm[id]; ok {
t.Errorf("#%d: id %d exists", i, id) t.Errorf("#%d: id %d exists", i, id)
} }
@ -62,7 +62,7 @@ func TestWatcherWatchID(t *testing.T) {
// unsynced watchers // unsynced watchers
for i := 10; i < 20; i++ { for i := 10; i < 20; i++ {
id := w.Watch([]byte("foo2"), nil, 1) id, _ := w.Watch(0, []byte("foo2"), nil, 1)
if _, ok := idm[id]; ok { if _, ok := idm[id]; ok {
t.Errorf("#%d: id %d exists", i, id) t.Errorf("#%d: id %d exists", i, id)
} }
@ -79,6 +79,41 @@ func TestWatcherWatchID(t *testing.T) {
} }
} }
func TestWatcherRequestsCustomID(t *testing.T) {
b, tmpPath := backend.NewDefaultTmpBackend()
s := WatchableKV(newWatchableStore(b, &lease.FakeLessor{}, nil))
defer cleanup(s, b, tmpPath)
w := s.NewWatchStream()
defer w.Close()
// - Request specifically ID #1
// - Try to duplicate it, get an error
// - Make sure the auto-assignment skips over things we manually assigned
tt := []struct {
givenID WatchID
expectedID WatchID
expectedErr error
}{
{1, 1, nil},
{1, 0, ErrWatcherDuplicateID},
{0, 0, nil},
{0, 2, nil},
}
for i, tcase := range tt {
id, err := w.Watch(tcase.givenID, []byte("foo"), nil, 0)
if tcase.expectedErr != nil || err != nil {
if err != tcase.expectedErr {
t.Errorf("expected get error %q in test case %q, got %q", tcase.expectedErr, i, err)
}
} else if tcase.expectedID != id {
t.Errorf("expected to create ID %d, got %d in test case %d", tcase.expectedID, id, i)
}
}
}
// TestWatcherWatchPrefix tests if Watch operation correctly watches // TestWatcherWatchPrefix tests if Watch operation correctly watches
// and returns events with matching prefixes. // and returns events with matching prefixes.
func TestWatcherWatchPrefix(t *testing.T) { func TestWatcherWatchPrefix(t *testing.T) {
@ -95,7 +130,7 @@ func TestWatcherWatchPrefix(t *testing.T) {
keyWatch, keyEnd, keyPut := []byte("foo"), []byte("fop"), []byte("foobar") keyWatch, keyEnd, keyPut := []byte("foo"), []byte("fop"), []byte("foobar")
for i := 0; i < 10; i++ { for i := 0; i < 10; i++ {
id := w.Watch(keyWatch, keyEnd, 0) id, _ := w.Watch(0, keyWatch, keyEnd, 0)
if _, ok := idm[id]; ok { if _, ok := idm[id]; ok {
t.Errorf("#%d: unexpected duplicated id %x", i, id) t.Errorf("#%d: unexpected duplicated id %x", i, id)
} }
@ -127,7 +162,7 @@ func TestWatcherWatchPrefix(t *testing.T) {
// unsynced watchers // unsynced watchers
for i := 10; i < 15; i++ { for i := 10; i < 15; i++ {
id := w.Watch(keyWatch1, keyEnd1, 1) id, _ := w.Watch(0, keyWatch1, keyEnd1, 1)
if _, ok := idm[id]; ok { if _, ok := idm[id]; ok {
t.Errorf("#%d: id %d exists", i, id) t.Errorf("#%d: id %d exists", i, id)
} }
@ -163,14 +198,14 @@ func TestWatcherWatchWrongRange(t *testing.T) {
w := s.NewWatchStream() w := s.NewWatchStream()
defer w.Close() defer w.Close()
if id := w.Watch([]byte("foa"), []byte("foa"), 1); id != -1 { if _, err := w.Watch(0, []byte("foa"), []byte("foa"), 1); err != ErrEmptyWatcherRange {
t.Fatalf("key == end range given; id expected -1, got %d", id) t.Fatalf("key == end range given; expected ErrEmptyWatcherRange, got %+v", err)
} }
if id := w.Watch([]byte("fob"), []byte("foa"), 1); id != -1 { if _, err := w.Watch(0, []byte("fob"), []byte("foa"), 1); err != ErrEmptyWatcherRange {
t.Fatalf("key > end range given; id expected -1, got %d", id) t.Fatalf("key > end range given; expected ErrEmptyWatcherRange, got %+v", err)
} }
// watch request with 'WithFromKey' has empty-byte range end // watch request with 'WithFromKey' has empty-byte range end
if id := w.Watch([]byte("foo"), []byte{}, 1); id != 0 { if id, _ := w.Watch(0, []byte("foo"), []byte{}, 1); id != 0 {
t.Fatalf("\x00 is range given; id expected 0, got %d", id) t.Fatalf("\x00 is range given; id expected 0, got %d", id)
} }
} }
@ -192,7 +227,7 @@ func TestWatchDeleteRange(t *testing.T) {
w := s.NewWatchStream() w := s.NewWatchStream()
from, to := []byte(testKeyPrefix), []byte(fmt.Sprintf("%s_%d", testKeyPrefix, 99)) from, to := []byte(testKeyPrefix), []byte(fmt.Sprintf("%s_%d", testKeyPrefix, 99))
w.Watch(from, to, 0) w.Watch(0, from, to, 0)
s.DeleteRange(from, to) s.DeleteRange(from, to)
@ -222,7 +257,7 @@ func TestWatchStreamCancelWatcherByID(t *testing.T) {
w := s.NewWatchStream() w := s.NewWatchStream()
defer w.Close() defer w.Close()
id := w.Watch([]byte("foo"), nil, 0) id, _ := w.Watch(0, []byte("foo"), nil, 0)
tests := []struct { tests := []struct {
cancelID WatchID cancelID WatchID
@ -284,7 +319,7 @@ func TestWatcherRequestProgress(t *testing.T) {
default: default:
} }
id := w.Watch(notTestKey, nil, 1) id, _ := w.Watch(0, notTestKey, nil, 1)
w.RequestProgress(id) w.RequestProgress(id)
select { select {
case resp := <-w.Chan(): case resp := <-w.Chan():
@ -295,7 +330,7 @@ func TestWatcherRequestProgress(t *testing.T) {
s.syncWatchers() s.syncWatchers()
w.RequestProgress(id) w.RequestProgress(id)
wrs := WatchResponse{WatchID: 0, Revision: 2} wrs := WatchResponse{WatchID: id, Revision: 2}
select { select {
case resp := <-w.Chan(): case resp := <-w.Chan():
if !reflect.DeepEqual(resp, wrs) { if !reflect.DeepEqual(resp, wrs) {
@ -318,7 +353,7 @@ func TestWatcherWatchWithFilter(t *testing.T) {
return e.Type == mvccpb.PUT return e.Type == mvccpb.PUT
} }
w.Watch([]byte("foo"), nil, 0, filterPut) w.Watch(0, []byte("foo"), nil, 0, filterPut)
done := make(chan struct{}) done := make(chan struct{})
go func() { go func() {

View File

@ -10,8 +10,8 @@ if ! [[ "$0" =~ scripts/genproto.sh ]]; then
exit 255 exit 255
fi fi
if [[ $(protoc --version | cut -f2 -d' ') != "3.5.0" ]]; then if [[ $(protoc --version | cut -f2 -d' ') != "3.5.1" ]]; then
echo "could not find protoc 3.5.0, is it installed + in PATH?" echo "could not find protoc 3.5.1, is it installed + in PATH?"
exit 255 exit 255
fi fi