Browse Source

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

*: allow user-provided watch ID to mvcc
Gyuho Lee 8 years ago
parent
commit
65464838ba

+ 1 - 0
Documentation/dev-guide/api_reference_v3.md

@@ -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 |
 | 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 |
+| 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 |
 
 
 

+ 5 - 0
Documentation/dev-guide/apispec/swagger/rpc.swagger.json

@@ -2283,6 +2283,11 @@
           "description": "start_revision is an optional revision to watch from (inclusive). No start_revision is \"now\".",
           "type": "string",
           "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"
         }
       }
     },

+ 4 - 1
clientv3/watch.go

@@ -367,7 +367,8 @@ func (w *watcher) closeStream(wgs *watchGrpcStream) {
 }
 
 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
 		close(ws.recvc)
 		return
@@ -453,6 +454,7 @@ func (w *watchGrpcStream) run() {
 		// Watch() requested
 		case wreq := <-w.reqc:
 			outc := make(chan WatchResponse, 1)
+			// TODO: pass custom watch ID?
 			ws := &watcherStream{
 				initReq: *wreq,
 				id:      -1,
@@ -553,6 +555,7 @@ func (w *watchGrpcStream) dispatchEvent(pbresp *pb.WatchResponse) bool {
 	for i, ev := range pbresp.Events {
 		events[i] = (*Event)(ev)
 	}
+	// TODO: return watch ID?
 	wr := &WatchResponse{
 		Header:          *pbresp.Header,
 		Events:          events,

+ 7 - 4
etcdserver/api/v3rpc/watch.go

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

+ 189 - 149
etcdserver/etcdserverpb/rpc.pb.go

@@ -1620,6 +1620,12 @@ type WatchCreateRequest struct {
 	// 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.
 	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{} }
@@ -1669,6 +1675,13 @@ func (m *WatchCreateRequest) GetPrevKv() bool {
 	return false
 }
 
+func (m *WatchCreateRequest) GetWatchId() int64 {
+	if m != nil {
+		return m.WatchId
+	}
+	return 0
+}
+
 type WatchCancelRequest struct {
 	// 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"`
@@ -5919,6 +5932,11 @@ func (m *WatchCreateRequest) MarshalTo(dAtA []byte) (int, error) {
 		}
 		i++
 	}
+	if m.WatchId != 0 {
+		dAtA[i] = 0x38
+		i++
+		i = encodeVarintRpc(dAtA, i, uint64(m.WatchId))
+	}
 	return i, nil
 }
 
@@ -8408,6 +8426,9 @@ func (m *WatchCreateRequest) Size() (n int) {
 	if m.PrevKv {
 		n += 2
 	}
+	if m.WatchId != 0 {
+		n += 1 + sovRpc(uint64(m.WatchId))
+	}
 	return n
 }
 
@@ -12309,6 +12330,25 @@ func (m *WatchCreateRequest) Unmarshal(dAtA []byte) error {
 				}
 			}
 			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:
 			iNdEx = preIndex
 			skippy, err := skipRpc(dAtA[iNdEx:])
@@ -18431,13 +18471,13 @@ var (
 func init() { proto.RegisterFile("rpc.proto", fileDescriptorRpc) }
 
 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,
 	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,
 	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,
-	0xf8, 0x07, 0x04, 0xf9, 0x0b, 0x41, 0x5e, 0x12, 0x20, 0x7f, 0xe0, 0xc0, 0xe7, 0xbc, 0x9c, 0x5f,
+	0xae, 0xb4, 0x31, 0x12, 0xc0, 0x71, 0x82, 0xbc, 0xe4, 0x25, 0x06, 0x82, 0x24, 0xaf, 0x41, 0x60,
+	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,
 	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,
@@ -18473,7 +18513,7 @@ var fileDescriptorRpc = []byte{
 	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,
 	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,
 	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,
@@ -18482,7 +18522,7 @@ var fileDescriptorRpc = []byte{
 	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,
 	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,
 	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,
@@ -18504,7 +18544,7 @@ var fileDescriptorRpc = []byte{
 	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,
 	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,
 	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,
@@ -18519,147 +18559,147 @@ var fileDescriptorRpc = []byte{
 	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,
 	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,
-	0xdc, 0x18, 0x45, 0xd8, 0x12, 0x2a, 0xb1, 0x6c, 0x8c, 0x3c, 0x4a, 0xcb, 0xea, 0x58, 0x6a, 0xa9,
-	0x04, 0x26, 0xca, 0x30, 0x5b, 0xa4, 0xeb, 0x89, 0x4a, 0x25, 0x8b, 0x62, 0x8c, 0xaa, 0x28, 0x95,
-	0xf0, 0x7c, 0xda, 0x3f, 0x7e, 0xf0, 0x5c, 0xf2, 0x75, 0x0a, 0x50, 0xd4, 0x86, 0xef, 0x7b, 0x22,
-	0xbb, 0x0f, 0x65, 0xc7, 0x35, 0xec, 0xc8, 0xde, 0x28, 0x31, 0xaa, 0x97, 0xa0, 0x1f, 0xc2, 0x74,
-	0xdf, 0xb6, 0x8e, 0x6d, 0xe2, 0x38, 0x07, 0xa6, 0xe5, 0x76, 0x8e, 0xce, 0xc5, 0xa1, 0xb6, 0x2c,
-	0xc9, 0xdb, 0x8c, 0x8a, 0x1a, 0x90, 0x3b, 0xea, 0x74, 0x5d, 0x62, 0x3b, 0xd5, 0xcc, 0x42, 0x7a,
-	0xb1, 0xbc, 0xfc, 0xe4, 0x22, 0xaf, 0x2d, 0x7d, 0xc8, 0xf8, 0x9b, 0xe7, 0x7d, 0xa2, 0xcb, 0xb1,
-	0xea, 0x41, 0x31, 0x1b, 0x38, 0x28, 0xde, 0x07, 0xf0, 0xf9, 0x69, 0xaa, 0xdd, 0xde, 0xd9, 0xdd,
-	0x6f, 0x56, 0x26, 0x50, 0x11, 0xa6, 0xb6, 0x77, 0xd6, 0x1a, 0x5b, 0x0d, 0x9a, 0x97, 0x71, 0x5d,
-	0xfa, 0x46, 0xf5, 0x21, 0x9a, 0x83, 0xa9, 0xd7, 0x94, 0x2a, 0xef, 0xdb, 0x69, 0x3d, 0xc7, 0xda,
-	0x1b, 0x6d, 0xfc, 0x4f, 0x29, 0x28, 0x89, 0x5d, 0x30, 0xd6, 0x56, 0x54, 0x55, 0xa4, 0x02, 0x2a,
-	0xe8, 0xa9, 0x94, 0xef, 0x8e, 0xb6, 0x38, 0xfc, 0xca, 0x26, 0xcd, 0x0d, 0x7c, 0xb1, 0x49, 0x5b,
-	0xb8, 0xd5, 0x6b, 0xc7, 0x86, 0x6f, 0x26, 0x36, 0x7c, 0xd1, 0x5d, 0x28, 0x79, 0xbb, 0xcd, 0x70,
-	0x44, 0xad, 0xcd, 0xeb, 0x45, 0xb9, 0x91, 0x28, 0x0d, 0xdd, 0x87, 0x2c, 0x19, 0x12, 0xd3, 0x75,
-	0xaa, 0x05, 0x96, 0x75, 0x4b, 0xf2, 0xfc, 0xdb, 0xa0, 0x54, 0x5d, 0x74, 0xe2, 0x3f, 0x82, 0x2b,
-	0xec, 0x9e, 0xf1, 0xc2, 0x36, 0x4c, 0xf5, 0x42, 0xd4, 0x6c, 0x6e, 0x09, 0xd7, 0xd1, 0x4f, 0x54,
-	0x86, 0xd4, 0xc6, 0x9a, 0x98, 0x68, 0x6a, 0x63, 0x0d, 0x7f, 0xa1, 0x01, 0x52, 0xc7, 0x8d, 0xe5,
-	0xcb, 0x90, 0x70, 0xa9, 0x3e, 0xed, 0xab, 0x9f, 0x85, 0x0c, 0xb1, 0x6d, 0xcb, 0x66, 0x5e, 0xcb,
-	0xeb, 0xbc, 0x81, 0xef, 0x09, 0x1b, 0x74, 0x32, 0xb4, 0x4e, 0xbd, 0xc0, 0xe0, 0xd2, 0x34, 0xcf,
-	0xd4, 0x4d, 0x98, 0x09, 0x70, 0x8d, 0x95, 0xfd, 0x1f, 0xc2, 0x55, 0x26, 0x6c, 0x93, 0x90, 0xfe,
-	0x4a, 0xb7, 0x33, 0x4c, 0xd4, 0xda, 0x87, 0x6b, 0x61, 0xc6, 0x9f, 0xd6, 0x47, 0xf8, 0x4f, 0x85,
-	0xc6, 0x66, 0xa7, 0x47, 0x9a, 0xd6, 0x56, 0xb2, 0x6d, 0x34, 0x3b, 0x9e, 0x92, 0x73, 0x47, 0x94,
-	0x49, 0xf6, 0x8d, 0xff, 0x43, 0x83, 0xeb, 0x91, 0xe1, 0x3f, 0xf1, 0xaa, 0xce, 0x03, 0x1c, 0xd3,
-	0xed, 0x43, 0xda, 0xb4, 0x83, 0xdf, 0xd0, 0x15, 0x8a, 0x67, 0x27, 0x4d, 0x30, 0x45, 0x61, 0xe7,
-	0xac, 0x58, 0x73, 0xf6, 0xc7, 0x91, 0x35, 0xe6, 0x16, 0x14, 0x18, 0x61, 0xcf, 0x35, 0xdc, 0x81,
-	0x13, 0x59, 0x8c, 0xbf, 0x11, 0x5b, 0x40, 0x0e, 0x1a, 0x6b, 0x5e, 0xef, 0x40, 0x96, 0x1d, 0x4e,
-	0xe5, 0xd1, 0x2c, 0x74, 0x1b, 0x50, 0xec, 0xd0, 0x05, 0x23, 0x3e, 0x81, 0xec, 0x4b, 0x86, 0xe8,
-	0x29, 0x96, 0x4d, 0xca, 0xa5, 0x30, 0x8d, 0x1e, 0xc7, 0x19, 0xf2, 0x3a, 0xfb, 0x66, 0x27, 0x19,
-	0x42, 0xec, 0x7d, 0x7d, 0x8b, 0x9f, 0x98, 0xf2, 0xba, 0xd7, 0xa6, 0x2e, 0x6b, 0x75, 0x3b, 0xc4,
-	0x74, 0x59, 0xef, 0x24, 0xeb, 0x55, 0x28, 0x78, 0x09, 0x2a, 0x5c, 0xd3, 0x4a, 0xbb, 0xad, 0x9c,
-	0x48, 0x3c, 0x79, 0x5a, 0x50, 0x1e, 0xfe, 0x46, 0x83, 0x2b, 0xca, 0x80, 0xb1, 0x1c, 0xf3, 0x14,
-	0xb2, 0x1c, 0xb7, 0x14, 0xc5, 0x6f, 0x36, 0x38, 0x8a, 0xab, 0xd1, 0x05, 0x0f, 0x5a, 0x82, 0x1c,
-	0xff, 0x92, 0xc7, 0xc2, 0x78, 0x76, 0xc9, 0x84, 0xef, 0xc3, 0x8c, 0x20, 0x91, 0x9e, 0x15, 0xb7,
-	0xb7, 0x99, 0x43, 0xf1, 0xe7, 0x30, 0x1b, 0x64, 0x1b, 0x6b, 0x4a, 0x8a, 0x91, 0xa9, 0xcb, 0x18,
-	0xb9, 0x22, 0x8d, 0xdc, 0xef, 0xb7, 0x95, 0x5a, 0x1d, 0x5e, 0x75, 0x75, 0x45, 0x52, 0xa1, 0x15,
-	0xf1, 0x26, 0x20, 0x45, 0xfc, 0xac, 0x13, 0x98, 0x91, 0xdb, 0x61, 0xab, 0xe3, 0x78, 0x27, 0xb8,
-	0x37, 0x80, 0x54, 0xe2, 0xcf, 0x6d, 0xd0, 0x1a, 0x39, 0xb2, 0x8d, 0xe3, 0x1e, 0xf1, 0xea, 0x13,
-	0x3d, 0xcf, 0xab, 0xc4, 0xb1, 0x32, 0x7a, 0x1d, 0xae, 0xbc, 0xb4, 0x86, 0x34, 0x35, 0x50, 0xaa,
-	0x1f, 0x32, 0xfc, 0x3e, 0xe7, 0x2d, 0x9b, 0xd7, 0xa6, 0xca, 0xd5, 0x01, 0x63, 0x29, 0xff, 0x5f,
-	0x0d, 0x8a, 0x2b, 0x5d, 0xc3, 0xee, 0x49, 0xc5, 0xef, 0x43, 0x96, 0xdf, 0x52, 0x04, 0x30, 0xf0,
-	0x20, 0x28, 0x46, 0xe5, 0xe5, 0x8d, 0x15, 0x7e, 0xa7, 0x11, 0xa3, 0xa8, 0xe1, 0xe2, 0xed, 0x60,
-	0x2d, 0xf4, 0x96, 0xb0, 0x86, 0xde, 0x82, 0x8c, 0x41, 0x87, 0xb0, 0x14, 0x5c, 0x0e, 0xdf, 0x0f,
-	0x99, 0x34, 0x76, 0x38, 0xe3, 0x5c, 0xf8, 0x3d, 0x28, 0x28, 0x1a, 0xe8, 0x0d, 0xf8, 0x45, 0x43,
-	0x1c, 0xc0, 0x56, 0x56, 0x9b, 0x1b, 0xaf, 0xf8, 0xc5, 0xb8, 0x0c, 0xb0, 0xd6, 0xf0, 0xda, 0x29,
-	0xfc, 0xa9, 0x18, 0x25, 0xf2, 0x9d, 0x6a, 0x8f, 0x96, 0x64, 0x4f, 0xea, 0x52, 0xf6, 0x9c, 0x41,
-	0x49, 0x4c, 0x7f, 0xdc, 0xf4, 0xcd, 0xe4, 0x25, 0xa4, 0x6f, 0xc5, 0x78, 0x5d, 0x30, 0xe2, 0x69,
-	0x28, 0x89, 0x84, 0x2e, 0xf6, 0xdf, 0xff, 0x68, 0x50, 0x96, 0x94, 0x71, 0x01, 0x4c, 0x89, 0xbd,
-	0xf0, 0x0a, 0xe0, 0x21, 0x2f, 0xd7, 0x20, 0xdb, 0x3e, 0xdc, 0xeb, 0xbc, 0x91, 0x60, 0xb3, 0x68,
-	0x51, 0x7a, 0x97, 0xeb, 0xe1, 0x2f, 0x3e, 0xa2, 0x45, 0x6f, 0xe1, 0xb6, 0x71, 0xe4, 0x6e, 0x98,
-	0x6d, 0x72, 0xc6, 0xce, 0x8d, 0x93, 0xba, 0x4f, 0x60, 0x97, 0x52, 0xf1, 0x32, 0xc4, 0x0e, 0x8b,
-	0xea, 0x4b, 0xd1, 0x0c, 0x5c, 0x59, 0x19, 0xb8, 0x27, 0x0d, 0xd3, 0x38, 0xec, 0xca, 0x8c, 0x45,
-	0xcb, 0x2c, 0x25, 0xae, 0x75, 0x1c, 0x95, 0xda, 0x80, 0x19, 0x4a, 0x25, 0xa6, 0xdb, 0x69, 0x29,
-	0xe9, 0x4d, 0x16, 0x31, 0x2d, 0x54, 0xc4, 0x0c, 0xc7, 0x79, 0x6d, 0xd9, 0x6d, 0x31, 0x35, 0xaf,
-	0x8d, 0xd7, 0xb8, 0xf0, 0x7d, 0x27, 0x50, 0xa6, 0xbe, 0xaf, 0x94, 0x45, 0x5f, 0xca, 0x0b, 0xe2,
-	0x8e, 0x90, 0x82, 0x9f, 0xc0, 0x55, 0xc9, 0x29, 0xc0, 0xbd, 0x11, 0xcc, 0x3b, 0x70, 0x4b, 0x32,
-	0xaf, 0x9e, 0xd0, 0xdb, 0xd3, 0xae, 0x50, 0xf8, 0x43, 0xed, 0x7c, 0x0e, 0x55, 0xcf, 0x4e, 0x76,
-	0x58, 0xb6, 0xba, 0xaa, 0x01, 0x03, 0x47, 0xec, 0x99, 0xbc, 0xce, 0xbe, 0x29, 0xcd, 0xb6, 0xba,
-	0xde, 0x91, 0x80, 0x7e, 0xe3, 0x55, 0x98, 0x93, 0x32, 0xc4, 0x31, 0x36, 0x28, 0x24, 0x62, 0x50,
-	0x9c, 0x10, 0xe1, 0x30, 0x3a, 0x74, 0xb4, 0xdb, 0x55, 0xce, 0xa0, 0x6b, 0x99, 0x4c, 0x4d, 0x91,
-	0x79, 0x95, 0xef, 0x08, 0x6a, 0x98, 0x5a, 0x31, 0x04, 0x99, 0x0a, 0x50, 0xc9, 0x62, 0x21, 0x28,
-	0x39, 0xb2, 0x10, 0x11, 0xd1, 0x9f, 0xc1, 0xbc, 0x67, 0x04, 0xf5, 0xdb, 0x2e, 0xb1, 0x7b, 0x1d,
-	0xc7, 0x51, 0xe0, 0xa0, 0xb8, 0x89, 0x3f, 0x80, 0xc9, 0x3e, 0x11, 0x39, 0xa5, 0xb0, 0x8c, 0x96,
-	0xf8, 0xfb, 0xed, 0x92, 0x32, 0x98, 0xf5, 0xe3, 0x36, 0xdc, 0x96, 0xd2, 0xb9, 0x47, 0x63, 0xc5,
-	0x87, 0x8d, 0x92, 0xb7, 0x6e, 0xee, 0xd6, 0xe8, 0xad, 0x3b, 0xcd, 0xd7, 0xde, 0x83, 0x28, 0x3f,
-	0xe2, 0x8e, 0x94, 0xb1, 0x35, 0x56, 0xad, 0xd8, 0xe4, 0x3e, 0xf5, 0x42, 0x72, 0x2c, 0x61, 0x87,
-	0x30, 0x1b, 0x8c, 0xe4, 0xb1, 0xd2, 0xd8, 0x2c, 0x64, 0x5c, 0xeb, 0x94, 0xc8, 0x24, 0xc6, 0x1b,
-	0xd2, 0x60, 0x2f, 0xcc, 0xc7, 0x32, 0xd8, 0xf0, 0x85, 0xb1, 0x2d, 0x39, 0xae, 0xbd, 0x74, 0x35,
-	0xe5, 0xe1, 0x8b, 0x37, 0xf0, 0x36, 0x5c, 0x0b, 0xa7, 0x89, 0xb1, 0x4c, 0x7e, 0xc5, 0x37, 0x70,
-	0x5c, 0x26, 0x19, 0x4b, 0xee, 0xc7, 0x7e, 0x32, 0x50, 0x12, 0xca, 0x58, 0x22, 0x75, 0xa8, 0xc5,
-	0xe5, 0x97, 0x1f, 0x63, 0xbf, 0x7a, 0xe9, 0x66, 0x2c, 0x61, 0x8e, 0x2f, 0x6c, 0xfc, 0xe5, 0xf7,
-	0x73, 0x44, 0x7a, 0x64, 0x8e, 0x10, 0x41, 0xe2, 0x67, 0xb1, 0x9f, 0x60, 0xd3, 0x09, 0x1d, 0x7e,
-	0x02, 0x1d, 0x57, 0x07, 0xad, 0x21, 0x9e, 0x0e, 0xd6, 0x90, 0x1b, 0x5b, 0x4d, 0xbb, 0x63, 0x2d,
-	0xc6, 0x27, 0x7e, 0xee, 0x8c, 0x64, 0xe6, 0xb1, 0x04, 0x7f, 0x0a, 0x0b, 0xc9, 0x49, 0x79, 0x1c,
-	0xc9, 0x8f, 0xeb, 0x90, 0xf7, 0x0e, 0x94, 0xca, 0x6f, 0x1f, 0x0a, 0x90, 0xdb, 0xde, 0xd9, 0xdb,
-	0x5d, 0x59, 0x6d, 0xf0, 0x1f, 0x3f, 0xac, 0xee, 0xe8, 0xfa, 0xfe, 0x6e, 0xb3, 0x92, 0x5a, 0xfe,
-	0x6d, 0x1a, 0x52, 0x9b, 0xaf, 0xd0, 0x9f, 0x43, 0x86, 0xbf, 0x04, 0x8e, 0x78, 0xfe, 0xad, 0x8d,
-	0x7a, 0xec, 0xc4, 0x37, 0xbe, 0xf8, 0xff, 0x5f, 0x7d, 0x95, 0xba, 0x8a, 0x2b, 0xf5, 0xe1, 0xbb,
-	0x87, 0xc4, 0x35, 0xea, 0xa7, 0xc3, 0x3a, 0xab, 0x0f, 0xcf, 0xb4, 0xc7, 0x68, 0x1f, 0xd2, 0xbb,
-	0x03, 0x17, 0x25, 0x3e, 0x0d, 0xd7, 0x92, 0xdf, 0x40, 0xf1, 0x1c, 0x13, 0x3c, 0x83, 0xcb, 0x8a,
-	0xe0, 0xfe, 0xc0, 0xa5, 0x62, 0x07, 0x50, 0x50, 0x5f, 0x31, 0x2f, 0x7c, 0x33, 0xae, 0x5d, 0xfc,
-	0x42, 0x8a, 0xef, 0x30, 0x75, 0x37, 0xf0, 0x35, 0x45, 0x1d, 0x7f, 0x6b, 0x55, 0x67, 0xd3, 0x3c,
-	0x33, 0x51, 0xe2, 0xab, 0x72, 0x2d, 0xf9, 0xe1, 0x34, 0x76, 0x36, 0xee, 0x99, 0x49, 0xc5, 0x9a,
-	0xe2, 0xdd, 0xb4, 0xe5, 0xa2, 0xdb, 0x31, 0xef, 0x66, 0xea, 0x0b, 0x51, 0x6d, 0x21, 0x99, 0x41,
-	0x28, 0x5a, 0x60, 0x8a, 0x6a, 0xf8, 0xaa, 0xa2, 0xa8, 0xe5, 0xb1, 0x3d, 0xd3, 0x1e, 0x2f, 0x1f,
-	0x43, 0x86, 0x21, 0xc4, 0xe8, 0x2f, 0xe4, 0x47, 0x2d, 0x06, 0xdb, 0x4e, 0x58, 0xfc, 0x00, 0xb6,
-	0x8c, 0xab, 0x4c, 0x19, 0xc2, 0x25, 0xa9, 0x8c, 0x61, 0xc4, 0xcf, 0xb4, 0xc7, 0x8b, 0xda, 0xdb,
-	0xda, 0xf2, 0x6f, 0x26, 0x21, 0xc3, 0xe0, 0x22, 0x64, 0x01, 0xf8, 0x68, 0x6a, 0x78, 0x96, 0x11,
-	0x7c, 0x36, 0x3c, 0xcb, 0x28, 0x10, 0x8b, 0xe7, 0x99, 0xe2, 0x2a, 0x9e, 0x91, 0x8a, 0x19, 0x12,
-	0x55, 0x67, 0xe0, 0x1a, 0xf5, 0xe9, 0x50, 0x00, 0x66, 0x3c, 0xcc, 0x50, 0x9c, 0xc0, 0x00, 0xaa,
-	0x1a, 0xde, 0x21, 0x31, 0x88, 0x2a, 0xc6, 0x4c, 0xe7, 0x4d, 0x7c, 0x5d, 0xf1, 0x2c, 0x57, 0x6b,
-	0x33, 0x46, 0xaa, 0xf7, 0xef, 0x34, 0x28, 0x07, 0x71, 0x51, 0x74, 0x37, 0x46, 0x72, 0x18, 0x5e,
-	0xad, 0xdd, 0x1b, 0xcd, 0x94, 0x64, 0x01, 0x57, 0x7f, 0x4a, 0x48, 0xdf, 0xa0, 0x8c, 0xc2, 0xf1,
-	0xe8, 0x1f, 0x34, 0x98, 0x0e, 0x81, 0x9d, 0x28, 0x4e, 0x43, 0x04, 0x4a, 0xad, 0xdd, 0xbf, 0x80,
-	0x4b, 0x18, 0xf2, 0x80, 0x19, 0xb2, 0x80, 0x6f, 0x44, 0x5c, 0xe1, 0x76, 0x7a, 0xc4, 0xb5, 0x84,
-	0x31, 0xde, 0x32, 0x70, 0x60, 0x32, 0x76, 0x19, 0x02, 0x40, 0x67, 0xec, 0x32, 0x04, 0x51, 0xcd,
-	0x11, 0xcb, 0xc0, 0xd1, 0x48, 0xba, 0xc5, 0x7f, 0x97, 0x86, 0xdc, 0x2a, 0xff, 0x05, 0x22, 0x72,
-	0x20, 0xef, 0x21, 0x80, 0x68, 0x3e, 0x0e, 0x8d, 0xf1, 0x6f, 0x0b, 0xb5, 0xdb, 0x89, 0xfd, 0x42,
-	0xfb, 0x7d, 0xa6, 0xfd, 0x36, 0xae, 0x49, 0xed, 0xe2, 0x87, 0x8e, 0x75, 0x7e, 0xed, 0xaf, 0x1b,
-	0xed, 0x36, 0x9d, 0xf8, 0xdf, 0x42, 0x51, 0x85, 0xe9, 0xd0, 0x9d, 0x58, 0x14, 0x48, 0x45, 0xfa,
-	0x6a, 0x78, 0x14, 0x8b, 0xd0, 0xbe, 0xc8, 0xb4, 0x63, 0x7c, 0x2b, 0x41, 0xbb, 0xcd, 0xd8, 0x03,
-	0x06, 0x70, 0x98, 0x2d, 0xde, 0x80, 0x00, 0x8a, 0x17, 0x6f, 0x40, 0x10, 0xa5, 0xbb, 0xd0, 0x80,
-	0x01, 0x63, 0xa7, 0x06, 0xbc, 0x06, 0xf0, 0x41, 0x35, 0x14, 0xeb, 0x57, 0xe5, 0xea, 0x14, 0x0e,
-	0xf9, 0x28, 0x1e, 0x17, 0xdd, 0x73, 0x21, 0xd5, 0xdd, 0x8e, 0x43, 0x43, 0x7f, 0xf9, 0x9b, 0x2c,
-	0x14, 0x5e, 0x1a, 0x1d, 0xd3, 0x25, 0xa6, 0x61, 0xb6, 0x08, 0x3a, 0x82, 0x0c, 0x2b, 0x8d, 0xe1,
-	0x2c, 0xa7, 0x62, 0x4d, 0xe1, 0x2c, 0x17, 0x00, 0x62, 0xf0, 0x3d, 0xa6, 0x79, 0x1e, 0xcf, 0x49,
-	0xcd, 0x3d, 0x5f, 0x7c, 0x9d, 0x61, 0x28, 0x74, 0xc2, 0x7f, 0x09, 0x59, 0x01, 0xcf, 0x87, 0x84,
-	0x05, 0xb0, 0x95, 0xda, 0xcd, 0xf8, 0xce, 0xa4, 0xed, 0xa5, 0xaa, 0x72, 0x18, 0x2f, 0xd5, 0xf5,
-	0x06, 0xc0, 0x07, 0x08, 0xc3, 0xce, 0x8d, 0xe0, 0x89, 0xb5, 0x85, 0x64, 0x06, 0xa1, 0xf7, 0x11,
-	0xd3, 0x7b, 0x17, 0xcf, 0xc7, 0xe9, 0x6d, 0x7b, 0xfc, 0x54, 0xf7, 0x21, 0x4c, 0xae, 0x1b, 0xce,
-	0x09, 0x0a, 0x15, 0x3b, 0xe5, 0x47, 0x03, 0xb5, 0x5a, 0x5c, 0x97, 0xd0, 0x74, 0x97, 0x69, 0xba,
-	0x85, 0xab, 0x71, 0x9a, 0x4e, 0x0c, 0x87, 0x56, 0x0f, 0x74, 0x02, 0x59, 0xfe, 0x3b, 0x82, 0xb0,
-	0x2f, 0x03, 0xbf, 0x45, 0x08, 0xfb, 0x32, 0xf8, 0xd3, 0x83, 0xcb, 0x69, 0x72, 0x61, 0x4a, 0x3e,
-	0xde, 0xa3, 0x5b, 0xa1, 0xa5, 0x09, 0x3e, 0xf4, 0xd7, 0xe6, 0x93, 0xba, 0x85, 0xbe, 0x87, 0x4c,
-	0xdf, 0x1d, 0x7c, 0x33, 0x76, 0xed, 0x04, 0xf7, 0x33, 0xed, 0xf1, 0xdb, 0x1a, 0x2d, 0x13, 0xe0,
-	0x83, 0xac, 0x91, 0xe8, 0x08, 0xe3, 0xb5, 0x91, 0xe8, 0x88, 0xe0, 0xb3, 0x78, 0x99, 0x29, 0x7f,
-	0x8a, 0x1f, 0xc6, 0x29, 0x77, 0x6d, 0xc3, 0x74, 0x8e, 0x88, 0xfd, 0x16, 0x07, 0xd3, 0x9c, 0x93,
-	0x4e, 0x9f, 0x46, 0xca, 0xef, 0xa7, 0x61, 0x92, 0x9e, 0x47, 0x69, 0x79, 0xf6, 0xaf, 0xf1, 0x61,
-	0x6b, 0x22, 0xe0, 0x59, 0xd8, 0x9a, 0x28, 0x02, 0x10, 0x2d, 0xcf, 0xec, 0xb7, 0xe6, 0x84, 0x31,
-	0x51, 0xaf, 0x3b, 0x50, 0x50, 0xee, 0xfa, 0x28, 0x46, 0x60, 0x10, 0x99, 0x0b, 0xd7, 0x85, 0x18,
-	0xa0, 0x00, 0xdf, 0x66, 0x3a, 0xe7, 0xf0, 0x6c, 0x40, 0x67, 0x9b, 0x73, 0x51, 0xa5, 0x7f, 0x0d,
-	0x45, 0x15, 0x13, 0x40, 0x31, 0x32, 0x43, 0xc8, 0x5f, 0x38, 0x25, 0xc6, 0x41, 0x0a, 0xd1, 0xec,
-	0xe0, 0xfd, 0xae, 0x5e, 0xb2, 0x52, 0xe5, 0x7d, 0xc8, 0x09, 0xa0, 0x20, 0x6e, 0xb6, 0x41, 0xa8,
-	0x30, 0x6e, 0xb6, 0x21, 0x94, 0x21, 0x7a, 0xcc, 0x63, 0x5a, 0xe9, 0x7d, 0x48, 0x96, 0x20, 0xa1,
-	0xf1, 0x05, 0x71, 0x93, 0x34, 0xfa, 0xd8, 0x57, 0x92, 0x46, 0xe5, 0x2e, 0x3a, 0x4a, 0xe3, 0x31,
-	0x71, 0x45, 0x2c, 0xc9, 0x7b, 0x1e, 0x4a, 0x10, 0xa8, 0xa6, 0x7c, 0x3c, 0x8a, 0x25, 0xe9, 0x54,
-	0xee, 0x2b, 0x15, 0xf9, 0x1e, 0x7d, 0x0e, 0xe0, 0x43, 0x1a, 0xe1, 0xd3, 0x56, 0x2c, 0x2e, 0x1a,
-	0x3e, 0x6d, 0xc5, 0xa3, 0x22, 0xd1, 0xfc, 0xe1, 0xeb, 0xe6, 0x17, 0x03, 0xaa, 0xfd, 0x5f, 0x34,
-	0x40, 0x51, 0x04, 0x04, 0x3d, 0x89, 0xd7, 0x10, 0x8b, 0xb8, 0xd6, 0x9e, 0x5e, 0x8e, 0x39, 0xa9,
-	0x44, 0xf8, 0x66, 0xb5, 0xd8, 0x88, 0xfe, 0x6b, 0x6a, 0xd8, 0x97, 0x1a, 0x94, 0x02, 0x10, 0x0a,
-	0x7a, 0x90, 0xb0, 0xc6, 0x21, 0xd0, 0xb6, 0xf6, 0xf0, 0x42, 0xbe, 0xa4, 0x93, 0x98, 0xb2, 0x23,
-	0xe4, 0x41, 0xfc, 0x1f, 0x35, 0x28, 0x07, 0x61, 0x17, 0x94, 0x20, 0x3f, 0x02, 0xfc, 0xd6, 0x16,
-	0x2f, 0x66, 0xbc, 0x78, 0xa9, 0xfc, 0xb3, 0x79, 0x1f, 0x72, 0x02, 0xac, 0x89, 0x0b, 0x88, 0x20,
-	0x6c, 0x1c, 0x17, 0x10, 0x21, 0xa4, 0x27, 0x21, 0x20, 0x6c, 0xab, 0x4b, 0x94, 0x10, 0x14, 0x88,
-	0x4e, 0x92, 0xc6, 0xd1, 0x21, 0x18, 0x82, 0x83, 0x46, 0x69, 0xf4, 0x43, 0x50, 0xc2, 0x39, 0x28,
-	0x41, 0xe0, 0x05, 0x21, 0x18, 0x46, 0x83, 0x12, 0x42, 0x90, 0x29, 0x55, 0x42, 0xd0, 0x07, 0x5f,
-	0xe2, 0x42, 0x30, 0x82, 0x88, 0xc7, 0x85, 0x60, 0x14, 0xbf, 0x49, 0x58, 0x57, 0xa6, 0x3b, 0x10,
-	0x82, 0x33, 0x31, 0x58, 0x0d, 0x7a, 0x9a, 0xe0, 0xd0, 0x58, 0xb0, 0xbd, 0xf6, 0xd6, 0x25, 0xb9,
-	0x47, 0xee, 0x7d, 0xbe, 0x14, 0x72, 0xef, 0x7f, 0xad, 0xc1, 0x6c, 0x1c, 0xd6, 0x83, 0x12, 0x74,
-	0x25, 0x00, 0xf5, 0xb5, 0xa5, 0xcb, 0xb2, 0x5f, 0xec, 0x35, 0x2f, 0x1a, 0x9e, 0x57, 0xfe, 0xfb,
-	0xbb, 0x79, 0xed, 0xff, 0xbe, 0x9b, 0xd7, 0x7e, 0xf1, 0xdd, 0xbc, 0xf6, 0xaf, 0xbf, 0x9c, 0x9f,
-	0x38, 0xcc, 0xb2, 0xff, 0xe1, 0xf5, 0xee, 0x1f, 0x02, 0x00, 0x00, 0xff, 0xff, 0x74, 0x55, 0x61,
-	0xe6, 0x68, 0x36, 0x00, 0x00,
+	0xd8, 0x37, 0xfe, 0x56, 0x83, 0xe2, 0x27, 0x86, 0xdb, 0x92, 0x4b, 0x87, 0x36, 0xa0, 0xec, 0x25,
+	0x37, 0x46, 0x11, 0xb6, 0x84, 0x4a, 0x2c, 0x1b, 0x23, 0x8f, 0xd2, 0xb2, 0x3a, 0x96, 0x5a, 0x2a,
+	0x81, 0x89, 0x32, 0xcc, 0x16, 0xe9, 0x7a, 0xa2, 0x52, 0xc9, 0xa2, 0x18, 0xa3, 0x2a, 0x4a, 0x25,
+	0x3c, 0x9f, 0xf6, 0x8f, 0x1f, 0x3c, 0x97, 0x7c, 0x9b, 0x02, 0x14, 0xb5, 0xe1, 0xfb, 0x9e, 0xc8,
+	0xee, 0x43, 0xd9, 0x71, 0x0d, 0x3b, 0xb2, 0x37, 0x4a, 0x8c, 0xea, 0x25, 0xe8, 0x87, 0x30, 0xdd,
+	0xb7, 0xad, 0x63, 0x9b, 0x38, 0xce, 0x81, 0x69, 0xb9, 0x9d, 0xa3, 0x73, 0x71, 0xa8, 0x2d, 0x4b,
+	0xf2, 0x36, 0xa3, 0xa2, 0x06, 0xe4, 0x8e, 0x3a, 0x5d, 0x97, 0xd8, 0x4e, 0x35, 0xb3, 0x90, 0x5e,
+	0x2c, 0x2f, 0x3f, 0xb9, 0xc8, 0x6b, 0x4b, 0x1f, 0x32, 0xfe, 0xe6, 0x79, 0x9f, 0xe8, 0x72, 0xac,
+	0x7a, 0x50, 0xcc, 0x06, 0x0e, 0xcf, 0x73, 0x30, 0xf5, 0x9a, 0x8a, 0xa0, 0x97, 0xe2, 0x1c, 0x3f,
+	0xdb, 0xb1, 0xf6, 0x46, 0x1b, 0xdf, 0x07, 0xf0, 0x45, 0xd1, 0x2c, 0xbc, 0xbd, 0xb3, 0xbb, 0xdf,
+	0xac, 0x4c, 0xa0, 0x22, 0x4c, 0x6d, 0xef, 0xac, 0x35, 0xb6, 0x1a, 0x34, 0x65, 0xe3, 0xba, 0x74,
+	0x9b, 0xea, 0xde, 0x80, 0x5c, 0x2d, 0x28, 0xf7, 0x9f, 0x52, 0x50, 0x12, 0x1b, 0x64, 0xac, 0x5d,
+	0xaa, 0xaa, 0x48, 0x05, 0x54, 0xd0, 0x03, 0x2b, 0xdf, 0x38, 0x6d, 0x71, 0x2e, 0x96, 0x4d, 0x9a,
+	0x36, 0xf8, 0x3e, 0x20, 0x6d, 0xe1, 0x71, 0xaf, 0x1d, 0x1b, 0xd9, 0x99, 0xd8, 0xc8, 0x46, 0x77,
+	0xa1, 0xe4, 0x6d, 0x44, 0xc3, 0x11, 0x65, 0x38, 0xaf, 0x17, 0xe5, 0x1e, 0xa3, 0x34, 0x74, 0x1f,
+	0xb2, 0x64, 0x48, 0x4c, 0xd7, 0xa9, 0x16, 0x58, 0x42, 0x2e, 0xc9, 0xa3, 0x71, 0x83, 0x52, 0x75,
+	0xd1, 0x89, 0xff, 0x08, 0xae, 0xb0, 0x2b, 0xc8, 0x0b, 0xdb, 0x30, 0xd5, 0xbb, 0x52, 0xb3, 0xb9,
+	0x25, 0x5c, 0x47, 0x3f, 0x51, 0x19, 0x52, 0x1b, 0x6b, 0x62, 0xa2, 0xa9, 0x8d, 0x35, 0xfc, 0x85,
+	0x06, 0x48, 0x1d, 0x37, 0x96, 0x2f, 0x43, 0xc2, 0xa5, 0xfa, 0xb4, 0xaf, 0x7e, 0x16, 0x32, 0xc4,
+	0xb6, 0x2d, 0x9b, 0x79, 0x2d, 0xaf, 0xf3, 0x06, 0xbe, 0x27, 0x6c, 0xd0, 0xc9, 0xd0, 0x3a, 0xf5,
+	0x62, 0x86, 0x4b, 0xd3, 0x3c, 0x53, 0x37, 0x61, 0x26, 0xc0, 0x35, 0x56, 0x61, 0x78, 0x08, 0x57,
+	0x99, 0xb0, 0x4d, 0x42, 0xfa, 0x2b, 0xdd, 0xce, 0x30, 0x51, 0x6b, 0x1f, 0xae, 0x85, 0x19, 0x7f,
+	0x5a, 0x1f, 0xe1, 0x3f, 0x15, 0x1a, 0x9b, 0x9d, 0x1e, 0x69, 0x5a, 0x5b, 0xc9, 0xb6, 0xd1, 0xc4,
+	0x79, 0x4a, 0xce, 0x1d, 0x51, 0x41, 0xd9, 0x37, 0xfe, 0x4f, 0x0d, 0xae, 0x47, 0x86, 0xff, 0xc4,
+	0xab, 0x3a, 0x0f, 0x70, 0x4c, 0xb7, 0x0f, 0x69, 0xd3, 0x0e, 0x7e, 0x79, 0x57, 0x28, 0x9e, 0x9d,
+	0x34, 0xf7, 0x14, 0x85, 0x9d, 0xb3, 0x62, 0xcd, 0xd9, 0x1f, 0x47, 0x96, 0x9f, 0x5b, 0x50, 0x60,
+	0x84, 0x3d, 0xd7, 0x70, 0x07, 0x4e, 0x64, 0x31, 0xfe, 0x46, 0x6c, 0x01, 0x39, 0x68, 0xac, 0x79,
+	0xbd, 0x03, 0x59, 0x76, 0x6e, 0x95, 0xa7, 0xb6, 0xd0, 0x45, 0x41, 0xb1, 0x43, 0x17, 0x8c, 0xf8,
+	0x04, 0xb2, 0x2f, 0x19, 0xd8, 0xa7, 0x58, 0x36, 0x29, 0x97, 0xc2, 0x34, 0x7a, 0x1c, 0x82, 0xc8,
+	0xeb, 0xec, 0x9b, 0x1d, 0x72, 0x08, 0xb1, 0xf7, 0xf5, 0x2d, 0x7e, 0x98, 0xca, 0xeb, 0x5e, 0x9b,
+	0xba, 0xac, 0xd5, 0xed, 0x10, 0xd3, 0x65, 0xbd, 0x93, 0xac, 0x57, 0xa1, 0xe0, 0x25, 0xa8, 0x70,
+	0x4d, 0x2b, 0xed, 0xb6, 0x72, 0x58, 0xf1, 0xe4, 0x69, 0x41, 0x79, 0xf8, 0x6b, 0x0d, 0xae, 0x28,
+	0x03, 0xc6, 0x72, 0xcc, 0x53, 0xc8, 0x72, 0x48, 0x53, 0xd4, 0xc5, 0xd9, 0xe0, 0x28, 0xae, 0x46,
+	0x17, 0x3c, 0x68, 0x09, 0x72, 0xfc, 0x4b, 0x9e, 0x18, 0xe3, 0xd9, 0x25, 0x13, 0xbe, 0x0f, 0x33,
+	0x82, 0x44, 0x7a, 0x56, 0xdc, 0xde, 0x66, 0x0e, 0xc5, 0x9f, 0xc3, 0x6c, 0x90, 0x6d, 0xac, 0x29,
+	0x29, 0x46, 0xa6, 0x2e, 0x63, 0xe4, 0x8a, 0x34, 0x72, 0xbf, 0xdf, 0x56, 0xca, 0x78, 0x78, 0xd5,
+	0xd5, 0x15, 0x49, 0x85, 0x56, 0xc4, 0x9b, 0x80, 0x14, 0xf1, 0xb3, 0x4e, 0x60, 0x46, 0x6e, 0x87,
+	0xad, 0x8e, 0xe3, 0x1d, 0xee, 0xde, 0x00, 0x52, 0x89, 0x3f, 0xb7, 0x41, 0x6b, 0xe4, 0xc8, 0x36,
+	0x8e, 0x7b, 0xc4, 0xab, 0x4f, 0xf4, 0xa8, 0xaf, 0x12, 0xc7, 0xca, 0xe8, 0x75, 0xb8, 0xf2, 0xd2,
+	0x1a, 0xd2, 0xd4, 0x40, 0xa9, 0x7e, 0xc8, 0xf0, 0xab, 0x9e, 0xb7, 0x6c, 0x5e, 0x9b, 0x2a, 0x57,
+	0x07, 0x8c, 0xa5, 0xfc, 0x7f, 0x35, 0x28, 0xae, 0x74, 0x0d, 0xbb, 0x27, 0x15, 0xbf, 0x0f, 0x59,
+	0x7e, 0x81, 0x11, 0x98, 0xc1, 0x83, 0xa0, 0x18, 0x95, 0x97, 0x37, 0x56, 0xf8, 0x75, 0x47, 0x8c,
+	0xa2, 0x86, 0x8b, 0x67, 0x85, 0xb5, 0xd0, 0x33, 0xc3, 0x1a, 0x7a, 0x0b, 0x32, 0x06, 0x1d, 0xc2,
+	0x52, 0x70, 0x39, 0x7c, 0x75, 0x64, 0xd2, 0xd8, 0xb9, 0x8d, 0x73, 0xe1, 0xf7, 0xa0, 0xa0, 0x68,
+	0xa0, 0x97, 0xe3, 0x17, 0x0d, 0x71, 0x00, 0x5b, 0x59, 0x6d, 0x6e, 0xbc, 0xe2, 0x77, 0xe6, 0x32,
+	0xc0, 0x5a, 0xc3, 0x6b, 0xa7, 0xf0, 0xa7, 0x62, 0x94, 0xc8, 0x77, 0xaa, 0x3d, 0x5a, 0x92, 0x3d,
+	0xa9, 0x4b, 0xd9, 0x73, 0x06, 0x25, 0x31, 0xfd, 0x71, 0xd3, 0x37, 0x93, 0x97, 0x90, 0xbe, 0x15,
+	0xe3, 0x75, 0xc1, 0x88, 0xa7, 0xa1, 0x24, 0x12, 0xba, 0xd8, 0x7f, 0xff, 0xa3, 0x41, 0x59, 0x52,
+	0xc6, 0xc5, 0x36, 0x25, 0x2c, 0xc3, 0x2b, 0x80, 0x07, 0xca, 0x5c, 0x83, 0x6c, 0xfb, 0x70, 0xaf,
+	0xf3, 0x46, 0xe2, 0xd0, 0xa2, 0x45, 0xe9, 0x5d, 0xae, 0x87, 0x3f, 0x06, 0x89, 0x16, 0xbd, 0xa0,
+	0xdb, 0xc6, 0x91, 0xbb, 0x61, 0xb6, 0xc9, 0x19, 0x3b, 0x37, 0x4e, 0xea, 0x3e, 0x81, 0xdd, 0x57,
+	0xc5, 0xa3, 0x11, 0x3b, 0x2c, 0xaa, 0x8f, 0x48, 0x33, 0x70, 0x65, 0x65, 0xe0, 0x9e, 0x34, 0x4c,
+	0xe3, 0xb0, 0x2b, 0x33, 0x16, 0x2d, 0xb3, 0x94, 0xb8, 0xd6, 0x71, 0x54, 0x6a, 0x03, 0x66, 0x28,
+	0x95, 0x98, 0x6e, 0xa7, 0xa5, 0xa4, 0x37, 0x59, 0xc4, 0xb4, 0x50, 0x11, 0x33, 0x1c, 0xe7, 0xb5,
+	0x65, 0xb7, 0xc5, 0xd4, 0xbc, 0x36, 0x5e, 0xe3, 0xc2, 0xf7, 0x9d, 0x40, 0x99, 0xfa, 0xbe, 0x52,
+	0x16, 0x7d, 0x29, 0x2f, 0x88, 0x3b, 0x42, 0x0a, 0x7e, 0x02, 0x57, 0x25, 0xa7, 0xc0, 0xfd, 0x46,
+	0x30, 0xef, 0xc0, 0x2d, 0xc9, 0xbc, 0x7a, 0x42, 0x2f, 0x56, 0xbb, 0x42, 0xe1, 0x0f, 0xb5, 0xf3,
+	0x39, 0x54, 0x3d, 0x3b, 0xd9, 0x61, 0xd9, 0xea, 0xaa, 0x06, 0x0c, 0x1c, 0xb1, 0x67, 0xf2, 0x3a,
+	0xfb, 0xa6, 0x34, 0xdb, 0xea, 0x7a, 0x47, 0x02, 0xfa, 0x8d, 0x57, 0x61, 0x4e, 0xca, 0x10, 0xc7,
+	0xd8, 0xa0, 0x90, 0x88, 0x41, 0x71, 0x42, 0x84, 0xc3, 0xe8, 0xd0, 0xd1, 0x6e, 0x57, 0x39, 0x83,
+	0xae, 0x65, 0x32, 0x35, 0x45, 0xe6, 0x55, 0xbe, 0x23, 0xa8, 0x61, 0x6a, 0xc5, 0x10, 0x64, 0x2a,
+	0x40, 0x25, 0x8b, 0x85, 0xa0, 0xe4, 0xc8, 0x42, 0x44, 0x44, 0x7f, 0x06, 0xf3, 0x9e, 0x11, 0xd4,
+	0x6f, 0xbb, 0xc4, 0xee, 0x75, 0x1c, 0x47, 0x41, 0x8a, 0xe2, 0x26, 0xfe, 0x00, 0x26, 0xfb, 0x44,
+	0xe4, 0x94, 0xc2, 0x32, 0x5a, 0xe2, 0x4f, 0xbb, 0x4b, 0xca, 0x60, 0xd6, 0x8f, 0xdb, 0x70, 0x5b,
+	0x4a, 0xe7, 0x1e, 0x8d, 0x15, 0x1f, 0x36, 0x4a, 0x5e, 0xc8, 0xb9, 0x5b, 0xa3, 0x17, 0xf2, 0x34,
+	0x5f, 0x7b, 0x0f, 0xbd, 0xfc, 0x88, 0x3b, 0x52, 0xc6, 0xd6, 0x58, 0xb5, 0x62, 0x93, 0xfb, 0xd4,
+	0x0b, 0xc9, 0xb1, 0x84, 0x1d, 0xc2, 0x6c, 0x30, 0x92, 0xc7, 0x4a, 0x63, 0xb3, 0x90, 0x71, 0xad,
+	0x53, 0x22, 0x93, 0x18, 0x6f, 0x48, 0x83, 0xbd, 0x30, 0x1f, 0xcb, 0x60, 0xc3, 0x17, 0xc6, 0xb6,
+	0xe4, 0xb8, 0xf6, 0xd2, 0xd5, 0x94, 0x87, 0x2f, 0xde, 0xc0, 0xdb, 0x70, 0x2d, 0x9c, 0x26, 0xc6,
+	0x32, 0xf9, 0x15, 0xdf, 0xc0, 0x71, 0x99, 0x64, 0x2c, 0xb9, 0x1f, 0xfb, 0xc9, 0x40, 0x49, 0x28,
+	0x63, 0x89, 0xd4, 0xa1, 0x16, 0x97, 0x5f, 0x7e, 0x8c, 0xfd, 0xea, 0xa5, 0x9b, 0xb1, 0x84, 0x39,
+	0xbe, 0xb0, 0xf1, 0x97, 0xdf, 0xcf, 0x11, 0xe9, 0x91, 0x39, 0x42, 0x04, 0x89, 0x9f, 0xc5, 0x7e,
+	0x82, 0x4d, 0x27, 0x74, 0xf8, 0x09, 0x74, 0x5c, 0x1d, 0xb4, 0x86, 0x78, 0x3a, 0x58, 0x43, 0x6e,
+	0x6c, 0x35, 0xed, 0x8e, 0xb5, 0x18, 0x9f, 0xf8, 0xb9, 0x33, 0x92, 0x99, 0xc7, 0x12, 0xfc, 0x29,
+	0x2c, 0x24, 0x27, 0xe5, 0x71, 0x24, 0x3f, 0xae, 0x43, 0xde, 0x3b, 0x50, 0x2a, 0x3f, 0x8b, 0x28,
+	0x40, 0x6e, 0x7b, 0x67, 0x6f, 0x77, 0x65, 0xb5, 0xc1, 0x7f, 0x17, 0xb1, 0xba, 0xa3, 0xeb, 0xfb,
+	0xbb, 0xcd, 0x4a, 0x6a, 0xf9, 0xb7, 0x69, 0x48, 0x6d, 0xbe, 0x42, 0x7f, 0x0e, 0x19, 0xfe, 0x48,
+	0x38, 0xe2, 0x65, 0xb8, 0x36, 0xea, 0x1d, 0x14, 0xdf, 0xf8, 0xe2, 0xff, 0x7f, 0xf5, 0x55, 0xea,
+	0x2a, 0xae, 0xd4, 0x87, 0xef, 0x1e, 0x12, 0xd7, 0xa8, 0x9f, 0x0e, 0xeb, 0xac, 0x3e, 0x3c, 0xd3,
+	0x1e, 0xa3, 0x7d, 0x48, 0xef, 0x0e, 0x5c, 0x94, 0xf8, 0x6a, 0x5c, 0x4b, 0x7e, 0x1e, 0xc5, 0x73,
+	0x4c, 0xf0, 0x0c, 0x2e, 0x2b, 0x82, 0xfb, 0x03, 0x97, 0x8a, 0x1d, 0x40, 0x41, 0x7d, 0xe0, 0xbc,
+	0xf0, 0x39, 0xb9, 0x76, 0xf1, 0xe3, 0x29, 0xbe, 0xc3, 0xd4, 0xdd, 0xc0, 0xd7, 0x14, 0x75, 0xfc,
+	0x19, 0x56, 0x9d, 0x4d, 0xf3, 0xcc, 0x44, 0x89, 0x0f, 0xce, 0xb5, 0xe4, 0x37, 0xd5, 0xd8, 0xd9,
+	0xb8, 0x67, 0x26, 0x15, 0x6b, 0x8a, 0x27, 0xd5, 0x96, 0x8b, 0x6e, 0xc7, 0x3c, 0xa9, 0xa9, 0x8f,
+	0x47, 0xb5, 0x85, 0x64, 0x06, 0xa1, 0x68, 0x81, 0x29, 0xaa, 0xe1, 0xab, 0x8a, 0xa2, 0x96, 0xc7,
+	0xf6, 0x4c, 0x7b, 0xbc, 0x7c, 0x0c, 0x19, 0x86, 0x10, 0xa3, 0xbf, 0x90, 0x1f, 0xb5, 0x18, 0xd8,
+	0x3b, 0x61, 0xf1, 0x03, 0xd8, 0x32, 0xae, 0x32, 0x65, 0x08, 0x97, 0xa4, 0x32, 0x86, 0x11, 0x3f,
+	0xd3, 0x1e, 0x2f, 0x6a, 0x6f, 0x6b, 0xcb, 0xbf, 0x99, 0x84, 0x0c, 0x83, 0x8b, 0x90, 0x05, 0xe0,
+	0xa3, 0xa9, 0xe1, 0x59, 0x46, 0xf0, 0xd9, 0xf0, 0x2c, 0xa3, 0x40, 0x2c, 0x9e, 0x67, 0x8a, 0xab,
+	0x78, 0x46, 0x2a, 0x66, 0x48, 0x54, 0x9d, 0x81, 0x6b, 0xd4, 0xa7, 0x43, 0x01, 0x98, 0xf1, 0x30,
+	0x43, 0x71, 0x02, 0x03, 0xa8, 0x6a, 0x78, 0x87, 0xc4, 0x20, 0xaa, 0x18, 0x33, 0x9d, 0x37, 0xf1,
+	0x75, 0xc5, 0xb3, 0x5c, 0xad, 0xcd, 0x18, 0xa9, 0xde, 0xbf, 0xd3, 0xa0, 0x1c, 0xc4, 0x45, 0xd1,
+	0xdd, 0x18, 0xc9, 0x61, 0x78, 0xb5, 0x76, 0x6f, 0x34, 0x53, 0x92, 0x05, 0x5c, 0xfd, 0x29, 0x21,
+	0x7d, 0x83, 0x32, 0x0a, 0xc7, 0xa3, 0x7f, 0xd0, 0x60, 0x3a, 0x04, 0x76, 0xa2, 0x38, 0x0d, 0x11,
+	0x28, 0xb5, 0x76, 0xff, 0x02, 0x2e, 0x61, 0xc8, 0x03, 0x66, 0xc8, 0x02, 0xbe, 0x11, 0x71, 0x85,
+	0xdb, 0xe9, 0x11, 0xd7, 0x12, 0xc6, 0x78, 0xcb, 0xc0, 0x81, 0xc9, 0xd8, 0x65, 0x08, 0x00, 0x9d,
+	0xb1, 0xcb, 0x10, 0x44, 0x35, 0x47, 0x2c, 0x03, 0x47, 0x23, 0xe9, 0x16, 0xff, 0x5d, 0x1a, 0x72,
+	0xab, 0xfc, 0xc7, 0x89, 0xc8, 0x81, 0xbc, 0x87, 0x00, 0xa2, 0xf9, 0x38, 0x34, 0xc6, 0xbf, 0x2d,
+	0xd4, 0x6e, 0x27, 0xf6, 0x0b, 0xed, 0xf7, 0x99, 0xf6, 0xdb, 0xb8, 0x26, 0xb5, 0x8b, 0xdf, 0x40,
+	0xd6, 0xf9, 0xb5, 0xbf, 0x6e, 0xb4, 0xdb, 0x74, 0xe2, 0x7f, 0x0b, 0x45, 0x15, 0xa6, 0x43, 0x77,
+	0x62, 0x51, 0x20, 0x15, 0xe9, 0xab, 0xe1, 0x51, 0x2c, 0x42, 0xfb, 0x22, 0xd3, 0x8e, 0xf1, 0xad,
+	0x04, 0xed, 0x36, 0x63, 0x0f, 0x18, 0xc0, 0x61, 0xb6, 0x78, 0x03, 0x02, 0x28, 0x5e, 0xbc, 0x01,
+	0x41, 0x94, 0xee, 0x42, 0x03, 0x06, 0x8c, 0x9d, 0x1a, 0xf0, 0x1a, 0xc0, 0x07, 0xd5, 0x50, 0xac,
+	0x5f, 0x95, 0xab, 0x53, 0x38, 0xe4, 0xa3, 0x78, 0x5c, 0x74, 0xcf, 0x85, 0x54, 0x77, 0x3b, 0x0e,
+	0x0d, 0xfd, 0xe5, 0xaf, 0xb3, 0x50, 0x78, 0x69, 0x74, 0x4c, 0x97, 0x98, 0x86, 0xd9, 0x22, 0xe8,
+	0x08, 0x32, 0xac, 0x34, 0x86, 0xb3, 0x9c, 0x8a, 0x35, 0x85, 0xb3, 0x5c, 0x00, 0x88, 0xc1, 0xf7,
+	0x98, 0xe6, 0x79, 0x3c, 0x27, 0x35, 0xf7, 0x7c, 0xf1, 0x75, 0x86, 0xa1, 0xd0, 0x09, 0xff, 0x25,
+	0x64, 0x05, 0x3c, 0x1f, 0x12, 0x16, 0xc0, 0x56, 0x6a, 0x37, 0xe3, 0x3b, 0x93, 0xb6, 0x97, 0xaa,
+	0xca, 0x61, 0xbc, 0x54, 0xd7, 0x1b, 0x00, 0x1f, 0x20, 0x0c, 0x3b, 0x37, 0x82, 0x27, 0xd6, 0x16,
+	0x92, 0x19, 0x84, 0xde, 0x47, 0x4c, 0xef, 0x5d, 0x3c, 0x1f, 0xa7, 0xb7, 0xed, 0xf1, 0x53, 0xdd,
+	0x87, 0x30, 0xb9, 0x6e, 0x38, 0x27, 0x28, 0x54, 0xec, 0x94, 0xdf, 0x13, 0xd4, 0x6a, 0x71, 0x5d,
+	0x42, 0xd3, 0x5d, 0xa6, 0xe9, 0x16, 0xae, 0xc6, 0x69, 0x3a, 0x31, 0x1c, 0x5a, 0x3d, 0xd0, 0x09,
+	0x64, 0xf9, 0x4f, 0x0c, 0xc2, 0xbe, 0x0c, 0xfc, 0x4c, 0x21, 0xec, 0xcb, 0xe0, 0xaf, 0x12, 0x2e,
+	0xa7, 0xc9, 0x85, 0x29, 0xf9, 0xae, 0x8f, 0x6e, 0x85, 0x96, 0x26, 0xf8, 0x1b, 0x80, 0xda, 0x7c,
+	0x52, 0xb7, 0xd0, 0xf7, 0x90, 0xe9, 0xbb, 0x83, 0x6f, 0xc6, 0xae, 0x9d, 0xe0, 0x7e, 0xa6, 0x3d,
+	0x7e, 0x5b, 0xa3, 0x65, 0x02, 0x7c, 0x90, 0x35, 0x12, 0x1d, 0x61, 0xbc, 0x36, 0x12, 0x1d, 0x11,
+	0x7c, 0x16, 0x2f, 0x33, 0xe5, 0x4f, 0xf1, 0xc3, 0x38, 0xe5, 0xae, 0x6d, 0x98, 0xce, 0x11, 0xb1,
+	0xdf, 0xe2, 0x60, 0x9a, 0x73, 0xd2, 0xe9, 0xd3, 0x48, 0xf9, 0xfd, 0x34, 0x4c, 0xd2, 0xf3, 0x28,
+	0x2d, 0xcf, 0xfe, 0x35, 0x3e, 0x6c, 0x4d, 0x04, 0x3c, 0x0b, 0x5b, 0x13, 0x45, 0x00, 0xa2, 0xe5,
+	0x99, 0xfd, 0x0c, 0x9d, 0x30, 0x26, 0xea, 0x75, 0x07, 0x0a, 0xca, 0x5d, 0x1f, 0xc5, 0x08, 0x0c,
+	0x22, 0x73, 0xe1, 0xba, 0x10, 0x03, 0x14, 0xe0, 0xdb, 0x4c, 0xe7, 0x1c, 0x9e, 0x0d, 0xe8, 0x6c,
+	0x73, 0x2e, 0xaa, 0xf4, 0xaf, 0xa1, 0xa8, 0x62, 0x02, 0x28, 0x46, 0x66, 0x08, 0xf9, 0x0b, 0xa7,
+	0xc4, 0x38, 0x48, 0x21, 0x9a, 0x1d, 0xbc, 0x9f, 0xdc, 0x4b, 0x56, 0xaa, 0xbc, 0x0f, 0x39, 0x01,
+	0x14, 0xc4, 0xcd, 0x36, 0x08, 0x15, 0xc6, 0xcd, 0x36, 0x84, 0x32, 0x44, 0x8f, 0x79, 0x4c, 0x2b,
+	0xbd, 0x0f, 0xc9, 0x12, 0x24, 0x34, 0xbe, 0x20, 0x6e, 0x92, 0x46, 0x1f, 0xfb, 0x4a, 0xd2, 0xa8,
+	0xdc, 0x45, 0x47, 0x69, 0x3c, 0x26, 0xae, 0x88, 0x25, 0x79, 0xcf, 0x43, 0x09, 0x02, 0xd5, 0x94,
+	0x8f, 0x47, 0xb1, 0x24, 0x9d, 0xca, 0x7d, 0xa5, 0x22, 0xdf, 0xa3, 0xcf, 0x01, 0x7c, 0x48, 0x23,
+	0x7c, 0xda, 0x8a, 0xc5, 0x45, 0xc3, 0xa7, 0xad, 0x78, 0x54, 0x24, 0x9a, 0x3f, 0x7c, 0xdd, 0xfc,
+	0x62, 0x40, 0xb5, 0xff, 0x8b, 0x06, 0x28, 0x8a, 0x80, 0xa0, 0x27, 0xf1, 0x1a, 0x62, 0x11, 0xd7,
+	0xda, 0xd3, 0xcb, 0x31, 0x27, 0x95, 0x08, 0xdf, 0xac, 0x16, 0x1b, 0xd1, 0x7f, 0x4d, 0x0d, 0xfb,
+	0x52, 0x83, 0x52, 0x00, 0x42, 0x41, 0x0f, 0x12, 0xd6, 0x38, 0x04, 0xda, 0xd6, 0x1e, 0x5e, 0xc8,
+	0x97, 0x74, 0x12, 0x53, 0x76, 0x84, 0x3c, 0x88, 0xff, 0xa3, 0x06, 0xe5, 0x20, 0xec, 0x82, 0x12,
+	0xe4, 0x47, 0x80, 0xdf, 0xda, 0xe2, 0xc5, 0x8c, 0x17, 0x2f, 0x95, 0x7f, 0x36, 0xef, 0x43, 0x4e,
+	0x80, 0x35, 0x71, 0x01, 0x11, 0x84, 0x8d, 0xe3, 0x02, 0x22, 0x84, 0xf4, 0x24, 0x04, 0x84, 0x6d,
+	0x75, 0x89, 0x12, 0x82, 0x02, 0xd1, 0x49, 0xd2, 0x38, 0x3a, 0x04, 0x43, 0x70, 0xd0, 0x28, 0x8d,
+	0x7e, 0x08, 0x4a, 0x38, 0x07, 0x25, 0x08, 0xbc, 0x20, 0x04, 0xc3, 0x68, 0x50, 0x42, 0x08, 0x32,
+	0xa5, 0x4a, 0x08, 0xfa, 0xe0, 0x4b, 0x5c, 0x08, 0x46, 0x10, 0xf1, 0xb8, 0x10, 0x8c, 0xe2, 0x37,
+	0x09, 0xeb, 0xca, 0x74, 0x07, 0x42, 0x70, 0x26, 0x06, 0xab, 0x41, 0x4f, 0x13, 0x1c, 0x1a, 0x0b,
+	0xb6, 0xd7, 0xde, 0xba, 0x24, 0xf7, 0xc8, 0xbd, 0xcf, 0x97, 0x42, 0xee, 0xfd, 0x7f, 0xd7, 0x60,
+	0x36, 0x0e, 0xeb, 0x41, 0x09, 0xba, 0x12, 0x80, 0xfa, 0xda, 0xd2, 0x65, 0xd9, 0x2f, 0xf6, 0x9a,
+	0x17, 0x0d, 0xcf, 0x2b, 0xff, 0xfd, 0xdd, 0xbc, 0xf6, 0x7f, 0xdf, 0xcd, 0x6b, 0xbf, 0xf8, 0x6e,
+	0x5e, 0xfb, 0xd7, 0x5f, 0xce, 0x4f, 0x1c, 0x66, 0xd9, 0x7f, 0xfe, 0x7a, 0xf7, 0x0f, 0x01, 0x00,
+	0x00, 0xff, 0xff, 0xb4, 0xfd, 0xab, 0xe6, 0x83, 0x36, 0x00, 0x00,
 }

+ 7 - 0
etcdserver/etcdserverpb/rpc.proto

@@ -671,6 +671,13 @@ message WatchCreateRequest {
   // 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 = 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 {

+ 2 - 2
mvcc/kv_test.go

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

+ 3 - 3
mvcc/watchable_store_bench_test.go

@@ -78,7 +78,7 @@ func BenchmarkWatchableStoreWatchSyncPut(b *testing.B) {
 	watchIDs := make([]WatchID, b.N)
 	for i := range watchIDs {
 		// 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()
@@ -142,7 +142,7 @@ func BenchmarkWatchableStoreUnsyncedCancel(b *testing.B) {
 	watchIDs := make([]WatchID, watcherN)
 	for i := 0; i < watcherN; i++ {
 		// 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
@@ -182,7 +182,7 @@ func BenchmarkWatchableStoreSyncedCancel(b *testing.B) {
 	watchIDs := make([]WatchID, watcherN)
 	for i := 0; i < watcherN; i++ {
 		// 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

+ 10 - 10
mvcc/watchable_store_test.go

@@ -42,7 +42,7 @@ func TestWatch(t *testing.T) {
 	s.Put(testKey, testValue, lease.NoLease)
 
 	w := s.NewWatchStream()
-	w.Watch(testKey, nil, 0)
+	w.Watch(0, testKey, nil, 0)
 
 	if !s.synced.contains(string(testKey)) {
 		// the key must have had an entry in synced
@@ -63,7 +63,7 @@ func TestNewWatcherCancel(t *testing.T) {
 	s.Put(testKey, testValue, lease.NoLease)
 
 	w := s.NewWatchStream()
-	wt := w.Watch(testKey, nil, 0)
+	wt, _ := w.Watch(0, testKey, nil, 0)
 
 	if err := w.Cancel(wt); err != nil {
 		t.Error(err)
@@ -114,7 +114,7 @@ func TestCancelUnsynced(t *testing.T) {
 	watchIDs := make([]WatchID, watcherN)
 	for i := 0; i < watcherN; i++ {
 		// 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 {
@@ -160,7 +160,7 @@ func TestSyncWatchers(t *testing.T) {
 
 	for i := 0; i < watcherN; i++ {
 		// 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
@@ -242,7 +242,7 @@ func TestWatchCompacted(t *testing.T) {
 	}
 
 	w := s.NewWatchStream()
-	wt := w.Watch(testKey, nil, compactRev-1)
+	wt, _ := w.Watch(0, testKey, nil, compactRev-1)
 
 	select {
 	case resp := <-w.Chan():
@@ -271,7 +271,7 @@ func TestWatchFutureRev(t *testing.T) {
 
 	w := s.NewWatchStream()
 	wrev := int64(10)
-	w.Watch(testKey, nil, wrev)
+	w.Watch(0, testKey, nil, wrev)
 
 	for i := 0; i < 10; i++ {
 		rev := s.Put(testKey, testValue, lease.NoLease)
@@ -310,7 +310,7 @@ func TestWatchRestore(t *testing.T) {
 	defer cleanup(newStore, newBackend, newPath)
 
 	w := newStore.NewWatchStream()
-	w.Watch(testKey, nil, rev-1)
+	w.Watch(0, testKey, nil, rev-1)
 
 	newStore.Restore(b)
 	select {
@@ -349,7 +349,7 @@ func TestWatchBatchUnsynced(t *testing.T) {
 	}
 
 	w := s.NewWatchStream()
-	w.Watch(v, nil, 1)
+	w.Watch(0, v, nil, 1)
 	for i := 0; i < batches; i++ {
 		if resp := <-w.Chan(); 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++ {
 		go func() {
 			w := s.NewWatchStream()
-			w.Watch(testKey, nil, 1)
+			w.Watch(0, testKey, nil, 1)
 			defer func() {
 				w.Close()
 				wg.Done()
@@ -561,7 +561,7 @@ func TestStressWatchCancelClose(t *testing.T) {
 			w := s.NewWatchStream()
 			ids := make([]WatchID, 10)
 			for i := range ids {
-				ids[i] = w.Watch(testKey, nil, 0)
+				ids[i], _ = w.Watch(0, testKey, nil, 0)
 			}
 			<-readyc
 			wg.Add(1 + len(ids)/2)

+ 25 - 12
mvcc/watcher.go

@@ -22,8 +22,14 @@ import (
 	"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 (
-	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
@@ -36,12 +42,13 @@ type WatchStream interface {
 	// happened on the given key or range [key, end) from the given startRev.
 	//
 	// 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.
-	//
-	Watch(key, end []byte, startRev int64, fcs ...FilterFunc) WatchID
+	// The watch ID is used when it's not equal to AutoWatchID. Otherwise,
+	// 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() <-chan WatchResponse
@@ -98,28 +105,34 @@ type watchStream struct {
 }
 
 // Watch creates a new watcher in the stream and returns its WatchID.
-// TODO: return error if ws is closed?
-func (ws *watchStream) Watch(key, end []byte, startRev int64, fcs ...FilterFunc) WatchID {
+func (ws *watchStream) Watch(id WatchID, key, end []byte, startRev int64, fcs ...FilterFunc) (WatchID, error) {
 	// prevent wrong range where key >= end lexicographically
 	// watch request with 'WithFromKey' has empty-byte range end
 	if len(end) != 0 && bytes.Compare(key, end) != -1 {
-		return -1
+		return -1, ErrEmptyWatcherRange
 	}
 
 	ws.mu.Lock()
 	defer ws.mu.Unlock()
 	if ws.closed {
-		return -1
+		return -1, ErrEmptyWatcherRange
 	}
 
-	id := ws.nextID
-	ws.nextID++
+	if id == AutoWatchID {
+		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...)
 
 	ws.cancels[id] = c
 	ws.watchers[id] = w
-	return id
+	return id, nil
 }
 
 func (ws *watchStream) Chan() <-chan WatchResponse {

+ 1 - 1
mvcc/watcher_bench_test.go

@@ -33,6 +33,6 @@ func BenchmarkKVWatcherMemoryUsage(b *testing.B) {
 	b.ReportAllocs()
 	b.StartTimer()
 	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)
 	}
 }

+ 49 - 14
mvcc/watcher_test.go

@@ -40,7 +40,7 @@ func TestWatcherWatchID(t *testing.T) {
 	idm := make(map[WatchID]struct{})
 
 	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 {
 			t.Errorf("#%d: id %d exists", i, id)
 		}
@@ -62,7 +62,7 @@ func TestWatcherWatchID(t *testing.T) {
 
 	// unsynced watchers
 	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 {
 			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
 // and returns events with matching prefixes.
 func TestWatcherWatchPrefix(t *testing.T) {
@@ -95,7 +130,7 @@ func TestWatcherWatchPrefix(t *testing.T) {
 	keyWatch, keyEnd, keyPut := []byte("foo"), []byte("fop"), []byte("foobar")
 
 	for i := 0; i < 10; i++ {
-		id := w.Watch(keyWatch, keyEnd, 0)
+		id, _ := w.Watch(0, keyWatch, keyEnd, 0)
 		if _, ok := idm[id]; ok {
 			t.Errorf("#%d: unexpected duplicated id %x", i, id)
 		}
@@ -127,7 +162,7 @@ func TestWatcherWatchPrefix(t *testing.T) {
 
 	// unsynced watchers
 	for i := 10; i < 15; i++ {
-		id := w.Watch(keyWatch1, keyEnd1, 1)
+		id, _ := w.Watch(0, keyWatch1, keyEnd1, 1)
 		if _, ok := idm[id]; ok {
 			t.Errorf("#%d: id %d exists", i, id)
 		}
@@ -163,14 +198,14 @@ func TestWatcherWatchWrongRange(t *testing.T) {
 	w := s.NewWatchStream()
 	defer w.Close()
 
-	if id := w.Watch([]byte("foa"), []byte("foa"), 1); id != -1 {
-		t.Fatalf("key == end range given; id expected -1, got %d", id)
+	if _, err := w.Watch(0, []byte("foa"), []byte("foa"), 1); err != ErrEmptyWatcherRange {
+		t.Fatalf("key == end range given; expected ErrEmptyWatcherRange, got %+v", err)
 	}
-	if id := w.Watch([]byte("fob"), []byte("foa"), 1); id != -1 {
-		t.Fatalf("key > end range given; id expected -1, got %d", id)
+	if _, err := w.Watch(0, []byte("fob"), []byte("foa"), 1); err != ErrEmptyWatcherRange {
+		t.Fatalf("key > end range given; expected ErrEmptyWatcherRange, got %+v", err)
 	}
 	// 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)
 	}
 }
@@ -192,7 +227,7 @@ func TestWatchDeleteRange(t *testing.T) {
 
 	w := s.NewWatchStream()
 	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)
 
@@ -222,7 +257,7 @@ func TestWatchStreamCancelWatcherByID(t *testing.T) {
 	w := s.NewWatchStream()
 	defer w.Close()
 
-	id := w.Watch([]byte("foo"), nil, 0)
+	id, _ := w.Watch(0, []byte("foo"), nil, 0)
 
 	tests := []struct {
 		cancelID WatchID
@@ -284,7 +319,7 @@ func TestWatcherRequestProgress(t *testing.T) {
 	default:
 	}
 
-	id := w.Watch(notTestKey, nil, 1)
+	id, _ := w.Watch(0, notTestKey, nil, 1)
 	w.RequestProgress(id)
 	select {
 	case resp := <-w.Chan():
@@ -295,7 +330,7 @@ func TestWatcherRequestProgress(t *testing.T) {
 	s.syncWatchers()
 
 	w.RequestProgress(id)
-	wrs := WatchResponse{WatchID: 0, Revision: 2}
+	wrs := WatchResponse{WatchID: id, Revision: 2}
 	select {
 	case resp := <-w.Chan():
 		if !reflect.DeepEqual(resp, wrs) {
@@ -318,7 +353,7 @@ func TestWatcherWatchWithFilter(t *testing.T) {
 		return e.Type == mvccpb.PUT
 	}
 
-	w.Watch([]byte("foo"), nil, 0, filterPut)
+	w.Watch(0, []byte("foo"), nil, 0, filterPut)
 	done := make(chan struct{})
 
 	go func() {

+ 2 - 2
scripts/genproto.sh

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