*: update vendor

This commit is contained in:
Gyu-Ho Lee 2016-06-26 10:38:51 -07:00
parent cf6907a1c9
commit c11e29e6da
16 changed files with 1667 additions and 434 deletions

29
glide.lock generated
View File

@ -1,5 +1,5 @@
hash: 899cf0cc8fe75956e38ad46350724245f8d593c906f41981c573229c262a946e hash: 899cf0cc8fe75956e38ad46350724245f8d593c906f41981c573229c262a946e
updated: 2016-06-17T16:32:21.379557428-07:00 updated: 2016-06-26T10:38:34.458889325-07:00
imports: imports:
- name: bitbucket.org/zombiezen/gopdf - name: bitbucket.org/zombiezen/gopdf
version: 1c63dc69751bc45441c2ce1f56b631c55294b4d5 version: 1c63dc69751bc45441c2ce1f56b631c55294b4d5
@ -12,7 +12,7 @@ imports:
- name: github.com/cloudfoundry-incubator/candiedyaml - name: github.com/cloudfoundry-incubator/candiedyaml
version: 99c3df83b51532e3615f851d8c2dbb638f5313bf version: 99c3df83b51532e3615f851d8c2dbb638f5313bf
- name: github.com/coreos/etcd - name: github.com/coreos/etcd
version: b03c832bed4cd0c7856ffb5b4ec52df5073be3f2 version: aab905f7ccd12733b16931953677bef7e8ce5400
subpackages: subpackages:
- client - client
- clientv3 - clientv3
@ -24,15 +24,15 @@ imports:
- mvcc/mvccpb - mvcc/mvccpb
- pkg/tlsutil - pkg/tlsutil
- name: github.com/coreos/go-systemd - name: github.com/coreos/go-systemd
version: a52323565816dee4d2f8dc5fdf1299f48757b848 version: b32b8467dbea18858bfebf65c1a6a761090f2c31
subpackages: subpackages:
- journal - journal
- name: github.com/coreos/pkg - name: github.com/coreos/pkg
version: 62adab7e2fbd7e45230489364764b5193f32f45a version: a48e304ff9331be6d5df352b6b47bd1395ab5dd7
subpackages: subpackages:
- capnslog - capnslog
- name: github.com/dustin/go-humanize - name: github.com/dustin/go-humanize
version: 499693e27ee0d14ffab67c31ad065fdb3d34ea75 version: fef948f2d241bd1fd0631108ecc2c9553bae60bf
- name: github.com/fatih/color - name: github.com/fatih/color
version: 87d4004f2ab62d0d255e0a38f1680aa534549fe3 version: 87d4004f2ab62d0d255e0a38f1680aa534549fe3
- name: github.com/gengo/grpc-gateway - name: github.com/gengo/grpc-gateway
@ -50,7 +50,7 @@ imports:
- proto - proto
- protoc-gen-gogo/descriptor - protoc-gen-gogo/descriptor
- name: github.com/golang/freetype - name: github.com/golang/freetype
version: a31d0146dac6f09aa27eda40995aeca7d7adc293 version: 38b4c392adc5eed94207994c4848fff99f4ac234
subpackages: subpackages:
- truetype - truetype
- raster - raster
@ -85,7 +85,7 @@ imports:
subpackages: subpackages:
- ps - ps
- name: github.com/hashicorp/consul - name: github.com/hashicorp/consul
version: af332d6d771c55f4ac69d74dd702b7a3917bb9a0 version: 09cfda47ed103910a8e1af76fa378a7e6acd5310
subpackages: subpackages:
- api - api
- name: github.com/hashicorp/go-cleanhttp - name: github.com/hashicorp/go-cleanhttp
@ -109,13 +109,13 @@ imports:
- name: github.com/mattn/go-runewidth - name: github.com/mattn/go-runewidth
version: d6bea18f789704b5f83375793155289da36a3c7f version: d6bea18f789704b5f83375793155289da36a3c7f
- name: github.com/olekukonko/tablewriter - name: github.com/olekukonko/tablewriter
version: 8d0265a48283795806b872b4728c67bf5c777f20 version: daf2955e742cf123959884fdff4685aa79b63135
- name: github.com/samuel/go-zookeeper - name: github.com/samuel/go-zookeeper
version: e64db453f3512cade908163702045e0f31137843 version: e64db453f3512cade908163702045e0f31137843
subpackages: subpackages:
- zk - zk
- name: github.com/spf13/cobra - name: github.com/spf13/cobra
version: bc81c21bd0d8be5ba2d6630a505d79d4467566e7 version: 6a8bd97bdb1fc0d08a83459940498ea49d3e8c93
- name: github.com/spf13/pflag - name: github.com/spf13/pflag
version: 367864438f1b1a3c7db4da06a2f55b144e6784e0 version: 367864438f1b1a3c7db4da06a2f55b144e6784e0
- name: github.com/ugorji/go - name: github.com/ugorji/go
@ -132,7 +132,7 @@ imports:
- math/f64 - math/f64
- tiff/lzw - tiff/lzw
- name: golang.org/x/net - name: golang.org/x/net
version: d7bf3545bb0dacf009c535b3d3fbf53ac0a339ab version: e445b19913b9d40fdbdfe19ac5e3d314aafd6f63
subpackages: subpackages:
- context - context
- http2 - http2
@ -160,7 +160,7 @@ imports:
- googleapi/internal/uritemplates - googleapi/internal/uritemplates
- gensupport - gensupport
- name: google.golang.org/appengine - name: google.golang.org/appengine
version: 7f59a8c76b8594d06044bfe0bcbe475cb2020482 version: 267c27e7492265b84fc6719503b14a1e17975d79
subpackages: subpackages:
- urlfetch - urlfetch
- internal - internal
@ -169,9 +169,10 @@ imports:
- internal/urlfetch - internal/urlfetch
- internal/base - internal/base
- internal/datastore - internal/datastore
- internal/log
- internal/remote_api - internal/remote_api
- name: google.golang.org/cloud - name: google.golang.org/cloud
version: 581cbbbaf53b821281c5df8f4bb9fa69c2d5f49c version: a4c7c3139dc2772ad6c3bb73d0a9236a79063f8c
subpackages: subpackages:
- storage - storage
- compute/metadata - compute/metadata
@ -179,7 +180,7 @@ imports:
- internal/opts - internal/opts
- internal/transport - internal/transport
- name: google.golang.org/grpc - name: google.golang.org/grpc
version: e78224b060cf3215247b7be455f80ea22e469b66 version: 0eb7c5dcd059d77bde1eb5bc7d953940170d0d18
subpackages: subpackages:
- codes - codes
- credentials - credentials
@ -192,4 +193,4 @@ imports:
- credentials/oauth - credentials/oauth
- name: gopkg.in/yaml.v2 - name: gopkg.in/yaml.v2
version: a83829b6f1293c91addabc89d0571c246397bbf4 version: a83829b6f1293c91addabc89d0571c246397bbf4
devImports: [] testImports: []

16
script-update-vendor.sh Executable file
View File

@ -0,0 +1,16 @@
#!/usr/bin/env bash
set -e
echo "Installing glide..."
GLIDE_ROOT="$GOPATH/src/github.com/Masterminds/glide"
rm -rf $GLIDE_ROOT
go get -u github.com/Masterminds/glide
pushd "${GLIDE_ROOT}"
git reset --hard HEAD
go install
popd
rm -rf vendor
glide -v
glide update --strip-vendor --strip-vcs --update-vendored
glide vc --only-code --no-tests

View File

@ -134,7 +134,15 @@ func (kv *kv) do(ctx context.Context, op Op) (OpResponse, error) {
// TODO: handle other ops // TODO: handle other ops
case tRange: case tRange:
var resp *pb.RangeResponse var resp *pb.RangeResponse
r := &pb.RangeRequest{Key: op.key, RangeEnd: op.end, Limit: op.limit, Revision: op.rev, Serializable: op.serializable} r := &pb.RangeRequest{
Key: op.key,
RangeEnd: op.end,
Limit: op.limit,
Revision: op.rev,
Serializable: op.serializable,
KeysOnly: op.keysOnly,
CountOnly: op.countOnly,
}
if op.sort != nil { if op.sort != nil {
r.SortOrder = pb.RangeRequest_SortOrder(op.sort.Order) r.SortOrder = pb.RangeRequest_SortOrder(op.sort.Order)
r.SortTarget = pb.RangeRequest_SortTarget(op.sort.Target) r.SortTarget = pb.RangeRequest_SortTarget(op.sort.Target)

View File

@ -354,8 +354,8 @@ func (l *lessor) recvKeepAlive(resp *pb.LeaseKeepAliveResponse) {
} }
} }
// deadlineLoop reaps any keep alive channels that have not recieved a resposne within // deadlineLoop reaps any keep alive channels that have not received a response
// the lease TTL // within the lease TTL
func (l *lessor) deadlineLoop() { func (l *lessor) deadlineLoop() {
for { for {
select { select {

View File

@ -41,6 +41,8 @@ type Op struct {
limit int64 limit int64
sort *SortOption sort *SortOption
serializable bool serializable bool
keysOnly bool
countOnly bool
// for range, watch // for range, watch
rev int64 rev int64
@ -56,7 +58,15 @@ type Op struct {
func (op Op) toRequestOp() *pb.RequestOp { func (op Op) toRequestOp() *pb.RequestOp {
switch op.t { switch op.t {
case tRange: case tRange:
r := &pb.RangeRequest{Key: op.key, RangeEnd: op.end, Limit: op.limit, Revision: op.rev, Serializable: op.serializable} r := &pb.RangeRequest{
Key: op.key,
RangeEnd: op.end,
Limit: op.limit,
Revision: op.rev,
Serializable: op.serializable,
KeysOnly: op.keysOnly,
CountOnly: op.countOnly,
}
if op.sort != nil { if op.sort != nil {
r.SortOrder = pb.RangeRequest_SortOrder(op.sort.Order) r.SortOrder = pb.RangeRequest_SortOrder(op.sort.Order)
r.SortTarget = pb.RangeRequest_SortTarget(op.sort.Target) r.SortTarget = pb.RangeRequest_SortTarget(op.sort.Target)
@ -97,6 +107,8 @@ func OpDelete(key string, opts ...OpOption) Op {
panic("unexpected sort in delete") panic("unexpected sort in delete")
case ret.serializable: case ret.serializable:
panic("unexpected serializable in delete") panic("unexpected serializable in delete")
case ret.countOnly:
panic("unexpected countOnly in delete")
} }
return ret return ret
} }
@ -115,6 +127,8 @@ func OpPut(key, val string, opts ...OpOption) Op {
panic("unexpected sort in put") panic("unexpected sort in put")
case ret.serializable: case ret.serializable:
panic("unexpected serializable in put") panic("unexpected serializable in put")
case ret.countOnly:
panic("unexpected countOnly in delete")
} }
return ret return ret
} }
@ -131,6 +145,8 @@ func opWatch(key string, opts ...OpOption) Op {
panic("unexpected sort in watch") panic("unexpected sort in watch")
case ret.serializable: case ret.serializable:
panic("unexpected serializable in watch") panic("unexpected serializable in watch")
case ret.countOnly:
panic("unexpected countOnly in delete")
} }
return ret return ret
} }
@ -208,6 +224,17 @@ func WithSerializable() OpOption {
return func(op *Op) { op.serializable = true } return func(op *Op) { op.serializable = true }
} }
// WithKeysOnly makes the 'Get' request return only the keys and the corresponding
// values will be omitted.
func WithKeysOnly() OpOption {
return func(op *Op) { op.keysOnly = true }
}
// WithCountOnly makes the 'Get' request return only the count of keys.
func WithCountOnly() OpOption {
return func(op *Op) { op.countOnly = true }
}
// WithFirstCreate gets the key with the oldest creation revision in the request range. // WithFirstCreate gets the key with the oldest creation revision in the request range.
func WithFirstCreate() []OpOption { return withTop(SortByCreateRevision, SortAscend) } func WithFirstCreate() []OpOption { return withTop(SortByCreateRevision, SortAscend) }

View File

@ -17,6 +17,7 @@ package clientv3
import ( import (
"fmt" "fmt"
"sync" "sync"
"time"
v3rpc "github.com/coreos/etcd/etcdserver/api/v3rpc/rpctypes" v3rpc "github.com/coreos/etcd/etcdserver/api/v3rpc/rpctypes"
pb "github.com/coreos/etcd/etcdserver/etcdserverpb" pb "github.com/coreos/etcd/etcdserver/etcdserverpb"
@ -27,6 +28,8 @@ import (
const ( const (
EventTypeDelete = mvccpb.DELETE EventTypeDelete = mvccpb.DELETE
EventTypePut = mvccpb.PUT EventTypePut = mvccpb.PUT
closeSendErrTimeout = 250 * time.Millisecond
) )
type Event mvccpb.Event type Event mvccpb.Event
@ -56,6 +59,8 @@ type WatchResponse struct {
// If the watch failed and the stream was about to close, before the channel is closed, // If the watch failed and the stream was about to close, before the channel is closed,
// the channel sends a final response that has Canceled set to true with a non-nil Err(). // the channel sends a final response that has Canceled set to true with a non-nil Err().
Canceled bool Canceled bool
closeErr error
} }
// IsCreate returns true if the event tells that the key is newly created. // IsCreate returns true if the event tells that the key is newly created.
@ -70,10 +75,12 @@ func (e *Event) IsModify() bool {
// Err is the error value if this WatchResponse holds an error. // Err is the error value if this WatchResponse holds an error.
func (wr *WatchResponse) Err() error { func (wr *WatchResponse) Err() error {
if wr.CompactRevision != 0 { switch {
case wr.closeErr != nil:
return v3rpc.Error(wr.closeErr)
case wr.CompactRevision != 0:
return v3rpc.ErrCompacted return v3rpc.ErrCompacted
} case wr.Canceled:
if wr.Canceled {
return v3rpc.ErrFutureRev return v3rpc.ErrFutureRev
} }
return nil return nil
@ -88,14 +95,26 @@ func (wr *WatchResponse) IsProgressNotify() bool {
type watcher struct { type watcher struct {
remote pb.WatchClient remote pb.WatchClient
// mu protects the grpc streams map
mu sync.RWMutex
// streams holds all the active grpc streams keyed by ctx value.
streams map[string]*watchGrpcStream
}
type watchGrpcStream struct {
owner *watcher
remote pb.WatchClient
// ctx controls internal remote.Watch requests // ctx controls internal remote.Watch requests
ctx context.Context ctx context.Context
// ctxKey is the key used when looking up this stream's context
ctxKey string
cancel context.CancelFunc cancel context.CancelFunc
// streams holds all active watchers
streams map[int64]*watcherStream
// mu protects the streams map // mu protects the streams map
mu sync.RWMutex mu sync.RWMutex
// streams holds all active watchers
streams map[int64]*watcherStream
// reqc sends a watch request from Watch() to the main goroutine // reqc sends a watch request from Watch() to the main goroutine
reqc chan *watchRequest reqc chan *watchRequest
@ -105,8 +124,11 @@ type watcher struct {
stopc chan struct{} stopc chan struct{}
// donec closes to broadcast shutdown // donec closes to broadcast shutdown
donec chan struct{} donec chan struct{}
// errc transmits errors from grpc Recv // errc transmits errors from grpc Recv to the watch stream reconn logic
errc chan error errc chan error
// the error that closed the watch stream
closeErr error
} }
// watchRequest is issued by the subscriber to start a new watcher // watchRequest is issued by the subscriber to start a new watcher
@ -123,6 +145,7 @@ type watchRequest struct {
// watcherStream represents a registered watcher // watcherStream represents a registered watcher
type watcherStream struct { type watcherStream struct {
// initReq is the request that initiated this request
initReq watchRequest initReq watchRequest
// outc publishes watch responses to subscriber // outc publishes watch responses to subscriber
@ -138,10 +161,30 @@ type watcherStream struct {
} }
func NewWatcher(c *Client) Watcher { func NewWatcher(c *Client) Watcher {
ctx, cancel := context.WithCancel(context.Background()) return &watcher{
w := &watcher{
remote: pb.NewWatchClient(c.conn), remote: pb.NewWatchClient(c.conn),
streams: make(map[string]*watchGrpcStream),
}
}
// never closes
var valCtxCh = make(chan struct{})
var zeroTime = time.Unix(0, 0)
// ctx with only the values; never Done
type valCtx struct{ context.Context }
func (vc *valCtx) Deadline() (time.Time, bool) { return zeroTime, false }
func (vc *valCtx) Done() <-chan struct{} { return valCtxCh }
func (vc *valCtx) Err() error { return nil }
func (w *watcher) newWatcherGrpcStream(inctx context.Context) *watchGrpcStream {
ctx, cancel := context.WithCancel(&valCtx{inctx})
wgs := &watchGrpcStream{
owner: w,
remote: w.remote,
ctx: ctx, ctx: ctx,
ctxKey: fmt.Sprintf("%v", inctx),
cancel: cancel, cancel: cancel,
streams: make(map[int64]*watcherStream), streams: make(map[int64]*watcherStream),
@ -151,8 +194,8 @@ func NewWatcher(c *Client) Watcher {
donec: make(chan struct{}), donec: make(chan struct{}),
errc: make(chan error, 1), errc: make(chan error, 1),
} }
go w.run() go wgs.run()
return w return wgs
} }
// Watch posts a watch request to run() and waits for a new watcher channel // Watch posts a watch request to run() and waits for a new watcher channel
@ -170,13 +213,41 @@ func (w *watcher) Watch(ctx context.Context, key string, opts ...OpOption) Watch
} }
ok := false ok := false
ctxKey := fmt.Sprintf("%v", ctx)
// find or allocate appropriate grpc watch stream
w.mu.Lock()
if w.streams == nil {
// closed
w.mu.Unlock()
ch := make(chan WatchResponse)
close(ch)
return ch
}
wgs := w.streams[ctxKey]
if wgs == nil {
wgs = w.newWatcherGrpcStream(ctx)
w.streams[ctxKey] = wgs
}
donec := wgs.donec
reqc := wgs.reqc
w.mu.Unlock()
// couldn't create channel; return closed channel
closeCh := make(chan WatchResponse, 1)
// submit request // submit request
select { select {
case w.reqc <- wr: case reqc <- wr:
ok = true ok = true
case <-wr.ctx.Done(): case <-wr.ctx.Done():
case <-w.donec: case <-donec:
if wgs.closeErr != nil {
closeCh <- WatchResponse{closeErr: wgs.closeErr}
break
}
// retry; may have dropped stream from no ctxs
return w.Watch(ctx, key, opts...)
} }
// receive channel // receive channel
@ -185,23 +256,44 @@ func (w *watcher) Watch(ctx context.Context, key string, opts ...OpOption) Watch
case ret := <-retc: case ret := <-retc:
return ret return ret
case <-ctx.Done(): case <-ctx.Done():
case <-w.donec: case <-donec:
if wgs.closeErr != nil {
closeCh <- WatchResponse{closeErr: wgs.closeErr}
break
}
// retry; may have dropped stream from no ctxs
return w.Watch(ctx, key, opts...)
} }
} }
// couldn't create channel; return closed channel close(closeCh)
ch := make(chan WatchResponse) return closeCh
close(ch)
return ch
} }
func (w *watcher) Close() error { func (w *watcher) Close() (err error) {
w.mu.Lock()
streams := w.streams
w.streams = nil
w.mu.Unlock()
for _, wgs := range streams {
if werr := wgs.Close(); werr != nil {
err = werr
}
}
return err
}
func (w *watchGrpcStream) Close() (err error) {
close(w.stopc) close(w.stopc)
<-w.donec <-w.donec
return toErr(w.ctx, <-w.errc) select {
case err = <-w.errc:
default:
}
return toErr(w.ctx, err)
} }
func (w *watcher) addStream(resp *pb.WatchResponse, pendingReq *watchRequest) { func (w *watchGrpcStream) addStream(resp *pb.WatchResponse, pendingReq *watchRequest) {
if pendingReq == nil { if pendingReq == nil {
// no pending request; ignore // no pending request; ignore
return return
@ -254,27 +346,27 @@ func (w *watcher) addStream(resp *pb.WatchResponse, pendingReq *watchRequest) {
} }
// closeStream closes the watcher resources and removes it // closeStream closes the watcher resources and removes it
func (w *watcher) closeStream(ws *watcherStream) { func (w *watchGrpcStream) closeStream(ws *watcherStream) {
// cancels request stream; subscriber receives nil channel // cancels request stream; subscriber receives nil channel
close(ws.initReq.retc) close(ws.initReq.retc)
// close subscriber's channel // close subscriber's channel
close(ws.outc) close(ws.outc)
// shutdown serveStream
close(ws.recvc)
delete(w.streams, ws.id) delete(w.streams, ws.id)
} }
// run is the root of the goroutines for managing a watcher client // run is the root of the goroutines for managing a watcher client
func (w *watcher) run() { func (w *watchGrpcStream) run() {
var wc pb.Watch_WatchClient var wc pb.Watch_WatchClient
var closeErr error var closeErr error
defer func() { defer func() {
select { w.owner.mu.Lock()
case w.errc <- closeErr: w.closeErr = closeErr
default: if w.owner.streams != nil {
delete(w.owner.streams, w.ctxKey)
} }
close(w.donec) close(w.donec)
w.owner.mu.Unlock()
w.cancel() w.cancel()
}() }()
@ -308,6 +400,18 @@ func (w *watcher) run() {
curReqC = w.reqc curReqC = w.reqc
case pbresp.Canceled: case pbresp.Canceled:
delete(cancelSet, pbresp.WatchId) delete(cancelSet, pbresp.WatchId)
// shutdown serveStream, if any
w.mu.Lock()
if ws, ok := w.streams[pbresp.WatchId]; ok {
close(ws.recvc)
delete(w.streams, ws.id)
}
numStreams := len(w.streams)
w.mu.Unlock()
if numStreams == 0 {
// don't leak watcher streams
return
}
default: default:
// dispatch to appropriate watch stream // dispatch to appropriate watch stream
if ok := w.dispatchEvent(pbresp); ok { if ok := w.dispatchEvent(pbresp); ok {
@ -328,7 +432,11 @@ func (w *watcher) run() {
} }
// watch client failed to recv; spawn another if possible // watch client failed to recv; spawn another if possible
// TODO report watch client errors from errc? // TODO report watch client errors from errc?
case <-w.errc: case err := <-w.errc:
if toErr(w.ctx, err) == v3rpc.ErrNoLeader {
closeErr = err
return
}
if wc, closeErr = w.newWatchClient(); closeErr != nil { if wc, closeErr = w.newWatchClient(); closeErr != nil {
return return
} }
@ -357,7 +465,7 @@ func (w *watcher) run() {
} }
// dispatchEvent sends a WatchResponse to the appropriate watcher stream // dispatchEvent sends a WatchResponse to the appropriate watcher stream
func (w *watcher) dispatchEvent(pbresp *pb.WatchResponse) bool { func (w *watchGrpcStream) dispatchEvent(pbresp *pb.WatchResponse) bool {
w.mu.RLock() w.mu.RLock()
defer w.mu.RUnlock() defer w.mu.RUnlock()
ws, ok := w.streams[pbresp.WatchId] ws, ok := w.streams[pbresp.WatchId]
@ -377,7 +485,7 @@ func (w *watcher) dispatchEvent(pbresp *pb.WatchResponse) bool {
} }
// serveWatchClient forwards messages from the grpc stream to run() // serveWatchClient forwards messages from the grpc stream to run()
func (w *watcher) serveWatchClient(wc pb.Watch_WatchClient) { func (w *watchGrpcStream) serveWatchClient(wc pb.Watch_WatchClient) {
for { for {
resp, err := wc.Recv() resp, err := wc.Recv()
if err != nil { if err != nil {
@ -396,7 +504,7 @@ func (w *watcher) serveWatchClient(wc pb.Watch_WatchClient) {
} }
// serveStream forwards watch responses from run() to the subscriber // serveStream forwards watch responses from run() to the subscriber
func (w *watcher) serveStream(ws *watcherStream) { func (w *watchGrpcStream) serveStream(ws *watcherStream) {
emptyWr := &WatchResponse{} emptyWr := &WatchResponse{}
wrs := []*WatchResponse{} wrs := []*WatchResponse{}
resuming := false resuming := false
@ -465,13 +573,23 @@ func (w *watcher) serveStream(ws *watcherStream) {
closing = true closing = true
} }
} }
// try to send off close error
if w.closeErr != nil {
select {
case ws.outc <- WatchResponse{closeErr: w.closeErr}:
case <-w.donec:
case <-time.After(closeSendErrTimeout):
}
}
w.mu.Lock() w.mu.Lock()
w.closeStream(ws) w.closeStream(ws)
w.mu.Unlock() w.mu.Unlock()
// lazily send cancel message if events on missing id // lazily send cancel message if events on missing id
} }
func (w *watcher) newWatchClient() (pb.Watch_WatchClient, error) { func (w *watchGrpcStream) newWatchClient() (pb.Watch_WatchClient, error) {
ws, rerr := w.resume() ws, rerr := w.resume()
if rerr != nil { if rerr != nil {
return nil, rerr return nil, rerr
@ -481,7 +599,7 @@ func (w *watcher) newWatchClient() (pb.Watch_WatchClient, error) {
} }
// resume creates a new WatchClient with all current watchers reestablished // resume creates a new WatchClient with all current watchers reestablished
func (w *watcher) resume() (ws pb.Watch_WatchClient, err error) { func (w *watchGrpcStream) resume() (ws pb.Watch_WatchClient, err error) {
for { for {
if ws, err = w.openWatchClient(); err != nil { if ws, err = w.openWatchClient(); err != nil {
break break
@ -493,7 +611,7 @@ func (w *watcher) resume() (ws pb.Watch_WatchClient, err error) {
} }
// openWatchClient retries opening a watchclient until retryConnection fails // openWatchClient retries opening a watchclient until retryConnection fails
func (w *watcher) openWatchClient() (ws pb.Watch_WatchClient, err error) { func (w *watchGrpcStream) openWatchClient() (ws pb.Watch_WatchClient, err error) {
for { for {
select { select {
case <-w.stopc: case <-w.stopc:
@ -514,7 +632,7 @@ func (w *watcher) openWatchClient() (ws pb.Watch_WatchClient, err error) {
} }
// resumeWatchers rebuilds every registered watcher on a new client // resumeWatchers rebuilds every registered watcher on a new client
func (w *watcher) resumeWatchers(wc pb.Watch_WatchClient) error { func (w *watchGrpcStream) resumeWatchers(wc pb.Watch_WatchClient) error {
w.mu.RLock() w.mu.RLock()
streams := make([]*watcherStream, 0, len(w.streams)) streams := make([]*watcherStream, 0, len(w.streams))
for _, ws := range w.streams { for _, ws := range w.streams {

View File

@ -222,6 +222,10 @@ type RangeRequest struct {
// a serializable range request is served locally without needing to reach consensus // a serializable range request is served locally without needing to reach consensus
// with other nodes in the cluster. // with other nodes in the cluster.
Serializable bool `protobuf:"varint,7,opt,name=serializable,proto3" json:"serializable,omitempty"` Serializable bool `protobuf:"varint,7,opt,name=serializable,proto3" json:"serializable,omitempty"`
// keys_only when set returns only the keys and not the values.
KeysOnly bool `protobuf:"varint,8,opt,name=keys_only,json=keysOnly,proto3" json:"keys_only,omitempty"`
// count_only when set returns only the count of the keys in the range.
CountOnly bool `protobuf:"varint,9,opt,name=count_only,json=countOnly,proto3" json:"count_only,omitempty"`
} }
func (m *RangeRequest) Reset() { *m = RangeRequest{} } func (m *RangeRequest) Reset() { *m = RangeRequest{} }
@ -232,9 +236,12 @@ func (*RangeRequest) Descriptor() ([]byte, []int) { return fileDescriptorRpc, []
type RangeResponse struct { type RangeResponse struct {
Header *ResponseHeader `protobuf:"bytes,1,opt,name=header" json:"header,omitempty"` Header *ResponseHeader `protobuf:"bytes,1,opt,name=header" json:"header,omitempty"`
// kvs is the list of key-value pairs matched by the range request. // kvs is the list of key-value pairs matched by the range request.
// kvs is empty when count is requested.
Kvs []*mvccpb.KeyValue `protobuf:"bytes,2,rep,name=kvs" json:"kvs,omitempty"` Kvs []*mvccpb.KeyValue `protobuf:"bytes,2,rep,name=kvs" json:"kvs,omitempty"`
// more indicates if there are more keys to return in the requested range. // more indicates if there are more keys to return in the requested range.
More bool `protobuf:"varint,3,opt,name=more,proto3" json:"more,omitempty"` More bool `protobuf:"varint,3,opt,name=more,proto3" json:"more,omitempty"`
// count is set to the number of keys within the range when requested.
Count int64 `protobuf:"varint,4,opt,name=count,proto3" json:"count,omitempty"`
} }
func (m *RangeResponse) Reset() { *m = RangeResponse{} } func (m *RangeResponse) Reset() { *m = RangeResponse{} }
@ -3648,6 +3655,26 @@ func (m *RangeRequest) MarshalTo(data []byte) (int, error) {
} }
i++ i++
} }
if m.KeysOnly {
data[i] = 0x40
i++
if m.KeysOnly {
data[i] = 1
} else {
data[i] = 0
}
i++
}
if m.CountOnly {
data[i] = 0x48
i++
if m.CountOnly {
data[i] = 1
} else {
data[i] = 0
}
i++
}
return i, nil return i, nil
} }
@ -3698,6 +3725,11 @@ func (m *RangeResponse) MarshalTo(data []byte) (int, error) {
} }
i++ i++
} }
if m.Count != 0 {
data[i] = 0x20
i++
i = encodeVarintRpc(data, i, uint64(m.Count))
}
return i, nil return i, nil
} }
@ -6199,6 +6231,12 @@ func (m *RangeRequest) Size() (n int) {
if m.Serializable { if m.Serializable {
n += 2 n += 2
} }
if m.KeysOnly {
n += 2
}
if m.CountOnly {
n += 2
}
return n return n
} }
@ -6218,6 +6256,9 @@ func (m *RangeResponse) Size() (n int) {
if m.More { if m.More {
n += 2 n += 2
} }
if m.Count != 0 {
n += 1 + sovRpc(uint64(m.Count))
}
return n return n
} }
@ -7558,6 +7599,46 @@ func (m *RangeRequest) Unmarshal(data []byte) error {
} }
} }
m.Serializable = bool(v != 0) m.Serializable = bool(v != 0)
case 8:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field KeysOnly", wireType)
}
var v int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := data[iNdEx]
iNdEx++
v |= (int(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
m.KeysOnly = bool(v != 0)
case 9:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field CountOnly", wireType)
}
var v int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := data[iNdEx]
iNdEx++
v |= (int(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
m.CountOnly = bool(v != 0)
default: default:
iNdEx = preIndex iNdEx = preIndex
skippy, err := skipRpc(data[iNdEx:]) skippy, err := skipRpc(data[iNdEx:])
@ -7692,6 +7773,25 @@ func (m *RangeResponse) Unmarshal(data []byte) error {
} }
} }
m.More = bool(v != 0) m.More = bool(v != 0)
case 4:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field Count", wireType)
}
m.Count = 0
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := data[iNdEx]
iNdEx++
m.Count |= (int64(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
default: default:
iNdEx = preIndex iNdEx = preIndex
skippy, err := skipRpc(data[iNdEx:]) skippy, err := skipRpc(data[iNdEx:])
@ -15025,198 +15125,200 @@ var (
) )
var fileDescriptorRpc = []byte{ var fileDescriptorRpc = []byte{
// 3078 bytes of a gzipped FileDescriptorProto // 3116 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xb4, 0x5a, 0x4b, 0x6f, 0x23, 0xc7, 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xb4, 0x5a, 0x4b, 0x73, 0x24, 0x47,
0xf1, 0xdf, 0x21, 0xa9, 0x07, 0x8b, 0x14, 0x57, 0xdb, 0xd2, 0xae, 0xa5, 0x59, 0xad, 0x56, 0xdb, 0x11, 0xde, 0x79, 0xe8, 0x31, 0x39, 0xa3, 0x59, 0x6d, 0x49, 0xbb, 0x1e, 0xf5, 0x6a, 0xb5, 0xda,
0xfb, 0xf4, 0x4b, 0xfc, 0x5b, 0xf6, 0x3f, 0x87, 0x24, 0x30, 0x40, 0x89, 0xcc, 0x5a, 0x96, 0x2c, 0xda, 0xa7, 0x5f, 0x1a, 0x2c, 0x1b, 0x0e, 0x40, 0x38, 0x62, 0xa4, 0x19, 0xd6, 0xb2, 0x64, 0x69,
0xad, 0x47, 0x5c, 0xd9, 0x01, 0x82, 0x08, 0x23, 0xb2, 0x57, 0x22, 0xc4, 0x97, 0x67, 0x86, 0xda, 0xdd, 0x9a, 0x95, 0x4d, 0x04, 0x81, 0xa2, 0x35, 0x53, 0x2b, 0x4d, 0x68, 0x5e, 0xee, 0xee, 0xd1,
0x5d, 0x27, 0x06, 0x02, 0x23, 0x3e, 0x24, 0x57, 0x07, 0x08, 0x92, 0x1c, 0xf3, 0x19, 0x72, 0xcb, 0x4a, 0x06, 0x22, 0x08, 0x07, 0x3e, 0xc0, 0xd5, 0x07, 0x02, 0x38, 0xf2, 0x1b, 0xb8, 0xf1, 0x03,
0x07, 0x08, 0x72, 0x49, 0x80, 0x1c, 0x73, 0x09, 0x82, 0x1c, 0x72, 0xc8, 0x3d, 0xc8, 0x29, 0xe9, 0x08, 0x2e, 0x38, 0x82, 0x23, 0x17, 0x82, 0xe0, 0xc0, 0x81, 0x3b, 0xc1, 0x09, 0xea, 0xd9, 0x5d,
0xe7, 0x4c, 0xcf, 0xb0, 0x87, 0x92, 0x33, 0xf1, 0x61, 0x57, 0xd3, 0xd5, 0xd5, 0xf5, 0xab, 0xae, 0xdd, 0x53, 0x3d, 0x92, 0x69, 0x7c, 0xd8, 0x55, 0x57, 0x56, 0x56, 0x7e, 0x59, 0x59, 0x95, 0xd9,
0xee, 0xaa, 0xa9, 0xaa, 0x21, 0x14, 0xbd, 0x61, 0x6b, 0x7d, 0xe8, 0x0d, 0x82, 0x01, 0x2a, 0x93, 0x99, 0xd9, 0x03, 0x05, 0x77, 0xd8, 0x5a, 0x1b, 0xba, 0x03, 0x7f, 0x80, 0x4a, 0xc4, 0x6f, 0xb5,
0xa0, 0xd5, 0xf6, 0x89, 0x77, 0x4e, 0xbc, 0xe1, 0xb1, 0xbd, 0x78, 0x32, 0x38, 0x19, 0xf0, 0x89, 0x3d, 0xe2, 0x9e, 0x11, 0x77, 0x78, 0x64, 0x2d, 0x1e, 0x0f, 0x8e, 0x07, 0x7c, 0xa2, 0xca, 0x9e,
0x2a, 0x7b, 0x12, 0x3c, 0xf6, 0x32, 0xe3, 0xa9, 0xf6, 0xce, 0x5b, 0x2d, 0xfe, 0xdf, 0xf0, 0xb8, 0x04, 0x8f, 0xb5, 0xc4, 0x78, 0xaa, 0xbd, 0xb3, 0x56, 0x8b, 0xff, 0x37, 0x3c, 0xaa, 0x9e, 0x9e,
0x7a, 0x76, 0x2e, 0xa7, 0x6e, 0xf2, 0x29, 0x77, 0x14, 0x9c, 0xf2, 0xff, 0xe8, 0x14, 0xfb, 0x23, 0xc9, 0xa9, 0xdb, 0x7c, 0xca, 0x19, 0xf9, 0x27, 0xfc, 0x3f, 0x3a, 0xc5, 0xfe, 0xc8, 0xc9, 0xe5,
0x27, 0x57, 0x4e, 0x06, 0x83, 0x93, 0x2e, 0xa9, 0xba, 0xc3, 0x4e, 0xd5, 0xed, 0xf7, 0x07, 0x81, 0xe3, 0xc1, 0xe0, 0xb8, 0x4b, 0xaa, 0xce, 0xb0, 0x53, 0x75, 0xfa, 0xfd, 0x81, 0xef, 0xf8, 0x9d,
0x1b, 0x74, 0x06, 0x7d, 0x5f, 0xcc, 0xe2, 0x2f, 0x2c, 0xa8, 0x38, 0xc4, 0x1f, 0x52, 0x0a, 0x79, 0x41, 0xdf, 0x13, 0xb3, 0xf8, 0xf3, 0x0c, 0x94, 0x6d, 0xe2, 0x0d, 0x29, 0x85, 0xbc, 0x47, 0x9c,
0x8f, 0xb8, 0x6d, 0xe2, 0xa1, 0x5b, 0x00, 0xad, 0xee, 0xc8, 0x0f, 0x88, 0x77, 0xd4, 0x69, 0x2f, 0x36, 0x71, 0xd1, 0x1d, 0x80, 0x56, 0x77, 0xe4, 0xf9, 0xc4, 0x3d, 0xec, 0xb4, 0x2b, 0x99, 0xd5,
0x59, 0x6b, 0xd6, 0xa3, 0x82, 0x53, 0x94, 0x94, 0xed, 0x36, 0xba, 0x09, 0xc5, 0x1e, 0xe9, 0x1d, 0xcc, 0x93, 0xbc, 0x5d, 0x90, 0x94, 0xad, 0x36, 0xba, 0x0d, 0x85, 0x1e, 0xe9, 0x1d, 0x89, 0xd9,
0x8b, 0xd9, 0x1c, 0x9f, 0x9d, 0x15, 0x04, 0x3a, 0x69, 0xc3, 0xac, 0x47, 0xce, 0x3b, 0x3e, 0x45, 0x2c, 0x9f, 0x9d, 0x15, 0x04, 0x3a, 0x69, 0xc1, 0xac, 0x4b, 0xce, 0x3a, 0x1e, 0x45, 0xa8, 0xe4,
0x58, 0xca, 0xd3, 0xb9, 0xbc, 0x13, 0x8e, 0xd9, 0x42, 0xcf, 0x7d, 0x16, 0x1c, 0x51, 0x31, 0xbd, 0xe8, 0x5c, 0xce, 0x0e, 0xc6, 0x6c, 0xa1, 0xeb, 0xbc, 0xf0, 0x0f, 0xa9, 0x98, 0x5e, 0x25, 0x2f,
0xa5, 0x82, 0x58, 0xc8, 0x08, 0x4d, 0x3a, 0xc6, 0x3f, 0xcb, 0x43, 0xd9, 0x71, 0xfb, 0x27, 0xc4, 0x16, 0x32, 0x42, 0x93, 0x8e, 0xf1, 0x97, 0x39, 0x28, 0xd9, 0x4e, 0xff, 0x98, 0xd8, 0xe4, 0x93,
0x21, 0x9f, 0x8c, 0x88, 0x1f, 0xa0, 0x79, 0xc8, 0x9f, 0x91, 0x97, 0x1c, 0xbe, 0xec, 0xb0, 0x47, 0x11, 0xf1, 0x7c, 0x34, 0x0f, 0xb9, 0x53, 0x72, 0xc1, 0xe1, 0x4b, 0x36, 0x7b, 0x14, 0xeb, 0x29,
0xb1, 0x9e, 0x72, 0x1c, 0x91, 0xbe, 0x00, 0x2e, 0xb3, 0xf5, 0x94, 0xd0, 0xe8, 0xb7, 0xd1, 0x22, 0xc7, 0x21, 0xe9, 0x0b, 0xe0, 0x12, 0x5b, 0x4f, 0x09, 0x8d, 0x7e, 0x1b, 0x2d, 0xc2, 0x54, 0xb7,
0x4c, 0x75, 0x3b, 0xbd, 0x4e, 0x20, 0x51, 0xc5, 0x20, 0xa6, 0x4e, 0x21, 0xa1, 0xce, 0x16, 0x80, 0xd3, 0xeb, 0xf8, 0x12, 0x55, 0x0c, 0x22, 0xea, 0xe4, 0x63, 0xea, 0x6c, 0x02, 0x78, 0x03, 0xd7,
0x3f, 0xf0, 0x82, 0xa3, 0x81, 0x47, 0x37, 0xbd, 0x34, 0x45, 0x67, 0x2b, 0x1b, 0xf7, 0xd6, 0xf5, 0x3f, 0x1c, 0xb8, 0x74, 0xd3, 0x95, 0x29, 0x3a, 0x5b, 0x5e, 0x7f, 0xb0, 0xa6, 0x1f, 0xc4, 0x9a,
0x83, 0x58, 0xd7, 0x15, 0x5a, 0x3f, 0xa0, 0xcc, 0xfb, 0x8c, 0xd7, 0x29, 0xfa, 0xea, 0x11, 0x7d, 0xae, 0xd0, 0xda, 0x3e, 0x65, 0xde, 0x63, 0xbc, 0x76, 0xc1, 0x53, 0x8f, 0xe8, 0x7b, 0x50, 0xe4,
0x07, 0x4a, 0x5c, 0x48, 0xe0, 0x7a, 0x27, 0x24, 0x58, 0x9a, 0xe6, 0x52, 0xee, 0x5f, 0x20, 0xa5, 0x42, 0x7c, 0xc7, 0x3d, 0x26, 0x7e, 0x65, 0x9a, 0x4b, 0x79, 0x78, 0x89, 0x94, 0x26, 0x67, 0xb6,
0xc9, 0x99, 0x1d, 0x0e, 0x2f, 0x9e, 0x11, 0x86, 0x32, 0xe5, 0xef, 0xb8, 0xdd, 0xce, 0xa7, 0xee, 0x39, 0xbc, 0x78, 0x46, 0x18, 0x4a, 0x94, 0xbf, 0xe3, 0x74, 0x3b, 0x9f, 0x3a, 0x47, 0x5d, 0x52,
0x71, 0x97, 0x2c, 0xcd, 0x50, 0x41, 0xb3, 0x4e, 0x8c, 0x86, 0xd7, 0xa1, 0x18, 0xea, 0x80, 0x66, 0x99, 0xa1, 0x82, 0x66, 0xed, 0x08, 0x8d, 0xed, 0x9f, 0x9a, 0xc1, 0x3b, 0x1c, 0xf4, 0xbb, 0x17,
0xa1, 0xb0, 0xb7, 0xbf, 0xd7, 0x98, 0xbf, 0x82, 0x00, 0xa6, 0x6b, 0x07, 0x5b, 0x8d, 0xbd, 0xfa, 0x95, 0x59, 0xce, 0x30, 0xcb, 0x08, 0x7b, 0x74, 0xcc, 0x0f, 0x6d, 0x30, 0xea, 0xfb, 0x62, 0xb6,
0xbc, 0x85, 0x4a, 0x30, 0x53, 0x6f, 0x88, 0x41, 0x0e, 0x6f, 0x02, 0x44, 0x68, 0x68, 0x06, 0xf2, 0xc0, 0x67, 0x0b, 0x9c, 0xc2, 0xa6, 0xf1, 0x1a, 0x14, 0x02, 0xfd, 0xd1, 0x2c, 0xe4, 0x77, 0xf7,
0x3b, 0x8d, 0xef, 0x52, 0x7e, 0xca, 0x73, 0xd8, 0x70, 0x0e, 0xb6, 0xf7, 0xf7, 0xe8, 0x02, 0xba, 0x76, 0x1b, 0xf3, 0xd7, 0x10, 0xc0, 0x74, 0x6d, 0x7f, 0xb3, 0xb1, 0x5b, 0x9f, 0xcf, 0xa0, 0x22,
0x78, 0xcb, 0x69, 0xd4, 0x9a, 0x8d, 0xf9, 0x1c, 0xe3, 0xf8, 0x60, 0xbf, 0x3e, 0x9f, 0x47, 0x45, 0xcc, 0xd4, 0x1b, 0x62, 0x90, 0xc5, 0x1b, 0x00, 0xa1, 0xa6, 0x68, 0x06, 0x72, 0xdb, 0x8d, 0xef,
0x98, 0x3a, 0xac, 0xed, 0x3e, 0x6d, 0xcc, 0x17, 0xf0, 0x67, 0x30, 0x27, 0xd5, 0x17, 0x57, 0x04, 0x53, 0x7e, 0xca, 0x73, 0xd0, 0xb0, 0xf7, 0xb7, 0xf6, 0x76, 0xe9, 0x02, 0xba, 0x78, 0xd3, 0x6e,
0xbd, 0x03, 0xd3, 0xa7, 0xfc, 0x9a, 0xf0, 0x93, 0x29, 0x6d, 0xac, 0x24, 0xf6, 0x1a, 0xbb, 0x4a, 0xd4, 0x9a, 0x8d, 0xf9, 0x2c, 0xe3, 0xf8, 0x60, 0xaf, 0x3e, 0x9f, 0x43, 0x05, 0x98, 0x3a, 0xa8,
0x8e, 0xe4, 0xa5, 0xdb, 0xcb, 0x9f, 0x9d, 0xfb, 0xf4, 0xd0, 0xf2, 0x74, 0xc9, 0xfc, 0xba, 0xb8, 0xed, 0x3c, 0x6f, 0xcc, 0xe7, 0xf1, 0x17, 0x19, 0x98, 0x93, 0x7b, 0x17, 0xf7, 0x0b, 0xbd, 0x03,
0xbf, 0xeb, 0x3b, 0xe4, 0xe5, 0xa1, 0xdb, 0x1d, 0x11, 0x87, 0x4d, 0x22, 0x04, 0x85, 0xde, 0xc0, 0xd3, 0x27, 0xfc, 0x8e, 0xf1, 0x63, 0x2d, 0xae, 0x2f, 0xc7, 0x0c, 0x15, 0xb9, 0x87, 0xb6, 0xe4,
0x23, 0xfc, 0x00, 0x67, 0x1d, 0xfe, 0x8c, 0xdf, 0x07, 0x78, 0x32, 0x0a, 0xd2, 0xaf, 0x04, 0x3d, 0xa5, 0xb6, 0xc9, 0x9d, 0x9e, 0x79, 0xf4, 0xc4, 0x73, 0x74, 0xc9, 0xfc, 0x9a, 0xb8, 0xfc, 0x6b,
0xf5, 0x73, 0x26, 0x41, 0x5e, 0x07, 0x31, 0xe0, 0x77, 0x81, 0xb8, 0x3e, 0x09, 0xef, 0x02, 0x1b, 0xdb, 0xe4, 0xe2, 0xc0, 0xe9, 0x8e, 0x88, 0xcd, 0x26, 0x11, 0x82, 0x7c, 0x6f, 0xe0, 0x12, 0x7e,
0xe0, 0x2d, 0x28, 0x71, 0x59, 0x59, 0x36, 0x42, 0x85, 0xa0, 0x3a, 0xe9, 0x92, 0x80, 0x64, 0xb8, 0xfa, 0xb3, 0x36, 0x7f, 0x66, 0x57, 0x82, 0x1b, 0x40, 0x9e, 0xbc, 0x18, 0xe0, 0xf7, 0x01, 0x9e,
0xab, 0x98, 0xc0, 0x42, 0x4c, 0x48, 0x26, 0xd3, 0x2e, 0xc1, 0x4c, 0x9b, 0x0b, 0x13, 0x38, 0x79, 0x8d, 0xfc, 0xe4, 0x5b, 0x46, 0x57, 0x9d, 0x31, 0xb9, 0xf2, 0x86, 0x89, 0x01, 0xbf, 0x5e, 0xc4,
0x47, 0x0d, 0xf1, 0x3f, 0x2c, 0x28, 0x4a, 0x0d, 0xf7, 0x87, 0xa8, 0x06, 0x73, 0x9e, 0x18, 0x1c, 0xf1, 0x48, 0x70, 0xbd, 0xd8, 0x00, 0x6f, 0x42, 0x91, 0xcb, 0x4a, 0xb3, 0x3d, 0x2a, 0x04, 0xd5,
0x71, 0x45, 0x24, 0x88, 0x9d, 0x7e, 0x57, 0xdf, 0xbb, 0xe2, 0x94, 0xe5, 0x12, 0x4e, 0x46, 0xdf, 0x49, 0x97, 0xf8, 0x24, 0xc5, 0xf5, 0xc7, 0x04, 0x16, 0x22, 0x42, 0x52, 0x19, 0xbc, 0x02, 0x33,
0x82, 0x92, 0x12, 0x31, 0x1c, 0x05, 0x1c, 0xae, 0xb4, 0xb1, 0x14, 0x17, 0x10, 0x1d, 0x17, 0x5d, 0x6d, 0x2e, 0x4c, 0xe0, 0xe4, 0x6c, 0x35, 0xc4, 0xff, 0xcc, 0x40, 0x41, 0x6a, 0xb8, 0x37, 0x44,
0x0e, 0x92, 0x9d, 0x12, 0x51, 0x13, 0x16, 0xd5, 0x62, 0xa1, 0xa0, 0x54, 0x23, 0xcf, 0xa5, 0xac, 0x35, 0x98, 0x73, 0xc5, 0xe0, 0x90, 0x2b, 0x22, 0x41, 0xac, 0xe4, 0xeb, 0xff, 0xde, 0x35, 0xbb,
0xc5, 0xa5, 0x8c, 0xdb, 0x98, 0x4a, 0x43, 0x72, 0xbd, 0x36, 0xb9, 0x59, 0x84, 0x19, 0x49, 0xc5, 0x24, 0x97, 0x70, 0x32, 0xfa, 0x0e, 0x14, 0x95, 0x88, 0xe1, 0xc8, 0xe7, 0x70, 0xc5, 0xf5, 0x4a,
0xff, 0xb4, 0x00, 0x94, 0x8d, 0xe8, 0x7e, 0xeb, 0x50, 0xf1, 0xe4, 0x28, 0xb6, 0xe1, 0x9b, 0xc6, 0x54, 0x40, 0x78, 0x5c, 0x74, 0x39, 0x48, 0x76, 0x4a, 0x44, 0x4d, 0x58, 0x54, 0x8b, 0x85, 0x82,
0x0d, 0x4b, 0xd3, 0x5e, 0x71, 0xe6, 0xd4, 0x22, 0xb1, 0xe5, 0x77, 0xa1, 0x1c, 0x4a, 0x89, 0xf6, 0x52, 0x8d, 0x1c, 0x97, 0xb2, 0x1a, 0x95, 0x32, 0x6e, 0x63, 0x2a, 0x0d, 0xc9, 0xf5, 0xda, 0xe4,
0xbc, 0x6c, 0xd8, 0x73, 0x28, 0xa1, 0xa4, 0x16, 0xb0, 0x5d, 0x7f, 0x04, 0xd7, 0xc3, 0xf5, 0x86, 0x46, 0x01, 0x66, 0x24, 0x15, 0xff, 0x2b, 0x03, 0xa0, 0x6c, 0x44, 0xf7, 0x5b, 0x87, 0xb2, 0x2b,
0x6d, 0xdf, 0x99, 0xb0, 0xed, 0x50, 0xe0, 0x82, 0x92, 0xa0, 0x6f, 0x1c, 0x58, 0x64, 0x13, 0x64, 0x47, 0x91, 0x0d, 0xdf, 0x36, 0x6e, 0x58, 0x9a, 0xf6, 0x9a, 0x3d, 0xa7, 0x16, 0x89, 0x2d, 0xbf,
0xfc, 0xcb, 0x3c, 0xcc, 0x6c, 0x0d, 0x7a, 0x43, 0xd7, 0x63, 0x67, 0x34, 0x4d, 0xe9, 0xa3, 0x6e, 0x0b, 0xa5, 0x40, 0x4a, 0xb8, 0xe7, 0x25, 0xc3, 0x9e, 0x03, 0x09, 0x45, 0xb5, 0x80, 0xed, 0xfa,
0xc0, 0xb7, 0x5b, 0xd9, 0xb8, 0x1b, 0x47, 0x90, 0x6c, 0xea, 0xaf, 0xc3, 0x59, 0x1d, 0xb9, 0x84, 0x23, 0xb8, 0x19, 0xac, 0x37, 0x6c, 0xfb, 0xde, 0x84, 0x6d, 0x07, 0x02, 0x17, 0x94, 0x04, 0x7d,
0x2d, 0x96, 0x81, 0x2c, 0x77, 0x89, 0xc5, 0x32, 0x8c, 0xc9, 0x25, 0xca, 0x09, 0xf2, 0x91, 0x13, 0xe3, 0xc0, 0x82, 0xa5, 0x20, 0xe3, 0x5f, 0xe7, 0x60, 0x66, 0x73, 0xd0, 0x1b, 0x3a, 0x2e, 0x3b,
0xd8, 0x30, 0x43, 0x17, 0x46, 0xc1, 0x97, 0xee, 0x45, 0x11, 0xd0, 0xab, 0x70, 0xb5, 0xe5, 0x11, 0xa3, 0x69, 0x4a, 0x1f, 0x75, 0x7d, 0xbe, 0xdd, 0xf2, 0xfa, 0xfd, 0x28, 0x82, 0x64, 0x53, 0x7f,
0x97, 0xd9, 0x43, 0x05, 0xe8, 0x29, 0xc9, 0x53, 0x11, 0x13, 0x8e, 0x0a, 0xd4, 0x77, 0xa1, 0xdc, 0x6d, 0xce, 0x6a, 0xcb, 0x25, 0x6c, 0xb1, 0x8c, 0x8d, 0xd9, 0x2b, 0x2c, 0x96, 0x91, 0x51, 0x2e,
0x1b, 0xb4, 0x23, 0xbe, 0x69, 0xc9, 0x57, 0xa2, 0xd4, 0x90, 0xe9, 0x86, 0x8a, 0x04, 0x2c, 0x72, 0x51, 0x4e, 0x90, 0x0b, 0x9d, 0xc0, 0x82, 0x19, 0xba, 0x30, 0x8c, 0xe7, 0x74, 0x2f, 0x8a, 0x80,
0x96, 0xe9, 0xac, 0x18, 0xe2, 0xb7, 0x60, 0x2e, 0xb6, 0x57, 0x16, 0xdc, 0x1a, 0x1f, 0x3e, 0xad, 0x5e, 0x85, 0xeb, 0x2d, 0x97, 0x38, 0xcc, 0x1e, 0x2a, 0xe6, 0x4f, 0x49, 0x9e, 0xb2, 0x98, 0xb0,
0xed, 0x8a, 0x48, 0xf8, 0x98, 0x07, 0x3f, 0x87, 0x46, 0x42, 0x1a, 0x50, 0x77, 0x1b, 0x07, 0x07, 0x55, 0xec, 0xbf, 0x0f, 0xa5, 0xde, 0xa0, 0x1d, 0xf2, 0x4d, 0x4b, 0xbe, 0x22, 0xa5, 0x06, 0x4c,
0x34, 0x6e, 0x7e, 0x3b, 0x5c, 0x22, 0x43, 0xa7, 0x16, 0x31, 0xaf, 0x68, 0x11, 0xd3, 0x52, 0x11, 0xb7, 0x54, 0x24, 0x60, 0xc1, 0xb8, 0x44, 0x67, 0xc5, 0x10, 0xbf, 0x05, 0x73, 0x91, 0xbd, 0xb2,
0x33, 0x17, 0x45, 0xcc, 0xfc, 0x66, 0x05, 0xca, 0xc2, 0x20, 0x47, 0xa3, 0x3e, 0x55, 0x0c, 0xff, 0x98, 0xd7, 0xf8, 0xf0, 0x79, 0x6d, 0x47, 0x04, 0xc8, 0xa7, 0x3c, 0x26, 0xda, 0x34, 0x40, 0xd2,
0x9a, 0x5e, 0xcb, 0xe6, 0x8b, 0xbe, 0x0a, 0x15, 0x55, 0x98, 0x69, 0x09, 0xe1, 0xf4, 0x80, 0x58, 0x38, 0xbb, 0xd3, 0xd8, 0xdf, 0xa7, 0xe1, 0xf4, 0xbb, 0xc1, 0x12, 0x19, 0x51, 0xb5, 0x40, 0x7a,
0x34, 0xbc, 0x6e, 0xb4, 0xb1, 0xa3, 0xb8, 0xd0, 0x5b, 0x30, 0xe3, 0x8f, 0x5a, 0x2d, 0xe2, 0xab, 0x4d, 0x0b, 0xa4, 0x19, 0x15, 0x48, 0xb3, 0x61, 0x20, 0xcd, 0x6d, 0x94, 0xa1, 0x24, 0x0c, 0x72,
0xf0, 0xf9, 0x4a, 0x32, 0x2c, 0x48, 0x0f, 0x77, 0x14, 0x1f, 0x5b, 0xf2, 0xcc, 0xed, 0x74, 0x47, 0x38, 0xea, 0x53, 0xc5, 0xf0, 0x6f, 0xe9, 0xb5, 0x6c, 0x9e, 0xf7, 0x55, 0xa8, 0xa8, 0xc2, 0x4c,
0x3c, 0x98, 0x4e, 0x5e, 0x22, 0xf9, 0xf0, 0x2f, 0x2c, 0x28, 0x71, 0x2d, 0x33, 0xc5, 0xa2, 0x15, 0x4b, 0x08, 0xa7, 0x07, 0xc4, 0x62, 0xe4, 0x4d, 0xa3, 0x8d, 0x6d, 0xc5, 0x85, 0xde, 0x82, 0x19,
0x28, 0x72, 0x1d, 0x48, 0x5b, 0x46, 0xa3, 0x59, 0x27, 0x22, 0xa0, 0x6f, 0xd0, 0x98, 0x28, 0xd7, 0x6f, 0xd4, 0x6a, 0x11, 0x4f, 0x05, 0xd5, 0x57, 0xe2, 0x61, 0x41, 0x7a, 0xb8, 0xad, 0xf8, 0xd8,
0xf9, 0x52, 0xb1, 0x25, 0xb3, 0x58, 0xaa, 0x59, 0xc4, 0x8a, 0x77, 0xe0, 0x1a, 0xb7, 0x4a, 0x8b, 0x92, 0x17, 0x4e, 0xa7, 0x3b, 0xe2, 0x21, 0x76, 0xf2, 0x12, 0xc9, 0x87, 0x7f, 0x95, 0x81, 0x22,
0xe5, 0x2d, 0xca, 0x8e, 0xfa, 0x9b, 0xdd, 0x4a, 0xbc, 0xd9, 0xe9, 0xdc, 0xf0, 0xf4, 0xa5, 0xdf, 0xd7, 0x32, 0x55, 0x2c, 0x5a, 0x86, 0x02, 0xd7, 0x81, 0xb4, 0x65, 0x34, 0xa2, 0xaf, 0xb5, 0x80,
0x69, 0xb9, 0x5d, 0xa9, 0x45, 0x38, 0xa6, 0x6f, 0x14, 0xa4, 0x0b, 0xcb, 0xf4, 0x32, 0x98, 0x83, 0x80, 0xbe, 0x45, 0x63, 0xa2, 0x5c, 0xe7, 0x49, 0xc5, 0x2a, 0x66, 0xb1, 0x54, 0xb3, 0x90, 0x15,
0xd2, 0x7b, 0xae, 0x7f, 0x2a, 0x55, 0xc2, 0x1f, 0x43, 0x59, 0x0c, 0x33, 0xd9, 0x90, 0xbe, 0x06, 0x6f, 0xc3, 0x0d, 0x6e, 0x95, 0x16, 0x4b, 0x85, 0x94, 0x1d, 0xf5, 0x64, 0x21, 0x13, 0x4b, 0x16,
0x4f, 0xa9, 0x14, 0xae, 0xf8, 0x9c, 0xc3, 0x9f, 0xf1, 0x35, 0xb8, 0x7a, 0xd0, 0x77, 0x87, 0xfe, 0xe8, 0xdc, 0xf0, 0xe4, 0xc2, 0xeb, 0xb4, 0x9c, 0xae, 0xd4, 0x22, 0x18, 0xd3, 0x37, 0x0a, 0xd2,
0xe9, 0x40, 0x05, 0x57, 0x96, 0xb7, 0xcd, 0x47, 0xb4, 0x4c, 0x88, 0x0f, 0xe1, 0xaa, 0x47, 0x7a, 0x85, 0xa5, 0x7a, 0x19, 0xcc, 0x41, 0xf1, 0x3d, 0xc7, 0x3b, 0x91, 0x2a, 0xe1, 0x8f, 0xa1, 0x24,
0x6e, 0xa7, 0xdf, 0xe9, 0x9f, 0x1c, 0x1d, 0xbf, 0x0c, 0x88, 0x2f, 0xd3, 0xba, 0x4a, 0x48, 0xde, 0x86, 0xa9, 0x6c, 0x48, 0x5f, 0x8e, 0x27, 0x54, 0x0a, 0x57, 0x7c, 0xce, 0xe6, 0xcf, 0xf8, 0x06,
0x64, 0x54, 0xa6, 0xda, 0x71, 0x77, 0x70, 0x2c, 0x5d, 0x9c, 0x3f, 0xe3, 0xdf, 0x58, 0x50, 0xfe, 0x5c, 0xdf, 0xef, 0x3b, 0x43, 0xef, 0x64, 0xa0, 0x82, 0x2b, 0x4b, 0x05, 0xe7, 0x43, 0x5a, 0x2a,
0xc8, 0x0d, 0x5a, 0xca, 0x0a, 0x68, 0x1b, 0x2a, 0xa1, 0x63, 0x73, 0x8a, 0xd4, 0x25, 0x11, 0xe1, 0xc4, 0xc7, 0x70, 0xdd, 0x25, 0x3d, 0xa7, 0xd3, 0xef, 0xf4, 0x8f, 0x0f, 0x8f, 0x2e, 0x7c, 0xe2,
0xf9, 0x9a, 0x2d, 0xe9, 0xe8, 0x2a, 0xc2, 0xcf, 0xb5, 0x74, 0x02, 0x17, 0xe5, 0xf6, 0x5b, 0xa4, 0xc9, 0x4c, 0xb1, 0x1c, 0x90, 0x37, 0x18, 0x95, 0xa9, 0x76, 0xd4, 0x1d, 0x1c, 0x49, 0x17, 0xe7,
0x1b, 0x8a, 0xca, 0xa5, 0x8b, 0xe2, 0x8c, 0xba, 0x28, 0x9d, 0xb0, 0x79, 0x35, 0x7a, 0xfb, 0x09, 0xcf, 0xf8, 0x77, 0x19, 0x28, 0x7d, 0xe4, 0xf8, 0x2d, 0x65, 0x05, 0xb4, 0x05, 0xe5, 0xc0, 0xb1,
0xb7, 0xfc, 0xd2, 0x02, 0x34, 0xae, 0xc3, 0x57, 0xcd, 0x3a, 0xef, 0x43, 0xc5, 0xa7, 0xde, 0x1e, 0x39, 0x45, 0xea, 0x12, 0x8b, 0xf0, 0x7c, 0xcd, 0xa6, 0x74, 0x74, 0x15, 0xe1, 0xe7, 0x5a, 0x3a,
0x1c, 0x25, 0x92, 0xde, 0x39, 0x4e, 0x0d, 0x83, 0x13, 0xb5, 0x30, 0xcd, 0xb6, 0x4f, 0xe8, 0x95, 0x81, 0x8b, 0x72, 0xfa, 0x2d, 0xd2, 0x0d, 0x44, 0x65, 0x93, 0x45, 0x71, 0x46, 0x5d, 0x94, 0x4e,
0xf6, 0x8f, 0x68, 0x02, 0xde, 0x79, 0xf6, 0x92, 0x07, 0xc4, 0x59, 0xa7, 0xa2, 0xc8, 0x7b, 0x9c, 0xd8, 0xb8, 0x1e, 0xbe, 0xfd, 0x84, 0x5b, 0xd2, 0x7c, 0x07, 0x8d, 0xeb, 0xf0, 0x55, 0x13, 0xd9,
0x8a, 0xab, 0x4a, 0x29, 0x5d, 0x79, 0xb4, 0x0c, 0xb3, 0xcf, 0x19, 0x55, 0xa5, 0xe3, 0xf4, 0x1d, 0x87, 0x50, 0xf6, 0xa8, 0xb7, 0xfb, 0x87, 0xb1, 0x3c, 0x7a, 0x8e, 0x53, 0x83, 0xe0, 0x44, 0x2d,
0xcf, 0xc7, 0xdb, 0x6d, 0xfc, 0x77, 0x0b, 0xe6, 0xa4, 0xf9, 0x33, 0xdd, 0x01, 0x1d, 0x22, 0x17, 0x4c, 0x13, 0xf8, 0x63, 0x7a, 0xa5, 0xbd, 0x43, 0x9a, 0xd3, 0x77, 0x5e, 0x5c, 0xf0, 0x80, 0x38,
0x83, 0x60, 0x09, 0x86, 0x38, 0x96, 0xb6, 0x4c, 0xcd, 0xd4, 0x90, 0xf9, 0x99, 0xb0, 0x32, 0x9d, 0x6b, 0x97, 0x15, 0x79, 0x97, 0x53, 0x71, 0x55, 0x29, 0xa5, 0x2b, 0x8f, 0x96, 0x60, 0xf6, 0x25,
0x12, 0xfb, 0x09, 0xc7, 0x34, 0xbe, 0xcf, 0xb7, 0x84, 0x9f, 0x25, 0x02, 0xbc, 0x73, 0x55, 0xd2, 0xa3, 0xaa, 0x0c, 0x9f, 0xbe, 0xe3, 0xf9, 0x78, 0xab, 0x8d, 0xff, 0x41, 0xd3, 0x36, 0x69, 0xfe,
0x43, 0xeb, 0xdc, 0x87, 0x69, 0x72, 0x4e, 0xfa, 0x81, 0xbf, 0x54, 0xe2, 0x41, 0x61, 0x4e, 0xe5, 0x54, 0x77, 0x40, 0x87, 0xc8, 0x46, 0x20, 0x58, 0x82, 0x21, 0x8e, 0xa5, 0x2d, 0x13, 0x36, 0x35,
0x87, 0x0d, 0x46, 0x75, 0xe4, 0x24, 0xfe, 0x7f, 0xb8, 0xb6, 0xcb, 0x12, 0xb9, 0xc7, 0xd4, 0xfa, 0x64, 0x7e, 0x26, 0xac, 0x4c, 0xa7, 0xc4, 0x7e, 0x82, 0x31, 0x8d, 0xef, 0xf3, 0x2d, 0xe1, 0x67,
0x7a, 0x4a, 0xd8, 0x6c, 0xee, 0x4a, 0xab, 0xe4, 0x83, 0xe6, 0x2e, 0xaa, 0x40, 0x6e, 0xbb, 0x2e, 0xb1, 0x00, 0x6f, 0x5f, 0x97, 0xf4, 0xc0, 0x3a, 0x0f, 0x61, 0x9a, 0x9c, 0x91, 0xbe, 0xef, 0x55,
0xf7, 0x90, 0xeb, 0xd4, 0xf1, 0xe7, 0xf4, 0xa0, 0xf5, 0x75, 0x99, 0xcc, 0x94, 0x10, 0xae, 0xe0, 0x8a, 0x3c, 0x28, 0xcc, 0xa9, 0xac, 0xb1, 0xc1, 0xa8, 0xb6, 0x9c, 0xc4, 0xdf, 0x84, 0x1b, 0x3b,
0xf3, 0x11, 0x3c, 0xcd, 0x3d, 0x89, 0xe7, 0x0d, 0x3c, 0x6e, 0x90, 0xa2, 0x23, 0x06, 0xf8, 0x9e, 0x2c, 0x91, 0x7b, 0x4a, 0xad, 0xaf, 0xa7, 0x84, 0xcd, 0xe6, 0x8e, 0xb4, 0x4a, 0xce, 0x6f, 0xee,
0xd4, 0x81, 0xee, 0x79, 0x70, 0x16, 0x5e, 0x36, 0x21, 0xcd, 0x0a, 0x55, 0xdd, 0x81, 0x85, 0x18, 0xa0, 0x32, 0x64, 0xb7, 0xea, 0x72, 0x0f, 0xd9, 0x4e, 0x1d, 0x7f, 0x46, 0x0f, 0x5a, 0x5f, 0x97,
0x57, 0xa6, 0xe0, 0xf4, 0x10, 0xae, 0x73, 0x61, 0x3b, 0x84, 0x0c, 0x6b, 0xdd, 0xce, 0x79, 0x2a, 0xca, 0x4c, 0x31, 0xe1, 0x0a, 0x3e, 0x17, 0xc2, 0xd3, 0xdc, 0x93, 0xb8, 0xee, 0xc0, 0xe5, 0x06,
0xea, 0x10, 0x6e, 0x24, 0x19, 0xbf, 0x5e, 0x1b, 0xe1, 0x53, 0x98, 0xfe, 0x80, 0x17, 0x8c, 0x9a, 0x29, 0xd8, 0x62, 0x80, 0x1f, 0x48, 0x1d, 0xe8, 0x9e, 0x07, 0xa7, 0xc1, 0x65, 0x13, 0xd2, 0x32,
0x2e, 0x05, 0xce, 0x4b, 0x23, 0x4c, 0xdf, 0xed, 0x89, 0x74, 0xbe, 0xe8, 0xf0, 0x67, 0x1e, 0xcd, 0x81, 0xaa, 0xdb, 0xb0, 0x10, 0xe1, 0x4a, 0x15, 0x9c, 0x1e, 0xc3, 0x4d, 0x2e, 0x6c, 0x9b, 0x90,
0x09, 0xf1, 0x9e, 0x3a, 0xbb, 0xe2, 0xad, 0x51, 0x74, 0xc2, 0x31, 0x5a, 0x65, 0xa5, 0x6a, 0x87, 0x61, 0xad, 0xdb, 0x39, 0x4b, 0x44, 0x1d, 0xc2, 0xad, 0x38, 0xe3, 0xd7, 0x6b, 0x23, 0x7c, 0x02,
0x5e, 0x0f, 0x3e, 0x5b, 0xe0, 0xb3, 0x1a, 0x85, 0x96, 0x4c, 0xf3, 0x02, 0xa9, 0xd6, 0x6e, 0x6b, 0xd3, 0x1f, 0xf0, 0x1a, 0x54, 0xd3, 0x25, 0xcf, 0x79, 0x69, 0x84, 0xe9, 0x3b, 0x3d, 0x91, 0xce,
0x6f, 0x8e, 0x50, 0x9e, 0x15, 0x97, 0x87, 0x9f, 0xc3, 0x35, 0x8d, 0x3f, 0x93, 0x19, 0xde, 0x80, 0x17, 0x6c, 0xfe, 0xcc, 0xa3, 0x39, 0x21, 0xee, 0x73, 0x7b, 0x47, 0xbc, 0x35, 0x0a, 0x76, 0x30,
0x69, 0x51, 0x15, 0xcb, 0xa0, 0xb5, 0x18, 0x5f, 0x25, 0x60, 0x1c, 0xc9, 0x83, 0xef, 0xc3, 0x82, 0x46, 0x2b, 0xac, 0xfa, 0xed, 0xd0, 0xeb, 0xc1, 0x67, 0xf3, 0x7c, 0x56, 0xa3, 0xd0, 0x4a, 0x6a,
0xa4, 0x90, 0xde, 0xc0, 0x74, 0x56, 0xdc, 0x3e, 0x78, 0x17, 0x16, 0xe3, 0x6c, 0x99, 0xae, 0x48, 0x5e, 0x20, 0xd5, 0xda, 0x6d, 0xed, 0xcd, 0x11, 0xc8, 0xcb, 0x44, 0xe5, 0xe1, 0x97, 0x70, 0x43,
0x4d, 0x81, 0x3e, 0x1d, 0xb6, 0xb5, 0x18, 0x98, 0x3c, 0x14, 0xdd, 0x60, 0xb9, 0x84, 0xc1, 0x42, 0xe3, 0x4f, 0x65, 0x86, 0x37, 0x60, 0x5a, 0x14, 0xda, 0x32, 0x68, 0x2d, 0x46, 0x57, 0x09, 0x18,
0x85, 0x94, 0x88, 0x4c, 0x0a, 0x2d, 0x28, 0xf3, 0xef, 0x76, 0xfc, 0xf0, 0x4d, 0xf7, 0x29, 0x20, 0x5b, 0xf2, 0xe0, 0x87, 0xb0, 0x20, 0x29, 0xa4, 0x37, 0x30, 0x9d, 0x15, 0xb7, 0x0f, 0xde, 0x81,
0x9d, 0x98, 0xe9, 0x50, 0xd6, 0x61, 0x46, 0x18, 0x5c, 0x25, 0x53, 0xe6, 0x53, 0x51, 0x4c, 0x4c, 0xc5, 0x28, 0x5b, 0xaa, 0x2b, 0x52, 0x53, 0xa0, 0xcf, 0x87, 0x6d, 0x2d, 0x06, 0xc6, 0x0f, 0x45,
0xa1, 0x3a, 0x79, 0xe6, 0xb9, 0x27, 0x3d, 0x12, 0xc6, 0x1c, 0x96, 0x42, 0xe8, 0xc4, 0x4c, 0x3b, 0x37, 0x58, 0x36, 0x66, 0xb0, 0x40, 0x21, 0x25, 0x22, 0x95, 0x42, 0x0b, 0xca, 0xfc, 0x3b, 0x1d,
0xfe, 0x03, 0x7d, 0x7d, 0xd6, 0xba, 0xae, 0xd7, 0x53, 0xc6, 0x7f, 0x17, 0xa6, 0x45, 0x6e, 0x22, 0x2f, 0x78, 0xd3, 0x7d, 0x0a, 0x48, 0x27, 0xa6, 0x3a, 0x94, 0x35, 0x98, 0x11, 0x06, 0x57, 0xc9,
0xf3, 0xf7, 0x07, 0x71, 0x31, 0x3a, 0xaf, 0x18, 0xd4, 0x44, 0x26, 0x23, 0x57, 0xb1, 0xc3, 0x92, 0x94, 0xf9, 0x54, 0x14, 0x13, 0x53, 0xa8, 0x4e, 0x5e, 0xb8, 0xce, 0x71, 0x8f, 0x04, 0x31, 0x87,
0xcd, 0x98, 0x7a, 0xa2, 0x39, 0x53, 0x47, 0x6f, 0xc2, 0x94, 0xcb, 0x96, 0x70, 0x5f, 0xac, 0x24, 0xa5, 0x10, 0x3a, 0x31, 0xd5, 0x8e, 0xff, 0x44, 0x5f, 0x9f, 0xb5, 0xae, 0xe3, 0xf6, 0x94, 0xf1,
0xb3, 0x42, 0x2e, 0xad, 0xf9, 0x72, 0x48, 0x1c, 0xc1, 0x85, 0xdf, 0x81, 0x92, 0x86, 0xc0, 0x92, 0xdf, 0x85, 0x69, 0x91, 0x9b, 0xc8, 0xfc, 0xfd, 0x51, 0x54, 0x8c, 0xce, 0x2b, 0x06, 0x35, 0x91,
0xdd, 0xc7, 0x8d, 0x26, 0xcd, 0x80, 0xcb, 0x30, 0x5b, 0xdb, 0x6a, 0x6e, 0x1f, 0x8a, 0x1c, 0xb8, 0xc9, 0xc8, 0x55, 0xec, 0xb0, 0x64, 0x7f, 0xa7, 0x1e, 0xeb, 0xf7, 0xd4, 0xd1, 0x9b, 0x30, 0xe5,
0x02, 0x50, 0x6f, 0x84, 0xe3, 0x1c, 0xcd, 0x82, 0xc4, 0x2a, 0xe9, 0xe1, 0xba, 0x3e, 0x56, 0x9a, 0xb0, 0x25, 0xdc, 0x17, 0xcb, 0xf1, 0xac, 0x90, 0x4b, 0x6b, 0x5e, 0x0c, 0x89, 0x2d, 0xb8, 0xf0,
0x3e, 0xb9, 0x4b, 0xe9, 0xf3, 0x02, 0xe6, 0xe4, 0xf6, 0x33, 0xdd, 0x81, 0xb7, 0xa8, 0x85, 0x99, 0x3b, 0x50, 0xd4, 0x10, 0x58, 0xb2, 0xfb, 0xb4, 0xd1, 0xa4, 0x19, 0x70, 0x09, 0x66, 0x6b, 0x9b,
0x18, 0x75, 0x05, 0x96, 0x0d, 0xb0, 0xca, 0x3b, 0x05, 0x23, 0xa6, 0xd9, 0xc3, 0x41, 0xe0, 0x06, 0xcd, 0xad, 0x03, 0x91, 0x03, 0x97, 0x01, 0xea, 0x8d, 0x60, 0x9c, 0xa5, 0x59, 0x90, 0x58, 0x25,
0x23, 0x5f, 0x5d, 0x81, 0xdf, 0x5b, 0x50, 0x51, 0x94, 0xac, 0xd5, 0xbb, 0x2a, 0x91, 0x44, 0xcc, 0x3d, 0x5c, 0xd7, 0x27, 0x93, 0xa4, 0x4f, 0xf6, 0x4a, 0xfa, 0x9c, 0xc3, 0x9c, 0xdc, 0x7e, 0xaa,
0x0b, 0x0b, 0xa4, 0x1b, 0x30, 0xdd, 0x3e, 0x3e, 0xe8, 0x7c, 0xaa, 0xba, 0x18, 0x72, 0xc4, 0xe8, 0x3b, 0xf0, 0x16, 0xb5, 0x30, 0x13, 0xa3, 0xae, 0xc0, 0x92, 0x01, 0x56, 0x79, 0xa7, 0x60, 0xc4,
0x5d, 0x81, 0x23, 0x5a, 0x68, 0x72, 0xc4, 0x72, 0x6f, 0xd6, 0x4c, 0xdb, 0xee, 0xb7, 0xc9, 0x0b, 0x34, 0x7b, 0xd8, 0xf7, 0x1d, 0x7f, 0xe4, 0xa9, 0x2b, 0xf0, 0xc7, 0x0c, 0x94, 0x15, 0x25, 0x6d,
0xfe, 0xa6, 0x2d, 0x38, 0x11, 0x81, 0xa7, 0xcb, 0xb2, 0xd5, 0xc6, 0xeb, 0x27, 0xbd, 0xf5, 0x46, 0xf5, 0xae, 0x4a, 0x24, 0x11, 0xf3, 0x82, 0x02, 0xe9, 0x16, 0x4c, 0xb7, 0x8f, 0xf6, 0x3b, 0x9f,
0x2f, 0x79, 0x6d, 0x14, 0x9c, 0x36, 0xfa, 0xac, 0xcb, 0xa4, 0x76, 0xb8, 0x08, 0x88, 0x11, 0xeb, 0xaa, 0x2e, 0x86, 0x1c, 0x31, 0x7a, 0x57, 0xe0, 0x88, 0xae, 0x9c, 0x1c, 0xb1, 0xdc, 0x9b, 0xf5,
0x1d, 0x5f, 0xa7, 0x36, 0x60, 0x81, 0x51, 0xe9, 0xbd, 0xa7, 0xc9, 0x74, 0x14, 0x31, 0x54, 0xd8, 0xe7, 0xb6, 0xfa, 0x6d, 0x72, 0xce, 0xdf, 0xb4, 0x79, 0x3b, 0x24, 0xf0, 0x74, 0x59, 0x76, 0xef,
0xb6, 0x12, 0x61, 0xdb, 0xf5, 0xfd, 0xe7, 0x03, 0xaf, 0x2d, 0xb7, 0x16, 0x8e, 0x71, 0x5d, 0x08, 0x78, 0xfd, 0xa4, 0x77, 0xf3, 0xe8, 0x25, 0xaf, 0x8d, 0xfc, 0x93, 0x46, 0x9f, 0x35, 0xae, 0xd4,
0x7f, 0xea, 0xc7, 0x02, 0xf3, 0x57, 0x95, 0xf2, 0x28, 0x92, 0xf2, 0x98, 0x04, 0x13, 0xa4, 0xe0, 0x0e, 0x17, 0x01, 0x31, 0x62, 0xbd, 0xe3, 0xe9, 0xd4, 0x06, 0x2c, 0x30, 0x2a, 0xbd, 0xf7, 0x34,
0xd7, 0xe1, 0xba, 0xe2, 0x94, 0x35, 0xf4, 0x04, 0xe6, 0x7d, 0xb8, 0xa5, 0x98, 0xb7, 0x4e, 0x59, 0x99, 0x0e, 0x23, 0x86, 0x0a, 0xdb, 0x99, 0x58, 0xd8, 0x76, 0x3c, 0xef, 0xe5, 0xc0, 0x6d, 0xcb,
0xa2, 0xf7, 0x44, 0x02, 0xfe, 0xb7, 0x7a, 0x6e, 0xc2, 0x52, 0xa8, 0x27, 0xcf, 0x41, 0x06, 0x5d, 0xad, 0x05, 0x63, 0x5c, 0x17, 0xc2, 0x9f, 0x7b, 0x91, 0xc0, 0xfc, 0x55, 0xa5, 0x3c, 0x09, 0xa5,
0x5d, 0x81, 0x91, 0x2f, 0xef, 0x0c, 0x95, 0xc5, 0x9e, 0x19, 0xcd, 0xa3, 0x2c, 0xea, 0x25, 0xc8, 0x3c, 0x25, 0xfe, 0x04, 0x29, 0xf8, 0x75, 0xb8, 0xa9, 0x38, 0x65, 0x0d, 0x3d, 0x81, 0x79, 0x0f,
0x9e, 0xf1, 0x16, 0x2c, 0x2b, 0x19, 0x32, 0x3b, 0x88, 0x0b, 0x19, 0x53, 0xc8, 0x24, 0x44, 0x1a, 0xee, 0x28, 0xe6, 0xcd, 0x13, 0x96, 0xe8, 0x3d, 0x93, 0x80, 0xff, 0xab, 0x9e, 0x1b, 0x50, 0x09,
0x8c, 0x2d, 0x9d, 0x6c, 0x76, 0x9d, 0x33, 0x6e, 0x5a, 0x2e, 0xd3, 0xd2, 0x64, 0x5e, 0x17, 0x37, 0xf4, 0xe4, 0x39, 0xc8, 0xa0, 0xab, 0x2b, 0x30, 0xf2, 0xe4, 0x9d, 0xa1, 0xb2, 0xd8, 0x33, 0xa3,
0x82, 0x29, 0xa6, 0x07, 0x6d, 0x49, 0x66, 0x02, 0x74, 0xb2, 0x3c, 0x08, 0x46, 0x1e, 0x3b, 0x88, 0xb9, 0x94, 0x45, 0xbd, 0x04, 0xd9, 0x33, 0xde, 0x84, 0x25, 0x25, 0x43, 0x66, 0x07, 0x51, 0x21,
0x31, 0xd1, 0xdf, 0x83, 0xd5, 0x50, 0x09, 0x66, 0xb7, 0x27, 0xf4, 0xb2, 0x76, 0x7c, 0x5f, 0x2b, 0x63, 0x0a, 0x99, 0x84, 0x48, 0x83, 0xb1, 0xa5, 0x93, 0xcd, 0xae, 0x73, 0x46, 0x4d, 0xcb, 0x65,
0x02, 0x4d, 0x1b, 0x7f, 0x00, 0x85, 0x21, 0x91, 0x31, 0xa5, 0xb4, 0x81, 0xd6, 0x45, 0x43, 0x7c, 0x66, 0x34, 0x99, 0x37, 0xc5, 0x8d, 0x60, 0x8a, 0xe9, 0x41, 0x5b, 0x92, 0x99, 0x00, 0x9d, 0x2c,
0x5d, 0x5b, 0xcc, 0xe7, 0x71, 0x1b, 0x6e, 0x2b, 0xe9, 0xc2, 0xa2, 0x46, 0xf1, 0x49, 0xa5, 0x54, 0x0f, 0x82, 0x91, 0xc7, 0x0e, 0x62, 0x4c, 0xf4, 0x0f, 0x60, 0x25, 0x50, 0x82, 0xd9, 0xed, 0x19,
0x81, 0x20, 0xcc, 0x3a, 0x5e, 0x20, 0xe4, 0xc5, 0xd9, 0x87, 0xad, 0xbe, 0xf7, 0x85, 0x21, 0x95, 0xbd, 0xac, 0x1d, 0xcf, 0xd3, 0x8a, 0x40, 0xd3, 0xc6, 0x1f, 0x41, 0x7e, 0x48, 0x64, 0x4c, 0x29,
0x6f, 0x65, 0x7a, 0x57, 0xec, 0x08, 0x9b, 0x86, 0x2e, 0x99, 0x49, 0xd8, 0x31, 0x2c, 0xc6, 0x3d, 0xae, 0xa3, 0x35, 0xd1, 0x63, 0x5f, 0xd3, 0x16, 0xf3, 0x79, 0xdc, 0x86, 0xbb, 0x4a, 0xba, 0xb0,
0x39, 0x53, 0x18, 0xa3, 0x59, 0x6f, 0x40, 0x4d, 0xa8, 0x82, 0x98, 0x18, 0x28, 0x85, 0x43, 0x37, 0xa8, 0x51, 0x7c, 0x5c, 0x29, 0x55, 0x20, 0x08, 0xb3, 0x8e, 0x17, 0x08, 0x39, 0x71, 0xf6, 0x41,
0xcf, 0xa4, 0xb0, 0x1b, 0x09, 0xe3, 0x57, 0x32, 0xab, 0xbe, 0xec, 0x34, 0x55, 0x3e, 0x23, 0x06, 0xab, 0xef, 0x7d, 0x61, 0x48, 0xe5, 0x5b, 0xa9, 0xde, 0x15, 0xdb, 0xc2, 0xa6, 0x81, 0x4b, 0xa6,
0x78, 0x0f, 0x6e, 0x24, 0xc3, 0x44, 0x26, 0x95, 0x0f, 0xc5, 0x05, 0x36, 0x45, 0x92, 0x4c, 0x72, 0x12, 0x76, 0x04, 0x8b, 0x51, 0x4f, 0x4e, 0x15, 0xc6, 0x68, 0xd6, 0xeb, 0x53, 0x13, 0xaa, 0x20,
0x3f, 0x8c, 0x82, 0x81, 0x16, 0x50, 0x32, 0x89, 0x74, 0xc0, 0x36, 0xc5, 0x97, 0xff, 0xc5, 0x7d, 0x26, 0x06, 0x4a, 0xe1, 0xc0, 0xcd, 0x53, 0x29, 0xec, 0x84, 0xc2, 0xf8, 0x95, 0x4c, 0xab, 0x2f,
0x0d, 0xc3, 0x4d, 0x26, 0x61, 0x7e, 0x24, 0x2c, 0xfb, 0xf1, 0x47, 0x31, 0x22, 0x3f, 0x31, 0x46, 0x3b, 0x4d, 0x95, 0xcf, 0x88, 0x01, 0xde, 0x85, 0x5b, 0xf1, 0x30, 0x91, 0x4a, 0xe5, 0x03, 0x71,
0x48, 0x27, 0x89, 0xa2, 0xd8, 0xd7, 0x70, 0xe9, 0x24, 0x46, 0x14, 0x40, 0xb3, 0x62, 0xb0, 0x77, 0x81, 0x4d, 0x91, 0x24, 0x95, 0xdc, 0x0f, 0xc3, 0x60, 0xa0, 0x05, 0x94, 0x54, 0x22, 0x6d, 0xb0,
0x48, 0x88, 0xc1, 0x07, 0xea, 0x62, 0xeb, 0x61, 0x37, 0xd3, 0x61, 0x7c, 0x14, 0xc5, 0xce, 0xb1, 0x4c, 0xf1, 0xe5, 0xff, 0x71, 0x5f, 0x83, 0x70, 0x93, 0x4a, 0x98, 0x17, 0x0a, 0x4b, 0x7f, 0xfc,
0xc8, 0x9c, 0x49, 0xf0, 0xc7, 0xb0, 0x96, 0x1e, 0x94, 0xb3, 0x48, 0x7e, 0x0d, 0x43, 0x31, 0x4c, 0x61, 0x8c, 0xc8, 0x4d, 0x8c, 0x11, 0xd2, 0x49, 0xc2, 0x28, 0xf6, 0x35, 0x5c, 0x3a, 0x89, 0x11,
0x28, 0xb5, 0x8f, 0x67, 0x25, 0x98, 0xd9, 0xdb, 0x3f, 0x78, 0x52, 0xdb, 0xa2, 0xa9, 0xec, 0xc6, 0x06, 0xd0, 0xb4, 0x18, 0xec, 0x1d, 0x12, 0x60, 0xf0, 0x81, 0xba, 0xd8, 0x7a, 0xd8, 0x4d, 0x75,
0x9f, 0xf3, 0x90, 0xdb, 0x39, 0x44, 0xdf, 0x87, 0x29, 0xd1, 0xfc, 0x9f, 0xf0, 0x6d, 0xc4, 0x9e, 0x18, 0x1f, 0x85, 0xb1, 0x73, 0x2c, 0x32, 0xa7, 0x12, 0xfc, 0x31, 0xac, 0x26, 0x07, 0xe5, 0x34,
0xf4, 0x19, 0x01, 0xaf, 0x7c, 0xfe, 0xa7, 0xbf, 0x7d, 0x99, 0xbb, 0x81, 0xaf, 0x55, 0xcf, 0xdf, 0x92, 0x5f, 0xc3, 0x50, 0x08, 0x12, 0x4a, 0xed, 0x9b, 0x5a, 0x11, 0x66, 0x76, 0xf7, 0xf6, 0x9f,
0x76, 0xbb, 0xc3, 0x53, 0xb7, 0x7a, 0x76, 0x5e, 0xe5, 0xef, 0x84, 0x6f, 0x5a, 0xaf, 0xa1, 0x43, 0xd5, 0x36, 0x69, 0x2a, 0xbb, 0xfe, 0x97, 0x1c, 0x64, 0xb7, 0x0f, 0xd0, 0x0f, 0x61, 0x4a, 0x34,
0xc8, 0xb3, 0x4f, 0x03, 0xa9, 0x1f, 0x4e, 0xec, 0xf4, 0xcf, 0x0b, 0xd8, 0xe6, 0x92, 0x17, 0xf1, 0xff, 0x27, 0x7c, 0x1b, 0xb1, 0x26, 0x7d, 0x46, 0xc0, 0xcb, 0x9f, 0xfd, 0xf9, 0xef, 0x5f, 0x64,
0x55, 0x5d, 0xf2, 0x70, 0x14, 0x30, 0xb9, 0x4d, 0x28, 0x69, 0x5f, 0x08, 0xd0, 0x85, 0x9f, 0x54, 0x6f, 0xe1, 0x1b, 0xd5, 0xb3, 0xb7, 0x9d, 0xee, 0xf0, 0xc4, 0xa9, 0x9e, 0x9e, 0x55, 0xf9, 0x3b,
0xec, 0x8b, 0xbf, 0x3e, 0xe0, 0x2b, 0x4c, 0xdb, 0xe6, 0x8b, 0x7e, 0x52, 0xdb, 0xa8, 0xa3, 0x9d, 0xe1, 0xdb, 0x99, 0xd7, 0xd0, 0x01, 0xe4, 0xd8, 0xa7, 0x81, 0xc4, 0x0f, 0x27, 0x56, 0xf2, 0xe7,
0xd4, 0x56, 0xeb, 0x22, 0x9b, 0xb5, 0x0d, 0x5e, 0xf4, 0x99, 0xb6, 0x03, 0xf9, 0xcd, 0xa2, 0x15, 0x05, 0x6c, 0x71, 0xc9, 0x8b, 0xf8, 0xba, 0x2e, 0x79, 0x38, 0xf2, 0x99, 0xdc, 0x26, 0x14, 0xb5,
0xa0, 0xdb, 0x86, 0x16, 0xb8, 0xde, 0xec, 0xb5, 0xd7, 0xd2, 0x19, 0x24, 0xd2, 0x1d, 0x8e, 0x74, 0x2f, 0x04, 0xe8, 0xd2, 0x4f, 0x2a, 0xd6, 0xe5, 0x5f, 0x1f, 0xf0, 0x35, 0xa6, 0x6d, 0xf3, 0xbc,
0x13, 0xdf, 0xd0, 0x91, 0x5a, 0x21, 0x1f, 0x05, 0xdc, 0x38, 0x85, 0x29, 0xde, 0x29, 0x43, 0x47, 0x1f, 0xd7, 0x36, 0xec, 0x68, 0xc7, 0xb5, 0xd5, 0xba, 0xc8, 0x66, 0x6d, 0xfd, 0xf3, 0x3e, 0xd3,
0xea, 0xc1, 0x36, 0xf4, 0x11, 0x53, 0xce, 0x37, 0xd6, 0x63, 0xc3, 0xcb, 0x1c, 0x6d, 0x01, 0x57, 0x76, 0x20, 0xbf, 0x59, 0xb4, 0x7c, 0x74, 0xd7, 0xd0, 0x02, 0xd7, 0x9b, 0xbd, 0xd6, 0x6a, 0x32,
0x42, 0x34, 0xde, 0x2c, 0xa3, 0x28, 0x8f, 0xac, 0xff, 0xb3, 0x36, 0xfe, 0x95, 0x83, 0x29, 0xde, 0x83, 0x44, 0xba, 0xc7, 0x91, 0x6e, 0xe3, 0x5b, 0x3a, 0x52, 0x2b, 0xe0, 0xa3, 0x80, 0xeb, 0x27,
0x52, 0x41, 0x43, 0x80, 0xa8, 0xf7, 0x94, 0xdc, 0xe7, 0x58, 0x37, 0x2b, 0xb9, 0xcf, 0xf1, 0xb6, 0x30, 0xc5, 0x3b, 0x65, 0xe8, 0x50, 0x3d, 0x58, 0x86, 0x3e, 0x62, 0xc2, 0xf9, 0x46, 0x7a, 0x6c,
0x15, 0xbe, 0xcd, 0x91, 0x97, 0xf1, 0x62, 0x88, 0xcc, 0x3f, 0x6e, 0x56, 0x4f, 0x18, 0x17, 0x33, 0x78, 0x89, 0xa3, 0x2d, 0xe0, 0x72, 0x80, 0xc6, 0x9b, 0x65, 0x14, 0xe5, 0x49, 0xe6, 0x1b, 0x99,
0xeb, 0x73, 0x28, 0x69, 0x3d, 0x24, 0x64, 0x92, 0x18, 0x6b, 0x42, 0x25, 0x2f, 0x81, 0xa1, 0x01, 0xf5, 0x7f, 0x67, 0x61, 0x8a, 0xb7, 0x54, 0xd0, 0x10, 0x20, 0xec, 0x3d, 0xc5, 0xf7, 0x39, 0xd6,
0x85, 0xef, 0x72, 0xd0, 0x5b, 0x78, 0x49, 0x37, 0xae, 0xc0, 0xf5, 0x38, 0x27, 0x03, 0xfe, 0x31, 0xcd, 0x8a, 0xef, 0x73, 0xbc, 0x6d, 0x85, 0xef, 0x72, 0xe4, 0x25, 0xbc, 0x18, 0x20, 0xf3, 0x8f,
0x2d, 0x89, 0xe2, 0x7d, 0x24, 0x74, 0xd7, 0x20, 0x3a, 0xd9, 0x8e, 0xb2, 0xef, 0x4d, 0x66, 0x4a, 0x9b, 0xd5, 0x63, 0xc6, 0xc5, 0xcc, 0xfa, 0x12, 0x8a, 0x5a, 0x0f, 0x09, 0x99, 0x24, 0x46, 0x9a,
0x55, 0x41, 0xe0, 0x9f, 0x51, 0x4e, 0x97, 0x71, 0x2a, 0xdb, 0xff, 0x9b, 0x7d, 0x0b, 0x13, 0xbf, 0x50, 0xf1, 0x4b, 0x60, 0x68, 0x40, 0xe1, 0xfb, 0x1c, 0xf4, 0x0e, 0xae, 0xe8, 0xc6, 0x15, 0xb8,
0x55, 0x40, 0x01, 0x14, 0xc3, 0x6e, 0x0e, 0x5a, 0x35, 0x55, 0xfa, 0x51, 0x1a, 0x6c, 0xdf, 0x4e, 0x2e, 0xe7, 0x64, 0xc0, 0x3f, 0xa3, 0x25, 0x51, 0xb4, 0x8f, 0x84, 0xee, 0x1b, 0x44, 0xc7, 0xdb,
0x9d, 0x97, 0x2a, 0x3c, 0xe0, 0x2a, 0xac, 0xe1, 0x9b, 0xa1, 0x0a, 0xf2, 0x37, 0x11, 0x55, 0x51, 0x51, 0xd6, 0x83, 0xc9, 0x4c, 0x89, 0x2a, 0x08, 0xfc, 0x53, 0xca, 0xe9, 0x30, 0x4e, 0x65, 0xfb,
0xd0, 0x56, 0xdd, 0x76, 0x9b, 0x19, 0xe2, 0x47, 0xb4, 0xa4, 0xd7, 0x9b, 0x34, 0xe8, 0x8e, 0xb1, 0xff, 0xb0, 0x6f, 0x61, 0xe2, 0xe7, 0x0f, 0xc8, 0x87, 0x42, 0xd0, 0xcd, 0x41, 0x2b, 0xa6, 0x4a,
0xc7, 0xa0, 0xf7, 0x79, 0x6c, 0x3c, 0x89, 0x45, 0xe2, 0xbf, 0xca, 0xf1, 0xef, 0xe2, 0xd5, 0x34, 0x3f, 0x4c, 0x83, 0xad, 0xbb, 0x89, 0xf3, 0x52, 0x85, 0x47, 0x5c, 0x85, 0x55, 0x7c, 0x3b, 0x50,
0x7c, 0x8f, 0xf3, 0xc7, 0x55, 0x10, 0x6d, 0x19, 0xb3, 0x0a, 0xb1, 0xae, 0x8f, 0x59, 0x85, 0x78, 0x41, 0xfe, 0xcc, 0xa2, 0x2a, 0x0a, 0xda, 0xaa, 0xd3, 0x6e, 0x33, 0x43, 0xfc, 0x94, 0x96, 0xf4,
0x57, 0xe7, 0x62, 0x15, 0x46, 0x9c, 0x9f, 0xa9, 0xf0, 0x02, 0x20, 0xea, 0xda, 0x20, 0xa3, 0x71, 0x7a, 0x93, 0x06, 0xdd, 0x33, 0xf6, 0x18, 0xf4, 0x3e, 0x8f, 0x85, 0x27, 0xb1, 0x48, 0xfc, 0x57,
0xb5, 0xc2, 0x20, 0x79, 0xf3, 0xc7, 0x1b, 0x3e, 0xf8, 0x21, 0xc7, 0xbe, 0x83, 0x57, 0xd2, 0xb0, 0x39, 0xfe, 0x7d, 0xbc, 0x92, 0x84, 0xef, 0x72, 0xfe, 0xa8, 0x0a, 0xa2, 0x2d, 0x63, 0x56, 0x21,
0xbb, 0x94, 0x9b, 0xf9, 0xf9, 0x6f, 0x0b, 0x50, 0xfa, 0xc0, 0xed, 0xf4, 0x03, 0xd2, 0x67, 0xcd, 0xd2, 0xf5, 0x31, 0xab, 0x10, 0xed, 0xea, 0x5c, 0xae, 0xc2, 0x88, 0xf3, 0x33, 0x15, 0xce, 0x01,
0x68, 0x74, 0x02, 0x53, 0x3c, 0xf2, 0x27, 0xdd, 0x5d, 0x6f, 0xa5, 0x24, 0xdd, 0x3d, 0xd6, 0x67, 0xc2, 0xae, 0x0d, 0x32, 0x1a, 0x57, 0x2b, 0x0c, 0xe2, 0x37, 0x7f, 0xbc, 0xe1, 0x83, 0x1f, 0x73,
0xc0, 0xf7, 0x39, 0xf4, 0x6d, 0x6c, 0x87, 0xd0, 0xbd, 0x48, 0x7e, 0x95, 0xf7, 0x08, 0xd8, 0x96, 0xec, 0x7b, 0x78, 0x39, 0x09, 0xbb, 0x4b, 0xb9, 0x99, 0x9f, 0xff, 0x3e, 0x0f, 0xc5, 0x0f, 0x9c,
0xcf, 0x60, 0x5a, 0xf4, 0x04, 0x50, 0x42, 0x5a, 0xac, 0x77, 0x60, 0xaf, 0x98, 0x27, 0x53, 0x6f, 0x4e, 0xdf, 0x27, 0x7d, 0xd6, 0x8c, 0x46, 0xc7, 0x30, 0xc5, 0x23, 0x7f, 0xdc, 0xdd, 0xf5, 0x56,
0x99, 0x8e, 0xe5, 0x73, 0x66, 0x06, 0xf6, 0x03, 0x80, 0xa8, 0x09, 0x95, 0xb4, 0xef, 0x58, 0xcf, 0x4a, 0xdc, 0xdd, 0x23, 0x7d, 0x06, 0xfc, 0x90, 0x43, 0xdf, 0xc5, 0x56, 0x00, 0xdd, 0x0b, 0xe5,
0xca, 0x5e, 0x4b, 0x67, 0x90, 0xc0, 0xaf, 0x71, 0xe0, 0x7b, 0xf8, 0xb6, 0x11, 0xb8, 0x1d, 0x2e, 0x57, 0x79, 0x8f, 0x80, 0x6d, 0xf9, 0x14, 0xa6, 0x45, 0x4f, 0x00, 0xc5, 0xa4, 0x45, 0x7a, 0x07,
0x60, 0xe0, 0x2d, 0x28, 0xb0, 0x2f, 0x5d, 0x28, 0x11, 0xfa, 0xb5, 0x8f, 0x61, 0xb6, 0x6d, 0x9a, 0xd6, 0xb2, 0x79, 0x32, 0xf1, 0x96, 0xe9, 0x58, 0x1e, 0x67, 0x66, 0x60, 0x3f, 0x02, 0x08, 0x9b,
0x92, 0x50, 0xf7, 0x38, 0xd4, 0x2a, 0x5e, 0x36, 0x42, 0xb1, 0x2f, 0x5e, 0x0c, 0x64, 0x04, 0xb3, 0x50, 0x71, 0xfb, 0x8e, 0xf5, 0xac, 0xac, 0xd5, 0x64, 0x06, 0x09, 0xfc, 0x1a, 0x07, 0x7e, 0x80,
0xea, 0x03, 0x17, 0xba, 0x95, 0xb0, 0x59, 0xfc, 0x63, 0x98, 0xbd, 0x9a, 0x36, 0x2d, 0x01, 0x1f, 0xef, 0x1a, 0x81, 0xdb, 0xc1, 0x02, 0x06, 0xde, 0x82, 0x3c, 0xfb, 0xd2, 0x85, 0x62, 0xa1, 0x5f,
0x71, 0x40, 0x8c, 0x6f, 0x99, 0x8d, 0x2a, 0xd9, 0x29, 0x28, 0x0d, 0x20, 0x3f, 0x9d, 0x87, 0x02, 0xfb, 0x18, 0x66, 0x59, 0xa6, 0x29, 0x09, 0xf5, 0x80, 0x43, 0xad, 0xe0, 0x25, 0x23, 0x14, 0xfb,
0xcb, 0x41, 0x58, 0xec, 0x8e, 0x4a, 0xb7, 0xa4, 0x85, 0xc7, 0x1a, 0x26, 0x49, 0x0b, 0x8f, 0x57, 0xe2, 0xc5, 0x40, 0x46, 0x30, 0xab, 0x3e, 0x70, 0xa1, 0x3b, 0x31, 0x9b, 0x45, 0x3f, 0x86, 0x59,
0x7d, 0x86, 0xd8, 0xcd, 0x7f, 0xb1, 0x45, 0x38, 0x17, 0xdb, 0x71, 0x00, 0x25, 0xad, 0xc0, 0x43, 0x2b, 0x49, 0xd3, 0x12, 0xf0, 0x09, 0x07, 0xc4, 0xf8, 0x8e, 0xd9, 0xa8, 0x92, 0x9d, 0x82, 0xd2,
0x06, 0x89, 0xf1, 0x76, 0x4c, 0x32, 0x76, 0x1b, 0xaa, 0x43, 0xbc, 0xc6, 0x41, 0x6d, 0x7c, 0x3d, 0x00, 0xf2, 0x8b, 0x79, 0xc8, 0xb3, 0x1c, 0x84, 0xc5, 0xee, 0xb0, 0x74, 0x8b, 0x5b, 0x78, 0xac,
0x0e, 0xda, 0x16, 0x6c, 0x0c, 0xf5, 0x87, 0x50, 0xd6, 0x2b, 0x41, 0x64, 0x10, 0x9a, 0xe8, 0xf7, 0x61, 0x12, 0xb7, 0xf0, 0x78, 0xd5, 0x67, 0x88, 0xdd, 0xfc, 0x47, 0x60, 0x84, 0x73, 0xb1, 0x1d,
0x24, 0x63, 0x85, 0xa9, 0x90, 0x34, 0x38, 0x4d, 0xf8, 0xfb, 0x34, 0xc5, 0xcb, 0xd0, 0x3f, 0x81, 0xfb, 0x50, 0xd4, 0x0a, 0x3c, 0x64, 0x90, 0x18, 0x6d, 0xc7, 0xc4, 0x63, 0xb7, 0xa1, 0x3a, 0xc4,
0x19, 0x59, 0x1f, 0x9a, 0xf6, 0x1b, 0xef, 0x10, 0x99, 0xf6, 0x9b, 0x28, 0x2e, 0x0d, 0x89, 0x00, 0xab, 0x1c, 0xd4, 0xc2, 0x37, 0xa3, 0xa0, 0x6d, 0xc1, 0xc6, 0x50, 0x7f, 0x0c, 0x25, 0xbd, 0x12,
0x87, 0x65, 0x79, 0xb0, 0x0a, 0xd0, 0x12, 0x92, 0x96, 0x11, 0x69, 0x90, 0x51, 0xcf, 0x23, 0x0d, 0x44, 0x06, 0xa1, 0xb1, 0x7e, 0x4f, 0x3c, 0x56, 0x98, 0x0a, 0x49, 0x83, 0xd3, 0x04, 0x3f, 0x79,
0x52, 0xab, 0x41, 0x26, 0x42, 0x9e, 0x90, 0x40, 0xde, 0x65, 0x95, 0xe0, 0xa3, 0x14, 0x89, 0x7a, 0x53, 0xbc, 0x0c, 0xfd, 0x13, 0x98, 0x91, 0xf5, 0xa1, 0x69, 0xbf, 0xd1, 0x0e, 0x91, 0x69, 0xbf,
0x34, 0xc4, 0x93, 0x58, 0x24, 0x2a, 0xe6, 0xa8, 0x2b, 0xf8, 0x15, 0x03, 0xaa, 0x0c, 0x85, 0xe8, 0xb1, 0xe2, 0xd2, 0x90, 0x08, 0x70, 0x58, 0x96, 0x07, 0xab, 0x00, 0x2d, 0x21, 0x69, 0x19, 0x91,
0x33, 0x80, 0xa8, 0x98, 0x4d, 0xbe, 0x8e, 0x8d, 0x1d, 0xb1, 0xe4, 0xeb, 0xd8, 0x5c, 0x0f, 0x1b, 0x04, 0x19, 0xf6, 0x3c, 0x92, 0x20, 0xb5, 0x1a, 0x64, 0x22, 0xe4, 0x31, 0xf1, 0xe5, 0x5d, 0x56,
0x3c, 0x38, 0x02, 0x17, 0x3f, 0x73, 0x61, 0xf0, 0x3f, 0xb7, 0x00, 0x8d, 0x17, 0xbf, 0xe8, 0x75, 0x09, 0x3e, 0x4a, 0x90, 0xa8, 0x47, 0x43, 0x3c, 0x89, 0x45, 0xa2, 0x62, 0x8e, 0xba, 0x8c, 0x5f,
0x33, 0x84, 0xb1, 0xd9, 0x66, 0xbf, 0x71, 0x39, 0xe6, 0xd4, 0xe8, 0x19, 0xe9, 0xd5, 0xe2, 0x4b, 0x31, 0xa0, 0xca, 0x50, 0x88, 0x7e, 0x02, 0x10, 0x16, 0xb3, 0xf1, 0xd7, 0xb1, 0xb1, 0x23, 0x16,
0x86, 0xcf, 0x99, 0x66, 0x5f, 0x58, 0x30, 0x17, 0x2b, 0x9f, 0xd1, 0x83, 0x94, 0x73, 0x4e, 0x34, 0x7f, 0x1d, 0x9b, 0xeb, 0x61, 0x83, 0x07, 0x87, 0xe0, 0xe2, 0x67, 0x2e, 0x0c, 0xfe, 0x97, 0x19,
0xec, 0xec, 0x87, 0x17, 0xf2, 0xa5, 0x66, 0x2c, 0xda, 0xad, 0x50, 0xd9, 0xda, 0x4f, 0x68, 0xd2, 0x40, 0xe3, 0xc5, 0x2f, 0x7a, 0xdd, 0x0c, 0x61, 0x6c, 0xb6, 0x59, 0x6f, 0x5c, 0x8d, 0x39, 0x31,
0x14, 0xaf, 0xb9, 0x51, 0x0a, 0xc0, 0x58, 0xd7, 0xcf, 0x7e, 0x74, 0x31, 0xe3, 0x25, 0x4e, 0x2b, 0x7a, 0x86, 0x7a, 0xb5, 0xf8, 0x92, 0xe1, 0x4b, 0xa6, 0xd9, 0xe7, 0x19, 0x98, 0x8b, 0x94, 0xcf,
0x4a, 0xe0, 0xa8, 0x5b, 0xc8, 0x52, 0xdd, 0xe4, 0x16, 0xf1, 0xa6, 0xa1, 0xc9, 0x2d, 0x12, 0x75, 0xe8, 0x51, 0xc2, 0x39, 0xc7, 0x1a, 0x76, 0xd6, 0xe3, 0x4b, 0xf9, 0x12, 0x33, 0x16, 0xed, 0x56,
0x7e, 0x9a, 0x5b, 0xb0, 0xaa, 0x57, 0xf3, 0x44, 0x59, 0xd0, 0xa7, 0x41, 0x4e, 0xf6, 0xc4, 0x44, 0xa8, 0x6c, 0xed, 0xe7, 0x34, 0x69, 0x8a, 0xd6, 0xdc, 0x28, 0x01, 0x60, 0xac, 0xeb, 0x67, 0x3d,
0x37, 0x60, 0x22, 0x64, 0xe4, 0x89, 0xaa, 0x9c, 0x47, 0x29, 0x12, 0x2f, 0xf0, 0xc4, 0x64, 0x37, 0xb9, 0x9c, 0xf1, 0x0a, 0xa7, 0x15, 0x26, 0x70, 0xd4, 0x2d, 0x64, 0xa9, 0x6e, 0x72, 0x8b, 0x68,
0x20, 0xcd, 0x13, 0x39, 0xaa, 0xe6, 0x89, 0x51, 0xf5, 0x6d, 0xf2, 0xc4, 0xb1, 0x96, 0xa8, 0xc9, 0xd3, 0xd0, 0xe4, 0x16, 0xb1, 0x3a, 0x3f, 0xc9, 0x2d, 0x58, 0xd5, 0xab, 0x79, 0xa2, 0x2c, 0xe8,
0x13, 0xc7, 0x0b, 0xf8, 0xb4, 0xb3, 0xe5, 0xe0, 0x31, 0x4f, 0x5c, 0x30, 0x54, 0xeb, 0xe8, 0x8d, 0x93, 0x20, 0x27, 0x7b, 0x62, 0xac, 0x1b, 0x30, 0x11, 0x32, 0xf4, 0x44, 0x55, 0xce, 0xa3, 0x04,
0x14, 0x9b, 0x1a, 0xdb, 0xad, 0xf6, 0x9b, 0x97, 0xe4, 0x9e, 0xec, 0x01, 0xe2, 0x34, 0x94, 0x07, 0x89, 0x97, 0x78, 0x62, 0xbc, 0x1b, 0x90, 0xe4, 0x89, 0x1c, 0x55, 0xf3, 0xc4, 0xb0, 0xfa, 0x36,
0xfc, 0xca, 0x82, 0x45, 0x53, 0xb9, 0x8f, 0x52, 0xc0, 0x52, 0x7a, 0xb5, 0xf6, 0xfa, 0x65, 0xd9, 0x79, 0xe2, 0x58, 0x4b, 0xd4, 0xe4, 0x89, 0xe3, 0x05, 0x7c, 0xd2, 0xd9, 0x72, 0xf0, 0x88, 0x27,
0x2f, 0x61, 0xb7, 0xd0, 0x27, 0x36, 0xcb, 0xbf, 0xfb, 0xeb, 0xaa, 0xf5, 0x47, 0xfa, 0xef, 0x2f, 0x2e, 0x18, 0xaa, 0x75, 0xf4, 0x46, 0x82, 0x4d, 0x8d, 0xed, 0x56, 0xeb, 0xcd, 0x2b, 0x72, 0x4f,
0xf4, 0xdf, 0xf1, 0x34, 0xff, 0xc9, 0xf4, 0xdb, 0xff, 0x09, 0x00, 0x00, 0xff, 0xff, 0xf7, 0xeb, 0xf6, 0x00, 0x71, 0x1a, 0xca, 0x03, 0x7e, 0x93, 0x81, 0x45, 0x53, 0xb9, 0x8f, 0x12, 0xc0, 0x12,
0x93, 0xc8, 0xb9, 0x2d, 0x00, 0x00, 0x7a, 0xb5, 0xd6, 0xda, 0x55, 0xd9, 0xaf, 0x60, 0xb7, 0xc0, 0x27, 0x36, 0x4a, 0x7f, 0xf8, 0xdb,
0x4a, 0xe6, 0x4b, 0xfa, 0xef, 0xaf, 0xf4, 0xdf, 0xd1, 0x34, 0xff, 0x15, 0xf6, 0xdb, 0xff, 0x0d,
0x00, 0x00, 0xff, 0xff, 0x7b, 0x02, 0x74, 0xf8, 0x0c, 0x2e, 0x00, 0x00,
} }

View File

@ -36,8 +36,8 @@ func NewURLs(strs []string) (URLs, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
if u.Scheme != "http" && u.Scheme != "https" { if u.Scheme != "http" && u.Scheme != "https" && u.Scheme != "unix" && u.Scheme != "unixs" {
return nil, fmt.Errorf("URL scheme must be http or https: %s", in) return nil, fmt.Errorf("URL scheme must be http, https, unix, or unixs: %s", in)
} }
if _, _, err := net.SplitHostPort(u.Host); err != nil { if _, _, err := net.SplitHostPort(u.Host); err != nil {
return nil, fmt.Errorf(`URL address does not have the form "host:port": %s`, in) return nil, fmt.Errorf(`URL address does not have the form "host:port": %s`, in)

View File

@ -39,7 +39,7 @@ func Comma(v int64) string {
// Commaf produces a string form of the given number in base 10 with // Commaf produces a string form of the given number in base 10 with
// commas after every three orders of magnitude. // commas after every three orders of magnitude.
// //
// e.g. Comma(834142.32) -> 834,142.32 // e.g. Commaf(834142.32) -> 834,142.32
func Commaf(v float64) string { func Commaf(v float64) string {
buf := &bytes.Buffer{} buf := &bytes.Buffer{}
if v < 0 { if v < 0 {

View File

@ -34,8 +34,8 @@ const (
) )
var ( var (
decimal = regexp.MustCompile(`^\d*\.?\d*$`) decimal = regexp.MustCompile(`^-*\d*\.?\d*$`)
percent = regexp.MustCompile(`^\d*\.?\d*$%$`) percent = regexp.MustCompile(`^-*\d*\.?\d*$%$`)
) )
type Border struct { type Border struct {

View File

@ -454,7 +454,7 @@ func terminalReadFrameError(err error) bool {
// //
// If the frame is larger than previously set with SetMaxReadFrameSize, the // If the frame is larger than previously set with SetMaxReadFrameSize, the
// returned error is ErrFrameTooLarge. Other errors may be of type // returned error is ErrFrameTooLarge. Other errors may be of type
// ConnectionError, StreamError, or anything else from from the underlying // ConnectionError, StreamError, or anything else from the underlying
// reader. // reader.
func (fr *Framer) ReadFrame() (Frame, error) { func (fr *Framer) ReadFrame() (Frame, error) {
fr.errDetail = nil fr.errDetail = nil

View File

@ -26,6 +26,8 @@ import (
"github.com/golang/protobuf/proto" "github.com/golang/protobuf/proto"
netcontext "golang.org/x/net/context" netcontext "golang.org/x/net/context"
basepb "google.golang.org/appengine/internal/base"
logpb "google.golang.org/appengine/internal/log"
remotepb "google.golang.org/appengine/internal/remote_api" remotepb "google.golang.org/appengine/internal/remote_api"
) )
@ -50,6 +52,7 @@ var (
apiDeadlineHeader = http.CanonicalHeaderKey("X-Google-RPC-Service-Deadline") apiDeadlineHeader = http.CanonicalHeaderKey("X-Google-RPC-Service-Deadline")
apiContentType = http.CanonicalHeaderKey("Content-Type") apiContentType = http.CanonicalHeaderKey("Content-Type")
apiContentTypeValue = []string{"application/octet-stream"} apiContentTypeValue = []string{"application/octet-stream"}
logFlushHeader = http.CanonicalHeaderKey("X-AppEngine-Log-Flush-Count")
apiHTTPClient = &http.Client{ apiHTTPClient = &http.Client{
Transport: &http.Transport{ Transport: &http.Transport{
@ -79,8 +82,8 @@ func handleHTTP(w http.ResponseWriter, r *http.Request) {
req: r, req: r,
outHeader: w.Header(), outHeader: w.Header(),
apiURL: apiURL(), apiURL: apiURL(),
logger: globalLogger(),
} }
stopFlushing := make(chan int)
ctxs.Lock() ctxs.Lock()
ctxs.m[r] = c ctxs.m[r] = c
@ -109,9 +112,26 @@ func handleHTTP(w http.ResponseWriter, r *http.Request) {
r.RemoteAddr = net.JoinHostPort(r.RemoteAddr, "80") r.RemoteAddr = net.JoinHostPort(r.RemoteAddr, "80")
} }
// Start goroutine responsible for flushing app logs.
// This is done after adding c to ctx.m (and stopped before removing it)
// because flushing logs requires making an API call.
go c.logFlusher(stopFlushing)
executeRequestSafely(c, r) executeRequestSafely(c, r)
c.outHeader = nil // make sure header changes aren't respected any more c.outHeader = nil // make sure header changes aren't respected any more
stopFlushing <- 1 // any logging beyond this point will be dropped
// Flush any pending logs asynchronously.
c.pendingLogs.Lock()
flushes := c.pendingLogs.flushes
if len(c.pendingLogs.lines) > 0 {
flushes++
}
c.pendingLogs.Unlock()
go c.flushLog(false)
w.Header().Set(logFlushHeader, strconv.Itoa(flushes))
// Avoid nil Write call if c.Write is never called. // Avoid nil Write call if c.Write is never called.
if c.outCode != 0 { if c.outCode != 0 {
w.WriteHeader(c.outCode) w.WriteHeader(c.outCode)
@ -186,13 +206,18 @@ var ctxs = struct {
// context represents the context of an in-flight HTTP request. // context represents the context of an in-flight HTTP request.
// It implements the appengine.Context and http.ResponseWriter interfaces. // It implements the appengine.Context and http.ResponseWriter interfaces.
type context struct { type context struct {
req *http.Request req *http.Request
logger *jsonLogger
outCode int outCode int
outHeader http.Header outHeader http.Header
outBody []byte outBody []byte
pendingLogs struct {
sync.Mutex
lines []*logpb.UserAppLogLine
flushes int
}
apiURL *url.URL apiURL *url.URL
} }
@ -265,9 +290,11 @@ func BackgroundContext() netcontext.Context {
}, },
}, },
apiURL: apiURL(), apiURL: apiURL(),
logger: globalLogger(),
} }
// TODO(dsymonds): Wire up the shutdown handler to do a final flush.
go ctxs.bg.logFlusher(make(chan int))
return toContext(ctxs.bg) return toContext(ctxs.bg)
} }
@ -279,7 +306,6 @@ func RegisterTestRequest(req *http.Request, apiURL *url.URL, decorate func(netco
c := &context{ c := &context{
req: req, req: req,
apiURL: apiURL, apiURL: apiURL,
logger: globalLogger(),
} }
ctxs.Lock() ctxs.Lock()
defer ctxs.Unlock() defer ctxs.Unlock()
@ -501,9 +527,120 @@ func (c *context) Request() *http.Request {
return c.req return c.req
} }
func ContextForTesting(req *http.Request) netcontext.Context { func (c *context) addLogLine(ll *logpb.UserAppLogLine) {
return toContext(&context{ // Truncate long log lines.
req: req, // TODO(dsymonds): Check if this is still necessary.
logger: testLogger, const lim = 8 << 10
}) if len(*ll.Message) > lim {
suffix := fmt.Sprintf("...(length %d)", len(*ll.Message))
ll.Message = proto.String((*ll.Message)[:lim-len(suffix)] + suffix)
}
c.pendingLogs.Lock()
c.pendingLogs.lines = append(c.pendingLogs.lines, ll)
c.pendingLogs.Unlock()
}
var logLevelName = map[int64]string{
0: "DEBUG",
1: "INFO",
2: "WARNING",
3: "ERROR",
4: "CRITICAL",
}
func logf(c *context, level int64, format string, args ...interface{}) {
s := fmt.Sprintf(format, args...)
s = strings.TrimRight(s, "\n") // Remove any trailing newline characters.
c.addLogLine(&logpb.UserAppLogLine{
TimestampUsec: proto.Int64(time.Now().UnixNano() / 1e3),
Level: &level,
Message: &s,
})
log.Print(logLevelName[level] + ": " + s)
}
// flushLog attempts to flush any pending logs to the appserver.
// It should not be called concurrently.
func (c *context) flushLog(force bool) (flushed bool) {
c.pendingLogs.Lock()
// Grab up to 30 MB. We can get away with up to 32 MB, but let's be cautious.
n, rem := 0, 30<<20
for ; n < len(c.pendingLogs.lines); n++ {
ll := c.pendingLogs.lines[n]
// Each log line will require about 3 bytes of overhead.
nb := proto.Size(ll) + 3
if nb > rem {
break
}
rem -= nb
}
lines := c.pendingLogs.lines[:n]
c.pendingLogs.lines = c.pendingLogs.lines[n:]
c.pendingLogs.Unlock()
if len(lines) == 0 && !force {
// Nothing to flush.
return false
}
rescueLogs := false
defer func() {
if rescueLogs {
c.pendingLogs.Lock()
c.pendingLogs.lines = append(lines, c.pendingLogs.lines...)
c.pendingLogs.Unlock()
}
}()
buf, err := proto.Marshal(&logpb.UserAppLogGroup{
LogLine: lines,
})
if err != nil {
log.Printf("internal.flushLog: marshaling UserAppLogGroup: %v", err)
rescueLogs = true
return false
}
req := &logpb.FlushRequest{
Logs: buf,
}
res := &basepb.VoidProto{}
c.pendingLogs.Lock()
c.pendingLogs.flushes++
c.pendingLogs.Unlock()
if err := Call(toContext(c), "logservice", "Flush", req, res); err != nil {
log.Printf("internal.flushLog: Flush RPC: %v", err)
rescueLogs = true
return false
}
return true
}
const (
// Log flushing parameters.
flushInterval = 1 * time.Second
forceFlushInterval = 60 * time.Second
)
func (c *context) logFlusher(stop <-chan int) {
lastFlush := time.Now()
tick := time.NewTicker(flushInterval)
for {
select {
case <-stop:
// Request finished.
tick.Stop()
return
case <-tick.C:
force := time.Now().Sub(lastFlush) > forceFlushInterval
if c.flushLog(force) {
lastFlush = time.Now()
}
}
}
}
func ContextForTesting(req *http.Request) netcontext.Context {
return toContext(&context{req: req})
} }

View File

@ -0,0 +1,899 @@
// Code generated by protoc-gen-go.
// source: google.golang.org/appengine/internal/log/log_service.proto
// DO NOT EDIT!
/*
Package log is a generated protocol buffer package.
It is generated from these files:
google.golang.org/appengine/internal/log/log_service.proto
It has these top-level messages:
LogServiceError
UserAppLogLine
UserAppLogGroup
FlushRequest
SetStatusRequest
LogOffset
LogLine
RequestLog
LogModuleVersion
LogReadRequest
LogReadResponse
LogUsageRecord
LogUsageRequest
LogUsageResponse
*/
package log
import proto "github.com/golang/protobuf/proto"
import fmt "fmt"
import math "math"
// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
var _ = fmt.Errorf
var _ = math.Inf
type LogServiceError_ErrorCode int32
const (
LogServiceError_OK LogServiceError_ErrorCode = 0
LogServiceError_INVALID_REQUEST LogServiceError_ErrorCode = 1
LogServiceError_STORAGE_ERROR LogServiceError_ErrorCode = 2
)
var LogServiceError_ErrorCode_name = map[int32]string{
0: "OK",
1: "INVALID_REQUEST",
2: "STORAGE_ERROR",
}
var LogServiceError_ErrorCode_value = map[string]int32{
"OK": 0,
"INVALID_REQUEST": 1,
"STORAGE_ERROR": 2,
}
func (x LogServiceError_ErrorCode) Enum() *LogServiceError_ErrorCode {
p := new(LogServiceError_ErrorCode)
*p = x
return p
}
func (x LogServiceError_ErrorCode) String() string {
return proto.EnumName(LogServiceError_ErrorCode_name, int32(x))
}
func (x *LogServiceError_ErrorCode) UnmarshalJSON(data []byte) error {
value, err := proto.UnmarshalJSONEnum(LogServiceError_ErrorCode_value, data, "LogServiceError_ErrorCode")
if err != nil {
return err
}
*x = LogServiceError_ErrorCode(value)
return nil
}
type LogServiceError struct {
XXX_unrecognized []byte `json:"-"`
}
func (m *LogServiceError) Reset() { *m = LogServiceError{} }
func (m *LogServiceError) String() string { return proto.CompactTextString(m) }
func (*LogServiceError) ProtoMessage() {}
type UserAppLogLine struct {
TimestampUsec *int64 `protobuf:"varint,1,req,name=timestamp_usec" json:"timestamp_usec,omitempty"`
Level *int64 `protobuf:"varint,2,req,name=level" json:"level,omitempty"`
Message *string `protobuf:"bytes,3,req,name=message" json:"message,omitempty"`
XXX_unrecognized []byte `json:"-"`
}
func (m *UserAppLogLine) Reset() { *m = UserAppLogLine{} }
func (m *UserAppLogLine) String() string { return proto.CompactTextString(m) }
func (*UserAppLogLine) ProtoMessage() {}
func (m *UserAppLogLine) GetTimestampUsec() int64 {
if m != nil && m.TimestampUsec != nil {
return *m.TimestampUsec
}
return 0
}
func (m *UserAppLogLine) GetLevel() int64 {
if m != nil && m.Level != nil {
return *m.Level
}
return 0
}
func (m *UserAppLogLine) GetMessage() string {
if m != nil && m.Message != nil {
return *m.Message
}
return ""
}
type UserAppLogGroup struct {
LogLine []*UserAppLogLine `protobuf:"bytes,2,rep,name=log_line" json:"log_line,omitempty"`
XXX_unrecognized []byte `json:"-"`
}
func (m *UserAppLogGroup) Reset() { *m = UserAppLogGroup{} }
func (m *UserAppLogGroup) String() string { return proto.CompactTextString(m) }
func (*UserAppLogGroup) ProtoMessage() {}
func (m *UserAppLogGroup) GetLogLine() []*UserAppLogLine {
if m != nil {
return m.LogLine
}
return nil
}
type FlushRequest struct {
Logs []byte `protobuf:"bytes,1,opt,name=logs" json:"logs,omitempty"`
XXX_unrecognized []byte `json:"-"`
}
func (m *FlushRequest) Reset() { *m = FlushRequest{} }
func (m *FlushRequest) String() string { return proto.CompactTextString(m) }
func (*FlushRequest) ProtoMessage() {}
func (m *FlushRequest) GetLogs() []byte {
if m != nil {
return m.Logs
}
return nil
}
type SetStatusRequest struct {
Status *string `protobuf:"bytes,1,req,name=status" json:"status,omitempty"`
XXX_unrecognized []byte `json:"-"`
}
func (m *SetStatusRequest) Reset() { *m = SetStatusRequest{} }
func (m *SetStatusRequest) String() string { return proto.CompactTextString(m) }
func (*SetStatusRequest) ProtoMessage() {}
func (m *SetStatusRequest) GetStatus() string {
if m != nil && m.Status != nil {
return *m.Status
}
return ""
}
type LogOffset struct {
RequestId []byte `protobuf:"bytes,1,opt,name=request_id" json:"request_id,omitempty"`
XXX_unrecognized []byte `json:"-"`
}
func (m *LogOffset) Reset() { *m = LogOffset{} }
func (m *LogOffset) String() string { return proto.CompactTextString(m) }
func (*LogOffset) ProtoMessage() {}
func (m *LogOffset) GetRequestId() []byte {
if m != nil {
return m.RequestId
}
return nil
}
type LogLine struct {
Time *int64 `protobuf:"varint,1,req,name=time" json:"time,omitempty"`
Level *int32 `protobuf:"varint,2,req,name=level" json:"level,omitempty"`
LogMessage *string `protobuf:"bytes,3,req,name=log_message" json:"log_message,omitempty"`
XXX_unrecognized []byte `json:"-"`
}
func (m *LogLine) Reset() { *m = LogLine{} }
func (m *LogLine) String() string { return proto.CompactTextString(m) }
func (*LogLine) ProtoMessage() {}
func (m *LogLine) GetTime() int64 {
if m != nil && m.Time != nil {
return *m.Time
}
return 0
}
func (m *LogLine) GetLevel() int32 {
if m != nil && m.Level != nil {
return *m.Level
}
return 0
}
func (m *LogLine) GetLogMessage() string {
if m != nil && m.LogMessage != nil {
return *m.LogMessage
}
return ""
}
type RequestLog struct {
AppId *string `protobuf:"bytes,1,req,name=app_id" json:"app_id,omitempty"`
ModuleId *string `protobuf:"bytes,37,opt,name=module_id,def=default" json:"module_id,omitempty"`
VersionId *string `protobuf:"bytes,2,req,name=version_id" json:"version_id,omitempty"`
RequestId []byte `protobuf:"bytes,3,req,name=request_id" json:"request_id,omitempty"`
Offset *LogOffset `protobuf:"bytes,35,opt,name=offset" json:"offset,omitempty"`
Ip *string `protobuf:"bytes,4,req,name=ip" json:"ip,omitempty"`
Nickname *string `protobuf:"bytes,5,opt,name=nickname" json:"nickname,omitempty"`
StartTime *int64 `protobuf:"varint,6,req,name=start_time" json:"start_time,omitempty"`
EndTime *int64 `protobuf:"varint,7,req,name=end_time" json:"end_time,omitempty"`
Latency *int64 `protobuf:"varint,8,req,name=latency" json:"latency,omitempty"`
Mcycles *int64 `protobuf:"varint,9,req,name=mcycles" json:"mcycles,omitempty"`
Method *string `protobuf:"bytes,10,req,name=method" json:"method,omitempty"`
Resource *string `protobuf:"bytes,11,req,name=resource" json:"resource,omitempty"`
HttpVersion *string `protobuf:"bytes,12,req,name=http_version" json:"http_version,omitempty"`
Status *int32 `protobuf:"varint,13,req,name=status" json:"status,omitempty"`
ResponseSize *int64 `protobuf:"varint,14,req,name=response_size" json:"response_size,omitempty"`
Referrer *string `protobuf:"bytes,15,opt,name=referrer" json:"referrer,omitempty"`
UserAgent *string `protobuf:"bytes,16,opt,name=user_agent" json:"user_agent,omitempty"`
UrlMapEntry *string `protobuf:"bytes,17,req,name=url_map_entry" json:"url_map_entry,omitempty"`
Combined *string `protobuf:"bytes,18,req,name=combined" json:"combined,omitempty"`
ApiMcycles *int64 `protobuf:"varint,19,opt,name=api_mcycles" json:"api_mcycles,omitempty"`
Host *string `protobuf:"bytes,20,opt,name=host" json:"host,omitempty"`
Cost *float64 `protobuf:"fixed64,21,opt,name=cost" json:"cost,omitempty"`
TaskQueueName *string `protobuf:"bytes,22,opt,name=task_queue_name" json:"task_queue_name,omitempty"`
TaskName *string `protobuf:"bytes,23,opt,name=task_name" json:"task_name,omitempty"`
WasLoadingRequest *bool `protobuf:"varint,24,opt,name=was_loading_request" json:"was_loading_request,omitempty"`
PendingTime *int64 `protobuf:"varint,25,opt,name=pending_time" json:"pending_time,omitempty"`
ReplicaIndex *int32 `protobuf:"varint,26,opt,name=replica_index,def=-1" json:"replica_index,omitempty"`
Finished *bool `protobuf:"varint,27,opt,name=finished,def=1" json:"finished,omitempty"`
CloneKey []byte `protobuf:"bytes,28,opt,name=clone_key" json:"clone_key,omitempty"`
Line []*LogLine `protobuf:"bytes,29,rep,name=line" json:"line,omitempty"`
LinesIncomplete *bool `protobuf:"varint,36,opt,name=lines_incomplete" json:"lines_incomplete,omitempty"`
AppEngineRelease []byte `protobuf:"bytes,38,opt,name=app_engine_release" json:"app_engine_release,omitempty"`
ExitReason *int32 `protobuf:"varint,30,opt,name=exit_reason" json:"exit_reason,omitempty"`
WasThrottledForTime *bool `protobuf:"varint,31,opt,name=was_throttled_for_time" json:"was_throttled_for_time,omitempty"`
WasThrottledForRequests *bool `protobuf:"varint,32,opt,name=was_throttled_for_requests" json:"was_throttled_for_requests,omitempty"`
ThrottledTime *int64 `protobuf:"varint,33,opt,name=throttled_time" json:"throttled_time,omitempty"`
ServerName []byte `protobuf:"bytes,34,opt,name=server_name" json:"server_name,omitempty"`
XXX_unrecognized []byte `json:"-"`
}
func (m *RequestLog) Reset() { *m = RequestLog{} }
func (m *RequestLog) String() string { return proto.CompactTextString(m) }
func (*RequestLog) ProtoMessage() {}
const Default_RequestLog_ModuleId string = "default"
const Default_RequestLog_ReplicaIndex int32 = -1
const Default_RequestLog_Finished bool = true
func (m *RequestLog) GetAppId() string {
if m != nil && m.AppId != nil {
return *m.AppId
}
return ""
}
func (m *RequestLog) GetModuleId() string {
if m != nil && m.ModuleId != nil {
return *m.ModuleId
}
return Default_RequestLog_ModuleId
}
func (m *RequestLog) GetVersionId() string {
if m != nil && m.VersionId != nil {
return *m.VersionId
}
return ""
}
func (m *RequestLog) GetRequestId() []byte {
if m != nil {
return m.RequestId
}
return nil
}
func (m *RequestLog) GetOffset() *LogOffset {
if m != nil {
return m.Offset
}
return nil
}
func (m *RequestLog) GetIp() string {
if m != nil && m.Ip != nil {
return *m.Ip
}
return ""
}
func (m *RequestLog) GetNickname() string {
if m != nil && m.Nickname != nil {
return *m.Nickname
}
return ""
}
func (m *RequestLog) GetStartTime() int64 {
if m != nil && m.StartTime != nil {
return *m.StartTime
}
return 0
}
func (m *RequestLog) GetEndTime() int64 {
if m != nil && m.EndTime != nil {
return *m.EndTime
}
return 0
}
func (m *RequestLog) GetLatency() int64 {
if m != nil && m.Latency != nil {
return *m.Latency
}
return 0
}
func (m *RequestLog) GetMcycles() int64 {
if m != nil && m.Mcycles != nil {
return *m.Mcycles
}
return 0
}
func (m *RequestLog) GetMethod() string {
if m != nil && m.Method != nil {
return *m.Method
}
return ""
}
func (m *RequestLog) GetResource() string {
if m != nil && m.Resource != nil {
return *m.Resource
}
return ""
}
func (m *RequestLog) GetHttpVersion() string {
if m != nil && m.HttpVersion != nil {
return *m.HttpVersion
}
return ""
}
func (m *RequestLog) GetStatus() int32 {
if m != nil && m.Status != nil {
return *m.Status
}
return 0
}
func (m *RequestLog) GetResponseSize() int64 {
if m != nil && m.ResponseSize != nil {
return *m.ResponseSize
}
return 0
}
func (m *RequestLog) GetReferrer() string {
if m != nil && m.Referrer != nil {
return *m.Referrer
}
return ""
}
func (m *RequestLog) GetUserAgent() string {
if m != nil && m.UserAgent != nil {
return *m.UserAgent
}
return ""
}
func (m *RequestLog) GetUrlMapEntry() string {
if m != nil && m.UrlMapEntry != nil {
return *m.UrlMapEntry
}
return ""
}
func (m *RequestLog) GetCombined() string {
if m != nil && m.Combined != nil {
return *m.Combined
}
return ""
}
func (m *RequestLog) GetApiMcycles() int64 {
if m != nil && m.ApiMcycles != nil {
return *m.ApiMcycles
}
return 0
}
func (m *RequestLog) GetHost() string {
if m != nil && m.Host != nil {
return *m.Host
}
return ""
}
func (m *RequestLog) GetCost() float64 {
if m != nil && m.Cost != nil {
return *m.Cost
}
return 0
}
func (m *RequestLog) GetTaskQueueName() string {
if m != nil && m.TaskQueueName != nil {
return *m.TaskQueueName
}
return ""
}
func (m *RequestLog) GetTaskName() string {
if m != nil && m.TaskName != nil {
return *m.TaskName
}
return ""
}
func (m *RequestLog) GetWasLoadingRequest() bool {
if m != nil && m.WasLoadingRequest != nil {
return *m.WasLoadingRequest
}
return false
}
func (m *RequestLog) GetPendingTime() int64 {
if m != nil && m.PendingTime != nil {
return *m.PendingTime
}
return 0
}
func (m *RequestLog) GetReplicaIndex() int32 {
if m != nil && m.ReplicaIndex != nil {
return *m.ReplicaIndex
}
return Default_RequestLog_ReplicaIndex
}
func (m *RequestLog) GetFinished() bool {
if m != nil && m.Finished != nil {
return *m.Finished
}
return Default_RequestLog_Finished
}
func (m *RequestLog) GetCloneKey() []byte {
if m != nil {
return m.CloneKey
}
return nil
}
func (m *RequestLog) GetLine() []*LogLine {
if m != nil {
return m.Line
}
return nil
}
func (m *RequestLog) GetLinesIncomplete() bool {
if m != nil && m.LinesIncomplete != nil {
return *m.LinesIncomplete
}
return false
}
func (m *RequestLog) GetAppEngineRelease() []byte {
if m != nil {
return m.AppEngineRelease
}
return nil
}
func (m *RequestLog) GetExitReason() int32 {
if m != nil && m.ExitReason != nil {
return *m.ExitReason
}
return 0
}
func (m *RequestLog) GetWasThrottledForTime() bool {
if m != nil && m.WasThrottledForTime != nil {
return *m.WasThrottledForTime
}
return false
}
func (m *RequestLog) GetWasThrottledForRequests() bool {
if m != nil && m.WasThrottledForRequests != nil {
return *m.WasThrottledForRequests
}
return false
}
func (m *RequestLog) GetThrottledTime() int64 {
if m != nil && m.ThrottledTime != nil {
return *m.ThrottledTime
}
return 0
}
func (m *RequestLog) GetServerName() []byte {
if m != nil {
return m.ServerName
}
return nil
}
type LogModuleVersion struct {
ModuleId *string `protobuf:"bytes,1,opt,name=module_id,def=default" json:"module_id,omitempty"`
VersionId *string `protobuf:"bytes,2,opt,name=version_id" json:"version_id,omitempty"`
XXX_unrecognized []byte `json:"-"`
}
func (m *LogModuleVersion) Reset() { *m = LogModuleVersion{} }
func (m *LogModuleVersion) String() string { return proto.CompactTextString(m) }
func (*LogModuleVersion) ProtoMessage() {}
const Default_LogModuleVersion_ModuleId string = "default"
func (m *LogModuleVersion) GetModuleId() string {
if m != nil && m.ModuleId != nil {
return *m.ModuleId
}
return Default_LogModuleVersion_ModuleId
}
func (m *LogModuleVersion) GetVersionId() string {
if m != nil && m.VersionId != nil {
return *m.VersionId
}
return ""
}
type LogReadRequest struct {
AppId *string `protobuf:"bytes,1,req,name=app_id" json:"app_id,omitempty"`
VersionId []string `protobuf:"bytes,2,rep,name=version_id" json:"version_id,omitempty"`
ModuleVersion []*LogModuleVersion `protobuf:"bytes,19,rep,name=module_version" json:"module_version,omitempty"`
StartTime *int64 `protobuf:"varint,3,opt,name=start_time" json:"start_time,omitempty"`
EndTime *int64 `protobuf:"varint,4,opt,name=end_time" json:"end_time,omitempty"`
Offset *LogOffset `protobuf:"bytes,5,opt,name=offset" json:"offset,omitempty"`
RequestId [][]byte `protobuf:"bytes,6,rep,name=request_id" json:"request_id,omitempty"`
MinimumLogLevel *int32 `protobuf:"varint,7,opt,name=minimum_log_level" json:"minimum_log_level,omitempty"`
IncludeIncomplete *bool `protobuf:"varint,8,opt,name=include_incomplete" json:"include_incomplete,omitempty"`
Count *int64 `protobuf:"varint,9,opt,name=count" json:"count,omitempty"`
CombinedLogRegex *string `protobuf:"bytes,14,opt,name=combined_log_regex" json:"combined_log_regex,omitempty"`
HostRegex *string `protobuf:"bytes,15,opt,name=host_regex" json:"host_regex,omitempty"`
ReplicaIndex *int32 `protobuf:"varint,16,opt,name=replica_index" json:"replica_index,omitempty"`
IncludeAppLogs *bool `protobuf:"varint,10,opt,name=include_app_logs" json:"include_app_logs,omitempty"`
AppLogsPerRequest *int32 `protobuf:"varint,17,opt,name=app_logs_per_request" json:"app_logs_per_request,omitempty"`
IncludeHost *bool `protobuf:"varint,11,opt,name=include_host" json:"include_host,omitempty"`
IncludeAll *bool `protobuf:"varint,12,opt,name=include_all" json:"include_all,omitempty"`
CacheIterator *bool `protobuf:"varint,13,opt,name=cache_iterator" json:"cache_iterator,omitempty"`
NumShards *int32 `protobuf:"varint,18,opt,name=num_shards" json:"num_shards,omitempty"`
XXX_unrecognized []byte `json:"-"`
}
func (m *LogReadRequest) Reset() { *m = LogReadRequest{} }
func (m *LogReadRequest) String() string { return proto.CompactTextString(m) }
func (*LogReadRequest) ProtoMessage() {}
func (m *LogReadRequest) GetAppId() string {
if m != nil && m.AppId != nil {
return *m.AppId
}
return ""
}
func (m *LogReadRequest) GetVersionId() []string {
if m != nil {
return m.VersionId
}
return nil
}
func (m *LogReadRequest) GetModuleVersion() []*LogModuleVersion {
if m != nil {
return m.ModuleVersion
}
return nil
}
func (m *LogReadRequest) GetStartTime() int64 {
if m != nil && m.StartTime != nil {
return *m.StartTime
}
return 0
}
func (m *LogReadRequest) GetEndTime() int64 {
if m != nil && m.EndTime != nil {
return *m.EndTime
}
return 0
}
func (m *LogReadRequest) GetOffset() *LogOffset {
if m != nil {
return m.Offset
}
return nil
}
func (m *LogReadRequest) GetRequestId() [][]byte {
if m != nil {
return m.RequestId
}
return nil
}
func (m *LogReadRequest) GetMinimumLogLevel() int32 {
if m != nil && m.MinimumLogLevel != nil {
return *m.MinimumLogLevel
}
return 0
}
func (m *LogReadRequest) GetIncludeIncomplete() bool {
if m != nil && m.IncludeIncomplete != nil {
return *m.IncludeIncomplete
}
return false
}
func (m *LogReadRequest) GetCount() int64 {
if m != nil && m.Count != nil {
return *m.Count
}
return 0
}
func (m *LogReadRequest) GetCombinedLogRegex() string {
if m != nil && m.CombinedLogRegex != nil {
return *m.CombinedLogRegex
}
return ""
}
func (m *LogReadRequest) GetHostRegex() string {
if m != nil && m.HostRegex != nil {
return *m.HostRegex
}
return ""
}
func (m *LogReadRequest) GetReplicaIndex() int32 {
if m != nil && m.ReplicaIndex != nil {
return *m.ReplicaIndex
}
return 0
}
func (m *LogReadRequest) GetIncludeAppLogs() bool {
if m != nil && m.IncludeAppLogs != nil {
return *m.IncludeAppLogs
}
return false
}
func (m *LogReadRequest) GetAppLogsPerRequest() int32 {
if m != nil && m.AppLogsPerRequest != nil {
return *m.AppLogsPerRequest
}
return 0
}
func (m *LogReadRequest) GetIncludeHost() bool {
if m != nil && m.IncludeHost != nil {
return *m.IncludeHost
}
return false
}
func (m *LogReadRequest) GetIncludeAll() bool {
if m != nil && m.IncludeAll != nil {
return *m.IncludeAll
}
return false
}
func (m *LogReadRequest) GetCacheIterator() bool {
if m != nil && m.CacheIterator != nil {
return *m.CacheIterator
}
return false
}
func (m *LogReadRequest) GetNumShards() int32 {
if m != nil && m.NumShards != nil {
return *m.NumShards
}
return 0
}
type LogReadResponse struct {
Log []*RequestLog `protobuf:"bytes,1,rep,name=log" json:"log,omitempty"`
Offset *LogOffset `protobuf:"bytes,2,opt,name=offset" json:"offset,omitempty"`
LastEndTime *int64 `protobuf:"varint,3,opt,name=last_end_time" json:"last_end_time,omitempty"`
XXX_unrecognized []byte `json:"-"`
}
func (m *LogReadResponse) Reset() { *m = LogReadResponse{} }
func (m *LogReadResponse) String() string { return proto.CompactTextString(m) }
func (*LogReadResponse) ProtoMessage() {}
func (m *LogReadResponse) GetLog() []*RequestLog {
if m != nil {
return m.Log
}
return nil
}
func (m *LogReadResponse) GetOffset() *LogOffset {
if m != nil {
return m.Offset
}
return nil
}
func (m *LogReadResponse) GetLastEndTime() int64 {
if m != nil && m.LastEndTime != nil {
return *m.LastEndTime
}
return 0
}
type LogUsageRecord struct {
VersionId *string `protobuf:"bytes,1,opt,name=version_id" json:"version_id,omitempty"`
StartTime *int32 `protobuf:"varint,2,opt,name=start_time" json:"start_time,omitempty"`
EndTime *int32 `protobuf:"varint,3,opt,name=end_time" json:"end_time,omitempty"`
Count *int64 `protobuf:"varint,4,opt,name=count" json:"count,omitempty"`
TotalSize *int64 `protobuf:"varint,5,opt,name=total_size" json:"total_size,omitempty"`
Records *int32 `protobuf:"varint,6,opt,name=records" json:"records,omitempty"`
XXX_unrecognized []byte `json:"-"`
}
func (m *LogUsageRecord) Reset() { *m = LogUsageRecord{} }
func (m *LogUsageRecord) String() string { return proto.CompactTextString(m) }
func (*LogUsageRecord) ProtoMessage() {}
func (m *LogUsageRecord) GetVersionId() string {
if m != nil && m.VersionId != nil {
return *m.VersionId
}
return ""
}
func (m *LogUsageRecord) GetStartTime() int32 {
if m != nil && m.StartTime != nil {
return *m.StartTime
}
return 0
}
func (m *LogUsageRecord) GetEndTime() int32 {
if m != nil && m.EndTime != nil {
return *m.EndTime
}
return 0
}
func (m *LogUsageRecord) GetCount() int64 {
if m != nil && m.Count != nil {
return *m.Count
}
return 0
}
func (m *LogUsageRecord) GetTotalSize() int64 {
if m != nil && m.TotalSize != nil {
return *m.TotalSize
}
return 0
}
func (m *LogUsageRecord) GetRecords() int32 {
if m != nil && m.Records != nil {
return *m.Records
}
return 0
}
type LogUsageRequest struct {
AppId *string `protobuf:"bytes,1,req,name=app_id" json:"app_id,omitempty"`
VersionId []string `protobuf:"bytes,2,rep,name=version_id" json:"version_id,omitempty"`
StartTime *int32 `protobuf:"varint,3,opt,name=start_time" json:"start_time,omitempty"`
EndTime *int32 `protobuf:"varint,4,opt,name=end_time" json:"end_time,omitempty"`
ResolutionHours *uint32 `protobuf:"varint,5,opt,name=resolution_hours,def=1" json:"resolution_hours,omitempty"`
CombineVersions *bool `protobuf:"varint,6,opt,name=combine_versions" json:"combine_versions,omitempty"`
UsageVersion *int32 `protobuf:"varint,7,opt,name=usage_version" json:"usage_version,omitempty"`
VersionsOnly *bool `protobuf:"varint,8,opt,name=versions_only" json:"versions_only,omitempty"`
XXX_unrecognized []byte `json:"-"`
}
func (m *LogUsageRequest) Reset() { *m = LogUsageRequest{} }
func (m *LogUsageRequest) String() string { return proto.CompactTextString(m) }
func (*LogUsageRequest) ProtoMessage() {}
const Default_LogUsageRequest_ResolutionHours uint32 = 1
func (m *LogUsageRequest) GetAppId() string {
if m != nil && m.AppId != nil {
return *m.AppId
}
return ""
}
func (m *LogUsageRequest) GetVersionId() []string {
if m != nil {
return m.VersionId
}
return nil
}
func (m *LogUsageRequest) GetStartTime() int32 {
if m != nil && m.StartTime != nil {
return *m.StartTime
}
return 0
}
func (m *LogUsageRequest) GetEndTime() int32 {
if m != nil && m.EndTime != nil {
return *m.EndTime
}
return 0
}
func (m *LogUsageRequest) GetResolutionHours() uint32 {
if m != nil && m.ResolutionHours != nil {
return *m.ResolutionHours
}
return Default_LogUsageRequest_ResolutionHours
}
func (m *LogUsageRequest) GetCombineVersions() bool {
if m != nil && m.CombineVersions != nil {
return *m.CombineVersions
}
return false
}
func (m *LogUsageRequest) GetUsageVersion() int32 {
if m != nil && m.UsageVersion != nil {
return *m.UsageVersion
}
return 0
}
func (m *LogUsageRequest) GetVersionsOnly() bool {
if m != nil && m.VersionsOnly != nil {
return *m.VersionsOnly
}
return false
}
type LogUsageResponse struct {
Usage []*LogUsageRecord `protobuf:"bytes,1,rep,name=usage" json:"usage,omitempty"`
Summary *LogUsageRecord `protobuf:"bytes,2,opt,name=summary" json:"summary,omitempty"`
XXX_unrecognized []byte `json:"-"`
}
func (m *LogUsageResponse) Reset() { *m = LogUsageResponse{} }
func (m *LogUsageResponse) String() string { return proto.CompactTextString(m) }
func (*LogUsageResponse) ProtoMessage() {}
func (m *LogUsageResponse) GetUsage() []*LogUsageRecord {
if m != nil {
return m.Usage
}
return nil
}
func (m *LogUsageResponse) GetSummary() *LogUsageRecord {
if m != nil {
return m.Summary
}
return nil
}
func init() {
}

View File

@ -1,109 +0,0 @@
// Copyright 2016 Google Inc. All rights reserved.
// Use of this source code is governed by the Apache 2.0
// license that can be found in the LICENSE file.
// +build !appengine
package internal
import (
"encoding/json"
"fmt"
"io"
"io/ioutil"
"log"
"os"
"strings"
"sync"
"time"
)
// jsonLogger writes logs in the JSON format required for Flex Logging. It can
// be used concurrently.
type jsonLogger struct {
mu sync.Mutex
enc *json.Encoder
}
type logLine struct {
Message string `json:"message"`
Timestamp logTimestamp `json:"timestamp"`
Severity string `json:"severity"`
TraceID string `json:"traceId,omitempty"`
}
type logTimestamp struct {
Seconds int64 `json:"seconds"`
Nanos int `json:"nanos"`
}
var (
logger *jsonLogger
loggerOnce sync.Once
logPath = "/var/log/app_engine/app.json"
stderrLogger = newJSONLogger(os.Stderr)
testLogger = newJSONLogger(ioutil.Discard)
levels = map[int64]string{
0: "DEBUG",
1: "INFO",
2: "WARNING",
3: "ERROR",
4: "CRITICAL",
}
)
func globalLogger() *jsonLogger {
loggerOnce.Do(func() {
f, err := os.OpenFile(logPath, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0666)
if err != nil {
log.Printf("failed to open/create log file, logging to stderr: %v", err)
logger = stderrLogger
return
}
logger = newJSONLogger(f)
})
return logger
}
func logf(ctx *context, level int64, format string, args ...interface{}) {
s := strings.TrimSpace(fmt.Sprintf(format, args...))
now := time.Now()
trace := ctx.req.Header.Get(traceHeader)
if i := strings.Index(trace, "/"); i > -1 {
trace = trace[:i]
}
line := &logLine{
Message: s,
Timestamp: logTimestamp{
Seconds: now.Unix(),
Nanos: now.Nanosecond(),
},
Severity: levels[level],
TraceID: trace,
}
if err := ctx.logger.emit(line); err != nil {
log.Printf("failed to write log line to file: %v", err)
}
log.Print(levels[level] + ": " + s)
}
func newJSONLogger(w io.Writer) *jsonLogger {
return &jsonLogger{
enc: json.NewEncoder(w),
}
}
func (l *jsonLogger) emit(line *logLine) error {
l.mu.Lock()
defer l.mu.Unlock()
return l.enc.Encode(line)
}

View File

@ -139,16 +139,20 @@ func RoundRobin(r naming.Resolver) Balancer {
return &roundRobin{r: r} return &roundRobin{r: r}
} }
type addrInfo struct {
addr Address
connected bool
}
type roundRobin struct { type roundRobin struct {
r naming.Resolver r naming.Resolver
w naming.Watcher w naming.Watcher
open []Address // all the addresses the client should potentially connect addrs []*addrInfo // all the addresses the client should potentially connect
mu sync.Mutex mu sync.Mutex
addrCh chan []Address // the channel to notify gRPC internals the list of addresses the client should connect to. addrCh chan []Address // the channel to notify gRPC internals the list of addresses the client should connect to.
connected []Address // all the connected addresses next int // index of the next address to return for Get()
next int // index of the next address to return for Get() waitCh chan struct{} // the channel to block when there is no connected address available
waitCh chan struct{} // the channel to block when there is no connected address available done bool // The Balancer is closed.
done bool // The Balancer is closed.
} }
func (rr *roundRobin) watchAddrUpdates() error { func (rr *roundRobin) watchAddrUpdates() error {
@ -166,8 +170,8 @@ func (rr *roundRobin) watchAddrUpdates() error {
switch update.Op { switch update.Op {
case naming.Add: case naming.Add:
var exist bool var exist bool
for _, v := range rr.open { for _, v := range rr.addrs {
if addr == v { if addr == v.addr {
exist = true exist = true
grpclog.Println("grpc: The name resolver wanted to add an existing address: ", addr) grpclog.Println("grpc: The name resolver wanted to add an existing address: ", addr)
break break
@ -176,12 +180,12 @@ func (rr *roundRobin) watchAddrUpdates() error {
if exist { if exist {
continue continue
} }
rr.open = append(rr.open, addr) rr.addrs = append(rr.addrs, &addrInfo{addr: addr})
case naming.Delete: case naming.Delete:
for i, v := range rr.open { for i, v := range rr.addrs {
if v == addr { if addr == v.addr {
copy(rr.open[i:], rr.open[i+1:]) copy(rr.addrs[i:], rr.addrs[i+1:])
rr.open = rr.open[:len(rr.open)-1] rr.addrs = rr.addrs[:len(rr.addrs)-1]
break break
} }
} }
@ -189,9 +193,11 @@ func (rr *roundRobin) watchAddrUpdates() error {
grpclog.Println("Unknown update.Op ", update.Op) grpclog.Println("Unknown update.Op ", update.Op)
} }
} }
// Make a copy of rr.open and write it onto rr.addrCh so that gRPC internals gets notified. // Make a copy of rr.addrs and write it onto rr.addrCh so that gRPC internals gets notified.
open := make([]Address, len(rr.open), len(rr.open)) open := make([]Address, len(rr.addrs))
copy(open, rr.open) for i, v := range rr.addrs {
open[i] = v.addr
}
if rr.done { if rr.done {
return ErrClientConnClosing return ErrClientConnClosing
} }
@ -202,7 +208,9 @@ func (rr *roundRobin) watchAddrUpdates() error {
func (rr *roundRobin) Start(target string) error { func (rr *roundRobin) Start(target string) error {
if rr.r == nil { if rr.r == nil {
// If there is no name resolver installed, it is not needed to // If there is no name resolver installed, it is not needed to
// do name resolution. In this case, rr.addrCh stays nil. // do name resolution. In this case, target is added into rr.addrs
// as the only address available and rr.addrCh stays nil.
rr.addrs = append(rr.addrs, &addrInfo{addr: Address{Addr: target}})
return nil return nil
} }
w, err := rr.r.Resolve(target) w, err := rr.r.Resolve(target)
@ -221,38 +229,41 @@ func (rr *roundRobin) Start(target string) error {
return nil return nil
} }
// Up appends addr to the end of rr.connected and sends notification if there // Up sets the connected state of addr and sends notification if there are pending
// are pending Get() calls. // Get() calls.
func (rr *roundRobin) Up(addr Address) func(error) { func (rr *roundRobin) Up(addr Address) func(error) {
rr.mu.Lock() rr.mu.Lock()
defer rr.mu.Unlock() defer rr.mu.Unlock()
for _, a := range rr.connected { var cnt int
if a == addr { for _, a := range rr.addrs {
return nil if a.addr == addr {
if a.connected {
return nil
}
a.connected = true
}
if a.connected {
cnt++
} }
} }
rr.connected = append(rr.connected, addr) // addr is only one which is connected. Notify the Get() callers who are blocking.
if len(rr.connected) == 1 { if cnt == 1 && rr.waitCh != nil {
// addr is only one available. Notify the Get() callers who are blocking. close(rr.waitCh)
if rr.waitCh != nil { rr.waitCh = nil
close(rr.waitCh)
rr.waitCh = nil
}
} }
return func(err error) { return func(err error) {
rr.down(addr, err) rr.down(addr, err)
} }
} }
// down removes addr from rr.connected and moves the remaining addrs forward. // down unsets the connected state of addr.
func (rr *roundRobin) down(addr Address, err error) { func (rr *roundRobin) down(addr Address, err error) {
rr.mu.Lock() rr.mu.Lock()
defer rr.mu.Unlock() defer rr.mu.Unlock()
for i, a := range rr.connected { for _, a := range rr.addrs {
if a == addr { if addr == a.addr {
copy(rr.connected[i:], rr.connected[i+1:]) a.connected = false
rr.connected = rr.connected[:len(rr.connected)-1] break
return
} }
} }
} }
@ -266,14 +277,26 @@ func (rr *roundRobin) Get(ctx context.Context, opts BalancerGetOptions) (addr Ad
err = ErrClientConnClosing err = ErrClientConnClosing
return return
} }
if rr.next >= len(rr.connected) {
rr.next = 0 if len(rr.addrs) > 0 {
} if rr.next >= len(rr.addrs) {
if len(rr.connected) > 0 { rr.next = 0
addr = rr.connected[rr.next] }
rr.next++ next := rr.next
rr.mu.Unlock() for {
return a := rr.addrs[next]
next = (next + 1) % len(rr.addrs)
if a.connected {
addr = a.addr
rr.next = next
rr.mu.Unlock()
return
}
if next == rr.next {
// Has iterated all the possible address but none is connected.
break
}
}
} }
// There is no address available. Wait on rr.waitCh. // There is no address available. Wait on rr.waitCh.
// TODO(zhaoq): Handle the case when opts.BlockingWait is false. // TODO(zhaoq): Handle the case when opts.BlockingWait is false.
@ -296,24 +319,35 @@ func (rr *roundRobin) Get(ctx context.Context, opts BalancerGetOptions) (addr Ad
err = ErrClientConnClosing err = ErrClientConnClosing
return return
} }
if len(rr.connected) == 0 {
// The newly added addr got removed by Down() again. if len(rr.addrs) > 0 {
if rr.waitCh == nil { if rr.next >= len(rr.addrs) {
ch = make(chan struct{}) rr.next = 0
rr.waitCh = ch }
} else { next := rr.next
ch = rr.waitCh for {
a := rr.addrs[next]
next = (next + 1) % len(rr.addrs)
if a.connected {
addr = a.addr
rr.next = next
rr.mu.Unlock()
return
}
if next == rr.next {
// Has iterated all the possible address but none is connected.
break
}
} }
rr.mu.Unlock()
continue
} }
if rr.next >= len(rr.connected) { // The newly added addr got removed by Down() again.
rr.next = 0 if rr.waitCh == nil {
ch = make(chan struct{})
rr.waitCh = ch
} else {
ch = rr.waitCh
} }
addr = rr.connected[rr.next]
rr.next++
rr.mu.Unlock() rr.mu.Unlock()
return
} }
} }
} }

View File

@ -57,7 +57,7 @@ func (ts TokenSource) GetRequestMetadata(ctx context.Context, uri ...string) (ma
return nil, err return nil, err
} }
return map[string]string{ return map[string]string{
"authorization": token.TokenType + " " + token.AccessToken, "authorization": token.Type() + " " + token.AccessToken,
}, nil }, nil
} }