From ec72547c8d25929155e6c797f965872fb8448b47 Mon Sep 17 00:00:00 2001 From: Chris Lu Date: Fri, 20 Aug 2021 01:12:52 -0700 Subject: [PATCH 001/130] started by copying from https://sourcegraph.com/github.com/timtadh/data-structures@master/-/tree/tree/bptree --- weed/util/bptree/bpmap.go | 77 ++ weed/util/bptree/bptree.go | 160 ++++ weed/util/bptree/bptree_node.go | 752 ++++++++++++++++ weed/util/bptree/bptree_test.go | 1460 +++++++++++++++++++++++++++++++ weed/util/bptree/int.go | 357 ++++++++ weed/util/bptree/rand.go | 2 + weed/util/bptree/string.go | 71 ++ weed/util/bptree/types.go | 103 +++ 8 files changed, 2982 insertions(+) create mode 100644 weed/util/bptree/bpmap.go create mode 100644 weed/util/bptree/bptree.go create mode 100644 weed/util/bptree/bptree_node.go create mode 100644 weed/util/bptree/bptree_test.go create mode 100644 weed/util/bptree/int.go create mode 100644 weed/util/bptree/rand.go create mode 100644 weed/util/bptree/string.go create mode 100644 weed/util/bptree/types.go diff --git a/weed/util/bptree/bpmap.go b/weed/util/bptree/bpmap.go new file mode 100644 index 000000000..37b2b25bb --- /dev/null +++ b/weed/util/bptree/bpmap.go @@ -0,0 +1,77 @@ +package bptree + +import ( + "fmt" +) + +/* A BpMap is a B+Tree with support for duplicate keys disabled. This makes it + * behave like a regular Map rather than a MultiMap. + */ +type BpMap BpTree + +func NewBpMap(node_size int) *BpMap { + return &BpMap{ + root: NewLeaf(node_size, true), + size: 0, + } +} + +func (self *BpMap) Size() int { + return (*BpTree)(self).Size() +} + +func (self *BpMap) Has(key Hashable) bool { + return (*BpTree)(self).Has(key) +} + +func (self *BpMap) Put(key Hashable, value interface{}) (err error) { + had := self.Has(key) + new_root, err := self.root.put(key, value) + if err != nil { + return err + } + self.root = new_root + if !had { + self.size += 1 + } + return nil +} + +func (self *BpMap) Get(key Hashable) (value interface{}, err error) { + j, l := self.root.get_start(key) + if l.keys[j].Equals(key) { + return l.values[j], nil + } + return nil, fmt.Errorf("key not found: %s", key) +} + +func (self *BpMap) Remove(key Hashable) (value interface{}, err error) { + value, err = self.Get(key) + if err != nil { + return nil, err + } + ns := self.root.NodeSize() + new_root, err := self.root.remove(key, func(value interface{}) bool { return true }) + if err != nil { + return nil, err + } + if new_root == nil { + self.root = NewLeaf(ns, true) + } else { + self.root = new_root + } + self.size-- + return value, nil +} + +func (self *BpMap) Keys() (ki KIterator) { + return (*BpTree)(self).Keys() +} + +func (self *BpMap) Values() (vi Iterator) { + return (*BpTree)(self).Values() +} + +func (self *BpMap) Iterate() (kvi KVIterator) { + return (*BpTree)(self).Iterate() +} diff --git a/weed/util/bptree/bptree.go b/weed/util/bptree/bptree.go new file mode 100644 index 000000000..4b68adb20 --- /dev/null +++ b/weed/util/bptree/bptree.go @@ -0,0 +1,160 @@ +package bptree + +// started by copying from https://sourcegraph.com/github.com/timtadh/data-structures@master/-/tree/tree/bptree + +/* A BpTree is a B+Tree with support for duplicate keys. This makes it behave as + * a MultiMap. Additionally you can use the Range operator to select k/v in a + * range. If from > to it will iterate backwards. + */ +type BpTree struct { + root *BpNode + size int +} + +type loc_iterator func() (i int, leaf *BpNode, li loc_iterator) + +func NewBpTree(node_size int) *BpTree { + return &BpTree{ + root: NewLeaf(node_size, false), + size: 0, + } +} + +func (self *BpTree) Size() int { + return self.size +} + +func (self *BpTree) Has(key Hashable) bool { + if len(self.root.keys) == 0 { + return false + } + j, l := self.root.get_start(key) + return l.keys[j].Equals(key) +} + +func (self *BpTree) Count(key Hashable) int { + if len(self.root.keys) == 0 { + return 0 + } + j, l := self.root.get_start(key) + count := 0 + end := false + for !end && l.keys[j].Equals(key) { + count++ + j, l, end = next_location(j, l) + } + return count +} + +func (self *BpTree) Add(key Hashable, value interface{}) (err error) { + new_root, err := self.root.put(key, value) + if err != nil { + return err + } + self.root = new_root + self.size += 1 + return nil +} + +func (self *BpTree) Replace(key Hashable, where WhereFunc, value interface{}) (err error) { + li := self.root.forward(key, key) + for i, leaf, next := li(); next != nil; i, leaf, next = next() { + if where(leaf.values[i]) { + leaf.values[i] = value + } + } + return nil +} + +func (self *BpTree) Find(key Hashable) (kvi KVIterator) { + return self.Range(key, key) +} + +func (self *BpTree) Range(from, to Hashable) (kvi KVIterator) { + var li loc_iterator + if !to.Less(from) { + li = self.root.forward(from, to) + } else { + li = self.root.backward(from, to) + } + kvi = func() (key Hashable, value interface{}, next KVIterator) { + var i int + var leaf *BpNode + i, leaf, li = li() + if li == nil { + return nil, nil, nil + } + return leaf.keys[i], leaf.values[i], kvi + } + return kvi +} + +func (self *BpTree) RemoveWhere(key Hashable, where WhereFunc) (err error) { + ns := self.root.NodeSize() + new_root, err := self.root.remove(key, where) + if err != nil { + return err + } + if new_root == nil { + self.root = NewLeaf(ns, false) + } else { + self.root = new_root + } + self.size -= 1 + return nil +} + +func (self *BpTree) Keys() (ki KIterator) { + li := self.root.all() + var prev Equatable + ki = func() (key Hashable, next KIterator) { + var i int + var leaf *BpNode + i, leaf, li = li() + if li == nil { + return nil, nil + } + if leaf.keys[i].Equals(prev) { + return ki() + } + prev = leaf.keys[i] + return leaf.keys[i], ki + } + return ki +} + +func (self *BpTree) Values() (vi Iterator) { + return MakeValuesIterator(self) +} + +func (self *BpTree) Items() (vi KIterator) { + return MakeItemsIterator(self) +} + +func (self *BpTree) Iterate() (kvi KVIterator) { + li := self.root.all() + kvi = func() (key Hashable, value interface{}, next KVIterator) { + var i int + var leaf *BpNode + i, leaf, li = li() + if li == nil { + return nil, nil, nil + } + return leaf.keys[i], leaf.values[i], kvi + } + return kvi +} + +func (self *BpTree) Backward() (kvi KVIterator) { + li := self.root.all_backward() + kvi = func() (key Hashable, value interface{}, next KVIterator) { + var i int + var leaf *BpNode + i, leaf, li = li() + if li == nil { + return nil, nil, nil + } + return leaf.keys[i], leaf.values[i], kvi + } + return kvi +} \ No newline at end of file diff --git a/weed/util/bptree/bptree_node.go b/weed/util/bptree/bptree_node.go new file mode 100644 index 000000000..3574371f5 --- /dev/null +++ b/weed/util/bptree/bptree_node.go @@ -0,0 +1,752 @@ +package bptree + +type BpNode struct { + keys []Hashable + values []interface{} + pointers []*BpNode + next *BpNode + prev *BpNode + no_dup bool +} + +func NewInternal(size int) *BpNode { + if size < 0 { + panic(NegativeSize()) + } + return &BpNode{ + keys: make([]Hashable, 0, size), + pointers: make([]*BpNode, 0, size), + } +} + +func NewLeaf(size int, no_dup bool) *BpNode { + if size < 0 { + panic(NegativeSize()) + } + return &BpNode{ + keys: make([]Hashable, 0, size), + values: make([]interface{}, 0, size), + no_dup: no_dup, + } +} + +func (self *BpNode) Full() bool { + return len(self.keys) == cap(self.keys) +} + +func (self *BpNode) Pure() bool { + if len(self.keys) == 0 { + return true + } + k0 := self.keys[0] + for _, k := range self.keys { + if !k0.Equals(k) { + return false + } + } + return true +} + +func (self *BpNode) Internal() bool { + return cap(self.pointers) > 0 +} + +func (self *BpNode) NodeSize() int { + return cap(self.keys) +} + +func (self *BpNode) Height() int { + if !self.Internal() { + return 1 + } else if len(self.pointers) == 0 { + panic(BpTreeError("Internal node has no pointers but asked for height")) + } + return self.pointers[0].Height() + 1 +} + +func (self *BpNode) count(key Hashable) int { + i, _ := self.find(key) + count := 0 + for ; i < len(self.keys); i++ { + if self.keys[i].Equals(key) { + count++ + } else { + break + } + } + return count +} + +func (self *BpNode) has(key Hashable) bool { + _, has := self.find(key) + return has +} + +func (self *BpNode) left_most_leaf() *BpNode { + if self.Internal() { + return self.pointers[0].left_most_leaf() + } + return self +} + +func (self *BpNode) right_most_leaf() *BpNode { + if self.Internal() { + return self.pointers[len(self.pointers)-1].right_most_leaf() + } + return self +} + +/* returns the index and leaf-block of the first key greater than or equal to + * the search key. (unless the search key is greater than all the keys in the + * tree, in that case it will be the last key in the tree) + */ +func (self *BpNode) get_start(key Hashable) (i int, leaf *BpNode) { + if self.Internal() { + return self.internal_get_start(key) + } else { + return self.leaf_get_start(key) + } +} + +func next_location(i int, leaf *BpNode) (int, *BpNode, bool) { + j := i + 1 + for j >= len(leaf.keys) && leaf.next != nil { + j = 0 + leaf = leaf.next + } + if j >= len(leaf.keys) { + return -1, nil, true + } + return j, leaf, false +} + +func prev_location(i int, leaf *BpNode) (int, *BpNode, bool) { + j := i - 1 + for j < 0 && leaf.prev != nil { + leaf = leaf.prev + j = len(leaf.keys) - 1 + } + if j < 0 { + return -1, nil, true + } + return j, leaf, false +} + +/* returns the index and leaf-block of the last key equal to the search key or + * the first key greater than the search key. (unless the search key is greater + * than all the keys in the tree, in that case it will be the last key in the + * tree) + */ +func (self *BpNode) get_end(key Hashable) (i int, leaf *BpNode) { + end := false + i, leaf = self.get_start(key) + pi, pleaf := i, leaf + for !end && leaf.keys[i].Equals(key) { + pi, pleaf = i, leaf + i, leaf, end = next_location(i, leaf) + } + return pi, pleaf +} + +func (self *BpNode) internal_get_start(key Hashable) (i int, leaf *BpNode) { + if !self.Internal() { + panic(BpTreeError("Expected a internal node")) + } + i, has := self.find(key) + if !has && i > 0 { + // if it doesn't have it and the index > 0 then we have the next block + // so we have to subtract one from the index. + i-- + } + child := self.pointers[i] + return child.get_start(key) +} + +func (self *BpNode) leaf_get_start(key Hashable) (i int, leaf *BpNode) { + i, has := self.find(key) + if i >= len(self.keys) && i > 0 { + i = len(self.keys) - 1 + } + if !has && (len(self.keys) == 0 || self.keys[i].Less(key)) && self.next != nil { + return self.next.leaf_get_start(key) + } + return i, self +} + +/* This puts the k/v pair into the B+Tree rooted at this node and returns the + * (possibly) new root of the tree. + */ +func (self *BpNode) put(key Hashable, value interface{}) (root *BpNode, err error) { + a, b, err := self.insert(key, value) + if err != nil { + return nil, err + } else if b == nil { + return a, nil + } + // else we have root split + root = NewInternal(self.NodeSize()) + root.put_kp(a.keys[0], a) + root.put_kp(b.keys[0], b) + return root, nil +} + +// right is only set on split +// left is always set. When split is false left is the pointer to block +// When split is true left is the pointer to the new left +// block +func (self *BpNode) insert(key Hashable, value interface{}) (a, b *BpNode, err error) { + if self.Internal() { + return self.internal_insert(key, value) + } else { // leaf node + return self.leaf_insert(key, value) + } +} + +/* - first find the child to insert into + * - do the child insert + * - if there was a split: + * - if the block is full, split this block + * - else insert the new key/pointer into this block + */ +func (self *BpNode) internal_insert(key Hashable, value interface{}) (a, b *BpNode, err error) { + if !self.Internal() { + return nil, nil, BpTreeError("Expected a internal node") + } + i, has := self.find(key) + if !has && i > 0 { + // if it doesn't have it and the index > 0 then we have the next block + // so we have to subtract one from the index. + i-- + } + child := self.pointers[i] + p, q, err := child.insert(key, value) + if err != nil { + return nil, nil, err + } + self.keys[i] = p.keys[0] + self.pointers[i] = p + if q != nil { + // we had a split + if self.Full() { + return self.internal_split(q.keys[0], q) + } else { + if err := self.put_kp(q.keys[0], q); err != nil { + return nil, nil, err + } + return self, nil, nil + } + } + return self, nil, nil +} + +/* On split + * - first assert that the key to be inserted is not already in the block. + * - Make a new block + * - balance the two blocks. + * - insert the new key/pointer combo into the correct block + */ +func (self *BpNode) internal_split(key Hashable, ptr *BpNode) (a, b *BpNode, err error) { + if !self.Internal() { + return nil, nil, BpTreeError("Expected a internal node") + } + if self.has(key) { + return nil, nil, BpTreeError("Tried to split an internal block on duplicate key") + } + a = self + b = NewInternal(self.NodeSize()) + balance_nodes(a, b) + if key.Less(b.keys[0]) { + if err := a.put_kp(key, ptr); err != nil { + return nil, nil, err + } + } else { + if err := b.put_kp(key, ptr); err != nil { + return nil, nil, err + } + } + return a, b, nil +} + +/* if the leaf is full then it will defer to a leaf_split + * (but in one case that will not actually split in the case of a insert into + * a pure block with a matching key) + * else this leaf will get a new entry. + */ +func (self *BpNode) leaf_insert(key Hashable, value interface{}) (a, b *BpNode, err error) { + if self.Internal() { + return nil, nil, BpTreeError("Expected a leaf node") + } + if self.no_dup { + i, has := self.find(key) + if has { + self.values[i] = value + return self, nil, nil + } + } + if self.Full() { + return self.leaf_split(key, value) + } else { + if err := self.put_kv(key, value); err != nil { + return nil, nil, err + } + return self, nil, nil + } +} + +/* on leaf split if the block is pure then it will defer to pure_leaf_split + * else + * - a new block will be made and inserted after this one + * - the two blocks will be balanced with balanced_nodes + * - if the key is less than b.keys[0] it will go in a else b + */ +func (self *BpNode) leaf_split(key Hashable, value interface{}) (a, b *BpNode, err error) { + if self.Internal() { + return nil, nil, BpTreeError("Expected a leaf node") + } + if self.Pure() { + return self.pure_leaf_split(key, value) + } + a = self + b = NewLeaf(self.NodeSize(), self.no_dup) + insert_linked_list_node(b, a, a.next) + balance_nodes(a, b) + if key.Less(b.keys[0]) { + if err := a.put_kv(key, value); err != nil { + return nil, nil, err + } + } else { + if err := b.put_kv(key, value); err != nil { + return nil, nil, err + } + } + return a, b, nil +} + +/* a pure leaf split has two cases: + * 1) the inserted key is less than the current pure block. + * - a new block should be created before the current block + * - the key should be put in it + * 2) the inserted key is greater than or equal to the pure block. + * - the end of run of pure blocks should be found + * - if the key is equal to pure block and the last block is not full insert + * the new kv + * - else split by making a new block after the last block in the run + * and putting the new key there. + * - always return the current block as "a" and the new block as "b" + */ +func (self *BpNode) pure_leaf_split(key Hashable, value interface{}) (a, b *BpNode, err error) { + if self.Internal() || !self.Pure() { + return nil, nil, BpTreeError("Expected a pure leaf node") + } + if key.Less(self.keys[0]) { + a = NewLeaf(self.NodeSize(), self.no_dup) + b = self + if err := a.put_kv(key, value); err != nil { + return nil, nil, err + } + insert_linked_list_node(a, b.prev, b) + return a, b, nil + } else { + a = self + e := self.find_end_of_pure_run() + if e.keys[0].Equals(key) && !e.Full() { + if err := e.put_kv(key, value); err != nil { + return nil, nil, err + } + return a, nil, nil + } else { + b = NewLeaf(self.NodeSize(), self.no_dup) + if err := b.put_kv(key, value); err != nil { + return nil, nil, err + } + insert_linked_list_node(b, e, e.next) + if e.keys[0].Equals(key) { + return a, nil, nil + } + return a, b, nil + } + } +} + +func (self *BpNode) put_kp(key Hashable, ptr *BpNode) error { + if self.Full() { + return BpTreeError("Block is full.") + } + if !self.Internal() { + return BpTreeError("Expected a internal node") + } + i, has := self.find(key) + if has { + return BpTreeError("Tried to insert a duplicate key into an internal node") + } else if i < 0 { + panic(BpTreeError("find returned a negative int")) + } else if i >= cap(self.keys) { + panic(BpTreeError("find returned a int > than cap(keys)")) + } + if err := self.put_key_at(i, key); err != nil { + return err + } + if err := self.put_pointer_at(i, ptr); err != nil { + return err + } + return nil +} + +func (self *BpNode) put_kv(key Hashable, value interface{}) error { + if self.Full() { + return BpTreeError("Block is full.") + } + if self.Internal() { + return BpTreeError("Expected a leaf node") + } + i, _ := self.find(key) + if i < 0 { + panic(BpTreeError("find returned a negative int")) + } else if i >= cap(self.keys) { + panic(BpTreeError("find returned a int > than cap(keys)")) + } + if err := self.put_key_at(i, key); err != nil { + return err + } + if err := self.put_value_at(i, value); err != nil { + return err + } + return nil +} + +func (self *BpNode) put_key_at(i int, key Hashable) error { + if self.Full() { + return BpTreeError("Block is full.") + } + self.keys = self.keys[:len(self.keys)+1] + for j := len(self.keys) - 1; j > i; j-- { + self.keys[j] = self.keys[j-1] + } + self.keys[i] = key + return nil +} + +func (self *BpNode) put_value_at(i int, value interface{}) error { + if len(self.values) == cap(self.values) { + return BpTreeError("Block is full.") + } + if self.Internal() { + return BpTreeError("Expected a leaf node") + } + self.values = self.values[:len(self.values)+1] + for j := len(self.values) - 1; j > i; j-- { + self.values[j] = self.values[j-1] + } + self.values[i] = value + return nil +} + +func (self *BpNode) put_pointer_at(i int, pointer *BpNode) error { + if len(self.pointers) == cap(self.pointers) { + return BpTreeError("Block is full.") + } + if !self.Internal() { + return BpTreeError("Expected a internal node") + } + self.pointers = self.pointers[:len(self.pointers)+1] + for j := len(self.pointers) - 1; j > i; j-- { + self.pointers[j] = self.pointers[j-1] + } + self.pointers[i] = pointer + return nil +} + +func (self *BpNode) remove(key Hashable, where WhereFunc) (a *BpNode, err error) { + if self.Internal() { + return self.internal_remove(key, nil, where) + } else { + return self.leaf_remove(key, self.keys[len(self.keys)-1], where) + } +} + +func (self *BpNode) internal_remove(key Hashable, sibling *BpNode, where WhereFunc) (a *BpNode, err error) { + if !self.Internal() { + panic(BpTreeError("Expected a internal node")) + } + i, has := self.find(key) + if !has && i > 0 { + // if it doesn't have it and the index > 0 then we have the next block + // so we have to subtract one from the index. + i-- + } + if i+1 < len(self.keys) { + sibling = self.pointers[i+1] + } else if sibling != nil { + sibling = sibling.left_most_leaf() + } + child := self.pointers[i] + if child.Internal() { + child, err = child.internal_remove(key, sibling, where) + } else { + if sibling == nil { + child, err = child.leaf_remove(key, nil, where) + } else { + child, err = child.leaf_remove(key, sibling.keys[0], where) + } + } + if err != nil { + return nil, err + } + if child == nil { + if err := self.remove_key_at(i); err != nil { + return nil, err + } + if err := self.remove_ptr_at(i); err != nil { + return nil, err + } + } else { + self.keys[i] = child.keys[0] + self.pointers[i] = child + } + if len(self.keys) == 0 { + return nil, nil + } + return self, nil +} + +func (self *BpNode) leaf_remove(key, stop Hashable, where WhereFunc) (a *BpNode, err error) { + if self.Internal() { + return nil, BpTreeError("Expected a leaf node") + } + a = self + for j, l, next := self.forward(key, key)(); next != nil; j, l, next = next() { + if where(l.values[j]) { + if err := l.remove_key_at(j); err != nil { + return nil, err + } + if err := l.remove_value_at(j); err != nil { + return nil, err + } + } + if len(l.keys) == 0 { + remove_linked_list_node(l) + if l.next == nil { + a = nil + } else if stop == nil { + a = nil + } else if !l.next.keys[0].Equals(stop) { + a = l.next + } else { + a = nil + } + } + } + return a, nil +} + +func (self *BpNode) remove_key_at(i int) error { + if i >= len(self.keys) || i < 0 { + return BpTreeError("i, %v, is out of bounds, %v, %v %v.", i, len(self.keys), len(self.values), self) + } + for j := i; j < len(self.keys)-1; j++ { + self.keys[j] = self.keys[j+1] + } + self.keys = self.keys[:len(self.keys)-1] + return nil +} + +func (self *BpNode) remove_value_at(i int) error { + if i >= len(self.values) || i < 0 { + return BpTreeError("i, %v, is out of bounds, %v.", i, len(self.values)) + } + for j := i; j < len(self.values)-1; j++ { + self.values[j] = self.values[j+1] + } + self.values = self.values[:len(self.values)-1] + return nil +} + +func (self *BpNode) remove_ptr_at(i int) error { + if i >= len(self.pointers) || i < 0 { + return BpTreeError("i, %v, is out of bounds, %v.", i, len(self.pointers)) + } + for j := i; j < len(self.pointers)-1; j++ { + self.pointers[j] = self.pointers[j+1] + } + self.pointers = self.pointers[:len(self.pointers)-1] + return nil +} + +func (self *BpNode) find(key Hashable) (int, bool) { + var l int = 0 + var r int = len(self.keys) - 1 + var m int + for l <= r { + m = ((r - l) >> 1) + l + if key.Less(self.keys[m]) { + r = m - 1 + } else if key.Equals(self.keys[m]) { + for j := m; j >= 0; j-- { + if j == 0 || !key.Equals(self.keys[j-1]) { + return j, true + } + } + } else { + l = m + 1 + } + } + return l, false +} + +func (self *BpNode) find_end_of_pure_run() *BpNode { + k := self.keys[0] + p := self + n := self.next + for n != nil && n.Pure() && k.Equals(n.keys[0]) { + p = n + n = n.next + } + return p +} + +func (self *BpNode) all() (li loc_iterator) { + j := -1 + l := self.left_most_leaf() + end := false + j, l, end = next_location(j, l) + li = func() (i int, leaf *BpNode, next loc_iterator) { + if end { + return -1, nil, nil + } + i = j + leaf = l + j, l, end = next_location(j, l) + return i, leaf, li + } + return li +} + +func (self *BpNode) all_backward() (li loc_iterator) { + l := self.right_most_leaf() + j := len(l.keys) + end := false + j, l, end = prev_location(j, l) + li = func() (i int, leaf *BpNode, next loc_iterator) { + if end { + return -1, nil, nil + } + i = j + leaf = l + j, l, end = prev_location(j, l) + return i, leaf, li + } + return li +} + +func (self *BpNode) forward(from, to Hashable) (li loc_iterator) { + j, l := self.get_start(from) + end := false + j-- + li = func() (i int, leaf *BpNode, next loc_iterator) { + j, l, end = next_location(j, l) + if end || to.Less(l.keys[j]) { + return -1, nil, nil + } + return j, l, li + } + return li +} + +func (self *BpNode) backward(from, to Hashable) (li loc_iterator) { + j, l := self.get_end(from) + end := false + li = func() (i int, leaf *BpNode, next loc_iterator) { + if end || l.keys[j].Less(to) { + return -1, nil, nil + } + i = j + leaf = l + j, l, end = prev_location(i, l) + return i, leaf, li + } + return li +} + +func insert_linked_list_node(n, prev, next *BpNode) { + if (prev != nil && prev.next != next) || (next != nil && next.prev != prev) { + panic(BpTreeError("prev and next not hooked up")) + } + n.prev = prev + n.next = next + if prev != nil { + prev.next = n + } + if next != nil { + next.prev = n + } +} + +func remove_linked_list_node(n *BpNode) { + if n.prev != nil { + n.prev.next = n.next + } + if n.next != nil { + n.next.prev = n.prev + } +} + +/* a must be full and b must be empty else there will be a panic + */ +func balance_nodes(a, b *BpNode) { + if len(b.keys) != 0 { + panic(BpTreeError("b was not empty")) + } + if !a.Full() { + panic(BpTreeError("a was not full", a)) + } + if cap(a.keys) != cap(b.keys) { + panic(BpTreeError("cap(a.keys) != cap(b.keys)")) + } + if cap(a.values) != cap(b.values) { + panic(BpTreeError("cap(a.values) != cap(b.values)")) + } + if cap(a.pointers) != cap(b.pointers) { + panic(BpTreeError("cap(a.pointers) != cap(b.pointers)")) + } + m := len(a.keys) / 2 + for m < len(a.keys) && a.keys[m-1].Equals(a.keys[m]) { + m++ + } + if m == len(a.keys) { + m-- + for m > 0 && a.keys[m-1].Equals(a.keys[m]) { + m-- + } + } + var lim int = len(a.keys) - m + b.keys = b.keys[:lim] + if cap(a.values) > 0 { + if cap(a.values) != cap(a.keys) { + panic(BpTreeError("cap(a.values) != cap(a.keys)")) + } + b.values = b.values[:lim] + } + if cap(a.pointers) > 0 { + if cap(a.pointers) != cap(a.keys) { + panic(BpTreeError("cap(a.pointers) != cap(a.keys)")) + } + b.pointers = b.pointers[:lim] + } + for i := 0; i < lim; i++ { + j := m + i + b.keys[i] = a.keys[j] + if cap(a.values) > 0 { + b.values[i] = a.values[j] + } + if cap(a.pointers) > 0 { + b.pointers[i] = a.pointers[j] + } + } + a.keys = a.keys[:m] + if cap(a.values) > 0 { + a.values = a.values[:m] + } + if cap(a.pointers) > 0 { + a.pointers = a.pointers[:m] + } +} diff --git a/weed/util/bptree/bptree_test.go b/weed/util/bptree/bptree_test.go new file mode 100644 index 000000000..cf978ede7 --- /dev/null +++ b/weed/util/bptree/bptree_test.go @@ -0,0 +1,1460 @@ +package bptree + +import ( + "encoding/hex" + "runtime/debug" + "sort" + "sync" + "testing" + + crand "crypto/rand" + "encoding/binary" + mrand "math/rand" + +) + +var rand *mrand.Rand + +func init() { + seed := make([]byte, 8) + if _, err := crand.Read(seed); err == nil { + rand = ThreadSafeRand(int64(binary.BigEndian.Uint64(seed))) + } else { + panic(err) + } +} + +func randslice(length int) []byte { + return RandSlice(length) +} + +func randstr(length int) String { + return String(RandStr(length)) +} + +type Strings []String + +func (self Strings) Len() int { + return len(self) +} + +func (self Strings) Less(i, j int) bool { + return self[i].Less(self[j]) +} + +func (self Strings) Swap(i, j int) { + self[i], self[j] = self[j], self[i] +} + +type record struct { + key String + value String +} + +type records []*record + +func (self records) Len() int { + return len(self) +} + +func (self records) Less(i, j int) bool { + return self[i].key.Less(self[j].key) +} + +func (self records) Swap(i, j int) { + self[i], self[j] = self[j], self[i] +} + +func BenchmarkBpTree(b *testing.B) { + b.StopTimer() + + recs := make(records, 100) + ranrec := func() *record { + return &record{randstr(20), randstr(20)} + } + + for i := range recs { + recs[i] = ranrec() + } + + b.StartTimer() + for i := 0; i < b.N; i++ { + t := NewBpTree(23) + for _, r := range recs { + t.Add(r.key, r.value) + } + for _, r := range recs { + t.RemoveWhere(r.key, func(value interface{}) bool { return true }) + } + } +} + +func TestAddHasCountFindIterateRemove(t *testing.T) { + + ranrec := func() *record { + return &record{ + randstr(12), + randstr(12), + } + } + + test := func(bpt *BpTree) { + var err error + recs := make(records, 128) + new_recs := make(records, 128) + for i := range recs { + r := ranrec() + recs[i] = r + new_recs[i] = &record{r.key, randstr(12)} + err = bpt.Add(r.key, r.value) + if err != nil { + t.Error(err) + } + if bpt.Size() != (i + 1) { + t.Error("size was wrong", bpt.Size(), i+1) + } + } + + for i, r := range recs { + if has := bpt.Has(r.key); !has { + t.Error(bpt, "Missing key") + } + if has := bpt.Has(randstr(10)); has { + t.Error("Table has extra key") + } + if count := bpt.Count(r.key); count != 1 { + t.Error(bpt, "Missing key") + } + if count := bpt.Count(randstr(10)); count != 0 { + t.Error("Table has extra key") + } + for k, v, next := bpt.Find(r.key)(); next != nil; k, v, next = next() { + if !k.Equals(r.key) { + t.Error(bpt, "Find Failed Key Error") + } + if !v.(String).Equals(r.value) { + t.Error(bpt, "Find Failed Value Error") + } + } + err = bpt.Replace(r.key, func(value interface{}) bool { return true }, new_recs[i].value) + if err != nil { + t.Error(err) + } + } + sort.Sort(recs) + sort.Sort(new_recs) + i := 0 + for k, v, next := bpt.Iterate()(); next != nil; k, v, next = next() { + if !recs[i].key.Equals(k) { + t.Error("iterate error wrong key") + } + if !new_recs[i].value.Equals(v.(String)) { + t.Error("iterate error wrong value") + } + i++ + } + i = len(recs) - 1 + for k, v, next := bpt.Backward()(); next != nil; k, v, next = next() { + if !recs[i].key.Equals(k) { + t.Error("iterate error wrong key") + } + if !new_recs[i].value.Equals(v.(String)) { + t.Error("iterate error wrong value") + } + i-- + } + i = 0 + for k, next := bpt.Keys()(); next != nil; k, next = next() { + if !recs[i].key.Equals(k) { + t.Error("iterate error wrong key") + } + i++ + } + i = 7 + for k, v, next := bpt.Range(recs[i].key, recs[i+(len(recs)/2)].key)(); next != nil; k, v, next = next() { + if !recs[i].key.Equals(k) { + t.Error("iterate error wrong key") + } + if !new_recs[i].value.Equals(v.(String)) { + t.Error("iterate error wrong value") + } + i++ + } + for k, v, next := bpt.Range(recs[i].key, recs[7].key)(); next != nil; k, v, next = next() { + if !recs[i].key.Equals(k) { + t.Error("iterate error wrong key") + } + if !new_recs[i].value.Equals(v.(String)) { + t.Error("iterate error wrong value", k, v, recs[i].value, new_recs[i].value) + } + i-- + } + for i, r := range recs { + if has := bpt.Has(r.key); !has { + t.Error(bpt, "Missing key") + } + if count := bpt.Count(r.key); count != 1 { + t.Error(bpt, "Missing key") + } + if err := bpt.RemoveWhere(r.key, func(value interface{}) bool { return true }); err != nil { + t.Fatal(bpt, err) + } + if has := bpt.Has(r.key); has { + t.Error("Table has extra key") + } + for _, x := range recs[i+1:] { + if has := bpt.Has(x.key); !has { + t.Error(bpt, "Missing key", x.key) + } + } + } + } + for i := 2; i < 64; i++ { + test(NewBpTree(i)) + } +} + +func TestBpMap(t *testing.T) { + + ranrec := func() *record { + return &record{ + randstr(12), + randstr(12), + } + } + + test := func(table MapOperable) { + recs := make(records, 400) + for i := range recs { + r := ranrec() + recs[i] = r + err := table.Put(r.key, String("")) + if err != nil { + t.Error(err) + } + err = table.Put(r.key, r.value) + if err != nil { + t.Error(err) + } + if table.Size() != (i + 1) { + t.Error("size was wrong", table.Size(), i+1) + } + } + + for _, r := range recs { + if has := table.Has(r.key); !has { + t.Error(table, "Missing key") + } + if has := table.Has(randstr(12)); has { + t.Error("Table has extra key") + } + if val, err := table.Get(r.key); err != nil { + t.Error(err) + } else if !(val.(String)).Equals(r.value) { + t.Error("wrong value") + } + } + + for i, x := range recs { + if val, err := table.Remove(x.key); err != nil { + t.Error(err) + } else if !(val.(String)).Equals(x.value) { + t.Error("wrong value") + } + for _, r := range recs[i+1:] { + if has := table.Has(r.key); !has { + t.Error("Missing key") + } + if has := table.Has(randstr(12)); has { + t.Error("Table has extra key") + } + if val, err := table.Get(r.key); err != nil { + t.Error(err) + } else if !(val.(String)).Equals(r.value) { + t.Error("wrong value") + } + } + if table.Size() != (len(recs) - (i + 1)) { + t.Error("size was wrong", table.Size(), (len(recs) - (i + 1))) + } + } + } + + test(NewBpMap(23)) +} + +func Test_get_start(t *testing.T) { + root := NewLeaf(2, false) + root, err := root.put(Int(1), 1) + if err != nil { + t.Error(err) + } + root, err = root.put(Int(5), 3) + if err != nil { + t.Error(err) + } + root, err = root.put(Int(3), 2) + if err != nil { + t.Error(err) + } + t.Log(root) + t.Log(root.pointers[0]) + t.Log(root.pointers[1]) + i, n := root.get_start(Int(1)) + if n != root.pointers[0] { + t.Error("wrong node from get_start") + } + if i != 0 { + t.Error("wrong index from get_start") + } + i, n = root.get_start(Int(3)) + if n != root.pointers[0] { + t.Error("wrong node from get_start") + } + if i != 1 { + t.Error("wrong index from get_start") + } + i, n = root.get_start(Int(5)) + if n != root.pointers[1] { + t.Error("wrong node from get_start") + } + if i != 0 { + t.Error("wrong index from get_start") + } + i, n = root.get_start(Int(2)) + if n != root.pointers[0] { + t.Error("wrong node from get_start") + } + if i != 1 { + t.Error("wrong index from get_start") + } + i, n = root.get_start(Int(4)) + t.Log(n) + if n != root.pointers[1] { + t.Error("wrong node from get_start") + } + if i != 0 { + t.Error("wrong index from get_start") + } + i, n = root.get_start(Int(0)) + if n != root.pointers[0] { + t.Error("wrong node from get_start") + } + if i != 0 { + t.Error("wrong index from get_start") + } + i, n = root.get_start(Int(5)) + if n != root.pointers[1] { + t.Error("wrong node from get_start") + } + if i != 0 { + t.Error("wrong index from get_start") + } +} + +func Test_get_end(t *testing.T) { + root := NewLeaf(3, false) + root, err := root.put(Int(1), -1) + if err != nil { + t.Fatal(err) + } + root, err = root.put(Int(4), -1) + if err != nil { + t.Fatal(err) + } + root, err = root.put(Int(3), 1) + if err != nil { + t.Fatal(err) + } + root, err = root.put(Int(3), 2) + if err != nil { + t.Fatal(err) + } + root, err = root.put(Int(3), 3) + if err != nil { + t.Fatal(err) + } + root, err = root.put(Int(3), 4) + if err != nil { + t.Fatal(err) + } + root, err = root.put(Int(3), 5) + if err != nil { + t.Fatal(err) + } + t.Log(root) + t.Log(root.pointers[0]) + t.Log(root.pointers[1]) + t.Log(root.pointers[2]) + i, n := root.get_start(Int(3)) + t.Log(n) + if n != root.pointers[1] { + t.Error("wrong node from get_start") + } + if i != 0 { + t.Error("wrong index from get_start") + } + i, n = root.get_end(Int(3)) + t.Log(n) + if n != root.pointers[1].next { + t.Error("wrong node from get_end") + } + if i != 1 { + t.Error("wrong index from get_end") + } + i, n = root.get_end(Int(1)) + t.Log(n) + if n != root.pointers[0] { + t.Error("wrong node from get_end") + } + if i != 0 { + t.Error("wrong index from get_end") + } + i, n = root.get_end(Int(4)) + t.Log(n) + if n != root.pointers[2] { + t.Error("wrong node from get_end") + } + if i != 0 { + t.Error("wrong index from get_end") + } + i, n = root.get_end(Int(0)) + t.Log(n) + if n != root.pointers[0] { + t.Error("wrong node from get_end") + } + if i != 0 { + t.Error("wrong index from get_end") + } + i, n = root.get_end(Int(5)) + t.Log(n) + if n != root.pointers[2] { + t.Error("wrong node from get_end") + } + if i != 0 { + t.Error("wrong index from get_end") + } + i, n = root.get_end(Int(2)) + t.Log(n) + if n != root.pointers[1] { + t.Error("wrong node from get_end") + } + if i != 0 { + t.Error("wrong index from get_end") + } +} + +func Test_put_no_root_split(t *testing.T) { + a := NewLeaf(2, false) + if err := a.put_kv(Int(1), 1); err != nil { + t.Error(err) + } + p, err := a.put(Int(1), 2) + if err != nil { + t.Error(err) + } else { + if p != a { + t.Errorf("p != a") + } + if !p.has(Int(1)) { + t.Error("p didn't have the right keys", p) + } + } + p, err = a.put(Int(1), 3) + if err != nil { + t.Error(err) + } else { + if p != a { + t.Errorf("p != a") + } + if !p.has(Int(1)) { + t.Error("p didn't have the right keys", p) + } + if p.next == nil { + t.Error("p.next should not be nil") + } + t.Log(p) + t.Log(p.next) + } +} + +func Test_put_root_split(t *testing.T) { + a := NewLeaf(2, false) + p, err := a.put(Int(1), 1) + if err != nil { + t.Error(err) + } else { + if p != a { + t.Errorf("p != a") + } + if !p.has(Int(1)) { + t.Error("p didn't have the right keys", p) + } + } + p, err = a.put(Int(3), 3) + if err != nil { + t.Error(err) + } else { + if p != a { + t.Errorf("p != a") + } + if !p.has(Int(1)) || !p.has(Int(3)) { + t.Error("p didn't have the right keys", p) + } + } + p, err = a.put(Int(2), 2) + if err != nil { + t.Error(err) + } else { + if p == a { + t.Errorf("p == a") + } + if !p.has(Int(1)) || !p.has(Int(3)) { + t.Error("p didn't have the right keys", p) + } + if len(p.pointers) != 2 { + t.Error("p didn't have right number of pointers", p) + } + if !p.pointers[0].has(Int(1)) || !p.pointers[0].has(Int(2)) { + t.Error("p.pointers[0] didn't have the right keys", p.pointers[0]) + } + if !p.pointers[1].has(Int(3)) { + t.Error("p.pointers[1] didn't have the right keys", p.pointers[1]) + } + t.Log(p) + t.Log(p.pointers[0]) + t.Log(p.pointers[1]) + } +} + +func Test_internal_insert_no_split(t *testing.T) { + a := NewInternal(3) + leaf := NewLeaf(1, false) + if err := leaf.put_kv(Int(1), 1); err != nil { + t.Error(err) + } + if err := a.put_kp(Int(1), leaf); err != nil { + t.Error(err) + } + if err := a.put_kp(Int(5), nil); err != nil { + t.Error(err) + } + p, q, err := a.internal_insert(Int(2), nil) + if err != nil { + t.Error(err) + } else { + if p != a { + t.Errorf("p != a") + } + if q != nil { + t.Errorf("q != nil") + } + if !p.has(Int(1)) || !p.has(Int(2)) || !p.has(Int(5)) { + t.Error("p didn't have the right keys", p) + } + } +} + +func Test_internal_insert_split_less(t *testing.T) { + a := NewInternal(3) + leaf := NewLeaf(1, false) + if err := leaf.put_kv(Int(1), 1); err != nil { + t.Error(err) + } + if err := a.put_kp(Int(1), leaf); err != nil { + t.Error(err) + } + if err := a.put_kp(Int(3), nil); err != nil { + t.Error(err) + } + if err := a.put_kp(Int(5), nil); err != nil { + t.Error(err) + } + p, q, err := a.internal_insert(Int(2), nil) + if err != nil { + t.Error(err) + } else { + if p != a { + t.Errorf("p != a") + } + if q == nil { + t.Errorf("q == nil") + } + if !p.has(Int(1)) || !p.has(Int(2)) { + t.Error("p didn't have the right keys", p) + } + if !q.has(Int(3)) || !q.has(Int(5)) { + t.Error("q didn't have the right keys", q) + } + } +} + +func Test_internal_split_less(t *testing.T) { + a := NewInternal(3) + if err := a.put_kp(Int(1), nil); err != nil { + t.Error(err) + } + if err := a.put_kp(Int(3), nil); err != nil { + t.Error(err) + } + if err := a.put_kp(Int(5), nil); err != nil { + t.Error(err) + } + p, q, err := a.internal_split(Int(2), nil) + if err != nil { + t.Error(err) + } else { + if p != a { + t.Errorf("p != a") + } + if q == nil { + t.Errorf("q == nil") + } + if !p.has(Int(1)) || !p.has(Int(2)) { + t.Error("p didn't have the right keys", p) + } + if !q.has(Int(3)) || !q.has(Int(5)) { + t.Error("q didn't have the right keys", q) + } + } +} + +func Test_internal_split_equal(t *testing.T) { + a := NewInternal(3) + if err := a.put_kp(Int(1), nil); err != nil { + t.Error(err) + } + if err := a.put_kp(Int(3), nil); err != nil { + t.Error(err) + } + if err := a.put_kp(Int(5), nil); err != nil { + t.Error(err) + } + p, q, err := a.internal_split(Int(3), nil) + if err == nil { + t.Error("split succeeded should have failed", p, q) + } +} + +func Test_internal_split_greater(t *testing.T) { + a := NewInternal(3) + if err := a.put_kp(Int(1), nil); err != nil { + t.Error(err) + } + if err := a.put_kp(Int(3), nil); err != nil { + t.Error(err) + } + if err := a.put_kp(Int(5), nil); err != nil { + t.Error(err) + } + p, q, err := a.internal_split(Int(4), nil) + if err != nil { + t.Error(err) + } else { + if p != a { + t.Errorf("p != a") + } + if q == nil { + t.Errorf("q == nil") + } + if !p.has(Int(1)) { + t.Error("p didn't have the right keys", p) + } + if !q.has(Int(3)) || !q.has(Int(4)) || !q.has(Int(5)) { + t.Error("q didn't have the right keys", q) + } + } +} + +func Test_leaf_insert_no_split(t *testing.T) { + a := NewLeaf(3, false) + insert_linked_list_node(a, nil, nil) + if err := a.put_kv(Int(1), 1); err != nil { + t.Error(err) + } + if err := a.put_kv(Int(3), 3); err != nil { + t.Error(err) + } + p, q, err := a.leaf_insert(Int(2), 2) + if err != nil { + t.Error(err) + } else { + if p != a { + t.Errorf("p != a") + } + if q != nil { + t.Errorf("q != nil") + } + if !p.has(Int(1)) || !p.has(Int(2)) || !p.has(Int(3)) { + t.Error("p didn't have the right keys", p) + } + } +} + +// tests the defer to split logic +func Test_leaf_insert_split_less(t *testing.T) { + a := NewLeaf(3, false) + insert_linked_list_node(a, nil, nil) + if err := a.put_kv(Int(1), 1); err != nil { + t.Error(err) + } + if err := a.put_kv(Int(3), 3); err != nil { + t.Error(err) + } + if err := a.put_kv(Int(5), 5); err != nil { + t.Error(err) + } + p, q, err := a.leaf_insert(Int(2), 2) + if err != nil { + t.Error(err) + } else { + if p != a { + t.Errorf("p != a") + } + if q == nil { + t.Errorf("q == nil") + } + if !p.has(Int(1)) || !p.has(Int(2)) { + t.Error("p didn't have the right keys", p) + } + if !q.has(Int(3)) || !q.has(Int(5)) { + t.Error("q didn't have the right keys", q) + } + } +} + +func Test_leaf_split_less(t *testing.T) { + a := NewLeaf(3, false) + insert_linked_list_node(a, nil, nil) + if err := a.put_kv(Int(1), 1); err != nil { + t.Error(err) + } + if err := a.put_kv(Int(3), 3); err != nil { + t.Error(err) + } + if err := a.put_kv(Int(5), 5); err != nil { + t.Error(err) + } + p, q, err := a.leaf_split(Int(2), 2) + if err != nil { + t.Error(err) + } else { + if p != a { + t.Errorf("p != a") + } + if q == nil { + t.Errorf("q == nil") + } + if !p.has(Int(1)) || !p.has(Int(2)) { + t.Error("p didn't have the right keys", p) + } + if !q.has(Int(3)) || !q.has(Int(5)) { + t.Error("q didn't have the right keys", q) + } + } +} + +func Test_leaf_split_equal(t *testing.T) { + a := NewLeaf(3, false) + insert_linked_list_node(a, nil, nil) + if err := a.put_kv(Int(1), 1); err != nil { + t.Error(err) + } + if err := a.put_kv(Int(3), 3); err != nil { + t.Error(err) + } + if err := a.put_kv(Int(5), 5); err != nil { + t.Error(err) + } + p, q, err := a.leaf_split(Int(3), 2) + if err != nil { + t.Error(err) + } else { + if p != a { + t.Errorf("p != a") + } + if q == nil { + t.Errorf("q == nil") + } + if !p.has(Int(1)) { + t.Error("p didn't have the right keys", p) + } + if !q.has(Int(3)) || q.count(Int(3)) != 2 || !q.has(Int(5)) { + t.Error("q didn't have the right keys", q, q.count(Int(3))) + } + } +} + +func Test_leaf_split_greater(t *testing.T) { + a := NewLeaf(3, false) + insert_linked_list_node(a, nil, nil) + if err := a.put_kv(Int(1), 1); err != nil { + t.Error(err) + } + if err := a.put_kv(Int(3), 3); err != nil { + t.Error(err) + } + if err := a.put_kv(Int(5), 5); err != nil { + t.Error(err) + } + p, q, err := a.leaf_split(Int(4), 2) + if err != nil { + t.Error(err) + } else { + if p != a { + t.Errorf("p != a") + } + if q == nil { + t.Errorf("q == nil") + } + if !p.has(Int(1)) { + t.Error("p didn't have the right keys", p) + } + if !q.has(Int(3)) || !q.has(Int(4)) || !q.has(Int(5)) { + t.Error("q didn't have the right keys", q) + } + } +} + +// tests the defer logic +func Test_pure_leaf_insert_split_less(t *testing.T) { + a := NewLeaf(2, false) + insert_linked_list_node(a, nil, nil) + b := NewLeaf(2, false) + insert_linked_list_node(b, a, nil) + c := NewLeaf(2, false) + insert_linked_list_node(c, b, nil) + d := NewLeaf(2, false) + insert_linked_list_node(d, c, nil) + if err := a.put_kv(Int(3), 1); err != nil { + t.Error(err) + } + if err := a.put_kv(Int(3), 2); err != nil { + t.Error(err) + } + if err := b.put_kv(Int(3), 3); err != nil { + t.Error(err) + } + if err := b.put_kv(Int(3), 4); err != nil { + t.Error(err) + } + if err := c.put_kv(Int(3), 5); err != nil { + t.Error(err) + } + if err := c.put_kv(Int(3), 6); err != nil { + t.Error(err) + } + if err := d.put_kv(Int(4), 6); err != nil { + t.Error(err) + } + p, q, err := a.leaf_insert(Int(2), 1) + if err != nil { + t.Error(err) + } else { + if q != a { + t.Errorf("q != a") + } + if p == nil || len(p.keys) != 1 || !p.keys[0].Equals(Int(2)) { + t.Errorf("p did not contain the right key") + } + if p.prev != nil { + t.Errorf("expected p.prev == nil") + } + if p.next != a { + t.Errorf("expected p.next == a") + } + if a.prev != p { + t.Errorf("expected a.prev == p") + } + if a.next != b { + t.Errorf("expected a.next == b") + } + if b.prev != a { + t.Errorf("expected b.prev == a") + } + if b.next != c { + t.Errorf("expected b.next == c") + } + if c.prev != b { + t.Errorf("expected c.prev == b") + } + if c.next != d { + t.Errorf("expected c.next == d") + } + if d.prev != c { + t.Errorf("expected d.prev == c") + } + if d.next != nil { + t.Errorf("expected d.next == nil") + } + } +} + +func Test_pure_leaf_split_less(t *testing.T) { + a := NewLeaf(2, false) + insert_linked_list_node(a, nil, nil) + b := NewLeaf(2, false) + insert_linked_list_node(b, a, nil) + c := NewLeaf(2, false) + insert_linked_list_node(c, b, nil) + d := NewLeaf(2, false) + insert_linked_list_node(d, c, nil) + if err := a.put_kv(Int(3), 1); err != nil { + t.Error(err) + } + if err := a.put_kv(Int(3), 2); err != nil { + t.Error(err) + } + if err := b.put_kv(Int(3), 3); err != nil { + t.Error(err) + } + if err := b.put_kv(Int(3), 4); err != nil { + t.Error(err) + } + if err := c.put_kv(Int(3), 5); err != nil { + t.Error(err) + } + if err := c.put_kv(Int(3), 6); err != nil { + t.Error(err) + } + if err := d.put_kv(Int(4), 6); err != nil { + t.Error(err) + } + p, q, err := a.pure_leaf_split(Int(2), 1) + if err != nil { + t.Error(err) + } else { + if q != a { + t.Errorf("q != a") + } + if p == nil || len(p.keys) != 1 || !p.keys[0].Equals(Int(2)) { + t.Errorf("p did not contain the right key") + } + if p.prev != nil { + t.Errorf("expected p.prev == nil") + } + if p.next != a { + t.Errorf("expected p.next == a") + } + if a.prev != p { + t.Errorf("expected a.prev == p") + } + if a.next != b { + t.Errorf("expected a.next == b") + } + if b.prev != a { + t.Errorf("expected b.prev == a") + } + if b.next != c { + t.Errorf("expected b.next == c") + } + if c.prev != b { + t.Errorf("expected c.prev == b") + } + if c.next != d { + t.Errorf("expected c.next == d") + } + if d.prev != c { + t.Errorf("expected d.prev == c") + } + if d.next != nil { + t.Errorf("expected d.next == nil") + } + } +} + +func Test_pure_leaf_split_equal(t *testing.T) { + a := NewLeaf(2, false) + insert_linked_list_node(a, nil, nil) + b := NewLeaf(2, false) + insert_linked_list_node(b, a, nil) + c := NewLeaf(2, false) + insert_linked_list_node(c, b, nil) + d := NewLeaf(2, false) + insert_linked_list_node(d, c, nil) + if err := a.put_kv(Int(3), 1); err != nil { + t.Error(err) + } + if err := a.put_kv(Int(3), 2); err != nil { + t.Error(err) + } + if err := b.put_kv(Int(3), 3); err != nil { + t.Error(err) + } + if err := b.put_kv(Int(3), 4); err != nil { + t.Error(err) + } + if err := c.put_kv(Int(3), 5); err != nil { + t.Error(err) + } + if err := d.put_kv(Int(4), 6); err != nil { + t.Error(err) + } + p, q, err := a.pure_leaf_split(Int(3), 1) + if err != nil { + t.Error(err) + } else { + if p != a { + t.Errorf("p != a") + } + if q != nil { + t.Errorf("q != nil") + } + if a.prev != nil { + t.Errorf("expected a.prev == nil") + } + if a.next != b { + t.Errorf("expected a.next == b") + } + if b.prev != a { + t.Errorf("expected b.prev == a") + } + if b.next != c { + t.Errorf("expected b.next == c") + } + if c.prev != b { + t.Errorf("expected c.prev == b") + } + if c.next != d { + t.Errorf("expected c.next == d") + } + if d.prev != c { + t.Errorf("expected d.prev == c") + } + if d.next != nil { + t.Errorf("expected d.next == nil") + } + } +} + +func Test_pure_leaf_split_greater(t *testing.T) { + a := NewLeaf(2, false) + insert_linked_list_node(a, nil, nil) + b := NewLeaf(2, false) + insert_linked_list_node(b, a, nil) + c := NewLeaf(2, false) + insert_linked_list_node(c, b, nil) + d := NewLeaf(2, false) + insert_linked_list_node(d, c, nil) + if err := a.put_kv(Int(3), 1); err != nil { + t.Error(err) + } + if err := a.put_kv(Int(3), 2); err != nil { + t.Error(err) + } + if err := b.put_kv(Int(3), 3); err != nil { + t.Error(err) + } + if err := b.put_kv(Int(3), 4); err != nil { + t.Error(err) + } + if err := c.put_kv(Int(3), 5); err != nil { + t.Error(err) + } + if err := d.put_kv(Int(5), 6); err != nil { + t.Error(err) + } + p, q, err := a.pure_leaf_split(Int(4), 1) + if err != nil { + t.Error(err) + } else { + if p != a { + t.Errorf("p != a") + } + if q == nil || len(q.keys) != 1 || !q.keys[0].Equals(Int(4)) { + t.Errorf("q != nil") + } + if a.prev != nil { + t.Errorf("expected a.prev == nil") + } + if a.next != b { + t.Errorf("expected a.next == b") + } + if b.prev != a { + t.Errorf("expected b.prev == a") + } + if b.next != c { + t.Errorf("expected b.next == c") + } + if c.prev != b { + t.Errorf("expected c.prev == b") + } + if c.next != q { + t.Errorf("expected c.next == q") + } + if q.prev != c { + t.Errorf("expected q.prev == c") + } + if q.next != d { + t.Errorf("expected q.next == d") + } + if d.prev != q { + t.Errorf("expected d.prev == q") + } + if d.next != nil { + t.Errorf("expected d.next == nil") + } + } +} + +func Test_find_end_of_pure_run(t *testing.T) { + a := NewLeaf(2, false) + insert_linked_list_node(a, nil, nil) + b := NewLeaf(2, false) + insert_linked_list_node(b, a, nil) + c := NewLeaf(2, false) + insert_linked_list_node(c, b, nil) + d := NewLeaf(2, false) + insert_linked_list_node(d, c, nil) + if err := a.put_kv(Int(3), 1); err != nil { + t.Error(err) + } + if err := a.put_kv(Int(3), 2); err != nil { + t.Error(err) + } + if err := b.put_kv(Int(3), 3); err != nil { + t.Error(err) + } + if err := b.put_kv(Int(3), 4); err != nil { + t.Error(err) + } + if err := c.put_kv(Int(3), 5); err != nil { + t.Error(err) + } + if err := c.put_kv(Int(3), 6); err != nil { + t.Error(err) + } + if err := d.put_kv(Int(4), 6); err != nil { + t.Error(err) + } + e := a.find_end_of_pure_run() + if e != c { + t.Errorf("end of run should have been block c %v %v", e, c) + } +} + +func Test_insert_linked_list_node(t *testing.T) { + a := NewLeaf(1, false) + insert_linked_list_node(a, nil, nil) + b := NewLeaf(2, false) + insert_linked_list_node(b, a, nil) + c := NewLeaf(3, false) + insert_linked_list_node(c, b, nil) + d := NewLeaf(4, false) + insert_linked_list_node(d, a, b) + if a.prev != nil { + t.Errorf("expected a.prev == nil") + } + if a.next != d { + t.Errorf("expected a.next == d") + } + if d.prev != a { + t.Errorf("expected d.prev == a") + } + if d.next != b { + t.Errorf("expected d.next == b") + } + if b.prev != d { + t.Errorf("expected b.prev == d") + } + if b.next != c { + t.Errorf("expected b.next == c") + } + if c.prev != b { + t.Errorf("expected c.prev == b") + } + if c.next != nil { + t.Errorf("expected c.next == nil") + } +} + +func Test_remove_linked_list_node(t *testing.T) { + a := NewLeaf(1, false) + insert_linked_list_node(a, nil, nil) + b := NewLeaf(2, false) + insert_linked_list_node(b, a, nil) + c := NewLeaf(3, false) + insert_linked_list_node(c, b, nil) + d := NewLeaf(4, false) + insert_linked_list_node(d, a, b) + if a.prev != nil { + t.Errorf("expected a.prev == nil") + } + if a.next != d { + t.Errorf("expected a.next == d") + } + if d.prev != a { + t.Errorf("expected d.prev == a") + } + if d.next != b { + t.Errorf("expected d.next == b") + } + if b.prev != d { + t.Errorf("expected b.prev == d") + } + if b.next != c { + t.Errorf("expected b.next == c") + } + if c.prev != b { + t.Errorf("expected c.prev == b") + } + if c.next != nil { + t.Errorf("expected c.next == nil") + } + remove_linked_list_node(d) + if a.prev != nil { + t.Errorf("expected a.prev == nil") + } + if a.next != b { + t.Errorf("expected a.next == b") + } + if b.prev != a { + t.Errorf("expected b.prev == a") + } + if b.next != c { + t.Errorf("expected b.next == c") + } + if c.prev != b { + t.Errorf("expected c.prev == b") + } + if c.next != nil { + t.Errorf("expected c.next == nil") + } + remove_linked_list_node(a) + if b.prev != nil { + t.Errorf("expected b.prev == nil") + } + if b.next != c { + t.Errorf("expected b.next == c") + } + if c.prev != b { + t.Errorf("expected c.prev == b") + } + if c.next != nil { + t.Errorf("expected c.next == nil") + } + remove_linked_list_node(c) + if b.prev != nil { + t.Errorf("expected b.prev == nil") + } + if b.next != nil { + t.Errorf("expected b.next == nil") + } + remove_linked_list_node(b) +} + +func Test_balance_leaf_nodes_with_dup(t *testing.T) { + a := NewLeaf(3, false) + b := NewLeaf(3, false) + if err := a.put_kv(Int(1), 1); err != nil { + t.Error(err) + } + if err := a.put_kv(Int(1), 1); err != nil { + t.Error(err) + } + if err := a.put_kv(Int(2), 1); err != nil { + t.Error(err) + } + balance_nodes(a, b) + if !a.has(Int(1)) || a.count(Int(1)) != 2 || a.has(Int(2)) { + t.Error("a had wrong items", a) + } + if !b.has(Int(2)) || b.count(Int(2)) != 1 || b.has(Int(1)) { + t.Error("a had wrong items", b) + } +} + +func Test_balance_leaf_nodes(t *testing.T) { + a := NewLeaf(7, false) + b := NewLeaf(7, false) + if err := a.put_kv(Int(1), 1); err != nil { + t.Error(err) + } + if err := a.put_kv(Int(2), 2); err != nil { + t.Error(err) + } + if err := a.put_kv(Int(3), 3); err != nil { + t.Error(err) + } + if err := a.put_kv(Int(4), 4); err != nil { + t.Error(err) + } + if err := a.put_kv(Int(5), 5); err != nil { + t.Error(err) + } + if err := a.put_kv(Int(6), 6); err != nil { + t.Error(err) + } + if err := a.put_kv(Int(7), 7); err != nil { + t.Error(err) + } + balance_nodes(a, b) + for i, k := range a.keys { + if int(k.(Int)) != i+1 { + t.Errorf("k != %d", i+1) + } + } + for i, k := range b.keys { + if int(k.(Int)) != 3+i+1 { + t.Errorf("k != %d", 3+i+1) + } + } + for i, v := range a.values { + if v.(int) != i+1 { + t.Errorf("k != %d", i+1) + } + } + for i, v := range b.values { + if v.(int) != 3+i+1 { + t.Errorf("v != %d", 3+i+1) + } + } + t.Log(a) + t.Log(b) +} + +func Test_balance_internal_nodes(t *testing.T) { + a := NewInternal(6) + b := NewInternal(6) + if err := a.put_kp(Int(1), nil); err != nil { + t.Error(err) + } + if err := a.put_kp(Int(2), nil); err != nil { + t.Error(err) + } + if err := a.put_kp(Int(3), nil); err != nil { + t.Error(err) + } + if err := a.put_kp(Int(4), nil); err != nil { + t.Error(err) + } + if err := a.put_kp(Int(5), nil); err != nil { + t.Error(err) + } + if err := a.put_kp(Int(6), nil); err != nil { + t.Error(err) + } + balance_nodes(a, b) + for i, k := range a.keys { + if int(k.(Int)) != i+1 { + t.Errorf("k != %d", i+1) + } + } + for i, k := range b.keys { + if int(k.(Int)) != 3+i+1 { + t.Errorf("k != %d", 3+i+1) + } + } + t.Log(a) + t.Log(b) +} + + +// copied from + +// ThreadSafeRand provides a thread safe version of math/rand.Rand using +// the same technique used in the math/rand package to make the top level +// functions thread safe. +func ThreadSafeRand(seed int64) *mrand.Rand { + return mrand.New(&lockedSource{src: mrand.NewSource(seed).(mrand.Source64)}) +} + +// from: https://golang.org/src/math/rand/rand.go?s=8161:8175#L317 +type lockedSource struct { + lk sync.Mutex + src mrand.Source64 +} + +func (r *lockedSource) Int63() (n int64) { + r.lk.Lock() + n = r.src.Int63() + r.lk.Unlock() + return +} + +func (r *lockedSource) Uint64() (n uint64) { + r.lk.Lock() + n = r.src.Uint64() + r.lk.Unlock() + return +} + +func (r *lockedSource) Seed(seed int64) { + r.lk.Lock() + r.src.Seed(seed) + r.lk.Unlock() +} + +// seedPos implements Seed for a lockedSource without a race condiiton. +func (r *lockedSource) seedPos(seed int64, readPos *int8) { + r.lk.Lock() + r.src.Seed(seed) + *readPos = 0 + r.lk.Unlock() +} + +// read implements Read for a lockedSource without a race condition. +func (r *lockedSource) read(p []byte, readVal *int64, readPos *int8) (n int, err error) { + r.lk.Lock() + n, err = read(p, r.src.Int63, readVal, readPos) + r.lk.Unlock() + return +} + +func read(p []byte, int63 func() int64, readVal *int64, readPos *int8) (n int, err error) { + pos := *readPos + val := *readVal + for n = 0; n < len(p); n++ { + if pos == 0 { + val = int63() + pos = 7 + } + p[n] = byte(val) + val >>= 8 + pos-- + } + *readPos = pos + *readVal = val + return +} + +// copied from https://sourcegraph.com/github.com/timtadh/data-structures@master/-/blob/test/support.go + +type T testing.T + +func (t *T) Assert(ok bool, msg string, vars ...interface{}) { + if !ok { + t.Log("\n" + string(debug.Stack())) + t.Fatalf(msg, vars...) + } +} + +func (t *T) AssertNil(errors ...error) { + any := false + for _, err := range errors { + if err != nil { + any = true + t.Log("\n" + string(debug.Stack())) + t.Error(err) + } + } + if any { + t.Fatal("assert failed") + } +} + +func RandSlice(length int) []byte { + slice := make([]byte, length) + if _, err := crand.Read(slice); err != nil { + panic(err) + } + return slice +} + +func RandHex(length int) string { + return hex.EncodeToString(RandSlice(length / 2)) +} + +func RandStr(length int) string { + return string(RandSlice(length)) +} \ No newline at end of file diff --git a/weed/util/bptree/int.go b/weed/util/bptree/int.go new file mode 100644 index 000000000..e8fd9511c --- /dev/null +++ b/weed/util/bptree/int.go @@ -0,0 +1,357 @@ +package bptree + +import ( + "encoding/binary" + "fmt" +) + +type Int8 int8 +type UInt8 uint8 +type Int16 int16 +type UInt16 uint16 +type Int32 int32 +type UInt32 uint32 +type Int64 int64 +type UInt64 uint64 +type Int int +type UInt uint + +func (self *Int8) MarshalBinary() ([]byte, error) { + bytes := make([]byte, 0) + bytes[0] = uint8(*self) + return bytes, nil +} + +func (self *Int8) UnmarshalBinary(data []byte) error { + if len(data) != 1 { + return fmt.Errorf("data wrong size") + } + *self = Int8(data[0]) + return nil +} + +func (self Int8) Equals(other Equatable) bool { + if o, ok := other.(Int8); ok { + return self == o + } else { + return false + } +} + +func (self Int8) Less(other Sortable) bool { + if o, ok := other.(Int8); ok { + return self < o + } else { + return false + } +} + +func (self Int8) Hash() int { + return int(self) +} + +func (self *UInt8) MarshalBinary() ([]byte, error) { + bytes := make([]byte, 0) + bytes[0] = uint8(*self) + return bytes, nil +} + +func (self *UInt8) UnmarshalBinary(data []byte) error { + if len(data) != 1 { + return fmt.Errorf("data wrong size") + } + *self = UInt8(data[0]) + return nil +} + +func (self UInt8) Equals(other Equatable) bool { + if o, ok := other.(UInt8); ok { + return self == o + } else { + return false + } +} + +func (self UInt8) Less(other Sortable) bool { + if o, ok := other.(UInt8); ok { + return self < o + } else { + return false + } +} + +func (self UInt8) Hash() int { + return int(self) +} + +func (self *Int16) MarshalBinary() ([]byte, error) { + bytes := make([]byte, 2) + binary.BigEndian.PutUint16(bytes, uint16(*self)) + return bytes, nil +} + +func (self *Int16) UnmarshalBinary(data []byte) error { + if len(data) != 2 { + return fmt.Errorf("data wrong size") + } + *self = Int16(binary.BigEndian.Uint16(data)) + return nil +} + +func (self Int16) Equals(other Equatable) bool { + if o, ok := other.(Int16); ok { + return self == o + } else { + return false + } +} + +func (self Int16) Less(other Sortable) bool { + if o, ok := other.(Int16); ok { + return self < o + } else { + return false + } +} + +func (self Int16) Hash() int { + return int(self) +} + +func (self *UInt16) MarshalBinary() ([]byte, error) { + bytes := make([]byte, 2) + binary.BigEndian.PutUint16(bytes, uint16(*self)) + return bytes, nil +} + +func (self *UInt16) UnmarshalBinary(data []byte) error { + if len(data) != 2 { + return fmt.Errorf("data wrong size") + } + *self = UInt16(binary.BigEndian.Uint16(data)) + return nil +} + +func (self UInt16) Equals(other Equatable) bool { + if o, ok := other.(UInt16); ok { + return self == o + } else { + return false + } +} + +func (self UInt16) Less(other Sortable) bool { + if o, ok := other.(UInt16); ok { + return self < o + } else { + return false + } +} + +func (self UInt16) Hash() int { + return int(self) +} + +func (self *Int32) MarshalBinary() ([]byte, error) { + bytes := make([]byte, 4) + binary.BigEndian.PutUint32(bytes, uint32(*self)) + return bytes, nil +} + +func (self *Int32) UnmarshalBinary(data []byte) error { + if len(data) != 4 { + return fmt.Errorf("data wrong size") + } + *self = Int32(binary.BigEndian.Uint32(data)) + return nil +} + +func (self Int32) Equals(other Equatable) bool { + if o, ok := other.(Int32); ok { + return self == o + } else { + return false + } +} + +func (self Int32) Less(other Sortable) bool { + if o, ok := other.(Int32); ok { + return self < o + } else { + return false + } +} + +func (self *UInt32) MarshalBinary() ([]byte, error) { + bytes := make([]byte, 4) + binary.BigEndian.PutUint32(bytes, uint32(*self)) + return bytes, nil +} + +func (self *UInt32) UnmarshalBinary(data []byte) error { + if len(data) != 4 { + return fmt.Errorf("data wrong size") + } + *self = UInt32(binary.BigEndian.Uint32(data)) + return nil +} + +func (self Int32) Hash() int { + return int(self) +} + +func (self UInt32) Equals(other Equatable) bool { + if o, ok := other.(UInt32); ok { + return self == o + } else { + return false + } +} + +func (self UInt32) Less(other Sortable) bool { + if o, ok := other.(UInt32); ok { + return self < o + } else { + return false + } +} + +func (self UInt32) Hash() int { + return int(self) +} + +func (self *Int64) MarshalBinary() ([]byte, error) { + bytes := make([]byte, 8) + binary.BigEndian.PutUint64(bytes, uint64(*self)) + return bytes, nil +} + +func (self *Int64) UnmarshalBinary(data []byte) error { + if len(data) != 8 { + return fmt.Errorf("data wrong size") + } + *self = Int64(binary.BigEndian.Uint64(data)) + return nil +} + +func (self Int64) Equals(other Equatable) bool { + if o, ok := other.(Int64); ok { + return self == o + } else { + return false + } +} + +func (self Int64) Less(other Sortable) bool { + if o, ok := other.(Int64); ok { + return self < o + } else { + return false + } +} + +func (self Int64) Hash() int { + return int(self>>32) ^ int(self) +} + +func (self *UInt64) MarshalBinary() ([]byte, error) { + bytes := make([]byte, 8) + binary.BigEndian.PutUint64(bytes, uint64(*self)) + return bytes, nil +} + +func (self *UInt64) UnmarshalBinary(data []byte) error { + if len(data) != 8 { + return fmt.Errorf("data wrong size") + } + *self = UInt64(binary.BigEndian.Uint64(data)) + return nil +} + +func (self UInt64) Equals(other Equatable) bool { + if o, ok := other.(UInt64); ok { + return self == o + } else { + return false + } +} + +func (self UInt64) Less(other Sortable) bool { + if o, ok := other.(UInt64); ok { + return self < o + } else { + return false + } +} + +func (self UInt64) Hash() int { + return int(self>>32) ^ int(self) +} + +func (self *Int) MarshalBinary() ([]byte, error) { + bytes := make([]byte, 4) + binary.BigEndian.PutUint32(bytes, uint32(*self)) + return bytes, nil +} + +func (self *Int) UnmarshalBinary(data []byte) error { + if len(data) != 4 { + return fmt.Errorf("data wrong size") + } + *self = Int(binary.BigEndian.Uint32(data)) + return nil +} + +func (self Int) Equals(other Equatable) bool { + if o, ok := other.(Int); ok { + return self == o + } else { + return false + } +} + +func (self Int) Less(other Sortable) bool { + if o, ok := other.(Int); ok { + return self < o + } else { + return false + } +} + +func (self Int) Hash() int { + return int(self) +} + +func (self *UInt) MarshalBinary() ([]byte, error) { + bytes := make([]byte, 4) + binary.BigEndian.PutUint32(bytes, uint32(*self)) + return bytes, nil +} + +func (self *UInt) UnmarshalBinary(data []byte) error { + if len(data) != 4 { + return fmt.Errorf("data wrong size") + } + *self = UInt(binary.BigEndian.Uint32(data)) + return nil +} + +func (self UInt) Equals(other Equatable) bool { + if o, ok := other.(UInt); ok { + return self == o + } else { + return false + } +} + +func (self UInt) Less(other Sortable) bool { + if o, ok := other.(UInt); ok { + return self < o + } else { + return false + } +} + +func (self UInt) Hash() int { + return int(self) +} diff --git a/weed/util/bptree/rand.go b/weed/util/bptree/rand.go new file mode 100644 index 000000000..08b2e50ab --- /dev/null +++ b/weed/util/bptree/rand.go @@ -0,0 +1,2 @@ +package bptree + diff --git a/weed/util/bptree/string.go b/weed/util/bptree/string.go new file mode 100644 index 000000000..262220878 --- /dev/null +++ b/weed/util/bptree/string.go @@ -0,0 +1,71 @@ +package bptree + +import ( + "bytes" + "hash/fnv" +) + +type String string +type ByteSlice []byte + +func (self *String) MarshalBinary() ([]byte, error) { + return []byte(*self), nil +} + +func (self *String) UnmarshalBinary(data []byte) error { + *self = String(data) + return nil +} + +func (self String) Equals(other Equatable) bool { + if o, ok := other.(String); ok { + return self == o + } else { + return false + } +} + +func (self String) Less(other Sortable) bool { + if o, ok := other.(String); ok { + return self < o + } else { + return false + } +} + +func (self String) Hash() int { + h := fnv.New32a() + h.Write([]byte(string(self))) + return int(h.Sum32()) +} + +func (self *ByteSlice) MarshalBinary() ([]byte, error) { + return []byte(*self), nil +} + +func (self *ByteSlice) UnmarshalBinary(data []byte) error { + *self = ByteSlice(data) + return nil +} + +func (self ByteSlice) Equals(other Equatable) bool { + if o, ok := other.(ByteSlice); ok { + return bytes.Equal(self, o) + } else { + return false + } +} + +func (self ByteSlice) Less(other Sortable) bool { + if o, ok := other.(ByteSlice); ok { + return bytes.Compare(self, o) < 0 // -1 if a < b + } else { + return false + } +} + +func (self ByteSlice) Hash() int { + h := fnv.New32a() + h.Write([]byte(self)) + return int(h.Sum32()) +} diff --git a/weed/util/bptree/types.go b/weed/util/bptree/types.go new file mode 100644 index 000000000..6a1d83098 --- /dev/null +++ b/weed/util/bptree/types.go @@ -0,0 +1,103 @@ +package bptree + +import ( + "errors" + "fmt" +) + +type Equatable interface { + Equals(b Equatable) bool +} + +type Sortable interface { + Equatable + Less(b Sortable) bool +} + +type Hashable interface { + Sortable + Hash() int +} + +var BpTreeError = fmt.Errorf + +func NegativeSize() error { + return errors.New("negative size") +} + +type Iterator func() (item interface{}, next Iterator) +type KIterator func() (key Hashable, next KIterator) +type KVIterator func() (key Hashable, value interface{}, next KVIterator) +type KVIterable interface { + Iterate() KVIterator +} + +type Sized interface { + Size() int +} + +type MapOperable interface { + Sized + Has(key Hashable) bool + Put(key Hashable, value interface{}) (err error) + Get(key Hashable) (value interface{}, err error) + Remove(key Hashable) (value interface{}, err error) +} + +type WhereFunc func(value interface{}) bool + +func MakeValuesIterator(obj KVIterable) Iterator { + kv_iterator := obj.Iterate() + var v_iterator Iterator + v_iterator = func() (value interface{}, next Iterator) { + _, value, kv_iterator = kv_iterator() + if kv_iterator == nil { + return nil, nil + } + return value, v_iterator + } + return v_iterator +} + +func MakeItemsIterator(obj KVIterable) (kit KIterator) { + kv_iterator := obj.Iterate() + kit = func() (item Hashable, next KIterator) { + var key Hashable + var value interface{} + key, value, kv_iterator = kv_iterator() + if kv_iterator == nil { + return nil, nil + } + return &MapEntry{key, value}, kit + } + return kit +} + +type MapEntry struct { + Key Hashable + Value interface{} +} + +func (m *MapEntry) Equals(other Equatable) bool { + if o, ok := other.(*MapEntry); ok { + return m.Key.Equals(o.Key) + } else { + return m.Key.Equals(other) + } +} + +func (m *MapEntry) Less(other Sortable) bool { + if o, ok := other.(*MapEntry); ok { + return m.Key.Less(o.Key) + } else { + return m.Key.Less(other) + } +} + +func (m *MapEntry) Hash() int { + return m.Key.Hash() +} + +func (m *MapEntry) String() string { + return fmt.Sprintf("", m.Key, m.Value) +} From 2d237da74a6e89ce932f88310d9fb6bcb2f2d586 Mon Sep 17 00:00:00 2001 From: Chris Lu Date: Fri, 20 Aug 2021 01:19:11 -0700 Subject: [PATCH 002/130] remove size since each put/get will have to update the root node --- weed/util/bptree/bpmap.go | 10 ---------- weed/util/bptree/bptree.go | 8 -------- weed/util/bptree/bptree_test.go | 9 --------- weed/util/bptree/types.go | 5 ----- 4 files changed, 32 deletions(-) diff --git a/weed/util/bptree/bpmap.go b/weed/util/bptree/bpmap.go index 37b2b25bb..43c07d71f 100644 --- a/weed/util/bptree/bpmap.go +++ b/weed/util/bptree/bpmap.go @@ -12,28 +12,19 @@ type BpMap BpTree func NewBpMap(node_size int) *BpMap { return &BpMap{ root: NewLeaf(node_size, true), - size: 0, } } -func (self *BpMap) Size() int { - return (*BpTree)(self).Size() -} - func (self *BpMap) Has(key Hashable) bool { return (*BpTree)(self).Has(key) } func (self *BpMap) Put(key Hashable, value interface{}) (err error) { - had := self.Has(key) new_root, err := self.root.put(key, value) if err != nil { return err } self.root = new_root - if !had { - self.size += 1 - } return nil } @@ -60,7 +51,6 @@ func (self *BpMap) Remove(key Hashable) (value interface{}, err error) { } else { self.root = new_root } - self.size-- return value, nil } diff --git a/weed/util/bptree/bptree.go b/weed/util/bptree/bptree.go index 4b68adb20..95f53ab29 100644 --- a/weed/util/bptree/bptree.go +++ b/weed/util/bptree/bptree.go @@ -8,7 +8,6 @@ package bptree */ type BpTree struct { root *BpNode - size int } type loc_iterator func() (i int, leaf *BpNode, li loc_iterator) @@ -16,14 +15,9 @@ type loc_iterator func() (i int, leaf *BpNode, li loc_iterator) func NewBpTree(node_size int) *BpTree { return &BpTree{ root: NewLeaf(node_size, false), - size: 0, } } -func (self *BpTree) Size() int { - return self.size -} - func (self *BpTree) Has(key Hashable) bool { if len(self.root.keys) == 0 { return false @@ -52,7 +46,6 @@ func (self *BpTree) Add(key Hashable, value interface{}) (err error) { return err } self.root = new_root - self.size += 1 return nil } @@ -100,7 +93,6 @@ func (self *BpTree) RemoveWhere(key Hashable, where WhereFunc) (err error) { } else { self.root = new_root } - self.size -= 1 return nil } diff --git a/weed/util/bptree/bptree_test.go b/weed/util/bptree/bptree_test.go index cf978ede7..38663c543 100644 --- a/weed/util/bptree/bptree_test.go +++ b/weed/util/bptree/bptree_test.go @@ -110,9 +110,6 @@ func TestAddHasCountFindIterateRemove(t *testing.T) { if err != nil { t.Error(err) } - if bpt.Size() != (i + 1) { - t.Error("size was wrong", bpt.Size(), i+1) - } } for i, r := range recs { @@ -236,9 +233,6 @@ func TestBpMap(t *testing.T) { if err != nil { t.Error(err) } - if table.Size() != (i + 1) { - t.Error("size was wrong", table.Size(), i+1) - } } for _, r := range recs { @@ -274,9 +268,6 @@ func TestBpMap(t *testing.T) { t.Error("wrong value") } } - if table.Size() != (len(recs) - (i + 1)) { - t.Error("size was wrong", table.Size(), (len(recs) - (i + 1))) - } } } diff --git a/weed/util/bptree/types.go b/weed/util/bptree/types.go index 6a1d83098..45084efdd 100644 --- a/weed/util/bptree/types.go +++ b/weed/util/bptree/types.go @@ -32,12 +32,7 @@ type KVIterable interface { Iterate() KVIterator } -type Sized interface { - Size() int -} - type MapOperable interface { - Sized Has(key Hashable) bool Put(key Hashable, value interface{}) (err error) Get(key Hashable) (value interface{}, err error) From 88d68cad872e6f0d10fadaa14949937b365d8545 Mon Sep 17 00:00:00 2001 From: Chris Lu Date: Fri, 20 Aug 2021 04:14:52 -0700 Subject: [PATCH 003/130] remove dedup --- weed/util/bptree/bpmap.go | 4 +- weed/util/bptree/bptree.go | 18 +------ weed/util/bptree/bptree_node.go | 17 ++---- weed/util/bptree/bptree_test.go | 95 +++++++++++++++------------------ 4 files changed, 51 insertions(+), 83 deletions(-) diff --git a/weed/util/bptree/bpmap.go b/weed/util/bptree/bpmap.go index 43c07d71f..1dff8643a 100644 --- a/weed/util/bptree/bpmap.go +++ b/weed/util/bptree/bpmap.go @@ -11,7 +11,7 @@ type BpMap BpTree func NewBpMap(node_size int) *BpMap { return &BpMap{ - root: NewLeaf(node_size, true), + root: NewLeaf(node_size), } } @@ -47,7 +47,7 @@ func (self *BpMap) Remove(key Hashable) (value interface{}, err error) { return nil, err } if new_root == nil { - self.root = NewLeaf(ns, true) + self.root = NewLeaf(ns) } else { self.root = new_root } diff --git a/weed/util/bptree/bptree.go b/weed/util/bptree/bptree.go index 95f53ab29..eff4f3238 100644 --- a/weed/util/bptree/bptree.go +++ b/weed/util/bptree/bptree.go @@ -14,7 +14,7 @@ type loc_iterator func() (i int, leaf *BpNode, li loc_iterator) func NewBpTree(node_size int) *BpTree { return &BpTree{ - root: NewLeaf(node_size, false), + root: NewLeaf(node_size), } } @@ -26,20 +26,6 @@ func (self *BpTree) Has(key Hashable) bool { return l.keys[j].Equals(key) } -func (self *BpTree) Count(key Hashable) int { - if len(self.root.keys) == 0 { - return 0 - } - j, l := self.root.get_start(key) - count := 0 - end := false - for !end && l.keys[j].Equals(key) { - count++ - j, l, end = next_location(j, l) - } - return count -} - func (self *BpTree) Add(key Hashable, value interface{}) (err error) { new_root, err := self.root.put(key, value) if err != nil { @@ -89,7 +75,7 @@ func (self *BpTree) RemoveWhere(key Hashable, where WhereFunc) (err error) { return err } if new_root == nil { - self.root = NewLeaf(ns, false) + self.root = NewLeaf(ns) } else { self.root = new_root } diff --git a/weed/util/bptree/bptree_node.go b/weed/util/bptree/bptree_node.go index 3574371f5..bd74e6327 100644 --- a/weed/util/bptree/bptree_node.go +++ b/weed/util/bptree/bptree_node.go @@ -6,7 +6,6 @@ type BpNode struct { pointers []*BpNode next *BpNode prev *BpNode - no_dup bool } func NewInternal(size int) *BpNode { @@ -19,14 +18,13 @@ func NewInternal(size int) *BpNode { } } -func NewLeaf(size int, no_dup bool) *BpNode { +func NewLeaf(size int) *BpNode { if size < 0 { panic(NegativeSize()) } return &BpNode{ keys: make([]Hashable, 0, size), values: make([]interface{}, 0, size), - no_dup: no_dup, } } @@ -276,13 +274,6 @@ func (self *BpNode) leaf_insert(key Hashable, value interface{}) (a, b *BpNode, if self.Internal() { return nil, nil, BpTreeError("Expected a leaf node") } - if self.no_dup { - i, has := self.find(key) - if has { - self.values[i] = value - return self, nil, nil - } - } if self.Full() { return self.leaf_split(key, value) } else { @@ -307,7 +298,7 @@ func (self *BpNode) leaf_split(key Hashable, value interface{}) (a, b *BpNode, e return self.pure_leaf_split(key, value) } a = self - b = NewLeaf(self.NodeSize(), self.no_dup) + b = NewLeaf(self.NodeSize()) insert_linked_list_node(b, a, a.next) balance_nodes(a, b) if key.Less(b.keys[0]) { @@ -339,7 +330,7 @@ func (self *BpNode) pure_leaf_split(key Hashable, value interface{}) (a, b *BpNo return nil, nil, BpTreeError("Expected a pure leaf node") } if key.Less(self.keys[0]) { - a = NewLeaf(self.NodeSize(), self.no_dup) + a = NewLeaf(self.NodeSize()) b = self if err := a.put_kv(key, value); err != nil { return nil, nil, err @@ -355,7 +346,7 @@ func (self *BpNode) pure_leaf_split(key Hashable, value interface{}) (a, b *BpNo } return a, nil, nil } else { - b = NewLeaf(self.NodeSize(), self.no_dup) + b = NewLeaf(self.NodeSize()) if err := b.put_kv(key, value); err != nil { return nil, nil, err } diff --git a/weed/util/bptree/bptree_test.go b/weed/util/bptree/bptree_test.go index 38663c543..ef73e862d 100644 --- a/weed/util/bptree/bptree_test.go +++ b/weed/util/bptree/bptree_test.go @@ -119,12 +119,6 @@ func TestAddHasCountFindIterateRemove(t *testing.T) { if has := bpt.Has(randstr(10)); has { t.Error("Table has extra key") } - if count := bpt.Count(r.key); count != 1 { - t.Error(bpt, "Missing key") - } - if count := bpt.Count(randstr(10)); count != 0 { - t.Error("Table has extra key") - } for k, v, next := bpt.Find(r.key)(); next != nil; k, v, next = next() { if !k.Equals(r.key) { t.Error(bpt, "Find Failed Key Error") @@ -190,9 +184,6 @@ func TestAddHasCountFindIterateRemove(t *testing.T) { if has := bpt.Has(r.key); !has { t.Error(bpt, "Missing key") } - if count := bpt.Count(r.key); count != 1 { - t.Error(bpt, "Missing key") - } if err := bpt.RemoveWhere(r.key, func(value interface{}) bool { return true }); err != nil { t.Fatal(bpt, err) } @@ -275,7 +266,7 @@ func TestBpMap(t *testing.T) { } func Test_get_start(t *testing.T) { - root := NewLeaf(2, false) + root := NewLeaf(2) root, err := root.put(Int(1), 1) if err != nil { t.Error(err) @@ -344,7 +335,7 @@ func Test_get_start(t *testing.T) { } func Test_get_end(t *testing.T) { - root := NewLeaf(3, false) + root := NewLeaf(3) root, err := root.put(Int(1), -1) if err != nil { t.Fatal(err) @@ -436,7 +427,7 @@ func Test_get_end(t *testing.T) { } func Test_put_no_root_split(t *testing.T) { - a := NewLeaf(2, false) + a := NewLeaf(2) if err := a.put_kv(Int(1), 1); err != nil { t.Error(err) } @@ -470,7 +461,7 @@ func Test_put_no_root_split(t *testing.T) { } func Test_put_root_split(t *testing.T) { - a := NewLeaf(2, false) + a := NewLeaf(2) p, err := a.put(Int(1), 1) if err != nil { t.Error(err) @@ -520,7 +511,7 @@ func Test_put_root_split(t *testing.T) { func Test_internal_insert_no_split(t *testing.T) { a := NewInternal(3) - leaf := NewLeaf(1, false) + leaf := NewLeaf(1) if err := leaf.put_kv(Int(1), 1); err != nil { t.Error(err) } @@ -548,7 +539,7 @@ func Test_internal_insert_no_split(t *testing.T) { func Test_internal_insert_split_less(t *testing.T) { a := NewInternal(3) - leaf := NewLeaf(1, false) + leaf := NewLeaf(1) if err := leaf.put_kv(Int(1), 1); err != nil { t.Error(err) } @@ -658,7 +649,7 @@ func Test_internal_split_greater(t *testing.T) { } func Test_leaf_insert_no_split(t *testing.T) { - a := NewLeaf(3, false) + a := NewLeaf(3) insert_linked_list_node(a, nil, nil) if err := a.put_kv(Int(1), 1); err != nil { t.Error(err) @@ -684,7 +675,7 @@ func Test_leaf_insert_no_split(t *testing.T) { // tests the defer to split logic func Test_leaf_insert_split_less(t *testing.T) { - a := NewLeaf(3, false) + a := NewLeaf(3) insert_linked_list_node(a, nil, nil) if err := a.put_kv(Int(1), 1); err != nil { t.Error(err) @@ -715,7 +706,7 @@ func Test_leaf_insert_split_less(t *testing.T) { } func Test_leaf_split_less(t *testing.T) { - a := NewLeaf(3, false) + a := NewLeaf(3) insert_linked_list_node(a, nil, nil) if err := a.put_kv(Int(1), 1); err != nil { t.Error(err) @@ -746,7 +737,7 @@ func Test_leaf_split_less(t *testing.T) { } func Test_leaf_split_equal(t *testing.T) { - a := NewLeaf(3, false) + a := NewLeaf(3) insert_linked_list_node(a, nil, nil) if err := a.put_kv(Int(1), 1); err != nil { t.Error(err) @@ -777,7 +768,7 @@ func Test_leaf_split_equal(t *testing.T) { } func Test_leaf_split_greater(t *testing.T) { - a := NewLeaf(3, false) + a := NewLeaf(3) insert_linked_list_node(a, nil, nil) if err := a.put_kv(Int(1), 1); err != nil { t.Error(err) @@ -809,13 +800,13 @@ func Test_leaf_split_greater(t *testing.T) { // tests the defer logic func Test_pure_leaf_insert_split_less(t *testing.T) { - a := NewLeaf(2, false) + a := NewLeaf(2) insert_linked_list_node(a, nil, nil) - b := NewLeaf(2, false) + b := NewLeaf(2) insert_linked_list_node(b, a, nil) - c := NewLeaf(2, false) + c := NewLeaf(2) insert_linked_list_node(c, b, nil) - d := NewLeaf(2, false) + d := NewLeaf(2) insert_linked_list_node(d, c, nil) if err := a.put_kv(Int(3), 1); err != nil { t.Error(err) @@ -882,13 +873,13 @@ func Test_pure_leaf_insert_split_less(t *testing.T) { } func Test_pure_leaf_split_less(t *testing.T) { - a := NewLeaf(2, false) + a := NewLeaf(2) insert_linked_list_node(a, nil, nil) - b := NewLeaf(2, false) + b := NewLeaf(2) insert_linked_list_node(b, a, nil) - c := NewLeaf(2, false) + c := NewLeaf(2) insert_linked_list_node(c, b, nil) - d := NewLeaf(2, false) + d := NewLeaf(2) insert_linked_list_node(d, c, nil) if err := a.put_kv(Int(3), 1); err != nil { t.Error(err) @@ -955,13 +946,13 @@ func Test_pure_leaf_split_less(t *testing.T) { } func Test_pure_leaf_split_equal(t *testing.T) { - a := NewLeaf(2, false) + a := NewLeaf(2) insert_linked_list_node(a, nil, nil) - b := NewLeaf(2, false) + b := NewLeaf(2) insert_linked_list_node(b, a, nil) - c := NewLeaf(2, false) + c := NewLeaf(2) insert_linked_list_node(c, b, nil) - d := NewLeaf(2, false) + d := NewLeaf(2) insert_linked_list_node(d, c, nil) if err := a.put_kv(Int(3), 1); err != nil { t.Error(err) @@ -1019,13 +1010,13 @@ func Test_pure_leaf_split_equal(t *testing.T) { } func Test_pure_leaf_split_greater(t *testing.T) { - a := NewLeaf(2, false) + a := NewLeaf(2) insert_linked_list_node(a, nil, nil) - b := NewLeaf(2, false) + b := NewLeaf(2) insert_linked_list_node(b, a, nil) - c := NewLeaf(2, false) + c := NewLeaf(2) insert_linked_list_node(c, b, nil) - d := NewLeaf(2, false) + d := NewLeaf(2) insert_linked_list_node(d, c, nil) if err := a.put_kv(Int(3), 1); err != nil { t.Error(err) @@ -1089,13 +1080,13 @@ func Test_pure_leaf_split_greater(t *testing.T) { } func Test_find_end_of_pure_run(t *testing.T) { - a := NewLeaf(2, false) + a := NewLeaf(2) insert_linked_list_node(a, nil, nil) - b := NewLeaf(2, false) + b := NewLeaf(2) insert_linked_list_node(b, a, nil) - c := NewLeaf(2, false) + c := NewLeaf(2) insert_linked_list_node(c, b, nil) - d := NewLeaf(2, false) + d := NewLeaf(2) insert_linked_list_node(d, c, nil) if err := a.put_kv(Int(3), 1); err != nil { t.Error(err) @@ -1125,13 +1116,13 @@ func Test_find_end_of_pure_run(t *testing.T) { } func Test_insert_linked_list_node(t *testing.T) { - a := NewLeaf(1, false) + a := NewLeaf(1) insert_linked_list_node(a, nil, nil) - b := NewLeaf(2, false) + b := NewLeaf(2) insert_linked_list_node(b, a, nil) - c := NewLeaf(3, false) + c := NewLeaf(3) insert_linked_list_node(c, b, nil) - d := NewLeaf(4, false) + d := NewLeaf(4) insert_linked_list_node(d, a, b) if a.prev != nil { t.Errorf("expected a.prev == nil") @@ -1160,13 +1151,13 @@ func Test_insert_linked_list_node(t *testing.T) { } func Test_remove_linked_list_node(t *testing.T) { - a := NewLeaf(1, false) + a := NewLeaf(1) insert_linked_list_node(a, nil, nil) - b := NewLeaf(2, false) + b := NewLeaf(2) insert_linked_list_node(b, a, nil) - c := NewLeaf(3, false) + c := NewLeaf(3) insert_linked_list_node(c, b, nil) - d := NewLeaf(4, false) + d := NewLeaf(4) insert_linked_list_node(d, a, b) if a.prev != nil { t.Errorf("expected a.prev == nil") @@ -1235,8 +1226,8 @@ func Test_remove_linked_list_node(t *testing.T) { } func Test_balance_leaf_nodes_with_dup(t *testing.T) { - a := NewLeaf(3, false) - b := NewLeaf(3, false) + a := NewLeaf(3) + b := NewLeaf(3) if err := a.put_kv(Int(1), 1); err != nil { t.Error(err) } @@ -1256,8 +1247,8 @@ func Test_balance_leaf_nodes_with_dup(t *testing.T) { } func Test_balance_leaf_nodes(t *testing.T) { - a := NewLeaf(7, false) - b := NewLeaf(7, false) + a := NewLeaf(7) + b := NewLeaf(7) if err := a.put_kv(Int(1), 1); err != nil { t.Error(err) } From 0c360eb6b25fe11c3db7c0fcc8df0f2923fc8902 Mon Sep 17 00:00:00 2001 From: Chris Lu Date: Fri, 20 Aug 2021 18:34:50 -0700 Subject: [PATCH 004/130] add getter and setter for root of tree and map --- weed/util/bptree/bpmap.go | 21 ++++++++++++++------- weed/util/bptree/bptree.go | 37 ++++++++++++++++++++++--------------- 2 files changed, 36 insertions(+), 22 deletions(-) diff --git a/weed/util/bptree/bpmap.go b/weed/util/bptree/bpmap.go index 1dff8643a..9b391fd88 100644 --- a/weed/util/bptree/bpmap.go +++ b/weed/util/bptree/bpmap.go @@ -9,6 +9,13 @@ import ( */ type BpMap BpTree +func (self *BpMap) getRoot() *BpNode { + return self.root +} +func (self *BpMap) setRoot(root *BpNode) { + self.root = root +} + func NewBpMap(node_size int) *BpMap { return &BpMap{ root: NewLeaf(node_size), @@ -20,16 +27,16 @@ func (self *BpMap) Has(key Hashable) bool { } func (self *BpMap) Put(key Hashable, value interface{}) (err error) { - new_root, err := self.root.put(key, value) + new_root, err := self.getRoot().put(key, value) if err != nil { return err } - self.root = new_root + self.setRoot(new_root) return nil } func (self *BpMap) Get(key Hashable) (value interface{}, err error) { - j, l := self.root.get_start(key) + j, l := self.getRoot().get_start(key) if l.keys[j].Equals(key) { return l.values[j], nil } @@ -41,15 +48,15 @@ func (self *BpMap) Remove(key Hashable) (value interface{}, err error) { if err != nil { return nil, err } - ns := self.root.NodeSize() - new_root, err := self.root.remove(key, func(value interface{}) bool { return true }) + ns := self.getRoot().NodeSize() + new_root, err := self.getRoot().remove(key, func(value interface{}) bool { return true }) if err != nil { return nil, err } if new_root == nil { - self.root = NewLeaf(ns) + self.setRoot(NewLeaf(ns)) } else { - self.root = new_root + self.setRoot(new_root) } return value, nil } diff --git a/weed/util/bptree/bptree.go b/weed/util/bptree/bptree.go index eff4f3238..68ee08720 100644 --- a/weed/util/bptree/bptree.go +++ b/weed/util/bptree/bptree.go @@ -10,6 +10,13 @@ type BpTree struct { root *BpNode } +func (self *BpTree) getRoot() *BpNode { + return self.root +} +func (self *BpTree) setRoot(root *BpNode) { + self.root = root +} + type loc_iterator func() (i int, leaf *BpNode, li loc_iterator) func NewBpTree(node_size int) *BpTree { @@ -19,24 +26,24 @@ func NewBpTree(node_size int) *BpTree { } func (self *BpTree) Has(key Hashable) bool { - if len(self.root.keys) == 0 { + if len(self.getRoot().keys) == 0 { return false } - j, l := self.root.get_start(key) + j, l := self.getRoot().get_start(key) return l.keys[j].Equals(key) } func (self *BpTree) Add(key Hashable, value interface{}) (err error) { - new_root, err := self.root.put(key, value) + new_root, err := self.getRoot().put(key, value) if err != nil { return err } - self.root = new_root + self.setRoot(new_root) return nil } func (self *BpTree) Replace(key Hashable, where WhereFunc, value interface{}) (err error) { - li := self.root.forward(key, key) + li := self.getRoot().forward(key, key) for i, leaf, next := li(); next != nil; i, leaf, next = next() { if where(leaf.values[i]) { leaf.values[i] = value @@ -52,9 +59,9 @@ func (self *BpTree) Find(key Hashable) (kvi KVIterator) { func (self *BpTree) Range(from, to Hashable) (kvi KVIterator) { var li loc_iterator if !to.Less(from) { - li = self.root.forward(from, to) + li = self.getRoot().forward(from, to) } else { - li = self.root.backward(from, to) + li = self.getRoot().backward(from, to) } kvi = func() (key Hashable, value interface{}, next KVIterator) { var i int @@ -69,21 +76,21 @@ func (self *BpTree) Range(from, to Hashable) (kvi KVIterator) { } func (self *BpTree) RemoveWhere(key Hashable, where WhereFunc) (err error) { - ns := self.root.NodeSize() - new_root, err := self.root.remove(key, where) + ns := self.getRoot().NodeSize() + new_root, err := self.getRoot().remove(key, where) if err != nil { return err } if new_root == nil { - self.root = NewLeaf(ns) + self.setRoot(NewLeaf(ns)) } else { - self.root = new_root + self.setRoot(new_root) } return nil } func (self *BpTree) Keys() (ki KIterator) { - li := self.root.all() + li := self.getRoot().all() var prev Equatable ki = func() (key Hashable, next KIterator) { var i int @@ -110,7 +117,7 @@ func (self *BpTree) Items() (vi KIterator) { } func (self *BpTree) Iterate() (kvi KVIterator) { - li := self.root.all() + li := self.getRoot().all() kvi = func() (key Hashable, value interface{}, next KVIterator) { var i int var leaf *BpNode @@ -124,7 +131,7 @@ func (self *BpTree) Iterate() (kvi KVIterator) { } func (self *BpTree) Backward() (kvi KVIterator) { - li := self.root.all_backward() + li := self.getRoot().all_backward() kvi = func() (key Hashable, value interface{}, next KVIterator) { var i int var leaf *BpNode @@ -135,4 +142,4 @@ func (self *BpTree) Backward() (kvi KVIterator) { return leaf.keys[i], leaf.values[i], kvi } return kvi -} \ No newline at end of file +} From 01661ec6a77eeefc44466df1c0dd90ac320f38fa Mon Sep 17 00:00:00 2001 From: Chris Lu Date: Fri, 20 Aug 2021 18:37:34 -0700 Subject: [PATCH 005/130] move to getter setter file --- weed/util/bptree/bpmap.go | 7 ------- weed/util/bptree/bptree.go | 7 ------- weed/util/bptree/getter_setter.go | 15 +++++++++++++++ 3 files changed, 15 insertions(+), 14 deletions(-) create mode 100644 weed/util/bptree/getter_setter.go diff --git a/weed/util/bptree/bpmap.go b/weed/util/bptree/bpmap.go index 9b391fd88..a68eae255 100644 --- a/weed/util/bptree/bpmap.go +++ b/weed/util/bptree/bpmap.go @@ -9,13 +9,6 @@ import ( */ type BpMap BpTree -func (self *BpMap) getRoot() *BpNode { - return self.root -} -func (self *BpMap) setRoot(root *BpNode) { - self.root = root -} - func NewBpMap(node_size int) *BpMap { return &BpMap{ root: NewLeaf(node_size), diff --git a/weed/util/bptree/bptree.go b/weed/util/bptree/bptree.go index 68ee08720..405e93ac7 100644 --- a/weed/util/bptree/bptree.go +++ b/weed/util/bptree/bptree.go @@ -10,13 +10,6 @@ type BpTree struct { root *BpNode } -func (self *BpTree) getRoot() *BpNode { - return self.root -} -func (self *BpTree) setRoot(root *BpNode) { - self.root = root -} - type loc_iterator func() (i int, leaf *BpNode, li loc_iterator) func NewBpTree(node_size int) *BpTree { diff --git a/weed/util/bptree/getter_setter.go b/weed/util/bptree/getter_setter.go new file mode 100644 index 000000000..cd5c8344b --- /dev/null +++ b/weed/util/bptree/getter_setter.go @@ -0,0 +1,15 @@ +package bptree + +func (self *BpMap) getRoot() *BpNode { + return self.root +} +func (self *BpMap) setRoot(root *BpNode) { + self.root = root +} + +func (self *BpTree) getRoot() *BpNode { + return self.root +} +func (self *BpTree) setRoot(root *BpNode) { + self.root = root +} From 172da83449e04641995eeb76ed015586fe6827f5 Mon Sep 17 00:00:00 2001 From: Chris Lu Date: Fri, 20 Aug 2021 18:50:16 -0700 Subject: [PATCH 006/130] bpnode use get prev and next --- weed/util/bptree/bptree_node.go | 46 +++++----- weed/util/bptree/bptree_test.go | 138 +++++++++++++++--------------- weed/util/bptree/getter_setter.go | 13 +++ 3 files changed, 105 insertions(+), 92 deletions(-) diff --git a/weed/util/bptree/bptree_node.go b/weed/util/bptree/bptree_node.go index bd74e6327..765a25cfa 100644 --- a/weed/util/bptree/bptree_node.go +++ b/weed/util/bptree/bptree_node.go @@ -108,9 +108,9 @@ func (self *BpNode) get_start(key Hashable) (i int, leaf *BpNode) { func next_location(i int, leaf *BpNode) (int, *BpNode, bool) { j := i + 1 - for j >= len(leaf.keys) && leaf.next != nil { + for j >= len(leaf.keys) && leaf.getNext() != nil { j = 0 - leaf = leaf.next + leaf = leaf.getNext() } if j >= len(leaf.keys) { return -1, nil, true @@ -120,8 +120,8 @@ func next_location(i int, leaf *BpNode) (int, *BpNode, bool) { func prev_location(i int, leaf *BpNode) (int, *BpNode, bool) { j := i - 1 - for j < 0 && leaf.prev != nil { - leaf = leaf.prev + for j < 0 && leaf.getPrev() != nil { + leaf = leaf.getPrev() j = len(leaf.keys) - 1 } if j < 0 { @@ -165,8 +165,8 @@ func (self *BpNode) leaf_get_start(key Hashable) (i int, leaf *BpNode) { if i >= len(self.keys) && i > 0 { i = len(self.keys) - 1 } - if !has && (len(self.keys) == 0 || self.keys[i].Less(key)) && self.next != nil { - return self.next.leaf_get_start(key) + if !has && (len(self.keys) == 0 || self.keys[i].Less(key)) && self.getNext() != nil { + return self.getNext().leaf_get_start(key) } return i, self } @@ -299,7 +299,7 @@ func (self *BpNode) leaf_split(key Hashable, value interface{}) (a, b *BpNode, e } a = self b = NewLeaf(self.NodeSize()) - insert_linked_list_node(b, a, a.next) + insert_linked_list_node(b, a, a.getNext()) balance_nodes(a, b) if key.Less(b.keys[0]) { if err := a.put_kv(key, value); err != nil { @@ -335,7 +335,7 @@ func (self *BpNode) pure_leaf_split(key Hashable, value interface{}) (a, b *BpNo if err := a.put_kv(key, value); err != nil { return nil, nil, err } - insert_linked_list_node(a, b.prev, b) + insert_linked_list_node(a, b.getPrev(), b) return a, b, nil } else { a = self @@ -350,7 +350,7 @@ func (self *BpNode) pure_leaf_split(key Hashable, value interface{}) (a, b *BpNo if err := b.put_kv(key, value); err != nil { return nil, nil, err } - insert_linked_list_node(b, e, e.next) + insert_linked_list_node(b, e, e.getNext()) if e.keys[0].Equals(key) { return a, nil, nil } @@ -516,12 +516,12 @@ func (self *BpNode) leaf_remove(key, stop Hashable, where WhereFunc) (a *BpNode, } if len(l.keys) == 0 { remove_linked_list_node(l) - if l.next == nil { + if l.getNext() == nil { a = nil } else if stop == nil { a = nil - } else if !l.next.keys[0].Equals(stop) { - a = l.next + } else if !l.getNext().keys[0].Equals(stop) { + a = l.getNext() } else { a = nil } @@ -587,10 +587,10 @@ func (self *BpNode) find(key Hashable) (int, bool) { func (self *BpNode) find_end_of_pure_run() *BpNode { k := self.keys[0] p := self - n := self.next + n := self.getNext() for n != nil && n.Pure() && k.Equals(n.keys[0]) { p = n - n = n.next + n = n.getNext() } return p } @@ -659,25 +659,25 @@ func (self *BpNode) backward(from, to Hashable) (li loc_iterator) { } func insert_linked_list_node(n, prev, next *BpNode) { - if (prev != nil && prev.next != next) || (next != nil && next.prev != prev) { + if (prev != nil && prev.getNext() != next) || (next != nil && next.getPrev() != prev) { panic(BpTreeError("prev and next not hooked up")) } - n.prev = prev - n.next = next + n.setPrev(prev) + n.setNext(next) if prev != nil { - prev.next = n + prev.setNext(n) } if next != nil { - next.prev = n + next.setPrev(n) } } func remove_linked_list_node(n *BpNode) { - if n.prev != nil { - n.prev.next = n.next + if n.getPrev() != nil { + n.getPrev().setNext(n.getNext()) } - if n.next != nil { - n.next.prev = n.prev + if n.getNext() != nil { + n.getNext().setPrev(n.getPrev()) } } diff --git a/weed/util/bptree/bptree_test.go b/weed/util/bptree/bptree_test.go index ef73e862d..3c99c616a 100644 --- a/weed/util/bptree/bptree_test.go +++ b/weed/util/bptree/bptree_test.go @@ -378,7 +378,7 @@ func Test_get_end(t *testing.T) { } i, n = root.get_end(Int(3)) t.Log(n) - if n != root.pointers[1].next { + if n != root.pointers[1].getNext() { t.Error("wrong node from get_end") } if i != 1 { @@ -452,11 +452,11 @@ func Test_put_no_root_split(t *testing.T) { if !p.has(Int(1)) { t.Error("p didn't have the right keys", p) } - if p.next == nil { + if p.getNext() == nil { t.Error("p.next should not be nil") } t.Log(p) - t.Log(p.next) + t.Log(p.getNext()) } } @@ -839,34 +839,34 @@ func Test_pure_leaf_insert_split_less(t *testing.T) { if p == nil || len(p.keys) != 1 || !p.keys[0].Equals(Int(2)) { t.Errorf("p did not contain the right key") } - if p.prev != nil { + if p.getPrev() != nil { t.Errorf("expected p.prev == nil") } - if p.next != a { + if p.getNext() != a { t.Errorf("expected p.next == a") } - if a.prev != p { + if a.getPrev() != p { t.Errorf("expected a.prev == p") } - if a.next != b { + if a.getNext() != b { t.Errorf("expected a.next == b") } - if b.prev != a { + if b.getPrev() != a { t.Errorf("expected b.prev == a") } - if b.next != c { + if b.getNext() != c { t.Errorf("expected b.next == c") } - if c.prev != b { + if c.getPrev() != b { t.Errorf("expected c.prev == b") } - if c.next != d { + if c.getNext() != d { t.Errorf("expected c.next == d") } - if d.prev != c { + if d.getPrev() != c { t.Errorf("expected d.prev == c") } - if d.next != nil { + if d.getNext() != nil { t.Errorf("expected d.next == nil") } } @@ -912,34 +912,34 @@ func Test_pure_leaf_split_less(t *testing.T) { if p == nil || len(p.keys) != 1 || !p.keys[0].Equals(Int(2)) { t.Errorf("p did not contain the right key") } - if p.prev != nil { + if p.getPrev() != nil { t.Errorf("expected p.prev == nil") } - if p.next != a { + if p.getNext() != a { t.Errorf("expected p.next == a") } - if a.prev != p { + if a.getPrev() != p { t.Errorf("expected a.prev == p") } - if a.next != b { + if a.getNext() != b { t.Errorf("expected a.next == b") } - if b.prev != a { + if b.getPrev() != a { t.Errorf("expected b.prev == a") } - if b.next != c { + if b.getNext() != c { t.Errorf("expected b.next == c") } - if c.prev != b { + if c.getPrev() != b { t.Errorf("expected c.prev == b") } - if c.next != d { + if c.getNext() != d { t.Errorf("expected c.next == d") } - if d.prev != c { + if d.getPrev() != c { t.Errorf("expected d.prev == c") } - if d.next != nil { + if d.getNext() != nil { t.Errorf("expected d.next == nil") } } @@ -982,28 +982,28 @@ func Test_pure_leaf_split_equal(t *testing.T) { if q != nil { t.Errorf("q != nil") } - if a.prev != nil { + if a.getPrev() != nil { t.Errorf("expected a.prev == nil") } - if a.next != b { + if a.getNext() != b { t.Errorf("expected a.next == b") } - if b.prev != a { + if b.getPrev() != a { t.Errorf("expected b.prev == a") } - if b.next != c { + if b.getNext() != c { t.Errorf("expected b.next == c") } - if c.prev != b { + if c.getPrev() != b { t.Errorf("expected c.prev == b") } - if c.next != d { + if c.getNext() != d { t.Errorf("expected c.next == d") } - if d.prev != c { + if d.getPrev() != c { t.Errorf("expected d.prev == c") } - if d.next != nil { + if d.getNext() != nil { t.Errorf("expected d.next == nil") } } @@ -1046,34 +1046,34 @@ func Test_pure_leaf_split_greater(t *testing.T) { if q == nil || len(q.keys) != 1 || !q.keys[0].Equals(Int(4)) { t.Errorf("q != nil") } - if a.prev != nil { + if a.getPrev() != nil { t.Errorf("expected a.prev == nil") } - if a.next != b { + if a.getNext() != b { t.Errorf("expected a.next == b") } - if b.prev != a { + if b.getPrev() != a { t.Errorf("expected b.prev == a") } - if b.next != c { + if b.getNext() != c { t.Errorf("expected b.next == c") } - if c.prev != b { + if c.getPrev() != b { t.Errorf("expected c.prev == b") } - if c.next != q { + if c.getNext() != q { t.Errorf("expected c.next == q") } - if q.prev != c { + if q.getPrev() != c { t.Errorf("expected q.prev == c") } - if q.next != d { + if q.getNext() != d { t.Errorf("expected q.next == d") } - if d.prev != q { + if d.getPrev() != q { t.Errorf("expected d.prev == q") } - if d.next != nil { + if d.getNext() != nil { t.Errorf("expected d.next == nil") } } @@ -1124,28 +1124,28 @@ func Test_insert_linked_list_node(t *testing.T) { insert_linked_list_node(c, b, nil) d := NewLeaf(4) insert_linked_list_node(d, a, b) - if a.prev != nil { + if a.getPrev() != nil { t.Errorf("expected a.prev == nil") } - if a.next != d { + if a.getNext() != d { t.Errorf("expected a.next == d") } - if d.prev != a { + if d.getPrev() != a { t.Errorf("expected d.prev == a") } - if d.next != b { + if d.getNext() != b { t.Errorf("expected d.next == b") } - if b.prev != d { + if b.getPrev() != d { t.Errorf("expected b.prev == d") } - if b.next != c { + if b.getNext() != c { t.Errorf("expected b.next == c") } - if c.prev != b { + if c.getPrev() != b { t.Errorf("expected c.prev == b") } - if c.next != nil { + if c.getNext() != nil { t.Errorf("expected c.next == nil") } } @@ -1159,67 +1159,67 @@ func Test_remove_linked_list_node(t *testing.T) { insert_linked_list_node(c, b, nil) d := NewLeaf(4) insert_linked_list_node(d, a, b) - if a.prev != nil { + if a.getPrev() != nil { t.Errorf("expected a.prev == nil") } - if a.next != d { + if a.getNext() != d { t.Errorf("expected a.next == d") } - if d.prev != a { + if d.getPrev() != a { t.Errorf("expected d.prev == a") } - if d.next != b { + if d.getNext() != b { t.Errorf("expected d.next == b") } - if b.prev != d { + if b.getPrev() != d { t.Errorf("expected b.prev == d") } - if b.next != c { + if b.getNext() != c { t.Errorf("expected b.next == c") } - if c.prev != b { + if c.getPrev() != b { t.Errorf("expected c.prev == b") } - if c.next != nil { + if c.getNext() != nil { t.Errorf("expected c.next == nil") } remove_linked_list_node(d) - if a.prev != nil { + if a.getPrev() != nil { t.Errorf("expected a.prev == nil") } - if a.next != b { + if a.getNext() != b { t.Errorf("expected a.next == b") } - if b.prev != a { + if b.getPrev() != a { t.Errorf("expected b.prev == a") } - if b.next != c { + if b.getNext() != c { t.Errorf("expected b.next == c") } - if c.prev != b { + if c.getPrev() != b { t.Errorf("expected c.prev == b") } - if c.next != nil { + if c.getNext() != nil { t.Errorf("expected c.next == nil") } remove_linked_list_node(a) - if b.prev != nil { + if b.getPrev() != nil { t.Errorf("expected b.prev == nil") } - if b.next != c { + if b.getNext() != c { t.Errorf("expected b.next == c") } - if c.prev != b { + if c.getPrev() != b { t.Errorf("expected c.prev == b") } - if c.next != nil { + if c.getNext() != nil { t.Errorf("expected c.next == nil") } remove_linked_list_node(c) - if b.prev != nil { + if b.getPrev() != nil { t.Errorf("expected b.prev == nil") } - if b.next != nil { + if b.getNext() != nil { t.Errorf("expected b.next == nil") } remove_linked_list_node(b) diff --git a/weed/util/bptree/getter_setter.go b/weed/util/bptree/getter_setter.go index cd5c8344b..da9d69b05 100644 --- a/weed/util/bptree/getter_setter.go +++ b/weed/util/bptree/getter_setter.go @@ -13,3 +13,16 @@ func (self *BpTree) getRoot() *BpNode { func (self *BpTree) setRoot(root *BpNode) { self.root = root } + +func (self *BpNode) getNext() *BpNode { + return self.next +} +func (self *BpNode) setNext(next *BpNode) { + self.next = next +} +func (self *BpNode) getPrev() *BpNode { + return self.prev +} +func (self *BpNode) setPrev(prev *BpNode) { + self.prev = prev +} From 5f6cc9a8145f79c0f679c8b3fda34c3c76a6d411 Mon Sep 17 00:00:00 2001 From: Chris Lu Date: Sat, 21 Aug 2021 13:36:52 -0700 Subject: [PATCH 007/130] make proto node --- weed/util/bptree/Makefile | 6 ++ weed/util/bptree/bptree.pb.go | 186 ++++++++++++++++++++++++++++++++++ weed/util/bptree/bptree.proto | 14 +++ 3 files changed, 206 insertions(+) create mode 100644 weed/util/bptree/Makefile create mode 100644 weed/util/bptree/bptree.pb.go create mode 100644 weed/util/bptree/bptree.proto diff --git a/weed/util/bptree/Makefile b/weed/util/bptree/Makefile new file mode 100644 index 000000000..a98f39a08 --- /dev/null +++ b/weed/util/bptree/Makefile @@ -0,0 +1,6 @@ +all: gen + +.PHONY : gen + +gen: + protoc bptree.proto --go_out=plugins=grpc:. --go_opt=paths=source_relative diff --git a/weed/util/bptree/bptree.pb.go b/weed/util/bptree/bptree.pb.go new file mode 100644 index 000000000..e7d155a36 --- /dev/null +++ b/weed/util/bptree/bptree.pb.go @@ -0,0 +1,186 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.25.0 +// protoc v3.12.3 +// source: bptree.proto + +package bptree + +import ( + proto "github.com/golang/protobuf/proto" + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +// This is a compile-time assertion that a sufficiently up-to-date version +// of the legacy proto package is being used. +const _ = proto.ProtoPackageIsVersion4 + +type ProtoNode struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Keys [][]byte `protobuf:"bytes,1,rep,name=keys,proto3" json:"keys,omitempty"` + Values [][]byte `protobuf:"bytes,2,rep,name=values,proto3" json:"values,omitempty"` + Pointers []int64 `protobuf:"varint,3,rep,packed,name=pointers,proto3" json:"pointers,omitempty"` + Next int64 `protobuf:"varint,4,opt,name=next,proto3" json:"next,omitempty"` + Prev int64 `protobuf:"varint,5,opt,name=prev,proto3" json:"prev,omitempty"` +} + +func (x *ProtoNode) Reset() { + *x = ProtoNode{} + if protoimpl.UnsafeEnabled { + mi := &file_bptree_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ProtoNode) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ProtoNode) ProtoMessage() {} + +func (x *ProtoNode) ProtoReflect() protoreflect.Message { + mi := &file_bptree_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ProtoNode.ProtoReflect.Descriptor instead. +func (*ProtoNode) Descriptor() ([]byte, []int) { + return file_bptree_proto_rawDescGZIP(), []int{0} +} + +func (x *ProtoNode) GetKeys() [][]byte { + if x != nil { + return x.Keys + } + return nil +} + +func (x *ProtoNode) GetValues() [][]byte { + if x != nil { + return x.Values + } + return nil +} + +func (x *ProtoNode) GetPointers() []int64 { + if x != nil { + return x.Pointers + } + return nil +} + +func (x *ProtoNode) GetNext() int64 { + if x != nil { + return x.Next + } + return 0 +} + +func (x *ProtoNode) GetPrev() int64 { + if x != nil { + return x.Prev + } + return 0 +} + +var File_bptree_proto protoreflect.FileDescriptor + +var file_bptree_proto_rawDesc = []byte{ + 0x0a, 0x0c, 0x62, 0x70, 0x74, 0x72, 0x65, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x06, + 0x62, 0x70, 0x74, 0x72, 0x65, 0x65, 0x22, 0x7b, 0x0a, 0x09, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x4e, + 0x6f, 0x64, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6b, 0x65, 0x79, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, + 0x0c, 0x52, 0x04, 0x6b, 0x65, 0x79, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x76, 0x61, 0x6c, 0x75, 0x65, + 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x06, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x12, + 0x1a, 0x0a, 0x08, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, + 0x03, 0x52, 0x08, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x6e, + 0x65, 0x78, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x04, 0x6e, 0x65, 0x78, 0x74, 0x12, + 0x12, 0x0a, 0x04, 0x70, 0x72, 0x65, 0x76, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x04, 0x70, + 0x72, 0x65, 0x76, 0x42, 0x31, 0x5a, 0x2f, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, + 0x6d, 0x2f, 0x63, 0x68, 0x72, 0x69, 0x73, 0x6c, 0x75, 0x73, 0x66, 0x2f, 0x73, 0x65, 0x61, 0x77, + 0x65, 0x65, 0x64, 0x66, 0x73, 0x2f, 0x77, 0x65, 0x65, 0x64, 0x2f, 0x75, 0x74, 0x69, 0x6c, 0x2f, + 0x62, 0x70, 0x74, 0x72, 0x65, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_bptree_proto_rawDescOnce sync.Once + file_bptree_proto_rawDescData = file_bptree_proto_rawDesc +) + +func file_bptree_proto_rawDescGZIP() []byte { + file_bptree_proto_rawDescOnce.Do(func() { + file_bptree_proto_rawDescData = protoimpl.X.CompressGZIP(file_bptree_proto_rawDescData) + }) + return file_bptree_proto_rawDescData +} + +var file_bptree_proto_msgTypes = make([]protoimpl.MessageInfo, 1) +var file_bptree_proto_goTypes = []interface{}{ + (*ProtoNode)(nil), // 0: bptree.ProtoNode +} +var file_bptree_proto_depIdxs = []int32{ + 0, // [0:0] is the sub-list for method output_type + 0, // [0:0] is the sub-list for method input_type + 0, // [0:0] is the sub-list for extension type_name + 0, // [0:0] is the sub-list for extension extendee + 0, // [0:0] is the sub-list for field type_name +} + +func init() { file_bptree_proto_init() } +func file_bptree_proto_init() { + if File_bptree_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_bptree_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ProtoNode); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_bptree_proto_rawDesc, + NumEnums: 0, + NumMessages: 1, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_bptree_proto_goTypes, + DependencyIndexes: file_bptree_proto_depIdxs, + MessageInfos: file_bptree_proto_msgTypes, + }.Build() + File_bptree_proto = out.File + file_bptree_proto_rawDesc = nil + file_bptree_proto_goTypes = nil + file_bptree_proto_depIdxs = nil +} diff --git a/weed/util/bptree/bptree.proto b/weed/util/bptree/bptree.proto new file mode 100644 index 000000000..1d55096a2 --- /dev/null +++ b/weed/util/bptree/bptree.proto @@ -0,0 +1,14 @@ +syntax = "proto3"; + +package bptree; + +option go_package = "github.com/chrislusf/seaweedfs/weed/util/bptree"; + +message ProtoNode { + repeated bytes keys = 1; + repeated bytes values = 2; + repeated int64 pointers = 3; + int64 next = 4; + int64 prev = 5; + int64 id = 6; +} From 849f185a20ea2216f2f780f872b6cd8bac854d2b Mon Sep 17 00:00:00 2001 From: Chris Lu Date: Sat, 21 Aug 2021 15:00:44 -0700 Subject: [PATCH 008/130] add memory kv store --- weed/util/bptree/tree_store/memory_store.go | 29 ++++++++++++++++++++ weed/util/bptree/tree_store/tree_store.go.go | 6 ++++ 2 files changed, 35 insertions(+) create mode 100644 weed/util/bptree/tree_store/memory_store.go create mode 100644 weed/util/bptree/tree_store/tree_store.go.go diff --git a/weed/util/bptree/tree_store/memory_store.go b/weed/util/bptree/tree_store/memory_store.go new file mode 100644 index 000000000..467455664 --- /dev/null +++ b/weed/util/bptree/tree_store/memory_store.go @@ -0,0 +1,29 @@ +package tree_store + +import "errors" + +var ( + NotFound = errors.New("not found") +) + +type MemoryTreeStore struct { + m map[int64][]byte +} + +func NewMemoryTreeStore() *MemoryTreeStore{ + return &MemoryTreeStore{ + m: make(map[int64][]byte), + } +} + +func (m *MemoryTreeStore) Put(k int64, v []byte) error { + m.m[k] = v + return nil +} + +func (m *MemoryTreeStore) Get(k int64) ([]byte, error) { + if v, found := m.m[k]; found { + return v, nil + } + return nil, NotFound +} diff --git a/weed/util/bptree/tree_store/tree_store.go.go b/weed/util/bptree/tree_store/tree_store.go.go new file mode 100644 index 000000000..6a0af6ae6 --- /dev/null +++ b/weed/util/bptree/tree_store/tree_store.go.go @@ -0,0 +1,6 @@ +package tree_store + +type TreeStore interface { + Put(k int64, v []byte) error + Get(k int64) ([]byte, error) +} From 38c8470d1d8e3687c01b4eb6a9d9ebd9a988eb43 Mon Sep 17 00:00:00 2001 From: Chris Lu Date: Sat, 21 Aug 2021 15:13:13 -0700 Subject: [PATCH 009/130] add back non_dedup --- weed/util/bptree/bpmap.go | 4 +- weed/util/bptree/bptree.go | 18 ++++++- weed/util/bptree/bptree_node.go | 17 ++++-- weed/util/bptree/bptree_test.go | 95 ++++++++++++++++++--------------- 4 files changed, 83 insertions(+), 51 deletions(-) diff --git a/weed/util/bptree/bpmap.go b/weed/util/bptree/bpmap.go index a68eae255..5dd95070e 100644 --- a/weed/util/bptree/bpmap.go +++ b/weed/util/bptree/bpmap.go @@ -11,7 +11,7 @@ type BpMap BpTree func NewBpMap(node_size int) *BpMap { return &BpMap{ - root: NewLeaf(node_size), + root: NewLeaf(node_size, true), } } @@ -47,7 +47,7 @@ func (self *BpMap) Remove(key Hashable) (value interface{}, err error) { return nil, err } if new_root == nil { - self.setRoot(NewLeaf(ns)) + self.setRoot(NewLeaf(ns, true)) } else { self.setRoot(new_root) } diff --git a/weed/util/bptree/bptree.go b/weed/util/bptree/bptree.go index 405e93ac7..12a4bfb0d 100644 --- a/weed/util/bptree/bptree.go +++ b/weed/util/bptree/bptree.go @@ -14,7 +14,7 @@ type loc_iterator func() (i int, leaf *BpNode, li loc_iterator) func NewBpTree(node_size int) *BpTree { return &BpTree{ - root: NewLeaf(node_size), + root: NewLeaf(node_size, false), } } @@ -26,6 +26,20 @@ func (self *BpTree) Has(key Hashable) bool { return l.keys[j].Equals(key) } +func (self *BpTree) Count(key Hashable) int { + if len(self.root.keys) == 0 { + return 0 + } + j, l := self.root.get_start(key) + count := 0 + end := false + for !end && l.keys[j].Equals(key) { + count++ + j, l, end = next_location(j, l) + } + return count +} + func (self *BpTree) Add(key Hashable, value interface{}) (err error) { new_root, err := self.getRoot().put(key, value) if err != nil { @@ -75,7 +89,7 @@ func (self *BpTree) RemoveWhere(key Hashable, where WhereFunc) (err error) { return err } if new_root == nil { - self.setRoot(NewLeaf(ns)) + self.setRoot(NewLeaf(ns, false)) } else { self.setRoot(new_root) } diff --git a/weed/util/bptree/bptree_node.go b/weed/util/bptree/bptree_node.go index 765a25cfa..3337292ba 100644 --- a/weed/util/bptree/bptree_node.go +++ b/weed/util/bptree/bptree_node.go @@ -6,6 +6,7 @@ type BpNode struct { pointers []*BpNode next *BpNode prev *BpNode + no_dup bool } func NewInternal(size int) *BpNode { @@ -18,13 +19,14 @@ func NewInternal(size int) *BpNode { } } -func NewLeaf(size int) *BpNode { +func NewLeaf(size int, no_dup bool) *BpNode { if size < 0 { panic(NegativeSize()) } return &BpNode{ keys: make([]Hashable, 0, size), values: make([]interface{}, 0, size), + no_dup: no_dup, } } @@ -274,6 +276,13 @@ func (self *BpNode) leaf_insert(key Hashable, value interface{}) (a, b *BpNode, if self.Internal() { return nil, nil, BpTreeError("Expected a leaf node") } + if self.no_dup { + i, has := self.find(key) + if has { + self.values[i] = value + return self, nil, nil + } + } if self.Full() { return self.leaf_split(key, value) } else { @@ -298,7 +307,7 @@ func (self *BpNode) leaf_split(key Hashable, value interface{}) (a, b *BpNode, e return self.pure_leaf_split(key, value) } a = self - b = NewLeaf(self.NodeSize()) + b = NewLeaf(self.NodeSize(), self.no_dup) insert_linked_list_node(b, a, a.getNext()) balance_nodes(a, b) if key.Less(b.keys[0]) { @@ -330,7 +339,7 @@ func (self *BpNode) pure_leaf_split(key Hashable, value interface{}) (a, b *BpNo return nil, nil, BpTreeError("Expected a pure leaf node") } if key.Less(self.keys[0]) { - a = NewLeaf(self.NodeSize()) + a = NewLeaf(self.NodeSize(), self.no_dup) b = self if err := a.put_kv(key, value); err != nil { return nil, nil, err @@ -346,7 +355,7 @@ func (self *BpNode) pure_leaf_split(key Hashable, value interface{}) (a, b *BpNo } return a, nil, nil } else { - b = NewLeaf(self.NodeSize()) + b = NewLeaf(self.NodeSize(), self.no_dup) if err := b.put_kv(key, value); err != nil { return nil, nil, err } diff --git a/weed/util/bptree/bptree_test.go b/weed/util/bptree/bptree_test.go index 3c99c616a..d1df0749e 100644 --- a/weed/util/bptree/bptree_test.go +++ b/weed/util/bptree/bptree_test.go @@ -119,6 +119,12 @@ func TestAddHasCountFindIterateRemove(t *testing.T) { if has := bpt.Has(randstr(10)); has { t.Error("Table has extra key") } + if count := bpt.Count(r.key); count != 1 { + t.Error(bpt, "Missing key") + } + if count := bpt.Count(randstr(10)); count != 0 { + t.Error("Table has extra key") + } for k, v, next := bpt.Find(r.key)(); next != nil; k, v, next = next() { if !k.Equals(r.key) { t.Error(bpt, "Find Failed Key Error") @@ -184,6 +190,9 @@ func TestAddHasCountFindIterateRemove(t *testing.T) { if has := bpt.Has(r.key); !has { t.Error(bpt, "Missing key") } + if count := bpt.Count(r.key); count != 1 { + t.Error(bpt, "Missing key") + } if err := bpt.RemoveWhere(r.key, func(value interface{}) bool { return true }); err != nil { t.Fatal(bpt, err) } @@ -266,7 +275,7 @@ func TestBpMap(t *testing.T) { } func Test_get_start(t *testing.T) { - root := NewLeaf(2) + root := NewLeaf(2, false) root, err := root.put(Int(1), 1) if err != nil { t.Error(err) @@ -335,7 +344,7 @@ func Test_get_start(t *testing.T) { } func Test_get_end(t *testing.T) { - root := NewLeaf(3) + root := NewLeaf(3, false) root, err := root.put(Int(1), -1) if err != nil { t.Fatal(err) @@ -427,7 +436,7 @@ func Test_get_end(t *testing.T) { } func Test_put_no_root_split(t *testing.T) { - a := NewLeaf(2) + a := NewLeaf(2, false) if err := a.put_kv(Int(1), 1); err != nil { t.Error(err) } @@ -461,7 +470,7 @@ func Test_put_no_root_split(t *testing.T) { } func Test_put_root_split(t *testing.T) { - a := NewLeaf(2) + a := NewLeaf(2, false) p, err := a.put(Int(1), 1) if err != nil { t.Error(err) @@ -511,7 +520,7 @@ func Test_put_root_split(t *testing.T) { func Test_internal_insert_no_split(t *testing.T) { a := NewInternal(3) - leaf := NewLeaf(1) + leaf := NewLeaf(1, false) if err := leaf.put_kv(Int(1), 1); err != nil { t.Error(err) } @@ -539,7 +548,7 @@ func Test_internal_insert_no_split(t *testing.T) { func Test_internal_insert_split_less(t *testing.T) { a := NewInternal(3) - leaf := NewLeaf(1) + leaf := NewLeaf(1, false) if err := leaf.put_kv(Int(1), 1); err != nil { t.Error(err) } @@ -649,7 +658,7 @@ func Test_internal_split_greater(t *testing.T) { } func Test_leaf_insert_no_split(t *testing.T) { - a := NewLeaf(3) + a := NewLeaf(3, false) insert_linked_list_node(a, nil, nil) if err := a.put_kv(Int(1), 1); err != nil { t.Error(err) @@ -675,7 +684,7 @@ func Test_leaf_insert_no_split(t *testing.T) { // tests the defer to split logic func Test_leaf_insert_split_less(t *testing.T) { - a := NewLeaf(3) + a := NewLeaf(3, false) insert_linked_list_node(a, nil, nil) if err := a.put_kv(Int(1), 1); err != nil { t.Error(err) @@ -706,7 +715,7 @@ func Test_leaf_insert_split_less(t *testing.T) { } func Test_leaf_split_less(t *testing.T) { - a := NewLeaf(3) + a := NewLeaf(3, false) insert_linked_list_node(a, nil, nil) if err := a.put_kv(Int(1), 1); err != nil { t.Error(err) @@ -737,7 +746,7 @@ func Test_leaf_split_less(t *testing.T) { } func Test_leaf_split_equal(t *testing.T) { - a := NewLeaf(3) + a := NewLeaf(3, false) insert_linked_list_node(a, nil, nil) if err := a.put_kv(Int(1), 1); err != nil { t.Error(err) @@ -768,7 +777,7 @@ func Test_leaf_split_equal(t *testing.T) { } func Test_leaf_split_greater(t *testing.T) { - a := NewLeaf(3) + a := NewLeaf(3, false) insert_linked_list_node(a, nil, nil) if err := a.put_kv(Int(1), 1); err != nil { t.Error(err) @@ -800,13 +809,13 @@ func Test_leaf_split_greater(t *testing.T) { // tests the defer logic func Test_pure_leaf_insert_split_less(t *testing.T) { - a := NewLeaf(2) + a := NewLeaf(2, false) insert_linked_list_node(a, nil, nil) - b := NewLeaf(2) + b := NewLeaf(2, false) insert_linked_list_node(b, a, nil) - c := NewLeaf(2) + c := NewLeaf(2, false) insert_linked_list_node(c, b, nil) - d := NewLeaf(2) + d := NewLeaf(2, false) insert_linked_list_node(d, c, nil) if err := a.put_kv(Int(3), 1); err != nil { t.Error(err) @@ -873,13 +882,13 @@ func Test_pure_leaf_insert_split_less(t *testing.T) { } func Test_pure_leaf_split_less(t *testing.T) { - a := NewLeaf(2) + a := NewLeaf(2, false) insert_linked_list_node(a, nil, nil) - b := NewLeaf(2) + b := NewLeaf(2, false) insert_linked_list_node(b, a, nil) - c := NewLeaf(2) + c := NewLeaf(2, false) insert_linked_list_node(c, b, nil) - d := NewLeaf(2) + d := NewLeaf(2, false) insert_linked_list_node(d, c, nil) if err := a.put_kv(Int(3), 1); err != nil { t.Error(err) @@ -946,13 +955,13 @@ func Test_pure_leaf_split_less(t *testing.T) { } func Test_pure_leaf_split_equal(t *testing.T) { - a := NewLeaf(2) + a := NewLeaf(2, false) insert_linked_list_node(a, nil, nil) - b := NewLeaf(2) + b := NewLeaf(2, false) insert_linked_list_node(b, a, nil) - c := NewLeaf(2) + c := NewLeaf(2, false) insert_linked_list_node(c, b, nil) - d := NewLeaf(2) + d := NewLeaf(2, false) insert_linked_list_node(d, c, nil) if err := a.put_kv(Int(3), 1); err != nil { t.Error(err) @@ -1010,13 +1019,13 @@ func Test_pure_leaf_split_equal(t *testing.T) { } func Test_pure_leaf_split_greater(t *testing.T) { - a := NewLeaf(2) + a := NewLeaf(2, false) insert_linked_list_node(a, nil, nil) - b := NewLeaf(2) + b := NewLeaf(2, false) insert_linked_list_node(b, a, nil) - c := NewLeaf(2) + c := NewLeaf(2, false) insert_linked_list_node(c, b, nil) - d := NewLeaf(2) + d := NewLeaf(2, false) insert_linked_list_node(d, c, nil) if err := a.put_kv(Int(3), 1); err != nil { t.Error(err) @@ -1080,13 +1089,13 @@ func Test_pure_leaf_split_greater(t *testing.T) { } func Test_find_end_of_pure_run(t *testing.T) { - a := NewLeaf(2) + a := NewLeaf(2, false) insert_linked_list_node(a, nil, nil) - b := NewLeaf(2) + b := NewLeaf(2, false) insert_linked_list_node(b, a, nil) - c := NewLeaf(2) + c := NewLeaf(2, false) insert_linked_list_node(c, b, nil) - d := NewLeaf(2) + d := NewLeaf(2, false) insert_linked_list_node(d, c, nil) if err := a.put_kv(Int(3), 1); err != nil { t.Error(err) @@ -1116,13 +1125,13 @@ func Test_find_end_of_pure_run(t *testing.T) { } func Test_insert_linked_list_node(t *testing.T) { - a := NewLeaf(1) + a := NewLeaf(1, false) insert_linked_list_node(a, nil, nil) - b := NewLeaf(2) + b := NewLeaf(2, false) insert_linked_list_node(b, a, nil) - c := NewLeaf(3) + c := NewLeaf(3, false) insert_linked_list_node(c, b, nil) - d := NewLeaf(4) + d := NewLeaf(4, false) insert_linked_list_node(d, a, b) if a.getPrev() != nil { t.Errorf("expected a.prev == nil") @@ -1151,13 +1160,13 @@ func Test_insert_linked_list_node(t *testing.T) { } func Test_remove_linked_list_node(t *testing.T) { - a := NewLeaf(1) + a := NewLeaf(1, false) insert_linked_list_node(a, nil, nil) - b := NewLeaf(2) + b := NewLeaf(2, false) insert_linked_list_node(b, a, nil) - c := NewLeaf(3) + c := NewLeaf(3, false) insert_linked_list_node(c, b, nil) - d := NewLeaf(4) + d := NewLeaf(4, false) insert_linked_list_node(d, a, b) if a.getPrev() != nil { t.Errorf("expected a.prev == nil") @@ -1226,8 +1235,8 @@ func Test_remove_linked_list_node(t *testing.T) { } func Test_balance_leaf_nodes_with_dup(t *testing.T) { - a := NewLeaf(3) - b := NewLeaf(3) + a := NewLeaf(3, false) + b := NewLeaf(3, false) if err := a.put_kv(Int(1), 1); err != nil { t.Error(err) } @@ -1247,8 +1256,8 @@ func Test_balance_leaf_nodes_with_dup(t *testing.T) { } func Test_balance_leaf_nodes(t *testing.T) { - a := NewLeaf(7) - b := NewLeaf(7) + a := NewLeaf(7, false) + b := NewLeaf(7, false) if err := a.put_kv(Int(1), 1); err != nil { t.Error(err) } From b3e49d27583a0e5346f270ccaae87049b368b215 Mon Sep 17 00:00:00 2001 From: Chris Lu Date: Sat, 21 Aug 2021 15:52:17 -0700 Subject: [PATCH 010/130] change value type to ItemValue --- weed/util/bptree/bpmap.go | 8 +- weed/util/bptree/bptree.go | 10 +- weed/util/bptree/bptree.pb.go | 33 ++++-- weed/util/bptree/bptree_node.go | 39 ++++--- weed/util/bptree/bptree_test.go | 188 ++++++++++++++++---------------- weed/util/bptree/types.go | 18 +-- 6 files changed, 156 insertions(+), 140 deletions(-) diff --git a/weed/util/bptree/bpmap.go b/weed/util/bptree/bpmap.go index 5dd95070e..50bedb980 100644 --- a/weed/util/bptree/bpmap.go +++ b/weed/util/bptree/bpmap.go @@ -19,7 +19,7 @@ func (self *BpMap) Has(key Hashable) bool { return (*BpTree)(self).Has(key) } -func (self *BpMap) Put(key Hashable, value interface{}) (err error) { +func (self *BpMap) Put(key Hashable, value ItemValue) (err error) { new_root, err := self.getRoot().put(key, value) if err != nil { return err @@ -28,7 +28,7 @@ func (self *BpMap) Put(key Hashable, value interface{}) (err error) { return nil } -func (self *BpMap) Get(key Hashable) (value interface{}, err error) { +func (self *BpMap) Get(key Hashable) (value ItemValue, err error) { j, l := self.getRoot().get_start(key) if l.keys[j].Equals(key) { return l.values[j], nil @@ -36,13 +36,13 @@ func (self *BpMap) Get(key Hashable) (value interface{}, err error) { return nil, fmt.Errorf("key not found: %s", key) } -func (self *BpMap) Remove(key Hashable) (value interface{}, err error) { +func (self *BpMap) Remove(key Hashable) (value ItemValue, err error) { value, err = self.Get(key) if err != nil { return nil, err } ns := self.getRoot().NodeSize() - new_root, err := self.getRoot().remove(key, func(value interface{}) bool { return true }) + new_root, err := self.getRoot().remove(key, func(value ItemValue) bool { return true }) if err != nil { return nil, err } diff --git a/weed/util/bptree/bptree.go b/weed/util/bptree/bptree.go index 12a4bfb0d..06e3f514e 100644 --- a/weed/util/bptree/bptree.go +++ b/weed/util/bptree/bptree.go @@ -40,7 +40,7 @@ func (self *BpTree) Count(key Hashable) int { return count } -func (self *BpTree) Add(key Hashable, value interface{}) (err error) { +func (self *BpTree) Add(key Hashable, value ItemValue) (err error) { new_root, err := self.getRoot().put(key, value) if err != nil { return err @@ -49,7 +49,7 @@ func (self *BpTree) Add(key Hashable, value interface{}) (err error) { return nil } -func (self *BpTree) Replace(key Hashable, where WhereFunc, value interface{}) (err error) { +func (self *BpTree) Replace(key Hashable, where WhereFunc, value ItemValue) (err error) { li := self.getRoot().forward(key, key) for i, leaf, next := li(); next != nil; i, leaf, next = next() { if where(leaf.values[i]) { @@ -70,7 +70,7 @@ func (self *BpTree) Range(from, to Hashable) (kvi KVIterator) { } else { li = self.getRoot().backward(from, to) } - kvi = func() (key Hashable, value interface{}, next KVIterator) { + kvi = func() (key Hashable, value ItemValue, next KVIterator) { var i int var leaf *BpNode i, leaf, li = li() @@ -125,7 +125,7 @@ func (self *BpTree) Items() (vi KIterator) { func (self *BpTree) Iterate() (kvi KVIterator) { li := self.getRoot().all() - kvi = func() (key Hashable, value interface{}, next KVIterator) { + kvi = func() (key Hashable, value ItemValue, next KVIterator) { var i int var leaf *BpNode i, leaf, li = li() @@ -139,7 +139,7 @@ func (self *BpTree) Iterate() (kvi KVIterator) { func (self *BpTree) Backward() (kvi KVIterator) { li := self.getRoot().all_backward() - kvi = func() (key Hashable, value interface{}, next KVIterator) { + kvi = func() (key Hashable, value ItemValue, next KVIterator) { var i int var leaf *BpNode i, leaf, li = li() diff --git a/weed/util/bptree/bptree.pb.go b/weed/util/bptree/bptree.pb.go index e7d155a36..078a54717 100644 --- a/weed/util/bptree/bptree.pb.go +++ b/weed/util/bptree/bptree.pb.go @@ -35,6 +35,7 @@ type ProtoNode struct { Pointers []int64 `protobuf:"varint,3,rep,packed,name=pointers,proto3" json:"pointers,omitempty"` Next int64 `protobuf:"varint,4,opt,name=next,proto3" json:"next,omitempty"` Prev int64 `protobuf:"varint,5,opt,name=prev,proto3" json:"prev,omitempty"` + Id int64 `protobuf:"varint,6,opt,name=id,proto3" json:"id,omitempty"` } func (x *ProtoNode) Reset() { @@ -104,22 +105,30 @@ func (x *ProtoNode) GetPrev() int64 { return 0 } +func (x *ProtoNode) GetId() int64 { + if x != nil { + return x.Id + } + return 0 +} + var File_bptree_proto protoreflect.FileDescriptor var file_bptree_proto_rawDesc = []byte{ 0x0a, 0x0c, 0x62, 0x70, 0x74, 0x72, 0x65, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x06, - 0x62, 0x70, 0x74, 0x72, 0x65, 0x65, 0x22, 0x7b, 0x0a, 0x09, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x4e, - 0x6f, 0x64, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6b, 0x65, 0x79, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, - 0x0c, 0x52, 0x04, 0x6b, 0x65, 0x79, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x76, 0x61, 0x6c, 0x75, 0x65, - 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x06, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x12, - 0x1a, 0x0a, 0x08, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, - 0x03, 0x52, 0x08, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x6e, - 0x65, 0x78, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x04, 0x6e, 0x65, 0x78, 0x74, 0x12, - 0x12, 0x0a, 0x04, 0x70, 0x72, 0x65, 0x76, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x04, 0x70, - 0x72, 0x65, 0x76, 0x42, 0x31, 0x5a, 0x2f, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, - 0x6d, 0x2f, 0x63, 0x68, 0x72, 0x69, 0x73, 0x6c, 0x75, 0x73, 0x66, 0x2f, 0x73, 0x65, 0x61, 0x77, - 0x65, 0x65, 0x64, 0x66, 0x73, 0x2f, 0x77, 0x65, 0x65, 0x64, 0x2f, 0x75, 0x74, 0x69, 0x6c, 0x2f, - 0x62, 0x70, 0x74, 0x72, 0x65, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x62, 0x70, 0x74, 0x72, 0x65, 0x65, 0x22, 0x8b, 0x01, 0x0a, 0x09, 0x50, 0x72, 0x6f, 0x74, 0x6f, + 0x4e, 0x6f, 0x64, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6b, 0x65, 0x79, 0x73, 0x18, 0x01, 0x20, 0x03, + 0x28, 0x0c, 0x52, 0x04, 0x6b, 0x65, 0x79, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x76, 0x61, 0x6c, 0x75, + 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x06, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, + 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x73, 0x18, 0x03, 0x20, 0x03, + 0x28, 0x03, 0x52, 0x08, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x73, 0x12, 0x12, 0x0a, 0x04, + 0x6e, 0x65, 0x78, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x04, 0x6e, 0x65, 0x78, 0x74, + 0x12, 0x12, 0x0a, 0x04, 0x70, 0x72, 0x65, 0x76, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x04, + 0x70, 0x72, 0x65, 0x76, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x03, + 0x52, 0x02, 0x69, 0x64, 0x42, 0x31, 0x5a, 0x2f, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, + 0x6f, 0x6d, 0x2f, 0x63, 0x68, 0x72, 0x69, 0x73, 0x6c, 0x75, 0x73, 0x66, 0x2f, 0x73, 0x65, 0x61, + 0x77, 0x65, 0x65, 0x64, 0x66, 0x73, 0x2f, 0x77, 0x65, 0x65, 0x64, 0x2f, 0x75, 0x74, 0x69, 0x6c, + 0x2f, 0x62, 0x70, 0x74, 0x72, 0x65, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/weed/util/bptree/bptree_node.go b/weed/util/bptree/bptree_node.go index 3337292ba..8fb7c8854 100644 --- a/weed/util/bptree/bptree_node.go +++ b/weed/util/bptree/bptree_node.go @@ -1,12 +1,15 @@ package bptree +type ItemValue Equatable + type BpNode struct { - keys []Hashable - values []interface{} - pointers []*BpNode - next *BpNode - prev *BpNode - no_dup bool + keys []Hashable + values []ItemValue + pointers []*BpNode + next *BpNode + prev *BpNode + no_dup bool + protoNode *ProtoNode } func NewInternal(size int) *BpNode { @@ -25,7 +28,7 @@ func NewLeaf(size int, no_dup bool) *BpNode { } return &BpNode{ keys: make([]Hashable, 0, size), - values: make([]interface{}, 0, size), + values: make([]ItemValue, 0, size), no_dup: no_dup, } } @@ -176,7 +179,7 @@ func (self *BpNode) leaf_get_start(key Hashable) (i int, leaf *BpNode) { /* This puts the k/v pair into the B+Tree rooted at this node and returns the * (possibly) new root of the tree. */ -func (self *BpNode) put(key Hashable, value interface{}) (root *BpNode, err error) { +func (self *BpNode) put(key Hashable, value ItemValue) (root *BpNode, err error) { a, b, err := self.insert(key, value) if err != nil { return nil, err @@ -194,7 +197,7 @@ func (self *BpNode) put(key Hashable, value interface{}) (root *BpNode, err erro // left is always set. When split is false left is the pointer to block // When split is true left is the pointer to the new left // block -func (self *BpNode) insert(key Hashable, value interface{}) (a, b *BpNode, err error) { +func (self *BpNode) insert(key Hashable, value ItemValue) (a, b *BpNode, err error) { if self.Internal() { return self.internal_insert(key, value) } else { // leaf node @@ -208,7 +211,7 @@ func (self *BpNode) insert(key Hashable, value interface{}) (a, b *BpNode, err e * - if the block is full, split this block * - else insert the new key/pointer into this block */ -func (self *BpNode) internal_insert(key Hashable, value interface{}) (a, b *BpNode, err error) { +func (self *BpNode) internal_insert(key Hashable, value ItemValue) (a, b *BpNode, err error) { if !self.Internal() { return nil, nil, BpTreeError("Expected a internal node") } @@ -272,7 +275,7 @@ func (self *BpNode) internal_split(key Hashable, ptr *BpNode) (a, b *BpNode, err * a pure block with a matching key) * else this leaf will get a new entry. */ -func (self *BpNode) leaf_insert(key Hashable, value interface{}) (a, b *BpNode, err error) { +func (self *BpNode) leaf_insert(key Hashable, value ItemValue) (a, b *BpNode, err error) { if self.Internal() { return nil, nil, BpTreeError("Expected a leaf node") } @@ -299,7 +302,7 @@ func (self *BpNode) leaf_insert(key Hashable, value interface{}) (a, b *BpNode, * - the two blocks will be balanced with balanced_nodes * - if the key is less than b.keys[0] it will go in a else b */ -func (self *BpNode) leaf_split(key Hashable, value interface{}) (a, b *BpNode, err error) { +func (self *BpNode) leaf_split(key Hashable, value ItemValue) (a, b *BpNode, err error) { if self.Internal() { return nil, nil, BpTreeError("Expected a leaf node") } @@ -334,7 +337,7 @@ func (self *BpNode) leaf_split(key Hashable, value interface{}) (a, b *BpNode, e * and putting the new key there. * - always return the current block as "a" and the new block as "b" */ -func (self *BpNode) pure_leaf_split(key Hashable, value interface{}) (a, b *BpNode, err error) { +func (self *BpNode) pure_leaf_split(key Hashable, value ItemValue) (a, b *BpNode, err error) { if self.Internal() || !self.Pure() { return nil, nil, BpTreeError("Expected a pure leaf node") } @@ -392,7 +395,7 @@ func (self *BpNode) put_kp(key Hashable, ptr *BpNode) error { return nil } -func (self *BpNode) put_kv(key Hashable, value interface{}) error { +func (self *BpNode) put_kv(key Hashable, value ItemValue) error { if self.Full() { return BpTreeError("Block is full.") } @@ -426,7 +429,7 @@ func (self *BpNode) put_key_at(i int, key Hashable) error { return nil } -func (self *BpNode) put_value_at(i int, value interface{}) error { +func (self *BpNode) put_value_at(i int, value ItemValue) error { if len(self.values) == cap(self.values) { return BpTreeError("Block is full.") } @@ -573,8 +576,8 @@ func (self *BpNode) remove_ptr_at(i int) error { } func (self *BpNode) find(key Hashable) (int, bool) { - var l int = 0 - var r int = len(self.keys) - 1 + var l = 0 + var r = len(self.keys) - 1 var m int for l <= r { m = ((r - l) >> 1) + l @@ -718,7 +721,7 @@ func balance_nodes(a, b *BpNode) { m-- } } - var lim int = len(a.keys) - m + var lim = len(a.keys) - m b.keys = b.keys[:lim] if cap(a.values) > 0 { if cap(a.values) != cap(a.keys) { diff --git a/weed/util/bptree/bptree_test.go b/weed/util/bptree/bptree_test.go index d1df0749e..5fdf817e9 100644 --- a/weed/util/bptree/bptree_test.go +++ b/weed/util/bptree/bptree_test.go @@ -48,7 +48,7 @@ func (self Strings) Swap(i, j int) { type record struct { key String - value String + value ItemValue } type records []*record @@ -84,7 +84,7 @@ func BenchmarkBpTree(b *testing.B) { t.Add(r.key, r.value) } for _, r := range recs { - t.RemoveWhere(r.key, func(value interface{}) bool { return true }) + t.RemoveWhere(r.key, func(value ItemValue) bool { return true }) } } } @@ -133,7 +133,7 @@ func TestAddHasCountFindIterateRemove(t *testing.T) { t.Error(bpt, "Find Failed Value Error") } } - err = bpt.Replace(r.key, func(value interface{}) bool { return true }, new_recs[i].value) + err = bpt.Replace(r.key, func(value ItemValue) bool { return true }, new_recs[i].value) if err != nil { t.Error(err) } @@ -193,7 +193,7 @@ func TestAddHasCountFindIterateRemove(t *testing.T) { if count := bpt.Count(r.key); count != 1 { t.Error(bpt, "Missing key") } - if err := bpt.RemoveWhere(r.key, func(value interface{}) bool { return true }); err != nil { + if err := bpt.RemoveWhere(r.key, func(value ItemValue) bool { return true }); err != nil { t.Fatal(bpt, err) } if has := bpt.Has(r.key); has { @@ -276,15 +276,15 @@ func TestBpMap(t *testing.T) { func Test_get_start(t *testing.T) { root := NewLeaf(2, false) - root, err := root.put(Int(1), 1) + root, err := root.put(Int(1), Int(1)) if err != nil { t.Error(err) } - root, err = root.put(Int(5), 3) + root, err = root.put(Int(5), Int(3)) if err != nil { t.Error(err) } - root, err = root.put(Int(3), 2) + root, err = root.put(Int(3), Int(2)) if err != nil { t.Error(err) } @@ -345,31 +345,31 @@ func Test_get_start(t *testing.T) { func Test_get_end(t *testing.T) { root := NewLeaf(3, false) - root, err := root.put(Int(1), -1) + root, err := root.put(Int(1), Int(-1)) if err != nil { t.Fatal(err) } - root, err = root.put(Int(4), -1) + root, err = root.put(Int(4), Int(-1)) if err != nil { t.Fatal(err) } - root, err = root.put(Int(3), 1) + root, err = root.put(Int(3), Int(1)) if err != nil { t.Fatal(err) } - root, err = root.put(Int(3), 2) + root, err = root.put(Int(3), Int(2)) if err != nil { t.Fatal(err) } - root, err = root.put(Int(3), 3) + root, err = root.put(Int(3), Int(3)) if err != nil { t.Fatal(err) } - root, err = root.put(Int(3), 4) + root, err = root.put(Int(3), Int(4)) if err != nil { t.Fatal(err) } - root, err = root.put(Int(3), 5) + root, err = root.put(Int(3), Int(5)) if err != nil { t.Fatal(err) } @@ -437,10 +437,10 @@ func Test_get_end(t *testing.T) { func Test_put_no_root_split(t *testing.T) { a := NewLeaf(2, false) - if err := a.put_kv(Int(1), 1); err != nil { + if err := a.put_kv(Int(1), Int(1)); err != nil { t.Error(err) } - p, err := a.put(Int(1), 2) + p, err := a.put(Int(1), Int(2)) if err != nil { t.Error(err) } else { @@ -451,7 +451,7 @@ func Test_put_no_root_split(t *testing.T) { t.Error("p didn't have the right keys", p) } } - p, err = a.put(Int(1), 3) + p, err = a.put(Int(1), Int(3)) if err != nil { t.Error(err) } else { @@ -471,7 +471,7 @@ func Test_put_no_root_split(t *testing.T) { func Test_put_root_split(t *testing.T) { a := NewLeaf(2, false) - p, err := a.put(Int(1), 1) + p, err := a.put(Int(1), Int(1)) if err != nil { t.Error(err) } else { @@ -482,7 +482,7 @@ func Test_put_root_split(t *testing.T) { t.Error("p didn't have the right keys", p) } } - p, err = a.put(Int(3), 3) + p, err = a.put(Int(3), Int(3)) if err != nil { t.Error(err) } else { @@ -493,7 +493,7 @@ func Test_put_root_split(t *testing.T) { t.Error("p didn't have the right keys", p) } } - p, err = a.put(Int(2), 2) + p, err = a.put(Int(2), Int(2)) if err != nil { t.Error(err) } else { @@ -521,7 +521,7 @@ func Test_put_root_split(t *testing.T) { func Test_internal_insert_no_split(t *testing.T) { a := NewInternal(3) leaf := NewLeaf(1, false) - if err := leaf.put_kv(Int(1), 1); err != nil { + if err := leaf.put_kv(Int(1), Int(1)); err != nil { t.Error(err) } if err := a.put_kp(Int(1), leaf); err != nil { @@ -549,7 +549,7 @@ func Test_internal_insert_no_split(t *testing.T) { func Test_internal_insert_split_less(t *testing.T) { a := NewInternal(3) leaf := NewLeaf(1, false) - if err := leaf.put_kv(Int(1), 1); err != nil { + if err := leaf.put_kv(Int(1), Int(1)); err != nil { t.Error(err) } if err := a.put_kp(Int(1), leaf); err != nil { @@ -660,13 +660,13 @@ func Test_internal_split_greater(t *testing.T) { func Test_leaf_insert_no_split(t *testing.T) { a := NewLeaf(3, false) insert_linked_list_node(a, nil, nil) - if err := a.put_kv(Int(1), 1); err != nil { + if err := a.put_kv(Int(1), Int(1)); err != nil { t.Error(err) } - if err := a.put_kv(Int(3), 3); err != nil { + if err := a.put_kv(Int(3), Int(3)); err != nil { t.Error(err) } - p, q, err := a.leaf_insert(Int(2), 2) + p, q, err := a.leaf_insert(Int(2), Int(2)) if err != nil { t.Error(err) } else { @@ -686,16 +686,16 @@ func Test_leaf_insert_no_split(t *testing.T) { func Test_leaf_insert_split_less(t *testing.T) { a := NewLeaf(3, false) insert_linked_list_node(a, nil, nil) - if err := a.put_kv(Int(1), 1); err != nil { + if err := a.put_kv(Int(1), Int(1)); err != nil { t.Error(err) } - if err := a.put_kv(Int(3), 3); err != nil { + if err := a.put_kv(Int(3), Int(3)); err != nil { t.Error(err) } - if err := a.put_kv(Int(5), 5); err != nil { + if err := a.put_kv(Int(5), Int(5)); err != nil { t.Error(err) } - p, q, err := a.leaf_insert(Int(2), 2) + p, q, err := a.leaf_insert(Int(2), Int(2)) if err != nil { t.Error(err) } else { @@ -717,16 +717,16 @@ func Test_leaf_insert_split_less(t *testing.T) { func Test_leaf_split_less(t *testing.T) { a := NewLeaf(3, false) insert_linked_list_node(a, nil, nil) - if err := a.put_kv(Int(1), 1); err != nil { + if err := a.put_kv(Int(1), Int(1)); err != nil { t.Error(err) } - if err := a.put_kv(Int(3), 3); err != nil { + if err := a.put_kv(Int(3), Int(3)); err != nil { t.Error(err) } - if err := a.put_kv(Int(5), 5); err != nil { + if err := a.put_kv(Int(5), Int(5)); err != nil { t.Error(err) } - p, q, err := a.leaf_split(Int(2), 2) + p, q, err := a.leaf_split(Int(2), Int(2)) if err != nil { t.Error(err) } else { @@ -748,16 +748,16 @@ func Test_leaf_split_less(t *testing.T) { func Test_leaf_split_equal(t *testing.T) { a := NewLeaf(3, false) insert_linked_list_node(a, nil, nil) - if err := a.put_kv(Int(1), 1); err != nil { + if err := a.put_kv(Int(1), Int(1)); err != nil { t.Error(err) } - if err := a.put_kv(Int(3), 3); err != nil { + if err := a.put_kv(Int(3), Int(3)); err != nil { t.Error(err) } - if err := a.put_kv(Int(5), 5); err != nil { + if err := a.put_kv(Int(5), Int(5)); err != nil { t.Error(err) } - p, q, err := a.leaf_split(Int(3), 2) + p, q, err := a.leaf_split(Int(3), Int(2)) if err != nil { t.Error(err) } else { @@ -779,16 +779,16 @@ func Test_leaf_split_equal(t *testing.T) { func Test_leaf_split_greater(t *testing.T) { a := NewLeaf(3, false) insert_linked_list_node(a, nil, nil) - if err := a.put_kv(Int(1), 1); err != nil { + if err := a.put_kv(Int(1), Int(1)); err != nil { t.Error(err) } - if err := a.put_kv(Int(3), 3); err != nil { + if err := a.put_kv(Int(3), Int(3)); err != nil { t.Error(err) } - if err := a.put_kv(Int(5), 5); err != nil { + if err := a.put_kv(Int(5), Int(5)); err != nil { t.Error(err) } - p, q, err := a.leaf_split(Int(4), 2) + p, q, err := a.leaf_split(Int(4), Int(2)) if err != nil { t.Error(err) } else { @@ -817,28 +817,28 @@ func Test_pure_leaf_insert_split_less(t *testing.T) { insert_linked_list_node(c, b, nil) d := NewLeaf(2, false) insert_linked_list_node(d, c, nil) - if err := a.put_kv(Int(3), 1); err != nil { + if err := a.put_kv(Int(3), Int(1)); err != nil { t.Error(err) } - if err := a.put_kv(Int(3), 2); err != nil { + if err := a.put_kv(Int(3), Int(2)); err != nil { t.Error(err) } - if err := b.put_kv(Int(3), 3); err != nil { + if err := b.put_kv(Int(3), Int(3)); err != nil { t.Error(err) } - if err := b.put_kv(Int(3), 4); err != nil { + if err := b.put_kv(Int(3), Int(4)); err != nil { t.Error(err) } - if err := c.put_kv(Int(3), 5); err != nil { + if err := c.put_kv(Int(3), Int(5)); err != nil { t.Error(err) } - if err := c.put_kv(Int(3), 6); err != nil { + if err := c.put_kv(Int(3), Int(6)); err != nil { t.Error(err) } - if err := d.put_kv(Int(4), 6); err != nil { + if err := d.put_kv(Int(4), Int(6)); err != nil { t.Error(err) } - p, q, err := a.leaf_insert(Int(2), 1) + p, q, err := a.leaf_insert(Int(2), Int(1)) if err != nil { t.Error(err) } else { @@ -890,28 +890,28 @@ func Test_pure_leaf_split_less(t *testing.T) { insert_linked_list_node(c, b, nil) d := NewLeaf(2, false) insert_linked_list_node(d, c, nil) - if err := a.put_kv(Int(3), 1); err != nil { + if err := a.put_kv(Int(3), Int(1)); err != nil { t.Error(err) } - if err := a.put_kv(Int(3), 2); err != nil { + if err := a.put_kv(Int(3), Int(2)); err != nil { t.Error(err) } - if err := b.put_kv(Int(3), 3); err != nil { + if err := b.put_kv(Int(3), Int(3)); err != nil { t.Error(err) } - if err := b.put_kv(Int(3), 4); err != nil { + if err := b.put_kv(Int(3), Int(4)); err != nil { t.Error(err) } - if err := c.put_kv(Int(3), 5); err != nil { + if err := c.put_kv(Int(3), Int(5)); err != nil { t.Error(err) } - if err := c.put_kv(Int(3), 6); err != nil { + if err := c.put_kv(Int(3), Int(6)); err != nil { t.Error(err) } - if err := d.put_kv(Int(4), 6); err != nil { + if err := d.put_kv(Int(4), Int(6)); err != nil { t.Error(err) } - p, q, err := a.pure_leaf_split(Int(2), 1) + p, q, err := a.pure_leaf_split(Int(2), Int(1)) if err != nil { t.Error(err) } else { @@ -963,25 +963,25 @@ func Test_pure_leaf_split_equal(t *testing.T) { insert_linked_list_node(c, b, nil) d := NewLeaf(2, false) insert_linked_list_node(d, c, nil) - if err := a.put_kv(Int(3), 1); err != nil { + if err := a.put_kv(Int(3), Int(1)); err != nil { t.Error(err) } - if err := a.put_kv(Int(3), 2); err != nil { + if err := a.put_kv(Int(3), Int(2)); err != nil { t.Error(err) } - if err := b.put_kv(Int(3), 3); err != nil { + if err := b.put_kv(Int(3), Int(3)); err != nil { t.Error(err) } - if err := b.put_kv(Int(3), 4); err != nil { + if err := b.put_kv(Int(3), Int(4)); err != nil { t.Error(err) } - if err := c.put_kv(Int(3), 5); err != nil { + if err := c.put_kv(Int(3), Int(5)); err != nil { t.Error(err) } - if err := d.put_kv(Int(4), 6); err != nil { + if err := d.put_kv(Int(4), Int(6)); err != nil { t.Error(err) } - p, q, err := a.pure_leaf_split(Int(3), 1) + p, q, err := a.pure_leaf_split(Int(3), Int(1)) if err != nil { t.Error(err) } else { @@ -1027,25 +1027,25 @@ func Test_pure_leaf_split_greater(t *testing.T) { insert_linked_list_node(c, b, nil) d := NewLeaf(2, false) insert_linked_list_node(d, c, nil) - if err := a.put_kv(Int(3), 1); err != nil { + if err := a.put_kv(Int(3), Int(1)); err != nil { t.Error(err) } - if err := a.put_kv(Int(3), 2); err != nil { + if err := a.put_kv(Int(3), Int(2)); err != nil { t.Error(err) } - if err := b.put_kv(Int(3), 3); err != nil { + if err := b.put_kv(Int(3), Int(3)); err != nil { t.Error(err) } - if err := b.put_kv(Int(3), 4); err != nil { + if err := b.put_kv(Int(3), Int(4)); err != nil { t.Error(err) } - if err := c.put_kv(Int(3), 5); err != nil { + if err := c.put_kv(Int(3), Int(5)); err != nil { t.Error(err) } - if err := d.put_kv(Int(5), 6); err != nil { + if err := d.put_kv(Int(5), Int(6)); err != nil { t.Error(err) } - p, q, err := a.pure_leaf_split(Int(4), 1) + p, q, err := a.pure_leaf_split(Int(4), Int(1)) if err != nil { t.Error(err) } else { @@ -1097,25 +1097,25 @@ func Test_find_end_of_pure_run(t *testing.T) { insert_linked_list_node(c, b, nil) d := NewLeaf(2, false) insert_linked_list_node(d, c, nil) - if err := a.put_kv(Int(3), 1); err != nil { + if err := a.put_kv(Int(3), Int(1)); err != nil { t.Error(err) } - if err := a.put_kv(Int(3), 2); err != nil { + if err := a.put_kv(Int(3), Int(2)); err != nil { t.Error(err) } - if err := b.put_kv(Int(3), 3); err != nil { + if err := b.put_kv(Int(3), Int(3)); err != nil { t.Error(err) } - if err := b.put_kv(Int(3), 4); err != nil { + if err := b.put_kv(Int(3), Int(4)); err != nil { t.Error(err) } - if err := c.put_kv(Int(3), 5); err != nil { + if err := c.put_kv(Int(3), Int(5)); err != nil { t.Error(err) } - if err := c.put_kv(Int(3), 6); err != nil { + if err := c.put_kv(Int(3), Int(6)); err != nil { t.Error(err) } - if err := d.put_kv(Int(4), 6); err != nil { + if err := d.put_kv(Int(4), Int(6)); err != nil { t.Error(err) } e := a.find_end_of_pure_run() @@ -1237,13 +1237,13 @@ func Test_remove_linked_list_node(t *testing.T) { func Test_balance_leaf_nodes_with_dup(t *testing.T) { a := NewLeaf(3, false) b := NewLeaf(3, false) - if err := a.put_kv(Int(1), 1); err != nil { + if err := a.put_kv(Int(1), Int(1)); err != nil { t.Error(err) } - if err := a.put_kv(Int(1), 1); err != nil { + if err := a.put_kv(Int(1), Int(1)); err != nil { t.Error(err) } - if err := a.put_kv(Int(2), 1); err != nil { + if err := a.put_kv(Int(2), Int(1)); err != nil { t.Error(err) } balance_nodes(a, b) @@ -1258,25 +1258,25 @@ func Test_balance_leaf_nodes_with_dup(t *testing.T) { func Test_balance_leaf_nodes(t *testing.T) { a := NewLeaf(7, false) b := NewLeaf(7, false) - if err := a.put_kv(Int(1), 1); err != nil { + if err := a.put_kv(Int(1), Int(1)); err != nil { t.Error(err) } - if err := a.put_kv(Int(2), 2); err != nil { + if err := a.put_kv(Int(2), Int(2)); err != nil { t.Error(err) } - if err := a.put_kv(Int(3), 3); err != nil { + if err := a.put_kv(Int(3), Int(3)); err != nil { t.Error(err) } - if err := a.put_kv(Int(4), 4); err != nil { + if err := a.put_kv(Int(4), Int(4)); err != nil { t.Error(err) } - if err := a.put_kv(Int(5), 5); err != nil { + if err := a.put_kv(Int(5), Int(5)); err != nil { t.Error(err) } - if err := a.put_kv(Int(6), 6); err != nil { + if err := a.put_kv(Int(6), Int(6)); err != nil { t.Error(err) } - if err := a.put_kv(Int(7), 7); err != nil { + if err := a.put_kv(Int(7), Int(7)); err != nil { t.Error(err) } balance_nodes(a, b) @@ -1291,12 +1291,12 @@ func Test_balance_leaf_nodes(t *testing.T) { } } for i, v := range a.values { - if v.(int) != i+1 { + if int(v.(Int)) != i+1 { t.Errorf("k != %d", i+1) } } for i, v := range b.values { - if v.(int) != 3+i+1 { + if int(v.(Int)) != 3+i+1 { t.Errorf("v != %d", 3+i+1) } } @@ -1413,10 +1413,14 @@ func read(p []byte, int63 func() int64, readVal *int64, readPos *int8) (n int, e type T testing.T -func (t *T) Assert(ok bool, msg string, vars ...interface{}) { +func (t *T) Assert(ok bool, msg string, vars ...ItemValue) { if !ok { t.Log("\n" + string(debug.Stack())) - t.Fatalf(msg, vars...) + var objects []interface{} + for _, t := range vars { + objects = append(objects, t) + } + t.Fatalf(msg, objects...) } } diff --git a/weed/util/bptree/types.go b/weed/util/bptree/types.go index 45084efdd..f828f7065 100644 --- a/weed/util/bptree/types.go +++ b/weed/util/bptree/types.go @@ -25,26 +25,26 @@ func NegativeSize() error { return errors.New("negative size") } -type Iterator func() (item interface{}, next Iterator) +type Iterator func() (item ItemValue, next Iterator) type KIterator func() (key Hashable, next KIterator) -type KVIterator func() (key Hashable, value interface{}, next KVIterator) +type KVIterator func() (key Hashable, value ItemValue, next KVIterator) type KVIterable interface { Iterate() KVIterator } type MapOperable interface { Has(key Hashable) bool - Put(key Hashable, value interface{}) (err error) - Get(key Hashable) (value interface{}, err error) - Remove(key Hashable) (value interface{}, err error) + Put(key Hashable, value ItemValue) (err error) + Get(key Hashable) (value ItemValue, err error) + Remove(key Hashable) (value ItemValue, err error) } -type WhereFunc func(value interface{}) bool +type WhereFunc func(value ItemValue) bool func MakeValuesIterator(obj KVIterable) Iterator { kv_iterator := obj.Iterate() var v_iterator Iterator - v_iterator = func() (value interface{}, next Iterator) { + v_iterator = func() (value ItemValue, next Iterator) { _, value, kv_iterator = kv_iterator() if kv_iterator == nil { return nil, nil @@ -58,7 +58,7 @@ func MakeItemsIterator(obj KVIterable) (kit KIterator) { kv_iterator := obj.Iterate() kit = func() (item Hashable, next KIterator) { var key Hashable - var value interface{} + var value ItemValue key, value, kv_iterator = kv_iterator() if kv_iterator == nil { return nil, nil @@ -70,7 +70,7 @@ func MakeItemsIterator(obj KVIterable) (kit KIterator) { type MapEntry struct { Key Hashable - Value interface{} + Value ItemValue } func (m *MapEntry) Equals(other Equatable) bool { From 51c8f2518f8515b51553854faf1c926f032387a1 Mon Sep 17 00:00:00 2001 From: Chris Lu Date: Sat, 21 Aug 2021 15:54:42 -0700 Subject: [PATCH 011/130] change key type to ItemKey --- weed/util/bptree/bpmap.go | 8 +++--- weed/util/bptree/bptree.go | 22 +++++++------- weed/util/bptree/bptree_node.go | 51 +++++++++++++++++---------------- weed/util/bptree/types.go | 18 ++++++------ 4 files changed, 50 insertions(+), 49 deletions(-) diff --git a/weed/util/bptree/bpmap.go b/weed/util/bptree/bpmap.go index 50bedb980..e7509b179 100644 --- a/weed/util/bptree/bpmap.go +++ b/weed/util/bptree/bpmap.go @@ -15,11 +15,11 @@ func NewBpMap(node_size int) *BpMap { } } -func (self *BpMap) Has(key Hashable) bool { +func (self *BpMap) Has(key ItemKey) bool { return (*BpTree)(self).Has(key) } -func (self *BpMap) Put(key Hashable, value ItemValue) (err error) { +func (self *BpMap) Put(key ItemKey, value ItemValue) (err error) { new_root, err := self.getRoot().put(key, value) if err != nil { return err @@ -28,7 +28,7 @@ func (self *BpMap) Put(key Hashable, value ItemValue) (err error) { return nil } -func (self *BpMap) Get(key Hashable) (value ItemValue, err error) { +func (self *BpMap) Get(key ItemKey) (value ItemValue, err error) { j, l := self.getRoot().get_start(key) if l.keys[j].Equals(key) { return l.values[j], nil @@ -36,7 +36,7 @@ func (self *BpMap) Get(key Hashable) (value ItemValue, err error) { return nil, fmt.Errorf("key not found: %s", key) } -func (self *BpMap) Remove(key Hashable) (value ItemValue, err error) { +func (self *BpMap) Remove(key ItemKey) (value ItemValue, err error) { value, err = self.Get(key) if err != nil { return nil, err diff --git a/weed/util/bptree/bptree.go b/weed/util/bptree/bptree.go index 06e3f514e..2d09026c3 100644 --- a/weed/util/bptree/bptree.go +++ b/weed/util/bptree/bptree.go @@ -18,7 +18,7 @@ func NewBpTree(node_size int) *BpTree { } } -func (self *BpTree) Has(key Hashable) bool { +func (self *BpTree) Has(key ItemKey) bool { if len(self.getRoot().keys) == 0 { return false } @@ -26,7 +26,7 @@ func (self *BpTree) Has(key Hashable) bool { return l.keys[j].Equals(key) } -func (self *BpTree) Count(key Hashable) int { +func (self *BpTree) Count(key ItemKey) int { if len(self.root.keys) == 0 { return 0 } @@ -40,7 +40,7 @@ func (self *BpTree) Count(key Hashable) int { return count } -func (self *BpTree) Add(key Hashable, value ItemValue) (err error) { +func (self *BpTree) Add(key ItemKey, value ItemValue) (err error) { new_root, err := self.getRoot().put(key, value) if err != nil { return err @@ -49,7 +49,7 @@ func (self *BpTree) Add(key Hashable, value ItemValue) (err error) { return nil } -func (self *BpTree) Replace(key Hashable, where WhereFunc, value ItemValue) (err error) { +func (self *BpTree) Replace(key ItemKey, where WhereFunc, value ItemValue) (err error) { li := self.getRoot().forward(key, key) for i, leaf, next := li(); next != nil; i, leaf, next = next() { if where(leaf.values[i]) { @@ -59,18 +59,18 @@ func (self *BpTree) Replace(key Hashable, where WhereFunc, value ItemValue) (err return nil } -func (self *BpTree) Find(key Hashable) (kvi KVIterator) { +func (self *BpTree) Find(key ItemKey) (kvi KVIterator) { return self.Range(key, key) } -func (self *BpTree) Range(from, to Hashable) (kvi KVIterator) { +func (self *BpTree) Range(from, to ItemKey) (kvi KVIterator) { var li loc_iterator if !to.Less(from) { li = self.getRoot().forward(from, to) } else { li = self.getRoot().backward(from, to) } - kvi = func() (key Hashable, value ItemValue, next KVIterator) { + kvi = func() (key ItemKey, value ItemValue, next KVIterator) { var i int var leaf *BpNode i, leaf, li = li() @@ -82,7 +82,7 @@ func (self *BpTree) Range(from, to Hashable) (kvi KVIterator) { return kvi } -func (self *BpTree) RemoveWhere(key Hashable, where WhereFunc) (err error) { +func (self *BpTree) RemoveWhere(key ItemKey, where WhereFunc) (err error) { ns := self.getRoot().NodeSize() new_root, err := self.getRoot().remove(key, where) if err != nil { @@ -99,7 +99,7 @@ func (self *BpTree) RemoveWhere(key Hashable, where WhereFunc) (err error) { func (self *BpTree) Keys() (ki KIterator) { li := self.getRoot().all() var prev Equatable - ki = func() (key Hashable, next KIterator) { + ki = func() (key ItemKey, next KIterator) { var i int var leaf *BpNode i, leaf, li = li() @@ -125,7 +125,7 @@ func (self *BpTree) Items() (vi KIterator) { func (self *BpTree) Iterate() (kvi KVIterator) { li := self.getRoot().all() - kvi = func() (key Hashable, value ItemValue, next KVIterator) { + kvi = func() (key ItemKey, value ItemValue, next KVIterator) { var i int var leaf *BpNode i, leaf, li = li() @@ -139,7 +139,7 @@ func (self *BpTree) Iterate() (kvi KVIterator) { func (self *BpTree) Backward() (kvi KVIterator) { li := self.getRoot().all_backward() - kvi = func() (key Hashable, value ItemValue, next KVIterator) { + kvi = func() (key ItemKey, value ItemValue, next KVIterator) { var i int var leaf *BpNode i, leaf, li = li() diff --git a/weed/util/bptree/bptree_node.go b/weed/util/bptree/bptree_node.go index 8fb7c8854..160dfad74 100644 --- a/weed/util/bptree/bptree_node.go +++ b/weed/util/bptree/bptree_node.go @@ -1,9 +1,10 @@ package bptree +type ItemKey Hashable type ItemValue Equatable type BpNode struct { - keys []Hashable + keys []ItemKey values []ItemValue pointers []*BpNode next *BpNode @@ -17,7 +18,7 @@ func NewInternal(size int) *BpNode { panic(NegativeSize()) } return &BpNode{ - keys: make([]Hashable, 0, size), + keys: make([]ItemKey, 0, size), pointers: make([]*BpNode, 0, size), } } @@ -27,7 +28,7 @@ func NewLeaf(size int, no_dup bool) *BpNode { panic(NegativeSize()) } return &BpNode{ - keys: make([]Hashable, 0, size), + keys: make([]ItemKey, 0, size), values: make([]ItemValue, 0, size), no_dup: no_dup, } @@ -67,7 +68,7 @@ func (self *BpNode) Height() int { return self.pointers[0].Height() + 1 } -func (self *BpNode) count(key Hashable) int { +func (self *BpNode) count(key ItemKey) int { i, _ := self.find(key) count := 0 for ; i < len(self.keys); i++ { @@ -80,7 +81,7 @@ func (self *BpNode) count(key Hashable) int { return count } -func (self *BpNode) has(key Hashable) bool { +func (self *BpNode) has(key ItemKey) bool { _, has := self.find(key) return has } @@ -103,7 +104,7 @@ func (self *BpNode) right_most_leaf() *BpNode { * the search key. (unless the search key is greater than all the keys in the * tree, in that case it will be the last key in the tree) */ -func (self *BpNode) get_start(key Hashable) (i int, leaf *BpNode) { +func (self *BpNode) get_start(key ItemKey) (i int, leaf *BpNode) { if self.Internal() { return self.internal_get_start(key) } else { @@ -140,7 +141,7 @@ func prev_location(i int, leaf *BpNode) (int, *BpNode, bool) { * than all the keys in the tree, in that case it will be the last key in the * tree) */ -func (self *BpNode) get_end(key Hashable) (i int, leaf *BpNode) { +func (self *BpNode) get_end(key ItemKey) (i int, leaf *BpNode) { end := false i, leaf = self.get_start(key) pi, pleaf := i, leaf @@ -151,7 +152,7 @@ func (self *BpNode) get_end(key Hashable) (i int, leaf *BpNode) { return pi, pleaf } -func (self *BpNode) internal_get_start(key Hashable) (i int, leaf *BpNode) { +func (self *BpNode) internal_get_start(key ItemKey) (i int, leaf *BpNode) { if !self.Internal() { panic(BpTreeError("Expected a internal node")) } @@ -165,7 +166,7 @@ func (self *BpNode) internal_get_start(key Hashable) (i int, leaf *BpNode) { return child.get_start(key) } -func (self *BpNode) leaf_get_start(key Hashable) (i int, leaf *BpNode) { +func (self *BpNode) leaf_get_start(key ItemKey) (i int, leaf *BpNode) { i, has := self.find(key) if i >= len(self.keys) && i > 0 { i = len(self.keys) - 1 @@ -179,7 +180,7 @@ func (self *BpNode) leaf_get_start(key Hashable) (i int, leaf *BpNode) { /* This puts the k/v pair into the B+Tree rooted at this node and returns the * (possibly) new root of the tree. */ -func (self *BpNode) put(key Hashable, value ItemValue) (root *BpNode, err error) { +func (self *BpNode) put(key ItemKey, value ItemValue) (root *BpNode, err error) { a, b, err := self.insert(key, value) if err != nil { return nil, err @@ -197,7 +198,7 @@ func (self *BpNode) put(key Hashable, value ItemValue) (root *BpNode, err error) // left is always set. When split is false left is the pointer to block // When split is true left is the pointer to the new left // block -func (self *BpNode) insert(key Hashable, value ItemValue) (a, b *BpNode, err error) { +func (self *BpNode) insert(key ItemKey, value ItemValue) (a, b *BpNode, err error) { if self.Internal() { return self.internal_insert(key, value) } else { // leaf node @@ -211,7 +212,7 @@ func (self *BpNode) insert(key Hashable, value ItemValue) (a, b *BpNode, err err * - if the block is full, split this block * - else insert the new key/pointer into this block */ -func (self *BpNode) internal_insert(key Hashable, value ItemValue) (a, b *BpNode, err error) { +func (self *BpNode) internal_insert(key ItemKey, value ItemValue) (a, b *BpNode, err error) { if !self.Internal() { return nil, nil, BpTreeError("Expected a internal node") } @@ -248,7 +249,7 @@ func (self *BpNode) internal_insert(key Hashable, value ItemValue) (a, b *BpNode * - balance the two blocks. * - insert the new key/pointer combo into the correct block */ -func (self *BpNode) internal_split(key Hashable, ptr *BpNode) (a, b *BpNode, err error) { +func (self *BpNode) internal_split(key ItemKey, ptr *BpNode) (a, b *BpNode, err error) { if !self.Internal() { return nil, nil, BpTreeError("Expected a internal node") } @@ -275,7 +276,7 @@ func (self *BpNode) internal_split(key Hashable, ptr *BpNode) (a, b *BpNode, err * a pure block with a matching key) * else this leaf will get a new entry. */ -func (self *BpNode) leaf_insert(key Hashable, value ItemValue) (a, b *BpNode, err error) { +func (self *BpNode) leaf_insert(key ItemKey, value ItemValue) (a, b *BpNode, err error) { if self.Internal() { return nil, nil, BpTreeError("Expected a leaf node") } @@ -302,7 +303,7 @@ func (self *BpNode) leaf_insert(key Hashable, value ItemValue) (a, b *BpNode, er * - the two blocks will be balanced with balanced_nodes * - if the key is less than b.keys[0] it will go in a else b */ -func (self *BpNode) leaf_split(key Hashable, value ItemValue) (a, b *BpNode, err error) { +func (self *BpNode) leaf_split(key ItemKey, value ItemValue) (a, b *BpNode, err error) { if self.Internal() { return nil, nil, BpTreeError("Expected a leaf node") } @@ -337,7 +338,7 @@ func (self *BpNode) leaf_split(key Hashable, value ItemValue) (a, b *BpNode, err * and putting the new key there. * - always return the current block as "a" and the new block as "b" */ -func (self *BpNode) pure_leaf_split(key Hashable, value ItemValue) (a, b *BpNode, err error) { +func (self *BpNode) pure_leaf_split(key ItemKey, value ItemValue) (a, b *BpNode, err error) { if self.Internal() || !self.Pure() { return nil, nil, BpTreeError("Expected a pure leaf node") } @@ -371,7 +372,7 @@ func (self *BpNode) pure_leaf_split(key Hashable, value ItemValue) (a, b *BpNode } } -func (self *BpNode) put_kp(key Hashable, ptr *BpNode) error { +func (self *BpNode) put_kp(key ItemKey, ptr *BpNode) error { if self.Full() { return BpTreeError("Block is full.") } @@ -395,7 +396,7 @@ func (self *BpNode) put_kp(key Hashable, ptr *BpNode) error { return nil } -func (self *BpNode) put_kv(key Hashable, value ItemValue) error { +func (self *BpNode) put_kv(key ItemKey, value ItemValue) error { if self.Full() { return BpTreeError("Block is full.") } @@ -417,7 +418,7 @@ func (self *BpNode) put_kv(key Hashable, value ItemValue) error { return nil } -func (self *BpNode) put_key_at(i int, key Hashable) error { +func (self *BpNode) put_key_at(i int, key ItemKey) error { if self.Full() { return BpTreeError("Block is full.") } @@ -459,7 +460,7 @@ func (self *BpNode) put_pointer_at(i int, pointer *BpNode) error { return nil } -func (self *BpNode) remove(key Hashable, where WhereFunc) (a *BpNode, err error) { +func (self *BpNode) remove(key ItemKey, where WhereFunc) (a *BpNode, err error) { if self.Internal() { return self.internal_remove(key, nil, where) } else { @@ -467,7 +468,7 @@ func (self *BpNode) remove(key Hashable, where WhereFunc) (a *BpNode, err error) } } -func (self *BpNode) internal_remove(key Hashable, sibling *BpNode, where WhereFunc) (a *BpNode, err error) { +func (self *BpNode) internal_remove(key ItemKey, sibling *BpNode, where WhereFunc) (a *BpNode, err error) { if !self.Internal() { panic(BpTreeError("Expected a internal node")) } @@ -512,7 +513,7 @@ func (self *BpNode) internal_remove(key Hashable, sibling *BpNode, where WhereFu return self, nil } -func (self *BpNode) leaf_remove(key, stop Hashable, where WhereFunc) (a *BpNode, err error) { +func (self *BpNode) leaf_remove(key, stop ItemKey, where WhereFunc) (a *BpNode, err error) { if self.Internal() { return nil, BpTreeError("Expected a leaf node") } @@ -575,7 +576,7 @@ func (self *BpNode) remove_ptr_at(i int) error { return nil } -func (self *BpNode) find(key Hashable) (int, bool) { +func (self *BpNode) find(key ItemKey) (int, bool) { var l = 0 var r = len(self.keys) - 1 var m int @@ -641,7 +642,7 @@ func (self *BpNode) all_backward() (li loc_iterator) { return li } -func (self *BpNode) forward(from, to Hashable) (li loc_iterator) { +func (self *BpNode) forward(from, to ItemKey) (li loc_iterator) { j, l := self.get_start(from) end := false j-- @@ -655,7 +656,7 @@ func (self *BpNode) forward(from, to Hashable) (li loc_iterator) { return li } -func (self *BpNode) backward(from, to Hashable) (li loc_iterator) { +func (self *BpNode) backward(from, to ItemKey) (li loc_iterator) { j, l := self.get_end(from) end := false li = func() (i int, leaf *BpNode, next loc_iterator) { diff --git a/weed/util/bptree/types.go b/weed/util/bptree/types.go index f828f7065..f987e0419 100644 --- a/weed/util/bptree/types.go +++ b/weed/util/bptree/types.go @@ -26,17 +26,17 @@ func NegativeSize() error { } type Iterator func() (item ItemValue, next Iterator) -type KIterator func() (key Hashable, next KIterator) -type KVIterator func() (key Hashable, value ItemValue, next KVIterator) +type KIterator func() (key ItemKey, next KIterator) +type KVIterator func() (key ItemKey, value ItemValue, next KVIterator) type KVIterable interface { Iterate() KVIterator } type MapOperable interface { - Has(key Hashable) bool - Put(key Hashable, value ItemValue) (err error) - Get(key Hashable) (value ItemValue, err error) - Remove(key Hashable) (value ItemValue, err error) + Has(key ItemKey) bool + Put(key ItemKey, value ItemValue) (err error) + Get(key ItemKey) (value ItemValue, err error) + Remove(key ItemKey) (value ItemValue, err error) } type WhereFunc func(value ItemValue) bool @@ -56,8 +56,8 @@ func MakeValuesIterator(obj KVIterable) Iterator { func MakeItemsIterator(obj KVIterable) (kit KIterator) { kv_iterator := obj.Iterate() - kit = func() (item Hashable, next KIterator) { - var key Hashable + kit = func() (item ItemKey, next KIterator) { + var key ItemKey var value ItemValue key, value, kv_iterator = kv_iterator() if kv_iterator == nil { @@ -69,7 +69,7 @@ func MakeItemsIterator(obj KVIterable) (kit KIterator) { } type MapEntry struct { - Key Hashable + Key ItemKey Value ItemValue } From df1d6133a82680d3b58c922ad02a14fc7ee017ba Mon Sep 17 00:00:00 2001 From: Chris Lu Date: Sun, 22 Aug 2021 18:19:26 -0700 Subject: [PATCH 012/130] bptree does not work well for auto-increasing keys --- weed/util/bptree/README.md | 60 ++++++++++++++++++++++++ weed/util/bptree/bpmap.go | 4 +- weed/util/bptree/bptree.go | 12 +++-- weed/util/bptree/bptree_node.go | 67 +++++++++++++++++---------- weed/util/bptree/bptree_store_test.go | 34 ++++++++++++++ weed/util/bptree/getter_setter.go | 44 ++++++++++++++++++ 6 files changed, 193 insertions(+), 28 deletions(-) create mode 100644 weed/util/bptree/README.md create mode 100644 weed/util/bptree/bptree_store_test.go diff --git a/weed/util/bptree/README.md b/weed/util/bptree/README.md new file mode 100644 index 000000000..1dddae940 --- /dev/null +++ b/weed/util/bptree/README.md @@ -0,0 +1,60 @@ +This adapts one b+ tree implementation +https://sourcegraph.com/github.com/timtadh/data-structures@master/-/tree/tree/bptree +to persist changes to on disk. + +# When a node needs to persist itself? + +* A node changed its key or value + * When an item is added. + * When an item is updated. + * When an item is deleted. + +* When a node is split. + * 2 new nodes are created (they shoud persist themselves). + * Parent node need to point to the new nodes. + +* When a node is merged. + * delete one node + * persist the merged node + + +In general, if one node is returned from a function, the node should have already been persisted. +The parent node may need to delete the old node. + +BpTree + Add(key ItemKey, value ItemValue) + new_root = self.getRoot().put(key,value) + a, b, err := self.insert(key, value) + self.internal_insert(key, value) + self.internal_split(q.keys[0], q) + persist(a,b) + self.persist() // child add q node + self.maybePersist(child == p) + self.leaf_insert(key, value) + self.persist() // if dedup + self.leaf_split(key, value) + self.pure_leaf_split(key, value) + persist(a,b) + a.persist() + persist(a,b) + self.put_kv(key, value) + new_root.persist() + self.setRoot(new_root) + oldroot.destroy() + // maybe persist BpTree new root + + Replace(key ItemKey, where WhereFunc, value ItemValue) + leaf.persist() + RemoveWhere(key ItemKey, where WhereFunc) + self.getRoot().remove(key, where) + self.internal_remove(key, nil, where) + child.leaf_remove(key, nil, where) + child.leaf_remove(key, sibling.keys[0], where) + l.destroy() // when the node is empty + a.maybePersist(hasChange) + self.destroy() // when no keys left + self.persist() // when some keys are left + self.leaf_remove(key, self.keys[len(self.keys)-1], where) + new_root.persist() // when new root is added + // maybe persist BpTree new root + \ No newline at end of file diff --git a/weed/util/bptree/bpmap.go b/weed/util/bptree/bpmap.go index e7509b179..cbf363c95 100644 --- a/weed/util/bptree/bpmap.go +++ b/weed/util/bptree/bpmap.go @@ -47,7 +47,9 @@ func (self *BpMap) Remove(key ItemKey) (value ItemValue, err error) { return nil, err } if new_root == nil { - self.setRoot(NewLeaf(ns, true)) + new_root = NewLeaf(ns, false) + err = new_root.persist() + self.setRoot(new_root) } else { self.setRoot(new_root) } diff --git a/weed/util/bptree/bptree.go b/weed/util/bptree/bptree.go index 2d09026c3..f9a5cf058 100644 --- a/weed/util/bptree/bptree.go +++ b/weed/util/bptree/bptree.go @@ -54,9 +54,13 @@ func (self *BpTree) Replace(key ItemKey, where WhereFunc, value ItemValue) (err for i, leaf, next := li(); next != nil; i, leaf, next = next() { if where(leaf.values[i]) { leaf.values[i] = value + if persistErr := leaf.persist(); persistErr != nil && err == nil { + err = persistErr + break + } } } - return nil + return err } func (self *BpTree) Find(key ItemKey) (kvi KVIterator) { @@ -89,11 +93,13 @@ func (self *BpTree) RemoveWhere(key ItemKey, where WhereFunc) (err error) { return err } if new_root == nil { - self.setRoot(NewLeaf(ns, false)) + new_root = NewLeaf(ns, false) + err = new_root.persist() + self.setRoot(new_root) } else { self.setRoot(new_root) } - return nil + return err } func (self *BpTree) Keys() (ki KIterator) { diff --git a/weed/util/bptree/bptree_node.go b/weed/util/bptree/bptree_node.go index 160dfad74..4e6d63ac6 100644 --- a/weed/util/bptree/bptree_node.go +++ b/weed/util/bptree/bptree_node.go @@ -2,15 +2,23 @@ package bptree type ItemKey Hashable type ItemValue Equatable +type PersistFunc func(node *BpNode) error +type DestroyFunc func(node *BpNode) error + +var ( + PersistFn PersistFunc + DestroyFn DestroyFunc +) type BpNode struct { - keys []ItemKey - values []ItemValue - pointers []*BpNode - next *BpNode - prev *BpNode - no_dup bool - protoNode *ProtoNode + keys []ItemKey + values []ItemValue + pointers []*BpNode + next *BpNode + prev *BpNode + no_dup bool + protoNodeId int64 + protoNode *ProtoNode } func NewInternal(size int) *BpNode { @@ -18,8 +26,9 @@ func NewInternal(size int) *BpNode { panic(NegativeSize()) } return &BpNode{ - keys: make([]ItemKey, 0, size), - pointers: make([]*BpNode, 0, size), + keys: make([]ItemKey, 0, size), + pointers: make([]*BpNode, 0, size), + protoNodeId: GetProtoNodeId(), } } @@ -28,9 +37,10 @@ func NewLeaf(size int, no_dup bool) *BpNode { panic(NegativeSize()) } return &BpNode{ - keys: make([]ItemKey, 0, size), - values: make([]ItemValue, 0, size), - no_dup: no_dup, + keys: make([]ItemKey, 0, size), + values: make([]ItemValue, 0, size), + no_dup: no_dup, + protoNodeId: GetProtoNodeId(), } } @@ -191,7 +201,7 @@ func (self *BpNode) put(key ItemKey, value ItemValue) (root *BpNode, err error) root = NewInternal(self.NodeSize()) root.put_kp(a.keys[0], a) root.put_kp(b.keys[0], b) - return root, nil + return root, root.persist() } // right is only set on split @@ -237,10 +247,10 @@ func (self *BpNode) internal_insert(key ItemKey, value ItemValue) (a, b *BpNode, if err := self.put_kp(q.keys[0], q); err != nil { return nil, nil, err } - return self, nil, nil + return self, nil, self.persist() } } - return self, nil, nil + return self, nil, self.maybePersist(child != p) } /* On split @@ -268,7 +278,7 @@ func (self *BpNode) internal_split(key ItemKey, ptr *BpNode) (a, b *BpNode, err return nil, nil, err } } - return a, b, nil + return a, b, persist(a, b) } /* if the leaf is full then it will defer to a leaf_split @@ -284,7 +294,7 @@ func (self *BpNode) leaf_insert(key ItemKey, value ItemValue) (a, b *BpNode, err i, has := self.find(key) if has { self.values[i] = value - return self, nil, nil + return self, nil, self.persist() } } if self.Full() { @@ -293,7 +303,7 @@ func (self *BpNode) leaf_insert(key ItemKey, value ItemValue) (a, b *BpNode, err if err := self.put_kv(key, value); err != nil { return nil, nil, err } - return self, nil, nil + return self, nil, self.persist() } } @@ -323,7 +333,7 @@ func (self *BpNode) leaf_split(key ItemKey, value ItemValue) (a, b *BpNode, err return nil, nil, err } } - return a, b, nil + return a, b, persist(a, b) } /* a pure leaf split has two cases: @@ -349,7 +359,7 @@ func (self *BpNode) pure_leaf_split(key ItemKey, value ItemValue) (a, b *BpNode, return nil, nil, err } insert_linked_list_node(a, b.getPrev(), b) - return a, b, nil + return a, b, persist(a, b) } else { a = self e := self.find_end_of_pure_run() @@ -357,7 +367,7 @@ func (self *BpNode) pure_leaf_split(key ItemKey, value ItemValue) (a, b *BpNode, if err := e.put_kv(key, value); err != nil { return nil, nil, err } - return a, nil, nil + return a, nil, a.persist() } else { b = NewLeaf(self.NodeSize(), self.no_dup) if err := b.put_kv(key, value); err != nil { @@ -367,7 +377,7 @@ func (self *BpNode) pure_leaf_split(key ItemKey, value ItemValue) (a, b *BpNode, if e.keys[0].Equals(key) { return a, nil, nil } - return a, b, nil + return a, b, persist(a, b) } } } @@ -484,6 +494,7 @@ func (self *BpNode) internal_remove(key ItemKey, sibling *BpNode, where WhereFun sibling = sibling.left_most_leaf() } child := self.pointers[i] + oldChild := child if child.Internal() { child, err = child.internal_remove(key, sibling, where) } else { @@ -508,9 +519,9 @@ func (self *BpNode) internal_remove(key ItemKey, sibling *BpNode, where WhereFun self.pointers[i] = child } if len(self.keys) == 0 { - return nil, nil + return nil, self.destroy() } - return self, nil + return self, self.maybePersist(oldChild != child) } func (self *BpNode) leaf_remove(key, stop ItemKey, where WhereFunc) (a *BpNode, err error) { @@ -518,8 +529,10 @@ func (self *BpNode) leaf_remove(key, stop ItemKey, where WhereFunc) (a *BpNode, return nil, BpTreeError("Expected a leaf node") } a = self + hasChange := false for j, l, next := self.forward(key, key)(); next != nil; j, l, next = next() { if where(l.values[j]) { + hasChange = true if err := l.remove_key_at(j); err != nil { return nil, err } @@ -538,8 +551,14 @@ func (self *BpNode) leaf_remove(key, stop ItemKey, where WhereFunc) (a *BpNode, } else { a = nil } + if err := l.destroy(); err != nil { + return nil, err + } } } + if a != nil { + return a, a.maybePersist(hasChange) + } return a, nil } diff --git a/weed/util/bptree/bptree_store_test.go b/weed/util/bptree/bptree_store_test.go new file mode 100644 index 000000000..a5e330aa9 --- /dev/null +++ b/weed/util/bptree/bptree_store_test.go @@ -0,0 +1,34 @@ +package bptree + +import ( + "fmt" + "testing" +) + +func TestAddRemove(t *testing.T) { + tree := NewBpTree(32) + PersistFn = func(node *BpNode) error { + println("saving", node.protoNodeId) + return nil + } + DestroyFn = func(node *BpNode) error { + println("delete", node.protoNodeId) + return nil + } + for i:=0;i<1024;i++{ + println("++++++++++", i) + tree.Add(String(fmt.Sprintf("%02d", i)), String(fmt.Sprintf("%02d", i))) + printTree(tree.root, "") + } +} + +func printTree(node *BpNode, prefix string) { + fmt.Printf("%sNode %d\n", prefix, node.protoNodeId) + prefix += " " + for i:=0;i Date: Sun, 12 Sep 2021 02:19:10 -0700 Subject: [PATCH 013/130] custom grpc port: master --- weed/command/master.go | 4 +++- weed/command/server.go | 1 + 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/weed/command/master.go b/weed/command/master.go index 2605f6f4b..bf7a5d420 100644 --- a/weed/command/master.go +++ b/weed/command/master.go @@ -27,6 +27,7 @@ var ( type MasterOptions struct { port *int + portGrpc *int ip *string ipBind *string metaFolder *string @@ -46,6 +47,7 @@ type MasterOptions struct { func init() { cmdMaster.Run = runMaster // break init cycle m.port = cmdMaster.Flag.Int("port", 9333, "http listen port") + m.portGrpc = cmdMaster.Flag.Int("port.grpc", 19333, "grpc listen port") m.ip = cmdMaster.Flag.String("ip", util.DetectedHostAddress(), "master | address, also used as identifier") m.ipBind = cmdMaster.Flag.String("ip.bind", "", "ip address to bind to") m.metaFolder = cmdMaster.Flag.String("mdir", os.TempDir(), "data directory to store meta data") @@ -130,7 +132,7 @@ func startMaster(masterOption MasterOptions, masterWhiteList []string) { ms.SetRaftServer(raftServer) r.HandleFunc("/cluster/status", raftServer.StatusHandler).Methods("GET") // starting grpc server - grpcPort := *masterOption.port + 10000 + grpcPort := *masterOption.portGrpc grpcL, err := util.NewListener(util.JoinHostPort(*masterOption.ipBind, grpcPort), 0) if err != nil { glog.Fatalf("master failed to listen on grpc port %d: %v", grpcPort, err) diff --git a/weed/command/server.go b/weed/command/server.go index b32d9d51e..0e42227f0 100644 --- a/weed/command/server.go +++ b/weed/command/server.go @@ -85,6 +85,7 @@ func init() { serverOptions.debugPort = cmdServer.Flag.Int("debug.port", 6060, "http port for debugging") masterOptions.port = cmdServer.Flag.Int("master.port", 9333, "master server http listen port") + masterOptions.portGrpc = cmdServer.Flag.Int("master.port.grpc", 19333, "master server grpc listen port") masterOptions.metaFolder = cmdServer.Flag.String("master.dir", "", "data directory to store meta data, default to same as -dir specified") masterOptions.peers = cmdServer.Flag.String("master.peers", "", "all master nodes in comma separated ip:masterPort list") masterOptions.volumeSizeLimitMB = cmdServer.Flag.Uint("master.volumeSizeLimitMB", 30*1000, "Master stops directing writes to oversized volumes.") From 232ad2fe65b48e65ce5e4431aaadbd925d9c1a59 Mon Sep 17 00:00:00 2001 From: Chris Lu Date: Sun, 12 Sep 2021 02:25:00 -0700 Subject: [PATCH 014/130] custom grpc port: master follower --- weed/command/master_follower.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/weed/command/master_follower.go b/weed/command/master_follower.go index 48548a389..cf7e45253 100644 --- a/weed/command/master_follower.go +++ b/weed/command/master_follower.go @@ -24,6 +24,7 @@ var ( func init() { cmdMasterFollower.Run = runMasterFollower // break init cycle mf.port = cmdMasterFollower.Flag.Int("port", 9334, "http listen port") + mf.portGrpc = cmdMasterFollower.Flag.Int("port.grpc", 19334, "grpc listen port") mf.ipBind = cmdMasterFollower.Flag.String("ip.bind", "", "ip address to bind to") mf.peers = cmdMasterFollower.Flag.String("masters", "localhost:9333", "all master nodes in comma separated ip:port list, example: 127.0.0.1:9093,127.0.0.1:9094,127.0.0.1:9095") @@ -121,7 +122,7 @@ func startMasterFollower(masterOptions MasterOptions) { } // starting grpc server - grpcPort := *masterOptions.port + 10000 + grpcPort := *masterOptions.portGrpc grpcL, err := util.NewListener(util.JoinHostPort(*masterOptions.ipBind, grpcPort), 0) if err != nil { glog.Fatalf("master failed to listen on grpc port %d: %v", grpcPort, err) From e690a2be161a3760595e98032e38ce41804a052c Mon Sep 17 00:00:00 2001 From: Chris Lu Date: Sun, 12 Sep 2021 02:25:15 -0700 Subject: [PATCH 015/130] custom grpc port: volume server --- weed/command/server.go | 1 + weed/command/volume.go | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/weed/command/server.go b/weed/command/server.go index 0e42227f0..fa7b2e2eb 100644 --- a/weed/command/server.go +++ b/weed/command/server.go @@ -109,6 +109,7 @@ func init() { filerOptions.concurrentUploadLimitMB = cmdServer.Flag.Int("filer.concurrentUploadLimitMB", 64, "limit total concurrent upload size") serverOptions.v.port = cmdServer.Flag.Int("volume.port", 8080, "volume server http listen port") + serverOptions.v.portGrpc = cmdServer.Flag.Int("volume.port.grpc", 18080, "volume server grpc listen port") serverOptions.v.publicPort = cmdServer.Flag.Int("volume.port.public", 0, "volume server public port") serverOptions.v.indexType = cmdServer.Flag.String("volume.index", "memory", "Choose [memory|leveldb|leveldbMedium|leveldbLarge] mode for memory~performance balance.") serverOptions.v.diskType = cmdServer.Flag.String("volume.disk", "", "[hdd|ssd|] hard drive or solid state drive or any tag") diff --git a/weed/command/volume.go b/weed/command/volume.go index 3278107f5..f5ec11724 100644 --- a/weed/command/volume.go +++ b/weed/command/volume.go @@ -36,6 +36,7 @@ var ( type VolumeServerOptions struct { port *int + portGrpc *int publicPort *int folders []string folderMaxLimits []int @@ -68,6 +69,7 @@ type VolumeServerOptions struct { func init() { cmdVolume.Run = runVolume // break init cycle v.port = cmdVolume.Flag.Int("port", 8080, "http listen port") + v.portGrpc = cmdVolume.Flag.Int("port.grpc", 18080, "grpc listen port") v.publicPort = cmdVolume.Flag.Int("port.public", 0, "port opened to public") v.ip = cmdVolume.Flag.String("ip", util.DetectedHostAddress(), "ip or server name, also used as identifier") v.publicUrl = cmdVolume.Flag.String("publicUrl", "", "Publicly accessible address") @@ -307,7 +309,7 @@ func (v VolumeServerOptions) isSeparatedPublicPort() bool { } func (v VolumeServerOptions) startGrpcService(vs volume_server_pb.VolumeServerServer) *grpc.Server { - grpcPort := *v.port + 10000 + grpcPort := *v.portGrpc grpcL, err := util.NewListener(util.JoinHostPort(*v.bindIp, grpcPort), 0) if err != nil { glog.Fatalf("failed to listen on grpc port %d: %v", grpcPort, err) From 2c9d4c8f43c1e95c75fc332ca83d19e33e5da3ac Mon Sep 17 00:00:00 2001 From: Chris Lu Date: Sun, 12 Sep 2021 02:28:37 -0700 Subject: [PATCH 016/130] custom grpc port: filer --- weed/command/filer.go | 4 +++- weed/command/server.go | 1 + 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/weed/command/filer.go b/weed/command/filer.go index 9a27978be..63dd53f9e 100644 --- a/weed/command/filer.go +++ b/weed/command/filer.go @@ -33,6 +33,7 @@ type FilerOptions struct { ip *string bindIp *string port *int + portGrpc *int publicPort *int collection *string defaultReplicaPlacement *string @@ -60,6 +61,7 @@ func init() { f.ip = cmdFiler.Flag.String("ip", util.DetectedHostAddress(), "filer server http listen ip address") f.bindIp = cmdFiler.Flag.String("ip.bind", "", "ip address to bind to") f.port = cmdFiler.Flag.Int("port", 8888, "filer server http listen port") + f.portGrpc = cmdFiler.Flag.Int("port.grpc", 18888, "filer server grpc listen port") f.publicPort = cmdFiler.Flag.Int("port.readonly", 0, "readonly port opened to public") f.defaultReplicaPlacement = cmdFiler.Flag.String("defaultReplicaPlacement", "", "default replication type. If not specified, use master setting.") f.disableDirListing = cmdFiler.Flag.Bool("disableDirListing", false, "turn off directory listing") @@ -229,7 +231,7 @@ func (fo *FilerOptions) startFiler() { } // starting grpc server - grpcPort := *fo.port + 10000 + grpcPort := *fo.portGrpc grpcL, err := util.NewListener(util.JoinHostPort(*fo.bindIp, grpcPort), 0) if err != nil { glog.Fatalf("failed to listen on grpc port %d: %v", grpcPort, err) diff --git a/weed/command/server.go b/weed/command/server.go index fa7b2e2eb..b45ea8f4a 100644 --- a/weed/command/server.go +++ b/weed/command/server.go @@ -98,6 +98,7 @@ func init() { filerOptions.collection = cmdServer.Flag.String("filer.collection", "", "all data will be stored in this collection") filerOptions.port = cmdServer.Flag.Int("filer.port", 8888, "filer server http listen port") + filerOptions.portGrpc = cmdServer.Flag.Int("filer.port.grpc", 18888, "filer server grpc listen port") filerOptions.publicPort = cmdServer.Flag.Int("filer.port.public", 0, "filer server public http listen port") filerOptions.defaultReplicaPlacement = cmdServer.Flag.String("filer.defaultReplicaPlacement", "", "default replication type. If not specified, use master setting.") filerOptions.disableDirListing = cmdServer.Flag.Bool("filer.disableDirListing", false, "turn off directory listing") From e5fc35ed0c970fea060a5b3b7a3f5efb5af425d6 Mon Sep 17 00:00:00 2001 From: Chris Lu Date: Sun, 12 Sep 2021 22:47:52 -0700 Subject: [PATCH 017/130] change server address from string to a type --- .../java/seaweedfs/client/FilerClient.java | 6 +- .../seaweedfs/client/FilerGrpcClient.java | 8 +- other/java/client/src/main/proto/filer.proto | 4 +- .../java/seaweed/hdfs/SeaweedFileSystem.java | 8 +- .../seaweed/hdfs/SeaweedFileSystemStore.java | 5 +- .../java/seaweed/hdfs/SeaweedFileSystem.java | 8 +- .../seaweed/hdfs/SeaweedFileSystemStore.java | 5 +- .../diff_volume_servers.go | 19 +- .../load_test_meta_tail.go | 4 +- .../repeated_vacuum/repeated_vacuum.go | 5 +- unmaintained/volume_tailer/volume_tailer.go | 3 +- weed/command/backup.go | 5 +- weed/command/benchmark.go | 4 +- weed/command/download.go | 3 +- weed/command/filer.go | 18 +- weed/command/filer_backup.go | 4 +- weed/command/filer_cat.go | 4 +- weed/command/filer_copy.go | 67 +- weed/command/filer_meta_backup.go | 4 +- weed/command/filer_meta_tail.go | 2 +- weed/command/filer_remote_sync.go | 8 +- weed/command/filer_remote_sync_buckets.go | 6 +- weed/command/filer_remote_sync_dir.go | 8 +- weed/command/filer_sync.go | 18 +- weed/command/iam.go | 23 +- weed/command/master.go | 24 +- weed/command/master_follower.go | 17 +- weed/command/mount_std.go | 20 +- weed/command/msg_broker.go | 16 +- weed/command/s3.go | 17 +- weed/command/server.go | 14 +- weed/command/shell.go | 9 +- weed/command/upload.go | 8 +- weed/command/volume.go | 14 +- weed/command/webdav.go | 17 +- weed/filer/filer.go | 11 +- weed/filer/meta_aggregator.go | 16 +- weed/filer/remote_mapping.go | 2 +- weed/filer/remote_storage.go | 4 +- weed/filesys/wfs.go | 10 +- weed/filesys/wfs_filer_client.go | 4 +- weed/filesys/wfs_write.go | 5 +- weed/iamapi/iamapi_server.go | 16 +- weed/messaging/broker/broker_append.go | 5 +- .../broker/broker_grpc_server_discovery.go | 11 +- weed/messaging/broker/broker_server.go | 6 +- weed/operation/assign_file_id.go | 29 +- weed/operation/chunked_file.go | 7 +- weed/operation/delete_content.go | 16 +- weed/operation/grpc_client.go | 49 +- weed/operation/lookup.go | 8 + weed/operation/submit.go | 3 +- weed/operation/sync_volume.go | 3 +- weed/operation/tail_volume.go | 5 +- weed/pb/filer.proto | 4 +- weed/pb/filer_pb/filer.pb.go | 761 +++++----- weed/pb/filer_pb_tail.go | 2 +- weed/pb/grpc_client_server.go | 49 +- weed/pb/master.proto | 15 +- weed/pb/master_pb/master.pb.go | 1313 ++++++++--------- weed/pb/server_address.go | 130 ++ weed/pb/volume_server.proto | 1 + weed/pb/volume_server_pb/volume_server.pb.go | 770 +++++----- weed/remote_storage/track_sync_offset.go | 4 +- weed/replication/replicator.go | 2 +- .../replication/sink/filersink/fetch_write.go | 2 +- weed/s3api/filer_multipart.go | 2 +- weed/s3api/s3api_handlers.go | 2 +- weed/s3api/s3api_object_copy_handlers.go | 8 +- weed/s3api/s3api_object_handlers.go | 10 +- .../s3api/s3api_object_handlers_postpolicy.go | 2 +- weed/s3api/s3api_object_multipart_handlers.go | 2 +- weed/s3api/s3api_server.go | 4 +- weed/server/filer_grpc_server.go | 15 +- weed/server/filer_grpc_server_remote.go | 5 +- weed/server/filer_server.go | 20 +- weed/server/master_grpc_server.go | 15 +- weed/server/master_grpc_server_collection.go | 2 +- weed/server/master_grpc_server_volume.go | 12 +- weed/server/master_server.go | 15 +- weed/server/master_server_handlers_admin.go | 13 +- weed/server/raft_server.go | 24 +- weed/server/raft_server_handlers.go | 9 +- weed/server/volume_grpc_client_to_master.go | 45 +- weed/server/volume_grpc_copy.go | 3 +- weed/server/volume_grpc_erasure_coding.go | 3 +- weed/server/volume_grpc_tail.go | 3 +- weed/server/volume_server.go | 11 +- weed/server/volume_server_handlers_ui.go | 3 +- weed/server/webdav_server.go | 9 +- weed/shell/command_ec_balance.go | 5 +- weed/shell/command_ec_common.go | 26 +- weed/shell/command_ec_decode.go | 15 +- weed/shell/command_ec_encode.go | 13 +- weed/shell/command_ec_rebuild.go | 11 +- weed/shell/command_fs_meta_save.go | 6 +- weed/shell/command_s3_clean_uploads.go | 2 +- weed/shell/command_volume_balance.go | 3 +- weed/shell/command_volume_check_disk.go | 17 +- .../command_volume_configure_replication.go | 3 +- weed/shell/command_volume_copy.go | 3 +- weed/shell/command_volume_delete.go | 3 +- weed/shell/command_volume_delete_empty.go | 3 +- weed/shell/command_volume_fix_replication.go | 7 +- weed/shell/command_volume_fsck.go | 11 +- weed/shell/command_volume_mark.go | 3 +- weed/shell/command_volume_mount.go | 5 +- weed/shell/command_volume_move.go | 19 +- weed/shell/command_volume_server_leave.go | 5 +- weed/shell/command_volume_tier_download.go | 5 +- weed/shell/command_volume_tier_move.go | 26 +- weed/shell/command_volume_tier_upload.go | 5 +- weed/shell/command_volume_unmount.go | 5 +- weed/shell/commands.go | 7 +- weed/storage/erasure_coding/ec_volume.go | 5 +- weed/storage/store.go | 11 +- weed/storage/store_ec.go | 9 +- weed/storage/store_ec_delete.go | 3 +- weed/storage/volume_backup.go | 3 +- weed/topology/allocate_volume.go | 2 +- weed/topology/data_node.go | 7 + weed/topology/rack.go | 3 +- weed/topology/topology.go | 9 +- weed/topology/topology_ec.go | 7 +- weed/topology/topology_test.go | 4 +- weed/topology/topology_vacuum.go | 13 +- weed/wdclient/masterclient.go | 25 +- weed/wdclient/vid_map.go | 6 + 128 files changed, 2138 insertions(+), 2082 deletions(-) create mode 100644 weed/pb/server_address.go diff --git a/other/java/client/src/main/java/seaweedfs/client/FilerClient.java b/other/java/client/src/main/java/seaweedfs/client/FilerClient.java index e962cbbcc..84ee66e7d 100644 --- a/other/java/client/src/main/java/seaweedfs/client/FilerClient.java +++ b/other/java/client/src/main/java/seaweedfs/client/FilerClient.java @@ -14,7 +14,11 @@ public class FilerClient extends FilerGrpcClient { private static final Logger LOG = LoggerFactory.getLogger(FilerClient.class); public FilerClient(String host, int grpcPort) { - super(host, grpcPort); + super(host, grpcPort-10000, grpcPort); + } + + public FilerClient(String host, int port, int grpcPort) { + super(host, port, grpcPort); } public static String toFileId(FilerProto.FileId fid) { diff --git a/other/java/client/src/main/java/seaweedfs/client/FilerGrpcClient.java b/other/java/client/src/main/java/seaweedfs/client/FilerGrpcClient.java index 56aa35876..54e7ccb68 100644 --- a/other/java/client/src/main/java/seaweedfs/client/FilerGrpcClient.java +++ b/other/java/client/src/main/java/seaweedfs/client/FilerGrpcClient.java @@ -40,11 +40,11 @@ public class FilerGrpcClient { private int volumeServerAccess = VOLUME_SERVER_ACCESS_DIRECT; private String filerAddress; - public FilerGrpcClient(String host, int grpcPort) { - this(host, grpcPort, sslContext); + public FilerGrpcClient(String host, int port, int grpcPort) { + this(host, port, grpcPort, sslContext); } - public FilerGrpcClient(String host, int grpcPort, SslContext sslContext) { + public FilerGrpcClient(String host, int port, int grpcPort, SslContext sslContext) { this(sslContext == null ? ManagedChannelBuilder.forAddress(host, grpcPort).usePlaintext() @@ -54,7 +54,7 @@ public class FilerGrpcClient { .negotiationType(NegotiationType.TLS) .sslContext(sslContext)); - filerAddress = SeaweedUtil.joinHostPort(host, grpcPort - 10000); + filerAddress = SeaweedUtil.joinHostPort(host, port); FilerProto.GetFilerConfigurationResponse filerConfigurationResponse = this.getBlockingStub().getFilerConfiguration( diff --git a/other/java/client/src/main/proto/filer.proto b/other/java/client/src/main/proto/filer.proto index 7b1838565..bb4b6cc15 100644 --- a/other/java/client/src/main/proto/filer.proto +++ b/other/java/client/src/main/proto/filer.proto @@ -238,13 +238,12 @@ message AssignVolumeRequest { message AssignVolumeResponse { string file_id = 1; - string url = 2; - string public_url = 3; int32 count = 4; string auth = 5; string collection = 6; string replication = 7; string error = 8; + Location location = 9; } message LookupVolumeRequest { @@ -258,6 +257,7 @@ message Locations { message Location { string url = 1; string public_url = 2; + uint32 grpc_port = 3; } message LookupVolumeResponse { map locations_map = 1; diff --git a/other/java/hdfs2/src/main/java/seaweed/hdfs/SeaweedFileSystem.java b/other/java/hdfs2/src/main/java/seaweed/hdfs/SeaweedFileSystem.java index 25395db7a..b6ea4c3bb 100644 --- a/other/java/hdfs2/src/main/java/seaweed/hdfs/SeaweedFileSystem.java +++ b/other/java/hdfs2/src/main/java/seaweed/hdfs/SeaweedFileSystem.java @@ -23,6 +23,7 @@ public class SeaweedFileSystem extends FileSystem { public static final String FS_SEAWEED_FILER_HOST = "fs.seaweed.filer.host"; public static final String FS_SEAWEED_FILER_PORT = "fs.seaweed.filer.port"; + public static final String FS_SEAWEED_FILER_PORT_GRPC = "fs.seaweed.filer.port.grpc"; public static final int FS_SEAWEED_DEFAULT_PORT = 8888; public static final String FS_SEAWEED_BUFFER_SIZE = "fs.seaweed.buffer.size"; public static final String FS_SEAWEED_REPLICATION = "fs.seaweed.replication"; @@ -50,9 +51,6 @@ public class SeaweedFileSystem extends FileSystem { // get host information from uri (overrides info in conf) String host = uri.getHost(); host = (host == null) ? conf.get(FS_SEAWEED_FILER_HOST, "localhost") : host; - if (host == null) { - throw new IOException("Invalid host specified"); - } conf.set(FS_SEAWEED_FILER_HOST, host); // get port information from uri, (overrides info in conf) @@ -60,10 +58,12 @@ public class SeaweedFileSystem extends FileSystem { port = (port == -1) ? FS_SEAWEED_DEFAULT_PORT : port; conf.setInt(FS_SEAWEED_FILER_PORT, port); + int grpcPort = conf.getInt(FS_SEAWEED_FILER_PORT_GRPC, port+10000); + setConf(conf); this.uri = uri; - seaweedFileSystemStore = new SeaweedFileSystemStore(host, port, conf); + seaweedFileSystemStore = new SeaweedFileSystemStore(host, port, grpcPort, conf); } diff --git a/other/java/hdfs2/src/main/java/seaweed/hdfs/SeaweedFileSystemStore.java b/other/java/hdfs2/src/main/java/seaweed/hdfs/SeaweedFileSystemStore.java index f4e8c9349..2ba8e1a10 100644 --- a/other/java/hdfs2/src/main/java/seaweed/hdfs/SeaweedFileSystemStore.java +++ b/other/java/hdfs2/src/main/java/seaweed/hdfs/SeaweedFileSystemStore.java @@ -27,9 +27,8 @@ public class SeaweedFileSystemStore { private FilerClient filerClient; private Configuration conf; - public SeaweedFileSystemStore(String host, int port, Configuration conf) { - int grpcPort = 10000 + port; - filerClient = new FilerClient(host, grpcPort); + public SeaweedFileSystemStore(String host, int port, int grpcPort, Configuration conf) { + filerClient = new FilerClient(host, port, grpcPort); this.conf = conf; String volumeServerAccessMode = this.conf.get(FS_SEAWEED_VOLUME_SERVER_ACCESS, "direct"); if (volumeServerAccessMode.equals("publicUrl")) { diff --git a/other/java/hdfs3/src/main/java/seaweed/hdfs/SeaweedFileSystem.java b/other/java/hdfs3/src/main/java/seaweed/hdfs/SeaweedFileSystem.java index 25395db7a..b6ea4c3bb 100644 --- a/other/java/hdfs3/src/main/java/seaweed/hdfs/SeaweedFileSystem.java +++ b/other/java/hdfs3/src/main/java/seaweed/hdfs/SeaweedFileSystem.java @@ -23,6 +23,7 @@ public class SeaweedFileSystem extends FileSystem { public static final String FS_SEAWEED_FILER_HOST = "fs.seaweed.filer.host"; public static final String FS_SEAWEED_FILER_PORT = "fs.seaweed.filer.port"; + public static final String FS_SEAWEED_FILER_PORT_GRPC = "fs.seaweed.filer.port.grpc"; public static final int FS_SEAWEED_DEFAULT_PORT = 8888; public static final String FS_SEAWEED_BUFFER_SIZE = "fs.seaweed.buffer.size"; public static final String FS_SEAWEED_REPLICATION = "fs.seaweed.replication"; @@ -50,9 +51,6 @@ public class SeaweedFileSystem extends FileSystem { // get host information from uri (overrides info in conf) String host = uri.getHost(); host = (host == null) ? conf.get(FS_SEAWEED_FILER_HOST, "localhost") : host; - if (host == null) { - throw new IOException("Invalid host specified"); - } conf.set(FS_SEAWEED_FILER_HOST, host); // get port information from uri, (overrides info in conf) @@ -60,10 +58,12 @@ public class SeaweedFileSystem extends FileSystem { port = (port == -1) ? FS_SEAWEED_DEFAULT_PORT : port; conf.setInt(FS_SEAWEED_FILER_PORT, port); + int grpcPort = conf.getInt(FS_SEAWEED_FILER_PORT_GRPC, port+10000); + setConf(conf); this.uri = uri; - seaweedFileSystemStore = new SeaweedFileSystemStore(host, port, conf); + seaweedFileSystemStore = new SeaweedFileSystemStore(host, port, grpcPort, conf); } diff --git a/other/java/hdfs3/src/main/java/seaweed/hdfs/SeaweedFileSystemStore.java b/other/java/hdfs3/src/main/java/seaweed/hdfs/SeaweedFileSystemStore.java index f4e8c9349..2ba8e1a10 100644 --- a/other/java/hdfs3/src/main/java/seaweed/hdfs/SeaweedFileSystemStore.java +++ b/other/java/hdfs3/src/main/java/seaweed/hdfs/SeaweedFileSystemStore.java @@ -27,9 +27,8 @@ public class SeaweedFileSystemStore { private FilerClient filerClient; private Configuration conf; - public SeaweedFileSystemStore(String host, int port, Configuration conf) { - int grpcPort = 10000 + port; - filerClient = new FilerClient(host, grpcPort); + public SeaweedFileSystemStore(String host, int port, int grpcPort, Configuration conf) { + filerClient = new FilerClient(host, port, grpcPort); this.conf = conf; String volumeServerAccessMode = this.conf.get(FS_SEAWEED_VOLUME_SERVER_ACCESS, "direct"); if (volumeServerAccessMode.equals("publicUrl")) { diff --git a/unmaintained/diff_volume_servers/diff_volume_servers.go b/unmaintained/diff_volume_servers/diff_volume_servers.go index 27a537617..e8361a6cf 100644 --- a/unmaintained/diff_volume_servers/diff_volume_servers.go +++ b/unmaintained/diff_volume_servers/diff_volume_servers.go @@ -6,13 +6,9 @@ import ( "errors" "flag" "fmt" - "io" - "math" - "os" - "strings" - "github.com/chrislusf/seaweedfs/weed/glog" "github.com/chrislusf/seaweedfs/weed/operation" + "github.com/chrislusf/seaweedfs/weed/pb" "github.com/chrislusf/seaweedfs/weed/pb/volume_server_pb" "github.com/chrislusf/seaweedfs/weed/security" "github.com/chrislusf/seaweedfs/weed/storage/idx" @@ -20,6 +16,9 @@ import ( "github.com/chrislusf/seaweedfs/weed/storage/types" "github.com/chrislusf/seaweedfs/weed/util" "google.golang.org/grpc" + "io" + "math" + "os" ) var ( @@ -45,13 +44,13 @@ func main() { grpcDialOption = security.LoadClientTLS(util.GetViper(), "grpc.client") vid := uint32(*volumeId) - servers := strings.Split(*serversStr, ",") + servers := pb.ServerAddresses(*serversStr).ToAddresses() if len(servers) < 2 { glog.Fatalf("You must specify more than 1 server\n") } - var referenceServer string + var referenceServer pb.ServerAddress var maxOffset int64 - allFiles := map[string]map[types.NeedleId]needleState{} + allFiles := map[pb.ServerAddress]map[types.NeedleId]needleState{} for _, addr := range servers { files, offset, err := getVolumeFiles(vid, addr) if err != nil { @@ -121,7 +120,7 @@ type needleState struct { size types.Size } -func getVolumeFiles(v uint32, addr string) (map[types.NeedleId]needleState, int64, error) { +func getVolumeFiles(v uint32, addr pb.ServerAddress) (map[types.NeedleId]needleState, int64, error) { var idxFile *bytes.Reader err := operation.WithVolumeServerClient(addr, grpcDialOption, func(vs volume_server_pb.VolumeServerClient) error { ctx, cancel := context.WithCancel(context.Background()) @@ -179,7 +178,7 @@ func getVolumeFiles(v uint32, addr string) (map[types.NeedleId]needleState, int6 return files, maxOffset, nil } -func getNeedleFileId(v uint32, nid types.NeedleId, addr string) (string, error) { +func getNeedleFileId(v uint32, nid types.NeedleId, addr pb.ServerAddress) (string, error) { var id string err := operation.WithVolumeServerClient(addr, grpcDialOption, func(vs volume_server_pb.VolumeServerClient) error { resp, err := vs.VolumeNeedleStatus(context.Background(), &volume_server_pb.VolumeNeedleStatusRequest{ diff --git a/unmaintained/load_test/load_test_meta_tail/load_test_meta_tail.go b/unmaintained/load_test/load_test_meta_tail/load_test_meta_tail.go index b77d77ddc..8ccad1e49 100644 --- a/unmaintained/load_test/load_test_meta_tail/load_test_meta_tail.go +++ b/unmaintained/load_test/load_test_meta_tail/load_test_meta_tail.go @@ -51,7 +51,7 @@ func main() { } func startGenerateMetadata() { - pb.WithFilerClient(*tailFiler, grpc.WithInsecure(), func(client filer_pb.SeaweedFilerClient) error { + pb.WithFilerClient(pb.ServerAddress(*tailFiler), grpc.WithInsecure(), func(client filer_pb.SeaweedFilerClient) error { for i := 0; i < *n; i++ { name := fmt.Sprintf("file%d", i) @@ -77,7 +77,7 @@ func startGenerateMetadata() { func startSubscribeMetadata(eachEntryFunc func(event *filer_pb.SubscribeMetadataResponse) error) { - tailErr := pb.FollowMetadata(*tailFiler, grpc.WithInsecure(), "tail", *dir, nil, 0, 0, eachEntryFunc, false) + tailErr := pb.FollowMetadata(pb.ServerAddress(*tailFiler), grpc.WithInsecure(), "tail", *dir, nil, 0, 0, eachEntryFunc, false) if tailErr != nil { fmt.Printf("tail %s: %v\n", *tailFiler, tailErr) diff --git a/unmaintained/repeated_vacuum/repeated_vacuum.go b/unmaintained/repeated_vacuum/repeated_vacuum.go index 2a9165f13..937e764bb 100644 --- a/unmaintained/repeated_vacuum/repeated_vacuum.go +++ b/unmaintained/repeated_vacuum/repeated_vacuum.go @@ -3,6 +3,7 @@ package main import ( "flag" "fmt" + "github.com/chrislusf/seaweedfs/weed/pb" "log" "math/rand" "time" @@ -32,7 +33,7 @@ func main() { go func() { for { println("vacuum threshold", *garbageThreshold) - _, _, err := util.Get(fmt.Sprintf("http://%s/vol/vacuum?garbageThreshold=%f", *master, *garbageThreshold)) + _, _, err := util.Get(fmt.Sprintf("http://%s/vol/vacuum?garbageThreshold=%f", pb.ServerAddress(*master).ToHttpAddress(), *garbageThreshold)) if err != nil { log.Fatalf("vacuum: %v", err) } @@ -52,7 +53,7 @@ func main() { } func genFile(grpcDialOption grpc.DialOption, i int) (*operation.AssignResult, string) { - assignResult, err := operation.Assign(func() string { return *master }, grpcDialOption, &operation.VolumeAssignRequest{ + assignResult, err := operation.Assign(func() pb.ServerAddress { return pb.ServerAddress(*master) }, grpcDialOption, &operation.VolumeAssignRequest{ Count: 1, Replication: *replication, }) diff --git a/unmaintained/volume_tailer/volume_tailer.go b/unmaintained/volume_tailer/volume_tailer.go index 32da2e6ab..3c6f4a987 100644 --- a/unmaintained/volume_tailer/volume_tailer.go +++ b/unmaintained/volume_tailer/volume_tailer.go @@ -2,6 +2,7 @@ package main import ( "flag" + "github.com/chrislusf/seaweedfs/weed/pb" "log" "time" @@ -37,7 +38,7 @@ func main() { sinceTimeNs = time.Now().Add(-*rewindDuration).UnixNano() } - err := operation.TailVolume(func()string{return *master}, grpcDialOption, vid, uint64(sinceTimeNs), *timeoutSeconds, func(n *needle.Needle) (err error) { + err := operation.TailVolume(func()pb.ServerAddress{return pb.ServerAddress(*master)}, grpcDialOption, vid, uint64(sinceTimeNs), *timeoutSeconds, func(n *needle.Needle) (err error) { if n.Size == 0 { println("-", n.String()) return nil diff --git a/weed/command/backup.go b/weed/command/backup.go index 4c5a2d820..2279d0d1a 100644 --- a/weed/command/backup.go +++ b/weed/command/backup.go @@ -2,6 +2,7 @@ package command import ( "fmt" + "github.com/chrislusf/seaweedfs/weed/pb" "github.com/chrislusf/seaweedfs/weed/security" "github.com/chrislusf/seaweedfs/weed/storage/needle" @@ -72,12 +73,12 @@ func runBackup(cmd *Command, args []string) bool { vid := needle.VolumeId(*s.volumeId) // find volume location, replication, ttl info - lookup, err := operation.LookupVolumeId(func() string { return *s.master }, grpcDialOption, vid.String()) + lookup, err := operation.LookupVolumeId(func() pb.ServerAddress { return pb.ServerAddress(*s.master) }, grpcDialOption, vid.String()) if err != nil { fmt.Printf("Error looking up volume %d: %v\n", vid, err) return true } - volumeServer := lookup.Locations[0].Url + volumeServer := lookup.Locations[0].ServerAddress() stats, err := operation.GetVolumeSyncStatus(volumeServer, grpcDialOption, uint32(vid)) if err != nil { diff --git a/weed/command/benchmark.go b/weed/command/benchmark.go index f0c8f6139..af5919adf 100644 --- a/weed/command/benchmark.go +++ b/weed/command/benchmark.go @@ -3,6 +3,7 @@ package command import ( "bufio" "fmt" + "github.com/chrislusf/seaweedfs/weed/pb" "io" "math" "math/rand" @@ -10,7 +11,6 @@ import ( "runtime" "runtime/pprof" "sort" - "strings" "sync" "time" @@ -129,7 +129,7 @@ func runBenchmark(cmd *Command, args []string) bool { defer pprof.StopCPUProfile() } - b.masterClient = wdclient.NewMasterClient(b.grpcDialOption, "client", "", 0, "", strings.Split(*b.masters, ",")) + b.masterClient = wdclient.NewMasterClient(b.grpcDialOption, "client", "", "", pb.ServerAddresses(*b.masters).ToAddresses()) go b.masterClient.KeepConnectedToMaster() b.masterClient.WaitUntilConnected() diff --git a/weed/command/download.go b/weed/command/download.go index a64d3f237..1d8a72d31 100644 --- a/weed/command/download.go +++ b/weed/command/download.go @@ -2,6 +2,7 @@ package command import ( "fmt" + "github.com/chrislusf/seaweedfs/weed/pb" "github.com/chrislusf/seaweedfs/weed/security" "google.golang.org/grpc" "io" @@ -49,7 +50,7 @@ func runDownload(cmd *Command, args []string) bool { grpcDialOption := security.LoadClientTLS(util.GetViper(), "grpc.client") for _, fid := range args { - if e := downloadToFile(func() string { return *d.server }, grpcDialOption, fid, util.ResolvePath(*d.dir)); e != nil { + if e := downloadToFile(func() pb.ServerAddress { return pb.ServerAddress(*d.server) }, grpcDialOption, fid, util.ResolvePath(*d.dir)); e != nil { fmt.Println("Download Error: ", fid, e) } } diff --git a/weed/command/filer.go b/weed/command/filer.go index 63dd53f9e..96802a1cb 100644 --- a/weed/command/filer.go +++ b/weed/command/filer.go @@ -29,7 +29,8 @@ var ( ) type FilerOptions struct { - masters *string + masters []pb.ServerAddress + mastersString *string ip *string bindIp *string port *int @@ -56,7 +57,7 @@ type FilerOptions struct { func init() { cmdFiler.Run = runFiler // break init cycle - f.masters = cmdFiler.Flag.String("master", "localhost:9333", "comma-separated master servers") + f.mastersString = cmdFiler.Flag.String("master", "localhost:9333", "comma-separated master servers") f.collection = cmdFiler.Flag.String("collection", "", "all data will be stored in this default collection") f.ip = cmdFiler.Flag.String("ip", util.DetectedHostAddress(), "filer server http listen ip address") f.bindIp = cmdFiler.Flag.String("ip.bind", "", "ip address to bind to") @@ -157,13 +158,15 @@ func runFiler(cmd *Command, args []string) bool { if *filerStartIam { filerIamOptions.filer = &filerAddress - filerIamOptions.masters = f.masters + filerIamOptions.masters = f.mastersString go func() { time.Sleep(startDelay * time.Second) filerIamOptions.startIamServer() }() } + f.masters = pb.ServerAddresses(*f.mastersString).ToAddresses() + f.startFiler() return true @@ -185,8 +188,10 @@ func (fo *FilerOptions) startFiler() { peers = strings.Split(*fo.peers, ",") } + filerAddress := pb.NewServerAddress(*fo.ip, *fo.port, *fo.portGrpc) + fs, nfs_err := weed_server.NewFilerServer(defaultMux, publicVolumeMux, &weed_server.FilerOption{ - Masters: strings.Split(*fo.masters, ","), + Masters: fo.masters, Collection: *fo.collection, DefaultReplication: *fo.defaultReplicaPlacement, DisableDirListing: *fo.disableDirListing, @@ -196,11 +201,10 @@ func (fo *FilerOptions) startFiler() { Rack: *fo.rack, DefaultLevelDbDir: defaultLevelDbDirectory, DisableHttp: *fo.disableHttp, - Host: *fo.ip, - Port: uint32(*fo.port), + Host: filerAddress, Cipher: *fo.cipher, SaveToFilerLimit: int64(*fo.saveToFilerLimit), - Filers: peers, + Filers: pb.FromAddressStrings(peers), ConcurrentUploadLimit: int64(*fo.concurrentUploadLimitMB) * 1024 * 1024, }) if nfs_err != nil { diff --git a/weed/command/filer_backup.go b/weed/command/filer_backup.go index 5b6409187..9e5041531 100644 --- a/weed/command/filer_backup.go +++ b/weed/command/filer_backup.go @@ -78,7 +78,7 @@ func doFilerBackup(grpcDialOption grpc.DialOption, backupOption *FilerBackupOpti return fmt.Errorf("no data sink configured in replication.toml") } - sourceFiler := *backupOption.filer + sourceFiler := pb.ServerAddress(*backupOption.filer) sourcePath := *backupOption.path timeAgo := *backupOption.timeAgo targetPath := dataSink.GetSinkToDirectory() @@ -102,7 +102,7 @@ func doFilerBackup(grpcDialOption grpc.DialOption, backupOption *FilerBackupOpti // create filer sink filerSource := &source.FilerSource{} - filerSource.DoInitialize(sourceFiler, pb.ServerToGrpcAddress(sourceFiler), sourcePath, *backupOption.proxyByFiler) + filerSource.DoInitialize(sourceFiler.ToHttpAddress(), sourceFiler.ToGrpcAddress(), sourcePath, *backupOption.proxyByFiler) dataSink.SetSourceFiler(filerSource) processEventFn := genProcessFunction(sourcePath, targetPath, dataSink, debug) diff --git a/weed/command/filer_cat.go b/weed/command/filer_cat.go index 09f5e97fe..71c3a48d6 100644 --- a/weed/command/filer_cat.go +++ b/weed/command/filer_cat.go @@ -23,7 +23,7 @@ var ( type FilerCatOptions struct { grpcDialOption grpc.DialOption - filerAddress string + filerAddress pb.ServerAddress filerClient filer_pb.SeaweedFilerClient output *string } @@ -78,7 +78,7 @@ func runFilerCat(cmd *Command, args []string) bool { return false } - filerCat.filerAddress = filerUrl.Host + filerCat.filerAddress = pb.ServerAddress(filerUrl.Host) filerCat.grpcDialOption = security.LoadClientTLS(util.GetViper(), "grpc.client") dir, name := util.FullPath(urlPath).DirAndName() diff --git a/weed/command/filer_copy.go b/weed/command/filer_copy.go index 05aa96292..0feae63b3 100644 --- a/weed/command/filer_copy.go +++ b/weed/command/filer_copy.go @@ -7,7 +7,6 @@ import ( "io" "io/ioutil" "net/http" - "net/url" "os" "path/filepath" "strconv" @@ -92,35 +91,21 @@ func runCopy(cmd *Command, args []string) bool { filerDestination := args[len(args)-1] fileOrDirs := args[0 : len(args)-1] - filerUrl, err := url.Parse(filerDestination) + filerAddress, urlPath, err := pb.ParseUrl(filerDestination) if err != nil { fmt.Printf("The last argument should be a URL on filer: %v\n", err) return false } - urlPath := filerUrl.Path if !strings.HasSuffix(urlPath, "/") { fmt.Printf("The last argument should be a folder and end with \"/\"\n") return false } - if filerUrl.Port() == "" { - fmt.Printf("The filer port should be specified.\n") - return false - } - - filerPort, parseErr := strconv.ParseUint(filerUrl.Port(), 10, 64) - if parseErr != nil { - fmt.Printf("The filer port parse error: %v\n", parseErr) - return false - } - - filerGrpcPort := filerPort + 10000 - filerGrpcAddress := util.JoinHostPort(filerUrl.Hostname(), int(filerGrpcPort)) copy.grpcDialOption = security.LoadClientTLS(util.GetViper(), "grpc.client") - masters, collection, replication, dirBuckets, maxMB, cipher, err := readFilerConfiguration(copy.grpcDialOption, filerGrpcAddress) + masters, collection, replication, dirBuckets, maxMB, cipher, err := readFilerConfiguration(copy.grpcDialOption, filerAddress) if err != nil { - fmt.Printf("read from filer %s: %v\n", filerGrpcAddress, err) + fmt.Printf("read from filer %s: %v\n", filerAddress, err) return false } if strings.HasPrefix(urlPath, dirBuckets+"/") { @@ -174,9 +159,8 @@ func runCopy(cmd *Command, args []string) bool { go func() { defer waitGroup.Done() worker := FileCopyWorker{ - options: ©, - filerHost: filerUrl.Host, - filerGrpcAddress: filerGrpcAddress, + options: ©, + filerAddress: filerAddress, } if err := worker.copyFiles(fileCopyTaskChan); err != nil { fmt.Fprintf(os.Stderr, "copy file error: %v\n", err) @@ -189,7 +173,7 @@ func runCopy(cmd *Command, args []string) bool { return true } -func readFilerConfiguration(grpcDialOption grpc.DialOption, filerGrpcAddress string) (masters []string, collection, replication string, dirBuckets string, maxMB uint32, cipher bool, err error) { +func readFilerConfiguration(grpcDialOption grpc.DialOption, filerGrpcAddress pb.ServerAddress) (masters []string, collection, replication string, dirBuckets string, maxMB uint32, cipher bool, err error) { err = pb.WithGrpcFilerClient(filerGrpcAddress, grpcDialOption, func(client filer_pb.SeaweedFilerClient) error { resp, err := client.GetFilerConfiguration(context.Background(), &filer_pb.GetFilerConfigurationRequest{}) if err != nil { @@ -241,9 +225,8 @@ func genFileCopyTask(fileOrDir string, destPath string, fileCopyTaskChan chan Fi } type FileCopyWorker struct { - options *CopyOptions - filerHost string - filerGrpcAddress string + options *CopyOptions + filerAddress pb.ServerAddress } func (worker *FileCopyWorker) copyFiles(fileCopyTaskChan chan FileCopyTask) error { @@ -321,7 +304,7 @@ func (worker *FileCopyWorker) checkExistingFileFirst(task FileCopyTask, f *os.Fi return } - err = pb.WithGrpcFilerClient(worker.filerGrpcAddress, worker.options.grpcDialOption, func(client filer_pb.SeaweedFilerClient) error { + err = pb.WithGrpcFilerClient(worker.filerAddress, worker.options.grpcDialOption, func(client filer_pb.SeaweedFilerClient) error { request := &filer_pb.LookupDirectoryEntryRequest{ Directory: task.destinationUrlPath, @@ -363,7 +346,7 @@ func (worker *FileCopyWorker) uploadFileAsOne(task FileCopyTask, f *os.File) err // assign a volume err = util.Retry("assignVolume", func() error { - return pb.WithGrpcFilerClient(worker.filerGrpcAddress, worker.options.grpcDialOption, func(client filer_pb.SeaweedFilerClient) error { + return pb.WithGrpcFilerClient(worker.filerAddress, worker.options.grpcDialOption, func(client filer_pb.SeaweedFilerClient) error { request := &filer_pb.AssignVolumeRequest{ Count: 1, @@ -381,7 +364,7 @@ func (worker *FileCopyWorker) uploadFileAsOne(task FileCopyTask, f *os.File) err if assignResult.Error != "" { return fmt.Errorf("assign volume failure %v: %v", request, assignResult.Error) } - if assignResult.Url == "" { + if assignResult.Location.Url == "" { return fmt.Errorf("assign volume failure %v: %v", request, assignResult) } return nil @@ -391,7 +374,7 @@ func (worker *FileCopyWorker) uploadFileAsOne(task FileCopyTask, f *os.File) err return fmt.Errorf("Failed to assign from %v: %v\n", worker.options.masters, err) } - targetUrl := "http://" + assignResult.Url + "/" + assignResult.FileId + targetUrl := "http://" + assignResult.Location.Url + "/" + assignResult.FileId uploadOption := &operation.UploadOption{ UploadUrl: targetUrl, Filename: fileName, @@ -414,10 +397,10 @@ func (worker *FileCopyWorker) uploadFileAsOne(task FileCopyTask, f *os.File) err chunks = append(chunks, uploadResult.ToPbFileChunk(assignResult.FileId, 0)) - fmt.Printf("copied %s => http://%s%s%s\n", f.Name(), worker.filerHost, task.destinationUrlPath, fileName) + fmt.Printf("copied %s => http://%s%s%s\n", f.Name(), worker.filerAddress.ToHttpAddress(), task.destinationUrlPath, fileName) } - if err := pb.WithGrpcFilerClient(worker.filerGrpcAddress, worker.options.grpcDialOption, func(client filer_pb.SeaweedFilerClient) error { + if err := pb.WithGrpcFilerClient(worker.filerAddress, worker.options.grpcDialOption, func(client filer_pb.SeaweedFilerClient) error { request := &filer_pb.CreateEntryRequest{ Directory: task.destinationUrlPath, Entry: &filer_pb.Entry{ @@ -443,7 +426,7 @@ func (worker *FileCopyWorker) uploadFileAsOne(task FileCopyTask, f *os.File) err } return nil }); err != nil { - return fmt.Errorf("upload data %v to http://%s%s%s: %v\n", fileName, worker.filerHost, task.destinationUrlPath, fileName, err) + return fmt.Errorf("upload data %v to http://%s%s%s: %v\n", fileName, worker.filerAddress.ToHttpAddress(), task.destinationUrlPath, fileName, err) } return nil @@ -474,7 +457,7 @@ func (worker *FileCopyWorker) uploadFileInChunks(task FileCopyTask, f *os.File, var assignResult *filer_pb.AssignVolumeResponse var assignError error err := util.Retry("assignVolume", func() error { - return pb.WithGrpcFilerClient(worker.filerGrpcAddress, worker.options.grpcDialOption, func(client filer_pb.SeaweedFilerClient) error { + return pb.WithGrpcFilerClient(worker.filerAddress, worker.options.grpcDialOption, func(client filer_pb.SeaweedFilerClient) error { request := &filer_pb.AssignVolumeRequest{ Count: 1, Replication: *worker.options.replication, @@ -498,7 +481,7 @@ func (worker *FileCopyWorker) uploadFileInChunks(task FileCopyTask, f *os.File, fmt.Printf("Failed to assign from %v: %v\n", worker.options.masters, err) } - targetUrl := "http://" + assignResult.Url + "/" + assignResult.FileId + targetUrl := "http://" + assignResult.Location.Url + "/" + assignResult.FileId if collection == "" { collection = assignResult.Collection } @@ -508,7 +491,7 @@ func (worker *FileCopyWorker) uploadFileInChunks(task FileCopyTask, f *os.File, uploadOption := &operation.UploadOption{ UploadUrl: targetUrl, - Filename: fileName+"-"+strconv.FormatInt(i+1, 10), + Filename: fileName + "-" + strconv.FormatInt(i+1, 10), Cipher: worker.options.cipher, IsInputCompressed: false, MimeType: "", @@ -542,8 +525,8 @@ func (worker *FileCopyWorker) uploadFileInChunks(task FileCopyTask, f *os.File, for _, chunk := range chunks { fileIds = append(fileIds, chunk.FileId) } - operation.DeleteFiles(func() string { - return copy.masters[0] + operation.DeleteFiles(func() pb.ServerAddress { + return pb.ServerAddress(copy.masters[0]) }, false, worker.options.grpcDialOption, fileIds) return uploadError } @@ -553,7 +536,7 @@ func (worker *FileCopyWorker) uploadFileInChunks(task FileCopyTask, f *os.File, return fmt.Errorf("create manifest: %v", manifestErr) } - if err := pb.WithGrpcFilerClient(worker.filerGrpcAddress, worker.options.grpcDialOption, func(client filer_pb.SeaweedFilerClient) error { + if err := pb.WithGrpcFilerClient(worker.filerAddress, worker.options.grpcDialOption, func(client filer_pb.SeaweedFilerClient) error { request := &filer_pb.CreateEntryRequest{ Directory: task.destinationUrlPath, Entry: &filer_pb.Entry{ @@ -579,10 +562,10 @@ func (worker *FileCopyWorker) uploadFileInChunks(task FileCopyTask, f *os.File, } return nil }); err != nil { - return fmt.Errorf("upload data %v to http://%s%s%s: %v\n", fileName, worker.filerHost, task.destinationUrlPath, fileName, err) + return fmt.Errorf("upload data %v to http://%s%s%s: %v\n", fileName, worker.filerAddress.ToHttpAddress(), task.destinationUrlPath, fileName, err) } - fmt.Printf("copied %s => http://%s%s%s\n", f.Name(), worker.filerHost, task.destinationUrlPath, fileName) + fmt.Printf("copied %s => http://%s%s%s\n", f.Name(), worker.filerAddress.ToHttpAddress(), task.destinationUrlPath, fileName) return nil } @@ -611,7 +594,7 @@ func (worker *FileCopyWorker) saveDataAsChunk(reader io.Reader, name string, off var fileId, host string var auth security.EncodedJwt - if flushErr := pb.WithGrpcFilerClient(worker.filerGrpcAddress, worker.options.grpcDialOption, func(client filer_pb.SeaweedFilerClient) error { + if flushErr := pb.WithGrpcFilerClient(worker.filerAddress, worker.options.grpcDialOption, func(client filer_pb.SeaweedFilerClient) error { ctx := context.Background() @@ -633,7 +616,7 @@ func (worker *FileCopyWorker) saveDataAsChunk(reader io.Reader, name string, off return fmt.Errorf("assign volume failure %v: %v", request, resp.Error) } - fileId, host, auth = resp.FileId, resp.Url, security.EncodedJwt(resp.Auth) + fileId, host, auth = resp.FileId, resp.Location.Url, security.EncodedJwt(resp.Auth) collection, replication = resp.Collection, resp.Replication return nil diff --git a/weed/command/filer_meta_backup.go b/weed/command/filer_meta_backup.go index 3757f63f1..0b8fa76c6 100644 --- a/weed/command/filer_meta_backup.go +++ b/weed/command/filer_meta_backup.go @@ -195,7 +195,7 @@ func (metaBackup *FilerMetaBackupOptions) streamMetadataBackup() error { return metaBackup.setOffset(lastTime) }) - return pb.FollowMetadata(*metaBackup.filerAddress, metaBackup.grpcDialOption, "meta_backup", + return pb.FollowMetadata(pb.ServerAddress(*metaBackup.filerAddress), metaBackup.grpcDialOption, "meta_backup", *metaBackup.filerDirectory, nil, startTime.UnixNano(), 0, processEventFnWithOffset, false) } @@ -224,7 +224,7 @@ var _ = filer_pb.FilerClient(&FilerMetaBackupOptions{}) func (metaBackup *FilerMetaBackupOptions) WithFilerClient(fn func(filer_pb.SeaweedFilerClient) error) error { - return pb.WithFilerClient(*metaBackup.filerAddress, metaBackup.grpcDialOption, func(client filer_pb.SeaweedFilerClient) error { + return pb.WithFilerClient(pb.ServerAddress(*metaBackup.filerAddress), metaBackup.grpcDialOption, func(client filer_pb.SeaweedFilerClient) error { return fn(client) }) diff --git a/weed/command/filer_meta_tail.go b/weed/command/filer_meta_tail.go index 0363ae8d1..85a3eaf84 100644 --- a/weed/command/filer_meta_tail.go +++ b/weed/command/filer_meta_tail.go @@ -103,7 +103,7 @@ func runFilerMetaTail(cmd *Command, args []string) bool { } } - tailErr := pb.FollowMetadata(*tailFiler, grpcDialOption, "tail", + tailErr := pb.FollowMetadata(pb.ServerAddress(*tailFiler), grpcDialOption, "tail", *tailTarget, nil, time.Now().Add(-*tailStart).UnixNano(), 0, func(resp *filer_pb.SubscribeMetadataResponse) error { if !shouldPrint(resp) { diff --git a/weed/command/filer_remote_sync.go b/weed/command/filer_remote_sync.go index 3776ee4d9..857fbb0eb 100644 --- a/weed/command/filer_remote_sync.go +++ b/weed/command/filer_remote_sync.go @@ -33,7 +33,7 @@ type RemoteSyncOptions struct { var _ = filer_pb.FilerClient(&RemoteSyncOptions{}) func (option *RemoteSyncOptions) WithFilerClient(fn func(filer_pb.SeaweedFilerClient) error) error { - return pb.WithFilerClient(*option.filerAddress, option.grpcDialOption, func(client filer_pb.SeaweedFilerClient) error { + return pb.WithFilerClient(pb.ServerAddress(*option.filerAddress), option.grpcDialOption, func(client filer_pb.SeaweedFilerClient) error { return fn(client) }) } @@ -90,12 +90,12 @@ func runFilerRemoteSynchronize(cmd *Command, args []string) bool { remoteSyncOptions.grpcDialOption = grpcDialOption dir := *remoteSyncOptions.dir - filerAddress := *remoteSyncOptions.filerAddress + filerAddress := pb.ServerAddress(*remoteSyncOptions.filerAddress) filerSource := &source.FilerSource{} filerSource.DoInitialize( - filerAddress, - pb.ServerToGrpcAddress(filerAddress), + filerAddress.ToHttpAddress(), + filerAddress.ToGrpcAddress(), "/", // does not matter *remoteSyncOptions.readChunkFromFiler, ) diff --git a/weed/command/filer_remote_sync_buckets.go b/weed/command/filer_remote_sync_buckets.go index 70f9f49c1..73c8de1a9 100644 --- a/weed/command/filer_remote_sync_buckets.go +++ b/weed/command/filer_remote_sync_buckets.go @@ -32,12 +32,12 @@ func (option *RemoteSyncOptions) followBucketUpdatesAndUploadToRemote(filerSourc processEventFnWithOffset := pb.AddOffsetFunc(eachEntryFunc, 3*time.Second, func(counter int64, lastTsNs int64) error { lastTime := time.Unix(0, lastTsNs) glog.V(0).Infof("remote sync %s progressed to %v %0.2f/sec", *option.filerAddress, lastTime, float64(counter)/float64(3)) - return remote_storage.SetSyncOffset(option.grpcDialOption, *option.filerAddress, option.bucketsDir, lastTsNs) + return remote_storage.SetSyncOffset(option.grpcDialOption, pb.ServerAddress(*option.filerAddress), option.bucketsDir, lastTsNs) }) lastOffsetTs := collectLastSyncOffset(option, option.bucketsDir) - return pb.FollowMetadata(*option.filerAddress, option.grpcDialOption, "filer.remote.sync", + return pb.FollowMetadata(pb.ServerAddress(*option.filerAddress), option.grpcDialOption, "filer.remote.sync", option.bucketsDir, []string{filer.DirectoryEtcRemote}, lastOffsetTs.UnixNano(), 0, processEventFnWithOffset, false) } @@ -357,7 +357,7 @@ func extractBucketPath(bucketsDir, dir string) (util.FullPath, bool) { func (option *RemoteSyncOptions) collectRemoteStorageConf() (err error) { - if mappings, err := filer.ReadMountMappings(option.grpcDialOption, *option.filerAddress); err != nil { + if mappings, err := filer.ReadMountMappings(option.grpcDialOption, pb.ServerAddress(*option.filerAddress)); err != nil { return err } else { option.mappings = mappings diff --git a/weed/command/filer_remote_sync_dir.go b/weed/command/filer_remote_sync_dir.go index dc2e9a1fb..50f1e35cf 100644 --- a/weed/command/filer_remote_sync_dir.go +++ b/weed/command/filer_remote_sync_dir.go @@ -20,7 +20,7 @@ import ( func followUpdatesAndUploadToRemote(option *RemoteSyncOptions, filerSource *source.FilerSource, mountedDir string) error { // read filer remote storage mount mappings - _, _, remoteStorageMountLocation, remoteStorage, detectErr := filer.DetectMountInfo(option.grpcDialOption, *option.filerAddress, mountedDir) + _, _, remoteStorageMountLocation, remoteStorage, detectErr := filer.DetectMountInfo(option.grpcDialOption, pb.ServerAddress(*option.filerAddress), mountedDir) if detectErr != nil { return fmt.Errorf("read mount info: %v", detectErr) } @@ -33,12 +33,12 @@ func followUpdatesAndUploadToRemote(option *RemoteSyncOptions, filerSource *sour processEventFnWithOffset := pb.AddOffsetFunc(eachEntryFunc, 3*time.Second, func(counter int64, lastTsNs int64) error { lastTime := time.Unix(0, lastTsNs) glog.V(0).Infof("remote sync %s progressed to %v %0.2f/sec", *option.filerAddress, lastTime, float64(counter)/float64(3)) - return remote_storage.SetSyncOffset(option.grpcDialOption, *option.filerAddress, mountedDir, lastTsNs) + return remote_storage.SetSyncOffset(option.grpcDialOption, pb.ServerAddress(*option.filerAddress), mountedDir, lastTsNs) }) lastOffsetTs := collectLastSyncOffset(option, mountedDir) - return pb.FollowMetadata(*option.filerAddress, option.grpcDialOption, "filer.remote.sync", + return pb.FollowMetadata(pb.ServerAddress(*option.filerAddress), option.grpcDialOption, "filer.remote.sync", mountedDir, []string{filer.DirectoryEtcRemote}, lastOffsetTs.UnixNano(), 0, processEventFnWithOffset, false) } @@ -171,7 +171,7 @@ func collectLastSyncOffset(option *RemoteSyncOptions, mountedDir string) time.Ti return time.Now() } - lastOffsetTsNs, err := remote_storage.GetSyncOffset(option.grpcDialOption, *option.filerAddress, mountedDir) + lastOffsetTsNs, err := remote_storage.GetSyncOffset(option.grpcDialOption, pb.ServerAddress(*option.filerAddress), mountedDir) if mountedDirEntry != nil { if err == nil && mountedDirEntry.Attributes.Crtime < lastOffsetTsNs/1000000 { lastOffsetTs = time.Unix(0, lastOffsetTsNs) diff --git a/weed/command/filer_sync.go b/weed/command/filer_sync.go index 33efdb2b7..20755dbe5 100644 --- a/weed/command/filer_sync.go +++ b/weed/command/filer_sync.go @@ -93,9 +93,11 @@ func runFilerSynchronize(cmd *Command, args []string) bool { grace.SetupProfiling(*syncCpuProfile, *syncMemProfile) + filerA := pb.ServerAddress(*syncOptions.filerA) + filerB := pb.ServerAddress(*syncOptions.filerB) go func() { for { - err := doSubscribeFilerMetaChanges(grpcDialOption, *syncOptions.filerA, *syncOptions.aPath, *syncOptions.aProxyByFiler, *syncOptions.filerB, + err := doSubscribeFilerMetaChanges(grpcDialOption, filerA, *syncOptions.aPath, *syncOptions.aProxyByFiler, filerB, *syncOptions.bPath, *syncOptions.bReplication, *syncOptions.bCollection, *syncOptions.bTtlSec, *syncOptions.bProxyByFiler, *syncOptions.bDiskType, *syncOptions.bDebug) if err != nil { glog.Errorf("sync from %s to %s: %v", *syncOptions.filerA, *syncOptions.filerB, err) @@ -107,7 +109,7 @@ func runFilerSynchronize(cmd *Command, args []string) bool { if !*syncOptions.isActivePassive { go func() { for { - err := doSubscribeFilerMetaChanges(grpcDialOption, *syncOptions.filerB, *syncOptions.bPath, *syncOptions.bProxyByFiler, *syncOptions.filerA, + err := doSubscribeFilerMetaChanges(grpcDialOption, filerB, *syncOptions.bPath, *syncOptions.bProxyByFiler, filerA, *syncOptions.aPath, *syncOptions.aReplication, *syncOptions.aCollection, *syncOptions.aTtlSec, *syncOptions.aProxyByFiler, *syncOptions.aDiskType, *syncOptions.aDebug) if err != nil { glog.Errorf("sync from %s to %s: %v", *syncOptions.filerB, *syncOptions.filerA, err) @@ -122,7 +124,7 @@ func runFilerSynchronize(cmd *Command, args []string) bool { return true } -func doSubscribeFilerMetaChanges(grpcDialOption grpc.DialOption, sourceFiler, sourcePath string, sourceReadChunkFromFiler bool, targetFiler, targetPath string, +func doSubscribeFilerMetaChanges(grpcDialOption grpc.DialOption, sourceFiler pb.ServerAddress, sourcePath string, sourceReadChunkFromFiler bool, targetFiler pb.ServerAddress, targetPath string, replicationStr, collection string, ttlSec int, sinkWriteChunkByFiler bool, diskType string, debug bool) error { // read source filer signature @@ -147,9 +149,9 @@ func doSubscribeFilerMetaChanges(grpcDialOption grpc.DialOption, sourceFiler, so // create filer sink filerSource := &source.FilerSource{} - filerSource.DoInitialize(sourceFiler, pb.ServerToGrpcAddress(sourceFiler), sourcePath, sourceReadChunkFromFiler) + filerSource.DoInitialize(sourceFiler.ToHttpAddress(), sourceFiler.ToGrpcAddress(), sourcePath, sourceReadChunkFromFiler) filerSink := &filersink.FilerSink{} - filerSink.DoInitialize(targetFiler, pb.ServerToGrpcAddress(targetFiler), targetPath, replicationStr, collection, ttlSec, diskType, grpcDialOption, sinkWriteChunkByFiler) + filerSink.DoInitialize(targetFiler.ToHttpAddress(), targetFiler.ToGrpcAddress(), targetPath, replicationStr, collection, ttlSec, diskType, grpcDialOption, sinkWriteChunkByFiler) filerSink.SetSourceFiler(filerSource) persistEventFn := genProcessFunction(sourcePath, targetPath, filerSink, debug) @@ -170,7 +172,7 @@ func doSubscribeFilerMetaChanges(grpcDialOption grpc.DialOption, sourceFiler, so return setOffset(grpcDialOption, targetFiler, SyncKeyPrefix, sourceFilerSignature, lastTsNs) }) - return pb.FollowMetadata(sourceFiler, grpcDialOption, "syncTo_"+targetFiler, + return pb.FollowMetadata(sourceFiler, grpcDialOption, "syncTo_"+string(targetFiler), sourcePath, nil, sourceFilerOffsetTsNs, targetFilerSignature, processEventFnWithOffset, false) } @@ -179,7 +181,7 @@ const ( SyncKeyPrefix = "sync." ) -func getOffset(grpcDialOption grpc.DialOption, filer string, signaturePrefix string, signature int32) (lastOffsetTsNs int64, readErr error) { +func getOffset(grpcDialOption grpc.DialOption, filer pb.ServerAddress, signaturePrefix string, signature int32) (lastOffsetTsNs int64, readErr error) { readErr = pb.WithFilerClient(filer, grpcDialOption, func(client filer_pb.SeaweedFilerClient) error { syncKey := []byte(signaturePrefix + "____") @@ -206,7 +208,7 @@ func getOffset(grpcDialOption grpc.DialOption, filer string, signaturePrefix str } -func setOffset(grpcDialOption grpc.DialOption, filer string, signaturePrefix string, signature int32, offsetTsNs int64) error { +func setOffset(grpcDialOption grpc.DialOption, filer pb.ServerAddress, signaturePrefix string, signature int32, offsetTsNs int64) error { return pb.WithFilerClient(filer, grpcDialOption, func(client filer_pb.SeaweedFilerClient) error { syncKey := []byte(signaturePrefix + "____") diff --git a/weed/command/iam.go b/weed/command/iam.go index ed4eea543..ebe9657f2 100644 --- a/weed/command/iam.go +++ b/weed/command/iam.go @@ -43,38 +43,35 @@ func runIam(cmd *Command, args []string) bool { } func (iamopt *IamOptions) startIamServer() bool { - filerGrpcAddress, err := pb.ParseServerToGrpcAddress(*iamopt.filer) - if err != nil { - glog.Fatal(err) - return false - } + filerAddress := pb.ServerAddress(*iamopt.filer) util.LoadConfiguration("security", false) grpcDialOption := security.LoadClientTLS(util.GetViper(), "grpc.client") for { - err = pb.WithGrpcFilerClient(filerGrpcAddress, grpcDialOption, func(client filer_pb.SeaweedFilerClient) error { + err := pb.WithGrpcFilerClient(filerAddress, grpcDialOption, func(client filer_pb.SeaweedFilerClient) error { resp, err := client.GetFilerConfiguration(context.Background(), &filer_pb.GetFilerConfigurationRequest{}) if err != nil { - return fmt.Errorf("get filer %s configuration: %v", filerGrpcAddress, err) + return fmt.Errorf("get filer %s configuration: %v", filerAddress, err) } glog.V(0).Infof("IAM read filer configuration: %s", resp) return nil }) if err != nil { - glog.V(0).Infof("wait to connect to filer %s grpc address %s", *iamopt.filer, filerGrpcAddress) + glog.V(0).Infof("wait to connect to filer %s grpc address %s", *iamopt.filer, filerAddress.ToGrpcAddress()) time.Sleep(time.Second) } else { - glog.V(0).Infof("connected to filer %s grpc address %s", *iamopt.filer, filerGrpcAddress) + glog.V(0).Infof("connected to filer %s grpc address %s", *iamopt.filer, filerAddress.ToGrpcAddress()) break } } + masters := pb.ServerAddresses(*iamopt.masters).ToAddresses() router := mux.NewRouter().SkipClean(true) _, iamApiServer_err := iamapi.NewIamApiServer(router, &iamapi.IamServerOption{ - Filer: *iamopt.filer, - Port: *iamopt.port, - FilerGrpcAddress: filerGrpcAddress, - GrpcDialOption: grpcDialOption, + Masters: masters, + Filer: filerAddress, + Port: *iamopt.port, + GrpcDialOption: grpcDialOption, }) glog.V(0).Info("NewIamApiServer created") if iamApiServer_err != nil { diff --git a/weed/command/master.go b/weed/command/master.go index bf7a5d420..adc9055ea 100644 --- a/weed/command/master.go +++ b/weed/command/master.go @@ -113,7 +113,7 @@ func startMaster(masterOption MasterOptions, masterWhiteList []string) { backend.LoadConfiguration(util.GetViper()) - myMasterAddress, peers := checkPeers(*masterOption.ip, *masterOption.port, *masterOption.peers) + myMasterAddress, peers := checkPeers(*masterOption.ip, *masterOption.port, *masterOption.portGrpc, *masterOption.peers) r := mux.NewRouter() ms := weed_server.NewMasterServer(r, masterOption.toMasterOption(masterWhiteList), peers) @@ -162,16 +162,14 @@ func startMaster(masterOption MasterOptions, masterWhiteList []string) { select {} } -func checkPeers(masterIp string, masterPort int, peers string) (masterAddress string, cleanedPeers []string) { +func checkPeers(masterIp string, masterPort int, masterGrpcPort int, peers string) (masterAddress pb.ServerAddress, cleanedPeers []pb.ServerAddress) { glog.V(0).Infof("current: %s:%d peers:%s", masterIp, masterPort, peers) - masterAddress = util.JoinHostPort(masterIp, masterPort) - if peers != "" { - cleanedPeers = strings.Split(peers, ",") - } + masterAddress = pb.NewServerAddress(masterIp, masterPort, masterGrpcPort) + cleanedPeers = pb.ServerAddresses(peers).ToAddresses() hasSelf := false for _, peer := range cleanedPeers { - if peer == masterAddress { + if peer.ToHttpAddress() == masterAddress.ToHttpAddress() { hasSelf = true break } @@ -181,13 +179,15 @@ func checkPeers(masterIp string, masterPort int, peers string) (masterAddress st cleanedPeers = append(cleanedPeers, masterAddress) } if len(cleanedPeers)%2 == 0 { - glog.Fatalf("Only odd number of masters are supported!") + glog.Fatalf("Only odd number of masters are supported: %+v", cleanedPeers) } return } -func isTheFirstOne(self string, peers []string) bool { - sort.Strings(peers) +func isTheFirstOne(self pb.ServerAddress, peers []pb.ServerAddress) bool { + sort.Slice(peers, func(i, j int) bool { + return strings.Compare(string(peers[i]), string(peers[j])) < 0 + }) if len(peers) <= 0 { return true } @@ -195,9 +195,9 @@ func isTheFirstOne(self string, peers []string) bool { } func (m *MasterOptions) toMasterOption(whiteList []string) *weed_server.MasterOption { + masterAddress := pb.NewServerAddress(*m.ip, *m.port, *m.portGrpc) return &weed_server.MasterOption{ - Host: *m.ip, - Port: *m.port, + Master: masterAddress, MetaFolder: *m.metaFolder, VolumeSizeLimitMB: uint32(*m.volumeSizeLimitMB), VolumePreallocate: *m.volumePreallocate, diff --git a/weed/command/master_follower.go b/weed/command/master_follower.go index cf7e45253..2bb9ff6d4 100644 --- a/weed/command/master_follower.go +++ b/weed/command/master_follower.go @@ -13,7 +13,6 @@ import ( "github.com/gorilla/mux" "google.golang.org/grpc/reflection" "net/http" - "strings" "time" ) @@ -79,19 +78,15 @@ func runMasterFollower(cmd *Command, args []string) bool { func startMasterFollower(masterOptions MasterOptions) { // collect settings from main masters - masters := strings.Split(*mf.peers, ",") - masterGrpcAddresses, err := pb.ParseServersToGrpcAddresses(masters) - if err != nil { - glog.V(0).Infof("ParseFilerGrpcAddress: %v", err) - return - } + masters := pb.ServerAddresses(*mf.peers).ToAddresses() + var err error grpcDialOption := security.LoadClientTLS(util.GetViper(), "grpc.master") for i := 0; i < 10; i++ { - err = pb.WithOneOfGrpcMasterClients(masterGrpcAddresses, grpcDialOption, func(client master_pb.SeaweedClient) error { + err = pb.WithOneOfGrpcMasterClients(masters, grpcDialOption, func(client master_pb.SeaweedClient) error { resp, err := client.GetMasterConfiguration(context.Background(), &master_pb.GetMasterConfigurationRequest{}) if err != nil { - return fmt.Errorf("get master grpc address %v configuration: %v", masterGrpcAddresses, err) + return fmt.Errorf("get master grpc address %v configuration: %v", masters, err) } masterOptions.defaultReplication = &resp.DefaultReplication masterOptions.volumeSizeLimitMB = aws.Uint(uint(resp.VolumeSizeLimitMB)) @@ -99,13 +94,13 @@ func startMasterFollower(masterOptions MasterOptions) { return nil }) if err != nil { - glog.V(0).Infof("failed to talk to filer %v: %v", masterGrpcAddresses, err) + glog.V(0).Infof("failed to talk to filer %v: %v", masters, err) glog.V(0).Infof("wait for %d seconds ...", i+1) time.Sleep(time.Duration(i+1) * time.Second) } } if err != nil { - glog.Errorf("failed to talk to filer %v: %v", masterGrpcAddresses, err) + glog.Errorf("failed to talk to filer %v: %v", masters, err) return } diff --git a/weed/command/mount_std.go b/weed/command/mount_std.go index e393e5894..2603260a2 100644 --- a/weed/command/mount_std.go +++ b/weed/command/mount_std.go @@ -70,35 +70,30 @@ func getParentInode(mountDir string) (uint64, error) { func RunMount(option *MountOptions, umask os.FileMode) bool { - filers := strings.Split(*option.filer, ",") - // parse filer grpc address - filerGrpcAddresses, err := pb.ParseServersToGrpcAddresses(filers) - if err != nil { - glog.V(0).Infof("ParseFilerGrpcAddress: %v", err) - return true - } + filerAddresses := pb.ServerAddresses(*option.filer).ToAddresses() util.LoadConfiguration("security", false) // try to connect to filer, filerBucketsPath may be useful later grpcDialOption := security.LoadClientTLS(util.GetViper(), "grpc.client") var cipher bool + var err error for i := 0; i < 10; i++ { - err = pb.WithOneOfGrpcFilerClients(filerGrpcAddresses, grpcDialOption, func(client filer_pb.SeaweedFilerClient) error { + err = pb.WithOneOfGrpcFilerClients(filerAddresses, grpcDialOption, func(client filer_pb.SeaweedFilerClient) error { resp, err := client.GetFilerConfiguration(context.Background(), &filer_pb.GetFilerConfigurationRequest{}) if err != nil { - return fmt.Errorf("get filer grpc address %v configuration: %v", filerGrpcAddresses, err) + return fmt.Errorf("get filer grpc address %v configuration: %v", filerAddresses, err) } cipher = resp.Cipher return nil }) if err != nil { - glog.V(0).Infof("failed to talk to filer %v: %v", filerGrpcAddresses, err) + glog.V(0).Infof("failed to talk to filer %v: %v", filerAddresses, err) glog.V(0).Infof("wait for %d seconds ...", i+1) time.Sleep(time.Duration(i+1) * time.Second) } } if err != nil { - glog.Errorf("failed to talk to filer %v: %v", filerGrpcAddresses, err) + glog.Errorf("failed to talk to filer %v: %v", filerAddresses, err) return true } @@ -206,8 +201,7 @@ func RunMount(option *MountOptions, umask os.FileMode) bool { seaweedFileSystem := filesys.NewSeaweedFileSystem(&filesys.Option{ MountDirectory: dir, - FilerAddresses: filers, - FilerGrpcAddresses: filerGrpcAddresses, + FilerAddresses: filerAddresses, GrpcDialOption: grpcDialOption, FilerMountRootPath: mountRoot, Collection: *option.collection, diff --git a/weed/command/msg_broker.go b/weed/command/msg_broker.go index 403bbe317..61517ab39 100644 --- a/weed/command/msg_broker.go +++ b/weed/command/msg_broker.go @@ -62,35 +62,31 @@ func (msgBrokerOpt *MessageBrokerOptions) startQueueServer() bool { grace.SetupProfiling(*messageBrokerStandaloneOptions.cpuprofile, *messageBrokerStandaloneOptions.memprofile) - filerGrpcAddress, err := pb.ParseServerToGrpcAddress(*msgBrokerOpt.filer) - if err != nil { - glog.Fatal(err) - return false - } + filerAddress := pb.ServerAddress(*msgBrokerOpt.filer) grpcDialOption := security.LoadClientTLS(util.GetViper(), "grpc.msg_broker") cipher := false for { - err = pb.WithGrpcFilerClient(filerGrpcAddress, grpcDialOption, func(client filer_pb.SeaweedFilerClient) error { + err := pb.WithGrpcFilerClient(filerAddress, grpcDialOption, func(client filer_pb.SeaweedFilerClient) error { resp, err := client.GetFilerConfiguration(context.Background(), &filer_pb.GetFilerConfigurationRequest{}) if err != nil { - return fmt.Errorf("get filer %s configuration: %v", filerGrpcAddress, err) + return fmt.Errorf("get filer %s configuration: %v", filerAddress, err) } cipher = resp.Cipher return nil }) if err != nil { - glog.V(0).Infof("wait to connect to filer %s grpc address %s", *msgBrokerOpt.filer, filerGrpcAddress) + glog.V(0).Infof("wait to connect to filer %s grpc address %s", *msgBrokerOpt.filer, filerAddress.ToGrpcAddress()) time.Sleep(time.Second) } else { - glog.V(0).Infof("connected to filer %s grpc address %s", *msgBrokerOpt.filer, filerGrpcAddress) + glog.V(0).Infof("connected to filer %s grpc address %s", *msgBrokerOpt.filer, filerAddress.ToGrpcAddress()) break } } qs, err := broker.NewMessageBroker(&broker.MessageBrokerOption{ - Filers: []string{*msgBrokerOpt.filer}, + Filers: []pb.ServerAddress{filerAddress}, DefaultReplication: "", MaxMB: 0, Ip: *msgBrokerOpt.ip, diff --git a/weed/command/s3.go b/weed/command/s3.go index c8292a7d5..f2c6c0769 100644 --- a/weed/command/s3.go +++ b/weed/command/s3.go @@ -137,11 +137,7 @@ func runS3(cmd *Command, args []string) bool { func (s3opt *S3Options) startS3Server() bool { - filerGrpcAddress, err := pb.ParseServerToGrpcAddress(*s3opt.filer) - if err != nil { - glog.Fatal(err) - return false - } + filerAddress := pb.ServerAddress(*s3opt.filer) filerBucketsPath := "/buckets" @@ -152,10 +148,10 @@ func (s3opt *S3Options) startS3Server() bool { var metricsIntervalSec int for { - err = pb.WithGrpcFilerClient(filerGrpcAddress, grpcDialOption, func(client filer_pb.SeaweedFilerClient) error { + err := pb.WithGrpcFilerClient(filerAddress, grpcDialOption, func(client filer_pb.SeaweedFilerClient) error { resp, err := client.GetFilerConfiguration(context.Background(), &filer_pb.GetFilerConfigurationRequest{}) if err != nil { - return fmt.Errorf("get filer %s configuration: %v", filerGrpcAddress, err) + return fmt.Errorf("get filer %s configuration: %v", filerAddress, err) } filerBucketsPath = resp.DirBuckets metricsAddress, metricsIntervalSec = resp.MetricsAddress, int(resp.MetricsIntervalSec) @@ -163,10 +159,10 @@ func (s3opt *S3Options) startS3Server() bool { return nil }) if err != nil { - glog.V(0).Infof("wait to connect to filer %s grpc address %s", *s3opt.filer, filerGrpcAddress) + glog.V(0).Infof("wait to connect to filer %s grpc address %s", *s3opt.filer, filerAddress.ToGrpcAddress()) time.Sleep(time.Second) } else { - glog.V(0).Infof("connected to filer %s grpc address %s", *s3opt.filer, filerGrpcAddress) + glog.V(0).Infof("connected to filer %s grpc address %s", *s3opt.filer, filerAddress.ToGrpcAddress()) break } } @@ -176,9 +172,8 @@ func (s3opt *S3Options) startS3Server() bool { router := mux.NewRouter().SkipClean(true) _, s3ApiServer_err := s3api.NewS3ApiServer(router, &s3api.S3ApiServerOption{ - Filer: *s3opt.filer, + Filer: filerAddress, Port: *s3opt.port, - FilerGrpcAddress: filerGrpcAddress, Config: *s3opt.config, DomainName: *s3opt.domainName, BucketsPath: filerBucketsPath, diff --git a/weed/command/server.go b/weed/command/server.go index b45ea8f4a..e498c39b4 100644 --- a/weed/command/server.go +++ b/weed/command/server.go @@ -2,6 +2,7 @@ package command import ( "fmt" + "github.com/chrislusf/seaweedfs/weed/pb" "github.com/chrislusf/seaweedfs/weed/util/grace" "net/http" "os" @@ -167,21 +168,16 @@ func runServer(cmd *Command, args []string) bool { *isStartingFiler = true } - if *isStartingMasterServer { - _, peerList := checkPeers(*serverIp, *masterOptions.port, *masterOptions.peers) - peers := strings.Join(peerList, ",") - masterOptions.peers = &peers - } - // ip address masterOptions.ip = serverIp masterOptions.ipBind = serverBindIp - filerOptions.masters = masterOptions.peers + _, masters := checkPeers(*masterOptions.ip, *masterOptions.port, *masterOptions.portGrpc, *masterOptions.peers) + filerOptions.masters = masters filerOptions.ip = serverIp filerOptions.bindIp = serverBindIp serverOptions.v.ip = serverIp serverOptions.v.bindIp = serverBindIp - serverOptions.v.masters = masterOptions.peers + serverOptions.v.masters = masters serverOptions.v.idleConnectionTimeout = serverTimeout serverOptions.v.dataCenter = serverDataCenter serverOptions.v.rack = serverRack @@ -197,7 +193,7 @@ func runServer(cmd *Command, args []string) bool { filerOptions.disableHttp = serverDisableHttp masterOptions.disableHttp = serverDisableHttp - filerAddress := util.JoinHostPort(*serverIp, *filerOptions.port) + filerAddress := string(pb.NewServerAddress(*serverIp, *filerOptions.port, *filerOptions.portGrpc)) s3Options.filer = &filerAddress webdavOptions.filer = &filerAddress msgBrokerOptions.filer = &filerAddress diff --git a/weed/command/shell.go b/weed/command/shell.go index 4a9f4b027..93bb69522 100644 --- a/weed/command/shell.go +++ b/weed/command/shell.go @@ -2,6 +2,7 @@ package command import ( "fmt" + "github.com/chrislusf/seaweedfs/weed/pb" "github.com/chrislusf/seaweedfs/weed/security" "github.com/chrislusf/seaweedfs/weed/shell" @@ -53,13 +54,7 @@ func runShell(command *Command, args []string) bool { fmt.Printf("master: %s filer: %s\n", *shellOptions.Masters, *shellInitialFiler) - var err error - shellOptions.FilerHost, shellOptions.FilerPort, err = util.ParseHostPort(*shellInitialFiler) - shellOptions.FilerAddress = *shellInitialFiler - if err != nil { - fmt.Printf("failed to parse filer %s: %v\n", *shellInitialFiler, err) - return false - } + shellOptions.FilerAddress = pb.ServerAddress(*shellInitialFiler) shellOptions.Directory = "/" shell.RunShell(shellOptions) diff --git a/weed/command/upload.go b/weed/command/upload.go index 9ae1befab..f46e70cb1 100644 --- a/weed/command/upload.go +++ b/weed/command/upload.go @@ -71,7 +71,7 @@ func runUpload(cmd *Command, args []string) bool { util.LoadConfiguration("security", false) grpcDialOption := security.LoadClientTLS(util.GetViper(), "grpc.client") - defaultReplication, err := readMasterConfiguration(grpcDialOption, *upload.master) + defaultReplication, err := readMasterConfiguration(grpcDialOption, pb.ServerAddress(*upload.master)) if err != nil { fmt.Printf("upload: %v", err) return false @@ -96,7 +96,7 @@ func runUpload(cmd *Command, args []string) bool { if e != nil { return e } - results, e := operation.SubmitFiles(func() string { return *upload.master }, grpcDialOption, parts, *upload.replication, *upload.collection, *upload.dataCenter, *upload.ttl, *upload.diskType, *upload.maxMB, *upload.usePublicUrl) + results, e := operation.SubmitFiles(func() pb.ServerAddress { return pb.ServerAddress(*upload.master) }, grpcDialOption, parts, *upload.replication, *upload.collection, *upload.dataCenter, *upload.ttl, *upload.diskType, *upload.maxMB, *upload.usePublicUrl) bytes, _ := json.Marshal(results) fmt.Println(string(bytes)) if e != nil { @@ -118,7 +118,7 @@ func runUpload(cmd *Command, args []string) bool { fmt.Println(e.Error()) return false } - results, err := operation.SubmitFiles(func() string { return *upload.master }, grpcDialOption, parts, *upload.replication, *upload.collection, *upload.dataCenter, *upload.ttl, *upload.diskType, *upload.maxMB, *upload.usePublicUrl) + results, err := operation.SubmitFiles(func() pb.ServerAddress { return pb.ServerAddress(*upload.master) }, grpcDialOption, parts, *upload.replication, *upload.collection, *upload.dataCenter, *upload.ttl, *upload.diskType, *upload.maxMB, *upload.usePublicUrl) if err != nil { fmt.Println(err.Error()) return false @@ -129,7 +129,7 @@ func runUpload(cmd *Command, args []string) bool { return true } -func readMasterConfiguration(grpcDialOption grpc.DialOption, masterAddress string) (replication string, err error) { +func readMasterConfiguration(grpcDialOption grpc.DialOption, masterAddress pb.ServerAddress) (replication string, err error) { err = pb.WithMasterClient(masterAddress, grpcDialOption, func(client master_pb.SeaweedClient) error { resp, err := client.GetMasterConfiguration(context.Background(), &master_pb.GetMasterConfigurationRequest{}) if err != nil { diff --git a/weed/command/volume.go b/weed/command/volume.go index f5ec11724..15e88d65e 100644 --- a/weed/command/volume.go +++ b/weed/command/volume.go @@ -44,7 +44,8 @@ type VolumeServerOptions struct { ip *string publicUrl *string bindIp *string - masters *string + mastersString *string + masters []pb.ServerAddress idleConnectionTimeout *int dataCenter *string rack *string @@ -74,7 +75,7 @@ func init() { v.ip = cmdVolume.Flag.String("ip", util.DetectedHostAddress(), "ip or server name, also used as identifier") v.publicUrl = cmdVolume.Flag.String("publicUrl", "", "Publicly accessible address") v.bindIp = cmdVolume.Flag.String("ip.bind", "", "ip address to bind to") - v.masters = cmdVolume.Flag.String("mserver", "localhost:9333", "comma-separated master servers") + v.mastersString = cmdVolume.Flag.String("mserver", "localhost:9333", "comma-separated master servers") v.preStopSeconds = cmdVolume.Flag.Int("preStopSeconds", 10, "number of seconds between stop send heartbeats and stop volume server") // v.pulseSeconds = cmdVolume.Flag.Int("pulseSeconds", 5, "number of seconds between heartbeats, must be smaller than or equal to the master's setting") v.idleConnectionTimeout = cmdVolume.Flag.Int("idleTimeout", 30, "connection idle seconds") @@ -125,6 +126,7 @@ func runVolume(cmd *Command, args []string) bool { go stats_collect.StartMetricsServer(*v.metricsHttpPort) minFreeSpaces := util.MustParseMinFreeSpace(*minFreeSpace, *minFreeSpacePercent) + v.masters = pb.ServerAddresses(*v.mastersString).ToAddresses() v.startVolumeServer(*volumeFolders, *maxVolumeCounts, *volumeWhiteListOption, minFreeSpaces) return true @@ -223,14 +225,12 @@ func (v VolumeServerOptions) startVolumeServer(volumeFolders, maxVolumeCounts, v volumeNeedleMapKind = storage.NeedleMapLevelDbLarge } - masters := *v.masters - volumeServer := weed_server.NewVolumeServer(volumeMux, publicVolumeMux, - *v.ip, *v.port, *v.publicUrl, + *v.ip, *v.port, *v.portGrpc, *v.publicUrl, v.folders, v.folderMaxLimits, minFreeSpaces, diskTypes, *v.idxFolder, volumeNeedleMapKind, - strings.Split(masters, ","), 5, *v.dataCenter, *v.rack, + v.masters, 5, *v.dataCenter, *v.rack, v.whiteList, *v.fixJpgOrientation, *v.readMode, *v.compactionMBPerSecond, @@ -375,7 +375,7 @@ func (v VolumeServerOptions) startClusterHttpService(handler http.Handler) httpd } func (v VolumeServerOptions) startTcpService(volumeServer *weed_server.VolumeServer) { - listeningAddress := util.JoinHostPort(*v.bindIp,*v.port+20000) + listeningAddress := util.JoinHostPort(*v.bindIp, *v.port+20000) glog.V(0).Infoln("Start Seaweed volume server", util.Version(), "tcp at", listeningAddress) listener, e := util.NewListener(listeningAddress, 0) if e != nil { diff --git a/weed/command/webdav.go b/weed/command/webdav.go index 781ea1e36..a7062d8cd 100644 --- a/weed/command/webdav.go +++ b/weed/command/webdav.go @@ -78,37 +78,32 @@ func (wo *WebDavOption) startWebDav() bool { } // parse filer grpc address - filerGrpcAddress, err := pb.ParseServerToGrpcAddress(*wo.filer) - if err != nil { - glog.Fatal(err) - return false - } + filerAddress := pb.ServerAddress(*wo.filer) grpcDialOption := security.LoadClientTLS(util.GetViper(), "grpc.client") var cipher bool // connect to filer for { - err = pb.WithGrpcFilerClient(filerGrpcAddress, grpcDialOption, func(client filer_pb.SeaweedFilerClient) error { + err := pb.WithGrpcFilerClient(filerAddress, grpcDialOption, func(client filer_pb.SeaweedFilerClient) error { resp, err := client.GetFilerConfiguration(context.Background(), &filer_pb.GetFilerConfigurationRequest{}) if err != nil { - return fmt.Errorf("get filer %s configuration: %v", filerGrpcAddress, err) + return fmt.Errorf("get filer %s configuration: %v", filerAddress, err) } cipher = resp.Cipher return nil }) if err != nil { - glog.V(0).Infof("wait to connect to filer %s grpc address %s", *wo.filer, filerGrpcAddress) + glog.V(0).Infof("wait to connect to filer %s grpc address %s", *wo.filer, filerAddress.ToGrpcAddress()) time.Sleep(time.Second) } else { - glog.V(0).Infof("connected to filer %s grpc address %s", *wo.filer, filerGrpcAddress) + glog.V(0).Infof("connected to filer %s grpc address %s", *wo.filer, filerAddress.ToGrpcAddress()) break } } ws, webdavServer_err := weed_server.NewWebDavServer(&weed_server.WebDavOption{ - Filer: *wo.filer, - FilerGrpcAddress: filerGrpcAddress, + Filer: filerAddress, GrpcDialOption: grpcDialOption, Collection: *wo.collection, Replication: *wo.replication, diff --git a/weed/filer/filer.go b/weed/filer/filer.go index 1a20abefc..f13782031 100644 --- a/weed/filer/filer.go +++ b/weed/filer/filer.go @@ -3,6 +3,7 @@ package filer import ( "context" "fmt" + "github.com/chrislusf/seaweedfs/weed/pb" "os" "strings" "time" @@ -45,10 +46,10 @@ type Filer struct { RemoteStorage *FilerRemoteStorage } -func NewFiler(masters []string, grpcDialOption grpc.DialOption, - filerHost string, filerGrpcPort uint32, collection string, replication string, dataCenter string, notifyFn func()) *Filer { +func NewFiler(masters []pb.ServerAddress, grpcDialOption grpc.DialOption, + filerHost pb.ServerAddress, collection string, replication string, dataCenter string, notifyFn func()) *Filer { f := &Filer{ - MasterClient: wdclient.NewMasterClient(grpcDialOption, "filer", filerHost, filerGrpcPort, dataCenter, masters), + MasterClient: wdclient.NewMasterClient(grpcDialOption, "filer", filerHost, dataCenter, masters), fileIdDeletionQueue: util.NewUnboundedQueue(), GrpcDialOption: grpcDialOption, FilerConf: NewFilerConf(), @@ -63,7 +64,7 @@ func NewFiler(masters []string, grpcDialOption grpc.DialOption, return f } -func (f *Filer) AggregateFromPeers(self string, filers []string) { +func (f *Filer) AggregateFromPeers(self pb.ServerAddress, filers []pb.ServerAddress) { // set peers found := false @@ -110,7 +111,7 @@ func (f *Filer) GetStore() (store FilerStore) { return f.Store } -func (fs *Filer) GetMaster() string { +func (fs *Filer) GetMaster() pb.ServerAddress { return fs.MasterClient.GetMaster() } diff --git a/weed/filer/meta_aggregator.go b/weed/filer/meta_aggregator.go index 913cbd454..008fd33a7 100644 --- a/weed/filer/meta_aggregator.go +++ b/weed/filer/meta_aggregator.go @@ -18,7 +18,7 @@ import ( ) type MetaAggregator struct { - filers []string + filers []pb.ServerAddress grpcDialOption grpc.DialOption MetaLogBuffer *log_buffer.LogBuffer // notifying clients @@ -28,7 +28,7 @@ type MetaAggregator struct { // MetaAggregator only aggregates data "on the fly". The logs are not re-persisted to disk. // The old data comes from what each LocalMetadata persisted on disk. -func NewMetaAggregator(filers []string, grpcDialOption grpc.DialOption) *MetaAggregator { +func NewMetaAggregator(filers []pb.ServerAddress, grpcDialOption grpc.DialOption) *MetaAggregator { t := &MetaAggregator{ filers: filers, grpcDialOption: grpcDialOption, @@ -40,13 +40,13 @@ func NewMetaAggregator(filers []string, grpcDialOption grpc.DialOption) *MetaAgg return t } -func (ma *MetaAggregator) StartLoopSubscribe(f *Filer, self string) { +func (ma *MetaAggregator) StartLoopSubscribe(f *Filer, self pb.ServerAddress) { for _, filer := range ma.filers { go ma.subscribeToOneFiler(f, self, filer) } } -func (ma *MetaAggregator) subscribeToOneFiler(f *Filer, self string, peer string) { +func (ma *MetaAggregator) subscribeToOneFiler(f *Filer, self pb.ServerAddress, peer pb.ServerAddress) { /* Each filer reads the "filer.store.id", which is the store's signature when filer starts. @@ -123,7 +123,7 @@ func (ma *MetaAggregator) subscribeToOneFiler(f *Filer, self string, peer string ctx, cancel := context.WithCancel(context.Background()) defer cancel() stream, err := client.SubscribeLocalMetadata(ctx, &filer_pb.SubscribeMetadataRequest{ - ClientName: "filer:" + self, + ClientName: "filer:" + string(self), PathPrefix: "/", SinceNs: lastTsNs, }) @@ -156,7 +156,7 @@ func (ma *MetaAggregator) subscribeToOneFiler(f *Filer, self string, peer string } } -func (ma *MetaAggregator) readFilerStoreSignature(peer string) (sig int32, err error) { +func (ma *MetaAggregator) readFilerStoreSignature(peer pb.ServerAddress) (sig int32, err error) { err = pb.WithFilerClient(peer, ma.grpcDialOption, func(client filer_pb.SeaweedFilerClient) error { resp, err := client.GetFilerConfiguration(context.Background(), &filer_pb.GetFilerConfigurationRequest{}) if err != nil { @@ -172,7 +172,7 @@ const ( MetaOffsetPrefix = "Meta" ) -func (ma *MetaAggregator) readOffset(f *Filer, peer string, peerSignature int32) (lastTsNs int64, err error) { +func (ma *MetaAggregator) readOffset(f *Filer, peer pb.ServerAddress, peerSignature int32) (lastTsNs int64, err error) { key := []byte(MetaOffsetPrefix + "xxxx") util.Uint32toBytes(key[len(MetaOffsetPrefix):], uint32(peerSignature)) @@ -195,7 +195,7 @@ func (ma *MetaAggregator) readOffset(f *Filer, peer string, peerSignature int32) return } -func (ma *MetaAggregator) updateOffset(f *Filer, peer string, peerSignature int32, lastTsNs int64) (err error) { +func (ma *MetaAggregator) updateOffset(f *Filer, peer pb.ServerAddress, peerSignature int32, lastTsNs int64) (err error) { key := []byte(MetaOffsetPrefix + "xxxx") util.Uint32toBytes(key[len(MetaOffsetPrefix):], uint32(peerSignature)) diff --git a/weed/filer/remote_mapping.go b/weed/filer/remote_mapping.go index fb74dca98..c95c4e5bd 100644 --- a/weed/filer/remote_mapping.go +++ b/weed/filer/remote_mapping.go @@ -9,7 +9,7 @@ import ( "google.golang.org/grpc" ) -func ReadMountMappings(grpcDialOption grpc.DialOption, filerAddress string) (mappings *remote_pb.RemoteStorageMapping, readErr error) { +func ReadMountMappings(grpcDialOption grpc.DialOption, filerAddress pb.ServerAddress) (mappings *remote_pb.RemoteStorageMapping, readErr error) { var oldContent []byte if readErr = pb.WithFilerClient(filerAddress, grpcDialOption, func(client filer_pb.SeaweedFilerClient) error { oldContent, readErr = ReadInsideFiler(client, DirectoryEtcRemote, REMOTE_STORAGE_MOUNT_FILE) diff --git a/weed/filer/remote_storage.go b/weed/filer/remote_storage.go index 4ff21f3b3..9d682b698 100644 --- a/weed/filer/remote_storage.go +++ b/weed/filer/remote_storage.go @@ -131,7 +131,7 @@ func UnmarshalRemoteStorageMappings(oldContent []byte) (mappings *remote_pb.Remo return } -func ReadRemoteStorageConf(grpcDialOption grpc.DialOption, filerAddress string, storageName string) (conf *remote_pb.RemoteConf, readErr error) { +func ReadRemoteStorageConf(grpcDialOption grpc.DialOption, filerAddress pb.ServerAddress, storageName string) (conf *remote_pb.RemoteConf, readErr error) { var oldContent []byte if readErr = pb.WithFilerClient(filerAddress, grpcDialOption, func(client filer_pb.SeaweedFilerClient) error { oldContent, readErr = ReadInsideFiler(client, DirectoryEtcRemote, storageName+REMOTE_STORAGE_CONF_SUFFIX) @@ -150,7 +150,7 @@ func ReadRemoteStorageConf(grpcDialOption grpc.DialOption, filerAddress string, return } -func DetectMountInfo(grpcDialOption grpc.DialOption, filerAddress string, dir string) (*remote_pb.RemoteStorageMapping, string, *remote_pb.RemoteStorageLocation, *remote_pb.RemoteConf, error) { +func DetectMountInfo(grpcDialOption grpc.DialOption, filerAddress pb.ServerAddress, dir string) (*remote_pb.RemoteStorageMapping, string, *remote_pb.RemoteStorageLocation, *remote_pb.RemoteConf, error) { mappings, listErr := ReadMountMappings(grpcDialOption, filerAddress) if listErr != nil { diff --git a/weed/filesys/wfs.go b/weed/filesys/wfs.go index 84d4bdfa2..92f6bae38 100644 --- a/weed/filesys/wfs.go +++ b/weed/filesys/wfs.go @@ -3,6 +3,7 @@ package filesys import ( "context" "fmt" + "github.com/chrislusf/seaweedfs/weed/pb" "math" "math/rand" "os" @@ -31,9 +32,8 @@ import ( type Option struct { MountDirectory string - FilerAddresses []string + FilerAddresses []pb.ServerAddress filerIndex int - FilerGrpcAddresses []string GrpcDialOption grpc.DialOption FilerMountRootPath string Collection string @@ -270,17 +270,17 @@ func (wfs *WFS) mapPbIdFromLocalToFiler(entry *filer_pb.Entry) { func (wfs *WFS) LookupFn() wdclient.LookupFileIdFunctionType { if wfs.option.VolumeServerAccess == "filerProxy" { return func(fileId string) (targetUrls []string, err error) { - return []string{"http://" + wfs.getCurrentFiler() + "/?proxyChunkId=" + fileId}, nil + return []string{"http://" + wfs.getCurrentFiler().ToHttpAddress() + "/?proxyChunkId=" + fileId}, nil } } return filer.LookupFn(wfs) } -func (wfs *WFS) getCurrentFiler() string { +func (wfs *WFS) getCurrentFiler() pb.ServerAddress { return wfs.option.FilerAddresses[wfs.option.filerIndex] } func (option *Option) setupUniqueCacheDirectory() { - cacheUniqueId := util.Md5String([]byte(option.MountDirectory + option.FilerGrpcAddresses[0] + option.FilerMountRootPath + util.Version()))[0:8] + cacheUniqueId := util.Md5String([]byte(option.MountDirectory + string(option.FilerAddresses[0]) + option.FilerMountRootPath + util.Version()))[0:8] option.uniqueCacheDir = path.Join(option.CacheDir, cacheUniqueId) option.uniqueCacheTempPageDir = filepath.Join(option.uniqueCacheDir, "sw") os.MkdirAll(option.uniqueCacheTempPageDir, os.FileMode(0777)&^option.Umask) diff --git a/weed/filesys/wfs_filer_client.go b/weed/filesys/wfs_filer_client.go index 95ebdb9b8..cce6aa1a1 100644 --- a/weed/filesys/wfs_filer_client.go +++ b/weed/filesys/wfs_filer_client.go @@ -16,10 +16,10 @@ func (wfs *WFS) WithFilerClient(fn func(filer_pb.SeaweedFilerClient) error) (err return util.Retry("filer grpc", func() error { i := wfs.option.filerIndex - n := len(wfs.option.FilerGrpcAddresses) + n := len(wfs.option.FilerAddresses) for x := 0; x < n; x++ { - filerGrpcAddress := wfs.option.FilerGrpcAddresses[i] + filerGrpcAddress := wfs.option.FilerAddresses[i].ToGrpcAddress() err = pb.WithCachedGrpcClient(func(grpcConnection *grpc.ClientConn) error { client := filer_pb.NewSeaweedFilerClient(grpcConnection) return fn(client) diff --git a/weed/filesys/wfs_write.go b/weed/filesys/wfs_write.go index ea8de24d5..3d08cb5e2 100644 --- a/weed/filesys/wfs_write.go +++ b/weed/filesys/wfs_write.go @@ -41,10 +41,7 @@ func (wfs *WFS) saveDataAsChunk(fullPath util.FullPath, writeOnly bool) filer.Sa } fileId, auth = resp.FileId, security.EncodedJwt(resp.Auth) - loc := &filer_pb.Location{ - Url: resp.Url, - PublicUrl: resp.PublicUrl, - } + loc := resp.Location host = wfs.AdjustedUrl(loc) collection, replication = resp.Collection, resp.Replication diff --git a/weed/iamapi/iamapi_server.go b/weed/iamapi/iamapi_server.go index ec718bd41..c8ff1042e 100644 --- a/weed/iamapi/iamapi_server.go +++ b/weed/iamapi/iamapi_server.go @@ -18,7 +18,6 @@ import ( "github.com/gorilla/mux" "google.golang.org/grpc" "net/http" - "strings" ) type IamS3ApiConfig interface { @@ -34,10 +33,9 @@ type IamS3ApiConfigure struct { } type IamServerOption struct { - Masters string - Filer string + Masters []pb.ServerAddress + Filer pb.ServerAddress Port int - FilerGrpcAddress string GrpcDialOption grpc.DialOption } @@ -51,7 +49,7 @@ var s3ApiConfigure IamS3ApiConfig func NewIamApiServer(router *mux.Router, option *IamServerOption) (iamApiServer *IamApiServer, err error) { s3ApiConfigure = IamS3ApiConfigure{ option: option, - masterClient: wdclient.NewMasterClient(option.GrpcDialOption, pb.AdminShellClient, "", 0, "", strings.Split(option.Masters, ",")), + masterClient: wdclient.NewMasterClient(option.GrpcDialOption, pb.AdminShellClient, "", "",option.Masters), } s3Option := s3api.S3ApiServerOption{Filer: option.Filer} iamApiServer = &IamApiServer{ @@ -78,7 +76,7 @@ func (iama *IamApiServer) registerRouter(router *mux.Router) { func (iam IamS3ApiConfigure) GetS3ApiConfiguration(s3cfg *iam_pb.S3ApiConfiguration) (err error) { var buf bytes.Buffer - err = pb.WithGrpcFilerClient(iam.option.FilerGrpcAddress, iam.option.GrpcDialOption, func(client filer_pb.SeaweedFilerClient) error { + err = pb.WithGrpcFilerClient(iam.option.Filer, iam.option.GrpcDialOption, func(client filer_pb.SeaweedFilerClient) error { if err = filer.ReadEntry(iam.masterClient, client, filer.IamConfigDirecotry, filer.IamIdentityFile, &buf); err != nil { return err } @@ -101,7 +99,7 @@ func (iam IamS3ApiConfigure) PutS3ApiConfiguration(s3cfg *iam_pb.S3ApiConfigurat return fmt.Errorf("ProtoToText: %s", err) } return pb.WithGrpcFilerClient( - iam.option.FilerGrpcAddress, + iam.option.Filer, iam.option.GrpcDialOption, func(client filer_pb.SeaweedFilerClient) error { err = util.Retry("saveIamIdentity", func() error { @@ -117,7 +115,7 @@ func (iam IamS3ApiConfigure) PutS3ApiConfiguration(s3cfg *iam_pb.S3ApiConfigurat func (iam IamS3ApiConfigure) GetPolicies(policies *Policies) (err error) { var buf bytes.Buffer - err = pb.WithGrpcFilerClient(iam.option.FilerGrpcAddress, iam.option.GrpcDialOption, func(client filer_pb.SeaweedFilerClient) error { + err = pb.WithGrpcFilerClient(iam.option.Filer, iam.option.GrpcDialOption, func(client filer_pb.SeaweedFilerClient) error { if err = filer.ReadEntry(iam.masterClient, client, filer.IamConfigDirecotry, filer.IamPoliciesFile, &buf); err != nil { return err } @@ -142,7 +140,7 @@ func (iam IamS3ApiConfigure) PutPolicies(policies *Policies) (err error) { return err } return pb.WithGrpcFilerClient( - iam.option.FilerGrpcAddress, + iam.option.Filer, iam.option.GrpcDialOption, func(client filer_pb.SeaweedFilerClient) error { if err := filer.SaveInsideFiler(client, filer.IamConfigDirecotry, filer.IamPoliciesFile, b); err != nil { diff --git a/weed/messaging/broker/broker_append.go b/weed/messaging/broker/broker_append.go index 599efad98..9958a0752 100644 --- a/weed/messaging/broker/broker_append.go +++ b/weed/messaging/broker/broker_append.go @@ -71,8 +71,9 @@ func (broker *MessageBroker) assignAndUpload(topicConfig *messaging_pb.TopicConf assignResult.Auth = security.EncodedJwt(resp.Auth) assignResult.Fid = resp.FileId - assignResult.Url = resp.Url - assignResult.PublicUrl = resp.PublicUrl + assignResult.Url = resp.Location.Url + assignResult.PublicUrl = resp.Location.PublicUrl + assignResult.GrpcPort = int(resp.Location.GrpcPort) assignResult.Count = uint64(resp.Count) return nil diff --git a/weed/messaging/broker/broker_grpc_server_discovery.go b/weed/messaging/broker/broker_grpc_server_discovery.go index 3c14f3220..2b5e03236 100644 --- a/weed/messaging/broker/broker_grpc_server_discovery.go +++ b/weed/messaging/broker/broker_grpc_server_discovery.go @@ -3,6 +3,7 @@ package broker import ( "context" "fmt" + "github.com/chrislusf/seaweedfs/weed/pb" "time" "github.com/chrislusf/seaweedfs/weed/glog" @@ -62,7 +63,7 @@ func (broker *MessageBroker) FindBroker(c context.Context, request *messaging_pb func (broker *MessageBroker) checkFilers() { // contact a filer about masters - var masters []string + var masters []pb.ServerAddress found := false for !found { for _, filer := range broker.option.Filers { @@ -71,7 +72,9 @@ func (broker *MessageBroker) checkFilers() { if err != nil { return err } - masters = append(masters, resp.Masters...) + for _, m := range resp.Masters { + masters = append(masters, pb.ServerAddress(m)) + } return nil }) if err == nil { @@ -85,7 +88,7 @@ func (broker *MessageBroker) checkFilers() { glog.V(0).Infof("received master list: %s", masters) // contact each masters for filers - var filers []string + var filers []pb.ServerAddress found = false for !found { for _, master := range masters { @@ -97,7 +100,7 @@ func (broker *MessageBroker) checkFilers() { return err } - filers = append(filers, resp.GrpcAddresses...) + filers = append(filers, pb.FromAddressStrings(resp.GrpcAddresses)...) return nil }) diff --git a/weed/messaging/broker/broker_server.go b/weed/messaging/broker/broker_server.go index 06162471c..fd41dd441 100644 --- a/weed/messaging/broker/broker_server.go +++ b/weed/messaging/broker/broker_server.go @@ -13,7 +13,7 @@ import ( ) type MessageBrokerOption struct { - Filers []string + Filers []pb.ServerAddress DefaultReplication string MaxMB int Ip string @@ -99,13 +99,13 @@ func (broker *MessageBroker) keepConnectedToOneFiler() { } -func (broker *MessageBroker) withFilerClient(filer string, fn func(filer_pb.SeaweedFilerClient) error) error { +func (broker *MessageBroker) withFilerClient(filer pb.ServerAddress, fn func(filer_pb.SeaweedFilerClient) error) error { return pb.WithFilerClient(filer, broker.grpcDialOption, fn) } -func (broker *MessageBroker) withMasterClient(master string, fn func(client master_pb.SeaweedClient) error) error { +func (broker *MessageBroker) withMasterClient(master pb.ServerAddress, fn func(client master_pb.SeaweedClient) error) error { return pb.WithMasterClient(master, broker.grpcDialOption, func(client master_pb.SeaweedClient) error { return fn(client) diff --git a/weed/operation/assign_file_id.go b/weed/operation/assign_file_id.go index 8dbdbbe57..9eac69631 100644 --- a/weed/operation/assign_file_id.go +++ b/weed/operation/assign_file_id.go @@ -3,6 +3,7 @@ package operation import ( "context" "fmt" + "github.com/chrislusf/seaweedfs/weed/pb" "github.com/chrislusf/seaweedfs/weed/storage/needle" "google.golang.org/grpc" @@ -22,18 +23,15 @@ type VolumeAssignRequest struct { WritableVolumeCount uint32 } -type AssignResultReplica struct { - Url string `json:"url,omitempty"` - PublicUrl string `json:"publicUrl,omitempty"` -} type AssignResult struct { - Fid string `json:"fid,omitempty"` - Url string `json:"url,omitempty"` - PublicUrl string `json:"publicUrl,omitempty"` - Count uint64 `json:"count,omitempty"` - Error string `json:"error,omitempty"` - Auth security.EncodedJwt `json:"auth,omitempty"` - Replicas []AssignResultReplica `json:"replicas,omitempty"` + Fid string `json:"fid,omitempty"` + Url string `json:"url,omitempty"` + PublicUrl string `json:"publicUrl,omitempty"` + GrpcPort int `json:"grpcPort,omitempty"` + Count uint64 `json:"count,omitempty"` + Error string `json:"error,omitempty"` + Auth security.EncodedJwt `json:"auth,omitempty"` + Replicas []Location `json:"replicas,omitempty"` } func Assign(masterFn GetMasterFn, grpcDialOption grpc.DialOption, primaryRequest *VolumeAssignRequest, alternativeRequests ...*VolumeAssignRequest) (*AssignResult, error) { @@ -70,12 +68,13 @@ func Assign(masterFn GetMasterFn, grpcDialOption grpc.DialOption, primaryRequest ret.Count = resp.Count ret.Fid = resp.Fid - ret.Url = resp.Url - ret.PublicUrl = resp.PublicUrl + ret.Url = resp.Location.Url + ret.PublicUrl = resp.Location.PublicUrl + ret.GrpcPort = int(resp.Location.GrpcPort) ret.Error = resp.Error ret.Auth = security.EncodedJwt(resp.Auth) for _, r := range resp.Replicas { - ret.Replicas = append(ret.Replicas, AssignResultReplica{ + ret.Replicas = append(ret.Replicas, Location{ Url: r.Url, PublicUrl: r.PublicUrl, }) @@ -104,7 +103,7 @@ func Assign(masterFn GetMasterFn, grpcDialOption grpc.DialOption, primaryRequest return ret, lastError } -func LookupJwt(master string, grpcDialOption grpc.DialOption, fileId string) (token security.EncodedJwt) { +func LookupJwt(master pb.ServerAddress, grpcDialOption grpc.DialOption, fileId string) (token security.EncodedJwt) { WithMasterServerClient(master, grpcDialOption, func(masterClient master_pb.SeaweedClient) error { diff --git a/weed/operation/chunked_file.go b/weed/operation/chunked_file.go index 94939f1f3..0227db1bf 100644 --- a/weed/operation/chunked_file.go +++ b/weed/operation/chunked_file.go @@ -4,6 +4,7 @@ import ( "encoding/json" "errors" "fmt" + "github.com/chrislusf/seaweedfs/weed/pb" "io" "io/ioutil" "net/http" @@ -42,7 +43,7 @@ type ChunkManifest struct { type ChunkedFileReader struct { totalSize int64 chunkList []*ChunkInfo - master string + master pb.ServerAddress pos int64 pr *io.PipeReader pw *io.PipeWriter @@ -127,7 +128,7 @@ func readChunkNeedle(fileUrl string, w io.Writer, offset int64, jwt string) (wri return io.Copy(w, resp.Body) } -func NewChunkedFileReader(chunkList []*ChunkInfo, master string, grpcDialOption grpc.DialOption) *ChunkedFileReader { +func NewChunkedFileReader(chunkList []*ChunkInfo, master pb.ServerAddress, grpcDialOption grpc.DialOption) *ChunkedFileReader { var totalSize int64 for _, chunk := range chunkList { totalSize += chunk.Size @@ -176,7 +177,7 @@ func (cf *ChunkedFileReader) WriteTo(w io.Writer) (n int64, err error) { for ; chunkIndex < len(cf.chunkList); chunkIndex++ { ci := cf.chunkList[chunkIndex] // if we need read date from local volume server first? - fileUrl, jwt, lookupError := LookupFileId(func() string { + fileUrl, jwt, lookupError := LookupFileId(func() pb.ServerAddress { return cf.master }, cf.grpcDialOption, ci.Fid) if lookupError != nil { diff --git a/weed/operation/delete_content.go b/weed/operation/delete_content.go index 15d07a52e..d762f51e1 100644 --- a/weed/operation/delete_content.go +++ b/weed/operation/delete_content.go @@ -4,6 +4,7 @@ import ( "context" "errors" "fmt" + "github.com/chrislusf/seaweedfs/weed/pb" "google.golang.org/grpc" "net/http" "strings" @@ -74,7 +75,7 @@ func DeleteFilesWithLookupVolumeId(grpcDialOption grpc.DialOption, fileIds []str return ret, err } - server_to_fileIds := make(map[string][]string) + server_to_fileIds := make(map[pb.ServerAddress][]string) for vid, result := range lookupResults { if result.Error != "" { ret = append(ret, &volume_server_pb.DeleteResult{ @@ -85,11 +86,12 @@ func DeleteFilesWithLookupVolumeId(grpcDialOption grpc.DialOption, fileIds []str continue } for _, location := range result.Locations { - if _, ok := server_to_fileIds[location.Url]; !ok { - server_to_fileIds[location.Url] = make([]string, 0) + serverAddress := location.ServerAddress() + if _, ok := server_to_fileIds[serverAddress]; !ok { + server_to_fileIds[serverAddress] = make([]string, 0) } - server_to_fileIds[location.Url] = append( - server_to_fileIds[location.Url], vid_to_fileIds[vid]...) + server_to_fileIds[serverAddress] = append( + server_to_fileIds[serverAddress], vid_to_fileIds[vid]...) } } @@ -97,7 +99,7 @@ func DeleteFilesWithLookupVolumeId(grpcDialOption grpc.DialOption, fileIds []str var wg sync.WaitGroup for server, fidList := range server_to_fileIds { wg.Add(1) - go func(server string, fidList []string) { + go func(server pb.ServerAddress, fidList []string) { defer wg.Done() if deleteResults, deleteErr := DeleteFilesAtOneVolumeServer(server, grpcDialOption, fidList, false); deleteErr != nil { @@ -119,7 +121,7 @@ func DeleteFilesWithLookupVolumeId(grpcDialOption grpc.DialOption, fileIds []str } // DeleteFilesAtOneVolumeServer deletes a list of files that is on one volume server via gRpc -func DeleteFilesAtOneVolumeServer(volumeServer string, grpcDialOption grpc.DialOption, fileIds []string, includeCookie bool) (ret []*volume_server_pb.DeleteResult, err error) { +func DeleteFilesAtOneVolumeServer(volumeServer pb.ServerAddress, grpcDialOption grpc.DialOption, fileIds []string, includeCookie bool) (ret []*volume_server_pb.DeleteResult, err error) { err = WithVolumeServerClient(volumeServer, grpcDialOption, func(volumeServerClient volume_server_pb.VolumeServerClient) error { diff --git a/weed/operation/grpc_client.go b/weed/operation/grpc_client.go index 39f70343a..743682203 100644 --- a/weed/operation/grpc_client.go +++ b/weed/operation/grpc_client.go @@ -1,68 +1,27 @@ package operation import ( - "fmt" - "github.com/chrislusf/seaweedfs/weed/util" - "strconv" - "strings" - "google.golang.org/grpc" - "github.com/chrislusf/seaweedfs/weed/glog" "github.com/chrislusf/seaweedfs/weed/pb" - "github.com/chrislusf/seaweedfs/weed/pb/filer_pb" "github.com/chrislusf/seaweedfs/weed/pb/master_pb" "github.com/chrislusf/seaweedfs/weed/pb/volume_server_pb" ) -func WithVolumeServerClient(volumeServer string, grpcDialOption grpc.DialOption, fn func(volume_server_pb.VolumeServerClient) error) error { - - grpcAddress, err := toVolumeServerGrpcAddress(volumeServer) - if err != nil { - return fmt.Errorf("failed to parse volume server %v: %v", volumeServer, err) - } +func WithVolumeServerClient(volumeServer pb.ServerAddress, grpcDialOption grpc.DialOption, fn func(volume_server_pb.VolumeServerClient) error) error { return pb.WithCachedGrpcClient(func(grpcConnection *grpc.ClientConn) error { client := volume_server_pb.NewVolumeServerClient(grpcConnection) return fn(client) - }, grpcAddress, grpcDialOption) + }, volumeServer.ToGrpcAddress(), grpcDialOption) } -func toVolumeServerGrpcAddress(volumeServer string) (grpcAddress string, err error) { - sepIndex := strings.LastIndex(volumeServer, ":") - port, err := strconv.Atoi(volumeServer[sepIndex+1:]) - if err != nil { - glog.Errorf("failed to parse volume server address: %v", volumeServer) - return "", err - } - return util.JoinHostPort(volumeServer[0:sepIndex], port+10000), nil -} - -func WithMasterServerClient(masterServer string, grpcDialOption grpc.DialOption, fn func(masterClient master_pb.SeaweedClient) error) error { - - masterGrpcAddress, parseErr := pb.ParseServerToGrpcAddress(masterServer) - if parseErr != nil { - return fmt.Errorf("failed to parse master %v: %v", masterServer, parseErr) - } +func WithMasterServerClient(masterServer pb.ServerAddress, grpcDialOption grpc.DialOption, fn func(masterClient master_pb.SeaweedClient) error) error { return pb.WithCachedGrpcClient(func(grpcConnection *grpc.ClientConn) error { client := master_pb.NewSeaweedClient(grpcConnection) return fn(client) - }, masterGrpcAddress, grpcDialOption) - -} - -func WithFilerServerClient(filerServer string, grpcDialOption grpc.DialOption, fn func(masterClient filer_pb.SeaweedFilerClient) error) error { - - filerGrpcAddress, parseErr := pb.ParseServerToGrpcAddress(filerServer) - if parseErr != nil { - return fmt.Errorf("failed to parse filer %v: %v", filerGrpcAddress, parseErr) - } - - return pb.WithCachedGrpcClient(func(grpcConnection *grpc.ClientConn) error { - client := filer_pb.NewSeaweedFilerClient(grpcConnection) - return fn(client) - }, filerGrpcAddress, grpcDialOption) + }, masterServer.ToGrpcAddress(), grpcDialOption) } diff --git a/weed/operation/lookup.go b/weed/operation/lookup.go index 8717f6b36..daf8cd152 100644 --- a/weed/operation/lookup.go +++ b/weed/operation/lookup.go @@ -4,6 +4,7 @@ import ( "context" "errors" "fmt" + "github.com/chrislusf/seaweedfs/weed/pb" "google.golang.org/grpc" "math/rand" "strings" @@ -15,7 +16,13 @@ import ( type Location struct { Url string `json:"url,omitempty"` PublicUrl string `json:"publicUrl,omitempty"` + GrpcPort int `json:"grpcPort,omitempty"` } + +func (l *Location) ServerAddress() pb.ServerAddress { + return pb.NewServerAddressWithGrpcPort(l.Url, l.GrpcPort) +} + type LookupResult struct { VolumeOrFileId string `json:"volumeOrFileId,omitempty"` Locations []Location `json:"locations,omitempty"` @@ -89,6 +96,7 @@ func LookupVolumeIds(masterFn GetMasterFn, grpcDialOption grpc.DialOption, vids locations = append(locations, Location{ Url: loc.Url, PublicUrl: loc.PublicUrl, + GrpcPort: int(loc.GrpcPort), }) } if vidLocations.Error != "" { diff --git a/weed/operation/submit.go b/weed/operation/submit.go index 80bc6fcb4..648df174a 100644 --- a/weed/operation/submit.go +++ b/weed/operation/submit.go @@ -1,6 +1,7 @@ package operation import ( + "github.com/chrislusf/seaweedfs/weed/pb" "io" "mime" "net/url" @@ -39,7 +40,7 @@ type SubmitResult struct { Error string `json:"error,omitempty"` } -type GetMasterFn func() string +type GetMasterFn func() pb.ServerAddress func SubmitFiles(masterFn GetMasterFn, grpcDialOption grpc.DialOption, files []FilePart, replication string, collection string, dataCenter string, ttl string, diskType string, maxMB int, usePublicUrl bool) ([]SubmitResult, error) { results := make([]SubmitResult, len(files)) diff --git a/weed/operation/sync_volume.go b/weed/operation/sync_volume.go index 5562f12ab..fdd22ac85 100644 --- a/weed/operation/sync_volume.go +++ b/weed/operation/sync_volume.go @@ -2,11 +2,12 @@ package operation import ( "context" + "github.com/chrislusf/seaweedfs/weed/pb" "github.com/chrislusf/seaweedfs/weed/pb/volume_server_pb" "google.golang.org/grpc" ) -func GetVolumeSyncStatus(server string, grpcDialOption grpc.DialOption, vid uint32) (resp *volume_server_pb.VolumeSyncStatusResponse, err error) { +func GetVolumeSyncStatus(server pb.ServerAddress, grpcDialOption grpc.DialOption, vid uint32) (resp *volume_server_pb.VolumeSyncStatusResponse, err error) { WithVolumeServerClient(server, grpcDialOption, func(client volume_server_pb.VolumeServerClient) error { diff --git a/weed/operation/tail_volume.go b/weed/operation/tail_volume.go index e3f2c0664..bedeeb3b5 100644 --- a/weed/operation/tail_volume.go +++ b/weed/operation/tail_volume.go @@ -3,6 +3,7 @@ package operation import ( "context" "fmt" + "github.com/chrislusf/seaweedfs/weed/pb" "io" "google.golang.org/grpc" @@ -21,12 +22,12 @@ func TailVolume(masterFn GetMasterFn, grpcDialOption grpc.DialOption, vid needle return fmt.Errorf("unable to locate volume %d", vid) } - volumeServer := lookup.Locations[0].Url + volumeServer := lookup.Locations[0].ServerAddress() return TailVolumeFromSource(volumeServer, grpcDialOption, vid, sinceNs, timeoutSeconds, fn) } -func TailVolumeFromSource(volumeServer string, grpcDialOption grpc.DialOption, vid needle.VolumeId, sinceNs uint64, idleTimeoutSeconds int, fn func(n *needle.Needle) error) error { +func TailVolumeFromSource(volumeServer pb.ServerAddress, grpcDialOption grpc.DialOption, vid needle.VolumeId, sinceNs uint64, idleTimeoutSeconds int, fn func(n *needle.Needle) error) error { return WithVolumeServerClient(volumeServer, grpcDialOption, func(client volume_server_pb.VolumeServerClient) error { ctx, cancel := context.WithCancel(context.Background()) defer cancel() diff --git a/weed/pb/filer.proto b/weed/pb/filer.proto index 7b1838565..bb4b6cc15 100644 --- a/weed/pb/filer.proto +++ b/weed/pb/filer.proto @@ -238,13 +238,12 @@ message AssignVolumeRequest { message AssignVolumeResponse { string file_id = 1; - string url = 2; - string public_url = 3; int32 count = 4; string auth = 5; string collection = 6; string replication = 7; string error = 8; + Location location = 9; } message LookupVolumeRequest { @@ -258,6 +257,7 @@ message Locations { message Location { string url = 1; string public_url = 2; + uint32 grpc_port = 3; } message LookupVolumeResponse { map locations_map = 1; diff --git a/weed/pb/filer_pb/filer.pb.go b/weed/pb/filer_pb/filer.pb.go index 240338384..088c1ef17 100644 --- a/weed/pb/filer_pb/filer.pb.go +++ b/weed/pb/filer_pb/filer.pb.go @@ -1689,14 +1689,13 @@ type AssignVolumeResponse struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - FileId string `protobuf:"bytes,1,opt,name=file_id,json=fileId,proto3" json:"file_id,omitempty"` - Url string `protobuf:"bytes,2,opt,name=url,proto3" json:"url,omitempty"` - PublicUrl string `protobuf:"bytes,3,opt,name=public_url,json=publicUrl,proto3" json:"public_url,omitempty"` - Count int32 `protobuf:"varint,4,opt,name=count,proto3" json:"count,omitempty"` - Auth string `protobuf:"bytes,5,opt,name=auth,proto3" json:"auth,omitempty"` - Collection string `protobuf:"bytes,6,opt,name=collection,proto3" json:"collection,omitempty"` - Replication string `protobuf:"bytes,7,opt,name=replication,proto3" json:"replication,omitempty"` - Error string `protobuf:"bytes,8,opt,name=error,proto3" json:"error,omitempty"` + FileId string `protobuf:"bytes,1,opt,name=file_id,json=fileId,proto3" json:"file_id,omitempty"` + Count int32 `protobuf:"varint,4,opt,name=count,proto3" json:"count,omitempty"` + Auth string `protobuf:"bytes,5,opt,name=auth,proto3" json:"auth,omitempty"` + Collection string `protobuf:"bytes,6,opt,name=collection,proto3" json:"collection,omitempty"` + Replication string `protobuf:"bytes,7,opt,name=replication,proto3" json:"replication,omitempty"` + Error string `protobuf:"bytes,8,opt,name=error,proto3" json:"error,omitempty"` + Location *Location `protobuf:"bytes,9,opt,name=location,proto3" json:"location,omitempty"` } func (x *AssignVolumeResponse) Reset() { @@ -1738,20 +1737,6 @@ func (x *AssignVolumeResponse) GetFileId() string { return "" } -func (x *AssignVolumeResponse) GetUrl() string { - if x != nil { - return x.Url - } - return "" -} - -func (x *AssignVolumeResponse) GetPublicUrl() string { - if x != nil { - return x.PublicUrl - } - return "" -} - func (x *AssignVolumeResponse) GetCount() int32 { if x != nil { return x.Count @@ -1787,6 +1772,13 @@ func (x *AssignVolumeResponse) GetError() string { return "" } +func (x *AssignVolumeResponse) GetLocation() *Location { + if x != nil { + return x.Location + } + return nil +} + type LookupVolumeRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -1888,6 +1880,7 @@ type Location struct { Url string `protobuf:"bytes,1,opt,name=url,proto3" json:"url,omitempty"` PublicUrl string `protobuf:"bytes,2,opt,name=public_url,json=publicUrl,proto3" json:"public_url,omitempty"` + GrpcPort uint32 `protobuf:"varint,3,opt,name=grpc_port,json=grpcPort,proto3" json:"grpc_port,omitempty"` } func (x *Location) Reset() { @@ -1936,6 +1929,13 @@ func (x *Location) GetPublicUrl() string { return "" } +func (x *Location) GetGrpcPort() uint32 { + if x != nil { + return x.GrpcPort + } + return 0 +} + type LookupVolumeResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -3690,316 +3690,318 @@ var file_filer_proto_rawDesc = []byte{ 0x68, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x12, 0x12, 0x0a, 0x04, 0x72, 0x61, 0x63, 0x6b, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x72, 0x61, 0x63, 0x6b, 0x12, 0x1b, 0x0a, 0x09, 0x64, 0x69, 0x73, 0x6b, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x08, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x64, 0x69, 0x73, 0x6b, 0x54, 0x79, 0x70, 0x65, 0x22, 0xe2, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x64, 0x69, 0x73, 0x6b, 0x54, 0x79, 0x70, 0x65, 0x22, 0xe1, 0x01, 0x0a, 0x14, 0x41, 0x73, 0x73, 0x69, 0x67, 0x6e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x17, 0x0a, 0x07, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x66, 0x69, 0x6c, 0x65, 0x49, 0x64, - 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, - 0x72, 0x6c, 0x12, 0x1d, 0x0a, 0x0a, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x75, 0x72, 0x6c, - 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x55, 0x72, - 0x6c, 0x12, 0x14, 0x0a, 0x05, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, - 0x52, 0x05, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x61, 0x75, 0x74, 0x68, 0x18, - 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x61, 0x75, 0x74, 0x68, 0x12, 0x1e, 0x0a, 0x0a, 0x63, - 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x0a, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x20, 0x0a, 0x0b, 0x72, - 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x0b, 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x14, 0x0a, - 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x72, - 0x72, 0x6f, 0x72, 0x22, 0x34, 0x0a, 0x13, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x56, 0x6f, 0x6c, - 0x75, 0x6d, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x76, 0x6f, - 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x09, - 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x49, 0x64, 0x73, 0x22, 0x3d, 0x0a, 0x09, 0x4c, 0x6f, 0x63, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x30, 0x0a, 0x09, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x66, 0x69, 0x6c, 0x65, - 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x4c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x09, 0x6c, - 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x3b, 0x0a, 0x08, 0x4c, 0x6f, 0x63, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x03, 0x75, 0x72, 0x6c, 0x12, 0x1d, 0x0a, 0x0a, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, - 0x5f, 0x75, 0x72, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x70, 0x75, 0x62, 0x6c, - 0x69, 0x63, 0x55, 0x72, 0x6c, 0x22, 0xc3, 0x01, 0x0a, 0x14, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, - 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x55, - 0x0a, 0x0d, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x5f, 0x6d, 0x61, 0x70, 0x18, - 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x30, 0x2e, 0x66, 0x69, 0x6c, 0x65, 0x72, 0x5f, 0x70, 0x62, - 0x2e, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x4c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x4d, - 0x61, 0x70, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0c, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x73, 0x4d, 0x61, 0x70, 0x1a, 0x54, 0x0a, 0x11, 0x4c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x73, 0x4d, 0x61, 0x70, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, - 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x29, 0x0a, 0x05, - 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x66, 0x69, - 0x6c, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x4c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, - 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x20, 0x0a, 0x0a, 0x43, - 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, - 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x7b, 0x0a, - 0x15, 0x43, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x4c, 0x69, 0x73, 0x74, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x34, 0x0a, 0x16, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, - 0x65, 0x5f, 0x6e, 0x6f, 0x72, 0x6d, 0x61, 0x6c, 0x5f, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x73, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x14, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x4e, - 0x6f, 0x72, 0x6d, 0x61, 0x6c, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x73, 0x12, 0x2c, 0x0a, 0x12, - 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x5f, 0x65, 0x63, 0x5f, 0x76, 0x6f, 0x6c, 0x75, 0x6d, - 0x65, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, - 0x65, 0x45, 0x63, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x73, 0x22, 0x50, 0x0a, 0x16, 0x43, 0x6f, - 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x36, 0x0a, 0x0b, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, - 0x6f, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x66, 0x69, 0x6c, 0x65, - 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x43, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, - 0x0b, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x39, 0x0a, 0x17, - 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x43, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1e, 0x0a, 0x0a, 0x63, 0x6f, 0x6c, 0x6c, 0x65, - 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x63, 0x6f, 0x6c, - 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x1a, 0x0a, 0x18, 0x44, 0x65, 0x6c, 0x65, 0x74, - 0x65, 0x43, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x22, 0x84, 0x01, 0x0a, 0x11, 0x53, 0x74, 0x61, 0x74, 0x69, 0x73, 0x74, 0x69, - 0x63, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x20, 0x0a, 0x0b, 0x72, 0x65, 0x70, - 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, - 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1e, 0x0a, 0x0a, 0x63, - 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x0a, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x10, 0x0a, 0x03, 0x74, - 0x74, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x74, 0x74, 0x6c, 0x12, 0x1b, 0x0a, - 0x09, 0x64, 0x69, 0x73, 0x6b, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x08, 0x64, 0x69, 0x73, 0x6b, 0x54, 0x79, 0x70, 0x65, 0x22, 0x6f, 0x0a, 0x12, 0x53, 0x74, - 0x61, 0x74, 0x69, 0x73, 0x74, 0x69, 0x63, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x12, 0x1d, 0x0a, 0x0a, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x04, - 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x53, 0x69, 0x7a, 0x65, 0x12, - 0x1b, 0x0a, 0x09, 0x75, 0x73, 0x65, 0x64, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x05, 0x20, 0x01, - 0x28, 0x04, 0x52, 0x08, 0x75, 0x73, 0x65, 0x64, 0x53, 0x69, 0x7a, 0x65, 0x12, 0x1d, 0x0a, 0x0a, - 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x04, - 0x52, 0x09, 0x66, 0x69, 0x6c, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0x1e, 0x0a, 0x1c, 0x47, - 0x65, 0x74, 0x46, 0x69, 0x6c, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0xfd, 0x02, 0x0a, 0x1d, - 0x47, 0x65, 0x74, 0x46, 0x69, 0x6c, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x18, 0x0a, - 0x07, 0x6d, 0x61, 0x73, 0x74, 0x65, 0x72, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, - 0x6d, 0x61, 0x73, 0x74, 0x65, 0x72, 0x73, 0x12, 0x20, 0x0a, 0x0b, 0x72, 0x65, 0x70, 0x6c, 0x69, - 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x72, 0x65, - 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1e, 0x0a, 0x0a, 0x63, 0x6f, 0x6c, - 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x63, - 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x15, 0x0a, 0x06, 0x6d, 0x61, 0x78, - 0x5f, 0x6d, 0x62, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x6d, 0x61, 0x78, 0x4d, 0x62, - 0x12, 0x1f, 0x0a, 0x0b, 0x64, 0x69, 0x72, 0x5f, 0x62, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x73, 0x18, - 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x64, 0x69, 0x72, 0x42, 0x75, 0x63, 0x6b, 0x65, 0x74, - 0x73, 0x12, 0x16, 0x0a, 0x06, 0x63, 0x69, 0x70, 0x68, 0x65, 0x72, 0x18, 0x07, 0x20, 0x01, 0x28, - 0x08, 0x52, 0x06, 0x63, 0x69, 0x70, 0x68, 0x65, 0x72, 0x12, 0x1c, 0x0a, 0x09, 0x73, 0x69, 0x67, - 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x05, 0x52, 0x09, 0x73, 0x69, - 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x12, 0x27, 0x0a, 0x0f, 0x6d, 0x65, 0x74, 0x72, 0x69, - 0x63, 0x73, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x0e, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, - 0x12, 0x30, 0x0a, 0x14, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x5f, 0x69, 0x6e, 0x74, 0x65, - 0x72, 0x76, 0x61, 0x6c, 0x5f, 0x73, 0x65, 0x63, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x05, 0x52, 0x12, - 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x53, - 0x65, 0x63, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x0b, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x1d, 0x0a, 0x0a, - 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x09, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, 0x22, 0xba, 0x01, 0x0a, 0x18, - 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, - 0x61, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x63, 0x6c, 0x69, 0x65, - 0x6e, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x63, - 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x70, 0x61, 0x74, - 0x68, 0x5f, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, - 0x70, 0x61, 0x74, 0x68, 0x50, 0x72, 0x65, 0x66, 0x69, 0x78, 0x12, 0x19, 0x0a, 0x08, 0x73, 0x69, - 0x6e, 0x63, 0x65, 0x5f, 0x6e, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x07, 0x73, 0x69, - 0x6e, 0x63, 0x65, 0x4e, 0x73, 0x12, 0x1c, 0x0a, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, - 0x72, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, - 0x75, 0x72, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x70, 0x61, 0x74, 0x68, 0x5f, 0x70, 0x72, 0x65, 0x66, - 0x69, 0x78, 0x65, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0c, 0x70, 0x61, 0x74, 0x68, - 0x50, 0x72, 0x65, 0x66, 0x69, 0x78, 0x65, 0x73, 0x22, 0x9a, 0x01, 0x0a, 0x19, 0x53, 0x75, 0x62, - 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, - 0x6f, 0x72, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x64, 0x69, 0x72, 0x65, 0x63, - 0x74, 0x6f, 0x72, 0x79, 0x12, 0x4a, 0x0a, 0x12, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x5f, 0x6e, 0x6f, - 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x1b, 0x2e, 0x66, 0x69, 0x6c, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x45, 0x76, 0x65, 0x6e, - 0x74, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x11, 0x65, - 0x76, 0x65, 0x6e, 0x74, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x12, 0x13, 0x0a, 0x05, 0x74, 0x73, 0x5f, 0x6e, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, - 0x04, 0x74, 0x73, 0x4e, 0x73, 0x22, 0x61, 0x0a, 0x08, 0x4c, 0x6f, 0x67, 0x45, 0x6e, 0x74, 0x72, - 0x79, 0x12, 0x13, 0x0a, 0x05, 0x74, 0x73, 0x5f, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, - 0x52, 0x04, 0x74, 0x73, 0x4e, 0x73, 0x12, 0x2c, 0x0a, 0x12, 0x70, 0x61, 0x72, 0x74, 0x69, 0x74, - 0x69, 0x6f, 0x6e, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x05, 0x52, 0x10, 0x70, 0x61, 0x72, 0x74, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x4b, 0x65, 0x79, - 0x48, 0x61, 0x73, 0x68, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x03, 0x20, 0x01, - 0x28, 0x0c, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x22, 0x65, 0x0a, 0x14, 0x4b, 0x65, 0x65, 0x70, - 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x65, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, - 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x67, 0x72, 0x70, 0x63, 0x5f, 0x70, 0x6f, 0x72, - 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x67, 0x72, 0x70, 0x63, 0x50, 0x6f, 0x72, - 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x18, 0x03, - 0x20, 0x03, 0x28, 0x09, 0x52, 0x09, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x22, - 0x17, 0x0a, 0x15, 0x4b, 0x65, 0x65, 0x70, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x65, 0x64, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x31, 0x0a, 0x13, 0x4c, 0x6f, 0x63, 0x61, - 0x74, 0x65, 0x42, 0x72, 0x6f, 0x6b, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, - 0x1a, 0x0a, 0x08, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x08, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x22, 0xcd, 0x01, 0x0a, 0x14, - 0x4c, 0x6f, 0x63, 0x61, 0x74, 0x65, 0x42, 0x72, 0x6f, 0x6b, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x66, 0x6f, 0x75, 0x6e, 0x64, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x08, 0x52, 0x05, 0x66, 0x6f, 0x75, 0x6e, 0x64, 0x12, 0x45, 0x0a, 0x09, 0x72, 0x65, - 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x27, 0x2e, - 0x66, 0x69, 0x6c, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x4c, 0x6f, 0x63, 0x61, 0x74, 0x65, 0x42, - 0x72, 0x6f, 0x6b, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x52, 0x65, - 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x09, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, - 0x73, 0x1a, 0x58, 0x0a, 0x08, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x25, 0x0a, - 0x0e, 0x67, 0x72, 0x70, 0x63, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x67, 0x72, 0x70, 0x63, 0x41, 0x64, 0x64, 0x72, 0x65, - 0x73, 0x73, 0x65, 0x73, 0x12, 0x25, 0x0a, 0x0e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, - 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0d, 0x72, 0x65, - 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0x20, 0x0a, 0x0c, 0x4b, - 0x76, 0x47, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x6b, - 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x22, 0x3b, 0x0a, - 0x0d, 0x4b, 0x76, 0x47, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x14, - 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x76, - 0x61, 0x6c, 0x75, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x22, 0x36, 0x0a, 0x0c, 0x4b, 0x76, - 0x50, 0x75, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, - 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, - 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x76, 0x61, 0x6c, - 0x75, 0x65, 0x22, 0x25, 0x0a, 0x0d, 0x4b, 0x76, 0x50, 0x75, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x22, 0xeb, 0x02, 0x0a, 0x09, 0x46, 0x69, - 0x6c, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, - 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, - 0x6e, 0x12, 0x3a, 0x0a, 0x09, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x02, - 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x66, 0x69, 0x6c, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, - 0x46, 0x69, 0x6c, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x2e, 0x50, 0x61, 0x74, 0x68, 0x43, 0x6f, - 0x6e, 0x66, 0x52, 0x09, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x1a, 0x87, 0x02, - 0x0a, 0x08, 0x50, 0x61, 0x74, 0x68, 0x43, 0x6f, 0x6e, 0x66, 0x12, 0x27, 0x0a, 0x0f, 0x6c, 0x6f, - 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x0e, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x72, 0x65, - 0x66, 0x69, 0x78, 0x12, 0x1e, 0x0a, 0x0a, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, - 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, - 0x69, 0x6f, 0x6e, 0x12, 0x20, 0x0a, 0x0b, 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x10, 0x0a, 0x03, 0x74, 0x74, 0x6c, 0x18, 0x04, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x03, 0x74, 0x74, 0x6c, 0x12, 0x1b, 0x0a, 0x09, 0x64, 0x69, 0x73, 0x6b, 0x5f, - 0x74, 0x79, 0x70, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x64, 0x69, 0x73, 0x6b, - 0x54, 0x79, 0x70, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x66, 0x73, 0x79, 0x6e, 0x63, 0x18, 0x06, 0x20, - 0x01, 0x28, 0x08, 0x52, 0x05, 0x66, 0x73, 0x79, 0x6e, 0x63, 0x12, 0x2e, 0x0a, 0x13, 0x76, 0x6f, - 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x67, 0x72, 0x6f, 0x77, 0x74, 0x68, 0x5f, 0x63, 0x6f, 0x75, 0x6e, - 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x11, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x47, - 0x72, 0x6f, 0x77, 0x74, 0x68, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x72, 0x65, - 0x61, 0x64, 0x5f, 0x6f, 0x6e, 0x6c, 0x79, 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x72, - 0x65, 0x61, 0x64, 0x4f, 0x6e, 0x6c, 0x79, 0x22, 0x4a, 0x0a, 0x16, 0x44, 0x6f, 0x77, 0x6e, 0x6c, - 0x6f, 0x61, 0x64, 0x54, 0x6f, 0x4c, 0x6f, 0x63, 0x61, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x12, - 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, - 0x61, 0x6d, 0x65, 0x22, 0x40, 0x0a, 0x17, 0x44, 0x6f, 0x77, 0x6e, 0x6c, 0x6f, 0x61, 0x64, 0x54, - 0x6f, 0x4c, 0x6f, 0x63, 0x61, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x25, - 0x0a, 0x05, 0x65, 0x6e, 0x74, 0x72, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, - 0x66, 0x69, 0x6c, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x05, - 0x65, 0x6e, 0x74, 0x72, 0x79, 0x32, 0xb6, 0x0d, 0x0a, 0x0c, 0x53, 0x65, 0x61, 0x77, 0x65, 0x65, - 0x64, 0x46, 0x69, 0x6c, 0x65, 0x72, 0x12, 0x67, 0x0a, 0x14, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, - 0x44, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x25, - 0x2e, 0x66, 0x69, 0x6c, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, - 0x44, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x26, 0x2e, 0x66, 0x69, 0x6c, 0x65, 0x72, 0x5f, 0x70, 0x62, - 0x2e, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x44, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, - 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, - 0x4e, 0x0a, 0x0b, 0x4c, 0x69, 0x73, 0x74, 0x45, 0x6e, 0x74, 0x72, 0x69, 0x65, 0x73, 0x12, 0x1c, - 0x2e, 0x66, 0x69, 0x6c, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x45, 0x6e, - 0x74, 0x72, 0x69, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x66, - 0x69, 0x6c, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x45, 0x6e, 0x74, 0x72, - 0x69, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x30, 0x01, 0x12, - 0x4c, 0x0a, 0x0b, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x1c, - 0x2e, 0x66, 0x69, 0x6c, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, - 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x66, - 0x69, 0x6c, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x45, 0x6e, - 0x74, 0x72, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x4c, 0x0a, - 0x0b, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x1c, 0x2e, 0x66, - 0x69, 0x6c, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x45, 0x6e, - 0x74, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x66, 0x69, 0x6c, - 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x45, 0x6e, 0x74, 0x72, - 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x52, 0x0a, 0x0d, 0x41, - 0x70, 0x70, 0x65, 0x6e, 0x64, 0x54, 0x6f, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x1e, 0x2e, 0x66, - 0x69, 0x6c, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x41, 0x70, 0x70, 0x65, 0x6e, 0x64, 0x54, 0x6f, - 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x66, - 0x69, 0x6c, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x41, 0x70, 0x70, 0x65, 0x6e, 0x64, 0x54, 0x6f, - 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, - 0x4c, 0x0a, 0x0b, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x1c, - 0x2e, 0x66, 0x69, 0x6c, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, - 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x66, - 0x69, 0x6c, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x45, 0x6e, - 0x74, 0x72, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x5e, 0x0a, - 0x11, 0x41, 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x52, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x45, 0x6e, 0x74, - 0x72, 0x79, 0x12, 0x22, 0x2e, 0x66, 0x69, 0x6c, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x41, 0x74, - 0x6f, 0x6d, 0x69, 0x63, 0x52, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x66, 0x69, 0x6c, 0x65, 0x72, 0x5f, 0x70, - 0x62, 0x2e, 0x41, 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x52, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x45, 0x6e, - 0x74, 0x72, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x4f, 0x0a, - 0x0c, 0x41, 0x73, 0x73, 0x69, 0x67, 0x6e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x12, 0x1d, 0x2e, - 0x66, 0x69, 0x6c, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x41, 0x73, 0x73, 0x69, 0x67, 0x6e, 0x56, - 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x66, - 0x69, 0x6c, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x41, 0x73, 0x73, 0x69, 0x67, 0x6e, 0x56, 0x6f, - 0x6c, 0x75, 0x6d, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x4f, - 0x0a, 0x0c, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x12, 0x1d, - 0x2e, 0x66, 0x69, 0x6c, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, - 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, - 0x66, 0x69, 0x6c, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x56, - 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, - 0x55, 0x0a, 0x0e, 0x43, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x4c, 0x69, 0x73, - 0x74, 0x12, 0x1f, 0x2e, 0x66, 0x69, 0x6c, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x43, 0x6f, 0x6c, + 0x12, 0x14, 0x0a, 0x05, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, + 0x05, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x61, 0x75, 0x74, 0x68, 0x18, 0x05, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x61, 0x75, 0x74, 0x68, 0x12, 0x1e, 0x0a, 0x0a, 0x63, 0x6f, + 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, + 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x20, 0x0a, 0x0b, 0x72, 0x65, + 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x0b, 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x14, 0x0a, 0x05, + 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x72, 0x72, + 0x6f, 0x72, 0x12, 0x2e, 0x0a, 0x08, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x09, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x66, 0x69, 0x6c, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, + 0x4c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x08, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x22, 0x34, 0x0a, 0x13, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x56, 0x6f, 0x6c, 0x75, + 0x6d, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x76, 0x6f, 0x6c, + 0x75, 0x6d, 0x65, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x09, 0x76, + 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x49, 0x64, 0x73, 0x22, 0x3d, 0x0a, 0x09, 0x4c, 0x6f, 0x63, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x30, 0x0a, 0x09, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x66, 0x69, 0x6c, 0x65, 0x72, + 0x5f, 0x70, 0x62, 0x2e, 0x4c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x09, 0x6c, 0x6f, + 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x58, 0x0a, 0x08, 0x4c, 0x6f, 0x63, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x03, 0x75, 0x72, 0x6c, 0x12, 0x1d, 0x0a, 0x0a, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, + 0x75, 0x72, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x70, 0x75, 0x62, 0x6c, 0x69, + 0x63, 0x55, 0x72, 0x6c, 0x12, 0x1b, 0x0a, 0x09, 0x67, 0x72, 0x70, 0x63, 0x5f, 0x70, 0x6f, 0x72, + 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x67, 0x72, 0x70, 0x63, 0x50, 0x6f, 0x72, + 0x74, 0x22, 0xc3, 0x01, 0x0a, 0x14, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x56, 0x6f, 0x6c, 0x75, + 0x6d, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x55, 0x0a, 0x0d, 0x6c, 0x6f, + 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x5f, 0x6d, 0x61, 0x70, 0x18, 0x01, 0x20, 0x03, 0x28, + 0x0b, 0x32, 0x30, 0x2e, 0x66, 0x69, 0x6c, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x4c, 0x6f, 0x6f, + 0x6b, 0x75, 0x70, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x2e, 0x4c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x4d, 0x61, 0x70, 0x45, 0x6e, + 0x74, 0x72, 0x79, 0x52, 0x0c, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x4d, 0x61, + 0x70, 0x1a, 0x54, 0x0a, 0x11, 0x4c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x4d, 0x61, + 0x70, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x29, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, + 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x66, 0x69, 0x6c, 0x65, 0x72, 0x5f, + 0x70, 0x62, 0x2e, 0x4c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x05, 0x76, 0x61, + 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x20, 0x0a, 0x0a, 0x43, 0x6f, 0x6c, 0x6c, 0x65, + 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x7b, 0x0a, 0x15, 0x43, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x66, 0x69, 0x6c, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x43, 0x6f, - 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x5b, 0x0a, 0x10, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, - 0x43, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x21, 0x2e, 0x66, 0x69, 0x6c, - 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x43, 0x6f, 0x6c, 0x6c, - 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, - 0x66, 0x69, 0x6c, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x43, - 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x22, 0x00, 0x12, 0x49, 0x0a, 0x0a, 0x53, 0x74, 0x61, 0x74, 0x69, 0x73, 0x74, 0x69, 0x63, - 0x73, 0x12, 0x1b, 0x2e, 0x66, 0x69, 0x6c, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x53, 0x74, 0x61, - 0x74, 0x69, 0x73, 0x74, 0x69, 0x63, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, - 0x2e, 0x66, 0x69, 0x6c, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x69, 0x73, - 0x74, 0x69, 0x63, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x6a, - 0x0a, 0x15, 0x47, 0x65, 0x74, 0x46, 0x69, 0x6c, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, - 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x26, 0x2e, 0x66, 0x69, 0x6c, 0x65, 0x72, 0x5f, - 0x70, 0x62, 0x2e, 0x47, 0x65, 0x74, 0x46, 0x69, 0x6c, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, - 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x27, 0x2e, 0x66, 0x69, 0x6c, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x47, 0x65, 0x74, 0x46, 0x69, + 0x73, 0x74, 0x12, 0x34, 0x0a, 0x16, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x5f, 0x6e, 0x6f, + 0x72, 0x6d, 0x61, 0x6c, 0x5f, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x73, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x08, 0x52, 0x14, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x4e, 0x6f, 0x72, 0x6d, 0x61, + 0x6c, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x73, 0x12, 0x2c, 0x0a, 0x12, 0x69, 0x6e, 0x63, 0x6c, + 0x75, 0x64, 0x65, 0x5f, 0x65, 0x63, 0x5f, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x73, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x45, 0x63, 0x56, + 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x73, 0x22, 0x50, 0x0a, 0x16, 0x43, 0x6f, 0x6c, 0x6c, 0x65, 0x63, + 0x74, 0x69, 0x6f, 0x6e, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x12, 0x36, 0x0a, 0x0b, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, + 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x66, 0x69, 0x6c, 0x65, 0x72, 0x5f, 0x70, 0x62, + 0x2e, 0x43, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0b, 0x63, 0x6f, 0x6c, + 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x39, 0x0a, 0x17, 0x44, 0x65, 0x6c, 0x65, + 0x74, 0x65, 0x43, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x12, 0x1e, 0x0a, 0x0a, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, + 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, + 0x69, 0x6f, 0x6e, 0x22, 0x1a, 0x0a, 0x18, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x43, 0x6f, 0x6c, + 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, + 0x84, 0x01, 0x0a, 0x11, 0x53, 0x74, 0x61, 0x74, 0x69, 0x73, 0x74, 0x69, 0x63, 0x73, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x20, 0x0a, 0x0b, 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x72, 0x65, 0x70, 0x6c, + 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1e, 0x0a, 0x0a, 0x63, 0x6f, 0x6c, 0x6c, 0x65, + 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x63, 0x6f, 0x6c, + 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x10, 0x0a, 0x03, 0x74, 0x74, 0x6c, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x74, 0x74, 0x6c, 0x12, 0x1b, 0x0a, 0x09, 0x64, 0x69, 0x73, + 0x6b, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x64, 0x69, + 0x73, 0x6b, 0x54, 0x79, 0x70, 0x65, 0x22, 0x6f, 0x0a, 0x12, 0x53, 0x74, 0x61, 0x74, 0x69, 0x73, + 0x74, 0x69, 0x63, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1d, 0x0a, 0x0a, + 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, + 0x52, 0x09, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x53, 0x69, 0x7a, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x75, + 0x73, 0x65, 0x64, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x04, 0x52, 0x08, + 0x75, 0x73, 0x65, 0x64, 0x53, 0x69, 0x7a, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x66, 0x69, 0x6c, 0x65, + 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x66, 0x69, + 0x6c, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0x1e, 0x0a, 0x1c, 0x47, 0x65, 0x74, 0x46, 0x69, 0x6c, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x60, 0x0a, 0x11, 0x53, 0x75, - 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, - 0x22, 0x2e, 0x66, 0x69, 0x6c, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x53, 0x75, 0x62, 0x73, 0x63, - 0x72, 0x69, 0x62, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x66, 0x69, 0x6c, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x53, - 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x30, 0x01, 0x12, 0x65, 0x0a, 0x16, - 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x4c, 0x6f, 0x63, 0x61, 0x6c, 0x4d, 0x65, - 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x22, 0x2e, 0x66, 0x69, 0x6c, 0x65, 0x72, 0x5f, 0x70, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0xfd, 0x02, 0x0a, 0x1d, 0x47, 0x65, 0x74, 0x46, + 0x69, 0x6c, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x61, 0x73, + 0x74, 0x65, 0x72, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x61, 0x73, 0x74, + 0x65, 0x72, 0x73, 0x12, 0x20, 0x0a, 0x0b, 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1e, 0x0a, 0x0a, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, + 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x63, 0x6f, 0x6c, 0x6c, 0x65, + 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x15, 0x0a, 0x06, 0x6d, 0x61, 0x78, 0x5f, 0x6d, 0x62, 0x18, + 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x6d, 0x61, 0x78, 0x4d, 0x62, 0x12, 0x1f, 0x0a, 0x0b, + 0x64, 0x69, 0x72, 0x5f, 0x62, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x0a, 0x64, 0x69, 0x72, 0x42, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x73, 0x12, 0x16, 0x0a, + 0x06, 0x63, 0x69, 0x70, 0x68, 0x65, 0x72, 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x63, + 0x69, 0x70, 0x68, 0x65, 0x72, 0x12, 0x1c, 0x0a, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, + 0x72, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x05, 0x52, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, + 0x75, 0x72, 0x65, 0x12, 0x27, 0x0a, 0x0f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x5f, 0x61, + 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x6d, 0x65, + 0x74, 0x72, 0x69, 0x63, 0x73, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x30, 0x0a, 0x14, + 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, + 0x5f, 0x73, 0x65, 0x63, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x05, 0x52, 0x12, 0x6d, 0x65, 0x74, 0x72, + 0x69, 0x63, 0x73, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x53, 0x65, 0x63, 0x12, 0x18, + 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x6c, 0x75, 0x73, + 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x6c, + 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, 0x22, 0xba, 0x01, 0x0a, 0x18, 0x53, 0x75, 0x62, 0x73, + 0x63, 0x72, 0x69, 0x62, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x5f, 0x6e, + 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x63, 0x6c, 0x69, 0x65, 0x6e, + 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x70, 0x61, 0x74, 0x68, 0x5f, 0x70, 0x72, + 0x65, 0x66, 0x69, 0x78, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x70, 0x61, 0x74, 0x68, + 0x50, 0x72, 0x65, 0x66, 0x69, 0x78, 0x12, 0x19, 0x0a, 0x08, 0x73, 0x69, 0x6e, 0x63, 0x65, 0x5f, + 0x6e, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x07, 0x73, 0x69, 0x6e, 0x63, 0x65, 0x4e, + 0x73, 0x12, 0x1c, 0x0a, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x18, 0x04, + 0x20, 0x01, 0x28, 0x05, 0x52, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x12, + 0x23, 0x0a, 0x0d, 0x70, 0x61, 0x74, 0x68, 0x5f, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x65, 0x73, + 0x18, 0x06, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0c, 0x70, 0x61, 0x74, 0x68, 0x50, 0x72, 0x65, 0x66, + 0x69, 0x78, 0x65, 0x73, 0x22, 0x9a, 0x01, 0x0a, 0x19, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, + 0x62, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, + 0x12, 0x4a, 0x0a, 0x12, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x5f, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x69, + 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x66, + 0x69, 0x6c, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x4e, 0x6f, 0x74, + 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x11, 0x65, 0x76, 0x65, 0x6e, 0x74, + 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x13, 0x0a, 0x05, + 0x74, 0x73, 0x5f, 0x6e, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x04, 0x74, 0x73, 0x4e, + 0x73, 0x22, 0x61, 0x0a, 0x08, 0x4c, 0x6f, 0x67, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x13, 0x0a, + 0x05, 0x74, 0x73, 0x5f, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x04, 0x74, 0x73, + 0x4e, 0x73, 0x12, 0x2c, 0x0a, 0x12, 0x70, 0x61, 0x72, 0x74, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x5f, + 0x6b, 0x65, 0x79, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x10, + 0x70, 0x61, 0x72, 0x74, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x4b, 0x65, 0x79, 0x48, 0x61, 0x73, 0x68, + 0x12, 0x12, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, + 0x64, 0x61, 0x74, 0x61, 0x22, 0x65, 0x0a, 0x14, 0x4b, 0x65, 0x65, 0x70, 0x43, 0x6f, 0x6e, 0x6e, + 0x65, 0x63, 0x74, 0x65, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, + 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, + 0x12, 0x1b, 0x0a, 0x09, 0x67, 0x72, 0x70, 0x63, 0x5f, 0x70, 0x6f, 0x72, 0x74, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x0d, 0x52, 0x08, 0x67, 0x72, 0x70, 0x63, 0x50, 0x6f, 0x72, 0x74, 0x12, 0x1c, 0x0a, + 0x09, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, + 0x52, 0x09, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x22, 0x17, 0x0a, 0x15, 0x4b, + 0x65, 0x65, 0x70, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x65, 0x64, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x31, 0x0a, 0x13, 0x4c, 0x6f, 0x63, 0x61, 0x74, 0x65, 0x42, 0x72, + 0x6f, 0x6b, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x72, + 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x72, + 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x22, 0xcd, 0x01, 0x0a, 0x14, 0x4c, 0x6f, 0x63, 0x61, + 0x74, 0x65, 0x42, 0x72, 0x6f, 0x6b, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x12, 0x14, 0x0a, 0x05, 0x66, 0x6f, 0x75, 0x6e, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, + 0x05, 0x66, 0x6f, 0x75, 0x6e, 0x64, 0x12, 0x45, 0x0a, 0x09, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, + 0x63, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x66, 0x69, 0x6c, 0x65, + 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x4c, 0x6f, 0x63, 0x61, 0x74, 0x65, 0x42, 0x72, 0x6f, 0x6b, 0x65, + 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, + 0x63, 0x65, 0x52, 0x09, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x1a, 0x58, 0x0a, + 0x08, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x25, 0x0a, 0x0e, 0x67, 0x72, 0x70, + 0x63, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x0d, 0x67, 0x72, 0x70, 0x63, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, + 0x12, 0x25, 0x0a, 0x0e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x63, 0x6f, 0x75, + 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0d, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, + 0x63, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0x20, 0x0a, 0x0c, 0x4b, 0x76, 0x47, 0x65, 0x74, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x0c, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x22, 0x3b, 0x0a, 0x0d, 0x4b, 0x76, 0x47, + 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, + 0x6c, 0x75, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, + 0x12, 0x14, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x22, 0x36, 0x0a, 0x0c, 0x4b, 0x76, 0x50, 0x75, 0x74, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x0c, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, + 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x25, + 0x0a, 0x0d, 0x4b, 0x76, 0x50, 0x75, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0x14, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, + 0x65, 0x72, 0x72, 0x6f, 0x72, 0x22, 0xeb, 0x02, 0x0a, 0x09, 0x46, 0x69, 0x6c, 0x65, 0x72, 0x43, + 0x6f, 0x6e, 0x66, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x05, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x3a, 0x0a, + 0x09, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x1c, 0x2e, 0x66, 0x69, 0x6c, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x46, 0x69, 0x6c, 0x65, + 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x2e, 0x50, 0x61, 0x74, 0x68, 0x43, 0x6f, 0x6e, 0x66, 0x52, 0x09, + 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x1a, 0x87, 0x02, 0x0a, 0x08, 0x50, 0x61, + 0x74, 0x68, 0x43, 0x6f, 0x6e, 0x66, 0x12, 0x27, 0x0a, 0x0f, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x5f, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x0e, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x72, 0x65, 0x66, 0x69, 0x78, 0x12, + 0x1e, 0x0a, 0x0a, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x0a, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, + 0x20, 0x0a, 0x0b, 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x12, 0x10, 0x0a, 0x03, 0x74, 0x74, 0x6c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, + 0x74, 0x74, 0x6c, 0x12, 0x1b, 0x0a, 0x09, 0x64, 0x69, 0x73, 0x6b, 0x5f, 0x74, 0x79, 0x70, 0x65, + 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x64, 0x69, 0x73, 0x6b, 0x54, 0x79, 0x70, 0x65, + 0x12, 0x14, 0x0a, 0x05, 0x66, 0x73, 0x79, 0x6e, 0x63, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, + 0x05, 0x66, 0x73, 0x79, 0x6e, 0x63, 0x12, 0x2e, 0x0a, 0x13, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, + 0x5f, 0x67, 0x72, 0x6f, 0x77, 0x74, 0x68, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x07, 0x20, + 0x01, 0x28, 0x0d, 0x52, 0x11, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x47, 0x72, 0x6f, 0x77, 0x74, + 0x68, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x72, 0x65, 0x61, 0x64, 0x5f, 0x6f, + 0x6e, 0x6c, 0x79, 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x72, 0x65, 0x61, 0x64, 0x4f, + 0x6e, 0x6c, 0x79, 0x22, 0x4a, 0x0a, 0x16, 0x44, 0x6f, 0x77, 0x6e, 0x6c, 0x6f, 0x61, 0x64, 0x54, + 0x6f, 0x4c, 0x6f, 0x63, 0x61, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1c, 0x0a, + 0x09, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x09, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x12, 0x12, 0x0a, 0x04, 0x6e, + 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x22, + 0x40, 0x0a, 0x17, 0x44, 0x6f, 0x77, 0x6e, 0x6c, 0x6f, 0x61, 0x64, 0x54, 0x6f, 0x4c, 0x6f, 0x63, + 0x61, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x25, 0x0a, 0x05, 0x65, 0x6e, + 0x74, 0x72, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x66, 0x69, 0x6c, 0x65, + 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x05, 0x65, 0x6e, 0x74, 0x72, + 0x79, 0x32, 0xb6, 0x0d, 0x0a, 0x0c, 0x53, 0x65, 0x61, 0x77, 0x65, 0x65, 0x64, 0x46, 0x69, 0x6c, + 0x65, 0x72, 0x12, 0x67, 0x0a, 0x14, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x44, 0x69, 0x72, 0x65, + 0x63, 0x74, 0x6f, 0x72, 0x79, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x25, 0x2e, 0x66, 0x69, 0x6c, + 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x44, 0x69, 0x72, 0x65, + 0x63, 0x74, 0x6f, 0x72, 0x79, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x26, 0x2e, 0x66, 0x69, 0x6c, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x4c, 0x6f, 0x6f, + 0x6b, 0x75, 0x70, 0x44, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x45, 0x6e, 0x74, 0x72, + 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x4e, 0x0a, 0x0b, 0x4c, + 0x69, 0x73, 0x74, 0x45, 0x6e, 0x74, 0x72, 0x69, 0x65, 0x73, 0x12, 0x1c, 0x2e, 0x66, 0x69, 0x6c, + 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x45, 0x6e, 0x74, 0x72, 0x69, 0x65, + 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x66, 0x69, 0x6c, 0x65, 0x72, + 0x5f, 0x70, 0x62, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x45, 0x6e, 0x74, 0x72, 0x69, 0x65, 0x73, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x30, 0x01, 0x12, 0x4c, 0x0a, 0x0b, 0x43, + 0x72, 0x65, 0x61, 0x74, 0x65, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x1c, 0x2e, 0x66, 0x69, 0x6c, + 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x45, 0x6e, 0x74, 0x72, + 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x66, 0x69, 0x6c, 0x65, 0x72, + 0x5f, 0x70, 0x62, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x4c, 0x0a, 0x0b, 0x55, 0x70, 0x64, + 0x61, 0x74, 0x65, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x1c, 0x2e, 0x66, 0x69, 0x6c, 0x65, 0x72, + 0x5f, 0x70, 0x62, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x66, 0x69, 0x6c, 0x65, 0x72, 0x5f, 0x70, + 0x62, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x52, 0x0a, 0x0d, 0x41, 0x70, 0x70, 0x65, 0x6e, + 0x64, 0x54, 0x6f, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x1e, 0x2e, 0x66, 0x69, 0x6c, 0x65, 0x72, + 0x5f, 0x70, 0x62, 0x2e, 0x41, 0x70, 0x70, 0x65, 0x6e, 0x64, 0x54, 0x6f, 0x45, 0x6e, 0x74, 0x72, + 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x66, 0x69, 0x6c, 0x65, 0x72, + 0x5f, 0x70, 0x62, 0x2e, 0x41, 0x70, 0x70, 0x65, 0x6e, 0x64, 0x54, 0x6f, 0x45, 0x6e, 0x74, 0x72, + 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x4c, 0x0a, 0x0b, 0x44, + 0x65, 0x6c, 0x65, 0x74, 0x65, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x1c, 0x2e, 0x66, 0x69, 0x6c, + 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x45, 0x6e, 0x74, 0x72, + 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x66, 0x69, 0x6c, 0x65, 0x72, + 0x5f, 0x70, 0x62, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x5e, 0x0a, 0x11, 0x41, 0x74, 0x6f, + 0x6d, 0x69, 0x63, 0x52, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x22, + 0x2e, 0x66, 0x69, 0x6c, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x41, 0x74, 0x6f, 0x6d, 0x69, 0x63, + 0x52, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x66, 0x69, 0x6c, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x41, 0x74, + 0x6f, 0x6d, 0x69, 0x63, 0x52, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x4f, 0x0a, 0x0c, 0x41, 0x73, 0x73, + 0x69, 0x67, 0x6e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x12, 0x1d, 0x2e, 0x66, 0x69, 0x6c, 0x65, + 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x41, 0x73, 0x73, 0x69, 0x67, 0x6e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, + 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x66, 0x69, 0x6c, 0x65, 0x72, + 0x5f, 0x70, 0x62, 0x2e, 0x41, 0x73, 0x73, 0x69, 0x67, 0x6e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x4f, 0x0a, 0x0c, 0x4c, 0x6f, + 0x6f, 0x6b, 0x75, 0x70, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x12, 0x1d, 0x2e, 0x66, 0x69, 0x6c, + 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x56, 0x6f, 0x6c, 0x75, + 0x6d, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x66, 0x69, 0x6c, 0x65, + 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x56, 0x6f, 0x6c, 0x75, 0x6d, + 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x55, 0x0a, 0x0e, 0x43, + 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x1f, 0x2e, + 0x66, 0x69, 0x6c, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x43, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, + 0x69, 0x6f, 0x6e, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, + 0x2e, 0x66, 0x69, 0x6c, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x43, 0x6f, 0x6c, 0x6c, 0x65, 0x63, + 0x74, 0x69, 0x6f, 0x6e, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x22, 0x00, 0x12, 0x5b, 0x0a, 0x10, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x43, 0x6f, 0x6c, 0x6c, + 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x21, 0x2e, 0x66, 0x69, 0x6c, 0x65, 0x72, 0x5f, 0x70, + 0x62, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x43, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, + 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x66, 0x69, 0x6c, 0x65, + 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x43, 0x6f, 0x6c, 0x6c, 0x65, + 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, + 0x49, 0x0a, 0x0a, 0x53, 0x74, 0x61, 0x74, 0x69, 0x73, 0x74, 0x69, 0x63, 0x73, 0x12, 0x1b, 0x2e, + 0x66, 0x69, 0x6c, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x69, 0x73, 0x74, + 0x69, 0x63, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x66, 0x69, 0x6c, + 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x69, 0x73, 0x74, 0x69, 0x63, 0x73, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x6a, 0x0a, 0x15, 0x47, 0x65, + 0x74, 0x46, 0x69, 0x6c, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x12, 0x26, 0x2e, 0x66, 0x69, 0x6c, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x47, + 0x65, 0x74, 0x46, 0x69, 0x6c, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x27, 0x2e, 0x66, 0x69, + 0x6c, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x47, 0x65, 0x74, 0x46, 0x69, 0x6c, 0x65, 0x72, 0x43, + 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x60, 0x0a, 0x11, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, + 0x69, 0x62, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x22, 0x2e, 0x66, 0x69, + 0x6c, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, + 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x23, 0x2e, 0x66, 0x69, 0x6c, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x53, 0x75, 0x62, 0x73, 0x63, + 0x72, 0x69, 0x62, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x30, 0x01, 0x12, 0x65, 0x0a, 0x16, 0x53, 0x75, 0x62, 0x73, + 0x63, 0x72, 0x69, 0x62, 0x65, 0x4c, 0x6f, 0x63, 0x61, 0x6c, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, + 0x74, 0x61, 0x12, 0x22, 0x2e, 0x66, 0x69, 0x6c, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x53, 0x75, + 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x66, 0x69, 0x6c, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x64, - 0x61, 0x74, 0x61, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x66, 0x69, 0x6c, - 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x4d, - 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, - 0x00, 0x30, 0x01, 0x12, 0x56, 0x0a, 0x0d, 0x4b, 0x65, 0x65, 0x70, 0x43, 0x6f, 0x6e, 0x6e, 0x65, - 0x63, 0x74, 0x65, 0x64, 0x12, 0x1e, 0x2e, 0x66, 0x69, 0x6c, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, - 0x4b, 0x65, 0x65, 0x70, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x65, 0x64, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x66, 0x69, 0x6c, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, - 0x4b, 0x65, 0x65, 0x70, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x65, 0x64, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x28, 0x01, 0x30, 0x01, 0x12, 0x4f, 0x0a, 0x0c, 0x4c, - 0x6f, 0x63, 0x61, 0x74, 0x65, 0x42, 0x72, 0x6f, 0x6b, 0x65, 0x72, 0x12, 0x1d, 0x2e, 0x66, 0x69, - 0x6c, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x4c, 0x6f, 0x63, 0x61, 0x74, 0x65, 0x42, 0x72, 0x6f, - 0x6b, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x66, 0x69, 0x6c, - 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x4c, 0x6f, 0x63, 0x61, 0x74, 0x65, 0x42, 0x72, 0x6f, 0x6b, - 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x3a, 0x0a, 0x05, - 0x4b, 0x76, 0x47, 0x65, 0x74, 0x12, 0x16, 0x2e, 0x66, 0x69, 0x6c, 0x65, 0x72, 0x5f, 0x70, 0x62, - 0x2e, 0x4b, 0x76, 0x47, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, - 0x66, 0x69, 0x6c, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x4b, 0x76, 0x47, 0x65, 0x74, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x3a, 0x0a, 0x05, 0x4b, 0x76, 0x50, 0x75, - 0x74, 0x12, 0x16, 0x2e, 0x66, 0x69, 0x6c, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x4b, 0x76, 0x50, - 0x75, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x66, 0x69, 0x6c, 0x65, - 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x4b, 0x76, 0x50, 0x75, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x22, 0x00, 0x12, 0x58, 0x0a, 0x0f, 0x44, 0x6f, 0x77, 0x6e, 0x6c, 0x6f, 0x61, 0x64, - 0x54, 0x6f, 0x4c, 0x6f, 0x63, 0x61, 0x6c, 0x12, 0x20, 0x2e, 0x66, 0x69, 0x6c, 0x65, 0x72, 0x5f, - 0x70, 0x62, 0x2e, 0x44, 0x6f, 0x77, 0x6e, 0x6c, 0x6f, 0x61, 0x64, 0x54, 0x6f, 0x4c, 0x6f, 0x63, - 0x61, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x66, 0x69, 0x6c, 0x65, - 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x44, 0x6f, 0x77, 0x6e, 0x6c, 0x6f, 0x61, 0x64, 0x54, 0x6f, 0x4c, - 0x6f, 0x63, 0x61, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, 0x4f, - 0x0a, 0x10, 0x73, 0x65, 0x61, 0x77, 0x65, 0x65, 0x64, 0x66, 0x73, 0x2e, 0x63, 0x6c, 0x69, 0x65, - 0x6e, 0x74, 0x42, 0x0a, 0x46, 0x69, 0x6c, 0x65, 0x72, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x5a, 0x2f, - 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x68, 0x72, 0x69, 0x73, - 0x6c, 0x75, 0x73, 0x66, 0x2f, 0x73, 0x65, 0x61, 0x77, 0x65, 0x65, 0x64, 0x66, 0x73, 0x2f, 0x77, - 0x65, 0x65, 0x64, 0x2f, 0x70, 0x62, 0x2f, 0x66, 0x69, 0x6c, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x62, - 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x61, 0x74, 0x61, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x30, 0x01, 0x12, + 0x56, 0x0a, 0x0d, 0x4b, 0x65, 0x65, 0x70, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x65, 0x64, + 0x12, 0x1e, 0x2e, 0x66, 0x69, 0x6c, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x4b, 0x65, 0x65, 0x70, + 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x65, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x1f, 0x2e, 0x66, 0x69, 0x6c, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x4b, 0x65, 0x65, 0x70, + 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x65, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x22, 0x00, 0x28, 0x01, 0x30, 0x01, 0x12, 0x4f, 0x0a, 0x0c, 0x4c, 0x6f, 0x63, 0x61, 0x74, + 0x65, 0x42, 0x72, 0x6f, 0x6b, 0x65, 0x72, 0x12, 0x1d, 0x2e, 0x66, 0x69, 0x6c, 0x65, 0x72, 0x5f, + 0x70, 0x62, 0x2e, 0x4c, 0x6f, 0x63, 0x61, 0x74, 0x65, 0x42, 0x72, 0x6f, 0x6b, 0x65, 0x72, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x66, 0x69, 0x6c, 0x65, 0x72, 0x5f, 0x70, + 0x62, 0x2e, 0x4c, 0x6f, 0x63, 0x61, 0x74, 0x65, 0x42, 0x72, 0x6f, 0x6b, 0x65, 0x72, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x3a, 0x0a, 0x05, 0x4b, 0x76, 0x47, 0x65, + 0x74, 0x12, 0x16, 0x2e, 0x66, 0x69, 0x6c, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x4b, 0x76, 0x47, + 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x66, 0x69, 0x6c, 0x65, + 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x4b, 0x76, 0x47, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x22, 0x00, 0x12, 0x3a, 0x0a, 0x05, 0x4b, 0x76, 0x50, 0x75, 0x74, 0x12, 0x16, 0x2e, + 0x66, 0x69, 0x6c, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x4b, 0x76, 0x50, 0x75, 0x74, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x66, 0x69, 0x6c, 0x65, 0x72, 0x5f, 0x70, 0x62, + 0x2e, 0x4b, 0x76, 0x50, 0x75, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, + 0x12, 0x58, 0x0a, 0x0f, 0x44, 0x6f, 0x77, 0x6e, 0x6c, 0x6f, 0x61, 0x64, 0x54, 0x6f, 0x4c, 0x6f, + 0x63, 0x61, 0x6c, 0x12, 0x20, 0x2e, 0x66, 0x69, 0x6c, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x44, + 0x6f, 0x77, 0x6e, 0x6c, 0x6f, 0x61, 0x64, 0x54, 0x6f, 0x4c, 0x6f, 0x63, 0x61, 0x6c, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x66, 0x69, 0x6c, 0x65, 0x72, 0x5f, 0x70, 0x62, + 0x2e, 0x44, 0x6f, 0x77, 0x6e, 0x6c, 0x6f, 0x61, 0x64, 0x54, 0x6f, 0x4c, 0x6f, 0x63, 0x61, 0x6c, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, 0x4f, 0x0a, 0x10, 0x73, 0x65, + 0x61, 0x77, 0x65, 0x65, 0x64, 0x66, 0x73, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x42, 0x0a, + 0x46, 0x69, 0x6c, 0x65, 0x72, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x5a, 0x2f, 0x67, 0x69, 0x74, 0x68, + 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x68, 0x72, 0x69, 0x73, 0x6c, 0x75, 0x73, 0x66, + 0x2f, 0x73, 0x65, 0x61, 0x77, 0x65, 0x65, 0x64, 0x66, 0x73, 0x2f, 0x77, 0x65, 0x65, 0x64, 0x2f, + 0x70, 0x62, 0x2f, 0x66, 0x69, 0x6c, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x33, } var ( @@ -4088,59 +4090,60 @@ var file_filer_proto_depIdxs = []int32{ 5, // 12: filer_pb.CreateEntryRequest.entry:type_name -> filer_pb.Entry 5, // 13: filer_pb.UpdateEntryRequest.entry:type_name -> filer_pb.Entry 8, // 14: filer_pb.AppendToEntryRequest.chunks:type_name -> filer_pb.FileChunk - 26, // 15: filer_pb.Locations.locations:type_name -> filer_pb.Location - 52, // 16: filer_pb.LookupVolumeResponse.locations_map:type_name -> filer_pb.LookupVolumeResponse.LocationsMapEntry - 28, // 17: filer_pb.CollectionListResponse.collections:type_name -> filer_pb.Collection - 7, // 18: filer_pb.SubscribeMetadataResponse.event_notification:type_name -> filer_pb.EventNotification - 53, // 19: filer_pb.LocateBrokerResponse.resources:type_name -> filer_pb.LocateBrokerResponse.Resource - 54, // 20: filer_pb.FilerConf.locations:type_name -> filer_pb.FilerConf.PathConf - 5, // 21: filer_pb.DownloadToLocalResponse.entry:type_name -> filer_pb.Entry - 25, // 22: filer_pb.LookupVolumeResponse.LocationsMapEntry.value:type_name -> filer_pb.Locations - 0, // 23: filer_pb.SeaweedFiler.LookupDirectoryEntry:input_type -> filer_pb.LookupDirectoryEntryRequest - 2, // 24: filer_pb.SeaweedFiler.ListEntries:input_type -> filer_pb.ListEntriesRequest - 12, // 25: filer_pb.SeaweedFiler.CreateEntry:input_type -> filer_pb.CreateEntryRequest - 14, // 26: filer_pb.SeaweedFiler.UpdateEntry:input_type -> filer_pb.UpdateEntryRequest - 16, // 27: filer_pb.SeaweedFiler.AppendToEntry:input_type -> filer_pb.AppendToEntryRequest - 18, // 28: filer_pb.SeaweedFiler.DeleteEntry:input_type -> filer_pb.DeleteEntryRequest - 20, // 29: filer_pb.SeaweedFiler.AtomicRenameEntry:input_type -> filer_pb.AtomicRenameEntryRequest - 22, // 30: filer_pb.SeaweedFiler.AssignVolume:input_type -> filer_pb.AssignVolumeRequest - 24, // 31: filer_pb.SeaweedFiler.LookupVolume:input_type -> filer_pb.LookupVolumeRequest - 29, // 32: filer_pb.SeaweedFiler.CollectionList:input_type -> filer_pb.CollectionListRequest - 31, // 33: filer_pb.SeaweedFiler.DeleteCollection:input_type -> filer_pb.DeleteCollectionRequest - 33, // 34: filer_pb.SeaweedFiler.Statistics:input_type -> filer_pb.StatisticsRequest - 35, // 35: filer_pb.SeaweedFiler.GetFilerConfiguration:input_type -> filer_pb.GetFilerConfigurationRequest - 37, // 36: filer_pb.SeaweedFiler.SubscribeMetadata:input_type -> filer_pb.SubscribeMetadataRequest - 37, // 37: filer_pb.SeaweedFiler.SubscribeLocalMetadata:input_type -> filer_pb.SubscribeMetadataRequest - 40, // 38: filer_pb.SeaweedFiler.KeepConnected:input_type -> filer_pb.KeepConnectedRequest - 42, // 39: filer_pb.SeaweedFiler.LocateBroker:input_type -> filer_pb.LocateBrokerRequest - 44, // 40: filer_pb.SeaweedFiler.KvGet:input_type -> filer_pb.KvGetRequest - 46, // 41: filer_pb.SeaweedFiler.KvPut:input_type -> filer_pb.KvPutRequest - 49, // 42: filer_pb.SeaweedFiler.DownloadToLocal:input_type -> filer_pb.DownloadToLocalRequest - 1, // 43: filer_pb.SeaweedFiler.LookupDirectoryEntry:output_type -> filer_pb.LookupDirectoryEntryResponse - 3, // 44: filer_pb.SeaweedFiler.ListEntries:output_type -> filer_pb.ListEntriesResponse - 13, // 45: filer_pb.SeaweedFiler.CreateEntry:output_type -> filer_pb.CreateEntryResponse - 15, // 46: filer_pb.SeaweedFiler.UpdateEntry:output_type -> filer_pb.UpdateEntryResponse - 17, // 47: filer_pb.SeaweedFiler.AppendToEntry:output_type -> filer_pb.AppendToEntryResponse - 19, // 48: filer_pb.SeaweedFiler.DeleteEntry:output_type -> filer_pb.DeleteEntryResponse - 21, // 49: filer_pb.SeaweedFiler.AtomicRenameEntry:output_type -> filer_pb.AtomicRenameEntryResponse - 23, // 50: filer_pb.SeaweedFiler.AssignVolume:output_type -> filer_pb.AssignVolumeResponse - 27, // 51: filer_pb.SeaweedFiler.LookupVolume:output_type -> filer_pb.LookupVolumeResponse - 30, // 52: filer_pb.SeaweedFiler.CollectionList:output_type -> filer_pb.CollectionListResponse - 32, // 53: filer_pb.SeaweedFiler.DeleteCollection:output_type -> filer_pb.DeleteCollectionResponse - 34, // 54: filer_pb.SeaweedFiler.Statistics:output_type -> filer_pb.StatisticsResponse - 36, // 55: filer_pb.SeaweedFiler.GetFilerConfiguration:output_type -> filer_pb.GetFilerConfigurationResponse - 38, // 56: filer_pb.SeaweedFiler.SubscribeMetadata:output_type -> filer_pb.SubscribeMetadataResponse - 38, // 57: filer_pb.SeaweedFiler.SubscribeLocalMetadata:output_type -> filer_pb.SubscribeMetadataResponse - 41, // 58: filer_pb.SeaweedFiler.KeepConnected:output_type -> filer_pb.KeepConnectedResponse - 43, // 59: filer_pb.SeaweedFiler.LocateBroker:output_type -> filer_pb.LocateBrokerResponse - 45, // 60: filer_pb.SeaweedFiler.KvGet:output_type -> filer_pb.KvGetResponse - 47, // 61: filer_pb.SeaweedFiler.KvPut:output_type -> filer_pb.KvPutResponse - 50, // 62: filer_pb.SeaweedFiler.DownloadToLocal:output_type -> filer_pb.DownloadToLocalResponse - 43, // [43:63] is the sub-list for method output_type - 23, // [23:43] is the sub-list for method input_type - 23, // [23:23] is the sub-list for extension type_name - 23, // [23:23] is the sub-list for extension extendee - 0, // [0:23] is the sub-list for field type_name + 26, // 15: filer_pb.AssignVolumeResponse.location:type_name -> filer_pb.Location + 26, // 16: filer_pb.Locations.locations:type_name -> filer_pb.Location + 52, // 17: filer_pb.LookupVolumeResponse.locations_map:type_name -> filer_pb.LookupVolumeResponse.LocationsMapEntry + 28, // 18: filer_pb.CollectionListResponse.collections:type_name -> filer_pb.Collection + 7, // 19: filer_pb.SubscribeMetadataResponse.event_notification:type_name -> filer_pb.EventNotification + 53, // 20: filer_pb.LocateBrokerResponse.resources:type_name -> filer_pb.LocateBrokerResponse.Resource + 54, // 21: filer_pb.FilerConf.locations:type_name -> filer_pb.FilerConf.PathConf + 5, // 22: filer_pb.DownloadToLocalResponse.entry:type_name -> filer_pb.Entry + 25, // 23: filer_pb.LookupVolumeResponse.LocationsMapEntry.value:type_name -> filer_pb.Locations + 0, // 24: filer_pb.SeaweedFiler.LookupDirectoryEntry:input_type -> filer_pb.LookupDirectoryEntryRequest + 2, // 25: filer_pb.SeaweedFiler.ListEntries:input_type -> filer_pb.ListEntriesRequest + 12, // 26: filer_pb.SeaweedFiler.CreateEntry:input_type -> filer_pb.CreateEntryRequest + 14, // 27: filer_pb.SeaweedFiler.UpdateEntry:input_type -> filer_pb.UpdateEntryRequest + 16, // 28: filer_pb.SeaweedFiler.AppendToEntry:input_type -> filer_pb.AppendToEntryRequest + 18, // 29: filer_pb.SeaweedFiler.DeleteEntry:input_type -> filer_pb.DeleteEntryRequest + 20, // 30: filer_pb.SeaweedFiler.AtomicRenameEntry:input_type -> filer_pb.AtomicRenameEntryRequest + 22, // 31: filer_pb.SeaweedFiler.AssignVolume:input_type -> filer_pb.AssignVolumeRequest + 24, // 32: filer_pb.SeaweedFiler.LookupVolume:input_type -> filer_pb.LookupVolumeRequest + 29, // 33: filer_pb.SeaweedFiler.CollectionList:input_type -> filer_pb.CollectionListRequest + 31, // 34: filer_pb.SeaweedFiler.DeleteCollection:input_type -> filer_pb.DeleteCollectionRequest + 33, // 35: filer_pb.SeaweedFiler.Statistics:input_type -> filer_pb.StatisticsRequest + 35, // 36: filer_pb.SeaweedFiler.GetFilerConfiguration:input_type -> filer_pb.GetFilerConfigurationRequest + 37, // 37: filer_pb.SeaweedFiler.SubscribeMetadata:input_type -> filer_pb.SubscribeMetadataRequest + 37, // 38: filer_pb.SeaweedFiler.SubscribeLocalMetadata:input_type -> filer_pb.SubscribeMetadataRequest + 40, // 39: filer_pb.SeaweedFiler.KeepConnected:input_type -> filer_pb.KeepConnectedRequest + 42, // 40: filer_pb.SeaweedFiler.LocateBroker:input_type -> filer_pb.LocateBrokerRequest + 44, // 41: filer_pb.SeaweedFiler.KvGet:input_type -> filer_pb.KvGetRequest + 46, // 42: filer_pb.SeaweedFiler.KvPut:input_type -> filer_pb.KvPutRequest + 49, // 43: filer_pb.SeaweedFiler.DownloadToLocal:input_type -> filer_pb.DownloadToLocalRequest + 1, // 44: filer_pb.SeaweedFiler.LookupDirectoryEntry:output_type -> filer_pb.LookupDirectoryEntryResponse + 3, // 45: filer_pb.SeaweedFiler.ListEntries:output_type -> filer_pb.ListEntriesResponse + 13, // 46: filer_pb.SeaweedFiler.CreateEntry:output_type -> filer_pb.CreateEntryResponse + 15, // 47: filer_pb.SeaweedFiler.UpdateEntry:output_type -> filer_pb.UpdateEntryResponse + 17, // 48: filer_pb.SeaweedFiler.AppendToEntry:output_type -> filer_pb.AppendToEntryResponse + 19, // 49: filer_pb.SeaweedFiler.DeleteEntry:output_type -> filer_pb.DeleteEntryResponse + 21, // 50: filer_pb.SeaweedFiler.AtomicRenameEntry:output_type -> filer_pb.AtomicRenameEntryResponse + 23, // 51: filer_pb.SeaweedFiler.AssignVolume:output_type -> filer_pb.AssignVolumeResponse + 27, // 52: filer_pb.SeaweedFiler.LookupVolume:output_type -> filer_pb.LookupVolumeResponse + 30, // 53: filer_pb.SeaweedFiler.CollectionList:output_type -> filer_pb.CollectionListResponse + 32, // 54: filer_pb.SeaweedFiler.DeleteCollection:output_type -> filer_pb.DeleteCollectionResponse + 34, // 55: filer_pb.SeaweedFiler.Statistics:output_type -> filer_pb.StatisticsResponse + 36, // 56: filer_pb.SeaweedFiler.GetFilerConfiguration:output_type -> filer_pb.GetFilerConfigurationResponse + 38, // 57: filer_pb.SeaweedFiler.SubscribeMetadata:output_type -> filer_pb.SubscribeMetadataResponse + 38, // 58: filer_pb.SeaweedFiler.SubscribeLocalMetadata:output_type -> filer_pb.SubscribeMetadataResponse + 41, // 59: filer_pb.SeaweedFiler.KeepConnected:output_type -> filer_pb.KeepConnectedResponse + 43, // 60: filer_pb.SeaweedFiler.LocateBroker:output_type -> filer_pb.LocateBrokerResponse + 45, // 61: filer_pb.SeaweedFiler.KvGet:output_type -> filer_pb.KvGetResponse + 47, // 62: filer_pb.SeaweedFiler.KvPut:output_type -> filer_pb.KvPutResponse + 50, // 63: filer_pb.SeaweedFiler.DownloadToLocal:output_type -> filer_pb.DownloadToLocalResponse + 44, // [44:64] is the sub-list for method output_type + 24, // [24:44] is the sub-list for method input_type + 24, // [24:24] is the sub-list for extension type_name + 24, // [24:24] is the sub-list for extension extendee + 0, // [0:24] is the sub-list for field type_name } func init() { file_filer_proto_init() } diff --git a/weed/pb/filer_pb_tail.go b/weed/pb/filer_pb_tail.go index ceaaa4f6d..74d1cca5e 100644 --- a/weed/pb/filer_pb_tail.go +++ b/weed/pb/filer_pb_tail.go @@ -12,7 +12,7 @@ import ( type ProcessMetadataFunc func(resp *filer_pb.SubscribeMetadataResponse) error -func FollowMetadata(filerAddress string, grpcDialOption grpc.DialOption, clientName string, +func FollowMetadata(filerAddress ServerAddress, grpcDialOption grpc.DialOption, clientName string, pathPrefix string, additionalPathPrefixes []string, lastTsNs int64, selfSignature int32, processEventFn ProcessMetadataFunc, fatalOnError bool) error { diff --git a/weed/pb/grpc_client_server.go b/weed/pb/grpc_client_server.go index f296cf983..0bd7b691e 100644 --- a/weed/pb/grpc_client_server.go +++ b/weed/pb/grpc_client_server.go @@ -136,20 +136,6 @@ func WithCachedGrpcClient(fn func(*grpc.ClientConn) error, address string, opts return executionErr } -func ParseServerToGrpcAddress(server string) (serverGrpcAddress string, err error) { - return ParseServerAddress(server, 10000) -} -func ParseServersToGrpcAddresses(servers []string) (serverGrpcAddresses []string, err error) { - for _, server := range servers { - if serverGrpcAddress, parseErr := ParseServerToGrpcAddress(server); parseErr == nil { - serverGrpcAddresses = append(serverGrpcAddresses, serverGrpcAddress) - } else { - return nil, parseErr - } - } - return -} - func ParseServerAddress(server string, deltaPort int) (newServerAddress string, err error) { host, port, parseErr := hostAndPort(server) @@ -198,27 +184,21 @@ func GrpcAddressToServerAddress(grpcAddress string) (serverAddress string) { return util.JoinHostPort(host, port) } -func WithMasterClient(master string, grpcDialOption grpc.DialOption, fn func(client master_pb.SeaweedClient) error) error { - - masterGrpcAddress, parseErr := ParseServerToGrpcAddress(master) - if parseErr != nil { - return fmt.Errorf("failed to parse master grpc %v: %v", master, parseErr) - } - +func WithMasterClient(master ServerAddress, grpcDialOption grpc.DialOption, fn func(client master_pb.SeaweedClient) error) error { return WithCachedGrpcClient(func(grpcConnection *grpc.ClientConn) error { client := master_pb.NewSeaweedClient(grpcConnection) return fn(client) - }, masterGrpcAddress, grpcDialOption) + }, master.ToGrpcAddress(), grpcDialOption) } -func WithOneOfGrpcMasterClients(masterGrpcAddresses []string, grpcDialOption grpc.DialOption, fn func(client master_pb.SeaweedClient) error) (err error) { +func WithOneOfGrpcMasterClients(masterGrpcAddresses []ServerAddress, grpcDialOption grpc.DialOption, fn func(client master_pb.SeaweedClient) error) (err error) { for _, masterGrpcAddress := range masterGrpcAddresses { err = WithCachedGrpcClient(func(grpcConnection *grpc.ClientConn) error { client := master_pb.NewSeaweedClient(grpcConnection) return fn(client) - }, masterGrpcAddress, grpcDialOption) + }, masterGrpcAddress.ToGrpcAddress(), grpcDialOption) if err == nil { return nil } @@ -236,37 +216,32 @@ func WithBrokerGrpcClient(brokerGrpcAddress string, grpcDialOption grpc.DialOpti } -func WithFilerClient(filer string, grpcDialOption grpc.DialOption, fn func(client filer_pb.SeaweedFilerClient) error) error { +func WithFilerClient(filer ServerAddress, grpcDialOption grpc.DialOption, fn func(client filer_pb.SeaweedFilerClient) error) error { - filerGrpcAddress, parseErr := ParseServerToGrpcAddress(filer) - if parseErr != nil { - return fmt.Errorf("failed to parse filer grpc %v: %v", filer, parseErr) - } - - return WithGrpcFilerClient(filerGrpcAddress, grpcDialOption, fn) + return WithGrpcFilerClient(filer, grpcDialOption, fn) } -func WithGrpcFilerClient(filerGrpcAddress string, grpcDialOption grpc.DialOption, fn func(client filer_pb.SeaweedFilerClient) error) error { +func WithGrpcFilerClient(filerGrpcAddress ServerAddress, grpcDialOption grpc.DialOption, fn func(client filer_pb.SeaweedFilerClient) error) error { return WithCachedGrpcClient(func(grpcConnection *grpc.ClientConn) error { client := filer_pb.NewSeaweedFilerClient(grpcConnection) return fn(client) - }, filerGrpcAddress, grpcDialOption) + }, filerGrpcAddress.ToGrpcAddress(), grpcDialOption) } -func WithOneOfGrpcFilerClients(filerGrpcAddresses []string, grpcDialOption grpc.DialOption, fn func(client filer_pb.SeaweedFilerClient) error) (err error) { +func WithOneOfGrpcFilerClients(filerAddresses []ServerAddress, grpcDialOption grpc.DialOption, fn func(client filer_pb.SeaweedFilerClient) error) (err error) { - for _, filerGrpcAddress := range filerGrpcAddresses { + for _, filerAddress := range filerAddresses { err = WithCachedGrpcClient(func(grpcConnection *grpc.ClientConn) error { client := filer_pb.NewSeaweedFilerClient(grpcConnection) return fn(client) - }, filerGrpcAddress, grpcDialOption) + }, filerAddress.ToGrpcAddress(), grpcDialOption) if err == nil { return nil } } return err -} +} \ No newline at end of file diff --git a/weed/pb/master.proto b/weed/pb/master.proto index 561c5ab98..fce3e04ec 100644 --- a/weed/pb/master.proto +++ b/weed/pb/master.proto @@ -62,6 +62,7 @@ message Heartbeat { bool has_no_ec_shards = 19; map max_volume_counts = 4; + uint32 grpc_port = 20; } @@ -127,7 +128,7 @@ message SuperBlockExtra { message KeepConnectedRequest { string name = 1; - uint32 grpc_port = 2; + string client_address = 3; } message VolumeLocation { @@ -137,6 +138,7 @@ message VolumeLocation { repeated uint32 deleted_vids = 4; string leader = 5; // optional when leader is not itself string data_center = 6; // optional when DataCenter is in use + uint32 grpc_port = 7; } message LookupVolumeRequest { @@ -156,6 +158,7 @@ message LookupVolumeResponse { message Location { string url = 1; string public_url = 2; + uint32 grpc_port = 3; } message AssignRequest { @@ -172,16 +175,11 @@ message AssignRequest { } message AssignResponse { string fid = 1; - string url = 2; - string public_url = 3; uint64 count = 4; string error = 5; string auth = 6; - message Replica { - string url = 1; - string public_url = 2; - } - repeated Replica replicas = 7; + repeated Location replicas = 7; + Location location = 8; } message StatisticsRequest { @@ -232,6 +230,7 @@ message DiskInfo { message DataNodeInfo { string id = 1; map diskInfos = 2; + uint32 grpc_port = 3; } message RackInfo { string id = 1; diff --git a/weed/pb/master_pb/master.pb.go b/weed/pb/master_pb/master.pb.go index 4ca4b4766..6f79479eb 100644 --- a/weed/pb/master_pb/master.pb.go +++ b/weed/pb/master_pb/master.pb.go @@ -53,6 +53,7 @@ type Heartbeat struct { DeletedEcShards []*VolumeEcShardInformationMessage `protobuf:"bytes,18,rep,name=deleted_ec_shards,json=deletedEcShards,proto3" json:"deleted_ec_shards,omitempty"` HasNoEcShards bool `protobuf:"varint,19,opt,name=has_no_ec_shards,json=hasNoEcShards,proto3" json:"has_no_ec_shards,omitempty"` MaxVolumeCounts map[string]uint32 `protobuf:"bytes,4,rep,name=max_volume_counts,json=maxVolumeCounts,proto3" json:"max_volume_counts,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"varint,2,opt,name=value,proto3"` + GrpcPort uint32 `protobuf:"varint,20,opt,name=grpc_port,json=grpcPort,proto3" json:"grpc_port,omitempty"` } func (x *Heartbeat) Reset() { @@ -199,6 +200,13 @@ func (x *Heartbeat) GetMaxVolumeCounts() map[string]uint32 { return nil } +func (x *Heartbeat) GetGrpcPort() uint32 { + if x != nil { + return x.GrpcPort + } + return 0 +} + type HeartbeatResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -748,8 +756,8 @@ type KeepConnectedRequest struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` - GrpcPort uint32 `protobuf:"varint,2,opt,name=grpc_port,json=grpcPort,proto3" json:"grpc_port,omitempty"` + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + ClientAddress string `protobuf:"bytes,3,opt,name=client_address,json=clientAddress,proto3" json:"client_address,omitempty"` } func (x *KeepConnectedRequest) Reset() { @@ -791,11 +799,11 @@ func (x *KeepConnectedRequest) GetName() string { return "" } -func (x *KeepConnectedRequest) GetGrpcPort() uint32 { +func (x *KeepConnectedRequest) GetClientAddress() string { if x != nil { - return x.GrpcPort + return x.ClientAddress } - return 0 + return "" } type VolumeLocation struct { @@ -809,6 +817,7 @@ type VolumeLocation struct { DeletedVids []uint32 `protobuf:"varint,4,rep,packed,name=deleted_vids,json=deletedVids,proto3" json:"deleted_vids,omitempty"` Leader string `protobuf:"bytes,5,opt,name=leader,proto3" json:"leader,omitempty"` // optional when leader is not itself DataCenter string `protobuf:"bytes,6,opt,name=data_center,json=dataCenter,proto3" json:"data_center,omitempty"` // optional when DataCenter is in use + GrpcPort uint32 `protobuf:"varint,7,opt,name=grpc_port,json=grpcPort,proto3" json:"grpc_port,omitempty"` } func (x *VolumeLocation) Reset() { @@ -885,6 +894,13 @@ func (x *VolumeLocation) GetDataCenter() string { return "" } +func (x *VolumeLocation) GetGrpcPort() uint32 { + if x != nil { + return x.GrpcPort + } + return 0 +} + type LookupVolumeRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -994,6 +1010,7 @@ type Location struct { Url string `protobuf:"bytes,1,opt,name=url,proto3" json:"url,omitempty"` PublicUrl string `protobuf:"bytes,2,opt,name=public_url,json=publicUrl,proto3" json:"public_url,omitempty"` + GrpcPort uint32 `protobuf:"varint,3,opt,name=grpc_port,json=grpcPort,proto3" json:"grpc_port,omitempty"` } func (x *Location) Reset() { @@ -1042,6 +1059,13 @@ func (x *Location) GetPublicUrl() string { return "" } +func (x *Location) GetGrpcPort() uint32 { + if x != nil { + return x.GrpcPort + } + return 0 +} + type AssignRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -1166,13 +1190,12 @@ type AssignResponse struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Fid string `protobuf:"bytes,1,opt,name=fid,proto3" json:"fid,omitempty"` - Url string `protobuf:"bytes,2,opt,name=url,proto3" json:"url,omitempty"` - PublicUrl string `protobuf:"bytes,3,opt,name=public_url,json=publicUrl,proto3" json:"public_url,omitempty"` - Count uint64 `protobuf:"varint,4,opt,name=count,proto3" json:"count,omitempty"` - Error string `protobuf:"bytes,5,opt,name=error,proto3" json:"error,omitempty"` - Auth string `protobuf:"bytes,6,opt,name=auth,proto3" json:"auth,omitempty"` - Replicas []*AssignResponse_Replica `protobuf:"bytes,7,rep,name=replicas,proto3" json:"replicas,omitempty"` + Fid string `protobuf:"bytes,1,opt,name=fid,proto3" json:"fid,omitempty"` + Count uint64 `protobuf:"varint,4,opt,name=count,proto3" json:"count,omitempty"` + Error string `protobuf:"bytes,5,opt,name=error,proto3" json:"error,omitempty"` + Auth string `protobuf:"bytes,6,opt,name=auth,proto3" json:"auth,omitempty"` + Replicas []*Location `protobuf:"bytes,7,rep,name=replicas,proto3" json:"replicas,omitempty"` + Location *Location `protobuf:"bytes,8,opt,name=location,proto3" json:"location,omitempty"` } func (x *AssignResponse) Reset() { @@ -1214,20 +1237,6 @@ func (x *AssignResponse) GetFid() string { return "" } -func (x *AssignResponse) GetUrl() string { - if x != nil { - return x.Url - } - return "" -} - -func (x *AssignResponse) GetPublicUrl() string { - if x != nil { - return x.PublicUrl - } - return "" -} - func (x *AssignResponse) GetCount() uint64 { if x != nil { return x.Count @@ -1249,13 +1258,20 @@ func (x *AssignResponse) GetAuth() string { return "" } -func (x *AssignResponse) GetReplicas() []*AssignResponse_Replica { +func (x *AssignResponse) GetReplicas() []*Location { if x != nil { return x.Replicas } return nil } +func (x *AssignResponse) GetLocation() *Location { + if x != nil { + return x.Location + } + return nil +} + type StatisticsRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -1740,6 +1756,7 @@ type DataNodeInfo struct { Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` DiskInfos map[string]*DiskInfo `protobuf:"bytes,2,rep,name=diskInfos,proto3" json:"diskInfos,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + GrpcPort uint32 `protobuf:"varint,3,opt,name=grpc_port,json=grpcPort,proto3" json:"grpc_port,omitempty"` } func (x *DataNodeInfo) Reset() { @@ -1788,6 +1805,13 @@ func (x *DataNodeInfo) GetDiskInfos() map[string]*DiskInfo { return nil } +func (x *DataNodeInfo) GetGrpcPort() uint32 { + if x != nil { + return x.GrpcPort + } + return 0 +} + type RackInfo struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -2845,61 +2869,6 @@ func (x *LookupVolumeResponse_VolumeIdLocation) GetAuth() string { return "" } -type AssignResponse_Replica struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Url string `protobuf:"bytes,1,opt,name=url,proto3" json:"url,omitempty"` - PublicUrl string `protobuf:"bytes,2,opt,name=public_url,json=publicUrl,proto3" json:"public_url,omitempty"` -} - -func (x *AssignResponse_Replica) Reset() { - *x = AssignResponse_Replica{} - if protoimpl.UnsafeEnabled { - mi := &file_master_proto_msgTypes[45] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *AssignResponse_Replica) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*AssignResponse_Replica) ProtoMessage() {} - -func (x *AssignResponse_Replica) ProtoReflect() protoreflect.Message { - mi := &file_master_proto_msgTypes[45] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use AssignResponse_Replica.ProtoReflect.Descriptor instead. -func (*AssignResponse_Replica) Descriptor() ([]byte, []int) { - return file_master_proto_rawDescGZIP(), []int{14, 0} -} - -func (x *AssignResponse_Replica) GetUrl() string { - if x != nil { - return x.Url - } - return "" -} - -func (x *AssignResponse_Replica) GetPublicUrl() string { - if x != nil { - return x.PublicUrl - } - return "" -} - type LookupEcVolumeResponse_EcShardIdLocation struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -2912,7 +2881,7 @@ type LookupEcVolumeResponse_EcShardIdLocation struct { func (x *LookupEcVolumeResponse_EcShardIdLocation) Reset() { *x = LookupEcVolumeResponse_EcShardIdLocation{} if protoimpl.UnsafeEnabled { - mi := &file_master_proto_msgTypes[50] + mi := &file_master_proto_msgTypes[49] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2925,7 +2894,7 @@ func (x *LookupEcVolumeResponse_EcShardIdLocation) String() string { func (*LookupEcVolumeResponse_EcShardIdLocation) ProtoMessage() {} func (x *LookupEcVolumeResponse_EcShardIdLocation) ProtoReflect() protoreflect.Message { - mi := &file_master_proto_msgTypes[50] + mi := &file_master_proto_msgTypes[49] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2959,7 +2928,7 @@ var File_master_proto protoreflect.FileDescriptor var file_master_proto_rawDesc = []byte{ 0x0a, 0x0c, 0x6d, 0x61, 0x73, 0x74, 0x65, 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x09, - 0x6d, 0x61, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x22, 0xfc, 0x06, 0x0a, 0x09, 0x48, 0x65, + 0x6d, 0x61, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x22, 0x99, 0x07, 0x0a, 0x09, 0x48, 0x65, 0x61, 0x72, 0x74, 0x62, 0x65, 0x61, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x70, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x6f, 0x72, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x04, 0x70, 0x6f, 0x72, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x70, @@ -3011,483 +2980,487 @@ var file_master_proto_rawDesc = []byte{ 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x48, 0x65, 0x61, 0x72, 0x74, 0x62, 0x65, 0x61, 0x74, 0x2e, 0x4d, 0x61, 0x78, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0f, 0x6d, 0x61, 0x78, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x43, 0x6f, - 0x75, 0x6e, 0x74, 0x73, 0x1a, 0x42, 0x0a, 0x14, 0x4d, 0x61, 0x78, 0x56, 0x6f, 0x6c, 0x75, 0x6d, - 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, - 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, - 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x76, - 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x80, 0x02, 0x0a, 0x11, 0x48, 0x65, 0x61, - 0x72, 0x74, 0x62, 0x65, 0x61, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2a, - 0x0a, 0x11, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x5f, 0x6c, 0x69, - 0x6d, 0x69, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0f, 0x76, 0x6f, 0x6c, 0x75, 0x6d, - 0x65, 0x53, 0x69, 0x7a, 0x65, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x6c, 0x65, - 0x61, 0x64, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x6c, 0x65, 0x61, 0x64, - 0x65, 0x72, 0x12, 0x27, 0x0a, 0x0f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x5f, 0x61, 0x64, - 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x6d, 0x65, 0x74, - 0x72, 0x69, 0x63, 0x73, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x38, 0x0a, 0x18, 0x6d, - 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x5f, - 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x16, 0x6d, - 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x53, 0x65, - 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x12, 0x44, 0x0a, 0x10, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, - 0x5f, 0x62, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, - 0x19, 0x2e, 0x6d, 0x61, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x53, 0x74, 0x6f, 0x72, - 0x61, 0x67, 0x65, 0x42, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x52, 0x0f, 0x73, 0x74, 0x6f, 0x72, - 0x61, 0x67, 0x65, 0x42, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x73, 0x22, 0x98, 0x04, 0x0a, 0x18, - 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x0d, 0x52, 0x02, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x69, 0x7a, 0x65, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x12, 0x1e, 0x0a, 0x0a, - 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x0a, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1d, 0x0a, 0x0a, - 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, - 0x52, 0x09, 0x66, 0x69, 0x6c, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x64, - 0x65, 0x6c, 0x65, 0x74, 0x65, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, - 0x04, 0x52, 0x0b, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x2c, - 0x0a, 0x12, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x5f, 0x63, - 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x04, 0x52, 0x10, 0x64, 0x65, 0x6c, 0x65, - 0x74, 0x65, 0x64, 0x42, 0x79, 0x74, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x1b, 0x0a, 0x09, - 0x72, 0x65, 0x61, 0x64, 0x5f, 0x6f, 0x6e, 0x6c, 0x79, 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, - 0x08, 0x72, 0x65, 0x61, 0x64, 0x4f, 0x6e, 0x6c, 0x79, 0x12, 0x2b, 0x0a, 0x11, 0x72, 0x65, 0x70, - 0x6c, 0x69, 0x63, 0x61, 0x5f, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x18, 0x08, - 0x20, 0x01, 0x28, 0x0d, 0x52, 0x10, 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x50, 0x6c, 0x61, - 0x63, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, - 0x6e, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, - 0x12, 0x10, 0x0a, 0x03, 0x74, 0x74, 0x6c, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x03, 0x74, - 0x74, 0x6c, 0x12, 0x29, 0x0a, 0x10, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x63, 0x74, 0x5f, 0x72, 0x65, - 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0f, 0x63, 0x6f, - 0x6d, 0x70, 0x61, 0x63, 0x74, 0x52, 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x2c, 0x0a, - 0x12, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x69, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x5f, 0x73, 0x65, 0x63, - 0x6f, 0x6e, 0x64, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x03, 0x52, 0x10, 0x6d, 0x6f, 0x64, 0x69, 0x66, - 0x69, 0x65, 0x64, 0x41, 0x74, 0x53, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x12, 0x2e, 0x0a, 0x13, 0x72, - 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x5f, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x5f, 0x6e, 0x61, - 0x6d, 0x65, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, - 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x2c, 0x0a, 0x12, 0x72, - 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x5f, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x5f, 0x6b, 0x65, - 0x79, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x53, - 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x4b, 0x65, 0x79, 0x12, 0x1b, 0x0a, 0x09, 0x64, 0x69, 0x73, - 0x6b, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x64, 0x69, - 0x73, 0x6b, 0x54, 0x79, 0x70, 0x65, 0x22, 0xc5, 0x01, 0x0a, 0x1d, 0x56, 0x6f, 0x6c, 0x75, 0x6d, - 0x65, 0x53, 0x68, 0x6f, 0x72, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x0d, 0x52, 0x02, 0x69, 0x64, 0x12, 0x1e, 0x0a, 0x0a, 0x63, 0x6f, 0x6c, 0x6c, - 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x63, 0x6f, - 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x2b, 0x0a, 0x11, 0x72, 0x65, 0x70, 0x6c, - 0x69, 0x63, 0x61, 0x5f, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x18, 0x08, 0x20, - 0x01, 0x28, 0x0d, 0x52, 0x10, 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x50, 0x6c, 0x61, 0x63, - 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, - 0x18, 0x09, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, - 0x10, 0x0a, 0x03, 0x74, 0x74, 0x6c, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x03, 0x74, 0x74, - 0x6c, 0x12, 0x1b, 0x0a, 0x09, 0x64, 0x69, 0x73, 0x6b, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x0f, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x64, 0x69, 0x73, 0x6b, 0x54, 0x79, 0x70, 0x65, 0x22, 0x92, - 0x01, 0x0a, 0x1f, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x45, 0x63, 0x53, 0x68, 0x61, 0x72, 0x64, - 0x49, 0x6e, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4d, 0x65, 0x73, 0x73, 0x61, - 0x67, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x02, - 0x69, 0x64, 0x12, 0x1e, 0x0a, 0x0a, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, - 0x6f, 0x6e, 0x12, 0x22, 0x0a, 0x0d, 0x65, 0x63, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x5f, 0x62, - 0x69, 0x74, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0b, 0x65, 0x63, 0x49, 0x6e, 0x64, - 0x65, 0x78, 0x42, 0x69, 0x74, 0x73, 0x12, 0x1b, 0x0a, 0x09, 0x64, 0x69, 0x73, 0x6b, 0x5f, 0x74, - 0x79, 0x70, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x64, 0x69, 0x73, 0x6b, 0x54, - 0x79, 0x70, 0x65, 0x22, 0xbe, 0x01, 0x0a, 0x0e, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x42, - 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x49, 0x0a, 0x0a, 0x70, 0x72, - 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x29, - 0x2e, 0x6d, 0x61, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x53, 0x74, 0x6f, 0x72, 0x61, - 0x67, 0x65, 0x42, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x2e, 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, - 0x74, 0x69, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0a, 0x70, 0x72, 0x6f, 0x70, 0x65, - 0x72, 0x74, 0x69, 0x65, 0x73, 0x1a, 0x3d, 0x0a, 0x0f, 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, - 0x69, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, - 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, - 0x3a, 0x02, 0x38, 0x01, 0x22, 0x07, 0x0a, 0x05, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0xbe, 0x01, - 0x0a, 0x0f, 0x53, 0x75, 0x70, 0x65, 0x72, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x45, 0x78, 0x74, 0x72, - 0x61, 0x12, 0x4f, 0x0a, 0x0e, 0x65, 0x72, 0x61, 0x73, 0x75, 0x72, 0x65, 0x5f, 0x63, 0x6f, 0x64, - 0x69, 0x6e, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x6d, 0x61, 0x73, 0x74, - 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x53, 0x75, 0x70, 0x65, 0x72, 0x42, 0x6c, 0x6f, 0x63, 0x6b, - 0x45, 0x78, 0x74, 0x72, 0x61, 0x2e, 0x45, 0x72, 0x61, 0x73, 0x75, 0x72, 0x65, 0x43, 0x6f, 0x64, - 0x69, 0x6e, 0x67, 0x52, 0x0d, 0x65, 0x72, 0x61, 0x73, 0x75, 0x72, 0x65, 0x43, 0x6f, 0x64, 0x69, - 0x6e, 0x67, 0x1a, 0x5a, 0x0a, 0x0d, 0x45, 0x72, 0x61, 0x73, 0x75, 0x72, 0x65, 0x43, 0x6f, 0x64, - 0x69, 0x6e, 0x67, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x0d, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x12, 0x16, 0x0a, 0x06, 0x70, 0x61, 0x72, 0x69, 0x74, - 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x06, 0x70, 0x61, 0x72, 0x69, 0x74, 0x79, 0x12, - 0x1d, 0x0a, 0x0a, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x03, 0x20, - 0x03, 0x28, 0x0d, 0x52, 0x09, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x49, 0x64, 0x73, 0x22, 0x47, - 0x0a, 0x14, 0x4b, 0x65, 0x65, 0x70, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x65, 0x64, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x67, 0x72, - 0x70, 0x63, 0x5f, 0x70, 0x6f, 0x72, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x67, - 0x72, 0x70, 0x63, 0x50, 0x6f, 0x72, 0x74, 0x22, 0xb8, 0x01, 0x0a, 0x0e, 0x56, 0x6f, 0x6c, 0x75, - 0x6d, 0x65, 0x4c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, - 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x6c, 0x12, 0x1d, 0x0a, 0x0a, - 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x75, 0x72, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x09, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x55, 0x72, 0x6c, 0x12, 0x19, 0x0a, 0x08, 0x6e, - 0x65, 0x77, 0x5f, 0x76, 0x69, 0x64, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0d, 0x52, 0x07, 0x6e, - 0x65, 0x77, 0x56, 0x69, 0x64, 0x73, 0x12, 0x21, 0x0a, 0x0c, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, - 0x64, 0x5f, 0x76, 0x69, 0x64, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0d, 0x52, 0x0b, 0x64, 0x65, - 0x6c, 0x65, 0x74, 0x65, 0x64, 0x56, 0x69, 0x64, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x6c, 0x65, 0x61, - 0x64, 0x65, 0x72, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x6c, 0x65, 0x61, 0x64, 0x65, - 0x72, 0x12, 0x1f, 0x0a, 0x0b, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x63, 0x65, 0x6e, 0x74, 0x65, 0x72, - 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x64, 0x61, 0x74, 0x61, 0x43, 0x65, 0x6e, 0x74, - 0x65, 0x72, 0x22, 0x62, 0x0a, 0x13, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x56, 0x6f, 0x6c, 0x75, - 0x6d, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2b, 0x0a, 0x12, 0x76, 0x6f, 0x6c, - 0x75, 0x6d, 0x65, 0x5f, 0x6f, 0x72, 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x69, 0x64, 0x73, 0x18, - 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0f, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x4f, 0x72, 0x46, - 0x69, 0x6c, 0x65, 0x49, 0x64, 0x73, 0x12, 0x1e, 0x0a, 0x0a, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, - 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x63, 0x6f, 0x6c, 0x6c, - 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x95, 0x02, 0x0a, 0x14, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, - 0x70, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, - 0x60, 0x0a, 0x13, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x69, 0x64, 0x5f, 0x6c, 0x6f, 0x63, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x30, 0x2e, 0x6d, - 0x61, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x56, - 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x56, 0x6f, - 0x6c, 0x75, 0x6d, 0x65, 0x49, 0x64, 0x4c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x11, - 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x49, 0x64, 0x4c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x73, 0x1a, 0x9a, 0x01, 0x0a, 0x10, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x49, 0x64, 0x4c, 0x6f, - 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x29, 0x0a, 0x11, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, - 0x5f, 0x6f, 0x72, 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x0e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x4f, 0x72, 0x46, 0x69, 0x6c, 0x65, 0x49, - 0x64, 0x12, 0x31, 0x0a, 0x09, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x02, - 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x6d, 0x61, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x70, 0x62, - 0x2e, 0x4c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x09, 0x6c, 0x6f, 0x63, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x03, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x12, 0x0a, 0x04, 0x61, 0x75, - 0x74, 0x68, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x61, 0x75, 0x74, 0x68, 0x22, 0x3b, - 0x0a, 0x08, 0x4c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, - 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x6c, 0x12, 0x1d, 0x0a, 0x0a, - 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x75, 0x72, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x09, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x55, 0x72, 0x6c, 0x22, 0xd0, 0x02, 0x0a, 0x0d, - 0x41, 0x73, 0x73, 0x69, 0x67, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x14, 0x0a, - 0x05, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x05, 0x63, 0x6f, - 0x75, 0x6e, 0x74, 0x12, 0x20, 0x0a, 0x0b, 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1e, 0x0a, 0x0a, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, + 0x75, 0x6e, 0x74, 0x73, 0x12, 0x1b, 0x0a, 0x09, 0x67, 0x72, 0x70, 0x63, 0x5f, 0x70, 0x6f, 0x72, + 0x74, 0x18, 0x14, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x67, 0x72, 0x70, 0x63, 0x50, 0x6f, 0x72, + 0x74, 0x1a, 0x42, 0x0a, 0x14, 0x4d, 0x61, 0x78, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x43, 0x6f, + 0x75, 0x6e, 0x74, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, + 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, + 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x80, 0x02, 0x0a, 0x11, 0x48, 0x65, 0x61, 0x72, 0x74, 0x62, + 0x65, 0x61, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2a, 0x0a, 0x11, 0x76, + 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0f, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x53, 0x69, + 0x7a, 0x65, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x6c, 0x65, 0x61, 0x64, 0x65, + 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x6c, 0x65, 0x61, 0x64, 0x65, 0x72, 0x12, + 0x27, 0x0a, 0x0f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, + 0x73, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, + 0x73, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x38, 0x0a, 0x18, 0x6d, 0x65, 0x74, 0x72, + 0x69, 0x63, 0x73, 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x5f, 0x73, 0x65, 0x63, + 0x6f, 0x6e, 0x64, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x16, 0x6d, 0x65, 0x74, 0x72, + 0x69, 0x63, 0x73, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x53, 0x65, 0x63, 0x6f, 0x6e, + 0x64, 0x73, 0x12, 0x44, 0x0a, 0x10, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x5f, 0x62, 0x61, + 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x6d, + 0x61, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, + 0x42, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x52, 0x0f, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, + 0x42, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x73, 0x22, 0x98, 0x04, 0x0a, 0x18, 0x56, 0x6f, 0x6c, + 0x75, 0x6d, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4d, 0x65, + 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x0d, 0x52, 0x02, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x04, 0x52, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x63, 0x6f, 0x6c, + 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x63, + 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1d, 0x0a, 0x0a, 0x66, 0x69, 0x6c, + 0x65, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x66, + 0x69, 0x6c, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x64, 0x65, 0x6c, 0x65, + 0x74, 0x65, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, + 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x2c, 0x0a, 0x12, 0x64, + 0x65, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x5f, 0x63, 0x6f, 0x75, 0x6e, + 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x04, 0x52, 0x10, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x64, + 0x42, 0x79, 0x74, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x72, 0x65, 0x61, + 0x64, 0x5f, 0x6f, 0x6e, 0x6c, 0x79, 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x72, 0x65, + 0x61, 0x64, 0x4f, 0x6e, 0x6c, 0x79, 0x12, 0x2b, 0x0a, 0x11, 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, + 0x61, 0x5f, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x18, 0x08, 0x20, 0x01, 0x28, + 0x0d, 0x52, 0x10, 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x50, 0x6c, 0x61, 0x63, 0x65, 0x6d, + 0x65, 0x6e, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x09, + 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x10, 0x0a, + 0x03, 0x74, 0x74, 0x6c, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x03, 0x74, 0x74, 0x6c, 0x12, + 0x29, 0x0a, 0x10, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x63, 0x74, 0x5f, 0x72, 0x65, 0x76, 0x69, 0x73, + 0x69, 0x6f, 0x6e, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0f, 0x63, 0x6f, 0x6d, 0x70, 0x61, + 0x63, 0x74, 0x52, 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x2c, 0x0a, 0x12, 0x6d, 0x6f, + 0x64, 0x69, 0x66, 0x69, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x5f, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, + 0x18, 0x0c, 0x20, 0x01, 0x28, 0x03, 0x52, 0x10, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x69, 0x65, 0x64, + 0x41, 0x74, 0x53, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x12, 0x2e, 0x0a, 0x13, 0x72, 0x65, 0x6d, 0x6f, + 0x74, 0x65, 0x5f, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, + 0x0d, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x53, 0x74, 0x6f, + 0x72, 0x61, 0x67, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x2c, 0x0a, 0x12, 0x72, 0x65, 0x6d, 0x6f, + 0x74, 0x65, 0x5f, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x0e, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x53, 0x74, 0x6f, 0x72, + 0x61, 0x67, 0x65, 0x4b, 0x65, 0x79, 0x12, 0x1b, 0x0a, 0x09, 0x64, 0x69, 0x73, 0x6b, 0x5f, 0x74, + 0x79, 0x70, 0x65, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x64, 0x69, 0x73, 0x6b, 0x54, + 0x79, 0x70, 0x65, 0x22, 0xc5, 0x01, 0x0a, 0x1d, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x53, 0x68, + 0x6f, 0x72, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4d, 0x65, + 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x0d, 0x52, 0x02, 0x69, 0x64, 0x12, 0x1e, 0x0a, 0x0a, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x63, 0x6f, 0x6c, 0x6c, 0x65, - 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x10, 0x0a, 0x03, 0x74, 0x74, 0x6c, 0x18, 0x04, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x03, 0x74, 0x74, 0x6c, 0x12, 0x1f, 0x0a, 0x0b, 0x64, 0x61, 0x74, 0x61, 0x5f, - 0x63, 0x65, 0x6e, 0x74, 0x65, 0x72, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x64, 0x61, - 0x74, 0x61, 0x43, 0x65, 0x6e, 0x74, 0x65, 0x72, 0x12, 0x12, 0x0a, 0x04, 0x72, 0x61, 0x63, 0x6b, - 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x72, 0x61, 0x63, 0x6b, 0x12, 0x1b, 0x0a, 0x09, - 0x64, 0x61, 0x74, 0x61, 0x5f, 0x6e, 0x6f, 0x64, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x08, 0x64, 0x61, 0x74, 0x61, 0x4e, 0x6f, 0x64, 0x65, 0x12, 0x32, 0x0a, 0x16, 0x6d, 0x65, 0x6d, - 0x6f, 0x72, 0x79, 0x5f, 0x6d, 0x61, 0x70, 0x5f, 0x6d, 0x61, 0x78, 0x5f, 0x73, 0x69, 0x7a, 0x65, - 0x5f, 0x6d, 0x62, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x12, 0x6d, 0x65, 0x6d, 0x6f, 0x72, - 0x79, 0x4d, 0x61, 0x70, 0x4d, 0x61, 0x78, 0x53, 0x69, 0x7a, 0x65, 0x4d, 0x62, 0x12, 0x32, 0x0a, - 0x15, 0x57, 0x72, 0x69, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, - 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x13, 0x57, 0x72, - 0x69, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x43, 0x6f, 0x75, 0x6e, - 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x64, 0x69, 0x73, 0x6b, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x0a, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x64, 0x69, 0x73, 0x6b, 0x54, 0x79, 0x70, 0x65, 0x22, 0x8e, - 0x02, 0x0a, 0x0e, 0x41, 0x73, 0x73, 0x69, 0x67, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x12, 0x10, 0x0a, 0x03, 0x66, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, - 0x66, 0x69, 0x64, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x03, 0x75, 0x72, 0x6c, 0x12, 0x1d, 0x0a, 0x0a, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, - 0x75, 0x72, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x70, 0x75, 0x62, 0x6c, 0x69, - 0x63, 0x55, 0x72, 0x6c, 0x12, 0x14, 0x0a, 0x05, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x04, 0x20, + 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x2b, 0x0a, 0x11, 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, + 0x5f, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0d, + 0x52, 0x10, 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x50, 0x6c, 0x61, 0x63, 0x65, 0x6d, 0x65, + 0x6e, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x09, 0x20, + 0x01, 0x28, 0x0d, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x10, 0x0a, 0x03, + 0x74, 0x74, 0x6c, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x03, 0x74, 0x74, 0x6c, 0x12, 0x1b, + 0x0a, 0x09, 0x64, 0x69, 0x73, 0x6b, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x0f, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x08, 0x64, 0x69, 0x73, 0x6b, 0x54, 0x79, 0x70, 0x65, 0x22, 0x92, 0x01, 0x0a, 0x1f, + 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x45, 0x63, 0x53, 0x68, 0x61, 0x72, 0x64, 0x49, 0x6e, 0x66, + 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, + 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x02, 0x69, 0x64, 0x12, + 0x1e, 0x0a, 0x0a, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x0a, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, + 0x22, 0x0a, 0x0d, 0x65, 0x63, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x5f, 0x62, 0x69, 0x74, 0x73, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0b, 0x65, 0x63, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x42, + 0x69, 0x74, 0x73, 0x12, 0x1b, 0x0a, 0x09, 0x64, 0x69, 0x73, 0x6b, 0x5f, 0x74, 0x79, 0x70, 0x65, + 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x64, 0x69, 0x73, 0x6b, 0x54, 0x79, 0x70, 0x65, + 0x22, 0xbe, 0x01, 0x0a, 0x0e, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x42, 0x61, 0x63, 0x6b, + 0x65, 0x6e, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x49, 0x0a, 0x0a, 0x70, 0x72, 0x6f, 0x70, 0x65, + 0x72, 0x74, 0x69, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x6d, 0x61, + 0x73, 0x74, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x42, + 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x2e, 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, + 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0a, 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, + 0x65, 0x73, 0x1a, 0x3d, 0x0a, 0x0f, 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, + 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, + 0x01, 0x22, 0x07, 0x0a, 0x05, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0xbe, 0x01, 0x0a, 0x0f, 0x53, + 0x75, 0x70, 0x65, 0x72, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x45, 0x78, 0x74, 0x72, 0x61, 0x12, 0x4f, + 0x0a, 0x0e, 0x65, 0x72, 0x61, 0x73, 0x75, 0x72, 0x65, 0x5f, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x6d, 0x61, 0x73, 0x74, 0x65, 0x72, 0x5f, + 0x70, 0x62, 0x2e, 0x53, 0x75, 0x70, 0x65, 0x72, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x45, 0x78, 0x74, + 0x72, 0x61, 0x2e, 0x45, 0x72, 0x61, 0x73, 0x75, 0x72, 0x65, 0x43, 0x6f, 0x64, 0x69, 0x6e, 0x67, + 0x52, 0x0d, 0x65, 0x72, 0x61, 0x73, 0x75, 0x72, 0x65, 0x43, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x1a, + 0x5a, 0x0a, 0x0d, 0x45, 0x72, 0x61, 0x73, 0x75, 0x72, 0x65, 0x43, 0x6f, 0x64, 0x69, 0x6e, 0x67, + 0x12, 0x12, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x04, + 0x64, 0x61, 0x74, 0x61, 0x12, 0x16, 0x0a, 0x06, 0x70, 0x61, 0x72, 0x69, 0x74, 0x79, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x0d, 0x52, 0x06, 0x70, 0x61, 0x72, 0x69, 0x74, 0x79, 0x12, 0x1d, 0x0a, 0x0a, + 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0d, + 0x52, 0x09, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x49, 0x64, 0x73, 0x22, 0x51, 0x0a, 0x14, 0x4b, + 0x65, 0x65, 0x70, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x65, 0x64, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x25, 0x0a, 0x0e, 0x63, 0x6c, 0x69, 0x65, 0x6e, + 0x74, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x0d, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x22, 0xd5, + 0x01, 0x0a, 0x0e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x4c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, + 0x75, 0x72, 0x6c, 0x12, 0x1d, 0x0a, 0x0a, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x75, 0x72, + 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x55, + 0x72, 0x6c, 0x12, 0x19, 0x0a, 0x08, 0x6e, 0x65, 0x77, 0x5f, 0x76, 0x69, 0x64, 0x73, 0x18, 0x03, + 0x20, 0x03, 0x28, 0x0d, 0x52, 0x07, 0x6e, 0x65, 0x77, 0x56, 0x69, 0x64, 0x73, 0x12, 0x21, 0x0a, + 0x0c, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x5f, 0x76, 0x69, 0x64, 0x73, 0x18, 0x04, 0x20, + 0x03, 0x28, 0x0d, 0x52, 0x0b, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x56, 0x69, 0x64, 0x73, + 0x12, 0x16, 0x0a, 0x06, 0x6c, 0x65, 0x61, 0x64, 0x65, 0x72, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x06, 0x6c, 0x65, 0x61, 0x64, 0x65, 0x72, 0x12, 0x1f, 0x0a, 0x0b, 0x64, 0x61, 0x74, 0x61, + 0x5f, 0x63, 0x65, 0x6e, 0x74, 0x65, 0x72, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x64, + 0x61, 0x74, 0x61, 0x43, 0x65, 0x6e, 0x74, 0x65, 0x72, 0x12, 0x1b, 0x0a, 0x09, 0x67, 0x72, 0x70, + 0x63, 0x5f, 0x70, 0x6f, 0x72, 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x67, 0x72, + 0x70, 0x63, 0x50, 0x6f, 0x72, 0x74, 0x22, 0x62, 0x0a, 0x13, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, + 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2b, 0x0a, + 0x12, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x6f, 0x72, 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x5f, + 0x69, 0x64, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0f, 0x76, 0x6f, 0x6c, 0x75, 0x6d, + 0x65, 0x4f, 0x72, 0x46, 0x69, 0x6c, 0x65, 0x49, 0x64, 0x73, 0x12, 0x1e, 0x0a, 0x0a, 0x63, 0x6f, + 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, + 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x95, 0x02, 0x0a, 0x14, 0x4c, + 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x60, 0x0a, 0x13, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x69, 0x64, + 0x5f, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x30, 0x2e, 0x6d, 0x61, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x4c, 0x6f, 0x6f, + 0x6b, 0x75, 0x70, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x49, 0x64, 0x4c, 0x6f, 0x63, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x52, 0x11, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x49, 0x64, 0x4c, 0x6f, 0x63, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x1a, 0x9a, 0x01, 0x0a, 0x10, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, + 0x49, 0x64, 0x4c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x29, 0x0a, 0x11, 0x76, 0x6f, + 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x6f, 0x72, 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x69, 0x64, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x4f, 0x72, 0x46, + 0x69, 0x6c, 0x65, 0x49, 0x64, 0x12, 0x31, 0x0a, 0x09, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x6d, 0x61, 0x73, 0x74, 0x65, + 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x4c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x09, 0x6c, + 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, + 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x12, + 0x0a, 0x04, 0x61, 0x75, 0x74, 0x68, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x61, 0x75, + 0x74, 0x68, 0x22, 0x58, 0x0a, 0x08, 0x4c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x10, + 0x0a, 0x03, 0x75, 0x72, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x6c, + 0x12, 0x1d, 0x0a, 0x0a, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x75, 0x72, 0x6c, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x55, 0x72, 0x6c, 0x12, + 0x1b, 0x0a, 0x09, 0x67, 0x72, 0x70, 0x63, 0x5f, 0x70, 0x6f, 0x72, 0x74, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x0d, 0x52, 0x08, 0x67, 0x72, 0x70, 0x63, 0x50, 0x6f, 0x72, 0x74, 0x22, 0xd0, 0x02, 0x0a, + 0x0d, 0x41, 0x73, 0x73, 0x69, 0x67, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x14, + 0x0a, 0x05, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x05, 0x63, + 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x20, 0x0a, 0x0b, 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x72, 0x65, 0x70, 0x6c, 0x69, + 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1e, 0x0a, 0x0a, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, + 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x63, 0x6f, 0x6c, 0x6c, + 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x10, 0x0a, 0x03, 0x74, 0x74, 0x6c, 0x18, 0x04, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x03, 0x74, 0x74, 0x6c, 0x12, 0x1f, 0x0a, 0x0b, 0x64, 0x61, 0x74, 0x61, + 0x5f, 0x63, 0x65, 0x6e, 0x74, 0x65, 0x72, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x64, + 0x61, 0x74, 0x61, 0x43, 0x65, 0x6e, 0x74, 0x65, 0x72, 0x12, 0x12, 0x0a, 0x04, 0x72, 0x61, 0x63, + 0x6b, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x72, 0x61, 0x63, 0x6b, 0x12, 0x1b, 0x0a, + 0x09, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x6e, 0x6f, 0x64, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x08, 0x64, 0x61, 0x74, 0x61, 0x4e, 0x6f, 0x64, 0x65, 0x12, 0x32, 0x0a, 0x16, 0x6d, 0x65, + 0x6d, 0x6f, 0x72, 0x79, 0x5f, 0x6d, 0x61, 0x70, 0x5f, 0x6d, 0x61, 0x78, 0x5f, 0x73, 0x69, 0x7a, + 0x65, 0x5f, 0x6d, 0x62, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x12, 0x6d, 0x65, 0x6d, 0x6f, + 0x72, 0x79, 0x4d, 0x61, 0x70, 0x4d, 0x61, 0x78, 0x53, 0x69, 0x7a, 0x65, 0x4d, 0x62, 0x12, 0x32, + 0x0a, 0x15, 0x57, 0x72, 0x69, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x76, 0x6f, 0x6c, 0x75, 0x6d, + 0x65, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x13, 0x57, + 0x72, 0x69, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x43, 0x6f, 0x75, + 0x6e, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x64, 0x69, 0x73, 0x6b, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, + 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x64, 0x69, 0x73, 0x6b, 0x54, 0x79, 0x70, 0x65, 0x22, + 0xc4, 0x01, 0x0a, 0x0e, 0x41, 0x73, 0x73, 0x69, 0x67, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x66, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x03, 0x66, 0x69, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x05, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x12, 0x0a, 0x04, 0x61, 0x75, 0x74, 0x68, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, - 0x61, 0x75, 0x74, 0x68, 0x12, 0x3d, 0x0a, 0x08, 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x73, - 0x18, 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x6d, 0x61, 0x73, 0x74, 0x65, 0x72, 0x5f, - 0x70, 0x62, 0x2e, 0x41, 0x73, 0x73, 0x69, 0x67, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x2e, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x52, 0x08, 0x72, 0x65, 0x70, 0x6c, 0x69, - 0x63, 0x61, 0x73, 0x1a, 0x3a, 0x0a, 0x07, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x12, 0x10, - 0x0a, 0x03, 0x75, 0x72, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x6c, - 0x12, 0x1d, 0x0a, 0x0a, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x75, 0x72, 0x6c, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x55, 0x72, 0x6c, 0x22, - 0x84, 0x01, 0x0a, 0x11, 0x53, 0x74, 0x61, 0x74, 0x69, 0x73, 0x74, 0x69, 0x63, 0x73, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x20, 0x0a, 0x0b, 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x72, 0x65, 0x70, 0x6c, - 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1e, 0x0a, 0x0a, 0x63, 0x6f, 0x6c, 0x6c, 0x65, - 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x63, 0x6f, 0x6c, - 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x10, 0x0a, 0x03, 0x74, 0x74, 0x6c, 0x18, 0x03, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x74, 0x74, 0x6c, 0x12, 0x1b, 0x0a, 0x09, 0x64, 0x69, 0x73, - 0x6b, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x64, 0x69, - 0x73, 0x6b, 0x54, 0x79, 0x70, 0x65, 0x22, 0x6f, 0x0a, 0x12, 0x53, 0x74, 0x61, 0x74, 0x69, 0x73, - 0x74, 0x69, 0x63, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1d, 0x0a, 0x0a, - 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, - 0x52, 0x09, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x53, 0x69, 0x7a, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x75, - 0x73, 0x65, 0x64, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x04, 0x52, 0x08, - 0x75, 0x73, 0x65, 0x64, 0x53, 0x69, 0x7a, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x66, 0x69, 0x6c, 0x65, - 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x66, 0x69, - 0x6c, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0x20, 0x0a, 0x0a, 0x43, 0x6f, 0x6c, 0x6c, 0x65, - 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x7b, 0x0a, 0x15, 0x43, 0x6f, 0x6c, - 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x12, 0x34, 0x0a, 0x16, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x5f, 0x6e, 0x6f, - 0x72, 0x6d, 0x61, 0x6c, 0x5f, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x73, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x08, 0x52, 0x14, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x4e, 0x6f, 0x72, 0x6d, 0x61, - 0x6c, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x73, 0x12, 0x2c, 0x0a, 0x12, 0x69, 0x6e, 0x63, 0x6c, - 0x75, 0x64, 0x65, 0x5f, 0x65, 0x63, 0x5f, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x73, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x45, 0x63, 0x56, - 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x73, 0x22, 0x51, 0x0a, 0x16, 0x43, 0x6f, 0x6c, 0x6c, 0x65, 0x63, - 0x74, 0x69, 0x6f, 0x6e, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x12, 0x37, 0x0a, 0x0b, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, - 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x6d, 0x61, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x70, - 0x62, 0x2e, 0x43, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0b, 0x63, 0x6f, - 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x2d, 0x0a, 0x17, 0x43, 0x6f, 0x6c, - 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x1a, 0x0a, 0x18, 0x43, 0x6f, 0x6c, 0x6c, - 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x91, 0x03, 0x0a, 0x08, 0x44, 0x69, 0x73, 0x6b, 0x49, 0x6e, 0x66, - 0x6f, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, - 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0b, 0x76, 0x6f, 0x6c, - 0x75, 0x6d, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x28, 0x0a, 0x10, 0x6d, 0x61, 0x78, 0x5f, - 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, - 0x28, 0x03, 0x52, 0x0e, 0x6d, 0x61, 0x78, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x43, 0x6f, 0x75, - 0x6e, 0x74, 0x12, 0x2a, 0x0a, 0x11, 0x66, 0x72, 0x65, 0x65, 0x5f, 0x76, 0x6f, 0x6c, 0x75, 0x6d, - 0x65, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0f, 0x66, - 0x72, 0x65, 0x65, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x2e, - 0x0a, 0x13, 0x61, 0x63, 0x74, 0x69, 0x76, 0x65, 0x5f, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, - 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x11, 0x61, 0x63, 0x74, - 0x69, 0x76, 0x65, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x46, - 0x0a, 0x0c, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x73, 0x18, 0x06, - 0x20, 0x03, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x6d, 0x61, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x70, 0x62, - 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x0b, 0x76, 0x6f, 0x6c, 0x75, 0x6d, - 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x73, 0x12, 0x50, 0x0a, 0x0e, 0x65, 0x63, 0x5f, 0x73, 0x68, 0x61, - 0x72, 0x64, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2a, - 0x2e, 0x6d, 0x61, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, - 0x65, 0x45, 0x63, 0x53, 0x68, 0x61, 0x72, 0x64, 0x49, 0x6e, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x0c, 0x65, 0x63, 0x53, 0x68, - 0x61, 0x72, 0x64, 0x49, 0x6e, 0x66, 0x6f, 0x73, 0x12, 0x2e, 0x0a, 0x13, 0x72, 0x65, 0x6d, 0x6f, - 0x74, 0x65, 0x5f, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, - 0x08, 0x20, 0x01, 0x28, 0x03, 0x52, 0x11, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x56, 0x6f, 0x6c, - 0x75, 0x6d, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0xb7, 0x01, 0x0a, 0x0c, 0x44, 0x61, 0x74, - 0x61, 0x4e, 0x6f, 0x64, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x44, 0x0a, 0x09, 0x64, 0x69, 0x73, - 0x6b, 0x49, 0x6e, 0x66, 0x6f, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x26, 0x2e, 0x6d, - 0x61, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x44, 0x61, 0x74, 0x61, 0x4e, 0x6f, 0x64, - 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x2e, 0x44, 0x69, 0x73, 0x6b, 0x49, 0x6e, 0x66, 0x6f, 0x73, 0x45, - 0x6e, 0x74, 0x72, 0x79, 0x52, 0x09, 0x64, 0x69, 0x73, 0x6b, 0x49, 0x6e, 0x66, 0x6f, 0x73, 0x1a, - 0x51, 0x0a, 0x0e, 0x44, 0x69, 0x73, 0x6b, 0x49, 0x6e, 0x66, 0x6f, 0x73, 0x45, 0x6e, 0x74, 0x72, - 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, - 0x6b, 0x65, 0x79, 0x12, 0x29, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x6d, 0x61, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x44, - 0x69, 0x73, 0x6b, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, - 0x38, 0x01, 0x22, 0xf0, 0x01, 0x0a, 0x08, 0x52, 0x61, 0x63, 0x6b, 0x49, 0x6e, 0x66, 0x6f, 0x12, - 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, - 0x3f, 0x0a, 0x0f, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x69, 0x6e, 0x66, - 0x6f, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x6d, 0x61, 0x73, 0x74, 0x65, - 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x44, 0x61, 0x74, 0x61, 0x4e, 0x6f, 0x64, 0x65, 0x49, 0x6e, 0x66, - 0x6f, 0x52, 0x0d, 0x64, 0x61, 0x74, 0x61, 0x4e, 0x6f, 0x64, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x73, - 0x12, 0x40, 0x0a, 0x09, 0x64, 0x69, 0x73, 0x6b, 0x49, 0x6e, 0x66, 0x6f, 0x73, 0x18, 0x03, 0x20, - 0x03, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x6d, 0x61, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, - 0x52, 0x61, 0x63, 0x6b, 0x49, 0x6e, 0x66, 0x6f, 0x2e, 0x44, 0x69, 0x73, 0x6b, 0x49, 0x6e, 0x66, - 0x6f, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x09, 0x64, 0x69, 0x73, 0x6b, 0x49, 0x6e, 0x66, - 0x6f, 0x73, 0x1a, 0x51, 0x0a, 0x0e, 0x44, 0x69, 0x73, 0x6b, 0x49, 0x6e, 0x66, 0x6f, 0x73, 0x45, - 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x29, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x6d, 0x61, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x70, - 0x62, 0x2e, 0x44, 0x69, 0x73, 0x6b, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, - 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xef, 0x01, 0x0a, 0x0e, 0x44, 0x61, 0x74, 0x61, 0x43, 0x65, - 0x6e, 0x74, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x32, 0x0a, 0x0a, 0x72, 0x61, 0x63, 0x6b, - 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x6d, - 0x61, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x52, 0x61, 0x63, 0x6b, 0x49, 0x6e, 0x66, - 0x6f, 0x52, 0x09, 0x72, 0x61, 0x63, 0x6b, 0x49, 0x6e, 0x66, 0x6f, 0x73, 0x12, 0x46, 0x0a, 0x09, - 0x64, 0x69, 0x73, 0x6b, 0x49, 0x6e, 0x66, 0x6f, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, - 0x28, 0x2e, 0x6d, 0x61, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x44, 0x61, 0x74, 0x61, - 0x43, 0x65, 0x6e, 0x74, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x2e, 0x44, 0x69, 0x73, 0x6b, 0x49, - 0x6e, 0x66, 0x6f, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x09, 0x64, 0x69, 0x73, 0x6b, 0x49, - 0x6e, 0x66, 0x6f, 0x73, 0x1a, 0x51, 0x0a, 0x0e, 0x44, 0x69, 0x73, 0x6b, 0x49, 0x6e, 0x66, 0x6f, - 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x29, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, - 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x6d, 0x61, 0x73, 0x74, 0x65, 0x72, - 0x5f, 0x70, 0x62, 0x2e, 0x44, 0x69, 0x73, 0x6b, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x05, 0x76, 0x61, - 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xfe, 0x01, 0x0a, 0x0c, 0x54, 0x6f, 0x70, 0x6f, - 0x6c, 0x6f, 0x67, 0x79, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x45, 0x0a, 0x11, 0x64, 0x61, 0x74, 0x61, - 0x5f, 0x63, 0x65, 0x6e, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x73, 0x18, 0x02, 0x20, - 0x03, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x6d, 0x61, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, - 0x44, 0x61, 0x74, 0x61, 0x43, 0x65, 0x6e, 0x74, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x0f, - 0x64, 0x61, 0x74, 0x61, 0x43, 0x65, 0x6e, 0x74, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x73, 0x12, - 0x44, 0x0a, 0x09, 0x64, 0x69, 0x73, 0x6b, 0x49, 0x6e, 0x66, 0x6f, 0x73, 0x18, 0x03, 0x20, 0x03, - 0x28, 0x0b, 0x32, 0x26, 0x2e, 0x6d, 0x61, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x54, - 0x6f, 0x70, 0x6f, 0x6c, 0x6f, 0x67, 0x79, 0x49, 0x6e, 0x66, 0x6f, 0x2e, 0x44, 0x69, 0x73, 0x6b, - 0x49, 0x6e, 0x66, 0x6f, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x09, 0x64, 0x69, 0x73, 0x6b, - 0x49, 0x6e, 0x66, 0x6f, 0x73, 0x1a, 0x51, 0x0a, 0x0e, 0x44, 0x69, 0x73, 0x6b, 0x49, 0x6e, 0x66, - 0x6f, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x29, 0x0a, 0x05, 0x76, 0x61, 0x6c, - 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x6d, 0x61, 0x73, 0x74, 0x65, - 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x44, 0x69, 0x73, 0x6b, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x05, 0x76, - 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x13, 0x0a, 0x11, 0x56, 0x6f, 0x6c, 0x75, - 0x6d, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x83, 0x01, - 0x0a, 0x12, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3c, 0x0a, 0x0d, 0x74, 0x6f, 0x70, 0x6f, 0x6c, 0x6f, 0x67, 0x79, - 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x6d, 0x61, - 0x73, 0x74, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x54, 0x6f, 0x70, 0x6f, 0x6c, 0x6f, 0x67, 0x79, - 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x0c, 0x74, 0x6f, 0x70, 0x6f, 0x6c, 0x6f, 0x67, 0x79, 0x49, 0x6e, - 0x66, 0x6f, 0x12, 0x2f, 0x0a, 0x14, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x69, 0x7a, - 0x65, 0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x5f, 0x6d, 0x62, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, - 0x52, 0x11, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x53, 0x69, 0x7a, 0x65, 0x4c, 0x69, 0x6d, 0x69, - 0x74, 0x4d, 0x62, 0x22, 0x34, 0x0a, 0x15, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x45, 0x63, 0x56, - 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, - 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, - 0x08, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x49, 0x64, 0x22, 0xfb, 0x01, 0x0a, 0x16, 0x4c, 0x6f, - 0x6f, 0x6b, 0x75, 0x70, 0x45, 0x63, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x69, - 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x49, - 0x64, 0x12, 0x61, 0x0a, 0x12, 0x73, 0x68, 0x61, 0x72, 0x64, 0x5f, 0x69, 0x64, 0x5f, 0x6c, 0x6f, - 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x33, 0x2e, - 0x6d, 0x61, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, - 0x45, 0x63, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x2e, 0x45, 0x63, 0x53, 0x68, 0x61, 0x72, 0x64, 0x49, 0x64, 0x4c, 0x6f, 0x63, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x52, 0x10, 0x73, 0x68, 0x61, 0x72, 0x64, 0x49, 0x64, 0x4c, 0x6f, 0x63, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x73, 0x1a, 0x61, 0x0a, 0x11, 0x45, 0x63, 0x53, 0x68, 0x61, 0x72, 0x64, 0x49, - 0x64, 0x4c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x19, 0x0a, 0x08, 0x73, 0x68, 0x61, - 0x72, 0x64, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x73, 0x68, 0x61, - 0x72, 0x64, 0x49, 0x64, 0x12, 0x31, 0x0a, 0x09, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x6d, 0x61, 0x73, 0x74, 0x65, 0x72, - 0x5f, 0x70, 0x62, 0x2e, 0x4c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x09, 0x6c, 0x6f, - 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x42, 0x0a, 0x13, 0x56, 0x61, 0x63, 0x75, 0x75, - 0x6d, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2b, - 0x0a, 0x11, 0x67, 0x61, 0x72, 0x62, 0x61, 0x67, 0x65, 0x5f, 0x74, 0x68, 0x72, 0x65, 0x73, 0x68, - 0x6f, 0x6c, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x02, 0x52, 0x10, 0x67, 0x61, 0x72, 0x62, 0x61, - 0x67, 0x65, 0x54, 0x68, 0x72, 0x65, 0x73, 0x68, 0x6f, 0x6c, 0x64, 0x22, 0x16, 0x0a, 0x14, 0x56, - 0x61, 0x63, 0x75, 0x75, 0x6d, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x22, 0x1f, 0x0a, 0x1d, 0x47, 0x65, 0x74, 0x4d, 0x61, 0x73, 0x74, 0x65, 0x72, - 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x22, 0xf3, 0x02, 0x0a, 0x1e, 0x47, 0x65, 0x74, 0x4d, 0x61, 0x73, 0x74, - 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x27, 0x0a, 0x0f, 0x6d, 0x65, 0x74, 0x72, 0x69, - 0x63, 0x73, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x0e, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, - 0x12, 0x38, 0x0a, 0x18, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x5f, 0x69, 0x6e, 0x74, 0x65, - 0x72, 0x76, 0x61, 0x6c, 0x5f, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x0d, 0x52, 0x16, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x49, 0x6e, 0x74, 0x65, 0x72, - 0x76, 0x61, 0x6c, 0x53, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x12, 0x44, 0x0a, 0x10, 0x73, 0x74, - 0x6f, 0x72, 0x61, 0x67, 0x65, 0x5f, 0x62, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x73, 0x18, 0x03, - 0x20, 0x03, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x6d, 0x61, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x70, 0x62, - 0x2e, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x42, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x52, - 0x0f, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x42, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x73, - 0x12, 0x2f, 0x0a, 0x13, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x5f, 0x72, 0x65, 0x70, 0x6c, - 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x12, 0x64, - 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x12, 0x16, 0x0a, 0x06, 0x6c, 0x65, 0x61, 0x64, 0x65, 0x72, 0x18, 0x05, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x06, 0x6c, 0x65, 0x61, 0x64, 0x65, 0x72, 0x12, 0x30, 0x0a, 0x15, 0x76, 0x6f, 0x6c, - 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x5f, 0x6d, - 0x5f, 0x62, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x11, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, - 0x53, 0x69, 0x7a, 0x65, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x4d, 0x42, 0x12, 0x2d, 0x0a, 0x12, 0x76, - 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x70, 0x72, 0x65, 0x61, 0x6c, 0x6c, 0x6f, 0x63, 0x61, 0x74, - 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x11, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x50, - 0x72, 0x65, 0x61, 0x6c, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x65, 0x22, 0x3b, 0x0a, 0x18, 0x4c, 0x69, - 0x73, 0x74, 0x4d, 0x61, 0x73, 0x74, 0x65, 0x72, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x73, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, - 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x63, 0x6c, 0x69, - 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x22, 0x42, 0x0a, 0x19, 0x4c, 0x69, 0x73, 0x74, 0x4d, - 0x61, 0x73, 0x74, 0x65, 0x72, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x25, 0x0a, 0x0e, 0x67, 0x72, 0x70, 0x63, 0x5f, 0x61, 0x64, 0x64, - 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0d, 0x67, 0x72, - 0x70, 0x63, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x22, 0xab, 0x01, 0x0a, 0x16, - 0x4c, 0x65, 0x61, 0x73, 0x65, 0x41, 0x64, 0x6d, 0x69, 0x6e, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x25, 0x0a, 0x0e, 0x70, 0x72, 0x65, 0x76, 0x69, 0x6f, - 0x75, 0x73, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0d, - 0x70, 0x72, 0x65, 0x76, 0x69, 0x6f, 0x75, 0x73, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x2c, 0x0a, - 0x12, 0x70, 0x72, 0x65, 0x76, 0x69, 0x6f, 0x75, 0x73, 0x5f, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x74, - 0x69, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x10, 0x70, 0x72, 0x65, 0x76, 0x69, - 0x6f, 0x75, 0x73, 0x4c, 0x6f, 0x63, 0x6b, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x6c, - 0x6f, 0x63, 0x6b, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, - 0x6c, 0x6f, 0x63, 0x6b, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x63, 0x6c, 0x69, 0x65, - 0x6e, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x63, - 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x22, 0x4d, 0x0a, 0x17, 0x4c, 0x65, 0x61, - 0x73, 0x65, 0x41, 0x64, 0x6d, 0x69, 0x6e, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x03, 0x52, 0x05, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x1c, 0x0a, 0x0a, 0x6c, 0x6f, - 0x63, 0x6b, 0x5f, 0x74, 0x73, 0x5f, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x08, - 0x6c, 0x6f, 0x63, 0x6b, 0x54, 0x73, 0x4e, 0x73, 0x22, 0x8c, 0x01, 0x0a, 0x18, 0x52, 0x65, 0x6c, - 0x65, 0x61, 0x73, 0x65, 0x41, 0x64, 0x6d, 0x69, 0x6e, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x25, 0x0a, 0x0e, 0x70, 0x72, 0x65, 0x76, 0x69, 0x6f, 0x75, - 0x73, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0d, 0x70, - 0x72, 0x65, 0x76, 0x69, 0x6f, 0x75, 0x73, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x2c, 0x0a, 0x12, - 0x70, 0x72, 0x65, 0x76, 0x69, 0x6f, 0x75, 0x73, 0x5f, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x74, 0x69, - 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x10, 0x70, 0x72, 0x65, 0x76, 0x69, 0x6f, - 0x75, 0x73, 0x4c, 0x6f, 0x63, 0x6b, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x6c, 0x6f, - 0x63, 0x6b, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6c, - 0x6f, 0x63, 0x6b, 0x4e, 0x61, 0x6d, 0x65, 0x22, 0x1b, 0x0a, 0x19, 0x52, 0x65, 0x6c, 0x65, 0x61, - 0x73, 0x65, 0x41, 0x64, 0x6d, 0x69, 0x6e, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x32, 0xca, 0x09, 0x0a, 0x07, 0x53, 0x65, 0x61, 0x77, 0x65, 0x65, 0x64, - 0x12, 0x49, 0x0a, 0x0d, 0x53, 0x65, 0x6e, 0x64, 0x48, 0x65, 0x61, 0x72, 0x74, 0x62, 0x65, 0x61, - 0x74, 0x12, 0x14, 0x2e, 0x6d, 0x61, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x48, 0x65, - 0x61, 0x72, 0x74, 0x62, 0x65, 0x61, 0x74, 0x1a, 0x1c, 0x2e, 0x6d, 0x61, 0x73, 0x74, 0x65, 0x72, - 0x5f, 0x70, 0x62, 0x2e, 0x48, 0x65, 0x61, 0x72, 0x74, 0x62, 0x65, 0x61, 0x74, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x28, 0x01, 0x30, 0x01, 0x12, 0x51, 0x0a, 0x0d, 0x4b, - 0x65, 0x65, 0x70, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x65, 0x64, 0x12, 0x1f, 0x2e, 0x6d, - 0x61, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x4b, 0x65, 0x65, 0x70, 0x43, 0x6f, 0x6e, - 0x6e, 0x65, 0x63, 0x74, 0x65, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, - 0x6d, 0x61, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, - 0x4c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x00, 0x28, 0x01, 0x30, 0x01, 0x12, 0x51, - 0x0a, 0x0c, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x12, 0x1e, - 0x2e, 0x6d, 0x61, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, - 0x70, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, - 0x2e, 0x6d, 0x61, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, - 0x70, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, - 0x00, 0x12, 0x3f, 0x0a, 0x06, 0x41, 0x73, 0x73, 0x69, 0x67, 0x6e, 0x12, 0x18, 0x2e, 0x6d, 0x61, - 0x73, 0x74, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x41, 0x73, 0x73, 0x69, 0x67, 0x6e, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x6d, 0x61, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x70, - 0x62, 0x2e, 0x41, 0x73, 0x73, 0x69, 0x67, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x22, 0x00, 0x12, 0x4b, 0x0a, 0x0a, 0x53, 0x74, 0x61, 0x74, 0x69, 0x73, 0x74, 0x69, 0x63, 0x73, - 0x12, 0x1c, 0x2e, 0x6d, 0x61, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x53, 0x74, 0x61, - 0x74, 0x69, 0x73, 0x74, 0x69, 0x63, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, - 0x2e, 0x6d, 0x61, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x69, - 0x73, 0x74, 0x69, 0x63, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, - 0x57, 0x0a, 0x0e, 0x43, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x4c, 0x69, 0x73, - 0x74, 0x12, 0x20, 0x2e, 0x6d, 0x61, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x43, 0x6f, - 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x6d, 0x61, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, - 0x43, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x5d, 0x0a, 0x10, 0x43, 0x6f, 0x6c, 0x6c, - 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x12, 0x22, 0x2e, 0x6d, + 0x61, 0x75, 0x74, 0x68, 0x12, 0x2f, 0x0a, 0x08, 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x73, + 0x18, 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x6d, 0x61, 0x73, 0x74, 0x65, 0x72, 0x5f, + 0x70, 0x62, 0x2e, 0x4c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x08, 0x72, 0x65, 0x70, + 0x6c, 0x69, 0x63, 0x61, 0x73, 0x12, 0x2f, 0x0a, 0x08, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x6d, 0x61, 0x73, 0x74, 0x65, 0x72, + 0x5f, 0x70, 0x62, 0x2e, 0x4c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x08, 0x6c, 0x6f, + 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x84, 0x01, 0x0a, 0x11, 0x53, 0x74, 0x61, 0x74, 0x69, + 0x73, 0x74, 0x69, 0x63, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x20, 0x0a, 0x0b, + 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x0b, 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1e, + 0x0a, 0x0a, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x0a, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x10, + 0x0a, 0x03, 0x74, 0x74, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x74, 0x74, 0x6c, + 0x12, 0x1b, 0x0a, 0x09, 0x64, 0x69, 0x73, 0x6b, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x04, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x08, 0x64, 0x69, 0x73, 0x6b, 0x54, 0x79, 0x70, 0x65, 0x22, 0x6f, 0x0a, + 0x12, 0x53, 0x74, 0x61, 0x74, 0x69, 0x73, 0x74, 0x69, 0x63, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x73, 0x69, 0x7a, + 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x53, 0x69, + 0x7a, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x75, 0x73, 0x65, 0x64, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, + 0x05, 0x20, 0x01, 0x28, 0x04, 0x52, 0x08, 0x75, 0x73, 0x65, 0x64, 0x53, 0x69, 0x7a, 0x65, 0x12, + 0x1d, 0x0a, 0x0a, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x06, 0x20, + 0x01, 0x28, 0x04, 0x52, 0x09, 0x66, 0x69, 0x6c, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0x20, + 0x0a, 0x0a, 0x43, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, + 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, + 0x22, 0x7b, 0x0a, 0x15, 0x43, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x4c, 0x69, + 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x34, 0x0a, 0x16, 0x69, 0x6e, 0x63, + 0x6c, 0x75, 0x64, 0x65, 0x5f, 0x6e, 0x6f, 0x72, 0x6d, 0x61, 0x6c, 0x5f, 0x76, 0x6f, 0x6c, 0x75, + 0x6d, 0x65, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x14, 0x69, 0x6e, 0x63, 0x6c, 0x75, + 0x64, 0x65, 0x4e, 0x6f, 0x72, 0x6d, 0x61, 0x6c, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x73, 0x12, + 0x2c, 0x0a, 0x12, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x5f, 0x65, 0x63, 0x5f, 0x76, 0x6f, + 0x6c, 0x75, 0x6d, 0x65, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x69, 0x6e, 0x63, + 0x6c, 0x75, 0x64, 0x65, 0x45, 0x63, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x73, 0x22, 0x51, 0x0a, + 0x16, 0x43, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x4c, 0x69, 0x73, 0x74, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x37, 0x0a, 0x0b, 0x63, 0x6f, 0x6c, 0x6c, 0x65, + 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x6d, 0x61, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x43, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, - 0x69, 0x6f, 0x6e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x23, 0x2e, 0x6d, 0x61, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x43, 0x6f, 0x6c, - 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x4b, 0x0a, 0x0a, 0x56, 0x6f, 0x6c, 0x75, 0x6d, - 0x65, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x1c, 0x2e, 0x6d, 0x61, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x70, - 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x6d, 0x61, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, - 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x22, 0x00, 0x12, 0x57, 0x0a, 0x0e, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x45, 0x63, - 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x12, 0x20, 0x2e, 0x6d, 0x61, 0x73, 0x74, 0x65, 0x72, 0x5f, - 0x70, 0x62, 0x2e, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x45, 0x63, 0x56, 0x6f, 0x6c, 0x75, 0x6d, - 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x6d, 0x61, 0x73, 0x74, 0x65, - 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x45, 0x63, 0x56, 0x6f, 0x6c, - 0x75, 0x6d, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x51, 0x0a, - 0x0c, 0x56, 0x61, 0x63, 0x75, 0x75, 0x6d, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x12, 0x1e, 0x2e, - 0x6d, 0x61, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x61, 0x63, 0x75, 0x75, 0x6d, - 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, - 0x6d, 0x61, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x61, 0x63, 0x75, 0x75, 0x6d, - 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, - 0x12, 0x6f, 0x0a, 0x16, 0x47, 0x65, 0x74, 0x4d, 0x61, 0x73, 0x74, 0x65, 0x72, 0x43, 0x6f, 0x6e, - 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x28, 0x2e, 0x6d, 0x61, 0x73, - 0x74, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x47, 0x65, 0x74, 0x4d, 0x61, 0x73, 0x74, 0x65, 0x72, - 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x29, 0x2e, 0x6d, 0x61, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x70, 0x62, - 0x2e, 0x47, 0x65, 0x74, 0x4d, 0x61, 0x73, 0x74, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, - 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, - 0x00, 0x12, 0x60, 0x0a, 0x11, 0x4c, 0x69, 0x73, 0x74, 0x4d, 0x61, 0x73, 0x74, 0x65, 0x72, 0x43, - 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x23, 0x2e, 0x6d, 0x61, 0x73, 0x74, 0x65, 0x72, 0x5f, - 0x70, 0x62, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x4d, 0x61, 0x73, 0x74, 0x65, 0x72, 0x43, 0x6c, 0x69, - 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, 0x2e, 0x6d, 0x61, - 0x73, 0x74, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x4d, 0x61, 0x73, 0x74, - 0x65, 0x72, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x22, 0x00, 0x12, 0x5a, 0x0a, 0x0f, 0x4c, 0x65, 0x61, 0x73, 0x65, 0x41, 0x64, 0x6d, 0x69, - 0x6e, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x21, 0x2e, 0x6d, 0x61, 0x73, 0x74, 0x65, 0x72, 0x5f, - 0x70, 0x62, 0x2e, 0x4c, 0x65, 0x61, 0x73, 0x65, 0x41, 0x64, 0x6d, 0x69, 0x6e, 0x54, 0x6f, 0x6b, - 0x65, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x6d, 0x61, 0x73, 0x74, - 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x4c, 0x65, 0x61, 0x73, 0x65, 0x41, 0x64, 0x6d, 0x69, 0x6e, - 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, - 0x60, 0x0a, 0x11, 0x52, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x41, 0x64, 0x6d, 0x69, 0x6e, 0x54, - 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x23, 0x2e, 0x6d, 0x61, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x70, 0x62, - 0x2e, 0x52, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x41, 0x64, 0x6d, 0x69, 0x6e, 0x54, 0x6f, 0x6b, - 0x65, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, 0x2e, 0x6d, 0x61, 0x73, 0x74, - 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x52, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x41, 0x64, 0x6d, - 0x69, 0x6e, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, - 0x00, 0x42, 0x32, 0x5a, 0x30, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, - 0x63, 0x68, 0x72, 0x69, 0x73, 0x6c, 0x75, 0x73, 0x66, 0x2f, 0x73, 0x65, 0x61, 0x77, 0x65, 0x65, - 0x64, 0x66, 0x73, 0x2f, 0x77, 0x65, 0x65, 0x64, 0x2f, 0x70, 0x62, 0x2f, 0x6d, 0x61, 0x73, 0x74, - 0x65, 0x72, 0x5f, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x69, 0x6f, 0x6e, 0x52, 0x0b, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, + 0x22, 0x2d, 0x0a, 0x17, 0x43, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x44, 0x65, + 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, + 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x22, + 0x1a, 0x0a, 0x18, 0x43, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x44, 0x65, 0x6c, + 0x65, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x91, 0x03, 0x0a, 0x08, + 0x44, 0x69, 0x73, 0x6b, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x21, 0x0a, 0x0c, + 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x03, 0x52, 0x0b, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, + 0x28, 0x0a, 0x10, 0x6d, 0x61, 0x78, 0x5f, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x63, 0x6f, + 0x75, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0e, 0x6d, 0x61, 0x78, 0x56, 0x6f, + 0x6c, 0x75, 0x6d, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x2a, 0x0a, 0x11, 0x66, 0x72, 0x65, + 0x65, 0x5f, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x04, + 0x20, 0x01, 0x28, 0x03, 0x52, 0x0f, 0x66, 0x72, 0x65, 0x65, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, + 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x2e, 0x0a, 0x13, 0x61, 0x63, 0x74, 0x69, 0x76, 0x65, 0x5f, + 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x05, 0x20, 0x01, + 0x28, 0x03, 0x52, 0x11, 0x61, 0x63, 0x74, 0x69, 0x76, 0x65, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, + 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x46, 0x0a, 0x0c, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, + 0x69, 0x6e, 0x66, 0x6f, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x6d, 0x61, + 0x73, 0x74, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x49, 0x6e, + 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, + 0x52, 0x0b, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x73, 0x12, 0x50, 0x0a, + 0x0e, 0x65, 0x63, 0x5f, 0x73, 0x68, 0x61, 0x72, 0x64, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x73, 0x18, + 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x6d, 0x61, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x70, + 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x45, 0x63, 0x53, 0x68, 0x61, 0x72, 0x64, 0x49, + 0x6e, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, + 0x65, 0x52, 0x0c, 0x65, 0x63, 0x53, 0x68, 0x61, 0x72, 0x64, 0x49, 0x6e, 0x66, 0x6f, 0x73, 0x12, + 0x2e, 0x0a, 0x13, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x5f, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, + 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x08, 0x20, 0x01, 0x28, 0x03, 0x52, 0x11, 0x72, 0x65, + 0x6d, 0x6f, 0x74, 0x65, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x22, + 0xd4, 0x01, 0x0a, 0x0c, 0x44, 0x61, 0x74, 0x61, 0x4e, 0x6f, 0x64, 0x65, 0x49, 0x6e, 0x66, 0x6f, + 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, + 0x12, 0x44, 0x0a, 0x09, 0x64, 0x69, 0x73, 0x6b, 0x49, 0x6e, 0x66, 0x6f, 0x73, 0x18, 0x02, 0x20, + 0x03, 0x28, 0x0b, 0x32, 0x26, 0x2e, 0x6d, 0x61, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, + 0x44, 0x61, 0x74, 0x61, 0x4e, 0x6f, 0x64, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x2e, 0x44, 0x69, 0x73, + 0x6b, 0x49, 0x6e, 0x66, 0x6f, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x09, 0x64, 0x69, 0x73, + 0x6b, 0x49, 0x6e, 0x66, 0x6f, 0x73, 0x12, 0x1b, 0x0a, 0x09, 0x67, 0x72, 0x70, 0x63, 0x5f, 0x70, + 0x6f, 0x72, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x67, 0x72, 0x70, 0x63, 0x50, + 0x6f, 0x72, 0x74, 0x1a, 0x51, 0x0a, 0x0e, 0x44, 0x69, 0x73, 0x6b, 0x49, 0x6e, 0x66, 0x6f, 0x73, + 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x29, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x6d, 0x61, 0x73, 0x74, 0x65, 0x72, 0x5f, + 0x70, 0x62, 0x2e, 0x44, 0x69, 0x73, 0x6b, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x05, 0x76, 0x61, 0x6c, + 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xf0, 0x01, 0x0a, 0x08, 0x52, 0x61, 0x63, 0x6b, 0x49, + 0x6e, 0x66, 0x6f, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x02, 0x69, 0x64, 0x12, 0x3f, 0x0a, 0x0f, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x6e, 0x6f, 0x64, 0x65, + 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x6d, + 0x61, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x44, 0x61, 0x74, 0x61, 0x4e, 0x6f, 0x64, + 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x0d, 0x64, 0x61, 0x74, 0x61, 0x4e, 0x6f, 0x64, 0x65, 0x49, + 0x6e, 0x66, 0x6f, 0x73, 0x12, 0x40, 0x0a, 0x09, 0x64, 0x69, 0x73, 0x6b, 0x49, 0x6e, 0x66, 0x6f, + 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x6d, 0x61, 0x73, 0x74, 0x65, 0x72, + 0x5f, 0x70, 0x62, 0x2e, 0x52, 0x61, 0x63, 0x6b, 0x49, 0x6e, 0x66, 0x6f, 0x2e, 0x44, 0x69, 0x73, + 0x6b, 0x49, 0x6e, 0x66, 0x6f, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x09, 0x64, 0x69, 0x73, + 0x6b, 0x49, 0x6e, 0x66, 0x6f, 0x73, 0x1a, 0x51, 0x0a, 0x0e, 0x44, 0x69, 0x73, 0x6b, 0x49, 0x6e, + 0x66, 0x6f, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x29, 0x0a, 0x05, 0x76, 0x61, + 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x6d, 0x61, 0x73, 0x74, + 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x44, 0x69, 0x73, 0x6b, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x05, + 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xef, 0x01, 0x0a, 0x0e, 0x44, 0x61, + 0x74, 0x61, 0x43, 0x65, 0x6e, 0x74, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x0e, 0x0a, 0x02, + 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x32, 0x0a, 0x0a, + 0x72, 0x61, 0x63, 0x6b, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x13, 0x2e, 0x6d, 0x61, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x52, 0x61, 0x63, + 0x6b, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x09, 0x72, 0x61, 0x63, 0x6b, 0x49, 0x6e, 0x66, 0x6f, 0x73, + 0x12, 0x46, 0x0a, 0x09, 0x64, 0x69, 0x73, 0x6b, 0x49, 0x6e, 0x66, 0x6f, 0x73, 0x18, 0x03, 0x20, + 0x03, 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x6d, 0x61, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, + 0x44, 0x61, 0x74, 0x61, 0x43, 0x65, 0x6e, 0x74, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x2e, 0x44, + 0x69, 0x73, 0x6b, 0x49, 0x6e, 0x66, 0x6f, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x09, 0x64, + 0x69, 0x73, 0x6b, 0x49, 0x6e, 0x66, 0x6f, 0x73, 0x1a, 0x51, 0x0a, 0x0e, 0x44, 0x69, 0x73, 0x6b, + 0x49, 0x6e, 0x66, 0x6f, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, + 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x29, 0x0a, 0x05, + 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x6d, 0x61, + 0x73, 0x74, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x44, 0x69, 0x73, 0x6b, 0x49, 0x6e, 0x66, 0x6f, + 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xfe, 0x01, 0x0a, 0x0c, + 0x54, 0x6f, 0x70, 0x6f, 0x6c, 0x6f, 0x67, 0x79, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x0e, 0x0a, 0x02, + 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x45, 0x0a, 0x11, + 0x64, 0x61, 0x74, 0x61, 0x5f, 0x63, 0x65, 0x6e, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x6e, 0x66, 0x6f, + 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x6d, 0x61, 0x73, 0x74, 0x65, 0x72, + 0x5f, 0x70, 0x62, 0x2e, 0x44, 0x61, 0x74, 0x61, 0x43, 0x65, 0x6e, 0x74, 0x65, 0x72, 0x49, 0x6e, + 0x66, 0x6f, 0x52, 0x0f, 0x64, 0x61, 0x74, 0x61, 0x43, 0x65, 0x6e, 0x74, 0x65, 0x72, 0x49, 0x6e, + 0x66, 0x6f, 0x73, 0x12, 0x44, 0x0a, 0x09, 0x64, 0x69, 0x73, 0x6b, 0x49, 0x6e, 0x66, 0x6f, 0x73, + 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x26, 0x2e, 0x6d, 0x61, 0x73, 0x74, 0x65, 0x72, 0x5f, + 0x70, 0x62, 0x2e, 0x54, 0x6f, 0x70, 0x6f, 0x6c, 0x6f, 0x67, 0x79, 0x49, 0x6e, 0x66, 0x6f, 0x2e, + 0x44, 0x69, 0x73, 0x6b, 0x49, 0x6e, 0x66, 0x6f, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x09, + 0x64, 0x69, 0x73, 0x6b, 0x49, 0x6e, 0x66, 0x6f, 0x73, 0x1a, 0x51, 0x0a, 0x0e, 0x44, 0x69, 0x73, + 0x6b, 0x49, 0x6e, 0x66, 0x6f, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, + 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x29, 0x0a, + 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x6d, + 0x61, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x44, 0x69, 0x73, 0x6b, 0x49, 0x6e, 0x66, + 0x6f, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x13, 0x0a, 0x11, + 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x22, 0x83, 0x01, 0x0a, 0x12, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x4c, 0x69, 0x73, 0x74, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3c, 0x0a, 0x0d, 0x74, 0x6f, 0x70, 0x6f, + 0x6c, 0x6f, 0x67, 0x79, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x17, 0x2e, 0x6d, 0x61, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x54, 0x6f, 0x70, 0x6f, + 0x6c, 0x6f, 0x67, 0x79, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x0c, 0x74, 0x6f, 0x70, 0x6f, 0x6c, 0x6f, + 0x67, 0x79, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x2f, 0x0a, 0x14, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, + 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x5f, 0x6d, 0x62, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x04, 0x52, 0x11, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x53, 0x69, 0x7a, 0x65, + 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x4d, 0x62, 0x22, 0x34, 0x0a, 0x15, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, + 0x70, 0x45, 0x63, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x12, 0x1b, 0x0a, 0x09, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x0d, 0x52, 0x08, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x49, 0x64, 0x22, 0xfb, 0x01, + 0x0a, 0x16, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x45, 0x63, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x76, 0x6f, 0x6c, 0x75, + 0x6d, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x76, 0x6f, 0x6c, + 0x75, 0x6d, 0x65, 0x49, 0x64, 0x12, 0x61, 0x0a, 0x12, 0x73, 0x68, 0x61, 0x72, 0x64, 0x5f, 0x69, + 0x64, 0x5f, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, + 0x0b, 0x32, 0x33, 0x2e, 0x6d, 0x61, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x4c, 0x6f, + 0x6f, 0x6b, 0x75, 0x70, 0x45, 0x63, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x45, 0x63, 0x53, 0x68, 0x61, 0x72, 0x64, 0x49, 0x64, 0x4c, 0x6f, + 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x10, 0x73, 0x68, 0x61, 0x72, 0x64, 0x49, 0x64, 0x4c, + 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x1a, 0x61, 0x0a, 0x11, 0x45, 0x63, 0x53, 0x68, + 0x61, 0x72, 0x64, 0x49, 0x64, 0x4c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x19, 0x0a, + 0x08, 0x73, 0x68, 0x61, 0x72, 0x64, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, + 0x07, 0x73, 0x68, 0x61, 0x72, 0x64, 0x49, 0x64, 0x12, 0x31, 0x0a, 0x09, 0x6c, 0x6f, 0x63, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x6d, 0x61, + 0x73, 0x74, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x4c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x52, 0x09, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x42, 0x0a, 0x13, 0x56, + 0x61, 0x63, 0x75, 0x75, 0x6d, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x12, 0x2b, 0x0a, 0x11, 0x67, 0x61, 0x72, 0x62, 0x61, 0x67, 0x65, 0x5f, 0x74, 0x68, + 0x72, 0x65, 0x73, 0x68, 0x6f, 0x6c, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x02, 0x52, 0x10, 0x67, + 0x61, 0x72, 0x62, 0x61, 0x67, 0x65, 0x54, 0x68, 0x72, 0x65, 0x73, 0x68, 0x6f, 0x6c, 0x64, 0x22, + 0x16, 0x0a, 0x14, 0x56, 0x61, 0x63, 0x75, 0x75, 0x6d, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1f, 0x0a, 0x1d, 0x47, 0x65, 0x74, 0x4d, 0x61, + 0x73, 0x74, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0xf3, 0x02, 0x0a, 0x1e, 0x47, 0x65, 0x74, + 0x4d, 0x61, 0x73, 0x74, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x27, 0x0a, 0x0f, 0x6d, + 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x41, 0x64, 0x64, + 0x72, 0x65, 0x73, 0x73, 0x12, 0x38, 0x0a, 0x18, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x5f, + 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x5f, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x16, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x49, + 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x53, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x12, 0x44, + 0x0a, 0x10, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x5f, 0x62, 0x61, 0x63, 0x6b, 0x65, 0x6e, + 0x64, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x6d, 0x61, 0x73, 0x74, 0x65, + 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x42, 0x61, 0x63, 0x6b, + 0x65, 0x6e, 0x64, 0x52, 0x0f, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x42, 0x61, 0x63, 0x6b, + 0x65, 0x6e, 0x64, 0x73, 0x12, 0x2f, 0x0a, 0x13, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x5f, + 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x12, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x16, 0x0a, 0x06, 0x6c, 0x65, 0x61, 0x64, 0x65, 0x72, 0x18, + 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x6c, 0x65, 0x61, 0x64, 0x65, 0x72, 0x12, 0x30, 0x0a, + 0x15, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x5f, 0x6c, 0x69, 0x6d, + 0x69, 0x74, 0x5f, 0x6d, 0x5f, 0x62, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x11, 0x76, 0x6f, + 0x6c, 0x75, 0x6d, 0x65, 0x53, 0x69, 0x7a, 0x65, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x4d, 0x42, 0x12, + 0x2d, 0x0a, 0x12, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x70, 0x72, 0x65, 0x61, 0x6c, 0x6c, + 0x6f, 0x63, 0x61, 0x74, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x11, 0x76, 0x6f, 0x6c, + 0x75, 0x6d, 0x65, 0x50, 0x72, 0x65, 0x61, 0x6c, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x65, 0x22, 0x3b, + 0x0a, 0x18, 0x4c, 0x69, 0x73, 0x74, 0x4d, 0x61, 0x73, 0x74, 0x65, 0x72, 0x43, 0x6c, 0x69, 0x65, + 0x6e, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x63, 0x6c, + 0x69, 0x65, 0x6e, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x0a, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x22, 0x42, 0x0a, 0x19, 0x4c, + 0x69, 0x73, 0x74, 0x4d, 0x61, 0x73, 0x74, 0x65, 0x72, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x73, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x25, 0x0a, 0x0e, 0x67, 0x72, 0x70, 0x63, + 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, + 0x52, 0x0d, 0x67, 0x72, 0x70, 0x63, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x22, + 0xab, 0x01, 0x0a, 0x16, 0x4c, 0x65, 0x61, 0x73, 0x65, 0x41, 0x64, 0x6d, 0x69, 0x6e, 0x54, 0x6f, + 0x6b, 0x65, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x25, 0x0a, 0x0e, 0x70, 0x72, + 0x65, 0x76, 0x69, 0x6f, 0x75, 0x73, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x03, 0x52, 0x0d, 0x70, 0x72, 0x65, 0x76, 0x69, 0x6f, 0x75, 0x73, 0x54, 0x6f, 0x6b, 0x65, + 0x6e, 0x12, 0x2c, 0x0a, 0x12, 0x70, 0x72, 0x65, 0x76, 0x69, 0x6f, 0x75, 0x73, 0x5f, 0x6c, 0x6f, + 0x63, 0x6b, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x10, 0x70, + 0x72, 0x65, 0x76, 0x69, 0x6f, 0x75, 0x73, 0x4c, 0x6f, 0x63, 0x6b, 0x54, 0x69, 0x6d, 0x65, 0x12, + 0x1b, 0x0a, 0x09, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x08, 0x6c, 0x6f, 0x63, 0x6b, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1f, 0x0a, 0x0b, + 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x0a, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x22, 0x4d, 0x0a, + 0x17, 0x4c, 0x65, 0x61, 0x73, 0x65, 0x41, 0x64, 0x6d, 0x69, 0x6e, 0x54, 0x6f, 0x6b, 0x65, 0x6e, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x6f, 0x6b, 0x65, + 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x1c, + 0x0a, 0x0a, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x74, 0x73, 0x5f, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x03, 0x52, 0x08, 0x6c, 0x6f, 0x63, 0x6b, 0x54, 0x73, 0x4e, 0x73, 0x22, 0x8c, 0x01, 0x0a, + 0x18, 0x52, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x41, 0x64, 0x6d, 0x69, 0x6e, 0x54, 0x6f, 0x6b, + 0x65, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x25, 0x0a, 0x0e, 0x70, 0x72, 0x65, + 0x76, 0x69, 0x6f, 0x75, 0x73, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x03, 0x52, 0x0d, 0x70, 0x72, 0x65, 0x76, 0x69, 0x6f, 0x75, 0x73, 0x54, 0x6f, 0x6b, 0x65, 0x6e, + 0x12, 0x2c, 0x0a, 0x12, 0x70, 0x72, 0x65, 0x76, 0x69, 0x6f, 0x75, 0x73, 0x5f, 0x6c, 0x6f, 0x63, + 0x6b, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x10, 0x70, 0x72, + 0x65, 0x76, 0x69, 0x6f, 0x75, 0x73, 0x4c, 0x6f, 0x63, 0x6b, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x1b, + 0x0a, 0x09, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x08, 0x6c, 0x6f, 0x63, 0x6b, 0x4e, 0x61, 0x6d, 0x65, 0x22, 0x1b, 0x0a, 0x19, 0x52, + 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x41, 0x64, 0x6d, 0x69, 0x6e, 0x54, 0x6f, 0x6b, 0x65, 0x6e, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x32, 0xca, 0x09, 0x0a, 0x07, 0x53, 0x65, 0x61, + 0x77, 0x65, 0x65, 0x64, 0x12, 0x49, 0x0a, 0x0d, 0x53, 0x65, 0x6e, 0x64, 0x48, 0x65, 0x61, 0x72, + 0x74, 0x62, 0x65, 0x61, 0x74, 0x12, 0x14, 0x2e, 0x6d, 0x61, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x70, + 0x62, 0x2e, 0x48, 0x65, 0x61, 0x72, 0x74, 0x62, 0x65, 0x61, 0x74, 0x1a, 0x1c, 0x2e, 0x6d, 0x61, + 0x73, 0x74, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x48, 0x65, 0x61, 0x72, 0x74, 0x62, 0x65, 0x61, + 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x28, 0x01, 0x30, 0x01, 0x12, + 0x51, 0x0a, 0x0d, 0x4b, 0x65, 0x65, 0x70, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x65, 0x64, + 0x12, 0x1f, 0x2e, 0x6d, 0x61, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x4b, 0x65, 0x65, + 0x70, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x65, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x19, 0x2e, 0x6d, 0x61, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, + 0x6c, 0x75, 0x6d, 0x65, 0x4c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x00, 0x28, 0x01, + 0x30, 0x01, 0x12, 0x51, 0x0a, 0x0c, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x56, 0x6f, 0x6c, 0x75, + 0x6d, 0x65, 0x12, 0x1e, 0x2e, 0x6d, 0x61, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x4c, + 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x6d, 0x61, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x4c, + 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x3f, 0x0a, 0x06, 0x41, 0x73, 0x73, 0x69, 0x67, 0x6e, 0x12, + 0x18, 0x2e, 0x6d, 0x61, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x41, 0x73, 0x73, 0x69, + 0x67, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x6d, 0x61, 0x73, 0x74, + 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x41, 0x73, 0x73, 0x69, 0x67, 0x6e, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x4b, 0x0a, 0x0a, 0x53, 0x74, 0x61, 0x74, 0x69, 0x73, + 0x74, 0x69, 0x63, 0x73, 0x12, 0x1c, 0x2e, 0x6d, 0x61, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x70, 0x62, + 0x2e, 0x53, 0x74, 0x61, 0x74, 0x69, 0x73, 0x74, 0x69, 0x63, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x6d, 0x61, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x53, + 0x74, 0x61, 0x74, 0x69, 0x73, 0x74, 0x69, 0x63, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x22, 0x00, 0x12, 0x57, 0x0a, 0x0e, 0x43, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, + 0x6e, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x20, 0x2e, 0x6d, 0x61, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x70, + 0x62, 0x2e, 0x43, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x4c, 0x69, 0x73, 0x74, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x6d, 0x61, 0x73, 0x74, 0x65, 0x72, + 0x5f, 0x70, 0x62, 0x2e, 0x43, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x4c, 0x69, + 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x5d, 0x0a, 0x10, + 0x43, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, + 0x12, 0x22, 0x2e, 0x6d, 0x61, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x43, 0x6f, 0x6c, + 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x6d, 0x61, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x70, 0x62, + 0x2e, 0x43, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x44, 0x65, 0x6c, 0x65, 0x74, + 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x4b, 0x0a, 0x0a, 0x56, + 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x1c, 0x2e, 0x6d, 0x61, 0x73, 0x74, + 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x4c, 0x69, 0x73, 0x74, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x6d, 0x61, 0x73, 0x74, 0x65, 0x72, + 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x57, 0x0a, 0x0e, 0x4c, 0x6f, 0x6f, 0x6b, + 0x75, 0x70, 0x45, 0x63, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x12, 0x20, 0x2e, 0x6d, 0x61, 0x73, + 0x74, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x45, 0x63, 0x56, + 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x6d, + 0x61, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x45, + 0x63, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, + 0x00, 0x12, 0x51, 0x0a, 0x0c, 0x56, 0x61, 0x63, 0x75, 0x75, 0x6d, 0x56, 0x6f, 0x6c, 0x75, 0x6d, + 0x65, 0x12, 0x1e, 0x2e, 0x6d, 0x61, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x61, + 0x63, 0x75, 0x75, 0x6d, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x1f, 0x2e, 0x6d, 0x61, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x61, + 0x63, 0x75, 0x75, 0x6d, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x22, 0x00, 0x12, 0x6f, 0x0a, 0x16, 0x47, 0x65, 0x74, 0x4d, 0x61, 0x73, 0x74, 0x65, + 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x28, + 0x2e, 0x6d, 0x61, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x47, 0x65, 0x74, 0x4d, 0x61, + 0x73, 0x74, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x29, 0x2e, 0x6d, 0x61, 0x73, 0x74, 0x65, + 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x47, 0x65, 0x74, 0x4d, 0x61, 0x73, 0x74, 0x65, 0x72, 0x43, 0x6f, + 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x60, 0x0a, 0x11, 0x4c, 0x69, 0x73, 0x74, 0x4d, 0x61, 0x73, + 0x74, 0x65, 0x72, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x23, 0x2e, 0x6d, 0x61, 0x73, + 0x74, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x4d, 0x61, 0x73, 0x74, 0x65, + 0x72, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x24, 0x2e, 0x6d, 0x61, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x4c, 0x69, 0x73, 0x74, + 0x4d, 0x61, 0x73, 0x74, 0x65, 0x72, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x5a, 0x0a, 0x0f, 0x4c, 0x65, 0x61, 0x73, 0x65, + 0x41, 0x64, 0x6d, 0x69, 0x6e, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x21, 0x2e, 0x6d, 0x61, 0x73, + 0x74, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x4c, 0x65, 0x61, 0x73, 0x65, 0x41, 0x64, 0x6d, 0x69, + 0x6e, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, + 0x6d, 0x61, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x4c, 0x65, 0x61, 0x73, 0x65, 0x41, + 0x64, 0x6d, 0x69, 0x6e, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x22, 0x00, 0x12, 0x60, 0x0a, 0x11, 0x52, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x41, 0x64, + 0x6d, 0x69, 0x6e, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x23, 0x2e, 0x6d, 0x61, 0x73, 0x74, 0x65, + 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x52, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x41, 0x64, 0x6d, 0x69, + 0x6e, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, 0x2e, + 0x6d, 0x61, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x52, 0x65, 0x6c, 0x65, 0x61, 0x73, + 0x65, 0x41, 0x64, 0x6d, 0x69, 0x6e, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, 0x32, 0x5a, 0x30, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, + 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x68, 0x72, 0x69, 0x73, 0x6c, 0x75, 0x73, 0x66, 0x2f, 0x73, 0x65, + 0x61, 0x77, 0x65, 0x65, 0x64, 0x66, 0x73, 0x2f, 0x77, 0x65, 0x65, 0x64, 0x2f, 0x70, 0x62, 0x2f, + 0x6d, 0x61, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x33, } var ( @@ -3502,59 +3475,58 @@ func file_master_proto_rawDescGZIP() []byte { return file_master_proto_rawDescData } -var file_master_proto_msgTypes = make([]protoimpl.MessageInfo, 51) +var file_master_proto_msgTypes = make([]protoimpl.MessageInfo, 50) var file_master_proto_goTypes = []interface{}{ - (*Heartbeat)(nil), // 0: master_pb.Heartbeat - (*HeartbeatResponse)(nil), // 1: master_pb.HeartbeatResponse - (*VolumeInformationMessage)(nil), // 2: master_pb.VolumeInformationMessage - (*VolumeShortInformationMessage)(nil), // 3: master_pb.VolumeShortInformationMessage - (*VolumeEcShardInformationMessage)(nil), // 4: master_pb.VolumeEcShardInformationMessage - (*StorageBackend)(nil), // 5: master_pb.StorageBackend - (*Empty)(nil), // 6: master_pb.Empty - (*SuperBlockExtra)(nil), // 7: master_pb.SuperBlockExtra - (*KeepConnectedRequest)(nil), // 8: master_pb.KeepConnectedRequest - (*VolumeLocation)(nil), // 9: master_pb.VolumeLocation - (*LookupVolumeRequest)(nil), // 10: master_pb.LookupVolumeRequest - (*LookupVolumeResponse)(nil), // 11: master_pb.LookupVolumeResponse - (*Location)(nil), // 12: master_pb.Location - (*AssignRequest)(nil), // 13: master_pb.AssignRequest - (*AssignResponse)(nil), // 14: master_pb.AssignResponse - (*StatisticsRequest)(nil), // 15: master_pb.StatisticsRequest - (*StatisticsResponse)(nil), // 16: master_pb.StatisticsResponse - (*Collection)(nil), // 17: master_pb.Collection - (*CollectionListRequest)(nil), // 18: master_pb.CollectionListRequest - (*CollectionListResponse)(nil), // 19: master_pb.CollectionListResponse - (*CollectionDeleteRequest)(nil), // 20: master_pb.CollectionDeleteRequest - (*CollectionDeleteResponse)(nil), // 21: master_pb.CollectionDeleteResponse - (*DiskInfo)(nil), // 22: master_pb.DiskInfo - (*DataNodeInfo)(nil), // 23: master_pb.DataNodeInfo - (*RackInfo)(nil), // 24: master_pb.RackInfo - (*DataCenterInfo)(nil), // 25: master_pb.DataCenterInfo - (*TopologyInfo)(nil), // 26: master_pb.TopologyInfo - (*VolumeListRequest)(nil), // 27: master_pb.VolumeListRequest - (*VolumeListResponse)(nil), // 28: master_pb.VolumeListResponse - (*LookupEcVolumeRequest)(nil), // 29: master_pb.LookupEcVolumeRequest - (*LookupEcVolumeResponse)(nil), // 30: master_pb.LookupEcVolumeResponse - (*VacuumVolumeRequest)(nil), // 31: master_pb.VacuumVolumeRequest - (*VacuumVolumeResponse)(nil), // 32: master_pb.VacuumVolumeResponse - (*GetMasterConfigurationRequest)(nil), // 33: master_pb.GetMasterConfigurationRequest - (*GetMasterConfigurationResponse)(nil), // 34: master_pb.GetMasterConfigurationResponse - (*ListMasterClientsRequest)(nil), // 35: master_pb.ListMasterClientsRequest - (*ListMasterClientsResponse)(nil), // 36: master_pb.ListMasterClientsResponse - (*LeaseAdminTokenRequest)(nil), // 37: master_pb.LeaseAdminTokenRequest - (*LeaseAdminTokenResponse)(nil), // 38: master_pb.LeaseAdminTokenResponse - (*ReleaseAdminTokenRequest)(nil), // 39: master_pb.ReleaseAdminTokenRequest - (*ReleaseAdminTokenResponse)(nil), // 40: master_pb.ReleaseAdminTokenResponse - nil, // 41: master_pb.Heartbeat.MaxVolumeCountsEntry - nil, // 42: master_pb.StorageBackend.PropertiesEntry - (*SuperBlockExtra_ErasureCoding)(nil), // 43: master_pb.SuperBlockExtra.ErasureCoding - (*LookupVolumeResponse_VolumeIdLocation)(nil), // 44: master_pb.LookupVolumeResponse.VolumeIdLocation - (*AssignResponse_Replica)(nil), // 45: master_pb.AssignResponse.Replica - nil, // 46: master_pb.DataNodeInfo.DiskInfosEntry - nil, // 47: master_pb.RackInfo.DiskInfosEntry - nil, // 48: master_pb.DataCenterInfo.DiskInfosEntry - nil, // 49: master_pb.TopologyInfo.DiskInfosEntry - (*LookupEcVolumeResponse_EcShardIdLocation)(nil), // 50: master_pb.LookupEcVolumeResponse.EcShardIdLocation + (*Heartbeat)(nil), // 0: master_pb.Heartbeat + (*HeartbeatResponse)(nil), // 1: master_pb.HeartbeatResponse + (*VolumeInformationMessage)(nil), // 2: master_pb.VolumeInformationMessage + (*VolumeShortInformationMessage)(nil), // 3: master_pb.VolumeShortInformationMessage + (*VolumeEcShardInformationMessage)(nil), // 4: master_pb.VolumeEcShardInformationMessage + (*StorageBackend)(nil), // 5: master_pb.StorageBackend + (*Empty)(nil), // 6: master_pb.Empty + (*SuperBlockExtra)(nil), // 7: master_pb.SuperBlockExtra + (*KeepConnectedRequest)(nil), // 8: master_pb.KeepConnectedRequest + (*VolumeLocation)(nil), // 9: master_pb.VolumeLocation + (*LookupVolumeRequest)(nil), // 10: master_pb.LookupVolumeRequest + (*LookupVolumeResponse)(nil), // 11: master_pb.LookupVolumeResponse + (*Location)(nil), // 12: master_pb.Location + (*AssignRequest)(nil), // 13: master_pb.AssignRequest + (*AssignResponse)(nil), // 14: master_pb.AssignResponse + (*StatisticsRequest)(nil), // 15: master_pb.StatisticsRequest + (*StatisticsResponse)(nil), // 16: master_pb.StatisticsResponse + (*Collection)(nil), // 17: master_pb.Collection + (*CollectionListRequest)(nil), // 18: master_pb.CollectionListRequest + (*CollectionListResponse)(nil), // 19: master_pb.CollectionListResponse + (*CollectionDeleteRequest)(nil), // 20: master_pb.CollectionDeleteRequest + (*CollectionDeleteResponse)(nil), // 21: master_pb.CollectionDeleteResponse + (*DiskInfo)(nil), // 22: master_pb.DiskInfo + (*DataNodeInfo)(nil), // 23: master_pb.DataNodeInfo + (*RackInfo)(nil), // 24: master_pb.RackInfo + (*DataCenterInfo)(nil), // 25: master_pb.DataCenterInfo + (*TopologyInfo)(nil), // 26: master_pb.TopologyInfo + (*VolumeListRequest)(nil), // 27: master_pb.VolumeListRequest + (*VolumeListResponse)(nil), // 28: master_pb.VolumeListResponse + (*LookupEcVolumeRequest)(nil), // 29: master_pb.LookupEcVolumeRequest + (*LookupEcVolumeResponse)(nil), // 30: master_pb.LookupEcVolumeResponse + (*VacuumVolumeRequest)(nil), // 31: master_pb.VacuumVolumeRequest + (*VacuumVolumeResponse)(nil), // 32: master_pb.VacuumVolumeResponse + (*GetMasterConfigurationRequest)(nil), // 33: master_pb.GetMasterConfigurationRequest + (*GetMasterConfigurationResponse)(nil), // 34: master_pb.GetMasterConfigurationResponse + (*ListMasterClientsRequest)(nil), // 35: master_pb.ListMasterClientsRequest + (*ListMasterClientsResponse)(nil), // 36: master_pb.ListMasterClientsResponse + (*LeaseAdminTokenRequest)(nil), // 37: master_pb.LeaseAdminTokenRequest + (*LeaseAdminTokenResponse)(nil), // 38: master_pb.LeaseAdminTokenResponse + (*ReleaseAdminTokenRequest)(nil), // 39: master_pb.ReleaseAdminTokenRequest + (*ReleaseAdminTokenResponse)(nil), // 40: master_pb.ReleaseAdminTokenResponse + nil, // 41: master_pb.Heartbeat.MaxVolumeCountsEntry + nil, // 42: master_pb.StorageBackend.PropertiesEntry + (*SuperBlockExtra_ErasureCoding)(nil), // 43: master_pb.SuperBlockExtra.ErasureCoding + (*LookupVolumeResponse_VolumeIdLocation)(nil), // 44: master_pb.LookupVolumeResponse.VolumeIdLocation + nil, // 45: master_pb.DataNodeInfo.DiskInfosEntry + nil, // 46: master_pb.RackInfo.DiskInfosEntry + nil, // 47: master_pb.DataCenterInfo.DiskInfosEntry + nil, // 48: master_pb.TopologyInfo.DiskInfosEntry + (*LookupEcVolumeResponse_EcShardIdLocation)(nil), // 49: master_pb.LookupEcVolumeResponse.EcShardIdLocation } var file_master_proto_depIdxs = []int32{ 2, // 0: master_pb.Heartbeat.volumes:type_name -> master_pb.VolumeInformationMessage @@ -3568,59 +3540,60 @@ var file_master_proto_depIdxs = []int32{ 42, // 8: master_pb.StorageBackend.properties:type_name -> master_pb.StorageBackend.PropertiesEntry 43, // 9: master_pb.SuperBlockExtra.erasure_coding:type_name -> master_pb.SuperBlockExtra.ErasureCoding 44, // 10: master_pb.LookupVolumeResponse.volume_id_locations:type_name -> master_pb.LookupVolumeResponse.VolumeIdLocation - 45, // 11: master_pb.AssignResponse.replicas:type_name -> master_pb.AssignResponse.Replica - 17, // 12: master_pb.CollectionListResponse.collections:type_name -> master_pb.Collection - 2, // 13: master_pb.DiskInfo.volume_infos:type_name -> master_pb.VolumeInformationMessage - 4, // 14: master_pb.DiskInfo.ec_shard_infos:type_name -> master_pb.VolumeEcShardInformationMessage - 46, // 15: master_pb.DataNodeInfo.diskInfos:type_name -> master_pb.DataNodeInfo.DiskInfosEntry - 23, // 16: master_pb.RackInfo.data_node_infos:type_name -> master_pb.DataNodeInfo - 47, // 17: master_pb.RackInfo.diskInfos:type_name -> master_pb.RackInfo.DiskInfosEntry - 24, // 18: master_pb.DataCenterInfo.rack_infos:type_name -> master_pb.RackInfo - 48, // 19: master_pb.DataCenterInfo.diskInfos:type_name -> master_pb.DataCenterInfo.DiskInfosEntry - 25, // 20: master_pb.TopologyInfo.data_center_infos:type_name -> master_pb.DataCenterInfo - 49, // 21: master_pb.TopologyInfo.diskInfos:type_name -> master_pb.TopologyInfo.DiskInfosEntry - 26, // 22: master_pb.VolumeListResponse.topology_info:type_name -> master_pb.TopologyInfo - 50, // 23: master_pb.LookupEcVolumeResponse.shard_id_locations:type_name -> master_pb.LookupEcVolumeResponse.EcShardIdLocation - 5, // 24: master_pb.GetMasterConfigurationResponse.storage_backends:type_name -> master_pb.StorageBackend - 12, // 25: master_pb.LookupVolumeResponse.VolumeIdLocation.locations:type_name -> master_pb.Location - 22, // 26: master_pb.DataNodeInfo.DiskInfosEntry.value:type_name -> master_pb.DiskInfo - 22, // 27: master_pb.RackInfo.DiskInfosEntry.value:type_name -> master_pb.DiskInfo - 22, // 28: master_pb.DataCenterInfo.DiskInfosEntry.value:type_name -> master_pb.DiskInfo - 22, // 29: master_pb.TopologyInfo.DiskInfosEntry.value:type_name -> master_pb.DiskInfo - 12, // 30: master_pb.LookupEcVolumeResponse.EcShardIdLocation.locations:type_name -> master_pb.Location - 0, // 31: master_pb.Seaweed.SendHeartbeat:input_type -> master_pb.Heartbeat - 8, // 32: master_pb.Seaweed.KeepConnected:input_type -> master_pb.KeepConnectedRequest - 10, // 33: master_pb.Seaweed.LookupVolume:input_type -> master_pb.LookupVolumeRequest - 13, // 34: master_pb.Seaweed.Assign:input_type -> master_pb.AssignRequest - 15, // 35: master_pb.Seaweed.Statistics:input_type -> master_pb.StatisticsRequest - 18, // 36: master_pb.Seaweed.CollectionList:input_type -> master_pb.CollectionListRequest - 20, // 37: master_pb.Seaweed.CollectionDelete:input_type -> master_pb.CollectionDeleteRequest - 27, // 38: master_pb.Seaweed.VolumeList:input_type -> master_pb.VolumeListRequest - 29, // 39: master_pb.Seaweed.LookupEcVolume:input_type -> master_pb.LookupEcVolumeRequest - 31, // 40: master_pb.Seaweed.VacuumVolume:input_type -> master_pb.VacuumVolumeRequest - 33, // 41: master_pb.Seaweed.GetMasterConfiguration:input_type -> master_pb.GetMasterConfigurationRequest - 35, // 42: master_pb.Seaweed.ListMasterClients:input_type -> master_pb.ListMasterClientsRequest - 37, // 43: master_pb.Seaweed.LeaseAdminToken:input_type -> master_pb.LeaseAdminTokenRequest - 39, // 44: master_pb.Seaweed.ReleaseAdminToken:input_type -> master_pb.ReleaseAdminTokenRequest - 1, // 45: master_pb.Seaweed.SendHeartbeat:output_type -> master_pb.HeartbeatResponse - 9, // 46: master_pb.Seaweed.KeepConnected:output_type -> master_pb.VolumeLocation - 11, // 47: master_pb.Seaweed.LookupVolume:output_type -> master_pb.LookupVolumeResponse - 14, // 48: master_pb.Seaweed.Assign:output_type -> master_pb.AssignResponse - 16, // 49: master_pb.Seaweed.Statistics:output_type -> master_pb.StatisticsResponse - 19, // 50: master_pb.Seaweed.CollectionList:output_type -> master_pb.CollectionListResponse - 21, // 51: master_pb.Seaweed.CollectionDelete:output_type -> master_pb.CollectionDeleteResponse - 28, // 52: master_pb.Seaweed.VolumeList:output_type -> master_pb.VolumeListResponse - 30, // 53: master_pb.Seaweed.LookupEcVolume:output_type -> master_pb.LookupEcVolumeResponse - 32, // 54: master_pb.Seaweed.VacuumVolume:output_type -> master_pb.VacuumVolumeResponse - 34, // 55: master_pb.Seaweed.GetMasterConfiguration:output_type -> master_pb.GetMasterConfigurationResponse - 36, // 56: master_pb.Seaweed.ListMasterClients:output_type -> master_pb.ListMasterClientsResponse - 38, // 57: master_pb.Seaweed.LeaseAdminToken:output_type -> master_pb.LeaseAdminTokenResponse - 40, // 58: master_pb.Seaweed.ReleaseAdminToken:output_type -> master_pb.ReleaseAdminTokenResponse - 45, // [45:59] is the sub-list for method output_type - 31, // [31:45] is the sub-list for method input_type - 31, // [31:31] is the sub-list for extension type_name - 31, // [31:31] is the sub-list for extension extendee - 0, // [0:31] is the sub-list for field type_name + 12, // 11: master_pb.AssignResponse.replicas:type_name -> master_pb.Location + 12, // 12: master_pb.AssignResponse.location:type_name -> master_pb.Location + 17, // 13: master_pb.CollectionListResponse.collections:type_name -> master_pb.Collection + 2, // 14: master_pb.DiskInfo.volume_infos:type_name -> master_pb.VolumeInformationMessage + 4, // 15: master_pb.DiskInfo.ec_shard_infos:type_name -> master_pb.VolumeEcShardInformationMessage + 45, // 16: master_pb.DataNodeInfo.diskInfos:type_name -> master_pb.DataNodeInfo.DiskInfosEntry + 23, // 17: master_pb.RackInfo.data_node_infos:type_name -> master_pb.DataNodeInfo + 46, // 18: master_pb.RackInfo.diskInfos:type_name -> master_pb.RackInfo.DiskInfosEntry + 24, // 19: master_pb.DataCenterInfo.rack_infos:type_name -> master_pb.RackInfo + 47, // 20: master_pb.DataCenterInfo.diskInfos:type_name -> master_pb.DataCenterInfo.DiskInfosEntry + 25, // 21: master_pb.TopologyInfo.data_center_infos:type_name -> master_pb.DataCenterInfo + 48, // 22: master_pb.TopologyInfo.diskInfos:type_name -> master_pb.TopologyInfo.DiskInfosEntry + 26, // 23: master_pb.VolumeListResponse.topology_info:type_name -> master_pb.TopologyInfo + 49, // 24: master_pb.LookupEcVolumeResponse.shard_id_locations:type_name -> master_pb.LookupEcVolumeResponse.EcShardIdLocation + 5, // 25: master_pb.GetMasterConfigurationResponse.storage_backends:type_name -> master_pb.StorageBackend + 12, // 26: master_pb.LookupVolumeResponse.VolumeIdLocation.locations:type_name -> master_pb.Location + 22, // 27: master_pb.DataNodeInfo.DiskInfosEntry.value:type_name -> master_pb.DiskInfo + 22, // 28: master_pb.RackInfo.DiskInfosEntry.value:type_name -> master_pb.DiskInfo + 22, // 29: master_pb.DataCenterInfo.DiskInfosEntry.value:type_name -> master_pb.DiskInfo + 22, // 30: master_pb.TopologyInfo.DiskInfosEntry.value:type_name -> master_pb.DiskInfo + 12, // 31: master_pb.LookupEcVolumeResponse.EcShardIdLocation.locations:type_name -> master_pb.Location + 0, // 32: master_pb.Seaweed.SendHeartbeat:input_type -> master_pb.Heartbeat + 8, // 33: master_pb.Seaweed.KeepConnected:input_type -> master_pb.KeepConnectedRequest + 10, // 34: master_pb.Seaweed.LookupVolume:input_type -> master_pb.LookupVolumeRequest + 13, // 35: master_pb.Seaweed.Assign:input_type -> master_pb.AssignRequest + 15, // 36: master_pb.Seaweed.Statistics:input_type -> master_pb.StatisticsRequest + 18, // 37: master_pb.Seaweed.CollectionList:input_type -> master_pb.CollectionListRequest + 20, // 38: master_pb.Seaweed.CollectionDelete:input_type -> master_pb.CollectionDeleteRequest + 27, // 39: master_pb.Seaweed.VolumeList:input_type -> master_pb.VolumeListRequest + 29, // 40: master_pb.Seaweed.LookupEcVolume:input_type -> master_pb.LookupEcVolumeRequest + 31, // 41: master_pb.Seaweed.VacuumVolume:input_type -> master_pb.VacuumVolumeRequest + 33, // 42: master_pb.Seaweed.GetMasterConfiguration:input_type -> master_pb.GetMasterConfigurationRequest + 35, // 43: master_pb.Seaweed.ListMasterClients:input_type -> master_pb.ListMasterClientsRequest + 37, // 44: master_pb.Seaweed.LeaseAdminToken:input_type -> master_pb.LeaseAdminTokenRequest + 39, // 45: master_pb.Seaweed.ReleaseAdminToken:input_type -> master_pb.ReleaseAdminTokenRequest + 1, // 46: master_pb.Seaweed.SendHeartbeat:output_type -> master_pb.HeartbeatResponse + 9, // 47: master_pb.Seaweed.KeepConnected:output_type -> master_pb.VolumeLocation + 11, // 48: master_pb.Seaweed.LookupVolume:output_type -> master_pb.LookupVolumeResponse + 14, // 49: master_pb.Seaweed.Assign:output_type -> master_pb.AssignResponse + 16, // 50: master_pb.Seaweed.Statistics:output_type -> master_pb.StatisticsResponse + 19, // 51: master_pb.Seaweed.CollectionList:output_type -> master_pb.CollectionListResponse + 21, // 52: master_pb.Seaweed.CollectionDelete:output_type -> master_pb.CollectionDeleteResponse + 28, // 53: master_pb.Seaweed.VolumeList:output_type -> master_pb.VolumeListResponse + 30, // 54: master_pb.Seaweed.LookupEcVolume:output_type -> master_pb.LookupEcVolumeResponse + 32, // 55: master_pb.Seaweed.VacuumVolume:output_type -> master_pb.VacuumVolumeResponse + 34, // 56: master_pb.Seaweed.GetMasterConfiguration:output_type -> master_pb.GetMasterConfigurationResponse + 36, // 57: master_pb.Seaweed.ListMasterClients:output_type -> master_pb.ListMasterClientsResponse + 38, // 58: master_pb.Seaweed.LeaseAdminToken:output_type -> master_pb.LeaseAdminTokenResponse + 40, // 59: master_pb.Seaweed.ReleaseAdminToken:output_type -> master_pb.ReleaseAdminTokenResponse + 46, // [46:60] is the sub-list for method output_type + 32, // [32:46] is the sub-list for method input_type + 32, // [32:32] is the sub-list for extension type_name + 32, // [32:32] is the sub-list for extension extendee + 0, // [0:32] is the sub-list for field type_name } func init() { file_master_proto_init() } @@ -4145,19 +4118,7 @@ func file_master_proto_init() { return nil } } - file_master_proto_msgTypes[45].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*AssignResponse_Replica); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_master_proto_msgTypes[50].Exporter = func(v interface{}, i int) interface{} { + file_master_proto_msgTypes[49].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*LookupEcVolumeResponse_EcShardIdLocation); i { case 0: return &v.state @@ -4176,7 +4137,7 @@ func file_master_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_master_proto_rawDesc, NumEnums: 0, - NumMessages: 51, + NumMessages: 50, NumExtensions: 0, NumServices: 1, }, diff --git a/weed/pb/server_address.go b/weed/pb/server_address.go new file mode 100644 index 000000000..8bafbeb85 --- /dev/null +++ b/weed/pb/server_address.go @@ -0,0 +1,130 @@ +package pb + +import ( + "fmt" + "github.com/chrislusf/seaweedfs/weed/pb/master_pb" + "github.com/chrislusf/seaweedfs/weed/util" + "net" + "strconv" + "strings" +) + +type ServerAddress string +type ServerAddresses string + +func NewServerAddress(host string, port int, grpcPort int) ServerAddress { + if grpcPort == port+10000 { + return ServerAddress(util.JoinHostPort(host, port)) + } + return ServerAddress(util.JoinHostPort(host, port) + "." + strconv.Itoa(grpcPort)) +} + +func NewServerAddressWithGrpcPort(address string, grpcPort int) ServerAddress { + if grpcPort == 0 { + return ServerAddress(address) + } + _, port, _ := hostAndPort(address) + if uint64(grpcPort) == port+10000 { + return ServerAddress(address) + } + return ServerAddress(address + "." + strconv.Itoa(grpcPort)) +} + +func NewServerAddressFromDataNode(dn *master_pb.DataNodeInfo) ServerAddress { + return NewServerAddressWithGrpcPort(dn.Id, int(dn.GrpcPort)) +} + +func NewServerAddressFromLocation(dn *master_pb.Location) ServerAddress { + return NewServerAddressWithGrpcPort(dn.Url, int(dn.GrpcPort)) +} + +func (sa ServerAddress) String() string { + return sa.ToHttpAddress() +} + +func (sa ServerAddress) ToHttpAddress() string { + portsSepIndex := strings.LastIndex(string(sa), ":") + if portsSepIndex < 0 { + return string(sa) + } + if portsSepIndex+1 >= len(sa) { + return string(sa) + } + ports := string(sa[portsSepIndex+1:]) + sepIndex := strings.LastIndex(string(ports), ".") + if sepIndex >= 0 { + host := string(sa[0:portsSepIndex]) + return net.JoinHostPort(host, ports[0:sepIndex]) + } + return string(sa) +} + +func (sa ServerAddress) ToGrpcAddress() string { + portsSepIndex := strings.LastIndex(string(sa), ":") + if portsSepIndex < 0 { + return string(sa) + } + if portsSepIndex+1 >= len(sa) { + return string(sa) + } + ports := string(sa[portsSepIndex+1:]) + sepIndex := strings.LastIndex(ports, ".") + if sepIndex >= 0 { + host := string(sa[0:portsSepIndex]) + return net.JoinHostPort(host, ports[sepIndex+1:]) + } + return ServerToGrpcAddress(string(sa)) +} + +func (sa ServerAddresses) ToAddresses() (addresses []ServerAddress) { + parts := strings.Split(string(sa), ",") + for _, address := range parts { + if address != "" { + addresses = append(addresses, ServerAddress(address)) + } + } + return +} + +func (sa ServerAddresses) ToAddressStrings() (addresses []string) { + parts := strings.Split(string(sa), ",") + for _, address := range parts { + addresses = append(addresses, address) + } + return +} + +func ToAddressStrings(addresses []ServerAddress) []string { + var strings []string + for _, addr := range addresses { + strings = append(strings, string(addr)) + } + return strings +} +func FromAddressStrings(strings []string) []ServerAddress { + var addresses []ServerAddress + for _, addr := range strings { + addresses = append(addresses, ServerAddress(addr)) + } + return addresses +} + +func ParseUrl(input string) (address ServerAddress, path string, err error) { + if !strings.HasPrefix(input, "http://") { + return "", "", fmt.Errorf("url %s needs prefix 'http://'", input) + } + input = input[7:] + pathSeparatorIndex := strings.Index(input, "/") + hostAndPorts := input + if pathSeparatorIndex > 0 { + path = input[pathSeparatorIndex:] + hostAndPorts = input[0:pathSeparatorIndex] + } + commaSeparatorIndex := strings.Index(input, ":") + if commaSeparatorIndex < 0 { + err = fmt.Errorf("port should be specified in %s", input) + return + } + address = ServerAddress(hostAndPorts) + return +} diff --git a/weed/pb/volume_server.proto b/weed/pb/volume_server.proto index 18662b804..6d81626b3 100644 --- a/weed/pb/volume_server.proto +++ b/weed/pb/volume_server.proto @@ -478,6 +478,7 @@ message FetchAndWriteNeedleRequest { message Replica { string url = 1; string public_url = 2; + int32 grpc_port = 3; } repeated Replica replicas = 6; string auth = 7; diff --git a/weed/pb/volume_server_pb/volume_server.pb.go b/weed/pb/volume_server_pb/volume_server.pb.go index a63a5a450..68924b5fc 100644 --- a/weed/pb/volume_server_pb/volume_server.pb.go +++ b/weed/pb/volume_server_pb/volume_server.pb.go @@ -4751,6 +4751,7 @@ type FetchAndWriteNeedleRequest_Replica struct { Url string `protobuf:"bytes,1,opt,name=url,proto3" json:"url,omitempty"` PublicUrl string `protobuf:"bytes,2,opt,name=public_url,json=publicUrl,proto3" json:"public_url,omitempty"` + GrpcPort int32 `protobuf:"varint,3,opt,name=grpc_port,json=grpcPort,proto3" json:"grpc_port,omitempty"` } func (x *FetchAndWriteNeedleRequest_Replica) Reset() { @@ -4799,6 +4800,13 @@ func (x *FetchAndWriteNeedleRequest_Replica) GetPublicUrl() string { return "" } +func (x *FetchAndWriteNeedleRequest_Replica) GetGrpcPort() int32 { + if x != nil { + return x.GrpcPort + } + return 0 +} + type QueryRequest_Filter struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -5758,7 +5766,7 @@ var file_volume_server_proto_rawDesc = []byte{ 0x1a, 0x0a, 0x18, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x4c, 0x65, 0x61, 0x76, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x1b, 0x0a, 0x19, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x4c, 0x65, 0x61, 0x76, 0x65, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0xbf, 0x03, 0x0a, 0x1a, 0x46, 0x65, 0x74, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0xdc, 0x03, 0x0a, 0x1a, 0x46, 0x65, 0x74, 0x63, 0x68, 0x41, 0x6e, 0x64, 0x57, 0x72, 0x69, 0x74, 0x65, 0x4e, 0x65, 0x65, 0x64, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x76, 0x6f, 0x6c, 0x75, @@ -5782,414 +5790,416 @@ var file_volume_server_proto_rawDesc = []byte{ 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x10, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x5f, 0x70, 0x62, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x4c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0e, - 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x4c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x1a, 0x3a, + 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x4c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x1a, 0x57, 0x0a, 0x07, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x6c, 0x12, 0x1d, 0x0a, 0x0a, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x75, 0x72, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x09, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x55, 0x72, 0x6c, 0x22, 0x1d, 0x0a, 0x1b, 0x46, 0x65, - 0x74, 0x63, 0x68, 0x41, 0x6e, 0x64, 0x57, 0x72, 0x69, 0x74, 0x65, 0x4e, 0x65, 0x65, 0x64, 0x6c, - 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0xf8, 0x0c, 0x0a, 0x0c, 0x51, 0x75, - 0x65, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1e, 0x0a, 0x0a, 0x73, 0x65, - 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0a, - 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x22, 0x0a, 0x0d, 0x66, 0x72, - 0x6f, 0x6d, 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, - 0x09, 0x52, 0x0b, 0x66, 0x72, 0x6f, 0x6d, 0x46, 0x69, 0x6c, 0x65, 0x49, 0x64, 0x73, 0x12, 0x3d, - 0x0a, 0x06, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x25, - 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, - 0x62, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x46, - 0x69, 0x6c, 0x74, 0x65, 0x72, 0x52, 0x06, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x12, 0x62, 0x0a, - 0x13, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x5f, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x31, 0x2e, 0x76, 0x6f, 0x6c, - 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x51, 0x75, - 0x65, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x49, 0x6e, 0x70, 0x75, 0x74, - 0x53, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x12, 0x69, - 0x6e, 0x70, 0x75, 0x74, 0x53, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x12, 0x65, 0x0a, 0x14, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x5f, 0x73, 0x65, 0x72, 0x69, - 0x61, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x32, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, - 0x70, 0x62, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, - 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x53, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x52, 0x13, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x53, 0x65, 0x72, 0x69, 0x61, - 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x1a, 0x4e, 0x0a, 0x06, 0x46, 0x69, 0x6c, 0x74, - 0x65, 0x72, 0x12, 0x14, 0x0a, 0x05, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x05, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x6f, 0x70, 0x65, 0x72, - 0x61, 0x6e, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6f, 0x70, 0x65, 0x72, 0x61, - 0x6e, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x1a, 0xd5, 0x05, 0x0a, 0x12, 0x49, 0x6e, 0x70, - 0x75, 0x74, 0x53, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, - 0x29, 0x0a, 0x10, 0x63, 0x6f, 0x6d, 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x74, - 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x63, 0x6f, 0x6d, 0x70, 0x72, - 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x54, 0x79, 0x70, 0x65, 0x12, 0x57, 0x0a, 0x09, 0x63, 0x73, - 0x76, 0x5f, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x3a, 0x2e, - 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, - 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x49, 0x6e, - 0x70, 0x75, 0x74, 0x53, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x2e, 0x43, 0x53, 0x56, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x52, 0x08, 0x63, 0x73, 0x76, 0x49, 0x6e, - 0x70, 0x75, 0x74, 0x12, 0x5a, 0x0a, 0x0a, 0x6a, 0x73, 0x6f, 0x6e, 0x5f, 0x69, 0x6e, 0x70, 0x75, - 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x3b, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, + 0x09, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x55, 0x72, 0x6c, 0x12, 0x1b, 0x0a, 0x09, 0x67, 0x72, + 0x70, 0x63, 0x5f, 0x70, 0x6f, 0x72, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x08, 0x67, + 0x72, 0x70, 0x63, 0x50, 0x6f, 0x72, 0x74, 0x22, 0x1d, 0x0a, 0x1b, 0x46, 0x65, 0x74, 0x63, 0x68, + 0x41, 0x6e, 0x64, 0x57, 0x72, 0x69, 0x74, 0x65, 0x4e, 0x65, 0x65, 0x64, 0x6c, 0x65, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0xf8, 0x0c, 0x0a, 0x0c, 0x51, 0x75, 0x65, 0x72, 0x79, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1e, 0x0a, 0x0a, 0x73, 0x65, 0x6c, 0x65, 0x63, + 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0a, 0x73, 0x65, 0x6c, + 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x22, 0x0a, 0x0d, 0x66, 0x72, 0x6f, 0x6d, 0x5f, + 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0b, + 0x66, 0x72, 0x6f, 0x6d, 0x46, 0x69, 0x6c, 0x65, 0x49, 0x64, 0x73, 0x12, 0x3d, 0x0a, 0x06, 0x66, + 0x69, 0x6c, 0x74, 0x65, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x76, 0x6f, + 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x51, + 0x75, 0x65, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x46, 0x69, 0x6c, 0x74, + 0x65, 0x72, 0x52, 0x06, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x12, 0x62, 0x0a, 0x13, 0x69, 0x6e, + 0x70, 0x75, 0x74, 0x5f, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x31, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x53, 0x65, 0x72, - 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x4a, 0x53, 0x4f, 0x4e, 0x49, - 0x6e, 0x70, 0x75, 0x74, 0x52, 0x09, 0x6a, 0x73, 0x6f, 0x6e, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x12, - 0x63, 0x0a, 0x0d, 0x70, 0x61, 0x72, 0x71, 0x75, 0x65, 0x74, 0x5f, 0x69, 0x6e, 0x70, 0x75, 0x74, - 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x3e, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, - 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x53, 0x65, 0x72, 0x69, - 0x61, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x50, 0x61, 0x72, 0x71, 0x75, 0x65, - 0x74, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x52, 0x0c, 0x70, 0x61, 0x72, 0x71, 0x75, 0x65, 0x74, 0x49, - 0x6e, 0x70, 0x75, 0x74, 0x1a, 0xc8, 0x02, 0x0a, 0x08, 0x43, 0x53, 0x56, 0x49, 0x6e, 0x70, 0x75, - 0x74, 0x12, 0x28, 0x0a, 0x10, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, - 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x66, 0x69, 0x6c, - 0x65, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x29, 0x0a, 0x10, 0x72, - 0x65, 0x63, 0x6f, 0x72, 0x64, 0x5f, 0x64, 0x65, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x65, 0x72, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x44, 0x65, 0x6c, - 0x69, 0x6d, 0x69, 0x74, 0x65, 0x72, 0x12, 0x27, 0x0a, 0x0f, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x5f, - 0x64, 0x65, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x65, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x0e, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x44, 0x65, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x65, 0x72, 0x12, - 0x29, 0x0a, 0x10, 0x71, 0x75, 0x6f, 0x74, 0x65, 0x5f, 0x63, 0x68, 0x61, 0x72, 0x61, 0x63, 0x74, - 0x6f, 0x65, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x71, 0x75, 0x6f, 0x74, 0x65, - 0x43, 0x68, 0x61, 0x72, 0x61, 0x63, 0x74, 0x6f, 0x65, 0x72, 0x12, 0x34, 0x0a, 0x16, 0x71, 0x75, - 0x6f, 0x74, 0x65, 0x5f, 0x65, 0x73, 0x63, 0x61, 0x70, 0x65, 0x5f, 0x63, 0x68, 0x61, 0x72, 0x61, - 0x63, 0x74, 0x65, 0x72, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x14, 0x71, 0x75, 0x6f, 0x74, - 0x65, 0x45, 0x73, 0x63, 0x61, 0x70, 0x65, 0x43, 0x68, 0x61, 0x72, 0x61, 0x63, 0x74, 0x65, 0x72, - 0x12, 0x1a, 0x0a, 0x08, 0x63, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x06, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x08, 0x63, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x41, 0x0a, 0x1d, - 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x5f, 0x71, 0x75, 0x6f, 0x74, 0x65, 0x64, 0x5f, 0x72, 0x65, 0x63, - 0x6f, 0x72, 0x64, 0x5f, 0x64, 0x65, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x65, 0x72, 0x18, 0x07, 0x20, - 0x01, 0x28, 0x08, 0x52, 0x1a, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x51, 0x75, 0x6f, 0x74, 0x65, 0x64, - 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x44, 0x65, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x65, 0x72, 0x1a, - 0x1f, 0x0a, 0x09, 0x4a, 0x53, 0x4f, 0x4e, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x12, 0x12, 0x0a, 0x04, - 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, - 0x1a, 0x0e, 0x0a, 0x0c, 0x50, 0x61, 0x72, 0x71, 0x75, 0x65, 0x74, 0x49, 0x6e, 0x70, 0x75, 0x74, - 0x1a, 0xf1, 0x03, 0x0a, 0x13, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x53, 0x65, 0x72, 0x69, 0x61, - 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x5b, 0x0a, 0x0a, 0x63, 0x73, 0x76, 0x5f, - 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x3c, 0x2e, 0x76, + 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x12, 0x69, 0x6e, 0x70, 0x75, + 0x74, 0x53, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x65, + 0x0a, 0x14, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x5f, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, + 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x32, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x53, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x2e, 0x43, 0x53, 0x56, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x52, 0x09, 0x63, 0x73, 0x76, 0x4f, - 0x75, 0x74, 0x70, 0x75, 0x74, 0x12, 0x5e, 0x0a, 0x0b, 0x6a, 0x73, 0x6f, 0x6e, 0x5f, 0x6f, 0x75, - 0x74, 0x70, 0x75, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x3d, 0x2e, 0x76, 0x6f, 0x6c, + 0x52, 0x13, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x53, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x1a, 0x4e, 0x0a, 0x06, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x12, + 0x14, 0x0a, 0x05, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, + 0x66, 0x69, 0x65, 0x6c, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x6e, 0x64, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x6e, 0x64, 0x12, + 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, + 0x76, 0x61, 0x6c, 0x75, 0x65, 0x1a, 0xd5, 0x05, 0x0a, 0x12, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x53, + 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x29, 0x0a, 0x10, + 0x63, 0x6f, 0x6d, 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x74, 0x79, 0x70, 0x65, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x63, 0x6f, 0x6d, 0x70, 0x72, 0x65, 0x73, 0x73, + 0x69, 0x6f, 0x6e, 0x54, 0x79, 0x70, 0x65, 0x12, 0x57, 0x0a, 0x09, 0x63, 0x73, 0x76, 0x5f, 0x69, + 0x6e, 0x70, 0x75, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x3a, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x51, 0x75, - 0x65, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x4f, 0x75, 0x74, 0x70, 0x75, - 0x74, 0x53, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x4a, - 0x53, 0x4f, 0x4e, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x52, 0x0a, 0x6a, 0x73, 0x6f, 0x6e, 0x4f, - 0x75, 0x74, 0x70, 0x75, 0x74, 0x1a, 0xe3, 0x01, 0x0a, 0x09, 0x43, 0x53, 0x56, 0x4f, 0x75, 0x74, - 0x70, 0x75, 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x71, 0x75, 0x6f, 0x74, 0x65, 0x5f, 0x66, 0x69, 0x65, - 0x6c, 0x64, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x71, 0x75, 0x6f, 0x74, 0x65, - 0x46, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x12, 0x29, 0x0a, 0x10, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, - 0x5f, 0x64, 0x65, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, + 0x65, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x49, 0x6e, 0x70, 0x75, 0x74, + 0x53, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x43, 0x53, + 0x56, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x52, 0x08, 0x63, 0x73, 0x76, 0x49, 0x6e, 0x70, 0x75, 0x74, + 0x12, 0x5a, 0x0a, 0x0a, 0x6a, 0x73, 0x6f, 0x6e, 0x5f, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x3b, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, + 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x2e, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x53, 0x65, 0x72, 0x69, 0x61, 0x6c, + 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x4a, 0x53, 0x4f, 0x4e, 0x49, 0x6e, 0x70, 0x75, + 0x74, 0x52, 0x09, 0x6a, 0x73, 0x6f, 0x6e, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x12, 0x63, 0x0a, 0x0d, + 0x70, 0x61, 0x72, 0x71, 0x75, 0x65, 0x74, 0x5f, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x18, 0x04, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x3e, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, + 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x2e, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x53, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, + 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x50, 0x61, 0x72, 0x71, 0x75, 0x65, 0x74, 0x49, 0x6e, + 0x70, 0x75, 0x74, 0x52, 0x0c, 0x70, 0x61, 0x72, 0x71, 0x75, 0x65, 0x74, 0x49, 0x6e, 0x70, 0x75, + 0x74, 0x1a, 0xc8, 0x02, 0x0a, 0x08, 0x43, 0x53, 0x56, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x12, 0x28, + 0x0a, 0x10, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x5f, 0x69, 0x6e, + 0x66, 0x6f, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x66, 0x69, 0x6c, 0x65, 0x48, 0x65, + 0x61, 0x64, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x29, 0x0a, 0x10, 0x72, 0x65, 0x63, 0x6f, + 0x72, 0x64, 0x5f, 0x64, 0x65, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x0f, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x44, 0x65, 0x6c, 0x69, 0x6d, 0x69, + 0x74, 0x65, 0x72, 0x12, 0x27, 0x0a, 0x0f, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x5f, 0x64, 0x65, 0x6c, + 0x69, 0x6d, 0x69, 0x74, 0x65, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x66, 0x69, + 0x65, 0x6c, 0x64, 0x44, 0x65, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x65, 0x72, 0x12, 0x29, 0x0a, 0x10, + 0x71, 0x75, 0x6f, 0x74, 0x65, 0x5f, 0x63, 0x68, 0x61, 0x72, 0x61, 0x63, 0x74, 0x6f, 0x65, 0x72, + 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x71, 0x75, 0x6f, 0x74, 0x65, 0x43, 0x68, 0x61, + 0x72, 0x61, 0x63, 0x74, 0x6f, 0x65, 0x72, 0x12, 0x34, 0x0a, 0x16, 0x71, 0x75, 0x6f, 0x74, 0x65, + 0x5f, 0x65, 0x73, 0x63, 0x61, 0x70, 0x65, 0x5f, 0x63, 0x68, 0x61, 0x72, 0x61, 0x63, 0x74, 0x65, + 0x72, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x14, 0x71, 0x75, 0x6f, 0x74, 0x65, 0x45, 0x73, + 0x63, 0x61, 0x70, 0x65, 0x43, 0x68, 0x61, 0x72, 0x61, 0x63, 0x74, 0x65, 0x72, 0x12, 0x1a, 0x0a, + 0x08, 0x63, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x08, 0x63, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x41, 0x0a, 0x1d, 0x61, 0x6c, 0x6c, + 0x6f, 0x77, 0x5f, 0x71, 0x75, 0x6f, 0x74, 0x65, 0x64, 0x5f, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, + 0x5f, 0x64, 0x65, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x65, 0x72, 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, + 0x52, 0x1a, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x51, 0x75, 0x6f, 0x74, 0x65, 0x64, 0x52, 0x65, 0x63, + 0x6f, 0x72, 0x64, 0x44, 0x65, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x65, 0x72, 0x1a, 0x1f, 0x0a, 0x09, + 0x4a, 0x53, 0x4f, 0x4e, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, + 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x1a, 0x0e, 0x0a, + 0x0c, 0x50, 0x61, 0x72, 0x71, 0x75, 0x65, 0x74, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x1a, 0xf1, 0x03, + 0x0a, 0x13, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x53, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x5b, 0x0a, 0x0a, 0x63, 0x73, 0x76, 0x5f, 0x6f, 0x75, 0x74, + 0x70, 0x75, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x3c, 0x2e, 0x76, 0x6f, 0x6c, 0x75, + 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x51, 0x75, 0x65, + 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, + 0x53, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x43, 0x53, + 0x56, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x52, 0x09, 0x63, 0x73, 0x76, 0x4f, 0x75, 0x74, 0x70, + 0x75, 0x74, 0x12, 0x5e, 0x0a, 0x0b, 0x6a, 0x73, 0x6f, 0x6e, 0x5f, 0x6f, 0x75, 0x74, 0x70, 0x75, + 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x3d, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, + 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x53, 0x65, + 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x4a, 0x53, 0x4f, 0x4e, + 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x52, 0x0a, 0x6a, 0x73, 0x6f, 0x6e, 0x4f, 0x75, 0x74, 0x70, + 0x75, 0x74, 0x1a, 0xe3, 0x01, 0x0a, 0x09, 0x43, 0x53, 0x56, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, + 0x12, 0x21, 0x0a, 0x0c, 0x71, 0x75, 0x6f, 0x74, 0x65, 0x5f, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x73, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x71, 0x75, 0x6f, 0x74, 0x65, 0x46, 0x69, 0x65, + 0x6c, 0x64, 0x73, 0x12, 0x29, 0x0a, 0x10, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x5f, 0x64, 0x65, + 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x72, + 0x65, 0x63, 0x6f, 0x72, 0x64, 0x44, 0x65, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x65, 0x72, 0x12, 0x27, + 0x0a, 0x0f, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x5f, 0x64, 0x65, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x65, + 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x44, 0x65, + 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x65, 0x72, 0x12, 0x29, 0x0a, 0x10, 0x71, 0x75, 0x6f, 0x74, 0x65, + 0x5f, 0x63, 0x68, 0x61, 0x72, 0x61, 0x63, 0x74, 0x6f, 0x65, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x0f, 0x71, 0x75, 0x6f, 0x74, 0x65, 0x43, 0x68, 0x61, 0x72, 0x61, 0x63, 0x74, 0x6f, + 0x65, 0x72, 0x12, 0x34, 0x0a, 0x16, 0x71, 0x75, 0x6f, 0x74, 0x65, 0x5f, 0x65, 0x73, 0x63, 0x61, + 0x70, 0x65, 0x5f, 0x63, 0x68, 0x61, 0x72, 0x61, 0x63, 0x74, 0x65, 0x72, 0x18, 0x05, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x14, 0x71, 0x75, 0x6f, 0x74, 0x65, 0x45, 0x73, 0x63, 0x61, 0x70, 0x65, 0x43, + 0x68, 0x61, 0x72, 0x61, 0x63, 0x74, 0x65, 0x72, 0x1a, 0x37, 0x0a, 0x0a, 0x4a, 0x53, 0x4f, 0x4e, + 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x12, 0x29, 0x0a, 0x10, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, + 0x5f, 0x64, 0x65, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x44, 0x65, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x65, - 0x72, 0x12, 0x27, 0x0a, 0x0f, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x5f, 0x64, 0x65, 0x6c, 0x69, 0x6d, - 0x69, 0x74, 0x65, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x66, 0x69, 0x65, 0x6c, - 0x64, 0x44, 0x65, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x65, 0x72, 0x12, 0x29, 0x0a, 0x10, 0x71, 0x75, - 0x6f, 0x74, 0x65, 0x5f, 0x63, 0x68, 0x61, 0x72, 0x61, 0x63, 0x74, 0x6f, 0x65, 0x72, 0x18, 0x04, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x71, 0x75, 0x6f, 0x74, 0x65, 0x43, 0x68, 0x61, 0x72, 0x61, - 0x63, 0x74, 0x6f, 0x65, 0x72, 0x12, 0x34, 0x0a, 0x16, 0x71, 0x75, 0x6f, 0x74, 0x65, 0x5f, 0x65, - 0x73, 0x63, 0x61, 0x70, 0x65, 0x5f, 0x63, 0x68, 0x61, 0x72, 0x61, 0x63, 0x74, 0x65, 0x72, 0x18, - 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x14, 0x71, 0x75, 0x6f, 0x74, 0x65, 0x45, 0x73, 0x63, 0x61, - 0x70, 0x65, 0x43, 0x68, 0x61, 0x72, 0x61, 0x63, 0x74, 0x65, 0x72, 0x1a, 0x37, 0x0a, 0x0a, 0x4a, - 0x53, 0x4f, 0x4e, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x12, 0x29, 0x0a, 0x10, 0x72, 0x65, 0x63, - 0x6f, 0x72, 0x64, 0x5f, 0x64, 0x65, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x65, 0x72, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x0f, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x44, 0x65, 0x6c, 0x69, 0x6d, - 0x69, 0x74, 0x65, 0x72, 0x22, 0x29, 0x0a, 0x0d, 0x51, 0x75, 0x65, 0x72, 0x69, 0x65, 0x64, 0x53, - 0x74, 0x72, 0x69, 0x70, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x73, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x73, 0x22, - 0x55, 0x0a, 0x19, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x4e, 0x65, 0x65, 0x64, 0x6c, 0x65, 0x53, - 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, - 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, - 0x08, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x49, 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x6e, 0x65, 0x65, - 0x64, 0x6c, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x08, 0x6e, 0x65, - 0x65, 0x64, 0x6c, 0x65, 0x49, 0x64, 0x22, 0xae, 0x01, 0x0a, 0x1a, 0x56, 0x6f, 0x6c, 0x75, 0x6d, - 0x65, 0x4e, 0x65, 0x65, 0x64, 0x6c, 0x65, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x6e, 0x65, 0x65, 0x64, 0x6c, 0x65, 0x5f, - 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x08, 0x6e, 0x65, 0x65, 0x64, 0x6c, 0x65, - 0x49, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x63, 0x6f, 0x6f, 0x6b, 0x69, 0x65, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x0d, 0x52, 0x06, 0x63, 0x6f, 0x6f, 0x6b, 0x69, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x69, - 0x7a, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x12, 0x23, - 0x0a, 0x0d, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x69, 0x65, 0x64, 0x18, - 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0c, 0x6c, 0x61, 0x73, 0x74, 0x4d, 0x6f, 0x64, 0x69, 0x66, - 0x69, 0x65, 0x64, 0x12, 0x10, 0x0a, 0x03, 0x63, 0x72, 0x63, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, - 0x52, 0x03, 0x63, 0x72, 0x63, 0x12, 0x10, 0x0a, 0x03, 0x74, 0x74, 0x6c, 0x18, 0x06, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x03, 0x74, 0x74, 0x6c, 0x32, 0x9f, 0x22, 0x0a, 0x0c, 0x56, 0x6f, 0x6c, 0x75, - 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x12, 0x5c, 0x0a, 0x0b, 0x42, 0x61, 0x74, 0x63, - 0x68, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x12, 0x24, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, - 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x42, 0x61, 0x74, 0x63, 0x68, - 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x25, 0x2e, - 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, - 0x2e, 0x42, 0x61, 0x74, 0x63, 0x68, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x6e, 0x0a, 0x11, 0x56, 0x61, 0x63, 0x75, 0x75, 0x6d, - 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x12, 0x2a, 0x2e, 0x76, 0x6f, - 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, - 0x61, 0x63, 0x75, 0x75, 0x6d, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x43, 0x68, 0x65, 0x63, 0x6b, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2b, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, - 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x61, 0x63, 0x75, 0x75, - 0x6d, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x74, 0x0a, 0x13, 0x56, 0x61, 0x63, 0x75, 0x75, 0x6d, - 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x43, 0x6f, 0x6d, 0x70, 0x61, 0x63, 0x74, 0x12, 0x2c, 0x2e, - 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, - 0x2e, 0x56, 0x61, 0x63, 0x75, 0x75, 0x6d, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x43, 0x6f, 0x6d, - 0x70, 0x61, 0x63, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2d, 0x2e, 0x76, 0x6f, - 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, - 0x61, 0x63, 0x75, 0x75, 0x6d, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x43, 0x6f, 0x6d, 0x70, 0x61, - 0x63, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x71, 0x0a, 0x12, - 0x56, 0x61, 0x63, 0x75, 0x75, 0x6d, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x43, 0x6f, 0x6d, 0x6d, - 0x69, 0x74, 0x12, 0x2b, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, - 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x61, 0x63, 0x75, 0x75, 0x6d, 0x56, 0x6f, 0x6c, 0x75, - 0x6d, 0x65, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x2c, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, - 0x70, 0x62, 0x2e, 0x56, 0x61, 0x63, 0x75, 0x75, 0x6d, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x43, - 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, - 0x74, 0x0a, 0x13, 0x56, 0x61, 0x63, 0x75, 0x75, 0x6d, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x43, - 0x6c, 0x65, 0x61, 0x6e, 0x75, 0x70, 0x12, 0x2c, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, - 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x61, 0x63, 0x75, 0x75, 0x6d, - 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x43, 0x6c, 0x65, 0x61, 0x6e, 0x75, 0x70, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2d, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, + 0x72, 0x22, 0x29, 0x0a, 0x0d, 0x51, 0x75, 0x65, 0x72, 0x69, 0x65, 0x64, 0x53, 0x74, 0x72, 0x69, + 0x70, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x73, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x0c, 0x52, 0x07, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x73, 0x22, 0x55, 0x0a, 0x19, + 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x4e, 0x65, 0x65, 0x64, 0x6c, 0x65, 0x53, 0x74, 0x61, 0x74, + 0x75, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x76, 0x6f, 0x6c, + 0x75, 0x6d, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x76, 0x6f, + 0x6c, 0x75, 0x6d, 0x65, 0x49, 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x6e, 0x65, 0x65, 0x64, 0x6c, 0x65, + 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x08, 0x6e, 0x65, 0x65, 0x64, 0x6c, + 0x65, 0x49, 0x64, 0x22, 0xae, 0x01, 0x0a, 0x1a, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x4e, 0x65, + 0x65, 0x64, 0x6c, 0x65, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x6e, 0x65, 0x65, 0x64, 0x6c, 0x65, 0x5f, 0x69, 0x64, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x08, 0x6e, 0x65, 0x65, 0x64, 0x6c, 0x65, 0x49, 0x64, 0x12, + 0x16, 0x0a, 0x06, 0x63, 0x6f, 0x6f, 0x6b, 0x69, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, + 0x06, 0x63, 0x6f, 0x6f, 0x6b, 0x69, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x6c, + 0x61, 0x73, 0x74, 0x5f, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x69, 0x65, 0x64, 0x18, 0x04, 0x20, 0x01, + 0x28, 0x04, 0x52, 0x0c, 0x6c, 0x61, 0x73, 0x74, 0x4d, 0x6f, 0x64, 0x69, 0x66, 0x69, 0x65, 0x64, + 0x12, 0x10, 0x0a, 0x03, 0x63, 0x72, 0x63, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x03, 0x63, + 0x72, 0x63, 0x12, 0x10, 0x0a, 0x03, 0x74, 0x74, 0x6c, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x03, 0x74, 0x74, 0x6c, 0x32, 0x9f, 0x22, 0x0a, 0x0c, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x53, + 0x65, 0x72, 0x76, 0x65, 0x72, 0x12, 0x5c, 0x0a, 0x0b, 0x42, 0x61, 0x74, 0x63, 0x68, 0x44, 0x65, + 0x6c, 0x65, 0x74, 0x65, 0x12, 0x24, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, + 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x42, 0x61, 0x74, 0x63, 0x68, 0x44, 0x65, 0x6c, + 0x65, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x25, 0x2e, 0x76, 0x6f, 0x6c, + 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x42, 0x61, + 0x74, 0x63, 0x68, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x22, 0x00, 0x12, 0x6e, 0x0a, 0x11, 0x56, 0x61, 0x63, 0x75, 0x75, 0x6d, 0x56, 0x6f, 0x6c, + 0x75, 0x6d, 0x65, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x12, 0x2a, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, + 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x61, 0x63, 0x75, + 0x75, 0x6d, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2b, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x61, 0x63, 0x75, 0x75, 0x6d, 0x56, 0x6f, - 0x6c, 0x75, 0x6d, 0x65, 0x43, 0x6c, 0x65, 0x61, 0x6e, 0x75, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x6b, 0x0a, 0x10, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x43, - 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x29, 0x2e, 0x76, 0x6f, 0x6c, 0x75, - 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x44, 0x65, 0x6c, - 0x65, 0x74, 0x65, 0x43, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2a, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, - 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x43, 0x6f, - 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x22, 0x00, 0x12, 0x65, 0x0a, 0x0e, 0x41, 0x6c, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x65, 0x56, 0x6f, - 0x6c, 0x75, 0x6d, 0x65, 0x12, 0x27, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, - 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x41, 0x6c, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x65, - 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x28, 0x2e, - 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, - 0x2e, 0x41, 0x6c, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x65, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x6b, 0x0a, 0x10, 0x56, 0x6f, 0x6c, - 0x75, 0x6d, 0x65, 0x53, 0x79, 0x6e, 0x63, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x29, 0x2e, - 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, - 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x53, 0x79, 0x6e, 0x63, 0x53, 0x74, 0x61, 0x74, 0x75, - 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2a, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, - 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, - 0x6d, 0x65, 0x53, 0x79, 0x6e, 0x63, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x7c, 0x0a, 0x15, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, - 0x49, 0x6e, 0x63, 0x72, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x61, 0x6c, 0x43, 0x6f, 0x70, 0x79, 0x12, - 0x2e, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, - 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x49, 0x6e, 0x63, 0x72, 0x65, 0x6d, 0x65, - 0x6e, 0x74, 0x61, 0x6c, 0x43, 0x6f, 0x70, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x2f, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, - 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x49, 0x6e, 0x63, 0x72, 0x65, 0x6d, 0x65, - 0x6e, 0x74, 0x61, 0x6c, 0x43, 0x6f, 0x70, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x22, 0x00, 0x30, 0x01, 0x12, 0x5c, 0x0a, 0x0b, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x4d, 0x6f, - 0x75, 0x6e, 0x74, 0x12, 0x24, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, - 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x4d, 0x6f, 0x75, - 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x25, 0x2e, 0x76, 0x6f, 0x6c, 0x75, - 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, - 0x75, 0x6d, 0x65, 0x4d, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x22, 0x00, 0x12, 0x62, 0x0a, 0x0d, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x55, 0x6e, 0x6d, 0x6f, - 0x75, 0x6e, 0x74, 0x12, 0x26, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, - 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x55, 0x6e, 0x6d, - 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x27, 0x2e, 0x76, 0x6f, - 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, - 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x55, 0x6e, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x5f, 0x0a, 0x0c, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, - 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x12, 0x25, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, + 0x6c, 0x75, 0x6d, 0x65, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x22, 0x00, 0x12, 0x74, 0x0a, 0x13, 0x56, 0x61, 0x63, 0x75, 0x75, 0x6d, 0x56, 0x6f, 0x6c, + 0x75, 0x6d, 0x65, 0x43, 0x6f, 0x6d, 0x70, 0x61, 0x63, 0x74, 0x12, 0x2c, 0x2e, 0x76, 0x6f, 0x6c, + 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x61, + 0x63, 0x75, 0x75, 0x6d, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x43, 0x6f, 0x6d, 0x70, 0x61, 0x63, + 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2d, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, + 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x61, 0x63, 0x75, + 0x75, 0x6d, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x43, 0x6f, 0x6d, 0x70, 0x61, 0x63, 0x74, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x71, 0x0a, 0x12, 0x56, 0x61, 0x63, + 0x75, 0x75, 0x6d, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x12, + 0x2b, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, + 0x70, 0x62, 0x2e, 0x56, 0x61, 0x63, 0x75, 0x75, 0x6d, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x43, + 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2c, 0x2e, 0x76, + 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, + 0x56, 0x61, 0x63, 0x75, 0x75, 0x6d, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x43, 0x6f, 0x6d, 0x6d, + 0x69, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x74, 0x0a, 0x13, + 0x56, 0x61, 0x63, 0x75, 0x75, 0x6d, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x43, 0x6c, 0x65, 0x61, + 0x6e, 0x75, 0x70, 0x12, 0x2c, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, + 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x61, 0x63, 0x75, 0x75, 0x6d, 0x56, 0x6f, 0x6c, + 0x75, 0x6d, 0x65, 0x43, 0x6c, 0x65, 0x61, 0x6e, 0x75, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x2d, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, + 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x61, 0x63, 0x75, 0x75, 0x6d, 0x56, 0x6f, 0x6c, 0x75, 0x6d, + 0x65, 0x43, 0x6c, 0x65, 0x61, 0x6e, 0x75, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x22, 0x00, 0x12, 0x6b, 0x0a, 0x10, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x43, 0x6f, 0x6c, 0x6c, + 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x29, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, + 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, + 0x43, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x2a, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, + 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x43, 0x6f, 0x6c, 0x6c, 0x65, + 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, + 0x65, 0x0a, 0x0e, 0x41, 0x6c, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x65, 0x56, 0x6f, 0x6c, 0x75, 0x6d, + 0x65, 0x12, 0x27, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, + 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x41, 0x6c, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x65, 0x56, 0x6f, 0x6c, + 0x75, 0x6d, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x28, 0x2e, 0x76, 0x6f, 0x6c, + 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x41, 0x6c, + 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x65, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x6b, 0x0a, 0x10, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, + 0x53, 0x79, 0x6e, 0x63, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x29, 0x2e, 0x76, 0x6f, 0x6c, + 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, + 0x6c, 0x75, 0x6d, 0x65, 0x53, 0x79, 0x6e, 0x63, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2a, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, + 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x53, + 0x79, 0x6e, 0x63, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x22, 0x00, 0x12, 0x7c, 0x0a, 0x15, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x49, 0x6e, 0x63, + 0x72, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x61, 0x6c, 0x43, 0x6f, 0x70, 0x79, 0x12, 0x2e, 0x2e, 0x76, + 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, + 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x49, 0x6e, 0x63, 0x72, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x61, + 0x6c, 0x43, 0x6f, 0x70, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2f, 0x2e, 0x76, + 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, + 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x49, 0x6e, 0x63, 0x72, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x61, + 0x6c, 0x43, 0x6f, 0x70, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x30, + 0x01, 0x12, 0x5c, 0x0a, 0x0b, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x4d, 0x6f, 0x75, 0x6e, 0x74, + 0x12, 0x24, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, + 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x4d, 0x6f, 0x75, 0x6e, 0x74, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x25, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, - 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x26, 0x2e, - 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, - 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x71, 0x0a, 0x12, 0x56, 0x6f, 0x6c, 0x75, 0x6d, - 0x65, 0x4d, 0x61, 0x72, 0x6b, 0x52, 0x65, 0x61, 0x64, 0x6f, 0x6e, 0x6c, 0x79, 0x12, 0x2b, 0x2e, - 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, - 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x4d, 0x61, 0x72, 0x6b, 0x52, 0x65, 0x61, 0x64, 0x6f, - 0x6e, 0x6c, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2c, 0x2e, 0x76, 0x6f, 0x6c, + 0x4d, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, + 0x62, 0x0a, 0x0d, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x55, 0x6e, 0x6d, 0x6f, 0x75, 0x6e, 0x74, + 0x12, 0x26, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, + 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x55, 0x6e, 0x6d, 0x6f, 0x75, 0x6e, + 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x27, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, + 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, + 0x6d, 0x65, 0x55, 0x6e, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x22, 0x00, 0x12, 0x5f, 0x0a, 0x0c, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x44, 0x65, 0x6c, + 0x65, 0x74, 0x65, 0x12, 0x25, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, + 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x44, 0x65, 0x6c, + 0x65, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x26, 0x2e, 0x76, 0x6f, 0x6c, + 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, + 0x6c, 0x75, 0x6d, 0x65, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x22, 0x00, 0x12, 0x71, 0x0a, 0x12, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x4d, 0x61, + 0x72, 0x6b, 0x52, 0x65, 0x61, 0x64, 0x6f, 0x6e, 0x6c, 0x79, 0x12, 0x2b, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x4d, 0x61, 0x72, 0x6b, 0x52, 0x65, 0x61, 0x64, 0x6f, 0x6e, 0x6c, 0x79, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x71, 0x0a, 0x12, 0x56, 0x6f, - 0x6c, 0x75, 0x6d, 0x65, 0x4d, 0x61, 0x72, 0x6b, 0x57, 0x72, 0x69, 0x74, 0x61, 0x62, 0x6c, 0x65, - 0x12, 0x2b, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, - 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x4d, 0x61, 0x72, 0x6b, 0x57, 0x72, - 0x69, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2c, 0x2e, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2c, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, + 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, + 0x65, 0x4d, 0x61, 0x72, 0x6b, 0x52, 0x65, 0x61, 0x64, 0x6f, 0x6e, 0x6c, 0x79, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x71, 0x0a, 0x12, 0x56, 0x6f, 0x6c, 0x75, 0x6d, + 0x65, 0x4d, 0x61, 0x72, 0x6b, 0x57, 0x72, 0x69, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x12, 0x2b, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x4d, 0x61, 0x72, 0x6b, 0x57, 0x72, 0x69, 0x74, 0x61, - 0x62, 0x6c, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x68, 0x0a, - 0x0f, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x65, - 0x12, 0x28, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, - 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, - 0x75, 0x72, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x29, 0x2e, 0x76, 0x6f, 0x6c, + 0x62, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2c, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, - 0x6c, 0x75, 0x6d, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x65, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x5f, 0x0a, 0x0c, 0x56, 0x6f, 0x6c, 0x75, 0x6d, - 0x65, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x25, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, - 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, - 0x65, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x26, - 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, - 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x59, 0x0a, 0x0a, 0x56, 0x6f, 0x6c, 0x75, - 0x6d, 0x65, 0x43, 0x6f, 0x70, 0x79, 0x12, 0x23, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, - 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, - 0x43, 0x6f, 0x70, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, 0x2e, 0x76, 0x6f, - 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, - 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x43, 0x6f, 0x70, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x22, 0x00, 0x12, 0x77, 0x0a, 0x14, 0x52, 0x65, 0x61, 0x64, 0x56, 0x6f, 0x6c, 0x75, 0x6d, - 0x65, 0x46, 0x69, 0x6c, 0x65, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x2d, 0x2e, 0x76, 0x6f, - 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x52, - 0x65, 0x61, 0x64, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x46, 0x69, 0x6c, 0x65, 0x53, 0x74, 0x61, - 0x74, 0x75, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2e, 0x2e, 0x76, 0x6f, 0x6c, - 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x52, 0x65, - 0x61, 0x64, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x46, 0x69, 0x6c, 0x65, 0x53, 0x74, 0x61, 0x74, - 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x55, 0x0a, 0x08, - 0x43, 0x6f, 0x70, 0x79, 0x46, 0x69, 0x6c, 0x65, 0x12, 0x21, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, - 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x43, 0x6f, 0x70, 0x79, - 0x46, 0x69, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x76, 0x6f, - 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x43, - 0x6f, 0x70, 0x79, 0x46, 0x69, 0x6c, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, - 0x00, 0x30, 0x01, 0x12, 0x65, 0x0a, 0x0e, 0x52, 0x65, 0x61, 0x64, 0x4e, 0x65, 0x65, 0x64, 0x6c, - 0x65, 0x42, 0x6c, 0x6f, 0x62, 0x12, 0x27, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, - 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x52, 0x65, 0x61, 0x64, 0x4e, 0x65, 0x65, - 0x64, 0x6c, 0x65, 0x42, 0x6c, 0x6f, 0x62, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x28, - 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, - 0x62, 0x2e, 0x52, 0x65, 0x61, 0x64, 0x4e, 0x65, 0x65, 0x64, 0x6c, 0x65, 0x42, 0x6c, 0x6f, 0x62, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x68, 0x0a, 0x0f, 0x57, 0x72, - 0x69, 0x74, 0x65, 0x4e, 0x65, 0x65, 0x64, 0x6c, 0x65, 0x42, 0x6c, 0x6f, 0x62, 0x12, 0x28, 0x2e, + 0x6c, 0x75, 0x6d, 0x65, 0x4d, 0x61, 0x72, 0x6b, 0x57, 0x72, 0x69, 0x74, 0x61, 0x62, 0x6c, 0x65, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x68, 0x0a, 0x0f, 0x56, 0x6f, + 0x6c, 0x75, 0x6d, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x65, 0x12, 0x28, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, - 0x2e, 0x57, 0x72, 0x69, 0x74, 0x65, 0x4e, 0x65, 0x65, 0x64, 0x6c, 0x65, 0x42, 0x6c, 0x6f, 0x62, + 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x29, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, - 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x57, 0x72, 0x69, 0x74, 0x65, - 0x4e, 0x65, 0x65, 0x64, 0x6c, 0x65, 0x42, 0x6c, 0x6f, 0x62, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x22, 0x00, 0x12, 0x6d, 0x0a, 0x10, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x54, 0x61, - 0x69, 0x6c, 0x53, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x12, 0x29, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, - 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, - 0x6d, 0x65, 0x54, 0x61, 0x69, 0x6c, 0x53, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x2a, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, - 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x54, 0x61, 0x69, - 0x6c, 0x53, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, - 0x00, 0x30, 0x01, 0x12, 0x71, 0x0a, 0x12, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x54, 0x61, 0x69, - 0x6c, 0x52, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, 0x12, 0x2b, 0x2e, 0x76, 0x6f, 0x6c, 0x75, - 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, - 0x75, 0x6d, 0x65, 0x54, 0x61, 0x69, 0x6c, 0x52, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2c, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, - 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, - 0x54, 0x61, 0x69, 0x6c, 0x52, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x7d, 0x0a, 0x16, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, - 0x45, 0x63, 0x53, 0x68, 0x61, 0x72, 0x64, 0x73, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, - 0x12, 0x2f, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, - 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x45, 0x63, 0x53, 0x68, 0x61, 0x72, - 0x64, 0x73, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x30, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, - 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x45, 0x63, 0x53, 0x68, 0x61, - 0x72, 0x64, 0x73, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x7a, 0x0a, 0x15, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x45, - 0x63, 0x53, 0x68, 0x61, 0x72, 0x64, 0x73, 0x52, 0x65, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x12, 0x2e, - 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, - 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x45, 0x63, 0x53, 0x68, 0x61, 0x72, 0x64, 0x73, - 0x52, 0x65, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2f, - 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, - 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x45, 0x63, 0x53, 0x68, 0x61, 0x72, 0x64, 0x73, - 0x52, 0x65, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, - 0x00, 0x12, 0x71, 0x0a, 0x12, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x45, 0x63, 0x53, 0x68, 0x61, - 0x72, 0x64, 0x73, 0x43, 0x6f, 0x70, 0x79, 0x12, 0x2b, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, - 0x65, 0x45, 0x63, 0x53, 0x68, 0x61, 0x72, 0x64, 0x73, 0x43, 0x6f, 0x70, 0x79, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2c, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, - 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x45, 0x63, - 0x53, 0x68, 0x61, 0x72, 0x64, 0x73, 0x43, 0x6f, 0x70, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x22, 0x00, 0x12, 0x77, 0x0a, 0x14, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x45, 0x63, - 0x53, 0x68, 0x61, 0x72, 0x64, 0x73, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x12, 0x2d, 0x2e, 0x76, - 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, - 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x45, 0x63, 0x53, 0x68, 0x61, 0x72, 0x64, 0x73, 0x44, 0x65, - 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2e, 0x2e, 0x76, 0x6f, + 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x22, 0x00, 0x12, 0x5f, 0x0a, 0x0c, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x53, 0x74, + 0x61, 0x74, 0x75, 0x73, 0x12, 0x25, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, + 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x53, 0x74, + 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x26, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, - 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x45, 0x63, 0x53, 0x68, 0x61, 0x72, 0x64, 0x73, 0x44, 0x65, 0x6c, - 0x65, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x74, 0x0a, - 0x13, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x45, 0x63, 0x53, 0x68, 0x61, 0x72, 0x64, 0x73, 0x4d, - 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x2c, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, - 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x45, 0x63, - 0x53, 0x68, 0x61, 0x72, 0x64, 0x73, 0x4d, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x2d, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, - 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x45, 0x63, 0x53, 0x68, - 0x61, 0x72, 0x64, 0x73, 0x4d, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x22, 0x00, 0x12, 0x7a, 0x0a, 0x15, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x45, 0x63, 0x53, - 0x68, 0x61, 0x72, 0x64, 0x73, 0x55, 0x6e, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x2e, 0x2e, 0x76, - 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, - 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x45, 0x63, 0x53, 0x68, 0x61, 0x72, 0x64, 0x73, 0x55, 0x6e, - 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2f, 0x2e, 0x76, - 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, - 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x45, 0x63, 0x53, 0x68, 0x61, 0x72, 0x64, 0x73, 0x55, 0x6e, - 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, - 0x70, 0x0a, 0x11, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x45, 0x63, 0x53, 0x68, 0x61, 0x72, 0x64, - 0x52, 0x65, 0x61, 0x64, 0x12, 0x2a, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, - 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x45, 0x63, - 0x53, 0x68, 0x61, 0x72, 0x64, 0x52, 0x65, 0x61, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x2b, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, - 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x45, 0x63, 0x53, 0x68, 0x61, 0x72, - 0x64, 0x52, 0x65, 0x61, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x30, - 0x01, 0x12, 0x71, 0x0a, 0x12, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x45, 0x63, 0x42, 0x6c, 0x6f, - 0x62, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x12, 0x2b, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, - 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, - 0x65, 0x45, 0x63, 0x42, 0x6c, 0x6f, 0x62, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2c, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, - 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x45, 0x63, - 0x42, 0x6c, 0x6f, 0x62, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x22, 0x00, 0x12, 0x7d, 0x0a, 0x16, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x45, 0x63, - 0x53, 0x68, 0x61, 0x72, 0x64, 0x73, 0x54, 0x6f, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x12, 0x2f, + 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x59, 0x0a, 0x0a, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x43, + 0x6f, 0x70, 0x79, 0x12, 0x23, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, + 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x43, 0x6f, 0x70, + 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, + 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, + 0x6d, 0x65, 0x43, 0x6f, 0x70, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, + 0x12, 0x77, 0x0a, 0x14, 0x52, 0x65, 0x61, 0x64, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x46, 0x69, + 0x6c, 0x65, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x2d, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, + 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x52, 0x65, 0x61, 0x64, + 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x46, 0x69, 0x6c, 0x65, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2e, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, + 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x52, 0x65, 0x61, 0x64, 0x56, + 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x46, 0x69, 0x6c, 0x65, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x55, 0x0a, 0x08, 0x43, 0x6f, 0x70, + 0x79, 0x46, 0x69, 0x6c, 0x65, 0x12, 0x21, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, + 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x43, 0x6f, 0x70, 0x79, 0x46, 0x69, 0x6c, + 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, + 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x43, 0x6f, 0x70, 0x79, + 0x46, 0x69, 0x6c, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x30, 0x01, + 0x12, 0x65, 0x0a, 0x0e, 0x52, 0x65, 0x61, 0x64, 0x4e, 0x65, 0x65, 0x64, 0x6c, 0x65, 0x42, 0x6c, + 0x6f, 0x62, 0x12, 0x27, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, + 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x52, 0x65, 0x61, 0x64, 0x4e, 0x65, 0x65, 0x64, 0x6c, 0x65, + 0x42, 0x6c, 0x6f, 0x62, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x28, 0x2e, 0x76, 0x6f, + 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x52, + 0x65, 0x61, 0x64, 0x4e, 0x65, 0x65, 0x64, 0x6c, 0x65, 0x42, 0x6c, 0x6f, 0x62, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x68, 0x0a, 0x0f, 0x57, 0x72, 0x69, 0x74, 0x65, + 0x4e, 0x65, 0x65, 0x64, 0x6c, 0x65, 0x42, 0x6c, 0x6f, 0x62, 0x12, 0x28, 0x2e, 0x76, 0x6f, 0x6c, + 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x57, 0x72, + 0x69, 0x74, 0x65, 0x4e, 0x65, 0x65, 0x64, 0x6c, 0x65, 0x42, 0x6c, 0x6f, 0x62, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x29, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, + 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x57, 0x72, 0x69, 0x74, 0x65, 0x4e, 0x65, 0x65, + 0x64, 0x6c, 0x65, 0x42, 0x6c, 0x6f, 0x62, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, + 0x00, 0x12, 0x6d, 0x0a, 0x10, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x54, 0x61, 0x69, 0x6c, 0x53, + 0x65, 0x6e, 0x64, 0x65, 0x72, 0x12, 0x29, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, + 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x54, + 0x61, 0x69, 0x6c, 0x53, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x2a, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, + 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x54, 0x61, 0x69, 0x6c, 0x53, 0x65, + 0x6e, 0x64, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x30, 0x01, + 0x12, 0x71, 0x0a, 0x12, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x54, 0x61, 0x69, 0x6c, 0x52, 0x65, + 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, 0x12, 0x2b, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, + 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, + 0x54, 0x61, 0x69, 0x6c, 0x52, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x2c, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, + 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x54, 0x61, 0x69, + 0x6c, 0x52, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x22, 0x00, 0x12, 0x7d, 0x0a, 0x16, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x45, 0x63, 0x53, + 0x68, 0x61, 0x72, 0x64, 0x73, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x12, 0x2f, 0x2e, + 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, + 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x45, 0x63, 0x53, 0x68, 0x61, 0x72, 0x64, 0x73, 0x47, + 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x30, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x45, 0x63, 0x53, 0x68, 0x61, 0x72, 0x64, 0x73, - 0x54, 0x6f, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x30, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, + 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x22, 0x00, 0x12, 0x7a, 0x0a, 0x15, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x45, 0x63, 0x53, 0x68, + 0x61, 0x72, 0x64, 0x73, 0x52, 0x65, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x12, 0x2e, 0x2e, 0x76, 0x6f, + 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, + 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x45, 0x63, 0x53, 0x68, 0x61, 0x72, 0x64, 0x73, 0x52, 0x65, 0x62, + 0x75, 0x69, 0x6c, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2f, 0x2e, 0x76, 0x6f, + 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, + 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x45, 0x63, 0x53, 0x68, 0x61, 0x72, 0x64, 0x73, 0x52, 0x65, 0x62, + 0x75, 0x69, 0x6c, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x71, + 0x0a, 0x12, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x45, 0x63, 0x53, 0x68, 0x61, 0x72, 0x64, 0x73, + 0x43, 0x6f, 0x70, 0x79, 0x12, 0x2b, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, + 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x45, 0x63, + 0x53, 0x68, 0x61, 0x72, 0x64, 0x73, 0x43, 0x6f, 0x70, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x2c, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, + 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x45, 0x63, 0x53, 0x68, 0x61, + 0x72, 0x64, 0x73, 0x43, 0x6f, 0x70, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, + 0x00, 0x12, 0x77, 0x0a, 0x14, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x45, 0x63, 0x53, 0x68, 0x61, + 0x72, 0x64, 0x73, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x12, 0x2d, 0x2e, 0x76, 0x6f, 0x6c, 0x75, + 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, + 0x75, 0x6d, 0x65, 0x45, 0x63, 0x53, 0x68, 0x61, 0x72, 0x64, 0x73, 0x44, 0x65, 0x6c, 0x65, 0x74, + 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2e, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, + 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, + 0x6d, 0x65, 0x45, 0x63, 0x53, 0x68, 0x61, 0x72, 0x64, 0x73, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x74, 0x0a, 0x13, 0x56, 0x6f, + 0x6c, 0x75, 0x6d, 0x65, 0x45, 0x63, 0x53, 0x68, 0x61, 0x72, 0x64, 0x73, 0x4d, 0x6f, 0x75, 0x6e, + 0x74, 0x12, 0x2c, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, + 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x45, 0x63, 0x53, 0x68, 0x61, + 0x72, 0x64, 0x73, 0x4d, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x2d, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x45, 0x63, 0x53, 0x68, 0x61, 0x72, 0x64, - 0x73, 0x54, 0x6f, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x22, 0x00, 0x12, 0x88, 0x01, 0x0a, 0x19, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x54, 0x69, - 0x65, 0x72, 0x4d, 0x6f, 0x76, 0x65, 0x44, 0x61, 0x74, 0x54, 0x6f, 0x52, 0x65, 0x6d, 0x6f, 0x74, - 0x65, 0x12, 0x32, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, - 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x54, 0x69, 0x65, 0x72, 0x4d, - 0x6f, 0x76, 0x65, 0x44, 0x61, 0x74, 0x54, 0x6f, 0x52, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x33, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, - 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x54, - 0x69, 0x65, 0x72, 0x4d, 0x6f, 0x76, 0x65, 0x44, 0x61, 0x74, 0x54, 0x6f, 0x52, 0x65, 0x6d, 0x6f, - 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x30, 0x01, 0x12, 0x8e, - 0x01, 0x0a, 0x1b, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x54, 0x69, 0x65, 0x72, 0x4d, 0x6f, 0x76, - 0x65, 0x44, 0x61, 0x74, 0x46, 0x72, 0x6f, 0x6d, 0x52, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x12, 0x34, + 0x73, 0x4d, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, + 0x12, 0x7a, 0x0a, 0x15, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x45, 0x63, 0x53, 0x68, 0x61, 0x72, + 0x64, 0x73, 0x55, 0x6e, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x2e, 0x2e, 0x76, 0x6f, 0x6c, 0x75, + 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, + 0x75, 0x6d, 0x65, 0x45, 0x63, 0x53, 0x68, 0x61, 0x72, 0x64, 0x73, 0x55, 0x6e, 0x6d, 0x6f, 0x75, + 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2f, 0x2e, 0x76, 0x6f, 0x6c, 0x75, + 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, + 0x75, 0x6d, 0x65, 0x45, 0x63, 0x53, 0x68, 0x61, 0x72, 0x64, 0x73, 0x55, 0x6e, 0x6d, 0x6f, 0x75, + 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x70, 0x0a, 0x11, + 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x45, 0x63, 0x53, 0x68, 0x61, 0x72, 0x64, 0x52, 0x65, 0x61, + 0x64, 0x12, 0x2a, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, + 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x45, 0x63, 0x53, 0x68, 0x61, + 0x72, 0x64, 0x52, 0x65, 0x61, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2b, 0x2e, + 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, + 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x45, 0x63, 0x53, 0x68, 0x61, 0x72, 0x64, 0x52, 0x65, + 0x61, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x30, 0x01, 0x12, 0x71, + 0x0a, 0x12, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x45, 0x63, 0x42, 0x6c, 0x6f, 0x62, 0x44, 0x65, + 0x6c, 0x65, 0x74, 0x65, 0x12, 0x2b, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, + 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x45, 0x63, + 0x42, 0x6c, 0x6f, 0x62, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x2c, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, + 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x45, 0x63, 0x42, 0x6c, 0x6f, + 0x62, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, + 0x00, 0x12, 0x7d, 0x0a, 0x16, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x45, 0x63, 0x53, 0x68, 0x61, + 0x72, 0x64, 0x73, 0x54, 0x6f, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x12, 0x2f, 0x2e, 0x76, 0x6f, + 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, + 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x45, 0x63, 0x53, 0x68, 0x61, 0x72, 0x64, 0x73, 0x54, 0x6f, 0x56, + 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x30, 0x2e, 0x76, + 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, + 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x45, 0x63, 0x53, 0x68, 0x61, 0x72, 0x64, 0x73, 0x54, 0x6f, + 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, + 0x12, 0x88, 0x01, 0x0a, 0x19, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x54, 0x69, 0x65, 0x72, 0x4d, + 0x6f, 0x76, 0x65, 0x44, 0x61, 0x74, 0x54, 0x6f, 0x52, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x12, 0x32, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x54, 0x69, 0x65, 0x72, 0x4d, 0x6f, 0x76, 0x65, - 0x44, 0x61, 0x74, 0x46, 0x72, 0x6f, 0x6d, 0x52, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x35, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, - 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x54, 0x69, - 0x65, 0x72, 0x4d, 0x6f, 0x76, 0x65, 0x44, 0x61, 0x74, 0x46, 0x72, 0x6f, 0x6d, 0x52, 0x65, 0x6d, - 0x6f, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x30, 0x01, 0x12, - 0x71, 0x0a, 0x12, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x53, - 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x2b, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, - 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x53, - 0x65, 0x72, 0x76, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x2c, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, + 0x44, 0x61, 0x74, 0x54, 0x6f, 0x52, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x33, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, + 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x54, 0x69, 0x65, 0x72, + 0x4d, 0x6f, 0x76, 0x65, 0x44, 0x61, 0x74, 0x54, 0x6f, 0x52, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x30, 0x01, 0x12, 0x8e, 0x01, 0x0a, 0x1b, + 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x54, 0x69, 0x65, 0x72, 0x4d, 0x6f, 0x76, 0x65, 0x44, 0x61, + 0x74, 0x46, 0x72, 0x6f, 0x6d, 0x52, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x12, 0x34, 0x2e, 0x76, 0x6f, + 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, + 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x54, 0x69, 0x65, 0x72, 0x4d, 0x6f, 0x76, 0x65, 0x44, 0x61, 0x74, + 0x46, 0x72, 0x6f, 0x6d, 0x52, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x35, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, + 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x54, 0x69, 0x65, 0x72, 0x4d, + 0x6f, 0x76, 0x65, 0x44, 0x61, 0x74, 0x46, 0x72, 0x6f, 0x6d, 0x52, 0x65, 0x6d, 0x6f, 0x74, 0x65, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x30, 0x01, 0x12, 0x71, 0x0a, 0x12, + 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, + 0x75, 0x73, 0x12, 0x2b, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, - 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x22, 0x00, 0x12, 0x6e, 0x0a, 0x11, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, - 0x65, 0x72, 0x4c, 0x65, 0x61, 0x76, 0x65, 0x12, 0x2a, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, - 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, - 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x4c, 0x65, 0x61, 0x76, 0x65, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x2b, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, - 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x53, 0x65, 0x72, - 0x76, 0x65, 0x72, 0x4c, 0x65, 0x61, 0x76, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x22, 0x00, 0x12, 0x74, 0x0a, 0x13, 0x46, 0x65, 0x74, 0x63, 0x68, 0x41, 0x6e, 0x64, 0x57, 0x72, - 0x69, 0x74, 0x65, 0x4e, 0x65, 0x65, 0x64, 0x6c, 0x65, 0x12, 0x2c, 0x2e, 0x76, 0x6f, 0x6c, 0x75, - 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x46, 0x65, 0x74, - 0x63, 0x68, 0x41, 0x6e, 0x64, 0x57, 0x72, 0x69, 0x74, 0x65, 0x4e, 0x65, 0x65, 0x64, 0x6c, 0x65, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2d, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, - 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x46, 0x65, 0x74, 0x63, 0x68, - 0x41, 0x6e, 0x64, 0x57, 0x72, 0x69, 0x74, 0x65, 0x4e, 0x65, 0x65, 0x64, 0x6c, 0x65, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x4c, 0x0a, 0x05, 0x51, 0x75, 0x65, 0x72, - 0x79, 0x12, 0x1e, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, - 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x1f, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, - 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x69, 0x65, 0x64, 0x53, 0x74, 0x72, 0x69, - 0x70, 0x65, 0x22, 0x00, 0x30, 0x01, 0x12, 0x71, 0x0a, 0x12, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, - 0x4e, 0x65, 0x65, 0x64, 0x6c, 0x65, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x2b, 0x2e, 0x76, - 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, - 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x4e, 0x65, 0x65, 0x64, 0x6c, 0x65, 0x53, 0x74, 0x61, 0x74, - 0x75, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2c, 0x2e, 0x76, 0x6f, 0x6c, 0x75, + 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x2c, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, + 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x53, + 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, + 0x6e, 0x0a, 0x11, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x4c, + 0x65, 0x61, 0x76, 0x65, 0x12, 0x2a, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, + 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x53, 0x65, + 0x72, 0x76, 0x65, 0x72, 0x4c, 0x65, 0x61, 0x76, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x2b, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, + 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, + 0x4c, 0x65, 0x61, 0x76, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, + 0x74, 0x0a, 0x13, 0x46, 0x65, 0x74, 0x63, 0x68, 0x41, 0x6e, 0x64, 0x57, 0x72, 0x69, 0x74, 0x65, + 0x4e, 0x65, 0x65, 0x64, 0x6c, 0x65, 0x12, 0x2c, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, + 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x46, 0x65, 0x74, 0x63, 0x68, 0x41, + 0x6e, 0x64, 0x57, 0x72, 0x69, 0x74, 0x65, 0x4e, 0x65, 0x65, 0x64, 0x6c, 0x65, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2d, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, + 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x46, 0x65, 0x74, 0x63, 0x68, 0x41, 0x6e, 0x64, + 0x57, 0x72, 0x69, 0x74, 0x65, 0x4e, 0x65, 0x65, 0x64, 0x6c, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x4c, 0x0a, 0x05, 0x51, 0x75, 0x65, 0x72, 0x79, 0x12, 0x1e, + 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, + 0x62, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, + 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, + 0x62, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x69, 0x65, 0x64, 0x53, 0x74, 0x72, 0x69, 0x70, 0x65, 0x22, + 0x00, 0x30, 0x01, 0x12, 0x71, 0x0a, 0x12, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x4e, 0x65, 0x65, + 0x64, 0x6c, 0x65, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x2b, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x4e, 0x65, 0x65, 0x64, 0x6c, 0x65, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, 0x39, 0x5a, 0x37, 0x67, 0x69, 0x74, - 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x68, 0x72, 0x69, 0x73, 0x6c, 0x75, 0x73, - 0x66, 0x2f, 0x73, 0x65, 0x61, 0x77, 0x65, 0x65, 0x64, 0x66, 0x73, 0x2f, 0x77, 0x65, 0x65, 0x64, - 0x2f, 0x70, 0x62, 0x2f, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, - 0x72, 0x5f, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2c, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, + 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, + 0x4e, 0x65, 0x65, 0x64, 0x6c, 0x65, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, 0x39, 0x5a, 0x37, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, + 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x68, 0x72, 0x69, 0x73, 0x6c, 0x75, 0x73, 0x66, 0x2f, 0x73, + 0x65, 0x61, 0x77, 0x65, 0x65, 0x64, 0x66, 0x73, 0x2f, 0x77, 0x65, 0x65, 0x64, 0x2f, 0x70, 0x62, + 0x2f, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, + 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/weed/remote_storage/track_sync_offset.go b/weed/remote_storage/track_sync_offset.go index 2dfb6d784..439491d12 100644 --- a/weed/remote_storage/track_sync_offset.go +++ b/weed/remote_storage/track_sync_offset.go @@ -13,7 +13,7 @@ const ( SyncKeyPrefix = "remote.sync." ) -func GetSyncOffset(grpcDialOption grpc.DialOption, filer string, dir string) (lastOffsetTsNs int64, readErr error) { +func GetSyncOffset(grpcDialOption grpc.DialOption, filer pb.ServerAddress, dir string) (lastOffsetTsNs int64, readErr error) { dirHash := uint32(util.HashStringToLong(dir)) @@ -42,7 +42,7 @@ func GetSyncOffset(grpcDialOption grpc.DialOption, filer string, dir string) (la } -func SetSyncOffset(grpcDialOption grpc.DialOption, filer string, dir string, offsetTsNs int64) error { +func SetSyncOffset(grpcDialOption grpc.DialOption, filer pb.ServerAddress, dir string, offsetTsNs int64) error { dirHash := uint32(util.HashStringToLong(dir)) diff --git a/weed/replication/replicator.go b/weed/replication/replicator.go index d7e609c68..3e100497f 100644 --- a/weed/replication/replicator.go +++ b/weed/replication/replicator.go @@ -82,7 +82,7 @@ func (r *Replicator) Replicate(ctx context.Context, key string, message *filer_p return r.sink.CreateEntry(key, message.NewEntry, message.Signatures) } -func ReadFilerSignature(grpcDialOption grpc.DialOption, filer string) (filerSignature int32, readErr error) { +func ReadFilerSignature(grpcDialOption grpc.DialOption, filer pb.ServerAddress) (filerSignature int32, readErr error) { if readErr = pb.WithFilerClient(filer, grpcDialOption, func(client filer_pb.SeaweedFilerClient) error { if resp, err := client.GetFilerConfiguration(context.Background(), &filer_pb.GetFilerConfigurationRequest{}); err != nil { return fmt.Errorf("GetFilerConfiguration %s: %v", filer, err) diff --git a/weed/replication/sink/filersink/fetch_write.go b/weed/replication/sink/filersink/fetch_write.go index e253eb96c..4c536b71c 100644 --- a/weed/replication/sink/filersink/fetch_write.go +++ b/weed/replication/sink/filersink/fetch_write.go @@ -91,7 +91,7 @@ func (fs *FilerSink) fetchAndWrite(sourceChunk *filer_pb.FileChunk, path string) return fmt.Errorf("assign volume failure %v: %v", request, resp.Error) } - fileId, host, auth = resp.FileId, resp.Url, security.EncodedJwt(resp.Auth) + fileId, host, auth = resp.FileId, resp.Location.Url, security.EncodedJwt(resp.Auth) return nil }) diff --git a/weed/s3api/filer_multipart.go b/weed/s3api/filer_multipart.go index 2b6707f2e..9a485ec66 100644 --- a/weed/s3api/filer_multipart.go +++ b/weed/s3api/filer_multipart.go @@ -130,7 +130,7 @@ func (s3a *S3ApiServer) completeMultipartUpload(input *s3.CompleteMultipartUploa output = &CompleteMultipartUploadResult{ CompleteMultipartUploadOutput: s3.CompleteMultipartUploadOutput{ - Location: aws.String(fmt.Sprintf("http://%s%s/%s", s3a.option.Filer, dirName, entryName)), + Location: aws.String(fmt.Sprintf("http://%s%s/%s", s3a.option.Filer.ToHttpAddress(), dirName, entryName)), Bucket: input.Bucket, ETag: aws.String("\"" + filer.ETagChunks(finalParts) + "\""), Key: objectKey(input.Key), diff --git a/weed/s3api/s3api_handlers.go b/weed/s3api/s3api_handlers.go index b4d2c22e7..2149ad9cd 100644 --- a/weed/s3api/s3api_handlers.go +++ b/weed/s3api/s3api_handlers.go @@ -18,7 +18,7 @@ func (s3a *S3ApiServer) WithFilerClient(fn func(filer_pb.SeaweedFilerClient) err return pb.WithCachedGrpcClient(func(grpcConnection *grpc.ClientConn) error { client := filer_pb.NewSeaweedFilerClient(grpcConnection) return fn(client) - }, s3a.option.FilerGrpcAddress, s3a.option.GrpcDialOption) + }, s3a.option.Filer.ToGrpcAddress(), s3a.option.GrpcDialOption) } func (s3a *S3ApiServer) AdjustedUrl(location *filer_pb.Location) string { diff --git a/weed/s3api/s3api_object_copy_handlers.go b/weed/s3api/s3api_object_copy_handlers.go index 3cda1da53..ba49e4a8e 100644 --- a/weed/s3api/s3api_object_copy_handlers.go +++ b/weed/s3api/s3api_object_copy_handlers.go @@ -67,9 +67,9 @@ func (s3a *S3ApiServer) CopyObjectHandler(w http.ResponseWriter, r *http.Request } dstUrl := fmt.Sprintf("http://%s%s/%s%s?collection=%s", - s3a.option.Filer, s3a.option.BucketsPath, dstBucket, dstObject, dstBucket) + s3a.option.Filer.ToHttpAddress(), s3a.option.BucketsPath, dstBucket, dstObject, dstBucket) srcUrl := fmt.Sprintf("http://%s%s/%s%s", - s3a.option.Filer, s3a.option.BucketsPath, srcBucket, srcObject) + s3a.option.Filer.ToHttpAddress(), s3a.option.BucketsPath, srcBucket, srcObject) _, _, resp, err := util.DownloadFile(srcUrl, "") if err != nil { @@ -148,9 +148,9 @@ func (s3a *S3ApiServer) CopyObjectPartHandler(w http.ResponseWriter, r *http.Req rangeHeader := r.Header.Get("x-amz-copy-source-range") dstUrl := fmt.Sprintf("http://%s%s/%s/%04d.part?collection=%s", - s3a.option.Filer, s3a.genUploadsFolder(dstBucket), uploadID, partID, dstBucket) + s3a.option.Filer.ToHttpAddress(), s3a.genUploadsFolder(dstBucket), uploadID, partID, dstBucket) srcUrl := fmt.Sprintf("http://%s%s/%s%s", - s3a.option.Filer, s3a.option.BucketsPath, srcBucket, srcObject) + s3a.option.Filer.ToHttpAddress(), s3a.option.BucketsPath, srcBucket, srcObject) dataReader, err := util.ReadUrlAsReaderCloser(srcUrl, rangeHeader) if err != nil { diff --git a/weed/s3api/s3api_object_handlers.go b/weed/s3api/s3api_object_handlers.go index 845c9a577..afc5c7ca9 100644 --- a/weed/s3api/s3api_object_handlers.go +++ b/weed/s3api/s3api_object_handlers.go @@ -92,7 +92,7 @@ func (s3a *S3ApiServer) PutObjectHandler(w http.ResponseWriter, r *http.Request) return } } else { - uploadUrl := fmt.Sprintf("http://%s%s/%s%s", s3a.option.Filer, s3a.option.BucketsPath, bucket, urlPathEscape(object)) + uploadUrl := fmt.Sprintf("http://%s%s/%s%s", s3a.option.Filer.ToHttpAddress(), s3a.option.BucketsPath, bucket, urlPathEscape(object)) etag, errCode := s3a.putToFiler(r, uploadUrl, dataReader) @@ -125,7 +125,7 @@ func (s3a *S3ApiServer) GetObjectHandler(w http.ResponseWriter, r *http.Request) } destUrl := fmt.Sprintf("http://%s%s/%s%s", - s3a.option.Filer, s3a.option.BucketsPath, bucket, urlPathEscape(object)) + s3a.option.Filer.ToHttpAddress(), s3a.option.BucketsPath, bucket, urlPathEscape(object)) s3a.proxyToFiler(w, r, destUrl, passThroughResponse) @@ -136,7 +136,7 @@ func (s3a *S3ApiServer) HeadObjectHandler(w http.ResponseWriter, r *http.Request bucket, object := getBucketAndObject(r) destUrl := fmt.Sprintf("http://%s%s/%s%s", - s3a.option.Filer, s3a.option.BucketsPath, bucket, urlPathEscape(object)) + s3a.option.Filer.ToHttpAddress(), s3a.option.BucketsPath, bucket, urlPathEscape(object)) s3a.proxyToFiler(w, r, destUrl, passThroughResponse) @@ -147,7 +147,7 @@ func (s3a *S3ApiServer) DeleteObjectHandler(w http.ResponseWriter, r *http.Reque bucket, object := getBucketAndObject(r) destUrl := fmt.Sprintf("http://%s%s/%s%s?recursive=true", - s3a.option.Filer, s3a.option.BucketsPath, bucket, urlPathEscape(object)) + s3a.option.Filer.ToHttpAddress(), s3a.option.BucketsPath, bucket, urlPathEscape(object)) s3a.proxyToFiler(w, r, destUrl, func(proxyResponse *http.Response, w http.ResponseWriter) { for k, v := range proxyResponse.Header { @@ -301,7 +301,6 @@ func (s3a *S3ApiServer) proxyToFiler(w http.ResponseWriter, r *http.Request, des return } - proxyReq.Header.Set("Host", s3a.option.Filer) proxyReq.Header.Set("X-Forwarded-For", r.RemoteAddr) for header, values := range r.Header { @@ -372,7 +371,6 @@ func (s3a *S3ApiServer) putToFiler(r *http.Request, uploadUrl string, dataReader return "", s3err.ErrInternalError } - proxyReq.Header.Set("Host", s3a.option.Filer) proxyReq.Header.Set("X-Forwarded-For", r.RemoteAddr) for header, values := range r.Header { diff --git a/weed/s3api/s3api_object_handlers_postpolicy.go b/weed/s3api/s3api_object_handlers_postpolicy.go index e1125689f..0e4658ef4 100644 --- a/weed/s3api/s3api_object_handlers_postpolicy.go +++ b/weed/s3api/s3api_object_handlers_postpolicy.go @@ -110,7 +110,7 @@ func (s3a *S3ApiServer) PostPolicyBucketHandler(w http.ResponseWriter, r *http.R } } - uploadUrl := fmt.Sprintf("http://%s%s/%s%s", s3a.option.Filer, s3a.option.BucketsPath, bucket, urlPathEscape(object)) + uploadUrl := fmt.Sprintf("http://%s%s/%s%s", s3a.option.Filer.ToHttpAddress(), s3a.option.BucketsPath, bucket, urlPathEscape(object)) etag, errCode := s3a.putToFiler(r, uploadUrl, fileBody) diff --git a/weed/s3api/s3api_object_multipart_handlers.go b/weed/s3api/s3api_object_multipart_handlers.go index 308d9a5f5..c9ad222b1 100644 --- a/weed/s3api/s3api_object_multipart_handlers.go +++ b/weed/s3api/s3api_object_multipart_handlers.go @@ -211,7 +211,7 @@ func (s3a *S3ApiServer) PutObjectPartHandler(w http.ResponseWriter, r *http.Requ defer dataReader.Close() uploadUrl := fmt.Sprintf("http://%s%s/%s/%04d.part?collection=%s", - s3a.option.Filer, s3a.genUploadsFolder(bucket), uploadID, partID, bucket) + s3a.option.Filer.ToHttpAddress(), s3a.genUploadsFolder(bucket), uploadID, partID, bucket) etag, errCode := s3a.putToFiler(r, uploadUrl, dataReader) diff --git a/weed/s3api/s3api_server.go b/weed/s3api/s3api_server.go index 27259c4a7..d216b0af2 100644 --- a/weed/s3api/s3api_server.go +++ b/weed/s3api/s3api_server.go @@ -2,6 +2,7 @@ package s3api import ( "fmt" + "github.com/chrislusf/seaweedfs/weed/pb" "net/http" "strings" "time" @@ -14,9 +15,8 @@ import ( ) type S3ApiServerOption struct { - Filer string + Filer pb.ServerAddress Port int - FilerGrpcAddress string Config string DomainName string BucketsPath string diff --git a/weed/server/filer_grpc_server.go b/weed/server/filer_grpc_server.go index 6a7df0f87..1df15d69f 100644 --- a/weed/server/filer_grpc_server.go +++ b/weed/server/filer_grpc_server.go @@ -3,6 +3,7 @@ package weed_server import ( "context" "fmt" + "github.com/chrislusf/seaweedfs/weed/pb" "os" "path/filepath" "strconv" @@ -107,6 +108,7 @@ func (fs *FilerServer) LookupVolume(ctx context.Context, req *filer_pb.LookupVol locs = append(locs, &filer_pb.Location{ Url: loc.Url, PublicUrl: loc.PublicUrl, + GrpcPort: uint32(loc.GrpcPort), }) } resp.LocationsMap[vidString] = &filer_pb.Locations{ @@ -306,10 +308,13 @@ func (fs *FilerServer) AssignVolume(ctx context.Context, req *filer_pb.AssignVol } return &filer_pb.AssignVolumeResponse{ - FileId: assignResult.Fid, - Count: int32(assignResult.Count), - Url: assignResult.Url, - PublicUrl: assignResult.PublicUrl, + FileId: assignResult.Fid, + Count: int32(assignResult.Count), + Location: &filer_pb.Location{ + Url: assignResult.Url, + PublicUrl: assignResult.PublicUrl, + GrpcPort: uint32(assignResult.GrpcPort), + }, Auth: string(assignResult.Auth), Collection: so.Collection, Replication: so.Replication, @@ -387,7 +392,7 @@ func (fs *FilerServer) GetFilerConfiguration(ctx context.Context, req *filer_pb. clusterId, _ := fs.filer.Store.KvGet(context.Background(), []byte("clusterId")) t := &filer_pb.GetFilerConfigurationResponse{ - Masters: fs.option.Masters, + Masters: pb.ToAddressStrings(fs.option.Masters), Collection: fs.option.Collection, Replication: fs.option.DefaultReplication, MaxMb: uint32(fs.option.MaxMB), diff --git a/weed/server/filer_grpc_server_remote.go b/weed/server/filer_grpc_server_remote.go index c47356a8e..9f986e6aa 100644 --- a/weed/server/filer_grpc_server_remote.go +++ b/weed/server/filer_grpc_server_remote.go @@ -5,6 +5,7 @@ import ( "fmt" "github.com/chrislusf/seaweedfs/weed/filer" "github.com/chrislusf/seaweedfs/weed/operation" + "github.com/chrislusf/seaweedfs/weed/pb" "github.com/chrislusf/seaweedfs/weed/pb/filer_pb" "github.com/chrislusf/seaweedfs/weed/pb/remote_pb" "github.com/chrislusf/seaweedfs/weed/pb/volume_server_pb" @@ -115,11 +116,13 @@ func (fs *FilerServer) DownloadToLocal(ctx context.Context, req *filer_pb.Downlo replicas = append(replicas, &volume_server_pb.FetchAndWriteNeedleRequest_Replica{ Url: r.Url, PublicUrl: r.PublicUrl, + GrpcPort: int32(r.GrpcPort), }) } // tell filer to tell volume server to download into needles - err = operation.WithVolumeServerClient(assignResult.Url, fs.grpcDialOption, func(volumeServerClient volume_server_pb.VolumeServerClient) error { + assignedServerAddress := pb.NewServerAddressWithGrpcPort(assignResult.Url, assignResult.GrpcPort) + err = operation.WithVolumeServerClient(assignedServerAddress, fs.grpcDialOption, func(volumeServerClient volume_server_pb.VolumeServerClient) error { _, fetchAndWriteErr := volumeServerClient.FetchAndWriteNeedle(context.Background(), &volume_server_pb.FetchAndWriteNeedleRequest{ VolumeId: uint32(fileId.VolumeId), NeedleId: uint64(fileId.Key), diff --git a/weed/server/filer_server.go b/weed/server/filer_server.go index 7e5e98660..b886bf641 100644 --- a/weed/server/filer_server.go +++ b/weed/server/filer_server.go @@ -46,7 +46,7 @@ import ( ) type FilerOption struct { - Masters []string + Masters []pb.ServerAddress Collection string DefaultReplication string DisableDirListing bool @@ -56,12 +56,11 @@ type FilerOption struct { Rack string DefaultLevelDbDir string DisableHttp bool - Host string - Port uint32 + Host pb.ServerAddress recursiveDelete bool Cipher bool SaveToFilerLimit int64 - Filers []string + Filers []pb.ServerAddress ConcurrentUploadLimit int64 } @@ -100,14 +99,14 @@ func NewFilerServer(defaultMux, readonlyMux *http.ServeMux, option *FilerOption) glog.Fatal("master list is required!") } - fs.filer = filer.NewFiler(option.Masters, fs.grpcDialOption, option.Host, option.Port, option.Collection, option.DefaultReplication, option.DataCenter, func() { + fs.filer = filer.NewFiler(option.Masters, fs.grpcDialOption, option.Host, option.Collection, option.DefaultReplication, option.DataCenter, func() { fs.listenersCond.Broadcast() }) fs.filer.Cipher = option.Cipher fs.checkWithMaster() - go stats.LoopPushingMetric("filer", stats.SourceName(fs.option.Port), fs.metricsAddress, fs.metricsIntervalSec) + go stats.LoopPushingMetric("filer", string(fs.option.Host), fs.metricsAddress, fs.metricsIntervalSec) go fs.filer.KeepConnectedToMaster() v := util.GetViper() @@ -143,7 +142,7 @@ func NewFilerServer(defaultMux, readonlyMux *http.ServeMux, option *FilerOption) readonlyMux.HandleFunc("/", fs.readonlyFilerHandler) } - fs.filer.AggregateFromPeers(util.JoinHostPort(option.Host, int(option.Port)), option.Filers) + fs.filer.AggregateFromPeers(option.Host, option.Filers) fs.filer.LoadBuckets() @@ -160,13 +159,6 @@ func NewFilerServer(defaultMux, readonlyMux *http.ServeMux, option *FilerOption) func (fs *FilerServer) checkWithMaster() { - for _, master := range fs.option.Masters { - _, err := pb.ParseServerToGrpcAddress(master) - if err != nil { - glog.Fatalf("invalid master address %s: %v", master, err) - } - } - isConnected := false for !isConnected { for _, master := range fs.option.Masters { diff --git a/weed/server/master_grpc_server.go b/weed/server/master_grpc_server.go index 94e050259..194520f49 100644 --- a/weed/server/master_grpc_server.go +++ b/weed/server/master_grpc_server.go @@ -2,6 +2,7 @@ package weed_server import ( "context" + "github.com/chrislusf/seaweedfs/weed/pb" "github.com/chrislusf/seaweedfs/weed/storage/backend" "github.com/chrislusf/seaweedfs/weed/util" "net" @@ -70,7 +71,7 @@ func (ms *MasterServer) SendHeartbeat(stream master_pb.Seaweed_SendHeartbeatServ dcName, rackName := ms.Topo.Configuration.Locate(heartbeat.Ip, heartbeat.DataCenter, heartbeat.Rack) dc := ms.Topo.GetOrCreateDataCenter(dcName) rack := dc.GetOrCreateRack(rackName) - dn = rack.GetOrCreateDataNode(heartbeat.Ip, int(heartbeat.Port), heartbeat.PublicUrl, heartbeat.MaxVolumeCounts) + dn = rack.GetOrCreateDataNode(heartbeat.Ip, int(heartbeat.Port), int(heartbeat.GrpcPort), heartbeat.PublicUrl, heartbeat.MaxVolumeCounts) glog.V(0).Infof("added volume server %d: %v:%d", dn.Counter, heartbeat.GetIp(), heartbeat.GetPort()) if err := stream.Send(&master_pb.HeartbeatResponse{ VolumeSizeLimit: uint64(ms.option.VolumeSizeLimitMB) * 1024 * 1024, @@ -168,7 +169,7 @@ func (ms *MasterServer) SendHeartbeat(stream master_pb.Seaweed_SendHeartbeatServ return err } if err := stream.Send(&master_pb.HeartbeatResponse{ - Leader: newLeader, + Leader: string(newLeader), }); err != nil { glog.Warningf("SendHeartbeat.Send response to to %s:%d %v", dn.Ip, dn.Port, err) return err @@ -189,7 +190,7 @@ func (ms *MasterServer) KeepConnected(stream master_pb.Seaweed_KeepConnectedServ return ms.informNewLeader(stream) } - peerAddress := findClientAddress(stream.Context(), req.GrpcPort) + peerAddress := pb.ServerAddress(req.ClientAddress) // buffer by 1 so we don't end up getting stuck writing to stopChan forever stopChan := make(chan bool, 1) @@ -241,15 +242,15 @@ func (ms *MasterServer) informNewLeader(stream master_pb.Seaweed_KeepConnectedSe return raft.NotLeaderError } if err := stream.Send(&master_pb.VolumeLocation{ - Leader: leader, + Leader: string(leader), }); err != nil { return err } return nil } -func (ms *MasterServer) addClient(clientType string, clientAddress string) (clientName string, messageChan chan *master_pb.VolumeLocation) { - clientName = clientType + "@" + clientAddress +func (ms *MasterServer) addClient(clientType string, clientAddress pb.ServerAddress) (clientName string, messageChan chan *master_pb.VolumeLocation) { + clientName = clientType + "@" + string(clientAddress) glog.V(0).Infof("+ client %v", clientName) // we buffer this because otherwise we end up in a potential deadlock where @@ -319,7 +320,7 @@ func (ms *MasterServer) GetMasterConfiguration(ctx context.Context, req *master_ DefaultReplication: ms.option.DefaultReplicaPlacement, VolumeSizeLimitMB: uint32(ms.option.VolumeSizeLimitMB), VolumePreallocate: ms.option.VolumePreallocate, - Leader: leader, + Leader: string(leader), } return resp, nil diff --git a/weed/server/master_grpc_server_collection.go b/weed/server/master_grpc_server_collection.go index b92d6bcbe..55f3faf8c 100644 --- a/weed/server/master_grpc_server_collection.go +++ b/weed/server/master_grpc_server_collection.go @@ -58,7 +58,7 @@ func (ms *MasterServer) doDeleteNormalCollection(collectionName string) error { } for _, server := range collection.ListVolumeServers() { - err := operation.WithVolumeServerClient(server.Url(), ms.grpcDialOption, func(client volume_server_pb.VolumeServerClient) error { + err := operation.WithVolumeServerClient(server.ServerAddress(), ms.grpcDialOption, func(client volume_server_pb.VolumeServerClient) error { _, deleteErr := client.DeleteCollection(context.Background(), &volume_server_pb.DeleteCollectionRequest{ Collection: collectionName, }) diff --git a/weed/server/master_grpc_server_volume.go b/weed/server/master_grpc_server_volume.go index 3a92889d2..49ac455fe 100644 --- a/weed/server/master_grpc_server_volume.go +++ b/weed/server/master_grpc_server_volume.go @@ -150,17 +150,21 @@ func (ms *MasterServer) Assign(ctx context.Context, req *master_pb.AssignRequest fid, count, dnList, err := ms.Topo.PickForWrite(req.Count, option) if err == nil { dn := dnList.Head() - var replicas []*master_pb.AssignResponse_Replica + var replicas []*master_pb.Location for _, r := range dnList.Rest() { - replicas = append(replicas, &master_pb.AssignResponse_Replica{ + replicas = append(replicas, &master_pb.Location{ Url: r.Url(), PublicUrl: r.PublicUrl, + GrpcPort: uint32(r.GrpcPort), }) } return &master_pb.AssignResponse{ Fid: fid, - Url: dn.Url(), - PublicUrl: dn.PublicUrl, + Location: &master_pb.Location{ + Url: dn.Url(), + PublicUrl: dn.PublicUrl, + GrpcPort: uint32(dn.GrpcPort), + }, Count: count, Auth: string(security.GenJwt(ms.guard.SigningKey, ms.guard.ExpiresAfterSec, fid)), Replicas: replicas, diff --git a/weed/server/master_server.go b/weed/server/master_server.go index 7c78be379..8de01abf7 100644 --- a/weed/server/master_server.go +++ b/weed/server/master_server.go @@ -2,6 +2,7 @@ package weed_server import ( "fmt" + "github.com/chrislusf/seaweedfs/weed/pb" "net/http" "net/http/httputil" "net/url" @@ -32,8 +33,7 @@ const ( ) type MasterOption struct { - Host string - Port int + Master pb.ServerAddress MetaFolder string VolumeSizeLimitMB uint32 VolumePreallocate bool @@ -70,7 +70,7 @@ type MasterServer struct { adminLocks *AdminLocks } -func NewMasterServer(r *mux.Router, option *MasterOption, peers []string) *MasterServer { +func NewMasterServer(r *mux.Router, option *MasterOption, peers []pb.ServerAddress) *MasterServer { v := util.GetViper() signingKey := v.GetString("jwt.signing.key") @@ -102,7 +102,7 @@ func NewMasterServer(r *mux.Router, option *MasterOption, peers []string) *Maste vgCh: make(chan *topology.VolumeGrowRequest, 1<<6), clientChans: make(map[string]chan *master_pb.VolumeLocation), grpcDialOption: grpcDialOption, - MasterClient: wdclient.NewMasterClient(grpcDialOption, "master", option.Host, 0, "", peers), + MasterClient: wdclient.NewMasterClient(grpcDialOption, "master", option.Master, "", peers), adminLocks: NewAdminLocks(), } ms.boundedLeaderChan = make(chan int, 16) @@ -224,14 +224,13 @@ func (ms *MasterServer) startAdminScripts() { scriptLines = append(scriptLines, "unlock") } - masterAddress := util.JoinHostPort(ms.option.Host, ms.option.Port) + masterAddress := string(ms.option.Master) var shellOptions shell.ShellOptions shellOptions.GrpcDialOption = security.LoadClientTLS(v, "grpc.master") shellOptions.Masters = &masterAddress - shellOptions.FilerHost, shellOptions.FilerPort, err = util.ParseHostPort(filerHostPort) - shellOptions.FilerAddress = filerHostPort + shellOptions.FilerAddress = pb.ServerAddress(filerHostPort) shellOptions.Directory = "/" if err != nil { glog.V(0).Infof("failed to parse master.filer.default = %s : %v\n", filerHostPort, err) @@ -299,7 +298,7 @@ func (ms *MasterServer) createSequencer(option *MasterOption) sequence.Sequencer case "snowflake": var err error snowflakeId := v.GetInt(SequencerSnowflakeId) - seq, err = sequence.NewSnowflakeSequencer(util.JoinHostPort(option.Host, option.Port), snowflakeId) + seq, err = sequence.NewSnowflakeSequencer(string(option.Master), snowflakeId) if err != nil { glog.Error(err) seq = nil diff --git a/weed/server/master_server_handlers_admin.go b/weed/server/master_server_handlers_admin.go index 4a86348d9..549ea86dc 100644 --- a/weed/server/master_server_handlers_admin.go +++ b/weed/server/master_server_handlers_admin.go @@ -3,6 +3,7 @@ package weed_server import ( "context" "fmt" + "github.com/chrislusf/seaweedfs/weed/pb" "math/rand" "net/http" "strconv" @@ -26,7 +27,7 @@ func (ms *MasterServer) collectionDeleteHandler(w http.ResponseWriter, r *http.R return } for _, server := range collection.ListVolumeServers() { - err := operation.WithVolumeServerClient(server.Url(), ms.grpcDialOption, func(client volume_server_pb.VolumeServerClient) error { + err := operation.WithVolumeServerClient(server.ServerAddress(), ms.grpcDialOption, func(client volume_server_pb.VolumeServerClient) error { _, deleteErr := client.DeleteCollection(context.Background(), &volume_server_pb.DeleteCollectionRequest{ Collection: collection.Name, }) @@ -118,21 +119,15 @@ func (ms *MasterServer) redirectHandler(w http.ResponseWriter, r *http.Request) } } -func (ms *MasterServer) selfUrl(r *http.Request) string { - if r.Host != "" { - return r.Host - } - return "localhost:" + strconv.Itoa(ms.option.Port) -} func (ms *MasterServer) submitFromMasterServerHandler(w http.ResponseWriter, r *http.Request) { if ms.Topo.IsLeader() { - submitForClientHandler(w, r, func() string { return ms.selfUrl(r) }, ms.grpcDialOption) + submitForClientHandler(w, r, func() pb.ServerAddress { return ms.option.Master }, ms.grpcDialOption) } else { masterUrl, err := ms.Topo.Leader() if err != nil { writeJsonError(w, r, http.StatusInternalServerError, err) } else { - submitForClientHandler(w, r, func() string { return masterUrl }, ms.grpcDialOption) + submitForClientHandler(w, r, func() pb.ServerAddress { return masterUrl }, ms.grpcDialOption) } } } diff --git a/weed/server/raft_server.go b/weed/server/raft_server.go index 85841e409..568bfc7b5 100644 --- a/weed/server/raft_server.go +++ b/weed/server/raft_server.go @@ -6,6 +6,7 @@ import ( "os" "path" "sort" + "strings" "time" "google.golang.org/grpc" @@ -19,10 +20,10 @@ import ( ) type RaftServer struct { - peers []string // initial peers to join with + peers []pb.ServerAddress // initial peers to join with raftServer raft.Server dataDir string - serverAddr string + serverAddr pb.ServerAddress topo *topology.Topology *raft.GrpcServer } @@ -51,7 +52,7 @@ func (s StateMachine) Recovery(data []byte) error { return nil } -func NewRaftServer(grpcDialOption grpc.DialOption, peers []string, serverAddr, dataDir string, topo *topology.Topology, raftResumeState bool) (*RaftServer, error) { +func NewRaftServer(grpcDialOption grpc.DialOption, peers []pb.ServerAddress, serverAddr pb.ServerAddress, dataDir string, topo *topology.Topology, raftResumeState bool) (*RaftServer, error) { s := &RaftServer{ peers: peers, serverAddr: serverAddr, @@ -80,7 +81,7 @@ func NewRaftServer(grpcDialOption grpc.DialOption, peers []string, serverAddr, d } stateMachine := StateMachine{topo: topo} - s.raftServer, err = raft.NewServer(s.serverAddr, s.dataDir, transporter, stateMachine, topo, "") + s.raftServer, err = raft.NewServer(string(s.serverAddr), s.dataDir, transporter, stateMachine, topo, "") if err != nil { glog.V(0).Infoln(err) return nil, err @@ -95,16 +96,17 @@ func NewRaftServer(grpcDialOption grpc.DialOption, peers []string, serverAddr, d } for _, peer := range s.peers { - if err := s.raftServer.AddPeer(peer, pb.ServerToGrpcAddress(peer)); err != nil { + if err := s.raftServer.AddPeer(string(peer), peer.ToGrpcAddress()); err != nil { return nil, err } } // Remove deleted peers for existsPeerName := range s.raftServer.Peers() { - exists, existingPeer := false, "" + exists := false + var existingPeer pb.ServerAddress for _, peer := range s.peers { - if pb.ServerToGrpcAddress(peer) == existsPeerName { + if peer.ToGrpcAddress() == existsPeerName { exists, existingPeer = true, peer break } @@ -141,8 +143,10 @@ func (s *RaftServer) Peers() (members []string) { return } -func isTheFirstOne(self string, peers []string) bool { - sort.Strings(peers) +func isTheFirstOne(self pb.ServerAddress, peers []pb.ServerAddress) bool { + sort.Slice(peers, func(i, j int) bool { + return strings.Compare(string(peers[i]), string(peers[j])) < 0 + }) if len(peers) <= 0 { return true } @@ -155,7 +159,7 @@ func (s *RaftServer) DoJoinCommand() { if _, err := s.raftServer.Do(&raft.DefaultJoinCommand{ Name: s.raftServer.Name(), - ConnectionString: pb.ServerToGrpcAddress(s.serverAddr), + ConnectionString: s.serverAddr.ToGrpcAddress(), }); err != nil { glog.Errorf("fail to send join command: %v", err) } diff --git a/weed/server/raft_server_handlers.go b/weed/server/raft_server_handlers.go index 252570eab..7e58f1e92 100644 --- a/weed/server/raft_server_handlers.go +++ b/weed/server/raft_server_handlers.go @@ -1,15 +1,16 @@ package weed_server import ( + "github.com/chrislusf/seaweedfs/weed/pb" "github.com/chrislusf/seaweedfs/weed/storage/needle" "net/http" ) type ClusterStatusResult struct { - IsLeader bool `json:"IsLeader,omitempty"` - Leader string `json:"Leader,omitempty"` - Peers []string `json:"Peers,omitempty"` - MaxVolumeId needle.VolumeId `json:"MaxVolumeId,omitempty"` + IsLeader bool `json:"IsLeader,omitempty"` + Leader pb.ServerAddress `json:"Leader,omitempty"` + Peers []string `json:"Peers,omitempty"` + MaxVolumeId needle.VolumeId `json:"MaxVolumeId,omitempty"` } func (s *RaftServer) StatusHandler(w http.ResponseWriter, r *http.Request) { diff --git a/weed/server/volume_grpc_client_to_master.go b/weed/server/volume_grpc_client_to_master.go index 770abdab7..2659307fc 100644 --- a/weed/server/volume_grpc_client_to_master.go +++ b/weed/server/volume_grpc_client_to_master.go @@ -19,7 +19,7 @@ import ( "github.com/chrislusf/seaweedfs/weed/util" ) -func (vs *VolumeServer) GetMaster() string { +func (vs *VolumeServer) GetMaster() pb.ServerAddress { return vs.currentMaster } @@ -54,7 +54,7 @@ func (vs *VolumeServer) heartbeat() { grpcDialOption := security.LoadClientTLS(util.GetViper(), "grpc.volume") var err error - var newLeader string + var newLeader pb.ServerAddress for vs.isHeartbeating { for _, master := range vs.SeedMasterNodes { if newLeader != "" { @@ -63,13 +63,8 @@ func (vs *VolumeServer) heartbeat() { time.Sleep(3 * time.Second) master = newLeader } - masterGrpcAddress, parseErr := pb.ParseServerToGrpcAddress(master) - if parseErr != nil { - glog.V(0).Infof("failed to parse master grpc %v: %v", masterGrpcAddress, parseErr) - continue - } vs.store.MasterAddress = master - newLeader, err = vs.doHeartbeat(master, masterGrpcAddress, grpcDialOption, time.Duration(vs.pulseSeconds)*time.Second) + newLeader, err = vs.doHeartbeat(master, grpcDialOption, time.Duration(vs.pulseSeconds)*time.Second) if err != nil { glog.V(0).Infof("heartbeat error: %v", err) time.Sleep(time.Duration(vs.pulseSeconds) * time.Second) @@ -92,25 +87,25 @@ func (vs *VolumeServer) StopHeartbeat() (isAlreadyStopping bool) { return false } -func (vs *VolumeServer) doHeartbeat(masterNode, masterGrpcAddress string, grpcDialOption grpc.DialOption, sleepInterval time.Duration) (newLeader string, err error) { +func (vs *VolumeServer) doHeartbeat(masterAddress pb.ServerAddress, grpcDialOption grpc.DialOption, sleepInterval time.Duration) (newLeader pb.ServerAddress, err error) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() - grpcConection, err := pb.GrpcDial(ctx, masterGrpcAddress, grpcDialOption) + grpcConection, err := pb.GrpcDial(ctx, masterAddress.ToGrpcAddress(), grpcDialOption) if err != nil { - return "", fmt.Errorf("fail to dial %s : %v", masterNode, err) + return "", fmt.Errorf("fail to dial %s : %v", masterAddress, err) } defer grpcConection.Close() client := master_pb.NewSeaweedClient(grpcConection) stream, err := client.SendHeartbeat(ctx) if err != nil { - glog.V(0).Infof("SendHeartbeat to %s: %v", masterNode, err) + glog.V(0).Infof("SendHeartbeat to %s: %v", masterAddress, err) return "", err } - glog.V(0).Infof("Heartbeat to: %v", masterNode) - vs.currentMaster = masterNode + glog.V(0).Infof("Heartbeat to: %v", masterAddress) + vs.currentMaster = masterAddress doneChan := make(chan error, 1) @@ -130,9 +125,9 @@ func (vs *VolumeServer) doHeartbeat(masterNode, masterGrpcAddress string, grpcDi } } } - if in.GetLeader() != "" && vs.currentMaster != in.GetLeader() { + if in.GetLeader() != "" && string(vs.currentMaster) != in.GetLeader() { glog.V(0).Infof("Volume Server found a new master newLeader: %v instead of %v", in.GetLeader(), vs.currentMaster) - newLeader = in.GetLeader() + newLeader = pb.ServerAddress(in.GetLeader()) doneChan <- nil return } @@ -140,12 +135,12 @@ func (vs *VolumeServer) doHeartbeat(masterNode, masterGrpcAddress string, grpcDi }() if err = stream.Send(vs.store.CollectHeartbeat()); err != nil { - glog.V(0).Infof("Volume Server Failed to talk with master %s: %v", masterNode, err) + glog.V(0).Infof("Volume Server Failed to talk with master %s: %v", masterAddress, err) return "", err } if err = stream.Send(vs.store.CollectErasureCodingHeartbeat()); err != nil { - glog.V(0).Infof("Volume Server Failed to talk with master %s: %v", masterNode, err) + glog.V(0).Infof("Volume Server Failed to talk with master %s: %v", masterAddress, err) return "", err } @@ -162,7 +157,7 @@ func (vs *VolumeServer) doHeartbeat(masterNode, masterGrpcAddress string, grpcDi } glog.V(0).Infof("volume server %s:%d adds volume %d", vs.store.Ip, vs.store.Port, volumeMessage.Id) if err = stream.Send(deltaBeat); err != nil { - glog.V(0).Infof("Volume Server Failed to update to master %s: %v", masterNode, err) + glog.V(0).Infof("Volume Server Failed to update to master %s: %v", masterAddress, err) return "", err } case ecShardMessage := <-vs.store.NewEcShardsChan: @@ -174,7 +169,7 @@ func (vs *VolumeServer) doHeartbeat(masterNode, masterGrpcAddress string, grpcDi glog.V(0).Infof("volume server %s:%d adds ec shard %d:%d", vs.store.Ip, vs.store.Port, ecShardMessage.Id, erasure_coding.ShardBits(ecShardMessage.EcIndexBits).ShardIds()) if err = stream.Send(deltaBeat); err != nil { - glog.V(0).Infof("Volume Server Failed to update to master %s: %v", masterNode, err) + glog.V(0).Infof("Volume Server Failed to update to master %s: %v", masterAddress, err) return "", err } case volumeMessage := <-vs.store.DeletedVolumesChan: @@ -185,7 +180,7 @@ func (vs *VolumeServer) doHeartbeat(masterNode, masterGrpcAddress string, grpcDi } glog.V(0).Infof("volume server %s:%d deletes volume %d", vs.store.Ip, vs.store.Port, volumeMessage.Id) if err = stream.Send(deltaBeat); err != nil { - glog.V(0).Infof("Volume Server Failed to update to master %s: %v", masterNode, err) + glog.V(0).Infof("Volume Server Failed to update to master %s: %v", masterAddress, err) return "", err } case ecShardMessage := <-vs.store.DeletedEcShardsChan: @@ -197,20 +192,20 @@ func (vs *VolumeServer) doHeartbeat(masterNode, masterGrpcAddress string, grpcDi glog.V(0).Infof("volume server %s:%d deletes ec shard %d:%d", vs.store.Ip, vs.store.Port, ecShardMessage.Id, erasure_coding.ShardBits(ecShardMessage.EcIndexBits).ShardIds()) if err = stream.Send(deltaBeat); err != nil { - glog.V(0).Infof("Volume Server Failed to update to master %s: %v", masterNode, err) + glog.V(0).Infof("Volume Server Failed to update to master %s: %v", masterAddress, err) return "", err } case <-volumeTickChan: glog.V(4).Infof("volume server %s:%d heartbeat", vs.store.Ip, vs.store.Port) vs.store.MaybeAdjustVolumeMax() if err = stream.Send(vs.store.CollectHeartbeat()); err != nil { - glog.V(0).Infof("Volume Server Failed to talk with master %s: %v", masterNode, err) + glog.V(0).Infof("Volume Server Failed to talk with master %s: %v", masterAddress, err) return "", err } case <-ecShardTickChan: glog.V(4).Infof("volume server %s:%d ec heartbeat", vs.store.Ip, vs.store.Port) if err = stream.Send(vs.store.CollectErasureCodingHeartbeat()); err != nil { - glog.V(0).Infof("Volume Server Failed to talk with master %s: %v", masterNode, err) + glog.V(0).Infof("Volume Server Failed to talk with master %s: %v", masterAddress, err) return "", err } case err = <-doneChan: @@ -229,7 +224,7 @@ func (vs *VolumeServer) doHeartbeat(masterNode, masterGrpcAddress string, grpcDi } glog.V(1).Infof("volume server %s:%d stops and deletes all volumes", vs.store.Ip, vs.store.Port) if err = stream.Send(emptyBeat); err != nil { - glog.V(0).Infof("Volume Server Failed to update to master %s: %v", masterNode, err) + glog.V(0).Infof("Volume Server Failed to update to master %s: %v", masterAddress, err) return "", err } return diff --git a/weed/server/volume_grpc_copy.go b/weed/server/volume_grpc_copy.go index 53ee3df0a..e046481fb 100644 --- a/weed/server/volume_grpc_copy.go +++ b/weed/server/volume_grpc_copy.go @@ -3,6 +3,7 @@ package weed_server import ( "context" "fmt" + "github.com/chrislusf/seaweedfs/weed/pb" "github.com/chrislusf/seaweedfs/weed/storage/types" "io" "io/ioutil" @@ -45,7 +46,7 @@ func (vs *VolumeServer) VolumeCopy(ctx context.Context, req *volume_server_pb.Vo // confirm size and timestamp var volFileInfoResp *volume_server_pb.ReadVolumeFileStatusResponse var dataBaseFileName, indexBaseFileName, idxFileName, datFileName string - err := operation.WithVolumeServerClient(req.SourceDataNode, vs.grpcDialOption, func(client volume_server_pb.VolumeServerClient) error { + err := operation.WithVolumeServerClient(pb.ServerAddress(req.SourceDataNode), vs.grpcDialOption, func(client volume_server_pb.VolumeServerClient) error { var err error volFileInfoResp, err = client.ReadVolumeFileStatus(context.Background(), &volume_server_pb.ReadVolumeFileStatusRequest{ diff --git a/weed/server/volume_grpc_erasure_coding.go b/weed/server/volume_grpc_erasure_coding.go index 364045d9b..653883c8e 100644 --- a/weed/server/volume_grpc_erasure_coding.go +++ b/weed/server/volume_grpc_erasure_coding.go @@ -3,6 +3,7 @@ package weed_server import ( "context" "fmt" + "github.com/chrislusf/seaweedfs/weed/pb" "github.com/chrislusf/seaweedfs/weed/storage/volume_info" "io" "io/ioutil" @@ -126,7 +127,7 @@ func (vs *VolumeServer) VolumeEcShardsCopy(ctx context.Context, req *volume_serv dataBaseFileName := storage.VolumeFileName(location.Directory, req.Collection, int(req.VolumeId)) indexBaseFileName := storage.VolumeFileName(location.IdxDirectory, req.Collection, int(req.VolumeId)) - err := operation.WithVolumeServerClient(req.SourceDataNode, vs.grpcDialOption, func(client volume_server_pb.VolumeServerClient) error { + err := operation.WithVolumeServerClient(pb.ServerAddress(req.SourceDataNode), vs.grpcDialOption, func(client volume_server_pb.VolumeServerClient) error { // copy ec data slices for _, shardId := range req.ShardIds { diff --git a/weed/server/volume_grpc_tail.go b/weed/server/volume_grpc_tail.go index 3ea902ed3..4022da44a 100644 --- a/weed/server/volume_grpc_tail.go +++ b/weed/server/volume_grpc_tail.go @@ -3,6 +3,7 @@ package weed_server import ( "context" "fmt" + "github.com/chrislusf/seaweedfs/weed/pb" "time" "github.com/chrislusf/seaweedfs/weed/glog" @@ -89,7 +90,7 @@ func (vs *VolumeServer) VolumeTailReceiver(ctx context.Context, req *volume_serv defer glog.V(1).Infof("receive tailing volume %d finished", v.Id) - return resp, operation.TailVolumeFromSource(req.SourceVolumeServer, vs.grpcDialOption, v.Id, req.SinceNs, int(req.IdleTimeoutSeconds), func(n *needle.Needle) error { + return resp, operation.TailVolumeFromSource(pb.ServerAddress(req.SourceVolumeServer), vs.grpcDialOption, v.Id, req.SinceNs, int(req.IdleTimeoutSeconds), func(n *needle.Needle) error { _, err := vs.store.WriteVolumeNeedle(v.Id, n, false, false) return err }) diff --git a/weed/server/volume_server.go b/weed/server/volume_server.go index 9406b5601..6b6692146 100644 --- a/weed/server/volume_server.go +++ b/weed/server/volume_server.go @@ -1,6 +1,7 @@ package weed_server import ( + "github.com/chrislusf/seaweedfs/weed/pb" "github.com/chrislusf/seaweedfs/weed/storage/types" "net/http" "sync" @@ -23,8 +24,8 @@ type VolumeServer struct { inFlightUploadDataLimitCond *sync.Cond inFlightDownloadDataLimitCond *sync.Cond - SeedMasterNodes []string - currentMaster string + SeedMasterNodes []pb.ServerAddress + currentMaster pb.ServerAddress pulseSeconds int dataCenter string rack string @@ -44,11 +45,11 @@ type VolumeServer struct { } func NewVolumeServer(adminMux, publicMux *http.ServeMux, ip string, - port int, publicUrl string, + port int, grpcPort int, publicUrl string, folders []string, maxCounts []int, minFreeSpaces []util.MinFreeSpace, diskTypes []types.DiskType, idxFolder string, needleMapKind storage.NeedleMapKind, - masterNodes []string, pulseSeconds int, + masterNodes []pb.ServerAddress, pulseSeconds int, dataCenter string, rack string, whiteList []string, fixJpgOrientation bool, @@ -90,7 +91,7 @@ func NewVolumeServer(adminMux, publicMux *http.ServeMux, ip string, vs.checkWithMaster() - vs.store = storage.NewStore(vs.grpcDialOption, port, ip, publicUrl, folders, maxCounts, minFreeSpaces, idxFolder, vs.needleMapKind, diskTypes) + vs.store = storage.NewStore(vs.grpcDialOption, ip, port, grpcPort, publicUrl, folders, maxCounts, minFreeSpaces, idxFolder, vs.needleMapKind, diskTypes) vs.guard = security.NewGuard(whiteList, signingKey, expiresAfterSec, readSigningKey, readExpiresAfterSec) handleStaticResources(adminMux) diff --git a/weed/server/volume_server_handlers_ui.go b/weed/server/volume_server_handlers_ui.go index 437e5c45d..2c420c2d6 100644 --- a/weed/server/volume_server_handlers_ui.go +++ b/weed/server/volume_server_handlers_ui.go @@ -1,6 +1,7 @@ package weed_server import ( + "github.com/chrislusf/seaweedfs/weed/pb" "net/http" "path/filepath" "time" @@ -35,7 +36,7 @@ func (vs *VolumeServer) uiStatusHandler(w http.ResponseWriter, r *http.Request) } args := struct { Version string - Masters []string + Masters []pb.ServerAddress Volumes interface{} EcVolumes interface{} RemoteVolumes interface{} diff --git a/weed/server/webdav_server.go b/weed/server/webdav_server.go index e99d4a358..239de69f8 100644 --- a/weed/server/webdav_server.go +++ b/weed/server/webdav_server.go @@ -27,8 +27,7 @@ import ( ) type WebDavOption struct { - Filer string - FilerGrpcAddress string + Filer pb.ServerAddress DomainName string BucketsPath string GrpcDialOption grpc.DialOption @@ -107,7 +106,7 @@ type WebDavFile struct { func NewWebDavFileSystem(option *WebDavOption) (webdav.FileSystem, error) { - cacheUniqueId := util.Md5String([]byte("webdav" + option.FilerGrpcAddress + util.Version()))[0:8] + cacheUniqueId := util.Md5String([]byte("webdav" + string(option.Filer) + util.Version()))[0:8] cacheDir := path.Join(option.CacheDir, cacheUniqueId) os.MkdirAll(cacheDir, os.FileMode(0755)) @@ -126,7 +125,7 @@ func (fs *WebDavFileSystem) WithFilerClient(fn func(filer_pb.SeaweedFilerClient) return pb.WithCachedGrpcClient(func(grpcConnection *grpc.ClientConn) error { client := filer_pb.NewSeaweedFilerClient(grpcConnection) return fn(client) - }, fs.option.FilerGrpcAddress, fs.option.GrpcDialOption) + }, fs.option.Filer.ToGrpcAddress(), fs.option.GrpcDialOption) } func (fs *WebDavFileSystem) AdjustedUrl(location *filer_pb.Location) string { @@ -398,7 +397,7 @@ func (f *WebDavFile) saveDataAsChunk(reader io.Reader, name string, offset int64 return fmt.Errorf("assign volume failure %v: %v", request, resp.Error) } - fileId, host, auth = resp.FileId, resp.Url, security.EncodedJwt(resp.Auth) + fileId, host, auth = resp.FileId, resp.Location.Url, security.EncodedJwt(resp.Auth) f.collection, f.replication = resp.Collection, resp.Replication return nil diff --git a/weed/shell/command_ec_balance.go b/weed/shell/command_ec_balance.go index b1ca926d5..d15c69d43 100644 --- a/weed/shell/command_ec_balance.go +++ b/weed/shell/command_ec_balance.go @@ -3,6 +3,7 @@ package shell import ( "flag" "fmt" + "github.com/chrislusf/seaweedfs/weed/pb" "github.com/chrislusf/seaweedfs/weed/storage/types" "io" "sort" @@ -215,10 +216,10 @@ func doDeduplicateEcShards(commandEnv *CommandEnv, collection string, vid needle duplicatedShardIds := []uint32{uint32(shardId)} for _, ecNode := range ecNodes[1:] { - if err := unmountEcShards(commandEnv.option.GrpcDialOption, vid, ecNode.info.Id, duplicatedShardIds); err != nil { + if err := unmountEcShards(commandEnv.option.GrpcDialOption, vid, pb.NewServerAddressFromDataNode(ecNode.info), duplicatedShardIds); err != nil { return err } - if err := sourceServerDeleteEcShards(commandEnv.option.GrpcDialOption, collection, vid, ecNode.info.Id, duplicatedShardIds); err != nil { + if err := sourceServerDeleteEcShards(commandEnv.option.GrpcDialOption, collection, vid, pb.NewServerAddressFromDataNode(ecNode.info), duplicatedShardIds); err != nil { return err } ecNode.deleteEcVolumeShards(vid, duplicatedShardIds) diff --git a/weed/shell/command_ec_common.go b/weed/shell/command_ec_common.go index fd35bb14b..51c8c32cd 100644 --- a/weed/shell/command_ec_common.go +++ b/weed/shell/command_ec_common.go @@ -3,6 +3,7 @@ package shell import ( "context" "fmt" + "github.com/chrislusf/seaweedfs/weed/pb" "github.com/chrislusf/seaweedfs/weed/storage/types" "math" "sort" @@ -22,20 +23,22 @@ func moveMountedShardToEcNode(commandEnv *CommandEnv, existingLocation *EcNode, if applyBalancing { + existingServerAddress := pb.NewServerAddressFromDataNode(existingLocation.info) + // ask destination node to copy shard and the ecx file from source node, and mount it - copiedShardIds, err = oneServerCopyAndMountEcShardsFromSource(commandEnv.option.GrpcDialOption, destinationEcNode, []uint32{uint32(shardId)}, vid, collection, existingLocation.info.Id) + copiedShardIds, err = oneServerCopyAndMountEcShardsFromSource(commandEnv.option.GrpcDialOption, destinationEcNode, []uint32{uint32(shardId)}, vid, collection, existingServerAddress) if err != nil { return err } // unmount the to be deleted shards - err = unmountEcShards(commandEnv.option.GrpcDialOption, vid, existingLocation.info.Id, copiedShardIds) + err = unmountEcShards(commandEnv.option.GrpcDialOption, vid, existingServerAddress, copiedShardIds) if err != nil { return err } // ask source node to delete the shard, and maybe the ecx file - err = sourceServerDeleteEcShards(commandEnv.option.GrpcDialOption, collection, vid, existingLocation.info.Id, copiedShardIds) + err = sourceServerDeleteEcShards(commandEnv.option.GrpcDialOption, collection, vid, existingServerAddress, copiedShardIds) if err != nil { return err } @@ -53,13 +56,14 @@ func moveMountedShardToEcNode(commandEnv *CommandEnv, existingLocation *EcNode, func oneServerCopyAndMountEcShardsFromSource(grpcDialOption grpc.DialOption, targetServer *EcNode, shardIdsToCopy []uint32, - volumeId needle.VolumeId, collection string, existingLocation string) (copiedShardIds []uint32, err error) { + volumeId needle.VolumeId, collection string, existingLocation pb.ServerAddress) (copiedShardIds []uint32, err error) { fmt.Printf("allocate %d.%v %s => %s\n", volumeId, shardIdsToCopy, existingLocation, targetServer.info.Id) - err = operation.WithVolumeServerClient(targetServer.info.Id, grpcDialOption, func(volumeServerClient volume_server_pb.VolumeServerClient) error { + targetAddress := pb.NewServerAddressFromDataNode(targetServer.info) + err = operation.WithVolumeServerClient(targetAddress, grpcDialOption, func(volumeServerClient volume_server_pb.VolumeServerClient) error { - if targetServer.info.Id != existingLocation { + if targetAddress != existingLocation { fmt.Printf("copy %d.%v %s => %s\n", volumeId, shardIdsToCopy, existingLocation, targetServer.info.Id) _, copyErr := volumeServerClient.VolumeEcShardsCopy(context.Background(), &volume_server_pb.VolumeEcShardsCopyRequest{ @@ -69,7 +73,7 @@ func oneServerCopyAndMountEcShardsFromSource(grpcDialOption grpc.DialOption, CopyEcxFile: true, CopyEcjFile: true, CopyVifFile: true, - SourceDataNode: existingLocation, + SourceDataNode: string(existingLocation), }) if copyErr != nil { return fmt.Errorf("copy %d.%v %s => %s : %v\n", volumeId, shardIdsToCopy, existingLocation, targetServer.info.Id, copyErr) @@ -86,7 +90,7 @@ func oneServerCopyAndMountEcShardsFromSource(grpcDialOption grpc.DialOption, return fmt.Errorf("mount %d.%v on %s : %v\n", volumeId, shardIdsToCopy, targetServer.info.Id, mountErr) } - if targetServer.info.Id != existingLocation { + if targetAddress != existingLocation { copiedShardIds = shardIdsToCopy glog.V(0).Infof("%s ec volume %d deletes shards %+v", existingLocation, volumeId, copiedShardIds) } @@ -233,7 +237,7 @@ func collectEcVolumeServersByDc(topo *master_pb.TopologyInfo, selectedDataCenter return } -func sourceServerDeleteEcShards(grpcDialOption grpc.DialOption, collection string, volumeId needle.VolumeId, sourceLocation string, toBeDeletedShardIds []uint32) error { +func sourceServerDeleteEcShards(grpcDialOption grpc.DialOption, collection string, volumeId needle.VolumeId, sourceLocation pb.ServerAddress, toBeDeletedShardIds []uint32) error { fmt.Printf("delete %d.%v from %s\n", volumeId, toBeDeletedShardIds, sourceLocation) @@ -248,7 +252,7 @@ func sourceServerDeleteEcShards(grpcDialOption grpc.DialOption, collection strin } -func unmountEcShards(grpcDialOption grpc.DialOption, volumeId needle.VolumeId, sourceLocation string, toBeUnmountedhardIds []uint32) error { +func unmountEcShards(grpcDialOption grpc.DialOption, volumeId needle.VolumeId, sourceLocation pb.ServerAddress, toBeUnmountedhardIds []uint32) error { fmt.Printf("unmount %d.%v from %s\n", volumeId, toBeUnmountedhardIds, sourceLocation) @@ -261,7 +265,7 @@ func unmountEcShards(grpcDialOption grpc.DialOption, volumeId needle.VolumeId, s }) } -func mountEcShards(grpcDialOption grpc.DialOption, collection string, volumeId needle.VolumeId, sourceLocation string, toBeMountedhardIds []uint32) error { +func mountEcShards(grpcDialOption grpc.DialOption, collection string, volumeId needle.VolumeId, sourceLocation pb.ServerAddress, toBeMountedhardIds []uint32) error { fmt.Printf("mount %d.%v on %s\n", volumeId, toBeMountedhardIds, sourceLocation) diff --git a/weed/shell/command_ec_decode.go b/weed/shell/command_ec_decode.go index e4d597d84..68b7820b2 100644 --- a/weed/shell/command_ec_decode.go +++ b/weed/shell/command_ec_decode.go @@ -4,6 +4,7 @@ import ( "context" "flag" "fmt" + "github.com/chrislusf/seaweedfs/weed/pb" "github.com/chrislusf/seaweedfs/weed/storage/types" "io" @@ -100,7 +101,7 @@ func doEcDecode(commandEnv *CommandEnv, topoInfo *master_pb.TopologyInfo, collec return nil } -func mountVolumeAndDeleteEcShards(grpcDialOption grpc.DialOption, collection, targetNodeLocation string, nodeToEcIndexBits map[string]erasure_coding.ShardBits, vid needle.VolumeId) error { +func mountVolumeAndDeleteEcShards(grpcDialOption grpc.DialOption, collection string, targetNodeLocation pb.ServerAddress, nodeToEcIndexBits map[pb.ServerAddress]erasure_coding.ShardBits, vid needle.VolumeId) error { // mount volume if err := operation.WithVolumeServerClient(targetNodeLocation, grpcDialOption, func(volumeServerClient volume_server_pb.VolumeServerClient) error { @@ -132,7 +133,7 @@ func mountVolumeAndDeleteEcShards(grpcDialOption grpc.DialOption, collection, ta return nil } -func generateNormalVolume(grpcDialOption grpc.DialOption, vid needle.VolumeId, collection string, sourceVolumeServer string) error { +func generateNormalVolume(grpcDialOption grpc.DialOption, vid needle.VolumeId, collection string, sourceVolumeServer pb.ServerAddress) error { fmt.Printf("generateNormalVolume from ec volume %d on %s\n", vid, sourceVolumeServer) @@ -148,7 +149,7 @@ func generateNormalVolume(grpcDialOption grpc.DialOption, vid needle.VolumeId, c } -func collectEcShards(commandEnv *CommandEnv, nodeToEcIndexBits map[string]erasure_coding.ShardBits, collection string, vid needle.VolumeId) (targetNodeLocation string, err error) { +func collectEcShards(commandEnv *CommandEnv, nodeToEcIndexBits map[pb.ServerAddress]erasure_coding.ShardBits, collection string, vid needle.VolumeId) (targetNodeLocation pb.ServerAddress, err error) { maxShardCount := 0 var exisitngEcIndexBits erasure_coding.ShardBits @@ -185,7 +186,7 @@ func collectEcShards(commandEnv *CommandEnv, nodeToEcIndexBits map[string]erasur CopyEcxFile: false, CopyEcjFile: true, CopyVifFile: true, - SourceDataNode: loc, + SourceDataNode: string(loc), }) if copyErr != nil { return fmt.Errorf("copy %d.%v %s => %s : %v\n", vid, needToCopyEcIndexBits.ShardIds(), loc, targetNodeLocation, copyErr) @@ -243,14 +244,14 @@ func collectEcShardIds(topoInfo *master_pb.TopologyInfo, selectedCollection stri return } -func collectEcNodeShardBits(topoInfo *master_pb.TopologyInfo, vid needle.VolumeId) map[string]erasure_coding.ShardBits { +func collectEcNodeShardBits(topoInfo *master_pb.TopologyInfo, vid needle.VolumeId) map[pb.ServerAddress]erasure_coding.ShardBits { - nodeToEcIndexBits := make(map[string]erasure_coding.ShardBits) + nodeToEcIndexBits := make(map[pb.ServerAddress]erasure_coding.ShardBits) eachDataNode(topoInfo, func(dc string, rack RackId, dn *master_pb.DataNodeInfo) { if diskInfo, found := dn.DiskInfos[string(types.HardDriveType)]; found { for _, v := range diskInfo.EcShardInfos { if v.Id == uint32(vid) { - nodeToEcIndexBits[dn.Id] = erasure_coding.ShardBits(v.EcIndexBits) + nodeToEcIndexBits[pb.NewServerAddressFromDataNode(dn)] = erasure_coding.ShardBits(v.EcIndexBits) } } } diff --git a/weed/shell/command_ec_encode.go b/weed/shell/command_ec_encode.go index 014b9bab7..39ca39a4f 100644 --- a/weed/shell/command_ec_encode.go +++ b/weed/shell/command_ec_encode.go @@ -4,6 +4,7 @@ import ( "context" "flag" "fmt" + "github.com/chrislusf/seaweedfs/weed/pb" "io" "sync" "time" @@ -106,7 +107,7 @@ func doEcEncode(commandEnv *CommandEnv, collection string, vid needle.VolumeId, } // generate ec shards - err = generateEcShards(commandEnv.option.GrpcDialOption, vid, collection, locations[0].Url) + err = generateEcShards(commandEnv.option.GrpcDialOption, vid, collection, locations[0].ServerAddress()) if err != nil { return fmt.Errorf("generate ec shards for volume %d on %s: %v", vid, locations[0].Url, err) } @@ -120,7 +121,7 @@ func doEcEncode(commandEnv *CommandEnv, collection string, vid needle.VolumeId, return nil } -func generateEcShards(grpcDialOption grpc.DialOption, volumeId needle.VolumeId, collection string, sourceVolumeServer string) error { +func generateEcShards(grpcDialOption grpc.DialOption, volumeId needle.VolumeId, collection string, sourceVolumeServer pb.ServerAddress) error { fmt.Printf("generateEcShards %s %d on %s ...\n", collection, volumeId, sourceVolumeServer) @@ -161,13 +162,13 @@ func spreadEcShards(commandEnv *CommandEnv, volumeId needle.VolumeId, collection } // unmount the to be deleted shards - err = unmountEcShards(commandEnv.option.GrpcDialOption, volumeId, existingLocations[0].Url, copiedShardIds) + err = unmountEcShards(commandEnv.option.GrpcDialOption, volumeId, existingLocations[0].ServerAddress(), copiedShardIds) if err != nil { return err } // ask the source volume server to clean up copied ec shards - err = sourceServerDeleteEcShards(commandEnv.option.GrpcDialOption, collection, volumeId, existingLocations[0].Url, copiedShardIds) + err = sourceServerDeleteEcShards(commandEnv.option.GrpcDialOption, collection, volumeId, existingLocations[0].ServerAddress(), copiedShardIds) if err != nil { return fmt.Errorf("source delete copied ecShards %s %d.%v: %v", existingLocations[0].Url, volumeId, copiedShardIds, err) } @@ -175,7 +176,7 @@ func spreadEcShards(commandEnv *CommandEnv, volumeId needle.VolumeId, collection // ask the source volume server to delete the original volume for _, location := range existingLocations { fmt.Printf("delete volume %d from %s\n", volumeId, location.Url) - err = deleteVolume(commandEnv.option.GrpcDialOption, volumeId, location.Url) + err = deleteVolume(commandEnv.option.GrpcDialOption, volumeId, location.ServerAddress()) if err != nil { return fmt.Errorf("deleteVolume %s volume %d: %v", location.Url, volumeId, err) } @@ -194,7 +195,7 @@ func parallelCopyEcShardsFromSource(grpcDialOption grpc.DialOption, targetServer copyFunc := func(server *EcNode, allocatedEcShardIds []uint32) { defer wg.Done() copiedShardIds, copyErr := oneServerCopyAndMountEcShardsFromSource(grpcDialOption, server, - allocatedEcShardIds, volumeId, collection, existingLocation.Url) + allocatedEcShardIds, volumeId, collection, existingLocation.ServerAddress()) if copyErr != nil { err = copyErr } else { diff --git a/weed/shell/command_ec_rebuild.go b/weed/shell/command_ec_rebuild.go index 8d5d7bb91..3bdb9b4a0 100644 --- a/weed/shell/command_ec_rebuild.go +++ b/weed/shell/command_ec_rebuild.go @@ -4,6 +4,7 @@ import ( "context" "flag" "fmt" + "github.com/chrislusf/seaweedfs/weed/pb" "io" "github.com/chrislusf/seaweedfs/weed/operation" @@ -141,7 +142,7 @@ func rebuildOneEcVolume(commandEnv *CommandEnv, rebuilder *EcNode, collection st // clean up working files // ask the rebuilder to delete the copied shards - err = sourceServerDeleteEcShards(commandEnv.option.GrpcDialOption, collection, volumeId, rebuilder.info.Id, copiedShardIds) + err = sourceServerDeleteEcShards(commandEnv.option.GrpcDialOption, collection, volumeId, pb.NewServerAddressFromDataNode(rebuilder.info), copiedShardIds) if err != nil { fmt.Fprintf(writer, "%s delete copied ec shards %s %d.%v\n", rebuilder.info.Id, collection, volumeId, copiedShardIds) } @@ -153,13 +154,13 @@ func rebuildOneEcVolume(commandEnv *CommandEnv, rebuilder *EcNode, collection st } // generate ec shards, and maybe ecx file - generatedShardIds, err = generateMissingShards(commandEnv.option.GrpcDialOption, collection, volumeId, rebuilder.info.Id) + generatedShardIds, err = generateMissingShards(commandEnv.option.GrpcDialOption, collection, volumeId, pb.NewServerAddressFromDataNode(rebuilder.info)) if err != nil { return err } // mount the generated shards - err = mountEcShards(commandEnv.option.GrpcDialOption, collection, volumeId, rebuilder.info.Id, generatedShardIds) + err = mountEcShards(commandEnv.option.GrpcDialOption, collection, volumeId, pb.NewServerAddressFromDataNode(rebuilder.info), generatedShardIds) if err != nil { return err } @@ -169,7 +170,7 @@ func rebuildOneEcVolume(commandEnv *CommandEnv, rebuilder *EcNode, collection st return nil } -func generateMissingShards(grpcDialOption grpc.DialOption, collection string, volumeId needle.VolumeId, sourceLocation string) (rebuiltShardIds []uint32, err error) { +func generateMissingShards(grpcDialOption grpc.DialOption, collection string, volumeId needle.VolumeId, sourceLocation pb.ServerAddress) (rebuiltShardIds []uint32, err error) { err = operation.WithVolumeServerClient(sourceLocation, grpcDialOption, func(volumeServerClient volume_server_pb.VolumeServerClient) error { resp, rebultErr := volumeServerClient.VolumeEcShardsRebuild(context.Background(), &volume_server_pb.VolumeEcShardsRebuildRequest{ @@ -212,7 +213,7 @@ func prepareDataToRecover(commandEnv *CommandEnv, rebuilder *EcNode, collection var copyErr error if applyBalancing { - copyErr = operation.WithVolumeServerClient(rebuilder.info.Id, commandEnv.option.GrpcDialOption, func(volumeServerClient volume_server_pb.VolumeServerClient) error { + copyErr = operation.WithVolumeServerClient(pb.NewServerAddressFromDataNode(rebuilder.info), commandEnv.option.GrpcDialOption, func(volumeServerClient volume_server_pb.VolumeServerClient) error { _, copyErr := volumeServerClient.VolumeEcShardsCopy(context.Background(), &volume_server_pb.VolumeEcShardsCopyRequest{ VolumeId: uint32(volumeId), Collection: collection, diff --git a/weed/shell/command_fs_meta_save.go b/weed/shell/command_fs_meta_save.go index d7cc2efef..2cbe83e21 100644 --- a/weed/shell/command_fs_meta_save.go +++ b/weed/shell/command_fs_meta_save.go @@ -63,8 +63,8 @@ func (c *commandFsMetaSave) Do(args []string, commandEnv *CommandEnv, writer io. fileName := *outputFileName if fileName == "" { t := time.Now() - fileName = fmt.Sprintf("%s-%d-%4d%02d%02d-%02d%02d%02d.meta", - commandEnv.option.FilerHost, commandEnv.option.FilerPort, t.Year(), t.Month(), t.Day(), t.Hour(), t.Minute(), t.Second()) + fileName = fmt.Sprintf("%s-%4d%02d%02d-%02d%02d%02d.meta", + commandEnv.option.FilerAddress.ToHttpAddress(), t.Year(), t.Month(), t.Day(), t.Hour(), t.Minute(), t.Second()) } dst, openErr := os.OpenFile(fileName, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644) @@ -105,7 +105,7 @@ func (c *commandFsMetaSave) Do(args []string, commandEnv *CommandEnv, writer io. }) if err == nil { - fmt.Fprintf(writer, "meta data for http://%s:%d%s is saved to %s\n", commandEnv.option.FilerHost, commandEnv.option.FilerPort, path, fileName) + fmt.Fprintf(writer, "meta data for http://%s%s is saved to %s\n", commandEnv.option.FilerAddress.ToHttpAddress(), path, fileName) } return err diff --git a/weed/shell/command_s3_clean_uploads.go b/weed/shell/command_s3_clean_uploads.go index 1ba31292c..4f893df7a 100644 --- a/weed/shell/command_s3_clean_uploads.go +++ b/weed/shell/command_s3_clean_uploads.go @@ -78,7 +78,7 @@ func (c *commandS3CleanUploads) cleanupUploads(commandEnv *CommandEnv, writer io } for _, staleUpload := range staleUploads { - deleteUrl := fmt.Sprintf("http://%s:%d%s/%s?recursive=true&ignoreRecursiveError=true", commandEnv.option.FilerHost, commandEnv.option.FilerPort, uploadsDir, staleUpload) + deleteUrl := fmt.Sprintf("http://%s%s/%s?recursive=true&ignoreRecursiveError=true", commandEnv.option.FilerAddress.ToHttpAddress(), uploadsDir, staleUpload) fmt.Fprintf(writer, "purge %s\n", deleteUrl) err = util.Delete(deleteUrl, "") diff --git a/weed/shell/command_volume_balance.go b/weed/shell/command_volume_balance.go index df3067722..8098fabdf 100644 --- a/weed/shell/command_volume_balance.go +++ b/weed/shell/command_volume_balance.go @@ -3,6 +3,7 @@ package shell import ( "flag" "fmt" + "github.com/chrislusf/seaweedfs/weed/pb" "github.com/chrislusf/seaweedfs/weed/storage/super_block" "github.com/chrislusf/seaweedfs/weed/storage/types" "io" @@ -325,7 +326,7 @@ func moveVolume(commandEnv *CommandEnv, v *master_pb.VolumeInformationMessage, f } fmt.Fprintf(os.Stdout, " moving %s volume %s%d %s => %s\n", v.DiskType, collectionPrefix, v.Id, fullNode.info.Id, emptyNode.info.Id) if applyChange { - return LiveMoveVolume(commandEnv.option.GrpcDialOption, os.Stderr, needle.VolumeId(v.Id), fullNode.info.Id, emptyNode.info.Id, 5*time.Second, v.DiskType, false) + return LiveMoveVolume(commandEnv.option.GrpcDialOption, os.Stderr, needle.VolumeId(v.Id), pb.NewServerAddressFromDataNode(fullNode.info), pb.NewServerAddressFromDataNode(emptyNode.info), 5*time.Second, v.DiskType, false) } return nil } diff --git a/weed/shell/command_volume_check_disk.go b/weed/shell/command_volume_check_disk.go index 7e060f3d3..a7343e258 100644 --- a/weed/shell/command_volume_check_disk.go +++ b/weed/shell/command_volume_check_disk.go @@ -6,6 +6,7 @@ import ( "flag" "fmt" "github.com/chrislusf/seaweedfs/weed/operation" + "github.com/chrislusf/seaweedfs/weed/pb" "github.com/chrislusf/seaweedfs/weed/pb/volume_server_pb" "github.com/chrislusf/seaweedfs/weed/storage/needle_map" "io" @@ -108,10 +109,10 @@ func (c *commandVolumeCheckDisk) syncTwoReplicas(aDB *needle_map.MemDb, bDB *nee aDB, bDB = needle_map.NewMemDb(), needle_map.NewMemDb() // read index db - if err := c.readIndexDatabase(aDB, a.info.Collection, a.info.Id, a.location.dataNode.Id, *verbose, writer); err != nil { + if err := c.readIndexDatabase(aDB, a.info.Collection, a.info.Id, pb.NewServerAddressFromDataNode(a.location.dataNode), *verbose, writer); err != nil { return err } - if err := c.readIndexDatabase(bDB, b.info.Collection, b.info.Id, b.location.dataNode.Id, *verbose, writer); err != nil { + if err := c.readIndexDatabase(bDB, b.info.Collection, b.info.Id, pb.NewServerAddressFromDataNode(b.location.dataNode), *verbose, writer); err != nil { return err } @@ -155,7 +156,7 @@ func (c *commandVolumeCheckDisk) doVolumeCheckDisk(subtrahend, minuend *needle_m for _, needleValue := range missingNeedles { - needleBlob, err := c.readSourceNeedleBlob(source.location.dataNode.Id, source.info.Id, needleValue) + needleBlob, err := c.readSourceNeedleBlob(pb.NewServerAddressFromDataNode(source.location.dataNode), source.info.Id, needleValue) if err != nil { return hasChanges, err } @@ -170,7 +171,7 @@ func (c *commandVolumeCheckDisk) doVolumeCheckDisk(subtrahend, minuend *needle_m hasChanges = true - if err = c.writeNeedleBlobToTarget(target.location.dataNode.Id, source.info.Id, needleValue, needleBlob); err != nil { + if err = c.writeNeedleBlobToTarget(pb.NewServerAddressFromDataNode(target.location.dataNode), source.info.Id, needleValue, needleBlob); err != nil { return hasChanges, err } @@ -179,7 +180,7 @@ func (c *commandVolumeCheckDisk) doVolumeCheckDisk(subtrahend, minuend *needle_m return } -func (c *commandVolumeCheckDisk) readSourceNeedleBlob(sourceVolumeServer string, volumeId uint32, needleValue needle_map.NeedleValue) (needleBlob []byte, err error) { +func (c *commandVolumeCheckDisk) readSourceNeedleBlob(sourceVolumeServer pb.ServerAddress, volumeId uint32, needleValue needle_map.NeedleValue) (needleBlob []byte, err error) { err = operation.WithVolumeServerClient(sourceVolumeServer, c.env.option.GrpcDialOption, func(client volume_server_pb.VolumeServerClient) error { resp, err := client.ReadNeedleBlob(context.Background(), &volume_server_pb.ReadNeedleBlobRequest{ @@ -197,7 +198,7 @@ func (c *commandVolumeCheckDisk) readSourceNeedleBlob(sourceVolumeServer string, return } -func (c *commandVolumeCheckDisk) writeNeedleBlobToTarget(targetVolumeServer string, volumeId uint32, needleValue needle_map.NeedleValue, needleBlob []byte) error { +func (c *commandVolumeCheckDisk) writeNeedleBlobToTarget(targetVolumeServer pb.ServerAddress, volumeId uint32, needleValue needle_map.NeedleValue, needleBlob []byte) error { return operation.WithVolumeServerClient(targetVolumeServer, c.env.option.GrpcDialOption, func(client volume_server_pb.VolumeServerClient) error { _, err := client.WriteNeedleBlob(context.Background(), &volume_server_pb.WriteNeedleBlobRequest{ @@ -211,7 +212,7 @@ func (c *commandVolumeCheckDisk) writeNeedleBlobToTarget(targetVolumeServer stri } -func (c *commandVolumeCheckDisk) readIndexDatabase(db *needle_map.MemDb, collection string, volumeId uint32, volumeServer string, verbose bool, writer io.Writer) error { +func (c *commandVolumeCheckDisk) readIndexDatabase(db *needle_map.MemDb, collection string, volumeId uint32, volumeServer pb.ServerAddress, verbose bool, writer io.Writer) error { var buf bytes.Buffer if err := c.copyVolumeIndexFile(collection, volumeId, volumeServer, &buf, verbose, writer); err != nil { @@ -226,7 +227,7 @@ func (c *commandVolumeCheckDisk) readIndexDatabase(db *needle_map.MemDb, collect } -func (c *commandVolumeCheckDisk) copyVolumeIndexFile(collection string, volumeId uint32, volumeServer string, buf *bytes.Buffer, verbose bool, writer io.Writer) error { +func (c *commandVolumeCheckDisk) copyVolumeIndexFile(collection string, volumeId uint32, volumeServer pb.ServerAddress, buf *bytes.Buffer, verbose bool, writer io.Writer) error { return operation.WithVolumeServerClient(volumeServer, c.env.option.GrpcDialOption, func(volumeServerClient volume_server_pb.VolumeServerClient) error { diff --git a/weed/shell/command_volume_configure_replication.go b/weed/shell/command_volume_configure_replication.go index e3f034873..ffc2656ea 100644 --- a/weed/shell/command_volume_configure_replication.go +++ b/weed/shell/command_volume_configure_replication.go @@ -5,6 +5,7 @@ import ( "errors" "flag" "fmt" + "github.com/chrislusf/seaweedfs/weed/pb" "io" "github.com/chrislusf/seaweedfs/weed/operation" @@ -83,7 +84,7 @@ func (c *commandVolumeConfigureReplication) Do(args []string, commandEnv *Comman } for _, dst := range allLocations { - err := operation.WithVolumeServerClient(dst.dataNode.Id, commandEnv.option.GrpcDialOption, func(volumeServerClient volume_server_pb.VolumeServerClient) error { + err := operation.WithVolumeServerClient(pb.NewServerAddressFromDataNode(dst.dataNode), commandEnv.option.GrpcDialOption, func(volumeServerClient volume_server_pb.VolumeServerClient) error { resp, configureErr := volumeServerClient.VolumeConfigure(context.Background(), &volume_server_pb.VolumeConfigureRequest{ VolumeId: uint32(vid), Replication: replicaPlacement.String(), diff --git a/weed/shell/command_volume_copy.go b/weed/shell/command_volume_copy.go index 85b4bb095..ca8f5c832 100644 --- a/weed/shell/command_volume_copy.go +++ b/weed/shell/command_volume_copy.go @@ -3,6 +3,7 @@ package shell import ( "flag" "fmt" + "github.com/chrislusf/seaweedfs/weed/pb" "io" "github.com/chrislusf/seaweedfs/weed/storage/needle" @@ -44,7 +45,7 @@ func (c *commandVolumeCopy) Do(args []string, commandEnv *CommandEnv, writer io. return nil } - sourceVolumeServer, targetVolumeServer := *sourceNodeStr, *targetNodeStr + sourceVolumeServer, targetVolumeServer := pb.ServerAddress(*sourceNodeStr), pb.ServerAddress(*targetNodeStr) volumeId := needle.VolumeId(*volumeIdInt) diff --git a/weed/shell/command_volume_delete.go b/weed/shell/command_volume_delete.go index 187caa1a4..4297b669b 100644 --- a/weed/shell/command_volume_delete.go +++ b/weed/shell/command_volume_delete.go @@ -2,6 +2,7 @@ package shell import ( "flag" + "github.com/chrislusf/seaweedfs/weed/pb" "io" "github.com/chrislusf/seaweedfs/weed/storage/needle" @@ -41,7 +42,7 @@ func (c *commandVolumeDelete) Do(args []string, commandEnv *CommandEnv, writer i return nil } - sourceVolumeServer := *nodeStr + sourceVolumeServer := pb.ServerAddress(*nodeStr) volumeId := needle.VolumeId(*volumeIdInt) diff --git a/weed/shell/command_volume_delete_empty.go b/weed/shell/command_volume_delete_empty.go index 079915f66..a20ac2f7e 100644 --- a/weed/shell/command_volume_delete_empty.go +++ b/weed/shell/command_volume_delete_empty.go @@ -2,6 +2,7 @@ package shell import ( "flag" + "github.com/chrislusf/seaweedfs/weed/pb" "github.com/chrislusf/seaweedfs/weed/pb/master_pb" "github.com/chrislusf/seaweedfs/weed/storage/needle" "io" @@ -58,7 +59,7 @@ func (c *commandVolumeDeleteEmpty) Do(args []string, commandEnv *CommandEnv, wri if v.Size <= 8 && v.ModifiedAtSecond+quietSeconds < nowUnixSeconds { if *applyBalancing { log.Printf("deleting empty volume %d from %s", v.Id, dn.Id) - if deleteErr := deleteVolume(commandEnv.option.GrpcDialOption, needle.VolumeId(v.Id), dn.Id); deleteErr != nil { + if deleteErr := deleteVolume(commandEnv.option.GrpcDialOption, needle.VolumeId(v.Id), pb.NewServerAddressFromDataNode(dn)); deleteErr != nil { err = deleteErr } continue diff --git a/weed/shell/command_volume_fix_replication.go b/weed/shell/command_volume_fix_replication.go index efd5ae5de..06e9ac003 100644 --- a/weed/shell/command_volume_fix_replication.go +++ b/weed/shell/command_volume_fix_replication.go @@ -4,6 +4,7 @@ import ( "context" "flag" "fmt" + "github.com/chrislusf/seaweedfs/weed/pb" "github.com/chrislusf/seaweedfs/weed/storage/needle" "github.com/chrislusf/seaweedfs/weed/storage/types" "io" @@ -147,7 +148,7 @@ func (c *commandVolumeFixReplication) fixOverReplicatedVolumes(commandEnv *Comma break } - if err := deleteVolume(commandEnv.option.GrpcDialOption, needle.VolumeId(replica.info.Id), replica.location.dataNode.Id); err != nil { + if err := deleteVolume(commandEnv.option.GrpcDialOption, needle.VolumeId(replica.info.Id), pb.NewServerAddressFromDataNode(replica.location.dataNode)); err != nil { return fmt.Errorf("deleting volume %d from %s : %v", replica.info.Id, replica.location.dataNode.Id, err) } @@ -200,10 +201,10 @@ func (c *commandVolumeFixReplication) fixOneUnderReplicatedVolume(commandEnv *Co break } - err := operation.WithVolumeServerClient(dst.dataNode.Id, commandEnv.option.GrpcDialOption, func(volumeServerClient volume_server_pb.VolumeServerClient) error { + err := operation.WithVolumeServerClient(pb.NewServerAddressFromDataNode(dst.dataNode), commandEnv.option.GrpcDialOption, func(volumeServerClient volume_server_pb.VolumeServerClient) error { _, replicateErr := volumeServerClient.VolumeCopy(context.Background(), &volume_server_pb.VolumeCopyRequest{ VolumeId: replica.info.Id, - SourceDataNode: replica.location.dataNode.Id, + SourceDataNode: string(pb.NewServerAddressFromDataNode(replica.location.dataNode)), }) if replicateErr != nil { return fmt.Errorf("copying from %s => %s : %v", replica.location.dataNode.Id, dst.dataNode.Id, replicateErr) diff --git a/weed/shell/command_volume_fsck.go b/weed/shell/command_volume_fsck.go index 9e778b918..a2c3e9fd6 100644 --- a/weed/shell/command_volume_fsck.go +++ b/weed/shell/command_volume_fsck.go @@ -5,6 +5,7 @@ import ( "context" "flag" "fmt" + "github.com/chrislusf/seaweedfs/weed/pb" "github.com/chrislusf/seaweedfs/weed/storage/needle" "io" "io/ioutil" @@ -437,7 +438,7 @@ func (c *commandVolumeFsck) oneVolumeFileIdsSubtractFilerFileIds(tempFolder stri } type VInfo struct { - server string + server pb.ServerAddress collection string isEcVolume bool } @@ -459,14 +460,14 @@ func (c *commandVolumeFsck) collectVolumeIds(commandEnv *CommandEnv, verbose boo for _, diskInfo := range t.DiskInfos { for _, vi := range diskInfo.VolumeInfos { volumeIdToServer[vi.Id] = VInfo{ - server: t.Id, + server: pb.NewServerAddressFromDataNode(t), collection: vi.Collection, isEcVolume: false, } } for _, ecShardInfo := range diskInfo.EcShardInfos { volumeIdToServer[ecShardInfo.Id] = VInfo{ - server: t.Id, + server: pb.NewServerAddressFromDataNode(t), collection: ecShardInfo.Collection, isEcVolume: true, } @@ -491,7 +492,7 @@ func (c *commandVolumeFsck) purgeFileIdsForOneVolume(volumeId uint32, fileIds [] var wg sync.WaitGroup for _, location := range locations { wg.Add(1) - go func(server string, fidList []string) { + go func(server pb.ServerAddress, fidList []string) { defer wg.Done() if deleteResults, deleteErr := operation.DeleteFilesAtOneVolumeServer(server, c.env.option.GrpcDialOption, fidList, false); deleteErr != nil { @@ -500,7 +501,7 @@ func (c *commandVolumeFsck) purgeFileIdsForOneVolume(volumeId uint32, fileIds [] resultChan <- deleteResults } - }(location.Url, fileIds) + }(location.ServerAddress(), fileIds) } wg.Wait() close(resultChan) diff --git a/weed/shell/command_volume_mark.go b/weed/shell/command_volume_mark.go index 19b614310..5531f4e3d 100644 --- a/weed/shell/command_volume_mark.go +++ b/weed/shell/command_volume_mark.go @@ -3,6 +3,7 @@ package shell import ( "flag" "fmt" + "github.com/chrislusf/seaweedfs/weed/pb" "io" "github.com/chrislusf/seaweedfs/weed/storage/needle" @@ -47,7 +48,7 @@ func (c *commandVolumeMark) Do(args []string, commandEnv *CommandEnv, writer io. markWritable = true } - sourceVolumeServer := *nodeStr + sourceVolumeServer := pb.ServerAddress(*nodeStr) volumeId := needle.VolumeId(*volumeIdInt) diff --git a/weed/shell/command_volume_mount.go b/weed/shell/command_volume_mount.go index bd588d0b5..e46ef3683 100644 --- a/weed/shell/command_volume_mount.go +++ b/weed/shell/command_volume_mount.go @@ -3,6 +3,7 @@ package shell import ( "context" "flag" + "github.com/chrislusf/seaweedfs/weed/pb" "io" "github.com/chrislusf/seaweedfs/weed/operation" @@ -45,7 +46,7 @@ func (c *commandVolumeMount) Do(args []string, commandEnv *CommandEnv, writer io return nil } - sourceVolumeServer := *nodeStr + sourceVolumeServer := pb.ServerAddress(*nodeStr) volumeId := needle.VolumeId(*volumeIdInt) @@ -53,7 +54,7 @@ func (c *commandVolumeMount) Do(args []string, commandEnv *CommandEnv, writer io } -func mountVolume(grpcDialOption grpc.DialOption, volumeId needle.VolumeId, sourceVolumeServer string) (err error) { +func mountVolume(grpcDialOption grpc.DialOption, volumeId needle.VolumeId, sourceVolumeServer pb.ServerAddress) (err error) { return operation.WithVolumeServerClient(sourceVolumeServer, grpcDialOption, func(volumeServerClient volume_server_pb.VolumeServerClient) error { _, mountErr := volumeServerClient.VolumeMount(context.Background(), &volume_server_pb.VolumeMountRequest{ VolumeId: uint32(volumeId), diff --git a/weed/shell/command_volume_move.go b/weed/shell/command_volume_move.go index 3dcf41354..c404d8e30 100644 --- a/weed/shell/command_volume_move.go +++ b/weed/shell/command_volume_move.go @@ -4,6 +4,7 @@ import ( "context" "flag" "fmt" + "github.com/chrislusf/seaweedfs/weed/pb" "github.com/chrislusf/seaweedfs/weed/wdclient" "io" "log" @@ -62,7 +63,7 @@ func (c *commandVolumeMove) Do(args []string, commandEnv *CommandEnv, writer io. return nil } - sourceVolumeServer, targetVolumeServer := *sourceNodeStr, *targetNodeStr + sourceVolumeServer, targetVolumeServer := pb.ServerAddress(*sourceNodeStr), pb.ServerAddress(*targetNodeStr) volumeId := needle.VolumeId(*volumeIdInt) @@ -74,7 +75,7 @@ func (c *commandVolumeMove) Do(args []string, commandEnv *CommandEnv, writer io. } // LiveMoveVolume moves one volume from one source volume server to one target volume server, with idleTimeout to drain the incoming requests. -func LiveMoveVolume(grpcDialOption grpc.DialOption, writer io.Writer, volumeId needle.VolumeId, sourceVolumeServer, targetVolumeServer string, idleTimeout time.Duration, diskType string, skipTailError bool) (err error) { +func LiveMoveVolume(grpcDialOption grpc.DialOption, writer io.Writer, volumeId needle.VolumeId, sourceVolumeServer, targetVolumeServer pb.ServerAddress, idleTimeout time.Duration, diskType string, skipTailError bool) (err error) { log.Printf("copying volume %d from %s to %s", volumeId, sourceVolumeServer, targetVolumeServer) lastAppendAtNs, err := copyVolume(grpcDialOption, volumeId, sourceVolumeServer, targetVolumeServer, diskType) @@ -100,7 +101,7 @@ func LiveMoveVolume(grpcDialOption grpc.DialOption, writer io.Writer, volumeId n return nil } -func copyVolume(grpcDialOption grpc.DialOption, volumeId needle.VolumeId, sourceVolumeServer, targetVolumeServer string, diskType string) (lastAppendAtNs uint64, err error) { +func copyVolume(grpcDialOption grpc.DialOption, volumeId needle.VolumeId, sourceVolumeServer, targetVolumeServer pb.ServerAddress, diskType string) (lastAppendAtNs uint64, err error) { // check to see if the volume is already read-only and if its not then we need // to mark it as read-only and then before we return we need to undo what we @@ -142,7 +143,7 @@ func copyVolume(grpcDialOption grpc.DialOption, volumeId needle.VolumeId, source err = operation.WithVolumeServerClient(targetVolumeServer, grpcDialOption, func(volumeServerClient volume_server_pb.VolumeServerClient) error { resp, replicateErr := volumeServerClient.VolumeCopy(context.Background(), &volume_server_pb.VolumeCopyRequest{ VolumeId: uint32(volumeId), - SourceDataNode: sourceVolumeServer, + SourceDataNode: string(sourceVolumeServer), DiskType: diskType, }) if replicateErr == nil { @@ -154,21 +155,21 @@ func copyVolume(grpcDialOption grpc.DialOption, volumeId needle.VolumeId, source return } -func tailVolume(grpcDialOption grpc.DialOption, volumeId needle.VolumeId, sourceVolumeServer, targetVolumeServer string, lastAppendAtNs uint64, idleTimeout time.Duration) (err error) { +func tailVolume(grpcDialOption grpc.DialOption, volumeId needle.VolumeId, sourceVolumeServer, targetVolumeServer pb.ServerAddress, lastAppendAtNs uint64, idleTimeout time.Duration) (err error) { return operation.WithVolumeServerClient(targetVolumeServer, grpcDialOption, func(volumeServerClient volume_server_pb.VolumeServerClient) error { _, replicateErr := volumeServerClient.VolumeTailReceiver(context.Background(), &volume_server_pb.VolumeTailReceiverRequest{ VolumeId: uint32(volumeId), SinceNs: lastAppendAtNs, IdleTimeoutSeconds: uint32(idleTimeout.Seconds()), - SourceVolumeServer: sourceVolumeServer, + SourceVolumeServer: string(sourceVolumeServer), }) return replicateErr }) } -func deleteVolume(grpcDialOption grpc.DialOption, volumeId needle.VolumeId, sourceVolumeServer string) (err error) { +func deleteVolume(grpcDialOption grpc.DialOption, volumeId needle.VolumeId, sourceVolumeServer pb.ServerAddress) (err error) { return operation.WithVolumeServerClient(sourceVolumeServer, grpcDialOption, func(volumeServerClient volume_server_pb.VolumeServerClient) error { _, deleteErr := volumeServerClient.VolumeDelete(context.Background(), &volume_server_pb.VolumeDeleteRequest{ VolumeId: uint32(volumeId), @@ -177,7 +178,7 @@ func deleteVolume(grpcDialOption grpc.DialOption, volumeId needle.VolumeId, sour }) } -func markVolumeWritable(grpcDialOption grpc.DialOption, volumeId needle.VolumeId, sourceVolumeServer string, writable bool) (err error) { +func markVolumeWritable(grpcDialOption grpc.DialOption, volumeId needle.VolumeId, sourceVolumeServer pb.ServerAddress, writable bool) (err error) { return operation.WithVolumeServerClient(sourceVolumeServer, grpcDialOption, func(volumeServerClient volume_server_pb.VolumeServerClient) error { if writable { _, err = volumeServerClient.VolumeMarkWritable(context.Background(), &volume_server_pb.VolumeMarkWritableRequest{ @@ -195,7 +196,7 @@ func markVolumeWritable(grpcDialOption grpc.DialOption, volumeId needle.VolumeId func markVolumeReplicasWritable(grpcDialOption grpc.DialOption, volumeId needle.VolumeId, locations []wdclient.Location, writable bool) error { for _, location := range locations { fmt.Printf("markVolumeReadonly %d on %s ...\n", volumeId, location.Url) - if err := markVolumeWritable(grpcDialOption, volumeId, location.Url, writable); err != nil { + if err := markVolumeWritable(grpcDialOption, volumeId, location.ServerAddress(), writable); err != nil { return err } } diff --git a/weed/shell/command_volume_server_leave.go b/weed/shell/command_volume_server_leave.go index 2a2e56e86..7f7b7148b 100644 --- a/weed/shell/command_volume_server_leave.go +++ b/weed/shell/command_volume_server_leave.go @@ -5,6 +5,7 @@ import ( "flag" "fmt" "github.com/chrislusf/seaweedfs/weed/operation" + "github.com/chrislusf/seaweedfs/weed/pb" "github.com/chrislusf/seaweedfs/weed/pb/volume_server_pb" "google.golang.org/grpc" "io" @@ -50,11 +51,11 @@ func (c *commandVolumeServerLeave) Do(args []string, commandEnv *CommandEnv, wri return fmt.Errorf("need to specify volume server by -node=:") } - return volumeServerLeave(commandEnv.option.GrpcDialOption, *volumeServer, writer) + return volumeServerLeave(commandEnv.option.GrpcDialOption, pb.ServerAddress(*volumeServer), writer) } -func volumeServerLeave(grpcDialOption grpc.DialOption, volumeServer string, writer io.Writer) (err error) { +func volumeServerLeave(grpcDialOption grpc.DialOption, volumeServer pb.ServerAddress, writer io.Writer) (err error) { return operation.WithVolumeServerClient(volumeServer, grpcDialOption, func(volumeServerClient volume_server_pb.VolumeServerClient) error { _, leaveErr := volumeServerClient.VolumeServerLeave(context.Background(), &volume_server_pb.VolumeServerLeaveRequest{}) if leaveErr != nil { diff --git a/weed/shell/command_volume_tier_download.go b/weed/shell/command_volume_tier_download.go index 33166ce65..bfb78608f 100644 --- a/weed/shell/command_volume_tier_download.go +++ b/weed/shell/command_volume_tier_download.go @@ -4,6 +4,7 @@ import ( "context" "flag" "fmt" + "github.com/chrislusf/seaweedfs/weed/pb" "io" "google.golang.org/grpc" @@ -112,7 +113,7 @@ func doVolumeTierDownload(commandEnv *CommandEnv, writer io.Writer, collection s // TODO parallelize this for _, loc := range locations { // copy the .dat file from remote tier to local - err = downloadDatFromRemoteTier(commandEnv.option.GrpcDialOption, writer, needle.VolumeId(vid), collection, loc.Url) + err = downloadDatFromRemoteTier(commandEnv.option.GrpcDialOption, writer, needle.VolumeId(vid), collection, loc.ServerAddress()) if err != nil { return fmt.Errorf("download dat file for volume %d to %s: %v", vid, loc.Url, err) } @@ -121,7 +122,7 @@ func doVolumeTierDownload(commandEnv *CommandEnv, writer io.Writer, collection s return nil } -func downloadDatFromRemoteTier(grpcDialOption grpc.DialOption, writer io.Writer, volumeId needle.VolumeId, collection string, targetVolumeServer string) error { +func downloadDatFromRemoteTier(grpcDialOption grpc.DialOption, writer io.Writer, volumeId needle.VolumeId, collection string, targetVolumeServer pb.ServerAddress) error { err := operation.WithVolumeServerClient(targetVolumeServer, grpcDialOption, func(volumeServerClient volume_server_pb.VolumeServerClient) error { stream, downloadErr := volumeServerClient.VolumeTierMoveDatFromRemote(context.Background(), &volume_server_pb.VolumeTierMoveDatFromRemoteRequest{ diff --git a/weed/shell/command_volume_tier_move.go b/weed/shell/command_volume_tier_move.go index c72958259..33b51ddf2 100644 --- a/weed/shell/command_volume_tier_move.go +++ b/weed/shell/command_volume_tier_move.go @@ -4,6 +4,7 @@ import ( "flag" "fmt" "github.com/chrislusf/seaweedfs/weed/glog" + "github.com/chrislusf/seaweedfs/weed/pb" "github.com/chrislusf/seaweedfs/weed/pb/master_pb" "github.com/chrislusf/seaweedfs/weed/storage/types" "github.com/chrislusf/seaweedfs/weed/wdclient" @@ -20,7 +21,7 @@ func init() { } type commandVolumeTierMove struct { - activeServers map[string]struct{} + activeServers map[pb.ServerAddress]struct{} activeServersLock sync.Mutex activeServersCond *sync.Cond } @@ -42,7 +43,7 @@ func (c *commandVolumeTierMove) Help() string { func (c *commandVolumeTierMove) Do(args []string, commandEnv *CommandEnv, writer io.Writer) (err error) { - c.activeServers = make(map[string]struct{}) + c.activeServers = make(map[pb.ServerAddress]struct{}) c.activeServersCond = sync.NewCond(new(sync.Mutex)) if err = commandEnv.confirmIsLocked(); err != nil { @@ -117,10 +118,10 @@ func (c *commandVolumeTierMove) doVolumeTierMove(commandEnv *CommandEnv, writer if isOneOf(dst.dataNode.Id, locations) { continue } - sourceVolumeServer := "" + var sourceVolumeServer pb.ServerAddress for _, loc := range locations { if loc.Url != dst.dataNode.Id { - sourceVolumeServer = loc.Url + sourceVolumeServer = loc.ServerAddress() } } if sourceVolumeServer == "" { @@ -135,16 +136,17 @@ func (c *commandVolumeTierMove) doVolumeTierMove(commandEnv *CommandEnv, writer break } + destServerAddress := pb.NewServerAddressFromDataNode(dst.dataNode) c.activeServersCond.L.Lock() _, isSourceActive := c.activeServers[sourceVolumeServer] - _, isDestActive := c.activeServers[dst.dataNode.Id] + _, isDestActive := c.activeServers[destServerAddress] for isSourceActive || isDestActive { c.activeServersCond.Wait() _, isSourceActive = c.activeServers[sourceVolumeServer] - _, isDestActive = c.activeServers[dst.dataNode.Id] + _, isDestActive = c.activeServers[destServerAddress] } c.activeServers[sourceVolumeServer] = struct{}{} - c.activeServers[dst.dataNode.Id] = struct{}{} + c.activeServers[destServerAddress] = struct{}{} c.activeServersCond.L.Unlock() wg.Add(1) @@ -153,7 +155,7 @@ func (c *commandVolumeTierMove) doVolumeTierMove(commandEnv *CommandEnv, writer fmt.Fprintf(writer, "move volume %d %s => %s: %v\n", vid, sourceVolumeServer, dst.dataNode.Id, err) } delete(c.activeServers, sourceVolumeServer) - delete(c.activeServers, dst.dataNode.Id) + delete(c.activeServers, destServerAddress) c.activeServersCond.Signal() wg.Done() }(dst) @@ -170,13 +172,13 @@ func (c *commandVolumeTierMove) doVolumeTierMove(commandEnv *CommandEnv, writer return nil } -func (c *commandVolumeTierMove) doMoveOneVolume(commandEnv *CommandEnv, writer io.Writer, vid needle.VolumeId, toDiskType types.DiskType, locations []wdclient.Location, sourceVolumeServer string, dst location) (err error) { +func (c *commandVolumeTierMove) doMoveOneVolume(commandEnv *CommandEnv, writer io.Writer, vid needle.VolumeId, toDiskType types.DiskType, locations []wdclient.Location, sourceVolumeServer pb.ServerAddress, dst location) (err error) { // mark all replicas as read only if err = markVolumeReplicasWritable(commandEnv.option.GrpcDialOption, vid, locations, false); err != nil { return fmt.Errorf("mark volume %d as readonly on %s: %v", vid, locations[0].Url, err) } - if err = LiveMoveVolume(commandEnv.option.GrpcDialOption, writer, vid, sourceVolumeServer, dst.dataNode.Id, 5*time.Second, toDiskType.ReadableString(), true); err != nil { + if err = LiveMoveVolume(commandEnv.option.GrpcDialOption, writer, vid, sourceVolumeServer, pb.NewServerAddressFromDataNode(dst.dataNode), 5*time.Second, toDiskType.ReadableString(), true); err != nil { // mark all replicas as writable if err = markVolumeReplicasWritable(commandEnv.option.GrpcDialOption, vid, locations, true); err != nil { @@ -191,8 +193,8 @@ func (c *commandVolumeTierMove) doMoveOneVolume(commandEnv *CommandEnv, writer i // remove the remaining replicas for _, loc := range locations { - if loc.Url != dst.dataNode.Id && loc.Url != sourceVolumeServer { - if err = deleteVolume(commandEnv.option.GrpcDialOption, vid, loc.Url); err != nil { + if loc.Url != dst.dataNode.Id && loc.ServerAddress() != sourceVolumeServer { + if err = deleteVolume(commandEnv.option.GrpcDialOption, vid, loc.ServerAddress()); err != nil { fmt.Fprintf(writer, "failed to delete volume %d on %s: %v\n", vid, loc.Url, err) } } diff --git a/weed/shell/command_volume_tier_upload.go b/weed/shell/command_volume_tier_upload.go index c8540a7dc..d50178cb9 100644 --- a/weed/shell/command_volume_tier_upload.go +++ b/weed/shell/command_volume_tier_upload.go @@ -4,6 +4,7 @@ import ( "context" "flag" "fmt" + "github.com/chrislusf/seaweedfs/weed/pb" "io" "time" @@ -107,7 +108,7 @@ func doVolumeTierUpload(commandEnv *CommandEnv, writer io.Writer, collection str } // copy the .dat file to remote tier - err = uploadDatToRemoteTier(commandEnv.option.GrpcDialOption, writer, needle.VolumeId(vid), collection, locations[0].Url, dest, keepLocalDatFile) + err = uploadDatToRemoteTier(commandEnv.option.GrpcDialOption, writer, needle.VolumeId(vid), collection, locations[0].ServerAddress(), dest, keepLocalDatFile) if err != nil { return fmt.Errorf("copy dat file for volume %d on %s to %s: %v", vid, locations[0].Url, dest, err) } @@ -115,7 +116,7 @@ func doVolumeTierUpload(commandEnv *CommandEnv, writer io.Writer, collection str return nil } -func uploadDatToRemoteTier(grpcDialOption grpc.DialOption, writer io.Writer, volumeId needle.VolumeId, collection string, sourceVolumeServer string, dest string, keepLocalDatFile bool) error { +func uploadDatToRemoteTier(grpcDialOption grpc.DialOption, writer io.Writer, volumeId needle.VolumeId, collection string, sourceVolumeServer pb.ServerAddress, dest string, keepLocalDatFile bool) error { err := operation.WithVolumeServerClient(sourceVolumeServer, grpcDialOption, func(volumeServerClient volume_server_pb.VolumeServerClient) error { stream, copyErr := volumeServerClient.VolumeTierMoveDatToRemote(context.Background(), &volume_server_pb.VolumeTierMoveDatToRemoteRequest{ diff --git a/weed/shell/command_volume_unmount.go b/weed/shell/command_volume_unmount.go index f7e5a501b..5fc158b76 100644 --- a/weed/shell/command_volume_unmount.go +++ b/weed/shell/command_volume_unmount.go @@ -3,6 +3,7 @@ package shell import ( "context" "flag" + "github.com/chrislusf/seaweedfs/weed/pb" "io" "github.com/chrislusf/seaweedfs/weed/operation" @@ -45,7 +46,7 @@ func (c *commandVolumeUnmount) Do(args []string, commandEnv *CommandEnv, writer return nil } - sourceVolumeServer := *nodeStr + sourceVolumeServer := pb.ServerAddress(*nodeStr) volumeId := needle.VolumeId(*volumeIdInt) @@ -53,7 +54,7 @@ func (c *commandVolumeUnmount) Do(args []string, commandEnv *CommandEnv, writer } -func unmountVolume(grpcDialOption grpc.DialOption, volumeId needle.VolumeId, sourceVolumeServer string) (err error) { +func unmountVolume(grpcDialOption grpc.DialOption, volumeId needle.VolumeId, sourceVolumeServer pb.ServerAddress) (err error) { return operation.WithVolumeServerClient(sourceVolumeServer, grpcDialOption, func(volumeServerClient volume_server_pb.VolumeServerClient) error { _, unmountErr := volumeServerClient.VolumeUnmount(context.Background(), &volume_server_pb.VolumeUnmountRequest{ VolumeId: uint32(volumeId), diff --git a/weed/shell/commands.go b/weed/shell/commands.go index aef71b419..18f357ac7 100644 --- a/weed/shell/commands.go +++ b/weed/shell/commands.go @@ -22,7 +22,7 @@ type ShellOptions struct { // shell transient context FilerHost string FilerPort int64 - FilerAddress string + FilerAddress pb.ServerAddress Directory string } @@ -46,7 +46,7 @@ var ( func NewCommandEnv(options ShellOptions) *CommandEnv { ce := &CommandEnv{ env: make(map[string]string), - MasterClient: wdclient.NewMasterClient(options.GrpcDialOption, pb.AdminShellClient, "", 0, "", strings.Split(*options.Masters, ",")), + MasterClient: wdclient.NewMasterClient(options.GrpcDialOption, pb.AdminShellClient, "", "", pb.ServerAddresses(*options.Masters).ToAddresses()), option: options, } ce.locker = exclusive_locks.NewExclusiveLocker(ce.MasterClient) @@ -98,8 +98,7 @@ var _ = filer_pb.FilerClient(&CommandEnv{}) func (ce *CommandEnv) WithFilerClient(fn func(filer_pb.SeaweedFilerClient) error) error { - filerGrpcAddress := util.JoinHostPort(ce.option.FilerHost, int(ce.option.FilerPort+10000)) - return pb.WithGrpcFilerClient(filerGrpcAddress, ce.option.GrpcDialOption, fn) + return pb.WithGrpcFilerClient(ce.option.FilerAddress, ce.option.GrpcDialOption, fn) } diff --git a/weed/storage/erasure_coding/ec_volume.go b/weed/storage/erasure_coding/ec_volume.go index a6e880350..f4cde310f 100644 --- a/weed/storage/erasure_coding/ec_volume.go +++ b/weed/storage/erasure_coding/ec_volume.go @@ -3,6 +3,7 @@ package erasure_coding import ( "errors" "fmt" + "github.com/chrislusf/seaweedfs/weed/pb" "github.com/chrislusf/seaweedfs/weed/storage/volume_info" "math" "os" @@ -30,7 +31,7 @@ type EcVolume struct { ecxFileSize int64 ecxCreatedAt time.Time Shards []*EcVolumeShard - ShardLocations map[ShardId][]string + ShardLocations map[ShardId][]pb.ServerAddress ShardLocationsRefreshTime time.Time ShardLocationsLock sync.RWMutex Version needle.Version @@ -69,7 +70,7 @@ func NewEcVolume(diskType types.DiskType, dir string, dirIdx string, collection volume_info.SaveVolumeInfo(dataBaseFileName+".vif", &volume_server_pb.VolumeInfo{Version: uint32(ev.Version)}) } - ev.ShardLocations = make(map[ShardId][]string) + ev.ShardLocations = make(map[ShardId][]pb.ServerAddress) return } diff --git a/weed/storage/store.go b/weed/storage/store.go index b5f8f9c4b..8381705d6 100644 --- a/weed/storage/store.go +++ b/weed/storage/store.go @@ -2,6 +2,7 @@ package storage import ( "fmt" + "github.com/chrislusf/seaweedfs/weed/pb" "github.com/chrislusf/seaweedfs/weed/storage/volume_info" "github.com/chrislusf/seaweedfs/weed/util" "path/filepath" @@ -31,11 +32,12 @@ type ReadOption struct { * A VolumeServer contains one Store */ type Store struct { - MasterAddress string + MasterAddress pb.ServerAddress grpcDialOption grpc.DialOption volumeSizeLimit uint64 // read from the master Ip string Port int + GrpcPort int PublicUrl string Locations []*DiskLocation dataCenter string // optional informaton, overwriting master setting if exists @@ -50,13 +52,13 @@ type Store struct { } func (s *Store) String() (str string) { - str = fmt.Sprintf("Ip:%s, Port:%d, PublicUrl:%s, dataCenter:%s, rack:%s, connected:%v, volumeSizeLimit:%d", s.Ip, s.Port, s.PublicUrl, s.dataCenter, s.rack, s.connected, s.GetVolumeSizeLimit()) + str = fmt.Sprintf("Ip:%s, Port:%d, GrpcPort:%d PublicUrl:%s, dataCenter:%s, rack:%s, connected:%v, volumeSizeLimit:%d", s.Ip, s.Port, s.GrpcPort, s.PublicUrl, s.dataCenter, s.rack, s.connected, s.GetVolumeSizeLimit()) return } -func NewStore(grpcDialOption grpc.DialOption, port int, ip, publicUrl string, dirnames []string, maxVolumeCounts []int, +func NewStore(grpcDialOption grpc.DialOption, ip string, port int, grpcPort int, publicUrl string, dirnames []string, maxVolumeCounts []int, minFreeSpaces []util.MinFreeSpace, idxFolder string, needleMapKind NeedleMapKind, diskTypes []DiskType) (s *Store) { - s = &Store{grpcDialOption: grpcDialOption, Port: port, Ip: ip, PublicUrl: publicUrl, NeedleMapKind: needleMapKind} + s = &Store{grpcDialOption: grpcDialOption, Port: port, Ip: ip, GrpcPort: grpcPort, PublicUrl: publicUrl, NeedleMapKind: needleMapKind} s.Locations = make([]*DiskLocation, 0) for i := 0; i < len(dirnames); i++ { location := NewDiskLocation(dirnames[i], maxVolumeCounts[i], minFreeSpaces[i], idxFolder, diskTypes[i]) @@ -311,6 +313,7 @@ func (s *Store) CollectHeartbeat() *master_pb.Heartbeat { return &master_pb.Heartbeat{ Ip: s.Ip, Port: uint32(s.Port), + GrpcPort: uint32(s.GrpcPort), PublicUrl: s.PublicUrl, MaxVolumeCounts: maxVolumeCounts, MaxFileKey: NeedleIdToUint64(maxFileKey), diff --git a/weed/storage/store_ec.go b/weed/storage/store_ec.go index 6ba7237e2..0e33a8e32 100644 --- a/weed/storage/store_ec.go +++ b/weed/storage/store_ec.go @@ -3,6 +3,7 @@ package storage import ( "context" "fmt" + "github.com/chrislusf/seaweedfs/weed/pb" "io" "os" "sort" @@ -255,7 +256,7 @@ func (s *Store) cachedLookupEcShardLocations(ecVolume *erasure_coding.EcVolume) shardId := erasure_coding.ShardId(shardIdLocations.ShardId) delete(ecVolume.ShardLocations, shardId) for _, loc := range shardIdLocations.Locations { - ecVolume.ShardLocations[shardId] = append(ecVolume.ShardLocations[shardId], loc.Url) + ecVolume.ShardLocations[shardId] = append(ecVolume.ShardLocations[shardId], pb.NewServerAddressFromLocation(loc)) } } ecVolume.ShardLocationsRefreshTime = time.Now() @@ -266,7 +267,7 @@ func (s *Store) cachedLookupEcShardLocations(ecVolume *erasure_coding.EcVolume) return } -func (s *Store) readRemoteEcShardInterval(sourceDataNodes []string, needleId types.NeedleId, vid needle.VolumeId, shardId erasure_coding.ShardId, buf []byte, offset int64) (n int, is_deleted bool, err error) { +func (s *Store) readRemoteEcShardInterval(sourceDataNodes []pb.ServerAddress, needleId types.NeedleId, vid needle.VolumeId, shardId erasure_coding.ShardId, buf []byte, offset int64) (n int, is_deleted bool, err error) { if len(sourceDataNodes) == 0 { return 0, false, fmt.Errorf("failed to find ec shard %d.%d", vid, shardId) @@ -284,7 +285,7 @@ func (s *Store) readRemoteEcShardInterval(sourceDataNodes []string, needleId typ return } -func (s *Store) doReadRemoteEcShardInterval(sourceDataNode string, needleId types.NeedleId, vid needle.VolumeId, shardId erasure_coding.ShardId, buf []byte, offset int64) (n int, is_deleted bool, err error) { +func (s *Store) doReadRemoteEcShardInterval(sourceDataNode pb.ServerAddress, needleId types.NeedleId, vid needle.VolumeId, shardId erasure_coding.ShardId, buf []byte, offset int64) (n int, is_deleted bool, err error) { err = operation.WithVolumeServerClient(sourceDataNode, s.grpcDialOption, func(client volume_server_pb.VolumeServerClient) error { @@ -349,7 +350,7 @@ func (s *Store) recoverOneRemoteEcShardInterval(needleId types.NeedleId, ecVolum // read from remote locations wg.Add(1) - go func(shardId erasure_coding.ShardId, locations []string) { + go func(shardId erasure_coding.ShardId, locations []pb.ServerAddress) { defer wg.Done() data := make([]byte, len(buf)) nRead, isDeleted, readErr := s.readRemoteEcShardInterval(locations, needleId, ecVolume.VolumeId, shardId, data, offset) diff --git a/weed/storage/store_ec_delete.go b/weed/storage/store_ec_delete.go index 6c10af3c5..d40165ff5 100644 --- a/weed/storage/store_ec_delete.go +++ b/weed/storage/store_ec_delete.go @@ -3,6 +3,7 @@ package storage import ( "context" "fmt" + "github.com/chrislusf/seaweedfs/weed/pb" "github.com/chrislusf/seaweedfs/weed/glog" "github.com/chrislusf/seaweedfs/weed/operation" @@ -85,7 +86,7 @@ func (s *Store) doDeleteNeedleFromRemoteEcShardServers(shardId erasure_coding.Sh } -func (s *Store) doDeleteNeedleFromRemoteEcShard(sourceDataNode string, vid needle.VolumeId, collection string, version needle.Version, needleId types.NeedleId) error { +func (s *Store) doDeleteNeedleFromRemoteEcShard(sourceDataNode pb.ServerAddress, vid needle.VolumeId, collection string, version needle.Version, needleId types.NeedleId) error { return operation.WithVolumeServerClient(sourceDataNode, s.grpcDialOption, func(client volume_server_pb.VolumeServerClient) error { diff --git a/weed/storage/volume_backup.go b/weed/storage/volume_backup.go index 82ea12a89..7fadd6fef 100644 --- a/weed/storage/volume_backup.go +++ b/weed/storage/volume_backup.go @@ -3,6 +3,7 @@ package storage import ( "context" "fmt" + "github.com/chrislusf/seaweedfs/weed/pb" "io" "os" @@ -62,7 +63,7 @@ update needle map when receiving new .dat bytes. But seems not necessary now.) */ -func (v *Volume) IncrementalBackup(volumeServer string, grpcDialOption grpc.DialOption) error { +func (v *Volume) IncrementalBackup(volumeServer pb.ServerAddress, grpcDialOption grpc.DialOption) error { startFromOffset, _, _ := v.FileStat() appendAtNs, err := v.findLastAppendAtNs() diff --git a/weed/topology/allocate_volume.go b/weed/topology/allocate_volume.go index 7c7fae683..83043c23f 100644 --- a/weed/topology/allocate_volume.go +++ b/weed/topology/allocate_volume.go @@ -15,7 +15,7 @@ type AllocateVolumeResult struct { func AllocateVolume(dn *DataNode, grpcDialOption grpc.DialOption, vid needle.VolumeId, option *VolumeGrowOption) error { - return operation.WithVolumeServerClient(dn.Url(), grpcDialOption, func(client volume_server_pb.VolumeServerClient) error { + return operation.WithVolumeServerClient(dn.ServerAddress(), grpcDialOption, func(client volume_server_pb.VolumeServerClient) error { _, deleteErr := client.AllocateVolume(context.Background(), &volume_server_pb.AllocateVolumeRequest{ VolumeId: uint32(vid), diff --git a/weed/topology/data_node.go b/weed/topology/data_node.go index cd3249c98..9f868681e 100644 --- a/weed/topology/data_node.go +++ b/weed/topology/data_node.go @@ -3,6 +3,7 @@ package topology import ( "fmt" "github.com/chrislusf/seaweedfs/weed/glog" + "github.com/chrislusf/seaweedfs/weed/pb" "github.com/chrislusf/seaweedfs/weed/pb/master_pb" "github.com/chrislusf/seaweedfs/weed/storage" "github.com/chrislusf/seaweedfs/weed/storage/needle" @@ -14,6 +15,7 @@ type DataNode struct { NodeImpl Ip string Port int + GrpcPort int PublicUrl string LastSeen int64 // unix time in seconds Counter int // in race condition, the previous dataNode was not dead @@ -208,6 +210,10 @@ func (dn *DataNode) Url() string { return util.JoinHostPort(dn.Ip, dn.Port) } +func (dn *DataNode) ServerAddress() pb.ServerAddress { + return pb.NewServerAddress(dn.Ip, dn.Port, dn.GrpcPort) +} + func (dn *DataNode) ToMap() interface{} { ret := make(map[string]interface{}) ret["Url"] = dn.Url() @@ -239,6 +245,7 @@ func (dn *DataNode) ToDataNodeInfo() *master_pb.DataNodeInfo { m := &master_pb.DataNodeInfo{ Id: string(dn.Id()), DiskInfos: make(map[string]*master_pb.DiskInfo), + GrpcPort: uint32(dn.GrpcPort), } for _, c := range dn.Children() { disk := c.(*Disk) diff --git a/weed/topology/rack.go b/weed/topology/rack.go index 9c77285c3..cd09746b2 100644 --- a/weed/topology/rack.go +++ b/weed/topology/rack.go @@ -30,7 +30,7 @@ func (r *Rack) FindDataNode(ip string, port int) *DataNode { } return nil } -func (r *Rack) GetOrCreateDataNode(ip string, port int, publicUrl string, maxVolumeCounts map[string]uint32) *DataNode { +func (r *Rack) GetOrCreateDataNode(ip string, port int, grpcPort int, publicUrl string, maxVolumeCounts map[string]uint32) *DataNode { for _, c := range r.Children() { dn := c.(*DataNode) if dn.MatchLocation(ip, port) { @@ -41,6 +41,7 @@ func (r *Rack) GetOrCreateDataNode(ip string, port int, publicUrl string, maxVol dn := NewDataNode(util.JoinHostPort(ip, port)) dn.Ip = ip dn.Port = port + dn.GrpcPort = grpcPort dn.PublicUrl = publicUrl dn.LastSeen = time.Now().Unix() r.LinkChildNode(dn) diff --git a/weed/topology/topology.go b/weed/topology/topology.go index 39fc7dcad..ad440e244 100644 --- a/weed/topology/topology.go +++ b/weed/topology/topology.go @@ -3,6 +3,7 @@ package topology import ( "errors" "fmt" + "github.com/chrislusf/seaweedfs/weed/pb" "github.com/chrislusf/seaweedfs/weed/storage/types" "math/rand" "sync" @@ -71,7 +72,7 @@ func (t *Topology) IsLeader() bool { return true } if leader, err := t.Leader(); err == nil { - if t.RaftServer.Name() == leader { + if pb.ServerAddress(t.RaftServer.Name()) == leader { return true } } @@ -79,11 +80,11 @@ func (t *Topology) IsLeader() bool { return false } -func (t *Topology) Leader() (string, error) { - l := "" +func (t *Topology) Leader() (pb.ServerAddress, error) { + var l pb.ServerAddress for count := 0; count < 3; count++ { if t.RaftServer != nil { - l = t.RaftServer.Leader() + l = pb.ServerAddress(t.RaftServer.Leader()) } else { return "", errors.New("Raft Server not ready yet!") } diff --git a/weed/topology/topology_ec.go b/weed/topology/topology_ec.go index 022eeb578..fdc4f274e 100644 --- a/weed/topology/topology_ec.go +++ b/weed/topology/topology_ec.go @@ -2,6 +2,7 @@ package topology import ( "github.com/chrislusf/seaweedfs/weed/glog" + "github.com/chrislusf/seaweedfs/weed/pb" "github.com/chrislusf/seaweedfs/weed/pb/master_pb" "github.com/chrislusf/seaweedfs/weed/storage/erasure_coding" "github.com/chrislusf/seaweedfs/weed/storage/needle" @@ -135,16 +136,16 @@ func (t *Topology) LookupEcShards(vid needle.VolumeId) (locations *EcShardLocati return } -func (t *Topology) ListEcServersByCollection(collection string) (dataNodes []string) { +func (t *Topology) ListEcServersByCollection(collection string) (dataNodes []pb.ServerAddress) { t.ecShardMapLock.RLock() defer t.ecShardMapLock.RUnlock() - dateNodeMap := make(map[string]bool) + dateNodeMap := make(map[pb.ServerAddress]bool) for _, ecVolumeLocation := range t.ecShardMap { if ecVolumeLocation.Collection == collection { for _, locations := range ecVolumeLocation.Locations { for _, loc := range locations { - dateNodeMap[string(loc.Id())] = true + dateNodeMap[loc.ServerAddress()] = true } } } diff --git a/weed/topology/topology_test.go b/weed/topology/topology_test.go index ecfe9d8d1..bfe7faff7 100644 --- a/weed/topology/topology_test.go +++ b/weed/topology/topology_test.go @@ -31,7 +31,7 @@ func TestHandlingVolumeServerHeartbeat(t *testing.T) { maxVolumeCounts := make(map[string]uint32) maxVolumeCounts[""] = 25 maxVolumeCounts["ssd"] = 12 - dn := rack.GetOrCreateDataNode("127.0.0.1", 34534, "127.0.0.1", maxVolumeCounts) + dn := rack.GetOrCreateDataNode("127.0.0.1", 34534, 0,"127.0.0.1", maxVolumeCounts) { volumeCount := 7 @@ -177,7 +177,7 @@ func TestAddRemoveVolume(t *testing.T) { maxVolumeCounts := make(map[string]uint32) maxVolumeCounts[""] = 25 maxVolumeCounts["ssd"] = 12 - dn := rack.GetOrCreateDataNode("127.0.0.1", 34534, "127.0.0.1", maxVolumeCounts) + dn := rack.GetOrCreateDataNode("127.0.0.1", 34534, 0,"127.0.0.1", maxVolumeCounts) v := storage.VolumeInfo{ Id: needle.VolumeId(1), diff --git a/weed/topology/topology_vacuum.go b/weed/topology/topology_vacuum.go index 9feb55b73..03340c17f 100644 --- a/weed/topology/topology_vacuum.go +++ b/weed/topology/topology_vacuum.go @@ -2,6 +2,7 @@ package topology import ( "context" + "github.com/chrislusf/seaweedfs/weed/pb" "sync/atomic" "time" @@ -19,7 +20,7 @@ func (t *Topology) batchVacuumVolumeCheck(grpcDialOption grpc.DialOption, vid ne ch := make(chan int, locationlist.Length()) errCount := int32(0) for index, dn := range locationlist.list { - go func(index int, url string, vid needle.VolumeId) { + go func(index int, url pb.ServerAddress, vid needle.VolumeId) { err := operation.WithVolumeServerClient(url, grpcDialOption, func(volumeServerClient volume_server_pb.VolumeServerClient) error { resp, err := volumeServerClient.VacuumVolumeCheck(context.Background(), &volume_server_pb.VacuumVolumeCheckRequest{ VolumeId: uint32(vid), @@ -39,7 +40,7 @@ func (t *Topology) batchVacuumVolumeCheck(grpcDialOption grpc.DialOption, vid ne if err != nil { glog.V(0).Infof("Checking vacuuming %d on %s: %v", vid, url, err) } - }(index, dn.Url(), vid) + }(index, dn.ServerAddress(), vid) } vacuumLocationList := NewVolumeLocationList() @@ -66,7 +67,7 @@ func (t *Topology) batchVacuumVolumeCompact(grpcDialOption grpc.DialOption, vl * ch := make(chan bool, locationlist.Length()) for index, dn := range locationlist.list { - go func(index int, url string, vid needle.VolumeId) { + go func(index int, url pb.ServerAddress, vid needle.VolumeId) { glog.V(0).Infoln(index, "Start vacuuming", vid, "on", url) err := operation.WithVolumeServerClient(url, grpcDialOption, func(volumeServerClient volume_server_pb.VolumeServerClient) error { _, err := volumeServerClient.VacuumVolumeCompact(context.Background(), &volume_server_pb.VacuumVolumeCompactRequest{ @@ -82,7 +83,7 @@ func (t *Topology) batchVacuumVolumeCompact(grpcDialOption grpc.DialOption, vl * glog.V(0).Infof("Complete vacuuming %d on %s", vid, url) ch <- true } - }(index, dn.Url(), vid) + }(index, dn.ServerAddress(), vid) } isVacuumSuccess := true @@ -104,7 +105,7 @@ func (t *Topology) batchVacuumVolumeCommit(grpcDialOption grpc.DialOption, vl *V isReadOnly := false for _, dn := range locationlist.list { glog.V(0).Infoln("Start Committing vacuum", vid, "on", dn.Url()) - err := operation.WithVolumeServerClient(dn.Url(), grpcDialOption, func(volumeServerClient volume_server_pb.VolumeServerClient) error { + err := operation.WithVolumeServerClient(dn.ServerAddress(), grpcDialOption, func(volumeServerClient volume_server_pb.VolumeServerClient) error { resp, err := volumeServerClient.VacuumVolumeCommit(context.Background(), &volume_server_pb.VacuumVolumeCommitRequest{ VolumeId: uint32(vid), }) @@ -130,7 +131,7 @@ func (t *Topology) batchVacuumVolumeCommit(grpcDialOption grpc.DialOption, vl *V func (t *Topology) batchVacuumVolumeCleanup(grpcDialOption grpc.DialOption, vl *VolumeLayout, vid needle.VolumeId, locationlist *VolumeLocationList) { for _, dn := range locationlist.list { glog.V(0).Infoln("Start cleaning up", vid, "on", dn.Url()) - err := operation.WithVolumeServerClient(dn.Url(), grpcDialOption, func(volumeServerClient volume_server_pb.VolumeServerClient) error { + err := operation.WithVolumeServerClient(dn.ServerAddress(), grpcDialOption, func(volumeServerClient volume_server_pb.VolumeServerClient) error { _, err := volumeServerClient.VacuumVolumeCleanup(context.Background(), &volume_server_pb.VacuumVolumeCleanupRequest{ VolumeId: uint32(vid), }) diff --git a/weed/wdclient/masterclient.go b/weed/wdclient/masterclient.go index d76ae31e2..9565b2795 100644 --- a/weed/wdclient/masterclient.go +++ b/weed/wdclient/masterclient.go @@ -15,27 +15,25 @@ import ( type MasterClient struct { clientType string - clientHost string - grpcPort uint32 - currentMaster string - masters []string + clientHost pb.ServerAddress + currentMaster pb.ServerAddress + masters []pb.ServerAddress grpcDialOption grpc.DialOption vidMap } -func NewMasterClient(grpcDialOption grpc.DialOption, clientType string, clientHost string, clientGrpcPort uint32, clientDataCenter string, masters []string) *MasterClient { +func NewMasterClient(grpcDialOption grpc.DialOption, clientType string, clientHost pb.ServerAddress, clientDataCenter string, masters []pb.ServerAddress) *MasterClient { return &MasterClient{ clientType: clientType, clientHost: clientHost, - grpcPort: clientGrpcPort, masters: masters, grpcDialOption: grpcDialOption, vidMap: newVidMap(clientDataCenter), } } -func (mc *MasterClient) GetMaster() string { +func (mc *MasterClient) GetMaster() pb.ServerAddress { mc.WaitUntilConnected() return mc.currentMaster } @@ -54,7 +52,7 @@ func (mc *MasterClient) KeepConnectedToMaster() { } } -func (mc *MasterClient) FindLeaderFromOtherPeers(myMasterAddress string) (leader string) { +func (mc *MasterClient) FindLeaderFromOtherPeers(myMasterAddress pb.ServerAddress) (leader string) { for _, master := range mc.masters { if master == myMasterAddress { continue @@ -81,7 +79,7 @@ func (mc *MasterClient) FindLeaderFromOtherPeers(myMasterAddress string) (leader } func (mc *MasterClient) tryAllMasters() { - nextHintedLeader := "" + var nextHintedLeader pb.ServerAddress for _, master := range mc.masters { nextHintedLeader = mc.tryConnectToMaster(master) @@ -94,8 +92,8 @@ func (mc *MasterClient) tryAllMasters() { } } -func (mc *MasterClient) tryConnectToMaster(master string) (nextHintedLeader string) { - glog.V(1).Infof("%s masterClient Connecting to master %v", mc.clientType, master) +func (mc *MasterClient) tryConnectToMaster(master pb.ServerAddress) (nextHintedLeader pb.ServerAddress) { + glog.V(0).Infof("%s masterClient Connecting to master %v", mc.clientType, master) gprcErr := pb.WithMasterClient(master, mc.grpcDialOption, func(client master_pb.SeaweedClient) error { ctx, cancel := context.WithCancel(context.Background()) @@ -107,7 +105,7 @@ func (mc *MasterClient) tryConnectToMaster(master string) (nextHintedLeader stri return err } - if err = stream.Send(&master_pb.KeepConnectedRequest{Name: mc.clientType, GrpcPort: mc.grpcPort}); err != nil { + if err = stream.Send(&master_pb.KeepConnectedRequest{Name: mc.clientType, ClientAddress: string(mc.clientHost)}); err != nil { glog.V(0).Infof("%s masterClient failed to send to %s: %v", mc.clientType, master, err) return err } @@ -125,7 +123,7 @@ func (mc *MasterClient) tryConnectToMaster(master string) (nextHintedLeader stri // maybe the leader is changed if volumeLocation.Leader != "" { glog.V(0).Infof("redirected to leader %v", volumeLocation.Leader) - nextHintedLeader = volumeLocation.Leader + nextHintedLeader = pb.ServerAddress(volumeLocation.Leader) return nil } @@ -134,6 +132,7 @@ func (mc *MasterClient) tryConnectToMaster(master string) (nextHintedLeader stri Url: volumeLocation.Url, PublicUrl: volumeLocation.PublicUrl, DataCenter: volumeLocation.DataCenter, + GrpcPort: int(volumeLocation.GrpcPort), } for _, newVid := range volumeLocation.NewVids { glog.V(1).Infof("%s: %s masterClient adds volume %d", mc.clientType, loc.Url, newVid) diff --git a/weed/wdclient/vid_map.go b/weed/wdclient/vid_map.go index 271baa132..670d1ad77 100644 --- a/weed/wdclient/vid_map.go +++ b/weed/wdclient/vid_map.go @@ -3,6 +3,7 @@ package wdclient import ( "errors" "fmt" + "github.com/chrislusf/seaweedfs/weed/pb" "strconv" "strings" "sync" @@ -25,6 +26,11 @@ type Location struct { Url string `json:"url,omitempty"` PublicUrl string `json:"publicUrl,omitempty"` DataCenter string `json:"dataCenter,omitempty"` + GrpcPort int `json:"grpcPort,omitempty"` +} + +func (l Location) ServerAddress() pb.ServerAddress { + return pb.NewServerAddressWithGrpcPort(l.Url, l.GrpcPort) } type vidMap struct { From e9760f261e58ebfa1c1510d1d1d00c83197fa37b Mon Sep 17 00:00:00 2001 From: Chris Lu Date: Mon, 13 Sep 2021 00:31:06 -0700 Subject: [PATCH 018/130] minor --- weed/server/filer_server_handlers_write_autochunk.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/weed/server/filer_server_handlers_write_autochunk.go b/weed/server/filer_server_handlers_write_autochunk.go index 6323d1589..872d83fca 100644 --- a/weed/server/filer_server_handlers_write_autochunk.go +++ b/weed/server/filer_server_handlers_write_autochunk.go @@ -62,7 +62,8 @@ func (fs *FilerServer) autoChunk(ctx context.Context, w http.ResponseWriter, r * } } else if reply != nil { if len(md5bytes) > 0 { - w.Header().Set("Content-MD5", util.Base64Encode(md5bytes)) + md5InBase64 := util.Base64Encode(md5bytes) + w.Header().Set("Content-MD5", md5InBase64) } writeJsonQuiet(w, r, http.StatusCreated, reply) } From f74b29416a95adfa2ed1aafcee34125dcdb48737 Mon Sep 17 00:00:00 2001 From: Chris Lu Date: Mon, 13 Sep 2021 00:31:46 -0700 Subject: [PATCH 019/130] better etag matching --- weed/server/filer_server_handlers_read.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/weed/server/filer_server_handlers_read.go b/weed/server/filer_server_handlers_read.go index 054a1bd00..613507d35 100644 --- a/weed/server/filer_server_handlers_read.go +++ b/weed/server/filer_server_handlers_read.go @@ -64,7 +64,7 @@ func (fs *FilerServer) GetOrHeadHandler(w http.ResponseWriter, r *http.Request) // set etag etag := filer.ETagEntry(entry) - if ifm := r.Header.Get("If-Match"); ifm != "" && ifm != "\""+etag+"\"" { + if ifm := r.Header.Get("If-Match"); ifm != "" && (ifm != "\""+etag+"\"" && ifm != etag){ w.WriteHeader(http.StatusPreconditionFailed) return } From 6cd1ce8b749a5befc0af67feaced94000f6fb7bf Mon Sep 17 00:00:00 2001 From: Chris Lu Date: Mon, 13 Sep 2021 01:55:49 -0700 Subject: [PATCH 020/130] erasure coding: add cleanup step if anything goes wrong --- weed/shell/command_ec_encode.go | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/weed/shell/command_ec_encode.go b/weed/shell/command_ec_encode.go index 39ca39a4f..fd01ac700 100644 --- a/weed/shell/command_ec_encode.go +++ b/weed/shell/command_ec_encode.go @@ -203,6 +203,14 @@ func parallelCopyEcShardsFromSource(grpcDialOption grpc.DialOption, targetServer server.addEcVolumeShards(volumeId, collection, copiedShardIds) } } + cleanupFunc := func(server *EcNode, allocatedEcShardIds []uint32) { + if err := unmountEcShards(grpcDialOption, volumeId, pb.NewServerAddressFromDataNode(server.info), allocatedEcShardIds); err != nil { + fmt.Printf("unmount aborted shards %d.%v on %s: %v\n", volumeId, allocatedEcShardIds, server.info.Id, err) + } + if err := sourceServerDeleteEcShards(grpcDialOption, collection, volumeId, pb.NewServerAddressFromDataNode(server.info), allocatedEcShardIds); err != nil { + fmt.Printf("remove aborted shards %d.%v on %s: %v\n", volumeId, allocatedEcShardIds, server.info.Id, err) + } + } // maybe parallelize for i, server := range targetServers { @@ -221,6 +229,12 @@ func parallelCopyEcShardsFromSource(grpcDialOption grpc.DialOption, targetServer close(shardIdChan) if err != nil { + for i, server := range targetServers { + if len(allocatedEcIds[i]) <= 0 { + continue + } + cleanupFunc(server, allocatedEcIds[i]) + } return nil, err } From 20ac710ceb221a9172f5e4868f045ae210c0262a Mon Sep 17 00:00:00 2001 From: Chris Lu Date: Mon, 13 Sep 2021 02:16:09 -0700 Subject: [PATCH 021/130] 2.68 --- k8s/helm_charts2/Chart.yaml | 4 ++-- other/java/client/pom.xml | 4 ++-- other/java/client/pom.xml.deploy | 4 ++-- other/java/client/pom_debug.xml | 2 +- .../client/src/main/java/seaweedfs/client/SeaweedWrite.java | 2 +- other/java/examples/pom.xml | 4 ++-- other/java/hdfs2/dependency-reduced-pom.xml | 4 ++-- other/java/hdfs2/pom.xml | 4 ++-- other/java/hdfs3/dependency-reduced-pom.xml | 4 ++-- other/java/hdfs3/pom.xml | 4 ++-- weed/util/constants.go | 2 +- 11 files changed, 19 insertions(+), 19 deletions(-) diff --git a/k8s/helm_charts2/Chart.yaml b/k8s/helm_charts2/Chart.yaml index de766aa04..c2a8158c9 100644 --- a/k8s/helm_charts2/Chart.yaml +++ b/k8s/helm_charts2/Chart.yaml @@ -1,5 +1,5 @@ apiVersion: v1 description: SeaweedFS name: seaweedfs -appVersion: "2.67" -version: "2.67" +appVersion: "2.68" +version: "2.68" diff --git a/other/java/client/pom.xml b/other/java/client/pom.xml index 70c5dbd31..e9e99e113 100644 --- a/other/java/client/pom.xml +++ b/other/java/client/pom.xml @@ -5,7 +5,7 @@ com.github.chrislusf seaweedfs-client - 1.6.7 + 1.6.8 org.sonatype.oss @@ -135,7 +135,7 @@ org.sonatype.plugins nexus-staging-maven-plugin - 1.6.7 + 1.6.8 true ossrh diff --git a/other/java/client/pom.xml.deploy b/other/java/client/pom.xml.deploy index 82cf5e82b..529bb2e7f 100644 --- a/other/java/client/pom.xml.deploy +++ b/other/java/client/pom.xml.deploy @@ -5,7 +5,7 @@ com.github.chrislusf seaweedfs-client - 1.6.7 + 1.6.8 org.sonatype.oss @@ -130,7 +130,7 @@ org.sonatype.plugins nexus-staging-maven-plugin - 1.6.7 + 1.6.8 true ossrh diff --git a/other/java/client/pom_debug.xml b/other/java/client/pom_debug.xml index c72c81ab7..2755a98a9 100644 --- a/other/java/client/pom_debug.xml +++ b/other/java/client/pom_debug.xml @@ -5,7 +5,7 @@ com.github.chrislusf seaweedfs-client - 1.6.7 + 1.6.8 org.sonatype.oss diff --git a/other/java/client/src/main/java/seaweedfs/client/SeaweedWrite.java b/other/java/client/src/main/java/seaweedfs/client/SeaweedWrite.java index 3f9d79b99..db7f1a19c 100644 --- a/other/java/client/src/main/java/seaweedfs/client/SeaweedWrite.java +++ b/other/java/client/src/main/java/seaweedfs/client/SeaweedWrite.java @@ -59,7 +59,7 @@ public class SeaweedWrite { String fileId = response.getFileId(); String auth = response.getAuth(); - String targetUrl = filerClient.getChunkUrl(fileId, response.getUrl(), response.getPublicUrl()); + String targetUrl = filerClient.getChunkUrl(fileId, response.getLocation().getUrl(), response.getLocation().getPublicUrl()); ByteString cipherKeyString = com.google.protobuf.ByteString.EMPTY; byte[] cipherKey = null; diff --git a/other/java/examples/pom.xml b/other/java/examples/pom.xml index 26c9bdfdc..89f41acc4 100644 --- a/other/java/examples/pom.xml +++ b/other/java/examples/pom.xml @@ -11,13 +11,13 @@ com.github.chrislusf seaweedfs-client - 1.6.7 + 1.6.8 compile com.github.chrislusf seaweedfs-hadoop2-client - 1.6.7 + 1.6.8 compile diff --git a/other/java/hdfs2/dependency-reduced-pom.xml b/other/java/hdfs2/dependency-reduced-pom.xml index bd31637ce..8497bdfef 100644 --- a/other/java/hdfs2/dependency-reduced-pom.xml +++ b/other/java/hdfs2/dependency-reduced-pom.xml @@ -86,7 +86,7 @@ org.sonatype.plugins nexus-staging-maven-plugin - 1.6.7 + 1.6.8 true ossrh @@ -301,7 +301,7 @@ - 1.6.7 + 1.6.8 2.9.2 diff --git a/other/java/hdfs2/pom.xml b/other/java/hdfs2/pom.xml index f15d24faa..fe348f34f 100644 --- a/other/java/hdfs2/pom.xml +++ b/other/java/hdfs2/pom.xml @@ -5,7 +5,7 @@ 4.0.0 - 1.6.7 + 1.6.8 2.9.2 @@ -105,7 +105,7 @@ org.sonatype.plugins nexus-staging-maven-plugin - 1.6.7 + 1.6.8 true ossrh diff --git a/other/java/hdfs3/dependency-reduced-pom.xml b/other/java/hdfs3/dependency-reduced-pom.xml index 4640b5a84..eea4b0b83 100644 --- a/other/java/hdfs3/dependency-reduced-pom.xml +++ b/other/java/hdfs3/dependency-reduced-pom.xml @@ -86,7 +86,7 @@ org.sonatype.plugins nexus-staging-maven-plugin - 1.6.7 + 1.6.8 true ossrh @@ -309,7 +309,7 @@ - 1.6.7 + 1.6.8 3.1.1 diff --git a/other/java/hdfs3/pom.xml b/other/java/hdfs3/pom.xml index efcc1e4c0..d734ff218 100644 --- a/other/java/hdfs3/pom.xml +++ b/other/java/hdfs3/pom.xml @@ -5,7 +5,7 @@ 4.0.0 - 1.6.7 + 1.6.8 3.1.1 @@ -105,7 +105,7 @@ org.sonatype.plugins nexus-staging-maven-plugin - 1.6.7 + 1.6.8 true ossrh diff --git a/weed/util/constants.go b/weed/util/constants.go index 852284e16..d1deb7bef 100644 --- a/weed/util/constants.go +++ b/weed/util/constants.go @@ -5,7 +5,7 @@ import ( ) var ( - VERSION_NUMBER = fmt.Sprintf("%.02f", 2.67) + VERSION_NUMBER = fmt.Sprintf("%.02f", 2.68) VERSION = sizeLimit + " " + VERSION_NUMBER COMMIT = "" ) From 96514f0f00f1ca6f0f253c1c4080a45887d09ad9 Mon Sep 17 00:00:00 2001 From: Chris Lu Date: Mon, 13 Sep 2021 02:19:48 -0700 Subject: [PATCH 022/130] fix tests --- weed/filer/leveldb/leveldb_store_test.go | 6 +++--- weed/filer/leveldb2/leveldb2_store_test.go | 4 ++-- weed/filer/leveldb3/leveldb3_store_test.go | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/weed/filer/leveldb/leveldb_store_test.go b/weed/filer/leveldb/leveldb_store_test.go index d437895f5..7149d84d2 100644 --- a/weed/filer/leveldb/leveldb_store_test.go +++ b/weed/filer/leveldb/leveldb_store_test.go @@ -13,7 +13,7 @@ import ( ) func TestCreateAndFind(t *testing.T) { - testFiler := filer.NewFiler(nil, nil, "", 0, "", "", "", nil) + testFiler := filer.NewFiler(nil, nil, "", "", "", "", nil) dir, _ := ioutil.TempDir("", "seaweedfs_filer_test") defer os.RemoveAll(dir) store := &LevelDBStore{} @@ -67,7 +67,7 @@ func TestCreateAndFind(t *testing.T) { } func TestEmptyRoot(t *testing.T) { - testFiler := filer.NewFiler(nil, nil, "", 0, "", "", "", nil) + testFiler := filer.NewFiler(nil, nil, "", "", "", "", nil) dir, _ := ioutil.TempDir("", "seaweedfs_filer_test2") defer os.RemoveAll(dir) store := &LevelDBStore{} @@ -90,7 +90,7 @@ func TestEmptyRoot(t *testing.T) { } func BenchmarkInsertEntry(b *testing.B) { - testFiler := filer.NewFiler(nil, nil, "", 0, "", "", "", nil) + testFiler := filer.NewFiler(nil, nil, "", "", "", "", nil) dir, _ := ioutil.TempDir("", "seaweedfs_filer_bench") defer os.RemoveAll(dir) store := &LevelDBStore{} diff --git a/weed/filer/leveldb2/leveldb2_store_test.go b/weed/filer/leveldb2/leveldb2_store_test.go index fd0ad18a3..9564feaab 100644 --- a/weed/filer/leveldb2/leveldb2_store_test.go +++ b/weed/filer/leveldb2/leveldb2_store_test.go @@ -11,7 +11,7 @@ import ( ) func TestCreateAndFind(t *testing.T) { - testFiler := filer.NewFiler(nil, nil, "", 0, "", "", "", nil) + testFiler := filer.NewFiler(nil, nil, "", "", "", "", nil) dir, _ := ioutil.TempDir("", "seaweedfs_filer_test") defer os.RemoveAll(dir) store := &LevelDB2Store{} @@ -65,7 +65,7 @@ func TestCreateAndFind(t *testing.T) { } func TestEmptyRoot(t *testing.T) { - testFiler := filer.NewFiler(nil, nil, "", 0, "", "", "", nil) + testFiler := filer.NewFiler(nil, nil, "", "", "", "", nil) dir, _ := ioutil.TempDir("", "seaweedfs_filer_test2") defer os.RemoveAll(dir) store := &LevelDB2Store{} diff --git a/weed/filer/leveldb3/leveldb3_store_test.go b/weed/filer/leveldb3/leveldb3_store_test.go index 0b970a539..6e7acf51c 100644 --- a/weed/filer/leveldb3/leveldb3_store_test.go +++ b/weed/filer/leveldb3/leveldb3_store_test.go @@ -11,7 +11,7 @@ import ( ) func TestCreateAndFind(t *testing.T) { - testFiler := filer.NewFiler(nil, nil, "", 0, "", "", "", nil) + testFiler := filer.NewFiler(nil, nil, "", "", "", "", nil) dir, _ := ioutil.TempDir("", "seaweedfs_filer_test") defer os.RemoveAll(dir) store := &LevelDB3Store{} @@ -65,7 +65,7 @@ func TestCreateAndFind(t *testing.T) { } func TestEmptyRoot(t *testing.T) { - testFiler := filer.NewFiler(nil, nil, "", 0, "", "", "", nil) + testFiler := filer.NewFiler(nil, nil, "", "", "", "", nil) dir, _ := ioutil.TempDir("", "seaweedfs_filer_test2") defer os.RemoveAll(dir) store := &LevelDB3Store{} From 7504be58f9fc7683869e164bc710b3e83da12e2f Mon Sep 17 00:00:00 2001 From: Chris Lu Date: Mon, 13 Sep 2021 04:00:57 -0700 Subject: [PATCH 023/130] Avoid xattr printed out as HTTP headers fix https://github.com/chrislusf/seaweedfs/issues/2336 --- weed/filesys/xattr.go | 19 +++++++++++++------ weed/server/filer_server_handlers_read.go | 5 ++++- 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/weed/filesys/xattr.go b/weed/filesys/xattr.go index 4b2ee0064..6021fd8ec 100644 --- a/weed/filesys/xattr.go +++ b/weed/filesys/xattr.go @@ -2,6 +2,7 @@ package filesys import ( "context" + "strings" "github.com/seaweedfs/fuse" @@ -10,6 +11,10 @@ import ( "github.com/chrislusf/seaweedfs/weed/util" ) +const ( + XATTR_PREFIX = "xattr-" // same as filer +) + func getxattr(entry *filer_pb.Entry, req *fuse.GetxattrRequest, resp *fuse.GetxattrResponse) error { if entry == nil { @@ -18,7 +23,7 @@ func getxattr(entry *filer_pb.Entry, req *fuse.GetxattrRequest, resp *fuse.Getxa if entry.Extended == nil { return fuse.ErrNoXattr } - data, found := entry.Extended[req.Name] + data, found := entry.Extended[XATTR_PREFIX+req.Name] if !found { return fuse.ErrNoXattr } @@ -47,7 +52,7 @@ func setxattr(entry *filer_pb.Entry, req *fuse.SetxattrRequest) error { if entry.Extended == nil { entry.Extended = make(map[string][]byte) } - data, _ := entry.Extended[req.Name] + data, _ := entry.Extended[XATTR_PREFIX+req.Name] newData := make([]byte, int(req.Position)+len(req.Xattr)) @@ -55,7 +60,7 @@ func setxattr(entry *filer_pb.Entry, req *fuse.SetxattrRequest) error { copy(newData[int(req.Position):], req.Xattr) - entry.Extended[req.Name] = newData + entry.Extended[XATTR_PREFIX+req.Name] = newData return nil @@ -71,13 +76,13 @@ func removexattr(entry *filer_pb.Entry, req *fuse.RemovexattrRequest) error { return fuse.ErrNoXattr } - _, found := entry.Extended[req.Name] + _, found := entry.Extended[XATTR_PREFIX+req.Name] if !found { return fuse.ErrNoXattr } - delete(entry.Extended, req.Name) + delete(entry.Extended, XATTR_PREFIX+req.Name) return nil @@ -90,7 +95,9 @@ func listxattr(entry *filer_pb.Entry, req *fuse.ListxattrRequest, resp *fuse.Lis } for k := range entry.Extended { - resp.Append(k) + if strings.HasPrefix(k, XATTR_PREFIX) { + resp.Append(k[len(XATTR_PREFIX):]) + } } size := req.Size diff --git a/weed/server/filer_server_handlers_read.go b/weed/server/filer_server_handlers_read.go index 613507d35..3e0d32189 100644 --- a/weed/server/filer_server_handlers_read.go +++ b/weed/server/filer_server_handlers_read.go @@ -97,7 +97,10 @@ func (fs *FilerServer) GetOrHeadHandler(w http.ResponseWriter, r *http.Request) // print out the header from extended properties for k, v := range entry.Extended { - w.Header().Set(k, string(v)) + if !strings.HasPrefix(k, "xattr-") { + // "xattr-" prefix is set in filesys.XATTR_PREFIX + w.Header().Set(k, string(v)) + } } //Seaweed custom header are not visible to Vue or javascript From 119d5908dd52b159c275bad10007daaf62188df0 Mon Sep 17 00:00:00 2001 From: Chris Lu Date: Mon, 13 Sep 2021 22:13:34 -0700 Subject: [PATCH 024/130] shell: do not need to lock to see volume -h --- weed/shell/command_collection_delete.go | 8 ++++---- weed/shell/command_ec_balance.go | 8 ++++---- weed/shell/command_ec_decode.go | 9 ++++----- weed/shell/command_ec_encode.go | 8 ++++---- weed/shell/command_ec_rebuild.go | 8 ++++---- weed/shell/command_volume_balance.go | 8 ++++---- weed/shell/command_volume_check_disk.go | 8 ++++---- weed/shell/command_volume_configure_replication.go | 8 ++++---- weed/shell/command_volume_copy.go | 8 ++++---- weed/shell/command_volume_delete.go | 8 ++++---- weed/shell/command_volume_delete_empty.go | 8 ++++---- weed/shell/command_volume_fix_replication.go | 8 ++++---- weed/shell/command_volume_fsck.go | 8 ++++---- weed/shell/command_volume_mark.go | 8 ++++---- weed/shell/command_volume_mount.go | 8 ++++---- weed/shell/command_volume_move.go | 8 ++++---- weed/shell/command_volume_server_evacuate.go | 8 ++++---- weed/shell/command_volume_server_leave.go | 8 ++++---- weed/shell/command_volume_tier_download.go | 8 ++++---- weed/shell/command_volume_tier_move.go | 8 ++++---- weed/shell/command_volume_tier_upload.go | 8 ++++---- weed/shell/command_volume_unmount.go | 8 ++++---- weed/shell/command_volume_vacuum.go | 8 ++++---- 23 files changed, 92 insertions(+), 93 deletions(-) diff --git a/weed/shell/command_collection_delete.go b/weed/shell/command_collection_delete.go index e43f2a093..da79b3437 100644 --- a/weed/shell/command_collection_delete.go +++ b/weed/shell/command_collection_delete.go @@ -29,10 +29,6 @@ func (c *commandCollectionDelete) Help() string { func (c *commandCollectionDelete) Do(args []string, commandEnv *CommandEnv, writer io.Writer) (err error) { - if err = commandEnv.confirmIsLocked(); err != nil { - return - } - colDeleteCommand := flag.NewFlagSet(c.Name(), flag.ContinueOnError) collectionName := colDeleteCommand.String("collection", "", "collection to delete. Use '_default_' for the empty-named collection.") applyBalancing := colDeleteCommand.Bool("force", false, "apply the collection") @@ -40,6 +36,10 @@ func (c *commandCollectionDelete) Do(args []string, commandEnv *CommandEnv, writ return nil } + if err = commandEnv.confirmIsLocked(); err != nil { + return + } + if *collectionName == "" { return fmt.Errorf("empty collection name is not allowed") } diff --git a/weed/shell/command_ec_balance.go b/weed/shell/command_ec_balance.go index d15c69d43..e5438b9c7 100644 --- a/weed/shell/command_ec_balance.go +++ b/weed/shell/command_ec_balance.go @@ -100,10 +100,6 @@ func (c *commandEcBalance) Help() string { func (c *commandEcBalance) Do(args []string, commandEnv *CommandEnv, writer io.Writer) (err error) { - if err = commandEnv.confirmIsLocked(); err != nil { - return - } - balanceCommand := flag.NewFlagSet(c.Name(), flag.ContinueOnError) collection := balanceCommand.String("collection", "EACH_COLLECTION", "collection name, or \"EACH_COLLECTION\" for each collection") dc := balanceCommand.String("dataCenter", "", "only apply the balancing for this dataCenter") @@ -112,6 +108,10 @@ func (c *commandEcBalance) Do(args []string, commandEnv *CommandEnv, writer io.W return nil } + if err = commandEnv.confirmIsLocked(); err != nil { + return + } + // collect all ec nodes allEcNodes, totalFreeEcSlots, err := collectEcNodes(commandEnv, *dc) if err != nil { diff --git a/weed/shell/command_ec_decode.go b/weed/shell/command_ec_decode.go index 68b7820b2..18cea0504 100644 --- a/weed/shell/command_ec_decode.go +++ b/weed/shell/command_ec_decode.go @@ -37,11 +37,6 @@ func (c *commandEcDecode) Help() string { } func (c *commandEcDecode) Do(args []string, commandEnv *CommandEnv, writer io.Writer) (err error) { - - if err = commandEnv.confirmIsLocked(); err != nil { - return - } - encodeCommand := flag.NewFlagSet(c.Name(), flag.ContinueOnError) volumeId := encodeCommand.Int("volumeId", 0, "the volume id") collection := encodeCommand.String("collection", "", "the collection name") @@ -49,6 +44,10 @@ func (c *commandEcDecode) Do(args []string, commandEnv *CommandEnv, writer io.Wr return nil } + if err = commandEnv.confirmIsLocked(); err != nil { + return + } + vid := needle.VolumeId(*volumeId) // collect topology information diff --git a/weed/shell/command_ec_encode.go b/weed/shell/command_ec_encode.go index fd01ac700..33eef9ca7 100644 --- a/weed/shell/command_ec_encode.go +++ b/weed/shell/command_ec_encode.go @@ -55,10 +55,6 @@ func (c *commandEcEncode) Help() string { func (c *commandEcEncode) Do(args []string, commandEnv *CommandEnv, writer io.Writer) (err error) { - if err = commandEnv.confirmIsLocked(); err != nil { - return - } - encodeCommand := flag.NewFlagSet(c.Name(), flag.ContinueOnError) volumeId := encodeCommand.Int("volumeId", 0, "the volume id") collection := encodeCommand.String("collection", "", "the collection name") @@ -69,6 +65,10 @@ func (c *commandEcEncode) Do(args []string, commandEnv *CommandEnv, writer io.Wr return nil } + if err = commandEnv.confirmIsLocked(); err != nil { + return + } + vid := needle.VolumeId(*volumeId) // volumeId is provided diff --git a/weed/shell/command_ec_rebuild.go b/weed/shell/command_ec_rebuild.go index 3bdb9b4a0..c3f72ea91 100644 --- a/weed/shell/command_ec_rebuild.go +++ b/weed/shell/command_ec_rebuild.go @@ -57,10 +57,6 @@ func (c *commandEcRebuild) Help() string { func (c *commandEcRebuild) Do(args []string, commandEnv *CommandEnv, writer io.Writer) (err error) { - if err = commandEnv.confirmIsLocked(); err != nil { - return - } - fixCommand := flag.NewFlagSet(c.Name(), flag.ContinueOnError) collection := fixCommand.String("collection", "EACH_COLLECTION", "collection name, or \"EACH_COLLECTION\" for each collection") applyChanges := fixCommand.Bool("force", false, "apply the changes") @@ -68,6 +64,10 @@ func (c *commandEcRebuild) Do(args []string, commandEnv *CommandEnv, writer io.W return nil } + if err = commandEnv.confirmIsLocked(); err != nil { + return + } + // collect all ec nodes allEcNodes, _, err := collectEcNodes(commandEnv, "") if err != nil { diff --git a/weed/shell/command_volume_balance.go b/weed/shell/command_volume_balance.go index 8098fabdf..72c4c6db5 100644 --- a/weed/shell/command_volume_balance.go +++ b/weed/shell/command_volume_balance.go @@ -63,10 +63,6 @@ func (c *commandVolumeBalance) Help() string { func (c *commandVolumeBalance) Do(args []string, commandEnv *CommandEnv, writer io.Writer) (err error) { - if err = commandEnv.confirmIsLocked(); err != nil { - return - } - balanceCommand := flag.NewFlagSet(c.Name(), flag.ContinueOnError) collection := balanceCommand.String("collection", "EACH_COLLECTION", "collection name, or use \"ALL_COLLECTIONS\" across collections, \"EACH_COLLECTION\" for each collection") dc := balanceCommand.String("dataCenter", "", "only apply the balancing for this dataCenter") @@ -75,6 +71,10 @@ func (c *commandVolumeBalance) Do(args []string, commandEnv *CommandEnv, writer return nil } + if err = commandEnv.confirmIsLocked(); err != nil { + return + } + // collect topology information topologyInfo, volumeSizeLimitMb, err := collectTopologyInfo(commandEnv) if err != nil { diff --git a/weed/shell/command_volume_check_disk.go b/weed/shell/command_volume_check_disk.go index a7343e258..bcc889136 100644 --- a/weed/shell/command_volume_check_disk.go +++ b/weed/shell/command_volume_check_disk.go @@ -42,10 +42,6 @@ func (c *commandVolumeCheckDisk) Help() string { func (c *commandVolumeCheckDisk) Do(args []string, commandEnv *CommandEnv, writer io.Writer) (err error) { - if err = commandEnv.confirmIsLocked(); err != nil { - return - } - fsckCommand := flag.NewFlagSet(c.Name(), flag.ContinueOnError) slowMode := fsckCommand.Bool("slow", false, "slow mode checks all replicas even file counts are the same") verbose := fsckCommand.Bool("v", false, "verbose mode") @@ -55,6 +51,10 @@ func (c *commandVolumeCheckDisk) Do(args []string, commandEnv *CommandEnv, write return nil } + if err = commandEnv.confirmIsLocked(); err != nil { + return + } + c.env = commandEnv // collect topology information diff --git a/weed/shell/command_volume_configure_replication.go b/weed/shell/command_volume_configure_replication.go index ffc2656ea..5640e58bb 100644 --- a/weed/shell/command_volume_configure_replication.go +++ b/weed/shell/command_volume_configure_replication.go @@ -36,10 +36,6 @@ func (c *commandVolumeConfigureReplication) Help() string { func (c *commandVolumeConfigureReplication) Do(args []string, commandEnv *CommandEnv, writer io.Writer) (err error) { - if err = commandEnv.confirmIsLocked(); err != nil { - return - } - configureReplicationCommand := flag.NewFlagSet(c.Name(), flag.ContinueOnError) volumeIdInt := configureReplicationCommand.Int("volumeId", 0, "the volume id") replicationString := configureReplicationCommand.String("replication", "", "the intended replication value") @@ -47,6 +43,10 @@ func (c *commandVolumeConfigureReplication) Do(args []string, commandEnv *Comman return nil } + if err = commandEnv.confirmIsLocked(); err != nil { + return + } + if *replicationString == "" { return fmt.Errorf("empty replication value") } diff --git a/weed/shell/command_volume_copy.go b/weed/shell/command_volume_copy.go index ca8f5c832..8d34acf8f 100644 --- a/weed/shell/command_volume_copy.go +++ b/weed/shell/command_volume_copy.go @@ -33,10 +33,6 @@ func (c *commandVolumeCopy) Help() string { func (c *commandVolumeCopy) Do(args []string, commandEnv *CommandEnv, writer io.Writer) (err error) { - if err = commandEnv.confirmIsLocked(); err != nil { - return - } - volCopyCommand := flag.NewFlagSet(c.Name(), flag.ContinueOnError) volumeIdInt := volCopyCommand.Int("volumeId", 0, "the volume id") sourceNodeStr := volCopyCommand.String("source", "", "the source volume server :") @@ -45,6 +41,10 @@ func (c *commandVolumeCopy) Do(args []string, commandEnv *CommandEnv, writer io. return nil } + if err = commandEnv.confirmIsLocked(); err != nil { + return + } + sourceVolumeServer, targetVolumeServer := pb.ServerAddress(*sourceNodeStr), pb.ServerAddress(*targetNodeStr) volumeId := needle.VolumeId(*volumeIdInt) diff --git a/weed/shell/command_volume_delete.go b/weed/shell/command_volume_delete.go index 4297b669b..135ad7285 100644 --- a/weed/shell/command_volume_delete.go +++ b/weed/shell/command_volume_delete.go @@ -31,10 +31,6 @@ func (c *commandVolumeDelete) Help() string { func (c *commandVolumeDelete) Do(args []string, commandEnv *CommandEnv, writer io.Writer) (err error) { - if err = commandEnv.confirmIsLocked(); err != nil { - return - } - volDeleteCommand := flag.NewFlagSet(c.Name(), flag.ContinueOnError) volumeIdInt := volDeleteCommand.Int("volumeId", 0, "the volume id") nodeStr := volDeleteCommand.String("node", "", "the volume server :") @@ -42,6 +38,10 @@ func (c *commandVolumeDelete) Do(args []string, commandEnv *CommandEnv, writer i return nil } + if err = commandEnv.confirmIsLocked(); err != nil { + return + } + sourceVolumeServer := pb.ServerAddress(*nodeStr) volumeId := needle.VolumeId(*volumeIdInt) diff --git a/weed/shell/command_volume_delete_empty.go b/weed/shell/command_volume_delete_empty.go index a20ac2f7e..fdc4fcf6a 100644 --- a/weed/shell/command_volume_delete_empty.go +++ b/weed/shell/command_volume_delete_empty.go @@ -33,10 +33,6 @@ func (c *commandVolumeDeleteEmpty) Help() string { func (c *commandVolumeDeleteEmpty) Do(args []string, commandEnv *CommandEnv, writer io.Writer) (err error) { - if err = commandEnv.confirmIsLocked(); err != nil { - return - } - volDeleteCommand := flag.NewFlagSet(c.Name(), flag.ContinueOnError) quietPeriod := volDeleteCommand.Duration("quietFor", 24*time.Hour, "select empty volumes with no recent writes, avoid newly created ones") applyBalancing := volDeleteCommand.Bool("force", false, "apply to delete empty volumes") @@ -44,6 +40,10 @@ func (c *commandVolumeDeleteEmpty) Do(args []string, commandEnv *CommandEnv, wri return nil } + if err = commandEnv.confirmIsLocked(); err != nil { + return + } + // collect topology information topologyInfo, _, err := collectTopologyInfo(commandEnv) if err != nil { diff --git a/weed/shell/command_volume_fix_replication.go b/weed/shell/command_volume_fix_replication.go index 06e9ac003..76a582b31 100644 --- a/weed/shell/command_volume_fix_replication.go +++ b/weed/shell/command_volume_fix_replication.go @@ -52,10 +52,6 @@ func (c *commandVolumeFixReplication) Help() string { func (c *commandVolumeFixReplication) Do(args []string, commandEnv *CommandEnv, writer io.Writer) (err error) { - if err = commandEnv.confirmIsLocked(); err != nil { - return - } - volFixReplicationCommand := flag.NewFlagSet(c.Name(), flag.ContinueOnError) c.collectionPattern = volFixReplicationCommand.String("collectionPattern", "", "match with wildcard characters '*' and '?'") skipChange := volFixReplicationCommand.Bool("n", false, "skip the changes") @@ -64,6 +60,10 @@ func (c *commandVolumeFixReplication) Do(args []string, commandEnv *CommandEnv, return nil } + if err = commandEnv.confirmIsLocked(); err != nil { + return + } + takeAction := !*skipChange // collect topology information diff --git a/weed/shell/command_volume_fsck.go b/weed/shell/command_volume_fsck.go index a2c3e9fd6..ce5ea66bf 100644 --- a/weed/shell/command_volume_fsck.go +++ b/weed/shell/command_volume_fsck.go @@ -58,10 +58,6 @@ func (c *commandVolumeFsck) Help() string { func (c *commandVolumeFsck) Do(args []string, commandEnv *CommandEnv, writer io.Writer) (err error) { - if err = commandEnv.confirmIsLocked(); err != nil { - return - } - fsckCommand := flag.NewFlagSet(c.Name(), flag.ContinueOnError) verbose := fsckCommand.Bool("v", false, "verbose mode") findMissingChunksInFiler := fsckCommand.Bool("findMissingChunksInFiler", false, "see \"help volume.fsck\"") @@ -71,6 +67,10 @@ func (c *commandVolumeFsck) Do(args []string, commandEnv *CommandEnv, writer io. return nil } + if err = commandEnv.confirmIsLocked(); err != nil { + return + } + c.env = commandEnv // create a temp folder diff --git a/weed/shell/command_volume_mark.go b/weed/shell/command_volume_mark.go index 5531f4e3d..7734ea9ce 100644 --- a/weed/shell/command_volume_mark.go +++ b/weed/shell/command_volume_mark.go @@ -29,10 +29,6 @@ func (c *commandVolumeMark) Help() string { func (c *commandVolumeMark) Do(args []string, commandEnv *CommandEnv, writer io.Writer) (err error) { - if err = commandEnv.confirmIsLocked(); err != nil { - return - } - volMarkCommand := flag.NewFlagSet(c.Name(), flag.ContinueOnError) volumeIdInt := volMarkCommand.Int("volumeId", 0, "the volume id") nodeStr := volMarkCommand.String("node", "", "the volume server :") @@ -48,6 +44,10 @@ func (c *commandVolumeMark) Do(args []string, commandEnv *CommandEnv, writer io. markWritable = true } + if err = commandEnv.confirmIsLocked(); err != nil { + return + } + sourceVolumeServer := pb.ServerAddress(*nodeStr) volumeId := needle.VolumeId(*volumeIdInt) diff --git a/weed/shell/command_volume_mount.go b/weed/shell/command_volume_mount.go index e46ef3683..385e43341 100644 --- a/weed/shell/command_volume_mount.go +++ b/weed/shell/command_volume_mount.go @@ -35,10 +35,6 @@ func (c *commandVolumeMount) Help() string { func (c *commandVolumeMount) Do(args []string, commandEnv *CommandEnv, writer io.Writer) (err error) { - if err = commandEnv.confirmIsLocked(); err != nil { - return - } - volMountCommand := flag.NewFlagSet(c.Name(), flag.ContinueOnError) volumeIdInt := volMountCommand.Int("volumeId", 0, "the volume id") nodeStr := volMountCommand.String("node", "", "the volume server :") @@ -46,6 +42,10 @@ func (c *commandVolumeMount) Do(args []string, commandEnv *CommandEnv, writer io return nil } + if err = commandEnv.confirmIsLocked(); err != nil { + return + } + sourceVolumeServer := pb.ServerAddress(*nodeStr) volumeId := needle.VolumeId(*volumeIdInt) diff --git a/weed/shell/command_volume_move.go b/weed/shell/command_volume_move.go index c404d8e30..675f65341 100644 --- a/weed/shell/command_volume_move.go +++ b/weed/shell/command_volume_move.go @@ -50,10 +50,6 @@ func (c *commandVolumeMove) Help() string { func (c *commandVolumeMove) Do(args []string, commandEnv *CommandEnv, writer io.Writer) (err error) { - if err = commandEnv.confirmIsLocked(); err != nil { - return - } - volMoveCommand := flag.NewFlagSet(c.Name(), flag.ContinueOnError) volumeIdInt := volMoveCommand.Int("volumeId", 0, "the volume id") sourceNodeStr := volMoveCommand.String("source", "", "the source volume server :") @@ -63,6 +59,10 @@ func (c *commandVolumeMove) Do(args []string, commandEnv *CommandEnv, writer io. return nil } + if err = commandEnv.confirmIsLocked(); err != nil { + return + } + sourceVolumeServer, targetVolumeServer := pb.ServerAddress(*sourceNodeStr), pb.ServerAddress(*targetNodeStr) volumeId := needle.VolumeId(*volumeIdInt) diff --git a/weed/shell/command_volume_server_evacuate.go b/weed/shell/command_volume_server_evacuate.go index 6e0c19ae1..691d6d552 100644 --- a/weed/shell/command_volume_server_evacuate.go +++ b/weed/shell/command_volume_server_evacuate.go @@ -44,10 +44,6 @@ func (c *commandVolumeServerEvacuate) Help() string { func (c *commandVolumeServerEvacuate) Do(args []string, commandEnv *CommandEnv, writer io.Writer) (err error) { - if err = commandEnv.confirmIsLocked(); err != nil { - return - } - vsEvacuateCommand := flag.NewFlagSet(c.Name(), flag.ContinueOnError) volumeServer := vsEvacuateCommand.String("node", "", ": of the volume server") skipNonMoveable := vsEvacuateCommand.Bool("skipNonMoveable", false, "skip volumes that can not be moved") @@ -57,6 +53,10 @@ func (c *commandVolumeServerEvacuate) Do(args []string, commandEnv *CommandEnv, return nil } + if err = commandEnv.confirmIsLocked(); err != nil { + return + } + if *volumeServer == "" { return fmt.Errorf("need to specify volume server by -node=:") } diff --git a/weed/shell/command_volume_server_leave.go b/weed/shell/command_volume_server_leave.go index 7f7b7148b..b1c42f4fd 100644 --- a/weed/shell/command_volume_server_leave.go +++ b/weed/shell/command_volume_server_leave.go @@ -37,16 +37,16 @@ func (c *commandVolumeServerLeave) Help() string { func (c *commandVolumeServerLeave) Do(args []string, commandEnv *CommandEnv, writer io.Writer) (err error) { - if err = commandEnv.confirmIsLocked(); err != nil { - return - } - vsLeaveCommand := flag.NewFlagSet(c.Name(), flag.ContinueOnError) volumeServer := vsLeaveCommand.String("node", "", ": of the volume server") if err = vsLeaveCommand.Parse(args); err != nil { return nil } + if err = commandEnv.confirmIsLocked(); err != nil { + return + } + if *volumeServer == "" { return fmt.Errorf("need to specify volume server by -node=:") } diff --git a/weed/shell/command_volume_tier_download.go b/weed/shell/command_volume_tier_download.go index bfb78608f..72331c8ce 100644 --- a/weed/shell/command_volume_tier_download.go +++ b/weed/shell/command_volume_tier_download.go @@ -43,10 +43,6 @@ func (c *commandVolumeTierDownload) Help() string { func (c *commandVolumeTierDownload) Do(args []string, commandEnv *CommandEnv, writer io.Writer) (err error) { - if err = commandEnv.confirmIsLocked(); err != nil { - return - } - tierCommand := flag.NewFlagSet(c.Name(), flag.ContinueOnError) volumeId := tierCommand.Int("volumeId", 0, "the volume id") collection := tierCommand.String("collection", "", "the collection name") @@ -54,6 +50,10 @@ func (c *commandVolumeTierDownload) Do(args []string, commandEnv *CommandEnv, wr return nil } + if err = commandEnv.confirmIsLocked(); err != nil { + return + } + vid := needle.VolumeId(*volumeId) // collect topology information diff --git a/weed/shell/command_volume_tier_move.go b/weed/shell/command_volume_tier_move.go index 33b51ddf2..348f1799a 100644 --- a/weed/shell/command_volume_tier_move.go +++ b/weed/shell/command_volume_tier_move.go @@ -46,10 +46,6 @@ func (c *commandVolumeTierMove) Do(args []string, commandEnv *CommandEnv, writer c.activeServers = make(map[pb.ServerAddress]struct{}) c.activeServersCond = sync.NewCond(new(sync.Mutex)) - if err = commandEnv.confirmIsLocked(); err != nil { - return - } - tierCommand := flag.NewFlagSet(c.Name(), flag.ContinueOnError) collectionPattern := tierCommand.String("collectionPattern", "", "match with wildcard characters '*' and '?'") fullPercentage := tierCommand.Float64("fullPercent", 95, "the volume reaches the percentage of max volume size") @@ -61,6 +57,10 @@ func (c *commandVolumeTierMove) Do(args []string, commandEnv *CommandEnv, writer return nil } + if err = commandEnv.confirmIsLocked(); err != nil { + return + } + fromDiskType := types.ToDiskType(*source) toDiskType := types.ToDiskType(*target) diff --git a/weed/shell/command_volume_tier_upload.go b/weed/shell/command_volume_tier_upload.go index d50178cb9..72f5347b0 100644 --- a/weed/shell/command_volume_tier_upload.go +++ b/weed/shell/command_volume_tier_upload.go @@ -57,10 +57,6 @@ func (c *commandVolumeTierUpload) Help() string { func (c *commandVolumeTierUpload) Do(args []string, commandEnv *CommandEnv, writer io.Writer) (err error) { - if err = commandEnv.confirmIsLocked(); err != nil { - return - } - tierCommand := flag.NewFlagSet(c.Name(), flag.ContinueOnError) volumeId := tierCommand.Int("volumeId", 0, "the volume id") collection := tierCommand.String("collection", "", "the collection name") @@ -72,6 +68,10 @@ func (c *commandVolumeTierUpload) Do(args []string, commandEnv *CommandEnv, writ return nil } + if err = commandEnv.confirmIsLocked(); err != nil { + return + } + vid := needle.VolumeId(*volumeId) // volumeId is provided diff --git a/weed/shell/command_volume_unmount.go b/weed/shell/command_volume_unmount.go index 5fc158b76..065cbf227 100644 --- a/weed/shell/command_volume_unmount.go +++ b/weed/shell/command_volume_unmount.go @@ -35,10 +35,6 @@ func (c *commandVolumeUnmount) Help() string { func (c *commandVolumeUnmount) Do(args []string, commandEnv *CommandEnv, writer io.Writer) (err error) { - if err = commandEnv.confirmIsLocked(); err != nil { - return - } - volUnmountCommand := flag.NewFlagSet(c.Name(), flag.ContinueOnError) volumeIdInt := volUnmountCommand.Int("volumeId", 0, "the volume id") nodeStr := volUnmountCommand.String("node", "", "the volume server :") @@ -46,6 +42,10 @@ func (c *commandVolumeUnmount) Do(args []string, commandEnv *CommandEnv, writer return nil } + if err = commandEnv.confirmIsLocked(); err != nil { + return + } + sourceVolumeServer := pb.ServerAddress(*nodeStr) volumeId := needle.VolumeId(*volumeIdInt) diff --git a/weed/shell/command_volume_vacuum.go b/weed/shell/command_volume_vacuum.go index 56f85f4fe..ecd4d7756 100644 --- a/weed/shell/command_volume_vacuum.go +++ b/weed/shell/command_volume_vacuum.go @@ -29,16 +29,16 @@ func (c *commandVacuum) Help() string { func (c *commandVacuum) Do(args []string, commandEnv *CommandEnv, writer io.Writer) (err error) { - if err = commandEnv.confirmIsLocked(); err != nil { - return - } - volumeVacuumCommand := flag.NewFlagSet(c.Name(), flag.ContinueOnError) garbageThreshold := volumeVacuumCommand.Float64("garbageThreshold", 0.3, "vacuum when garbage is more than this limit") if err = volumeVacuumCommand.Parse(args); err != nil { return nil } + if err = commandEnv.confirmIsLocked(); err != nil { + return + } + err = commandEnv.MasterClient.WithClient(func(client master_pb.SeaweedClient) error { _, err = client.VacuumVolume(context.Background(), &master_pb.VacuumVolumeRequest{ GarbageThreshold: float32(*garbageThreshold), From 2789d10342b89691898ae1da774842c5b1ad261d Mon Sep 17 00:00:00 2001 From: Chris Lu Date: Tue, 14 Sep 2021 10:37:06 -0700 Subject: [PATCH 025/130] go fmt --- weed/command/webdav.go | 20 ++++++++--------- weed/ftpd/ftp_server.go | 2 +- weed/iamapi/iamapi_server.go | 10 ++++----- weed/operation/lookup.go | 2 +- weed/pb/grpc_client_server.go | 2 +- weed/remote_storage/s3/s3_storage_client.go | 2 +- weed/server/filer_grpc_server_remote.go | 2 +- weed/server/filer_server_handlers_read.go | 2 +- weed/server/master_grpc_server.go | 2 +- weed/server/master_grpc_server_volume.go | 10 ++++----- weed/server/volume_grpc_remote.go | 2 +- weed/server/webdav_server.go | 24 ++++++++++----------- weed/shell/command_remote_cache.go | 2 +- weed/shell/command_remote_unmount.go | 1 - weed/topology/topology_test.go | 4 ++-- weed/util/network.go | 6 +++--- 16 files changed, 46 insertions(+), 47 deletions(-) diff --git a/weed/command/webdav.go b/weed/command/webdav.go index a7062d8cd..bf4609d63 100644 --- a/weed/command/webdav.go +++ b/weed/command/webdav.go @@ -103,16 +103,16 @@ func (wo *WebDavOption) startWebDav() bool { } ws, webdavServer_err := weed_server.NewWebDavServer(&weed_server.WebDavOption{ - Filer: filerAddress, - GrpcDialOption: grpcDialOption, - Collection: *wo.collection, - Replication: *wo.replication, - DiskType: *wo.disk, - Uid: uid, - Gid: gid, - Cipher: cipher, - CacheDir: util.ResolvePath(*wo.cacheDir), - CacheSizeMB: *wo.cacheSizeMB, + Filer: filerAddress, + GrpcDialOption: grpcDialOption, + Collection: *wo.collection, + Replication: *wo.replication, + DiskType: *wo.disk, + Uid: uid, + Gid: gid, + Cipher: cipher, + CacheDir: util.ResolvePath(*wo.cacheDir), + CacheSizeMB: *wo.cacheSizeMB, }) if webdavServer_err != nil { glog.Fatalf("WebDav Server startup error: %v", webdavServer_err) diff --git a/weed/ftpd/ftp_server.go b/weed/ftpd/ftp_server.go index 4daded00a..253ff3edd 100644 --- a/weed/ftpd/ftp_server.go +++ b/weed/ftpd/ftp_server.go @@ -3,8 +3,8 @@ package ftpd import ( "crypto/tls" "errors" - "net" "github.com/chrislusf/seaweedfs/weed/util" + "net" ftpserver "github.com/fclairamb/ftpserverlib" "google.golang.org/grpc" diff --git a/weed/iamapi/iamapi_server.go b/weed/iamapi/iamapi_server.go index c8ff1042e..9fea30780 100644 --- a/weed/iamapi/iamapi_server.go +++ b/weed/iamapi/iamapi_server.go @@ -33,10 +33,10 @@ type IamS3ApiConfigure struct { } type IamServerOption struct { - Masters []pb.ServerAddress - Filer pb.ServerAddress - Port int - GrpcDialOption grpc.DialOption + Masters []pb.ServerAddress + Filer pb.ServerAddress + Port int + GrpcDialOption grpc.DialOption } type IamApiServer struct { @@ -49,7 +49,7 @@ var s3ApiConfigure IamS3ApiConfig func NewIamApiServer(router *mux.Router, option *IamServerOption) (iamApiServer *IamApiServer, err error) { s3ApiConfigure = IamS3ApiConfigure{ option: option, - masterClient: wdclient.NewMasterClient(option.GrpcDialOption, pb.AdminShellClient, "", "",option.Masters), + masterClient: wdclient.NewMasterClient(option.GrpcDialOption, pb.AdminShellClient, "", "", option.Masters), } s3Option := s3api.S3ApiServerOption{Filer: option.Filer} iamApiServer = &IamApiServer{ diff --git a/weed/operation/lookup.go b/weed/operation/lookup.go index daf8cd152..c222dff92 100644 --- a/weed/operation/lookup.go +++ b/weed/operation/lookup.go @@ -96,7 +96,7 @@ func LookupVolumeIds(masterFn GetMasterFn, grpcDialOption grpc.DialOption, vids locations = append(locations, Location{ Url: loc.Url, PublicUrl: loc.PublicUrl, - GrpcPort: int(loc.GrpcPort), + GrpcPort: int(loc.GrpcPort), }) } if vidLocations.Error != "" { diff --git a/weed/pb/grpc_client_server.go b/weed/pb/grpc_client_server.go index 0bd7b691e..08fb7fac7 100644 --- a/weed/pb/grpc_client_server.go +++ b/weed/pb/grpc_client_server.go @@ -244,4 +244,4 @@ func WithOneOfGrpcFilerClients(filerAddresses []ServerAddress, grpcDialOption gr } return err -} \ No newline at end of file +} diff --git a/weed/remote_storage/s3/s3_storage_client.go b/weed/remote_storage/s3/s3_storage_client.go index fc00bcfe5..b8701cb52 100644 --- a/weed/remote_storage/s3/s3_storage_client.go +++ b/weed/remote_storage/s3/s3_storage_client.go @@ -269,7 +269,7 @@ func (s *s3RemoteStorageClient) CreateBucket(name string) (err error) { func (s *s3RemoteStorageClient) DeleteBucket(name string) (err error) { _, err = s.conn.DeleteBucket(&s3.DeleteBucketInput{ - Bucket: aws.String(name), + Bucket: aws.String(name), }) if err != nil { return fmt.Errorf("delete bucket %s: %v", name, err) diff --git a/weed/server/filer_grpc_server_remote.go b/weed/server/filer_grpc_server_remote.go index 9f986e6aa..3c7fef90a 100644 --- a/weed/server/filer_grpc_server_remote.go +++ b/weed/server/filer_grpc_server_remote.go @@ -116,7 +116,7 @@ func (fs *FilerServer) DownloadToLocal(ctx context.Context, req *filer_pb.Downlo replicas = append(replicas, &volume_server_pb.FetchAndWriteNeedleRequest_Replica{ Url: r.Url, PublicUrl: r.PublicUrl, - GrpcPort: int32(r.GrpcPort), + GrpcPort: int32(r.GrpcPort), }) } diff --git a/weed/server/filer_server_handlers_read.go b/weed/server/filer_server_handlers_read.go index 3e0d32189..57f2e5300 100644 --- a/weed/server/filer_server_handlers_read.go +++ b/weed/server/filer_server_handlers_read.go @@ -64,7 +64,7 @@ func (fs *FilerServer) GetOrHeadHandler(w http.ResponseWriter, r *http.Request) // set etag etag := filer.ETagEntry(entry) - if ifm := r.Header.Get("If-Match"); ifm != "" && (ifm != "\""+etag+"\"" && ifm != etag){ + if ifm := r.Header.Get("If-Match"); ifm != "" && (ifm != "\""+etag+"\"" && ifm != etag) { w.WriteHeader(http.StatusPreconditionFailed) return } diff --git a/weed/server/master_grpc_server.go b/weed/server/master_grpc_server.go index 194520f49..c669adaa6 100644 --- a/weed/server/master_grpc_server.go +++ b/weed/server/master_grpc_server.go @@ -24,7 +24,7 @@ func (ms *MasterServer) SendHeartbeat(stream master_pb.Seaweed_SendHeartbeatServ defer func() { if dn != nil { dn.Counter-- - if dn.Counter > 0 { + if dn.Counter > 0 { glog.V(0).Infof("disconnect phantom volume server %s:%d remaining %d", dn.Ip, dn.Port, dn.Counter) return } diff --git a/weed/server/master_grpc_server_volume.go b/weed/server/master_grpc_server_volume.go index 49ac455fe..4048225f3 100644 --- a/weed/server/master_grpc_server_volume.go +++ b/weed/server/master_grpc_server_volume.go @@ -155,19 +155,19 @@ func (ms *MasterServer) Assign(ctx context.Context, req *master_pb.AssignRequest replicas = append(replicas, &master_pb.Location{ Url: r.Url(), PublicUrl: r.PublicUrl, - GrpcPort: uint32(r.GrpcPort), + GrpcPort: uint32(r.GrpcPort), }) } return &master_pb.AssignResponse{ - Fid: fid, + Fid: fid, Location: &master_pb.Location{ Url: dn.Url(), PublicUrl: dn.PublicUrl, GrpcPort: uint32(dn.GrpcPort), }, - Count: count, - Auth: string(security.GenJwt(ms.guard.SigningKey, ms.guard.ExpiresAfterSec, fid)), - Replicas: replicas, + Count: count, + Auth: string(security.GenJwt(ms.guard.SigningKey, ms.guard.ExpiresAfterSec, fid)), + Replicas: replicas, }, nil } //glog.V(4).Infoln("waiting for volume growing...") diff --git a/weed/server/volume_grpc_remote.go b/weed/server/volume_grpc_remote.go index aff57e52b..9114db329 100644 --- a/weed/server/volume_grpc_remote.go +++ b/weed/server/volume_grpc_remote.go @@ -53,7 +53,7 @@ func (vs *VolumeServer) FetchAndWriteNeedle(ctx context.Context, req *volume_ser } } }() - if len(req.Replicas)>0{ + if len(req.Replicas) > 0 { fileId := needle.NewFileId(v.Id, req.NeedleId, req.Cookie) for _, replica := range req.Replicas { wg.Add(1) diff --git a/weed/server/webdav_server.go b/weed/server/webdav_server.go index 239de69f8..6c1de3154 100644 --- a/weed/server/webdav_server.go +++ b/weed/server/webdav_server.go @@ -27,18 +27,18 @@ import ( ) type WebDavOption struct { - Filer pb.ServerAddress - DomainName string - BucketsPath string - GrpcDialOption grpc.DialOption - Collection string - Replication string - DiskType string - Uid uint32 - Gid uint32 - Cipher bool - CacheDir string - CacheSizeMB int64 + Filer pb.ServerAddress + DomainName string + BucketsPath string + GrpcDialOption grpc.DialOption + Collection string + Replication string + DiskType string + Uid uint32 + Gid uint32 + Cipher bool + CacheDir string + CacheSizeMB int64 } type WebDavServer struct { diff --git a/weed/shell/command_remote_cache.go b/weed/shell/command_remote_cache.go index 58b6d7b5e..2ae20d143 100644 --- a/weed/shell/command_remote_cache.go +++ b/weed/shell/command_remote_cache.go @@ -79,7 +79,7 @@ func (c *commandRemoteCache) Do(args []string, commandEnv *CommandEnv, writer io return nil } -func (c *commandRemoteCache) doCacheOneDirectory(commandEnv *CommandEnv, writer io.Writer, dir string, fileFiler *FileFilter, concurrency int) (error) { +func (c *commandRemoteCache) doCacheOneDirectory(commandEnv *CommandEnv, writer io.Writer, dir string, fileFiler *FileFilter, concurrency int) error { mappings, localMountedDir, remoteStorageMountedLocation, remoteStorageConf, detectErr := detectMountInfo(commandEnv, writer, dir) if detectErr != nil { jsonPrintln(writer, mappings) diff --git a/weed/shell/command_remote_unmount.go b/weed/shell/command_remote_unmount.go index 04fd5e388..d030143a3 100644 --- a/weed/shell/command_remote_unmount.go +++ b/weed/shell/command_remote_unmount.go @@ -118,4 +118,3 @@ func (c *commandRemoteUnmount) purgeMountedData(commandEnv *CommandEnv, dir stri return nil } - diff --git a/weed/topology/topology_test.go b/weed/topology/topology_test.go index bfe7faff7..2ece48a95 100644 --- a/weed/topology/topology_test.go +++ b/weed/topology/topology_test.go @@ -31,7 +31,7 @@ func TestHandlingVolumeServerHeartbeat(t *testing.T) { maxVolumeCounts := make(map[string]uint32) maxVolumeCounts[""] = 25 maxVolumeCounts["ssd"] = 12 - dn := rack.GetOrCreateDataNode("127.0.0.1", 34534, 0,"127.0.0.1", maxVolumeCounts) + dn := rack.GetOrCreateDataNode("127.0.0.1", 34534, 0, "127.0.0.1", maxVolumeCounts) { volumeCount := 7 @@ -177,7 +177,7 @@ func TestAddRemoveVolume(t *testing.T) { maxVolumeCounts := make(map[string]uint32) maxVolumeCounts[""] = 25 maxVolumeCounts["ssd"] = 12 - dn := rack.GetOrCreateDataNode("127.0.0.1", 34534, 0,"127.0.0.1", maxVolumeCounts) + dn := rack.GetOrCreateDataNode("127.0.0.1", 34534, 0, "127.0.0.1", maxVolumeCounts) v := storage.VolumeInfo{ Id: needle.VolumeId(1), diff --git a/weed/util/network.go b/weed/util/network.go index 43b4a794d..687b6ec22 100644 --- a/weed/util/network.go +++ b/weed/util/network.go @@ -15,11 +15,11 @@ func DetectedHostAddress() string { return "" } - if v4Address := selectIpV4(netInterfaces, true); v4Address != ""{ + if v4Address := selectIpV4(netInterfaces, true); v4Address != "" { return v4Address } - if v6Address := selectIpV4(netInterfaces, false); v6Address != ""{ + if v6Address := selectIpV4(netInterfaces, false); v6Address != "" { return v6Address } @@ -59,4 +59,4 @@ func JoinHostPort(host string, port int) string { return host + ":" + portStr } return net.JoinHostPort(host, portStr) -} \ No newline at end of file +} From 7a135b7735f4a6d9a63691a490f9a20df80e8eab Mon Sep 17 00:00:00 2001 From: Chris Lu Date: Tue, 14 Sep 2021 12:07:57 -0700 Subject: [PATCH 026/130] Update README.md --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index f07665651..08728263c 100644 --- a/README.md +++ b/README.md @@ -147,6 +147,7 @@ Faster and Cheaper than direct cloud storage! * [AES256-GCM Encrypted Storage][FilerDataEncryption] safely stores the encrypted data. * [Super Large Files][SuperLargeFiles] stores large or super large files in tens of TB. * [Cloud Drive][CloudDrive] mounts cloud storage to local cluster, cached for fast read and write with asynchronous write back. +* [Gateway to Remote Object Store][GatewayToRemoteObjectStore] mirror bucket operations to remote object storage, in addition to [Cloud Drive][CloudDrive] ## Kubernetes ## * [Kubernetes CSI Driver][SeaweedFsCsiDriver] A Container Storage Interface (CSI) Driver. [![Docker Pulls](https://img.shields.io/docker/pulls/chrislusf/seaweedfs-csi-driver.svg?maxAge=4800)](https://hub.docker.com/r/chrislusf/seaweedfs-csi-driver/) @@ -170,6 +171,7 @@ Faster and Cheaper than direct cloud storage! [FilerStoreReplication]: https://github.com/chrislusf/seaweedfs/wiki/Filer-Store-Replication [KeyLargeValueStore]: https://github.com/chrislusf/seaweedfs/wiki/Filer-as-a-Key-Large-Value-Store [CloudDrive]: https://github.com/chrislusf/seaweedfs/wiki/Cloud-Drive-Architecture +[GatewayToRemoteObjectStore]: https://github.com/chrislusf/seaweedfs/wiki/Gateway-to-Remote-Object-Storage [Back to TOC](#table-of-contents) From 0c099c1bea14cb26ca375e19c32037b328ca0b27 Mon Sep 17 00:00:00 2001 From: Chris Lu Date: Tue, 14 Sep 2021 12:09:04 -0700 Subject: [PATCH 027/130] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 08728263c..b15cf4051 100644 --- a/README.md +++ b/README.md @@ -147,7 +147,7 @@ Faster and Cheaper than direct cloud storage! * [AES256-GCM Encrypted Storage][FilerDataEncryption] safely stores the encrypted data. * [Super Large Files][SuperLargeFiles] stores large or super large files in tens of TB. * [Cloud Drive][CloudDrive] mounts cloud storage to local cluster, cached for fast read and write with asynchronous write back. -* [Gateway to Remote Object Store][GatewayToRemoteObjectStore] mirror bucket operations to remote object storage, in addition to [Cloud Drive][CloudDrive] +* [Gateway to Remote Object Store][GatewayToRemoteObjectStore] mirrors bucket operations to remote object storage, in addition to [Cloud Drive][CloudDrive] ## Kubernetes ## * [Kubernetes CSI Driver][SeaweedFsCsiDriver] A Container Storage Interface (CSI) Driver. [![Docker Pulls](https://img.shields.io/docker/pulls/chrislusf/seaweedfs-csi-driver.svg?maxAge=4800)](https://hub.docker.com/r/chrislusf/seaweedfs-csi-driver/) From 63da4bbb5463b56c4beafc499d26a2d8f26a08fd Mon Sep 17 00:00:00 2001 From: Chris Lu Date: Wed, 15 Sep 2021 22:47:17 -0700 Subject: [PATCH 028/130] separate filer.remote.gateway command to avoid confusion --- weed/command/filer_remote_gateway.go | 113 ++++++++++++++++++ ...ets.go => filer_remote_gateway_buckets.go} | 12 +- weed/command/filer_remote_sync.go | 41 +------ weed/command/filer_remote_sync_dir.go | 13 +- 4 files changed, 127 insertions(+), 52 deletions(-) create mode 100644 weed/command/filer_remote_gateway.go rename weed/command/{filer_remote_sync_buckets.go => filer_remote_gateway_buckets.go} (94%) diff --git a/weed/command/filer_remote_gateway.go b/weed/command/filer_remote_gateway.go new file mode 100644 index 000000000..ea23daf5e --- /dev/null +++ b/weed/command/filer_remote_gateway.go @@ -0,0 +1,113 @@ +package command + +import ( + "context" + "fmt" + "github.com/chrislusf/seaweedfs/weed/glog" + "github.com/chrislusf/seaweedfs/weed/pb" + "github.com/chrislusf/seaweedfs/weed/pb/filer_pb" + "github.com/chrislusf/seaweedfs/weed/pb/remote_pb" + "github.com/chrislusf/seaweedfs/weed/replication/source" + "github.com/chrislusf/seaweedfs/weed/security" + "github.com/chrislusf/seaweedfs/weed/util" + "google.golang.org/grpc" + "os" + "time" +) + +type RemoteGatewayOptions struct { + filerAddress *string + grpcDialOption grpc.DialOption + readChunkFromFiler *bool + timeAgo *time.Duration + createBucketAt *string + createBucketRandomSuffix *bool + + mappings *remote_pb.RemoteStorageMapping + remoteConfs map[string]*remote_pb.RemoteConf + bucketsDir string +} + +var _ = filer_pb.FilerClient(&RemoteGatewayOptions{}) + +func (option *RemoteGatewayOptions) WithFilerClient(fn func(filer_pb.SeaweedFilerClient) error) error { + return pb.WithFilerClient(pb.ServerAddress(*option.filerAddress), option.grpcDialOption, func(client filer_pb.SeaweedFilerClient) error { + return fn(client) + }) +} +func (option *RemoteGatewayOptions) AdjustedUrl(location *filer_pb.Location) string { + return location.Url +} + +var ( + remoteGatewayOptions RemoteGatewayOptions +) + +func init() { + cmdFilerRemoteGateway.Run = runFilerRemoteGateway // break init cycle + remoteGatewayOptions.filerAddress = cmdFilerRemoteGateway.Flag.String("filer", "localhost:8888", "filer of the SeaweedFS cluster") + remoteGatewayOptions.createBucketAt = cmdFilerRemoteGateway.Flag.String("createBucketAt", "", "one remote storage name to create new buckets in") + remoteGatewayOptions.createBucketRandomSuffix = cmdFilerRemoteGateway.Flag.Bool("createBucketWithRandomSuffix", true, "add randomized suffix to bucket name to avoid conflicts") + remoteGatewayOptions.readChunkFromFiler = cmdFilerRemoteGateway.Flag.Bool("filerProxy", false, "read file chunks from filer instead of volume servers") + remoteGatewayOptions.timeAgo = cmdFilerRemoteGateway.Flag.Duration("timeAgo", 0, "start time before now. \"300ms\", \"1.5h\" or \"2h45m\". Valid time units are \"ns\", \"us\" (or \"µs\"), \"ms\", \"s\", \"m\", \"h\"") +} + +var cmdFilerRemoteGateway = &Command{ + UsageLine: "filer.remote.gateway", + Short: "resumable continuously write back bucket creation, deletion, and other local updates to remote storage", + Long: `resumable continuously write back bucket creation, deletion, and other local updates to remote storage + + filer.remote.gateway listens on filer local buckets update events. + If any bucket is created, deleted, or updated, it will mirror the changes to remote object store. + + weed filer.remote.sync -createBucketAt=cloud1 + +`, +} + +func runFilerRemoteGateway(cmd *Command, args []string) bool { + + util.LoadConfiguration("security", false) + grpcDialOption := security.LoadClientTLS(util.GetViper(), "grpc.client") + remoteGatewayOptions.grpcDialOption = grpcDialOption + + filerAddress := pb.ServerAddress(*remoteGatewayOptions.filerAddress) + + filerSource := &source.FilerSource{} + filerSource.DoInitialize( + filerAddress.ToHttpAddress(), + filerAddress.ToGrpcAddress(), + "/", // does not matter + *remoteGatewayOptions.readChunkFromFiler, + ) + + remoteGatewayOptions.bucketsDir = "/buckets" + // check buckets again + remoteGatewayOptions.WithFilerClient(func(filerClient filer_pb.SeaweedFilerClient) error { + resp, err := filerClient.GetFilerConfiguration(context.Background(), &filer_pb.GetFilerConfigurationRequest{}) + if err != nil { + return err + } + remoteGatewayOptions.bucketsDir = resp.DirBuckets + return nil + }) + + // read filer remote storage mount mappings + if detectErr := remoteGatewayOptions.collectRemoteStorageConf(); detectErr != nil { + fmt.Fprintf(os.Stderr, "read mount info: %v\n", detectErr) + return true + } + + // synchronize /buckets folder + fmt.Printf("synchronize buckets in %s ...\n", remoteGatewayOptions.bucketsDir) + util.RetryForever("filer.remote.sync buckets", func() error { + return remoteGatewayOptions.followBucketUpdatesAndUploadToRemote(filerSource) + }, func(err error) bool { + if err != nil { + glog.Errorf("synchronize %s: %v", remoteGatewayOptions.bucketsDir, err) + } + return true + }) + return true + +} diff --git a/weed/command/filer_remote_sync_buckets.go b/weed/command/filer_remote_gateway_buckets.go similarity index 94% rename from weed/command/filer_remote_sync_buckets.go rename to weed/command/filer_remote_gateway_buckets.go index 73c8de1a9..e16e4f731 100644 --- a/weed/command/filer_remote_sync_buckets.go +++ b/weed/command/filer_remote_gateway_buckets.go @@ -17,7 +17,7 @@ import ( "time" ) -func (option *RemoteSyncOptions) followBucketUpdatesAndUploadToRemote(filerSource *source.FilerSource) error { +func (option *RemoteGatewayOptions) followBucketUpdatesAndUploadToRemote(filerSource *source.FilerSource) error { // read filer remote storage mount mappings if detectErr := option.collectRemoteStorageConf(); detectErr != nil { @@ -35,13 +35,13 @@ func (option *RemoteSyncOptions) followBucketUpdatesAndUploadToRemote(filerSourc return remote_storage.SetSyncOffset(option.grpcDialOption, pb.ServerAddress(*option.filerAddress), option.bucketsDir, lastTsNs) }) - lastOffsetTs := collectLastSyncOffset(option, option.bucketsDir) + lastOffsetTs := collectLastSyncOffset(option, option.grpcDialOption, pb.ServerAddress(*option.filerAddress), option.bucketsDir, *option.timeAgo) return pb.FollowMetadata(pb.ServerAddress(*option.filerAddress), option.grpcDialOption, "filer.remote.sync", option.bucketsDir, []string{filer.DirectoryEtcRemote}, lastOffsetTs.UnixNano(), 0, processEventFnWithOffset, false) } -func (option *RemoteSyncOptions) makeBucketedEventProcessor(filerSource *source.FilerSource) (pb.ProcessMetadataFunc, error) { +func (option *RemoteGatewayOptions) makeBucketedEventProcessor(filerSource *source.FilerSource) (pb.ProcessMetadataFunc, error) { handleCreateBucket := func(entry *filer_pb.Entry) error { if !entry.IsDirectory { @@ -307,7 +307,7 @@ func (option *RemoteSyncOptions) makeBucketedEventProcessor(filerSource *source. return eachEntryFunc, nil } -func (option *RemoteSyncOptions) findRemoteStorageClient(bucketName string) (client remote_storage.RemoteStorageClient, remoteStorageMountLocation *remote_pb.RemoteStorageLocation, err error) { +func (option *RemoteGatewayOptions) findRemoteStorageClient(bucketName string) (client remote_storage.RemoteStorageClient, remoteStorageMountLocation *remote_pb.RemoteStorageLocation, err error) { bucket := util.FullPath(option.bucketsDir).Child(bucketName) var isMounted bool @@ -327,7 +327,7 @@ func (option *RemoteSyncOptions) findRemoteStorageClient(bucketName string) (cli return client, remoteStorageMountLocation, nil } -func (option *RemoteSyncOptions) detectBucketInfo(actualDir string) (bucket util.FullPath, remoteStorageMountLocation *remote_pb.RemoteStorageLocation, remoteConf *remote_pb.RemoteConf, ok bool) { +func (option *RemoteGatewayOptions) detectBucketInfo(actualDir string) (bucket util.FullPath, remoteStorageMountLocation *remote_pb.RemoteStorageLocation, remoteConf *remote_pb.RemoteConf, ok bool) { bucket, ok = extractBucketPath(option.bucketsDir, actualDir) if !ok { return "", nil, nil, false @@ -355,7 +355,7 @@ func extractBucketPath(bucketsDir, dir string) (util.FullPath, bool) { return util.FullPath(bucketsDir).Child(parts[0]), true } -func (option *RemoteSyncOptions) collectRemoteStorageConf() (err error) { +func (option *RemoteGatewayOptions) collectRemoteStorageConf() (err error) { if mappings, err := filer.ReadMountMappings(option.grpcDialOption, pb.ServerAddress(*option.filerAddress)); err != nil { return err diff --git a/weed/command/filer_remote_sync.go b/weed/command/filer_remote_sync.go index 857fbb0eb..65cf8e91f 100644 --- a/weed/command/filer_remote_sync.go +++ b/weed/command/filer_remote_sync.go @@ -1,17 +1,14 @@ package command import ( - "context" "fmt" "github.com/chrislusf/seaweedfs/weed/glog" "github.com/chrislusf/seaweedfs/weed/pb" "github.com/chrislusf/seaweedfs/weed/pb/filer_pb" - "github.com/chrislusf/seaweedfs/weed/pb/remote_pb" "github.com/chrislusf/seaweedfs/weed/replication/source" "github.com/chrislusf/seaweedfs/weed/security" "github.com/chrislusf/seaweedfs/weed/util" "google.golang.org/grpc" - "os" "time" ) @@ -19,15 +16,9 @@ type RemoteSyncOptions struct { filerAddress *string grpcDialOption grpc.DialOption readChunkFromFiler *bool - debug *bool timeAgo *time.Duration dir *string - createBucketAt *string - createBucketRandomSuffix *bool - mappings *remote_pb.RemoteStorageMapping - remoteConfs map[string]*remote_pb.RemoteConf - bucketsDir string } var _ = filer_pb.FilerClient(&RemoteSyncOptions{}) @@ -49,10 +40,7 @@ func init() { cmdFilerRemoteSynchronize.Run = runFilerRemoteSynchronize // break init cycle remoteSyncOptions.filerAddress = cmdFilerRemoteSynchronize.Flag.String("filer", "localhost:8888", "filer of the SeaweedFS cluster") remoteSyncOptions.dir = cmdFilerRemoteSynchronize.Flag.String("dir", "", "a mounted directory on filer") - remoteSyncOptions.createBucketAt = cmdFilerRemoteSynchronize.Flag.String("createBucketAt", "", "one remote storage name to create new buckets in") - remoteSyncOptions.createBucketRandomSuffix = cmdFilerRemoteSynchronize.Flag.Bool("createBucketWithRandomSuffix", true, "add randomized suffix to bucket name to avoid conflicts") remoteSyncOptions.readChunkFromFiler = cmdFilerRemoteSynchronize.Flag.Bool("filerProxy", false, "read file chunks from filer instead of volume servers") - remoteSyncOptions.debug = cmdFilerRemoteSynchronize.Flag.Bool("debug", false, "debug mode to print out filer updated remote files") remoteSyncOptions.timeAgo = cmdFilerRemoteSynchronize.Flag.Duration("timeAgo", 0, "start time before now. \"300ms\", \"1.5h\" or \"2h45m\". Valid time units are \"ns\", \"us\" (or \"µs\"), \"ms\", \"s\", \"m\", \"h\"") } @@ -100,18 +88,7 @@ func runFilerRemoteSynchronize(cmd *Command, args []string) bool { *remoteSyncOptions.readChunkFromFiler, ) - remoteSyncOptions.bucketsDir = "/buckets" - // check buckets again - remoteSyncOptions.WithFilerClient(func(filerClient filer_pb.SeaweedFilerClient) error { - resp, err := filerClient.GetFilerConfiguration(context.Background(), &filer_pb.GetFilerConfigurationRequest{}) - if err != nil { - return err - } - remoteSyncOptions.bucketsDir = resp.DirBuckets - return nil - }) - - if dir != "" && dir != remoteSyncOptions.bucketsDir { + if dir != "" { fmt.Printf("synchronize %s to remote storage...\n", dir) util.RetryForever("filer.remote.sync "+dir, func() error { return followUpdatesAndUploadToRemote(&remoteSyncOptions, filerSource, dir) @@ -124,22 +101,6 @@ func runFilerRemoteSynchronize(cmd *Command, args []string) bool { return true } - // read filer remote storage mount mappings - if detectErr := remoteSyncOptions.collectRemoteStorageConf(); detectErr != nil { - fmt.Fprintf(os.Stderr, "read mount info: %v\n", detectErr) - return true - } - - // synchronize /buckets folder - fmt.Printf("synchronize buckets in %s ...\n", remoteSyncOptions.bucketsDir) - util.RetryForever("filer.remote.sync buckets", func() error { - return remoteSyncOptions.followBucketUpdatesAndUploadToRemote(filerSource) - }, func(err error) bool { - if err != nil { - glog.Errorf("synchronize %s: %v", remoteSyncOptions.bucketsDir, err) - } - return true - }) return true } diff --git a/weed/command/filer_remote_sync_dir.go b/weed/command/filer_remote_sync_dir.go index 50f1e35cf..8ff933833 100644 --- a/weed/command/filer_remote_sync_dir.go +++ b/weed/command/filer_remote_sync_dir.go @@ -12,6 +12,7 @@ import ( "github.com/chrislusf/seaweedfs/weed/replication/source" "github.com/chrislusf/seaweedfs/weed/util" "github.com/golang/protobuf/proto" + "google.golang.org/grpc" "os" "strings" "time" @@ -36,7 +37,7 @@ func followUpdatesAndUploadToRemote(option *RemoteSyncOptions, filerSource *sour return remote_storage.SetSyncOffset(option.grpcDialOption, pb.ServerAddress(*option.filerAddress), mountedDir, lastTsNs) }) - lastOffsetTs := collectLastSyncOffset(option, mountedDir) + lastOffsetTs := collectLastSyncOffset(option, option.grpcDialOption, pb.ServerAddress(*option.filerAddress), mountedDir, *option.timeAgo) return pb.FollowMetadata(pb.ServerAddress(*option.filerAddress), option.grpcDialOption, "filer.remote.sync", mountedDir, []string{filer.DirectoryEtcRemote}, lastOffsetTs.UnixNano(), 0, processEventFnWithOffset, false) @@ -159,19 +160,19 @@ func makeEventProcessor(remoteStorage *remote_pb.RemoteConf, mountedDir string, return eachEntryFunc, nil } -func collectLastSyncOffset(option *RemoteSyncOptions, mountedDir string) time.Time { +func collectLastSyncOffset(filerClient filer_pb.FilerClient, grpcDialOption grpc.DialOption, filerAddress pb.ServerAddress, mountedDir string, timeAgo time.Duration) time.Time { // 1. specified by timeAgo // 2. last offset timestamp for this directory // 3. directory creation time var lastOffsetTs time.Time - if *option.timeAgo == 0 { - mountedDirEntry, err := filer_pb.GetEntry(option, util.FullPath(mountedDir)) + if timeAgo == 0 { + mountedDirEntry, err := filer_pb.GetEntry(filerClient, util.FullPath(mountedDir)) if err != nil { glog.V(0).Infof("get mounted directory %s: %v", mountedDir, err) return time.Now() } - lastOffsetTsNs, err := remote_storage.GetSyncOffset(option.grpcDialOption, pb.ServerAddress(*option.filerAddress), mountedDir) + lastOffsetTsNs, err := remote_storage.GetSyncOffset(grpcDialOption, filerAddress, mountedDir) if mountedDirEntry != nil { if err == nil && mountedDirEntry.Attributes.Crtime < lastOffsetTsNs/1000000 { lastOffsetTs = time.Unix(0, lastOffsetTsNs) @@ -183,7 +184,7 @@ func collectLastSyncOffset(option *RemoteSyncOptions, mountedDir string) time.Ti lastOffsetTs = time.Now() } } else { - lastOffsetTs = time.Now().Add(-*option.timeAgo) + lastOffsetTs = time.Now().Add(-timeAgo) } return lastOffsetTs } From f0907eb83cddd7c0160583702e0bb633ac551caf Mon Sep 17 00:00:00 2001 From: Chris Lu Date: Wed, 15 Sep 2021 22:48:04 -0700 Subject: [PATCH 029/130] add command filre.remote.gateway --- weed/command/command.go | 1 + 1 file changed, 1 insertion(+) diff --git a/weed/command/command.go b/weed/command/command.go index 8d6525652..dbc18a053 100644 --- a/weed/command/command.go +++ b/weed/command/command.go @@ -21,6 +21,7 @@ var Commands = []*Command{ cmdFilerCopy, cmdFilerMetaBackup, cmdFilerMetaTail, + cmdFilerRemoteGateway, cmdFilerRemoteSynchronize, cmdFilerReplicate, cmdFilerSynchronize, From b5f49104124aac262d75742577c13a946505df1c Mon Sep 17 00:00:00 2001 From: Chris Lu Date: Wed, 15 Sep 2021 22:53:10 -0700 Subject: [PATCH 030/130] adjust help messages --- weed/command/filer_remote_gateway.go | 4 ++-- weed/command/filer_remote_sync.go | 13 ------------- 2 files changed, 2 insertions(+), 15 deletions(-) diff --git a/weed/command/filer_remote_gateway.go b/weed/command/filer_remote_gateway.go index ea23daf5e..1b44f31ce 100644 --- a/weed/command/filer_remote_gateway.go +++ b/weed/command/filer_remote_gateway.go @@ -54,8 +54,8 @@ func init() { var cmdFilerRemoteGateway = &Command{ UsageLine: "filer.remote.gateway", - Short: "resumable continuously write back bucket creation, deletion, and other local updates to remote storage", - Long: `resumable continuously write back bucket creation, deletion, and other local updates to remote storage + Short: "resumable continuously write back bucket creation, deletion, and other local updates to remote object store", + Long: `resumable continuously write back bucket creation, deletion, and other local updates to remote object store filer.remote.gateway listens on filer local buckets update events. If any bucket is created, deleted, or updated, it will mirror the changes to remote object store. diff --git a/weed/command/filer_remote_sync.go b/weed/command/filer_remote_sync.go index 65cf8e91f..d44178ee7 100644 --- a/weed/command/filer_remote_sync.go +++ b/weed/command/filer_remote_sync.go @@ -53,21 +53,8 @@ var cmdFilerRemoteSynchronize = &Command{ If any mounted remote file is updated, it will fetch the updated content, and write to the remote storage. - There are two modes: - - 1)By default, watch /buckets folder and write back all changes. - - # if there is only one remote storage configured - weed filer.remote.sync - # if there are multiple remote storages configured - # specify a remote storage to create new buckets. - weed filer.remote.sync -createBucketAt=cloud1 - - 2)Write back one mounted folder to remote storage - weed filer.remote.sync -dir=/mount/s3_on_cloud - `, } From 94a01fcfcb473057028c6e24b49032f0897be958 Mon Sep 17 00:00:00 2001 From: Chris Lu Date: Wed, 15 Sep 2021 23:04:16 -0700 Subject: [PATCH 031/130] filer.remote.gateway: add options to include or exclude new bucket names to mirror --- weed/command/filer_remote_gateway.go | 4 ++++ weed/command/filer_remote_gateway_buckets.go | 11 +++++++++++ 2 files changed, 15 insertions(+) diff --git a/weed/command/filer_remote_gateway.go b/weed/command/filer_remote_gateway.go index 1b44f31ce..be5ab3fe2 100644 --- a/weed/command/filer_remote_gateway.go +++ b/weed/command/filer_remote_gateway.go @@ -22,6 +22,8 @@ type RemoteGatewayOptions struct { timeAgo *time.Duration createBucketAt *string createBucketRandomSuffix *bool + include *string + exclude *string mappings *remote_pb.RemoteStorageMapping remoteConfs map[string]*remote_pb.RemoteConf @@ -50,6 +52,8 @@ func init() { remoteGatewayOptions.createBucketRandomSuffix = cmdFilerRemoteGateway.Flag.Bool("createBucketWithRandomSuffix", true, "add randomized suffix to bucket name to avoid conflicts") remoteGatewayOptions.readChunkFromFiler = cmdFilerRemoteGateway.Flag.Bool("filerProxy", false, "read file chunks from filer instead of volume servers") remoteGatewayOptions.timeAgo = cmdFilerRemoteGateway.Flag.Duration("timeAgo", 0, "start time before now. \"300ms\", \"1.5h\" or \"2h45m\". Valid time units are \"ns\", \"us\" (or \"µs\"), \"ms\", \"s\", \"m\", \"h\"") + remoteGatewayOptions.include = cmdFilerRemoteGateway.Flag.String("include", "", "pattens of new bucket names, e.g., s3*") + remoteGatewayOptions.exclude = cmdFilerRemoteGateway.Flag.String("exclude", "", "pattens of new bucket names, e.g., local*") } var cmdFilerRemoteGateway = &Command{ diff --git a/weed/command/filer_remote_gateway_buckets.go b/weed/command/filer_remote_gateway_buckets.go index e16e4f731..bd3e76859 100644 --- a/weed/command/filer_remote_gateway_buckets.go +++ b/weed/command/filer_remote_gateway_buckets.go @@ -13,6 +13,7 @@ import ( "github.com/golang/protobuf/proto" "math" "math/rand" + "path/filepath" "strings" "time" ) @@ -75,6 +76,16 @@ func (option *RemoteGatewayOptions) makeBucketedEventProcessor(filerSource *sour } bucketName := strings.ToLower(entry.Name) + if *option.include != "" { + if ok, _ := filepath.Match(*option.include, entry.Name); !ok { + return nil + } + } + if *option.exclude != "" { + if ok, _ := filepath.Match(*option.exclude, entry.Name); ok { + return nil + } + } if *option.createBucketRandomSuffix { // https://docs.aws.amazon.com/AmazonS3/latest/userguide/bucketnamingrules.html if len(bucketName)+5 > 63 { From b751debd31846dc3f512e18c765f9000cb53698a Mon Sep 17 00:00:00 2001 From: Chris Lu Date: Sat, 18 Sep 2021 01:29:47 -0700 Subject: [PATCH 032/130] split node based on the last inserted key --- weed/util/bptree/bpmap.go | 6 +- weed/util/bptree/bptree.go | 6 +- weed/util/bptree/bptree_node.go | 79 +++++----- weed/util/bptree/bptree_store_test.go | 8 +- weed/util/bptree/bptree_test.go | 213 ++++++++++---------------- 5 files changed, 128 insertions(+), 184 deletions(-) diff --git a/weed/util/bptree/bpmap.go b/weed/util/bptree/bpmap.go index cbf363c95..399ac7b86 100644 --- a/weed/util/bptree/bpmap.go +++ b/weed/util/bptree/bpmap.go @@ -11,7 +11,7 @@ type BpMap BpTree func NewBpMap(node_size int) *BpMap { return &BpMap{ - root: NewLeaf(node_size, true), + root: NewLeaf(node_size), } } @@ -41,13 +41,13 @@ func (self *BpMap) Remove(key ItemKey) (value ItemValue, err error) { if err != nil { return nil, err } - ns := self.getRoot().NodeSize() + ns := self.getRoot().Capacity() new_root, err := self.getRoot().remove(key, func(value ItemValue) bool { return true }) if err != nil { return nil, err } if new_root == nil { - new_root = NewLeaf(ns, false) + new_root = NewLeaf(ns) err = new_root.persist() self.setRoot(new_root) } else { diff --git a/weed/util/bptree/bptree.go b/weed/util/bptree/bptree.go index f9a5cf058..3ad73ad30 100644 --- a/weed/util/bptree/bptree.go +++ b/weed/util/bptree/bptree.go @@ -14,7 +14,7 @@ type loc_iterator func() (i int, leaf *BpNode, li loc_iterator) func NewBpTree(node_size int) *BpTree { return &BpTree{ - root: NewLeaf(node_size, false), + root: NewLeaf(node_size), } } @@ -87,13 +87,13 @@ func (self *BpTree) Range(from, to ItemKey) (kvi KVIterator) { } func (self *BpTree) RemoveWhere(key ItemKey, where WhereFunc) (err error) { - ns := self.getRoot().NodeSize() + ns := self.getRoot().Capacity() new_root, err := self.getRoot().remove(key, where) if err != nil { return err } if new_root == nil { - new_root = NewLeaf(ns, false) + new_root = NewLeaf(ns) err = new_root.persist() self.setRoot(new_root) } else { diff --git a/weed/util/bptree/bptree_node.go b/weed/util/bptree/bptree_node.go index 4e6d63ac6..5c3461cfd 100644 --- a/weed/util/bptree/bptree_node.go +++ b/weed/util/bptree/bptree_node.go @@ -16,7 +16,6 @@ type BpNode struct { pointers []*BpNode next *BpNode prev *BpNode - no_dup bool protoNodeId int64 protoNode *ProtoNode } @@ -32,14 +31,13 @@ func NewInternal(size int) *BpNode { } } -func NewLeaf(size int, no_dup bool) *BpNode { +func NewLeaf(size int) *BpNode { if size < 0 { panic(NegativeSize()) } return &BpNode{ keys: make([]ItemKey, 0, size), values: make([]ItemValue, 0, size), - no_dup: no_dup, protoNodeId: GetProtoNodeId(), } } @@ -65,7 +63,11 @@ func (self *BpNode) Internal() bool { return cap(self.pointers) > 0 } -func (self *BpNode) NodeSize() int { +func (self *BpNode) Len() int { + return len(self.keys) +} + +func (self *BpNode) Capacity() int { return cap(self.keys) } @@ -78,19 +80,6 @@ func (self *BpNode) Height() int { return self.pointers[0].Height() + 1 } -func (self *BpNode) count(key ItemKey) int { - i, _ := self.find(key) - count := 0 - for ; i < len(self.keys); i++ { - if self.keys[i].Equals(key) { - count++ - } else { - break - } - } - return count -} - func (self *BpNode) has(key ItemKey) bool { _, has := self.find(key) return has @@ -198,7 +187,7 @@ func (self *BpNode) put(key ItemKey, value ItemValue) (root *BpNode, err error) return a, nil } // else we have root split - root = NewInternal(self.NodeSize()) + root = NewInternal(self.Capacity()) root.put_kp(a.keys[0], a) root.put_kp(b.keys[0], b) return root, root.persist() @@ -267,9 +256,9 @@ func (self *BpNode) internal_split(key ItemKey, ptr *BpNode) (a, b *BpNode, err return nil, nil, BpTreeError("Tried to split an internal block on duplicate key") } a = self - b = NewInternal(self.NodeSize()) - balance_nodes(a, b) - if key.Less(b.keys[0]) { + b = NewInternal(self.Capacity()) + balance_nodes(a, b, key) + if b.Len() > 0 && key.Less(b.keys[0]) { if err := a.put_kp(key, ptr); err != nil { return nil, nil, err } @@ -290,7 +279,7 @@ func (self *BpNode) leaf_insert(key ItemKey, value ItemValue) (a, b *BpNode, err if self.Internal() { return nil, nil, BpTreeError("Expected a leaf node") } - if self.no_dup { + if true { // no_dup = true i, has := self.find(key) if has { self.values[i] = value @@ -321,10 +310,10 @@ func (self *BpNode) leaf_split(key ItemKey, value ItemValue) (a, b *BpNode, err return self.pure_leaf_split(key, value) } a = self - b = NewLeaf(self.NodeSize(), self.no_dup) + b = NewLeaf(self.Capacity()) insert_linked_list_node(b, a, a.getNext()) - balance_nodes(a, b) - if key.Less(b.keys[0]) { + balance_nodes(a, b, key) + if b.Len() > 0 && key.Less(b.keys[0]) { if err := a.put_kv(key, value); err != nil { return nil, nil, err } @@ -353,7 +342,7 @@ func (self *BpNode) pure_leaf_split(key ItemKey, value ItemValue) (a, b *BpNode, return nil, nil, BpTreeError("Expected a pure leaf node") } if key.Less(self.keys[0]) { - a = NewLeaf(self.NodeSize(), self.no_dup) + a = NewLeaf(self.Capacity()) b = self if err := a.put_kv(key, value); err != nil { return nil, nil, err @@ -369,7 +358,7 @@ func (self *BpNode) pure_leaf_split(key ItemKey, value ItemValue) (a, b *BpNode, } return a, nil, a.persist() } else { - b = NewLeaf(self.NodeSize(), self.no_dup) + b = NewLeaf(self.Capacity()) if err := b.put_kv(key, value); err != nil { return nil, nil, err } @@ -604,11 +593,7 @@ func (self *BpNode) find(key ItemKey) (int, bool) { if key.Less(self.keys[m]) { r = m - 1 } else if key.Equals(self.keys[m]) { - for j := m; j >= 0; j-- { - if j == 0 || !key.Equals(self.keys[j-1]) { - return j, true - } - } + return m, true } else { l = m + 1 } @@ -713,9 +698,15 @@ func remove_linked_list_node(n *BpNode) { } } -/* a must be full and b must be empty else there will be a panic +/** + * a must be full and b must be empty else there will be a panic + * + * Different from common btree implementation, this splits the nodes by the inserted key. + * Items less than the splitKey stays in a, or moved to b if otherwise. + * This should help for monotonically increasing inserts. + * */ -func balance_nodes(a, b *BpNode) { +func balance_nodes(a, b *BpNode, splitKey ItemKey) { if len(b.keys) != 0 { panic(BpTreeError("b was not empty")) } @@ -731,16 +722,8 @@ func balance_nodes(a, b *BpNode) { if cap(a.pointers) != cap(b.pointers) { panic(BpTreeError("cap(a.pointers) != cap(b.pointers)")) } - m := len(a.keys) / 2 - for m < len(a.keys) && a.keys[m-1].Equals(a.keys[m]) { - m++ - } - if m == len(a.keys) { - m-- - for m > 0 && a.keys[m-1].Equals(a.keys[m]) { - m-- - } - } + + m := find_split_index(a, b, splitKey) var lim = len(a.keys) - m b.keys = b.keys[:lim] if cap(a.values) > 0 { @@ -773,3 +756,11 @@ func balance_nodes(a, b *BpNode) { a.pointers = a.pointers[:m] } } + +func find_split_index(a, b *BpNode, splitKey ItemKey) int { + m := len(a.keys) + for m > 0 && !a.keys[m-1].Less(splitKey) { + m-- + } + return m +} diff --git a/weed/util/bptree/bptree_store_test.go b/weed/util/bptree/bptree_store_test.go index a5e330aa9..6ed4abca8 100644 --- a/weed/util/bptree/bptree_store_test.go +++ b/weed/util/bptree/bptree_store_test.go @@ -6,7 +6,7 @@ import ( ) func TestAddRemove(t *testing.T) { - tree := NewBpTree(32) + tree := NewBpTree(5) PersistFn = func(node *BpNode) error { println("saving", node.protoNodeId) return nil @@ -24,11 +24,11 @@ func TestAddRemove(t *testing.T) { func printTree(node *BpNode, prefix string) { fmt.Printf("%sNode %d\n", prefix, node.protoNodeId) - prefix += " " + prefix += " " for i:=0;i Date: Sat, 18 Sep 2021 14:04:30 -0700 Subject: [PATCH 033/130] wip --- weed/filer/redis3/kv_directory_children.go | 49 +++++ weed/filer/redis3/redis_cluster_store.go | 42 +++++ weed/filer/redis3/redis_store.go | 36 ++++ weed/filer/redis3/universal_redis_store.go | 175 ++++++++++++++++++ weed/filer/redis3/universal_redis_store_kv.go | 42 +++++ weed/util/bptree/bptree_store_test.go | 14 +- weed/util/bptree/serde.go | 10 + 7 files changed, 366 insertions(+), 2 deletions(-) create mode 100644 weed/filer/redis3/kv_directory_children.go create mode 100644 weed/filer/redis3/redis_cluster_store.go create mode 100644 weed/filer/redis3/redis_store.go create mode 100644 weed/filer/redis3/universal_redis_store.go create mode 100644 weed/filer/redis3/universal_redis_store_kv.go create mode 100644 weed/util/bptree/serde.go diff --git a/weed/filer/redis3/kv_directory_children.go b/weed/filer/redis3/kv_directory_children.go new file mode 100644 index 000000000..f3152c970 --- /dev/null +++ b/weed/filer/redis3/kv_directory_children.go @@ -0,0 +1,49 @@ +package redis3 + +import ( + "context" + "fmt" + "github.com/chrislusf/seaweedfs/weed/util/bptree" + "github.com/go-redis/redis/v8" + "github.com/golang/protobuf/proto" +) + +func insertChild(ctx context.Context, client redis.UniversalClient, key string, name string) error { + data, err := client.Get(ctx, key).Result() + if err != nil { + if err != redis.Nil { + return fmt.Errorf("read %s: %v", key, err) + } + } + rootNode := &bptree.ProtoNode{} + if err := proto.UnmarshalMerge([]byte(data), rootNode); err != nil { + return fmt.Errorf("decoding root for %s: %v", key, err) + } + tree := rootNode.ToBpTree() + tree.Add(bptree.String(name), nil) + return nil +} + +func removeChild(ctx context.Context, client redis.UniversalClient, key string, name string) error { + data, err := client.Get(ctx, key).Result() + if err != nil { + if err != redis.Nil { + return fmt.Errorf("read %s: %v", key, err) + } + } + rootNode := &bptree.ProtoNode{} + if err := proto.UnmarshalMerge([]byte(data), rootNode); err != nil { + return fmt.Errorf("decoding root for %s: %v", key, err) + } + tree := rootNode.ToBpTree() + tree.Add(bptree.String(name), nil) + return nil +} + +func removeChildren(ctx context.Context, client redis.UniversalClient, key string, onDeleteFn func(name string) error) error { + return nil +} + +func iterateChildren(ctx context.Context, client redis.UniversalClient, key string, eachFn func(name string) error) error { + return nil +} diff --git a/weed/filer/redis3/redis_cluster_store.go b/weed/filer/redis3/redis_cluster_store.go new file mode 100644 index 000000000..e0c620450 --- /dev/null +++ b/weed/filer/redis3/redis_cluster_store.go @@ -0,0 +1,42 @@ +package redis3 + +import ( + "github.com/chrislusf/seaweedfs/weed/filer" + "github.com/chrislusf/seaweedfs/weed/util" + "github.com/go-redis/redis/v8" +) + +func init() { + filer.Stores = append(filer.Stores, &RedisCluster3Store{}) +} + +type RedisCluster3Store struct { + UniversalRedis3Store +} + +func (store *RedisCluster3Store) GetName() string { + return "redis_cluster3" +} + +func (store *RedisCluster3Store) Initialize(configuration util.Configuration, prefix string) (err error) { + + configuration.SetDefault(prefix+"useReadOnly", false) + configuration.SetDefault(prefix+"routeByLatency", false) + + return store.initialize( + configuration.GetStringSlice(prefix+"addresses"), + configuration.GetString(prefix+"password"), + configuration.GetBool(prefix+"useReadOnly"), + configuration.GetBool(prefix+"routeByLatency"), + ) +} + +func (store *RedisCluster3Store) initialize(addresses []string, password string, readOnly, routeByLatency bool) (err error) { + store.Client = redis.NewClusterClient(&redis.ClusterOptions{ + Addrs: addresses, + Password: password, + ReadOnly: readOnly, + RouteByLatency: routeByLatency, + }) + return +} diff --git a/weed/filer/redis3/redis_store.go b/weed/filer/redis3/redis_store.go new file mode 100644 index 000000000..fdbf994ec --- /dev/null +++ b/weed/filer/redis3/redis_store.go @@ -0,0 +1,36 @@ +package redis3 + +import ( + "github.com/chrislusf/seaweedfs/weed/filer" + "github.com/chrislusf/seaweedfs/weed/util" + "github.com/go-redis/redis/v8" +) + +func init() { + filer.Stores = append(filer.Stores, &Redis3Store{}) +} + +type Redis3Store struct { + UniversalRedis3Store +} + +func (store *Redis3Store) GetName() string { + return "redis3" +} + +func (store *Redis3Store) Initialize(configuration util.Configuration, prefix string) (err error) { + return store.initialize( + configuration.GetString(prefix+"address"), + configuration.GetString(prefix+"password"), + configuration.GetInt(prefix+"database"), + ) +} + +func (store *Redis3Store) initialize(hostPort string, password string, database int) (err error) { + store.Client = redis.NewClient(&redis.Options{ + Addr: hostPort, + Password: password, + DB: database, + }) + return +} diff --git a/weed/filer/redis3/universal_redis_store.go b/weed/filer/redis3/universal_redis_store.go new file mode 100644 index 000000000..958338afe --- /dev/null +++ b/weed/filer/redis3/universal_redis_store.go @@ -0,0 +1,175 @@ +package redis3 + +import ( + "context" + "fmt" + "time" + + "github.com/go-redis/redis/v8" + + "github.com/chrislusf/seaweedfs/weed/filer" + "github.com/chrislusf/seaweedfs/weed/glog" + "github.com/chrislusf/seaweedfs/weed/pb/filer_pb" + "github.com/chrislusf/seaweedfs/weed/util" +) + +const ( + DIR_LIST_MARKER = "\x00" +) + +type UniversalRedis3Store struct { + Client redis.UniversalClient +} + +func (store *UniversalRedis3Store) BeginTransaction(ctx context.Context) (context.Context, error) { + return ctx, nil +} +func (store *UniversalRedis3Store) CommitTransaction(ctx context.Context) error { + return nil +} +func (store *UniversalRedis3Store) RollbackTransaction(ctx context.Context) error { + return nil +} + +func (store *UniversalRedis3Store) InsertEntry(ctx context.Context, entry *filer.Entry) (err error) { + + value, err := entry.EncodeAttributesAndChunks() + if err != nil { + return fmt.Errorf("encoding %s %+v: %v", entry.FullPath, entry.Attr, err) + } + + if len(entry.Chunks) > 50 { + value = util.MaybeGzipData(value) + } + + if err = store.Client.Set(ctx, string(entry.FullPath), value, time.Duration(entry.TtlSec)*time.Second).Err(); err != nil { + return fmt.Errorf("persisting %s : %v", entry.FullPath, err) + } + + dir, name := entry.FullPath.DirAndName() + + if name != "" { + if err = insertChild(ctx, store.Client, genDirectoryListKey(dir), name); err != nil { + return fmt.Errorf("persisting %s in parent dir: %v", entry.FullPath, err) + } + } + + return nil +} + +func (store *UniversalRedis3Store) UpdateEntry(ctx context.Context, entry *filer.Entry) (err error) { + + return store.InsertEntry(ctx, entry) +} + +func (store *UniversalRedis3Store) FindEntry(ctx context.Context, fullpath util.FullPath) (entry *filer.Entry, err error) { + + data, err := store.Client.Get(ctx, string(fullpath)).Result() + if err == redis.Nil { + return nil, filer_pb.ErrNotFound + } + + if err != nil { + return nil, fmt.Errorf("get %s : %v", fullpath, err) + } + + entry = &filer.Entry{ + FullPath: fullpath, + } + err = entry.DecodeAttributesAndChunks(util.MaybeDecompressData([]byte(data))) + if err != nil { + return entry, fmt.Errorf("decode %s : %v", entry.FullPath, err) + } + + return entry, nil +} + +func (store *UniversalRedis3Store) DeleteEntry(ctx context.Context, fullpath util.FullPath) (err error) { + + _, err = store.Client.Del(ctx, genDirectoryListKey(string(fullpath))).Result() + if err != nil { + return fmt.Errorf("delete dir list %s : %v", fullpath, err) + } + + _, err = store.Client.Del(ctx, string(fullpath)).Result() + if err != nil { + return fmt.Errorf("delete %s : %v", fullpath, err) + } + + dir, name := fullpath.DirAndName() + + if name != "" { + if err = removeChild(ctx, store.Client, genDirectoryListKey(dir), name); err != nil { + return fmt.Errorf("DeleteEntry %s in parent dir: %v", fullpath, err) + } + } + + return nil +} + +func (store *UniversalRedis3Store) DeleteFolderChildren(ctx context.Context, fullpath util.FullPath) (err error) { + + return removeChildren(ctx, store.Client, genDirectoryListKey(string(fullpath)), func(name string) error { + path := util.NewFullPath(string(fullpath), name) + _, err = store.Client.Del(ctx, string(path)).Result() + if err != nil { + return fmt.Errorf("DeleteFolderChildren %s in parent dir: %v", fullpath, err) + } + return nil + }) + +} + +func (store *UniversalRedis3Store) ListDirectoryPrefixedEntries(ctx context.Context, dirPath util.FullPath, startFileName string, includeStartFile bool, limit int64, prefix string, eachEntryFunc filer.ListEachEntryFunc) (lastFileName string, err error) { + return lastFileName, filer.ErrUnsupportedListDirectoryPrefixed +} + +func (store *UniversalRedis3Store) ListDirectoryEntries(ctx context.Context, dirPath util.FullPath, startFileName string, includeStartFile bool, limit int64, eachEntryFunc filer.ListEachEntryFunc) (lastFileName string, err error) { + + dirListKey := genDirectoryListKey(string(dirPath)) + start := int64(0) + if startFileName != "" { + start, _ = store.Client.ZRank(ctx, dirListKey, startFileName).Result() + if !includeStartFile { + start++ + } + } + members, err := store.Client.ZRange(ctx, dirListKey, start, start+int64(limit)-1).Result() + if err != nil { + return lastFileName, fmt.Errorf("list %s : %v", dirPath, err) + } + + // fetch entry meta + for _, fileName := range members { + path := util.NewFullPath(string(dirPath), fileName) + entry, err := store.FindEntry(ctx, path) + lastFileName = fileName + if err != nil { + glog.V(0).Infof("list %s : %v", path, err) + if err == filer_pb.ErrNotFound { + continue + } + } else { + if entry.TtlSec > 0 { + if entry.Attr.Crtime.Add(time.Duration(entry.TtlSec) * time.Second).Before(time.Now()) { + store.Client.Del(ctx, string(path)).Result() + store.Client.ZRem(ctx, dirListKey, fileName).Result() + continue + } + } + if !eachEntryFunc(entry) { + break + } + } + } + + return lastFileName, err +} + +func genDirectoryListKey(dir string) (dirList string) { + return dir + DIR_LIST_MARKER +} + +func (store *UniversalRedis3Store) Shutdown() { + store.Client.Close() +} diff --git a/weed/filer/redis3/universal_redis_store_kv.go b/weed/filer/redis3/universal_redis_store_kv.go new file mode 100644 index 000000000..a9c440a37 --- /dev/null +++ b/weed/filer/redis3/universal_redis_store_kv.go @@ -0,0 +1,42 @@ +package redis3 + +import ( + "context" + "fmt" + + "github.com/chrislusf/seaweedfs/weed/filer" + "github.com/go-redis/redis/v8" +) + +func (store *UniversalRedis3Store) KvPut(ctx context.Context, key []byte, value []byte) (err error) { + + _, err = store.Client.Set(ctx, string(key), value, 0).Result() + + if err != nil { + return fmt.Errorf("kv put: %v", err) + } + + return nil +} + +func (store *UniversalRedis3Store) KvGet(ctx context.Context, key []byte) (value []byte, err error) { + + data, err := store.Client.Get(ctx, string(key)).Result() + + if err == redis.Nil { + return nil, filer.ErrKvNotFound + } + + return []byte(data), err +} + +func (store *UniversalRedis3Store) KvDelete(ctx context.Context, key []byte) (err error) { + + _, err = store.Client.Del(ctx, string(key)).Result() + + if err != nil { + return fmt.Errorf("kv delete: %v", err) + } + + return nil +} diff --git a/weed/util/bptree/bptree_store_test.go b/weed/util/bptree/bptree_store_test.go index 6ed4abca8..82dcbbf55 100644 --- a/weed/util/bptree/bptree_store_test.go +++ b/weed/util/bptree/bptree_store_test.go @@ -15,11 +15,21 @@ func TestAddRemove(t *testing.T) { println("delete", node.protoNodeId) return nil } - for i:=0;i<1024;i++{ + for i:=0;i<32;i++{ println("++++++++++", i) - tree.Add(String(fmt.Sprintf("%02d", i)), String(fmt.Sprintf("%02d", i))) + tree.Add(String(fmt.Sprintf("%02d", i)), nil) printTree(tree.root, "") } + + if !tree.Has(String("30")) { + t.Errorf("lookup error") + } + tree.RemoveWhere(String("30"), func(value ItemValue) bool { + return true + }) + if tree.Has(String("30")) { + t.Errorf("remove error") + } } func printTree(node *BpNode, prefix string) { diff --git a/weed/util/bptree/serde.go b/weed/util/bptree/serde.go new file mode 100644 index 000000000..2a98a774a --- /dev/null +++ b/weed/util/bptree/serde.go @@ -0,0 +1,10 @@ +package bptree + +func (protoNode *ProtoNode) ToBpTree() *BpTree { + node := protoNode.ToBpNode() + return &BpTree{root: node} +} + +func (protoNode *ProtoNode) ToBpNode() *BpNode { + return nil +} \ No newline at end of file From 49d971e602af60cfe9f7a716cca7172e7d415ba4 Mon Sep 17 00:00:00 2001 From: Chris Lu Date: Sat, 18 Sep 2021 14:05:16 -0700 Subject: [PATCH 034/130] filer: redis store needs to clean up batch deleted sub folders --- weed/filer/redis/universal_redis_store.go | 2 ++ weed/filer/redis2/universal_redis_store.go | 2 ++ 2 files changed, 4 insertions(+) diff --git a/weed/filer/redis/universal_redis_store.go b/weed/filer/redis/universal_redis_store.go index 30d11a7f4..2cc7f49b1 100644 --- a/weed/filer/redis/universal_redis_store.go +++ b/weed/filer/redis/universal_redis_store.go @@ -120,6 +120,8 @@ func (store *UniversalRedisStore) DeleteFolderChildren(ctx context.Context, full if err != nil { return fmt.Errorf("delete %s in parent dir: %v", fullpath, err) } + // not efficient, but need to remove if it is a directory + store.Client.Del(ctx, genDirectoryListKey(string(path))) } return nil diff --git a/weed/filer/redis2/universal_redis_store.go b/weed/filer/redis2/universal_redis_store.go index aab3d1f4a..ecf68a9ee 100644 --- a/weed/filer/redis2/universal_redis_store.go +++ b/weed/filer/redis2/universal_redis_store.go @@ -144,6 +144,8 @@ func (store *UniversalRedis2Store) DeleteFolderChildren(ctx context.Context, ful if err != nil { return fmt.Errorf("DeleteFolderChildren %s in parent dir: %v", fullpath, err) } + // not efficient, but need to remove if it is a directory + store.Client.Del(ctx, genDirectoryListKey(string(path))) } return nil From e066e2642ce8cd99854042773abb34616695c4fc Mon Sep 17 00:00:00 2001 From: Chris Lu Date: Sat, 18 Sep 2021 15:32:17 -0700 Subject: [PATCH 035/130] add NodeStore --- weed/util/bptree/bpmap.go | 6 +- weed/util/bptree/bptree.go | 6 +- weed/util/bptree/bptree_node.go | 27 +++---- weed/util/bptree/bptree_store_test.go | 39 ++++++---- weed/util/bptree/bptree_test.go | 106 +++++++++++++------------- weed/util/bptree/getter_setter.go | 8 +- weed/util/bptree/serde_test.go | 46 +++++++++++ 7 files changed, 147 insertions(+), 91 deletions(-) create mode 100644 weed/util/bptree/serde_test.go diff --git a/weed/util/bptree/bpmap.go b/weed/util/bptree/bpmap.go index 399ac7b86..0c13a132f 100644 --- a/weed/util/bptree/bpmap.go +++ b/weed/util/bptree/bpmap.go @@ -9,9 +9,9 @@ import ( */ type BpMap BpTree -func NewBpMap(node_size int) *BpMap { +func NewBpMap(node_size int, nodeStore NodeStore) *BpMap { return &BpMap{ - root: NewLeaf(node_size), + root: NewLeaf(node_size, nodeStore), } } @@ -47,7 +47,7 @@ func (self *BpMap) Remove(key ItemKey) (value ItemValue, err error) { return nil, err } if new_root == nil { - new_root = NewLeaf(ns) + new_root = NewLeaf(ns, self.root.nodeStore) err = new_root.persist() self.setRoot(new_root) } else { diff --git a/weed/util/bptree/bptree.go b/weed/util/bptree/bptree.go index 3ad73ad30..141c595f3 100644 --- a/weed/util/bptree/bptree.go +++ b/weed/util/bptree/bptree.go @@ -12,9 +12,9 @@ type BpTree struct { type loc_iterator func() (i int, leaf *BpNode, li loc_iterator) -func NewBpTree(node_size int) *BpTree { +func NewBpTree(node_size int, nodeStore NodeStore) *BpTree { return &BpTree{ - root: NewLeaf(node_size), + root: NewLeaf(node_size, nodeStore), } } @@ -93,7 +93,7 @@ func (self *BpTree) RemoveWhere(key ItemKey, where WhereFunc) (err error) { return err } if new_root == nil { - new_root = NewLeaf(ns) + new_root = NewLeaf(ns, self.root.nodeStore) err = new_root.persist() self.setRoot(new_root) } else { diff --git a/weed/util/bptree/bptree_node.go b/weed/util/bptree/bptree_node.go index 5c3461cfd..507d9d318 100644 --- a/weed/util/bptree/bptree_node.go +++ b/weed/util/bptree/bptree_node.go @@ -2,13 +2,11 @@ package bptree type ItemKey Hashable type ItemValue Equatable -type PersistFunc func(node *BpNode) error -type DestroyFunc func(node *BpNode) error -var ( - PersistFn PersistFunc - DestroyFn DestroyFunc -) +type NodeStore interface { + PersistFunc(node *BpNode) error + DestroyFunc(node *BpNode) error +} type BpNode struct { keys []ItemKey @@ -18,9 +16,10 @@ type BpNode struct { prev *BpNode protoNodeId int64 protoNode *ProtoNode + nodeStore NodeStore } -func NewInternal(size int) *BpNode { +func NewInternal(size int, nodeStore NodeStore) *BpNode { if size < 0 { panic(NegativeSize()) } @@ -28,10 +27,11 @@ func NewInternal(size int) *BpNode { keys: make([]ItemKey, 0, size), pointers: make([]*BpNode, 0, size), protoNodeId: GetProtoNodeId(), + nodeStore: nodeStore, } } -func NewLeaf(size int) *BpNode { +func NewLeaf(size int, nodeStore NodeStore) *BpNode { if size < 0 { panic(NegativeSize()) } @@ -39,6 +39,7 @@ func NewLeaf(size int) *BpNode { keys: make([]ItemKey, 0, size), values: make([]ItemValue, 0, size), protoNodeId: GetProtoNodeId(), + nodeStore: nodeStore, } } @@ -187,7 +188,7 @@ func (self *BpNode) put(key ItemKey, value ItemValue) (root *BpNode, err error) return a, nil } // else we have root split - root = NewInternal(self.Capacity()) + root = NewInternal(self.Capacity(), self.nodeStore) root.put_kp(a.keys[0], a) root.put_kp(b.keys[0], b) return root, root.persist() @@ -256,7 +257,7 @@ func (self *BpNode) internal_split(key ItemKey, ptr *BpNode) (a, b *BpNode, err return nil, nil, BpTreeError("Tried to split an internal block on duplicate key") } a = self - b = NewInternal(self.Capacity()) + b = NewInternal(self.Capacity(), self.nodeStore) balance_nodes(a, b, key) if b.Len() > 0 && key.Less(b.keys[0]) { if err := a.put_kp(key, ptr); err != nil { @@ -310,7 +311,7 @@ func (self *BpNode) leaf_split(key ItemKey, value ItemValue) (a, b *BpNode, err return self.pure_leaf_split(key, value) } a = self - b = NewLeaf(self.Capacity()) + b = NewLeaf(self.Capacity(), self.nodeStore) insert_linked_list_node(b, a, a.getNext()) balance_nodes(a, b, key) if b.Len() > 0 && key.Less(b.keys[0]) { @@ -342,7 +343,7 @@ func (self *BpNode) pure_leaf_split(key ItemKey, value ItemValue) (a, b *BpNode, return nil, nil, BpTreeError("Expected a pure leaf node") } if key.Less(self.keys[0]) { - a = NewLeaf(self.Capacity()) + a = NewLeaf(self.Capacity(), self.nodeStore) b = self if err := a.put_kv(key, value); err != nil { return nil, nil, err @@ -358,7 +359,7 @@ func (self *BpNode) pure_leaf_split(key ItemKey, value ItemValue) (a, b *BpNode, } return a, nil, a.persist() } else { - b = NewLeaf(self.Capacity()) + b = NewLeaf(self.Capacity(), self.nodeStore) if err := b.put_kv(key, value); err != nil { return nil, nil, err } diff --git a/weed/util/bptree/bptree_store_test.go b/weed/util/bptree/bptree_store_test.go index 82dcbbf55..2e034171c 100644 --- a/weed/util/bptree/bptree_store_test.go +++ b/weed/util/bptree/bptree_store_test.go @@ -5,29 +5,38 @@ import ( "testing" ) +type nodeStorePrintlnImpl struct { +} + +func (n *nodeStorePrintlnImpl) PersistFunc(node *BpNode) error { + println("saving node", node.protoNodeId) + return nil +} +func (n *nodeStorePrintlnImpl) DestroyFunc(node *BpNode) error { + println("delete node", node.protoNodeId) + return nil +} + func TestAddRemove(t *testing.T) { - tree := NewBpTree(5) - PersistFn = func(node *BpNode) error { - println("saving", node.protoNodeId) - return nil - } - DestroyFn = func(node *BpNode) error { - println("delete", node.protoNodeId) - return nil - } - for i:=0;i<32;i++{ + + tree := NewBpTree(3, &nodeStorePrintlnImpl{}) + for i:=0;i<9;i++{ println("++++++++++", i) tree.Add(String(fmt.Sprintf("%02d", i)), nil) printTree(tree.root, "") } - if !tree.Has(String("30")) { + if !tree.Has(String("08")) { t.Errorf("lookup error") } - tree.RemoveWhere(String("30"), func(value ItemValue) bool { - return true - }) - if tree.Has(String("30")) { + for i:=5;i<9;i++{ + println("----------", i) + tree.RemoveWhere(String(fmt.Sprintf("%02d", i)), func(value ItemValue) bool { + return true + }) + printTree(tree.root, "") + } + if tree.Has(String("08")) { t.Errorf("remove error") } } diff --git a/weed/util/bptree/bptree_test.go b/weed/util/bptree/bptree_test.go index fc5b6f900..1fd6d1122 100644 --- a/weed/util/bptree/bptree_test.go +++ b/weed/util/bptree/bptree_test.go @@ -79,7 +79,7 @@ func BenchmarkBpTree(b *testing.B) { b.StartTimer() for i := 0; i < b.N; i++ { - t := NewBpTree(23) + t := NewBpTree(23, nil) for _, r := range recs { t.Add(r.key, r.value) } @@ -207,7 +207,7 @@ func TestAddHasCountFindIterateRemove(t *testing.T) { } } for i := 2; i < 64; i++ { - test(NewBpTree(i)) + test(NewBpTree(i, nil)) } } @@ -271,11 +271,11 @@ func TestBpMap(t *testing.T) { } } - test(NewBpMap(23)) + test(NewBpMap(23, nil)) } func Test_get_start(t *testing.T) { - root := NewLeaf(2) + root := NewLeaf(2, nil) root, err := root.put(Int(1), Int(1)) if err != nil { t.Error(err) @@ -344,7 +344,7 @@ func Test_get_start(t *testing.T) { } func Test_get_end(t *testing.T) { - root := NewLeaf(3) + root := NewLeaf(3, nil) root, err := root.put(Int(1), Int(1)) if err != nil { t.Fatal(err) @@ -388,7 +388,7 @@ func Test_get_end(t *testing.T) { } func Test_put_no_root_split(t *testing.T) { - a := NewLeaf(2) + a := NewLeaf(2, nil) if err := a.put_kv(Int(1), Int(1)); err != nil { t.Error(err) } @@ -423,7 +423,7 @@ func Test_put_no_root_split(t *testing.T) { } func Test_put_root_split(t *testing.T) { - a := NewLeaf(2) + a := NewLeaf(2, nil) p, err := a.put(Int(1), Int(1)) if err != nil { t.Error(err) @@ -472,8 +472,8 @@ func Test_put_root_split(t *testing.T) { } func Test_internal_insert_no_split(t *testing.T) { - a := NewInternal(3) - leaf := NewLeaf(1) + a := NewInternal(3, nil) + leaf := NewLeaf(1, nil) if err := leaf.put_kv(Int(1), Int(1)); err != nil { t.Error(err) } @@ -500,8 +500,8 @@ func Test_internal_insert_no_split(t *testing.T) { } func Test_internal_insert_split_less(t *testing.T) { - a := NewInternal(3) - leaf := NewLeaf(1) + a := NewInternal(3, nil) + leaf := NewLeaf(1, nil) if err := leaf.put_kv(Int(1), Int(1)); err != nil { t.Error(err) } @@ -534,7 +534,7 @@ func Test_internal_insert_split_less(t *testing.T) { } func Test_internal_split_less(t *testing.T) { - a := NewInternal(3) + a := NewInternal(3, nil) if err := a.put_kp(Int(1), nil); err != nil { t.Error(err) } @@ -564,7 +564,7 @@ func Test_internal_split_less(t *testing.T) { } func Test_internal_split_equal(t *testing.T) { - a := NewInternal(3) + a := NewInternal(3, nil) if err := a.put_kp(Int(1), nil); err != nil { t.Error(err) } @@ -581,7 +581,7 @@ func Test_internal_split_equal(t *testing.T) { } func Test_internal_split_greater(t *testing.T) { - a := NewInternal(3) + a := NewInternal(3, nil) if err := a.put_kp(Int(1), nil); err != nil { t.Error(err) } @@ -611,7 +611,7 @@ func Test_internal_split_greater(t *testing.T) { } func Test_leaf_insert_no_split(t *testing.T) { - a := NewLeaf(3) + a := NewLeaf(3, nil) insert_linked_list_node(a, nil, nil) if err := a.put_kv(Int(1), Int(1)); err != nil { t.Error(err) @@ -637,7 +637,7 @@ func Test_leaf_insert_no_split(t *testing.T) { // tests the defer to split logic func Test_leaf_insert_split_less(t *testing.T) { - a := NewLeaf(3) + a := NewLeaf(3, nil) insert_linked_list_node(a, nil, nil) if err := a.put_kv(Int(1), Int(1)); err != nil { t.Error(err) @@ -668,7 +668,7 @@ func Test_leaf_insert_split_less(t *testing.T) { } func Test_leaf_split_less(t *testing.T) { - a := NewLeaf(3) + a := NewLeaf(3, nil) insert_linked_list_node(a, nil, nil) if err := a.put_kv(Int(1), Int(1)); err != nil { t.Error(err) @@ -699,7 +699,7 @@ func Test_leaf_split_less(t *testing.T) { } func Test_leaf_split_equal(t *testing.T) { - a := NewLeaf(3) + a := NewLeaf(3, nil) insert_linked_list_node(a, nil, nil) if err := a.put_kv(Int(1), Int(1)); err != nil { t.Error(err) @@ -730,7 +730,7 @@ func Test_leaf_split_equal(t *testing.T) { } func Test_leaf_split_greater(t *testing.T) { - a := NewLeaf(3) + a := NewLeaf(3, nil) insert_linked_list_node(a, nil, nil) if err := a.put_kv(Int(1), Int(1)); err != nil { t.Error(err) @@ -762,13 +762,13 @@ func Test_leaf_split_greater(t *testing.T) { // tests the defer logic func Test_pure_leaf_insert_split_less(t *testing.T) { - a := NewLeaf(2) + a := NewLeaf(2, nil) insert_linked_list_node(a, nil, nil) - b := NewLeaf(2) + b := NewLeaf(2, nil) insert_linked_list_node(b, a, nil) - c := NewLeaf(2) + c := NewLeaf(2, nil) insert_linked_list_node(c, b, nil) - d := NewLeaf(2) + d := NewLeaf(2, nil) insert_linked_list_node(d, c, nil) if err := a.put_kv(Int(3), Int(1)); err != nil { t.Error(err) @@ -835,13 +835,13 @@ func Test_pure_leaf_insert_split_less(t *testing.T) { } func Test_pure_leaf_split_less(t *testing.T) { - a := NewLeaf(2) + a := NewLeaf(2, nil) insert_linked_list_node(a, nil, nil) - b := NewLeaf(2) + b := NewLeaf(2, nil) insert_linked_list_node(b, a, nil) - c := NewLeaf(2) + c := NewLeaf(2, nil) insert_linked_list_node(c, b, nil) - d := NewLeaf(2) + d := NewLeaf(2, nil) insert_linked_list_node(d, c, nil) if err := a.put_kv(Int(3), Int(1)); err != nil { t.Error(err) @@ -908,13 +908,13 @@ func Test_pure_leaf_split_less(t *testing.T) { } func Test_pure_leaf_split_equal(t *testing.T) { - a := NewLeaf(2) + a := NewLeaf(2, nil) insert_linked_list_node(a, nil, nil) - b := NewLeaf(2) + b := NewLeaf(2, nil) insert_linked_list_node(b, a, nil) - c := NewLeaf(2) + c := NewLeaf(2, nil) insert_linked_list_node(c, b, nil) - d := NewLeaf(2) + d := NewLeaf(2, nil) insert_linked_list_node(d, c, nil) if err := a.put_kv(Int(3), Int(1)); err != nil { t.Error(err) @@ -972,13 +972,13 @@ func Test_pure_leaf_split_equal(t *testing.T) { } func Test_pure_leaf_split_greater(t *testing.T) { - a := NewLeaf(2) + a := NewLeaf(2, nil) insert_linked_list_node(a, nil, nil) - b := NewLeaf(2) + b := NewLeaf(2, nil) insert_linked_list_node(b, a, nil) - c := NewLeaf(2) + c := NewLeaf(2, nil) insert_linked_list_node(c, b, nil) - d := NewLeaf(2) + d := NewLeaf(2, nil) insert_linked_list_node(d, c, nil) if err := a.put_kv(Int(3), Int(1)); err != nil { t.Error(err) @@ -1042,13 +1042,13 @@ func Test_pure_leaf_split_greater(t *testing.T) { } func Test_find_end_of_pure_run(t *testing.T) { - a := NewLeaf(2) + a := NewLeaf(2, nil) insert_linked_list_node(a, nil, nil) - b := NewLeaf(2) + b := NewLeaf(2, nil) insert_linked_list_node(b, a, nil) - c := NewLeaf(2) + c := NewLeaf(2, nil) insert_linked_list_node(c, b, nil) - d := NewLeaf(2) + d := NewLeaf(2, nil) insert_linked_list_node(d, c, nil) if err := a.put_kv(Int(3), Int(1)); err != nil { t.Error(err) @@ -1078,13 +1078,13 @@ func Test_find_end_of_pure_run(t *testing.T) { } func Test_insert_linked_list_node(t *testing.T) { - a := NewLeaf(1) + a := NewLeaf(1, nil) insert_linked_list_node(a, nil, nil) - b := NewLeaf(2) + b := NewLeaf(2, nil) insert_linked_list_node(b, a, nil) - c := NewLeaf(3) + c := NewLeaf(3, nil) insert_linked_list_node(c, b, nil) - d := NewLeaf(4) + d := NewLeaf(4, nil) insert_linked_list_node(d, a, b) if a.getPrev() != nil { t.Errorf("expected a.prev == nil") @@ -1113,13 +1113,13 @@ func Test_insert_linked_list_node(t *testing.T) { } func Test_remove_linked_list_node(t *testing.T) { - a := NewLeaf(1) + a := NewLeaf(1, nil) insert_linked_list_node(a, nil, nil) - b := NewLeaf(2) + b := NewLeaf(2, nil) insert_linked_list_node(b, a, nil) - c := NewLeaf(3) + c := NewLeaf(3, nil) insert_linked_list_node(c, b, nil) - d := NewLeaf(4) + d := NewLeaf(4, nil) insert_linked_list_node(d, a, b) if a.getPrev() != nil { t.Errorf("expected a.prev == nil") @@ -1188,8 +1188,8 @@ func Test_remove_linked_list_node(t *testing.T) { } func Test_balance_leaf_nodes_with_dup(t *testing.T) { - a := NewLeaf(3) - b := NewLeaf(3) + a := NewLeaf(3, nil) + b := NewLeaf(3, nil) if err := a.put_kv(Int(1), Int(1)); err != nil { t.Error(err) } @@ -1209,8 +1209,8 @@ func Test_balance_leaf_nodes_with_dup(t *testing.T) { } func Test_balance_leaf_nodes(t *testing.T) { - a := NewLeaf(7) - b := NewLeaf(7) + a := NewLeaf(7, nil) + b := NewLeaf(7, nil) if err := a.put_kv(Int(1), Int(1)); err != nil { t.Error(err) } @@ -1258,8 +1258,8 @@ func Test_balance_leaf_nodes(t *testing.T) { } func Test_balance_internal_nodes(t *testing.T) { - a := NewInternal(6) - b := NewInternal(6) + a := NewInternal(6, nil) + b := NewInternal(6, nil) if err := a.put_kp(Int(1), nil); err != nil { t.Error(err) } diff --git a/weed/util/bptree/getter_setter.go b/weed/util/bptree/getter_setter.go index caafc1bbd..dcaa7a0b6 100644 --- a/weed/util/bptree/getter_setter.go +++ b/weed/util/bptree/getter_setter.go @@ -45,14 +45,14 @@ func (self *BpNode) maybePersist(shouldPersist bool) error { return self.persist() } func (self *BpNode) persist() error { - if PersistFn != nil { - return PersistFn(self) + if self.nodeStore != nil { + return self.nodeStore.PersistFunc(self) } return nil } func (self *BpNode) destroy() error { - if DestroyFn != nil { - return DestroyFn(self) + if self.nodeStore != nil { + return self.nodeStore.DestroyFunc(self) } return nil } diff --git a/weed/util/bptree/serde_test.go b/weed/util/bptree/serde_test.go new file mode 100644 index 000000000..27ccccb78 --- /dev/null +++ b/weed/util/bptree/serde_test.go @@ -0,0 +1,46 @@ +package bptree + +import ( + "fmt" + "testing" +) + +type nodeStoreMapImpl struct { + m map[int64]*ProtoNode +} + +func (n *nodeStoreMapImpl) PersistFunc(node *BpNode) error { + println("saving node", node.protoNodeId) + n.m[node.protoNodeId] = node.protoNode + return nil +} +func (n *nodeStoreMapImpl) DestroyFunc(node *BpNode) error { + println("delete node", node.protoNodeId) + delete(n.m, node.protoNodeId) + return nil +} + +func TestSerDe(t *testing.T) { + + nodeStore := &nodeStoreMapImpl{ + m: make(map[int64]*ProtoNode), + } + + tree := NewBpTree(3, nodeStore) + + for i:=0;i<32;i++{ + println("add", i) + tree.Add(String(fmt.Sprintf("%02d", i)), nil) + } + + for i:=5;i<9;i++{ + println("----------", i) + tree.RemoveWhere(String(fmt.Sprintf("%02d", i)), func(value ItemValue) bool { + return true + }) + printTree(tree.root, "") + } + + + +} \ No newline at end of file From 71175461ef4216645ab4a29b253a4f24bb0f5e2d Mon Sep 17 00:00:00 2001 From: Chris Lu Date: Sun, 19 Sep 2021 00:18:59 -0700 Subject: [PATCH 036/130] add glog for s3 handlers --- weed/s3api/s3api_bucket_handlers.go | 5 +++++ weed/s3api/s3api_object_copy_handlers.go | 4 ++++ weed/s3api/s3api_object_handlers.go | 4 +++- weed/s3api/s3api_object_handlers_postpolicy.go | 3 +++ weed/s3api/s3api_object_tagging_handlers.go | 3 +++ weed/s3api/s3api_objects_list_handlers.go | 2 ++ 6 files changed, 20 insertions(+), 1 deletion(-) diff --git a/weed/s3api/s3api_bucket_handlers.go b/weed/s3api/s3api_bucket_handlers.go index 8beb954aa..024c90c8a 100644 --- a/weed/s3api/s3api_bucket_handlers.go +++ b/weed/s3api/s3api_bucket_handlers.go @@ -27,6 +27,8 @@ type ListAllMyBucketsResult struct { func (s3a *S3ApiServer) ListBucketsHandler(w http.ResponseWriter, r *http.Request) { + glog.V(3).Infof("ListBucketsHandler") + var identity *Identity var s3Err s3err.ErrorCode if s3a.iam.isEnabled() { @@ -75,6 +77,7 @@ func (s3a *S3ApiServer) ListBucketsHandler(w http.ResponseWriter, r *http.Reques func (s3a *S3ApiServer) PutBucketHandler(w http.ResponseWriter, r *http.Request) { bucket, _ := getBucketAndObject(r) + glog.V(3).Infof("PutBucketHandler %s", bucket) // avoid duplicated buckets errCode := s3err.ErrNone @@ -128,6 +131,7 @@ func (s3a *S3ApiServer) PutBucketHandler(w http.ResponseWriter, r *http.Request) func (s3a *S3ApiServer) DeleteBucketHandler(w http.ResponseWriter, r *http.Request) { bucket, _ := getBucketAndObject(r) + glog.V(3).Infof("DeleteBucketHandler %s", bucket) if err := s3a.checkBucket(r, bucket); err != s3err.ErrNone { s3err.WriteErrorResponse(w, err, r) @@ -162,6 +166,7 @@ func (s3a *S3ApiServer) DeleteBucketHandler(w http.ResponseWriter, r *http.Reque func (s3a *S3ApiServer) HeadBucketHandler(w http.ResponseWriter, r *http.Request) { bucket, _ := getBucketAndObject(r) + glog.V(3).Infof("HeadBucketHandler %s", bucket) if err := s3a.checkBucket(r, bucket); err != s3err.ErrNone { s3err.WriteErrorResponse(w, err, r) diff --git a/weed/s3api/s3api_object_copy_handlers.go b/weed/s3api/s3api_object_copy_handlers.go index ba49e4a8e..59040997c 100644 --- a/weed/s3api/s3api_object_copy_handlers.go +++ b/weed/s3api/s3api_object_copy_handlers.go @@ -27,6 +27,8 @@ func (s3a *S3ApiServer) CopyObjectHandler(w http.ResponseWriter, r *http.Request srcBucket, srcObject := pathToBucketAndObject(cpSrcPath) + glog.V(3).Infof("CopyObjectHandler %s %s => %s %s", srcBucket, srcObject, dstBucket, dstObject) + if (srcBucket == dstBucket && srcObject == dstObject || cpSrcPath == "") && isReplace(r) { fullPath := util.FullPath(fmt.Sprintf("%s/%s%s", s3a.option.BucketsPath, dstBucket, dstObject)) dir, name := fullPath.DirAndName() @@ -139,6 +141,8 @@ func (s3a *S3ApiServer) CopyObjectPartHandler(w http.ResponseWriter, r *http.Req return } + glog.V(3).Infof("CopyObjectPartHandler %s %s => %s part %d", srcBucket, srcObject, dstBucket, partID) + // check partID with maximum part ID for multipart objects if partID > globalMaxPartID { s3err.WriteErrorResponse(w, s3err.ErrInvalidMaxParts, r) diff --git a/weed/s3api/s3api_object_handlers.go b/weed/s3api/s3api_object_handlers.go index afc5c7ca9..2e4ef5a30 100644 --- a/weed/s3api/s3api_object_handlers.go +++ b/weed/s3api/s3api_object_handlers.go @@ -193,6 +193,8 @@ func (s3a *S3ApiServer) DeleteMultipleObjectsHandler(w http.ResponseWriter, r *h bucket, _ := getBucketAndObject(r) + glog.V(3).Infof("DeleteMultipleObjectsHandler %s", bucket) + deleteXMLBytes, err := ioutil.ReadAll(r.Body) if err != nil { s3err.WriteErrorResponse(w, s3err.ErrInternalError, r) @@ -291,7 +293,7 @@ var passThroughHeaders = []string{ func (s3a *S3ApiServer) proxyToFiler(w http.ResponseWriter, r *http.Request, destUrl string, responseFn func(proxyResponse *http.Response, w http.ResponseWriter)) { - glog.V(2).Infof("s3 proxying %s to %s", r.Method, destUrl) + glog.V(3).Infof("s3 proxying %s to %s", r.Method, destUrl) proxyReq, err := http.NewRequest(r.Method, destUrl, r.Body) diff --git a/weed/s3api/s3api_object_handlers_postpolicy.go b/weed/s3api/s3api_object_handlers_postpolicy.go index 0e4658ef4..c0e2589ae 100644 --- a/weed/s3api/s3api_object_handlers_postpolicy.go +++ b/weed/s3api/s3api_object_handlers_postpolicy.go @@ -5,6 +5,7 @@ import ( "encoding/base64" "errors" "fmt" + "github.com/chrislusf/seaweedfs/weed/glog" "github.com/chrislusf/seaweedfs/weed/s3api/policy" "github.com/chrislusf/seaweedfs/weed/s3api/s3err" "github.com/dustin/go-humanize" @@ -24,6 +25,8 @@ func (s3a *S3ApiServer) PostPolicyBucketHandler(w http.ResponseWriter, r *http.R bucket := mux.Vars(r)["bucket"] + glog.V(3).Infof("PostPolicyBucketHandler %s", bucket) + reader, err := r.MultipartReader() if err != nil { s3err.WriteErrorResponse(w, s3err.ErrMalformedPOSTRequest, r) diff --git a/weed/s3api/s3api_object_tagging_handlers.go b/weed/s3api/s3api_object_tagging_handlers.go index fd3ec2ff7..2ee339e29 100644 --- a/weed/s3api/s3api_object_tagging_handlers.go +++ b/weed/s3api/s3api_object_tagging_handlers.go @@ -17,6 +17,7 @@ import ( func (s3a *S3ApiServer) GetObjectTaggingHandler(w http.ResponseWriter, r *http.Request) { bucket, object := getBucketAndObject(r) + glog.V(3).Infof("GetObjectTaggingHandler %s %s", bucket, object) target := util.FullPath(fmt.Sprintf("%s/%s%s", s3a.option.BucketsPath, bucket, object)) dir, name := target.DirAndName() @@ -42,6 +43,7 @@ func (s3a *S3ApiServer) GetObjectTaggingHandler(w http.ResponseWriter, r *http.R func (s3a *S3ApiServer) PutObjectTaggingHandler(w http.ResponseWriter, r *http.Request) { bucket, object := getBucketAndObject(r) + glog.V(3).Infof("PutObjectTaggingHandler %s %s", bucket, object) target := util.FullPath(fmt.Sprintf("%s/%s%s", s3a.option.BucketsPath, bucket, object)) dir, name := target.DirAndName() @@ -97,6 +99,7 @@ func (s3a *S3ApiServer) PutObjectTaggingHandler(w http.ResponseWriter, r *http.R func (s3a *S3ApiServer) DeleteObjectTaggingHandler(w http.ResponseWriter, r *http.Request) { bucket, object := getBucketAndObject(r) + glog.V(3).Infof("DeleteObjectTaggingHandler %s %s", bucket, object) target := util.FullPath(fmt.Sprintf("%s/%s%s", s3a.option.BucketsPath, bucket, object)) dir, name := target.DirAndName() diff --git a/weed/s3api/s3api_objects_list_handlers.go b/weed/s3api/s3api_objects_list_handlers.go index 51a58af6a..c1d226e32 100644 --- a/weed/s3api/s3api_objects_list_handlers.go +++ b/weed/s3api/s3api_objects_list_handlers.go @@ -40,6 +40,7 @@ func (s3a *S3ApiServer) ListObjectsV2Handler(w http.ResponseWriter, r *http.Requ // collect parameters bucket, _ := getBucketAndObject(r) + glog.V(3).Infof("ListObjectsV2Handler %s", bucket) originalPrefix, continuationToken, startAfter, delimiter, _, maxKeys := getListObjectsV2Args(r.URL.Query()) @@ -95,6 +96,7 @@ func (s3a *S3ApiServer) ListObjectsV1Handler(w http.ResponseWriter, r *http.Requ // collect parameters bucket, _ := getBucketAndObject(r) + glog.V(3).Infof("ListObjectsV1Handler %s", bucket) originalPrefix, marker, delimiter, maxKeys := getListObjectsV1Args(r.URL.Query()) From 4cbba2b1c67a86bed3f05e7cc79e9a06f14a5a63 Mon Sep 17 00:00:00 2001 From: Chris Lu Date: Sun, 19 Sep 2021 00:28:22 -0700 Subject: [PATCH 037/130] add more glog to s3 --- weed/s3api/s3api_object_handlers.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/weed/s3api/s3api_object_handlers.go b/weed/s3api/s3api_object_handlers.go index 2e4ef5a30..4130d13c3 100644 --- a/weed/s3api/s3api_object_handlers.go +++ b/weed/s3api/s3api_object_handlers.go @@ -118,6 +118,7 @@ func urlPathEscape(object string) string { func (s3a *S3ApiServer) GetObjectHandler(w http.ResponseWriter, r *http.Request) { bucket, object := getBucketAndObject(r) + glog.V(3).Infof("GetObjectHandler %s %s", bucket, object) if strings.HasSuffix(r.URL.Path, "/") { s3err.WriteErrorResponse(w, s3err.ErrNotImplemented, r) @@ -134,6 +135,7 @@ func (s3a *S3ApiServer) GetObjectHandler(w http.ResponseWriter, r *http.Request) func (s3a *S3ApiServer) HeadObjectHandler(w http.ResponseWriter, r *http.Request) { bucket, object := getBucketAndObject(r) + glog.V(3).Infof("HeadObjectHandler %s %s", bucket, object) destUrl := fmt.Sprintf("http://%s%s/%s%s", s3a.option.Filer.ToHttpAddress(), s3a.option.BucketsPath, bucket, urlPathEscape(object)) @@ -145,6 +147,7 @@ func (s3a *S3ApiServer) HeadObjectHandler(w http.ResponseWriter, r *http.Request func (s3a *S3ApiServer) DeleteObjectHandler(w http.ResponseWriter, r *http.Request) { bucket, object := getBucketAndObject(r) + glog.V(3).Infof("DeleteObjectHandler %s %s", bucket, object) destUrl := fmt.Sprintf("http://%s%s/%s%s?recursive=true", s3a.option.Filer.ToHttpAddress(), s3a.option.BucketsPath, bucket, urlPathEscape(object)) @@ -192,7 +195,6 @@ type DeleteObjectsResponse struct { func (s3a *S3ApiServer) DeleteMultipleObjectsHandler(w http.ResponseWriter, r *http.Request) { bucket, _ := getBucketAndObject(r) - glog.V(3).Infof("DeleteMultipleObjectsHandler %s", bucket) deleteXMLBytes, err := ioutil.ReadAll(r.Body) From 59dd271734909b80982b6459635f74cd14175619 Mon Sep 17 00:00:00 2001 From: Chris Lu Date: Sun, 19 Sep 2021 00:29:51 -0700 Subject: [PATCH 038/130] more glog --- weed/s3api/s3api_object_handlers.go | 1 + 1 file changed, 1 insertion(+) diff --git a/weed/s3api/s3api_object_handlers.go b/weed/s3api/s3api_object_handlers.go index 4130d13c3..54b6da61c 100644 --- a/weed/s3api/s3api_object_handlers.go +++ b/weed/s3api/s3api_object_handlers.go @@ -41,6 +41,7 @@ func (s3a *S3ApiServer) PutObjectHandler(w http.ResponseWriter, r *http.Request) // http://docs.aws.amazon.com/AmazonS3/latest/dev/UploadingObjects.html bucket, object := getBucketAndObject(r) + glog.V(3).Infof("PutObjectHandler %s %s", bucket, object) _, err := validateContentMd5(r.Header) if err != nil { From eab7cf4bd3d0eba6c30e4d753231820bbba841cf Mon Sep 17 00:00:00 2001 From: Chris Lu Date: Sun, 19 Sep 2021 00:41:45 -0700 Subject: [PATCH 039/130] restore dev binaries building --- .github/workflows/binaries_dev.yml | 113 ++++++++++++++++------------- 1 file changed, 64 insertions(+), 49 deletions(-) diff --git a/.github/workflows/binaries_dev.yml b/.github/workflows/binaries_dev.yml index ac804df9c..45ac2540e 100644 --- a/.github/workflows/binaries_dev.yml +++ b/.github/workflows/binaries_dev.yml @@ -6,57 +6,72 @@ on: jobs: - - build-latest-docker-image: - runs-on: [ubuntu-latest] + cleanup: + runs-on: ubuntu-latest steps: - - - name: Checkout + + - name: Delete old release assets + uses: mknejp/delete-release-assets@v1 + with: + token: ${{ github.token }} + tag: dev + fail-if-no-assets: false + assets: | + weed-* + + build_dev: + needs: cleanup + runs-on: ubuntu-latest + strategy: + matrix: + goos: [linux, windows, darwin, freebsd] + goarch: [amd64, arm, arm64] + exclude: + - goarch: arm + goos: darwin + - goarch: 386 + goos: darwin + - goarch: arm + goos: windows + - goarch: arm64 + goos: windows + + steps: + + - name: Check out code into the Go module directory uses: actions/checkout@v2 - - - name: Docker meta - id: docker_meta - uses: docker/metadata-action@v3 + + - name: Set BUILD_TIME env + run: echo BUILD_TIME=$(date -u +%Y%m%d-%H%M) >> ${GITHUB_ENV} + + - name: Go Release Binaries Large Disk + uses: wangyoucao577/go-release-action@v1.20 with: - images: | - chrislusf/seaweedfs - ghcr.io/chrislusf/seaweedfs - tags: | - type=raw,value=latest - labels: | - org.opencontainers.image.title=seaweedfs - org.opencontainers.image.vendor=Chris Lu - - - name: Set up QEMU - uses: docker/setup-qemu-action@v1 - - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v1 + github_token: ${{ secrets.GITHUB_TOKEN }} + goos: ${{ matrix.goos }} + goarch: ${{ matrix.goarch }} + release_tag: dev + overwrite: true + pre_command: export CGO_ENABLED=0 + build_flags: -tags 5BytesOffset # optional, default is + ldflags: -extldflags -static -X github.com/chrislusf/seaweedfs/weed/util.COMMIT=${{github.sha}} + # Where to run `go build .` + project_path: weed + binary_name: weed-large-disk + asset_name: "weed-large-disk-${{ env.BUILD_TIME }}-${{ matrix.goos }}-${{ matrix.goarch }}" + + - name: Go Release Binaries Normal Volume Size + uses: wangyoucao577/go-release-action@v1.20 with: - buildkitd-flags: "--debug" - - - name: Login to Docker Hub - if: github.event_name != 'pull_request' - uses: docker/login-action@v1 - with: - username: ${{ secrets.DOCKER_USERNAME }} - password: ${{ secrets.DOCKER_PASSWORD }} - - - name: Login to GHCR - if: github.event_name != 'pull_request' - uses: docker/login-action@v1 - with: - registry: ghcr.io - username: ${{ secrets.GHCR_USERNAME }} - password: ${{ secrets.GHCR_TOKEN }} - - - name: Build - uses: docker/build-push-action@v2 - with: - context: ./docker - push: ${{ github.event_name != 'pull_request' }} - file: ./docker/Dockerfile - platforms: linux/amd64, linux/arm, linux/arm64 - tags: ${{ steps.docker_meta.outputs.tags }} - labels: ${{ steps.docker_meta.outputs.labels }} + github_token: ${{ secrets.GITHUB_TOKEN }} + goos: ${{ matrix.goos }} + goarch: ${{ matrix.goarch }} + release_tag: dev + overwrite: true + pre_command: export CGO_ENABLED=0 + ldflags: -extldflags -static -X github.com/chrislusf/seaweedfs/weed/util.COMMIT=${{github.sha}} + # Where to run `go build .` + project_path: weed + binary_name: weed-normal-disk + asset_name: "weed-${{ env.BUILD_TIME }}-${{ matrix.goos }}-${{ matrix.goarch }}" From c1255d30ad97cebdaf9de7930356fc716e24826d Mon Sep 17 00:00:00 2001 From: Chris Lu Date: Sun, 19 Sep 2021 01:33:26 -0700 Subject: [PATCH 040/130] add cache error log --- weed/server/filer_server_handlers_read.go | 1 + 1 file changed, 1 insertion(+) diff --git a/weed/server/filer_server_handlers_read.go b/weed/server/filer_server_handlers_read.go index 57f2e5300..8d6a03cb8 100644 --- a/weed/server/filer_server_handlers_read.go +++ b/weed/server/filer_server_handlers_read.go @@ -177,6 +177,7 @@ func (fs *FilerServer) GetOrHeadHandler(w http.ResponseWriter, r *http.Request) Directory: dir, Name: name, }); err != nil { + glog.Errorf("cache %s: %v", entry.FullPath, err) return fmt.Errorf("cache %s: %v", entry.FullPath, err) } else { chunks = resp.Entry.Chunks From ede7a65a504d3f4f8dc9d51916d23a116e5fd610 Mon Sep 17 00:00:00 2001 From: Chris Lu Date: Sun, 19 Sep 2021 01:33:50 -0700 Subject: [PATCH 041/130] update log --- weed/server/filer_server_handlers_read.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/weed/server/filer_server_handlers_read.go b/weed/server/filer_server_handlers_read.go index 8d6a03cb8..9fc9da60f 100644 --- a/weed/server/filer_server_handlers_read.go +++ b/weed/server/filer_server_handlers_read.go @@ -177,7 +177,7 @@ func (fs *FilerServer) GetOrHeadHandler(w http.ResponseWriter, r *http.Request) Directory: dir, Name: name, }); err != nil { - glog.Errorf("cache %s: %v", entry.FullPath, err) + glog.Errorf("DownloadToLocal %s: %v", entry.FullPath, err) return fmt.Errorf("cache %s: %v", entry.FullPath, err) } else { chunks = resp.Entry.Chunks From 5abdc0be7750f51f7eb171ff80e6ec20674f3617 Mon Sep 17 00:00:00 2001 From: Chris Lu Date: Sun, 19 Sep 2021 03:24:47 -0700 Subject: [PATCH 042/130] s3: avoid overwriting object with ACL/LegalHold/Retension/LockConfiguration requests --- weed/s3api/s3api_object_skip_handlers.go | 38 ++++++++++++++++++++++++ weed/s3api/s3api_server.go | 9 ++++++ 2 files changed, 47 insertions(+) create mode 100644 weed/s3api/s3api_object_skip_handlers.go diff --git a/weed/s3api/s3api_object_skip_handlers.go b/weed/s3api/s3api_object_skip_handlers.go new file mode 100644 index 000000000..3dc74af85 --- /dev/null +++ b/weed/s3api/s3api_object_skip_handlers.go @@ -0,0 +1,38 @@ +package s3api + +import ( + "net/http" +) + +// PutObjectAclHandler Put object ACL +// https://docs.aws.amazon.com/AmazonS3/latest/API/API_PutObjectAcl.html +func (s3a *S3ApiServer) PutObjectAclHandler(w http.ResponseWriter, r *http.Request) { + + w.WriteHeader(http.StatusNoContent) + +} + +// PutObjectRetentionHandler Put object Retention +// https://docs.aws.amazon.com/AmazonS3/latest/API/API_PutObjectRetention.html +func (s3a *S3ApiServer) PutObjectRetentionHandler(w http.ResponseWriter, r *http.Request) { + + w.WriteHeader(http.StatusNoContent) + +} + +// PutObjectLegalHoldHandler Put object Legal Hold +// https://docs.aws.amazon.com/AmazonS3/latest/API/API_PutObjectLegalHold.html +func (s3a *S3ApiServer) PutObjectLegalHoldHandler(w http.ResponseWriter, r *http.Request) { + + w.WriteHeader(http.StatusNoContent) + +} + +// PutObjectLockConfigurationHandler Put object Lock configuration +// https://docs.aws.amazon.com/AmazonS3/latest/API/API_PutObjectLockConfiguration.html +func (s3a *S3ApiServer) PutObjectLockConfigurationHandler(w http.ResponseWriter, r *http.Request) { + + w.WriteHeader(http.StatusNoContent) + +} + diff --git a/weed/s3api/s3api_server.go b/weed/s3api/s3api_server.go index d216b0af2..5e72fdcb3 100644 --- a/weed/s3api/s3api_server.go +++ b/weed/s3api/s3api_server.go @@ -90,6 +90,15 @@ func (s3a *S3ApiServer) registerRouter(router *mux.Router) { // DeleteObjectTagging bucket.Methods("DELETE").Path("/{object:.+}").HandlerFunc(track(s3a.iam.Auth(s3a.DeleteObjectTaggingHandler, ACTION_TAGGING), "DELETE")).Queries("tagging", "") + // PutObjectACL + bucket.Methods("PUT").Path("/{object:.+}").HandlerFunc(track(s3a.iam.Auth(s3a.PutObjectAclHandler, ACTION_WRITE), "PUT")).Queries("acl", "") + // PutObjectRetention + bucket.Methods("PUT").Path("/{object:.+}").HandlerFunc(track(s3a.iam.Auth(s3a.PutObjectRetentionHandler, ACTION_WRITE), "PUT")).Queries("retention", "") + // PutObjectLegalHold + bucket.Methods("PUT").Path("/{object:.+}").HandlerFunc(track(s3a.iam.Auth(s3a.PutObjectLegalHoldHandler, ACTION_WRITE), "PUT")).Queries("legal-hold", "") + // PutObjectLockConfiguration + bucket.Methods("PUT").Path("/{object:.+}").HandlerFunc(track(s3a.iam.Auth(s3a.PutObjectLockConfigurationHandler, ACTION_WRITE), "PUT")).Queries("object-lock", "") + // CopyObject bucket.Methods("PUT").Path("/{object:.+}").HeadersRegexp("X-Amz-Copy-Source", ".*?(\\/|%2F).*?").HandlerFunc(track(s3a.iam.Auth(s3a.CopyObjectHandler, ACTION_WRITE), "COPY")) // PutObject From ad5099e57011e3a7cac8e34436ecf718acf3e5b3 Mon Sep 17 00:00:00 2001 From: Chris Lu Date: Sun, 19 Sep 2021 12:02:23 -0700 Subject: [PATCH 043/130] refactor --- weed/shell/commands.go | 2 +- weed/wdclient/exclusive_locks/exclusive_locker.go | 12 +++++++----- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/weed/shell/commands.go b/weed/shell/commands.go index 18f357ac7..6b614c159 100644 --- a/weed/shell/commands.go +++ b/weed/shell/commands.go @@ -49,7 +49,7 @@ func NewCommandEnv(options ShellOptions) *CommandEnv { MasterClient: wdclient.NewMasterClient(options.GrpcDialOption, pb.AdminShellClient, "", "", pb.ServerAddresses(*options.Masters).ToAddresses()), option: options, } - ce.locker = exclusive_locks.NewExclusiveLocker(ce.MasterClient) + ce.locker = exclusive_locks.NewExclusiveLocker(ce.MasterClient, "admin") return ce } diff --git a/weed/wdclient/exclusive_locks/exclusive_locker.go b/weed/wdclient/exclusive_locks/exclusive_locker.go index 0fa138496..2f033f36b 100644 --- a/weed/wdclient/exclusive_locks/exclusive_locker.go +++ b/weed/wdclient/exclusive_locks/exclusive_locker.go @@ -14,7 +14,6 @@ const ( RenewInteval = 4 * time.Second SafeRenewInteval = 3 * time.Second InitLockInteval = 1 * time.Second - AdminLockName = "admin" ) type ExclusiveLocker struct { @@ -22,13 +21,16 @@ type ExclusiveLocker struct { lockTsNs int64 isLocking bool masterClient *wdclient.MasterClient + lockName string } -func NewExclusiveLocker(masterClient *wdclient.MasterClient) *ExclusiveLocker { +func NewExclusiveLocker(masterClient *wdclient.MasterClient, lockName string) *ExclusiveLocker { return &ExclusiveLocker{ masterClient: masterClient, + lockName: lockName, } } + func (l *ExclusiveLocker) IsLocking() bool { return l.isLocking } @@ -55,7 +57,7 @@ func (l *ExclusiveLocker) RequestLock(clientName string) { resp, err := client.LeaseAdminToken(ctx, &master_pb.LeaseAdminTokenRequest{ PreviousToken: atomic.LoadInt64(&l.token), PreviousLockTime: atomic.LoadInt64(&l.lockTsNs), - LockName: AdminLockName, + LockName: l.lockName, ClientName: clientName, }) if err == nil { @@ -83,7 +85,7 @@ func (l *ExclusiveLocker) RequestLock(clientName string) { resp, err := client.LeaseAdminToken(ctx2, &master_pb.LeaseAdminTokenRequest{ PreviousToken: atomic.LoadInt64(&l.token), PreviousLockTime: atomic.LoadInt64(&l.lockTsNs), - LockName: AdminLockName, + LockName: l.lockName, ClientName: clientName, }) if err == nil { @@ -114,7 +116,7 @@ func (l *ExclusiveLocker) ReleaseLock() { client.ReleaseAdminToken(ctx, &master_pb.ReleaseAdminTokenRequest{ PreviousToken: atomic.LoadInt64(&l.token), PreviousLockTime: atomic.LoadInt64(&l.lockTsNs), - LockName: AdminLockName, + LockName: l.lockName, }) return nil }) From 84fdda85e740ffbcb8b86806c17b33fb86a5af96 Mon Sep 17 00:00:00 2001 From: Chris Lu Date: Sun, 19 Sep 2021 12:06:15 -0700 Subject: [PATCH 044/130] go fmt --- weed/command/filer_remote_gateway.go | 4 ++-- weed/command/filer_remote_sync.go | 11 +++++------ weed/s3api/s3api_object_skip_handlers.go | 1 - 3 files changed, 7 insertions(+), 9 deletions(-) diff --git a/weed/command/filer_remote_gateway.go b/weed/command/filer_remote_gateway.go index be5ab3fe2..9426f3841 100644 --- a/weed/command/filer_remote_gateway.go +++ b/weed/command/filer_remote_gateway.go @@ -22,8 +22,8 @@ type RemoteGatewayOptions struct { timeAgo *time.Duration createBucketAt *string createBucketRandomSuffix *bool - include *string - exclude *string + include *string + exclude *string mappings *remote_pb.RemoteStorageMapping remoteConfs map[string]*remote_pb.RemoteConf diff --git a/weed/command/filer_remote_sync.go b/weed/command/filer_remote_sync.go index d44178ee7..c55544925 100644 --- a/weed/command/filer_remote_sync.go +++ b/weed/command/filer_remote_sync.go @@ -13,12 +13,11 @@ import ( ) type RemoteSyncOptions struct { - filerAddress *string - grpcDialOption grpc.DialOption - readChunkFromFiler *bool - timeAgo *time.Duration - dir *string - + filerAddress *string + grpcDialOption grpc.DialOption + readChunkFromFiler *bool + timeAgo *time.Duration + dir *string } var _ = filer_pb.FilerClient(&RemoteSyncOptions{}) diff --git a/weed/s3api/s3api_object_skip_handlers.go b/weed/s3api/s3api_object_skip_handlers.go index 3dc74af85..935787fbb 100644 --- a/weed/s3api/s3api_object_skip_handlers.go +++ b/weed/s3api/s3api_object_skip_handlers.go @@ -35,4 +35,3 @@ func (s3a *S3ApiServer) PutObjectLockConfigurationHandler(w http.ResponseWriter, w.WriteHeader(http.StatusNoContent) } - From fa7c65bd4bf431d285c093a5674c43b5763a653a Mon Sep 17 00:00:00 2001 From: Chris Lu Date: Sun, 19 Sep 2021 21:44:06 -0700 Subject: [PATCH 045/130] 2.69 --- k8s/helm_charts2/Chart.yaml | 4 ++-- weed/util/constants.go | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/k8s/helm_charts2/Chart.yaml b/k8s/helm_charts2/Chart.yaml index c2a8158c9..3f4e90f3a 100644 --- a/k8s/helm_charts2/Chart.yaml +++ b/k8s/helm_charts2/Chart.yaml @@ -1,5 +1,5 @@ apiVersion: v1 description: SeaweedFS name: seaweedfs -appVersion: "2.68" -version: "2.68" +appVersion: "2.69" +version: "2.69" diff --git a/weed/util/constants.go b/weed/util/constants.go index d1deb7bef..0f379102d 100644 --- a/weed/util/constants.go +++ b/weed/util/constants.go @@ -5,7 +5,7 @@ import ( ) var ( - VERSION_NUMBER = fmt.Sprintf("%.02f", 2.68) + VERSION_NUMBER = fmt.Sprintf("%.02f", 2.69) VERSION = sizeLimit + " " + VERSION_NUMBER COMMIT = "" ) From 52fe86df4517c285014cb9f652126f2cc0eca130 Mon Sep 17 00:00:00 2001 From: Chris Lu Date: Mon, 20 Sep 2021 14:05:59 -0700 Subject: [PATCH 046/130] use default 10000 for grpc port --- weed/command/filer.go | 5 ++++- weed/command/master.go | 6 +++++- weed/command/master_follower.go | 6 +++++- weed/command/server.go | 6 +++--- weed/command/volume.go | 5 ++++- weed/pb/server_address.go | 2 +- 6 files changed, 22 insertions(+), 8 deletions(-) diff --git a/weed/command/filer.go b/weed/command/filer.go index 96802a1cb..9679870d9 100644 --- a/weed/command/filer.go +++ b/weed/command/filer.go @@ -62,7 +62,7 @@ func init() { f.ip = cmdFiler.Flag.String("ip", util.DetectedHostAddress(), "filer server http listen ip address") f.bindIp = cmdFiler.Flag.String("ip.bind", "", "ip address to bind to") f.port = cmdFiler.Flag.Int("port", 8888, "filer server http listen port") - f.portGrpc = cmdFiler.Flag.Int("port.grpc", 18888, "filer server grpc listen port") + f.portGrpc = cmdFiler.Flag.Int("port.grpc", 0, "filer server grpc listen port") f.publicPort = cmdFiler.Flag.Int("port.readonly", 0, "readonly port opened to public") f.defaultReplicaPlacement = cmdFiler.Flag.String("defaultReplicaPlacement", "", "default replication type. If not specified, use master setting.") f.disableDirListing = cmdFiler.Flag.Bool("disableDirListing", false, "turn off directory listing") @@ -180,6 +180,9 @@ func (fo *FilerOptions) startFiler() { if *fo.publicPort != 0 { publicVolumeMux = http.NewServeMux() } + if *fo.portGrpc == 0 { + *fo.portGrpc = 10000 + *fo.port + } defaultLevelDbDirectory := util.ResolvePath(*fo.defaultLevelDbDirectory + "/filerldb2") diff --git a/weed/command/master.go b/weed/command/master.go index adc9055ea..fc3e41d8d 100644 --- a/weed/command/master.go +++ b/weed/command/master.go @@ -47,7 +47,7 @@ type MasterOptions struct { func init() { cmdMaster.Run = runMaster // break init cycle m.port = cmdMaster.Flag.Int("port", 9333, "http listen port") - m.portGrpc = cmdMaster.Flag.Int("port.grpc", 19333, "grpc listen port") + m.portGrpc = cmdMaster.Flag.Int("port.grpc", 0, "grpc listen port") m.ip = cmdMaster.Flag.String("ip", util.DetectedHostAddress(), "master | address, also used as identifier") m.ipBind = cmdMaster.Flag.String("ip.bind", "", "ip address to bind to") m.metaFolder = cmdMaster.Flag.String("mdir", os.TempDir(), "data directory to store meta data") @@ -113,6 +113,10 @@ func startMaster(masterOption MasterOptions, masterWhiteList []string) { backend.LoadConfiguration(util.GetViper()) + if *masterOption.portGrpc == 0 { + *masterOption.portGrpc = 10000 + *masterOption.port + } + myMasterAddress, peers := checkPeers(*masterOption.ip, *masterOption.port, *masterOption.portGrpc, *masterOption.peers) r := mux.NewRouter() diff --git a/weed/command/master_follower.go b/weed/command/master_follower.go index 2bb9ff6d4..95f1c80b8 100644 --- a/weed/command/master_follower.go +++ b/weed/command/master_follower.go @@ -23,7 +23,7 @@ var ( func init() { cmdMasterFollower.Run = runMasterFollower // break init cycle mf.port = cmdMasterFollower.Flag.Int("port", 9334, "http listen port") - mf.portGrpc = cmdMasterFollower.Flag.Int("port.grpc", 19334, "grpc listen port") + mf.portGrpc = cmdMasterFollower.Flag.Int("port.grpc", 0, "grpc listen port") mf.ipBind = cmdMasterFollower.Flag.String("ip.bind", "", "ip address to bind to") mf.peers = cmdMasterFollower.Flag.String("masters", "localhost:9333", "all master nodes in comma separated ip:port list, example: 127.0.0.1:9093,127.0.0.1:9094,127.0.0.1:9095") @@ -70,6 +70,10 @@ func runMasterFollower(cmd *Command, args []string) bool { util.LoadConfiguration("security", false) util.LoadConfiguration("master", false) + if *mf.portGrpc == 0 { + *mf.portGrpc = 10000 + *mf.port + } + startMasterFollower(mf) return true diff --git a/weed/command/server.go b/weed/command/server.go index e498c39b4..8c0eeed85 100644 --- a/weed/command/server.go +++ b/weed/command/server.go @@ -86,7 +86,7 @@ func init() { serverOptions.debugPort = cmdServer.Flag.Int("debug.port", 6060, "http port for debugging") masterOptions.port = cmdServer.Flag.Int("master.port", 9333, "master server http listen port") - masterOptions.portGrpc = cmdServer.Flag.Int("master.port.grpc", 19333, "master server grpc listen port") + masterOptions.portGrpc = cmdServer.Flag.Int("master.port.grpc", 0, "master server grpc listen port") masterOptions.metaFolder = cmdServer.Flag.String("master.dir", "", "data directory to store meta data, default to same as -dir specified") masterOptions.peers = cmdServer.Flag.String("master.peers", "", "all master nodes in comma separated ip:masterPort list") masterOptions.volumeSizeLimitMB = cmdServer.Flag.Uint("master.volumeSizeLimitMB", 30*1000, "Master stops directing writes to oversized volumes.") @@ -99,7 +99,7 @@ func init() { filerOptions.collection = cmdServer.Flag.String("filer.collection", "", "all data will be stored in this collection") filerOptions.port = cmdServer.Flag.Int("filer.port", 8888, "filer server http listen port") - filerOptions.portGrpc = cmdServer.Flag.Int("filer.port.grpc", 18888, "filer server grpc listen port") + filerOptions.portGrpc = cmdServer.Flag.Int("filer.port.grpc", 0, "filer server grpc listen port") filerOptions.publicPort = cmdServer.Flag.Int("filer.port.public", 0, "filer server public http listen port") filerOptions.defaultReplicaPlacement = cmdServer.Flag.String("filer.defaultReplicaPlacement", "", "default replication type. If not specified, use master setting.") filerOptions.disableDirListing = cmdServer.Flag.Bool("filer.disableDirListing", false, "turn off directory listing") @@ -111,7 +111,7 @@ func init() { filerOptions.concurrentUploadLimitMB = cmdServer.Flag.Int("filer.concurrentUploadLimitMB", 64, "limit total concurrent upload size") serverOptions.v.port = cmdServer.Flag.Int("volume.port", 8080, "volume server http listen port") - serverOptions.v.portGrpc = cmdServer.Flag.Int("volume.port.grpc", 18080, "volume server grpc listen port") + serverOptions.v.portGrpc = cmdServer.Flag.Int("volume.port.grpc", 0, "volume server grpc listen port") serverOptions.v.publicPort = cmdServer.Flag.Int("volume.port.public", 0, "volume server public port") serverOptions.v.indexType = cmdServer.Flag.String("volume.index", "memory", "Choose [memory|leveldb|leveldbMedium|leveldbLarge] mode for memory~performance balance.") serverOptions.v.diskType = cmdServer.Flag.String("volume.disk", "", "[hdd|ssd|] hard drive or solid state drive or any tag") diff --git a/weed/command/volume.go b/weed/command/volume.go index 15e88d65e..fa4ad3b7f 100644 --- a/weed/command/volume.go +++ b/weed/command/volume.go @@ -70,7 +70,7 @@ type VolumeServerOptions struct { func init() { cmdVolume.Run = runVolume // break init cycle v.port = cmdVolume.Flag.Int("port", 8080, "http listen port") - v.portGrpc = cmdVolume.Flag.Int("port.grpc", 18080, "grpc listen port") + v.portGrpc = cmdVolume.Flag.Int("port.grpc", 0, "grpc listen port") v.publicPort = cmdVolume.Flag.Int("port.public", 0, "port opened to public") v.ip = cmdVolume.Flag.String("ip", util.DetectedHostAddress(), "ip or server name, also used as identifier") v.publicUrl = cmdVolume.Flag.String("publicUrl", "", "Publicly accessible address") @@ -197,6 +197,9 @@ func (v VolumeServerOptions) startVolumeServer(volumeFolders, maxVolumeCounts, v if *v.publicPort == 0 { *v.publicPort = *v.port } + if *v.portGrpc == 0 { + *v.portGrpc = 10000 + *v.port + } if *v.publicUrl == "" { *v.publicUrl = util.JoinHostPort(*v.ip, *v.publicPort) } diff --git a/weed/pb/server_address.go b/weed/pb/server_address.go index 8bafbeb85..b60551c71 100644 --- a/weed/pb/server_address.go +++ b/weed/pb/server_address.go @@ -13,7 +13,7 @@ type ServerAddress string type ServerAddresses string func NewServerAddress(host string, port int, grpcPort int) ServerAddress { - if grpcPort == port+10000 { + if grpcPort == 0 || grpcPort == port+10000 { return ServerAddress(util.JoinHostPort(host, port)) } return ServerAddress(util.JoinHostPort(host, port) + "." + strconv.Itoa(grpcPort)) From a814f3f0a80ac511132bd3ac97356f333f128b1c Mon Sep 17 00:00:00 2001 From: Chris Lu Date: Sat, 25 Sep 2021 01:04:51 -0700 Subject: [PATCH 047/130] adjust metadata tail output --- weed/command/filer_meta_tail.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/weed/command/filer_meta_tail.go b/weed/command/filer_meta_tail.go index 85a3eaf84..cff39fe49 100644 --- a/weed/command/filer_meta_tail.go +++ b/weed/command/filer_meta_tail.go @@ -71,12 +71,12 @@ func runFilerMetaTail(cmd *Command, args []string) bool { } shouldPrint := func(resp *filer_pb.SubscribeMetadataResponse) bool { - if filterFunc == nil { - return true - } if resp.EventNotification.OldEntry == nil && resp.EventNotification.NewEntry == nil { return false } + if filterFunc == nil { + return true + } if resp.EventNotification.OldEntry != nil && filterFunc(resp.Directory, resp.EventNotification.OldEntry.Name) { return true } From 2baed2e1e995ad331985a7b8c359e732b223ad3a Mon Sep 17 00:00:00 2001 From: Chris Lu Date: Sat, 25 Sep 2021 01:18:44 -0700 Subject: [PATCH 048/130] avoid possible metadata subscription data loss Previous implementation append filer logs into one file. So one file is not always sorted, which can lead to miss reading some entries, especially when different filers have different write throughput. --- weed/filer/filer.go | 2 ++ weed/filer/filer_notify.go | 12 +++++++----- .../messaging/broker/broker_grpc_server_subscribe.go | 6 ++++-- weed/util/file_util.go | 8 ++++++++ 4 files changed, 21 insertions(+), 7 deletions(-) diff --git a/weed/filer/filer.go b/weed/filer/filer.go index f13782031..76d2f3f47 100644 --- a/weed/filer/filer.go +++ b/weed/filer/filer.go @@ -44,6 +44,7 @@ type Filer struct { Signature int32 FilerConf *FilerConf RemoteStorage *FilerRemoteStorage + UniqueFileId uint32 } func NewFiler(masters []pb.ServerAddress, grpcDialOption grpc.DialOption, @@ -54,6 +55,7 @@ func NewFiler(masters []pb.ServerAddress, grpcDialOption grpc.DialOption, GrpcDialOption: grpcDialOption, FilerConf: NewFilerConf(), RemoteStorage: NewFilerRemoteStorage(), + UniqueFileId: uint32(util.RandomInt32()), } f.LocalMetaLogBuffer = log_buffer.NewLogBuffer("local", LogFlushInterval, f.logFlushFunc, notifyFn) f.metaLogCollection = collection diff --git a/weed/filer/filer_notify.go b/weed/filer/filer_notify.go index 7ab101102..e44ddfd59 100644 --- a/weed/filer/filer_notify.go +++ b/weed/filer/filer_notify.go @@ -4,6 +4,7 @@ import ( "context" "fmt" "io" + "math" "strings" "time" @@ -92,8 +93,8 @@ func (f *Filer) logFlushFunc(startTime, stopTime time.Time, buf []byte) { startTime, stopTime = startTime.UTC(), stopTime.UTC() - targetFile := fmt.Sprintf("%s/%04d-%02d-%02d/%02d-%02d.segment", SystemLogDir, - startTime.Year(), startTime.Month(), startTime.Day(), startTime.Hour(), startTime.Minute(), + targetFile := fmt.Sprintf("%s/%04d-%02d-%02d/%02d-%02d.%08x", SystemLogDir, + startTime.Year(), startTime.Month(), startTime.Day(), startTime.Hour(), startTime.Minute(), f.UniqueFileId, // startTime.Second(), startTime.Nanosecond(), ) @@ -111,7 +112,7 @@ func (f *Filer) ReadPersistedLogBuffer(startTime time.Time, eachLogEntryFn func( startTime = startTime.UTC() startDate := fmt.Sprintf("%04d-%02d-%02d", startTime.Year(), startTime.Month(), startTime.Day()) - startHourMinute := fmt.Sprintf("%02d-%02d.segment", startTime.Hour(), startTime.Minute()) + startHourMinute := fmt.Sprintf("%02d-%02d", startTime.Hour(), startTime.Minute()) sizeBuf := make([]byte, 4) startTsNs := startTime.UnixNano() @@ -122,14 +123,15 @@ func (f *Filer) ReadPersistedLogBuffer(startTime time.Time, eachLogEntryFn func( } for _, dayEntry := range dayEntries { // println("checking day", dayEntry.FullPath) - hourMinuteEntries, _, listHourMinuteErr := f.ListDirectoryEntries(context.Background(), util.NewFullPath(SystemLogDir, dayEntry.Name()), "", false, 24*60, "", "", "") + hourMinuteEntries, _, listHourMinuteErr := f.ListDirectoryEntries(context.Background(), util.NewFullPath(SystemLogDir, dayEntry.Name()), "", false, math.MaxInt32, "", "", "") if listHourMinuteErr != nil { return lastTsNs, fmt.Errorf("fail to list log %s by day: %v", dayEntry.Name(), listHourMinuteErr) } for _, hourMinuteEntry := range hourMinuteEntries { // println("checking hh-mm", hourMinuteEntry.FullPath) if dayEntry.Name() == startDate { - if strings.Compare(hourMinuteEntry.Name(), startHourMinute) < 0 { + hourMinute := util.FileNameBase(hourMinuteEntry.Name()) + if strings.Compare(hourMinute, startHourMinute) < 0 { continue } } diff --git a/weed/messaging/broker/broker_grpc_server_subscribe.go b/weed/messaging/broker/broker_grpc_server_subscribe.go index d21fb351f..f07a961db 100644 --- a/weed/messaging/broker/broker_grpc_server_subscribe.go +++ b/weed/messaging/broker/broker_grpc_server_subscribe.go @@ -2,6 +2,7 @@ package broker import ( "fmt" + "github.com/chrislusf/seaweedfs/weed/util" "github.com/chrislusf/seaweedfs/weed/util/log_buffer" "io" "strings" @@ -141,7 +142,7 @@ func (broker *MessageBroker) Subscribe(stream messaging_pb.SeaweedMessaging_Subs func (broker *MessageBroker) readPersistedLogBuffer(tp *TopicPartition, startTime time.Time, eachLogEntryFn func(logEntry *filer_pb.LogEntry) error) (err error) { startTime = startTime.UTC() startDate := fmt.Sprintf("%04d-%02d-%02d", startTime.Year(), startTime.Month(), startTime.Day()) - startHourMinute := fmt.Sprintf("%02d-%02d.segment", startTime.Hour(), startTime.Minute()) + startHourMinute := fmt.Sprintf("%02d-%02d", startTime.Hour(), startTime.Minute()) sizeBuf := make([]byte, 4) startTsNs := startTime.UnixNano() @@ -153,7 +154,8 @@ func (broker *MessageBroker) readPersistedLogBuffer(tp *TopicPartition, startTim dayDir := fmt.Sprintf("%s/%s", topicDir, dayEntry.Name) return filer_pb.List(broker, dayDir, "", func(hourMinuteEntry *filer_pb.Entry, isLast bool) error { if dayEntry.Name == startDate { - if strings.Compare(hourMinuteEntry.Name, startHourMinute) < 0 { + hourMinute := util.FileNameBase(hourMinuteEntry.Name) + if strings.Compare(hourMinute, startHourMinute) < 0 { return nil } } diff --git a/weed/util/file_util.go b/weed/util/file_util.go index f83f80265..f9cc4f70b 100644 --- a/weed/util/file_util.go +++ b/weed/util/file_util.go @@ -87,3 +87,11 @@ func ResolvePath(path string) string { return path } + +func FileNameBase(filename string) string { + lastDotIndex := strings.LastIndex(filename, ".") + if lastDotIndex < 0 { + return filename + } + return filename[:lastDotIndex] +} From 9887610b545c38ac3bce580a6bbf96ba63b61308 Mon Sep 17 00:00:00 2001 From: Chris Lu Date: Sun, 26 Sep 2021 11:54:13 -0700 Subject: [PATCH 049/130] log tsNs should be processing time --- weed/filer/meta_aggregator.go | 2 +- weed/util/log_buffer/log_buffer.go | 18 +++++++++--------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/weed/filer/meta_aggregator.go b/weed/filer/meta_aggregator.go index 008fd33a7..eba2a044a 100644 --- a/weed/filer/meta_aggregator.go +++ b/weed/filer/meta_aggregator.go @@ -110,7 +110,7 @@ func (ma *MetaAggregator) subscribeToOneFiler(f *Filer, self pb.ServerAddress, p } dir := event.Directory // println("received meta change", dir, "size", len(data)) - ma.MetaLogBuffer.AddToBuffer([]byte(dir), data, 0) + ma.MetaLogBuffer.AddToBuffer([]byte(dir), data, event.TsNs) if maybeReplicateMetadataChange != nil { maybeReplicateMetadataChange(event) } diff --git a/weed/util/log_buffer/log_buffer.go b/weed/util/log_buffer/log_buffer.go index c2158e7eb..a6d94670a 100644 --- a/weed/util/log_buffer/log_buffer.go +++ b/weed/util/log_buffer/log_buffer.go @@ -56,7 +56,7 @@ func NewLogBuffer(name string, flushInterval time.Duration, flushFn func(startTi return lb } -func (m *LogBuffer) AddToBuffer(partitionKey, data []byte, eventTsNs int64) { +func (m *LogBuffer) AddToBuffer(partitionKey, data []byte, processingTsNs int64) { m.Lock() defer func() { @@ -68,20 +68,20 @@ func (m *LogBuffer) AddToBuffer(partitionKey, data []byte, eventTsNs int64) { // need to put the timestamp inside the lock var ts time.Time - if eventTsNs == 0 { + if processingTsNs == 0 { ts = time.Now() - eventTsNs = ts.UnixNano() + processingTsNs = ts.UnixNano() } else { - ts = time.Unix(0, eventTsNs) + ts = time.Unix(0, processingTsNs) } - if m.lastTsNs >= eventTsNs { + if m.lastTsNs >= processingTsNs { // this is unlikely to happen, but just in case - eventTsNs = m.lastTsNs + 1 - ts = time.Unix(0, eventTsNs) + processingTsNs = m.lastTsNs + 1 + ts = time.Unix(0, processingTsNs) } - m.lastTsNs = eventTsNs + m.lastTsNs = processingTsNs logEntry := &filer_pb.LogEntry{ - TsNs: eventTsNs, + TsNs: processingTsNs, PartitionKeyHash: util.HashToInt32(partitionKey), Data: data, } From 603ea2db7329615e81c75e63bd8c86379c700795 Mon Sep 17 00:00:00 2001 From: Chris Lu Date: Sun, 26 Sep 2021 11:55:27 -0700 Subject: [PATCH 050/130] avoid looping forever if there are no more metadata updates --- weed/server/filer_grpc_server_sub_meta.go | 6 +----- weed/util/log_buffer/log_buffer.go | 6 +++++- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/weed/server/filer_grpc_server_sub_meta.go b/weed/server/filer_grpc_server_sub_meta.go index a900275b9..39109071e 100644 --- a/weed/server/filer_grpc_server_sub_meta.go +++ b/weed/server/filer_grpc_server_sub_meta.go @@ -49,11 +49,6 @@ func (fs *FilerServer) SubscribeMetadata(req *filer_pb.SubscribeMetadataRequest, if processedTsNs != 0 { lastReadTime = time.Unix(0, processedTsNs) - } else { - if readInMemoryLogErr == log_buffer.ResumeFromDiskError { - time.Sleep(1127 * time.Millisecond) - continue - } } glog.V(4).Infof("read in memory %v aggregated subscribe %s from %+v", clientName, req.PathPrefix, lastReadTime) @@ -66,6 +61,7 @@ func (fs *FilerServer) SubscribeMetadata(req *filer_pb.SubscribeMetadataRequest, }, eachLogEntryFn) if readInMemoryLogErr != nil { if readInMemoryLogErr == log_buffer.ResumeFromDiskError { + time.Sleep(1127 * time.Millisecond) continue } glog.Errorf("processed to %v: %v", lastReadTime, readInMemoryLogErr) diff --git a/weed/util/log_buffer/log_buffer.go b/weed/util/log_buffer/log_buffer.go index a6d94670a..c7ddf2d9c 100644 --- a/weed/util/log_buffer/log_buffer.go +++ b/weed/util/log_buffer/log_buffer.go @@ -189,7 +189,11 @@ func (m *LogBuffer) ReadFromBuffer(lastReadTime time.Time) (bufferCopy *bytes.Bu defer m.RUnlock() if !m.lastFlushTime.IsZero() && m.lastFlushTime.After(lastReadTime) { - return nil, ResumeFromDiskError + if time.Now().Sub(m.lastFlushTime) < m.flushInterval * 2 { + diff := m.lastFlushTime.Sub(lastReadTime) + glog.V(4).Infof("lastFlush:%v lastRead:%v diff:%v", m.lastFlushTime, lastReadTime, diff) + return nil, ResumeFromDiskError + } } /* From ecaab0e0487aaf5af5149aa8ea578a9f3cb2516c Mon Sep 17 00:00:00 2001 From: Chris Lu Date: Sun, 26 Sep 2021 12:59:49 -0700 Subject: [PATCH 051/130] adjust test code --- test/s3/multipart/aws_upload.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/test/s3/multipart/aws_upload.go b/test/s3/multipart/aws_upload.go index 8c15cf6ed..0553bd403 100644 --- a/test/s3/multipart/aws_upload.go +++ b/test/s3/multipart/aws_upload.go @@ -19,10 +19,10 @@ import ( const ( maxPartSize = int64(5 * 1024 * 1024) maxRetries = 3 - awsAccessKeyID = "Your access key" - awsSecretAccessKey = "Your secret key" - awsBucketRegion = "S3 bucket region" - awsBucketName = "newBucket" + awsAccessKeyID = "any" + awsSecretAccessKey = "any" + awsBucketRegion = "us‑west‑1" + awsBucketName = "bucket1" ) var ( @@ -37,7 +37,7 @@ func main() { if err != nil { fmt.Printf("bad credentials: %s", err) } - cfg := aws.NewConfig().WithRegion(awsBucketRegion).WithCredentials(creds).WithDisableSSL(true).WithEndpoint("localhost:8333") + cfg := aws.NewConfig().WithRegion(awsBucketRegion).WithCredentials(creds).WithDisableSSL(true).WithEndpoint("localhost:8333").WithS3ForcePathStyle(true) svc := s3.New(session.New(), cfg) file, err := os.Open(*filename) From cee4d20bc118e5ea38123e39fed29f362ff8af01 Mon Sep 17 00:00:00 2001 From: Chris Lu Date: Sun, 26 Sep 2021 17:37:46 -0700 Subject: [PATCH 052/130] 2.70 --- k8s/helm_charts2/Chart.yaml | 4 ++-- weed/util/constants.go | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/k8s/helm_charts2/Chart.yaml b/k8s/helm_charts2/Chart.yaml index 3f4e90f3a..14952e3e8 100644 --- a/k8s/helm_charts2/Chart.yaml +++ b/k8s/helm_charts2/Chart.yaml @@ -1,5 +1,5 @@ apiVersion: v1 description: SeaweedFS name: seaweedfs -appVersion: "2.69" -version: "2.69" +appVersion: "2.70" +version: "2.70" diff --git a/weed/util/constants.go b/weed/util/constants.go index 0f379102d..95c8086e5 100644 --- a/weed/util/constants.go +++ b/weed/util/constants.go @@ -5,7 +5,7 @@ import ( ) var ( - VERSION_NUMBER = fmt.Sprintf("%.02f", 2.69) + VERSION_NUMBER = fmt.Sprintf("%.02f", 2.70) VERSION = sizeLimit + " " + VERSION_NUMBER COMMIT = "" ) From 4a1d4d7462334088ea979c01ae42563eefb0b9f1 Mon Sep 17 00:00:00 2001 From: Chris Lu Date: Sun, 26 Sep 2021 22:34:14 -0700 Subject: [PATCH 053/130] s3: default to allow empty folder for better performance --- weed/command/filer.go | 2 +- weed/command/s3.go | 2 +- weed/command/server.go | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/weed/command/filer.go b/weed/command/filer.go index 9679870d9..fdd8cb4f2 100644 --- a/weed/command/filer.go +++ b/weed/command/filer.go @@ -87,7 +87,7 @@ func init() { filerS3Options.tlsPrivateKey = cmdFiler.Flag.String("s3.key.file", "", "path to the TLS private key file") filerS3Options.tlsCertificate = cmdFiler.Flag.String("s3.cert.file", "", "path to the TLS certificate file") filerS3Options.config = cmdFiler.Flag.String("s3.config", "", "path to the config file") - filerS3Options.allowEmptyFolder = cmdFiler.Flag.Bool("s3.allowEmptyFolder", false, "allow empty folders") + filerS3Options.allowEmptyFolder = cmdFiler.Flag.Bool("s3.allowEmptyFolder", true, "allow empty folders") // start webdav on filer filerStartWebDav = cmdFiler.Flag.Bool("webdav", false, "whether to start webdav gateway") diff --git a/weed/command/s3.go b/weed/command/s3.go index f2c6c0769..e9f4ea885 100644 --- a/weed/command/s3.go +++ b/weed/command/s3.go @@ -42,7 +42,7 @@ func init() { s3StandaloneOptions.tlsPrivateKey = cmdS3.Flag.String("key.file", "", "path to the TLS private key file") s3StandaloneOptions.tlsCertificate = cmdS3.Flag.String("cert.file", "", "path to the TLS certificate file") s3StandaloneOptions.metricsHttpPort = cmdS3.Flag.Int("metricsPort", 0, "Prometheus metrics listen port") - s3StandaloneOptions.allowEmptyFolder = cmdS3.Flag.Bool("allowEmptyFolder", false, "allow empty folders") + s3StandaloneOptions.allowEmptyFolder = cmdS3.Flag.Bool("allowEmptyFolder", true, "allow empty folders") } var cmdS3 = &Command{ diff --git a/weed/command/server.go b/weed/command/server.go index 8c0eeed85..9f402c9f8 100644 --- a/weed/command/server.go +++ b/weed/command/server.go @@ -132,7 +132,7 @@ func init() { s3Options.tlsPrivateKey = cmdServer.Flag.String("s3.key.file", "", "path to the TLS private key file") s3Options.tlsCertificate = cmdServer.Flag.String("s3.cert.file", "", "path to the TLS certificate file") s3Options.config = cmdServer.Flag.String("s3.config", "", "path to the config file") - s3Options.allowEmptyFolder = cmdServer.Flag.Bool("s3.allowEmptyFolder", false, "allow empty folders") + s3Options.allowEmptyFolder = cmdServer.Flag.Bool("s3.allowEmptyFolder", true, "allow empty folders") webdavOptions.port = cmdServer.Flag.Int("webdav.port", 7333, "webdav server http listen port") webdavOptions.collection = cmdServer.Flag.String("webdav.collection", "", "collection to create the files") From c4d7ee6c5cb039f8503c24718ba927f7347a8289 Mon Sep 17 00:00:00 2001 From: Chris Lu Date: Mon, 27 Sep 2021 01:45:32 -0700 Subject: [PATCH 054/130] volume server: read all files in a volume --- weed/pb/volume_server.proto | 10 + weed/pb/volume_server_pb/volume_server.pb.go | 2210 ++++++++++-------- weed/server/volume_grpc_read_all.go | 50 + 3 files changed, 1269 insertions(+), 1001 deletions(-) create mode 100644 weed/server/volume_grpc_read_all.go diff --git a/weed/pb/volume_server.proto b/weed/pb/volume_server.proto index 6d81626b3..c2b478a25 100644 --- a/weed/pb/volume_server.proto +++ b/weed/pb/volume_server.proto @@ -58,6 +58,8 @@ service VolumeServer { } rpc WriteNeedleBlob (WriteNeedleBlobRequest) returns (WriteNeedleBlobResponse) { } + rpc ReadAllNeedles (ReadAllNeedlesRequest) returns (stream ReadAllNeedlesResponse) { + } rpc VolumeTailSender (VolumeTailSenderRequest) returns (stream VolumeTailSenderResponse) { } @@ -284,6 +286,14 @@ message WriteNeedleBlobRequest { message WriteNeedleBlobResponse { } +message ReadAllNeedlesRequest { + uint32 volume_id = 1; +} +message ReadAllNeedlesResponse { + uint64 needle_id = 1; + bytes needle_blob = 2; +} + message VolumeTailSenderRequest { uint32 volume_id = 1; uint64 since_ns = 2; diff --git a/weed/pb/volume_server_pb/volume_server.pb.go b/weed/pb/volume_server_pb/volume_server.pb.go index 68924b5fc..2f1ddb105 100644 --- a/weed/pb/volume_server_pb/volume_server.pb.go +++ b/weed/pb/volume_server_pb/volume_server.pb.go @@ -2209,6 +2209,108 @@ func (*WriteNeedleBlobResponse) Descriptor() ([]byte, []int) { return file_volume_server_proto_rawDescGZIP(), []int{41} } +type ReadAllNeedlesRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + VolumeId uint32 `protobuf:"varint,1,opt,name=volume_id,json=volumeId,proto3" json:"volume_id,omitempty"` +} + +func (x *ReadAllNeedlesRequest) Reset() { + *x = ReadAllNeedlesRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_volume_server_proto_msgTypes[42] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ReadAllNeedlesRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ReadAllNeedlesRequest) ProtoMessage() {} + +func (x *ReadAllNeedlesRequest) ProtoReflect() protoreflect.Message { + mi := &file_volume_server_proto_msgTypes[42] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ReadAllNeedlesRequest.ProtoReflect.Descriptor instead. +func (*ReadAllNeedlesRequest) Descriptor() ([]byte, []int) { + return file_volume_server_proto_rawDescGZIP(), []int{42} +} + +func (x *ReadAllNeedlesRequest) GetVolumeId() uint32 { + if x != nil { + return x.VolumeId + } + return 0 +} + +type ReadAllNeedlesResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + NeedleId uint64 `protobuf:"varint,1,opt,name=needle_id,json=needleId,proto3" json:"needle_id,omitempty"` + NeedleBlob []byte `protobuf:"bytes,2,opt,name=needle_blob,json=needleBlob,proto3" json:"needle_blob,omitempty"` +} + +func (x *ReadAllNeedlesResponse) Reset() { + *x = ReadAllNeedlesResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_volume_server_proto_msgTypes[43] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ReadAllNeedlesResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ReadAllNeedlesResponse) ProtoMessage() {} + +func (x *ReadAllNeedlesResponse) ProtoReflect() protoreflect.Message { + mi := &file_volume_server_proto_msgTypes[43] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ReadAllNeedlesResponse.ProtoReflect.Descriptor instead. +func (*ReadAllNeedlesResponse) Descriptor() ([]byte, []int) { + return file_volume_server_proto_rawDescGZIP(), []int{43} +} + +func (x *ReadAllNeedlesResponse) GetNeedleId() uint64 { + if x != nil { + return x.NeedleId + } + return 0 +} + +func (x *ReadAllNeedlesResponse) GetNeedleBlob() []byte { + if x != nil { + return x.NeedleBlob + } + return nil +} + type VolumeTailSenderRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -2222,7 +2324,7 @@ type VolumeTailSenderRequest struct { func (x *VolumeTailSenderRequest) Reset() { *x = VolumeTailSenderRequest{} if protoimpl.UnsafeEnabled { - mi := &file_volume_server_proto_msgTypes[42] + mi := &file_volume_server_proto_msgTypes[44] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2235,7 +2337,7 @@ func (x *VolumeTailSenderRequest) String() string { func (*VolumeTailSenderRequest) ProtoMessage() {} func (x *VolumeTailSenderRequest) ProtoReflect() protoreflect.Message { - mi := &file_volume_server_proto_msgTypes[42] + mi := &file_volume_server_proto_msgTypes[44] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2248,7 +2350,7 @@ func (x *VolumeTailSenderRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use VolumeTailSenderRequest.ProtoReflect.Descriptor instead. func (*VolumeTailSenderRequest) Descriptor() ([]byte, []int) { - return file_volume_server_proto_rawDescGZIP(), []int{42} + return file_volume_server_proto_rawDescGZIP(), []int{44} } func (x *VolumeTailSenderRequest) GetVolumeId() uint32 { @@ -2285,7 +2387,7 @@ type VolumeTailSenderResponse struct { func (x *VolumeTailSenderResponse) Reset() { *x = VolumeTailSenderResponse{} if protoimpl.UnsafeEnabled { - mi := &file_volume_server_proto_msgTypes[43] + mi := &file_volume_server_proto_msgTypes[45] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2298,7 +2400,7 @@ func (x *VolumeTailSenderResponse) String() string { func (*VolumeTailSenderResponse) ProtoMessage() {} func (x *VolumeTailSenderResponse) ProtoReflect() protoreflect.Message { - mi := &file_volume_server_proto_msgTypes[43] + mi := &file_volume_server_proto_msgTypes[45] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2311,7 +2413,7 @@ func (x *VolumeTailSenderResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use VolumeTailSenderResponse.ProtoReflect.Descriptor instead. func (*VolumeTailSenderResponse) Descriptor() ([]byte, []int) { - return file_volume_server_proto_rawDescGZIP(), []int{43} + return file_volume_server_proto_rawDescGZIP(), []int{45} } func (x *VolumeTailSenderResponse) GetNeedleHeader() []byte { @@ -2349,7 +2451,7 @@ type VolumeTailReceiverRequest struct { func (x *VolumeTailReceiverRequest) Reset() { *x = VolumeTailReceiverRequest{} if protoimpl.UnsafeEnabled { - mi := &file_volume_server_proto_msgTypes[44] + mi := &file_volume_server_proto_msgTypes[46] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2362,7 +2464,7 @@ func (x *VolumeTailReceiverRequest) String() string { func (*VolumeTailReceiverRequest) ProtoMessage() {} func (x *VolumeTailReceiverRequest) ProtoReflect() protoreflect.Message { - mi := &file_volume_server_proto_msgTypes[44] + mi := &file_volume_server_proto_msgTypes[46] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2375,7 +2477,7 @@ func (x *VolumeTailReceiverRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use VolumeTailReceiverRequest.ProtoReflect.Descriptor instead. func (*VolumeTailReceiverRequest) Descriptor() ([]byte, []int) { - return file_volume_server_proto_rawDescGZIP(), []int{44} + return file_volume_server_proto_rawDescGZIP(), []int{46} } func (x *VolumeTailReceiverRequest) GetVolumeId() uint32 { @@ -2415,7 +2517,7 @@ type VolumeTailReceiverResponse struct { func (x *VolumeTailReceiverResponse) Reset() { *x = VolumeTailReceiverResponse{} if protoimpl.UnsafeEnabled { - mi := &file_volume_server_proto_msgTypes[45] + mi := &file_volume_server_proto_msgTypes[47] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2428,7 +2530,7 @@ func (x *VolumeTailReceiverResponse) String() string { func (*VolumeTailReceiverResponse) ProtoMessage() {} func (x *VolumeTailReceiverResponse) ProtoReflect() protoreflect.Message { - mi := &file_volume_server_proto_msgTypes[45] + mi := &file_volume_server_proto_msgTypes[47] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2441,7 +2543,7 @@ func (x *VolumeTailReceiverResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use VolumeTailReceiverResponse.ProtoReflect.Descriptor instead. func (*VolumeTailReceiverResponse) Descriptor() ([]byte, []int) { - return file_volume_server_proto_rawDescGZIP(), []int{45} + return file_volume_server_proto_rawDescGZIP(), []int{47} } type VolumeEcShardsGenerateRequest struct { @@ -2456,7 +2558,7 @@ type VolumeEcShardsGenerateRequest struct { func (x *VolumeEcShardsGenerateRequest) Reset() { *x = VolumeEcShardsGenerateRequest{} if protoimpl.UnsafeEnabled { - mi := &file_volume_server_proto_msgTypes[46] + mi := &file_volume_server_proto_msgTypes[48] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2469,7 +2571,7 @@ func (x *VolumeEcShardsGenerateRequest) String() string { func (*VolumeEcShardsGenerateRequest) ProtoMessage() {} func (x *VolumeEcShardsGenerateRequest) ProtoReflect() protoreflect.Message { - mi := &file_volume_server_proto_msgTypes[46] + mi := &file_volume_server_proto_msgTypes[48] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2482,7 +2584,7 @@ func (x *VolumeEcShardsGenerateRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use VolumeEcShardsGenerateRequest.ProtoReflect.Descriptor instead. func (*VolumeEcShardsGenerateRequest) Descriptor() ([]byte, []int) { - return file_volume_server_proto_rawDescGZIP(), []int{46} + return file_volume_server_proto_rawDescGZIP(), []int{48} } func (x *VolumeEcShardsGenerateRequest) GetVolumeId() uint32 { @@ -2508,7 +2610,7 @@ type VolumeEcShardsGenerateResponse struct { func (x *VolumeEcShardsGenerateResponse) Reset() { *x = VolumeEcShardsGenerateResponse{} if protoimpl.UnsafeEnabled { - mi := &file_volume_server_proto_msgTypes[47] + mi := &file_volume_server_proto_msgTypes[49] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2521,7 +2623,7 @@ func (x *VolumeEcShardsGenerateResponse) String() string { func (*VolumeEcShardsGenerateResponse) ProtoMessage() {} func (x *VolumeEcShardsGenerateResponse) ProtoReflect() protoreflect.Message { - mi := &file_volume_server_proto_msgTypes[47] + mi := &file_volume_server_proto_msgTypes[49] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2534,7 +2636,7 @@ func (x *VolumeEcShardsGenerateResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use VolumeEcShardsGenerateResponse.ProtoReflect.Descriptor instead. func (*VolumeEcShardsGenerateResponse) Descriptor() ([]byte, []int) { - return file_volume_server_proto_rawDescGZIP(), []int{47} + return file_volume_server_proto_rawDescGZIP(), []int{49} } type VolumeEcShardsRebuildRequest struct { @@ -2549,7 +2651,7 @@ type VolumeEcShardsRebuildRequest struct { func (x *VolumeEcShardsRebuildRequest) Reset() { *x = VolumeEcShardsRebuildRequest{} if protoimpl.UnsafeEnabled { - mi := &file_volume_server_proto_msgTypes[48] + mi := &file_volume_server_proto_msgTypes[50] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2562,7 +2664,7 @@ func (x *VolumeEcShardsRebuildRequest) String() string { func (*VolumeEcShardsRebuildRequest) ProtoMessage() {} func (x *VolumeEcShardsRebuildRequest) ProtoReflect() protoreflect.Message { - mi := &file_volume_server_proto_msgTypes[48] + mi := &file_volume_server_proto_msgTypes[50] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2575,7 +2677,7 @@ func (x *VolumeEcShardsRebuildRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use VolumeEcShardsRebuildRequest.ProtoReflect.Descriptor instead. func (*VolumeEcShardsRebuildRequest) Descriptor() ([]byte, []int) { - return file_volume_server_proto_rawDescGZIP(), []int{48} + return file_volume_server_proto_rawDescGZIP(), []int{50} } func (x *VolumeEcShardsRebuildRequest) GetVolumeId() uint32 { @@ -2603,7 +2705,7 @@ type VolumeEcShardsRebuildResponse struct { func (x *VolumeEcShardsRebuildResponse) Reset() { *x = VolumeEcShardsRebuildResponse{} if protoimpl.UnsafeEnabled { - mi := &file_volume_server_proto_msgTypes[49] + mi := &file_volume_server_proto_msgTypes[51] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2616,7 +2718,7 @@ func (x *VolumeEcShardsRebuildResponse) String() string { func (*VolumeEcShardsRebuildResponse) ProtoMessage() {} func (x *VolumeEcShardsRebuildResponse) ProtoReflect() protoreflect.Message { - mi := &file_volume_server_proto_msgTypes[49] + mi := &file_volume_server_proto_msgTypes[51] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2629,7 +2731,7 @@ func (x *VolumeEcShardsRebuildResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use VolumeEcShardsRebuildResponse.ProtoReflect.Descriptor instead. func (*VolumeEcShardsRebuildResponse) Descriptor() ([]byte, []int) { - return file_volume_server_proto_rawDescGZIP(), []int{49} + return file_volume_server_proto_rawDescGZIP(), []int{51} } func (x *VolumeEcShardsRebuildResponse) GetRebuiltShardIds() []uint32 { @@ -2656,7 +2758,7 @@ type VolumeEcShardsCopyRequest struct { func (x *VolumeEcShardsCopyRequest) Reset() { *x = VolumeEcShardsCopyRequest{} if protoimpl.UnsafeEnabled { - mi := &file_volume_server_proto_msgTypes[50] + mi := &file_volume_server_proto_msgTypes[52] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2669,7 +2771,7 @@ func (x *VolumeEcShardsCopyRequest) String() string { func (*VolumeEcShardsCopyRequest) ProtoMessage() {} func (x *VolumeEcShardsCopyRequest) ProtoReflect() protoreflect.Message { - mi := &file_volume_server_proto_msgTypes[50] + mi := &file_volume_server_proto_msgTypes[52] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2682,7 +2784,7 @@ func (x *VolumeEcShardsCopyRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use VolumeEcShardsCopyRequest.ProtoReflect.Descriptor instead. func (*VolumeEcShardsCopyRequest) Descriptor() ([]byte, []int) { - return file_volume_server_proto_rawDescGZIP(), []int{50} + return file_volume_server_proto_rawDescGZIP(), []int{52} } func (x *VolumeEcShardsCopyRequest) GetVolumeId() uint32 { @@ -2743,7 +2845,7 @@ type VolumeEcShardsCopyResponse struct { func (x *VolumeEcShardsCopyResponse) Reset() { *x = VolumeEcShardsCopyResponse{} if protoimpl.UnsafeEnabled { - mi := &file_volume_server_proto_msgTypes[51] + mi := &file_volume_server_proto_msgTypes[53] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2756,7 +2858,7 @@ func (x *VolumeEcShardsCopyResponse) String() string { func (*VolumeEcShardsCopyResponse) ProtoMessage() {} func (x *VolumeEcShardsCopyResponse) ProtoReflect() protoreflect.Message { - mi := &file_volume_server_proto_msgTypes[51] + mi := &file_volume_server_proto_msgTypes[53] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2769,7 +2871,7 @@ func (x *VolumeEcShardsCopyResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use VolumeEcShardsCopyResponse.ProtoReflect.Descriptor instead. func (*VolumeEcShardsCopyResponse) Descriptor() ([]byte, []int) { - return file_volume_server_proto_rawDescGZIP(), []int{51} + return file_volume_server_proto_rawDescGZIP(), []int{53} } type VolumeEcShardsDeleteRequest struct { @@ -2785,7 +2887,7 @@ type VolumeEcShardsDeleteRequest struct { func (x *VolumeEcShardsDeleteRequest) Reset() { *x = VolumeEcShardsDeleteRequest{} if protoimpl.UnsafeEnabled { - mi := &file_volume_server_proto_msgTypes[52] + mi := &file_volume_server_proto_msgTypes[54] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2798,7 +2900,7 @@ func (x *VolumeEcShardsDeleteRequest) String() string { func (*VolumeEcShardsDeleteRequest) ProtoMessage() {} func (x *VolumeEcShardsDeleteRequest) ProtoReflect() protoreflect.Message { - mi := &file_volume_server_proto_msgTypes[52] + mi := &file_volume_server_proto_msgTypes[54] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2811,7 +2913,7 @@ func (x *VolumeEcShardsDeleteRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use VolumeEcShardsDeleteRequest.ProtoReflect.Descriptor instead. func (*VolumeEcShardsDeleteRequest) Descriptor() ([]byte, []int) { - return file_volume_server_proto_rawDescGZIP(), []int{52} + return file_volume_server_proto_rawDescGZIP(), []int{54} } func (x *VolumeEcShardsDeleteRequest) GetVolumeId() uint32 { @@ -2844,7 +2946,7 @@ type VolumeEcShardsDeleteResponse struct { func (x *VolumeEcShardsDeleteResponse) Reset() { *x = VolumeEcShardsDeleteResponse{} if protoimpl.UnsafeEnabled { - mi := &file_volume_server_proto_msgTypes[53] + mi := &file_volume_server_proto_msgTypes[55] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2857,7 +2959,7 @@ func (x *VolumeEcShardsDeleteResponse) String() string { func (*VolumeEcShardsDeleteResponse) ProtoMessage() {} func (x *VolumeEcShardsDeleteResponse) ProtoReflect() protoreflect.Message { - mi := &file_volume_server_proto_msgTypes[53] + mi := &file_volume_server_proto_msgTypes[55] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2870,7 +2972,7 @@ func (x *VolumeEcShardsDeleteResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use VolumeEcShardsDeleteResponse.ProtoReflect.Descriptor instead. func (*VolumeEcShardsDeleteResponse) Descriptor() ([]byte, []int) { - return file_volume_server_proto_rawDescGZIP(), []int{53} + return file_volume_server_proto_rawDescGZIP(), []int{55} } type VolumeEcShardsMountRequest struct { @@ -2886,7 +2988,7 @@ type VolumeEcShardsMountRequest struct { func (x *VolumeEcShardsMountRequest) Reset() { *x = VolumeEcShardsMountRequest{} if protoimpl.UnsafeEnabled { - mi := &file_volume_server_proto_msgTypes[54] + mi := &file_volume_server_proto_msgTypes[56] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2899,7 +3001,7 @@ func (x *VolumeEcShardsMountRequest) String() string { func (*VolumeEcShardsMountRequest) ProtoMessage() {} func (x *VolumeEcShardsMountRequest) ProtoReflect() protoreflect.Message { - mi := &file_volume_server_proto_msgTypes[54] + mi := &file_volume_server_proto_msgTypes[56] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2912,7 +3014,7 @@ func (x *VolumeEcShardsMountRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use VolumeEcShardsMountRequest.ProtoReflect.Descriptor instead. func (*VolumeEcShardsMountRequest) Descriptor() ([]byte, []int) { - return file_volume_server_proto_rawDescGZIP(), []int{54} + return file_volume_server_proto_rawDescGZIP(), []int{56} } func (x *VolumeEcShardsMountRequest) GetVolumeId() uint32 { @@ -2945,7 +3047,7 @@ type VolumeEcShardsMountResponse struct { func (x *VolumeEcShardsMountResponse) Reset() { *x = VolumeEcShardsMountResponse{} if protoimpl.UnsafeEnabled { - mi := &file_volume_server_proto_msgTypes[55] + mi := &file_volume_server_proto_msgTypes[57] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2958,7 +3060,7 @@ func (x *VolumeEcShardsMountResponse) String() string { func (*VolumeEcShardsMountResponse) ProtoMessage() {} func (x *VolumeEcShardsMountResponse) ProtoReflect() protoreflect.Message { - mi := &file_volume_server_proto_msgTypes[55] + mi := &file_volume_server_proto_msgTypes[57] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2971,7 +3073,7 @@ func (x *VolumeEcShardsMountResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use VolumeEcShardsMountResponse.ProtoReflect.Descriptor instead. func (*VolumeEcShardsMountResponse) Descriptor() ([]byte, []int) { - return file_volume_server_proto_rawDescGZIP(), []int{55} + return file_volume_server_proto_rawDescGZIP(), []int{57} } type VolumeEcShardsUnmountRequest struct { @@ -2986,7 +3088,7 @@ type VolumeEcShardsUnmountRequest struct { func (x *VolumeEcShardsUnmountRequest) Reset() { *x = VolumeEcShardsUnmountRequest{} if protoimpl.UnsafeEnabled { - mi := &file_volume_server_proto_msgTypes[56] + mi := &file_volume_server_proto_msgTypes[58] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2999,7 +3101,7 @@ func (x *VolumeEcShardsUnmountRequest) String() string { func (*VolumeEcShardsUnmountRequest) ProtoMessage() {} func (x *VolumeEcShardsUnmountRequest) ProtoReflect() protoreflect.Message { - mi := &file_volume_server_proto_msgTypes[56] + mi := &file_volume_server_proto_msgTypes[58] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3012,7 +3114,7 @@ func (x *VolumeEcShardsUnmountRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use VolumeEcShardsUnmountRequest.ProtoReflect.Descriptor instead. func (*VolumeEcShardsUnmountRequest) Descriptor() ([]byte, []int) { - return file_volume_server_proto_rawDescGZIP(), []int{56} + return file_volume_server_proto_rawDescGZIP(), []int{58} } func (x *VolumeEcShardsUnmountRequest) GetVolumeId() uint32 { @@ -3038,7 +3140,7 @@ type VolumeEcShardsUnmountResponse struct { func (x *VolumeEcShardsUnmountResponse) Reset() { *x = VolumeEcShardsUnmountResponse{} if protoimpl.UnsafeEnabled { - mi := &file_volume_server_proto_msgTypes[57] + mi := &file_volume_server_proto_msgTypes[59] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3051,7 +3153,7 @@ func (x *VolumeEcShardsUnmountResponse) String() string { func (*VolumeEcShardsUnmountResponse) ProtoMessage() {} func (x *VolumeEcShardsUnmountResponse) ProtoReflect() protoreflect.Message { - mi := &file_volume_server_proto_msgTypes[57] + mi := &file_volume_server_proto_msgTypes[59] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3064,7 +3166,7 @@ func (x *VolumeEcShardsUnmountResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use VolumeEcShardsUnmountResponse.ProtoReflect.Descriptor instead. func (*VolumeEcShardsUnmountResponse) Descriptor() ([]byte, []int) { - return file_volume_server_proto_rawDescGZIP(), []int{57} + return file_volume_server_proto_rawDescGZIP(), []int{59} } type VolumeEcShardReadRequest struct { @@ -3082,7 +3184,7 @@ type VolumeEcShardReadRequest struct { func (x *VolumeEcShardReadRequest) Reset() { *x = VolumeEcShardReadRequest{} if protoimpl.UnsafeEnabled { - mi := &file_volume_server_proto_msgTypes[58] + mi := &file_volume_server_proto_msgTypes[60] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3095,7 +3197,7 @@ func (x *VolumeEcShardReadRequest) String() string { func (*VolumeEcShardReadRequest) ProtoMessage() {} func (x *VolumeEcShardReadRequest) ProtoReflect() protoreflect.Message { - mi := &file_volume_server_proto_msgTypes[58] + mi := &file_volume_server_proto_msgTypes[60] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3108,7 +3210,7 @@ func (x *VolumeEcShardReadRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use VolumeEcShardReadRequest.ProtoReflect.Descriptor instead. func (*VolumeEcShardReadRequest) Descriptor() ([]byte, []int) { - return file_volume_server_proto_rawDescGZIP(), []int{58} + return file_volume_server_proto_rawDescGZIP(), []int{60} } func (x *VolumeEcShardReadRequest) GetVolumeId() uint32 { @@ -3158,7 +3260,7 @@ type VolumeEcShardReadResponse struct { func (x *VolumeEcShardReadResponse) Reset() { *x = VolumeEcShardReadResponse{} if protoimpl.UnsafeEnabled { - mi := &file_volume_server_proto_msgTypes[59] + mi := &file_volume_server_proto_msgTypes[61] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3171,7 +3273,7 @@ func (x *VolumeEcShardReadResponse) String() string { func (*VolumeEcShardReadResponse) ProtoMessage() {} func (x *VolumeEcShardReadResponse) ProtoReflect() protoreflect.Message { - mi := &file_volume_server_proto_msgTypes[59] + mi := &file_volume_server_proto_msgTypes[61] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3184,7 +3286,7 @@ func (x *VolumeEcShardReadResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use VolumeEcShardReadResponse.ProtoReflect.Descriptor instead. func (*VolumeEcShardReadResponse) Descriptor() ([]byte, []int) { - return file_volume_server_proto_rawDescGZIP(), []int{59} + return file_volume_server_proto_rawDescGZIP(), []int{61} } func (x *VolumeEcShardReadResponse) GetData() []byte { @@ -3215,7 +3317,7 @@ type VolumeEcBlobDeleteRequest struct { func (x *VolumeEcBlobDeleteRequest) Reset() { *x = VolumeEcBlobDeleteRequest{} if protoimpl.UnsafeEnabled { - mi := &file_volume_server_proto_msgTypes[60] + mi := &file_volume_server_proto_msgTypes[62] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3228,7 +3330,7 @@ func (x *VolumeEcBlobDeleteRequest) String() string { func (*VolumeEcBlobDeleteRequest) ProtoMessage() {} func (x *VolumeEcBlobDeleteRequest) ProtoReflect() protoreflect.Message { - mi := &file_volume_server_proto_msgTypes[60] + mi := &file_volume_server_proto_msgTypes[62] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3241,7 +3343,7 @@ func (x *VolumeEcBlobDeleteRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use VolumeEcBlobDeleteRequest.ProtoReflect.Descriptor instead. func (*VolumeEcBlobDeleteRequest) Descriptor() ([]byte, []int) { - return file_volume_server_proto_rawDescGZIP(), []int{60} + return file_volume_server_proto_rawDescGZIP(), []int{62} } func (x *VolumeEcBlobDeleteRequest) GetVolumeId() uint32 { @@ -3281,7 +3383,7 @@ type VolumeEcBlobDeleteResponse struct { func (x *VolumeEcBlobDeleteResponse) Reset() { *x = VolumeEcBlobDeleteResponse{} if protoimpl.UnsafeEnabled { - mi := &file_volume_server_proto_msgTypes[61] + mi := &file_volume_server_proto_msgTypes[63] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3294,7 +3396,7 @@ func (x *VolumeEcBlobDeleteResponse) String() string { func (*VolumeEcBlobDeleteResponse) ProtoMessage() {} func (x *VolumeEcBlobDeleteResponse) ProtoReflect() protoreflect.Message { - mi := &file_volume_server_proto_msgTypes[61] + mi := &file_volume_server_proto_msgTypes[63] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3307,7 +3409,7 @@ func (x *VolumeEcBlobDeleteResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use VolumeEcBlobDeleteResponse.ProtoReflect.Descriptor instead. func (*VolumeEcBlobDeleteResponse) Descriptor() ([]byte, []int) { - return file_volume_server_proto_rawDescGZIP(), []int{61} + return file_volume_server_proto_rawDescGZIP(), []int{63} } type VolumeEcShardsToVolumeRequest struct { @@ -3322,7 +3424,7 @@ type VolumeEcShardsToVolumeRequest struct { func (x *VolumeEcShardsToVolumeRequest) Reset() { *x = VolumeEcShardsToVolumeRequest{} if protoimpl.UnsafeEnabled { - mi := &file_volume_server_proto_msgTypes[62] + mi := &file_volume_server_proto_msgTypes[64] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3335,7 +3437,7 @@ func (x *VolumeEcShardsToVolumeRequest) String() string { func (*VolumeEcShardsToVolumeRequest) ProtoMessage() {} func (x *VolumeEcShardsToVolumeRequest) ProtoReflect() protoreflect.Message { - mi := &file_volume_server_proto_msgTypes[62] + mi := &file_volume_server_proto_msgTypes[64] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3348,7 +3450,7 @@ func (x *VolumeEcShardsToVolumeRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use VolumeEcShardsToVolumeRequest.ProtoReflect.Descriptor instead. func (*VolumeEcShardsToVolumeRequest) Descriptor() ([]byte, []int) { - return file_volume_server_proto_rawDescGZIP(), []int{62} + return file_volume_server_proto_rawDescGZIP(), []int{64} } func (x *VolumeEcShardsToVolumeRequest) GetVolumeId() uint32 { @@ -3374,7 +3476,7 @@ type VolumeEcShardsToVolumeResponse struct { func (x *VolumeEcShardsToVolumeResponse) Reset() { *x = VolumeEcShardsToVolumeResponse{} if protoimpl.UnsafeEnabled { - mi := &file_volume_server_proto_msgTypes[63] + mi := &file_volume_server_proto_msgTypes[65] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3387,7 +3489,7 @@ func (x *VolumeEcShardsToVolumeResponse) String() string { func (*VolumeEcShardsToVolumeResponse) ProtoMessage() {} func (x *VolumeEcShardsToVolumeResponse) ProtoReflect() protoreflect.Message { - mi := &file_volume_server_proto_msgTypes[63] + mi := &file_volume_server_proto_msgTypes[65] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3400,7 +3502,7 @@ func (x *VolumeEcShardsToVolumeResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use VolumeEcShardsToVolumeResponse.ProtoReflect.Descriptor instead. func (*VolumeEcShardsToVolumeResponse) Descriptor() ([]byte, []int) { - return file_volume_server_proto_rawDescGZIP(), []int{63} + return file_volume_server_proto_rawDescGZIP(), []int{65} } type ReadVolumeFileStatusRequest struct { @@ -3414,7 +3516,7 @@ type ReadVolumeFileStatusRequest struct { func (x *ReadVolumeFileStatusRequest) Reset() { *x = ReadVolumeFileStatusRequest{} if protoimpl.UnsafeEnabled { - mi := &file_volume_server_proto_msgTypes[64] + mi := &file_volume_server_proto_msgTypes[66] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3427,7 +3529,7 @@ func (x *ReadVolumeFileStatusRequest) String() string { func (*ReadVolumeFileStatusRequest) ProtoMessage() {} func (x *ReadVolumeFileStatusRequest) ProtoReflect() protoreflect.Message { - mi := &file_volume_server_proto_msgTypes[64] + mi := &file_volume_server_proto_msgTypes[66] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3440,7 +3542,7 @@ func (x *ReadVolumeFileStatusRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use ReadVolumeFileStatusRequest.ProtoReflect.Descriptor instead. func (*ReadVolumeFileStatusRequest) Descriptor() ([]byte, []int) { - return file_volume_server_proto_rawDescGZIP(), []int{64} + return file_volume_server_proto_rawDescGZIP(), []int{66} } func (x *ReadVolumeFileStatusRequest) GetVolumeId() uint32 { @@ -3469,7 +3571,7 @@ type ReadVolumeFileStatusResponse struct { func (x *ReadVolumeFileStatusResponse) Reset() { *x = ReadVolumeFileStatusResponse{} if protoimpl.UnsafeEnabled { - mi := &file_volume_server_proto_msgTypes[65] + mi := &file_volume_server_proto_msgTypes[67] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3482,7 +3584,7 @@ func (x *ReadVolumeFileStatusResponse) String() string { func (*ReadVolumeFileStatusResponse) ProtoMessage() {} func (x *ReadVolumeFileStatusResponse) ProtoReflect() protoreflect.Message { - mi := &file_volume_server_proto_msgTypes[65] + mi := &file_volume_server_proto_msgTypes[67] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3495,7 +3597,7 @@ func (x *ReadVolumeFileStatusResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use ReadVolumeFileStatusResponse.ProtoReflect.Descriptor instead. func (*ReadVolumeFileStatusResponse) Descriptor() ([]byte, []int) { - return file_volume_server_proto_rawDescGZIP(), []int{65} + return file_volume_server_proto_rawDescGZIP(), []int{67} } func (x *ReadVolumeFileStatusResponse) GetVolumeId() uint32 { @@ -3578,7 +3680,7 @@ type DiskStatus struct { func (x *DiskStatus) Reset() { *x = DiskStatus{} if protoimpl.UnsafeEnabled { - mi := &file_volume_server_proto_msgTypes[66] + mi := &file_volume_server_proto_msgTypes[68] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3591,7 +3693,7 @@ func (x *DiskStatus) String() string { func (*DiskStatus) ProtoMessage() {} func (x *DiskStatus) ProtoReflect() protoreflect.Message { - mi := &file_volume_server_proto_msgTypes[66] + mi := &file_volume_server_proto_msgTypes[68] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3604,7 +3706,7 @@ func (x *DiskStatus) ProtoReflect() protoreflect.Message { // Deprecated: Use DiskStatus.ProtoReflect.Descriptor instead. func (*DiskStatus) Descriptor() ([]byte, []int) { - return file_volume_server_proto_rawDescGZIP(), []int{66} + return file_volume_server_proto_rawDescGZIP(), []int{68} } func (x *DiskStatus) GetDir() string { @@ -3673,7 +3775,7 @@ type MemStatus struct { func (x *MemStatus) Reset() { *x = MemStatus{} if protoimpl.UnsafeEnabled { - mi := &file_volume_server_proto_msgTypes[67] + mi := &file_volume_server_proto_msgTypes[69] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3686,7 +3788,7 @@ func (x *MemStatus) String() string { func (*MemStatus) ProtoMessage() {} func (x *MemStatus) ProtoReflect() protoreflect.Message { - mi := &file_volume_server_proto_msgTypes[67] + mi := &file_volume_server_proto_msgTypes[69] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3699,7 +3801,7 @@ func (x *MemStatus) ProtoReflect() protoreflect.Message { // Deprecated: Use MemStatus.ProtoReflect.Descriptor instead. func (*MemStatus) Descriptor() ([]byte, []int) { - return file_volume_server_proto_rawDescGZIP(), []int{67} + return file_volume_server_proto_rawDescGZIP(), []int{69} } func (x *MemStatus) GetGoroutines() int32 { @@ -3769,7 +3871,7 @@ type RemoteFile struct { func (x *RemoteFile) Reset() { *x = RemoteFile{} if protoimpl.UnsafeEnabled { - mi := &file_volume_server_proto_msgTypes[68] + mi := &file_volume_server_proto_msgTypes[70] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3782,7 +3884,7 @@ func (x *RemoteFile) String() string { func (*RemoteFile) ProtoMessage() {} func (x *RemoteFile) ProtoReflect() protoreflect.Message { - mi := &file_volume_server_proto_msgTypes[68] + mi := &file_volume_server_proto_msgTypes[70] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3795,7 +3897,7 @@ func (x *RemoteFile) ProtoReflect() protoreflect.Message { // Deprecated: Use RemoteFile.ProtoReflect.Descriptor instead. func (*RemoteFile) Descriptor() ([]byte, []int) { - return file_volume_server_proto_rawDescGZIP(), []int{68} + return file_volume_server_proto_rawDescGZIP(), []int{70} } func (x *RemoteFile) GetBackendType() string { @@ -3860,7 +3962,7 @@ type VolumeInfo struct { func (x *VolumeInfo) Reset() { *x = VolumeInfo{} if protoimpl.UnsafeEnabled { - mi := &file_volume_server_proto_msgTypes[69] + mi := &file_volume_server_proto_msgTypes[71] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3873,7 +3975,7 @@ func (x *VolumeInfo) String() string { func (*VolumeInfo) ProtoMessage() {} func (x *VolumeInfo) ProtoReflect() protoreflect.Message { - mi := &file_volume_server_proto_msgTypes[69] + mi := &file_volume_server_proto_msgTypes[71] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3886,7 +3988,7 @@ func (x *VolumeInfo) ProtoReflect() protoreflect.Message { // Deprecated: Use VolumeInfo.ProtoReflect.Descriptor instead. func (*VolumeInfo) Descriptor() ([]byte, []int) { - return file_volume_server_proto_rawDescGZIP(), []int{69} + return file_volume_server_proto_rawDescGZIP(), []int{71} } func (x *VolumeInfo) GetFiles() []*RemoteFile { @@ -3925,7 +4027,7 @@ type VolumeTierMoveDatToRemoteRequest struct { func (x *VolumeTierMoveDatToRemoteRequest) Reset() { *x = VolumeTierMoveDatToRemoteRequest{} if protoimpl.UnsafeEnabled { - mi := &file_volume_server_proto_msgTypes[70] + mi := &file_volume_server_proto_msgTypes[72] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3938,7 +4040,7 @@ func (x *VolumeTierMoveDatToRemoteRequest) String() string { func (*VolumeTierMoveDatToRemoteRequest) ProtoMessage() {} func (x *VolumeTierMoveDatToRemoteRequest) ProtoReflect() protoreflect.Message { - mi := &file_volume_server_proto_msgTypes[70] + mi := &file_volume_server_proto_msgTypes[72] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3951,7 +4053,7 @@ func (x *VolumeTierMoveDatToRemoteRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use VolumeTierMoveDatToRemoteRequest.ProtoReflect.Descriptor instead. func (*VolumeTierMoveDatToRemoteRequest) Descriptor() ([]byte, []int) { - return file_volume_server_proto_rawDescGZIP(), []int{70} + return file_volume_server_proto_rawDescGZIP(), []int{72} } func (x *VolumeTierMoveDatToRemoteRequest) GetVolumeId() uint32 { @@ -3994,7 +4096,7 @@ type VolumeTierMoveDatToRemoteResponse struct { func (x *VolumeTierMoveDatToRemoteResponse) Reset() { *x = VolumeTierMoveDatToRemoteResponse{} if protoimpl.UnsafeEnabled { - mi := &file_volume_server_proto_msgTypes[71] + mi := &file_volume_server_proto_msgTypes[73] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4007,7 +4109,7 @@ func (x *VolumeTierMoveDatToRemoteResponse) String() string { func (*VolumeTierMoveDatToRemoteResponse) ProtoMessage() {} func (x *VolumeTierMoveDatToRemoteResponse) ProtoReflect() protoreflect.Message { - mi := &file_volume_server_proto_msgTypes[71] + mi := &file_volume_server_proto_msgTypes[73] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4020,7 +4122,7 @@ func (x *VolumeTierMoveDatToRemoteResponse) ProtoReflect() protoreflect.Message // Deprecated: Use VolumeTierMoveDatToRemoteResponse.ProtoReflect.Descriptor instead. func (*VolumeTierMoveDatToRemoteResponse) Descriptor() ([]byte, []int) { - return file_volume_server_proto_rawDescGZIP(), []int{71} + return file_volume_server_proto_rawDescGZIP(), []int{73} } func (x *VolumeTierMoveDatToRemoteResponse) GetProcessed() int64 { @@ -4050,7 +4152,7 @@ type VolumeTierMoveDatFromRemoteRequest struct { func (x *VolumeTierMoveDatFromRemoteRequest) Reset() { *x = VolumeTierMoveDatFromRemoteRequest{} if protoimpl.UnsafeEnabled { - mi := &file_volume_server_proto_msgTypes[72] + mi := &file_volume_server_proto_msgTypes[74] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4063,7 +4165,7 @@ func (x *VolumeTierMoveDatFromRemoteRequest) String() string { func (*VolumeTierMoveDatFromRemoteRequest) ProtoMessage() {} func (x *VolumeTierMoveDatFromRemoteRequest) ProtoReflect() protoreflect.Message { - mi := &file_volume_server_proto_msgTypes[72] + mi := &file_volume_server_proto_msgTypes[74] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4076,7 +4178,7 @@ func (x *VolumeTierMoveDatFromRemoteRequest) ProtoReflect() protoreflect.Message // Deprecated: Use VolumeTierMoveDatFromRemoteRequest.ProtoReflect.Descriptor instead. func (*VolumeTierMoveDatFromRemoteRequest) Descriptor() ([]byte, []int) { - return file_volume_server_proto_rawDescGZIP(), []int{72} + return file_volume_server_proto_rawDescGZIP(), []int{74} } func (x *VolumeTierMoveDatFromRemoteRequest) GetVolumeId() uint32 { @@ -4112,7 +4214,7 @@ type VolumeTierMoveDatFromRemoteResponse struct { func (x *VolumeTierMoveDatFromRemoteResponse) Reset() { *x = VolumeTierMoveDatFromRemoteResponse{} if protoimpl.UnsafeEnabled { - mi := &file_volume_server_proto_msgTypes[73] + mi := &file_volume_server_proto_msgTypes[75] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4125,7 +4227,7 @@ func (x *VolumeTierMoveDatFromRemoteResponse) String() string { func (*VolumeTierMoveDatFromRemoteResponse) ProtoMessage() {} func (x *VolumeTierMoveDatFromRemoteResponse) ProtoReflect() protoreflect.Message { - mi := &file_volume_server_proto_msgTypes[73] + mi := &file_volume_server_proto_msgTypes[75] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4138,7 +4240,7 @@ func (x *VolumeTierMoveDatFromRemoteResponse) ProtoReflect() protoreflect.Messag // Deprecated: Use VolumeTierMoveDatFromRemoteResponse.ProtoReflect.Descriptor instead. func (*VolumeTierMoveDatFromRemoteResponse) Descriptor() ([]byte, []int) { - return file_volume_server_proto_rawDescGZIP(), []int{73} + return file_volume_server_proto_rawDescGZIP(), []int{75} } func (x *VolumeTierMoveDatFromRemoteResponse) GetProcessed() int64 { @@ -4164,7 +4266,7 @@ type VolumeServerStatusRequest struct { func (x *VolumeServerStatusRequest) Reset() { *x = VolumeServerStatusRequest{} if protoimpl.UnsafeEnabled { - mi := &file_volume_server_proto_msgTypes[74] + mi := &file_volume_server_proto_msgTypes[76] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4177,7 +4279,7 @@ func (x *VolumeServerStatusRequest) String() string { func (*VolumeServerStatusRequest) ProtoMessage() {} func (x *VolumeServerStatusRequest) ProtoReflect() protoreflect.Message { - mi := &file_volume_server_proto_msgTypes[74] + mi := &file_volume_server_proto_msgTypes[76] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4190,7 +4292,7 @@ func (x *VolumeServerStatusRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use VolumeServerStatusRequest.ProtoReflect.Descriptor instead. func (*VolumeServerStatusRequest) Descriptor() ([]byte, []int) { - return file_volume_server_proto_rawDescGZIP(), []int{74} + return file_volume_server_proto_rawDescGZIP(), []int{76} } type VolumeServerStatusResponse struct { @@ -4205,7 +4307,7 @@ type VolumeServerStatusResponse struct { func (x *VolumeServerStatusResponse) Reset() { *x = VolumeServerStatusResponse{} if protoimpl.UnsafeEnabled { - mi := &file_volume_server_proto_msgTypes[75] + mi := &file_volume_server_proto_msgTypes[77] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4218,7 +4320,7 @@ func (x *VolumeServerStatusResponse) String() string { func (*VolumeServerStatusResponse) ProtoMessage() {} func (x *VolumeServerStatusResponse) ProtoReflect() protoreflect.Message { - mi := &file_volume_server_proto_msgTypes[75] + mi := &file_volume_server_proto_msgTypes[77] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4231,7 +4333,7 @@ func (x *VolumeServerStatusResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use VolumeServerStatusResponse.ProtoReflect.Descriptor instead. func (*VolumeServerStatusResponse) Descriptor() ([]byte, []int) { - return file_volume_server_proto_rawDescGZIP(), []int{75} + return file_volume_server_proto_rawDescGZIP(), []int{77} } func (x *VolumeServerStatusResponse) GetDiskStatuses() []*DiskStatus { @@ -4257,7 +4359,7 @@ type VolumeServerLeaveRequest struct { func (x *VolumeServerLeaveRequest) Reset() { *x = VolumeServerLeaveRequest{} if protoimpl.UnsafeEnabled { - mi := &file_volume_server_proto_msgTypes[76] + mi := &file_volume_server_proto_msgTypes[78] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4270,7 +4372,7 @@ func (x *VolumeServerLeaveRequest) String() string { func (*VolumeServerLeaveRequest) ProtoMessage() {} func (x *VolumeServerLeaveRequest) ProtoReflect() protoreflect.Message { - mi := &file_volume_server_proto_msgTypes[76] + mi := &file_volume_server_proto_msgTypes[78] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4283,7 +4385,7 @@ func (x *VolumeServerLeaveRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use VolumeServerLeaveRequest.ProtoReflect.Descriptor instead. func (*VolumeServerLeaveRequest) Descriptor() ([]byte, []int) { - return file_volume_server_proto_rawDescGZIP(), []int{76} + return file_volume_server_proto_rawDescGZIP(), []int{78} } type VolumeServerLeaveResponse struct { @@ -4295,7 +4397,7 @@ type VolumeServerLeaveResponse struct { func (x *VolumeServerLeaveResponse) Reset() { *x = VolumeServerLeaveResponse{} if protoimpl.UnsafeEnabled { - mi := &file_volume_server_proto_msgTypes[77] + mi := &file_volume_server_proto_msgTypes[79] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4308,7 +4410,7 @@ func (x *VolumeServerLeaveResponse) String() string { func (*VolumeServerLeaveResponse) ProtoMessage() {} func (x *VolumeServerLeaveResponse) ProtoReflect() protoreflect.Message { - mi := &file_volume_server_proto_msgTypes[77] + mi := &file_volume_server_proto_msgTypes[79] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4321,7 +4423,7 @@ func (x *VolumeServerLeaveResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use VolumeServerLeaveResponse.ProtoReflect.Descriptor instead. func (*VolumeServerLeaveResponse) Descriptor() ([]byte, []int) { - return file_volume_server_proto_rawDescGZIP(), []int{77} + return file_volume_server_proto_rawDescGZIP(), []int{79} } // remote storage @@ -4345,7 +4447,7 @@ type FetchAndWriteNeedleRequest struct { func (x *FetchAndWriteNeedleRequest) Reset() { *x = FetchAndWriteNeedleRequest{} if protoimpl.UnsafeEnabled { - mi := &file_volume_server_proto_msgTypes[78] + mi := &file_volume_server_proto_msgTypes[80] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4358,7 +4460,7 @@ func (x *FetchAndWriteNeedleRequest) String() string { func (*FetchAndWriteNeedleRequest) ProtoMessage() {} func (x *FetchAndWriteNeedleRequest) ProtoReflect() protoreflect.Message { - mi := &file_volume_server_proto_msgTypes[78] + mi := &file_volume_server_proto_msgTypes[80] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4371,7 +4473,7 @@ func (x *FetchAndWriteNeedleRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use FetchAndWriteNeedleRequest.ProtoReflect.Descriptor instead. func (*FetchAndWriteNeedleRequest) Descriptor() ([]byte, []int) { - return file_volume_server_proto_rawDescGZIP(), []int{78} + return file_volume_server_proto_rawDescGZIP(), []int{80} } func (x *FetchAndWriteNeedleRequest) GetVolumeId() uint32 { @@ -4446,7 +4548,7 @@ type FetchAndWriteNeedleResponse struct { func (x *FetchAndWriteNeedleResponse) Reset() { *x = FetchAndWriteNeedleResponse{} if protoimpl.UnsafeEnabled { - mi := &file_volume_server_proto_msgTypes[79] + mi := &file_volume_server_proto_msgTypes[81] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4459,7 +4561,7 @@ func (x *FetchAndWriteNeedleResponse) String() string { func (*FetchAndWriteNeedleResponse) ProtoMessage() {} func (x *FetchAndWriteNeedleResponse) ProtoReflect() protoreflect.Message { - mi := &file_volume_server_proto_msgTypes[79] + mi := &file_volume_server_proto_msgTypes[81] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4472,7 +4574,7 @@ func (x *FetchAndWriteNeedleResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use FetchAndWriteNeedleResponse.ProtoReflect.Descriptor instead. func (*FetchAndWriteNeedleResponse) Descriptor() ([]byte, []int) { - return file_volume_server_proto_rawDescGZIP(), []int{79} + return file_volume_server_proto_rawDescGZIP(), []int{81} } // select on volume servers @@ -4491,7 +4593,7 @@ type QueryRequest struct { func (x *QueryRequest) Reset() { *x = QueryRequest{} if protoimpl.UnsafeEnabled { - mi := &file_volume_server_proto_msgTypes[80] + mi := &file_volume_server_proto_msgTypes[82] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4504,7 +4606,7 @@ func (x *QueryRequest) String() string { func (*QueryRequest) ProtoMessage() {} func (x *QueryRequest) ProtoReflect() protoreflect.Message { - mi := &file_volume_server_proto_msgTypes[80] + mi := &file_volume_server_proto_msgTypes[82] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4517,7 +4619,7 @@ func (x *QueryRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use QueryRequest.ProtoReflect.Descriptor instead. func (*QueryRequest) Descriptor() ([]byte, []int) { - return file_volume_server_proto_rawDescGZIP(), []int{80} + return file_volume_server_proto_rawDescGZIP(), []int{82} } func (x *QueryRequest) GetSelections() []string { @@ -4566,7 +4668,7 @@ type QueriedStripe struct { func (x *QueriedStripe) Reset() { *x = QueriedStripe{} if protoimpl.UnsafeEnabled { - mi := &file_volume_server_proto_msgTypes[81] + mi := &file_volume_server_proto_msgTypes[83] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4579,7 +4681,7 @@ func (x *QueriedStripe) String() string { func (*QueriedStripe) ProtoMessage() {} func (x *QueriedStripe) ProtoReflect() protoreflect.Message { - mi := &file_volume_server_proto_msgTypes[81] + mi := &file_volume_server_proto_msgTypes[83] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4592,7 +4694,7 @@ func (x *QueriedStripe) ProtoReflect() protoreflect.Message { // Deprecated: Use QueriedStripe.ProtoReflect.Descriptor instead. func (*QueriedStripe) Descriptor() ([]byte, []int) { - return file_volume_server_proto_rawDescGZIP(), []int{81} + return file_volume_server_proto_rawDescGZIP(), []int{83} } func (x *QueriedStripe) GetRecords() []byte { @@ -4614,7 +4716,7 @@ type VolumeNeedleStatusRequest struct { func (x *VolumeNeedleStatusRequest) Reset() { *x = VolumeNeedleStatusRequest{} if protoimpl.UnsafeEnabled { - mi := &file_volume_server_proto_msgTypes[82] + mi := &file_volume_server_proto_msgTypes[84] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4627,7 +4729,7 @@ func (x *VolumeNeedleStatusRequest) String() string { func (*VolumeNeedleStatusRequest) ProtoMessage() {} func (x *VolumeNeedleStatusRequest) ProtoReflect() protoreflect.Message { - mi := &file_volume_server_proto_msgTypes[82] + mi := &file_volume_server_proto_msgTypes[84] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4640,7 +4742,7 @@ func (x *VolumeNeedleStatusRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use VolumeNeedleStatusRequest.ProtoReflect.Descriptor instead. func (*VolumeNeedleStatusRequest) Descriptor() ([]byte, []int) { - return file_volume_server_proto_rawDescGZIP(), []int{82} + return file_volume_server_proto_rawDescGZIP(), []int{84} } func (x *VolumeNeedleStatusRequest) GetVolumeId() uint32 { @@ -4673,7 +4775,7 @@ type VolumeNeedleStatusResponse struct { func (x *VolumeNeedleStatusResponse) Reset() { *x = VolumeNeedleStatusResponse{} if protoimpl.UnsafeEnabled { - mi := &file_volume_server_proto_msgTypes[83] + mi := &file_volume_server_proto_msgTypes[85] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4686,7 +4788,7 @@ func (x *VolumeNeedleStatusResponse) String() string { func (*VolumeNeedleStatusResponse) ProtoMessage() {} func (x *VolumeNeedleStatusResponse) ProtoReflect() protoreflect.Message { - mi := &file_volume_server_proto_msgTypes[83] + mi := &file_volume_server_proto_msgTypes[85] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4699,7 +4801,7 @@ func (x *VolumeNeedleStatusResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use VolumeNeedleStatusResponse.ProtoReflect.Descriptor instead. func (*VolumeNeedleStatusResponse) Descriptor() ([]byte, []int) { - return file_volume_server_proto_rawDescGZIP(), []int{83} + return file_volume_server_proto_rawDescGZIP(), []int{85} } func (x *VolumeNeedleStatusResponse) GetNeedleId() uint64 { @@ -4757,7 +4859,7 @@ type FetchAndWriteNeedleRequest_Replica struct { func (x *FetchAndWriteNeedleRequest_Replica) Reset() { *x = FetchAndWriteNeedleRequest_Replica{} if protoimpl.UnsafeEnabled { - mi := &file_volume_server_proto_msgTypes[84] + mi := &file_volume_server_proto_msgTypes[86] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4770,7 +4872,7 @@ func (x *FetchAndWriteNeedleRequest_Replica) String() string { func (*FetchAndWriteNeedleRequest_Replica) ProtoMessage() {} func (x *FetchAndWriteNeedleRequest_Replica) ProtoReflect() protoreflect.Message { - mi := &file_volume_server_proto_msgTypes[84] + mi := &file_volume_server_proto_msgTypes[86] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4783,7 +4885,7 @@ func (x *FetchAndWriteNeedleRequest_Replica) ProtoReflect() protoreflect.Message // Deprecated: Use FetchAndWriteNeedleRequest_Replica.ProtoReflect.Descriptor instead. func (*FetchAndWriteNeedleRequest_Replica) Descriptor() ([]byte, []int) { - return file_volume_server_proto_rawDescGZIP(), []int{78, 0} + return file_volume_server_proto_rawDescGZIP(), []int{80, 0} } func (x *FetchAndWriteNeedleRequest_Replica) GetUrl() string { @@ -4820,7 +4922,7 @@ type QueryRequest_Filter struct { func (x *QueryRequest_Filter) Reset() { *x = QueryRequest_Filter{} if protoimpl.UnsafeEnabled { - mi := &file_volume_server_proto_msgTypes[85] + mi := &file_volume_server_proto_msgTypes[87] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4833,7 +4935,7 @@ func (x *QueryRequest_Filter) String() string { func (*QueryRequest_Filter) ProtoMessage() {} func (x *QueryRequest_Filter) ProtoReflect() protoreflect.Message { - mi := &file_volume_server_proto_msgTypes[85] + mi := &file_volume_server_proto_msgTypes[87] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4846,7 +4948,7 @@ func (x *QueryRequest_Filter) ProtoReflect() protoreflect.Message { // Deprecated: Use QueryRequest_Filter.ProtoReflect.Descriptor instead. func (*QueryRequest_Filter) Descriptor() ([]byte, []int) { - return file_volume_server_proto_rawDescGZIP(), []int{80, 0} + return file_volume_server_proto_rawDescGZIP(), []int{82, 0} } func (x *QueryRequest_Filter) GetField() string { @@ -4885,7 +4987,7 @@ type QueryRequest_InputSerialization struct { func (x *QueryRequest_InputSerialization) Reset() { *x = QueryRequest_InputSerialization{} if protoimpl.UnsafeEnabled { - mi := &file_volume_server_proto_msgTypes[86] + mi := &file_volume_server_proto_msgTypes[88] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4898,7 +5000,7 @@ func (x *QueryRequest_InputSerialization) String() string { func (*QueryRequest_InputSerialization) ProtoMessage() {} func (x *QueryRequest_InputSerialization) ProtoReflect() protoreflect.Message { - mi := &file_volume_server_proto_msgTypes[86] + mi := &file_volume_server_proto_msgTypes[88] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4911,7 +5013,7 @@ func (x *QueryRequest_InputSerialization) ProtoReflect() protoreflect.Message { // Deprecated: Use QueryRequest_InputSerialization.ProtoReflect.Descriptor instead. func (*QueryRequest_InputSerialization) Descriptor() ([]byte, []int) { - return file_volume_server_proto_rawDescGZIP(), []int{80, 1} + return file_volume_server_proto_rawDescGZIP(), []int{82, 1} } func (x *QueryRequest_InputSerialization) GetCompressionType() string { @@ -4954,7 +5056,7 @@ type QueryRequest_OutputSerialization struct { func (x *QueryRequest_OutputSerialization) Reset() { *x = QueryRequest_OutputSerialization{} if protoimpl.UnsafeEnabled { - mi := &file_volume_server_proto_msgTypes[87] + mi := &file_volume_server_proto_msgTypes[89] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4967,7 +5069,7 @@ func (x *QueryRequest_OutputSerialization) String() string { func (*QueryRequest_OutputSerialization) ProtoMessage() {} func (x *QueryRequest_OutputSerialization) ProtoReflect() protoreflect.Message { - mi := &file_volume_server_proto_msgTypes[87] + mi := &file_volume_server_proto_msgTypes[89] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4980,7 +5082,7 @@ func (x *QueryRequest_OutputSerialization) ProtoReflect() protoreflect.Message { // Deprecated: Use QueryRequest_OutputSerialization.ProtoReflect.Descriptor instead. func (*QueryRequest_OutputSerialization) Descriptor() ([]byte, []int) { - return file_volume_server_proto_rawDescGZIP(), []int{80, 2} + return file_volume_server_proto_rawDescGZIP(), []int{82, 2} } func (x *QueryRequest_OutputSerialization) GetCsvOutput() *QueryRequest_OutputSerialization_CSVOutput { @@ -5015,7 +5117,7 @@ type QueryRequest_InputSerialization_CSVInput struct { func (x *QueryRequest_InputSerialization_CSVInput) Reset() { *x = QueryRequest_InputSerialization_CSVInput{} if protoimpl.UnsafeEnabled { - mi := &file_volume_server_proto_msgTypes[88] + mi := &file_volume_server_proto_msgTypes[90] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5028,7 +5130,7 @@ func (x *QueryRequest_InputSerialization_CSVInput) String() string { func (*QueryRequest_InputSerialization_CSVInput) ProtoMessage() {} func (x *QueryRequest_InputSerialization_CSVInput) ProtoReflect() protoreflect.Message { - mi := &file_volume_server_proto_msgTypes[88] + mi := &file_volume_server_proto_msgTypes[90] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5041,7 +5143,7 @@ func (x *QueryRequest_InputSerialization_CSVInput) ProtoReflect() protoreflect.M // Deprecated: Use QueryRequest_InputSerialization_CSVInput.ProtoReflect.Descriptor instead. func (*QueryRequest_InputSerialization_CSVInput) Descriptor() ([]byte, []int) { - return file_volume_server_proto_rawDescGZIP(), []int{80, 1, 0} + return file_volume_server_proto_rawDescGZIP(), []int{82, 1, 0} } func (x *QueryRequest_InputSerialization_CSVInput) GetFileHeaderInfo() string { @@ -5104,7 +5206,7 @@ type QueryRequest_InputSerialization_JSONInput struct { func (x *QueryRequest_InputSerialization_JSONInput) Reset() { *x = QueryRequest_InputSerialization_JSONInput{} if protoimpl.UnsafeEnabled { - mi := &file_volume_server_proto_msgTypes[89] + mi := &file_volume_server_proto_msgTypes[91] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5117,7 +5219,7 @@ func (x *QueryRequest_InputSerialization_JSONInput) String() string { func (*QueryRequest_InputSerialization_JSONInput) ProtoMessage() {} func (x *QueryRequest_InputSerialization_JSONInput) ProtoReflect() protoreflect.Message { - mi := &file_volume_server_proto_msgTypes[89] + mi := &file_volume_server_proto_msgTypes[91] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5130,7 +5232,7 @@ func (x *QueryRequest_InputSerialization_JSONInput) ProtoReflect() protoreflect. // Deprecated: Use QueryRequest_InputSerialization_JSONInput.ProtoReflect.Descriptor instead. func (*QueryRequest_InputSerialization_JSONInput) Descriptor() ([]byte, []int) { - return file_volume_server_proto_rawDescGZIP(), []int{80, 1, 1} + return file_volume_server_proto_rawDescGZIP(), []int{82, 1, 1} } func (x *QueryRequest_InputSerialization_JSONInput) GetType() string { @@ -5149,7 +5251,7 @@ type QueryRequest_InputSerialization_ParquetInput struct { func (x *QueryRequest_InputSerialization_ParquetInput) Reset() { *x = QueryRequest_InputSerialization_ParquetInput{} if protoimpl.UnsafeEnabled { - mi := &file_volume_server_proto_msgTypes[90] + mi := &file_volume_server_proto_msgTypes[92] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5162,7 +5264,7 @@ func (x *QueryRequest_InputSerialization_ParquetInput) String() string { func (*QueryRequest_InputSerialization_ParquetInput) ProtoMessage() {} func (x *QueryRequest_InputSerialization_ParquetInput) ProtoReflect() protoreflect.Message { - mi := &file_volume_server_proto_msgTypes[90] + mi := &file_volume_server_proto_msgTypes[92] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5175,7 +5277,7 @@ func (x *QueryRequest_InputSerialization_ParquetInput) ProtoReflect() protorefle // Deprecated: Use QueryRequest_InputSerialization_ParquetInput.ProtoReflect.Descriptor instead. func (*QueryRequest_InputSerialization_ParquetInput) Descriptor() ([]byte, []int) { - return file_volume_server_proto_rawDescGZIP(), []int{80, 1, 2} + return file_volume_server_proto_rawDescGZIP(), []int{82, 1, 2} } type QueryRequest_OutputSerialization_CSVOutput struct { @@ -5193,7 +5295,7 @@ type QueryRequest_OutputSerialization_CSVOutput struct { func (x *QueryRequest_OutputSerialization_CSVOutput) Reset() { *x = QueryRequest_OutputSerialization_CSVOutput{} if protoimpl.UnsafeEnabled { - mi := &file_volume_server_proto_msgTypes[91] + mi := &file_volume_server_proto_msgTypes[93] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5206,7 +5308,7 @@ func (x *QueryRequest_OutputSerialization_CSVOutput) String() string { func (*QueryRequest_OutputSerialization_CSVOutput) ProtoMessage() {} func (x *QueryRequest_OutputSerialization_CSVOutput) ProtoReflect() protoreflect.Message { - mi := &file_volume_server_proto_msgTypes[91] + mi := &file_volume_server_proto_msgTypes[93] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5219,7 +5321,7 @@ func (x *QueryRequest_OutputSerialization_CSVOutput) ProtoReflect() protoreflect // Deprecated: Use QueryRequest_OutputSerialization_CSVOutput.ProtoReflect.Descriptor instead. func (*QueryRequest_OutputSerialization_CSVOutput) Descriptor() ([]byte, []int) { - return file_volume_server_proto_rawDescGZIP(), []int{80, 2, 0} + return file_volume_server_proto_rawDescGZIP(), []int{82, 2, 0} } func (x *QueryRequest_OutputSerialization_CSVOutput) GetQuoteFields() string { @@ -5268,7 +5370,7 @@ type QueryRequest_OutputSerialization_JSONOutput struct { func (x *QueryRequest_OutputSerialization_JSONOutput) Reset() { *x = QueryRequest_OutputSerialization_JSONOutput{} if protoimpl.UnsafeEnabled { - mi := &file_volume_server_proto_msgTypes[92] + mi := &file_volume_server_proto_msgTypes[94] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5281,7 +5383,7 @@ func (x *QueryRequest_OutputSerialization_JSONOutput) String() string { func (*QueryRequest_OutputSerialization_JSONOutput) ProtoMessage() {} func (x *QueryRequest_OutputSerialization_JSONOutput) ProtoReflect() protoreflect.Message { - mi := &file_volume_server_proto_msgTypes[92] + mi := &file_volume_server_proto_msgTypes[94] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5294,7 +5396,7 @@ func (x *QueryRequest_OutputSerialization_JSONOutput) ProtoReflect() protoreflec // Deprecated: Use QueryRequest_OutputSerialization_JSONOutput.ProtoReflect.Descriptor instead. func (*QueryRequest_OutputSerialization_JSONOutput) Descriptor() ([]byte, []int) { - return file_volume_server_proto_rawDescGZIP(), []int{80, 2, 1} + return file_volume_server_proto_rawDescGZIP(), []int{82, 2, 1} } func (x *QueryRequest_OutputSerialization_JSONOutput) GetRecordDelimiter() string { @@ -5513,693 +5615,708 @@ var file_volume_server_proto_rawDesc = []byte{ 0x12, 0x1f, 0x0a, 0x0b, 0x6e, 0x65, 0x65, 0x64, 0x6c, 0x65, 0x5f, 0x62, 0x6c, 0x6f, 0x62, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0a, 0x6e, 0x65, 0x65, 0x64, 0x6c, 0x65, 0x42, 0x6c, 0x6f, 0x62, 0x22, 0x19, 0x0a, 0x17, 0x57, 0x72, 0x69, 0x74, 0x65, 0x4e, 0x65, 0x65, 0x64, 0x6c, 0x65, - 0x42, 0x6c, 0x6f, 0x62, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x83, 0x01, 0x0a, - 0x17, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x54, 0x61, 0x69, 0x6c, 0x53, 0x65, 0x6e, 0x64, 0x65, - 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x76, 0x6f, 0x6c, 0x75, - 0x6d, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x76, 0x6f, 0x6c, - 0x75, 0x6d, 0x65, 0x49, 0x64, 0x12, 0x19, 0x0a, 0x08, 0x73, 0x69, 0x6e, 0x63, 0x65, 0x5f, 0x6e, - 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x07, 0x73, 0x69, 0x6e, 0x63, 0x65, 0x4e, 0x73, - 0x12, 0x30, 0x0a, 0x14, 0x69, 0x64, 0x6c, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, - 0x5f, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x12, - 0x69, 0x64, 0x6c, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x53, 0x65, 0x63, 0x6f, 0x6e, - 0x64, 0x73, 0x22, 0x84, 0x01, 0x0a, 0x18, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x54, 0x61, 0x69, - 0x6c, 0x53, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, - 0x23, 0x0a, 0x0d, 0x6e, 0x65, 0x65, 0x64, 0x6c, 0x65, 0x5f, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0c, 0x6e, 0x65, 0x65, 0x64, 0x6c, 0x65, 0x48, 0x65, - 0x61, 0x64, 0x65, 0x72, 0x12, 0x1f, 0x0a, 0x0b, 0x6e, 0x65, 0x65, 0x64, 0x6c, 0x65, 0x5f, 0x62, - 0x6f, 0x64, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0a, 0x6e, 0x65, 0x65, 0x64, 0x6c, - 0x65, 0x42, 0x6f, 0x64, 0x79, 0x12, 0x22, 0x0a, 0x0d, 0x69, 0x73, 0x5f, 0x6c, 0x61, 0x73, 0x74, - 0x5f, 0x63, 0x68, 0x75, 0x6e, 0x6b, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x69, 0x73, - 0x4c, 0x61, 0x73, 0x74, 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x22, 0xb7, 0x01, 0x0a, 0x19, 0x56, 0x6f, - 0x6c, 0x75, 0x6d, 0x65, 0x54, 0x61, 0x69, 0x6c, 0x52, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x76, 0x6f, 0x6c, 0x75, 0x6d, - 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x76, 0x6f, 0x6c, 0x75, - 0x6d, 0x65, 0x49, 0x64, 0x12, 0x19, 0x0a, 0x08, 0x73, 0x69, 0x6e, 0x63, 0x65, 0x5f, 0x6e, 0x73, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x07, 0x73, 0x69, 0x6e, 0x63, 0x65, 0x4e, 0x73, 0x12, - 0x30, 0x0a, 0x14, 0x69, 0x64, 0x6c, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x5f, - 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x12, 0x69, - 0x64, 0x6c, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x53, 0x65, 0x63, 0x6f, 0x6e, 0x64, - 0x73, 0x12, 0x30, 0x0a, 0x14, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x76, 0x6f, 0x6c, 0x75, - 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x12, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x53, 0x65, 0x72, - 0x76, 0x65, 0x72, 0x22, 0x1c, 0x0a, 0x1a, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x54, 0x61, 0x69, - 0x6c, 0x52, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x22, 0x5c, 0x0a, 0x1d, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x45, 0x63, 0x53, 0x68, 0x61, - 0x72, 0x64, 0x73, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x69, 0x64, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x49, 0x64, 0x12, - 0x1e, 0x0a, 0x0a, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x0a, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x22, - 0x20, 0x0a, 0x1e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x45, 0x63, 0x53, 0x68, 0x61, 0x72, 0x64, - 0x73, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x22, 0x5b, 0x0a, 0x1c, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x45, 0x63, 0x53, 0x68, 0x61, - 0x72, 0x64, 0x73, 0x52, 0x65, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x49, 0x64, 0x12, 0x1e, - 0x0a, 0x0a, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x0a, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x4b, - 0x0a, 0x1d, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x45, 0x63, 0x53, 0x68, 0x61, 0x72, 0x64, 0x73, - 0x52, 0x65, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, - 0x2a, 0x0a, 0x11, 0x72, 0x65, 0x62, 0x75, 0x69, 0x6c, 0x74, 0x5f, 0x73, 0x68, 0x61, 0x72, 0x64, - 0x5f, 0x69, 0x64, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0d, 0x52, 0x0f, 0x72, 0x65, 0x62, 0x75, - 0x69, 0x6c, 0x74, 0x53, 0x68, 0x61, 0x72, 0x64, 0x49, 0x64, 0x73, 0x22, 0x8b, 0x02, 0x0a, 0x19, - 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x45, 0x63, 0x53, 0x68, 0x61, 0x72, 0x64, 0x73, 0x43, 0x6f, - 0x70, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x76, 0x6f, 0x6c, - 0x75, 0x6d, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x76, 0x6f, - 0x6c, 0x75, 0x6d, 0x65, 0x49, 0x64, 0x12, 0x1e, 0x0a, 0x0a, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, - 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x63, 0x6f, 0x6c, 0x6c, - 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1b, 0x0a, 0x09, 0x73, 0x68, 0x61, 0x72, 0x64, 0x5f, - 0x69, 0x64, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0d, 0x52, 0x08, 0x73, 0x68, 0x61, 0x72, 0x64, - 0x49, 0x64, 0x73, 0x12, 0x22, 0x0a, 0x0d, 0x63, 0x6f, 0x70, 0x79, 0x5f, 0x65, 0x63, 0x78, 0x5f, - 0x66, 0x69, 0x6c, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x63, 0x6f, 0x70, 0x79, - 0x45, 0x63, 0x78, 0x46, 0x69, 0x6c, 0x65, 0x12, 0x28, 0x0a, 0x10, 0x73, 0x6f, 0x75, 0x72, 0x63, - 0x65, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x6e, 0x6f, 0x64, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x0e, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x44, 0x61, 0x74, 0x61, 0x4e, 0x6f, 0x64, - 0x65, 0x12, 0x22, 0x0a, 0x0d, 0x63, 0x6f, 0x70, 0x79, 0x5f, 0x65, 0x63, 0x6a, 0x5f, 0x66, 0x69, - 0x6c, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x63, 0x6f, 0x70, 0x79, 0x45, 0x63, - 0x6a, 0x46, 0x69, 0x6c, 0x65, 0x12, 0x22, 0x0a, 0x0d, 0x63, 0x6f, 0x70, 0x79, 0x5f, 0x76, 0x69, - 0x66, 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x63, 0x6f, - 0x70, 0x79, 0x56, 0x69, 0x66, 0x46, 0x69, 0x6c, 0x65, 0x22, 0x1c, 0x0a, 0x1a, 0x56, 0x6f, 0x6c, - 0x75, 0x6d, 0x65, 0x45, 0x63, 0x53, 0x68, 0x61, 0x72, 0x64, 0x73, 0x43, 0x6f, 0x70, 0x79, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x77, 0x0a, 0x1b, 0x56, 0x6f, 0x6c, 0x75, 0x6d, - 0x65, 0x45, 0x63, 0x53, 0x68, 0x61, 0x72, 0x64, 0x73, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, + 0x42, 0x6c, 0x6f, 0x62, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x34, 0x0a, 0x15, + 0x52, 0x65, 0x61, 0x64, 0x41, 0x6c, 0x6c, 0x4e, 0x65, 0x65, 0x64, 0x6c, 0x65, 0x73, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, + 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, + 0x49, 0x64, 0x22, 0x56, 0x0a, 0x16, 0x52, 0x65, 0x61, 0x64, 0x41, 0x6c, 0x6c, 0x4e, 0x65, 0x65, + 0x64, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1b, 0x0a, 0x09, + 0x6e, 0x65, 0x65, 0x64, 0x6c, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, + 0x08, 0x6e, 0x65, 0x65, 0x64, 0x6c, 0x65, 0x49, 0x64, 0x12, 0x1f, 0x0a, 0x0b, 0x6e, 0x65, 0x65, + 0x64, 0x6c, 0x65, 0x5f, 0x62, 0x6c, 0x6f, 0x62, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0a, + 0x6e, 0x65, 0x65, 0x64, 0x6c, 0x65, 0x42, 0x6c, 0x6f, 0x62, 0x22, 0x83, 0x01, 0x0a, 0x17, 0x56, + 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x54, 0x61, 0x69, 0x6c, 0x53, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x76, 0x6f, 0x6c, 0x75, 0x6d, - 0x65, 0x49, 0x64, 0x12, 0x1e, 0x0a, 0x0a, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, - 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, - 0x69, 0x6f, 0x6e, 0x12, 0x1b, 0x0a, 0x09, 0x73, 0x68, 0x61, 0x72, 0x64, 0x5f, 0x69, 0x64, 0x73, - 0x18, 0x03, 0x20, 0x03, 0x28, 0x0d, 0x52, 0x08, 0x73, 0x68, 0x61, 0x72, 0x64, 0x49, 0x64, 0x73, - 0x22, 0x1e, 0x0a, 0x1c, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x45, 0x63, 0x53, 0x68, 0x61, 0x72, - 0x64, 0x73, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x22, 0x76, 0x0a, 0x1a, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x45, 0x63, 0x53, 0x68, 0x61, 0x72, - 0x64, 0x73, 0x4d, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, - 0x0a, 0x09, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x0d, 0x52, 0x08, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x49, 0x64, 0x12, 0x1e, 0x0a, 0x0a, 0x63, - 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x0a, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1b, 0x0a, 0x09, 0x73, - 0x68, 0x61, 0x72, 0x64, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0d, 0x52, 0x08, - 0x73, 0x68, 0x61, 0x72, 0x64, 0x49, 0x64, 0x73, 0x22, 0x1d, 0x0a, 0x1b, 0x56, 0x6f, 0x6c, 0x75, - 0x6d, 0x65, 0x45, 0x63, 0x53, 0x68, 0x61, 0x72, 0x64, 0x73, 0x4d, 0x6f, 0x75, 0x6e, 0x74, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x58, 0x0a, 0x1c, 0x56, 0x6f, 0x6c, 0x75, 0x6d, - 0x65, 0x45, 0x63, 0x53, 0x68, 0x61, 0x72, 0x64, 0x73, 0x55, 0x6e, 0x6d, 0x6f, 0x75, 0x6e, 0x74, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x76, 0x6f, 0x6c, 0x75, 0x6d, - 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x76, 0x6f, 0x6c, 0x75, - 0x6d, 0x65, 0x49, 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x73, 0x68, 0x61, 0x72, 0x64, 0x5f, 0x69, 0x64, - 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0d, 0x52, 0x08, 0x73, 0x68, 0x61, 0x72, 0x64, 0x49, 0x64, - 0x73, 0x22, 0x1f, 0x0a, 0x1d, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x45, 0x63, 0x53, 0x68, 0x61, - 0x72, 0x64, 0x73, 0x55, 0x6e, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x22, 0x99, 0x01, 0x0a, 0x18, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x45, 0x63, 0x53, - 0x68, 0x61, 0x72, 0x64, 0x52, 0x65, 0x61, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, + 0x65, 0x49, 0x64, 0x12, 0x19, 0x0a, 0x08, 0x73, 0x69, 0x6e, 0x63, 0x65, 0x5f, 0x6e, 0x73, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x07, 0x73, 0x69, 0x6e, 0x63, 0x65, 0x4e, 0x73, 0x12, 0x30, + 0x0a, 0x14, 0x69, 0x64, 0x6c, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x5f, 0x73, + 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x12, 0x69, 0x64, + 0x6c, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x53, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, + 0x22, 0x84, 0x01, 0x0a, 0x18, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x54, 0x61, 0x69, 0x6c, 0x53, + 0x65, 0x6e, 0x64, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x23, 0x0a, + 0x0d, 0x6e, 0x65, 0x65, 0x64, 0x6c, 0x65, 0x5f, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0c, 0x6e, 0x65, 0x65, 0x64, 0x6c, 0x65, 0x48, 0x65, 0x61, 0x64, + 0x65, 0x72, 0x12, 0x1f, 0x0a, 0x0b, 0x6e, 0x65, 0x65, 0x64, 0x6c, 0x65, 0x5f, 0x62, 0x6f, 0x64, + 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0a, 0x6e, 0x65, 0x65, 0x64, 0x6c, 0x65, 0x42, + 0x6f, 0x64, 0x79, 0x12, 0x22, 0x0a, 0x0d, 0x69, 0x73, 0x5f, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x63, + 0x68, 0x75, 0x6e, 0x6b, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x69, 0x73, 0x4c, 0x61, + 0x73, 0x74, 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x22, 0xb7, 0x01, 0x0a, 0x19, 0x56, 0x6f, 0x6c, 0x75, + 0x6d, 0x65, 0x54, 0x61, 0x69, 0x6c, 0x52, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, + 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, + 0x49, 0x64, 0x12, 0x19, 0x0a, 0x08, 0x73, 0x69, 0x6e, 0x63, 0x65, 0x5f, 0x6e, 0x73, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x04, 0x52, 0x07, 0x73, 0x69, 0x6e, 0x63, 0x65, 0x4e, 0x73, 0x12, 0x30, 0x0a, + 0x14, 0x69, 0x64, 0x6c, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x5f, 0x73, 0x65, + 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x12, 0x69, 0x64, 0x6c, + 0x65, 0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x53, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x12, + 0x30, 0x0a, 0x14, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, + 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x12, 0x73, + 0x6f, 0x75, 0x72, 0x63, 0x65, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, + 0x72, 0x22, 0x1c, 0x0a, 0x1a, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x54, 0x61, 0x69, 0x6c, 0x52, + 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, + 0x5c, 0x0a, 0x1d, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x45, 0x63, 0x53, 0x68, 0x61, 0x72, 0x64, + 0x73, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x12, 0x1b, 0x0a, 0x09, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x0d, 0x52, 0x08, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x49, 0x64, 0x12, 0x1e, 0x0a, + 0x0a, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x0a, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x20, 0x0a, + 0x1e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x45, 0x63, 0x53, 0x68, 0x61, 0x72, 0x64, 0x73, 0x47, + 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, + 0x5b, 0x0a, 0x1c, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x45, 0x63, 0x53, 0x68, 0x61, 0x72, 0x64, + 0x73, 0x52, 0x65, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x0d, 0x52, 0x08, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x49, 0x64, 0x12, 0x19, 0x0a, 0x08, - 0x73, 0x68, 0x61, 0x72, 0x64, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, - 0x73, 0x68, 0x61, 0x72, 0x64, 0x49, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x6f, 0x66, 0x66, 0x73, 0x65, - 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x12, - 0x12, 0x0a, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x04, 0x73, - 0x69, 0x7a, 0x65, 0x12, 0x19, 0x0a, 0x08, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x18, - 0x05, 0x20, 0x01, 0x28, 0x04, 0x52, 0x07, 0x66, 0x69, 0x6c, 0x65, 0x4b, 0x65, 0x79, 0x22, 0x4e, - 0x0a, 0x19, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x45, 0x63, 0x53, 0x68, 0x61, 0x72, 0x64, 0x52, - 0x65, 0x61, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x64, - 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x12, - 0x1d, 0x0a, 0x0a, 0x69, 0x73, 0x5f, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x08, 0x52, 0x09, 0x69, 0x73, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x22, 0x8d, - 0x01, 0x0a, 0x19, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x45, 0x63, 0x42, 0x6c, 0x6f, 0x62, 0x44, - 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, - 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, - 0x08, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x49, 0x64, 0x12, 0x1e, 0x0a, 0x0a, 0x63, 0x6f, 0x6c, - 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x63, - 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x19, 0x0a, 0x08, 0x66, 0x69, 0x6c, - 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x07, 0x66, 0x69, 0x6c, - 0x65, 0x4b, 0x65, 0x79, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, - 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x1c, - 0x0a, 0x1a, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x45, 0x63, 0x42, 0x6c, 0x6f, 0x62, 0x44, 0x65, - 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x5c, 0x0a, 0x1d, - 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x45, 0x63, 0x53, 0x68, 0x61, 0x72, 0x64, 0x73, 0x54, 0x6f, - 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, - 0x09, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, - 0x52, 0x08, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x49, 0x64, 0x12, 0x1e, 0x0a, 0x0a, 0x63, 0x6f, - 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, - 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x20, 0x0a, 0x1e, 0x56, 0x6f, - 0x6c, 0x75, 0x6d, 0x65, 0x45, 0x63, 0x53, 0x68, 0x61, 0x72, 0x64, 0x73, 0x54, 0x6f, 0x56, 0x6f, - 0x6c, 0x75, 0x6d, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x3a, 0x0a, 0x1b, - 0x52, 0x65, 0x61, 0x64, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x46, 0x69, 0x6c, 0x65, 0x53, 0x74, - 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x76, - 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, - 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x49, 0x64, 0x22, 0x8a, 0x03, 0x0a, 0x1c, 0x52, 0x65, 0x61, - 0x64, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x46, 0x69, 0x6c, 0x65, 0x53, 0x74, 0x61, 0x74, 0x75, - 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x76, 0x6f, 0x6c, - 0x75, 0x6d, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x76, 0x6f, - 0x6c, 0x75, 0x6d, 0x65, 0x49, 0x64, 0x12, 0x3b, 0x0a, 0x1a, 0x69, 0x64, 0x78, 0x5f, 0x66, 0x69, - 0x6c, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x5f, 0x73, 0x65, 0x63, - 0x6f, 0x6e, 0x64, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x17, 0x69, 0x64, 0x78, 0x46, - 0x69, 0x6c, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x53, 0x65, 0x63, 0x6f, - 0x6e, 0x64, 0x73, 0x12, 0x22, 0x0a, 0x0d, 0x69, 0x64, 0x78, 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x5f, - 0x73, 0x69, 0x7a, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, 0x69, 0x64, 0x78, 0x46, - 0x69, 0x6c, 0x65, 0x53, 0x69, 0x7a, 0x65, 0x12, 0x3b, 0x0a, 0x1a, 0x64, 0x61, 0x74, 0x5f, 0x66, - 0x69, 0x6c, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x5f, 0x73, 0x65, - 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x17, 0x64, 0x61, 0x74, - 0x46, 0x69, 0x6c, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x53, 0x65, 0x63, - 0x6f, 0x6e, 0x64, 0x73, 0x12, 0x22, 0x0a, 0x0d, 0x64, 0x61, 0x74, 0x5f, 0x66, 0x69, 0x6c, 0x65, - 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, 0x64, 0x61, 0x74, - 0x46, 0x69, 0x6c, 0x65, 0x53, 0x69, 0x7a, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x66, 0x69, 0x6c, 0x65, - 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x66, 0x69, - 0x6c, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x2f, 0x0a, 0x13, 0x63, 0x6f, 0x6d, 0x70, 0x61, - 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x72, 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x07, - 0x20, 0x01, 0x28, 0x0d, 0x52, 0x12, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, - 0x52, 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x1e, 0x0a, 0x0a, 0x63, 0x6f, 0x6c, 0x6c, - 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x63, 0x6f, - 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1b, 0x0a, 0x09, 0x64, 0x69, 0x73, 0x6b, - 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x64, 0x69, 0x73, - 0x6b, 0x54, 0x79, 0x70, 0x65, 0x22, 0xbb, 0x01, 0x0a, 0x0a, 0x44, 0x69, 0x73, 0x6b, 0x53, 0x74, - 0x61, 0x74, 0x75, 0x73, 0x12, 0x10, 0x0a, 0x03, 0x64, 0x69, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x03, 0x64, 0x69, 0x72, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x6c, 0x6c, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x04, 0x52, 0x03, 0x61, 0x6c, 0x6c, 0x12, 0x12, 0x0a, 0x04, 0x75, 0x73, 0x65, 0x64, - 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x04, 0x75, 0x73, 0x65, 0x64, 0x12, 0x12, 0x0a, 0x04, - 0x66, 0x72, 0x65, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x04, 0x66, 0x72, 0x65, 0x65, - 0x12, 0x21, 0x0a, 0x0c, 0x70, 0x65, 0x72, 0x63, 0x65, 0x6e, 0x74, 0x5f, 0x66, 0x72, 0x65, 0x65, - 0x18, 0x05, 0x20, 0x01, 0x28, 0x02, 0x52, 0x0b, 0x70, 0x65, 0x72, 0x63, 0x65, 0x6e, 0x74, 0x46, - 0x72, 0x65, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x70, 0x65, 0x72, 0x63, 0x65, 0x6e, 0x74, 0x5f, 0x75, - 0x73, 0x65, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x02, 0x52, 0x0b, 0x70, 0x65, 0x72, 0x63, 0x65, - 0x6e, 0x74, 0x55, 0x73, 0x65, 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x64, 0x69, 0x73, 0x6b, 0x5f, 0x74, - 0x79, 0x70, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x64, 0x69, 0x73, 0x6b, 0x54, - 0x79, 0x70, 0x65, 0x22, 0xa3, 0x01, 0x0a, 0x09, 0x4d, 0x65, 0x6d, 0x53, 0x74, 0x61, 0x74, 0x75, - 0x73, 0x12, 0x1e, 0x0a, 0x0a, 0x67, 0x6f, 0x72, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x65, 0x73, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0a, 0x67, 0x6f, 0x72, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x65, - 0x73, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x6c, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x03, - 0x61, 0x6c, 0x6c, 0x12, 0x12, 0x0a, 0x04, 0x75, 0x73, 0x65, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, - 0x04, 0x52, 0x04, 0x75, 0x73, 0x65, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x66, 0x72, 0x65, 0x65, 0x18, - 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x04, 0x66, 0x72, 0x65, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x73, - 0x65, 0x6c, 0x66, 0x18, 0x05, 0x20, 0x01, 0x28, 0x04, 0x52, 0x04, 0x73, 0x65, 0x6c, 0x66, 0x12, - 0x12, 0x0a, 0x04, 0x68, 0x65, 0x61, 0x70, 0x18, 0x06, 0x20, 0x01, 0x28, 0x04, 0x52, 0x04, 0x68, - 0x65, 0x61, 0x70, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x63, 0x6b, 0x18, 0x07, 0x20, 0x01, - 0x28, 0x04, 0x52, 0x05, 0x73, 0x74, 0x61, 0x63, 0x6b, 0x22, 0xd8, 0x01, 0x0a, 0x0a, 0x52, 0x65, - 0x6d, 0x6f, 0x74, 0x65, 0x46, 0x69, 0x6c, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x62, 0x61, 0x63, 0x6b, - 0x65, 0x6e, 0x64, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, - 0x62, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x54, 0x79, 0x70, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x62, - 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x09, 0x62, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x49, 0x64, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, - 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x16, 0x0a, 0x06, - 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x6f, 0x66, - 0x66, 0x73, 0x65, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x73, 0x69, 0x7a, - 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x04, 0x52, 0x08, 0x66, 0x69, 0x6c, 0x65, 0x53, 0x69, 0x7a, - 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x69, 0x65, 0x64, 0x5f, 0x74, 0x69, - 0x6d, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0c, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x69, - 0x65, 0x64, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, - 0x69, 0x6f, 0x6e, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x65, 0x78, 0x74, 0x65, 0x6e, - 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x7c, 0x0a, 0x0a, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x49, 0x6e, - 0x66, 0x6f, 0x12, 0x32, 0x0a, 0x05, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, - 0x0b, 0x32, 0x1c, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, - 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x46, 0x69, 0x6c, 0x65, 0x52, - 0x05, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, - 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, - 0x12, 0x20, 0x0a, 0x0b, 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, - 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x22, 0xc8, 0x01, 0x0a, 0x20, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x54, 0x69, 0x65, - 0x72, 0x4d, 0x6f, 0x76, 0x65, 0x44, 0x61, 0x74, 0x54, 0x6f, 0x52, 0x65, 0x6d, 0x6f, 0x74, 0x65, + 0x28, 0x0d, 0x52, 0x08, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x49, 0x64, 0x12, 0x1e, 0x0a, 0x0a, + 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x0a, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x4b, 0x0a, 0x1d, + 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x45, 0x63, 0x53, 0x68, 0x61, 0x72, 0x64, 0x73, 0x52, 0x65, + 0x62, 0x75, 0x69, 0x6c, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2a, 0x0a, + 0x11, 0x72, 0x65, 0x62, 0x75, 0x69, 0x6c, 0x74, 0x5f, 0x73, 0x68, 0x61, 0x72, 0x64, 0x5f, 0x69, + 0x64, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0d, 0x52, 0x0f, 0x72, 0x65, 0x62, 0x75, 0x69, 0x6c, + 0x74, 0x53, 0x68, 0x61, 0x72, 0x64, 0x49, 0x64, 0x73, 0x22, 0x8b, 0x02, 0x0a, 0x19, 0x56, 0x6f, + 0x6c, 0x75, 0x6d, 0x65, 0x45, 0x63, 0x53, 0x68, 0x61, 0x72, 0x64, 0x73, 0x43, 0x6f, 0x70, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x49, 0x64, 0x12, 0x1e, 0x0a, 0x0a, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, - 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x38, 0x0a, 0x18, 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x5f, 0x62, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x5f, 0x6e, 0x61, 0x6d, 0x65, - 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x16, 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x42, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x2d, - 0x0a, 0x13, 0x6b, 0x65, 0x65, 0x70, 0x5f, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x5f, 0x64, 0x61, 0x74, - 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x6b, 0x65, 0x65, - 0x70, 0x4c, 0x6f, 0x63, 0x61, 0x6c, 0x44, 0x61, 0x74, 0x46, 0x69, 0x6c, 0x65, 0x22, 0x73, 0x0a, - 0x21, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x54, 0x69, 0x65, 0x72, 0x4d, 0x6f, 0x76, 0x65, 0x44, - 0x61, 0x74, 0x54, 0x6f, 0x52, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x65, 0x64, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x65, 0x64, - 0x12, 0x30, 0x0a, 0x13, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x65, 0x64, 0x50, 0x65, 0x72, - 0x63, 0x65, 0x6e, 0x74, 0x61, 0x67, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x02, 0x52, 0x13, 0x70, - 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x65, 0x64, 0x50, 0x65, 0x72, 0x63, 0x65, 0x6e, 0x74, 0x61, - 0x67, 0x65, 0x22, 0x92, 0x01, 0x0a, 0x22, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x54, 0x69, 0x65, - 0x72, 0x4d, 0x6f, 0x76, 0x65, 0x44, 0x61, 0x74, 0x46, 0x72, 0x6f, 0x6d, 0x52, 0x65, 0x6d, 0x6f, - 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x76, 0x6f, 0x6c, + 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1b, 0x0a, 0x09, 0x73, 0x68, 0x61, 0x72, 0x64, 0x5f, 0x69, 0x64, + 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0d, 0x52, 0x08, 0x73, 0x68, 0x61, 0x72, 0x64, 0x49, 0x64, + 0x73, 0x12, 0x22, 0x0a, 0x0d, 0x63, 0x6f, 0x70, 0x79, 0x5f, 0x65, 0x63, 0x78, 0x5f, 0x66, 0x69, + 0x6c, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x63, 0x6f, 0x70, 0x79, 0x45, 0x63, + 0x78, 0x46, 0x69, 0x6c, 0x65, 0x12, 0x28, 0x0a, 0x10, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, + 0x64, 0x61, 0x74, 0x61, 0x5f, 0x6e, 0x6f, 0x64, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x0e, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x44, 0x61, 0x74, 0x61, 0x4e, 0x6f, 0x64, 0x65, 0x12, + 0x22, 0x0a, 0x0d, 0x63, 0x6f, 0x70, 0x79, 0x5f, 0x65, 0x63, 0x6a, 0x5f, 0x66, 0x69, 0x6c, 0x65, + 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x63, 0x6f, 0x70, 0x79, 0x45, 0x63, 0x6a, 0x46, + 0x69, 0x6c, 0x65, 0x12, 0x22, 0x0a, 0x0d, 0x63, 0x6f, 0x70, 0x79, 0x5f, 0x76, 0x69, 0x66, 0x5f, + 0x66, 0x69, 0x6c, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x63, 0x6f, 0x70, 0x79, + 0x56, 0x69, 0x66, 0x46, 0x69, 0x6c, 0x65, 0x22, 0x1c, 0x0a, 0x1a, 0x56, 0x6f, 0x6c, 0x75, 0x6d, + 0x65, 0x45, 0x63, 0x53, 0x68, 0x61, 0x72, 0x64, 0x73, 0x43, 0x6f, 0x70, 0x79, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x77, 0x0a, 0x1b, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x45, + 0x63, 0x53, 0x68, 0x61, 0x72, 0x64, 0x73, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x69, + 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x49, + 0x64, 0x12, 0x1e, 0x0a, 0x0a, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, + 0x6e, 0x12, 0x1b, 0x0a, 0x09, 0x73, 0x68, 0x61, 0x72, 0x64, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x03, + 0x20, 0x03, 0x28, 0x0d, 0x52, 0x08, 0x73, 0x68, 0x61, 0x72, 0x64, 0x49, 0x64, 0x73, 0x22, 0x1e, + 0x0a, 0x1c, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x45, 0x63, 0x53, 0x68, 0x61, 0x72, 0x64, 0x73, + 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x76, + 0x0a, 0x1a, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x45, 0x63, 0x53, 0x68, 0x61, 0x72, 0x64, 0x73, + 0x4d, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, + 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, + 0x08, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x49, 0x64, 0x12, 0x1e, 0x0a, 0x0a, 0x63, 0x6f, 0x6c, + 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x63, + 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1b, 0x0a, 0x09, 0x73, 0x68, 0x61, + 0x72, 0x64, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0d, 0x52, 0x08, 0x73, 0x68, + 0x61, 0x72, 0x64, 0x49, 0x64, 0x73, 0x22, 0x1d, 0x0a, 0x1b, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, + 0x45, 0x63, 0x53, 0x68, 0x61, 0x72, 0x64, 0x73, 0x4d, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x58, 0x0a, 0x1c, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x45, + 0x63, 0x53, 0x68, 0x61, 0x72, 0x64, 0x73, 0x55, 0x6e, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, + 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, + 0x49, 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x73, 0x68, 0x61, 0x72, 0x64, 0x5f, 0x69, 0x64, 0x73, 0x18, + 0x03, 0x20, 0x03, 0x28, 0x0d, 0x52, 0x08, 0x73, 0x68, 0x61, 0x72, 0x64, 0x49, 0x64, 0x73, 0x22, + 0x1f, 0x0a, 0x1d, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x45, 0x63, 0x53, 0x68, 0x61, 0x72, 0x64, + 0x73, 0x55, 0x6e, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x22, 0x99, 0x01, 0x0a, 0x18, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x45, 0x63, 0x53, 0x68, 0x61, + 0x72, 0x64, 0x52, 0x65, 0x61, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, + 0x09, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, + 0x52, 0x08, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x49, 0x64, 0x12, 0x19, 0x0a, 0x08, 0x73, 0x68, + 0x61, 0x72, 0x64, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x73, 0x68, + 0x61, 0x72, 0x64, 0x49, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x12, 0x12, 0x0a, + 0x04, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x04, 0x73, 0x69, 0x7a, + 0x65, 0x12, 0x19, 0x0a, 0x08, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x05, 0x20, + 0x01, 0x28, 0x04, 0x52, 0x07, 0x66, 0x69, 0x6c, 0x65, 0x4b, 0x65, 0x79, 0x22, 0x4e, 0x0a, 0x19, + 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x45, 0x63, 0x53, 0x68, 0x61, 0x72, 0x64, 0x52, 0x65, 0x61, + 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x61, 0x74, + 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x12, 0x1d, 0x0a, + 0x0a, 0x69, 0x73, 0x5f, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x08, 0x52, 0x09, 0x69, 0x73, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x22, 0x8d, 0x01, 0x0a, + 0x19, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x45, 0x63, 0x42, 0x6c, 0x6f, 0x62, 0x44, 0x65, 0x6c, + 0x65, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x76, 0x6f, + 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x76, + 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x49, 0x64, 0x12, 0x1e, 0x0a, 0x0a, 0x63, 0x6f, 0x6c, 0x6c, 0x65, + 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x63, 0x6f, 0x6c, + 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x19, 0x0a, 0x08, 0x66, 0x69, 0x6c, 0x65, 0x5f, + 0x6b, 0x65, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x07, 0x66, 0x69, 0x6c, 0x65, 0x4b, + 0x65, 0x79, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, + 0x01, 0x28, 0x0d, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x1c, 0x0a, 0x1a, + 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x45, 0x63, 0x42, 0x6c, 0x6f, 0x62, 0x44, 0x65, 0x6c, 0x65, + 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x5c, 0x0a, 0x1d, 0x56, 0x6f, + 0x6c, 0x75, 0x6d, 0x65, 0x45, 0x63, 0x53, 0x68, 0x61, 0x72, 0x64, 0x73, 0x54, 0x6f, 0x56, 0x6f, + 0x6c, 0x75, 0x6d, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x76, + 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, + 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x49, 0x64, 0x12, 0x1e, 0x0a, 0x0a, 0x63, 0x6f, 0x6c, 0x6c, + 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x63, 0x6f, + 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x20, 0x0a, 0x1e, 0x56, 0x6f, 0x6c, 0x75, + 0x6d, 0x65, 0x45, 0x63, 0x53, 0x68, 0x61, 0x72, 0x64, 0x73, 0x54, 0x6f, 0x56, 0x6f, 0x6c, 0x75, + 0x6d, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x3a, 0x0a, 0x1b, 0x52, 0x65, + 0x61, 0x64, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x46, 0x69, 0x6c, 0x65, 0x53, 0x74, 0x61, 0x74, + 0x75, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x76, 0x6f, - 0x6c, 0x75, 0x6d, 0x65, 0x49, 0x64, 0x12, 0x1e, 0x0a, 0x0a, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, - 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x63, 0x6f, 0x6c, 0x6c, - 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x2f, 0x0a, 0x14, 0x6b, 0x65, 0x65, 0x70, 0x5f, 0x72, - 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x5f, 0x64, 0x61, 0x74, 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x18, 0x03, - 0x20, 0x01, 0x28, 0x08, 0x52, 0x11, 0x6b, 0x65, 0x65, 0x70, 0x52, 0x65, 0x6d, 0x6f, 0x74, 0x65, - 0x44, 0x61, 0x74, 0x46, 0x69, 0x6c, 0x65, 0x22, 0x75, 0x0a, 0x23, 0x56, 0x6f, 0x6c, 0x75, 0x6d, - 0x65, 0x54, 0x69, 0x65, 0x72, 0x4d, 0x6f, 0x76, 0x65, 0x44, 0x61, 0x74, 0x46, 0x72, 0x6f, 0x6d, - 0x52, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1c, - 0x0a, 0x09, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x03, 0x52, 0x09, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x65, 0x64, 0x12, 0x30, 0x0a, 0x13, - 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x65, 0x64, 0x50, 0x65, 0x72, 0x63, 0x65, 0x6e, 0x74, - 0x61, 0x67, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x02, 0x52, 0x13, 0x70, 0x72, 0x6f, 0x63, 0x65, - 0x73, 0x73, 0x65, 0x64, 0x50, 0x65, 0x72, 0x63, 0x65, 0x6e, 0x74, 0x61, 0x67, 0x65, 0x22, 0x1b, - 0x0a, 0x19, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x53, 0x74, - 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0xa1, 0x01, 0x0a, 0x1a, + 0x6c, 0x75, 0x6d, 0x65, 0x49, 0x64, 0x22, 0x8a, 0x03, 0x0a, 0x1c, 0x52, 0x65, 0x61, 0x64, 0x56, + 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x46, 0x69, 0x6c, 0x65, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x76, 0x6f, 0x6c, 0x75, 0x6d, + 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x76, 0x6f, 0x6c, 0x75, + 0x6d, 0x65, 0x49, 0x64, 0x12, 0x3b, 0x0a, 0x1a, 0x69, 0x64, 0x78, 0x5f, 0x66, 0x69, 0x6c, 0x65, + 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x5f, 0x73, 0x65, 0x63, 0x6f, 0x6e, + 0x64, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x17, 0x69, 0x64, 0x78, 0x46, 0x69, 0x6c, + 0x65, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x53, 0x65, 0x63, 0x6f, 0x6e, 0x64, + 0x73, 0x12, 0x22, 0x0a, 0x0d, 0x69, 0x64, 0x78, 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x73, 0x69, + 0x7a, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, 0x69, 0x64, 0x78, 0x46, 0x69, 0x6c, + 0x65, 0x53, 0x69, 0x7a, 0x65, 0x12, 0x3b, 0x0a, 0x1a, 0x64, 0x61, 0x74, 0x5f, 0x66, 0x69, 0x6c, + 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x5f, 0x73, 0x65, 0x63, 0x6f, + 0x6e, 0x64, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x17, 0x64, 0x61, 0x74, 0x46, 0x69, + 0x6c, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x53, 0x65, 0x63, 0x6f, 0x6e, + 0x64, 0x73, 0x12, 0x22, 0x0a, 0x0d, 0x64, 0x61, 0x74, 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x73, + 0x69, 0x7a, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, 0x64, 0x61, 0x74, 0x46, 0x69, + 0x6c, 0x65, 0x53, 0x69, 0x7a, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x63, + 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x66, 0x69, 0x6c, 0x65, + 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x2f, 0x0a, 0x13, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x63, 0x74, + 0x69, 0x6f, 0x6e, 0x5f, 0x72, 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x07, 0x20, 0x01, + 0x28, 0x0d, 0x52, 0x12, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, + 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x1e, 0x0a, 0x0a, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, + 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x63, 0x6f, 0x6c, 0x6c, + 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1b, 0x0a, 0x09, 0x64, 0x69, 0x73, 0x6b, 0x5f, 0x74, + 0x79, 0x70, 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x64, 0x69, 0x73, 0x6b, 0x54, + 0x79, 0x70, 0x65, 0x22, 0xbb, 0x01, 0x0a, 0x0a, 0x44, 0x69, 0x73, 0x6b, 0x53, 0x74, 0x61, 0x74, + 0x75, 0x73, 0x12, 0x10, 0x0a, 0x03, 0x64, 0x69, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x03, 0x64, 0x69, 0x72, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x6c, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x04, 0x52, 0x03, 0x61, 0x6c, 0x6c, 0x12, 0x12, 0x0a, 0x04, 0x75, 0x73, 0x65, 0x64, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x04, 0x52, 0x04, 0x75, 0x73, 0x65, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x66, 0x72, + 0x65, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x04, 0x66, 0x72, 0x65, 0x65, 0x12, 0x21, + 0x0a, 0x0c, 0x70, 0x65, 0x72, 0x63, 0x65, 0x6e, 0x74, 0x5f, 0x66, 0x72, 0x65, 0x65, 0x18, 0x05, + 0x20, 0x01, 0x28, 0x02, 0x52, 0x0b, 0x70, 0x65, 0x72, 0x63, 0x65, 0x6e, 0x74, 0x46, 0x72, 0x65, + 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x70, 0x65, 0x72, 0x63, 0x65, 0x6e, 0x74, 0x5f, 0x75, 0x73, 0x65, + 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x02, 0x52, 0x0b, 0x70, 0x65, 0x72, 0x63, 0x65, 0x6e, 0x74, + 0x55, 0x73, 0x65, 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x64, 0x69, 0x73, 0x6b, 0x5f, 0x74, 0x79, 0x70, + 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x64, 0x69, 0x73, 0x6b, 0x54, 0x79, 0x70, + 0x65, 0x22, 0xa3, 0x01, 0x0a, 0x09, 0x4d, 0x65, 0x6d, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, + 0x1e, 0x0a, 0x0a, 0x67, 0x6f, 0x72, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x65, 0x73, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x05, 0x52, 0x0a, 0x67, 0x6f, 0x72, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x65, 0x73, 0x12, + 0x10, 0x0a, 0x03, 0x61, 0x6c, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x03, 0x61, 0x6c, + 0x6c, 0x12, 0x12, 0x0a, 0x04, 0x75, 0x73, 0x65, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, + 0x04, 0x75, 0x73, 0x65, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x66, 0x72, 0x65, 0x65, 0x18, 0x04, 0x20, + 0x01, 0x28, 0x04, 0x52, 0x04, 0x66, 0x72, 0x65, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x65, 0x6c, + 0x66, 0x18, 0x05, 0x20, 0x01, 0x28, 0x04, 0x52, 0x04, 0x73, 0x65, 0x6c, 0x66, 0x12, 0x12, 0x0a, + 0x04, 0x68, 0x65, 0x61, 0x70, 0x18, 0x06, 0x20, 0x01, 0x28, 0x04, 0x52, 0x04, 0x68, 0x65, 0x61, + 0x70, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x63, 0x6b, 0x18, 0x07, 0x20, 0x01, 0x28, 0x04, + 0x52, 0x05, 0x73, 0x74, 0x61, 0x63, 0x6b, 0x22, 0xd8, 0x01, 0x0a, 0x0a, 0x52, 0x65, 0x6d, 0x6f, + 0x74, 0x65, 0x46, 0x69, 0x6c, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x62, 0x61, 0x63, 0x6b, 0x65, 0x6e, + 0x64, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x62, 0x61, + 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x54, 0x79, 0x70, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x62, 0x61, 0x63, + 0x6b, 0x65, 0x6e, 0x64, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x62, + 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x49, 0x64, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x16, 0x0a, 0x06, 0x6f, 0x66, + 0x66, 0x73, 0x65, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x6f, 0x66, 0x66, 0x73, + 0x65, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, + 0x05, 0x20, 0x01, 0x28, 0x04, 0x52, 0x08, 0x66, 0x69, 0x6c, 0x65, 0x53, 0x69, 0x7a, 0x65, 0x12, + 0x23, 0x0a, 0x0d, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x69, 0x65, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, + 0x18, 0x06, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0c, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x69, 0x65, 0x64, + 0x54, 0x69, 0x6d, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, + 0x6e, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, + 0x6f, 0x6e, 0x22, 0x7c, 0x0a, 0x0a, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x49, 0x6e, 0x66, 0x6f, + 0x12, 0x32, 0x0a, 0x05, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, + 0x1c, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, + 0x70, 0x62, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x46, 0x69, 0x6c, 0x65, 0x52, 0x05, 0x66, + 0x69, 0x6c, 0x65, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x20, + 0x0a, 0x0b, 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x0b, 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x22, 0xc8, 0x01, 0x0a, 0x20, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x54, 0x69, 0x65, 0x72, 0x4d, + 0x6f, 0x76, 0x65, 0x44, 0x61, 0x74, 0x54, 0x6f, 0x52, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, + 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, + 0x49, 0x64, 0x12, 0x1e, 0x0a, 0x0a, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, + 0x6f, 0x6e, 0x12, 0x38, 0x0a, 0x18, 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x5f, 0x62, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x16, 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x42, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x2d, 0x0a, 0x13, + 0x6b, 0x65, 0x65, 0x70, 0x5f, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x5f, 0x64, 0x61, 0x74, 0x5f, 0x66, + 0x69, 0x6c, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x6b, 0x65, 0x65, 0x70, 0x4c, + 0x6f, 0x63, 0x61, 0x6c, 0x44, 0x61, 0x74, 0x46, 0x69, 0x6c, 0x65, 0x22, 0x73, 0x0a, 0x21, 0x56, + 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x54, 0x69, 0x65, 0x72, 0x4d, 0x6f, 0x76, 0x65, 0x44, 0x61, 0x74, + 0x54, 0x6f, 0x52, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x12, 0x1c, 0x0a, 0x09, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x65, 0x64, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x03, 0x52, 0x09, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x65, 0x64, 0x12, 0x30, + 0x0a, 0x13, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x65, 0x64, 0x50, 0x65, 0x72, 0x63, 0x65, + 0x6e, 0x74, 0x61, 0x67, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x02, 0x52, 0x13, 0x70, 0x72, 0x6f, + 0x63, 0x65, 0x73, 0x73, 0x65, 0x64, 0x50, 0x65, 0x72, 0x63, 0x65, 0x6e, 0x74, 0x61, 0x67, 0x65, + 0x22, 0x92, 0x01, 0x0a, 0x22, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x54, 0x69, 0x65, 0x72, 0x4d, + 0x6f, 0x76, 0x65, 0x44, 0x61, 0x74, 0x46, 0x72, 0x6f, 0x6d, 0x52, 0x65, 0x6d, 0x6f, 0x74, 0x65, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x76, 0x6f, 0x6c, 0x75, 0x6d, + 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x76, 0x6f, 0x6c, 0x75, + 0x6d, 0x65, 0x49, 0x64, 0x12, 0x1e, 0x0a, 0x0a, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, + 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, + 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x2f, 0x0a, 0x14, 0x6b, 0x65, 0x65, 0x70, 0x5f, 0x72, 0x65, 0x6d, + 0x6f, 0x74, 0x65, 0x5f, 0x64, 0x61, 0x74, 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x08, 0x52, 0x11, 0x6b, 0x65, 0x65, 0x70, 0x52, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x44, 0x61, + 0x74, 0x46, 0x69, 0x6c, 0x65, 0x22, 0x75, 0x0a, 0x23, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x54, + 0x69, 0x65, 0x72, 0x4d, 0x6f, 0x76, 0x65, 0x44, 0x61, 0x74, 0x46, 0x72, 0x6f, 0x6d, 0x52, 0x65, + 0x6d, 0x6f, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1c, 0x0a, 0x09, + 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, + 0x09, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x65, 0x64, 0x12, 0x30, 0x0a, 0x13, 0x70, 0x72, + 0x6f, 0x63, 0x65, 0x73, 0x73, 0x65, 0x64, 0x50, 0x65, 0x72, 0x63, 0x65, 0x6e, 0x74, 0x61, 0x67, + 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x02, 0x52, 0x13, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, + 0x65, 0x64, 0x50, 0x65, 0x72, 0x63, 0x65, 0x6e, 0x74, 0x61, 0x67, 0x65, 0x22, 0x1b, 0x0a, 0x19, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, - 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x41, 0x0a, 0x0d, 0x64, 0x69, - 0x73, 0x6b, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, - 0x0b, 0x32, 0x1c, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, - 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x44, 0x69, 0x73, 0x6b, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, - 0x0c, 0x64, 0x69, 0x73, 0x6b, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x65, 0x73, 0x12, 0x40, 0x0a, - 0x0d, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, - 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x4d, 0x65, 0x6d, 0x53, 0x74, 0x61, 0x74, 0x75, - 0x73, 0x52, 0x0c, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, - 0x1a, 0x0a, 0x18, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x4c, - 0x65, 0x61, 0x76, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x1b, 0x0a, 0x19, 0x56, - 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x4c, 0x65, 0x61, 0x76, 0x65, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0xdc, 0x03, 0x0a, 0x1a, 0x46, 0x65, 0x74, - 0x63, 0x68, 0x41, 0x6e, 0x64, 0x57, 0x72, 0x69, 0x74, 0x65, 0x4e, 0x65, 0x65, 0x64, 0x6c, 0x65, + 0x75, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0xa1, 0x01, 0x0a, 0x1a, 0x56, 0x6f, + 0x6c, 0x75, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x41, 0x0a, 0x0d, 0x64, 0x69, 0x73, 0x6b, + 0x5f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, + 0x1c, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, + 0x70, 0x62, 0x2e, 0x44, 0x69, 0x73, 0x6b, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x0c, 0x64, + 0x69, 0x73, 0x6b, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x65, 0x73, 0x12, 0x40, 0x0a, 0x0d, 0x6d, + 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, + 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x4d, 0x65, 0x6d, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, + 0x0c, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0x1a, 0x0a, + 0x18, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x4c, 0x65, 0x61, + 0x76, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x1b, 0x0a, 0x19, 0x56, 0x6f, 0x6c, + 0x75, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x4c, 0x65, 0x61, 0x76, 0x65, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0xdc, 0x03, 0x0a, 0x1a, 0x46, 0x65, 0x74, 0x63, 0x68, + 0x41, 0x6e, 0x64, 0x57, 0x72, 0x69, 0x74, 0x65, 0x4e, 0x65, 0x65, 0x64, 0x6c, 0x65, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, + 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, + 0x49, 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x6e, 0x65, 0x65, 0x64, 0x6c, 0x65, 0x5f, 0x69, 0x64, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x08, 0x6e, 0x65, 0x65, 0x64, 0x6c, 0x65, 0x49, 0x64, 0x12, + 0x16, 0x0a, 0x06, 0x63, 0x6f, 0x6f, 0x6b, 0x69, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, + 0x06, 0x63, 0x6f, 0x6f, 0x6b, 0x69, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x6f, 0x66, 0x66, 0x73, 0x65, + 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x12, + 0x12, 0x0a, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x04, 0x73, + 0x69, 0x7a, 0x65, 0x12, 0x50, 0x0a, 0x08, 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x73, 0x18, + 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x34, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, + 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x46, 0x65, 0x74, 0x63, 0x68, 0x41, 0x6e, + 0x64, 0x57, 0x72, 0x69, 0x74, 0x65, 0x4e, 0x65, 0x65, 0x64, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x2e, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x52, 0x08, 0x72, 0x65, 0x70, + 0x6c, 0x69, 0x63, 0x61, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x61, 0x75, 0x74, 0x68, 0x18, 0x07, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x04, 0x61, 0x75, 0x74, 0x68, 0x12, 0x36, 0x0a, 0x0b, 0x72, 0x65, 0x6d, + 0x6f, 0x74, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, + 0x2e, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x5f, 0x70, 0x62, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x74, + 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x52, 0x0a, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x43, 0x6f, 0x6e, + 0x66, 0x12, 0x49, 0x0a, 0x0f, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x5f, 0x6c, 0x6f, 0x63, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x10, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x72, 0x65, 0x6d, + 0x6f, 0x74, 0x65, 0x5f, 0x70, 0x62, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x53, 0x74, 0x6f, + 0x72, 0x61, 0x67, 0x65, 0x4c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0e, 0x72, 0x65, + 0x6d, 0x6f, 0x74, 0x65, 0x4c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x1a, 0x57, 0x0a, 0x07, + 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, 0x6c, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x6c, 0x12, 0x1d, 0x0a, 0x0a, 0x70, 0x75, 0x62, + 0x6c, 0x69, 0x63, 0x5f, 0x75, 0x72, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x70, + 0x75, 0x62, 0x6c, 0x69, 0x63, 0x55, 0x72, 0x6c, 0x12, 0x1b, 0x0a, 0x09, 0x67, 0x72, 0x70, 0x63, + 0x5f, 0x70, 0x6f, 0x72, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x08, 0x67, 0x72, 0x70, + 0x63, 0x50, 0x6f, 0x72, 0x74, 0x22, 0x1d, 0x0a, 0x1b, 0x46, 0x65, 0x74, 0x63, 0x68, 0x41, 0x6e, + 0x64, 0x57, 0x72, 0x69, 0x74, 0x65, 0x4e, 0x65, 0x65, 0x64, 0x6c, 0x65, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x22, 0xf8, 0x0c, 0x0a, 0x0c, 0x51, 0x75, 0x65, 0x72, 0x79, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1e, 0x0a, 0x0a, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x69, + 0x6f, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0a, 0x73, 0x65, 0x6c, 0x65, 0x63, + 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x22, 0x0a, 0x0d, 0x66, 0x72, 0x6f, 0x6d, 0x5f, 0x66, 0x69, + 0x6c, 0x65, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0b, 0x66, 0x72, + 0x6f, 0x6d, 0x46, 0x69, 0x6c, 0x65, 0x49, 0x64, 0x73, 0x12, 0x3d, 0x0a, 0x06, 0x66, 0x69, 0x6c, + 0x74, 0x65, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x76, 0x6f, 0x6c, 0x75, + 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x51, 0x75, 0x65, + 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, + 0x52, 0x06, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x12, 0x62, 0x0a, 0x13, 0x69, 0x6e, 0x70, 0x75, + 0x74, 0x5f, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, + 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x31, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, + 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x53, 0x65, 0x72, 0x69, 0x61, + 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x12, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x53, + 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x65, 0x0a, 0x14, + 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x5f, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x32, 0x2e, 0x76, 0x6f, 0x6c, + 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x51, 0x75, + 0x65, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x4f, 0x75, 0x74, 0x70, 0x75, + 0x74, 0x53, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x13, + 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x53, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x1a, 0x4e, 0x0a, 0x06, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x12, 0x14, 0x0a, + 0x05, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x66, 0x69, + 0x65, 0x6c, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x6e, 0x64, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x6e, 0x64, 0x12, 0x14, 0x0a, + 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, + 0x6c, 0x75, 0x65, 0x1a, 0xd5, 0x05, 0x0a, 0x12, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x53, 0x65, 0x72, + 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x29, 0x0a, 0x10, 0x63, 0x6f, + 0x6d, 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x63, 0x6f, 0x6d, 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, + 0x6e, 0x54, 0x79, 0x70, 0x65, 0x12, 0x57, 0x0a, 0x09, 0x63, 0x73, 0x76, 0x5f, 0x69, 0x6e, 0x70, + 0x75, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x3a, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, + 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x51, 0x75, 0x65, 0x72, + 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x53, 0x65, + 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x43, 0x53, 0x56, 0x49, + 0x6e, 0x70, 0x75, 0x74, 0x52, 0x08, 0x63, 0x73, 0x76, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x12, 0x5a, + 0x0a, 0x0a, 0x6a, 0x73, 0x6f, 0x6e, 0x5f, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x3b, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, + 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x2e, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x53, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x4a, 0x53, 0x4f, 0x4e, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x52, + 0x09, 0x6a, 0x73, 0x6f, 0x6e, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x12, 0x63, 0x0a, 0x0d, 0x70, 0x61, + 0x72, 0x71, 0x75, 0x65, 0x74, 0x5f, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x3e, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, + 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x2e, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x53, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x50, 0x61, 0x72, 0x71, 0x75, 0x65, 0x74, 0x49, 0x6e, 0x70, 0x75, + 0x74, 0x52, 0x0c, 0x70, 0x61, 0x72, 0x71, 0x75, 0x65, 0x74, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x1a, + 0xc8, 0x02, 0x0a, 0x08, 0x43, 0x53, 0x56, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x12, 0x28, 0x0a, 0x10, + 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x5f, 0x69, 0x6e, 0x66, 0x6f, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x66, 0x69, 0x6c, 0x65, 0x48, 0x65, 0x61, 0x64, + 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x29, 0x0a, 0x10, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, + 0x5f, 0x64, 0x65, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x0f, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x44, 0x65, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x65, + 0x72, 0x12, 0x27, 0x0a, 0x0f, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x5f, 0x64, 0x65, 0x6c, 0x69, 0x6d, + 0x69, 0x74, 0x65, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x66, 0x69, 0x65, 0x6c, + 0x64, 0x44, 0x65, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x65, 0x72, 0x12, 0x29, 0x0a, 0x10, 0x71, 0x75, + 0x6f, 0x74, 0x65, 0x5f, 0x63, 0x68, 0x61, 0x72, 0x61, 0x63, 0x74, 0x6f, 0x65, 0x72, 0x18, 0x04, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x71, 0x75, 0x6f, 0x74, 0x65, 0x43, 0x68, 0x61, 0x72, 0x61, + 0x63, 0x74, 0x6f, 0x65, 0x72, 0x12, 0x34, 0x0a, 0x16, 0x71, 0x75, 0x6f, 0x74, 0x65, 0x5f, 0x65, + 0x73, 0x63, 0x61, 0x70, 0x65, 0x5f, 0x63, 0x68, 0x61, 0x72, 0x61, 0x63, 0x74, 0x65, 0x72, 0x18, + 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x14, 0x71, 0x75, 0x6f, 0x74, 0x65, 0x45, 0x73, 0x63, 0x61, + 0x70, 0x65, 0x43, 0x68, 0x61, 0x72, 0x61, 0x63, 0x74, 0x65, 0x72, 0x12, 0x1a, 0x0a, 0x08, 0x63, + 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x63, + 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x41, 0x0a, 0x1d, 0x61, 0x6c, 0x6c, 0x6f, 0x77, + 0x5f, 0x71, 0x75, 0x6f, 0x74, 0x65, 0x64, 0x5f, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x5f, 0x64, + 0x65, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x65, 0x72, 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x1a, + 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x51, 0x75, 0x6f, 0x74, 0x65, 0x64, 0x52, 0x65, 0x63, 0x6f, 0x72, + 0x64, 0x44, 0x65, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x65, 0x72, 0x1a, 0x1f, 0x0a, 0x09, 0x4a, 0x53, + 0x4f, 0x4e, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x1a, 0x0e, 0x0a, 0x0c, 0x50, + 0x61, 0x72, 0x71, 0x75, 0x65, 0x74, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x1a, 0xf1, 0x03, 0x0a, 0x13, + 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x53, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x12, 0x5b, 0x0a, 0x0a, 0x63, 0x73, 0x76, 0x5f, 0x6f, 0x75, 0x74, 0x70, 0x75, + 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x3c, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, + 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x53, 0x65, + 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x43, 0x53, 0x56, 0x4f, + 0x75, 0x74, 0x70, 0x75, 0x74, 0x52, 0x09, 0x63, 0x73, 0x76, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, + 0x12, 0x5e, 0x0a, 0x0b, 0x6a, 0x73, 0x6f, 0x6e, 0x5f, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x3d, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, + 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x53, 0x65, 0x72, 0x69, + 0x61, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x4a, 0x53, 0x4f, 0x4e, 0x4f, 0x75, + 0x74, 0x70, 0x75, 0x74, 0x52, 0x0a, 0x6a, 0x73, 0x6f, 0x6e, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, + 0x1a, 0xe3, 0x01, 0x0a, 0x09, 0x43, 0x53, 0x56, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x12, 0x21, + 0x0a, 0x0c, 0x71, 0x75, 0x6f, 0x74, 0x65, 0x5f, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x71, 0x75, 0x6f, 0x74, 0x65, 0x46, 0x69, 0x65, 0x6c, 0x64, + 0x73, 0x12, 0x29, 0x0a, 0x10, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x5f, 0x64, 0x65, 0x6c, 0x69, + 0x6d, 0x69, 0x74, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x72, 0x65, 0x63, + 0x6f, 0x72, 0x64, 0x44, 0x65, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x65, 0x72, 0x12, 0x27, 0x0a, 0x0f, + 0x66, 0x69, 0x65, 0x6c, 0x64, 0x5f, 0x64, 0x65, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x65, 0x72, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x44, 0x65, 0x6c, 0x69, + 0x6d, 0x69, 0x74, 0x65, 0x72, 0x12, 0x29, 0x0a, 0x10, 0x71, 0x75, 0x6f, 0x74, 0x65, 0x5f, 0x63, + 0x68, 0x61, 0x72, 0x61, 0x63, 0x74, 0x6f, 0x65, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x0f, 0x71, 0x75, 0x6f, 0x74, 0x65, 0x43, 0x68, 0x61, 0x72, 0x61, 0x63, 0x74, 0x6f, 0x65, 0x72, + 0x12, 0x34, 0x0a, 0x16, 0x71, 0x75, 0x6f, 0x74, 0x65, 0x5f, 0x65, 0x73, 0x63, 0x61, 0x70, 0x65, + 0x5f, 0x63, 0x68, 0x61, 0x72, 0x61, 0x63, 0x74, 0x65, 0x72, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x14, 0x71, 0x75, 0x6f, 0x74, 0x65, 0x45, 0x73, 0x63, 0x61, 0x70, 0x65, 0x43, 0x68, 0x61, + 0x72, 0x61, 0x63, 0x74, 0x65, 0x72, 0x1a, 0x37, 0x0a, 0x0a, 0x4a, 0x53, 0x4f, 0x4e, 0x4f, 0x75, + 0x74, 0x70, 0x75, 0x74, 0x12, 0x29, 0x0a, 0x10, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x5f, 0x64, + 0x65, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, + 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x44, 0x65, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x65, 0x72, 0x22, + 0x29, 0x0a, 0x0d, 0x51, 0x75, 0x65, 0x72, 0x69, 0x65, 0x64, 0x53, 0x74, 0x72, 0x69, 0x70, 0x65, + 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x0c, 0x52, 0x07, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x73, 0x22, 0x55, 0x0a, 0x19, 0x56, 0x6f, + 0x6c, 0x75, 0x6d, 0x65, 0x4e, 0x65, 0x65, 0x64, 0x6c, 0x65, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x49, 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x6e, 0x65, 0x65, 0x64, 0x6c, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x08, 0x6e, 0x65, 0x65, 0x64, 0x6c, 0x65, 0x49, - 0x64, 0x12, 0x16, 0x0a, 0x06, 0x63, 0x6f, 0x6f, 0x6b, 0x69, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, - 0x0d, 0x52, 0x06, 0x63, 0x6f, 0x6f, 0x6b, 0x69, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x6f, 0x66, 0x66, - 0x73, 0x65, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x6f, 0x66, 0x66, 0x73, 0x65, - 0x74, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, - 0x04, 0x73, 0x69, 0x7a, 0x65, 0x12, 0x50, 0x0a, 0x08, 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, - 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x34, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, - 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x46, 0x65, 0x74, 0x63, 0x68, - 0x41, 0x6e, 0x64, 0x57, 0x72, 0x69, 0x74, 0x65, 0x4e, 0x65, 0x65, 0x64, 0x6c, 0x65, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x52, 0x08, 0x72, - 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x61, 0x75, 0x74, 0x68, 0x18, - 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x61, 0x75, 0x74, 0x68, 0x12, 0x36, 0x0a, 0x0b, 0x72, - 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x15, 0x2e, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x5f, 0x70, 0x62, 0x2e, 0x52, 0x65, 0x6d, - 0x6f, 0x74, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x52, 0x0a, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x43, - 0x6f, 0x6e, 0x66, 0x12, 0x49, 0x0a, 0x0f, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x5f, 0x6c, 0x6f, - 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x10, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x72, - 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x5f, 0x70, 0x62, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x53, - 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x4c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0e, - 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x4c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x1a, 0x57, - 0x0a, 0x07, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, 0x6c, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x6c, 0x12, 0x1d, 0x0a, 0x0a, 0x70, - 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x75, 0x72, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x09, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x55, 0x72, 0x6c, 0x12, 0x1b, 0x0a, 0x09, 0x67, 0x72, - 0x70, 0x63, 0x5f, 0x70, 0x6f, 0x72, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x08, 0x67, - 0x72, 0x70, 0x63, 0x50, 0x6f, 0x72, 0x74, 0x22, 0x1d, 0x0a, 0x1b, 0x46, 0x65, 0x74, 0x63, 0x68, - 0x41, 0x6e, 0x64, 0x57, 0x72, 0x69, 0x74, 0x65, 0x4e, 0x65, 0x65, 0x64, 0x6c, 0x65, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0xf8, 0x0c, 0x0a, 0x0c, 0x51, 0x75, 0x65, 0x72, 0x79, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1e, 0x0a, 0x0a, 0x73, 0x65, 0x6c, 0x65, 0x63, - 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0a, 0x73, 0x65, 0x6c, - 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x22, 0x0a, 0x0d, 0x66, 0x72, 0x6f, 0x6d, 0x5f, - 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0b, - 0x66, 0x72, 0x6f, 0x6d, 0x46, 0x69, 0x6c, 0x65, 0x49, 0x64, 0x73, 0x12, 0x3d, 0x0a, 0x06, 0x66, - 0x69, 0x6c, 0x74, 0x65, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x76, 0x6f, - 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x51, - 0x75, 0x65, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x46, 0x69, 0x6c, 0x74, - 0x65, 0x72, 0x52, 0x06, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x12, 0x62, 0x0a, 0x13, 0x69, 0x6e, - 0x70, 0x75, 0x74, 0x5f, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x31, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, - 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x53, 0x65, 0x72, - 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x12, 0x69, 0x6e, 0x70, 0x75, - 0x74, 0x53, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x65, - 0x0a, 0x14, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x5f, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, - 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x32, 0x2e, 0x76, - 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, - 0x51, 0x75, 0x65, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x4f, 0x75, 0x74, - 0x70, 0x75, 0x74, 0x53, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x52, 0x13, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x53, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x1a, 0x4e, 0x0a, 0x06, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x12, - 0x14, 0x0a, 0x05, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, - 0x66, 0x69, 0x65, 0x6c, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x6e, 0x64, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x6e, 0x64, 0x12, - 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, - 0x76, 0x61, 0x6c, 0x75, 0x65, 0x1a, 0xd5, 0x05, 0x0a, 0x12, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x53, - 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x29, 0x0a, 0x10, - 0x63, 0x6f, 0x6d, 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x74, 0x79, 0x70, 0x65, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x63, 0x6f, 0x6d, 0x70, 0x72, 0x65, 0x73, 0x73, - 0x69, 0x6f, 0x6e, 0x54, 0x79, 0x70, 0x65, 0x12, 0x57, 0x0a, 0x09, 0x63, 0x73, 0x76, 0x5f, 0x69, - 0x6e, 0x70, 0x75, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x3a, 0x2e, 0x76, 0x6f, 0x6c, - 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x51, 0x75, - 0x65, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x49, 0x6e, 0x70, 0x75, 0x74, - 0x53, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x43, 0x53, - 0x56, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x52, 0x08, 0x63, 0x73, 0x76, 0x49, 0x6e, 0x70, 0x75, 0x74, - 0x12, 0x5a, 0x0a, 0x0a, 0x6a, 0x73, 0x6f, 0x6e, 0x5f, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x18, 0x03, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x3b, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, - 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x2e, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x53, 0x65, 0x72, 0x69, 0x61, 0x6c, - 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x4a, 0x53, 0x4f, 0x4e, 0x49, 0x6e, 0x70, 0x75, - 0x74, 0x52, 0x09, 0x6a, 0x73, 0x6f, 0x6e, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x12, 0x63, 0x0a, 0x0d, - 0x70, 0x61, 0x72, 0x71, 0x75, 0x65, 0x74, 0x5f, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x18, 0x04, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x3e, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, - 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x2e, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x53, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, - 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x50, 0x61, 0x72, 0x71, 0x75, 0x65, 0x74, 0x49, 0x6e, - 0x70, 0x75, 0x74, 0x52, 0x0c, 0x70, 0x61, 0x72, 0x71, 0x75, 0x65, 0x74, 0x49, 0x6e, 0x70, 0x75, - 0x74, 0x1a, 0xc8, 0x02, 0x0a, 0x08, 0x43, 0x53, 0x56, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x12, 0x28, - 0x0a, 0x10, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x5f, 0x69, 0x6e, - 0x66, 0x6f, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x66, 0x69, 0x6c, 0x65, 0x48, 0x65, - 0x61, 0x64, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x29, 0x0a, 0x10, 0x72, 0x65, 0x63, 0x6f, - 0x72, 0x64, 0x5f, 0x64, 0x65, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x0f, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x44, 0x65, 0x6c, 0x69, 0x6d, 0x69, - 0x74, 0x65, 0x72, 0x12, 0x27, 0x0a, 0x0f, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x5f, 0x64, 0x65, 0x6c, - 0x69, 0x6d, 0x69, 0x74, 0x65, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x66, 0x69, - 0x65, 0x6c, 0x64, 0x44, 0x65, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x65, 0x72, 0x12, 0x29, 0x0a, 0x10, - 0x71, 0x75, 0x6f, 0x74, 0x65, 0x5f, 0x63, 0x68, 0x61, 0x72, 0x61, 0x63, 0x74, 0x6f, 0x65, 0x72, - 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x71, 0x75, 0x6f, 0x74, 0x65, 0x43, 0x68, 0x61, - 0x72, 0x61, 0x63, 0x74, 0x6f, 0x65, 0x72, 0x12, 0x34, 0x0a, 0x16, 0x71, 0x75, 0x6f, 0x74, 0x65, - 0x5f, 0x65, 0x73, 0x63, 0x61, 0x70, 0x65, 0x5f, 0x63, 0x68, 0x61, 0x72, 0x61, 0x63, 0x74, 0x65, - 0x72, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x14, 0x71, 0x75, 0x6f, 0x74, 0x65, 0x45, 0x73, - 0x63, 0x61, 0x70, 0x65, 0x43, 0x68, 0x61, 0x72, 0x61, 0x63, 0x74, 0x65, 0x72, 0x12, 0x1a, 0x0a, - 0x08, 0x63, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x08, 0x63, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x41, 0x0a, 0x1d, 0x61, 0x6c, 0x6c, - 0x6f, 0x77, 0x5f, 0x71, 0x75, 0x6f, 0x74, 0x65, 0x64, 0x5f, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, - 0x5f, 0x64, 0x65, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x65, 0x72, 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, - 0x52, 0x1a, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x51, 0x75, 0x6f, 0x74, 0x65, 0x64, 0x52, 0x65, 0x63, - 0x6f, 0x72, 0x64, 0x44, 0x65, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x65, 0x72, 0x1a, 0x1f, 0x0a, 0x09, - 0x4a, 0x53, 0x4f, 0x4e, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, - 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x1a, 0x0e, 0x0a, - 0x0c, 0x50, 0x61, 0x72, 0x71, 0x75, 0x65, 0x74, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x1a, 0xf1, 0x03, - 0x0a, 0x13, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x53, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x5b, 0x0a, 0x0a, 0x63, 0x73, 0x76, 0x5f, 0x6f, 0x75, 0x74, - 0x70, 0x75, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x3c, 0x2e, 0x76, 0x6f, 0x6c, 0x75, - 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x51, 0x75, 0x65, - 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, - 0x53, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x43, 0x53, - 0x56, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x52, 0x09, 0x63, 0x73, 0x76, 0x4f, 0x75, 0x74, 0x70, - 0x75, 0x74, 0x12, 0x5e, 0x0a, 0x0b, 0x6a, 0x73, 0x6f, 0x6e, 0x5f, 0x6f, 0x75, 0x74, 0x70, 0x75, - 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x3d, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, - 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x53, 0x65, - 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x4a, 0x53, 0x4f, 0x4e, - 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x52, 0x0a, 0x6a, 0x73, 0x6f, 0x6e, 0x4f, 0x75, 0x74, 0x70, - 0x75, 0x74, 0x1a, 0xe3, 0x01, 0x0a, 0x09, 0x43, 0x53, 0x56, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, - 0x12, 0x21, 0x0a, 0x0c, 0x71, 0x75, 0x6f, 0x74, 0x65, 0x5f, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x73, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x71, 0x75, 0x6f, 0x74, 0x65, 0x46, 0x69, 0x65, - 0x6c, 0x64, 0x73, 0x12, 0x29, 0x0a, 0x10, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x5f, 0x64, 0x65, - 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x72, - 0x65, 0x63, 0x6f, 0x72, 0x64, 0x44, 0x65, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x65, 0x72, 0x12, 0x27, - 0x0a, 0x0f, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x5f, 0x64, 0x65, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x65, - 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x44, 0x65, - 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x65, 0x72, 0x12, 0x29, 0x0a, 0x10, 0x71, 0x75, 0x6f, 0x74, 0x65, - 0x5f, 0x63, 0x68, 0x61, 0x72, 0x61, 0x63, 0x74, 0x6f, 0x65, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x0f, 0x71, 0x75, 0x6f, 0x74, 0x65, 0x43, 0x68, 0x61, 0x72, 0x61, 0x63, 0x74, 0x6f, - 0x65, 0x72, 0x12, 0x34, 0x0a, 0x16, 0x71, 0x75, 0x6f, 0x74, 0x65, 0x5f, 0x65, 0x73, 0x63, 0x61, - 0x70, 0x65, 0x5f, 0x63, 0x68, 0x61, 0x72, 0x61, 0x63, 0x74, 0x65, 0x72, 0x18, 0x05, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x14, 0x71, 0x75, 0x6f, 0x74, 0x65, 0x45, 0x73, 0x63, 0x61, 0x70, 0x65, 0x43, - 0x68, 0x61, 0x72, 0x61, 0x63, 0x74, 0x65, 0x72, 0x1a, 0x37, 0x0a, 0x0a, 0x4a, 0x53, 0x4f, 0x4e, - 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x12, 0x29, 0x0a, 0x10, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, - 0x5f, 0x64, 0x65, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x0f, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x44, 0x65, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x65, - 0x72, 0x22, 0x29, 0x0a, 0x0d, 0x51, 0x75, 0x65, 0x72, 0x69, 0x65, 0x64, 0x53, 0x74, 0x72, 0x69, - 0x70, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x73, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x0c, 0x52, 0x07, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x73, 0x22, 0x55, 0x0a, 0x19, - 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x4e, 0x65, 0x65, 0x64, 0x6c, 0x65, 0x53, 0x74, 0x61, 0x74, - 0x75, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x76, 0x6f, 0x6c, - 0x75, 0x6d, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x76, 0x6f, - 0x6c, 0x75, 0x6d, 0x65, 0x49, 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x6e, 0x65, 0x65, 0x64, 0x6c, 0x65, - 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x08, 0x6e, 0x65, 0x65, 0x64, 0x6c, - 0x65, 0x49, 0x64, 0x22, 0xae, 0x01, 0x0a, 0x1a, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x4e, 0x65, - 0x65, 0x64, 0x6c, 0x65, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x6e, 0x65, 0x65, 0x64, 0x6c, 0x65, 0x5f, 0x69, 0x64, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x08, 0x6e, 0x65, 0x65, 0x64, 0x6c, 0x65, 0x49, 0x64, 0x12, - 0x16, 0x0a, 0x06, 0x63, 0x6f, 0x6f, 0x6b, 0x69, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, - 0x06, 0x63, 0x6f, 0x6f, 0x6b, 0x69, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x18, - 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x6c, - 0x61, 0x73, 0x74, 0x5f, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x69, 0x65, 0x64, 0x18, 0x04, 0x20, 0x01, - 0x28, 0x04, 0x52, 0x0c, 0x6c, 0x61, 0x73, 0x74, 0x4d, 0x6f, 0x64, 0x69, 0x66, 0x69, 0x65, 0x64, - 0x12, 0x10, 0x0a, 0x03, 0x63, 0x72, 0x63, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x03, 0x63, - 0x72, 0x63, 0x12, 0x10, 0x0a, 0x03, 0x74, 0x74, 0x6c, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x03, 0x74, 0x74, 0x6c, 0x32, 0x9f, 0x22, 0x0a, 0x0c, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x53, - 0x65, 0x72, 0x76, 0x65, 0x72, 0x12, 0x5c, 0x0a, 0x0b, 0x42, 0x61, 0x74, 0x63, 0x68, 0x44, 0x65, - 0x6c, 0x65, 0x74, 0x65, 0x12, 0x24, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, - 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x42, 0x61, 0x74, 0x63, 0x68, 0x44, 0x65, 0x6c, - 0x65, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x25, 0x2e, 0x76, 0x6f, 0x6c, - 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x42, 0x61, - 0x74, 0x63, 0x68, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x22, 0x00, 0x12, 0x6e, 0x0a, 0x11, 0x56, 0x61, 0x63, 0x75, 0x75, 0x6d, 0x56, 0x6f, 0x6c, - 0x75, 0x6d, 0x65, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x12, 0x2a, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, - 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x61, 0x63, 0x75, - 0x75, 0x6d, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2b, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, - 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x61, 0x63, 0x75, 0x75, 0x6d, 0x56, 0x6f, - 0x6c, 0x75, 0x6d, 0x65, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x22, 0x00, 0x12, 0x74, 0x0a, 0x13, 0x56, 0x61, 0x63, 0x75, 0x75, 0x6d, 0x56, 0x6f, 0x6c, - 0x75, 0x6d, 0x65, 0x43, 0x6f, 0x6d, 0x70, 0x61, 0x63, 0x74, 0x12, 0x2c, 0x2e, 0x76, 0x6f, 0x6c, - 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x61, - 0x63, 0x75, 0x75, 0x6d, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x43, 0x6f, 0x6d, 0x70, 0x61, 0x63, - 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2d, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, + 0x64, 0x22, 0xae, 0x01, 0x0a, 0x1a, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x4e, 0x65, 0x65, 0x64, + 0x6c, 0x65, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x12, 0x1b, 0x0a, 0x09, 0x6e, 0x65, 0x65, 0x64, 0x6c, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x04, 0x52, 0x08, 0x6e, 0x65, 0x65, 0x64, 0x6c, 0x65, 0x49, 0x64, 0x12, 0x16, 0x0a, + 0x06, 0x63, 0x6f, 0x6f, 0x6b, 0x69, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x06, 0x63, + 0x6f, 0x6f, 0x6b, 0x69, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x0d, 0x52, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x6c, 0x61, 0x73, + 0x74, 0x5f, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x69, 0x65, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, + 0x52, 0x0c, 0x6c, 0x61, 0x73, 0x74, 0x4d, 0x6f, 0x64, 0x69, 0x66, 0x69, 0x65, 0x64, 0x12, 0x10, + 0x0a, 0x03, 0x63, 0x72, 0x63, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x03, 0x63, 0x72, 0x63, + 0x12, 0x10, 0x0a, 0x03, 0x74, 0x74, 0x6c, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x74, + 0x74, 0x6c, 0x32, 0x88, 0x23, 0x0a, 0x0c, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x53, 0x65, 0x72, + 0x76, 0x65, 0x72, 0x12, 0x5c, 0x0a, 0x0b, 0x42, 0x61, 0x74, 0x63, 0x68, 0x44, 0x65, 0x6c, 0x65, + 0x74, 0x65, 0x12, 0x24, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, + 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x42, 0x61, 0x74, 0x63, 0x68, 0x44, 0x65, 0x6c, 0x65, 0x74, + 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x25, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, + 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x42, 0x61, 0x74, 0x63, + 0x68, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, + 0x00, 0x12, 0x6e, 0x0a, 0x11, 0x56, 0x61, 0x63, 0x75, 0x75, 0x6d, 0x56, 0x6f, 0x6c, 0x75, 0x6d, + 0x65, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x12, 0x2a, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, + 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x61, 0x63, 0x75, 0x75, 0x6d, + 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x2b, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, + 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x61, 0x63, 0x75, 0x75, 0x6d, 0x56, 0x6f, 0x6c, 0x75, + 0x6d, 0x65, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, + 0x00, 0x12, 0x74, 0x0a, 0x13, 0x56, 0x61, 0x63, 0x75, 0x75, 0x6d, 0x56, 0x6f, 0x6c, 0x75, 0x6d, + 0x65, 0x43, 0x6f, 0x6d, 0x70, 0x61, 0x63, 0x74, 0x12, 0x2c, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x61, 0x63, 0x75, 0x75, 0x6d, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x43, 0x6f, 0x6d, 0x70, 0x61, 0x63, 0x74, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x71, 0x0a, 0x12, 0x56, 0x61, 0x63, - 0x75, 0x75, 0x6d, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x12, - 0x2b, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, - 0x70, 0x62, 0x2e, 0x56, 0x61, 0x63, 0x75, 0x75, 0x6d, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x43, - 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2c, 0x2e, 0x76, - 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, - 0x56, 0x61, 0x63, 0x75, 0x75, 0x6d, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x43, 0x6f, 0x6d, 0x6d, - 0x69, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x74, 0x0a, 0x13, - 0x56, 0x61, 0x63, 0x75, 0x75, 0x6d, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x43, 0x6c, 0x65, 0x61, - 0x6e, 0x75, 0x70, 0x12, 0x2c, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, - 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x61, 0x63, 0x75, 0x75, 0x6d, 0x56, 0x6f, 0x6c, - 0x75, 0x6d, 0x65, 0x43, 0x6c, 0x65, 0x61, 0x6e, 0x75, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x2d, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2d, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, + 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x61, 0x63, 0x75, 0x75, 0x6d, + 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x43, 0x6f, 0x6d, 0x70, 0x61, 0x63, 0x74, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x71, 0x0a, 0x12, 0x56, 0x61, 0x63, 0x75, 0x75, + 0x6d, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x12, 0x2b, 0x2e, + 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, + 0x2e, 0x56, 0x61, 0x63, 0x75, 0x75, 0x6d, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x43, 0x6f, 0x6d, + 0x6d, 0x69, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2c, 0x2e, 0x76, 0x6f, 0x6c, + 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x61, + 0x63, 0x75, 0x75, 0x6d, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x74, 0x0a, 0x13, 0x56, 0x61, + 0x63, 0x75, 0x75, 0x6d, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x43, 0x6c, 0x65, 0x61, 0x6e, 0x75, + 0x70, 0x12, 0x2c, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x61, 0x63, 0x75, 0x75, 0x6d, 0x56, 0x6f, 0x6c, 0x75, 0x6d, - 0x65, 0x43, 0x6c, 0x65, 0x61, 0x6e, 0x75, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x22, 0x00, 0x12, 0x6b, 0x0a, 0x10, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x43, 0x6f, 0x6c, 0x6c, - 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x29, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, - 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, - 0x43, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x2a, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, - 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x43, 0x6f, 0x6c, 0x6c, 0x65, - 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, - 0x65, 0x0a, 0x0e, 0x41, 0x6c, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x65, 0x56, 0x6f, 0x6c, 0x75, 0x6d, - 0x65, 0x12, 0x27, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, - 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x41, 0x6c, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x65, 0x56, 0x6f, 0x6c, - 0x75, 0x6d, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x28, 0x2e, 0x76, 0x6f, 0x6c, - 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x41, 0x6c, - 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x65, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x6b, 0x0a, 0x10, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, - 0x53, 0x79, 0x6e, 0x63, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x29, 0x2e, 0x76, 0x6f, 0x6c, - 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, - 0x6c, 0x75, 0x6d, 0x65, 0x53, 0x79, 0x6e, 0x63, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2a, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, - 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x53, - 0x79, 0x6e, 0x63, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x22, 0x00, 0x12, 0x7c, 0x0a, 0x15, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x49, 0x6e, 0x63, - 0x72, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x61, 0x6c, 0x43, 0x6f, 0x70, 0x79, 0x12, 0x2e, 0x2e, 0x76, - 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, - 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x49, 0x6e, 0x63, 0x72, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x61, - 0x6c, 0x43, 0x6f, 0x70, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2f, 0x2e, 0x76, - 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, - 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x49, 0x6e, 0x63, 0x72, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x61, - 0x6c, 0x43, 0x6f, 0x70, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x30, - 0x01, 0x12, 0x5c, 0x0a, 0x0b, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x4d, 0x6f, 0x75, 0x6e, 0x74, - 0x12, 0x24, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, - 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x4d, 0x6f, 0x75, 0x6e, 0x74, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x25, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, - 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, - 0x4d, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, - 0x62, 0x0a, 0x0d, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x55, 0x6e, 0x6d, 0x6f, 0x75, 0x6e, 0x74, - 0x12, 0x26, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, - 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x55, 0x6e, 0x6d, 0x6f, 0x75, 0x6e, - 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x27, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, - 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, - 0x6d, 0x65, 0x55, 0x6e, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x22, 0x00, 0x12, 0x5f, 0x0a, 0x0c, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x44, 0x65, 0x6c, - 0x65, 0x74, 0x65, 0x12, 0x25, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, - 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x44, 0x65, 0x6c, - 0x65, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x26, 0x2e, 0x76, 0x6f, 0x6c, - 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, - 0x6c, 0x75, 0x6d, 0x65, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x22, 0x00, 0x12, 0x71, 0x0a, 0x12, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x4d, 0x61, - 0x72, 0x6b, 0x52, 0x65, 0x61, 0x64, 0x6f, 0x6e, 0x6c, 0x79, 0x12, 0x2b, 0x2e, 0x76, 0x6f, 0x6c, - 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, - 0x6c, 0x75, 0x6d, 0x65, 0x4d, 0x61, 0x72, 0x6b, 0x52, 0x65, 0x61, 0x64, 0x6f, 0x6e, 0x6c, 0x79, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2c, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, - 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, - 0x65, 0x4d, 0x61, 0x72, 0x6b, 0x52, 0x65, 0x61, 0x64, 0x6f, 0x6e, 0x6c, 0x79, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x71, 0x0a, 0x12, 0x56, 0x6f, 0x6c, 0x75, 0x6d, - 0x65, 0x4d, 0x61, 0x72, 0x6b, 0x57, 0x72, 0x69, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x12, 0x2b, 0x2e, - 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, - 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x4d, 0x61, 0x72, 0x6b, 0x57, 0x72, 0x69, 0x74, 0x61, - 0x62, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2c, 0x2e, 0x76, 0x6f, 0x6c, - 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, - 0x6c, 0x75, 0x6d, 0x65, 0x4d, 0x61, 0x72, 0x6b, 0x57, 0x72, 0x69, 0x74, 0x61, 0x62, 0x6c, 0x65, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x68, 0x0a, 0x0f, 0x56, 0x6f, - 0x6c, 0x75, 0x6d, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x65, 0x12, 0x28, 0x2e, - 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, - 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x65, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x29, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, - 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, - 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x22, 0x00, 0x12, 0x5f, 0x0a, 0x0c, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x53, 0x74, - 0x61, 0x74, 0x75, 0x73, 0x12, 0x25, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, - 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x53, 0x74, - 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x26, 0x2e, 0x76, 0x6f, - 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, - 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x59, 0x0a, 0x0a, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x43, - 0x6f, 0x70, 0x79, 0x12, 0x23, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, - 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x43, 0x6f, 0x70, - 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, - 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, - 0x6d, 0x65, 0x43, 0x6f, 0x70, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, - 0x12, 0x77, 0x0a, 0x14, 0x52, 0x65, 0x61, 0x64, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x46, 0x69, - 0x6c, 0x65, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x2d, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, - 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x52, 0x65, 0x61, 0x64, - 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x46, 0x69, 0x6c, 0x65, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2e, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, - 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x52, 0x65, 0x61, 0x64, 0x56, - 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x46, 0x69, 0x6c, 0x65, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x55, 0x0a, 0x08, 0x43, 0x6f, 0x70, - 0x79, 0x46, 0x69, 0x6c, 0x65, 0x12, 0x21, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, - 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x43, 0x6f, 0x70, 0x79, 0x46, 0x69, 0x6c, - 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, - 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x43, 0x6f, 0x70, 0x79, - 0x46, 0x69, 0x6c, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x30, 0x01, - 0x12, 0x65, 0x0a, 0x0e, 0x52, 0x65, 0x61, 0x64, 0x4e, 0x65, 0x65, 0x64, 0x6c, 0x65, 0x42, 0x6c, - 0x6f, 0x62, 0x12, 0x27, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, - 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x52, 0x65, 0x61, 0x64, 0x4e, 0x65, 0x65, 0x64, 0x6c, 0x65, - 0x42, 0x6c, 0x6f, 0x62, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x28, 0x2e, 0x76, 0x6f, - 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x52, - 0x65, 0x61, 0x64, 0x4e, 0x65, 0x65, 0x64, 0x6c, 0x65, 0x42, 0x6c, 0x6f, 0x62, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x68, 0x0a, 0x0f, 0x57, 0x72, 0x69, 0x74, 0x65, - 0x4e, 0x65, 0x65, 0x64, 0x6c, 0x65, 0x42, 0x6c, 0x6f, 0x62, 0x12, 0x28, 0x2e, 0x76, 0x6f, 0x6c, - 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x57, 0x72, - 0x69, 0x74, 0x65, 0x4e, 0x65, 0x65, 0x64, 0x6c, 0x65, 0x42, 0x6c, 0x6f, 0x62, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x29, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, - 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x57, 0x72, 0x69, 0x74, 0x65, 0x4e, 0x65, 0x65, - 0x64, 0x6c, 0x65, 0x42, 0x6c, 0x6f, 0x62, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, - 0x00, 0x12, 0x6d, 0x0a, 0x10, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x54, 0x61, 0x69, 0x6c, 0x53, - 0x65, 0x6e, 0x64, 0x65, 0x72, 0x12, 0x29, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, - 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x54, - 0x61, 0x69, 0x6c, 0x53, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x2a, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, - 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x54, 0x61, 0x69, 0x6c, 0x53, 0x65, - 0x6e, 0x64, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x30, 0x01, - 0x12, 0x71, 0x0a, 0x12, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x54, 0x61, 0x69, 0x6c, 0x52, 0x65, - 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, 0x12, 0x2b, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, - 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, - 0x54, 0x61, 0x69, 0x6c, 0x52, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x2c, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, - 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x54, 0x61, 0x69, - 0x6c, 0x52, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x22, 0x00, 0x12, 0x7d, 0x0a, 0x16, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x45, 0x63, 0x53, - 0x68, 0x61, 0x72, 0x64, 0x73, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x12, 0x2f, 0x2e, - 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, - 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x45, 0x63, 0x53, 0x68, 0x61, 0x72, 0x64, 0x73, 0x47, - 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x30, - 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, - 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x45, 0x63, 0x53, 0x68, 0x61, 0x72, 0x64, 0x73, - 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x22, 0x00, 0x12, 0x7a, 0x0a, 0x15, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x45, 0x63, 0x53, 0x68, - 0x61, 0x72, 0x64, 0x73, 0x52, 0x65, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x12, 0x2e, 0x2e, 0x76, 0x6f, - 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, - 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x45, 0x63, 0x53, 0x68, 0x61, 0x72, 0x64, 0x73, 0x52, 0x65, 0x62, - 0x75, 0x69, 0x6c, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2f, 0x2e, 0x76, 0x6f, - 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, - 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x45, 0x63, 0x53, 0x68, 0x61, 0x72, 0x64, 0x73, 0x52, 0x65, 0x62, - 0x75, 0x69, 0x6c, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x71, - 0x0a, 0x12, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x45, 0x63, 0x53, 0x68, 0x61, 0x72, 0x64, 0x73, - 0x43, 0x6f, 0x70, 0x79, 0x12, 0x2b, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, - 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x45, 0x63, - 0x53, 0x68, 0x61, 0x72, 0x64, 0x73, 0x43, 0x6f, 0x70, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x2c, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, - 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x45, 0x63, 0x53, 0x68, 0x61, - 0x72, 0x64, 0x73, 0x43, 0x6f, 0x70, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, - 0x00, 0x12, 0x77, 0x0a, 0x14, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x45, 0x63, 0x53, 0x68, 0x61, - 0x72, 0x64, 0x73, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x12, 0x2d, 0x2e, 0x76, 0x6f, 0x6c, 0x75, - 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, - 0x75, 0x6d, 0x65, 0x45, 0x63, 0x53, 0x68, 0x61, 0x72, 0x64, 0x73, 0x44, 0x65, 0x6c, 0x65, 0x74, - 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2e, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, - 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, - 0x6d, 0x65, 0x45, 0x63, 0x53, 0x68, 0x61, 0x72, 0x64, 0x73, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x74, 0x0a, 0x13, 0x56, 0x6f, - 0x6c, 0x75, 0x6d, 0x65, 0x45, 0x63, 0x53, 0x68, 0x61, 0x72, 0x64, 0x73, 0x4d, 0x6f, 0x75, 0x6e, - 0x74, 0x12, 0x2c, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, - 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x45, 0x63, 0x53, 0x68, 0x61, - 0x72, 0x64, 0x73, 0x4d, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x65, 0x43, 0x6c, 0x65, 0x61, 0x6e, 0x75, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2d, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, - 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x45, 0x63, 0x53, 0x68, 0x61, 0x72, 0x64, - 0x73, 0x4d, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, - 0x12, 0x7a, 0x0a, 0x15, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x45, 0x63, 0x53, 0x68, 0x61, 0x72, - 0x64, 0x73, 0x55, 0x6e, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x2e, 0x2e, 0x76, 0x6f, 0x6c, 0x75, - 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, - 0x75, 0x6d, 0x65, 0x45, 0x63, 0x53, 0x68, 0x61, 0x72, 0x64, 0x73, 0x55, 0x6e, 0x6d, 0x6f, 0x75, - 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2f, 0x2e, 0x76, 0x6f, 0x6c, 0x75, - 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, - 0x75, 0x6d, 0x65, 0x45, 0x63, 0x53, 0x68, 0x61, 0x72, 0x64, 0x73, 0x55, 0x6e, 0x6d, 0x6f, 0x75, - 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x70, 0x0a, 0x11, - 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x45, 0x63, 0x53, 0x68, 0x61, 0x72, 0x64, 0x52, 0x65, 0x61, - 0x64, 0x12, 0x2a, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, - 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x45, 0x63, 0x53, 0x68, 0x61, - 0x72, 0x64, 0x52, 0x65, 0x61, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2b, 0x2e, - 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, - 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x45, 0x63, 0x53, 0x68, 0x61, 0x72, 0x64, 0x52, 0x65, - 0x61, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x30, 0x01, 0x12, 0x71, - 0x0a, 0x12, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x45, 0x63, 0x42, 0x6c, 0x6f, 0x62, 0x44, 0x65, - 0x6c, 0x65, 0x74, 0x65, 0x12, 0x2b, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, - 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x45, 0x63, - 0x42, 0x6c, 0x6f, 0x62, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x2c, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, - 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x45, 0x63, 0x42, 0x6c, 0x6f, - 0x62, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, - 0x00, 0x12, 0x7d, 0x0a, 0x16, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x45, 0x63, 0x53, 0x68, 0x61, - 0x72, 0x64, 0x73, 0x54, 0x6f, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x12, 0x2f, 0x2e, 0x76, 0x6f, + 0x70, 0x62, 0x2e, 0x56, 0x61, 0x63, 0x75, 0x75, 0x6d, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x43, + 0x6c, 0x65, 0x61, 0x6e, 0x75, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, + 0x12, 0x6b, 0x0a, 0x10, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x43, 0x6f, 0x6c, 0x6c, 0x65, 0x63, + 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x29, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, + 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x43, 0x6f, + 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x2a, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, + 0x70, 0x62, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x43, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, + 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x65, 0x0a, + 0x0e, 0x41, 0x6c, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x65, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x12, + 0x27, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, + 0x70, 0x62, 0x2e, 0x41, 0x6c, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x65, 0x56, 0x6f, 0x6c, 0x75, 0x6d, + 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x28, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, + 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x41, 0x6c, 0x6c, 0x6f, + 0x63, 0x61, 0x74, 0x65, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x22, 0x00, 0x12, 0x6b, 0x0a, 0x10, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x53, 0x79, + 0x6e, 0x63, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x29, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, + 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, + 0x6d, 0x65, 0x53, 0x79, 0x6e, 0x63, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x2a, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, + 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x53, 0x79, 0x6e, + 0x63, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, + 0x00, 0x12, 0x7c, 0x0a, 0x15, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x49, 0x6e, 0x63, 0x72, 0x65, + 0x6d, 0x65, 0x6e, 0x74, 0x61, 0x6c, 0x43, 0x6f, 0x70, 0x79, 0x12, 0x2e, 0x2e, 0x76, 0x6f, 0x6c, + 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, + 0x6c, 0x75, 0x6d, 0x65, 0x49, 0x6e, 0x63, 0x72, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x61, 0x6c, 0x43, + 0x6f, 0x70, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2f, 0x2e, 0x76, 0x6f, 0x6c, + 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, + 0x6c, 0x75, 0x6d, 0x65, 0x49, 0x6e, 0x63, 0x72, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x61, 0x6c, 0x43, + 0x6f, 0x70, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x30, 0x01, 0x12, + 0x5c, 0x0a, 0x0b, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x4d, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x24, + 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, + 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x4d, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x25, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, + 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x4d, 0x6f, + 0x75, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x62, 0x0a, + 0x0d, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x55, 0x6e, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x26, + 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, + 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x55, 0x6e, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x27, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, + 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, + 0x55, 0x6e, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, + 0x00, 0x12, 0x5f, 0x0a, 0x0c, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x44, 0x65, 0x6c, 0x65, 0x74, + 0x65, 0x12, 0x25, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, + 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x44, 0x65, 0x6c, 0x65, 0x74, + 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x26, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, + 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, + 0x6d, 0x65, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x22, 0x00, 0x12, 0x71, 0x0a, 0x12, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x4d, 0x61, 0x72, 0x6b, + 0x52, 0x65, 0x61, 0x64, 0x6f, 0x6e, 0x6c, 0x79, 0x12, 0x2b, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, + 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, + 0x6d, 0x65, 0x4d, 0x61, 0x72, 0x6b, 0x52, 0x65, 0x61, 0x64, 0x6f, 0x6e, 0x6c, 0x79, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2c, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, + 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x4d, + 0x61, 0x72, 0x6b, 0x52, 0x65, 0x61, 0x64, 0x6f, 0x6e, 0x6c, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x71, 0x0a, 0x12, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x4d, + 0x61, 0x72, 0x6b, 0x57, 0x72, 0x69, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x12, 0x2b, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, - 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x45, 0x63, 0x53, 0x68, 0x61, 0x72, 0x64, 0x73, 0x54, 0x6f, 0x56, - 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x30, 0x2e, 0x76, + 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x4d, 0x61, 0x72, 0x6b, 0x57, 0x72, 0x69, 0x74, 0x61, 0x62, 0x6c, + 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2c, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, + 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, + 0x6d, 0x65, 0x4d, 0x61, 0x72, 0x6b, 0x57, 0x72, 0x69, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x68, 0x0a, 0x0f, 0x56, 0x6f, 0x6c, 0x75, + 0x6d, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x65, 0x12, 0x28, 0x2e, 0x76, 0x6f, + 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, + 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x65, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x29, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, + 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x43, + 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x22, 0x00, 0x12, 0x5f, 0x0a, 0x0c, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x53, 0x74, 0x61, 0x74, + 0x75, 0x73, 0x12, 0x25, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, + 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x53, 0x74, 0x61, 0x74, + 0x75, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x26, 0x2e, 0x76, 0x6f, 0x6c, 0x75, + 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, + 0x75, 0x6d, 0x65, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x22, 0x00, 0x12, 0x59, 0x0a, 0x0a, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x43, 0x6f, 0x70, + 0x79, 0x12, 0x23, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, + 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x43, 0x6f, 0x70, 0x79, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, + 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, + 0x43, 0x6f, 0x70, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x77, + 0x0a, 0x14, 0x52, 0x65, 0x61, 0x64, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x46, 0x69, 0x6c, 0x65, + 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x2d, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, + 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x52, 0x65, 0x61, 0x64, 0x56, 0x6f, + 0x6c, 0x75, 0x6d, 0x65, 0x46, 0x69, 0x6c, 0x65, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2e, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, + 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x52, 0x65, 0x61, 0x64, 0x56, 0x6f, 0x6c, + 0x75, 0x6d, 0x65, 0x46, 0x69, 0x6c, 0x65, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x55, 0x0a, 0x08, 0x43, 0x6f, 0x70, 0x79, 0x46, + 0x69, 0x6c, 0x65, 0x12, 0x21, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, + 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x43, 0x6f, 0x70, 0x79, 0x46, 0x69, 0x6c, 0x65, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, + 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x43, 0x6f, 0x70, 0x79, 0x46, 0x69, + 0x6c, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x30, 0x01, 0x12, 0x65, + 0x0a, 0x0e, 0x52, 0x65, 0x61, 0x64, 0x4e, 0x65, 0x65, 0x64, 0x6c, 0x65, 0x42, 0x6c, 0x6f, 0x62, + 0x12, 0x27, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, + 0x5f, 0x70, 0x62, 0x2e, 0x52, 0x65, 0x61, 0x64, 0x4e, 0x65, 0x65, 0x64, 0x6c, 0x65, 0x42, 0x6c, + 0x6f, 0x62, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x28, 0x2e, 0x76, 0x6f, 0x6c, 0x75, + 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x52, 0x65, 0x61, + 0x64, 0x4e, 0x65, 0x65, 0x64, 0x6c, 0x65, 0x42, 0x6c, 0x6f, 0x62, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x68, 0x0a, 0x0f, 0x57, 0x72, 0x69, 0x74, 0x65, 0x4e, 0x65, + 0x65, 0x64, 0x6c, 0x65, 0x42, 0x6c, 0x6f, 0x62, 0x12, 0x28, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, + 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x57, 0x72, 0x69, 0x74, + 0x65, 0x4e, 0x65, 0x65, 0x64, 0x6c, 0x65, 0x42, 0x6c, 0x6f, 0x62, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x29, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, + 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x57, 0x72, 0x69, 0x74, 0x65, 0x4e, 0x65, 0x65, 0x64, 0x6c, + 0x65, 0x42, 0x6c, 0x6f, 0x62, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, + 0x67, 0x0a, 0x0e, 0x52, 0x65, 0x61, 0x64, 0x41, 0x6c, 0x6c, 0x4e, 0x65, 0x65, 0x64, 0x6c, 0x65, + 0x73, 0x12, 0x27, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, + 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x52, 0x65, 0x61, 0x64, 0x41, 0x6c, 0x6c, 0x4e, 0x65, 0x65, 0x64, + 0x6c, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x28, 0x2e, 0x76, 0x6f, 0x6c, + 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x52, 0x65, + 0x61, 0x64, 0x41, 0x6c, 0x6c, 0x4e, 0x65, 0x65, 0x64, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x30, 0x01, 0x12, 0x6d, 0x0a, 0x10, 0x56, 0x6f, 0x6c, 0x75, + 0x6d, 0x65, 0x54, 0x61, 0x69, 0x6c, 0x53, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x12, 0x29, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, - 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x45, 0x63, 0x53, 0x68, 0x61, 0x72, 0x64, 0x73, 0x54, 0x6f, - 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, - 0x12, 0x88, 0x01, 0x0a, 0x19, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x54, 0x69, 0x65, 0x72, 0x4d, - 0x6f, 0x76, 0x65, 0x44, 0x61, 0x74, 0x54, 0x6f, 0x52, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x12, 0x32, - 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, - 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x54, 0x69, 0x65, 0x72, 0x4d, 0x6f, 0x76, 0x65, - 0x44, 0x61, 0x74, 0x54, 0x6f, 0x52, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x33, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, - 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x54, 0x69, 0x65, 0x72, - 0x4d, 0x6f, 0x76, 0x65, 0x44, 0x61, 0x74, 0x54, 0x6f, 0x52, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x30, 0x01, 0x12, 0x8e, 0x01, 0x0a, 0x1b, - 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x54, 0x69, 0x65, 0x72, 0x4d, 0x6f, 0x76, 0x65, 0x44, 0x61, - 0x74, 0x46, 0x72, 0x6f, 0x6d, 0x52, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x12, 0x34, 0x2e, 0x76, 0x6f, + 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x54, 0x61, 0x69, 0x6c, 0x53, 0x65, 0x6e, 0x64, 0x65, 0x72, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2a, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, + 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, + 0x65, 0x54, 0x61, 0x69, 0x6c, 0x53, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x22, 0x00, 0x30, 0x01, 0x12, 0x71, 0x0a, 0x12, 0x56, 0x6f, 0x6c, 0x75, 0x6d, + 0x65, 0x54, 0x61, 0x69, 0x6c, 0x52, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, 0x12, 0x2b, 0x2e, + 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, + 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x54, 0x61, 0x69, 0x6c, 0x52, 0x65, 0x63, 0x65, 0x69, + 0x76, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2c, 0x2e, 0x76, 0x6f, 0x6c, + 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, + 0x6c, 0x75, 0x6d, 0x65, 0x54, 0x61, 0x69, 0x6c, 0x52, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x7d, 0x0a, 0x16, 0x56, 0x6f, + 0x6c, 0x75, 0x6d, 0x65, 0x45, 0x63, 0x53, 0x68, 0x61, 0x72, 0x64, 0x73, 0x47, 0x65, 0x6e, 0x65, + 0x72, 0x61, 0x74, 0x65, 0x12, 0x2f, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, + 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x45, 0x63, + 0x53, 0x68, 0x61, 0x72, 0x64, 0x73, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x30, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, + 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x45, + 0x63, 0x53, 0x68, 0x61, 0x72, 0x64, 0x73, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x7a, 0x0a, 0x15, 0x56, 0x6f, 0x6c, + 0x75, 0x6d, 0x65, 0x45, 0x63, 0x53, 0x68, 0x61, 0x72, 0x64, 0x73, 0x52, 0x65, 0x62, 0x75, 0x69, + 0x6c, 0x64, 0x12, 0x2e, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, + 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x45, 0x63, 0x53, 0x68, + 0x61, 0x72, 0x64, 0x73, 0x52, 0x65, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x2f, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, + 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x45, 0x63, 0x53, 0x68, + 0x61, 0x72, 0x64, 0x73, 0x52, 0x65, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x71, 0x0a, 0x12, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x45, + 0x63, 0x53, 0x68, 0x61, 0x72, 0x64, 0x73, 0x43, 0x6f, 0x70, 0x79, 0x12, 0x2b, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, - 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x54, 0x69, 0x65, 0x72, 0x4d, 0x6f, 0x76, 0x65, 0x44, 0x61, 0x74, - 0x46, 0x72, 0x6f, 0x6d, 0x52, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x35, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, - 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x54, 0x69, 0x65, 0x72, 0x4d, - 0x6f, 0x76, 0x65, 0x44, 0x61, 0x74, 0x46, 0x72, 0x6f, 0x6d, 0x52, 0x65, 0x6d, 0x6f, 0x74, 0x65, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x30, 0x01, 0x12, 0x71, 0x0a, 0x12, - 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, - 0x75, 0x73, 0x12, 0x2b, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, - 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, - 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x2c, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, - 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x53, - 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, - 0x6e, 0x0a, 0x11, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x4c, - 0x65, 0x61, 0x76, 0x65, 0x12, 0x2a, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, - 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x53, 0x65, - 0x72, 0x76, 0x65, 0x72, 0x4c, 0x65, 0x61, 0x76, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x2b, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, - 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, - 0x4c, 0x65, 0x61, 0x76, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, - 0x74, 0x0a, 0x13, 0x46, 0x65, 0x74, 0x63, 0x68, 0x41, 0x6e, 0x64, 0x57, 0x72, 0x69, 0x74, 0x65, - 0x4e, 0x65, 0x65, 0x64, 0x6c, 0x65, 0x12, 0x2c, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, - 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x46, 0x65, 0x74, 0x63, 0x68, 0x41, - 0x6e, 0x64, 0x57, 0x72, 0x69, 0x74, 0x65, 0x4e, 0x65, 0x65, 0x64, 0x6c, 0x65, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2d, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, - 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x46, 0x65, 0x74, 0x63, 0x68, 0x41, 0x6e, 0x64, - 0x57, 0x72, 0x69, 0x74, 0x65, 0x4e, 0x65, 0x65, 0x64, 0x6c, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x4c, 0x0a, 0x05, 0x51, 0x75, 0x65, 0x72, 0x79, 0x12, 0x1e, - 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, - 0x62, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, - 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, - 0x62, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x69, 0x65, 0x64, 0x53, 0x74, 0x72, 0x69, 0x70, 0x65, 0x22, - 0x00, 0x30, 0x01, 0x12, 0x71, 0x0a, 0x12, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x4e, 0x65, 0x65, - 0x64, 0x6c, 0x65, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x2b, 0x2e, 0x76, 0x6f, 0x6c, 0x75, + 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x45, 0x63, 0x53, 0x68, 0x61, 0x72, 0x64, 0x73, 0x43, 0x6f, 0x70, + 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2c, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, + 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, + 0x6d, 0x65, 0x45, 0x63, 0x53, 0x68, 0x61, 0x72, 0x64, 0x73, 0x43, 0x6f, 0x70, 0x79, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x77, 0x0a, 0x14, 0x56, 0x6f, 0x6c, 0x75, + 0x6d, 0x65, 0x45, 0x63, 0x53, 0x68, 0x61, 0x72, 0x64, 0x73, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, + 0x12, 0x2d, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, + 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x45, 0x63, 0x53, 0x68, 0x61, 0x72, + 0x64, 0x73, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x2e, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, + 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x45, 0x63, 0x53, 0x68, 0x61, 0x72, 0x64, + 0x73, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, + 0x00, 0x12, 0x74, 0x0a, 0x13, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x45, 0x63, 0x53, 0x68, 0x61, + 0x72, 0x64, 0x73, 0x4d, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x2c, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, + 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, + 0x6d, 0x65, 0x45, 0x63, 0x53, 0x68, 0x61, 0x72, 0x64, 0x73, 0x4d, 0x6f, 0x75, 0x6e, 0x74, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2d, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, + 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, + 0x45, 0x63, 0x53, 0x68, 0x61, 0x72, 0x64, 0x73, 0x4d, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x7a, 0x0a, 0x15, 0x56, 0x6f, 0x6c, 0x75, 0x6d, + 0x65, 0x45, 0x63, 0x53, 0x68, 0x61, 0x72, 0x64, 0x73, 0x55, 0x6e, 0x6d, 0x6f, 0x75, 0x6e, 0x74, + 0x12, 0x2e, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, + 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x45, 0x63, 0x53, 0x68, 0x61, 0x72, + 0x64, 0x73, 0x55, 0x6e, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x2f, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, + 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x45, 0x63, 0x53, 0x68, 0x61, 0x72, + 0x64, 0x73, 0x55, 0x6e, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x22, 0x00, 0x12, 0x70, 0x0a, 0x11, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x45, 0x63, 0x53, + 0x68, 0x61, 0x72, 0x64, 0x52, 0x65, 0x61, 0x64, 0x12, 0x2a, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, + 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, + 0x6d, 0x65, 0x45, 0x63, 0x53, 0x68, 0x61, 0x72, 0x64, 0x52, 0x65, 0x61, 0x64, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2b, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, + 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x45, 0x63, + 0x53, 0x68, 0x61, 0x72, 0x64, 0x52, 0x65, 0x61, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x22, 0x00, 0x30, 0x01, 0x12, 0x71, 0x0a, 0x12, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x45, + 0x63, 0x42, 0x6c, 0x6f, 0x62, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x12, 0x2b, 0x2e, 0x76, 0x6f, + 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, + 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x45, 0x63, 0x42, 0x6c, 0x6f, 0x62, 0x44, 0x65, 0x6c, 0x65, 0x74, + 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2c, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, + 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, + 0x6d, 0x65, 0x45, 0x63, 0x42, 0x6c, 0x6f, 0x62, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x7d, 0x0a, 0x16, 0x56, 0x6f, 0x6c, 0x75, + 0x6d, 0x65, 0x45, 0x63, 0x53, 0x68, 0x61, 0x72, 0x64, 0x73, 0x54, 0x6f, 0x56, 0x6f, 0x6c, 0x75, + 0x6d, 0x65, 0x12, 0x2f, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, + 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x45, 0x63, 0x53, 0x68, + 0x61, 0x72, 0x64, 0x73, 0x54, 0x6f, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x30, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, + 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x45, 0x63, 0x53, + 0x68, 0x61, 0x72, 0x64, 0x73, 0x54, 0x6f, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x88, 0x01, 0x0a, 0x19, 0x56, 0x6f, 0x6c, 0x75, + 0x6d, 0x65, 0x54, 0x69, 0x65, 0x72, 0x4d, 0x6f, 0x76, 0x65, 0x44, 0x61, 0x74, 0x54, 0x6f, 0x52, + 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x12, 0x32, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, + 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x54, + 0x69, 0x65, 0x72, 0x4d, 0x6f, 0x76, 0x65, 0x44, 0x61, 0x74, 0x54, 0x6f, 0x52, 0x65, 0x6d, 0x6f, + 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x33, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, - 0x75, 0x6d, 0x65, 0x4e, 0x65, 0x65, 0x64, 0x6c, 0x65, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, + 0x75, 0x6d, 0x65, 0x54, 0x69, 0x65, 0x72, 0x4d, 0x6f, 0x76, 0x65, 0x44, 0x61, 0x74, 0x54, 0x6f, + 0x52, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, + 0x30, 0x01, 0x12, 0x8e, 0x01, 0x0a, 0x1b, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x54, 0x69, 0x65, + 0x72, 0x4d, 0x6f, 0x76, 0x65, 0x44, 0x61, 0x74, 0x46, 0x72, 0x6f, 0x6d, 0x52, 0x65, 0x6d, 0x6f, + 0x74, 0x65, 0x12, 0x34, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, + 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x54, 0x69, 0x65, 0x72, + 0x4d, 0x6f, 0x76, 0x65, 0x44, 0x61, 0x74, 0x46, 0x72, 0x6f, 0x6d, 0x52, 0x65, 0x6d, 0x6f, 0x74, + 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x35, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, + 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, + 0x6d, 0x65, 0x54, 0x69, 0x65, 0x72, 0x4d, 0x6f, 0x76, 0x65, 0x44, 0x61, 0x74, 0x46, 0x72, 0x6f, + 0x6d, 0x52, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, + 0x00, 0x30, 0x01, 0x12, 0x71, 0x0a, 0x12, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x53, 0x65, 0x72, + 0x76, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x2b, 0x2e, 0x76, 0x6f, 0x6c, 0x75, + 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, + 0x75, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2c, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, - 0x4e, 0x65, 0x65, 0x64, 0x6c, 0x65, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, 0x39, 0x5a, 0x37, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, - 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x68, 0x72, 0x69, 0x73, 0x6c, 0x75, 0x73, 0x66, 0x2f, 0x73, - 0x65, 0x61, 0x77, 0x65, 0x65, 0x64, 0x66, 0x73, 0x2f, 0x77, 0x65, 0x65, 0x64, 0x2f, 0x70, 0x62, - 0x2f, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, - 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x6e, 0x0a, 0x11, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, + 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x4c, 0x65, 0x61, 0x76, 0x65, 0x12, 0x2a, 0x2e, 0x76, 0x6f, + 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, + 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x4c, 0x65, 0x61, 0x76, 0x65, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2b, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, + 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, + 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x4c, 0x65, 0x61, 0x76, 0x65, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x74, 0x0a, 0x13, 0x46, 0x65, 0x74, 0x63, 0x68, 0x41, + 0x6e, 0x64, 0x57, 0x72, 0x69, 0x74, 0x65, 0x4e, 0x65, 0x65, 0x64, 0x6c, 0x65, 0x12, 0x2c, 0x2e, + 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, + 0x2e, 0x46, 0x65, 0x74, 0x63, 0x68, 0x41, 0x6e, 0x64, 0x57, 0x72, 0x69, 0x74, 0x65, 0x4e, 0x65, + 0x65, 0x64, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2d, 0x2e, 0x76, 0x6f, + 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x46, + 0x65, 0x74, 0x63, 0x68, 0x41, 0x6e, 0x64, 0x57, 0x72, 0x69, 0x74, 0x65, 0x4e, 0x65, 0x65, 0x64, + 0x6c, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x4c, 0x0a, 0x05, + 0x51, 0x75, 0x65, 0x72, 0x79, 0x12, 0x1e, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, + 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, + 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x69, 0x65, 0x64, + 0x53, 0x74, 0x72, 0x69, 0x70, 0x65, 0x22, 0x00, 0x30, 0x01, 0x12, 0x71, 0x0a, 0x12, 0x56, 0x6f, + 0x6c, 0x75, 0x6d, 0x65, 0x4e, 0x65, 0x65, 0x64, 0x6c, 0x65, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, + 0x12, 0x2b, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, + 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x4e, 0x65, 0x65, 0x64, 0x6c, 0x65, + 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2c, 0x2e, + 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, + 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x4e, 0x65, 0x65, 0x64, 0x6c, 0x65, 0x53, 0x74, 0x61, + 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, 0x39, 0x5a, + 0x37, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x68, 0x72, 0x69, + 0x73, 0x6c, 0x75, 0x73, 0x66, 0x2f, 0x73, 0x65, 0x61, 0x77, 0x65, 0x65, 0x64, 0x66, 0x73, 0x2f, + 0x77, 0x65, 0x65, 0x64, 0x2f, 0x70, 0x62, 0x2f, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, + 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -6214,7 +6331,7 @@ func file_volume_server_proto_rawDescGZIP() []byte { return file_volume_server_proto_rawDescData } -var file_volume_server_proto_msgTypes = make([]protoimpl.MessageInfo, 93) +var file_volume_server_proto_msgTypes = make([]protoimpl.MessageInfo, 95) var file_volume_server_proto_goTypes = []interface{}{ (*BatchDeleteRequest)(nil), // 0: volume_server_pb.BatchDeleteRequest (*BatchDeleteResponse)(nil), // 1: volume_server_pb.BatchDeleteResponse @@ -6258,76 +6375,78 @@ var file_volume_server_proto_goTypes = []interface{}{ (*ReadNeedleBlobResponse)(nil), // 39: volume_server_pb.ReadNeedleBlobResponse (*WriteNeedleBlobRequest)(nil), // 40: volume_server_pb.WriteNeedleBlobRequest (*WriteNeedleBlobResponse)(nil), // 41: volume_server_pb.WriteNeedleBlobResponse - (*VolumeTailSenderRequest)(nil), // 42: volume_server_pb.VolumeTailSenderRequest - (*VolumeTailSenderResponse)(nil), // 43: volume_server_pb.VolumeTailSenderResponse - (*VolumeTailReceiverRequest)(nil), // 44: volume_server_pb.VolumeTailReceiverRequest - (*VolumeTailReceiverResponse)(nil), // 45: volume_server_pb.VolumeTailReceiverResponse - (*VolumeEcShardsGenerateRequest)(nil), // 46: volume_server_pb.VolumeEcShardsGenerateRequest - (*VolumeEcShardsGenerateResponse)(nil), // 47: volume_server_pb.VolumeEcShardsGenerateResponse - (*VolumeEcShardsRebuildRequest)(nil), // 48: volume_server_pb.VolumeEcShardsRebuildRequest - (*VolumeEcShardsRebuildResponse)(nil), // 49: volume_server_pb.VolumeEcShardsRebuildResponse - (*VolumeEcShardsCopyRequest)(nil), // 50: volume_server_pb.VolumeEcShardsCopyRequest - (*VolumeEcShardsCopyResponse)(nil), // 51: volume_server_pb.VolumeEcShardsCopyResponse - (*VolumeEcShardsDeleteRequest)(nil), // 52: volume_server_pb.VolumeEcShardsDeleteRequest - (*VolumeEcShardsDeleteResponse)(nil), // 53: volume_server_pb.VolumeEcShardsDeleteResponse - (*VolumeEcShardsMountRequest)(nil), // 54: volume_server_pb.VolumeEcShardsMountRequest - (*VolumeEcShardsMountResponse)(nil), // 55: volume_server_pb.VolumeEcShardsMountResponse - (*VolumeEcShardsUnmountRequest)(nil), // 56: volume_server_pb.VolumeEcShardsUnmountRequest - (*VolumeEcShardsUnmountResponse)(nil), // 57: volume_server_pb.VolumeEcShardsUnmountResponse - (*VolumeEcShardReadRequest)(nil), // 58: volume_server_pb.VolumeEcShardReadRequest - (*VolumeEcShardReadResponse)(nil), // 59: volume_server_pb.VolumeEcShardReadResponse - (*VolumeEcBlobDeleteRequest)(nil), // 60: volume_server_pb.VolumeEcBlobDeleteRequest - (*VolumeEcBlobDeleteResponse)(nil), // 61: volume_server_pb.VolumeEcBlobDeleteResponse - (*VolumeEcShardsToVolumeRequest)(nil), // 62: volume_server_pb.VolumeEcShardsToVolumeRequest - (*VolumeEcShardsToVolumeResponse)(nil), // 63: volume_server_pb.VolumeEcShardsToVolumeResponse - (*ReadVolumeFileStatusRequest)(nil), // 64: volume_server_pb.ReadVolumeFileStatusRequest - (*ReadVolumeFileStatusResponse)(nil), // 65: volume_server_pb.ReadVolumeFileStatusResponse - (*DiskStatus)(nil), // 66: volume_server_pb.DiskStatus - (*MemStatus)(nil), // 67: volume_server_pb.MemStatus - (*RemoteFile)(nil), // 68: volume_server_pb.RemoteFile - (*VolumeInfo)(nil), // 69: volume_server_pb.VolumeInfo - (*VolumeTierMoveDatToRemoteRequest)(nil), // 70: volume_server_pb.VolumeTierMoveDatToRemoteRequest - (*VolumeTierMoveDatToRemoteResponse)(nil), // 71: volume_server_pb.VolumeTierMoveDatToRemoteResponse - (*VolumeTierMoveDatFromRemoteRequest)(nil), // 72: volume_server_pb.VolumeTierMoveDatFromRemoteRequest - (*VolumeTierMoveDatFromRemoteResponse)(nil), // 73: volume_server_pb.VolumeTierMoveDatFromRemoteResponse - (*VolumeServerStatusRequest)(nil), // 74: volume_server_pb.VolumeServerStatusRequest - (*VolumeServerStatusResponse)(nil), // 75: volume_server_pb.VolumeServerStatusResponse - (*VolumeServerLeaveRequest)(nil), // 76: volume_server_pb.VolumeServerLeaveRequest - (*VolumeServerLeaveResponse)(nil), // 77: volume_server_pb.VolumeServerLeaveResponse - (*FetchAndWriteNeedleRequest)(nil), // 78: volume_server_pb.FetchAndWriteNeedleRequest - (*FetchAndWriteNeedleResponse)(nil), // 79: volume_server_pb.FetchAndWriteNeedleResponse - (*QueryRequest)(nil), // 80: volume_server_pb.QueryRequest - (*QueriedStripe)(nil), // 81: volume_server_pb.QueriedStripe - (*VolumeNeedleStatusRequest)(nil), // 82: volume_server_pb.VolumeNeedleStatusRequest - (*VolumeNeedleStatusResponse)(nil), // 83: volume_server_pb.VolumeNeedleStatusResponse - (*FetchAndWriteNeedleRequest_Replica)(nil), // 84: volume_server_pb.FetchAndWriteNeedleRequest.Replica - (*QueryRequest_Filter)(nil), // 85: volume_server_pb.QueryRequest.Filter - (*QueryRequest_InputSerialization)(nil), // 86: volume_server_pb.QueryRequest.InputSerialization - (*QueryRequest_OutputSerialization)(nil), // 87: volume_server_pb.QueryRequest.OutputSerialization - (*QueryRequest_InputSerialization_CSVInput)(nil), // 88: volume_server_pb.QueryRequest.InputSerialization.CSVInput - (*QueryRequest_InputSerialization_JSONInput)(nil), // 89: volume_server_pb.QueryRequest.InputSerialization.JSONInput - (*QueryRequest_InputSerialization_ParquetInput)(nil), // 90: volume_server_pb.QueryRequest.InputSerialization.ParquetInput - (*QueryRequest_OutputSerialization_CSVOutput)(nil), // 91: volume_server_pb.QueryRequest.OutputSerialization.CSVOutput - (*QueryRequest_OutputSerialization_JSONOutput)(nil), // 92: volume_server_pb.QueryRequest.OutputSerialization.JSONOutput - (*remote_pb.RemoteConf)(nil), // 93: remote_pb.RemoteConf - (*remote_pb.RemoteStorageLocation)(nil), // 94: remote_pb.RemoteStorageLocation + (*ReadAllNeedlesRequest)(nil), // 42: volume_server_pb.ReadAllNeedlesRequest + (*ReadAllNeedlesResponse)(nil), // 43: volume_server_pb.ReadAllNeedlesResponse + (*VolumeTailSenderRequest)(nil), // 44: volume_server_pb.VolumeTailSenderRequest + (*VolumeTailSenderResponse)(nil), // 45: volume_server_pb.VolumeTailSenderResponse + (*VolumeTailReceiverRequest)(nil), // 46: volume_server_pb.VolumeTailReceiverRequest + (*VolumeTailReceiverResponse)(nil), // 47: volume_server_pb.VolumeTailReceiverResponse + (*VolumeEcShardsGenerateRequest)(nil), // 48: volume_server_pb.VolumeEcShardsGenerateRequest + (*VolumeEcShardsGenerateResponse)(nil), // 49: volume_server_pb.VolumeEcShardsGenerateResponse + (*VolumeEcShardsRebuildRequest)(nil), // 50: volume_server_pb.VolumeEcShardsRebuildRequest + (*VolumeEcShardsRebuildResponse)(nil), // 51: volume_server_pb.VolumeEcShardsRebuildResponse + (*VolumeEcShardsCopyRequest)(nil), // 52: volume_server_pb.VolumeEcShardsCopyRequest + (*VolumeEcShardsCopyResponse)(nil), // 53: volume_server_pb.VolumeEcShardsCopyResponse + (*VolumeEcShardsDeleteRequest)(nil), // 54: volume_server_pb.VolumeEcShardsDeleteRequest + (*VolumeEcShardsDeleteResponse)(nil), // 55: volume_server_pb.VolumeEcShardsDeleteResponse + (*VolumeEcShardsMountRequest)(nil), // 56: volume_server_pb.VolumeEcShardsMountRequest + (*VolumeEcShardsMountResponse)(nil), // 57: volume_server_pb.VolumeEcShardsMountResponse + (*VolumeEcShardsUnmountRequest)(nil), // 58: volume_server_pb.VolumeEcShardsUnmountRequest + (*VolumeEcShardsUnmountResponse)(nil), // 59: volume_server_pb.VolumeEcShardsUnmountResponse + (*VolumeEcShardReadRequest)(nil), // 60: volume_server_pb.VolumeEcShardReadRequest + (*VolumeEcShardReadResponse)(nil), // 61: volume_server_pb.VolumeEcShardReadResponse + (*VolumeEcBlobDeleteRequest)(nil), // 62: volume_server_pb.VolumeEcBlobDeleteRequest + (*VolumeEcBlobDeleteResponse)(nil), // 63: volume_server_pb.VolumeEcBlobDeleteResponse + (*VolumeEcShardsToVolumeRequest)(nil), // 64: volume_server_pb.VolumeEcShardsToVolumeRequest + (*VolumeEcShardsToVolumeResponse)(nil), // 65: volume_server_pb.VolumeEcShardsToVolumeResponse + (*ReadVolumeFileStatusRequest)(nil), // 66: volume_server_pb.ReadVolumeFileStatusRequest + (*ReadVolumeFileStatusResponse)(nil), // 67: volume_server_pb.ReadVolumeFileStatusResponse + (*DiskStatus)(nil), // 68: volume_server_pb.DiskStatus + (*MemStatus)(nil), // 69: volume_server_pb.MemStatus + (*RemoteFile)(nil), // 70: volume_server_pb.RemoteFile + (*VolumeInfo)(nil), // 71: volume_server_pb.VolumeInfo + (*VolumeTierMoveDatToRemoteRequest)(nil), // 72: volume_server_pb.VolumeTierMoveDatToRemoteRequest + (*VolumeTierMoveDatToRemoteResponse)(nil), // 73: volume_server_pb.VolumeTierMoveDatToRemoteResponse + (*VolumeTierMoveDatFromRemoteRequest)(nil), // 74: volume_server_pb.VolumeTierMoveDatFromRemoteRequest + (*VolumeTierMoveDatFromRemoteResponse)(nil), // 75: volume_server_pb.VolumeTierMoveDatFromRemoteResponse + (*VolumeServerStatusRequest)(nil), // 76: volume_server_pb.VolumeServerStatusRequest + (*VolumeServerStatusResponse)(nil), // 77: volume_server_pb.VolumeServerStatusResponse + (*VolumeServerLeaveRequest)(nil), // 78: volume_server_pb.VolumeServerLeaveRequest + (*VolumeServerLeaveResponse)(nil), // 79: volume_server_pb.VolumeServerLeaveResponse + (*FetchAndWriteNeedleRequest)(nil), // 80: volume_server_pb.FetchAndWriteNeedleRequest + (*FetchAndWriteNeedleResponse)(nil), // 81: volume_server_pb.FetchAndWriteNeedleResponse + (*QueryRequest)(nil), // 82: volume_server_pb.QueryRequest + (*QueriedStripe)(nil), // 83: volume_server_pb.QueriedStripe + (*VolumeNeedleStatusRequest)(nil), // 84: volume_server_pb.VolumeNeedleStatusRequest + (*VolumeNeedleStatusResponse)(nil), // 85: volume_server_pb.VolumeNeedleStatusResponse + (*FetchAndWriteNeedleRequest_Replica)(nil), // 86: volume_server_pb.FetchAndWriteNeedleRequest.Replica + (*QueryRequest_Filter)(nil), // 87: volume_server_pb.QueryRequest.Filter + (*QueryRequest_InputSerialization)(nil), // 88: volume_server_pb.QueryRequest.InputSerialization + (*QueryRequest_OutputSerialization)(nil), // 89: volume_server_pb.QueryRequest.OutputSerialization + (*QueryRequest_InputSerialization_CSVInput)(nil), // 90: volume_server_pb.QueryRequest.InputSerialization.CSVInput + (*QueryRequest_InputSerialization_JSONInput)(nil), // 91: volume_server_pb.QueryRequest.InputSerialization.JSONInput + (*QueryRequest_InputSerialization_ParquetInput)(nil), // 92: volume_server_pb.QueryRequest.InputSerialization.ParquetInput + (*QueryRequest_OutputSerialization_CSVOutput)(nil), // 93: volume_server_pb.QueryRequest.OutputSerialization.CSVOutput + (*QueryRequest_OutputSerialization_JSONOutput)(nil), // 94: volume_server_pb.QueryRequest.OutputSerialization.JSONOutput + (*remote_pb.RemoteConf)(nil), // 95: remote_pb.RemoteConf + (*remote_pb.RemoteStorageLocation)(nil), // 96: remote_pb.RemoteStorageLocation } var file_volume_server_proto_depIdxs = []int32{ 2, // 0: volume_server_pb.BatchDeleteResponse.results:type_name -> volume_server_pb.DeleteResult - 68, // 1: volume_server_pb.VolumeInfo.files:type_name -> volume_server_pb.RemoteFile - 66, // 2: volume_server_pb.VolumeServerStatusResponse.disk_statuses:type_name -> volume_server_pb.DiskStatus - 67, // 3: volume_server_pb.VolumeServerStatusResponse.memory_status:type_name -> volume_server_pb.MemStatus - 84, // 4: volume_server_pb.FetchAndWriteNeedleRequest.replicas:type_name -> volume_server_pb.FetchAndWriteNeedleRequest.Replica - 93, // 5: volume_server_pb.FetchAndWriteNeedleRequest.remote_conf:type_name -> remote_pb.RemoteConf - 94, // 6: volume_server_pb.FetchAndWriteNeedleRequest.remote_location:type_name -> remote_pb.RemoteStorageLocation - 85, // 7: volume_server_pb.QueryRequest.filter:type_name -> volume_server_pb.QueryRequest.Filter - 86, // 8: volume_server_pb.QueryRequest.input_serialization:type_name -> volume_server_pb.QueryRequest.InputSerialization - 87, // 9: volume_server_pb.QueryRequest.output_serialization:type_name -> volume_server_pb.QueryRequest.OutputSerialization - 88, // 10: volume_server_pb.QueryRequest.InputSerialization.csv_input:type_name -> volume_server_pb.QueryRequest.InputSerialization.CSVInput - 89, // 11: volume_server_pb.QueryRequest.InputSerialization.json_input:type_name -> volume_server_pb.QueryRequest.InputSerialization.JSONInput - 90, // 12: volume_server_pb.QueryRequest.InputSerialization.parquet_input:type_name -> volume_server_pb.QueryRequest.InputSerialization.ParquetInput - 91, // 13: volume_server_pb.QueryRequest.OutputSerialization.csv_output:type_name -> volume_server_pb.QueryRequest.OutputSerialization.CSVOutput - 92, // 14: volume_server_pb.QueryRequest.OutputSerialization.json_output:type_name -> volume_server_pb.QueryRequest.OutputSerialization.JSONOutput + 70, // 1: volume_server_pb.VolumeInfo.files:type_name -> volume_server_pb.RemoteFile + 68, // 2: volume_server_pb.VolumeServerStatusResponse.disk_statuses:type_name -> volume_server_pb.DiskStatus + 69, // 3: volume_server_pb.VolumeServerStatusResponse.memory_status:type_name -> volume_server_pb.MemStatus + 86, // 4: volume_server_pb.FetchAndWriteNeedleRequest.replicas:type_name -> volume_server_pb.FetchAndWriteNeedleRequest.Replica + 95, // 5: volume_server_pb.FetchAndWriteNeedleRequest.remote_conf:type_name -> remote_pb.RemoteConf + 96, // 6: volume_server_pb.FetchAndWriteNeedleRequest.remote_location:type_name -> remote_pb.RemoteStorageLocation + 87, // 7: volume_server_pb.QueryRequest.filter:type_name -> volume_server_pb.QueryRequest.Filter + 88, // 8: volume_server_pb.QueryRequest.input_serialization:type_name -> volume_server_pb.QueryRequest.InputSerialization + 89, // 9: volume_server_pb.QueryRequest.output_serialization:type_name -> volume_server_pb.QueryRequest.OutputSerialization + 90, // 10: volume_server_pb.QueryRequest.InputSerialization.csv_input:type_name -> volume_server_pb.QueryRequest.InputSerialization.CSVInput + 91, // 11: volume_server_pb.QueryRequest.InputSerialization.json_input:type_name -> volume_server_pb.QueryRequest.InputSerialization.JSONInput + 92, // 12: volume_server_pb.QueryRequest.InputSerialization.parquet_input:type_name -> volume_server_pb.QueryRequest.InputSerialization.ParquetInput + 93, // 13: volume_server_pb.QueryRequest.OutputSerialization.csv_output:type_name -> volume_server_pb.QueryRequest.OutputSerialization.CSVOutput + 94, // 14: volume_server_pb.QueryRequest.OutputSerialization.json_output:type_name -> volume_server_pb.QueryRequest.OutputSerialization.JSONOutput 0, // 15: volume_server_pb.VolumeServer.BatchDelete:input_type -> volume_server_pb.BatchDeleteRequest 4, // 16: volume_server_pb.VolumeServer.VacuumVolumeCheck:input_type -> volume_server_pb.VacuumVolumeCheckRequest 6, // 17: volume_server_pb.VolumeServer.VacuumVolumeCompact:input_type -> volume_server_pb.VacuumVolumeCompactRequest @@ -6345,69 +6464,71 @@ var file_volume_server_proto_depIdxs = []int32{ 30, // 29: volume_server_pb.VolumeServer.VolumeConfigure:input_type -> volume_server_pb.VolumeConfigureRequest 32, // 30: volume_server_pb.VolumeServer.VolumeStatus:input_type -> volume_server_pb.VolumeStatusRequest 34, // 31: volume_server_pb.VolumeServer.VolumeCopy:input_type -> volume_server_pb.VolumeCopyRequest - 64, // 32: volume_server_pb.VolumeServer.ReadVolumeFileStatus:input_type -> volume_server_pb.ReadVolumeFileStatusRequest + 66, // 32: volume_server_pb.VolumeServer.ReadVolumeFileStatus:input_type -> volume_server_pb.ReadVolumeFileStatusRequest 36, // 33: volume_server_pb.VolumeServer.CopyFile:input_type -> volume_server_pb.CopyFileRequest 38, // 34: volume_server_pb.VolumeServer.ReadNeedleBlob:input_type -> volume_server_pb.ReadNeedleBlobRequest 40, // 35: volume_server_pb.VolumeServer.WriteNeedleBlob:input_type -> volume_server_pb.WriteNeedleBlobRequest - 42, // 36: volume_server_pb.VolumeServer.VolumeTailSender:input_type -> volume_server_pb.VolumeTailSenderRequest - 44, // 37: volume_server_pb.VolumeServer.VolumeTailReceiver:input_type -> volume_server_pb.VolumeTailReceiverRequest - 46, // 38: volume_server_pb.VolumeServer.VolumeEcShardsGenerate:input_type -> volume_server_pb.VolumeEcShardsGenerateRequest - 48, // 39: volume_server_pb.VolumeServer.VolumeEcShardsRebuild:input_type -> volume_server_pb.VolumeEcShardsRebuildRequest - 50, // 40: volume_server_pb.VolumeServer.VolumeEcShardsCopy:input_type -> volume_server_pb.VolumeEcShardsCopyRequest - 52, // 41: volume_server_pb.VolumeServer.VolumeEcShardsDelete:input_type -> volume_server_pb.VolumeEcShardsDeleteRequest - 54, // 42: volume_server_pb.VolumeServer.VolumeEcShardsMount:input_type -> volume_server_pb.VolumeEcShardsMountRequest - 56, // 43: volume_server_pb.VolumeServer.VolumeEcShardsUnmount:input_type -> volume_server_pb.VolumeEcShardsUnmountRequest - 58, // 44: volume_server_pb.VolumeServer.VolumeEcShardRead:input_type -> volume_server_pb.VolumeEcShardReadRequest - 60, // 45: volume_server_pb.VolumeServer.VolumeEcBlobDelete:input_type -> volume_server_pb.VolumeEcBlobDeleteRequest - 62, // 46: volume_server_pb.VolumeServer.VolumeEcShardsToVolume:input_type -> volume_server_pb.VolumeEcShardsToVolumeRequest - 70, // 47: volume_server_pb.VolumeServer.VolumeTierMoveDatToRemote:input_type -> volume_server_pb.VolumeTierMoveDatToRemoteRequest - 72, // 48: volume_server_pb.VolumeServer.VolumeTierMoveDatFromRemote:input_type -> volume_server_pb.VolumeTierMoveDatFromRemoteRequest - 74, // 49: volume_server_pb.VolumeServer.VolumeServerStatus:input_type -> volume_server_pb.VolumeServerStatusRequest - 76, // 50: volume_server_pb.VolumeServer.VolumeServerLeave:input_type -> volume_server_pb.VolumeServerLeaveRequest - 78, // 51: volume_server_pb.VolumeServer.FetchAndWriteNeedle:input_type -> volume_server_pb.FetchAndWriteNeedleRequest - 80, // 52: volume_server_pb.VolumeServer.Query:input_type -> volume_server_pb.QueryRequest - 82, // 53: volume_server_pb.VolumeServer.VolumeNeedleStatus:input_type -> volume_server_pb.VolumeNeedleStatusRequest - 1, // 54: volume_server_pb.VolumeServer.BatchDelete:output_type -> volume_server_pb.BatchDeleteResponse - 5, // 55: volume_server_pb.VolumeServer.VacuumVolumeCheck:output_type -> volume_server_pb.VacuumVolumeCheckResponse - 7, // 56: volume_server_pb.VolumeServer.VacuumVolumeCompact:output_type -> volume_server_pb.VacuumVolumeCompactResponse - 9, // 57: volume_server_pb.VolumeServer.VacuumVolumeCommit:output_type -> volume_server_pb.VacuumVolumeCommitResponse - 11, // 58: volume_server_pb.VolumeServer.VacuumVolumeCleanup:output_type -> volume_server_pb.VacuumVolumeCleanupResponse - 13, // 59: volume_server_pb.VolumeServer.DeleteCollection:output_type -> volume_server_pb.DeleteCollectionResponse - 15, // 60: volume_server_pb.VolumeServer.AllocateVolume:output_type -> volume_server_pb.AllocateVolumeResponse - 17, // 61: volume_server_pb.VolumeServer.VolumeSyncStatus:output_type -> volume_server_pb.VolumeSyncStatusResponse - 19, // 62: volume_server_pb.VolumeServer.VolumeIncrementalCopy:output_type -> volume_server_pb.VolumeIncrementalCopyResponse - 21, // 63: volume_server_pb.VolumeServer.VolumeMount:output_type -> volume_server_pb.VolumeMountResponse - 23, // 64: volume_server_pb.VolumeServer.VolumeUnmount:output_type -> volume_server_pb.VolumeUnmountResponse - 25, // 65: volume_server_pb.VolumeServer.VolumeDelete:output_type -> volume_server_pb.VolumeDeleteResponse - 27, // 66: volume_server_pb.VolumeServer.VolumeMarkReadonly:output_type -> volume_server_pb.VolumeMarkReadonlyResponse - 29, // 67: volume_server_pb.VolumeServer.VolumeMarkWritable:output_type -> volume_server_pb.VolumeMarkWritableResponse - 31, // 68: volume_server_pb.VolumeServer.VolumeConfigure:output_type -> volume_server_pb.VolumeConfigureResponse - 33, // 69: volume_server_pb.VolumeServer.VolumeStatus:output_type -> volume_server_pb.VolumeStatusResponse - 35, // 70: volume_server_pb.VolumeServer.VolumeCopy:output_type -> volume_server_pb.VolumeCopyResponse - 65, // 71: volume_server_pb.VolumeServer.ReadVolumeFileStatus:output_type -> volume_server_pb.ReadVolumeFileStatusResponse - 37, // 72: volume_server_pb.VolumeServer.CopyFile:output_type -> volume_server_pb.CopyFileResponse - 39, // 73: volume_server_pb.VolumeServer.ReadNeedleBlob:output_type -> volume_server_pb.ReadNeedleBlobResponse - 41, // 74: volume_server_pb.VolumeServer.WriteNeedleBlob:output_type -> volume_server_pb.WriteNeedleBlobResponse - 43, // 75: volume_server_pb.VolumeServer.VolumeTailSender:output_type -> volume_server_pb.VolumeTailSenderResponse - 45, // 76: volume_server_pb.VolumeServer.VolumeTailReceiver:output_type -> volume_server_pb.VolumeTailReceiverResponse - 47, // 77: volume_server_pb.VolumeServer.VolumeEcShardsGenerate:output_type -> volume_server_pb.VolumeEcShardsGenerateResponse - 49, // 78: volume_server_pb.VolumeServer.VolumeEcShardsRebuild:output_type -> volume_server_pb.VolumeEcShardsRebuildResponse - 51, // 79: volume_server_pb.VolumeServer.VolumeEcShardsCopy:output_type -> volume_server_pb.VolumeEcShardsCopyResponse - 53, // 80: volume_server_pb.VolumeServer.VolumeEcShardsDelete:output_type -> volume_server_pb.VolumeEcShardsDeleteResponse - 55, // 81: volume_server_pb.VolumeServer.VolumeEcShardsMount:output_type -> volume_server_pb.VolumeEcShardsMountResponse - 57, // 82: volume_server_pb.VolumeServer.VolumeEcShardsUnmount:output_type -> volume_server_pb.VolumeEcShardsUnmountResponse - 59, // 83: volume_server_pb.VolumeServer.VolumeEcShardRead:output_type -> volume_server_pb.VolumeEcShardReadResponse - 61, // 84: volume_server_pb.VolumeServer.VolumeEcBlobDelete:output_type -> volume_server_pb.VolumeEcBlobDeleteResponse - 63, // 85: volume_server_pb.VolumeServer.VolumeEcShardsToVolume:output_type -> volume_server_pb.VolumeEcShardsToVolumeResponse - 71, // 86: volume_server_pb.VolumeServer.VolumeTierMoveDatToRemote:output_type -> volume_server_pb.VolumeTierMoveDatToRemoteResponse - 73, // 87: volume_server_pb.VolumeServer.VolumeTierMoveDatFromRemote:output_type -> volume_server_pb.VolumeTierMoveDatFromRemoteResponse - 75, // 88: volume_server_pb.VolumeServer.VolumeServerStatus:output_type -> volume_server_pb.VolumeServerStatusResponse - 77, // 89: volume_server_pb.VolumeServer.VolumeServerLeave:output_type -> volume_server_pb.VolumeServerLeaveResponse - 79, // 90: volume_server_pb.VolumeServer.FetchAndWriteNeedle:output_type -> volume_server_pb.FetchAndWriteNeedleResponse - 81, // 91: volume_server_pb.VolumeServer.Query:output_type -> volume_server_pb.QueriedStripe - 83, // 92: volume_server_pb.VolumeServer.VolumeNeedleStatus:output_type -> volume_server_pb.VolumeNeedleStatusResponse - 54, // [54:93] is the sub-list for method output_type - 15, // [15:54] is the sub-list for method input_type + 42, // 36: volume_server_pb.VolumeServer.ReadAllNeedles:input_type -> volume_server_pb.ReadAllNeedlesRequest + 44, // 37: volume_server_pb.VolumeServer.VolumeTailSender:input_type -> volume_server_pb.VolumeTailSenderRequest + 46, // 38: volume_server_pb.VolumeServer.VolumeTailReceiver:input_type -> volume_server_pb.VolumeTailReceiverRequest + 48, // 39: volume_server_pb.VolumeServer.VolumeEcShardsGenerate:input_type -> volume_server_pb.VolumeEcShardsGenerateRequest + 50, // 40: volume_server_pb.VolumeServer.VolumeEcShardsRebuild:input_type -> volume_server_pb.VolumeEcShardsRebuildRequest + 52, // 41: volume_server_pb.VolumeServer.VolumeEcShardsCopy:input_type -> volume_server_pb.VolumeEcShardsCopyRequest + 54, // 42: volume_server_pb.VolumeServer.VolumeEcShardsDelete:input_type -> volume_server_pb.VolumeEcShardsDeleteRequest + 56, // 43: volume_server_pb.VolumeServer.VolumeEcShardsMount:input_type -> volume_server_pb.VolumeEcShardsMountRequest + 58, // 44: volume_server_pb.VolumeServer.VolumeEcShardsUnmount:input_type -> volume_server_pb.VolumeEcShardsUnmountRequest + 60, // 45: volume_server_pb.VolumeServer.VolumeEcShardRead:input_type -> volume_server_pb.VolumeEcShardReadRequest + 62, // 46: volume_server_pb.VolumeServer.VolumeEcBlobDelete:input_type -> volume_server_pb.VolumeEcBlobDeleteRequest + 64, // 47: volume_server_pb.VolumeServer.VolumeEcShardsToVolume:input_type -> volume_server_pb.VolumeEcShardsToVolumeRequest + 72, // 48: volume_server_pb.VolumeServer.VolumeTierMoveDatToRemote:input_type -> volume_server_pb.VolumeTierMoveDatToRemoteRequest + 74, // 49: volume_server_pb.VolumeServer.VolumeTierMoveDatFromRemote:input_type -> volume_server_pb.VolumeTierMoveDatFromRemoteRequest + 76, // 50: volume_server_pb.VolumeServer.VolumeServerStatus:input_type -> volume_server_pb.VolumeServerStatusRequest + 78, // 51: volume_server_pb.VolumeServer.VolumeServerLeave:input_type -> volume_server_pb.VolumeServerLeaveRequest + 80, // 52: volume_server_pb.VolumeServer.FetchAndWriteNeedle:input_type -> volume_server_pb.FetchAndWriteNeedleRequest + 82, // 53: volume_server_pb.VolumeServer.Query:input_type -> volume_server_pb.QueryRequest + 84, // 54: volume_server_pb.VolumeServer.VolumeNeedleStatus:input_type -> volume_server_pb.VolumeNeedleStatusRequest + 1, // 55: volume_server_pb.VolumeServer.BatchDelete:output_type -> volume_server_pb.BatchDeleteResponse + 5, // 56: volume_server_pb.VolumeServer.VacuumVolumeCheck:output_type -> volume_server_pb.VacuumVolumeCheckResponse + 7, // 57: volume_server_pb.VolumeServer.VacuumVolumeCompact:output_type -> volume_server_pb.VacuumVolumeCompactResponse + 9, // 58: volume_server_pb.VolumeServer.VacuumVolumeCommit:output_type -> volume_server_pb.VacuumVolumeCommitResponse + 11, // 59: volume_server_pb.VolumeServer.VacuumVolumeCleanup:output_type -> volume_server_pb.VacuumVolumeCleanupResponse + 13, // 60: volume_server_pb.VolumeServer.DeleteCollection:output_type -> volume_server_pb.DeleteCollectionResponse + 15, // 61: volume_server_pb.VolumeServer.AllocateVolume:output_type -> volume_server_pb.AllocateVolumeResponse + 17, // 62: volume_server_pb.VolumeServer.VolumeSyncStatus:output_type -> volume_server_pb.VolumeSyncStatusResponse + 19, // 63: volume_server_pb.VolumeServer.VolumeIncrementalCopy:output_type -> volume_server_pb.VolumeIncrementalCopyResponse + 21, // 64: volume_server_pb.VolumeServer.VolumeMount:output_type -> volume_server_pb.VolumeMountResponse + 23, // 65: volume_server_pb.VolumeServer.VolumeUnmount:output_type -> volume_server_pb.VolumeUnmountResponse + 25, // 66: volume_server_pb.VolumeServer.VolumeDelete:output_type -> volume_server_pb.VolumeDeleteResponse + 27, // 67: volume_server_pb.VolumeServer.VolumeMarkReadonly:output_type -> volume_server_pb.VolumeMarkReadonlyResponse + 29, // 68: volume_server_pb.VolumeServer.VolumeMarkWritable:output_type -> volume_server_pb.VolumeMarkWritableResponse + 31, // 69: volume_server_pb.VolumeServer.VolumeConfigure:output_type -> volume_server_pb.VolumeConfigureResponse + 33, // 70: volume_server_pb.VolumeServer.VolumeStatus:output_type -> volume_server_pb.VolumeStatusResponse + 35, // 71: volume_server_pb.VolumeServer.VolumeCopy:output_type -> volume_server_pb.VolumeCopyResponse + 67, // 72: volume_server_pb.VolumeServer.ReadVolumeFileStatus:output_type -> volume_server_pb.ReadVolumeFileStatusResponse + 37, // 73: volume_server_pb.VolumeServer.CopyFile:output_type -> volume_server_pb.CopyFileResponse + 39, // 74: volume_server_pb.VolumeServer.ReadNeedleBlob:output_type -> volume_server_pb.ReadNeedleBlobResponse + 41, // 75: volume_server_pb.VolumeServer.WriteNeedleBlob:output_type -> volume_server_pb.WriteNeedleBlobResponse + 43, // 76: volume_server_pb.VolumeServer.ReadAllNeedles:output_type -> volume_server_pb.ReadAllNeedlesResponse + 45, // 77: volume_server_pb.VolumeServer.VolumeTailSender:output_type -> volume_server_pb.VolumeTailSenderResponse + 47, // 78: volume_server_pb.VolumeServer.VolumeTailReceiver:output_type -> volume_server_pb.VolumeTailReceiverResponse + 49, // 79: volume_server_pb.VolumeServer.VolumeEcShardsGenerate:output_type -> volume_server_pb.VolumeEcShardsGenerateResponse + 51, // 80: volume_server_pb.VolumeServer.VolumeEcShardsRebuild:output_type -> volume_server_pb.VolumeEcShardsRebuildResponse + 53, // 81: volume_server_pb.VolumeServer.VolumeEcShardsCopy:output_type -> volume_server_pb.VolumeEcShardsCopyResponse + 55, // 82: volume_server_pb.VolumeServer.VolumeEcShardsDelete:output_type -> volume_server_pb.VolumeEcShardsDeleteResponse + 57, // 83: volume_server_pb.VolumeServer.VolumeEcShardsMount:output_type -> volume_server_pb.VolumeEcShardsMountResponse + 59, // 84: volume_server_pb.VolumeServer.VolumeEcShardsUnmount:output_type -> volume_server_pb.VolumeEcShardsUnmountResponse + 61, // 85: volume_server_pb.VolumeServer.VolumeEcShardRead:output_type -> volume_server_pb.VolumeEcShardReadResponse + 63, // 86: volume_server_pb.VolumeServer.VolumeEcBlobDelete:output_type -> volume_server_pb.VolumeEcBlobDeleteResponse + 65, // 87: volume_server_pb.VolumeServer.VolumeEcShardsToVolume:output_type -> volume_server_pb.VolumeEcShardsToVolumeResponse + 73, // 88: volume_server_pb.VolumeServer.VolumeTierMoveDatToRemote:output_type -> volume_server_pb.VolumeTierMoveDatToRemoteResponse + 75, // 89: volume_server_pb.VolumeServer.VolumeTierMoveDatFromRemote:output_type -> volume_server_pb.VolumeTierMoveDatFromRemoteResponse + 77, // 90: volume_server_pb.VolumeServer.VolumeServerStatus:output_type -> volume_server_pb.VolumeServerStatusResponse + 79, // 91: volume_server_pb.VolumeServer.VolumeServerLeave:output_type -> volume_server_pb.VolumeServerLeaveResponse + 81, // 92: volume_server_pb.VolumeServer.FetchAndWriteNeedle:output_type -> volume_server_pb.FetchAndWriteNeedleResponse + 83, // 93: volume_server_pb.VolumeServer.Query:output_type -> volume_server_pb.QueriedStripe + 85, // 94: volume_server_pb.VolumeServer.VolumeNeedleStatus:output_type -> volume_server_pb.VolumeNeedleStatusResponse + 55, // [55:95] is the sub-list for method output_type + 15, // [15:55] is the sub-list for method input_type 15, // [15:15] is the sub-list for extension type_name 15, // [15:15] is the sub-list for extension extendee 0, // [0:15] is the sub-list for field type_name @@ -6924,7 +7045,7 @@ func file_volume_server_proto_init() { } } file_volume_server_proto_msgTypes[42].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*VolumeTailSenderRequest); i { + switch v := v.(*ReadAllNeedlesRequest); i { case 0: return &v.state case 1: @@ -6936,7 +7057,7 @@ func file_volume_server_proto_init() { } } file_volume_server_proto_msgTypes[43].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*VolumeTailSenderResponse); i { + switch v := v.(*ReadAllNeedlesResponse); i { case 0: return &v.state case 1: @@ -6948,7 +7069,7 @@ func file_volume_server_proto_init() { } } file_volume_server_proto_msgTypes[44].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*VolumeTailReceiverRequest); i { + switch v := v.(*VolumeTailSenderRequest); i { case 0: return &v.state case 1: @@ -6960,7 +7081,7 @@ func file_volume_server_proto_init() { } } file_volume_server_proto_msgTypes[45].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*VolumeTailReceiverResponse); i { + switch v := v.(*VolumeTailSenderResponse); i { case 0: return &v.state case 1: @@ -6972,7 +7093,7 @@ func file_volume_server_proto_init() { } } file_volume_server_proto_msgTypes[46].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*VolumeEcShardsGenerateRequest); i { + switch v := v.(*VolumeTailReceiverRequest); i { case 0: return &v.state case 1: @@ -6984,7 +7105,7 @@ func file_volume_server_proto_init() { } } file_volume_server_proto_msgTypes[47].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*VolumeEcShardsGenerateResponse); i { + switch v := v.(*VolumeTailReceiverResponse); i { case 0: return &v.state case 1: @@ -6996,7 +7117,7 @@ func file_volume_server_proto_init() { } } file_volume_server_proto_msgTypes[48].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*VolumeEcShardsRebuildRequest); i { + switch v := v.(*VolumeEcShardsGenerateRequest); i { case 0: return &v.state case 1: @@ -7008,7 +7129,7 @@ func file_volume_server_proto_init() { } } file_volume_server_proto_msgTypes[49].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*VolumeEcShardsRebuildResponse); i { + switch v := v.(*VolumeEcShardsGenerateResponse); i { case 0: return &v.state case 1: @@ -7020,7 +7141,7 @@ func file_volume_server_proto_init() { } } file_volume_server_proto_msgTypes[50].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*VolumeEcShardsCopyRequest); i { + switch v := v.(*VolumeEcShardsRebuildRequest); i { case 0: return &v.state case 1: @@ -7032,7 +7153,7 @@ func file_volume_server_proto_init() { } } file_volume_server_proto_msgTypes[51].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*VolumeEcShardsCopyResponse); i { + switch v := v.(*VolumeEcShardsRebuildResponse); i { case 0: return &v.state case 1: @@ -7044,7 +7165,7 @@ func file_volume_server_proto_init() { } } file_volume_server_proto_msgTypes[52].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*VolumeEcShardsDeleteRequest); i { + switch v := v.(*VolumeEcShardsCopyRequest); i { case 0: return &v.state case 1: @@ -7056,7 +7177,7 @@ func file_volume_server_proto_init() { } } file_volume_server_proto_msgTypes[53].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*VolumeEcShardsDeleteResponse); i { + switch v := v.(*VolumeEcShardsCopyResponse); i { case 0: return &v.state case 1: @@ -7068,7 +7189,7 @@ func file_volume_server_proto_init() { } } file_volume_server_proto_msgTypes[54].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*VolumeEcShardsMountRequest); i { + switch v := v.(*VolumeEcShardsDeleteRequest); i { case 0: return &v.state case 1: @@ -7080,7 +7201,7 @@ func file_volume_server_proto_init() { } } file_volume_server_proto_msgTypes[55].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*VolumeEcShardsMountResponse); i { + switch v := v.(*VolumeEcShardsDeleteResponse); i { case 0: return &v.state case 1: @@ -7092,7 +7213,7 @@ func file_volume_server_proto_init() { } } file_volume_server_proto_msgTypes[56].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*VolumeEcShardsUnmountRequest); i { + switch v := v.(*VolumeEcShardsMountRequest); i { case 0: return &v.state case 1: @@ -7104,7 +7225,7 @@ func file_volume_server_proto_init() { } } file_volume_server_proto_msgTypes[57].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*VolumeEcShardsUnmountResponse); i { + switch v := v.(*VolumeEcShardsMountResponse); i { case 0: return &v.state case 1: @@ -7116,7 +7237,7 @@ func file_volume_server_proto_init() { } } file_volume_server_proto_msgTypes[58].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*VolumeEcShardReadRequest); i { + switch v := v.(*VolumeEcShardsUnmountRequest); i { case 0: return &v.state case 1: @@ -7128,7 +7249,7 @@ func file_volume_server_proto_init() { } } file_volume_server_proto_msgTypes[59].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*VolumeEcShardReadResponse); i { + switch v := v.(*VolumeEcShardsUnmountResponse); i { case 0: return &v.state case 1: @@ -7140,7 +7261,7 @@ func file_volume_server_proto_init() { } } file_volume_server_proto_msgTypes[60].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*VolumeEcBlobDeleteRequest); i { + switch v := v.(*VolumeEcShardReadRequest); i { case 0: return &v.state case 1: @@ -7152,7 +7273,7 @@ func file_volume_server_proto_init() { } } file_volume_server_proto_msgTypes[61].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*VolumeEcBlobDeleteResponse); i { + switch v := v.(*VolumeEcShardReadResponse); i { case 0: return &v.state case 1: @@ -7164,7 +7285,7 @@ func file_volume_server_proto_init() { } } file_volume_server_proto_msgTypes[62].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*VolumeEcShardsToVolumeRequest); i { + switch v := v.(*VolumeEcBlobDeleteRequest); i { case 0: return &v.state case 1: @@ -7176,7 +7297,7 @@ func file_volume_server_proto_init() { } } file_volume_server_proto_msgTypes[63].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*VolumeEcShardsToVolumeResponse); i { + switch v := v.(*VolumeEcBlobDeleteResponse); i { case 0: return &v.state case 1: @@ -7188,7 +7309,7 @@ func file_volume_server_proto_init() { } } file_volume_server_proto_msgTypes[64].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ReadVolumeFileStatusRequest); i { + switch v := v.(*VolumeEcShardsToVolumeRequest); i { case 0: return &v.state case 1: @@ -7200,7 +7321,7 @@ func file_volume_server_proto_init() { } } file_volume_server_proto_msgTypes[65].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ReadVolumeFileStatusResponse); i { + switch v := v.(*VolumeEcShardsToVolumeResponse); i { case 0: return &v.state case 1: @@ -7212,7 +7333,7 @@ func file_volume_server_proto_init() { } } file_volume_server_proto_msgTypes[66].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*DiskStatus); i { + switch v := v.(*ReadVolumeFileStatusRequest); i { case 0: return &v.state case 1: @@ -7224,7 +7345,7 @@ func file_volume_server_proto_init() { } } file_volume_server_proto_msgTypes[67].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*MemStatus); i { + switch v := v.(*ReadVolumeFileStatusResponse); i { case 0: return &v.state case 1: @@ -7236,7 +7357,7 @@ func file_volume_server_proto_init() { } } file_volume_server_proto_msgTypes[68].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*RemoteFile); i { + switch v := v.(*DiskStatus); i { case 0: return &v.state case 1: @@ -7248,7 +7369,7 @@ func file_volume_server_proto_init() { } } file_volume_server_proto_msgTypes[69].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*VolumeInfo); i { + switch v := v.(*MemStatus); i { case 0: return &v.state case 1: @@ -7260,7 +7381,7 @@ func file_volume_server_proto_init() { } } file_volume_server_proto_msgTypes[70].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*VolumeTierMoveDatToRemoteRequest); i { + switch v := v.(*RemoteFile); i { case 0: return &v.state case 1: @@ -7272,7 +7393,7 @@ func file_volume_server_proto_init() { } } file_volume_server_proto_msgTypes[71].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*VolumeTierMoveDatToRemoteResponse); i { + switch v := v.(*VolumeInfo); i { case 0: return &v.state case 1: @@ -7284,7 +7405,7 @@ func file_volume_server_proto_init() { } } file_volume_server_proto_msgTypes[72].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*VolumeTierMoveDatFromRemoteRequest); i { + switch v := v.(*VolumeTierMoveDatToRemoteRequest); i { case 0: return &v.state case 1: @@ -7296,7 +7417,7 @@ func file_volume_server_proto_init() { } } file_volume_server_proto_msgTypes[73].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*VolumeTierMoveDatFromRemoteResponse); i { + switch v := v.(*VolumeTierMoveDatToRemoteResponse); i { case 0: return &v.state case 1: @@ -7308,7 +7429,7 @@ func file_volume_server_proto_init() { } } file_volume_server_proto_msgTypes[74].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*VolumeServerStatusRequest); i { + switch v := v.(*VolumeTierMoveDatFromRemoteRequest); i { case 0: return &v.state case 1: @@ -7320,7 +7441,7 @@ func file_volume_server_proto_init() { } } file_volume_server_proto_msgTypes[75].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*VolumeServerStatusResponse); i { + switch v := v.(*VolumeTierMoveDatFromRemoteResponse); i { case 0: return &v.state case 1: @@ -7332,7 +7453,7 @@ func file_volume_server_proto_init() { } } file_volume_server_proto_msgTypes[76].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*VolumeServerLeaveRequest); i { + switch v := v.(*VolumeServerStatusRequest); i { case 0: return &v.state case 1: @@ -7344,7 +7465,7 @@ func file_volume_server_proto_init() { } } file_volume_server_proto_msgTypes[77].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*VolumeServerLeaveResponse); i { + switch v := v.(*VolumeServerStatusResponse); i { case 0: return &v.state case 1: @@ -7356,7 +7477,7 @@ func file_volume_server_proto_init() { } } file_volume_server_proto_msgTypes[78].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*FetchAndWriteNeedleRequest); i { + switch v := v.(*VolumeServerLeaveRequest); i { case 0: return &v.state case 1: @@ -7368,7 +7489,7 @@ func file_volume_server_proto_init() { } } file_volume_server_proto_msgTypes[79].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*FetchAndWriteNeedleResponse); i { + switch v := v.(*VolumeServerLeaveResponse); i { case 0: return &v.state case 1: @@ -7380,7 +7501,7 @@ func file_volume_server_proto_init() { } } file_volume_server_proto_msgTypes[80].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*QueryRequest); i { + switch v := v.(*FetchAndWriteNeedleRequest); i { case 0: return &v.state case 1: @@ -7392,7 +7513,7 @@ func file_volume_server_proto_init() { } } file_volume_server_proto_msgTypes[81].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*QueriedStripe); i { + switch v := v.(*FetchAndWriteNeedleResponse); i { case 0: return &v.state case 1: @@ -7404,7 +7525,7 @@ func file_volume_server_proto_init() { } } file_volume_server_proto_msgTypes[82].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*VolumeNeedleStatusRequest); i { + switch v := v.(*QueryRequest); i { case 0: return &v.state case 1: @@ -7416,7 +7537,7 @@ func file_volume_server_proto_init() { } } file_volume_server_proto_msgTypes[83].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*VolumeNeedleStatusResponse); i { + switch v := v.(*QueriedStripe); i { case 0: return &v.state case 1: @@ -7428,7 +7549,7 @@ func file_volume_server_proto_init() { } } file_volume_server_proto_msgTypes[84].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*FetchAndWriteNeedleRequest_Replica); i { + switch v := v.(*VolumeNeedleStatusRequest); i { case 0: return &v.state case 1: @@ -7440,7 +7561,7 @@ func file_volume_server_proto_init() { } } file_volume_server_proto_msgTypes[85].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*QueryRequest_Filter); i { + switch v := v.(*VolumeNeedleStatusResponse); i { case 0: return &v.state case 1: @@ -7452,7 +7573,7 @@ func file_volume_server_proto_init() { } } file_volume_server_proto_msgTypes[86].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*QueryRequest_InputSerialization); i { + switch v := v.(*FetchAndWriteNeedleRequest_Replica); i { case 0: return &v.state case 1: @@ -7464,7 +7585,7 @@ func file_volume_server_proto_init() { } } file_volume_server_proto_msgTypes[87].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*QueryRequest_OutputSerialization); i { + switch v := v.(*QueryRequest_Filter); i { case 0: return &v.state case 1: @@ -7476,7 +7597,7 @@ func file_volume_server_proto_init() { } } file_volume_server_proto_msgTypes[88].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*QueryRequest_InputSerialization_CSVInput); i { + switch v := v.(*QueryRequest_InputSerialization); i { case 0: return &v.state case 1: @@ -7488,7 +7609,7 @@ func file_volume_server_proto_init() { } } file_volume_server_proto_msgTypes[89].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*QueryRequest_InputSerialization_JSONInput); i { + switch v := v.(*QueryRequest_OutputSerialization); i { case 0: return &v.state case 1: @@ -7500,7 +7621,7 @@ func file_volume_server_proto_init() { } } file_volume_server_proto_msgTypes[90].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*QueryRequest_InputSerialization_ParquetInput); i { + switch v := v.(*QueryRequest_InputSerialization_CSVInput); i { case 0: return &v.state case 1: @@ -7512,7 +7633,7 @@ func file_volume_server_proto_init() { } } file_volume_server_proto_msgTypes[91].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*QueryRequest_OutputSerialization_CSVOutput); i { + switch v := v.(*QueryRequest_InputSerialization_JSONInput); i { case 0: return &v.state case 1: @@ -7524,6 +7645,30 @@ func file_volume_server_proto_init() { } } file_volume_server_proto_msgTypes[92].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*QueryRequest_InputSerialization_ParquetInput); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_volume_server_proto_msgTypes[93].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*QueryRequest_OutputSerialization_CSVOutput); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_volume_server_proto_msgTypes[94].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*QueryRequest_OutputSerialization_JSONOutput); i { case 0: return &v.state @@ -7542,7 +7687,7 @@ func file_volume_server_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_volume_server_proto_rawDesc, NumEnums: 0, - NumMessages: 93, + NumMessages: 95, NumExtensions: 0, NumServices: 1, }, @@ -7591,6 +7736,7 @@ type VolumeServerClient interface { CopyFile(ctx context.Context, in *CopyFileRequest, opts ...grpc.CallOption) (VolumeServer_CopyFileClient, error) ReadNeedleBlob(ctx context.Context, in *ReadNeedleBlobRequest, opts ...grpc.CallOption) (*ReadNeedleBlobResponse, error) WriteNeedleBlob(ctx context.Context, in *WriteNeedleBlobRequest, opts ...grpc.CallOption) (*WriteNeedleBlobResponse, error) + ReadAllNeedles(ctx context.Context, in *ReadAllNeedlesRequest, opts ...grpc.CallOption) (VolumeServer_ReadAllNeedlesClient, error) VolumeTailSender(ctx context.Context, in *VolumeTailSenderRequest, opts ...grpc.CallOption) (VolumeServer_VolumeTailSenderClient, error) VolumeTailReceiver(ctx context.Context, in *VolumeTailReceiverRequest, opts ...grpc.CallOption) (*VolumeTailReceiverResponse, error) // erasure coding @@ -7858,8 +8004,40 @@ func (c *volumeServerClient) WriteNeedleBlob(ctx context.Context, in *WriteNeedl return out, nil } +func (c *volumeServerClient) ReadAllNeedles(ctx context.Context, in *ReadAllNeedlesRequest, opts ...grpc.CallOption) (VolumeServer_ReadAllNeedlesClient, error) { + stream, err := c.cc.NewStream(ctx, &_VolumeServer_serviceDesc.Streams[2], "/volume_server_pb.VolumeServer/ReadAllNeedles", opts...) + if err != nil { + return nil, err + } + x := &volumeServerReadAllNeedlesClient{stream} + if err := x.ClientStream.SendMsg(in); err != nil { + return nil, err + } + if err := x.ClientStream.CloseSend(); err != nil { + return nil, err + } + return x, nil +} + +type VolumeServer_ReadAllNeedlesClient interface { + Recv() (*ReadAllNeedlesResponse, error) + grpc.ClientStream +} + +type volumeServerReadAllNeedlesClient struct { + grpc.ClientStream +} + +func (x *volumeServerReadAllNeedlesClient) Recv() (*ReadAllNeedlesResponse, error) { + m := new(ReadAllNeedlesResponse) + if err := x.ClientStream.RecvMsg(m); err != nil { + return nil, err + } + return m, nil +} + func (c *volumeServerClient) VolumeTailSender(ctx context.Context, in *VolumeTailSenderRequest, opts ...grpc.CallOption) (VolumeServer_VolumeTailSenderClient, error) { - stream, err := c.cc.NewStream(ctx, &_VolumeServer_serviceDesc.Streams[2], "/volume_server_pb.VolumeServer/VolumeTailSender", opts...) + stream, err := c.cc.NewStream(ctx, &_VolumeServer_serviceDesc.Streams[3], "/volume_server_pb.VolumeServer/VolumeTailSender", opts...) if err != nil { return nil, err } @@ -7954,7 +8132,7 @@ func (c *volumeServerClient) VolumeEcShardsUnmount(ctx context.Context, in *Volu } func (c *volumeServerClient) VolumeEcShardRead(ctx context.Context, in *VolumeEcShardReadRequest, opts ...grpc.CallOption) (VolumeServer_VolumeEcShardReadClient, error) { - stream, err := c.cc.NewStream(ctx, &_VolumeServer_serviceDesc.Streams[3], "/volume_server_pb.VolumeServer/VolumeEcShardRead", opts...) + stream, err := c.cc.NewStream(ctx, &_VolumeServer_serviceDesc.Streams[4], "/volume_server_pb.VolumeServer/VolumeEcShardRead", opts...) if err != nil { return nil, err } @@ -8004,7 +8182,7 @@ func (c *volumeServerClient) VolumeEcShardsToVolume(ctx context.Context, in *Vol } func (c *volumeServerClient) VolumeTierMoveDatToRemote(ctx context.Context, in *VolumeTierMoveDatToRemoteRequest, opts ...grpc.CallOption) (VolumeServer_VolumeTierMoveDatToRemoteClient, error) { - stream, err := c.cc.NewStream(ctx, &_VolumeServer_serviceDesc.Streams[4], "/volume_server_pb.VolumeServer/VolumeTierMoveDatToRemote", opts...) + stream, err := c.cc.NewStream(ctx, &_VolumeServer_serviceDesc.Streams[5], "/volume_server_pb.VolumeServer/VolumeTierMoveDatToRemote", opts...) if err != nil { return nil, err } @@ -8036,7 +8214,7 @@ func (x *volumeServerVolumeTierMoveDatToRemoteClient) Recv() (*VolumeTierMoveDat } func (c *volumeServerClient) VolumeTierMoveDatFromRemote(ctx context.Context, in *VolumeTierMoveDatFromRemoteRequest, opts ...grpc.CallOption) (VolumeServer_VolumeTierMoveDatFromRemoteClient, error) { - stream, err := c.cc.NewStream(ctx, &_VolumeServer_serviceDesc.Streams[5], "/volume_server_pb.VolumeServer/VolumeTierMoveDatFromRemote", opts...) + stream, err := c.cc.NewStream(ctx, &_VolumeServer_serviceDesc.Streams[6], "/volume_server_pb.VolumeServer/VolumeTierMoveDatFromRemote", opts...) if err != nil { return nil, err } @@ -8095,7 +8273,7 @@ func (c *volumeServerClient) FetchAndWriteNeedle(ctx context.Context, in *FetchA } func (c *volumeServerClient) Query(ctx context.Context, in *QueryRequest, opts ...grpc.CallOption) (VolumeServer_QueryClient, error) { - stream, err := c.cc.NewStream(ctx, &_VolumeServer_serviceDesc.Streams[6], "/volume_server_pb.VolumeServer/Query", opts...) + stream, err := c.cc.NewStream(ctx, &_VolumeServer_serviceDesc.Streams[7], "/volume_server_pb.VolumeServer/Query", opts...) if err != nil { return nil, err } @@ -8160,6 +8338,7 @@ type VolumeServerServer interface { CopyFile(*CopyFileRequest, VolumeServer_CopyFileServer) error ReadNeedleBlob(context.Context, *ReadNeedleBlobRequest) (*ReadNeedleBlobResponse, error) WriteNeedleBlob(context.Context, *WriteNeedleBlobRequest) (*WriteNeedleBlobResponse, error) + ReadAllNeedles(*ReadAllNeedlesRequest, VolumeServer_ReadAllNeedlesServer) error VolumeTailSender(*VolumeTailSenderRequest, VolumeServer_VolumeTailSenderServer) error VolumeTailReceiver(context.Context, *VolumeTailReceiverRequest) (*VolumeTailReceiverResponse, error) // erasure coding @@ -8251,6 +8430,9 @@ func (*UnimplementedVolumeServerServer) ReadNeedleBlob(context.Context, *ReadNee func (*UnimplementedVolumeServerServer) WriteNeedleBlob(context.Context, *WriteNeedleBlobRequest) (*WriteNeedleBlobResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method WriteNeedleBlob not implemented") } +func (*UnimplementedVolumeServerServer) ReadAllNeedles(*ReadAllNeedlesRequest, VolumeServer_ReadAllNeedlesServer) error { + return status.Errorf(codes.Unimplemented, "method ReadAllNeedles not implemented") +} func (*UnimplementedVolumeServerServer) VolumeTailSender(*VolumeTailSenderRequest, VolumeServer_VolumeTailSenderServer) error { return status.Errorf(codes.Unimplemented, "method VolumeTailSender not implemented") } @@ -8694,6 +8876,27 @@ func _VolumeServer_WriteNeedleBlob_Handler(srv interface{}, ctx context.Context, return interceptor(ctx, in, info, handler) } +func _VolumeServer_ReadAllNeedles_Handler(srv interface{}, stream grpc.ServerStream) error { + m := new(ReadAllNeedlesRequest) + if err := stream.RecvMsg(m); err != nil { + return err + } + return srv.(VolumeServerServer).ReadAllNeedles(m, &volumeServerReadAllNeedlesServer{stream}) +} + +type VolumeServer_ReadAllNeedlesServer interface { + Send(*ReadAllNeedlesResponse) error + grpc.ServerStream +} + +type volumeServerReadAllNeedlesServer struct { + grpc.ServerStream +} + +func (x *volumeServerReadAllNeedlesServer) Send(m *ReadAllNeedlesResponse) error { + return x.ServerStream.SendMsg(m) +} + func _VolumeServer_VolumeTailSender_Handler(srv interface{}, stream grpc.ServerStream) error { m := new(VolumeTailSenderRequest) if err := stream.RecvMsg(m); err != nil { @@ -9177,6 +9380,11 @@ var _VolumeServer_serviceDesc = grpc.ServiceDesc{ Handler: _VolumeServer_CopyFile_Handler, ServerStreams: true, }, + { + StreamName: "ReadAllNeedles", + Handler: _VolumeServer_ReadAllNeedles_Handler, + ServerStreams: true, + }, { StreamName: "VolumeTailSender", Handler: _VolumeServer_VolumeTailSender_Handler, diff --git a/weed/server/volume_grpc_read_all.go b/weed/server/volume_grpc_read_all.go new file mode 100644 index 000000000..f1f79a2e3 --- /dev/null +++ b/weed/server/volume_grpc_read_all.go @@ -0,0 +1,50 @@ +package weed_server + +import ( + "fmt" + "github.com/chrislusf/seaweedfs/weed/pb/volume_server_pb" + "github.com/chrislusf/seaweedfs/weed/storage" + "github.com/chrislusf/seaweedfs/weed/storage/needle" + "github.com/chrislusf/seaweedfs/weed/storage/super_block" +) + +func (vs *VolumeServer) ReadAllNeedles(req *volume_server_pb.ReadAllNeedlesRequest, stream volume_server_pb.VolumeServer_ReadAllNeedlesServer) (err error) { + + v := vs.store.GetVolume(needle.VolumeId(req.VolumeId)) + if v == nil { + return fmt.Errorf("not found volume id %d", req.VolumeId) + } + + scanner := &VolumeFileScanner4ReadAll{ + stream: stream, + } + + err = storage.ScanVolumeFileFrom(v.Version(), v.DataBackend, super_block.SuperBlockSize, scanner) + + return err + +} + +type VolumeFileScanner4ReadAll struct { + stream volume_server_pb.VolumeServer_ReadAllNeedlesServer +} + +func (scanner *VolumeFileScanner4ReadAll) VisitSuperBlock(superBlock super_block.SuperBlock) error { + return nil + +} +func (scanner *VolumeFileScanner4ReadAll) ReadNeedleBody() bool { + return true +} + +func (scanner *VolumeFileScanner4ReadAll) VisitNeedle(n *needle.Needle, offset int64, needleHeader, needleBody []byte) error { + + sendErr := scanner.stream.Send(&volume_server_pb.ReadAllNeedlesResponse{ + NeedleId: uint64(n.Id), + NeedleBlob: needleBody, + }) + if sendErr != nil { + return sendErr + } + return nil +} From aa64f2ac4cc0008b7670bd7c253d17185fd108b5 Mon Sep 17 00:00:00 2001 From: Chris Lu Date: Mon, 27 Sep 2021 01:53:41 -0700 Subject: [PATCH 055/130] send needle data instead of raw needle body --- weed/server/volume_grpc_read_all.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/weed/server/volume_grpc_read_all.go b/weed/server/volume_grpc_read_all.go index f1f79a2e3..6e7b30dbd 100644 --- a/weed/server/volume_grpc_read_all.go +++ b/weed/server/volume_grpc_read_all.go @@ -41,7 +41,7 @@ func (scanner *VolumeFileScanner4ReadAll) VisitNeedle(n *needle.Needle, offset i sendErr := scanner.stream.Send(&volume_server_pb.ReadAllNeedlesResponse{ NeedleId: uint64(n.Id), - NeedleBlob: needleBody, + NeedleBlob: n.Data, }) if sendErr != nil { return sendErr From 5956a8b05a7836907aa8fe4ae169edc8f05e9476 Mon Sep 17 00:00:00 2001 From: Chris Lu Date: Mon, 27 Sep 2021 01:58:03 -0700 Subject: [PATCH 056/130] adjust comment --- weed/storage/needle/needle.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/weed/storage/needle/needle.go b/weed/storage/needle/needle.go index 845ffdb24..7443796a7 100644 --- a/weed/storage/needle/needle.go +++ b/weed/storage/needle/needle.go @@ -31,9 +31,9 @@ type Needle struct { Data []byte `comment:"The actual file data"` Flags byte `comment:"boolean flags"` //version2 NameSize uint8 //version2 - Name []byte `comment:"maximum 256 characters"` //version2 + Name []byte `comment:"maximum 255 characters"` //version2 MimeSize uint8 //version2 - Mime []byte `comment:"maximum 256 characters"` //version2 + Mime []byte `comment:"maximum 255 characters"` //version2 PairsSize uint16 //version2 Pairs []byte `comment:"additional name value pairs, json format, maximum 64kB"` LastModified uint64 //only store LastModifiedBytesLength bytes, which is 5 bytes to disk From 1904448d4eb34b6b0ff3dc71676ec1f5f2d2cd40 Mon Sep 17 00:00:00 2001 From: Chris Lu Date: Mon, 27 Sep 2021 02:01:29 -0700 Subject: [PATCH 057/130] adjust starting offset --- weed/server/volume_grpc_read_all.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/weed/server/volume_grpc_read_all.go b/weed/server/volume_grpc_read_all.go index 6e7b30dbd..21b42d8b1 100644 --- a/weed/server/volume_grpc_read_all.go +++ b/weed/server/volume_grpc_read_all.go @@ -19,7 +19,9 @@ func (vs *VolumeServer) ReadAllNeedles(req *volume_server_pb.ReadAllNeedlesReque stream: stream, } - err = storage.ScanVolumeFileFrom(v.Version(), v.DataBackend, super_block.SuperBlockSize, scanner) + offset := int64(v.SuperBlock.BlockSize()) + + err = storage.ScanVolumeFileFrom(v.Version(), v.DataBackend, offset, scanner) return err From 225b019fe0b29c2eb073104f83bb9f14c3e345e3 Mon Sep 17 00:00:00 2001 From: Chris Lu Date: Mon, 27 Sep 2021 02:51:31 -0700 Subject: [PATCH 058/130] stream read multiple volumes in a volume server --- .../stream_read_volume/stream_read_volume.go | 64 + weed/pb/volume_server.proto | 8 +- weed/pb/volume_server_pb/volume_server.pb.go | 1398 +++++++++-------- weed/server/volume_grpc_read_all.go | 18 +- 4 files changed, 793 insertions(+), 695 deletions(-) create mode 100644 unmaintained/stream_read_volume/stream_read_volume.go diff --git a/unmaintained/stream_read_volume/stream_read_volume.go b/unmaintained/stream_read_volume/stream_read_volume.go new file mode 100644 index 000000000..e120b9920 --- /dev/null +++ b/unmaintained/stream_read_volume/stream_read_volume.go @@ -0,0 +1,64 @@ +package main + +import ( + "context" + "errors" + "flag" + "fmt" + "github.com/chrislusf/seaweedfs/weed/operation" + "github.com/chrislusf/seaweedfs/weed/pb" + "github.com/chrislusf/seaweedfs/weed/pb/volume_server_pb" + "github.com/chrislusf/seaweedfs/weed/security" + "github.com/chrislusf/seaweedfs/weed/util" + "google.golang.org/grpc" + "io" +) + +var ( + volumeServer = flag.String("volumeServer", "localhost:8080", "a volume server") + volumeId = flag.Int("volumeId", -1, "a volume id to stream read") + grpcDialOption grpc.DialOption +) + +func main() { + flag.Parse() + + util.LoadConfiguration("security", false) + grpcDialOption = security.LoadClientTLS(util.GetViper(), "grpc.client") + + vid := uint32(*volumeId) + + eachNeedleFunc := func(resp *volume_server_pb.ReadAllNeedlesResponse) error { + fmt.Printf("%d,%x%08x %d\n", resp.VolumeId, resp.NeedleId, resp.Cookie, len(resp.NeedleBlob)) + return nil + } + + err := operation.WithVolumeServerClient(pb.ServerAddress(*volumeServer), grpcDialOption, func(vs volume_server_pb.VolumeServerClient) error { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + copyFileClient, err := vs.ReadAllNeedles(ctx, &volume_server_pb.ReadAllNeedlesRequest{ + VolumeIds: []uint32{vid}, + }) + if err != nil { + return err + } + for { + resp, err := copyFileClient.Recv() + if errors.Is(err, io.EOF) { + break + } + if err != nil { + return err + } + if err = eachNeedleFunc(resp); err != nil { + return err + } + } + return nil + }) + if err != nil { + fmt.Printf("read %s: %v\n", *volumeServer, err) + } + +} + diff --git a/weed/pb/volume_server.proto b/weed/pb/volume_server.proto index c2b478a25..ff8950574 100644 --- a/weed/pb/volume_server.proto +++ b/weed/pb/volume_server.proto @@ -287,11 +287,13 @@ message WriteNeedleBlobResponse { } message ReadAllNeedlesRequest { - uint32 volume_id = 1; + repeated uint32 volume_ids = 1; } message ReadAllNeedlesResponse { - uint64 needle_id = 1; - bytes needle_blob = 2; + uint32 volume_id = 1; + uint64 needle_id = 2; + uint32 cookie = 3; + bytes needle_blob = 5; } message VolumeTailSenderRequest { diff --git a/weed/pb/volume_server_pb/volume_server.pb.go b/weed/pb/volume_server_pb/volume_server.pb.go index 2f1ddb105..5b5751ea9 100644 --- a/weed/pb/volume_server_pb/volume_server.pb.go +++ b/weed/pb/volume_server_pb/volume_server.pb.go @@ -2214,7 +2214,7 @@ type ReadAllNeedlesRequest struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - VolumeId uint32 `protobuf:"varint,1,opt,name=volume_id,json=volumeId,proto3" json:"volume_id,omitempty"` + VolumeIds []uint32 `protobuf:"varint,1,rep,packed,name=volume_ids,json=volumeIds,proto3" json:"volume_ids,omitempty"` } func (x *ReadAllNeedlesRequest) Reset() { @@ -2249,11 +2249,11 @@ func (*ReadAllNeedlesRequest) Descriptor() ([]byte, []int) { return file_volume_server_proto_rawDescGZIP(), []int{42} } -func (x *ReadAllNeedlesRequest) GetVolumeId() uint32 { +func (x *ReadAllNeedlesRequest) GetVolumeIds() []uint32 { if x != nil { - return x.VolumeId + return x.VolumeIds } - return 0 + return nil } type ReadAllNeedlesResponse struct { @@ -2261,8 +2261,10 @@ type ReadAllNeedlesResponse struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - NeedleId uint64 `protobuf:"varint,1,opt,name=needle_id,json=needleId,proto3" json:"needle_id,omitempty"` - NeedleBlob []byte `protobuf:"bytes,2,opt,name=needle_blob,json=needleBlob,proto3" json:"needle_blob,omitempty"` + VolumeId uint32 `protobuf:"varint,1,opt,name=volume_id,json=volumeId,proto3" json:"volume_id,omitempty"` + NeedleId uint64 `protobuf:"varint,2,opt,name=needle_id,json=needleId,proto3" json:"needle_id,omitempty"` + Cookie uint32 `protobuf:"varint,3,opt,name=cookie,proto3" json:"cookie,omitempty"` + NeedleBlob []byte `protobuf:"bytes,5,opt,name=needle_blob,json=needleBlob,proto3" json:"needle_blob,omitempty"` } func (x *ReadAllNeedlesResponse) Reset() { @@ -2297,6 +2299,13 @@ func (*ReadAllNeedlesResponse) Descriptor() ([]byte, []int) { return file_volume_server_proto_rawDescGZIP(), []int{43} } +func (x *ReadAllNeedlesResponse) GetVolumeId() uint32 { + if x != nil { + return x.VolumeId + } + return 0 +} + func (x *ReadAllNeedlesResponse) GetNeedleId() uint64 { if x != nil { return x.NeedleId @@ -2304,6 +2313,13 @@ func (x *ReadAllNeedlesResponse) GetNeedleId() uint64 { return 0 } +func (x *ReadAllNeedlesResponse) GetCookie() uint32 { + if x != nil { + return x.Cookie + } + return 0 +} + func (x *ReadAllNeedlesResponse) GetNeedleBlob() []byte { if x != nil { return x.NeedleBlob @@ -5615,708 +5631,712 @@ var file_volume_server_proto_rawDesc = []byte{ 0x12, 0x1f, 0x0a, 0x0b, 0x6e, 0x65, 0x65, 0x64, 0x6c, 0x65, 0x5f, 0x62, 0x6c, 0x6f, 0x62, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0a, 0x6e, 0x65, 0x65, 0x64, 0x6c, 0x65, 0x42, 0x6c, 0x6f, 0x62, 0x22, 0x19, 0x0a, 0x17, 0x57, 0x72, 0x69, 0x74, 0x65, 0x4e, 0x65, 0x65, 0x64, 0x6c, 0x65, - 0x42, 0x6c, 0x6f, 0x62, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x34, 0x0a, 0x15, + 0x42, 0x6c, 0x6f, 0x62, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x36, 0x0a, 0x15, 0x52, 0x65, 0x61, 0x64, 0x41, 0x6c, 0x6c, 0x4e, 0x65, 0x65, 0x64, 0x6c, 0x65, 0x73, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, - 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, - 0x49, 0x64, 0x22, 0x56, 0x0a, 0x16, 0x52, 0x65, 0x61, 0x64, 0x41, 0x6c, 0x6c, 0x4e, 0x65, 0x65, - 0x64, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1b, 0x0a, 0x09, - 0x6e, 0x65, 0x65, 0x64, 0x6c, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, - 0x08, 0x6e, 0x65, 0x65, 0x64, 0x6c, 0x65, 0x49, 0x64, 0x12, 0x1f, 0x0a, 0x0b, 0x6e, 0x65, 0x65, - 0x64, 0x6c, 0x65, 0x5f, 0x62, 0x6c, 0x6f, 0x62, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0a, - 0x6e, 0x65, 0x65, 0x64, 0x6c, 0x65, 0x42, 0x6c, 0x6f, 0x62, 0x22, 0x83, 0x01, 0x0a, 0x17, 0x56, - 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x54, 0x61, 0x69, 0x6c, 0x53, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, - 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x76, 0x6f, 0x6c, 0x75, 0x6d, - 0x65, 0x49, 0x64, 0x12, 0x19, 0x0a, 0x08, 0x73, 0x69, 0x6e, 0x63, 0x65, 0x5f, 0x6e, 0x73, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x07, 0x73, 0x69, 0x6e, 0x63, 0x65, 0x4e, 0x73, 0x12, 0x30, - 0x0a, 0x14, 0x69, 0x64, 0x6c, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x5f, 0x73, - 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x12, 0x69, 0x64, - 0x6c, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x53, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, - 0x22, 0x84, 0x01, 0x0a, 0x18, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x54, 0x61, 0x69, 0x6c, 0x53, - 0x65, 0x6e, 0x64, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x23, 0x0a, - 0x0d, 0x6e, 0x65, 0x65, 0x64, 0x6c, 0x65, 0x5f, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0c, 0x6e, 0x65, 0x65, 0x64, 0x6c, 0x65, 0x48, 0x65, 0x61, 0x64, - 0x65, 0x72, 0x12, 0x1f, 0x0a, 0x0b, 0x6e, 0x65, 0x65, 0x64, 0x6c, 0x65, 0x5f, 0x62, 0x6f, 0x64, - 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0a, 0x6e, 0x65, 0x65, 0x64, 0x6c, 0x65, 0x42, - 0x6f, 0x64, 0x79, 0x12, 0x22, 0x0a, 0x0d, 0x69, 0x73, 0x5f, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x63, - 0x68, 0x75, 0x6e, 0x6b, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x69, 0x73, 0x4c, 0x61, - 0x73, 0x74, 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x22, 0xb7, 0x01, 0x0a, 0x19, 0x56, 0x6f, 0x6c, 0x75, - 0x6d, 0x65, 0x54, 0x61, 0x69, 0x6c, 0x52, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, - 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, - 0x49, 0x64, 0x12, 0x19, 0x0a, 0x08, 0x73, 0x69, 0x6e, 0x63, 0x65, 0x5f, 0x6e, 0x73, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x04, 0x52, 0x07, 0x73, 0x69, 0x6e, 0x63, 0x65, 0x4e, 0x73, 0x12, 0x30, 0x0a, - 0x14, 0x69, 0x64, 0x6c, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x5f, 0x73, 0x65, - 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x12, 0x69, 0x64, 0x6c, - 0x65, 0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x53, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x12, - 0x30, 0x0a, 0x14, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, - 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x12, 0x73, - 0x6f, 0x75, 0x72, 0x63, 0x65, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, - 0x72, 0x22, 0x1c, 0x0a, 0x1a, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x54, 0x61, 0x69, 0x6c, 0x52, - 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, - 0x5c, 0x0a, 0x1d, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x45, 0x63, 0x53, 0x68, 0x61, 0x72, 0x64, - 0x73, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x12, 0x1b, 0x0a, 0x09, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x0d, 0x52, 0x08, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x49, 0x64, 0x12, 0x1e, 0x0a, - 0x0a, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x0a, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x20, 0x0a, - 0x1e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x45, 0x63, 0x53, 0x68, 0x61, 0x72, 0x64, 0x73, 0x47, - 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, - 0x5b, 0x0a, 0x1c, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x45, 0x63, 0x53, 0x68, 0x61, 0x72, 0x64, - 0x73, 0x52, 0x65, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, + 0x69, 0x64, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0d, 0x52, 0x09, 0x76, 0x6f, 0x6c, 0x75, 0x6d, + 0x65, 0x49, 0x64, 0x73, 0x22, 0x8b, 0x01, 0x0a, 0x16, 0x52, 0x65, 0x61, 0x64, 0x41, 0x6c, 0x6c, + 0x4e, 0x65, 0x65, 0x64, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x0d, 0x52, 0x08, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x49, 0x64, 0x12, 0x1e, 0x0a, 0x0a, - 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x0a, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x4b, 0x0a, 0x1d, - 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x45, 0x63, 0x53, 0x68, 0x61, 0x72, 0x64, 0x73, 0x52, 0x65, - 0x62, 0x75, 0x69, 0x6c, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2a, 0x0a, - 0x11, 0x72, 0x65, 0x62, 0x75, 0x69, 0x6c, 0x74, 0x5f, 0x73, 0x68, 0x61, 0x72, 0x64, 0x5f, 0x69, - 0x64, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0d, 0x52, 0x0f, 0x72, 0x65, 0x62, 0x75, 0x69, 0x6c, - 0x74, 0x53, 0x68, 0x61, 0x72, 0x64, 0x49, 0x64, 0x73, 0x22, 0x8b, 0x02, 0x0a, 0x19, 0x56, 0x6f, - 0x6c, 0x75, 0x6d, 0x65, 0x45, 0x63, 0x53, 0x68, 0x61, 0x72, 0x64, 0x73, 0x43, 0x6f, 0x70, 0x79, + 0x28, 0x0d, 0x52, 0x08, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x49, 0x64, 0x12, 0x1b, 0x0a, 0x09, + 0x6e, 0x65, 0x65, 0x64, 0x6c, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, + 0x08, 0x6e, 0x65, 0x65, 0x64, 0x6c, 0x65, 0x49, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x63, 0x6f, 0x6f, + 0x6b, 0x69, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x06, 0x63, 0x6f, 0x6f, 0x6b, 0x69, + 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x6e, 0x65, 0x65, 0x64, 0x6c, 0x65, 0x5f, 0x62, 0x6c, 0x6f, 0x62, + 0x18, 0x05, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0a, 0x6e, 0x65, 0x65, 0x64, 0x6c, 0x65, 0x42, 0x6c, + 0x6f, 0x62, 0x22, 0x83, 0x01, 0x0a, 0x17, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x54, 0x61, 0x69, + 0x6c, 0x53, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, + 0x0a, 0x09, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x0d, 0x52, 0x08, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x49, 0x64, 0x12, 0x19, 0x0a, 0x08, 0x73, + 0x69, 0x6e, 0x63, 0x65, 0x5f, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x07, 0x73, + 0x69, 0x6e, 0x63, 0x65, 0x4e, 0x73, 0x12, 0x30, 0x0a, 0x14, 0x69, 0x64, 0x6c, 0x65, 0x5f, 0x74, + 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x5f, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x0d, 0x52, 0x12, 0x69, 0x64, 0x6c, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75, + 0x74, 0x53, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x22, 0x84, 0x01, 0x0a, 0x18, 0x56, 0x6f, 0x6c, + 0x75, 0x6d, 0x65, 0x54, 0x61, 0x69, 0x6c, 0x53, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x6e, 0x65, 0x65, 0x64, 0x6c, 0x65, 0x5f, + 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0c, 0x6e, 0x65, + 0x65, 0x64, 0x6c, 0x65, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x12, 0x1f, 0x0a, 0x0b, 0x6e, 0x65, + 0x65, 0x64, 0x6c, 0x65, 0x5f, 0x62, 0x6f, 0x64, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, + 0x0a, 0x6e, 0x65, 0x65, 0x64, 0x6c, 0x65, 0x42, 0x6f, 0x64, 0x79, 0x12, 0x22, 0x0a, 0x0d, 0x69, + 0x73, 0x5f, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x63, 0x68, 0x75, 0x6e, 0x6b, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x08, 0x52, 0x0b, 0x69, 0x73, 0x4c, 0x61, 0x73, 0x74, 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x22, + 0xb7, 0x01, 0x0a, 0x19, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x54, 0x61, 0x69, 0x6c, 0x52, 0x65, + 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, + 0x09, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, + 0x52, 0x08, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x49, 0x64, 0x12, 0x19, 0x0a, 0x08, 0x73, 0x69, + 0x6e, 0x63, 0x65, 0x5f, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x07, 0x73, 0x69, + 0x6e, 0x63, 0x65, 0x4e, 0x73, 0x12, 0x30, 0x0a, 0x14, 0x69, 0x64, 0x6c, 0x65, 0x5f, 0x74, 0x69, + 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x5f, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x0d, 0x52, 0x12, 0x69, 0x64, 0x6c, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, + 0x53, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x12, 0x30, 0x0a, 0x14, 0x73, 0x6f, 0x75, 0x72, 0x63, + 0x65, 0x5f, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x18, + 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x12, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x56, 0x6f, 0x6c, + 0x75, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x22, 0x1c, 0x0a, 0x1a, 0x56, 0x6f, 0x6c, + 0x75, 0x6d, 0x65, 0x54, 0x61, 0x69, 0x6c, 0x52, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x5c, 0x0a, 0x1d, 0x56, 0x6f, 0x6c, 0x75, 0x6d, + 0x65, 0x45, 0x63, 0x53, 0x68, 0x61, 0x72, 0x64, 0x73, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, + 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x76, 0x6f, 0x6c, 0x75, + 0x6d, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x76, 0x6f, 0x6c, + 0x75, 0x6d, 0x65, 0x49, 0x64, 0x12, 0x1e, 0x0a, 0x0a, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, + 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x63, 0x6f, 0x6c, 0x6c, 0x65, + 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x20, 0x0a, 0x1e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x45, + 0x63, 0x53, 0x68, 0x61, 0x72, 0x64, 0x73, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x5b, 0x0a, 0x1c, 0x56, 0x6f, 0x6c, 0x75, 0x6d, + 0x65, 0x45, 0x63, 0x53, 0x68, 0x61, 0x72, 0x64, 0x73, 0x52, 0x65, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x49, 0x64, 0x12, 0x1e, 0x0a, 0x0a, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, - 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1b, 0x0a, 0x09, 0x73, 0x68, 0x61, 0x72, 0x64, 0x5f, 0x69, 0x64, - 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0d, 0x52, 0x08, 0x73, 0x68, 0x61, 0x72, 0x64, 0x49, 0x64, - 0x73, 0x12, 0x22, 0x0a, 0x0d, 0x63, 0x6f, 0x70, 0x79, 0x5f, 0x65, 0x63, 0x78, 0x5f, 0x66, 0x69, - 0x6c, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x63, 0x6f, 0x70, 0x79, 0x45, 0x63, - 0x78, 0x46, 0x69, 0x6c, 0x65, 0x12, 0x28, 0x0a, 0x10, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, - 0x64, 0x61, 0x74, 0x61, 0x5f, 0x6e, 0x6f, 0x64, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x0e, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x44, 0x61, 0x74, 0x61, 0x4e, 0x6f, 0x64, 0x65, 0x12, - 0x22, 0x0a, 0x0d, 0x63, 0x6f, 0x70, 0x79, 0x5f, 0x65, 0x63, 0x6a, 0x5f, 0x66, 0x69, 0x6c, 0x65, - 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x63, 0x6f, 0x70, 0x79, 0x45, 0x63, 0x6a, 0x46, - 0x69, 0x6c, 0x65, 0x12, 0x22, 0x0a, 0x0d, 0x63, 0x6f, 0x70, 0x79, 0x5f, 0x76, 0x69, 0x66, 0x5f, - 0x66, 0x69, 0x6c, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x63, 0x6f, 0x70, 0x79, - 0x56, 0x69, 0x66, 0x46, 0x69, 0x6c, 0x65, 0x22, 0x1c, 0x0a, 0x1a, 0x56, 0x6f, 0x6c, 0x75, 0x6d, - 0x65, 0x45, 0x63, 0x53, 0x68, 0x61, 0x72, 0x64, 0x73, 0x43, 0x6f, 0x70, 0x79, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x77, 0x0a, 0x1b, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x45, - 0x63, 0x53, 0x68, 0x61, 0x72, 0x64, 0x73, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x69, - 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x49, - 0x64, 0x12, 0x1e, 0x0a, 0x0a, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, - 0x6e, 0x12, 0x1b, 0x0a, 0x09, 0x73, 0x68, 0x61, 0x72, 0x64, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x03, - 0x20, 0x03, 0x28, 0x0d, 0x52, 0x08, 0x73, 0x68, 0x61, 0x72, 0x64, 0x49, 0x64, 0x73, 0x22, 0x1e, - 0x0a, 0x1c, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x45, 0x63, 0x53, 0x68, 0x61, 0x72, 0x64, 0x73, - 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x76, - 0x0a, 0x1a, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x45, 0x63, 0x53, 0x68, 0x61, 0x72, 0x64, 0x73, - 0x4d, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, + 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x4b, 0x0a, 0x1d, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x45, 0x63, + 0x53, 0x68, 0x61, 0x72, 0x64, 0x73, 0x52, 0x65, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2a, 0x0a, 0x11, 0x72, 0x65, 0x62, 0x75, 0x69, 0x6c, 0x74, + 0x5f, 0x73, 0x68, 0x61, 0x72, 0x64, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0d, + 0x52, 0x0f, 0x72, 0x65, 0x62, 0x75, 0x69, 0x6c, 0x74, 0x53, 0x68, 0x61, 0x72, 0x64, 0x49, 0x64, + 0x73, 0x22, 0x8b, 0x02, 0x0a, 0x19, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x45, 0x63, 0x53, 0x68, + 0x61, 0x72, 0x64, 0x73, 0x43, 0x6f, 0x70, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, + 0x1b, 0x0a, 0x09, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x0d, 0x52, 0x08, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x49, 0x64, 0x12, 0x1e, 0x0a, 0x0a, + 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x0a, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1b, 0x0a, 0x09, + 0x73, 0x68, 0x61, 0x72, 0x64, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0d, 0x52, + 0x08, 0x73, 0x68, 0x61, 0x72, 0x64, 0x49, 0x64, 0x73, 0x12, 0x22, 0x0a, 0x0d, 0x63, 0x6f, 0x70, + 0x79, 0x5f, 0x65, 0x63, 0x78, 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, + 0x52, 0x0b, 0x63, 0x6f, 0x70, 0x79, 0x45, 0x63, 0x78, 0x46, 0x69, 0x6c, 0x65, 0x12, 0x28, 0x0a, + 0x10, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x6e, 0x6f, 0x64, + 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x44, + 0x61, 0x74, 0x61, 0x4e, 0x6f, 0x64, 0x65, 0x12, 0x22, 0x0a, 0x0d, 0x63, 0x6f, 0x70, 0x79, 0x5f, + 0x65, 0x63, 0x6a, 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, + 0x63, 0x6f, 0x70, 0x79, 0x45, 0x63, 0x6a, 0x46, 0x69, 0x6c, 0x65, 0x12, 0x22, 0x0a, 0x0d, 0x63, + 0x6f, 0x70, 0x79, 0x5f, 0x76, 0x69, 0x66, 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x18, 0x07, 0x20, 0x01, + 0x28, 0x08, 0x52, 0x0b, 0x63, 0x6f, 0x70, 0x79, 0x56, 0x69, 0x66, 0x46, 0x69, 0x6c, 0x65, 0x22, + 0x1c, 0x0a, 0x1a, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x45, 0x63, 0x53, 0x68, 0x61, 0x72, 0x64, + 0x73, 0x43, 0x6f, 0x70, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x77, 0x0a, + 0x1b, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x45, 0x63, 0x53, 0x68, 0x61, 0x72, 0x64, 0x73, 0x44, + 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x49, 0x64, 0x12, 0x1e, 0x0a, 0x0a, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1b, 0x0a, 0x09, 0x73, 0x68, 0x61, 0x72, 0x64, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0d, 0x52, 0x08, 0x73, 0x68, - 0x61, 0x72, 0x64, 0x49, 0x64, 0x73, 0x22, 0x1d, 0x0a, 0x1b, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, - 0x45, 0x63, 0x53, 0x68, 0x61, 0x72, 0x64, 0x73, 0x4d, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x58, 0x0a, 0x1c, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x45, - 0x63, 0x53, 0x68, 0x61, 0x72, 0x64, 0x73, 0x55, 0x6e, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, - 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, - 0x49, 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x73, 0x68, 0x61, 0x72, 0x64, 0x5f, 0x69, 0x64, 0x73, 0x18, - 0x03, 0x20, 0x03, 0x28, 0x0d, 0x52, 0x08, 0x73, 0x68, 0x61, 0x72, 0x64, 0x49, 0x64, 0x73, 0x22, - 0x1f, 0x0a, 0x1d, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x45, 0x63, 0x53, 0x68, 0x61, 0x72, 0x64, - 0x73, 0x55, 0x6e, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x22, 0x99, 0x01, 0x0a, 0x18, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x45, 0x63, 0x53, 0x68, 0x61, - 0x72, 0x64, 0x52, 0x65, 0x61, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, + 0x61, 0x72, 0x64, 0x49, 0x64, 0x73, 0x22, 0x1e, 0x0a, 0x1c, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, + 0x45, 0x63, 0x53, 0x68, 0x61, 0x72, 0x64, 0x73, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x76, 0x0a, 0x1a, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, + 0x45, 0x63, 0x53, 0x68, 0x61, 0x72, 0x64, 0x73, 0x4d, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x69, + 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x49, + 0x64, 0x12, 0x1e, 0x0a, 0x0a, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, + 0x6e, 0x12, 0x1b, 0x0a, 0x09, 0x73, 0x68, 0x61, 0x72, 0x64, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x03, + 0x20, 0x03, 0x28, 0x0d, 0x52, 0x08, 0x73, 0x68, 0x61, 0x72, 0x64, 0x49, 0x64, 0x73, 0x22, 0x1d, + 0x0a, 0x1b, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x45, 0x63, 0x53, 0x68, 0x61, 0x72, 0x64, 0x73, + 0x4d, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x58, 0x0a, + 0x1c, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x45, 0x63, 0x53, 0x68, 0x61, 0x72, 0x64, 0x73, 0x55, + 0x6e, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, - 0x52, 0x08, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x49, 0x64, 0x12, 0x19, 0x0a, 0x08, 0x73, 0x68, - 0x61, 0x72, 0x64, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x73, 0x68, - 0x61, 0x72, 0x64, 0x49, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x18, - 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x12, 0x12, 0x0a, - 0x04, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x04, 0x73, 0x69, 0x7a, - 0x65, 0x12, 0x19, 0x0a, 0x08, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x05, 0x20, - 0x01, 0x28, 0x04, 0x52, 0x07, 0x66, 0x69, 0x6c, 0x65, 0x4b, 0x65, 0x79, 0x22, 0x4e, 0x0a, 0x19, - 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x45, 0x63, 0x53, 0x68, 0x61, 0x72, 0x64, 0x52, 0x65, 0x61, - 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x61, 0x74, - 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x12, 0x1d, 0x0a, - 0x0a, 0x69, 0x73, 0x5f, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x08, 0x52, 0x09, 0x69, 0x73, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x22, 0x8d, 0x01, 0x0a, - 0x19, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x45, 0x63, 0x42, 0x6c, 0x6f, 0x62, 0x44, 0x65, 0x6c, - 0x65, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x76, 0x6f, - 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x76, - 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x49, 0x64, 0x12, 0x1e, 0x0a, 0x0a, 0x63, 0x6f, 0x6c, 0x6c, 0x65, - 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x63, 0x6f, 0x6c, - 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x19, 0x0a, 0x08, 0x66, 0x69, 0x6c, 0x65, 0x5f, - 0x6b, 0x65, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x07, 0x66, 0x69, 0x6c, 0x65, 0x4b, - 0x65, 0x79, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, - 0x01, 0x28, 0x0d, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x1c, 0x0a, 0x1a, - 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x45, 0x63, 0x42, 0x6c, 0x6f, 0x62, 0x44, 0x65, 0x6c, 0x65, - 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x5c, 0x0a, 0x1d, 0x56, 0x6f, - 0x6c, 0x75, 0x6d, 0x65, 0x45, 0x63, 0x53, 0x68, 0x61, 0x72, 0x64, 0x73, 0x54, 0x6f, 0x56, 0x6f, - 0x6c, 0x75, 0x6d, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x76, - 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, - 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x49, 0x64, 0x12, 0x1e, 0x0a, 0x0a, 0x63, 0x6f, 0x6c, 0x6c, - 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x63, 0x6f, - 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x20, 0x0a, 0x1e, 0x56, 0x6f, 0x6c, 0x75, - 0x6d, 0x65, 0x45, 0x63, 0x53, 0x68, 0x61, 0x72, 0x64, 0x73, 0x54, 0x6f, 0x56, 0x6f, 0x6c, 0x75, - 0x6d, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x3a, 0x0a, 0x1b, 0x52, 0x65, - 0x61, 0x64, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x46, 0x69, 0x6c, 0x65, 0x53, 0x74, 0x61, 0x74, - 0x75, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x76, 0x6f, 0x6c, - 0x75, 0x6d, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x76, 0x6f, - 0x6c, 0x75, 0x6d, 0x65, 0x49, 0x64, 0x22, 0x8a, 0x03, 0x0a, 0x1c, 0x52, 0x65, 0x61, 0x64, 0x56, - 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x46, 0x69, 0x6c, 0x65, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x76, 0x6f, 0x6c, 0x75, 0x6d, - 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x76, 0x6f, 0x6c, 0x75, - 0x6d, 0x65, 0x49, 0x64, 0x12, 0x3b, 0x0a, 0x1a, 0x69, 0x64, 0x78, 0x5f, 0x66, 0x69, 0x6c, 0x65, - 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x5f, 0x73, 0x65, 0x63, 0x6f, 0x6e, - 0x64, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x17, 0x69, 0x64, 0x78, 0x46, 0x69, 0x6c, - 0x65, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x53, 0x65, 0x63, 0x6f, 0x6e, 0x64, - 0x73, 0x12, 0x22, 0x0a, 0x0d, 0x69, 0x64, 0x78, 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x73, 0x69, - 0x7a, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, 0x69, 0x64, 0x78, 0x46, 0x69, 0x6c, - 0x65, 0x53, 0x69, 0x7a, 0x65, 0x12, 0x3b, 0x0a, 0x1a, 0x64, 0x61, 0x74, 0x5f, 0x66, 0x69, 0x6c, - 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x5f, 0x73, 0x65, 0x63, 0x6f, - 0x6e, 0x64, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x17, 0x64, 0x61, 0x74, 0x46, 0x69, - 0x6c, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x53, 0x65, 0x63, 0x6f, 0x6e, - 0x64, 0x73, 0x12, 0x22, 0x0a, 0x0d, 0x64, 0x61, 0x74, 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x73, - 0x69, 0x7a, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, 0x64, 0x61, 0x74, 0x46, 0x69, - 0x6c, 0x65, 0x53, 0x69, 0x7a, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x63, - 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x66, 0x69, 0x6c, 0x65, - 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x2f, 0x0a, 0x13, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x63, 0x74, - 0x69, 0x6f, 0x6e, 0x5f, 0x72, 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x07, 0x20, 0x01, - 0x28, 0x0d, 0x52, 0x12, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, - 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x1e, 0x0a, 0x0a, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, - 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x63, 0x6f, 0x6c, 0x6c, - 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1b, 0x0a, 0x09, 0x64, 0x69, 0x73, 0x6b, 0x5f, 0x74, - 0x79, 0x70, 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x64, 0x69, 0x73, 0x6b, 0x54, - 0x79, 0x70, 0x65, 0x22, 0xbb, 0x01, 0x0a, 0x0a, 0x44, 0x69, 0x73, 0x6b, 0x53, 0x74, 0x61, 0x74, - 0x75, 0x73, 0x12, 0x10, 0x0a, 0x03, 0x64, 0x69, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x03, 0x64, 0x69, 0x72, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x6c, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x04, 0x52, 0x03, 0x61, 0x6c, 0x6c, 0x12, 0x12, 0x0a, 0x04, 0x75, 0x73, 0x65, 0x64, 0x18, 0x03, - 0x20, 0x01, 0x28, 0x04, 0x52, 0x04, 0x75, 0x73, 0x65, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x66, 0x72, - 0x65, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x04, 0x66, 0x72, 0x65, 0x65, 0x12, 0x21, - 0x0a, 0x0c, 0x70, 0x65, 0x72, 0x63, 0x65, 0x6e, 0x74, 0x5f, 0x66, 0x72, 0x65, 0x65, 0x18, 0x05, - 0x20, 0x01, 0x28, 0x02, 0x52, 0x0b, 0x70, 0x65, 0x72, 0x63, 0x65, 0x6e, 0x74, 0x46, 0x72, 0x65, - 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x70, 0x65, 0x72, 0x63, 0x65, 0x6e, 0x74, 0x5f, 0x75, 0x73, 0x65, - 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x02, 0x52, 0x0b, 0x70, 0x65, 0x72, 0x63, 0x65, 0x6e, 0x74, - 0x55, 0x73, 0x65, 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x64, 0x69, 0x73, 0x6b, 0x5f, 0x74, 0x79, 0x70, - 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x64, 0x69, 0x73, 0x6b, 0x54, 0x79, 0x70, - 0x65, 0x22, 0xa3, 0x01, 0x0a, 0x09, 0x4d, 0x65, 0x6d, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, - 0x1e, 0x0a, 0x0a, 0x67, 0x6f, 0x72, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x65, 0x73, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x05, 0x52, 0x0a, 0x67, 0x6f, 0x72, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x65, 0x73, 0x12, - 0x10, 0x0a, 0x03, 0x61, 0x6c, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x03, 0x61, 0x6c, - 0x6c, 0x12, 0x12, 0x0a, 0x04, 0x75, 0x73, 0x65, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, - 0x04, 0x75, 0x73, 0x65, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x66, 0x72, 0x65, 0x65, 0x18, 0x04, 0x20, - 0x01, 0x28, 0x04, 0x52, 0x04, 0x66, 0x72, 0x65, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x65, 0x6c, - 0x66, 0x18, 0x05, 0x20, 0x01, 0x28, 0x04, 0x52, 0x04, 0x73, 0x65, 0x6c, 0x66, 0x12, 0x12, 0x0a, - 0x04, 0x68, 0x65, 0x61, 0x70, 0x18, 0x06, 0x20, 0x01, 0x28, 0x04, 0x52, 0x04, 0x68, 0x65, 0x61, - 0x70, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x63, 0x6b, 0x18, 0x07, 0x20, 0x01, 0x28, 0x04, - 0x52, 0x05, 0x73, 0x74, 0x61, 0x63, 0x6b, 0x22, 0xd8, 0x01, 0x0a, 0x0a, 0x52, 0x65, 0x6d, 0x6f, - 0x74, 0x65, 0x46, 0x69, 0x6c, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x62, 0x61, 0x63, 0x6b, 0x65, 0x6e, - 0x64, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x62, 0x61, - 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x54, 0x79, 0x70, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x62, 0x61, 0x63, - 0x6b, 0x65, 0x6e, 0x64, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x62, - 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x49, 0x64, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, - 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x16, 0x0a, 0x06, 0x6f, 0x66, - 0x66, 0x73, 0x65, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x6f, 0x66, 0x66, 0x73, - 0x65, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, - 0x05, 0x20, 0x01, 0x28, 0x04, 0x52, 0x08, 0x66, 0x69, 0x6c, 0x65, 0x53, 0x69, 0x7a, 0x65, 0x12, - 0x23, 0x0a, 0x0d, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x69, 0x65, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, - 0x18, 0x06, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0c, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x69, 0x65, 0x64, - 0x54, 0x69, 0x6d, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, - 0x6e, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, - 0x6f, 0x6e, 0x22, 0x7c, 0x0a, 0x0a, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x49, 0x6e, 0x66, 0x6f, - 0x12, 0x32, 0x0a, 0x05, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, - 0x1c, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, - 0x70, 0x62, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x46, 0x69, 0x6c, 0x65, 0x52, 0x05, 0x66, - 0x69, 0x6c, 0x65, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x20, - 0x0a, 0x0b, 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x0b, 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x22, 0xc8, 0x01, 0x0a, 0x20, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x54, 0x69, 0x65, 0x72, 0x4d, - 0x6f, 0x76, 0x65, 0x44, 0x61, 0x74, 0x54, 0x6f, 0x52, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, - 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, - 0x49, 0x64, 0x12, 0x1e, 0x0a, 0x0a, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, - 0x6f, 0x6e, 0x12, 0x38, 0x0a, 0x18, 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x5f, 0x62, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x16, 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x42, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x2d, 0x0a, 0x13, - 0x6b, 0x65, 0x65, 0x70, 0x5f, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x5f, 0x64, 0x61, 0x74, 0x5f, 0x66, - 0x69, 0x6c, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x6b, 0x65, 0x65, 0x70, 0x4c, - 0x6f, 0x63, 0x61, 0x6c, 0x44, 0x61, 0x74, 0x46, 0x69, 0x6c, 0x65, 0x22, 0x73, 0x0a, 0x21, 0x56, - 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x54, 0x69, 0x65, 0x72, 0x4d, 0x6f, 0x76, 0x65, 0x44, 0x61, 0x74, - 0x54, 0x6f, 0x52, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x12, 0x1c, 0x0a, 0x09, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x65, 0x64, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x03, 0x52, 0x09, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x65, 0x64, 0x12, 0x30, - 0x0a, 0x13, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x65, 0x64, 0x50, 0x65, 0x72, 0x63, 0x65, - 0x6e, 0x74, 0x61, 0x67, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x02, 0x52, 0x13, 0x70, 0x72, 0x6f, - 0x63, 0x65, 0x73, 0x73, 0x65, 0x64, 0x50, 0x65, 0x72, 0x63, 0x65, 0x6e, 0x74, 0x61, 0x67, 0x65, - 0x22, 0x92, 0x01, 0x0a, 0x22, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x54, 0x69, 0x65, 0x72, 0x4d, - 0x6f, 0x76, 0x65, 0x44, 0x61, 0x74, 0x46, 0x72, 0x6f, 0x6d, 0x52, 0x65, 0x6d, 0x6f, 0x74, 0x65, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x76, 0x6f, 0x6c, 0x75, 0x6d, - 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x76, 0x6f, 0x6c, 0x75, - 0x6d, 0x65, 0x49, 0x64, 0x12, 0x1e, 0x0a, 0x0a, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, - 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, - 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x2f, 0x0a, 0x14, 0x6b, 0x65, 0x65, 0x70, 0x5f, 0x72, 0x65, 0x6d, - 0x6f, 0x74, 0x65, 0x5f, 0x64, 0x61, 0x74, 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x18, 0x03, 0x20, 0x01, - 0x28, 0x08, 0x52, 0x11, 0x6b, 0x65, 0x65, 0x70, 0x52, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x44, 0x61, - 0x74, 0x46, 0x69, 0x6c, 0x65, 0x22, 0x75, 0x0a, 0x23, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x54, - 0x69, 0x65, 0x72, 0x4d, 0x6f, 0x76, 0x65, 0x44, 0x61, 0x74, 0x46, 0x72, 0x6f, 0x6d, 0x52, 0x65, - 0x6d, 0x6f, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1c, 0x0a, 0x09, - 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, - 0x09, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x65, 0x64, 0x12, 0x30, 0x0a, 0x13, 0x70, 0x72, - 0x6f, 0x63, 0x65, 0x73, 0x73, 0x65, 0x64, 0x50, 0x65, 0x72, 0x63, 0x65, 0x6e, 0x74, 0x61, 0x67, - 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x02, 0x52, 0x13, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, - 0x65, 0x64, 0x50, 0x65, 0x72, 0x63, 0x65, 0x6e, 0x74, 0x61, 0x67, 0x65, 0x22, 0x1b, 0x0a, 0x19, - 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, - 0x75, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0xa1, 0x01, 0x0a, 0x1a, 0x56, 0x6f, - 0x6c, 0x75, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x41, 0x0a, 0x0d, 0x64, 0x69, 0x73, 0x6b, - 0x5f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, - 0x1c, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, - 0x70, 0x62, 0x2e, 0x44, 0x69, 0x73, 0x6b, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x0c, 0x64, - 0x69, 0x73, 0x6b, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x65, 0x73, 0x12, 0x40, 0x0a, 0x0d, 0x6d, - 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, - 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x4d, 0x65, 0x6d, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, - 0x0c, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0x1a, 0x0a, - 0x18, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x4c, 0x65, 0x61, - 0x76, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x1b, 0x0a, 0x19, 0x56, 0x6f, 0x6c, - 0x75, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x4c, 0x65, 0x61, 0x76, 0x65, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0xdc, 0x03, 0x0a, 0x1a, 0x46, 0x65, 0x74, 0x63, 0x68, - 0x41, 0x6e, 0x64, 0x57, 0x72, 0x69, 0x74, 0x65, 0x4e, 0x65, 0x65, 0x64, 0x6c, 0x65, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, - 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, - 0x49, 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x6e, 0x65, 0x65, 0x64, 0x6c, 0x65, 0x5f, 0x69, 0x64, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x08, 0x6e, 0x65, 0x65, 0x64, 0x6c, 0x65, 0x49, 0x64, 0x12, - 0x16, 0x0a, 0x06, 0x63, 0x6f, 0x6f, 0x6b, 0x69, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, - 0x06, 0x63, 0x6f, 0x6f, 0x6b, 0x69, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x6f, 0x66, 0x66, 0x73, 0x65, - 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x12, - 0x12, 0x0a, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x04, 0x73, - 0x69, 0x7a, 0x65, 0x12, 0x50, 0x0a, 0x08, 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x73, 0x18, - 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x34, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, - 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x46, 0x65, 0x74, 0x63, 0x68, 0x41, 0x6e, - 0x64, 0x57, 0x72, 0x69, 0x74, 0x65, 0x4e, 0x65, 0x65, 0x64, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x2e, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x52, 0x08, 0x72, 0x65, 0x70, - 0x6c, 0x69, 0x63, 0x61, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x61, 0x75, 0x74, 0x68, 0x18, 0x07, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x04, 0x61, 0x75, 0x74, 0x68, 0x12, 0x36, 0x0a, 0x0b, 0x72, 0x65, 0x6d, - 0x6f, 0x74, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, - 0x2e, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x5f, 0x70, 0x62, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x74, - 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x52, 0x0a, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x43, 0x6f, 0x6e, - 0x66, 0x12, 0x49, 0x0a, 0x0f, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x5f, 0x6c, 0x6f, 0x63, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x10, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x72, 0x65, 0x6d, - 0x6f, 0x74, 0x65, 0x5f, 0x70, 0x62, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x53, 0x74, 0x6f, - 0x72, 0x61, 0x67, 0x65, 0x4c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0e, 0x72, 0x65, - 0x6d, 0x6f, 0x74, 0x65, 0x4c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x1a, 0x57, 0x0a, 0x07, - 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, 0x6c, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x6c, 0x12, 0x1d, 0x0a, 0x0a, 0x70, 0x75, 0x62, - 0x6c, 0x69, 0x63, 0x5f, 0x75, 0x72, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x70, - 0x75, 0x62, 0x6c, 0x69, 0x63, 0x55, 0x72, 0x6c, 0x12, 0x1b, 0x0a, 0x09, 0x67, 0x72, 0x70, 0x63, - 0x5f, 0x70, 0x6f, 0x72, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x08, 0x67, 0x72, 0x70, - 0x63, 0x50, 0x6f, 0x72, 0x74, 0x22, 0x1d, 0x0a, 0x1b, 0x46, 0x65, 0x74, 0x63, 0x68, 0x41, 0x6e, - 0x64, 0x57, 0x72, 0x69, 0x74, 0x65, 0x4e, 0x65, 0x65, 0x64, 0x6c, 0x65, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x22, 0xf8, 0x0c, 0x0a, 0x0c, 0x51, 0x75, 0x65, 0x72, 0x79, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1e, 0x0a, 0x0a, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x69, - 0x6f, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0a, 0x73, 0x65, 0x6c, 0x65, 0x63, - 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x22, 0x0a, 0x0d, 0x66, 0x72, 0x6f, 0x6d, 0x5f, 0x66, 0x69, - 0x6c, 0x65, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0b, 0x66, 0x72, - 0x6f, 0x6d, 0x46, 0x69, 0x6c, 0x65, 0x49, 0x64, 0x73, 0x12, 0x3d, 0x0a, 0x06, 0x66, 0x69, 0x6c, - 0x74, 0x65, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x76, 0x6f, 0x6c, 0x75, - 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x51, 0x75, 0x65, - 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, - 0x52, 0x06, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x12, 0x62, 0x0a, 0x13, 0x69, 0x6e, 0x70, 0x75, - 0x74, 0x5f, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, - 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x31, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, - 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x53, 0x65, 0x72, 0x69, 0x61, - 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x12, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x53, - 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x65, 0x0a, 0x14, - 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x5f, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x32, 0x2e, 0x76, 0x6f, 0x6c, - 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x51, 0x75, - 0x65, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x4f, 0x75, 0x74, 0x70, 0x75, - 0x74, 0x53, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x13, - 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x53, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x1a, 0x4e, 0x0a, 0x06, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x12, 0x14, 0x0a, - 0x05, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x66, 0x69, - 0x65, 0x6c, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x6e, 0x64, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x6e, 0x64, 0x12, 0x14, 0x0a, - 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, - 0x6c, 0x75, 0x65, 0x1a, 0xd5, 0x05, 0x0a, 0x12, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x53, 0x65, 0x72, - 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x29, 0x0a, 0x10, 0x63, 0x6f, - 0x6d, 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x63, 0x6f, 0x6d, 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, - 0x6e, 0x54, 0x79, 0x70, 0x65, 0x12, 0x57, 0x0a, 0x09, 0x63, 0x73, 0x76, 0x5f, 0x69, 0x6e, 0x70, - 0x75, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x3a, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, - 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x51, 0x75, 0x65, 0x72, - 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x53, 0x65, - 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x43, 0x53, 0x56, 0x49, - 0x6e, 0x70, 0x75, 0x74, 0x52, 0x08, 0x63, 0x73, 0x76, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x12, 0x5a, - 0x0a, 0x0a, 0x6a, 0x73, 0x6f, 0x6e, 0x5f, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x18, 0x03, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x3b, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, - 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x2e, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x53, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x4a, 0x53, 0x4f, 0x4e, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x52, - 0x09, 0x6a, 0x73, 0x6f, 0x6e, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x12, 0x63, 0x0a, 0x0d, 0x70, 0x61, - 0x72, 0x71, 0x75, 0x65, 0x74, 0x5f, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x3e, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, - 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x2e, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x53, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x50, 0x61, 0x72, 0x71, 0x75, 0x65, 0x74, 0x49, 0x6e, 0x70, 0x75, - 0x74, 0x52, 0x0c, 0x70, 0x61, 0x72, 0x71, 0x75, 0x65, 0x74, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x1a, - 0xc8, 0x02, 0x0a, 0x08, 0x43, 0x53, 0x56, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x12, 0x28, 0x0a, 0x10, - 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x5f, 0x69, 0x6e, 0x66, 0x6f, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x66, 0x69, 0x6c, 0x65, 0x48, 0x65, 0x61, 0x64, - 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x29, 0x0a, 0x10, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, - 0x5f, 0x64, 0x65, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x0f, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x44, 0x65, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x65, - 0x72, 0x12, 0x27, 0x0a, 0x0f, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x5f, 0x64, 0x65, 0x6c, 0x69, 0x6d, - 0x69, 0x74, 0x65, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x66, 0x69, 0x65, 0x6c, - 0x64, 0x44, 0x65, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x65, 0x72, 0x12, 0x29, 0x0a, 0x10, 0x71, 0x75, - 0x6f, 0x74, 0x65, 0x5f, 0x63, 0x68, 0x61, 0x72, 0x61, 0x63, 0x74, 0x6f, 0x65, 0x72, 0x18, 0x04, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x71, 0x75, 0x6f, 0x74, 0x65, 0x43, 0x68, 0x61, 0x72, 0x61, - 0x63, 0x74, 0x6f, 0x65, 0x72, 0x12, 0x34, 0x0a, 0x16, 0x71, 0x75, 0x6f, 0x74, 0x65, 0x5f, 0x65, - 0x73, 0x63, 0x61, 0x70, 0x65, 0x5f, 0x63, 0x68, 0x61, 0x72, 0x61, 0x63, 0x74, 0x65, 0x72, 0x18, - 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x14, 0x71, 0x75, 0x6f, 0x74, 0x65, 0x45, 0x73, 0x63, 0x61, - 0x70, 0x65, 0x43, 0x68, 0x61, 0x72, 0x61, 0x63, 0x74, 0x65, 0x72, 0x12, 0x1a, 0x0a, 0x08, 0x63, - 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x63, - 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x41, 0x0a, 0x1d, 0x61, 0x6c, 0x6c, 0x6f, 0x77, - 0x5f, 0x71, 0x75, 0x6f, 0x74, 0x65, 0x64, 0x5f, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x5f, 0x64, - 0x65, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x65, 0x72, 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x1a, - 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x51, 0x75, 0x6f, 0x74, 0x65, 0x64, 0x52, 0x65, 0x63, 0x6f, 0x72, - 0x64, 0x44, 0x65, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x65, 0x72, 0x1a, 0x1f, 0x0a, 0x09, 0x4a, 0x53, - 0x4f, 0x4e, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x1a, 0x0e, 0x0a, 0x0c, 0x50, - 0x61, 0x72, 0x71, 0x75, 0x65, 0x74, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x1a, 0xf1, 0x03, 0x0a, 0x13, - 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x53, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x12, 0x5b, 0x0a, 0x0a, 0x63, 0x73, 0x76, 0x5f, 0x6f, 0x75, 0x74, 0x70, 0x75, - 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x3c, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, - 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x53, 0x65, - 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x43, 0x53, 0x56, 0x4f, - 0x75, 0x74, 0x70, 0x75, 0x74, 0x52, 0x09, 0x63, 0x73, 0x76, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, - 0x12, 0x5e, 0x0a, 0x0b, 0x6a, 0x73, 0x6f, 0x6e, 0x5f, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x18, - 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x3d, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, - 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x53, 0x65, 0x72, 0x69, - 0x61, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x4a, 0x53, 0x4f, 0x4e, 0x4f, 0x75, - 0x74, 0x70, 0x75, 0x74, 0x52, 0x0a, 0x6a, 0x73, 0x6f, 0x6e, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, - 0x1a, 0xe3, 0x01, 0x0a, 0x09, 0x43, 0x53, 0x56, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x12, 0x21, - 0x0a, 0x0c, 0x71, 0x75, 0x6f, 0x74, 0x65, 0x5f, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x71, 0x75, 0x6f, 0x74, 0x65, 0x46, 0x69, 0x65, 0x6c, 0x64, - 0x73, 0x12, 0x29, 0x0a, 0x10, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x5f, 0x64, 0x65, 0x6c, 0x69, - 0x6d, 0x69, 0x74, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x72, 0x65, 0x63, - 0x6f, 0x72, 0x64, 0x44, 0x65, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x65, 0x72, 0x12, 0x27, 0x0a, 0x0f, - 0x66, 0x69, 0x65, 0x6c, 0x64, 0x5f, 0x64, 0x65, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x65, 0x72, 0x18, - 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x44, 0x65, 0x6c, 0x69, - 0x6d, 0x69, 0x74, 0x65, 0x72, 0x12, 0x29, 0x0a, 0x10, 0x71, 0x75, 0x6f, 0x74, 0x65, 0x5f, 0x63, - 0x68, 0x61, 0x72, 0x61, 0x63, 0x74, 0x6f, 0x65, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x0f, 0x71, 0x75, 0x6f, 0x74, 0x65, 0x43, 0x68, 0x61, 0x72, 0x61, 0x63, 0x74, 0x6f, 0x65, 0x72, - 0x12, 0x34, 0x0a, 0x16, 0x71, 0x75, 0x6f, 0x74, 0x65, 0x5f, 0x65, 0x73, 0x63, 0x61, 0x70, 0x65, - 0x5f, 0x63, 0x68, 0x61, 0x72, 0x61, 0x63, 0x74, 0x65, 0x72, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x14, 0x71, 0x75, 0x6f, 0x74, 0x65, 0x45, 0x73, 0x63, 0x61, 0x70, 0x65, 0x43, 0x68, 0x61, - 0x72, 0x61, 0x63, 0x74, 0x65, 0x72, 0x1a, 0x37, 0x0a, 0x0a, 0x4a, 0x53, 0x4f, 0x4e, 0x4f, 0x75, - 0x74, 0x70, 0x75, 0x74, 0x12, 0x29, 0x0a, 0x10, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x5f, 0x64, - 0x65, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, - 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x44, 0x65, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x65, 0x72, 0x22, - 0x29, 0x0a, 0x0d, 0x51, 0x75, 0x65, 0x72, 0x69, 0x65, 0x64, 0x53, 0x74, 0x72, 0x69, 0x70, 0x65, - 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x0c, 0x52, 0x07, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x73, 0x22, 0x55, 0x0a, 0x19, 0x56, 0x6f, - 0x6c, 0x75, 0x6d, 0x65, 0x4e, 0x65, 0x65, 0x64, 0x6c, 0x65, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x76, 0x6f, 0x6c, 0x75, 0x6d, - 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x76, 0x6f, 0x6c, 0x75, - 0x6d, 0x65, 0x49, 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x6e, 0x65, 0x65, 0x64, 0x6c, 0x65, 0x5f, 0x69, - 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x08, 0x6e, 0x65, 0x65, 0x64, 0x6c, 0x65, 0x49, - 0x64, 0x22, 0xae, 0x01, 0x0a, 0x1a, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x4e, 0x65, 0x65, 0x64, - 0x6c, 0x65, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x12, 0x1b, 0x0a, 0x09, 0x6e, 0x65, 0x65, 0x64, 0x6c, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x04, 0x52, 0x08, 0x6e, 0x65, 0x65, 0x64, 0x6c, 0x65, 0x49, 0x64, 0x12, 0x16, 0x0a, - 0x06, 0x63, 0x6f, 0x6f, 0x6b, 0x69, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x06, 0x63, - 0x6f, 0x6f, 0x6b, 0x69, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x03, 0x20, - 0x01, 0x28, 0x0d, 0x52, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x6c, 0x61, 0x73, - 0x74, 0x5f, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x69, 0x65, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, - 0x52, 0x0c, 0x6c, 0x61, 0x73, 0x74, 0x4d, 0x6f, 0x64, 0x69, 0x66, 0x69, 0x65, 0x64, 0x12, 0x10, - 0x0a, 0x03, 0x63, 0x72, 0x63, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x03, 0x63, 0x72, 0x63, - 0x12, 0x10, 0x0a, 0x03, 0x74, 0x74, 0x6c, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x74, - 0x74, 0x6c, 0x32, 0x88, 0x23, 0x0a, 0x0c, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x53, 0x65, 0x72, - 0x76, 0x65, 0x72, 0x12, 0x5c, 0x0a, 0x0b, 0x42, 0x61, 0x74, 0x63, 0x68, 0x44, 0x65, 0x6c, 0x65, - 0x74, 0x65, 0x12, 0x24, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, - 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x42, 0x61, 0x74, 0x63, 0x68, 0x44, 0x65, 0x6c, 0x65, 0x74, - 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x25, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, - 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x42, 0x61, 0x74, 0x63, - 0x68, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, - 0x00, 0x12, 0x6e, 0x0a, 0x11, 0x56, 0x61, 0x63, 0x75, 0x75, 0x6d, 0x56, 0x6f, 0x6c, 0x75, 0x6d, - 0x65, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x12, 0x2a, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, - 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x61, 0x63, 0x75, 0x75, 0x6d, - 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x2b, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, - 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x61, 0x63, 0x75, 0x75, 0x6d, 0x56, 0x6f, 0x6c, 0x75, - 0x6d, 0x65, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, - 0x00, 0x12, 0x74, 0x0a, 0x13, 0x56, 0x61, 0x63, 0x75, 0x75, 0x6d, 0x56, 0x6f, 0x6c, 0x75, 0x6d, - 0x65, 0x43, 0x6f, 0x6d, 0x70, 0x61, 0x63, 0x74, 0x12, 0x2c, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, - 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x61, 0x63, 0x75, - 0x75, 0x6d, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x43, 0x6f, 0x6d, 0x70, 0x61, 0x63, 0x74, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2d, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, - 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x61, 0x63, 0x75, 0x75, 0x6d, - 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x43, 0x6f, 0x6d, 0x70, 0x61, 0x63, 0x74, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x71, 0x0a, 0x12, 0x56, 0x61, 0x63, 0x75, 0x75, - 0x6d, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x12, 0x2b, 0x2e, - 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, - 0x2e, 0x56, 0x61, 0x63, 0x75, 0x75, 0x6d, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x43, 0x6f, 0x6d, - 0x6d, 0x69, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2c, 0x2e, 0x76, 0x6f, 0x6c, - 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x61, - 0x63, 0x75, 0x75, 0x6d, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x74, 0x0a, 0x13, 0x56, 0x61, - 0x63, 0x75, 0x75, 0x6d, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x43, 0x6c, 0x65, 0x61, 0x6e, 0x75, - 0x70, 0x12, 0x2c, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, - 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x61, 0x63, 0x75, 0x75, 0x6d, 0x56, 0x6f, 0x6c, 0x75, 0x6d, - 0x65, 0x43, 0x6c, 0x65, 0x61, 0x6e, 0x75, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x2d, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, - 0x70, 0x62, 0x2e, 0x56, 0x61, 0x63, 0x75, 0x75, 0x6d, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x43, - 0x6c, 0x65, 0x61, 0x6e, 0x75, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, - 0x12, 0x6b, 0x0a, 0x10, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x43, 0x6f, 0x6c, 0x6c, 0x65, 0x63, - 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x29, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, - 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x43, 0x6f, - 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x2a, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, - 0x70, 0x62, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x43, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, - 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x65, 0x0a, - 0x0e, 0x41, 0x6c, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x65, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x12, - 0x27, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, - 0x70, 0x62, 0x2e, 0x41, 0x6c, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x65, 0x56, 0x6f, 0x6c, 0x75, 0x6d, - 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x28, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, - 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x41, 0x6c, 0x6c, 0x6f, - 0x63, 0x61, 0x74, 0x65, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x22, 0x00, 0x12, 0x6b, 0x0a, 0x10, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x53, 0x79, - 0x6e, 0x63, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x29, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, - 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, - 0x6d, 0x65, 0x53, 0x79, 0x6e, 0x63, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x2a, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, - 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x53, 0x79, 0x6e, - 0x63, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, - 0x00, 0x12, 0x7c, 0x0a, 0x15, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x49, 0x6e, 0x63, 0x72, 0x65, - 0x6d, 0x65, 0x6e, 0x74, 0x61, 0x6c, 0x43, 0x6f, 0x70, 0x79, 0x12, 0x2e, 0x2e, 0x76, 0x6f, 0x6c, - 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, - 0x6c, 0x75, 0x6d, 0x65, 0x49, 0x6e, 0x63, 0x72, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x61, 0x6c, 0x43, - 0x6f, 0x70, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2f, 0x2e, 0x76, 0x6f, 0x6c, - 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, - 0x6c, 0x75, 0x6d, 0x65, 0x49, 0x6e, 0x63, 0x72, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x61, 0x6c, 0x43, - 0x6f, 0x70, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x30, 0x01, 0x12, - 0x5c, 0x0a, 0x0b, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x4d, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x24, - 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, - 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x4d, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x25, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, - 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x4d, 0x6f, - 0x75, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x62, 0x0a, - 0x0d, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x55, 0x6e, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x26, - 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, - 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x55, 0x6e, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x27, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, - 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, - 0x55, 0x6e, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, - 0x00, 0x12, 0x5f, 0x0a, 0x0c, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x44, 0x65, 0x6c, 0x65, 0x74, - 0x65, 0x12, 0x25, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, - 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x44, 0x65, 0x6c, 0x65, 0x74, - 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x26, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, - 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, - 0x6d, 0x65, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x22, 0x00, 0x12, 0x71, 0x0a, 0x12, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x4d, 0x61, 0x72, 0x6b, - 0x52, 0x65, 0x61, 0x64, 0x6f, 0x6e, 0x6c, 0x79, 0x12, 0x2b, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, - 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, - 0x6d, 0x65, 0x4d, 0x61, 0x72, 0x6b, 0x52, 0x65, 0x61, 0x64, 0x6f, 0x6e, 0x6c, 0x79, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2c, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, - 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x4d, - 0x61, 0x72, 0x6b, 0x52, 0x65, 0x61, 0x64, 0x6f, 0x6e, 0x6c, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x71, 0x0a, 0x12, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x4d, - 0x61, 0x72, 0x6b, 0x57, 0x72, 0x69, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x12, 0x2b, 0x2e, 0x76, 0x6f, - 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, - 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x4d, 0x61, 0x72, 0x6b, 0x57, 0x72, 0x69, 0x74, 0x61, 0x62, 0x6c, - 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2c, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, - 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, - 0x6d, 0x65, 0x4d, 0x61, 0x72, 0x6b, 0x57, 0x72, 0x69, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x68, 0x0a, 0x0f, 0x56, 0x6f, 0x6c, 0x75, - 0x6d, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x65, 0x12, 0x28, 0x2e, 0x76, 0x6f, - 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, - 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x65, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x29, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, - 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x43, - 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x22, 0x00, 0x12, 0x5f, 0x0a, 0x0c, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x53, 0x74, 0x61, 0x74, - 0x75, 0x73, 0x12, 0x25, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, - 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x53, 0x74, 0x61, 0x74, - 0x75, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x26, 0x2e, 0x76, 0x6f, 0x6c, 0x75, - 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, - 0x75, 0x6d, 0x65, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x22, 0x00, 0x12, 0x59, 0x0a, 0x0a, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x43, 0x6f, 0x70, - 0x79, 0x12, 0x23, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, - 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x43, 0x6f, 0x70, 0x79, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, - 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, - 0x43, 0x6f, 0x70, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x77, - 0x0a, 0x14, 0x52, 0x65, 0x61, 0x64, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x46, 0x69, 0x6c, 0x65, - 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x2d, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, - 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x52, 0x65, 0x61, 0x64, 0x56, 0x6f, - 0x6c, 0x75, 0x6d, 0x65, 0x46, 0x69, 0x6c, 0x65, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2e, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, - 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x52, 0x65, 0x61, 0x64, 0x56, 0x6f, 0x6c, - 0x75, 0x6d, 0x65, 0x46, 0x69, 0x6c, 0x65, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x55, 0x0a, 0x08, 0x43, 0x6f, 0x70, 0x79, 0x46, - 0x69, 0x6c, 0x65, 0x12, 0x21, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, - 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x43, 0x6f, 0x70, 0x79, 0x46, 0x69, 0x6c, 0x65, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, - 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x43, 0x6f, 0x70, 0x79, 0x46, 0x69, - 0x6c, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x30, 0x01, 0x12, 0x65, - 0x0a, 0x0e, 0x52, 0x65, 0x61, 0x64, 0x4e, 0x65, 0x65, 0x64, 0x6c, 0x65, 0x42, 0x6c, 0x6f, 0x62, - 0x12, 0x27, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, - 0x5f, 0x70, 0x62, 0x2e, 0x52, 0x65, 0x61, 0x64, 0x4e, 0x65, 0x65, 0x64, 0x6c, 0x65, 0x42, 0x6c, - 0x6f, 0x62, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x28, 0x2e, 0x76, 0x6f, 0x6c, 0x75, - 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x52, 0x65, 0x61, - 0x64, 0x4e, 0x65, 0x65, 0x64, 0x6c, 0x65, 0x42, 0x6c, 0x6f, 0x62, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x68, 0x0a, 0x0f, 0x57, 0x72, 0x69, 0x74, 0x65, 0x4e, 0x65, - 0x65, 0x64, 0x6c, 0x65, 0x42, 0x6c, 0x6f, 0x62, 0x12, 0x28, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, - 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x57, 0x72, 0x69, 0x74, - 0x65, 0x4e, 0x65, 0x65, 0x64, 0x6c, 0x65, 0x42, 0x6c, 0x6f, 0x62, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x29, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, - 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x57, 0x72, 0x69, 0x74, 0x65, 0x4e, 0x65, 0x65, 0x64, 0x6c, - 0x65, 0x42, 0x6c, 0x6f, 0x62, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, - 0x67, 0x0a, 0x0e, 0x52, 0x65, 0x61, 0x64, 0x41, 0x6c, 0x6c, 0x4e, 0x65, 0x65, 0x64, 0x6c, 0x65, - 0x73, 0x12, 0x27, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, - 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x52, 0x65, 0x61, 0x64, 0x41, 0x6c, 0x6c, 0x4e, 0x65, 0x65, 0x64, - 0x6c, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x28, 0x2e, 0x76, 0x6f, 0x6c, - 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x52, 0x65, - 0x61, 0x64, 0x41, 0x6c, 0x6c, 0x4e, 0x65, 0x65, 0x64, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x30, 0x01, 0x12, 0x6d, 0x0a, 0x10, 0x56, 0x6f, 0x6c, 0x75, - 0x6d, 0x65, 0x54, 0x61, 0x69, 0x6c, 0x53, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x12, 0x29, 0x2e, 0x76, - 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, - 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x54, 0x61, 0x69, 0x6c, 0x53, 0x65, 0x6e, 0x64, 0x65, 0x72, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2a, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, - 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, - 0x65, 0x54, 0x61, 0x69, 0x6c, 0x53, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x22, 0x00, 0x30, 0x01, 0x12, 0x71, 0x0a, 0x12, 0x56, 0x6f, 0x6c, 0x75, 0x6d, - 0x65, 0x54, 0x61, 0x69, 0x6c, 0x52, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, 0x12, 0x2b, 0x2e, - 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, - 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x54, 0x61, 0x69, 0x6c, 0x52, 0x65, 0x63, 0x65, 0x69, - 0x76, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2c, 0x2e, 0x76, 0x6f, 0x6c, - 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, - 0x6c, 0x75, 0x6d, 0x65, 0x54, 0x61, 0x69, 0x6c, 0x52, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x7d, 0x0a, 0x16, 0x56, 0x6f, - 0x6c, 0x75, 0x6d, 0x65, 0x45, 0x63, 0x53, 0x68, 0x61, 0x72, 0x64, 0x73, 0x47, 0x65, 0x6e, 0x65, - 0x72, 0x61, 0x74, 0x65, 0x12, 0x2f, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, - 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x45, 0x63, - 0x53, 0x68, 0x61, 0x72, 0x64, 0x73, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x30, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, - 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x45, - 0x63, 0x53, 0x68, 0x61, 0x72, 0x64, 0x73, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x7a, 0x0a, 0x15, 0x56, 0x6f, 0x6c, - 0x75, 0x6d, 0x65, 0x45, 0x63, 0x53, 0x68, 0x61, 0x72, 0x64, 0x73, 0x52, 0x65, 0x62, 0x75, 0x69, - 0x6c, 0x64, 0x12, 0x2e, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, - 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x45, 0x63, 0x53, 0x68, - 0x61, 0x72, 0x64, 0x73, 0x52, 0x65, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x2f, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, - 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x45, 0x63, 0x53, 0x68, - 0x61, 0x72, 0x64, 0x73, 0x52, 0x65, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x71, 0x0a, 0x12, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x45, - 0x63, 0x53, 0x68, 0x61, 0x72, 0x64, 0x73, 0x43, 0x6f, 0x70, 0x79, 0x12, 0x2b, 0x2e, 0x76, 0x6f, - 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, - 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x45, 0x63, 0x53, 0x68, 0x61, 0x72, 0x64, 0x73, 0x43, 0x6f, 0x70, - 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2c, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, - 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, - 0x6d, 0x65, 0x45, 0x63, 0x53, 0x68, 0x61, 0x72, 0x64, 0x73, 0x43, 0x6f, 0x70, 0x79, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x77, 0x0a, 0x14, 0x56, 0x6f, 0x6c, 0x75, - 0x6d, 0x65, 0x45, 0x63, 0x53, 0x68, 0x61, 0x72, 0x64, 0x73, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, - 0x12, 0x2d, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, - 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x45, 0x63, 0x53, 0x68, 0x61, 0x72, - 0x64, 0x73, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x2e, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, - 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x45, 0x63, 0x53, 0x68, 0x61, 0x72, 0x64, - 0x73, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, - 0x00, 0x12, 0x74, 0x0a, 0x13, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x45, 0x63, 0x53, 0x68, 0x61, - 0x72, 0x64, 0x73, 0x4d, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x2c, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, - 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, - 0x6d, 0x65, 0x45, 0x63, 0x53, 0x68, 0x61, 0x72, 0x64, 0x73, 0x4d, 0x6f, 0x75, 0x6e, 0x74, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2d, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, - 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, - 0x45, 0x63, 0x53, 0x68, 0x61, 0x72, 0x64, 0x73, 0x4d, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x7a, 0x0a, 0x15, 0x56, 0x6f, 0x6c, 0x75, 0x6d, + 0x52, 0x08, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x49, 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x73, 0x68, + 0x61, 0x72, 0x64, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0d, 0x52, 0x08, 0x73, + 0x68, 0x61, 0x72, 0x64, 0x49, 0x64, 0x73, 0x22, 0x1f, 0x0a, 0x1d, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x45, 0x63, 0x53, 0x68, 0x61, 0x72, 0x64, 0x73, 0x55, 0x6e, 0x6d, 0x6f, 0x75, 0x6e, 0x74, - 0x12, 0x2e, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, - 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x45, 0x63, 0x53, 0x68, 0x61, 0x72, - 0x64, 0x73, 0x55, 0x6e, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x2f, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, - 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x45, 0x63, 0x53, 0x68, 0x61, 0x72, - 0x64, 0x73, 0x55, 0x6e, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x22, 0x00, 0x12, 0x70, 0x0a, 0x11, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x45, 0x63, 0x53, - 0x68, 0x61, 0x72, 0x64, 0x52, 0x65, 0x61, 0x64, 0x12, 0x2a, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, - 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, - 0x6d, 0x65, 0x45, 0x63, 0x53, 0x68, 0x61, 0x72, 0x64, 0x52, 0x65, 0x61, 0x64, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2b, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, - 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x45, 0x63, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x99, 0x01, 0x0a, 0x18, 0x56, 0x6f, 0x6c, + 0x75, 0x6d, 0x65, 0x45, 0x63, 0x53, 0x68, 0x61, 0x72, 0x64, 0x52, 0x65, 0x61, 0x64, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, + 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, + 0x49, 0x64, 0x12, 0x19, 0x0a, 0x08, 0x73, 0x68, 0x61, 0x72, 0x64, 0x5f, 0x69, 0x64, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x73, 0x68, 0x61, 0x72, 0x64, 0x49, 0x64, 0x12, 0x16, 0x0a, + 0x06, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x6f, + 0x66, 0x66, 0x73, 0x65, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x04, 0x20, + 0x01, 0x28, 0x03, 0x52, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x12, 0x19, 0x0a, 0x08, 0x66, 0x69, 0x6c, + 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x05, 0x20, 0x01, 0x28, 0x04, 0x52, 0x07, 0x66, 0x69, 0x6c, + 0x65, 0x4b, 0x65, 0x79, 0x22, 0x4e, 0x0a, 0x19, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x45, 0x63, 0x53, 0x68, 0x61, 0x72, 0x64, 0x52, 0x65, 0x61, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x22, 0x00, 0x30, 0x01, 0x12, 0x71, 0x0a, 0x12, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x45, - 0x63, 0x42, 0x6c, 0x6f, 0x62, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x12, 0x2b, 0x2e, 0x76, 0x6f, - 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, - 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x45, 0x63, 0x42, 0x6c, 0x6f, 0x62, 0x44, 0x65, 0x6c, 0x65, 0x74, - 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2c, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, - 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, - 0x6d, 0x65, 0x45, 0x63, 0x42, 0x6c, 0x6f, 0x62, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x7d, 0x0a, 0x16, 0x56, 0x6f, 0x6c, 0x75, - 0x6d, 0x65, 0x45, 0x63, 0x53, 0x68, 0x61, 0x72, 0x64, 0x73, 0x54, 0x6f, 0x56, 0x6f, 0x6c, 0x75, - 0x6d, 0x65, 0x12, 0x2f, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, - 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x45, 0x63, 0x53, 0x68, + 0x65, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, + 0x04, 0x64, 0x61, 0x74, 0x61, 0x12, 0x1d, 0x0a, 0x0a, 0x69, 0x73, 0x5f, 0x64, 0x65, 0x6c, 0x65, + 0x74, 0x65, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x69, 0x73, 0x44, 0x65, 0x6c, + 0x65, 0x74, 0x65, 0x64, 0x22, 0x8d, 0x01, 0x0a, 0x19, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x45, + 0x63, 0x42, 0x6c, 0x6f, 0x62, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x69, 0x64, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x49, 0x64, 0x12, + 0x1e, 0x0a, 0x0a, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x0a, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, + 0x19, 0x0a, 0x08, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x04, 0x52, 0x07, 0x66, 0x69, 0x6c, 0x65, 0x4b, 0x65, 0x79, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, + 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x76, 0x65, 0x72, + 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x1c, 0x0a, 0x1a, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x45, 0x63, + 0x42, 0x6c, 0x6f, 0x62, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x22, 0x5c, 0x0a, 0x1d, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x45, 0x63, 0x53, 0x68, 0x61, 0x72, 0x64, 0x73, 0x54, 0x6f, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x30, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, - 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x45, 0x63, 0x53, - 0x68, 0x61, 0x72, 0x64, 0x73, 0x54, 0x6f, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x88, 0x01, 0x0a, 0x19, 0x56, 0x6f, 0x6c, 0x75, - 0x6d, 0x65, 0x54, 0x69, 0x65, 0x72, 0x4d, 0x6f, 0x76, 0x65, 0x44, 0x61, 0x74, 0x54, 0x6f, 0x52, - 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x12, 0x32, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, - 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x54, - 0x69, 0x65, 0x72, 0x4d, 0x6f, 0x76, 0x65, 0x44, 0x61, 0x74, 0x54, 0x6f, 0x52, 0x65, 0x6d, 0x6f, - 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x33, 0x2e, 0x76, 0x6f, 0x6c, 0x75, - 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, + 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x69, 0x64, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x49, 0x64, + 0x12, 0x1e, 0x0a, 0x0a, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, + 0x22, 0x20, 0x0a, 0x1e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x45, 0x63, 0x53, 0x68, 0x61, 0x72, + 0x64, 0x73, 0x54, 0x6f, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x22, 0x3a, 0x0a, 0x1b, 0x52, 0x65, 0x61, 0x64, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, + 0x46, 0x69, 0x6c, 0x65, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x49, 0x64, 0x22, 0x8a, + 0x03, 0x0a, 0x1c, 0x52, 0x65, 0x61, 0x64, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x46, 0x69, 0x6c, + 0x65, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0x1b, 0x0a, 0x09, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x0d, 0x52, 0x08, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x49, 0x64, 0x12, 0x3b, 0x0a, 0x1a, + 0x69, 0x64, 0x78, 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, + 0x6d, 0x70, 0x5f, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, + 0x52, 0x17, 0x69, 0x64, 0x78, 0x46, 0x69, 0x6c, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, + 0x6d, 0x70, 0x53, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x12, 0x22, 0x0a, 0x0d, 0x69, 0x64, 0x78, + 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, + 0x52, 0x0b, 0x69, 0x64, 0x78, 0x46, 0x69, 0x6c, 0x65, 0x53, 0x69, 0x7a, 0x65, 0x12, 0x3b, 0x0a, + 0x1a, 0x64, 0x61, 0x74, 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, + 0x61, 0x6d, 0x70, 0x5f, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, + 0x04, 0x52, 0x17, 0x64, 0x61, 0x74, 0x46, 0x69, 0x6c, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, + 0x61, 0x6d, 0x70, 0x53, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x12, 0x22, 0x0a, 0x0d, 0x64, 0x61, + 0x74, 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, + 0x04, 0x52, 0x0b, 0x64, 0x61, 0x74, 0x46, 0x69, 0x6c, 0x65, 0x53, 0x69, 0x7a, 0x65, 0x12, 0x1d, + 0x0a, 0x0a, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x06, 0x20, 0x01, + 0x28, 0x04, 0x52, 0x09, 0x66, 0x69, 0x6c, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x2f, 0x0a, + 0x13, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x72, 0x65, 0x76, 0x69, + 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x12, 0x63, 0x6f, 0x6d, 0x70, + 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x1e, + 0x0a, 0x0a, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x08, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x0a, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1b, + 0x0a, 0x09, 0x64, 0x69, 0x73, 0x6b, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x08, 0x64, 0x69, 0x73, 0x6b, 0x54, 0x79, 0x70, 0x65, 0x22, 0xbb, 0x01, 0x0a, 0x0a, + 0x44, 0x69, 0x73, 0x6b, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x10, 0x0a, 0x03, 0x64, 0x69, + 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x64, 0x69, 0x72, 0x12, 0x10, 0x0a, 0x03, + 0x61, 0x6c, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x03, 0x61, 0x6c, 0x6c, 0x12, 0x12, + 0x0a, 0x04, 0x75, 0x73, 0x65, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x04, 0x75, 0x73, + 0x65, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x66, 0x72, 0x65, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, + 0x52, 0x04, 0x66, 0x72, 0x65, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x70, 0x65, 0x72, 0x63, 0x65, 0x6e, + 0x74, 0x5f, 0x66, 0x72, 0x65, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x02, 0x52, 0x0b, 0x70, 0x65, + 0x72, 0x63, 0x65, 0x6e, 0x74, 0x46, 0x72, 0x65, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x70, 0x65, 0x72, + 0x63, 0x65, 0x6e, 0x74, 0x5f, 0x75, 0x73, 0x65, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x02, 0x52, + 0x0b, 0x70, 0x65, 0x72, 0x63, 0x65, 0x6e, 0x74, 0x55, 0x73, 0x65, 0x64, 0x12, 0x1b, 0x0a, 0x09, + 0x64, 0x69, 0x73, 0x6b, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x08, 0x64, 0x69, 0x73, 0x6b, 0x54, 0x79, 0x70, 0x65, 0x22, 0xa3, 0x01, 0x0a, 0x09, 0x4d, 0x65, + 0x6d, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x1e, 0x0a, 0x0a, 0x67, 0x6f, 0x72, 0x6f, 0x75, + 0x74, 0x69, 0x6e, 0x65, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0a, 0x67, 0x6f, 0x72, + 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x65, 0x73, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x6c, 0x6c, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x04, 0x52, 0x03, 0x61, 0x6c, 0x6c, 0x12, 0x12, 0x0a, 0x04, 0x75, 0x73, 0x65, + 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x04, 0x75, 0x73, 0x65, 0x64, 0x12, 0x12, 0x0a, + 0x04, 0x66, 0x72, 0x65, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x04, 0x66, 0x72, 0x65, + 0x65, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x65, 0x6c, 0x66, 0x18, 0x05, 0x20, 0x01, 0x28, 0x04, 0x52, + 0x04, 0x73, 0x65, 0x6c, 0x66, 0x12, 0x12, 0x0a, 0x04, 0x68, 0x65, 0x61, 0x70, 0x18, 0x06, 0x20, + 0x01, 0x28, 0x04, 0x52, 0x04, 0x68, 0x65, 0x61, 0x70, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x74, 0x61, + 0x63, 0x6b, 0x18, 0x07, 0x20, 0x01, 0x28, 0x04, 0x52, 0x05, 0x73, 0x74, 0x61, 0x63, 0x6b, 0x22, + 0xd8, 0x01, 0x0a, 0x0a, 0x52, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x46, 0x69, 0x6c, 0x65, 0x12, 0x21, + 0x0a, 0x0c, 0x62, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x62, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x54, 0x79, 0x70, + 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x62, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x5f, 0x69, 0x64, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x62, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x49, 0x64, + 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, + 0x65, 0x79, 0x12, 0x16, 0x0a, 0x06, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x18, 0x04, 0x20, 0x01, + 0x28, 0x04, 0x52, 0x06, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x66, 0x69, + 0x6c, 0x65, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x04, 0x52, 0x08, 0x66, + 0x69, 0x6c, 0x65, 0x53, 0x69, 0x7a, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x6d, 0x6f, 0x64, 0x69, 0x66, + 0x69, 0x65, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0c, + 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x69, 0x65, 0x64, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x1c, 0x0a, 0x09, + 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x09, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x7c, 0x0a, 0x0a, 0x56, 0x6f, + 0x6c, 0x75, 0x6d, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x32, 0x0a, 0x05, 0x66, 0x69, 0x6c, 0x65, + 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, + 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x74, + 0x65, 0x46, 0x69, 0x6c, 0x65, 0x52, 0x05, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x12, 0x18, 0x0a, 0x07, + 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x76, + 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x20, 0x0a, 0x0b, 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x72, 0x65, 0x70, + 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0xc8, 0x01, 0x0a, 0x20, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x54, 0x69, 0x65, 0x72, 0x4d, 0x6f, 0x76, 0x65, 0x44, 0x61, 0x74, 0x54, 0x6f, - 0x52, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, - 0x30, 0x01, 0x12, 0x8e, 0x01, 0x0a, 0x1b, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x54, 0x69, 0x65, - 0x72, 0x4d, 0x6f, 0x76, 0x65, 0x44, 0x61, 0x74, 0x46, 0x72, 0x6f, 0x6d, 0x52, 0x65, 0x6d, 0x6f, - 0x74, 0x65, 0x12, 0x34, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, - 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x54, 0x69, 0x65, 0x72, - 0x4d, 0x6f, 0x76, 0x65, 0x44, 0x61, 0x74, 0x46, 0x72, 0x6f, 0x6d, 0x52, 0x65, 0x6d, 0x6f, 0x74, - 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x35, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, - 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, - 0x6d, 0x65, 0x54, 0x69, 0x65, 0x72, 0x4d, 0x6f, 0x76, 0x65, 0x44, 0x61, 0x74, 0x46, 0x72, 0x6f, - 0x6d, 0x52, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, - 0x00, 0x30, 0x01, 0x12, 0x71, 0x0a, 0x12, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x53, 0x65, 0x72, - 0x76, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x2b, 0x2e, 0x76, 0x6f, 0x6c, 0x75, - 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, - 0x75, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2c, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, - 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, - 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x6e, 0x0a, 0x11, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, - 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x4c, 0x65, 0x61, 0x76, 0x65, 0x12, 0x2a, 0x2e, 0x76, 0x6f, - 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, - 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x4c, 0x65, 0x61, 0x76, 0x65, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2b, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, - 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, - 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x4c, 0x65, 0x61, 0x76, 0x65, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x74, 0x0a, 0x13, 0x46, 0x65, 0x74, 0x63, 0x68, 0x41, - 0x6e, 0x64, 0x57, 0x72, 0x69, 0x74, 0x65, 0x4e, 0x65, 0x65, 0x64, 0x6c, 0x65, 0x12, 0x2c, 0x2e, + 0x52, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, + 0x09, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, + 0x52, 0x08, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x49, 0x64, 0x12, 0x1e, 0x0a, 0x0a, 0x63, 0x6f, + 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, + 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x38, 0x0a, 0x18, 0x64, 0x65, + 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x62, 0x61, 0x63, 0x6b, 0x65, 0x6e, + 0x64, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x16, 0x64, 0x65, + 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, + 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x2d, 0x0a, 0x13, 0x6b, 0x65, 0x65, 0x70, 0x5f, 0x6c, 0x6f, 0x63, + 0x61, 0x6c, 0x5f, 0x64, 0x61, 0x74, 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, + 0x08, 0x52, 0x10, 0x6b, 0x65, 0x65, 0x70, 0x4c, 0x6f, 0x63, 0x61, 0x6c, 0x44, 0x61, 0x74, 0x46, + 0x69, 0x6c, 0x65, 0x22, 0x73, 0x0a, 0x21, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x54, 0x69, 0x65, + 0x72, 0x4d, 0x6f, 0x76, 0x65, 0x44, 0x61, 0x74, 0x54, 0x6f, 0x52, 0x65, 0x6d, 0x6f, 0x74, 0x65, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x70, 0x72, 0x6f, 0x63, + 0x65, 0x73, 0x73, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x70, 0x72, 0x6f, + 0x63, 0x65, 0x73, 0x73, 0x65, 0x64, 0x12, 0x30, 0x0a, 0x13, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, + 0x73, 0x65, 0x64, 0x50, 0x65, 0x72, 0x63, 0x65, 0x6e, 0x74, 0x61, 0x67, 0x65, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x02, 0x52, 0x13, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x65, 0x64, 0x50, 0x65, + 0x72, 0x63, 0x65, 0x6e, 0x74, 0x61, 0x67, 0x65, 0x22, 0x92, 0x01, 0x0a, 0x22, 0x56, 0x6f, 0x6c, + 0x75, 0x6d, 0x65, 0x54, 0x69, 0x65, 0x72, 0x4d, 0x6f, 0x76, 0x65, 0x44, 0x61, 0x74, 0x46, 0x72, + 0x6f, 0x6d, 0x52, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, + 0x1b, 0x0a, 0x09, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x0d, 0x52, 0x08, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x49, 0x64, 0x12, 0x1e, 0x0a, 0x0a, + 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x0a, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x2f, 0x0a, 0x14, + 0x6b, 0x65, 0x65, 0x70, 0x5f, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x5f, 0x64, 0x61, 0x74, 0x5f, + 0x66, 0x69, 0x6c, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x11, 0x6b, 0x65, 0x65, 0x70, + 0x52, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x44, 0x61, 0x74, 0x46, 0x69, 0x6c, 0x65, 0x22, 0x75, 0x0a, + 0x23, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x54, 0x69, 0x65, 0x72, 0x4d, 0x6f, 0x76, 0x65, 0x44, + 0x61, 0x74, 0x46, 0x72, 0x6f, 0x6d, 0x52, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x65, + 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, + 0x65, 0x64, 0x12, 0x30, 0x0a, 0x13, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x65, 0x64, 0x50, + 0x65, 0x72, 0x63, 0x65, 0x6e, 0x74, 0x61, 0x67, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x02, 0x52, + 0x13, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x65, 0x64, 0x50, 0x65, 0x72, 0x63, 0x65, 0x6e, + 0x74, 0x61, 0x67, 0x65, 0x22, 0x1b, 0x0a, 0x19, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x53, 0x65, + 0x72, 0x76, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x22, 0xa1, 0x01, 0x0a, 0x1a, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, + 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x12, 0x41, 0x0a, 0x0d, 0x64, 0x69, 0x73, 0x6b, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x65, + 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, + 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x44, 0x69, 0x73, 0x6b, 0x53, + 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x0c, 0x64, 0x69, 0x73, 0x6b, 0x53, 0x74, 0x61, 0x74, 0x75, + 0x73, 0x65, 0x73, 0x12, 0x40, 0x0a, 0x0d, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x5f, 0x73, 0x74, + 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x76, 0x6f, 0x6c, + 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x4d, 0x65, + 0x6d, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x0c, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x53, + 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0x1a, 0x0a, 0x18, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x53, + 0x65, 0x72, 0x76, 0x65, 0x72, 0x4c, 0x65, 0x61, 0x76, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x22, 0x1b, 0x0a, 0x19, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, + 0x72, 0x4c, 0x65, 0x61, 0x76, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0xdc, + 0x03, 0x0a, 0x1a, 0x46, 0x65, 0x74, 0x63, 0x68, 0x41, 0x6e, 0x64, 0x57, 0x72, 0x69, 0x74, 0x65, + 0x4e, 0x65, 0x65, 0x64, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, + 0x09, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, + 0x52, 0x08, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x49, 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x6e, 0x65, + 0x65, 0x64, 0x6c, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x08, 0x6e, + 0x65, 0x65, 0x64, 0x6c, 0x65, 0x49, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x63, 0x6f, 0x6f, 0x6b, 0x69, + 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x06, 0x63, 0x6f, 0x6f, 0x6b, 0x69, 0x65, 0x12, + 0x16, 0x0a, 0x06, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, + 0x06, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x18, + 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x12, 0x50, 0x0a, 0x08, 0x72, + 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x34, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x46, 0x65, 0x74, 0x63, 0x68, 0x41, 0x6e, 0x64, 0x57, 0x72, 0x69, 0x74, 0x65, 0x4e, 0x65, - 0x65, 0x64, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2d, 0x2e, 0x76, 0x6f, - 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x46, - 0x65, 0x74, 0x63, 0x68, 0x41, 0x6e, 0x64, 0x57, 0x72, 0x69, 0x74, 0x65, 0x4e, 0x65, 0x65, 0x64, - 0x6c, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x4c, 0x0a, 0x05, - 0x51, 0x75, 0x65, 0x72, 0x79, 0x12, 0x1e, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, - 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, - 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x69, 0x65, 0x64, - 0x53, 0x74, 0x72, 0x69, 0x70, 0x65, 0x22, 0x00, 0x30, 0x01, 0x12, 0x71, 0x0a, 0x12, 0x56, 0x6f, - 0x6c, 0x75, 0x6d, 0x65, 0x4e, 0x65, 0x65, 0x64, 0x6c, 0x65, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, - 0x12, 0x2b, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, - 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x4e, 0x65, 0x65, 0x64, 0x6c, 0x65, - 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2c, 0x2e, + 0x65, 0x64, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x52, 0x65, 0x70, 0x6c, + 0x69, 0x63, 0x61, 0x52, 0x08, 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x73, 0x12, 0x12, 0x0a, + 0x04, 0x61, 0x75, 0x74, 0x68, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x61, 0x75, 0x74, + 0x68, 0x12, 0x36, 0x0a, 0x0b, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66, + 0x18, 0x0f, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x5f, + 0x70, 0x62, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x52, 0x0a, 0x72, + 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x12, 0x49, 0x0a, 0x0f, 0x72, 0x65, 0x6d, + 0x6f, 0x74, 0x65, 0x5f, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x10, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x5f, 0x70, 0x62, 0x2e, 0x52, + 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x4c, 0x6f, 0x63, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0e, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x4c, 0x6f, 0x63, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x1a, 0x57, 0x0a, 0x07, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x12, + 0x10, 0x0a, 0x03, 0x75, 0x72, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, + 0x6c, 0x12, 0x1d, 0x0a, 0x0a, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x75, 0x72, 0x6c, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x55, 0x72, 0x6c, + 0x12, 0x1b, 0x0a, 0x09, 0x67, 0x72, 0x70, 0x63, 0x5f, 0x70, 0x6f, 0x72, 0x74, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x05, 0x52, 0x08, 0x67, 0x72, 0x70, 0x63, 0x50, 0x6f, 0x72, 0x74, 0x22, 0x1d, 0x0a, + 0x1b, 0x46, 0x65, 0x74, 0x63, 0x68, 0x41, 0x6e, 0x64, 0x57, 0x72, 0x69, 0x74, 0x65, 0x4e, 0x65, + 0x65, 0x64, 0x6c, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0xf8, 0x0c, 0x0a, + 0x0c, 0x51, 0x75, 0x65, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1e, 0x0a, + 0x0a, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, + 0x09, 0x52, 0x0a, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x22, 0x0a, + 0x0d, 0x66, 0x72, 0x6f, 0x6d, 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x02, + 0x20, 0x03, 0x28, 0x09, 0x52, 0x0b, 0x66, 0x72, 0x6f, 0x6d, 0x46, 0x69, 0x6c, 0x65, 0x49, 0x64, + 0x73, 0x12, 0x3d, 0x0a, 0x06, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x25, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, + 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x2e, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x52, 0x06, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, + 0x12, 0x62, 0x0a, 0x13, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x5f, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, + 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x31, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, - 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x4e, 0x65, 0x65, 0x64, 0x6c, 0x65, 0x53, 0x74, 0x61, - 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, 0x39, 0x5a, - 0x37, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x68, 0x72, 0x69, - 0x73, 0x6c, 0x75, 0x73, 0x66, 0x2f, 0x73, 0x65, 0x61, 0x77, 0x65, 0x65, 0x64, 0x66, 0x73, 0x2f, - 0x77, 0x65, 0x65, 0x64, 0x2f, 0x70, 0x62, 0x2f, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, - 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x49, 0x6e, + 0x70, 0x75, 0x74, 0x53, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x52, 0x12, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x53, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x65, 0x0a, 0x14, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x5f, 0x73, + 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x32, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, + 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x2e, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x53, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, + 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x13, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x53, 0x65, + 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x1a, 0x4e, 0x0a, 0x06, 0x46, + 0x69, 0x6c, 0x74, 0x65, 0x72, 0x12, 0x14, 0x0a, 0x05, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x6f, + 0x70, 0x65, 0x72, 0x61, 0x6e, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6f, 0x70, + 0x65, 0x72, 0x61, 0x6e, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x1a, 0xd5, 0x05, 0x0a, 0x12, + 0x49, 0x6e, 0x70, 0x75, 0x74, 0x53, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x12, 0x29, 0x0a, 0x10, 0x63, 0x6f, 0x6d, 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, + 0x6e, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x63, 0x6f, + 0x6d, 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x54, 0x79, 0x70, 0x65, 0x12, 0x57, 0x0a, + 0x09, 0x63, 0x73, 0x76, 0x5f, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x3a, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, + 0x5f, 0x70, 0x62, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x2e, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x53, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x2e, 0x43, 0x53, 0x56, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x52, 0x08, 0x63, 0x73, + 0x76, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x12, 0x5a, 0x0a, 0x0a, 0x6a, 0x73, 0x6f, 0x6e, 0x5f, 0x69, + 0x6e, 0x70, 0x75, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x3b, 0x2e, 0x76, 0x6f, 0x6c, + 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x51, 0x75, + 0x65, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x49, 0x6e, 0x70, 0x75, 0x74, + 0x53, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x4a, 0x53, + 0x4f, 0x4e, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x52, 0x09, 0x6a, 0x73, 0x6f, 0x6e, 0x49, 0x6e, 0x70, + 0x75, 0x74, 0x12, 0x63, 0x0a, 0x0d, 0x70, 0x61, 0x72, 0x71, 0x75, 0x65, 0x74, 0x5f, 0x69, 0x6e, + 0x70, 0x75, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x3e, 0x2e, 0x76, 0x6f, 0x6c, 0x75, + 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x51, 0x75, 0x65, + 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x53, + 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x50, 0x61, 0x72, + 0x71, 0x75, 0x65, 0x74, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x52, 0x0c, 0x70, 0x61, 0x72, 0x71, 0x75, + 0x65, 0x74, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x1a, 0xc8, 0x02, 0x0a, 0x08, 0x43, 0x53, 0x56, 0x49, + 0x6e, 0x70, 0x75, 0x74, 0x12, 0x28, 0x0a, 0x10, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x68, 0x65, 0x61, + 0x64, 0x65, 0x72, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, + 0x66, 0x69, 0x6c, 0x65, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x29, + 0x0a, 0x10, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x5f, 0x64, 0x65, 0x6c, 0x69, 0x6d, 0x69, 0x74, + 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, + 0x44, 0x65, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x65, 0x72, 0x12, 0x27, 0x0a, 0x0f, 0x66, 0x69, 0x65, + 0x6c, 0x64, 0x5f, 0x64, 0x65, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x65, 0x72, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x0e, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x44, 0x65, 0x6c, 0x69, 0x6d, 0x69, 0x74, + 0x65, 0x72, 0x12, 0x29, 0x0a, 0x10, 0x71, 0x75, 0x6f, 0x74, 0x65, 0x5f, 0x63, 0x68, 0x61, 0x72, + 0x61, 0x63, 0x74, 0x6f, 0x65, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x71, 0x75, + 0x6f, 0x74, 0x65, 0x43, 0x68, 0x61, 0x72, 0x61, 0x63, 0x74, 0x6f, 0x65, 0x72, 0x12, 0x34, 0x0a, + 0x16, 0x71, 0x75, 0x6f, 0x74, 0x65, 0x5f, 0x65, 0x73, 0x63, 0x61, 0x70, 0x65, 0x5f, 0x63, 0x68, + 0x61, 0x72, 0x61, 0x63, 0x74, 0x65, 0x72, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x14, 0x71, + 0x75, 0x6f, 0x74, 0x65, 0x45, 0x73, 0x63, 0x61, 0x70, 0x65, 0x43, 0x68, 0x61, 0x72, 0x61, 0x63, + 0x74, 0x65, 0x72, 0x12, 0x1a, 0x0a, 0x08, 0x63, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x18, + 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x63, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x12, + 0x41, 0x0a, 0x1d, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x5f, 0x71, 0x75, 0x6f, 0x74, 0x65, 0x64, 0x5f, + 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x5f, 0x64, 0x65, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x65, 0x72, + 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x1a, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x51, 0x75, 0x6f, + 0x74, 0x65, 0x64, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x44, 0x65, 0x6c, 0x69, 0x6d, 0x69, 0x74, + 0x65, 0x72, 0x1a, 0x1f, 0x0a, 0x09, 0x4a, 0x53, 0x4f, 0x4e, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x12, + 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, + 0x79, 0x70, 0x65, 0x1a, 0x0e, 0x0a, 0x0c, 0x50, 0x61, 0x72, 0x71, 0x75, 0x65, 0x74, 0x49, 0x6e, + 0x70, 0x75, 0x74, 0x1a, 0xf1, 0x03, 0x0a, 0x13, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x53, 0x65, + 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x5b, 0x0a, 0x0a, 0x63, + 0x73, 0x76, 0x5f, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x3c, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, + 0x70, 0x62, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, + 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x53, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x2e, 0x43, 0x53, 0x56, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x52, 0x09, 0x63, + 0x73, 0x76, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x12, 0x5e, 0x0a, 0x0b, 0x6a, 0x73, 0x6f, 0x6e, + 0x5f, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x3d, 0x2e, + 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, + 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x4f, 0x75, + 0x74, 0x70, 0x75, 0x74, 0x53, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x2e, 0x4a, 0x53, 0x4f, 0x4e, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x52, 0x0a, 0x6a, 0x73, + 0x6f, 0x6e, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x1a, 0xe3, 0x01, 0x0a, 0x09, 0x43, 0x53, 0x56, + 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x71, 0x75, 0x6f, 0x74, 0x65, 0x5f, + 0x66, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x71, 0x75, + 0x6f, 0x74, 0x65, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x12, 0x29, 0x0a, 0x10, 0x72, 0x65, 0x63, + 0x6f, 0x72, 0x64, 0x5f, 0x64, 0x65, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x65, 0x72, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x0f, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x44, 0x65, 0x6c, 0x69, 0x6d, + 0x69, 0x74, 0x65, 0x72, 0x12, 0x27, 0x0a, 0x0f, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x5f, 0x64, 0x65, + 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x65, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x66, + 0x69, 0x65, 0x6c, 0x64, 0x44, 0x65, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x65, 0x72, 0x12, 0x29, 0x0a, + 0x10, 0x71, 0x75, 0x6f, 0x74, 0x65, 0x5f, 0x63, 0x68, 0x61, 0x72, 0x61, 0x63, 0x74, 0x6f, 0x65, + 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x71, 0x75, 0x6f, 0x74, 0x65, 0x43, 0x68, + 0x61, 0x72, 0x61, 0x63, 0x74, 0x6f, 0x65, 0x72, 0x12, 0x34, 0x0a, 0x16, 0x71, 0x75, 0x6f, 0x74, + 0x65, 0x5f, 0x65, 0x73, 0x63, 0x61, 0x70, 0x65, 0x5f, 0x63, 0x68, 0x61, 0x72, 0x61, 0x63, 0x74, + 0x65, 0x72, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x14, 0x71, 0x75, 0x6f, 0x74, 0x65, 0x45, + 0x73, 0x63, 0x61, 0x70, 0x65, 0x43, 0x68, 0x61, 0x72, 0x61, 0x63, 0x74, 0x65, 0x72, 0x1a, 0x37, + 0x0a, 0x0a, 0x4a, 0x53, 0x4f, 0x4e, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x12, 0x29, 0x0a, 0x10, + 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x5f, 0x64, 0x65, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x65, 0x72, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x44, 0x65, + 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x65, 0x72, 0x22, 0x29, 0x0a, 0x0d, 0x51, 0x75, 0x65, 0x72, 0x69, + 0x65, 0x64, 0x53, 0x74, 0x72, 0x69, 0x70, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x63, 0x6f, + 0x72, 0x64, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x72, 0x65, 0x63, 0x6f, 0x72, + 0x64, 0x73, 0x22, 0x55, 0x0a, 0x19, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x4e, 0x65, 0x65, 0x64, + 0x6c, 0x65, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, + 0x1b, 0x0a, 0x09, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x0d, 0x52, 0x08, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x49, 0x64, 0x12, 0x1b, 0x0a, 0x09, + 0x6e, 0x65, 0x65, 0x64, 0x6c, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, + 0x08, 0x6e, 0x65, 0x65, 0x64, 0x6c, 0x65, 0x49, 0x64, 0x22, 0xae, 0x01, 0x0a, 0x1a, 0x56, 0x6f, + 0x6c, 0x75, 0x6d, 0x65, 0x4e, 0x65, 0x65, 0x64, 0x6c, 0x65, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x6e, 0x65, 0x65, 0x64, + 0x6c, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x08, 0x6e, 0x65, 0x65, + 0x64, 0x6c, 0x65, 0x49, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x63, 0x6f, 0x6f, 0x6b, 0x69, 0x65, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x06, 0x63, 0x6f, 0x6f, 0x6b, 0x69, 0x65, 0x12, 0x12, 0x0a, + 0x04, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x04, 0x73, 0x69, 0x7a, + 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x69, + 0x65, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0c, 0x6c, 0x61, 0x73, 0x74, 0x4d, 0x6f, + 0x64, 0x69, 0x66, 0x69, 0x65, 0x64, 0x12, 0x10, 0x0a, 0x03, 0x63, 0x72, 0x63, 0x18, 0x05, 0x20, + 0x01, 0x28, 0x0d, 0x52, 0x03, 0x63, 0x72, 0x63, 0x12, 0x10, 0x0a, 0x03, 0x74, 0x74, 0x6c, 0x18, + 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x74, 0x74, 0x6c, 0x32, 0x88, 0x23, 0x0a, 0x0c, 0x56, + 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x12, 0x5c, 0x0a, 0x0b, 0x42, + 0x61, 0x74, 0x63, 0x68, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x12, 0x24, 0x2e, 0x76, 0x6f, 0x6c, + 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x42, 0x61, + 0x74, 0x63, 0x68, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x25, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, + 0x5f, 0x70, 0x62, 0x2e, 0x42, 0x61, 0x74, 0x63, 0x68, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x6e, 0x0a, 0x11, 0x56, 0x61, 0x63, + 0x75, 0x75, 0x6d, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x12, 0x2a, + 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, + 0x62, 0x2e, 0x56, 0x61, 0x63, 0x75, 0x75, 0x6d, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x43, 0x68, + 0x65, 0x63, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2b, 0x2e, 0x76, 0x6f, 0x6c, + 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x61, + 0x63, 0x75, 0x75, 0x6d, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x74, 0x0a, 0x13, 0x56, 0x61, 0x63, + 0x75, 0x75, 0x6d, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x43, 0x6f, 0x6d, 0x70, 0x61, 0x63, 0x74, + 0x12, 0x2c, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, + 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x61, 0x63, 0x75, 0x75, 0x6d, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, + 0x43, 0x6f, 0x6d, 0x70, 0x61, 0x63, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2d, + 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, + 0x62, 0x2e, 0x56, 0x61, 0x63, 0x75, 0x75, 0x6d, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x43, 0x6f, + 0x6d, 0x70, 0x61, 0x63, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, + 0x71, 0x0a, 0x12, 0x56, 0x61, 0x63, 0x75, 0x75, 0x6d, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x43, + 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x12, 0x2b, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, + 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x61, 0x63, 0x75, 0x75, 0x6d, 0x56, + 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x2c, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, + 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x61, 0x63, 0x75, 0x75, 0x6d, 0x56, 0x6f, 0x6c, 0x75, + 0x6d, 0x65, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x22, 0x00, 0x12, 0x74, 0x0a, 0x13, 0x56, 0x61, 0x63, 0x75, 0x75, 0x6d, 0x56, 0x6f, 0x6c, 0x75, + 0x6d, 0x65, 0x43, 0x6c, 0x65, 0x61, 0x6e, 0x75, 0x70, 0x12, 0x2c, 0x2e, 0x76, 0x6f, 0x6c, 0x75, + 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x61, 0x63, + 0x75, 0x75, 0x6d, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x43, 0x6c, 0x65, 0x61, 0x6e, 0x75, 0x70, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2d, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, + 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x61, 0x63, 0x75, 0x75, + 0x6d, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x43, 0x6c, 0x65, 0x61, 0x6e, 0x75, 0x70, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x6b, 0x0a, 0x10, 0x44, 0x65, 0x6c, 0x65, + 0x74, 0x65, 0x43, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x29, 0x2e, 0x76, + 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, + 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x43, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2a, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, + 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, + 0x65, 0x43, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x65, 0x0a, 0x0e, 0x41, 0x6c, 0x6c, 0x6f, 0x63, 0x61, 0x74, + 0x65, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x12, 0x27, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, + 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x41, 0x6c, 0x6c, 0x6f, 0x63, + 0x61, 0x74, 0x65, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x28, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, + 0x5f, 0x70, 0x62, 0x2e, 0x41, 0x6c, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x65, 0x56, 0x6f, 0x6c, 0x75, + 0x6d, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x6b, 0x0a, 0x10, + 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x53, 0x79, 0x6e, 0x63, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, + 0x12, 0x29, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, + 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x53, 0x79, 0x6e, 0x63, 0x53, 0x74, + 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2a, 0x2e, 0x76, 0x6f, + 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, + 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x53, 0x79, 0x6e, 0x63, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x7c, 0x0a, 0x15, 0x56, 0x6f, 0x6c, + 0x75, 0x6d, 0x65, 0x49, 0x6e, 0x63, 0x72, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x61, 0x6c, 0x43, 0x6f, + 0x70, 0x79, 0x12, 0x2e, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, + 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x49, 0x6e, 0x63, 0x72, + 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x61, 0x6c, 0x43, 0x6f, 0x70, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x2f, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, + 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x49, 0x6e, 0x63, 0x72, + 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x61, 0x6c, 0x43, 0x6f, 0x70, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x22, 0x00, 0x30, 0x01, 0x12, 0x5c, 0x0a, 0x0b, 0x56, 0x6f, 0x6c, 0x75, 0x6d, + 0x65, 0x4d, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x24, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, + 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, + 0x4d, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x25, 0x2e, 0x76, + 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, + 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x4d, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x62, 0x0a, 0x0d, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x55, + 0x6e, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x26, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, + 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, + 0x55, 0x6e, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x27, + 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, + 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x55, 0x6e, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x5f, 0x0a, 0x0c, 0x56, 0x6f, 0x6c, + 0x75, 0x6d, 0x65, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x12, 0x25, 0x2e, 0x76, 0x6f, 0x6c, 0x75, + 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, + 0x75, 0x6d, 0x65, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x26, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, + 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x71, 0x0a, 0x12, 0x56, 0x6f, + 0x6c, 0x75, 0x6d, 0x65, 0x4d, 0x61, 0x72, 0x6b, 0x52, 0x65, 0x61, 0x64, 0x6f, 0x6e, 0x6c, 0x79, + 0x12, 0x2b, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, + 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x4d, 0x61, 0x72, 0x6b, 0x52, 0x65, + 0x61, 0x64, 0x6f, 0x6e, 0x6c, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2c, 0x2e, + 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, + 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x4d, 0x61, 0x72, 0x6b, 0x52, 0x65, 0x61, 0x64, 0x6f, + 0x6e, 0x6c, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x71, 0x0a, + 0x12, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x4d, 0x61, 0x72, 0x6b, 0x57, 0x72, 0x69, 0x74, 0x61, + 0x62, 0x6c, 0x65, 0x12, 0x2b, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, + 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x4d, 0x61, 0x72, + 0x6b, 0x57, 0x72, 0x69, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x2c, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, + 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x4d, 0x61, 0x72, 0x6b, 0x57, 0x72, + 0x69, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, + 0x12, 0x68, 0x0a, 0x0f, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, + 0x75, 0x72, 0x65, 0x12, 0x28, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, + 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x43, 0x6f, 0x6e, + 0x66, 0x69, 0x67, 0x75, 0x72, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x29, 0x2e, + 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, + 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x65, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x5f, 0x0a, 0x0c, 0x56, 0x6f, + 0x6c, 0x75, 0x6d, 0x65, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x25, 0x2e, 0x76, 0x6f, 0x6c, + 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, + 0x6c, 0x75, 0x6d, 0x65, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x26, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, + 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x53, 0x74, 0x61, 0x74, 0x75, + 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x59, 0x0a, 0x0a, 0x56, + 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x43, 0x6f, 0x70, 0x79, 0x12, 0x23, 0x2e, 0x76, 0x6f, 0x6c, 0x75, + 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, + 0x75, 0x6d, 0x65, 0x43, 0x6f, 0x70, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, + 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, + 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x43, 0x6f, 0x70, 0x79, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x77, 0x0a, 0x14, 0x52, 0x65, 0x61, 0x64, 0x56, 0x6f, + 0x6c, 0x75, 0x6d, 0x65, 0x46, 0x69, 0x6c, 0x65, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x2d, + 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, + 0x62, 0x2e, 0x52, 0x65, 0x61, 0x64, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x46, 0x69, 0x6c, 0x65, + 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2e, 0x2e, + 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, + 0x2e, 0x52, 0x65, 0x61, 0x64, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x46, 0x69, 0x6c, 0x65, 0x53, + 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, + 0x55, 0x0a, 0x08, 0x43, 0x6f, 0x70, 0x79, 0x46, 0x69, 0x6c, 0x65, 0x12, 0x21, 0x2e, 0x76, 0x6f, + 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x43, + 0x6f, 0x70, 0x79, 0x46, 0x69, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, + 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, + 0x62, 0x2e, 0x43, 0x6f, 0x70, 0x79, 0x46, 0x69, 0x6c, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x22, 0x00, 0x30, 0x01, 0x12, 0x65, 0x0a, 0x0e, 0x52, 0x65, 0x61, 0x64, 0x4e, 0x65, + 0x65, 0x64, 0x6c, 0x65, 0x42, 0x6c, 0x6f, 0x62, 0x12, 0x27, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, + 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x52, 0x65, 0x61, 0x64, + 0x4e, 0x65, 0x65, 0x64, 0x6c, 0x65, 0x42, 0x6c, 0x6f, 0x62, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x28, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, + 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x52, 0x65, 0x61, 0x64, 0x4e, 0x65, 0x65, 0x64, 0x6c, 0x65, 0x42, + 0x6c, 0x6f, 0x62, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x68, 0x0a, + 0x0f, 0x57, 0x72, 0x69, 0x74, 0x65, 0x4e, 0x65, 0x65, 0x64, 0x6c, 0x65, 0x42, 0x6c, 0x6f, 0x62, + 0x12, 0x28, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, + 0x5f, 0x70, 0x62, 0x2e, 0x57, 0x72, 0x69, 0x74, 0x65, 0x4e, 0x65, 0x65, 0x64, 0x6c, 0x65, 0x42, + 0x6c, 0x6f, 0x62, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x29, 0x2e, 0x76, 0x6f, 0x6c, + 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x57, 0x72, + 0x69, 0x74, 0x65, 0x4e, 0x65, 0x65, 0x64, 0x6c, 0x65, 0x42, 0x6c, 0x6f, 0x62, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x67, 0x0a, 0x0e, 0x52, 0x65, 0x61, 0x64, 0x41, + 0x6c, 0x6c, 0x4e, 0x65, 0x65, 0x64, 0x6c, 0x65, 0x73, 0x12, 0x27, 0x2e, 0x76, 0x6f, 0x6c, 0x75, + 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x52, 0x65, 0x61, + 0x64, 0x41, 0x6c, 0x6c, 0x4e, 0x65, 0x65, 0x64, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x28, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, + 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x52, 0x65, 0x61, 0x64, 0x41, 0x6c, 0x6c, 0x4e, 0x65, 0x65, + 0x64, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x30, 0x01, + 0x12, 0x6d, 0x0a, 0x10, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x54, 0x61, 0x69, 0x6c, 0x53, 0x65, + 0x6e, 0x64, 0x65, 0x72, 0x12, 0x29, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, + 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x54, 0x61, + 0x69, 0x6c, 0x53, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x2a, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, + 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x54, 0x61, 0x69, 0x6c, 0x53, 0x65, 0x6e, + 0x64, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x30, 0x01, 0x12, + 0x71, 0x0a, 0x12, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x54, 0x61, 0x69, 0x6c, 0x52, 0x65, 0x63, + 0x65, 0x69, 0x76, 0x65, 0x72, 0x12, 0x2b, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, + 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x54, + 0x61, 0x69, 0x6c, 0x52, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x2c, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, + 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x54, 0x61, 0x69, 0x6c, + 0x52, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x22, 0x00, 0x12, 0x7d, 0x0a, 0x16, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x45, 0x63, 0x53, 0x68, + 0x61, 0x72, 0x64, 0x73, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x12, 0x2f, 0x2e, 0x76, + 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, + 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x45, 0x63, 0x53, 0x68, 0x61, 0x72, 0x64, 0x73, 0x47, 0x65, + 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x30, 0x2e, + 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, + 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x45, 0x63, 0x53, 0x68, 0x61, 0x72, 0x64, 0x73, 0x47, + 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, + 0x00, 0x12, 0x7a, 0x0a, 0x15, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x45, 0x63, 0x53, 0x68, 0x61, + 0x72, 0x64, 0x73, 0x52, 0x65, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x12, 0x2e, 0x2e, 0x76, 0x6f, 0x6c, + 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, + 0x6c, 0x75, 0x6d, 0x65, 0x45, 0x63, 0x53, 0x68, 0x61, 0x72, 0x64, 0x73, 0x52, 0x65, 0x62, 0x75, + 0x69, 0x6c, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2f, 0x2e, 0x76, 0x6f, 0x6c, + 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, + 0x6c, 0x75, 0x6d, 0x65, 0x45, 0x63, 0x53, 0x68, 0x61, 0x72, 0x64, 0x73, 0x52, 0x65, 0x62, 0x75, + 0x69, 0x6c, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x71, 0x0a, + 0x12, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x45, 0x63, 0x53, 0x68, 0x61, 0x72, 0x64, 0x73, 0x43, + 0x6f, 0x70, 0x79, 0x12, 0x2b, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, + 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x45, 0x63, 0x53, + 0x68, 0x61, 0x72, 0x64, 0x73, 0x43, 0x6f, 0x70, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x2c, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, + 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x45, 0x63, 0x53, 0x68, 0x61, 0x72, + 0x64, 0x73, 0x43, 0x6f, 0x70, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, + 0x12, 0x77, 0x0a, 0x14, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x45, 0x63, 0x53, 0x68, 0x61, 0x72, + 0x64, 0x73, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x12, 0x2d, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, + 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, + 0x6d, 0x65, 0x45, 0x63, 0x53, 0x68, 0x61, 0x72, 0x64, 0x73, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2e, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, + 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, + 0x65, 0x45, 0x63, 0x53, 0x68, 0x61, 0x72, 0x64, 0x73, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x74, 0x0a, 0x13, 0x56, 0x6f, 0x6c, + 0x75, 0x6d, 0x65, 0x45, 0x63, 0x53, 0x68, 0x61, 0x72, 0x64, 0x73, 0x4d, 0x6f, 0x75, 0x6e, 0x74, + 0x12, 0x2c, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, + 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x45, 0x63, 0x53, 0x68, 0x61, 0x72, + 0x64, 0x73, 0x4d, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2d, + 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, + 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x45, 0x63, 0x53, 0x68, 0x61, 0x72, 0x64, 0x73, + 0x4d, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, + 0x7a, 0x0a, 0x15, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x45, 0x63, 0x53, 0x68, 0x61, 0x72, 0x64, + 0x73, 0x55, 0x6e, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x2e, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, + 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, + 0x6d, 0x65, 0x45, 0x63, 0x53, 0x68, 0x61, 0x72, 0x64, 0x73, 0x55, 0x6e, 0x6d, 0x6f, 0x75, 0x6e, + 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2f, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, + 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, + 0x6d, 0x65, 0x45, 0x63, 0x53, 0x68, 0x61, 0x72, 0x64, 0x73, 0x55, 0x6e, 0x6d, 0x6f, 0x75, 0x6e, + 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x70, 0x0a, 0x11, 0x56, + 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x45, 0x63, 0x53, 0x68, 0x61, 0x72, 0x64, 0x52, 0x65, 0x61, 0x64, + 0x12, 0x2a, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, + 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x45, 0x63, 0x53, 0x68, 0x61, 0x72, + 0x64, 0x52, 0x65, 0x61, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2b, 0x2e, 0x76, + 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, + 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x45, 0x63, 0x53, 0x68, 0x61, 0x72, 0x64, 0x52, 0x65, 0x61, + 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x30, 0x01, 0x12, 0x71, 0x0a, + 0x12, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x45, 0x63, 0x42, 0x6c, 0x6f, 0x62, 0x44, 0x65, 0x6c, + 0x65, 0x74, 0x65, 0x12, 0x2b, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, + 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x45, 0x63, 0x42, + 0x6c, 0x6f, 0x62, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x2c, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, + 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x45, 0x63, 0x42, 0x6c, 0x6f, 0x62, + 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, + 0x12, 0x7d, 0x0a, 0x16, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x45, 0x63, 0x53, 0x68, 0x61, 0x72, + 0x64, 0x73, 0x54, 0x6f, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x12, 0x2f, 0x2e, 0x76, 0x6f, 0x6c, + 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, + 0x6c, 0x75, 0x6d, 0x65, 0x45, 0x63, 0x53, 0x68, 0x61, 0x72, 0x64, 0x73, 0x54, 0x6f, 0x56, 0x6f, + 0x6c, 0x75, 0x6d, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x30, 0x2e, 0x76, 0x6f, + 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, + 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x45, 0x63, 0x53, 0x68, 0x61, 0x72, 0x64, 0x73, 0x54, 0x6f, 0x56, + 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, + 0x88, 0x01, 0x0a, 0x19, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x54, 0x69, 0x65, 0x72, 0x4d, 0x6f, + 0x76, 0x65, 0x44, 0x61, 0x74, 0x54, 0x6f, 0x52, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x12, 0x32, 0x2e, + 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, + 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x54, 0x69, 0x65, 0x72, 0x4d, 0x6f, 0x76, 0x65, 0x44, + 0x61, 0x74, 0x54, 0x6f, 0x52, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x33, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, + 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x54, 0x69, 0x65, 0x72, 0x4d, + 0x6f, 0x76, 0x65, 0x44, 0x61, 0x74, 0x54, 0x6f, 0x52, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x30, 0x01, 0x12, 0x8e, 0x01, 0x0a, 0x1b, 0x56, + 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x54, 0x69, 0x65, 0x72, 0x4d, 0x6f, 0x76, 0x65, 0x44, 0x61, 0x74, + 0x46, 0x72, 0x6f, 0x6d, 0x52, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x12, 0x34, 0x2e, 0x76, 0x6f, 0x6c, + 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, + 0x6c, 0x75, 0x6d, 0x65, 0x54, 0x69, 0x65, 0x72, 0x4d, 0x6f, 0x76, 0x65, 0x44, 0x61, 0x74, 0x46, + 0x72, 0x6f, 0x6d, 0x52, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x35, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, + 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x54, 0x69, 0x65, 0x72, 0x4d, 0x6f, + 0x76, 0x65, 0x44, 0x61, 0x74, 0x46, 0x72, 0x6f, 0x6d, 0x52, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x30, 0x01, 0x12, 0x71, 0x0a, 0x12, 0x56, + 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x75, + 0x73, 0x12, 0x2b, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, + 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, + 0x72, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2c, + 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, + 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x53, 0x74, + 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x6e, + 0x0a, 0x11, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x4c, 0x65, + 0x61, 0x76, 0x65, 0x12, 0x2a, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, + 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x53, 0x65, 0x72, + 0x76, 0x65, 0x72, 0x4c, 0x65, 0x61, 0x76, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x2b, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, + 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x4c, + 0x65, 0x61, 0x76, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x74, + 0x0a, 0x13, 0x46, 0x65, 0x74, 0x63, 0x68, 0x41, 0x6e, 0x64, 0x57, 0x72, 0x69, 0x74, 0x65, 0x4e, + 0x65, 0x65, 0x64, 0x6c, 0x65, 0x12, 0x2c, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, + 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x46, 0x65, 0x74, 0x63, 0x68, 0x41, 0x6e, + 0x64, 0x57, 0x72, 0x69, 0x74, 0x65, 0x4e, 0x65, 0x65, 0x64, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x2d, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, + 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x46, 0x65, 0x74, 0x63, 0x68, 0x41, 0x6e, 0x64, 0x57, + 0x72, 0x69, 0x74, 0x65, 0x4e, 0x65, 0x65, 0x64, 0x6c, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x22, 0x00, 0x12, 0x4c, 0x0a, 0x05, 0x51, 0x75, 0x65, 0x72, 0x79, 0x12, 0x1e, 0x2e, + 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, + 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, + 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, + 0x2e, 0x51, 0x75, 0x65, 0x72, 0x69, 0x65, 0x64, 0x53, 0x74, 0x72, 0x69, 0x70, 0x65, 0x22, 0x00, + 0x30, 0x01, 0x12, 0x71, 0x0a, 0x12, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x4e, 0x65, 0x65, 0x64, + 0x6c, 0x65, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x2b, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, + 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, + 0x6d, 0x65, 0x4e, 0x65, 0x65, 0x64, 0x6c, 0x65, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2c, 0x2e, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, + 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x4e, + 0x65, 0x65, 0x64, 0x6c, 0x65, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, 0x39, 0x5a, 0x37, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, + 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x68, 0x72, 0x69, 0x73, 0x6c, 0x75, 0x73, 0x66, 0x2f, 0x73, 0x65, + 0x61, 0x77, 0x65, 0x65, 0x64, 0x66, 0x73, 0x2f, 0x77, 0x65, 0x65, 0x64, 0x2f, 0x70, 0x62, 0x2f, + 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x62, + 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/weed/server/volume_grpc_read_all.go b/weed/server/volume_grpc_read_all.go index 21b42d8b1..3ee0b7d86 100644 --- a/weed/server/volume_grpc_read_all.go +++ b/weed/server/volume_grpc_read_all.go @@ -10,13 +10,23 @@ import ( func (vs *VolumeServer) ReadAllNeedles(req *volume_server_pb.ReadAllNeedlesRequest, stream volume_server_pb.VolumeServer_ReadAllNeedlesServer) (err error) { - v := vs.store.GetVolume(needle.VolumeId(req.VolumeId)) + for _, vid := range req.VolumeIds { + if err := vs.streaReadOneVolume(needle.VolumeId(vid), stream, err); err != nil { + return err + } + } + return nil +} + +func (vs *VolumeServer) streaReadOneVolume(vid needle.VolumeId, stream volume_server_pb.VolumeServer_ReadAllNeedlesServer, err error) error { + v := vs.store.GetVolume(vid) if v == nil { - return fmt.Errorf("not found volume id %d", req.VolumeId) + return fmt.Errorf("not found volume id %d", vid) } scanner := &VolumeFileScanner4ReadAll{ stream: stream, + v: v, } offset := int64(v.SuperBlock.BlockSize()) @@ -24,11 +34,11 @@ func (vs *VolumeServer) ReadAllNeedles(req *volume_server_pb.ReadAllNeedlesReque err = storage.ScanVolumeFileFrom(v.Version(), v.DataBackend, offset, scanner) return err - } type VolumeFileScanner4ReadAll struct { stream volume_server_pb.VolumeServer_ReadAllNeedlesServer + v *storage.Volume } func (scanner *VolumeFileScanner4ReadAll) VisitSuperBlock(superBlock super_block.SuperBlock) error { @@ -42,7 +52,9 @@ func (scanner *VolumeFileScanner4ReadAll) ReadNeedleBody() bool { func (scanner *VolumeFileScanner4ReadAll) VisitNeedle(n *needle.Needle, offset int64, needleHeader, needleBody []byte) error { sendErr := scanner.stream.Send(&volume_server_pb.ReadAllNeedlesResponse{ + VolumeId: uint32(scanner.v.Id), NeedleId: uint64(n.Id), + Cookie: uint32(n.Cookie), NeedleBlob: n.Data, }) if sendErr != nil { From 2e9372dcf7567e41074eac0a6f3698dda931453d Mon Sep 17 00:00:00 2001 From: Chris Lu Date: Mon, 27 Sep 2021 03:07:44 -0700 Subject: [PATCH 059/130] volume stream read skips deleted content --- weed/server/volume_grpc_read_all.go | 34 +++-------------------- weed/storage/volume_read_all.go | 42 +++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+), 31 deletions(-) create mode 100644 weed/storage/volume_read_all.go diff --git a/weed/server/volume_grpc_read_all.go b/weed/server/volume_grpc_read_all.go index 3ee0b7d86..7fe5bad03 100644 --- a/weed/server/volume_grpc_read_all.go +++ b/weed/server/volume_grpc_read_all.go @@ -5,7 +5,6 @@ import ( "github.com/chrislusf/seaweedfs/weed/pb/volume_server_pb" "github.com/chrislusf/seaweedfs/weed/storage" "github.com/chrislusf/seaweedfs/weed/storage/needle" - "github.com/chrislusf/seaweedfs/weed/storage/super_block" ) func (vs *VolumeServer) ReadAllNeedles(req *volume_server_pb.ReadAllNeedlesRequest, stream volume_server_pb.VolumeServer_ReadAllNeedlesServer) (err error) { @@ -24,9 +23,9 @@ func (vs *VolumeServer) streaReadOneVolume(vid needle.VolumeId, stream volume_se return fmt.Errorf("not found volume id %d", vid) } - scanner := &VolumeFileScanner4ReadAll{ - stream: stream, - v: v, + scanner := &storage.VolumeFileScanner4ReadAll{ + Stream: stream, + V: v, } offset := int64(v.SuperBlock.BlockSize()) @@ -35,30 +34,3 @@ func (vs *VolumeServer) streaReadOneVolume(vid needle.VolumeId, stream volume_se return err } - -type VolumeFileScanner4ReadAll struct { - stream volume_server_pb.VolumeServer_ReadAllNeedlesServer - v *storage.Volume -} - -func (scanner *VolumeFileScanner4ReadAll) VisitSuperBlock(superBlock super_block.SuperBlock) error { - return nil - -} -func (scanner *VolumeFileScanner4ReadAll) ReadNeedleBody() bool { - return true -} - -func (scanner *VolumeFileScanner4ReadAll) VisitNeedle(n *needle.Needle, offset int64, needleHeader, needleBody []byte) error { - - sendErr := scanner.stream.Send(&volume_server_pb.ReadAllNeedlesResponse{ - VolumeId: uint32(scanner.v.Id), - NeedleId: uint64(n.Id), - Cookie: uint32(n.Cookie), - NeedleBlob: n.Data, - }) - if sendErr != nil { - return sendErr - } - return nil -} diff --git a/weed/storage/volume_read_all.go b/weed/storage/volume_read_all.go new file mode 100644 index 000000000..453a4495c --- /dev/null +++ b/weed/storage/volume_read_all.go @@ -0,0 +1,42 @@ +package storage + +import ( + "github.com/chrislusf/seaweedfs/weed/pb/volume_server_pb" + "github.com/chrislusf/seaweedfs/weed/storage/needle" + "github.com/chrislusf/seaweedfs/weed/storage/super_block" +) + +type VolumeFileScanner4ReadAll struct { + Stream volume_server_pb.VolumeServer_ReadAllNeedlesServer + V *Volume +} + +func (scanner *VolumeFileScanner4ReadAll) VisitSuperBlock(superBlock super_block.SuperBlock) error { + return nil + +} +func (scanner *VolumeFileScanner4ReadAll) ReadNeedleBody() bool { + return true +} + +func (scanner *VolumeFileScanner4ReadAll) VisitNeedle(n *needle.Needle, offset int64, needleHeader, needleBody []byte) error { + + nv, ok := scanner.V.nm.Get(n.Id) + if !ok { + return nil + } + if nv.Offset.ToActualOffset() != offset { + return nil + } + + sendErr := scanner.Stream.Send(&volume_server_pb.ReadAllNeedlesResponse{ + VolumeId: uint32(scanner.V.Id), + NeedleId: uint64(n.Id), + Cookie: uint32(n.Cookie), + NeedleBlob: n.Data, + }) + if sendErr != nil { + return sendErr + } + return nil +} From 040443e2d1c9dd5959bfe0acd346c555c9f3d559 Mon Sep 17 00:00:00 2001 From: Chris Lu Date: Mon, 27 Sep 2021 23:59:45 -0700 Subject: [PATCH 060/130] fix possible error case --- weed/filer/filer_notify_append.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/weed/filer/filer_notify_append.go b/weed/filer/filer_notify_append.go index 73762cde7..e30ef4e54 100644 --- a/weed/filer/filer_notify_append.go +++ b/weed/filer/filer_notify_append.go @@ -33,6 +33,8 @@ func (f *Filer) appendToFile(targetFile string, data []byte) error { Gid: OS_GID, }, } + } else if err != nil { + return fmt.Errorf("find %s: %v", fullpath, err) } else { offset = int64(TotalSize(entry.Chunks)) } From a067deaabcf9b7d6feb58e13dda24721da3e153e Mon Sep 17 00:00:00 2001 From: Chris Lu Date: Tue, 28 Sep 2021 16:54:18 -0700 Subject: [PATCH 061/130] avoid possible modified location list fix issue 1 of https://github.com/chrislusf/seaweedfs/issues/2345 --- weed/topology/volume_layout.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/weed/topology/volume_layout.go b/weed/topology/volume_layout.go index f315cb7e4..db05102fe 100644 --- a/weed/topology/volume_layout.go +++ b/weed/topology/volume_layout.go @@ -302,7 +302,7 @@ func (vl *VolumeLayout) PickForWrite(count uint64, option *VolumeGrowOption) (*n } counter++ if rand.Intn(counter) < 1 { - vid, locationList = v, volumeLocationList + vid, locationList = v, volumeLocationList.Copy() } } } From adfd79e243b7f332e6ddcd1f9bdfb27cb556dbea Mon Sep 17 00:00:00 2001 From: Chris Lu Date: Wed, 29 Sep 2021 10:52:53 -0700 Subject: [PATCH 062/130] skip arm platform --- .github/workflows/binaries_dev.yml | 8 +------- .github/workflows/container_dev.yml | 2 +- .github/workflows/container_release.yml | 1 + 3 files changed, 3 insertions(+), 8 deletions(-) diff --git a/.github/workflows/binaries_dev.yml b/.github/workflows/binaries_dev.yml index 45ac2540e..0c6370dca 100644 --- a/.github/workflows/binaries_dev.yml +++ b/.github/workflows/binaries_dev.yml @@ -26,14 +26,8 @@ jobs: strategy: matrix: goos: [linux, windows, darwin, freebsd] - goarch: [amd64, arm, arm64] + goarch: [amd64, arm64] exclude: - - goarch: arm - goos: darwin - - goarch: 386 - goos: darwin - - goarch: arm - goos: windows - goarch: arm64 goos: windows diff --git a/.github/workflows/container_dev.yml b/.github/workflows/container_dev.yml index 0261275fe..84e995c8e 100644 --- a/.github/workflows/container_dev.yml +++ b/.github/workflows/container_dev.yml @@ -58,6 +58,6 @@ jobs: context: ./docker push: ${{ github.event_name != 'pull_request' }} file: ./docker/Dockerfile.go_build - platforms: linux/amd64, linux/arm, linux/arm64, linux/386 + platforms: linux/amd64, linux/arm64 tags: ${{ steps.docker_meta.outputs.tags }} labels: ${{ steps.docker_meta.outputs.labels }} diff --git a/.github/workflows/container_release.yml b/.github/workflows/container_release.yml index 625ab53cf..b345b1b86 100644 --- a/.github/workflows/container_release.yml +++ b/.github/workflows/container_release.yml @@ -63,6 +63,7 @@ jobs: platforms: linux/amd64, linux/arm, linux/arm64, linux/386 tags: ${{ steps.docker_meta.outputs.tags }} labels: ${{ steps.docker_meta.outputs.labels }} + build-large-release-container: runs-on: [ubuntu-latest] From 4c7a3cf2fd841cc0996f887bb73560bfdea9528d Mon Sep 17 00:00:00 2001 From: Konstantin Lebedev Date: Thu, 30 Sep 2021 14:06:32 +0500 Subject: [PATCH 063/130] trivy: security fix --- go.mod | 12 +++++++++++- go.sum | 31 +++++++++++++++++++++++++++++++ 2 files changed, 42 insertions(+), 1 deletion(-) diff --git a/go.mod b/go.mod index a36d93b67..8c7e88bd7 100644 --- a/go.mod +++ b/go.mod @@ -133,7 +133,7 @@ require ( gocloud.dev v0.20.0 gocloud.dev/pubsub/natspubsub v0.20.0 gocloud.dev/pubsub/rabbitpubsub v0.20.0 - golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0 // indirect + golang.org/x/crypto v0.0.0-20201216223049-8b5274cf687f // indirect golang.org/x/image v0.0.0-20200119044424-58c23975cae1 golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f // indirect @@ -165,25 +165,35 @@ require ( ) require ( + github.com/apache/thrift v0.14.0 // indirect github.com/coreos/etcd v3.3.10+incompatible // indirect github.com/cznic/mathutil v0.0.0-20181122101859-297441e03548 // indirect github.com/d4l3k/messagediff v1.2.1 // indirect github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2 // indirect + github.com/gin-contrib/sse v0.1.0 // indirect + github.com/gin-gonic/gin v1.6.0 // indirect + github.com/go-playground/locales v0.13.0 // indirect + github.com/go-playground/universal-translator v0.17.0 // indirect + github.com/go-playground/validator/v10 v10.2.0 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/jcmturner/aescts/v2 v2.0.0 // indirect github.com/jcmturner/dnsutils/v2 v2.0.0 // indirect github.com/jcmturner/goidentity/v6 v6.0.1 // indirect github.com/jcmturner/rpc/v2 v2.0.2 // indirect + github.com/leodido/go-urn v1.2.0 // indirect github.com/mattn/go-runewidth v0.0.7 // indirect + github.com/miekg/dns v1.1.25-0.20191211073109-8ebf2e419df7 // indirect github.com/opentracing/opentracing-go v1.2.0 // indirect github.com/pingcap/errors v0.11.5-0.20201126102027-b0a155152ca3 // indirect github.com/pingcap/failpoint v0.0.0-20210316064728-7acb0f0a3dfd // indirect github.com/pingcap/kvproto v0.0.0-20210806074406-317f69fb54b4 // indirect github.com/pingcap/log v0.0.0-20210317133921-96f4fcab92a4 // indirect github.com/pingcap/parser v0.0.0-20210525032559-c37778aff307 // indirect + github.com/satori/go.uuid v1.2.1-0.20181016170032-d91630c85102 // indirect github.com/spf13/pflag v1.0.5 // indirect github.com/tikv/pd v1.1.0-beta.0.20210323121136-78679e5e209d // indirect github.com/twmb/murmur3 v1.1.3 // indirect + github.com/ugorji/go/codec v1.1.7 // indirect go.etcd.io/etcd/api/v3 v3.5.0 // indirect go.etcd.io/etcd/client/pkg/v3 v3.5.0 // indirect go.etcd.io/etcd/client/v3 v3.5.0 // indirect diff --git a/go.sum b/go.sum index 2cceeaf08..a8b8bbc37 100644 --- a/go.sum +++ b/go.sum @@ -134,6 +134,8 @@ github.com/antihax/optional v0.0.0-20180407024304-ca021399b1a6/go.mod h1:V8iCPQY github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= +github.com/apache/thrift v0.14.0 h1:vqZ2DP42i8th2OsgCcYZkirtbzvpZEFx53LiWDJXIAs= +github.com/apache/thrift v0.14.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= github.com/appleboy/gin-jwt/v2 v2.6.3/go.mod h1:MfPYA4ogzvOcVkRwAxT7quHOtQmVKDpTwxyUrC2DNw0= github.com/appleboy/gofight/v2 v2.1.2/go.mod h1:frW+U1QZEdDgixycTj4CygQ48yLTUhplt43+Wczp3rw= github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= @@ -290,10 +292,13 @@ github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeME github.com/gin-contrib/gzip v0.0.1/go.mod h1:fGBJBCdt6qCZuCAOwWuFhBB4OOq9EFqlo5dEaFhhu5w= github.com/gin-contrib/sse v0.0.0-20170109093832-22d885f9ecc7/go.mod h1:VJ0WA2NBN22VlZ2dKZQPAPnyWw5XTlK1KymzLKsr59s= github.com/gin-contrib/sse v0.0.0-20190301062529-5545eab6dad3/go.mod h1:VJ0WA2NBN22VlZ2dKZQPAPnyWw5XTlK1KymzLKsr59s= +github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= github.com/gin-gonic/gin v1.3.0/go.mod h1:7cKuhb5qV2ggCFctp2fJQ+ErvciLZrIeoOSOm6mUr7Y= github.com/gin-gonic/gin v1.4.0/go.mod h1:OW2EZn3DO8Ln9oIKOvM++LBO+5UPHJJDH72/q/3rZdM= github.com/gin-gonic/gin v1.5.0/go.mod h1:Nd6IXA8m5kNZdNEHMBd93KT+mdY3+bewLgRvmCsR2Do= +github.com/gin-gonic/gin v1.6.0 h1:Lb3veSYoGaNck69fV2+Vf2juLSsHpMTf3Vk5+X+EDJg= +github.com/gin-gonic/gin v1.6.0/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M= github.com/go-chi/chi v4.0.2+incompatible/go.mod h1:eB3wogJHnLi3x/kFX2A+IbTBlXxmMeXJVKy9tTv1XzQ= github.com/go-echarts/go-echarts v1.0.0/go.mod h1:qbmyAb/Rl1f2w7wKba1D4LoNq4U164yO4/wedFbcWyo= github.com/go-errors/errors v1.1.1 h1:ljK/pL5ltg3qoN+OtN6yCv9HWSfMwxSx90GJCZQxYNg= @@ -325,9 +330,16 @@ github.com/go-openapi/spec v0.19.4/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8 github.com/go-openapi/swag v0.17.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg= github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= +github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= github.com/go-playground/locales v0.12.1/go.mod h1:IUMDtCfWo/w/mtMfIE/IG2K+Ey3ygWanZIBtBW0W2TM= +github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q= +github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8= github.com/go-playground/overalls v0.0.0-20180201144345-22ec1a223b7c/go.mod h1:UqxAgEOt89sCiXlrc/ycnx00LVvUO/eS8tMUkWX4R7w= github.com/go-playground/universal-translator v0.16.0/go.mod h1:1AnU7NaIRDWWzGEKwgtJRd2xk99HeFyHw3yid4rvQIY= +github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no= +github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA= +github.com/go-playground/validator/v10 v10.2.0 h1:KgJ0snyC2R9VXYN2rneOtQcw5aHQB1Vv0sFl1UcHBOY= +github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI= github.com/go-redis/redis/v8 v8.4.4 h1:fGqgxCTR1sydaKI00oQf3OmkU/DIe/I/fYXvGklCIuc= github.com/go-redis/redis/v8 v8.4.4/go.mod h1:nA0bQuF0i5JFx4Ta9RZxGKXFrQ8cRWntra97f0196iY= github.com/go-sql-driver/mysql v1.3.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= @@ -634,6 +646,8 @@ github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kurin/blazer v0.5.3 h1:SAgYv0TKU0kN/ETfO5ExjNAPyMt2FocO2s/UlCHfjAk= github.com/kurin/blazer v0.5.3/go.mod h1:4FCXMUWo9DllR2Do4TtBd377ezyAJ51vB5uTBjt0pGU= github.com/leodido/go-urn v1.1.0/go.mod h1:+cyI34gQWZcE1eQU7NVgKkkzdXDQHr1dBMtdAPozLkw= +github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y= +github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= github.com/lib/pq v1.1.1/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.10.0 h1:Zx5DJFEYQXio93kgXnQ09fXNiUKsqv4OUEu2UtGcB1E= github.com/lib/pq v1.10.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= @@ -677,6 +691,8 @@ github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5 github.com/mgechev/dots v0.0.0-20190921121421-c36f7dcfbb81/go.mod h1:KQ7+USdGKfpPjXk4Ga+5XxQM4Lm4e3gAogrreFAYpOg= github.com/mgechev/revive v1.0.2/go.mod h1:rb0dQy1LVAxW9SWy5R3LPUjevzUbUS316U5MFySA2lo= github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= +github.com/miekg/dns v1.1.25-0.20191211073109-8ebf2e419df7 h1:AnRQ1PsgPhnGl4fv0/MDlbTxSBGeWXtDZPmbahfcHTM= +github.com/miekg/dns v1.1.25-0.20191211073109-8ebf2e419df7/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso= github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= @@ -869,6 +885,8 @@ github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E= github.com/sasha-s/go-deadlock v0.2.0/go.mod h1:StQn567HiB1fF2yJ44N9au7wOhrPS3iZqiDbRupzT10= github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= +github.com/satori/go.uuid v1.2.1-0.20181016170032-d91630c85102 h1:WAQaHPfnpevd8SKXCcy5nk3JzEv2h5Q0kSwvoMqXiZs= +github.com/satori/go.uuid v1.2.1-0.20181016170032-d91630c85102/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= github.com/seaweedfs/fuse v1.2.0 h1:m4Ar3I0TY/Tzyb80lfJiXUKFcMBHqA5e/cZwxQtilZo= github.com/seaweedfs/fuse v1.2.0/go.mod h1:iwbDQv5BZACY54r6AO/6xsLNuMaYcBKSkLTZVfmK594= @@ -949,12 +967,15 @@ github.com/tecbot/gorocksdb v0.0.0-20191217155057-f0fad39f321c h1:g+WoO5jjkqGAzH github.com/tecbot/gorocksdb v0.0.0-20191217155057-f0fad39f321c/go.mod h1:ahpPrc7HpcfEWDQRZEmnXMzHY03mLDYMCxeDzy46i+8= github.com/thoas/go-funk v0.7.0/go.mod h1:+IWnUfUmFO1+WVYQWQtIJHeRRdaIyyYglZN7xzUPe4Q= github.com/tidwall/gjson v1.3.5/go.mod h1:P256ACg0Mn+j1RXIDXoss50DeIABTYK1PULOJHhxOls= +github.com/tidwall/gjson v1.6.6 h1:Gh0D/kZV+L9rcuE2hE8Hn2dTYe2L6j6SKwcPlKpXAcs= +github.com/tidwall/gjson v1.6.6/go.mod h1:zeFuBCIqD4sN/gmqBzZ4j7Jd6UcA2Fc56x7QFsv+8fI= github.com/tidwall/gjson v1.8.1 h1:8j5EE9Hrh3l9Od1OIEDAb7IpezNA20UdRngNAj5N0WU= github.com/tidwall/gjson v1.8.1/go.mod h1:5/xDoumyyDNerp2U36lyolv46b3uF/9Bu6OfyQ9GImk= github.com/tidwall/match v1.0.1/go.mod h1:LujAq0jyVjBy028G1WhWfIzbpQfMO8bBZ6Tyb0+pL9E= github.com/tidwall/match v1.0.3 h1:FQUVvBImDutD8wJLN6c5eMzWtjgONK9MwIBCOrUJKeE= github.com/tidwall/match v1.0.3/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= +github.com/tidwall/pretty v1.0.2/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= github.com/tidwall/pretty v1.1.0 h1:K3hMW5epkdAVwibsQEfR/7Zj0Qgt4DxtNumTq/VloO8= github.com/tidwall/pretty v1.1.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= github.com/tikv/client-go/v2 v2.0.0-alpha.0.20210824090536-16d902a3c7e5 h1:7CJYiW8gKiI3IQOQSAZyqZq0GxB+bmrnZgk9QNZ1cPo= @@ -974,9 +995,11 @@ github.com/tylertreat/BoomFilters v0.0.0-20210315201527-1a82519a3e43 h1:QEePdg0t github.com/tylertreat/BoomFilters v0.0.0-20210315201527-1a82519a3e43/go.mod h1:OYRfF6eb5wY9VRFkXJH8FFBi3plw2v+giaIu7P054pM= github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= github.com/ugorji/go v1.1.5-pre/go.mod h1:FwP/aQVg39TXzItUBMwnWp9T9gPQnXw4Poh4/oBQZ/0= +github.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo= github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= github.com/ugorji/go/codec v0.0.0-20181022190402-e5e69e061d4f/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= github.com/ugorji/go/codec v1.1.5-pre/go.mod h1:tULtS6Gy1AE1yCENaw4Vb//HLH5njI2tfCQDUqRd8fI= +github.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs= github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= github.com/unrolled/render v1.0.1/go.mod h1:gN9T0NhL4Bfbwu8ann7Ry/TGHYfosul+J0obPf6NBdM= github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= @@ -1098,6 +1121,7 @@ golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY= golang.org/x/crypto v0.0.0-20191002192127-34f69633bfdc/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191205180655-e7c4368fe9dd/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= @@ -1111,6 +1135,8 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0 h1:hb9wdF1z5waM+dSIICn1l0DkLVDT3hqhhQsDNUmHPRE= golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20201216223049-8b5274cf687f h1:aZp0e2vLN4MToVqnjNEYEtrEA8RH8U8FN1CU7JgqsPU= +golang.org/x/crypto v0.0.0-20201216223049-8b5274cf687f/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -1176,6 +1202,7 @@ golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191002035440-2ec189313ef0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191112182307-2180aed22343/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -1264,6 +1291,8 @@ golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191010194322-b09406accb47/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -1321,6 +1350,7 @@ golang.org/x/sys v0.0.0-20210817142637-7d9622a276b7 h1:lQ8Btl/sJr2+f4ql7ffKUKfnV golang.org/x/sys v0.0.0-20210817142637-7d9622a276b7/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf h1:2ucpDCmfkl8Bd/FsLtiD653Wf96cW37s+iGx93zsu4k= golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -1361,6 +1391,7 @@ golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgw golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20190907020128-2ca718005c18/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= From fc51ffce2b2e07145e6c7144781a40491febd6cb Mon Sep 17 00:00:00 2001 From: Konstantin Lebedev Date: Thu, 30 Sep 2021 20:24:24 +0500 Subject: [PATCH 064/130] https://github.com/chrislusf/seaweedfs/issues/1846 --- weed/shell/command_volume_fix_replication.go | 84 ++++++++++++-------- 1 file changed, 49 insertions(+), 35 deletions(-) diff --git a/weed/shell/command_volume_fix_replication.go b/weed/shell/command_volume_fix_replication.go index 76a582b31..9ac082e81 100644 --- a/weed/shell/command_volume_fix_replication.go +++ b/weed/shell/command_volume_fix_replication.go @@ -56,6 +56,8 @@ func (c *commandVolumeFixReplication) Do(args []string, commandEnv *CommandEnv, c.collectionPattern = volFixReplicationCommand.String("collectionPattern", "", "match with wildcard characters '*' and '?'") skipChange := volFixReplicationCommand.Bool("n", false, "skip the changes") retryCount := volFixReplicationCommand.Int("retry", 0, "how many times to retry") + volumespPerStep := volFixReplicationCommand.Int("volumes_per_step", 0, "how many volumes to fix in one cycle") + if err = volFixReplicationCommand.Parse(args); err != nil { return nil } @@ -66,44 +68,54 @@ func (c *commandVolumeFixReplication) Do(args []string, commandEnv *CommandEnv, takeAction := !*skipChange - // collect topology information - topologyInfo, _, err := collectTopologyInfo(commandEnv) - if err != nil { - return err - } + underReplicatedVolumeIdsCount := 1 + for underReplicatedVolumeIdsCount > 0 { + // collect topology information + topologyInfo, _, err := collectTopologyInfo(commandEnv) + if err != nil { + return err + } - // find all volumes that needs replication - // collect all data nodes - volumeReplicas, allLocations := collectVolumeReplicaLocations(topologyInfo) + // find all volumes that needs replication + // collect all data nodes + volumeReplicas, allLocations := collectVolumeReplicaLocations(topologyInfo) - if len(allLocations) == 0 { - return fmt.Errorf("no data nodes at all") - } + if len(allLocations) == 0 { + return fmt.Errorf("no data nodes at all") + } - // find all under replicated volumes - var underReplicatedVolumeIds, overReplicatedVolumeIds []uint32 - for vid, replicas := range volumeReplicas { - replica := replicas[0] - replicaPlacement, _ := super_block.NewReplicaPlacementFromByte(byte(replica.info.ReplicaPlacement)) - if replicaPlacement.GetCopyCount() > len(replicas) { - underReplicatedVolumeIds = append(underReplicatedVolumeIds, vid) - } else if replicaPlacement.GetCopyCount() < len(replicas) { - overReplicatedVolumeIds = append(overReplicatedVolumeIds, vid) - fmt.Fprintf(writer, "volume %d replication %s, but over replicated %+d\n", replica.info.Id, replicaPlacement, len(replicas)) + // find all under replicated volumes + var underReplicatedVolumeIds, overReplicatedVolumeIds []uint32 + for vid, replicas := range volumeReplicas { + replica := replicas[0] + replicaPlacement, _ := super_block.NewReplicaPlacementFromByte(byte(replica.info.ReplicaPlacement)) + if replicaPlacement.GetCopyCount() > len(replicas) { + underReplicatedVolumeIds = append(underReplicatedVolumeIds, vid) + } else if replicaPlacement.GetCopyCount() < len(replicas) { + overReplicatedVolumeIds = append(overReplicatedVolumeIds, vid) + fmt.Fprintf(writer, "volume %d replication %s, but over replicated %+d\n", replica.info.Id, replicaPlacement, len(replicas)) + } + } + + if len(overReplicatedVolumeIds) > 0 { + if err := c.fixOverReplicatedVolumes(commandEnv, writer, takeAction, overReplicatedVolumeIds, volumeReplicas, allLocations); err != nil { + return err + } + } + + underReplicatedVolumeIdsCount = len(underReplicatedVolumeIds) + if underReplicatedVolumeIdsCount > 0 { + // find the most under populated data nodes + if err := c.fixUnderReplicatedVolumes(commandEnv, writer, takeAction, underReplicatedVolumeIds, volumeReplicas, allLocations, *retryCount, *volumespPerStep); err != nil { + return err + } + } + + if *skipChange { + break } } - - if len(overReplicatedVolumeIds) > 0 { - return c.fixOverReplicatedVolumes(commandEnv, writer, takeAction, overReplicatedVolumeIds, volumeReplicas, allLocations) - } - - if len(underReplicatedVolumeIds) == 0 { - return nil - } - - // find the most under populated data nodes - return c.fixUnderReplicatedVolumes(commandEnv, writer, takeAction, underReplicatedVolumeIds, volumeReplicas, allLocations, *retryCount) - + return nil } func collectVolumeReplicaLocations(topologyInfo *master_pb.TopologyInfo) (map[uint32][]*VolumeReplica, []location) { @@ -156,8 +168,10 @@ func (c *commandVolumeFixReplication) fixOverReplicatedVolumes(commandEnv *Comma return nil } -func (c *commandVolumeFixReplication) fixUnderReplicatedVolumes(commandEnv *CommandEnv, writer io.Writer, takeAction bool, underReplicatedVolumeIds []uint32, volumeReplicas map[uint32][]*VolumeReplica, allLocations []location, retryCount int) (err error) { - +func (c *commandVolumeFixReplication) fixUnderReplicatedVolumes(commandEnv *CommandEnv, writer io.Writer, takeAction bool, underReplicatedVolumeIds []uint32, volumeReplicas map[uint32][]*VolumeReplica, allLocations []location, retryCount int, volumespPerStep int) (err error) { + if len(underReplicatedVolumeIds) > volumespPerStep && volumespPerStep > 0 { + underReplicatedVolumeIds = underReplicatedVolumeIds[0:volumespPerStep] + } for _, vid := range underReplicatedVolumeIds { for i := 0; i < retryCount+1; i++ { if err = c.fixOneUnderReplicatedVolume(commandEnv, writer, takeAction, volumeReplicas, vid, allLocations); err == nil { From 2cecde89c31db80fc72713aa2cef1e0d4a26fa70 Mon Sep 17 00:00:00 2001 From: Konstantin Lebedev Date: Fri, 1 Oct 2021 00:17:54 +0500 Subject: [PATCH 065/130] rename opt volumesPerStep --- weed/shell/command_volume_fix_replication.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/weed/shell/command_volume_fix_replication.go b/weed/shell/command_volume_fix_replication.go index 9ac082e81..21b3ead6b 100644 --- a/weed/shell/command_volume_fix_replication.go +++ b/weed/shell/command_volume_fix_replication.go @@ -56,7 +56,7 @@ func (c *commandVolumeFixReplication) Do(args []string, commandEnv *CommandEnv, c.collectionPattern = volFixReplicationCommand.String("collectionPattern", "", "match with wildcard characters '*' and '?'") skipChange := volFixReplicationCommand.Bool("n", false, "skip the changes") retryCount := volFixReplicationCommand.Int("retry", 0, "how many times to retry") - volumespPerStep := volFixReplicationCommand.Int("volumes_per_step", 0, "how many volumes to fix in one cycle") + volumesPerStep := volFixReplicationCommand.Int("volumesPerStep", 0, "how many volumes to fix in one cycle") if err = volFixReplicationCommand.Parse(args); err != nil { return nil @@ -106,7 +106,7 @@ func (c *commandVolumeFixReplication) Do(args []string, commandEnv *CommandEnv, underReplicatedVolumeIdsCount = len(underReplicatedVolumeIds) if underReplicatedVolumeIdsCount > 0 { // find the most under populated data nodes - if err := c.fixUnderReplicatedVolumes(commandEnv, writer, takeAction, underReplicatedVolumeIds, volumeReplicas, allLocations, *retryCount, *volumespPerStep); err != nil { + if err := c.fixUnderReplicatedVolumes(commandEnv, writer, takeAction, underReplicatedVolumeIds, volumeReplicas, allLocations, *retryCount, *volumesPerStep); err != nil { return err } } @@ -168,9 +168,9 @@ func (c *commandVolumeFixReplication) fixOverReplicatedVolumes(commandEnv *Comma return nil } -func (c *commandVolumeFixReplication) fixUnderReplicatedVolumes(commandEnv *CommandEnv, writer io.Writer, takeAction bool, underReplicatedVolumeIds []uint32, volumeReplicas map[uint32][]*VolumeReplica, allLocations []location, retryCount int, volumespPerStep int) (err error) { - if len(underReplicatedVolumeIds) > volumespPerStep && volumespPerStep > 0 { - underReplicatedVolumeIds = underReplicatedVolumeIds[0:volumespPerStep] +func (c *commandVolumeFixReplication) fixUnderReplicatedVolumes(commandEnv *CommandEnv, writer io.Writer, takeAction bool, underReplicatedVolumeIds []uint32, volumeReplicas map[uint32][]*VolumeReplica, allLocations []location, retryCount int, volumesPerStep int) (err error) { + if len(underReplicatedVolumeIds) > volumesPerStep && volumesPerStep > 0 { + underReplicatedVolumeIds = underReplicatedVolumeIds[0:volumesPerStep] } for _, vid := range underReplicatedVolumeIds { for i := 0; i < retryCount+1; i++ { From 3ffbaaa0717b424d9899010e8e9df2b4cfb6968f Mon Sep 17 00:00:00 2001 From: chrislusf Date: Fri, 1 Oct 2021 03:16:03 +0000 Subject: [PATCH 066/130] use github.com/linxGnu/grocksdb --- go.mod | 1 + weed/filer/rocksdb/rocksdb_store.go | 2 +- weed/filer/rocksdb/rocksdb_ttl.go | 7 ++++++- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/go.mod b/go.mod index 8c7e88bd7..4f248e7eb 100644 --- a/go.mod +++ b/go.mod @@ -181,6 +181,7 @@ require ( github.com/jcmturner/goidentity/v6 v6.0.1 // indirect github.com/jcmturner/rpc/v2 v2.0.2 // indirect github.com/leodido/go-urn v1.2.0 // indirect + github.com/linxGnu/grocksdb v1.6.38 // indirect github.com/mattn/go-runewidth v0.0.7 // indirect github.com/miekg/dns v1.1.25-0.20191211073109-8ebf2e419df7 // indirect github.com/opentracing/opentracing-go v1.2.0 // indirect diff --git a/weed/filer/rocksdb/rocksdb_store.go b/weed/filer/rocksdb/rocksdb_store.go index 729da7d9b..f48c3988c 100644 --- a/weed/filer/rocksdb/rocksdb_store.go +++ b/weed/filer/rocksdb/rocksdb_store.go @@ -10,7 +10,7 @@ import ( "io" "os" - "github.com/tecbot/gorocksdb" + gorocksdb "github.com/linxGnu/grocksdb" "github.com/chrislusf/seaweedfs/weed/filer" "github.com/chrislusf/seaweedfs/weed/glog" diff --git a/weed/filer/rocksdb/rocksdb_ttl.go b/weed/filer/rocksdb/rocksdb_ttl.go index faed22310..7e9643083 100644 --- a/weed/filer/rocksdb/rocksdb_ttl.go +++ b/weed/filer/rocksdb/rocksdb_ttl.go @@ -5,7 +5,7 @@ package rocksdb import ( "time" - "github.com/tecbot/gorocksdb" + gorocksdb "github.com/linxGnu/grocksdb" "github.com/chrislusf/seaweedfs/weed/filer" ) @@ -38,3 +38,8 @@ func (t *TTLFilter) Filter(level int, key, val []byte) (remove bool, newVal []by func (t *TTLFilter) Name() string { return "TTLFilter" } +func (t *TTLFilter) SetIgnoreSnapshots(value bool) { +} + +func (t *TTLFilter) Destroy() { +} From 92d65c917c6efe6b9bdad6e62fdf8cda82f76377 Mon Sep 17 00:00:00 2001 From: Chris Lu Date: Thu, 30 Sep 2021 20:19:48 -0700 Subject: [PATCH 067/130] use grocksdb --- go.mod | 1 - 1 file changed, 1 deletion(-) diff --git a/go.mod b/go.mod index 4f248e7eb..c3992eefb 100644 --- a/go.mod +++ b/go.mod @@ -111,7 +111,6 @@ require ( github.com/streadway/amqp v0.0.0-20200108173154-1c71cc93ed71 github.com/stretchr/testify v1.7.0 github.com/syndtr/goleveldb v1.0.1-0.20190318030020-c3a204f8e965 - github.com/tecbot/gorocksdb v0.0.0-20191217155057-f0fad39f321c github.com/tidwall/gjson v1.8.1 github.com/tidwall/match v1.0.3 github.com/tidwall/pretty v1.1.0 // indirect From 3b159db1430ac70d436a7f93297e3d8eff424950 Mon Sep 17 00:00:00 2001 From: chrislusf Date: Fri, 1 Oct 2021 03:53:38 +0000 Subject: [PATCH 068/130] add grocksdb --- go.sum | 2 ++ 1 file changed, 2 insertions(+) diff --git a/go.sum b/go.sum index a8b8bbc37..353289c57 100644 --- a/go.sum +++ b/go.sum @@ -653,6 +653,8 @@ github.com/lib/pq v1.10.0 h1:Zx5DJFEYQXio93kgXnQ09fXNiUKsqv4OUEu2UtGcB1E= github.com/lib/pq v1.10.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM= github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4= +github.com/linxGnu/grocksdb v1.6.38 h1:2VTvvGFJr5Tkei/Rqp4Sc1J7T65p6reFjBNCcyYNFV8= +github.com/linxGnu/grocksdb v1.6.38/go.mod h1:/+iSQrn7Izt6kFhHBQvcE6FkklsKXa8hc35pFyFDrDw= github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/magiconair/properties v1.8.1 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzRKO2BQ4= From 7b776be285de6c3980af9c1368a0534e5e62d7df Mon Sep 17 00:00:00 2001 From: Chris Lu Date: Thu, 30 Sep 2021 21:10:23 -0700 Subject: [PATCH 069/130] build docker image for chrislusf/seaweedfs:rocksdb --- docker/Dockerfile.go_rocksdb | 14 ++++++++++ docker/Dockerfile.rocksdb_large | 47 +++++++++++++++++++++++++++++++++ docker/Makefile | 6 +++++ 3 files changed, 67 insertions(+) create mode 100644 docker/Dockerfile.go_rocksdb create mode 100644 docker/Dockerfile.rocksdb_large diff --git a/docker/Dockerfile.go_rocksdb b/docker/Dockerfile.go_rocksdb new file mode 100644 index 000000000..18367e40c --- /dev/null +++ b/docker/Dockerfile.go_rocksdb @@ -0,0 +1,14 @@ +FROM amd64/golang:1.17-buster + +RUN apt-get update +RUN apt-get install -y build-essential libsnappy-dev zlib1g-dev libbz2-dev libgflags-dev liblz4-dev libzstd-dev + +ENV ROCKSDB_VERSION v6.22.1 + +# build RocksDB +RUN cd /tmp && \ + git clone https://github.com/facebook/rocksdb.git /tmp/rocksdb --depth 1 --single-branch --branch $ROCKSDB_VERSION && \ + cd rocksdb && \ + make static_lib && \ + make install-static + diff --git a/docker/Dockerfile.rocksdb_large b/docker/Dockerfile.rocksdb_large new file mode 100644 index 000000000..719e0afd8 --- /dev/null +++ b/docker/Dockerfile.rocksdb_large @@ -0,0 +1,47 @@ +FROM chrislusf/gorocksdb as builder + +ENV CGO_CFLAGS "-I/tmp/rocksdb/include" +ENV CGO_LDFLAGS "-L/tmp/rocksdb -lrocksdb -lstdc++ -lm -lz -lbz2 -lsnappy -llz4 -lzstd" + +# build SeaweedFS +RUN mkdir -p /go/src/github.com/chrislusf/ +RUN git clone https://github.com/chrislusf/seaweedfs /go/src/github.com/chrislusf/seaweedfs +ARG BRANCH=${BRANCH:-master} +RUN cd /go/src/github.com/chrislusf/seaweedfs && git checkout $BRANCH +RUN cd /go/src/github.com/chrislusf/seaweedfs/weed \ + && export LDFLAGS="-X github.com/chrislusf/seaweedfs/weed/util.COMMIT=$(git rev-parse --short HEAD)" \ + && go install -tags "5BytesOffset rocksdb" -ldflags "-extldflags -static ${LDFLAGS}" + + +FROM alpine AS final +LABEL author="Chris Lu" +COPY --from=builder /go/bin/weed /usr/bin/ +RUN mkdir -p /etc/seaweedfs +COPY --from=builder /go/src/github.com/chrislusf/seaweedfs/docker/filer.toml /etc/seaweedfs/filer.toml +COPY --from=builder /go/src/github.com/chrislusf/seaweedfs/docker/entrypoint.sh /entrypoint.sh +RUN apk add fuse snappy gflags + +# volume server gprc port +EXPOSE 18080 +# volume server http port +EXPOSE 8080 +# filer server gprc port +EXPOSE 18888 +# filer server http port +EXPOSE 8888 +# master server shared gprc port +EXPOSE 19333 +# master server shared http port +EXPOSE 9333 +# s3 server http port +EXPOSE 8333 +# webdav server http port +EXPOSE 7333 + +RUN mkdir -p /data/filerldb2 + +VOLUME /data + +RUN chmod +x /entrypoint.sh + +ENTRYPOINT ["/entrypoint.sh"] diff --git a/docker/Makefile b/docker/Makefile index 8efc0ded2..b5fff1c16 100644 --- a/docker/Makefile +++ b/docker/Makefile @@ -13,6 +13,12 @@ build: binary docker build --no-cache -t chrislusf/seaweedfs:local -f Dockerfile.local . rm ./weed +build_gorocksdb: + docker build --no-cache -t chrislusf/gorocksdb -f Dockerfile.go_rocksdb . + +build_rocksdb: + docker build --no-cache -t chrislusf/seaweedfs:rocksdb -f Dockerfile.rocksdb_large . + s3tests_build: docker build --no-cache -t chrislusf/ceph-s3-tests:local -f Dockerfile.s3tests . From 41efc6e629e9ef9733f56f0ade848dc6d7c11c3a Mon Sep 17 00:00:00 2001 From: Chris Lu Date: Thu, 30 Sep 2021 21:23:05 -0700 Subject: [PATCH 070/130] Update go.mod CVE-2020-28483 high severity Vulnerable versions: < 1.7.0 Patched version: 1.7.0 This affects all versions of package github.com/gin-gonic/gin under 1.7.0. When gin is exposed directly to the internet, a client's IP can be spoofed by setting the X-Forwarded-For header. --- go.mod | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/go.mod b/go.mod index c3992eefb..df2d17f21 100644 --- a/go.mod +++ b/go.mod @@ -170,7 +170,7 @@ require ( github.com/d4l3k/messagediff v1.2.1 // indirect github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2 // indirect github.com/gin-contrib/sse v0.1.0 // indirect - github.com/gin-gonic/gin v1.6.0 // indirect + github.com/gin-gonic/gin v1.7.0 // indirect github.com/go-playground/locales v0.13.0 // indirect github.com/go-playground/universal-translator v0.17.0 // indirect github.com/go-playground/validator/v10 v10.2.0 // indirect From 10d639210173e0a6facd4962a2d64966075170ea Mon Sep 17 00:00:00 2001 From: Chris Lu Date: Thu, 30 Sep 2021 21:29:58 -0700 Subject: [PATCH 071/130] clean up --- go.mod | 22 ---------------------- go.sum | 2 ++ 2 files changed, 2 insertions(+), 22 deletions(-) diff --git a/go.mod b/go.mod index df2d17f21..3b0582c52 100644 --- a/go.mod +++ b/go.mod @@ -164,16 +164,7 @@ require ( ) require ( - github.com/apache/thrift v0.14.0 // indirect github.com/coreos/etcd v3.3.10+incompatible // indirect - github.com/cznic/mathutil v0.0.0-20181122101859-297441e03548 // indirect - github.com/d4l3k/messagediff v1.2.1 // indirect - github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2 // indirect - github.com/gin-contrib/sse v0.1.0 // indirect - github.com/gin-gonic/gin v1.7.0 // indirect - github.com/go-playground/locales v0.13.0 // indirect - github.com/go-playground/universal-translator v0.17.0 // indirect - github.com/go-playground/validator/v10 v10.2.0 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/jcmturner/aescts/v2 v2.0.0 // indirect github.com/jcmturner/dnsutils/v2 v2.0.0 // indirect @@ -184,28 +175,15 @@ require ( github.com/mattn/go-runewidth v0.0.7 // indirect github.com/miekg/dns v1.1.25-0.20191211073109-8ebf2e419df7 // indirect github.com/opentracing/opentracing-go v1.2.0 // indirect - github.com/pingcap/errors v0.11.5-0.20201126102027-b0a155152ca3 // indirect - github.com/pingcap/failpoint v0.0.0-20210316064728-7acb0f0a3dfd // indirect - github.com/pingcap/kvproto v0.0.0-20210806074406-317f69fb54b4 // indirect - github.com/pingcap/log v0.0.0-20210317133921-96f4fcab92a4 // indirect - github.com/pingcap/parser v0.0.0-20210525032559-c37778aff307 // indirect - github.com/satori/go.uuid v1.2.1-0.20181016170032-d91630c85102 // indirect github.com/spf13/pflag v1.0.5 // indirect - github.com/tikv/pd v1.1.0-beta.0.20210323121136-78679e5e209d // indirect - github.com/twmb/murmur3 v1.1.3 // indirect - github.com/ugorji/go/codec v1.1.7 // indirect go.etcd.io/etcd/api/v3 v3.5.0 // indirect go.etcd.io/etcd/client/pkg/v3 v3.5.0 // indirect go.etcd.io/etcd/client/v3 v3.5.0 // indirect go.uber.org/atomic v1.7.0 // indirect go.uber.org/multierr v1.7.0 // indirect go.uber.org/zap v1.17.0 // indirect - golang.org/x/lint v0.0.0-20210508222113-6edffad5e616 // indirect - golang.org/x/mod v0.4.2 // indirect golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect - gopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect - gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect ) // replace github.com/seaweedfs/fuse => /Users/chris/go/src/github.com/seaweedfs/fuse diff --git a/go.sum b/go.sum index 353289c57..abd342f0c 100644 --- a/go.sum +++ b/go.sum @@ -299,6 +299,7 @@ github.com/gin-gonic/gin v1.4.0/go.mod h1:OW2EZn3DO8Ln9oIKOvM++LBO+5UPHJJDH72/q/ github.com/gin-gonic/gin v1.5.0/go.mod h1:Nd6IXA8m5kNZdNEHMBd93KT+mdY3+bewLgRvmCsR2Do= github.com/gin-gonic/gin v1.6.0 h1:Lb3veSYoGaNck69fV2+Vf2juLSsHpMTf3Vk5+X+EDJg= github.com/gin-gonic/gin v1.6.0/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M= +github.com/gin-gonic/gin v1.7.0/go.mod h1:jD2toBW3GZUr5UMcdrwQA10I7RuaFOl/SGeDjXkfUtY= github.com/go-chi/chi v4.0.2+incompatible/go.mod h1:eB3wogJHnLi3x/kFX2A+IbTBlXxmMeXJVKy9tTv1XzQ= github.com/go-echarts/go-echarts v1.0.0/go.mod h1:qbmyAb/Rl1f2w7wKba1D4LoNq4U164yO4/wedFbcWyo= github.com/go-errors/errors v1.1.1 h1:ljK/pL5ltg3qoN+OtN6yCv9HWSfMwxSx90GJCZQxYNg= @@ -340,6 +341,7 @@ github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD87 github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA= github.com/go-playground/validator/v10 v10.2.0 h1:KgJ0snyC2R9VXYN2rneOtQcw5aHQB1Vv0sFl1UcHBOY= github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI= +github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4= github.com/go-redis/redis/v8 v8.4.4 h1:fGqgxCTR1sydaKI00oQf3OmkU/DIe/I/fYXvGklCIuc= github.com/go-redis/redis/v8 v8.4.4/go.mod h1:nA0bQuF0i5JFx4Ta9RZxGKXFrQ8cRWntra97f0196iY= github.com/go-sql-driver/mysql v1.3.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= From 0c12a7d12ab4e0be3df68462d8fd8999140a1d30 Mon Sep 17 00:00:00 2001 From: Chris Lu Date: Thu, 30 Sep 2021 21:43:34 -0700 Subject: [PATCH 072/130] github action build rocksdb image --- .github/workflows/container_release.yml | 62 ++++++++++++++++++------- docker/Dockerfile.go_rocksdb | 14 ------ docker/Dockerfile.rocksdb_large | 14 +++++- 3 files changed, 59 insertions(+), 31 deletions(-) delete mode 100644 docker/Dockerfile.go_rocksdb diff --git a/.github/workflows/container_release.yml b/.github/workflows/container_release.yml index b345b1b86..f008f0e70 100644 --- a/.github/workflows/container_release.yml +++ b/.github/workflows/container_release.yml @@ -45,14 +45,6 @@ jobs: with: username: ${{ secrets.DOCKER_USERNAME }} password: ${{ secrets.DOCKER_PASSWORD }} - - - name: Login to GHCR - if: github.event_name != 'pull_request' - uses: docker/login-action@v1 - with: - registry: ghcr.io - username: ${{ secrets.GHCR_USERNAME }} - password: ${{ secrets.GHCR_TOKEN }} - name: Build uses: docker/build-push-action@v2 @@ -102,14 +94,6 @@ jobs: with: username: ${{ secrets.DOCKER_USERNAME }} password: ${{ secrets.DOCKER_PASSWORD }} - - - name: Login to GHCR - if: github.event_name != 'pull_request' - uses: docker/login-action@v1 - with: - registry: ghcr.io - username: ${{ secrets.GHCR_USERNAME }} - password: ${{ secrets.GHCR_TOKEN }} - name: Build uses: docker/build-push-action@v2 @@ -120,3 +104,49 @@ jobs: platforms: linux/amd64, linux/arm, linux/arm64, linux/386 tags: ${{ steps.docker_meta.outputs.tags }} labels: ${{ steps.docker_meta.outputs.labels }} + + build-large-release-container_rocksdb: + runs-on: [ubuntu-latest] + + steps: + - + name: Checkout + uses: actions/checkout@v2 + - + name: Docker meta + id: docker_meta + uses: docker/metadata-action@v3 + with: + images: | + chrislusf/seaweedfs + tags: | + type=ref,event=tag,suffix=_large_disk_rocksdb + flavor: | + latest=false + labels: | + org.opencontainers.image.title=seaweedfs + org.opencontainers.image.description=SeaweedFS is a distributed storage system for blobs, objects, files, and data lake, to store and serve billions of files fast! + org.opencontainers.image.vendor=Chris Lu + - + name: Set up QEMU + uses: docker/setup-qemu-action@v1 + - + name: Set up Docker Buildx + uses: docker/setup-buildx-action@v1 + - + name: Login to Docker Hub + if: github.event_name != 'pull_request' + uses: docker/login-action@v1 + with: + username: ${{ secrets.DOCKER_USERNAME }} + password: ${{ secrets.DOCKER_PASSWORD }} + - + name: Build + uses: docker/build-push-action@v2 + with: + context: ./docker + push: ${{ github.event_name != 'pull_request' }} + file: ./docker/Dockerfile.rocksdb_large + platforms: linux/amd64 + tags: ${{ steps.docker_meta.outputs.tags }} + labels: ${{ steps.docker_meta.outputs.labels }} diff --git a/docker/Dockerfile.go_rocksdb b/docker/Dockerfile.go_rocksdb deleted file mode 100644 index 18367e40c..000000000 --- a/docker/Dockerfile.go_rocksdb +++ /dev/null @@ -1,14 +0,0 @@ -FROM amd64/golang:1.17-buster - -RUN apt-get update -RUN apt-get install -y build-essential libsnappy-dev zlib1g-dev libbz2-dev libgflags-dev liblz4-dev libzstd-dev - -ENV ROCKSDB_VERSION v6.22.1 - -# build RocksDB -RUN cd /tmp && \ - git clone https://github.com/facebook/rocksdb.git /tmp/rocksdb --depth 1 --single-branch --branch $ROCKSDB_VERSION && \ - cd rocksdb && \ - make static_lib && \ - make install-static - diff --git a/docker/Dockerfile.rocksdb_large b/docker/Dockerfile.rocksdb_large index 719e0afd8..f3bc7eb7d 100644 --- a/docker/Dockerfile.rocksdb_large +++ b/docker/Dockerfile.rocksdb_large @@ -1,4 +1,16 @@ -FROM chrislusf/gorocksdb as builder +FROM golang:1.17-buster as builder + +RUN apt-get update +RUN apt-get install -y build-essential libsnappy-dev zlib1g-dev libbz2-dev libgflags-dev liblz4-dev libzstd-dev + +ENV ROCKSDB_VERSION v6.22.1 + +# build RocksDB +RUN cd /tmp && \ + git clone https://github.com/facebook/rocksdb.git /tmp/rocksdb --depth 1 --single-branch --branch $ROCKSDB_VERSION && \ + cd rocksdb && \ + make static_lib && \ + make install-static ENV CGO_CFLAGS "-I/tmp/rocksdb/include" ENV CGO_LDFLAGS "-L/tmp/rocksdb -lrocksdb -lstdc++ -lm -lz -lbz2 -lsnappy -llz4 -lzstd" From f58ea6a2ee0430828474ef4a36beaffbf9edfbd8 Mon Sep 17 00:00:00 2001 From: Chris Lu Date: Fri, 1 Oct 2021 02:19:30 -0700 Subject: [PATCH 073/130] add source name to error message --- weed/storage/backend/s3_backend/s3_download.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/weed/storage/backend/s3_backend/s3_download.go b/weed/storage/backend/s3_backend/s3_download.go index dbc28446a..224cbbc56 100644 --- a/weed/storage/backend/s3_backend/s3_download.go +++ b/weed/storage/backend/s3_backend/s3_download.go @@ -47,7 +47,7 @@ func downloadFromS3(sess s3iface.S3API, destFileName string, sourceBucket string Key: aws.String(sourceKey), }) if err != nil { - return fileSize, fmt.Errorf("failed to download file %s: %v", destFileName, err) + return fileSize, fmt.Errorf("failed to download /buckets/%s%s to %s: %v", sourceBucket, sourceKey, destFileName, err) } glog.V(1).Infof("downloaded file %s\n", destFileName) From 5e64b22b45dc272254cd9d5eeb1e51814035d7fd Mon Sep 17 00:00:00 2001 From: Konstantin Lebedev Date: Fri, 1 Oct 2021 18:51:22 +0500 Subject: [PATCH 074/130] check that the topology has been updated --- weed/shell/command_ec_decode.go | 12 ++++++ weed/shell/command_volume_fix_replication.go | 45 ++++++++++++++++++-- 2 files changed, 54 insertions(+), 3 deletions(-) diff --git a/weed/shell/command_ec_decode.go b/weed/shell/command_ec_decode.go index 18cea0504..3483156cb 100644 --- a/weed/shell/command_ec_decode.go +++ b/weed/shell/command_ec_decode.go @@ -208,6 +208,18 @@ func collectEcShards(commandEnv *CommandEnv, nodeToEcIndexBits map[pb.ServerAddr } +func LookupVolumeIds(commandEnv *CommandEnv, volumeIds []string) (err error, volumeIdLocations []*master_pb.LookupVolumeResponse_VolumeIdLocation) { + var resp *master_pb.LookupVolumeResponse + err = commandEnv.MasterClient.WithClient(func(client master_pb.SeaweedClient) error { + resp, err = client.LookupVolume(context.Background(), &master_pb.LookupVolumeRequest{VolumeOrFileIds: volumeIds}) + return err + }) + if err != nil { + return err, nil + } + return nil, resp.VolumeIdLocations +} + func collectTopologyInfo(commandEnv *CommandEnv) (topoInfo *master_pb.TopologyInfo, volumeSizeLimitMb uint64, err error) { var resp *master_pb.VolumeListResponse diff --git a/weed/shell/command_volume_fix_replication.go b/weed/shell/command_volume_fix_replication.go index 21b3ead6b..f03cd550e 100644 --- a/weed/shell/command_volume_fix_replication.go +++ b/weed/shell/command_volume_fix_replication.go @@ -10,6 +10,8 @@ import ( "io" "path/filepath" "sort" + "strconv" + "time" "github.com/chrislusf/seaweedfs/weed/operation" "github.com/chrislusf/seaweedfs/weed/pb/master_pb" @@ -70,6 +72,8 @@ func (c *commandVolumeFixReplication) Do(args []string, commandEnv *CommandEnv, underReplicatedVolumeIdsCount := 1 for underReplicatedVolumeIdsCount > 0 { + fixedVolumeReplicas := map[string]int{} + // collect topology information topologyInfo, _, err := collectTopologyInfo(commandEnv) if err != nil { @@ -106,7 +110,8 @@ func (c *commandVolumeFixReplication) Do(args []string, commandEnv *CommandEnv, underReplicatedVolumeIdsCount = len(underReplicatedVolumeIds) if underReplicatedVolumeIdsCount > 0 { // find the most under populated data nodes - if err := c.fixUnderReplicatedVolumes(commandEnv, writer, takeAction, underReplicatedVolumeIds, volumeReplicas, allLocations, *retryCount, *volumesPerStep); err != nil { + err, fixedVolumeReplicas = c.fixUnderReplicatedVolumes(commandEnv, writer, takeAction, underReplicatedVolumeIds, volumeReplicas, allLocations, *retryCount, *volumesPerStep) + if err != nil { return err } } @@ -114,6 +119,36 @@ func (c *commandVolumeFixReplication) Do(args []string, commandEnv *CommandEnv, if *skipChange { break } + + // check that the topology has been updated + if len(fixedVolumeReplicas) > 0 { + fixedVolumes := make([]string, 0, len(fixedVolumeReplicas)) + for k, _ := range fixedVolumeReplicas { + fixedVolumes = append(fixedVolumes, k) + } + err, volumeIdLocations := LookupVolumeIds(commandEnv, fixedVolumes) + if err != nil { + return err + } + for _, volumeIdLocation := range volumeIdLocations { + volumeId := volumeIdLocation.VolumeOrFileId + volumeIdLocationCount := len(volumeIdLocation.Locations) + i := 0 + for fixedVolumeReplicas[volumeId] >= volumeIdLocationCount { + fmt.Fprintf(writer, "the number of locations for volume %s has not increased yet, let's wait\n", volumeId) + time.Sleep(time.Duration(i+1) * time.Second * 7) + err, volumeLocIds := LookupVolumeIds(commandEnv, []string{volumeId}) + if err != nil { + return err + } + volumeIdLocationCount = len(volumeLocIds[0].Locations) + if *retryCount > i { + return fmt.Errorf("replicas volume %s mismatch in topology", volumeId) + } + i += 1 + } + } + } } return nil } @@ -168,18 +203,22 @@ func (c *commandVolumeFixReplication) fixOverReplicatedVolumes(commandEnv *Comma return nil } -func (c *commandVolumeFixReplication) fixUnderReplicatedVolumes(commandEnv *CommandEnv, writer io.Writer, takeAction bool, underReplicatedVolumeIds []uint32, volumeReplicas map[uint32][]*VolumeReplica, allLocations []location, retryCount int, volumesPerStep int) (err error) { +func (c *commandVolumeFixReplication) fixUnderReplicatedVolumes(commandEnv *CommandEnv, writer io.Writer, takeAction bool, underReplicatedVolumeIds []uint32, volumeReplicas map[uint32][]*VolumeReplica, allLocations []location, retryCount int, volumesPerStep int) (err error, fixedVolumes map[string]int) { + fixedVolumes = map[string]int{} if len(underReplicatedVolumeIds) > volumesPerStep && volumesPerStep > 0 { underReplicatedVolumeIds = underReplicatedVolumeIds[0:volumesPerStep] } for _, vid := range underReplicatedVolumeIds { for i := 0; i < retryCount+1; i++ { if err = c.fixOneUnderReplicatedVolume(commandEnv, writer, takeAction, volumeReplicas, vid, allLocations); err == nil { + if takeAction { + fixedVolumes[strconv.FormatUint(uint64(vid), 10)] = len(volumeReplicas[vid]) + } break } } } - return + return nil, fixedVolumes } func (c *commandVolumeFixReplication) fixOneUnderReplicatedVolume(commandEnv *CommandEnv, writer io.Writer, takeAction bool, volumeReplicas map[uint32][]*VolumeReplica, vid uint32, allLocations []location) error { From e862b2529a82c7f2a7cc8c37e1610c1e807ac40f Mon Sep 17 00:00:00 2001 From: Chris Lu Date: Fri, 1 Oct 2021 12:10:11 -0700 Subject: [PATCH 075/130] refactor --- weed/shell/command_ec_decode.go | 6 +++--- weed/shell/command_volume_fix_replication.go | 10 +++++----- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/weed/shell/command_ec_decode.go b/weed/shell/command_ec_decode.go index 3483156cb..cfa24cc31 100644 --- a/weed/shell/command_ec_decode.go +++ b/weed/shell/command_ec_decode.go @@ -208,16 +208,16 @@ func collectEcShards(commandEnv *CommandEnv, nodeToEcIndexBits map[pb.ServerAddr } -func LookupVolumeIds(commandEnv *CommandEnv, volumeIds []string) (err error, volumeIdLocations []*master_pb.LookupVolumeResponse_VolumeIdLocation) { +func lookupVolumeIds(commandEnv *CommandEnv, volumeIds []string) (volumeIdLocations []*master_pb.LookupVolumeResponse_VolumeIdLocation, err error) { var resp *master_pb.LookupVolumeResponse err = commandEnv.MasterClient.WithClient(func(client master_pb.SeaweedClient) error { resp, err = client.LookupVolume(context.Background(), &master_pb.LookupVolumeRequest{VolumeOrFileIds: volumeIds}) return err }) if err != nil { - return err, nil + return nil, err } - return nil, resp.VolumeIdLocations + return resp.VolumeIdLocations, nil } func collectTopologyInfo(commandEnv *CommandEnv) (topoInfo *master_pb.TopologyInfo, volumeSizeLimitMb uint64, err error) { diff --git a/weed/shell/command_volume_fix_replication.go b/weed/shell/command_volume_fix_replication.go index f03cd550e..5d7506c0b 100644 --- a/weed/shell/command_volume_fix_replication.go +++ b/weed/shell/command_volume_fix_replication.go @@ -110,7 +110,7 @@ func (c *commandVolumeFixReplication) Do(args []string, commandEnv *CommandEnv, underReplicatedVolumeIdsCount = len(underReplicatedVolumeIds) if underReplicatedVolumeIdsCount > 0 { // find the most under populated data nodes - err, fixedVolumeReplicas = c.fixUnderReplicatedVolumes(commandEnv, writer, takeAction, underReplicatedVolumeIds, volumeReplicas, allLocations, *retryCount, *volumesPerStep) + fixedVolumeReplicas, err = c.fixUnderReplicatedVolumes(commandEnv, writer, takeAction, underReplicatedVolumeIds, volumeReplicas, allLocations, *retryCount, *volumesPerStep) if err != nil { return err } @@ -126,7 +126,7 @@ func (c *commandVolumeFixReplication) Do(args []string, commandEnv *CommandEnv, for k, _ := range fixedVolumeReplicas { fixedVolumes = append(fixedVolumes, k) } - err, volumeIdLocations := LookupVolumeIds(commandEnv, fixedVolumes) + volumeIdLocations, err := lookupVolumeIds(commandEnv, fixedVolumes) if err != nil { return err } @@ -137,7 +137,7 @@ func (c *commandVolumeFixReplication) Do(args []string, commandEnv *CommandEnv, for fixedVolumeReplicas[volumeId] >= volumeIdLocationCount { fmt.Fprintf(writer, "the number of locations for volume %s has not increased yet, let's wait\n", volumeId) time.Sleep(time.Duration(i+1) * time.Second * 7) - err, volumeLocIds := LookupVolumeIds(commandEnv, []string{volumeId}) + volumeLocIds, err := lookupVolumeIds(commandEnv, []string{volumeId}) if err != nil { return err } @@ -203,7 +203,7 @@ func (c *commandVolumeFixReplication) fixOverReplicatedVolumes(commandEnv *Comma return nil } -func (c *commandVolumeFixReplication) fixUnderReplicatedVolumes(commandEnv *CommandEnv, writer io.Writer, takeAction bool, underReplicatedVolumeIds []uint32, volumeReplicas map[uint32][]*VolumeReplica, allLocations []location, retryCount int, volumesPerStep int) (err error, fixedVolumes map[string]int) { +func (c *commandVolumeFixReplication) fixUnderReplicatedVolumes(commandEnv *CommandEnv, writer io.Writer, takeAction bool, underReplicatedVolumeIds []uint32, volumeReplicas map[uint32][]*VolumeReplica, allLocations []location, retryCount int, volumesPerStep int) (fixedVolumes map[string]int, err error) { fixedVolumes = map[string]int{} if len(underReplicatedVolumeIds) > volumesPerStep && volumesPerStep > 0 { underReplicatedVolumeIds = underReplicatedVolumeIds[0:volumesPerStep] @@ -218,7 +218,7 @@ func (c *commandVolumeFixReplication) fixUnderReplicatedVolumes(commandEnv *Comm } } } - return nil, fixedVolumes + return fixedVolumes, nil } func (c *commandVolumeFixReplication) fixOneUnderReplicatedVolume(commandEnv *CommandEnv, writer io.Writer, takeAction bool, volumeReplicas map[uint32][]*VolumeReplica, vid uint32, allLocations []location) error { From 1e3fdf366f97786e337c443387c493aff0ff24bf Mon Sep 17 00:00:00 2001 From: Chris Lu Date: Fri, 1 Oct 2021 12:10:24 -0700 Subject: [PATCH 076/130] go fmt --- weed/util/log_buffer/log_buffer.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/weed/util/log_buffer/log_buffer.go b/weed/util/log_buffer/log_buffer.go index c7ddf2d9c..3dcea147f 100644 --- a/weed/util/log_buffer/log_buffer.go +++ b/weed/util/log_buffer/log_buffer.go @@ -189,7 +189,7 @@ func (m *LogBuffer) ReadFromBuffer(lastReadTime time.Time) (bufferCopy *bytes.Bu defer m.RUnlock() if !m.lastFlushTime.IsZero() && m.lastFlushTime.After(lastReadTime) { - if time.Now().Sub(m.lastFlushTime) < m.flushInterval * 2 { + if time.Now().Sub(m.lastFlushTime) < m.flushInterval*2 { diff := m.lastFlushTime.Sub(lastReadTime) glog.V(4).Infof("lastFlush:%v lastRead:%v diff:%v", m.lastFlushTime, lastReadTime, diff) return nil, ResumeFromDiskError From ec3351a4ec21553b3d14587f101ed214ccd8fa7c Mon Sep 17 00:00:00 2001 From: Chris Lu Date: Fri, 1 Oct 2021 16:24:53 -0700 Subject: [PATCH 077/130] adjust comments --- k8s/helm_charts2/values.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/k8s/helm_charts2/values.yaml b/k8s/helm_charts2/values.yaml index adc0f1d2c..b801bbded 100644 --- a/k8s/helm_charts2/values.yaml +++ b/k8s/helm_charts2/values.yaml @@ -161,7 +161,7 @@ volume: # Directories to store data files. dir[,dir]... (default "/tmp") dir: "/data" - # Directories to store index files. dir[,dir]... (default "/tmp") + # Directories to store index files. dir[,dir]... (default is the same as "dir") dir_idx: null # Maximum numbers of volumes, count[,count]... From af207bbaf05ce441e9a87f0833c09a36c0629e85 Mon Sep 17 00:00:00 2001 From: Chris Lu Date: Fri, 1 Oct 2021 23:23:39 -0700 Subject: [PATCH 078/130] retry both assign volume and uploading data fix https://github.com/chrislusf/seaweedfs/issues/2351 --- weed/command/filer_copy.go | 62 +++++++++++++++++++++----------------- 1 file changed, 34 insertions(+), 28 deletions(-) diff --git a/weed/command/filer_copy.go b/weed/command/filer_copy.go index 0feae63b3..630f066d6 100644 --- a/weed/command/filer_copy.go +++ b/weed/command/filer_copy.go @@ -344,9 +344,9 @@ func (worker *FileCopyWorker) uploadFileAsOne(task FileCopyTask, f *os.File) err return err } - // assign a volume - err = util.Retry("assignVolume", func() error { - return pb.WithGrpcFilerClient(worker.filerAddress, worker.options.grpcDialOption, func(client filer_pb.SeaweedFilerClient) error { + err = util.Retry("upload", func() error { + // assign a volume + assignErr := pb.WithGrpcFilerClient(worker.filerAddress, worker.options.grpcDialOption, func(client filer_pb.SeaweedFilerClient) error { request := &filer_pb.AssignVolumeRequest{ Count: 1, @@ -369,35 +369,41 @@ func (worker *FileCopyWorker) uploadFileAsOne(task FileCopyTask, f *os.File) err } return nil }) + if assignErr != nil { + return assignErr + } + + // uplaod data + targetUrl := "http://" + assignResult.Location.Url + "/" + assignResult.FileId + uploadOption := &operation.UploadOption{ + UploadUrl: targetUrl, + Filename: fileName, + Cipher: worker.options.cipher, + IsInputCompressed: false, + MimeType: mimeType, + PairMap: nil, + Jwt: security.EncodedJwt(assignResult.Auth), + } + uploadResult, err := operation.UploadData(data, uploadOption) + if err != nil { + return fmt.Errorf("upload data %v to %s: %v\n", fileName, targetUrl, err) + } + if uploadResult.Error != "" { + return fmt.Errorf("upload %v to %s result: %v\n", fileName, targetUrl, uploadResult.Error) + } + if *worker.options.verbose { + fmt.Printf("uploaded %s to %s\n", fileName, targetUrl) + } + + fmt.Printf("copied %s => http://%s%s%s\n", f.Name(), worker.filerAddress.ToHttpAddress(), task.destinationUrlPath, fileName) + chunks = append(chunks, uploadResult.ToPbFileChunk(assignResult.FileId, 0)) + + return nil }) if err != nil { - return fmt.Errorf("Failed to assign from %v: %v\n", worker.options.masters, err) + return fmt.Errorf("upload %v: %v\n", fileName, err) } - targetUrl := "http://" + assignResult.Location.Url + "/" + assignResult.FileId - uploadOption := &operation.UploadOption{ - UploadUrl: targetUrl, - Filename: fileName, - Cipher: worker.options.cipher, - IsInputCompressed: false, - MimeType: mimeType, - PairMap: nil, - Jwt: security.EncodedJwt(assignResult.Auth), - } - uploadResult, err := operation.UploadData(data, uploadOption) - if err != nil { - return fmt.Errorf("upload data %v to %s: %v\n", fileName, targetUrl, err) - } - if uploadResult.Error != "" { - return fmt.Errorf("upload %v to %s result: %v\n", fileName, targetUrl, uploadResult.Error) - } - if *worker.options.verbose { - fmt.Printf("uploaded %s to %s\n", fileName, targetUrl) - } - - chunks = append(chunks, uploadResult.ToPbFileChunk(assignResult.FileId, 0)) - - fmt.Printf("copied %s => http://%s%s%s\n", f.Name(), worker.filerAddress.ToHttpAddress(), task.destinationUrlPath, fileName) } if err := pb.WithGrpcFilerClient(worker.filerAddress, worker.options.grpcDialOption, func(client filer_pb.SeaweedFilerClient) error { From b297849147a570af9a5413bd668e786a5897de34 Mon Sep 17 00:00:00 2001 From: Chris Lu Date: Fri, 1 Oct 2021 23:24:54 -0700 Subject: [PATCH 079/130] typo --- weed/command/filer_copy.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/weed/command/filer_copy.go b/weed/command/filer_copy.go index 630f066d6..2f3b69da6 100644 --- a/weed/command/filer_copy.go +++ b/weed/command/filer_copy.go @@ -373,7 +373,7 @@ func (worker *FileCopyWorker) uploadFileAsOne(task FileCopyTask, f *os.File) err return assignErr } - // uplaod data + // upload data targetUrl := "http://" + assignResult.Location.Url + "/" + assignResult.FileId uploadOption := &operation.UploadOption{ UploadUrl: targetUrl, From 4c1741fdbb93cc0f3228b59e3912b8aa896a24c0 Mon Sep 17 00:00:00 2001 From: Chris Lu Date: Sat, 2 Oct 2021 14:02:56 -0700 Subject: [PATCH 080/130] working skiplist --- weed/util/skiplist/Makefile | 6 + weed/util/skiplist/serde.go | 54 ++++ weed/util/skiplist/skiplist.go | 480 ++++++++++++++++++++++++++++ weed/util/skiplist/skiplist.pb.go | 386 ++++++++++++++++++++++ weed/util/skiplist/skiplist.proto | 27 ++ weed/util/skiplist/skiplist_test.go | 212 ++++++++++++ 6 files changed, 1165 insertions(+) create mode 100644 weed/util/skiplist/Makefile create mode 100644 weed/util/skiplist/serde.go create mode 100644 weed/util/skiplist/skiplist.go create mode 100644 weed/util/skiplist/skiplist.pb.go create mode 100644 weed/util/skiplist/skiplist.proto create mode 100644 weed/util/skiplist/skiplist_test.go diff --git a/weed/util/skiplist/Makefile b/weed/util/skiplist/Makefile new file mode 100644 index 000000000..af4afe639 --- /dev/null +++ b/weed/util/skiplist/Makefile @@ -0,0 +1,6 @@ +all: gen + +.PHONY : gen + +gen: + protoc skiplist.proto --go_out=plugins=grpc:. --go_opt=paths=source_relative diff --git a/weed/util/skiplist/serde.go b/weed/util/skiplist/serde.go new file mode 100644 index 000000000..2337b4b19 --- /dev/null +++ b/weed/util/skiplist/serde.go @@ -0,0 +1,54 @@ +package skiplist + +import "bytes" + +func compareElement(a *SkipListElement, key []byte) int { + if len(a.Values) == 0 { + return -1 + } + if bytes.Compare(a.Values[0], key) < 0 { + return -1 + } + if bytes.Compare(a.Values[len(a.Values)-1], key) > 0 { + return 1 + } + return 0 +} + +var ( + memStore = make(map[int64]*SkipListElement) +) + +func (node *SkipListElement) Reference() *SkipListElementReference { + if node == nil { + return nil + } + return &SkipListElementReference{ + ElementPointer: node.Id, + Key: node.Values[0], + } +} +func (node *SkipListElement) Save() { + if node == nil { + return + } + memStore[node.Id] = node + //println("++ node", node.Id, string(node.Values[0])) +} + +func (node *SkipListElement) DeleteSelf() { + if node == nil { + return + } + delete(memStore, node.Id) + //println("++ node", node.Id, string(node.Values[0])) +} + +func (ref *SkipListElementReference) Load() *SkipListElement { + if ref == nil { + return nil + } + //println("~ node", ref.ElementPointer, string(ref.Key)) + return memStore[ref.ElementPointer] +} + diff --git a/weed/util/skiplist/skiplist.go b/weed/util/skiplist/skiplist.go new file mode 100644 index 000000000..a47cf4608 --- /dev/null +++ b/weed/util/skiplist/skiplist.go @@ -0,0 +1,480 @@ +package skiplist + +import ( + "bytes" + "fmt" + "math/bits" + "math/rand" + "time" +) + +const ( + // maxLevel denotes the maximum height of the skiplist. This height will keep the skiplist + // efficient for up to 34m entries. If there is a need for much more, please adjust this constant accordingly. + maxLevel = 25 +) + +type SkipList struct { + startLevels [maxLevel]*SkipListElementReference + endLevels [maxLevel]*SkipListElementReference + maxNewLevel int + maxLevel int + elementCount int +} + +// NewSeedEps returns a new empty, initialized Skiplist. +// Given a seed, a deterministic height/list behaviour can be achieved. +// Eps is used to compare keys given by the ExtractKey() function on equality. +func NewSeed(seed int64) *SkipList { + + // Initialize random number generator. + rand.Seed(seed) + //fmt.Printf("SkipList seed: %v\n", seed) + + list := &SkipList{ + maxNewLevel: maxLevel, + maxLevel: 0, + elementCount: 0, + } + + return list +} + +// New returns a new empty, initialized Skiplist. +func New() *SkipList { + return NewSeed(time.Now().UTC().UnixNano()) +} + +// IsEmpty checks, if the skiplist is empty. +func (t *SkipList) IsEmpty() bool { + return t.startLevels[0] == nil +} + +func (t *SkipList) generateLevel(maxLevel int) int { + level := maxLevel - 1 + // First we apply some mask which makes sure that we don't get a level + // above our desired level. Then we find the first set bit. + var x = rand.Uint64() & ((1 << uint(maxLevel-1)) - 1) + zeroes := bits.TrailingZeros64(x) + if zeroes <= maxLevel { + level = zeroes + } + + return level +} + +func (t *SkipList) findEntryIndex(key []byte, level int) int { + // Find good entry point so we don't accidentally skip half the list. + for i := t.maxLevel; i >= 0; i-- { + if t.startLevels[i] != nil && bytes.Compare(t.startLevels[i].Key, key) < 0 || i <= level { + return i + } + } + return 0 +} + +func (t *SkipList) findExtended(key []byte, findGreaterOrEqual bool) (foundElem *SkipListElement, ok bool) { + + foundElem = nil + ok = false + + if t.IsEmpty() { + return + } + + index := t.findEntryIndex(key, 0) + var currentNode *SkipListElement + + currentNode = t.startLevels[index].Load() + + // In case, that our first element is already greater-or-equal! + if findGreaterOrEqual && compareElement(currentNode, key) > 0 { + foundElem = currentNode + ok = true + return + } + + for { + if compareElement(currentNode, key) == 0 { + foundElem = currentNode + ok = true + return + } + + // Which direction are we continuing next time? + if currentNode.Next[index] != nil && bytes.Compare(currentNode.Next[index].Key, key) <= 0 { + // Go right + currentNode = currentNode.Next[index].Load() + } else { + if index > 0 { + + // Early exit + if currentNode.Next[0] != nil && bytes.Compare(currentNode.Next[0].Key, key) == 0 { + currentNodeNext := currentNode.Next[0].Load() + foundElem = currentNodeNext + ok = true + return + } + // Go down + index-- + } else { + // Element is not found and we reached the bottom. + if findGreaterOrEqual { + foundElem = currentNode.Next[index].Load() + ok = foundElem != nil + } + + return + } + } + } +} + +// Find tries to find an element in the skiplist based on the key from the given ListElement. +// elem can be used, if ok is true. +// Find runs in approx. O(log(n)) +func (t *SkipList) Find(key []byte) (elem *SkipListElement, ok bool) { + + if t == nil || key == nil { + return + } + + elem, ok = t.findExtended(key, false) + return +} + +// FindGreaterOrEqual finds the first element, that is greater or equal to the given ListElement e. +// The comparison is done on the keys (So on ExtractKey()). +// FindGreaterOrEqual runs in approx. O(log(n)) +func (t *SkipList) FindGreaterOrEqual(key []byte) (elem *SkipListElement, ok bool) { + + if t == nil || key == nil { + return + } + + elem, ok = t.findExtended(key, true) + return +} + +// Delete removes an element equal to e from the skiplist, if there is one. +// If there are multiple entries with the same value, Delete will remove one of them +// (Which one will change based on the actual skiplist layout) +// Delete runs in approx. O(log(n)) +func (t *SkipList) Delete(key []byte) { + + if t == nil || t.IsEmpty() || key == nil { + return + } + + index := t.findEntryIndex(key, t.maxLevel) + + var currentNode *SkipListElement + var nextNode *SkipListElement + + for { + + if currentNode == nil { + nextNode = t.startLevels[index].Load() + } else { + nextNode = currentNode.Next[index].Load() + } + + // Found and remove! + if nextNode != nil && compareElement(nextNode, key) == 0 { + + if currentNode != nil { + currentNode.Next[index] = nextNode.Next[index] + currentNode.Save() + } + + if index == 0 { + if nextNode.Next[index] != nil { + nextNextNode := nextNode.Next[index].Load() + nextNextNode.Prev = currentNode.Reference() + nextNextNode.Save() + } + t.elementCount-- + nextNode.DeleteSelf() + } + + // Link from start needs readjustments. + startNextKey := t.startLevels[index].Key + if compareElement(nextNode, startNextKey) == 0 { + t.startLevels[index] = nextNode.Next[index] + // This was our currently highest node! + if t.startLevels[index] == nil { + t.maxLevel = index - 1 + } + } + + // Link from end needs readjustments. + if nextNode.Next[index] == nil { + t.endLevels[index] = currentNode.Reference() + } + nextNode.Next[index] = nil + } + + if nextNode != nil && compareElement(nextNode, key) < 0 { + // Go right + currentNode = nextNode + } else { + // Go down + index-- + if index < 0 { + break + } + } + } + +} + +// Insert inserts the given ListElement into the skiplist. +// Insert runs in approx. O(log(n)) +func (t *SkipList) Insert(key []byte) { + + if t == nil || key == nil { + return + } + + level := t.generateLevel(t.maxNewLevel) + + // Only grow the height of the skiplist by one at a time! + if level > t.maxLevel { + level = t.maxLevel + 1 + t.maxLevel = level + } + + elem := &SkipListElement{ + Id: rand.Int63(), + Next: make([]*SkipListElementReference, t.maxNewLevel, t.maxNewLevel), + Level: int32(level), + Values: [][]byte{key}, + } + + t.elementCount++ + + newFirst := true + newLast := true + if !t.IsEmpty() { + newFirst = compareElement(elem, t.startLevels[0].Key) < 0 + newLast = compareElement(elem, t.endLevels[0].Key) > 0 + } + + normallyInserted := false + if !newFirst && !newLast { + + normallyInserted = true + + index := t.findEntryIndex(key, level) + + var currentNode *SkipListElement + var nextNodeRef *SkipListElementReference + + for { + + if currentNode == nil { + nextNodeRef = t.startLevels[index] + } else { + nextNodeRef = currentNode.Next[index] + } + + var nextNode *SkipListElement + + // Connect node to next + if index <= level && (nextNodeRef == nil || bytes.Compare(nextNodeRef.Key, key) > 0) { + elem.Next[index] = nextNodeRef + if currentNode != nil { + currentNode.Next[index] = elem.Reference() + currentNode.Save() + } + if index == 0 { + elem.Prev = currentNode.Reference() + if nextNodeRef != nil { + nextNode = nextNodeRef.Load() + nextNode.Prev = elem.Reference() + nextNode.Save() + } + } + } + + if nextNodeRef != nil && bytes.Compare(nextNodeRef.Key, key) <= 0 { + // Go right + if nextNode == nil { + // reuse nextNode when index == 0 + nextNode = nextNodeRef.Load() + } + currentNode = nextNode + } else { + // Go down + index-- + if index < 0 { + break + } + } + } + } + + // Where we have a left-most position that needs to be referenced! + for i := level; i >= 0; i-- { + + didSomething := false + + if newFirst || normallyInserted { + + if t.startLevels[i] == nil || bytes.Compare(t.startLevels[i].Key, key) > 0 { + if i == 0 && t.startLevels[i] != nil { + startLevelElement := t.startLevels[i].Load() + startLevelElement.Prev = elem.Reference() + startLevelElement.Save() + } + elem.Next[i] = t.startLevels[i] + t.startLevels[i] = elem.Reference() + } + + // link the endLevels to this element! + if elem.Next[i] == nil { + t.endLevels[i] = elem.Reference() + } + + didSomething = true + } + + if newLast { + // Places the element after the very last element on this level! + // This is very important, so we are not linking the very first element (newFirst AND newLast) to itself! + if !newFirst { + if t.endLevels[i] != nil { + endLevelElement := t.endLevels[i].Load() + endLevelElement.Next[i] = elem.Reference() + endLevelElement.Save() + } + if i == 0 { + elem.Prev = t.endLevels[i] + } + t.endLevels[i] = elem.Reference() + } + + // Link the startLevels to this element! + if t.startLevels[i] == nil || bytes.Compare(t.startLevels[i].Key, key) > 0 { + t.startLevels[i] = elem.Reference() + } + + didSomething = true + } + + if !didSomething { + break + } + } + + elem.Save() + +} + +// GetValue extracts the ListElement value from a skiplist node. +func (e *SkipListElement) GetValue() []byte { + return e.Values[0] +} + +// GetSmallestNode returns the very first/smallest node in the skiplist. +// GetSmallestNode runs in O(1) +func (t *SkipList) GetSmallestNode() *SkipListElement { + return t.startLevels[0].Load() +} + +// GetLargestNode returns the very last/largest node in the skiplist. +// GetLargestNode runs in O(1) +func (t *SkipList) GetLargestNode() *SkipListElement { + return t.endLevels[0].Load() +} + +// Next returns the next element based on the given node. +// Next will loop around to the first node, if you call it on the last! +func (t *SkipList) Next(e *SkipListElement) *SkipListElement { + if e.Next[0] == nil { + return t.startLevels[0].Load() + } + return e.Next[0].Load() +} + +// Prev returns the previous element based on the given node. +// Prev will loop around to the last node, if you call it on the first! +func (t *SkipList) Prev(e *SkipListElement) *SkipListElement { + if e.Prev == nil { + return t.endLevels[0].Load() + } + return e.Prev.Load() +} + +// GetNodeCount returns the number of nodes currently in the skiplist. +func (t *SkipList) GetNodeCount() int { + return t.elementCount +} + +// String returns a string format of the skiplist. Useful to get a graphical overview and/or debugging. +func (t *SkipList) println() { + + print("start --> ") + for i, l := range t.startLevels { + if l == nil { + break + } + if i > 0 { + print(" -> ") + } + next := "---" + if l != nil { + next = string(l.Key) + } + print(fmt.Sprintf("[%v]", next)) + } + println() + + nodeRef := t.startLevels[0] + for nodeRef != nil { + print(fmt.Sprintf("%v: ", string(nodeRef.Key))) + node := nodeRef.Load() + for i := 0; i <= int(node.Level); i++ { + + l := node.Next[i] + + next := "---" + if l != nil { + next = string(l.Key) + } + + if i == 0 { + prev := "---" + + if node.Prev != nil { + prev = string(node.Prev.Key) + } + print(fmt.Sprintf("[%v|%v]", prev, next)) + } else { + print(fmt.Sprintf("[%v]", next)) + } + if i < int(node.Level) { + print(" -> ") + } + + } + println() + nodeRef = node.Next[0] + } + + print("end --> ") + for i, l := range t.endLevels { + if l == nil { + break + } + if i > 0 { + print(" -> ") + } + next := "---" + if l != nil { + next = string(l.Key) + } + print(fmt.Sprintf("[%v]", next)) + } + println() +} diff --git a/weed/util/skiplist/skiplist.pb.go b/weed/util/skiplist/skiplist.pb.go new file mode 100644 index 000000000..63b6c74a3 --- /dev/null +++ b/weed/util/skiplist/skiplist.pb.go @@ -0,0 +1,386 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.25.0 +// protoc v3.12.3 +// source: skiplist.proto + +package skiplist + +import ( + proto "github.com/golang/protobuf/proto" + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +// This is a compile-time assertion that a sufficiently up-to-date version +// of the legacy proto package is being used. +const _ = proto.ProtoPackageIsVersion4 + +type SkipListProto struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + StartLevels []*SkipListElementReference `protobuf:"bytes,1,rep,name=start_levels,json=startLevels,proto3" json:"start_levels,omitempty"` + EndLevels []*SkipListElementReference `protobuf:"bytes,2,rep,name=end_levels,json=endLevels,proto3" json:"end_levels,omitempty"` + MaxNewLevel int32 `protobuf:"varint,3,opt,name=max_new_level,json=maxNewLevel,proto3" json:"max_new_level,omitempty"` + MaxLevel int32 `protobuf:"varint,4,opt,name=max_level,json=maxLevel,proto3" json:"max_level,omitempty"` + ElementCount int64 `protobuf:"varint,5,opt,name=element_count,json=elementCount,proto3" json:"element_count,omitempty"` + Eps float64 `protobuf:"fixed64,7,opt,name=eps,proto3" json:"eps,omitempty"` +} + +func (x *SkipListProto) Reset() { + *x = SkipListProto{} + if protoimpl.UnsafeEnabled { + mi := &file_skiplist_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *SkipListProto) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SkipListProto) ProtoMessage() {} + +func (x *SkipListProto) ProtoReflect() protoreflect.Message { + mi := &file_skiplist_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SkipListProto.ProtoReflect.Descriptor instead. +func (*SkipListProto) Descriptor() ([]byte, []int) { + return file_skiplist_proto_rawDescGZIP(), []int{0} +} + +func (x *SkipListProto) GetStartLevels() []*SkipListElementReference { + if x != nil { + return x.StartLevels + } + return nil +} + +func (x *SkipListProto) GetEndLevels() []*SkipListElementReference { + if x != nil { + return x.EndLevels + } + return nil +} + +func (x *SkipListProto) GetMaxNewLevel() int32 { + if x != nil { + return x.MaxNewLevel + } + return 0 +} + +func (x *SkipListProto) GetMaxLevel() int32 { + if x != nil { + return x.MaxLevel + } + return 0 +} + +func (x *SkipListProto) GetElementCount() int64 { + if x != nil { + return x.ElementCount + } + return 0 +} + +func (x *SkipListProto) GetEps() float64 { + if x != nil { + return x.Eps + } + return 0 +} + +type SkipListElementReference struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + ElementPointer int64 `protobuf:"varint,1,opt,name=element_pointer,json=elementPointer,proto3" json:"element_pointer,omitempty"` + Key []byte `protobuf:"bytes,2,opt,name=key,proto3" json:"key,omitempty"` +} + +func (x *SkipListElementReference) Reset() { + *x = SkipListElementReference{} + if protoimpl.UnsafeEnabled { + mi := &file_skiplist_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *SkipListElementReference) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SkipListElementReference) ProtoMessage() {} + +func (x *SkipListElementReference) ProtoReflect() protoreflect.Message { + mi := &file_skiplist_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SkipListElementReference.ProtoReflect.Descriptor instead. +func (*SkipListElementReference) Descriptor() ([]byte, []int) { + return file_skiplist_proto_rawDescGZIP(), []int{1} +} + +func (x *SkipListElementReference) GetElementPointer() int64 { + if x != nil { + return x.ElementPointer + } + return 0 +} + +func (x *SkipListElementReference) GetKey() []byte { + if x != nil { + return x.Key + } + return nil +} + +type SkipListElement struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Id int64 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"` + Next []*SkipListElementReference `protobuf:"bytes,2,rep,name=next,proto3" json:"next,omitempty"` + Level int32 `protobuf:"varint,3,opt,name=level,proto3" json:"level,omitempty"` + Values [][]byte `protobuf:"bytes,4,rep,name=values,proto3" json:"values,omitempty"` + Prev *SkipListElementReference `protobuf:"bytes,5,opt,name=prev,proto3" json:"prev,omitempty"` +} + +func (x *SkipListElement) Reset() { + *x = SkipListElement{} + if protoimpl.UnsafeEnabled { + mi := &file_skiplist_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *SkipListElement) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SkipListElement) ProtoMessage() {} + +func (x *SkipListElement) ProtoReflect() protoreflect.Message { + mi := &file_skiplist_proto_msgTypes[2] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SkipListElement.ProtoReflect.Descriptor instead. +func (*SkipListElement) Descriptor() ([]byte, []int) { + return file_skiplist_proto_rawDescGZIP(), []int{2} +} + +func (x *SkipListElement) GetId() int64 { + if x != nil { + return x.Id + } + return 0 +} + +func (x *SkipListElement) GetNext() []*SkipListElementReference { + if x != nil { + return x.Next + } + return nil +} + +func (x *SkipListElement) GetLevel() int32 { + if x != nil { + return x.Level + } + return 0 +} + +func (x *SkipListElement) GetValues() [][]byte { + if x != nil { + return x.Values + } + return nil +} + +func (x *SkipListElement) GetPrev() *SkipListElementReference { + if x != nil { + return x.Prev + } + return nil +} + +var File_skiplist_proto protoreflect.FileDescriptor + +var file_skiplist_proto_rawDesc = []byte{ + 0x0a, 0x0e, 0x73, 0x6b, 0x69, 0x70, 0x6c, 0x69, 0x73, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x12, 0x08, 0x73, 0x6b, 0x69, 0x70, 0x6c, 0x69, 0x73, 0x74, 0x22, 0x91, 0x02, 0x0a, 0x0d, 0x53, + 0x6b, 0x69, 0x70, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x45, 0x0a, 0x0c, + 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x73, 0x18, 0x01, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x73, 0x6b, 0x69, 0x70, 0x6c, 0x69, 0x73, 0x74, 0x2e, 0x53, 0x6b, + 0x69, 0x70, 0x4c, 0x69, 0x73, 0x74, 0x45, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x66, + 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x52, 0x0b, 0x73, 0x74, 0x61, 0x72, 0x74, 0x4c, 0x65, 0x76, + 0x65, 0x6c, 0x73, 0x12, 0x41, 0x0a, 0x0a, 0x65, 0x6e, 0x64, 0x5f, 0x6c, 0x65, 0x76, 0x65, 0x6c, + 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x73, 0x6b, 0x69, 0x70, 0x6c, 0x69, + 0x73, 0x74, 0x2e, 0x53, 0x6b, 0x69, 0x70, 0x4c, 0x69, 0x73, 0x74, 0x45, 0x6c, 0x65, 0x6d, 0x65, + 0x6e, 0x74, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x52, 0x09, 0x65, 0x6e, 0x64, + 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x73, 0x12, 0x22, 0x0a, 0x0d, 0x6d, 0x61, 0x78, 0x5f, 0x6e, 0x65, + 0x77, 0x5f, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0b, 0x6d, + 0x61, 0x78, 0x4e, 0x65, 0x77, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x12, 0x1b, 0x0a, 0x09, 0x6d, 0x61, + 0x78, 0x5f, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x08, 0x6d, + 0x61, 0x78, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x12, 0x23, 0x0a, 0x0d, 0x65, 0x6c, 0x65, 0x6d, 0x65, + 0x6e, 0x74, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0c, + 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x10, 0x0a, 0x03, + 0x65, 0x70, 0x73, 0x18, 0x07, 0x20, 0x01, 0x28, 0x01, 0x52, 0x03, 0x65, 0x70, 0x73, 0x22, 0x55, + 0x0a, 0x18, 0x53, 0x6b, 0x69, 0x70, 0x4c, 0x69, 0x73, 0x74, 0x45, 0x6c, 0x65, 0x6d, 0x65, 0x6e, + 0x74, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x12, 0x27, 0x0a, 0x0f, 0x65, 0x6c, + 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x03, 0x52, 0x0e, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x50, 0x6f, 0x69, 0x6e, + 0x74, 0x65, 0x72, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, + 0x52, 0x03, 0x6b, 0x65, 0x79, 0x22, 0xbf, 0x01, 0x0a, 0x0f, 0x53, 0x6b, 0x69, 0x70, 0x4c, 0x69, + 0x73, 0x74, 0x45, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x02, 0x69, 0x64, 0x12, 0x36, 0x0a, 0x04, 0x6e, 0x65, 0x78, + 0x74, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x73, 0x6b, 0x69, 0x70, 0x6c, 0x69, + 0x73, 0x74, 0x2e, 0x53, 0x6b, 0x69, 0x70, 0x4c, 0x69, 0x73, 0x74, 0x45, 0x6c, 0x65, 0x6d, 0x65, + 0x6e, 0x74, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x52, 0x04, 0x6e, 0x65, 0x78, + 0x74, 0x12, 0x14, 0x0a, 0x05, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, + 0x52, 0x05, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x12, 0x16, 0x0a, 0x06, 0x76, 0x61, 0x6c, 0x75, 0x65, + 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x06, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x12, + 0x36, 0x0a, 0x04, 0x70, 0x72, 0x65, 0x76, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, + 0x73, 0x6b, 0x69, 0x70, 0x6c, 0x69, 0x73, 0x74, 0x2e, 0x53, 0x6b, 0x69, 0x70, 0x4c, 0x69, 0x73, + 0x74, 0x45, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, + 0x65, 0x52, 0x04, 0x70, 0x72, 0x65, 0x76, 0x42, 0x33, 0x5a, 0x31, 0x67, 0x69, 0x74, 0x68, 0x75, + 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x68, 0x72, 0x69, 0x73, 0x6c, 0x75, 0x73, 0x66, 0x2f, + 0x73, 0x65, 0x61, 0x77, 0x65, 0x65, 0x64, 0x66, 0x73, 0x2f, 0x77, 0x65, 0x65, 0x64, 0x2f, 0x75, + 0x74, 0x69, 0x6c, 0x2f, 0x73, 0x6b, 0x69, 0x70, 0x6c, 0x69, 0x73, 0x74, 0x62, 0x06, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_skiplist_proto_rawDescOnce sync.Once + file_skiplist_proto_rawDescData = file_skiplist_proto_rawDesc +) + +func file_skiplist_proto_rawDescGZIP() []byte { + file_skiplist_proto_rawDescOnce.Do(func() { + file_skiplist_proto_rawDescData = protoimpl.X.CompressGZIP(file_skiplist_proto_rawDescData) + }) + return file_skiplist_proto_rawDescData +} + +var file_skiplist_proto_msgTypes = make([]protoimpl.MessageInfo, 3) +var file_skiplist_proto_goTypes = []interface{}{ + (*SkipListProto)(nil), // 0: skiplist.SkipListProto + (*SkipListElementReference)(nil), // 1: skiplist.SkipListElementReference + (*SkipListElement)(nil), // 2: skiplist.SkipListElement +} +var file_skiplist_proto_depIdxs = []int32{ + 1, // 0: skiplist.SkipListProto.start_levels:type_name -> skiplist.SkipListElementReference + 1, // 1: skiplist.SkipListProto.end_levels:type_name -> skiplist.SkipListElementReference + 1, // 2: skiplist.SkipListElement.next:type_name -> skiplist.SkipListElementReference + 1, // 3: skiplist.SkipListElement.prev:type_name -> skiplist.SkipListElementReference + 4, // [4:4] is the sub-list for method output_type + 4, // [4:4] is the sub-list for method input_type + 4, // [4:4] is the sub-list for extension type_name + 4, // [4:4] is the sub-list for extension extendee + 0, // [0:4] is the sub-list for field type_name +} + +func init() { file_skiplist_proto_init() } +func file_skiplist_proto_init() { + if File_skiplist_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_skiplist_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*SkipListProto); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_skiplist_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*SkipListElementReference); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_skiplist_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*SkipListElement); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_skiplist_proto_rawDesc, + NumEnums: 0, + NumMessages: 3, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_skiplist_proto_goTypes, + DependencyIndexes: file_skiplist_proto_depIdxs, + MessageInfos: file_skiplist_proto_msgTypes, + }.Build() + File_skiplist_proto = out.File + file_skiplist_proto_rawDesc = nil + file_skiplist_proto_goTypes = nil + file_skiplist_proto_depIdxs = nil +} diff --git a/weed/util/skiplist/skiplist.proto b/weed/util/skiplist/skiplist.proto new file mode 100644 index 000000000..ce84ed996 --- /dev/null +++ b/weed/util/skiplist/skiplist.proto @@ -0,0 +1,27 @@ +syntax = "proto3"; + +package skiplist; + +option go_package = "github.com/chrislusf/seaweedfs/weed/util/skiplist"; + +message SkipListProto { + repeated SkipListElementReference start_levels = 1; + repeated SkipListElementReference end_levels = 2; + int32 max_new_level = 3; + int32 max_level = 4; + int64 element_count = 5; + double eps = 7; +} + +message SkipListElementReference { + int64 element_pointer = 1; + bytes key = 2; +} + +message SkipListElement { + int64 id = 1; + repeated SkipListElementReference next = 2; + int32 level = 3; + repeated bytes values = 4; + SkipListElementReference prev = 5; +} diff --git a/weed/util/skiplist/skiplist_test.go b/weed/util/skiplist/skiplist_test.go new file mode 100644 index 000000000..ca41e382a --- /dev/null +++ b/weed/util/skiplist/skiplist_test.go @@ -0,0 +1,212 @@ +package skiplist + +import ( + "bytes" + "math/rand" + "strconv" + "testing" +) + +const ( + maxN = 10000 +) + +func TestInsertAndFind(t *testing.T) { + + k0 := []byte("0") + var list *SkipList + + var listPointer *SkipList + listPointer.Insert(k0) + if _, ok := listPointer.Find(k0); ok { + t.Fail() + } + + list = New() + if _, ok := list.Find(k0); ok { + t.Fail() + } + if !list.IsEmpty() { + t.Fail() + } + + // Test at the beginning of the list. + for i := 0; i < maxN; i++ { + key := []byte(strconv.Itoa(maxN-i)) + list.Insert(key) + } + for i := 0; i < maxN; i++ { + key := []byte(strconv.Itoa(maxN-i)) + if _, ok := list.Find(key); !ok { + t.Fail() + } + } + + + list = New() + // Test at the end of the list. + for i := 0; i < maxN; i++ { + key := []byte(strconv.Itoa(i)) + list.Insert(key) + } + for i := 0; i < maxN; i++ { + key := []byte(strconv.Itoa(i)) + if _, ok := list.Find(key); !ok { + t.Fail() + } + } + + list = New() + // Test at random positions in the list. + rList := rand.Perm(maxN) + for _, e := range rList { + key := []byte(strconv.Itoa(e)) + println("insert", e) + list.Insert(key) + } + for _, e := range rList { + key := []byte(strconv.Itoa(e)) + println("find", e) + if _, ok := list.Find(key); !ok { + t.Fail() + } + } + println("print list") + list.println() + +} + +func Element(x int) []byte { + return []byte(strconv.Itoa(x)) +} + +func TestDelete(t *testing.T) { + + k0 := []byte("0") + + var list *SkipList + + // Delete on empty list + list.Delete(k0) + + list = New() + + list.Delete(k0) + if !list.IsEmpty() { + t.Fail() + } + + list.Insert(k0) + list.Delete(k0) + if !list.IsEmpty() { + t.Fail() + } + + // Delete elements at the beginning of the list. + for i := 0; i < maxN; i++ { + list.Insert(Element(i)) + } + for i := 0; i < maxN; i++ { + list.Delete(Element(i)) + } + if !list.IsEmpty() { + t.Fail() + } + + list = New() + // Delete elements at the end of the list. + for i := 0; i < maxN; i++ { + list.Insert(Element(i)) + } + for i := 0; i < maxN; i++ { + list.Delete(Element(maxN - i - 1)) + } + if !list.IsEmpty() { + t.Fail() + } + + list = New() + // Delete elements at random positions in the list. + rList := rand.Perm(maxN) + for _, e := range rList { + list.Insert(Element(e)) + } + for _, e := range rList { + list.Delete(Element(e)) + } + if !list.IsEmpty() { + t.Fail() + } +} + +func TestNext(t *testing.T) { + list := New() + + for i := 0; i < maxN; i++ { + list.Insert(Element(i)) + } + + smallest := list.GetSmallestNode() + largest := list.GetLargestNode() + + lastNode := smallest + node := lastNode + for node != largest { + node = list.Next(node) + // Must always be incrementing here! + if bytes.Compare(node.Values[0], lastNode.Values[0]) <= 0 { + t.Fail() + } + // Next.Prev must always point to itself! + if list.Next(list.Prev(node)) != node { + t.Fail() + } + lastNode = node + } + + if list.Next(largest) != smallest { + t.Fail() + } +} + +func TestPrev(t *testing.T) { + list := New() + + for i := 0; i < maxN; i++ { + list.Insert(Element(i)) + } + + smallest := list.GetSmallestNode() + largest := list.GetLargestNode() + + lastNode := largest + node := lastNode + for node != smallest { + node = list.Prev(node) + // Must always be incrementing here! + if bytes.Compare(node.Values[0], lastNode.Values[0]) >= 0 { + t.Fail() + } + // Next.Prev must always point to itself! + if list.Prev(list.Next(node)) != node { + t.Fail() + } + lastNode = node + } + + if list.Prev(smallest) != largest { + t.Fail() + } +} + +func TestGetNodeCount(t *testing.T) { + list := New() + + for i := 0; i < maxN; i++ { + list.Insert(Element(i)) + } + + if list.GetNodeCount() != maxN { + t.Fail() + } +} From 57e2fd3f9bb3c094f897daaaed3851f5b5af0ed2 Mon Sep 17 00:00:00 2001 From: Chris Lu Date: Sat, 2 Oct 2021 14:03:54 -0700 Subject: [PATCH 081/130] remove bptree --- weed/util/bptree/Makefile | 6 - weed/util/bptree/README.md | 60 - weed/util/bptree/bpmap.go | 69 - weed/util/bptree/bptree.go | 158 -- weed/util/bptree/bptree.pb.go | 195 --- weed/util/bptree/bptree.proto | 14 - weed/util/bptree/bptree_node.go | 767 ---------- weed/util/bptree/bptree_store_test.go | 53 - weed/util/bptree/bptree_test.go | 1408 ------------------ weed/util/bptree/getter_setter.go | 72 - weed/util/bptree/int.go | 357 ----- weed/util/bptree/rand.go | 2 - weed/util/bptree/serde.go | 10 - weed/util/bptree/serde_test.go | 46 - weed/util/bptree/string.go | 71 - weed/util/bptree/tree_store/memory_store.go | 29 - weed/util/bptree/tree_store/tree_store.go.go | 6 - weed/util/bptree/types.go | 98 -- 18 files changed, 3421 deletions(-) delete mode 100644 weed/util/bptree/Makefile delete mode 100644 weed/util/bptree/README.md delete mode 100644 weed/util/bptree/bpmap.go delete mode 100644 weed/util/bptree/bptree.go delete mode 100644 weed/util/bptree/bptree.pb.go delete mode 100644 weed/util/bptree/bptree.proto delete mode 100644 weed/util/bptree/bptree_node.go delete mode 100644 weed/util/bptree/bptree_store_test.go delete mode 100644 weed/util/bptree/bptree_test.go delete mode 100644 weed/util/bptree/getter_setter.go delete mode 100644 weed/util/bptree/int.go delete mode 100644 weed/util/bptree/rand.go delete mode 100644 weed/util/bptree/serde.go delete mode 100644 weed/util/bptree/serde_test.go delete mode 100644 weed/util/bptree/string.go delete mode 100644 weed/util/bptree/tree_store/memory_store.go delete mode 100644 weed/util/bptree/tree_store/tree_store.go.go delete mode 100644 weed/util/bptree/types.go diff --git a/weed/util/bptree/Makefile b/weed/util/bptree/Makefile deleted file mode 100644 index a98f39a08..000000000 --- a/weed/util/bptree/Makefile +++ /dev/null @@ -1,6 +0,0 @@ -all: gen - -.PHONY : gen - -gen: - protoc bptree.proto --go_out=plugins=grpc:. --go_opt=paths=source_relative diff --git a/weed/util/bptree/README.md b/weed/util/bptree/README.md deleted file mode 100644 index 1dddae940..000000000 --- a/weed/util/bptree/README.md +++ /dev/null @@ -1,60 +0,0 @@ -This adapts one b+ tree implementation -https://sourcegraph.com/github.com/timtadh/data-structures@master/-/tree/tree/bptree -to persist changes to on disk. - -# When a node needs to persist itself? - -* A node changed its key or value - * When an item is added. - * When an item is updated. - * When an item is deleted. - -* When a node is split. - * 2 new nodes are created (they shoud persist themselves). - * Parent node need to point to the new nodes. - -* When a node is merged. - * delete one node - * persist the merged node - - -In general, if one node is returned from a function, the node should have already been persisted. -The parent node may need to delete the old node. - -BpTree - Add(key ItemKey, value ItemValue) - new_root = self.getRoot().put(key,value) - a, b, err := self.insert(key, value) - self.internal_insert(key, value) - self.internal_split(q.keys[0], q) - persist(a,b) - self.persist() // child add q node - self.maybePersist(child == p) - self.leaf_insert(key, value) - self.persist() // if dedup - self.leaf_split(key, value) - self.pure_leaf_split(key, value) - persist(a,b) - a.persist() - persist(a,b) - self.put_kv(key, value) - new_root.persist() - self.setRoot(new_root) - oldroot.destroy() - // maybe persist BpTree new root - - Replace(key ItemKey, where WhereFunc, value ItemValue) - leaf.persist() - RemoveWhere(key ItemKey, where WhereFunc) - self.getRoot().remove(key, where) - self.internal_remove(key, nil, where) - child.leaf_remove(key, nil, where) - child.leaf_remove(key, sibling.keys[0], where) - l.destroy() // when the node is empty - a.maybePersist(hasChange) - self.destroy() // when no keys left - self.persist() // when some keys are left - self.leaf_remove(key, self.keys[len(self.keys)-1], where) - new_root.persist() // when new root is added - // maybe persist BpTree new root - \ No newline at end of file diff --git a/weed/util/bptree/bpmap.go b/weed/util/bptree/bpmap.go deleted file mode 100644 index 0c13a132f..000000000 --- a/weed/util/bptree/bpmap.go +++ /dev/null @@ -1,69 +0,0 @@ -package bptree - -import ( - "fmt" -) - -/* A BpMap is a B+Tree with support for duplicate keys disabled. This makes it - * behave like a regular Map rather than a MultiMap. - */ -type BpMap BpTree - -func NewBpMap(node_size int, nodeStore NodeStore) *BpMap { - return &BpMap{ - root: NewLeaf(node_size, nodeStore), - } -} - -func (self *BpMap) Has(key ItemKey) bool { - return (*BpTree)(self).Has(key) -} - -func (self *BpMap) Put(key ItemKey, value ItemValue) (err error) { - new_root, err := self.getRoot().put(key, value) - if err != nil { - return err - } - self.setRoot(new_root) - return nil -} - -func (self *BpMap) Get(key ItemKey) (value ItemValue, err error) { - j, l := self.getRoot().get_start(key) - if l.keys[j].Equals(key) { - return l.values[j], nil - } - return nil, fmt.Errorf("key not found: %s", key) -} - -func (self *BpMap) Remove(key ItemKey) (value ItemValue, err error) { - value, err = self.Get(key) - if err != nil { - return nil, err - } - ns := self.getRoot().Capacity() - new_root, err := self.getRoot().remove(key, func(value ItemValue) bool { return true }) - if err != nil { - return nil, err - } - if new_root == nil { - new_root = NewLeaf(ns, self.root.nodeStore) - err = new_root.persist() - self.setRoot(new_root) - } else { - self.setRoot(new_root) - } - return value, nil -} - -func (self *BpMap) Keys() (ki KIterator) { - return (*BpTree)(self).Keys() -} - -func (self *BpMap) Values() (vi Iterator) { - return (*BpTree)(self).Values() -} - -func (self *BpMap) Iterate() (kvi KVIterator) { - return (*BpTree)(self).Iterate() -} diff --git a/weed/util/bptree/bptree.go b/weed/util/bptree/bptree.go deleted file mode 100644 index 141c595f3..000000000 --- a/weed/util/bptree/bptree.go +++ /dev/null @@ -1,158 +0,0 @@ -package bptree - -// started by copying from https://sourcegraph.com/github.com/timtadh/data-structures@master/-/tree/tree/bptree - -/* A BpTree is a B+Tree with support for duplicate keys. This makes it behave as - * a MultiMap. Additionally you can use the Range operator to select k/v in a - * range. If from > to it will iterate backwards. - */ -type BpTree struct { - root *BpNode -} - -type loc_iterator func() (i int, leaf *BpNode, li loc_iterator) - -func NewBpTree(node_size int, nodeStore NodeStore) *BpTree { - return &BpTree{ - root: NewLeaf(node_size, nodeStore), - } -} - -func (self *BpTree) Has(key ItemKey) bool { - if len(self.getRoot().keys) == 0 { - return false - } - j, l := self.getRoot().get_start(key) - return l.keys[j].Equals(key) -} - -func (self *BpTree) Count(key ItemKey) int { - if len(self.root.keys) == 0 { - return 0 - } - j, l := self.root.get_start(key) - count := 0 - end := false - for !end && l.keys[j].Equals(key) { - count++ - j, l, end = next_location(j, l) - } - return count -} - -func (self *BpTree) Add(key ItemKey, value ItemValue) (err error) { - new_root, err := self.getRoot().put(key, value) - if err != nil { - return err - } - self.setRoot(new_root) - return nil -} - -func (self *BpTree) Replace(key ItemKey, where WhereFunc, value ItemValue) (err error) { - li := self.getRoot().forward(key, key) - for i, leaf, next := li(); next != nil; i, leaf, next = next() { - if where(leaf.values[i]) { - leaf.values[i] = value - if persistErr := leaf.persist(); persistErr != nil && err == nil { - err = persistErr - break - } - } - } - return err -} - -func (self *BpTree) Find(key ItemKey) (kvi KVIterator) { - return self.Range(key, key) -} - -func (self *BpTree) Range(from, to ItemKey) (kvi KVIterator) { - var li loc_iterator - if !to.Less(from) { - li = self.getRoot().forward(from, to) - } else { - li = self.getRoot().backward(from, to) - } - kvi = func() (key ItemKey, value ItemValue, next KVIterator) { - var i int - var leaf *BpNode - i, leaf, li = li() - if li == nil { - return nil, nil, nil - } - return leaf.keys[i], leaf.values[i], kvi - } - return kvi -} - -func (self *BpTree) RemoveWhere(key ItemKey, where WhereFunc) (err error) { - ns := self.getRoot().Capacity() - new_root, err := self.getRoot().remove(key, where) - if err != nil { - return err - } - if new_root == nil { - new_root = NewLeaf(ns, self.root.nodeStore) - err = new_root.persist() - self.setRoot(new_root) - } else { - self.setRoot(new_root) - } - return err -} - -func (self *BpTree) Keys() (ki KIterator) { - li := self.getRoot().all() - var prev Equatable - ki = func() (key ItemKey, next KIterator) { - var i int - var leaf *BpNode - i, leaf, li = li() - if li == nil { - return nil, nil - } - if leaf.keys[i].Equals(prev) { - return ki() - } - prev = leaf.keys[i] - return leaf.keys[i], ki - } - return ki -} - -func (self *BpTree) Values() (vi Iterator) { - return MakeValuesIterator(self) -} - -func (self *BpTree) Items() (vi KIterator) { - return MakeItemsIterator(self) -} - -func (self *BpTree) Iterate() (kvi KVIterator) { - li := self.getRoot().all() - kvi = func() (key ItemKey, value ItemValue, next KVIterator) { - var i int - var leaf *BpNode - i, leaf, li = li() - if li == nil { - return nil, nil, nil - } - return leaf.keys[i], leaf.values[i], kvi - } - return kvi -} - -func (self *BpTree) Backward() (kvi KVIterator) { - li := self.getRoot().all_backward() - kvi = func() (key ItemKey, value ItemValue, next KVIterator) { - var i int - var leaf *BpNode - i, leaf, li = li() - if li == nil { - return nil, nil, nil - } - return leaf.keys[i], leaf.values[i], kvi - } - return kvi -} diff --git a/weed/util/bptree/bptree.pb.go b/weed/util/bptree/bptree.pb.go deleted file mode 100644 index 078a54717..000000000 --- a/weed/util/bptree/bptree.pb.go +++ /dev/null @@ -1,195 +0,0 @@ -// Code generated by protoc-gen-go. DO NOT EDIT. -// versions: -// protoc-gen-go v1.25.0 -// protoc v3.12.3 -// source: bptree.proto - -package bptree - -import ( - proto "github.com/golang/protobuf/proto" - protoreflect "google.golang.org/protobuf/reflect/protoreflect" - protoimpl "google.golang.org/protobuf/runtime/protoimpl" - reflect "reflect" - sync "sync" -) - -const ( - // Verify that this generated code is sufficiently up-to-date. - _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) - // Verify that runtime/protoimpl is sufficiently up-to-date. - _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) -) - -// This is a compile-time assertion that a sufficiently up-to-date version -// of the legacy proto package is being used. -const _ = proto.ProtoPackageIsVersion4 - -type ProtoNode struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Keys [][]byte `protobuf:"bytes,1,rep,name=keys,proto3" json:"keys,omitempty"` - Values [][]byte `protobuf:"bytes,2,rep,name=values,proto3" json:"values,omitempty"` - Pointers []int64 `protobuf:"varint,3,rep,packed,name=pointers,proto3" json:"pointers,omitempty"` - Next int64 `protobuf:"varint,4,opt,name=next,proto3" json:"next,omitempty"` - Prev int64 `protobuf:"varint,5,opt,name=prev,proto3" json:"prev,omitempty"` - Id int64 `protobuf:"varint,6,opt,name=id,proto3" json:"id,omitempty"` -} - -func (x *ProtoNode) Reset() { - *x = ProtoNode{} - if protoimpl.UnsafeEnabled { - mi := &file_bptree_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *ProtoNode) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ProtoNode) ProtoMessage() {} - -func (x *ProtoNode) ProtoReflect() protoreflect.Message { - mi := &file_bptree_proto_msgTypes[0] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ProtoNode.ProtoReflect.Descriptor instead. -func (*ProtoNode) Descriptor() ([]byte, []int) { - return file_bptree_proto_rawDescGZIP(), []int{0} -} - -func (x *ProtoNode) GetKeys() [][]byte { - if x != nil { - return x.Keys - } - return nil -} - -func (x *ProtoNode) GetValues() [][]byte { - if x != nil { - return x.Values - } - return nil -} - -func (x *ProtoNode) GetPointers() []int64 { - if x != nil { - return x.Pointers - } - return nil -} - -func (x *ProtoNode) GetNext() int64 { - if x != nil { - return x.Next - } - return 0 -} - -func (x *ProtoNode) GetPrev() int64 { - if x != nil { - return x.Prev - } - return 0 -} - -func (x *ProtoNode) GetId() int64 { - if x != nil { - return x.Id - } - return 0 -} - -var File_bptree_proto protoreflect.FileDescriptor - -var file_bptree_proto_rawDesc = []byte{ - 0x0a, 0x0c, 0x62, 0x70, 0x74, 0x72, 0x65, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x06, - 0x62, 0x70, 0x74, 0x72, 0x65, 0x65, 0x22, 0x8b, 0x01, 0x0a, 0x09, 0x50, 0x72, 0x6f, 0x74, 0x6f, - 0x4e, 0x6f, 0x64, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6b, 0x65, 0x79, 0x73, 0x18, 0x01, 0x20, 0x03, - 0x28, 0x0c, 0x52, 0x04, 0x6b, 0x65, 0x79, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x76, 0x61, 0x6c, 0x75, - 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x06, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, - 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x73, 0x18, 0x03, 0x20, 0x03, - 0x28, 0x03, 0x52, 0x08, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x73, 0x12, 0x12, 0x0a, 0x04, - 0x6e, 0x65, 0x78, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x04, 0x6e, 0x65, 0x78, 0x74, - 0x12, 0x12, 0x0a, 0x04, 0x70, 0x72, 0x65, 0x76, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x04, - 0x70, 0x72, 0x65, 0x76, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x03, - 0x52, 0x02, 0x69, 0x64, 0x42, 0x31, 0x5a, 0x2f, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, - 0x6f, 0x6d, 0x2f, 0x63, 0x68, 0x72, 0x69, 0x73, 0x6c, 0x75, 0x73, 0x66, 0x2f, 0x73, 0x65, 0x61, - 0x77, 0x65, 0x65, 0x64, 0x66, 0x73, 0x2f, 0x77, 0x65, 0x65, 0x64, 0x2f, 0x75, 0x74, 0x69, 0x6c, - 0x2f, 0x62, 0x70, 0x74, 0x72, 0x65, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, -} - -var ( - file_bptree_proto_rawDescOnce sync.Once - file_bptree_proto_rawDescData = file_bptree_proto_rawDesc -) - -func file_bptree_proto_rawDescGZIP() []byte { - file_bptree_proto_rawDescOnce.Do(func() { - file_bptree_proto_rawDescData = protoimpl.X.CompressGZIP(file_bptree_proto_rawDescData) - }) - return file_bptree_proto_rawDescData -} - -var file_bptree_proto_msgTypes = make([]protoimpl.MessageInfo, 1) -var file_bptree_proto_goTypes = []interface{}{ - (*ProtoNode)(nil), // 0: bptree.ProtoNode -} -var file_bptree_proto_depIdxs = []int32{ - 0, // [0:0] is the sub-list for method output_type - 0, // [0:0] is the sub-list for method input_type - 0, // [0:0] is the sub-list for extension type_name - 0, // [0:0] is the sub-list for extension extendee - 0, // [0:0] is the sub-list for field type_name -} - -func init() { file_bptree_proto_init() } -func file_bptree_proto_init() { - if File_bptree_proto != nil { - return - } - if !protoimpl.UnsafeEnabled { - file_bptree_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ProtoNode); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - } - type x struct{} - out := protoimpl.TypeBuilder{ - File: protoimpl.DescBuilder{ - GoPackagePath: reflect.TypeOf(x{}).PkgPath(), - RawDescriptor: file_bptree_proto_rawDesc, - NumEnums: 0, - NumMessages: 1, - NumExtensions: 0, - NumServices: 0, - }, - GoTypes: file_bptree_proto_goTypes, - DependencyIndexes: file_bptree_proto_depIdxs, - MessageInfos: file_bptree_proto_msgTypes, - }.Build() - File_bptree_proto = out.File - file_bptree_proto_rawDesc = nil - file_bptree_proto_goTypes = nil - file_bptree_proto_depIdxs = nil -} diff --git a/weed/util/bptree/bptree.proto b/weed/util/bptree/bptree.proto deleted file mode 100644 index 1d55096a2..000000000 --- a/weed/util/bptree/bptree.proto +++ /dev/null @@ -1,14 +0,0 @@ -syntax = "proto3"; - -package bptree; - -option go_package = "github.com/chrislusf/seaweedfs/weed/util/bptree"; - -message ProtoNode { - repeated bytes keys = 1; - repeated bytes values = 2; - repeated int64 pointers = 3; - int64 next = 4; - int64 prev = 5; - int64 id = 6; -} diff --git a/weed/util/bptree/bptree_node.go b/weed/util/bptree/bptree_node.go deleted file mode 100644 index 507d9d318..000000000 --- a/weed/util/bptree/bptree_node.go +++ /dev/null @@ -1,767 +0,0 @@ -package bptree - -type ItemKey Hashable -type ItemValue Equatable - -type NodeStore interface { - PersistFunc(node *BpNode) error - DestroyFunc(node *BpNode) error -} - -type BpNode struct { - keys []ItemKey - values []ItemValue - pointers []*BpNode - next *BpNode - prev *BpNode - protoNodeId int64 - protoNode *ProtoNode - nodeStore NodeStore -} - -func NewInternal(size int, nodeStore NodeStore) *BpNode { - if size < 0 { - panic(NegativeSize()) - } - return &BpNode{ - keys: make([]ItemKey, 0, size), - pointers: make([]*BpNode, 0, size), - protoNodeId: GetProtoNodeId(), - nodeStore: nodeStore, - } -} - -func NewLeaf(size int, nodeStore NodeStore) *BpNode { - if size < 0 { - panic(NegativeSize()) - } - return &BpNode{ - keys: make([]ItemKey, 0, size), - values: make([]ItemValue, 0, size), - protoNodeId: GetProtoNodeId(), - nodeStore: nodeStore, - } -} - -func (self *BpNode) Full() bool { - return len(self.keys) == cap(self.keys) -} - -func (self *BpNode) Pure() bool { - if len(self.keys) == 0 { - return true - } - k0 := self.keys[0] - for _, k := range self.keys { - if !k0.Equals(k) { - return false - } - } - return true -} - -func (self *BpNode) Internal() bool { - return cap(self.pointers) > 0 -} - -func (self *BpNode) Len() int { - return len(self.keys) -} - -func (self *BpNode) Capacity() int { - return cap(self.keys) -} - -func (self *BpNode) Height() int { - if !self.Internal() { - return 1 - } else if len(self.pointers) == 0 { - panic(BpTreeError("Internal node has no pointers but asked for height")) - } - return self.pointers[0].Height() + 1 -} - -func (self *BpNode) has(key ItemKey) bool { - _, has := self.find(key) - return has -} - -func (self *BpNode) left_most_leaf() *BpNode { - if self.Internal() { - return self.pointers[0].left_most_leaf() - } - return self -} - -func (self *BpNode) right_most_leaf() *BpNode { - if self.Internal() { - return self.pointers[len(self.pointers)-1].right_most_leaf() - } - return self -} - -/* returns the index and leaf-block of the first key greater than or equal to - * the search key. (unless the search key is greater than all the keys in the - * tree, in that case it will be the last key in the tree) - */ -func (self *BpNode) get_start(key ItemKey) (i int, leaf *BpNode) { - if self.Internal() { - return self.internal_get_start(key) - } else { - return self.leaf_get_start(key) - } -} - -func next_location(i int, leaf *BpNode) (int, *BpNode, bool) { - j := i + 1 - for j >= len(leaf.keys) && leaf.getNext() != nil { - j = 0 - leaf = leaf.getNext() - } - if j >= len(leaf.keys) { - return -1, nil, true - } - return j, leaf, false -} - -func prev_location(i int, leaf *BpNode) (int, *BpNode, bool) { - j := i - 1 - for j < 0 && leaf.getPrev() != nil { - leaf = leaf.getPrev() - j = len(leaf.keys) - 1 - } - if j < 0 { - return -1, nil, true - } - return j, leaf, false -} - -/* returns the index and leaf-block of the last key equal to the search key or - * the first key greater than the search key. (unless the search key is greater - * than all the keys in the tree, in that case it will be the last key in the - * tree) - */ -func (self *BpNode) get_end(key ItemKey) (i int, leaf *BpNode) { - end := false - i, leaf = self.get_start(key) - pi, pleaf := i, leaf - for !end && leaf.keys[i].Equals(key) { - pi, pleaf = i, leaf - i, leaf, end = next_location(i, leaf) - } - return pi, pleaf -} - -func (self *BpNode) internal_get_start(key ItemKey) (i int, leaf *BpNode) { - if !self.Internal() { - panic(BpTreeError("Expected a internal node")) - } - i, has := self.find(key) - if !has && i > 0 { - // if it doesn't have it and the index > 0 then we have the next block - // so we have to subtract one from the index. - i-- - } - child := self.pointers[i] - return child.get_start(key) -} - -func (self *BpNode) leaf_get_start(key ItemKey) (i int, leaf *BpNode) { - i, has := self.find(key) - if i >= len(self.keys) && i > 0 { - i = len(self.keys) - 1 - } - if !has && (len(self.keys) == 0 || self.keys[i].Less(key)) && self.getNext() != nil { - return self.getNext().leaf_get_start(key) - } - return i, self -} - -/* This puts the k/v pair into the B+Tree rooted at this node and returns the - * (possibly) new root of the tree. - */ -func (self *BpNode) put(key ItemKey, value ItemValue) (root *BpNode, err error) { - a, b, err := self.insert(key, value) - if err != nil { - return nil, err - } else if b == nil { - return a, nil - } - // else we have root split - root = NewInternal(self.Capacity(), self.nodeStore) - root.put_kp(a.keys[0], a) - root.put_kp(b.keys[0], b) - return root, root.persist() -} - -// right is only set on split -// left is always set. When split is false left is the pointer to block -// When split is true left is the pointer to the new left -// block -func (self *BpNode) insert(key ItemKey, value ItemValue) (a, b *BpNode, err error) { - if self.Internal() { - return self.internal_insert(key, value) - } else { // leaf node - return self.leaf_insert(key, value) - } -} - -/* - first find the child to insert into - * - do the child insert - * - if there was a split: - * - if the block is full, split this block - * - else insert the new key/pointer into this block - */ -func (self *BpNode) internal_insert(key ItemKey, value ItemValue) (a, b *BpNode, err error) { - if !self.Internal() { - return nil, nil, BpTreeError("Expected a internal node") - } - i, has := self.find(key) - if !has && i > 0 { - // if it doesn't have it and the index > 0 then we have the next block - // so we have to subtract one from the index. - i-- - } - child := self.pointers[i] - p, q, err := child.insert(key, value) - if err != nil { - return nil, nil, err - } - self.keys[i] = p.keys[0] - self.pointers[i] = p - if q != nil { - // we had a split - if self.Full() { - return self.internal_split(q.keys[0], q) - } else { - if err := self.put_kp(q.keys[0], q); err != nil { - return nil, nil, err - } - return self, nil, self.persist() - } - } - return self, nil, self.maybePersist(child != p) -} - -/* On split - * - first assert that the key to be inserted is not already in the block. - * - Make a new block - * - balance the two blocks. - * - insert the new key/pointer combo into the correct block - */ -func (self *BpNode) internal_split(key ItemKey, ptr *BpNode) (a, b *BpNode, err error) { - if !self.Internal() { - return nil, nil, BpTreeError("Expected a internal node") - } - if self.has(key) { - return nil, nil, BpTreeError("Tried to split an internal block on duplicate key") - } - a = self - b = NewInternal(self.Capacity(), self.nodeStore) - balance_nodes(a, b, key) - if b.Len() > 0 && key.Less(b.keys[0]) { - if err := a.put_kp(key, ptr); err != nil { - return nil, nil, err - } - } else { - if err := b.put_kp(key, ptr); err != nil { - return nil, nil, err - } - } - return a, b, persist(a, b) -} - -/* if the leaf is full then it will defer to a leaf_split - * (but in one case that will not actually split in the case of a insert into - * a pure block with a matching key) - * else this leaf will get a new entry. - */ -func (self *BpNode) leaf_insert(key ItemKey, value ItemValue) (a, b *BpNode, err error) { - if self.Internal() { - return nil, nil, BpTreeError("Expected a leaf node") - } - if true { // no_dup = true - i, has := self.find(key) - if has { - self.values[i] = value - return self, nil, self.persist() - } - } - if self.Full() { - return self.leaf_split(key, value) - } else { - if err := self.put_kv(key, value); err != nil { - return nil, nil, err - } - return self, nil, self.persist() - } -} - -/* on leaf split if the block is pure then it will defer to pure_leaf_split - * else - * - a new block will be made and inserted after this one - * - the two blocks will be balanced with balanced_nodes - * - if the key is less than b.keys[0] it will go in a else b - */ -func (self *BpNode) leaf_split(key ItemKey, value ItemValue) (a, b *BpNode, err error) { - if self.Internal() { - return nil, nil, BpTreeError("Expected a leaf node") - } - if self.Pure() { - return self.pure_leaf_split(key, value) - } - a = self - b = NewLeaf(self.Capacity(), self.nodeStore) - insert_linked_list_node(b, a, a.getNext()) - balance_nodes(a, b, key) - if b.Len() > 0 && key.Less(b.keys[0]) { - if err := a.put_kv(key, value); err != nil { - return nil, nil, err - } - } else { - if err := b.put_kv(key, value); err != nil { - return nil, nil, err - } - } - return a, b, persist(a, b) -} - -/* a pure leaf split has two cases: - * 1) the inserted key is less than the current pure block. - * - a new block should be created before the current block - * - the key should be put in it - * 2) the inserted key is greater than or equal to the pure block. - * - the end of run of pure blocks should be found - * - if the key is equal to pure block and the last block is not full insert - * the new kv - * - else split by making a new block after the last block in the run - * and putting the new key there. - * - always return the current block as "a" and the new block as "b" - */ -func (self *BpNode) pure_leaf_split(key ItemKey, value ItemValue) (a, b *BpNode, err error) { - if self.Internal() || !self.Pure() { - return nil, nil, BpTreeError("Expected a pure leaf node") - } - if key.Less(self.keys[0]) { - a = NewLeaf(self.Capacity(), self.nodeStore) - b = self - if err := a.put_kv(key, value); err != nil { - return nil, nil, err - } - insert_linked_list_node(a, b.getPrev(), b) - return a, b, persist(a, b) - } else { - a = self - e := self.find_end_of_pure_run() - if e.keys[0].Equals(key) && !e.Full() { - if err := e.put_kv(key, value); err != nil { - return nil, nil, err - } - return a, nil, a.persist() - } else { - b = NewLeaf(self.Capacity(), self.nodeStore) - if err := b.put_kv(key, value); err != nil { - return nil, nil, err - } - insert_linked_list_node(b, e, e.getNext()) - if e.keys[0].Equals(key) { - return a, nil, nil - } - return a, b, persist(a, b) - } - } -} - -func (self *BpNode) put_kp(key ItemKey, ptr *BpNode) error { - if self.Full() { - return BpTreeError("Block is full.") - } - if !self.Internal() { - return BpTreeError("Expected a internal node") - } - i, has := self.find(key) - if has { - return BpTreeError("Tried to insert a duplicate key into an internal node") - } else if i < 0 { - panic(BpTreeError("find returned a negative int")) - } else if i >= cap(self.keys) { - panic(BpTreeError("find returned a int > than cap(keys)")) - } - if err := self.put_key_at(i, key); err != nil { - return err - } - if err := self.put_pointer_at(i, ptr); err != nil { - return err - } - return nil -} - -func (self *BpNode) put_kv(key ItemKey, value ItemValue) error { - if self.Full() { - return BpTreeError("Block is full.") - } - if self.Internal() { - return BpTreeError("Expected a leaf node") - } - i, _ := self.find(key) - if i < 0 { - panic(BpTreeError("find returned a negative int")) - } else if i >= cap(self.keys) { - panic(BpTreeError("find returned a int > than cap(keys)")) - } - if err := self.put_key_at(i, key); err != nil { - return err - } - if err := self.put_value_at(i, value); err != nil { - return err - } - return nil -} - -func (self *BpNode) put_key_at(i int, key ItemKey) error { - if self.Full() { - return BpTreeError("Block is full.") - } - self.keys = self.keys[:len(self.keys)+1] - for j := len(self.keys) - 1; j > i; j-- { - self.keys[j] = self.keys[j-1] - } - self.keys[i] = key - return nil -} - -func (self *BpNode) put_value_at(i int, value ItemValue) error { - if len(self.values) == cap(self.values) { - return BpTreeError("Block is full.") - } - if self.Internal() { - return BpTreeError("Expected a leaf node") - } - self.values = self.values[:len(self.values)+1] - for j := len(self.values) - 1; j > i; j-- { - self.values[j] = self.values[j-1] - } - self.values[i] = value - return nil -} - -func (self *BpNode) put_pointer_at(i int, pointer *BpNode) error { - if len(self.pointers) == cap(self.pointers) { - return BpTreeError("Block is full.") - } - if !self.Internal() { - return BpTreeError("Expected a internal node") - } - self.pointers = self.pointers[:len(self.pointers)+1] - for j := len(self.pointers) - 1; j > i; j-- { - self.pointers[j] = self.pointers[j-1] - } - self.pointers[i] = pointer - return nil -} - -func (self *BpNode) remove(key ItemKey, where WhereFunc) (a *BpNode, err error) { - if self.Internal() { - return self.internal_remove(key, nil, where) - } else { - return self.leaf_remove(key, self.keys[len(self.keys)-1], where) - } -} - -func (self *BpNode) internal_remove(key ItemKey, sibling *BpNode, where WhereFunc) (a *BpNode, err error) { - if !self.Internal() { - panic(BpTreeError("Expected a internal node")) - } - i, has := self.find(key) - if !has && i > 0 { - // if it doesn't have it and the index > 0 then we have the next block - // so we have to subtract one from the index. - i-- - } - if i+1 < len(self.keys) { - sibling = self.pointers[i+1] - } else if sibling != nil { - sibling = sibling.left_most_leaf() - } - child := self.pointers[i] - oldChild := child - if child.Internal() { - child, err = child.internal_remove(key, sibling, where) - } else { - if sibling == nil { - child, err = child.leaf_remove(key, nil, where) - } else { - child, err = child.leaf_remove(key, sibling.keys[0], where) - } - } - if err != nil { - return nil, err - } - if child == nil { - if err := self.remove_key_at(i); err != nil { - return nil, err - } - if err := self.remove_ptr_at(i); err != nil { - return nil, err - } - } else { - self.keys[i] = child.keys[0] - self.pointers[i] = child - } - if len(self.keys) == 0 { - return nil, self.destroy() - } - return self, self.maybePersist(oldChild != child) -} - -func (self *BpNode) leaf_remove(key, stop ItemKey, where WhereFunc) (a *BpNode, err error) { - if self.Internal() { - return nil, BpTreeError("Expected a leaf node") - } - a = self - hasChange := false - for j, l, next := self.forward(key, key)(); next != nil; j, l, next = next() { - if where(l.values[j]) { - hasChange = true - if err := l.remove_key_at(j); err != nil { - return nil, err - } - if err := l.remove_value_at(j); err != nil { - return nil, err - } - } - if len(l.keys) == 0 { - remove_linked_list_node(l) - if l.getNext() == nil { - a = nil - } else if stop == nil { - a = nil - } else if !l.getNext().keys[0].Equals(stop) { - a = l.getNext() - } else { - a = nil - } - if err := l.destroy(); err != nil { - return nil, err - } - } - } - if a != nil { - return a, a.maybePersist(hasChange) - } - return a, nil -} - -func (self *BpNode) remove_key_at(i int) error { - if i >= len(self.keys) || i < 0 { - return BpTreeError("i, %v, is out of bounds, %v, %v %v.", i, len(self.keys), len(self.values), self) - } - for j := i; j < len(self.keys)-1; j++ { - self.keys[j] = self.keys[j+1] - } - self.keys = self.keys[:len(self.keys)-1] - return nil -} - -func (self *BpNode) remove_value_at(i int) error { - if i >= len(self.values) || i < 0 { - return BpTreeError("i, %v, is out of bounds, %v.", i, len(self.values)) - } - for j := i; j < len(self.values)-1; j++ { - self.values[j] = self.values[j+1] - } - self.values = self.values[:len(self.values)-1] - return nil -} - -func (self *BpNode) remove_ptr_at(i int) error { - if i >= len(self.pointers) || i < 0 { - return BpTreeError("i, %v, is out of bounds, %v.", i, len(self.pointers)) - } - for j := i; j < len(self.pointers)-1; j++ { - self.pointers[j] = self.pointers[j+1] - } - self.pointers = self.pointers[:len(self.pointers)-1] - return nil -} - -func (self *BpNode) find(key ItemKey) (int, bool) { - var l = 0 - var r = len(self.keys) - 1 - var m int - for l <= r { - m = ((r - l) >> 1) + l - if key.Less(self.keys[m]) { - r = m - 1 - } else if key.Equals(self.keys[m]) { - return m, true - } else { - l = m + 1 - } - } - return l, false -} - -func (self *BpNode) find_end_of_pure_run() *BpNode { - k := self.keys[0] - p := self - n := self.getNext() - for n != nil && n.Pure() && k.Equals(n.keys[0]) { - p = n - n = n.getNext() - } - return p -} - -func (self *BpNode) all() (li loc_iterator) { - j := -1 - l := self.left_most_leaf() - end := false - j, l, end = next_location(j, l) - li = func() (i int, leaf *BpNode, next loc_iterator) { - if end { - return -1, nil, nil - } - i = j - leaf = l - j, l, end = next_location(j, l) - return i, leaf, li - } - return li -} - -func (self *BpNode) all_backward() (li loc_iterator) { - l := self.right_most_leaf() - j := len(l.keys) - end := false - j, l, end = prev_location(j, l) - li = func() (i int, leaf *BpNode, next loc_iterator) { - if end { - return -1, nil, nil - } - i = j - leaf = l - j, l, end = prev_location(j, l) - return i, leaf, li - } - return li -} - -func (self *BpNode) forward(from, to ItemKey) (li loc_iterator) { - j, l := self.get_start(from) - end := false - j-- - li = func() (i int, leaf *BpNode, next loc_iterator) { - j, l, end = next_location(j, l) - if end || to.Less(l.keys[j]) { - return -1, nil, nil - } - return j, l, li - } - return li -} - -func (self *BpNode) backward(from, to ItemKey) (li loc_iterator) { - j, l := self.get_end(from) - end := false - li = func() (i int, leaf *BpNode, next loc_iterator) { - if end || l.keys[j].Less(to) { - return -1, nil, nil - } - i = j - leaf = l - j, l, end = prev_location(i, l) - return i, leaf, li - } - return li -} - -func insert_linked_list_node(n, prev, next *BpNode) { - if (prev != nil && prev.getNext() != next) || (next != nil && next.getPrev() != prev) { - panic(BpTreeError("prev and next not hooked up")) - } - n.setPrev(prev) - n.setNext(next) - if prev != nil { - prev.setNext(n) - } - if next != nil { - next.setPrev(n) - } -} - -func remove_linked_list_node(n *BpNode) { - if n.getPrev() != nil { - n.getPrev().setNext(n.getNext()) - } - if n.getNext() != nil { - n.getNext().setPrev(n.getPrev()) - } -} - -/** - * a must be full and b must be empty else there will be a panic - * - * Different from common btree implementation, this splits the nodes by the inserted key. - * Items less than the splitKey stays in a, or moved to b if otherwise. - * This should help for monotonically increasing inserts. - * - */ -func balance_nodes(a, b *BpNode, splitKey ItemKey) { - if len(b.keys) != 0 { - panic(BpTreeError("b was not empty")) - } - if !a.Full() { - panic(BpTreeError("a was not full", a)) - } - if cap(a.keys) != cap(b.keys) { - panic(BpTreeError("cap(a.keys) != cap(b.keys)")) - } - if cap(a.values) != cap(b.values) { - panic(BpTreeError("cap(a.values) != cap(b.values)")) - } - if cap(a.pointers) != cap(b.pointers) { - panic(BpTreeError("cap(a.pointers) != cap(b.pointers)")) - } - - m := find_split_index(a, b, splitKey) - var lim = len(a.keys) - m - b.keys = b.keys[:lim] - if cap(a.values) > 0 { - if cap(a.values) != cap(a.keys) { - panic(BpTreeError("cap(a.values) != cap(a.keys)")) - } - b.values = b.values[:lim] - } - if cap(a.pointers) > 0 { - if cap(a.pointers) != cap(a.keys) { - panic(BpTreeError("cap(a.pointers) != cap(a.keys)")) - } - b.pointers = b.pointers[:lim] - } - for i := 0; i < lim; i++ { - j := m + i - b.keys[i] = a.keys[j] - if cap(a.values) > 0 { - b.values[i] = a.values[j] - } - if cap(a.pointers) > 0 { - b.pointers[i] = a.pointers[j] - } - } - a.keys = a.keys[:m] - if cap(a.values) > 0 { - a.values = a.values[:m] - } - if cap(a.pointers) > 0 { - a.pointers = a.pointers[:m] - } -} - -func find_split_index(a, b *BpNode, splitKey ItemKey) int { - m := len(a.keys) - for m > 0 && !a.keys[m-1].Less(splitKey) { - m-- - } - return m -} diff --git a/weed/util/bptree/bptree_store_test.go b/weed/util/bptree/bptree_store_test.go deleted file mode 100644 index 2e034171c..000000000 --- a/weed/util/bptree/bptree_store_test.go +++ /dev/null @@ -1,53 +0,0 @@ -package bptree - -import ( - "fmt" - "testing" -) - -type nodeStorePrintlnImpl struct { -} - -func (n *nodeStorePrintlnImpl) PersistFunc(node *BpNode) error { - println("saving node", node.protoNodeId) - return nil -} -func (n *nodeStorePrintlnImpl) DestroyFunc(node *BpNode) error { - println("delete node", node.protoNodeId) - return nil -} - -func TestAddRemove(t *testing.T) { - - tree := NewBpTree(3, &nodeStorePrintlnImpl{}) - for i:=0;i<9;i++{ - println("++++++++++", i) - tree.Add(String(fmt.Sprintf("%02d", i)), nil) - printTree(tree.root, "") - } - - if !tree.Has(String("08")) { - t.Errorf("lookup error") - } - for i:=5;i<9;i++{ - println("----------", i) - tree.RemoveWhere(String(fmt.Sprintf("%02d", i)), func(value ItemValue) bool { - return true - }) - printTree(tree.root, "") - } - if tree.Has(String("08")) { - t.Errorf("remove error") - } -} - -func printTree(node *BpNode, prefix string) { - fmt.Printf("%sNode %d\n", prefix, node.protoNodeId) - prefix += " " - for i:=0;i>= 8 - pos-- - } - *readPos = pos - *readVal = val - return -} - -// copied from https://sourcegraph.com/github.com/timtadh/data-structures@master/-/blob/test/support.go - -type T testing.T - -func (t *T) Assert(ok bool, msg string, vars ...ItemValue) { - if !ok { - t.Log("\n" + string(debug.Stack())) - var objects []interface{} - for _, t := range vars { - objects = append(objects, t) - } - t.Fatalf(msg, objects...) - } -} - -func (t *T) AssertNil(errors ...error) { - any := false - for _, err := range errors { - if err != nil { - any = true - t.Log("\n" + string(debug.Stack())) - t.Error(err) - } - } - if any { - t.Fatal("assert failed") - } -} - -func RandSlice(length int) []byte { - slice := make([]byte, length) - if _, err := crand.Read(slice); err != nil { - panic(err) - } - return slice -} - -func RandHex(length int) string { - return hex.EncodeToString(RandSlice(length / 2)) -} - -func RandStr(length int) string { - return string(RandSlice(length)) -} \ No newline at end of file diff --git a/weed/util/bptree/getter_setter.go b/weed/util/bptree/getter_setter.go deleted file mode 100644 index dcaa7a0b6..000000000 --- a/weed/util/bptree/getter_setter.go +++ /dev/null @@ -1,72 +0,0 @@ -package bptree - -var ( - protoNodeId = int64(0) -) -func GetProtoNodeId() int64 { - protoNodeId++ - return protoNodeId -} - -func (self *BpMap) getRoot() *BpNode { - return self.root -} -func (self *BpMap) setRoot(root *BpNode) { - self.root = root -} - -func (self *BpTree) getRoot() *BpNode { - return self.root -} -func (self *BpTree) setRoot(root *BpNode) { - self.root = root -} - -func (self *BpNode) getNext() *BpNode { - return self.next -} -func (self *BpNode) setNext(next *BpNode) { - self.next = next -} -func (self *BpNode) getPrev() *BpNode { - return self.prev -} -func (self *BpNode) setPrev(prev *BpNode) { - self.prev = prev -} -func (self *BpNode) getNode(x int)(*BpNode) { - return self.pointers[x] -} - -func (self *BpNode) maybePersist(shouldPersist bool) error { - if !shouldPersist { - return nil - } - return self.persist() -} -func (self *BpNode) persist() error { - if self.nodeStore != nil { - return self.nodeStore.PersistFunc(self) - } - return nil -} -func (self *BpNode) destroy() error { - if self.nodeStore != nil { - return self.nodeStore.DestroyFunc(self) - } - return nil -} - -func persist(a, b *BpNode) error { - if a != nil { - if err := a.persist(); err != nil { - return err - } - } - if b != nil { - if err := b.persist(); err != nil { - return err - } - } - return nil -} \ No newline at end of file diff --git a/weed/util/bptree/int.go b/weed/util/bptree/int.go deleted file mode 100644 index e8fd9511c..000000000 --- a/weed/util/bptree/int.go +++ /dev/null @@ -1,357 +0,0 @@ -package bptree - -import ( - "encoding/binary" - "fmt" -) - -type Int8 int8 -type UInt8 uint8 -type Int16 int16 -type UInt16 uint16 -type Int32 int32 -type UInt32 uint32 -type Int64 int64 -type UInt64 uint64 -type Int int -type UInt uint - -func (self *Int8) MarshalBinary() ([]byte, error) { - bytes := make([]byte, 0) - bytes[0] = uint8(*self) - return bytes, nil -} - -func (self *Int8) UnmarshalBinary(data []byte) error { - if len(data) != 1 { - return fmt.Errorf("data wrong size") - } - *self = Int8(data[0]) - return nil -} - -func (self Int8) Equals(other Equatable) bool { - if o, ok := other.(Int8); ok { - return self == o - } else { - return false - } -} - -func (self Int8) Less(other Sortable) bool { - if o, ok := other.(Int8); ok { - return self < o - } else { - return false - } -} - -func (self Int8) Hash() int { - return int(self) -} - -func (self *UInt8) MarshalBinary() ([]byte, error) { - bytes := make([]byte, 0) - bytes[0] = uint8(*self) - return bytes, nil -} - -func (self *UInt8) UnmarshalBinary(data []byte) error { - if len(data) != 1 { - return fmt.Errorf("data wrong size") - } - *self = UInt8(data[0]) - return nil -} - -func (self UInt8) Equals(other Equatable) bool { - if o, ok := other.(UInt8); ok { - return self == o - } else { - return false - } -} - -func (self UInt8) Less(other Sortable) bool { - if o, ok := other.(UInt8); ok { - return self < o - } else { - return false - } -} - -func (self UInt8) Hash() int { - return int(self) -} - -func (self *Int16) MarshalBinary() ([]byte, error) { - bytes := make([]byte, 2) - binary.BigEndian.PutUint16(bytes, uint16(*self)) - return bytes, nil -} - -func (self *Int16) UnmarshalBinary(data []byte) error { - if len(data) != 2 { - return fmt.Errorf("data wrong size") - } - *self = Int16(binary.BigEndian.Uint16(data)) - return nil -} - -func (self Int16) Equals(other Equatable) bool { - if o, ok := other.(Int16); ok { - return self == o - } else { - return false - } -} - -func (self Int16) Less(other Sortable) bool { - if o, ok := other.(Int16); ok { - return self < o - } else { - return false - } -} - -func (self Int16) Hash() int { - return int(self) -} - -func (self *UInt16) MarshalBinary() ([]byte, error) { - bytes := make([]byte, 2) - binary.BigEndian.PutUint16(bytes, uint16(*self)) - return bytes, nil -} - -func (self *UInt16) UnmarshalBinary(data []byte) error { - if len(data) != 2 { - return fmt.Errorf("data wrong size") - } - *self = UInt16(binary.BigEndian.Uint16(data)) - return nil -} - -func (self UInt16) Equals(other Equatable) bool { - if o, ok := other.(UInt16); ok { - return self == o - } else { - return false - } -} - -func (self UInt16) Less(other Sortable) bool { - if o, ok := other.(UInt16); ok { - return self < o - } else { - return false - } -} - -func (self UInt16) Hash() int { - return int(self) -} - -func (self *Int32) MarshalBinary() ([]byte, error) { - bytes := make([]byte, 4) - binary.BigEndian.PutUint32(bytes, uint32(*self)) - return bytes, nil -} - -func (self *Int32) UnmarshalBinary(data []byte) error { - if len(data) != 4 { - return fmt.Errorf("data wrong size") - } - *self = Int32(binary.BigEndian.Uint32(data)) - return nil -} - -func (self Int32) Equals(other Equatable) bool { - if o, ok := other.(Int32); ok { - return self == o - } else { - return false - } -} - -func (self Int32) Less(other Sortable) bool { - if o, ok := other.(Int32); ok { - return self < o - } else { - return false - } -} - -func (self *UInt32) MarshalBinary() ([]byte, error) { - bytes := make([]byte, 4) - binary.BigEndian.PutUint32(bytes, uint32(*self)) - return bytes, nil -} - -func (self *UInt32) UnmarshalBinary(data []byte) error { - if len(data) != 4 { - return fmt.Errorf("data wrong size") - } - *self = UInt32(binary.BigEndian.Uint32(data)) - return nil -} - -func (self Int32) Hash() int { - return int(self) -} - -func (self UInt32) Equals(other Equatable) bool { - if o, ok := other.(UInt32); ok { - return self == o - } else { - return false - } -} - -func (self UInt32) Less(other Sortable) bool { - if o, ok := other.(UInt32); ok { - return self < o - } else { - return false - } -} - -func (self UInt32) Hash() int { - return int(self) -} - -func (self *Int64) MarshalBinary() ([]byte, error) { - bytes := make([]byte, 8) - binary.BigEndian.PutUint64(bytes, uint64(*self)) - return bytes, nil -} - -func (self *Int64) UnmarshalBinary(data []byte) error { - if len(data) != 8 { - return fmt.Errorf("data wrong size") - } - *self = Int64(binary.BigEndian.Uint64(data)) - return nil -} - -func (self Int64) Equals(other Equatable) bool { - if o, ok := other.(Int64); ok { - return self == o - } else { - return false - } -} - -func (self Int64) Less(other Sortable) bool { - if o, ok := other.(Int64); ok { - return self < o - } else { - return false - } -} - -func (self Int64) Hash() int { - return int(self>>32) ^ int(self) -} - -func (self *UInt64) MarshalBinary() ([]byte, error) { - bytes := make([]byte, 8) - binary.BigEndian.PutUint64(bytes, uint64(*self)) - return bytes, nil -} - -func (self *UInt64) UnmarshalBinary(data []byte) error { - if len(data) != 8 { - return fmt.Errorf("data wrong size") - } - *self = UInt64(binary.BigEndian.Uint64(data)) - return nil -} - -func (self UInt64) Equals(other Equatable) bool { - if o, ok := other.(UInt64); ok { - return self == o - } else { - return false - } -} - -func (self UInt64) Less(other Sortable) bool { - if o, ok := other.(UInt64); ok { - return self < o - } else { - return false - } -} - -func (self UInt64) Hash() int { - return int(self>>32) ^ int(self) -} - -func (self *Int) MarshalBinary() ([]byte, error) { - bytes := make([]byte, 4) - binary.BigEndian.PutUint32(bytes, uint32(*self)) - return bytes, nil -} - -func (self *Int) UnmarshalBinary(data []byte) error { - if len(data) != 4 { - return fmt.Errorf("data wrong size") - } - *self = Int(binary.BigEndian.Uint32(data)) - return nil -} - -func (self Int) Equals(other Equatable) bool { - if o, ok := other.(Int); ok { - return self == o - } else { - return false - } -} - -func (self Int) Less(other Sortable) bool { - if o, ok := other.(Int); ok { - return self < o - } else { - return false - } -} - -func (self Int) Hash() int { - return int(self) -} - -func (self *UInt) MarshalBinary() ([]byte, error) { - bytes := make([]byte, 4) - binary.BigEndian.PutUint32(bytes, uint32(*self)) - return bytes, nil -} - -func (self *UInt) UnmarshalBinary(data []byte) error { - if len(data) != 4 { - return fmt.Errorf("data wrong size") - } - *self = UInt(binary.BigEndian.Uint32(data)) - return nil -} - -func (self UInt) Equals(other Equatable) bool { - if o, ok := other.(UInt); ok { - return self == o - } else { - return false - } -} - -func (self UInt) Less(other Sortable) bool { - if o, ok := other.(UInt); ok { - return self < o - } else { - return false - } -} - -func (self UInt) Hash() int { - return int(self) -} diff --git a/weed/util/bptree/rand.go b/weed/util/bptree/rand.go deleted file mode 100644 index 08b2e50ab..000000000 --- a/weed/util/bptree/rand.go +++ /dev/null @@ -1,2 +0,0 @@ -package bptree - diff --git a/weed/util/bptree/serde.go b/weed/util/bptree/serde.go deleted file mode 100644 index 2a98a774a..000000000 --- a/weed/util/bptree/serde.go +++ /dev/null @@ -1,10 +0,0 @@ -package bptree - -func (protoNode *ProtoNode) ToBpTree() *BpTree { - node := protoNode.ToBpNode() - return &BpTree{root: node} -} - -func (protoNode *ProtoNode) ToBpNode() *BpNode { - return nil -} \ No newline at end of file diff --git a/weed/util/bptree/serde_test.go b/weed/util/bptree/serde_test.go deleted file mode 100644 index 27ccccb78..000000000 --- a/weed/util/bptree/serde_test.go +++ /dev/null @@ -1,46 +0,0 @@ -package bptree - -import ( - "fmt" - "testing" -) - -type nodeStoreMapImpl struct { - m map[int64]*ProtoNode -} - -func (n *nodeStoreMapImpl) PersistFunc(node *BpNode) error { - println("saving node", node.protoNodeId) - n.m[node.protoNodeId] = node.protoNode - return nil -} -func (n *nodeStoreMapImpl) DestroyFunc(node *BpNode) error { - println("delete node", node.protoNodeId) - delete(n.m, node.protoNodeId) - return nil -} - -func TestSerDe(t *testing.T) { - - nodeStore := &nodeStoreMapImpl{ - m: make(map[int64]*ProtoNode), - } - - tree := NewBpTree(3, nodeStore) - - for i:=0;i<32;i++{ - println("add", i) - tree.Add(String(fmt.Sprintf("%02d", i)), nil) - } - - for i:=5;i<9;i++{ - println("----------", i) - tree.RemoveWhere(String(fmt.Sprintf("%02d", i)), func(value ItemValue) bool { - return true - }) - printTree(tree.root, "") - } - - - -} \ No newline at end of file diff --git a/weed/util/bptree/string.go b/weed/util/bptree/string.go deleted file mode 100644 index 262220878..000000000 --- a/weed/util/bptree/string.go +++ /dev/null @@ -1,71 +0,0 @@ -package bptree - -import ( - "bytes" - "hash/fnv" -) - -type String string -type ByteSlice []byte - -func (self *String) MarshalBinary() ([]byte, error) { - return []byte(*self), nil -} - -func (self *String) UnmarshalBinary(data []byte) error { - *self = String(data) - return nil -} - -func (self String) Equals(other Equatable) bool { - if o, ok := other.(String); ok { - return self == o - } else { - return false - } -} - -func (self String) Less(other Sortable) bool { - if o, ok := other.(String); ok { - return self < o - } else { - return false - } -} - -func (self String) Hash() int { - h := fnv.New32a() - h.Write([]byte(string(self))) - return int(h.Sum32()) -} - -func (self *ByteSlice) MarshalBinary() ([]byte, error) { - return []byte(*self), nil -} - -func (self *ByteSlice) UnmarshalBinary(data []byte) error { - *self = ByteSlice(data) - return nil -} - -func (self ByteSlice) Equals(other Equatable) bool { - if o, ok := other.(ByteSlice); ok { - return bytes.Equal(self, o) - } else { - return false - } -} - -func (self ByteSlice) Less(other Sortable) bool { - if o, ok := other.(ByteSlice); ok { - return bytes.Compare(self, o) < 0 // -1 if a < b - } else { - return false - } -} - -func (self ByteSlice) Hash() int { - h := fnv.New32a() - h.Write([]byte(self)) - return int(h.Sum32()) -} diff --git a/weed/util/bptree/tree_store/memory_store.go b/weed/util/bptree/tree_store/memory_store.go deleted file mode 100644 index 467455664..000000000 --- a/weed/util/bptree/tree_store/memory_store.go +++ /dev/null @@ -1,29 +0,0 @@ -package tree_store - -import "errors" - -var ( - NotFound = errors.New("not found") -) - -type MemoryTreeStore struct { - m map[int64][]byte -} - -func NewMemoryTreeStore() *MemoryTreeStore{ - return &MemoryTreeStore{ - m: make(map[int64][]byte), - } -} - -func (m *MemoryTreeStore) Put(k int64, v []byte) error { - m.m[k] = v - return nil -} - -func (m *MemoryTreeStore) Get(k int64) ([]byte, error) { - if v, found := m.m[k]; found { - return v, nil - } - return nil, NotFound -} diff --git a/weed/util/bptree/tree_store/tree_store.go.go b/weed/util/bptree/tree_store/tree_store.go.go deleted file mode 100644 index 6a0af6ae6..000000000 --- a/weed/util/bptree/tree_store/tree_store.go.go +++ /dev/null @@ -1,6 +0,0 @@ -package tree_store - -type TreeStore interface { - Put(k int64, v []byte) error - Get(k int64) ([]byte, error) -} diff --git a/weed/util/bptree/types.go b/weed/util/bptree/types.go deleted file mode 100644 index f987e0419..000000000 --- a/weed/util/bptree/types.go +++ /dev/null @@ -1,98 +0,0 @@ -package bptree - -import ( - "errors" - "fmt" -) - -type Equatable interface { - Equals(b Equatable) bool -} - -type Sortable interface { - Equatable - Less(b Sortable) bool -} - -type Hashable interface { - Sortable - Hash() int -} - -var BpTreeError = fmt.Errorf - -func NegativeSize() error { - return errors.New("negative size") -} - -type Iterator func() (item ItemValue, next Iterator) -type KIterator func() (key ItemKey, next KIterator) -type KVIterator func() (key ItemKey, value ItemValue, next KVIterator) -type KVIterable interface { - Iterate() KVIterator -} - -type MapOperable interface { - Has(key ItemKey) bool - Put(key ItemKey, value ItemValue) (err error) - Get(key ItemKey) (value ItemValue, err error) - Remove(key ItemKey) (value ItemValue, err error) -} - -type WhereFunc func(value ItemValue) bool - -func MakeValuesIterator(obj KVIterable) Iterator { - kv_iterator := obj.Iterate() - var v_iterator Iterator - v_iterator = func() (value ItemValue, next Iterator) { - _, value, kv_iterator = kv_iterator() - if kv_iterator == nil { - return nil, nil - } - return value, v_iterator - } - return v_iterator -} - -func MakeItemsIterator(obj KVIterable) (kit KIterator) { - kv_iterator := obj.Iterate() - kit = func() (item ItemKey, next KIterator) { - var key ItemKey - var value ItemValue - key, value, kv_iterator = kv_iterator() - if kv_iterator == nil { - return nil, nil - } - return &MapEntry{key, value}, kit - } - return kit -} - -type MapEntry struct { - Key ItemKey - Value ItemValue -} - -func (m *MapEntry) Equals(other Equatable) bool { - if o, ok := other.(*MapEntry); ok { - return m.Key.Equals(o.Key) - } else { - return m.Key.Equals(other) - } -} - -func (m *MapEntry) Less(other Sortable) bool { - if o, ok := other.(*MapEntry); ok { - return m.Key.Less(o.Key) - } else { - return m.Key.Less(other) - } -} - -func (m *MapEntry) Hash() int { - return m.Key.Hash() -} - -func (m *MapEntry) String() string { - return fmt.Sprintf("", m.Key, m.Value) -} From 69b84bb771e816e2914daee8635379b1150e0de7 Mon Sep 17 00:00:00 2001 From: Chris Lu Date: Sat, 2 Oct 2021 14:15:49 -0700 Subject: [PATCH 082/130] TestFindGreaterOrEqual --- weed/util/skiplist/skiplist_test.go | 45 +++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/weed/util/skiplist/skiplist_test.go b/weed/util/skiplist/skiplist_test.go index ca41e382a..60bd5f923 100644 --- a/weed/util/skiplist/skiplist_test.go +++ b/weed/util/skiplist/skiplist_test.go @@ -2,6 +2,7 @@ package skiplist import ( "bytes" + "fmt" "math/rand" "strconv" "testing" @@ -210,3 +211,47 @@ func TestGetNodeCount(t *testing.T) { t.Fail() } } + +func TestFindGreaterOrEqual(t *testing.T) { + + maxNumber := maxN * 100 + + var list *SkipList + var listPointer *SkipList + + // Test on empty list. + if _, ok := listPointer.FindGreaterOrEqual(Element(0)); ok { + t.Fail() + } + + list = New() + + for i := 0; i < maxN; i++ { + list.Insert(Element(rand.Intn(maxNumber))) + } + + for i := 0; i < maxN; i++ { + key := Element(rand.Intn(maxNumber)) + if v, ok := list.FindGreaterOrEqual(key); ok { + // if f is v should be bigger than the element before + if bytes.Compare(v.Prev.Key, key) >= 0 { + fmt.Printf("PrevV: %s\n key: %s\n\n", string(v.Prev.Key), string(key)) + t.Fail() + } + // v should be bigger or equal to f + // If we compare directly, we get an equal key with a difference on the 10th decimal point, which fails. + if bytes.Compare(v.Values[0], key) < 0 { + fmt.Printf("v: %s\n key: %s\n\n", string(v.Values[0]), string(key)) + t.Fail() + } + } else { + lastV := list.GetLargestNode().GetValue() + // It is OK, to fail, as long as f is bigger than the last element. + if bytes.Compare(key, lastV) <= 0 { + fmt.Printf("lastV: %s\n key: %s\n\n", string(lastV), string(key)) + t.Fail() + } + } + } + +} \ No newline at end of file From 4f50f8c2ca7243a358e7ee9dab40a5a42fc462d1 Mon Sep 17 00:00:00 2001 From: Chris Lu Date: Sun, 3 Oct 2021 01:07:35 -0700 Subject: [PATCH 083/130] insert key and value --- weed/util/skiplist/serde.go | 12 +--- weed/util/skiplist/skiplist.go | 41 +++++------- weed/util/skiplist/skiplist.pb.go | 97 +++++++++++++---------------- weed/util/skiplist/skiplist.proto | 7 +-- weed/util/skiplist/skiplist_test.go | 50 ++++++--------- 5 files changed, 84 insertions(+), 123 deletions(-) diff --git a/weed/util/skiplist/serde.go b/weed/util/skiplist/serde.go index 2337b4b19..135d3b0b5 100644 --- a/weed/util/skiplist/serde.go +++ b/weed/util/skiplist/serde.go @@ -3,16 +3,10 @@ package skiplist import "bytes" func compareElement(a *SkipListElement, key []byte) int { - if len(a.Values) == 0 { + if len(a.Key) == 0 { return -1 } - if bytes.Compare(a.Values[0], key) < 0 { - return -1 - } - if bytes.Compare(a.Values[len(a.Values)-1], key) > 0 { - return 1 - } - return 0 + return bytes.Compare(a.Key, key) } var ( @@ -25,7 +19,7 @@ func (node *SkipListElement) Reference() *SkipListElementReference { } return &SkipListElementReference{ ElementPointer: node.Id, - Key: node.Values[0], + Key: node.Key, } } func (node *SkipListElement) Save() { diff --git a/weed/util/skiplist/skiplist.go b/weed/util/skiplist/skiplist.go index a47cf4608..19fa556ae 100644 --- a/weed/util/skiplist/skiplist.go +++ b/weed/util/skiplist/skiplist.go @@ -15,11 +15,11 @@ const ( ) type SkipList struct { - startLevels [maxLevel]*SkipListElementReference - endLevels [maxLevel]*SkipListElementReference - maxNewLevel int - maxLevel int - elementCount int + startLevels [maxLevel]*SkipListElementReference + endLevels [maxLevel]*SkipListElementReference + maxNewLevel int + maxLevel int + // elementCount int } // NewSeedEps returns a new empty, initialized Skiplist. @@ -32,9 +32,9 @@ func NewSeed(seed int64) *SkipList { //fmt.Printf("SkipList seed: %v\n", seed) list := &SkipList{ - maxNewLevel: maxLevel, - maxLevel: 0, - elementCount: 0, + maxNewLevel: maxLevel, + maxLevel: 0, + // elementCount: 0, } return list @@ -193,7 +193,7 @@ func (t *SkipList) Delete(key []byte) { nextNextNode.Prev = currentNode.Reference() nextNextNode.Save() } - t.elementCount-- + // t.elementCount-- nextNode.DeleteSelf() } @@ -230,7 +230,7 @@ func (t *SkipList) Delete(key []byte) { // Insert inserts the given ListElement into the skiplist. // Insert runs in approx. O(log(n)) -func (t *SkipList) Insert(key []byte) { +func (t *SkipList) Insert(key, value []byte) { if t == nil || key == nil { return @@ -245,13 +245,14 @@ func (t *SkipList) Insert(key []byte) { } elem := &SkipListElement{ - Id: rand.Int63(), - Next: make([]*SkipListElementReference, t.maxNewLevel, t.maxNewLevel), - Level: int32(level), - Values: [][]byte{key}, + Id: rand.Int63(), + Next: make([]*SkipListElementReference, t.maxNewLevel, t.maxNewLevel), + Level: int32(level), + Key: key, + Value: value, } - t.elementCount++ + // t.elementCount++ newFirst := true newLast := true @@ -371,11 +372,6 @@ func (t *SkipList) Insert(key []byte) { } -// GetValue extracts the ListElement value from a skiplist node. -func (e *SkipListElement) GetValue() []byte { - return e.Values[0] -} - // GetSmallestNode returns the very first/smallest node in the skiplist. // GetSmallestNode runs in O(1) func (t *SkipList) GetSmallestNode() *SkipListElement { @@ -406,11 +402,6 @@ func (t *SkipList) Prev(e *SkipListElement) *SkipListElement { return e.Prev.Load() } -// GetNodeCount returns the number of nodes currently in the skiplist. -func (t *SkipList) GetNodeCount() int { - return t.elementCount -} - // String returns a string format of the skiplist. Useful to get a graphical overview and/or debugging. func (t *SkipList) println() { diff --git a/weed/util/skiplist/skiplist.pb.go b/weed/util/skiplist/skiplist.pb.go index 63b6c74a3..82afec453 100644 --- a/weed/util/skiplist/skiplist.pb.go +++ b/weed/util/skiplist/skiplist.pb.go @@ -30,12 +30,10 @@ type SkipListProto struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - StartLevels []*SkipListElementReference `protobuf:"bytes,1,rep,name=start_levels,json=startLevels,proto3" json:"start_levels,omitempty"` - EndLevels []*SkipListElementReference `protobuf:"bytes,2,rep,name=end_levels,json=endLevels,proto3" json:"end_levels,omitempty"` - MaxNewLevel int32 `protobuf:"varint,3,opt,name=max_new_level,json=maxNewLevel,proto3" json:"max_new_level,omitempty"` - MaxLevel int32 `protobuf:"varint,4,opt,name=max_level,json=maxLevel,proto3" json:"max_level,omitempty"` - ElementCount int64 `protobuf:"varint,5,opt,name=element_count,json=elementCount,proto3" json:"element_count,omitempty"` - Eps float64 `protobuf:"fixed64,7,opt,name=eps,proto3" json:"eps,omitempty"` + StartLevels []*SkipListElementReference `protobuf:"bytes,1,rep,name=start_levels,json=startLevels,proto3" json:"start_levels,omitempty"` + EndLevels []*SkipListElementReference `protobuf:"bytes,2,rep,name=end_levels,json=endLevels,proto3" json:"end_levels,omitempty"` + MaxNewLevel int32 `protobuf:"varint,3,opt,name=max_new_level,json=maxNewLevel,proto3" json:"max_new_level,omitempty"` + MaxLevel int32 `protobuf:"varint,4,opt,name=max_level,json=maxLevel,proto3" json:"max_level,omitempty"` } func (x *SkipListProto) Reset() { @@ -98,20 +96,6 @@ func (x *SkipListProto) GetMaxLevel() int32 { return 0 } -func (x *SkipListProto) GetElementCount() int64 { - if x != nil { - return x.ElementCount - } - return 0 -} - -func (x *SkipListProto) GetEps() float64 { - if x != nil { - return x.Eps - } - return 0 -} - type SkipListElementReference struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -172,11 +156,12 @@ type SkipListElement struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Id int64 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"` - Next []*SkipListElementReference `protobuf:"bytes,2,rep,name=next,proto3" json:"next,omitempty"` - Level int32 `protobuf:"varint,3,opt,name=level,proto3" json:"level,omitempty"` - Values [][]byte `protobuf:"bytes,4,rep,name=values,proto3" json:"values,omitempty"` - Prev *SkipListElementReference `protobuf:"bytes,5,opt,name=prev,proto3" json:"prev,omitempty"` + Id int64 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"` + Next []*SkipListElementReference `protobuf:"bytes,2,rep,name=next,proto3" json:"next,omitempty"` + Level int32 `protobuf:"varint,3,opt,name=level,proto3" json:"level,omitempty"` + Key []byte `protobuf:"bytes,4,opt,name=key,proto3" json:"key,omitempty"` + Value []byte `protobuf:"bytes,5,opt,name=value,proto3" json:"value,omitempty"` + Prev *SkipListElementReference `protobuf:"bytes,6,opt,name=prev,proto3" json:"prev,omitempty"` } func (x *SkipListElement) Reset() { @@ -232,9 +217,16 @@ func (x *SkipListElement) GetLevel() int32 { return 0 } -func (x *SkipListElement) GetValues() [][]byte { +func (x *SkipListElement) GetKey() []byte { if x != nil { - return x.Values + return x.Key + } + return nil +} + +func (x *SkipListElement) GetValue() []byte { + if x != nil { + return x.Value } return nil } @@ -250,7 +242,7 @@ var File_skiplist_proto protoreflect.FileDescriptor var file_skiplist_proto_rawDesc = []byte{ 0x0a, 0x0e, 0x73, 0x6b, 0x69, 0x70, 0x6c, 0x69, 0x73, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x12, 0x08, 0x73, 0x6b, 0x69, 0x70, 0x6c, 0x69, 0x73, 0x74, 0x22, 0x91, 0x02, 0x0a, 0x0d, 0x53, + 0x12, 0x08, 0x73, 0x6b, 0x69, 0x70, 0x6c, 0x69, 0x73, 0x74, 0x22, 0xda, 0x01, 0x0a, 0x0d, 0x53, 0x6b, 0x69, 0x70, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x45, 0x0a, 0x0c, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x73, 0x6b, 0x69, 0x70, 0x6c, 0x69, 0x73, 0x74, 0x2e, 0x53, 0x6b, @@ -264,32 +256,29 @@ var file_skiplist_proto_rawDesc = []byte{ 0x77, 0x5f, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0b, 0x6d, 0x61, 0x78, 0x4e, 0x65, 0x77, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x12, 0x1b, 0x0a, 0x09, 0x6d, 0x61, 0x78, 0x5f, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x08, 0x6d, - 0x61, 0x78, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x12, 0x23, 0x0a, 0x0d, 0x65, 0x6c, 0x65, 0x6d, 0x65, - 0x6e, 0x74, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0c, - 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x10, 0x0a, 0x03, - 0x65, 0x70, 0x73, 0x18, 0x07, 0x20, 0x01, 0x28, 0x01, 0x52, 0x03, 0x65, 0x70, 0x73, 0x22, 0x55, - 0x0a, 0x18, 0x53, 0x6b, 0x69, 0x70, 0x4c, 0x69, 0x73, 0x74, 0x45, 0x6c, 0x65, 0x6d, 0x65, 0x6e, - 0x74, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x12, 0x27, 0x0a, 0x0f, 0x65, 0x6c, - 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x03, 0x52, 0x0e, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x50, 0x6f, 0x69, 0x6e, - 0x74, 0x65, 0x72, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, - 0x52, 0x03, 0x6b, 0x65, 0x79, 0x22, 0xbf, 0x01, 0x0a, 0x0f, 0x53, 0x6b, 0x69, 0x70, 0x4c, 0x69, - 0x73, 0x74, 0x45, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x02, 0x69, 0x64, 0x12, 0x36, 0x0a, 0x04, 0x6e, 0x65, 0x78, - 0x74, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x73, 0x6b, 0x69, 0x70, 0x6c, 0x69, - 0x73, 0x74, 0x2e, 0x53, 0x6b, 0x69, 0x70, 0x4c, 0x69, 0x73, 0x74, 0x45, 0x6c, 0x65, 0x6d, 0x65, - 0x6e, 0x74, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x52, 0x04, 0x6e, 0x65, 0x78, - 0x74, 0x12, 0x14, 0x0a, 0x05, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, - 0x52, 0x05, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x12, 0x16, 0x0a, 0x06, 0x76, 0x61, 0x6c, 0x75, 0x65, - 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x06, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x12, - 0x36, 0x0a, 0x04, 0x70, 0x72, 0x65, 0x76, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, - 0x73, 0x6b, 0x69, 0x70, 0x6c, 0x69, 0x73, 0x74, 0x2e, 0x53, 0x6b, 0x69, 0x70, 0x4c, 0x69, 0x73, - 0x74, 0x45, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, - 0x65, 0x52, 0x04, 0x70, 0x72, 0x65, 0x76, 0x42, 0x33, 0x5a, 0x31, 0x67, 0x69, 0x74, 0x68, 0x75, - 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x68, 0x72, 0x69, 0x73, 0x6c, 0x75, 0x73, 0x66, 0x2f, - 0x73, 0x65, 0x61, 0x77, 0x65, 0x65, 0x64, 0x66, 0x73, 0x2f, 0x77, 0x65, 0x65, 0x64, 0x2f, 0x75, - 0x74, 0x69, 0x6c, 0x2f, 0x73, 0x6b, 0x69, 0x70, 0x6c, 0x69, 0x73, 0x74, 0x62, 0x06, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x33, + 0x61, 0x78, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x22, 0x55, 0x0a, 0x18, 0x53, 0x6b, 0x69, 0x70, 0x4c, + 0x69, 0x73, 0x74, 0x45, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, + 0x6e, 0x63, 0x65, 0x12, 0x27, 0x0a, 0x0f, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x70, + 0x6f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0e, 0x65, 0x6c, + 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x12, 0x10, 0x0a, 0x03, + 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x22, 0xcf, + 0x01, 0x0a, 0x0f, 0x53, 0x6b, 0x69, 0x70, 0x4c, 0x69, 0x73, 0x74, 0x45, 0x6c, 0x65, 0x6d, 0x65, + 0x6e, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x02, + 0x69, 0x64, 0x12, 0x36, 0x0a, 0x04, 0x6e, 0x65, 0x78, 0x74, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x22, 0x2e, 0x73, 0x6b, 0x69, 0x70, 0x6c, 0x69, 0x73, 0x74, 0x2e, 0x53, 0x6b, 0x69, 0x70, + 0x4c, 0x69, 0x73, 0x74, 0x45, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x66, 0x65, 0x72, + 0x65, 0x6e, 0x63, 0x65, 0x52, 0x04, 0x6e, 0x65, 0x78, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x6c, 0x65, + 0x76, 0x65, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x6c, 0x65, 0x76, 0x65, 0x6c, + 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x03, 0x6b, + 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, + 0x0c, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x36, 0x0a, 0x04, 0x70, 0x72, 0x65, 0x76, + 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x73, 0x6b, 0x69, 0x70, 0x6c, 0x69, 0x73, + 0x74, 0x2e, 0x53, 0x6b, 0x69, 0x70, 0x4c, 0x69, 0x73, 0x74, 0x45, 0x6c, 0x65, 0x6d, 0x65, 0x6e, + 0x74, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x52, 0x04, 0x70, 0x72, 0x65, 0x76, + 0x42, 0x33, 0x5a, 0x31, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, + 0x68, 0x72, 0x69, 0x73, 0x6c, 0x75, 0x73, 0x66, 0x2f, 0x73, 0x65, 0x61, 0x77, 0x65, 0x65, 0x64, + 0x66, 0x73, 0x2f, 0x77, 0x65, 0x65, 0x64, 0x2f, 0x75, 0x74, 0x69, 0x6c, 0x2f, 0x73, 0x6b, 0x69, + 0x70, 0x6c, 0x69, 0x73, 0x74, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/weed/util/skiplist/skiplist.proto b/weed/util/skiplist/skiplist.proto index ce84ed996..bfb190b33 100644 --- a/weed/util/skiplist/skiplist.proto +++ b/weed/util/skiplist/skiplist.proto @@ -9,8 +9,6 @@ message SkipListProto { repeated SkipListElementReference end_levels = 2; int32 max_new_level = 3; int32 max_level = 4; - int64 element_count = 5; - double eps = 7; } message SkipListElementReference { @@ -22,6 +20,7 @@ message SkipListElement { int64 id = 1; repeated SkipListElementReference next = 2; int32 level = 3; - repeated bytes values = 4; - SkipListElementReference prev = 5; + bytes key = 4; + bytes value = 5; + SkipListElementReference prev = 6; } diff --git a/weed/util/skiplist/skiplist_test.go b/weed/util/skiplist/skiplist_test.go index 60bd5f923..811fd5be9 100644 --- a/weed/util/skiplist/skiplist_test.go +++ b/weed/util/skiplist/skiplist_test.go @@ -18,7 +18,7 @@ func TestInsertAndFind(t *testing.T) { var list *SkipList var listPointer *SkipList - listPointer.Insert(k0) + listPointer.Insert(k0, k0) if _, ok := listPointer.Find(k0); ok { t.Fail() } @@ -34,7 +34,7 @@ func TestInsertAndFind(t *testing.T) { // Test at the beginning of the list. for i := 0; i < maxN; i++ { key := []byte(strconv.Itoa(maxN-i)) - list.Insert(key) + list.Insert(key, key) } for i := 0; i < maxN; i++ { key := []byte(strconv.Itoa(maxN-i)) @@ -48,7 +48,7 @@ func TestInsertAndFind(t *testing.T) { // Test at the end of the list. for i := 0; i < maxN; i++ { key := []byte(strconv.Itoa(i)) - list.Insert(key) + list.Insert(key, key) } for i := 0; i < maxN; i++ { key := []byte(strconv.Itoa(i)) @@ -62,17 +62,17 @@ func TestInsertAndFind(t *testing.T) { rList := rand.Perm(maxN) for _, e := range rList { key := []byte(strconv.Itoa(e)) - println("insert", e) - list.Insert(key) + // println("insert", e) + list.Insert(key, key) } for _, e := range rList { key := []byte(strconv.Itoa(e)) - println("find", e) + // println("find", e) if _, ok := list.Find(key); !ok { t.Fail() } } - println("print list") + // println("print list") list.println() } @@ -97,7 +97,7 @@ func TestDelete(t *testing.T) { t.Fail() } - list.Insert(k0) + list.Insert(k0, k0) list.Delete(k0) if !list.IsEmpty() { t.Fail() @@ -105,7 +105,7 @@ func TestDelete(t *testing.T) { // Delete elements at the beginning of the list. for i := 0; i < maxN; i++ { - list.Insert(Element(i)) + list.Insert(Element(i), Element(i)) } for i := 0; i < maxN; i++ { list.Delete(Element(i)) @@ -117,7 +117,7 @@ func TestDelete(t *testing.T) { list = New() // Delete elements at the end of the list. for i := 0; i < maxN; i++ { - list.Insert(Element(i)) + list.Insert(Element(i), Element(i)) } for i := 0; i < maxN; i++ { list.Delete(Element(maxN - i - 1)) @@ -130,7 +130,7 @@ func TestDelete(t *testing.T) { // Delete elements at random positions in the list. rList := rand.Perm(maxN) for _, e := range rList { - list.Insert(Element(e)) + list.Insert(Element(e), Element(e)) } for _, e := range rList { list.Delete(Element(e)) @@ -144,7 +144,7 @@ func TestNext(t *testing.T) { list := New() for i := 0; i < maxN; i++ { - list.Insert(Element(i)) + list.Insert(Element(i), Element(i)) } smallest := list.GetSmallestNode() @@ -155,7 +155,7 @@ func TestNext(t *testing.T) { for node != largest { node = list.Next(node) // Must always be incrementing here! - if bytes.Compare(node.Values[0], lastNode.Values[0]) <= 0 { + if bytes.Compare(node.Key, lastNode.Key) <= 0 { t.Fail() } // Next.Prev must always point to itself! @@ -174,7 +174,7 @@ func TestPrev(t *testing.T) { list := New() for i := 0; i < maxN; i++ { - list.Insert(Element(i)) + list.Insert(Element(i), Element(i)) } smallest := list.GetSmallestNode() @@ -185,7 +185,7 @@ func TestPrev(t *testing.T) { for node != smallest { node = list.Prev(node) // Must always be incrementing here! - if bytes.Compare(node.Values[0], lastNode.Values[0]) >= 0 { + if bytes.Compare(node.Key, lastNode.Key) >= 0 { t.Fail() } // Next.Prev must always point to itself! @@ -200,18 +200,6 @@ func TestPrev(t *testing.T) { } } -func TestGetNodeCount(t *testing.T) { - list := New() - - for i := 0; i < maxN; i++ { - list.Insert(Element(i)) - } - - if list.GetNodeCount() != maxN { - t.Fail() - } -} - func TestFindGreaterOrEqual(t *testing.T) { maxNumber := maxN * 100 @@ -227,21 +215,21 @@ func TestFindGreaterOrEqual(t *testing.T) { list = New() for i := 0; i < maxN; i++ { - list.Insert(Element(rand.Intn(maxNumber))) + list.Insert(Element(rand.Intn(maxNumber)), Element(i)) } for i := 0; i < maxN; i++ { key := Element(rand.Intn(maxNumber)) if v, ok := list.FindGreaterOrEqual(key); ok { // if f is v should be bigger than the element before - if bytes.Compare(v.Prev.Key, key) >= 0 { + if v.Prev != nil && bytes.Compare(v.Prev.Key, key) >= 0 { fmt.Printf("PrevV: %s\n key: %s\n\n", string(v.Prev.Key), string(key)) t.Fail() } // v should be bigger or equal to f // If we compare directly, we get an equal key with a difference on the 10th decimal point, which fails. - if bytes.Compare(v.Values[0], key) < 0 { - fmt.Printf("v: %s\n key: %s\n\n", string(v.Values[0]), string(key)) + if bytes.Compare(v.Key, key) < 0 { + fmt.Printf("v: %s\n key: %s\n\n", string(v.Key), string(key)) t.Fail() } } else { From d343b0db5744d5516fe6ffa368dda792c47e9ee3 Mon Sep 17 00:00:00 2001 From: Chris Lu Date: Sun, 3 Oct 2021 01:15:14 -0700 Subject: [PATCH 084/130] update value --- weed/util/skiplist/skiplist.go | 13 +++++++++++++ weed/util/skiplist/skiplist_test.go | 29 ++++++++++++++++++++++++++++- 2 files changed, 41 insertions(+), 1 deletion(-) diff --git a/weed/util/skiplist/skiplist.go b/weed/util/skiplist/skiplist.go index 19fa556ae..50ce53525 100644 --- a/weed/util/skiplist/skiplist.go +++ b/weed/util/skiplist/skiplist.go @@ -1,5 +1,7 @@ package skiplist +// adapted from https://github.com/MauriceGit/skiplist/blob/master/skiplist.go + import ( "bytes" "fmt" @@ -402,6 +404,17 @@ func (t *SkipList) Prev(e *SkipListElement) *SkipListElement { return e.Prev.Load() } +// ChangeValue can be used to change the actual value of a node in the skiplist +// without the need of Deleting and reinserting the node again. +// Be advised, that ChangeValue only works, if the actual key from ExtractKey() will stay the same! +// ok is an indicator, wether the value is actually changed. +func (t *SkipList) ChangeValue(e *SkipListElement, newValue []byte) (ok bool) { + // The key needs to stay correct, so this is very important! + e.Value = newValue + e.Save() + return true +} + // String returns a string format of the skiplist. Useful to get a graphical overview and/or debugging. func (t *SkipList) println() { diff --git a/weed/util/skiplist/skiplist_test.go b/weed/util/skiplist/skiplist_test.go index 811fd5be9..b414c267b 100644 --- a/weed/util/skiplist/skiplist_test.go +++ b/weed/util/skiplist/skiplist_test.go @@ -242,4 +242,31 @@ func TestFindGreaterOrEqual(t *testing.T) { } } -} \ No newline at end of file +} + +func TestChangeValue(t *testing.T) { + list := New() + + for i := 0; i < maxN; i++ { + list.Insert(Element(i), []byte("value")) + } + + for i := 0; i < maxN; i++ { + // The key only looks at the int so the string doesn't matter here! + f1, ok := list.Find(Element(i)) + if !ok { + t.Fail() + } + ok = list.ChangeValue(f1, []byte("different value")) + if !ok { + t.Fail() + } + f2, ok := list.Find(Element(i)) + if !ok { + t.Fail() + } + if bytes.Compare(f2.GetValue(), []byte("different value")) != 0 { + t.Fail() + } + } +} From 22d8684e88bea2a36063568ba95e6d205aac33b0 Mon Sep 17 00:00:00 2001 From: Chris Lu Date: Sun, 3 Oct 2021 02:19:21 -0700 Subject: [PATCH 085/130] refactor out listStore --- weed/util/skiplist/list_store.go | 32 +++++++ weed/util/skiplist/serde.go | 39 ++++---- weed/util/skiplist/skiplist.go | 135 +++++++++++++++++++--------- weed/util/skiplist/skiplist_test.go | 78 ++++++++-------- 4 files changed, 182 insertions(+), 102 deletions(-) create mode 100644 weed/util/skiplist/list_store.go diff --git a/weed/util/skiplist/list_store.go b/weed/util/skiplist/list_store.go new file mode 100644 index 000000000..0eb1106bc --- /dev/null +++ b/weed/util/skiplist/list_store.go @@ -0,0 +1,32 @@ +package skiplist + +type ListStore interface { + SaveElement(id int64, element *SkipListElement) error + DeleteElement(id int64) error + LoadElement(id int64) (*SkipListElement, error) +} + +type MemStore struct { + m map[int64]*SkipListElement +} + +func newMemStore() *MemStore { + return &MemStore{ + m: make(map[int64]*SkipListElement), + } +} + +func (m *MemStore) SaveElement(id int64, element *SkipListElement) error { + m.m[id] = element + return nil +} + +func (m *MemStore) DeleteElement(id int64) error { + delete(m.m, id) + return nil +} + +func (m *MemStore) LoadElement(id int64) (*SkipListElement, error) { + element := m.m[id] + return element, nil +} diff --git a/weed/util/skiplist/serde.go b/weed/util/skiplist/serde.go index 135d3b0b5..5b7089e80 100644 --- a/weed/util/skiplist/serde.go +++ b/weed/util/skiplist/serde.go @@ -9,10 +9,6 @@ func compareElement(a *SkipListElement, key []byte) int { return bytes.Compare(a.Key, key) } -var ( - memStore = make(map[int64]*SkipListElement) -) - func (node *SkipListElement) Reference() *SkipListElementReference { if node == nil { return nil @@ -22,27 +18,24 @@ func (node *SkipListElement) Reference() *SkipListElementReference { Key: node.Key, } } -func (node *SkipListElement) Save() { - if node == nil { - return - } - memStore[node.Id] = node - //println("++ node", node.Id, string(node.Values[0])) -} -func (node *SkipListElement) DeleteSelf() { - if node == nil { - return - } - delete(memStore, node.Id) - //println("++ node", node.Id, string(node.Values[0])) -} - -func (ref *SkipListElementReference) Load() *SkipListElement { - if ref == nil { +func (t *SkipList) saveElement(element *SkipListElement) error { + if element == nil { return nil } - //println("~ node", ref.ElementPointer, string(ref.Key)) - return memStore[ref.ElementPointer] + return t.listStore.SaveElement(element.Id, element) } +func (t *SkipList) deleteElement(element *SkipListElement) error { + if element == nil { + return nil + } + return t.listStore.DeleteElement(element.Id) +} + +func (t *SkipList) loadElement(ref *SkipListElementReference) (*SkipListElement, error) { + if ref == nil { + return nil, nil + } + return t.listStore.LoadElement(ref.ElementPointer) +} diff --git a/weed/util/skiplist/skiplist.go b/weed/util/skiplist/skiplist.go index 50ce53525..498af085d 100644 --- a/weed/util/skiplist/skiplist.go +++ b/weed/util/skiplist/skiplist.go @@ -21,13 +21,14 @@ type SkipList struct { endLevels [maxLevel]*SkipListElementReference maxNewLevel int maxLevel int + listStore ListStore // elementCount int } // NewSeedEps returns a new empty, initialized Skiplist. // Given a seed, a deterministic height/list behaviour can be achieved. // Eps is used to compare keys given by the ExtractKey() function on equality. -func NewSeed(seed int64) *SkipList { +func NewSeed(seed int64, listStore ListStore) *SkipList { // Initialize random number generator. rand.Seed(seed) @@ -36,6 +37,7 @@ func NewSeed(seed int64) *SkipList { list := &SkipList{ maxNewLevel: maxLevel, maxLevel: 0, + listStore: listStore, // elementCount: 0, } @@ -43,8 +45,8 @@ func NewSeed(seed int64) *SkipList { } // New returns a new empty, initialized Skiplist. -func New() *SkipList { - return NewSeed(time.Now().UTC().UnixNano()) +func New(listStore ListStore) *SkipList { + return NewSeed(time.Now().UTC().UnixNano(), listStore) } // IsEmpty checks, if the skiplist is empty. @@ -75,7 +77,7 @@ func (t *SkipList) findEntryIndex(key []byte, level int) int { return 0 } -func (t *SkipList) findExtended(key []byte, findGreaterOrEqual bool) (foundElem *SkipListElement, ok bool) { +func (t *SkipList) findExtended(key []byte, findGreaterOrEqual bool) (foundElem *SkipListElement, ok bool, err error) { foundElem = nil ok = false @@ -87,7 +89,10 @@ func (t *SkipList) findExtended(key []byte, findGreaterOrEqual bool) (foundElem index := t.findEntryIndex(key, 0) var currentNode *SkipListElement - currentNode = t.startLevels[index].Load() + currentNode, err = t.loadElement(t.startLevels[index]) + if err != nil { + return + } // In case, that our first element is already greater-or-equal! if findGreaterOrEqual && compareElement(currentNode, key) > 0 { @@ -106,13 +111,20 @@ func (t *SkipList) findExtended(key []byte, findGreaterOrEqual bool) (foundElem // Which direction are we continuing next time? if currentNode.Next[index] != nil && bytes.Compare(currentNode.Next[index].Key, key) <= 0 { // Go right - currentNode = currentNode.Next[index].Load() + currentNode, err = t.loadElement(currentNode.Next[index]) + if err != nil { + return + } } else { if index > 0 { // Early exit if currentNode.Next[0] != nil && bytes.Compare(currentNode.Next[0].Key, key) == 0 { - currentNodeNext := currentNode.Next[0].Load() + var currentNodeNext *SkipListElement + currentNodeNext, err = t.loadElement(currentNode.Next[0]) + if err != nil { + return + } foundElem = currentNodeNext ok = true return @@ -122,7 +134,10 @@ func (t *SkipList) findExtended(key []byte, findGreaterOrEqual bool) (foundElem } else { // Element is not found and we reached the bottom. if findGreaterOrEqual { - foundElem = currentNode.Next[index].Load() + foundElem, err = t.loadElement(currentNode.Next[index]) + if err != nil { + return + } ok = foundElem != nil } @@ -135,26 +150,26 @@ func (t *SkipList) findExtended(key []byte, findGreaterOrEqual bool) (foundElem // Find tries to find an element in the skiplist based on the key from the given ListElement. // elem can be used, if ok is true. // Find runs in approx. O(log(n)) -func (t *SkipList) Find(key []byte) (elem *SkipListElement, ok bool) { +func (t *SkipList) Find(key []byte) (elem *SkipListElement, ok bool, err error) { if t == nil || key == nil { return } - elem, ok = t.findExtended(key, false) + elem, ok, err = t.findExtended(key, false) return } // FindGreaterOrEqual finds the first element, that is greater or equal to the given ListElement e. // The comparison is done on the keys (So on ExtractKey()). // FindGreaterOrEqual runs in approx. O(log(n)) -func (t *SkipList) FindGreaterOrEqual(key []byte) (elem *SkipListElement, ok bool) { +func (t *SkipList) FindGreaterOrEqual(key []byte) (elem *SkipListElement, ok bool, err error) { if t == nil || key == nil { return } - elem, ok = t.findExtended(key, true) + elem, ok, err = t.findExtended(key, true) return } @@ -162,7 +177,7 @@ func (t *SkipList) FindGreaterOrEqual(key []byte) (elem *SkipListElement, ok boo // If there are multiple entries with the same value, Delete will remove one of them // (Which one will change based on the actual skiplist layout) // Delete runs in approx. O(log(n)) -func (t *SkipList) Delete(key []byte) { +func (t *SkipList) Delete(key []byte) (err error) { if t == nil || t.IsEmpty() || key == nil { return @@ -176,9 +191,12 @@ func (t *SkipList) Delete(key []byte) { for { if currentNode == nil { - nextNode = t.startLevels[index].Load() + nextNode, err = t.loadElement(t.startLevels[index]) } else { - nextNode = currentNode.Next[index].Load() + nextNode, err = t.loadElement(currentNode.Next[index]) + } + if err != nil { + return err } // Found and remove! @@ -186,17 +204,26 @@ func (t *SkipList) Delete(key []byte) { if currentNode != nil { currentNode.Next[index] = nextNode.Next[index] - currentNode.Save() + if err = t.saveElement(currentNode); err != nil { + return err + } } if index == 0 { if nextNode.Next[index] != nil { - nextNextNode := nextNode.Next[index].Load() + nextNextNode, err := t.loadElement(nextNode.Next[index]) + if err != nil { + return err + } nextNextNode.Prev = currentNode.Reference() - nextNextNode.Save() + if err = t.saveElement(nextNextNode); err != nil { + return err + } } // t.elementCount-- - nextNode.DeleteSelf() + if err = t.deleteElement(nextNode); err != nil { + return err + } } // Link from start needs readjustments. @@ -227,12 +254,12 @@ func (t *SkipList) Delete(key []byte) { } } } - + return } // Insert inserts the given ListElement into the skiplist. // Insert runs in approx. O(log(n)) -func (t *SkipList) Insert(key, value []byte) { +func (t *SkipList) Insert(key, value []byte) (err error){ if t == nil || key == nil { return @@ -288,14 +315,20 @@ func (t *SkipList) Insert(key, value []byte) { elem.Next[index] = nextNodeRef if currentNode != nil { currentNode.Next[index] = elem.Reference() - currentNode.Save() + if err = t.saveElement(currentNode); err != nil { + return + } } if index == 0 { elem.Prev = currentNode.Reference() if nextNodeRef != nil { - nextNode = nextNodeRef.Load() + if nextNode, err = t.loadElement(nextNodeRef); err != nil { + return + } nextNode.Prev = elem.Reference() - nextNode.Save() + if err = t.saveElement(nextNode); err != nil { + return + } } } } @@ -304,7 +337,9 @@ func (t *SkipList) Insert(key, value []byte) { // Go right if nextNode == nil { // reuse nextNode when index == 0 - nextNode = nextNodeRef.Load() + if nextNode, err = t.loadElement(nextNodeRef); err != nil { + return + } } currentNode = nextNode } else { @@ -326,9 +361,14 @@ func (t *SkipList) Insert(key, value []byte) { if t.startLevels[i] == nil || bytes.Compare(t.startLevels[i].Key, key) > 0 { if i == 0 && t.startLevels[i] != nil { - startLevelElement := t.startLevels[i].Load() + startLevelElement, err := t.loadElement(t.startLevels[i]) + if err != nil { + return err + } startLevelElement.Prev = elem.Reference() - startLevelElement.Save() + if err = t.saveElement(startLevelElement); err != nil { + return err + } } elem.Next[i] = t.startLevels[i] t.startLevels[i] = elem.Reference() @@ -347,9 +387,14 @@ func (t *SkipList) Insert(key, value []byte) { // This is very important, so we are not linking the very first element (newFirst AND newLast) to itself! if !newFirst { if t.endLevels[i] != nil { - endLevelElement := t.endLevels[i].Load() + endLevelElement, err := t.loadElement(t.endLevels[i]) + if err != nil { + return err + } endLevelElement.Next[i] = elem.Reference() - endLevelElement.Save() + if err = t.saveElement(endLevelElement); err != nil { + return err + } } if i == 0 { elem.Prev = t.endLevels[i] @@ -370,49 +415,51 @@ func (t *SkipList) Insert(key, value []byte) { } } - elem.Save() + if err = t.saveElement(elem); err != nil { + return err + } + return nil } // GetSmallestNode returns the very first/smallest node in the skiplist. // GetSmallestNode runs in O(1) -func (t *SkipList) GetSmallestNode() *SkipListElement { - return t.startLevels[0].Load() +func (t *SkipList) GetSmallestNode() (*SkipListElement, error) { + return t.loadElement(t.startLevels[0]) } // GetLargestNode returns the very last/largest node in the skiplist. // GetLargestNode runs in O(1) -func (t *SkipList) GetLargestNode() *SkipListElement { - return t.endLevels[0].Load() +func (t *SkipList) GetLargestNode() (*SkipListElement, error) { + return t.loadElement(t.endLevels[0]) } // Next returns the next element based on the given node. // Next will loop around to the first node, if you call it on the last! -func (t *SkipList) Next(e *SkipListElement) *SkipListElement { +func (t *SkipList) Next(e *SkipListElement) (*SkipListElement, error) { if e.Next[0] == nil { - return t.startLevels[0].Load() + return t.loadElement(t.startLevels[0]) } - return e.Next[0].Load() + return t.loadElement(e.Next[0]) } // Prev returns the previous element based on the given node. // Prev will loop around to the last node, if you call it on the first! -func (t *SkipList) Prev(e *SkipListElement) *SkipListElement { +func (t *SkipList) Prev(e *SkipListElement) (*SkipListElement, error) { if e.Prev == nil { - return t.endLevels[0].Load() + return t.loadElement(t.endLevels[0]) } - return e.Prev.Load() + return t.loadElement(e.Prev) } // ChangeValue can be used to change the actual value of a node in the skiplist // without the need of Deleting and reinserting the node again. // Be advised, that ChangeValue only works, if the actual key from ExtractKey() will stay the same! // ok is an indicator, wether the value is actually changed. -func (t *SkipList) ChangeValue(e *SkipListElement, newValue []byte) (ok bool) { +func (t *SkipList) ChangeValue(e *SkipListElement, newValue []byte) (err error) { // The key needs to stay correct, so this is very important! e.Value = newValue - e.Save() - return true + return t.saveElement(e) } // String returns a string format of the skiplist. Useful to get a graphical overview and/or debugging. @@ -437,7 +484,7 @@ func (t *SkipList) println() { nodeRef := t.startLevels[0] for nodeRef != nil { print(fmt.Sprintf("%v: ", string(nodeRef.Key))) - node := nodeRef.Load() + node, _ := t.loadElement(nodeRef) for i := 0; i <= int(node.Level); i++ { l := node.Next[i] diff --git a/weed/util/skiplist/skiplist_test.go b/weed/util/skiplist/skiplist_test.go index b414c267b..75cbc6cfd 100644 --- a/weed/util/skiplist/skiplist_test.go +++ b/weed/util/skiplist/skiplist_test.go @@ -12,6 +12,10 @@ const ( maxN = 10000 ) +var ( + memStore = newMemStore() +) + func TestInsertAndFind(t *testing.T) { k0 := []byte("0") @@ -19,12 +23,12 @@ func TestInsertAndFind(t *testing.T) { var listPointer *SkipList listPointer.Insert(k0, k0) - if _, ok := listPointer.Find(k0); ok { + if _, ok, _ := listPointer.Find(k0); ok { t.Fail() } - list = New() - if _, ok := list.Find(k0); ok { + list = New(memStore) + if _, ok, _ := list.Find(k0); ok { t.Fail() } if !list.IsEmpty() { @@ -33,18 +37,17 @@ func TestInsertAndFind(t *testing.T) { // Test at the beginning of the list. for i := 0; i < maxN; i++ { - key := []byte(strconv.Itoa(maxN-i)) + key := []byte(strconv.Itoa(maxN - i)) list.Insert(key, key) } for i := 0; i < maxN; i++ { - key := []byte(strconv.Itoa(maxN-i)) - if _, ok := list.Find(key); !ok { + key := []byte(strconv.Itoa(maxN - i)) + if _, ok, _ := list.Find(key); !ok { t.Fail() } } - - list = New() + list = New(memStore) // Test at the end of the list. for i := 0; i < maxN; i++ { key := []byte(strconv.Itoa(i)) @@ -52,12 +55,12 @@ func TestInsertAndFind(t *testing.T) { } for i := 0; i < maxN; i++ { key := []byte(strconv.Itoa(i)) - if _, ok := list.Find(key); !ok { + if _, ok, _ := list.Find(key); !ok { t.Fail() } } - list = New() + list = New(memStore) // Test at random positions in the list. rList := rand.Perm(maxN) for _, e := range rList { @@ -68,7 +71,7 @@ func TestInsertAndFind(t *testing.T) { for _, e := range rList { key := []byte(strconv.Itoa(e)) // println("find", e) - if _, ok := list.Find(key); !ok { + if _, ok, _ := list.Find(key); !ok { t.Fail() } } @@ -90,7 +93,7 @@ func TestDelete(t *testing.T) { // Delete on empty list list.Delete(k0) - list = New() + list = New(memStore) list.Delete(k0) if !list.IsEmpty() { @@ -114,7 +117,7 @@ func TestDelete(t *testing.T) { t.Fail() } - list = New() + list = New(memStore) // Delete elements at the end of the list. for i := 0; i < maxN; i++ { list.Insert(Element(i), Element(i)) @@ -126,7 +129,7 @@ func TestDelete(t *testing.T) { t.Fail() } - list = New() + list = New(memStore) // Delete elements at random positions in the list. rList := rand.Perm(maxN) for _, e := range rList { @@ -141,61 +144,65 @@ func TestDelete(t *testing.T) { } func TestNext(t *testing.T) { - list := New() + list := New(memStore) for i := 0; i < maxN; i++ { list.Insert(Element(i), Element(i)) } - smallest := list.GetSmallestNode() - largest := list.GetLargestNode() + smallest, _ := list.GetSmallestNode() + largest, _ := list.GetLargestNode() lastNode := smallest node := lastNode for node != largest { - node = list.Next(node) + node, _ = list.Next(node) // Must always be incrementing here! if bytes.Compare(node.Key, lastNode.Key) <= 0 { t.Fail() } // Next.Prev must always point to itself! - if list.Next(list.Prev(node)) != node { + prevNode, _ := list.Prev(node) + nextNode, _ := list.Next(prevNode) + if nextNode != node { t.Fail() } lastNode = node } - if list.Next(largest) != smallest { + if nextNode, _ := list.Next(largest); nextNode != smallest { t.Fail() } } func TestPrev(t *testing.T) { - list := New() + list := New(memStore) for i := 0; i < maxN; i++ { list.Insert(Element(i), Element(i)) } - smallest := list.GetSmallestNode() - largest := list.GetLargestNode() + smallest, _ := list.GetSmallestNode() + largest, _ := list.GetLargestNode() lastNode := largest node := lastNode for node != smallest { - node = list.Prev(node) + node, _ = list.Prev(node) // Must always be incrementing here! if bytes.Compare(node.Key, lastNode.Key) >= 0 { t.Fail() } // Next.Prev must always point to itself! - if list.Prev(list.Next(node)) != node { + nextNode, _ := list.Next(node) + prevNode, _ := list.Prev(nextNode) + if prevNode != node { t.Fail() } lastNode = node } - if list.Prev(smallest) != largest { + if prevNode, _ := list.Prev(smallest); prevNode != largest { t.Fail() } } @@ -208,11 +215,11 @@ func TestFindGreaterOrEqual(t *testing.T) { var listPointer *SkipList // Test on empty list. - if _, ok := listPointer.FindGreaterOrEqual(Element(0)); ok { + if _, ok, _ := listPointer.FindGreaterOrEqual(Element(0)); ok { t.Fail() } - list = New() + list = New(memStore) for i := 0; i < maxN; i++ { list.Insert(Element(rand.Intn(maxNumber)), Element(i)) @@ -220,7 +227,7 @@ func TestFindGreaterOrEqual(t *testing.T) { for i := 0; i < maxN; i++ { key := Element(rand.Intn(maxNumber)) - if v, ok := list.FindGreaterOrEqual(key); ok { + if v, ok, _ := list.FindGreaterOrEqual(key); ok { // if f is v should be bigger than the element before if v.Prev != nil && bytes.Compare(v.Prev.Key, key) >= 0 { fmt.Printf("PrevV: %s\n key: %s\n\n", string(v.Prev.Key), string(key)) @@ -233,7 +240,8 @@ func TestFindGreaterOrEqual(t *testing.T) { t.Fail() } } else { - lastV := list.GetLargestNode().GetValue() + lastNode, _ := list.GetLargestNode() + lastV := lastNode.GetValue() // It is OK, to fail, as long as f is bigger than the last element. if bytes.Compare(key, lastV) <= 0 { fmt.Printf("lastV: %s\n key: %s\n\n", string(lastV), string(key)) @@ -245,7 +253,7 @@ func TestFindGreaterOrEqual(t *testing.T) { } func TestChangeValue(t *testing.T) { - list := New() + list := New(memStore) for i := 0; i < maxN; i++ { list.Insert(Element(i), []byte("value")) @@ -253,15 +261,15 @@ func TestChangeValue(t *testing.T) { for i := 0; i < maxN; i++ { // The key only looks at the int so the string doesn't matter here! - f1, ok := list.Find(Element(i)) + f1, ok, _ := list.Find(Element(i)) if !ok { t.Fail() } - ok = list.ChangeValue(f1, []byte("different value")) - if !ok { + err := list.ChangeValue(f1, []byte("different value")) + if err != nil { t.Fail() } - f2, ok := list.Find(Element(i)) + f2, ok, _ := list.Find(Element(i)) if !ok { t.Fail() } From 1d2dfe593c8d9dcb11704c2573f5295d001f5532 Mon Sep 17 00:00:00 2001 From: Janikio Date: Sun, 3 Oct 2021 18:34:47 +0200 Subject: [PATCH 086/130] added namespace to ingress --- k8s/helm_charts2/templates/ingress.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/k8s/helm_charts2/templates/ingress.yaml b/k8s/helm_charts2/templates/ingress.yaml index dcd52c138..4f1b4251d 100644 --- a/k8s/helm_charts2/templates/ingress.yaml +++ b/k8s/helm_charts2/templates/ingress.yaml @@ -2,6 +2,7 @@ apiVersion: extensions/v1beta1 kind: Ingress metadata: name: ingress-{{ template "seaweedfs.name" . }}-filer + namespace: {{ .Release.Namespace }} annotations: kubernetes.io/ingress.class: "nginx" nginx.ingress.kubernetes.io/auth-type: "basic" @@ -32,6 +33,7 @@ apiVersion: extensions/v1beta1 kind: Ingress metadata: name: ingress-{{ template "seaweedfs.name" . }}-master + namespace: {{ .Release.Namespace }} annotations: kubernetes.io/ingress.class: "nginx" nginx.ingress.kubernetes.io/auth-type: "basic" From ca04c59ac95951b17e6081e681fa35e3ca275038 Mon Sep 17 00:00:00 2001 From: Janikio Date: Sun, 3 Oct 2021 18:35:04 +0200 Subject: [PATCH 087/130] ignore ServiceMonitor when monitoring is disabled --- k8s/helm_charts2/templates/filer-servicemonitor.yaml | 2 ++ k8s/helm_charts2/templates/s3-servicemonitor.yaml | 2 ++ k8s/helm_charts2/templates/volume-servicemonitor.yaml | 2 ++ 3 files changed, 6 insertions(+) diff --git a/k8s/helm_charts2/templates/filer-servicemonitor.yaml b/k8s/helm_charts2/templates/filer-servicemonitor.yaml index f07f6ebef..ed45442dc 100644 --- a/k8s/helm_charts2/templates/filer-servicemonitor.yaml +++ b/k8s/helm_charts2/templates/filer-servicemonitor.yaml @@ -1,4 +1,5 @@ {{- if .Values.filer.metricsPort }} +{{- if .Values.global.monitoring.enabled }} apiVersion: monitoring.coreos.com/v1 kind: ServiceMonitor metadata: @@ -15,4 +16,5 @@ spec: selector: app: {{ template "seaweedfs.name" . }} component: filer +{{- end }} {{- end }} \ No newline at end of file diff --git a/k8s/helm_charts2/templates/s3-servicemonitor.yaml b/k8s/helm_charts2/templates/s3-servicemonitor.yaml index 7f18f00f5..b549893c7 100644 --- a/k8s/helm_charts2/templates/s3-servicemonitor.yaml +++ b/k8s/helm_charts2/templates/s3-servicemonitor.yaml @@ -1,4 +1,5 @@ {{- if .Values.s3.metricsPort }} +{{- if .Values.global.monitoring.enabled }} apiVersion: monitoring.coreos.com/v1 kind: ServiceMonitor metadata: @@ -15,4 +16,5 @@ spec: selector: app: {{ template "seaweedfs.name" . }} component: s3 +{{- end }} {{- end }} \ No newline at end of file diff --git a/k8s/helm_charts2/templates/volume-servicemonitor.yaml b/k8s/helm_charts2/templates/volume-servicemonitor.yaml index 1b286e9b6..90d70e8de 100644 --- a/k8s/helm_charts2/templates/volume-servicemonitor.yaml +++ b/k8s/helm_charts2/templates/volume-servicemonitor.yaml @@ -1,4 +1,5 @@ {{- if .Values.volume.metricsPort }} +{{- if .Values.global.monitoring.enabled }} apiVersion: monitoring.coreos.com/v1 kind: ServiceMonitor metadata: @@ -15,4 +16,5 @@ spec: selector: app: {{ template "seaweedfs.name" . }} component: volume +{{- end }} {{- end }} \ No newline at end of file From a481c4a45ef60de22d6dacf83010542ce8c6e1bb Mon Sep 17 00:00:00 2001 From: Chris Lu Date: Sun, 3 Oct 2021 13:50:52 -0700 Subject: [PATCH 088/130] return previous element if visited --- weed/util/skiplist/skiplist.go | 15 ++++++++------- weed/util/skiplist/skiplist_test.go | 18 +++++++++--------- 2 files changed, 17 insertions(+), 16 deletions(-) diff --git a/weed/util/skiplist/skiplist.go b/weed/util/skiplist/skiplist.go index 498af085d..b48a05b4a 100644 --- a/weed/util/skiplist/skiplist.go +++ b/weed/util/skiplist/skiplist.go @@ -67,17 +67,17 @@ func (t *SkipList) generateLevel(maxLevel int) int { return level } -func (t *SkipList) findEntryIndex(key []byte, level int) int { +func (t *SkipList) findEntryIndex(key []byte, minLevel int) int { // Find good entry point so we don't accidentally skip half the list. for i := t.maxLevel; i >= 0; i-- { - if t.startLevels[i] != nil && bytes.Compare(t.startLevels[i].Key, key) < 0 || i <= level { + if t.startLevels[i] != nil && bytes.Compare(t.startLevels[i].Key, key) < 0 || i <= minLevel { return i } } return 0 } -func (t *SkipList) findExtended(key []byte, findGreaterOrEqual bool) (foundElem *SkipListElement, ok bool, err error) { +func (t *SkipList) findExtended(key []byte, findGreaterOrEqual bool) (prevElementIfVisited *SkipListElement, foundElem *SkipListElement, ok bool, err error) { foundElem = nil ok = false @@ -120,6 +120,7 @@ func (t *SkipList) findExtended(key []byte, findGreaterOrEqual bool) (foundElem // Early exit if currentNode.Next[0] != nil && bytes.Compare(currentNode.Next[0].Key, key) == 0 { + prevElementIfVisited = currentNode var currentNodeNext *SkipListElement currentNodeNext, err = t.loadElement(currentNode.Next[0]) if err != nil { @@ -150,26 +151,26 @@ func (t *SkipList) findExtended(key []byte, findGreaterOrEqual bool) (foundElem // Find tries to find an element in the skiplist based on the key from the given ListElement. // elem can be used, if ok is true. // Find runs in approx. O(log(n)) -func (t *SkipList) Find(key []byte) (elem *SkipListElement, ok bool, err error) { +func (t *SkipList) Find(key []byte) (prevIfVisited *SkipListElement, elem *SkipListElement, ok bool, err error) { if t == nil || key == nil { return } - elem, ok, err = t.findExtended(key, false) + prevIfVisited, elem, ok, err = t.findExtended(key, false) return } // FindGreaterOrEqual finds the first element, that is greater or equal to the given ListElement e. // The comparison is done on the keys (So on ExtractKey()). // FindGreaterOrEqual runs in approx. O(log(n)) -func (t *SkipList) FindGreaterOrEqual(key []byte) (elem *SkipListElement, ok bool, err error) { +func (t *SkipList) FindGreaterOrEqual(key []byte) (prevIfVisited *SkipListElement, elem *SkipListElement, ok bool, err error) { if t == nil || key == nil { return } - elem, ok, err = t.findExtended(key, true) + prevIfVisited, elem, ok, err = t.findExtended(key, true) return } diff --git a/weed/util/skiplist/skiplist_test.go b/weed/util/skiplist/skiplist_test.go index 75cbc6cfd..115656cd9 100644 --- a/weed/util/skiplist/skiplist_test.go +++ b/weed/util/skiplist/skiplist_test.go @@ -23,12 +23,12 @@ func TestInsertAndFind(t *testing.T) { var listPointer *SkipList listPointer.Insert(k0, k0) - if _, ok, _ := listPointer.Find(k0); ok { + if _, _, ok, _ := listPointer.Find(k0); ok { t.Fail() } list = New(memStore) - if _, ok, _ := list.Find(k0); ok { + if _, _, ok, _ := list.Find(k0); ok { t.Fail() } if !list.IsEmpty() { @@ -42,7 +42,7 @@ func TestInsertAndFind(t *testing.T) { } for i := 0; i < maxN; i++ { key := []byte(strconv.Itoa(maxN - i)) - if _, ok, _ := list.Find(key); !ok { + if _, _, ok, _ := list.Find(key); !ok { t.Fail() } } @@ -55,7 +55,7 @@ func TestInsertAndFind(t *testing.T) { } for i := 0; i < maxN; i++ { key := []byte(strconv.Itoa(i)) - if _, ok, _ := list.Find(key); !ok { + if _, _, ok, _ := list.Find(key); !ok { t.Fail() } } @@ -71,7 +71,7 @@ func TestInsertAndFind(t *testing.T) { for _, e := range rList { key := []byte(strconv.Itoa(e)) // println("find", e) - if _, ok, _ := list.Find(key); !ok { + if _, _, ok, _ := list.Find(key); !ok { t.Fail() } } @@ -215,7 +215,7 @@ func TestFindGreaterOrEqual(t *testing.T) { var listPointer *SkipList // Test on empty list. - if _, ok, _ := listPointer.FindGreaterOrEqual(Element(0)); ok { + if _, _, ok, _ := listPointer.FindGreaterOrEqual(Element(0)); ok { t.Fail() } @@ -227,7 +227,7 @@ func TestFindGreaterOrEqual(t *testing.T) { for i := 0; i < maxN; i++ { key := Element(rand.Intn(maxNumber)) - if v, ok, _ := list.FindGreaterOrEqual(key); ok { + if _, v, ok, _ := list.FindGreaterOrEqual(key); ok { // if f is v should be bigger than the element before if v.Prev != nil && bytes.Compare(v.Prev.Key, key) >= 0 { fmt.Printf("PrevV: %s\n key: %s\n\n", string(v.Prev.Key), string(key)) @@ -261,7 +261,7 @@ func TestChangeValue(t *testing.T) { for i := 0; i < maxN; i++ { // The key only looks at the int so the string doesn't matter here! - f1, ok, _ := list.Find(Element(i)) + _, f1, ok, _ := list.Find(Element(i)) if !ok { t.Fail() } @@ -269,7 +269,7 @@ func TestChangeValue(t *testing.T) { if err != nil { t.Fail() } - f2, ok, _ := list.Find(Element(i)) + _, f2, ok, _ := list.Find(Element(i)) if !ok { t.Fail() } From e6196cdc503dbe135c3ac22f4e13f62968d30036 Mon Sep 17 00:00:00 2001 From: Chris Lu Date: Sun, 3 Oct 2021 17:54:25 -0700 Subject: [PATCH 089/130] add name list --- weed/util/skiplist/name_batch.go | 102 +++++++++ weed/util/skiplist/name_list.go | 303 +++++++++++++++++++++++++++ weed/util/skiplist/name_list_test.go | 73 +++++++ weed/util/skiplist/skiplist.pb.go | 75 ++++++- weed/util/skiplist/skiplist.proto | 4 + 5 files changed, 551 insertions(+), 6 deletions(-) create mode 100644 weed/util/skiplist/name_batch.go create mode 100644 weed/util/skiplist/name_list.go create mode 100644 weed/util/skiplist/name_list_test.go diff --git a/weed/util/skiplist/name_batch.go b/weed/util/skiplist/name_batch.go new file mode 100644 index 000000000..18427d341 --- /dev/null +++ b/weed/util/skiplist/name_batch.go @@ -0,0 +1,102 @@ +package skiplist + +import ( + "github.com/chrislusf/seaweedfs/weed/glog" + "github.com/golang/protobuf/proto" + "sort" + "strings" +) + +type NameBatch struct { + key string + names map[string]struct{} +} + +func (nb *NameBatch) ContainsName(name string) (found bool) { + _, found = nb.names[name] + return +} +func (nb *NameBatch) WriteName(name string) { + if nb.key == "" || strings.Compare(nb.key, name) > 0 { + nb.key = name + } + nb.names[name] = struct{}{} +} +func (nb *NameBatch) DeleteName(name string) { + delete(nb.names, name) + if nb.key == name { + nb.key = "" + for n := range nb.names { + if nb.key == "" || strings.Compare(nb.key, n) > 0 { + nb.key = n + } + } + } +} +func (nb *NameBatch) ListNames(startFrom string, visitNamesFn func(name string) bool) bool { + var names []string + needFilter := startFrom == "" + for n := range nb.names { + if !needFilter || strings.Compare(n, startFrom) >= 0 { + names = append(names, n) + } + } + sort.Slice(names, func(i, j int) bool { + return strings.Compare(names[i], names[j]) < 0 + }) + for _, n := range names { + if !visitNamesFn(n) { + return false + } + } + return true +} + +func NewNameBatch() *NameBatch { + return &NameBatch{ + names: make(map[string]struct{}), + } +} + +func LoadNameBatch(data []byte) *NameBatch { + t := &NameBatchData{} + if len(data) > 0 { + err := proto.Unmarshal(data, t) + if err != nil { + glog.Errorf("unmarshal into NameBatchData{} : %v", err) + return nil + } + } + nb := NewNameBatch() + for _, n := range t.Names { + name := string(n) + if nb.key == "" || strings.Compare(nb.key, name) > 0 { + nb.key = name + } + nb.names[name] = struct{}{} + } + return nb +} + +func (nb *NameBatch) ToBytes() []byte { + t := &NameBatchData{} + for n := range nb.names { + t.Names = append(t.Names, []byte(n)) + } + data, _ := proto.Marshal(t) + return data +} + +func (nb *NameBatch) SplitBy(name string) (x, y *NameBatch) { + x, y = NewNameBatch(), NewNameBatch() + + for n := range nb.names { + // there should be no equal case though + if strings.Compare(n, name) <= 0 { + x.WriteName(n) + } else { + y.WriteName(n) + } + } + return +} diff --git a/weed/util/skiplist/name_list.go b/weed/util/skiplist/name_list.go new file mode 100644 index 000000000..db328afba --- /dev/null +++ b/weed/util/skiplist/name_list.go @@ -0,0 +1,303 @@ +package skiplist + +import ( + "bytes" +) + +type NameList struct { + skipList *SkipList + batchSize int +} + +func NewNameList(store ListStore, batchSize int) *NameList { + return &NameList{ + skipList: New(store), + batchSize: batchSize, + } +} + +/* +Be reluctant to create new nodes. Try to fit into either previous node or next node. +Prefer to add to previous node. + +There are multiple cases after finding the name for greater or equal node + 1. found and node.Key == name + The node contains a batch with leading key the same as the name + nothing to do + 2. no such node found or node.Key > name + + if no such node found + prevNode = list.LargestNode + + // case 2.1 + if previousNode contains name + nothing to do + + // prefer to add to previous node + if prevNode != nil { + // case 2.2 + if prevNode has capacity + prevNode.add name, and save + return + // case 2.3 + split prevNode by name + } + + // case 2.4 + // merge into next node. Avoid too many nodes if adding data in reverse order. + if nextNode is not nil and nextNode has capacity + delete nextNode.Key + nextNode.Key = name + nextNode.batch.add name + insert nodeNode.Key + return + + // case 2.5 + if prevNode is nil + insert new node with key = name, value = batch{name} + return + +*/ +func (nl *NameList) WriteName(name string) error { + lookupKey := []byte(name) + prevNode, nextNode, found, err := nl.skipList.FindGreaterOrEqual(lookupKey) + if err != nil { + return err + } + // case 1: the name already exists as one leading key in the batch + if found && bytes.Compare(nextNode.Key, lookupKey) == 0 { + return nil + } + + if !found { + prevNode, err = nl.skipList.GetLargestNode() + if err != nil { + return err + } + } + + if nextNode != nil && prevNode == nil { + prevNode, err = nl.skipList.loadElement(nextNode.Prev) + if err != nil { + return err + } + } + + if prevNode != nil { + prevNameBatch := LoadNameBatch(prevNode.Value) + // case 2.1 + if prevNameBatch.ContainsName(name) { + return nil + } + + // case 2.2 + if len(prevNameBatch.names) < nl.batchSize { + prevNameBatch.WriteName(name) + return nl.skipList.ChangeValue(prevNode, prevNameBatch.ToBytes()) + } + + // case 2.3 + x, y := prevNameBatch.SplitBy(name) + addToX := len(x.names) <= len(y.names) + if len(x.names) != len(prevNameBatch.names) { + if addToX { + x.WriteName(name) + } + if x.key == prevNameBatch.key { + if err := nl.skipList.ChangeValue(prevNode, x.ToBytes()); err != nil { + return err + } + } else { + if err := nl.skipList.Insert([]byte(x.key), x.ToBytes()); err != nil { + return err + } + } + } + if len(y.names) != len(prevNameBatch.names) { + if !addToX { + y.WriteName(name) + } + if y.key == prevNameBatch.key { + if err := nl.skipList.ChangeValue(prevNode, y.ToBytes()); err != nil { + return err + } + } else { + if err := nl.skipList.Insert([]byte(y.key), y.ToBytes()); err != nil { + return err + } + } + } + return nil + + } + + // case 2.4 + if nextNode != nil { + nextNameBatch := LoadNameBatch(nextNode.Value) + if len(nextNameBatch.names) < nl.batchSize { + if err := nl.skipList.Delete(nextNode.Key); err != nil { + return err + } + nextNameBatch.WriteName(name) + if err := nl.skipList.Insert([]byte(nextNameBatch.key), nextNameBatch.ToBytes()); err != nil { + return err + } + return nil + } + } + + // case 2.5 + // now prevNode is nil + newNameBatch := NewNameBatch() + newNameBatch.WriteName(name) + if err := nl.skipList.Insert([]byte(newNameBatch.key), newNameBatch.ToBytes()); err != nil { + return err + } + + return nil +} + +/* +// case 1: exists in nextNode +if nextNode != nil && nextNode.Key == name { + remove from nextNode, update nextNode + // TODO: merge with prevNode if possible? + return +} +if nextNode is nil + prevNode = list.Largestnode +if prevNode == nil and nextNode.Prev != nil + prevNode = load(nextNode.Prev) + +// case 2: does not exist +// case 2.1 +if prevNode == nil { + return +} +// case 2.2 +if prevNameBatch does not contain name { + return +} + +// case 3 +delete from prevNameBatch +if prevNameBatch + nextNode < capacityList + // case 3.1 + merge +else + // case 3.2 + update prevNode + + +*/ +func (nl *NameList) DeleteName(name string) error { + lookupKey := []byte(name) + prevNode, nextNode, found, err := nl.skipList.FindGreaterOrEqual(lookupKey) + if err != nil { + return err + } + + // case 1 + var nextNameBatch *NameBatch + if nextNode != nil { + nextNameBatch = LoadNameBatch(nextNode.Value) + } + if found && bytes.Compare(nextNode.Key, lookupKey) == 0 { + if err := nl.skipList.Delete(nextNode.Key); err != nil { + return err + } + nextNameBatch.DeleteName(name) + if len(nextNameBatch.names) > 0 { + if err := nl.skipList.Insert([]byte(nextNameBatch.key), nextNameBatch.ToBytes()); err != nil { + return err + } + } + return nil + } + + if !found { + prevNode, err = nl.skipList.GetLargestNode() + if err != nil { + return err + } + } + + if nextNode != nil && prevNode == nil { + prevNode, err = nl.skipList.loadElement(nextNode.Prev) + if err != nil { + return err + } + } + + // case 2 + if prevNode == nil { + // case 2.1 + return nil + } + prevNameBatch := LoadNameBatch(prevNode.Value) + if !prevNameBatch.ContainsName(name) { + // case 2.2 + return nil + } + + // case 3 + prevNameBatch.DeleteName(name) + if len(prevNameBatch.names) == 0 { + if err := nl.skipList.Delete(prevNode.Key); err != nil { + return err + } + return nil + } + if nextNameBatch != nil && len(nextNameBatch.names) + len(prevNameBatch.names) < nl.batchSize { + // case 3.1 merge nextNode and prevNode + if err := nl.skipList.Delete(nextNode.Key); err != nil { + return err + } + for nextName := range nextNameBatch.names { + prevNameBatch.WriteName(nextName) + } + return nl.skipList.ChangeValue(prevNode, prevNameBatch.ToBytes()) + } else { + // case 3.2 update prevNode + return nl.skipList.ChangeValue(prevNode, prevNameBatch.ToBytes()) + } + + return nil +} + +func (nl *NameList) ListNames(startFrom string, visitNamesFn func(name string) bool) error { + lookupKey := []byte(startFrom) + prevNode, nextNode, found, err := nl.skipList.FindGreaterOrEqual(lookupKey) + if err != nil { + return err + } + if found && bytes.Compare(nextNode.Key, lookupKey) == 0 { + prevNode = nil + } + if !found { + prevNode, err = nl.skipList.GetLargestNode() + if err != nil { + return err + } + } + + if prevNode != nil { + prevNameBatch := LoadNameBatch(prevNode.Value) + if !prevNameBatch.ListNames(startFrom, visitNamesFn) { + return nil + } + } + + for nextNode != nil { + nextNameBatch := LoadNameBatch(nextNode.Value) + if !nextNameBatch.ListNames(startFrom, visitNamesFn) { + return nil + } + nextNode, err = nl.skipList.loadElement(nextNode.Next[0]) + if err != nil { + return err + } + } + + return nil +} diff --git a/weed/util/skiplist/name_list_test.go b/weed/util/skiplist/name_list_test.go new file mode 100644 index 000000000..811a101f2 --- /dev/null +++ b/weed/util/skiplist/name_list_test.go @@ -0,0 +1,73 @@ +package skiplist + +import ( + "math/rand" + "strconv" + "testing" +) + +const ( + maxNameCount = 100 +) + +func String(x int) string { + return strconv.Itoa(x) +} + +func TestNameList(t *testing.T) { + list := NewNameList(memStore, 7) + + for i := 0; i < maxNameCount; i++ { + list.WriteName(String(i)) + } + + counter := 0 + list.ListNames("", func(name string) bool { + counter++ + print(name, " ") + return true + }) + if counter != maxNameCount { + t.Fail() + } + + // list.skipList.println() + + deleteBase := 5 + deleteCount := maxNameCount - 3 * deleteBase + + for i := deleteBase; i < deleteBase+deleteCount; i++ { + list.DeleteName(String(i)) + } + + counter = 0 + list.ListNames("", func(name string) bool { + counter++ + return true + }) + // list.skipList.println() + if counter != maxNameCount-deleteCount { + t.Fail() + } + + // randomized deletion + list = NewNameList(memStore, 7) + // Delete elements at random positions in the list. + rList := rand.Perm(maxN) + for _, i := range rList { + list.WriteName(String(i)) + } + for _, i := range rList { + list.DeleteName(String(i)) + } + counter = 0 + list.ListNames("", func(name string) bool { + counter++ + print(name, " ") + return true + }) + if counter != 0 { + t.Fail() + } + +} diff --git a/weed/util/skiplist/skiplist.pb.go b/weed/util/skiplist/skiplist.pb.go index 82afec453..adb121bfc 100644 --- a/weed/util/skiplist/skiplist.pb.go +++ b/weed/util/skiplist/skiplist.pb.go @@ -238,6 +238,53 @@ func (x *SkipListElement) GetPrev() *SkipListElementReference { return nil } +type NameBatchData struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Names [][]byte `protobuf:"bytes,1,rep,name=names,proto3" json:"names,omitempty"` +} + +func (x *NameBatchData) Reset() { + *x = NameBatchData{} + if protoimpl.UnsafeEnabled { + mi := &file_skiplist_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *NameBatchData) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*NameBatchData) ProtoMessage() {} + +func (x *NameBatchData) ProtoReflect() protoreflect.Message { + mi := &file_skiplist_proto_msgTypes[3] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use NameBatchData.ProtoReflect.Descriptor instead. +func (*NameBatchData) Descriptor() ([]byte, []int) { + return file_skiplist_proto_rawDescGZIP(), []int{3} +} + +func (x *NameBatchData) GetNames() [][]byte { + if x != nil { + return x.Names + } + return nil +} + var File_skiplist_proto protoreflect.FileDescriptor var file_skiplist_proto_rawDesc = []byte{ @@ -275,10 +322,13 @@ var file_skiplist_proto_rawDesc = []byte{ 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x73, 0x6b, 0x69, 0x70, 0x6c, 0x69, 0x73, 0x74, 0x2e, 0x53, 0x6b, 0x69, 0x70, 0x4c, 0x69, 0x73, 0x74, 0x45, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x52, 0x04, 0x70, 0x72, 0x65, 0x76, - 0x42, 0x33, 0x5a, 0x31, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, - 0x68, 0x72, 0x69, 0x73, 0x6c, 0x75, 0x73, 0x66, 0x2f, 0x73, 0x65, 0x61, 0x77, 0x65, 0x65, 0x64, - 0x66, 0x73, 0x2f, 0x77, 0x65, 0x65, 0x64, 0x2f, 0x75, 0x74, 0x69, 0x6c, 0x2f, 0x73, 0x6b, 0x69, - 0x70, 0x6c, 0x69, 0x73, 0x74, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x22, 0x25, 0x0a, 0x0d, 0x4e, 0x61, 0x6d, 0x65, 0x42, 0x61, 0x74, 0x63, 0x68, 0x44, 0x61, 0x74, + 0x61, 0x12, 0x14, 0x0a, 0x05, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0c, + 0x52, 0x05, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x42, 0x33, 0x5a, 0x31, 0x67, 0x69, 0x74, 0x68, 0x75, + 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x68, 0x72, 0x69, 0x73, 0x6c, 0x75, 0x73, 0x66, 0x2f, + 0x73, 0x65, 0x61, 0x77, 0x65, 0x65, 0x64, 0x66, 0x73, 0x2f, 0x77, 0x65, 0x65, 0x64, 0x2f, 0x75, + 0x74, 0x69, 0x6c, 0x2f, 0x73, 0x6b, 0x69, 0x70, 0x6c, 0x69, 0x73, 0x74, 0x62, 0x06, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -293,11 +343,12 @@ func file_skiplist_proto_rawDescGZIP() []byte { return file_skiplist_proto_rawDescData } -var file_skiplist_proto_msgTypes = make([]protoimpl.MessageInfo, 3) +var file_skiplist_proto_msgTypes = make([]protoimpl.MessageInfo, 4) var file_skiplist_proto_goTypes = []interface{}{ (*SkipListProto)(nil), // 0: skiplist.SkipListProto (*SkipListElementReference)(nil), // 1: skiplist.SkipListElementReference (*SkipListElement)(nil), // 2: skiplist.SkipListElement + (*NameBatchData)(nil), // 3: skiplist.NameBatchData } var file_skiplist_proto_depIdxs = []int32{ 1, // 0: skiplist.SkipListProto.start_levels:type_name -> skiplist.SkipListElementReference @@ -353,6 +404,18 @@ func file_skiplist_proto_init() { return nil } } + file_skiplist_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*NameBatchData); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } } type x struct{} out := protoimpl.TypeBuilder{ @@ -360,7 +423,7 @@ func file_skiplist_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_skiplist_proto_rawDesc, NumEnums: 0, - NumMessages: 3, + NumMessages: 4, NumExtensions: 0, NumServices: 0, }, diff --git a/weed/util/skiplist/skiplist.proto b/weed/util/skiplist/skiplist.proto index bfb190b33..2991ad830 100644 --- a/weed/util/skiplist/skiplist.proto +++ b/weed/util/skiplist/skiplist.proto @@ -24,3 +24,7 @@ message SkipListElement { bytes value = 5; SkipListElementReference prev = 6; } + +message NameBatchData { + repeated bytes names = 1; +} \ No newline at end of file From ba7fbac07fa315cf3c0082e54093fbe0ba3865c2 Mon Sep 17 00:00:00 2001 From: Chris Lu Date: Sun, 3 Oct 2021 19:23:34 -0700 Subject: [PATCH 090/130] rename --- weed/util/skiplist/{serde.go => skiplist_serde.go} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename weed/util/skiplist/{serde.go => skiplist_serde.go} (100%) diff --git a/weed/util/skiplist/serde.go b/weed/util/skiplist/skiplist_serde.go similarity index 100% rename from weed/util/skiplist/serde.go rename to weed/util/skiplist/skiplist_serde.go From 366f522a2d2adcb31250d3b0967947749b3ab4a2 Mon Sep 17 00:00:00 2001 From: Chris Lu Date: Mon, 4 Oct 2021 01:01:31 -0700 Subject: [PATCH 091/130] add redis3 --- weed/command/imports.go | 1 + weed/filer.toml | 5 ++ weed/filer/redis3/kv_directory_children.go | 88 ++++++++++++++++++--- weed/filer/redis3/skiplist_element_store.go | 52 ++++++++++++ weed/filer/redis3/universal_redis_store.go | 36 +++++---- weed/server/filer_server.go | 1 + weed/util/skiplist/name_batch.go | 2 +- weed/util/skiplist/name_list.go | 25 +++++- weed/util/skiplist/name_list_serde.go | 71 +++++++++++++++++ weed/util/skiplist/name_list_test.go | 4 +- weed/util/skiplist/skiplist.go | 59 ++++++++++---- 11 files changed, 296 insertions(+), 48 deletions(-) create mode 100644 weed/filer.toml create mode 100644 weed/filer/redis3/skiplist_element_store.go create mode 100644 weed/util/skiplist/name_list_serde.go diff --git a/weed/command/imports.go b/weed/command/imports.go index a2f59189f..48cda5f90 100644 --- a/weed/command/imports.go +++ b/weed/command/imports.go @@ -29,6 +29,7 @@ import ( _ "github.com/chrislusf/seaweedfs/weed/filer/postgres2" _ "github.com/chrislusf/seaweedfs/weed/filer/redis" _ "github.com/chrislusf/seaweedfs/weed/filer/redis2" + _ "github.com/chrislusf/seaweedfs/weed/filer/redis3" _ "github.com/chrislusf/seaweedfs/weed/filer/sqlite" _ "github.com/chrislusf/seaweedfs/weed/filer/tikv" ) diff --git a/weed/filer.toml b/weed/filer.toml new file mode 100644 index 000000000..a0af38d95 --- /dev/null +++ b/weed/filer.toml @@ -0,0 +1,5 @@ +[redis3] +enabled = true +address = "localhost:6379" +password = "" +database = 0 diff --git a/weed/filer/redis3/kv_directory_children.go b/weed/filer/redis3/kv_directory_children.go index f3152c970..5465a833d 100644 --- a/weed/filer/redis3/kv_directory_children.go +++ b/weed/filer/redis3/kv_directory_children.go @@ -3,11 +3,13 @@ package redis3 import ( "context" "fmt" - "github.com/chrislusf/seaweedfs/weed/util/bptree" + "github.com/chrislusf/seaweedfs/weed/glog" + "github.com/chrislusf/seaweedfs/weed/util/skiplist" "github.com/go-redis/redis/v8" - "github.com/golang/protobuf/proto" ) +const maxNameBatchSizeLimit = 5 + func insertChild(ctx context.Context, client redis.UniversalClient, key string, name string) error { data, err := client.Get(ctx, key).Result() if err != nil { @@ -15,12 +17,22 @@ func insertChild(ctx context.Context, client redis.UniversalClient, key string, return fmt.Errorf("read %s: %v", key, err) } } - rootNode := &bptree.ProtoNode{} - if err := proto.UnmarshalMerge([]byte(data), rootNode); err != nil { - return fmt.Errorf("decoding root for %s: %v", key, err) + store := newSkipListElementStore(key, client) + nameList := skiplist.LoadNameList([]byte(data), store, maxNameBatchSizeLimit) + + // println("add", key, name) + if err := nameList.WriteName(name); err != nil { + glog.Errorf("add %s %s: %v", key, name, err) + return err } - tree := rootNode.ToBpTree() - tree.Add(bptree.String(name), nil) + if !nameList.HasChanges() { + return nil + } + + if err := client.Set(ctx, key, nameList.ToBytes(), 0).Err(); err != nil { + return err + } + return nil } @@ -31,19 +43,69 @@ func removeChild(ctx context.Context, client redis.UniversalClient, key string, return fmt.Errorf("read %s: %v", key, err) } } - rootNode := &bptree.ProtoNode{} - if err := proto.UnmarshalMerge([]byte(data), rootNode); err != nil { - return fmt.Errorf("decoding root for %s: %v", key, err) + store := newSkipListElementStore(key, client) + nameList := skiplist.LoadNameList([]byte(data), store, maxNameBatchSizeLimit) + + if err := nameList.DeleteName(name); err != nil { + return err } - tree := rootNode.ToBpTree() - tree.Add(bptree.String(name), nil) + if !nameList.HasChanges() { + return nil + } + + if err := client.Set(ctx, key, nameList.ToBytes(), 0).Err(); err != nil { + return err + } + return nil } func removeChildren(ctx context.Context, client redis.UniversalClient, key string, onDeleteFn func(name string) error) error { + + data, err := client.Get(ctx, key).Result() + if err != nil { + if err != redis.Nil { + return fmt.Errorf("read %s: %v", key, err) + } + } + store := newSkipListElementStore(key, client) + nameList := skiplist.LoadNameList([]byte(data), store, maxNameBatchSizeLimit) + + if err = nameList.ListNames("", func(name string) bool { + if err := onDeleteFn(name); err != nil { + glog.Errorf("delete %s child %s: %v", key, name, err) + return false + } + return true + }); err != nil { + return err + } + + if err = nameList.RemoteAllListElement(); err != nil { + return err + } + return nil + } -func iterateChildren(ctx context.Context, client redis.UniversalClient, key string, eachFn func(name string) error) error { +func listChildren(ctx context.Context, client redis.UniversalClient, key string, startFileName string, eachFn func(name string) bool) error { + + data, err := client.Get(ctx, key).Result() + if err != nil { + if err != redis.Nil { + return fmt.Errorf("read %s: %v", key, err) + } + } + store := newSkipListElementStore(key, client) + nameList := skiplist.LoadNameList([]byte(data), store, maxNameBatchSizeLimit) + + if err = nameList.ListNames(startFileName, func(name string) bool { + return eachFn(name) + }); err != nil { + return err + } + return nil + } diff --git a/weed/filer/redis3/skiplist_element_store.go b/weed/filer/redis3/skiplist_element_store.go new file mode 100644 index 000000000..fa13d35e9 --- /dev/null +++ b/weed/filer/redis3/skiplist_element_store.go @@ -0,0 +1,52 @@ +package redis3 + +import ( + "context" + "fmt" + "github.com/chrislusf/seaweedfs/weed/glog" + "github.com/chrislusf/seaweedfs/weed/util/skiplist" + "github.com/go-redis/redis/v8" + "github.com/golang/protobuf/proto" +) + +type SkipListElementStore struct { + prefix string + client redis.UniversalClient +} + +var _ = skiplist.ListStore(&SkipListElementStore{}) + +func newSkipListElementStore(prefix string, client redis.UniversalClient) *SkipListElementStore { + return &SkipListElementStore{ + prefix: prefix, + client: client, + } +} + +func (m *SkipListElementStore) SaveElement(id int64, element *skiplist.SkipListElement) error { + key := fmt.Sprintf("%s%d", m.prefix, id) + data, err := proto.Marshal(element) + if err != nil { + glog.Errorf("marshal %s: %v", key, err) + } + return m.client.Set(context.Background(), key, data, 0).Err() +} + +func (m *SkipListElementStore) DeleteElement(id int64) error { + key := fmt.Sprintf("%s%d", m.prefix, id) + return m.client.Del(context.Background(), key).Err() +} + +func (m *SkipListElementStore) LoadElement(id int64) (*skiplist.SkipListElement, error) { + key := fmt.Sprintf("%s%d", m.prefix, id) + data, err := m.client.Get(context.Background(), key).Result() + if err != nil { + if err == redis.Nil { + return nil, nil + } + return nil, err + } + t := &skiplist.SkipListElement{} + err = proto.Unmarshal([]byte(data), t) + return t, err +} diff --git a/weed/filer/redis3/universal_redis_store.go b/weed/filer/redis3/universal_redis_store.go index 958338afe..8a89e7c48 100644 --- a/weed/filer/redis3/universal_redis_store.go +++ b/weed/filer/redis3/universal_redis_store.go @@ -115,6 +115,8 @@ func (store *UniversalRedis3Store) DeleteFolderChildren(ctx context.Context, ful if err != nil { return fmt.Errorf("DeleteFolderChildren %s in parent dir: %v", fullpath, err) } + // not efficient, but need to remove if it is a directory + store.Client.Del(ctx, genDirectoryListKey(string(path))) return nil }) @@ -127,41 +129,41 @@ func (store *UniversalRedis3Store) ListDirectoryPrefixedEntries(ctx context.Cont func (store *UniversalRedis3Store) ListDirectoryEntries(ctx context.Context, dirPath util.FullPath, startFileName string, includeStartFile bool, limit int64, eachEntryFunc filer.ListEachEntryFunc) (lastFileName string, err error) { dirListKey := genDirectoryListKey(string(dirPath)) - start := int64(0) - if startFileName != "" { - start, _ = store.Client.ZRank(ctx, dirListKey, startFileName).Result() - if !includeStartFile { - start++ - } - } - members, err := store.Client.ZRange(ctx, dirListKey, start, start+int64(limit)-1).Result() - if err != nil { - return lastFileName, fmt.Errorf("list %s : %v", dirPath, err) - } + counter := int64(0) + + err = listChildren(ctx, store.Client, dirListKey, startFileName, func(fileName string) bool { + if startFileName != "" { + if !includeStartFile && startFileName == fileName { + return true + } + } - // fetch entry meta - for _, fileName := range members { path := util.NewFullPath(string(dirPath), fileName) entry, err := store.FindEntry(ctx, path) lastFileName = fileName if err != nil { glog.V(0).Infof("list %s : %v", path, err) if err == filer_pb.ErrNotFound { - continue + return true } } else { if entry.TtlSec > 0 { if entry.Attr.Crtime.Add(time.Duration(entry.TtlSec) * time.Second).Before(time.Now()) { store.Client.Del(ctx, string(path)).Result() store.Client.ZRem(ctx, dirListKey, fileName).Result() - continue + return true } } + counter++ if !eachEntryFunc(entry) { - break + return false + } + if counter >= limit { + return false } } - } + return true + }) return lastFileName, err } diff --git a/weed/server/filer_server.go b/weed/server/filer_server.go index b886bf641..aa66b4187 100644 --- a/weed/server/filer_server.go +++ b/weed/server/filer_server.go @@ -34,6 +34,7 @@ import ( _ "github.com/chrislusf/seaweedfs/weed/filer/postgres2" _ "github.com/chrislusf/seaweedfs/weed/filer/redis" _ "github.com/chrislusf/seaweedfs/weed/filer/redis2" + _ "github.com/chrislusf/seaweedfs/weed/filer/redis3" _ "github.com/chrislusf/seaweedfs/weed/filer/sqlite" "github.com/chrislusf/seaweedfs/weed/glog" "github.com/chrislusf/seaweedfs/weed/notification" diff --git a/weed/util/skiplist/name_batch.go b/weed/util/skiplist/name_batch.go index 18427d341..71e5aeeba 100644 --- a/weed/util/skiplist/name_batch.go +++ b/weed/util/skiplist/name_batch.go @@ -35,7 +35,7 @@ func (nb *NameBatch) DeleteName(name string) { } func (nb *NameBatch) ListNames(startFrom string, visitNamesFn func(name string) bool) bool { var names []string - needFilter := startFrom == "" + needFilter := startFrom != "" for n := range nb.names { if !needFilter || strings.Compare(n, startFrom) >= 0 { names = append(names, n) diff --git a/weed/util/skiplist/name_list.go b/weed/util/skiplist/name_list.go index db328afba..4ba26665a 100644 --- a/weed/util/skiplist/name_list.go +++ b/weed/util/skiplist/name_list.go @@ -9,7 +9,7 @@ type NameList struct { batchSize int } -func NewNameList(store ListStore, batchSize int) *NameList { +func newNameList(store ListStore, batchSize int) *NameList { return &NameList{ skipList: New(store), batchSize: batchSize, @@ -59,6 +59,7 @@ There are multiple cases after finding the name for greater or equal node */ func (nl *NameList) WriteName(name string) error { + lookupKey := []byte(name) prevNode, nextNode, found, err := nl.skipList.FindGreaterOrEqual(lookupKey) if err != nil { @@ -301,3 +302,25 @@ func (nl *NameList) ListNames(startFrom string, visitNamesFn func(name string) b return nil } + +func (nl *NameList) RemoteAllListElement() error { + + t := nl.skipList + + nodeRef := t.startLevels[0] + for nodeRef != nil { + node, err := t.loadElement(nodeRef) + if err != nil { + return err + } + if node == nil { + return nil + } + if err := t.deleteElement(node); err != nil { + return err + } + nodeRef = node.Next[0] + } + return nil + +} \ No newline at end of file diff --git a/weed/util/skiplist/name_list_serde.go b/weed/util/skiplist/name_list_serde.go new file mode 100644 index 000000000..be9f06698 --- /dev/null +++ b/weed/util/skiplist/name_list_serde.go @@ -0,0 +1,71 @@ +package skiplist + +import ( + "github.com/chrislusf/seaweedfs/weed/glog" + "github.com/golang/protobuf/proto" +) + +func LoadNameList(data []byte, store ListStore, batchSize int) *NameList { + + nl := &NameList{ + skipList: New(store), + batchSize: batchSize, + } + + if len(data) == 0 { + return nl + } + + message := &SkipListProto{} + if err := proto.Unmarshal(data, message); err != nil { + glog.Errorf("loading skiplist: %v", err) + } + nl.skipList.maxNewLevel = int(message.MaxNewLevel) + nl.skipList.maxLevel = int(message.MaxLevel) + for i, ref := range message.StartLevels { + nl.skipList.startLevels[i] = &SkipListElementReference{ + ElementPointer: ref.ElementPointer, + Key: ref.Key, + } + } + for i, ref := range message.EndLevels { + nl.skipList.endLevels[i] = &SkipListElementReference{ + ElementPointer: ref.ElementPointer, + Key: ref.Key, + } + } + return nl +} + +func (nl *NameList) HasChanges() bool { + return nl.skipList.hasChanges +} + +func (nl *NameList) ToBytes() []byte { + message := &SkipListProto{} + message.MaxNewLevel = int32(nl.skipList.maxNewLevel) + message.MaxLevel = int32(nl.skipList.maxLevel) + for _, ref := range nl.skipList.startLevels { + if ref == nil { + break + } + message.StartLevels = append(message.StartLevels, &SkipListElementReference{ + ElementPointer: ref.ElementPointer, + Key: ref.Key, + }) + } + for _, ref := range nl.skipList.endLevels { + if ref == nil { + break + } + message.EndLevels = append(message.EndLevels, &SkipListElementReference{ + ElementPointer: ref.ElementPointer, + Key: ref.Key, + }) + } + data, err := proto.Marshal(message) + if err != nil { + glog.Errorf("marshal skiplist: %v", err) + } + return data +} \ No newline at end of file diff --git a/weed/util/skiplist/name_list_test.go b/weed/util/skiplist/name_list_test.go index 811a101f2..b3a686553 100644 --- a/weed/util/skiplist/name_list_test.go +++ b/weed/util/skiplist/name_list_test.go @@ -15,7 +15,7 @@ func String(x int) string { } func TestNameList(t *testing.T) { - list := NewNameList(memStore, 7) + list := newNameList(memStore, 7) for i := 0; i < maxNameCount; i++ { list.WriteName(String(i)) @@ -51,7 +51,7 @@ func TestNameList(t *testing.T) { } // randomized deletion - list = NewNameList(memStore, 7) + list = newNameList(memStore, 7) // Delete elements at random positions in the list. rList := rand.Perm(maxN) for _, i := range rList { diff --git a/weed/util/skiplist/skiplist.go b/weed/util/skiplist/skiplist.go index b48a05b4a..52e6c606a 100644 --- a/weed/util/skiplist/skiplist.go +++ b/weed/util/skiplist/skiplist.go @@ -22,6 +22,7 @@ type SkipList struct { maxNewLevel int maxLevel int listStore ListStore + hasChanges bool // elementCount int } @@ -93,6 +94,9 @@ func (t *SkipList) findExtended(key []byte, findGreaterOrEqual bool) (prevElemen if err != nil { return } + if currentNode == nil { + return + } // In case, that our first element is already greater-or-equal! if findGreaterOrEqual && compareElement(currentNode, key) > 0 { @@ -115,6 +119,9 @@ func (t *SkipList) findExtended(key []byte, findGreaterOrEqual bool) (prevElemen if err != nil { return } + if currentNode == nil { + return + } } else { if index > 0 { @@ -126,6 +133,9 @@ func (t *SkipList) findExtended(key []byte, findGreaterOrEqual bool) (prevElemen if err != nil { return } + if currentNodeNext == nil { + return + } foundElem = currentNodeNext ok = true return @@ -216,9 +226,11 @@ func (t *SkipList) Delete(key []byte) (err error) { if err != nil { return err } - nextNextNode.Prev = currentNode.Reference() - if err = t.saveElement(nextNextNode); err != nil { - return err + if nextNextNode != nil { + nextNextNode.Prev = currentNode.Reference() + if err = t.saveElement(nextNextNode); err != nil { + return err + } } } // t.elementCount-- @@ -230,6 +242,7 @@ func (t *SkipList) Delete(key []byte) (err error) { // Link from start needs readjustments. startNextKey := t.startLevels[index].Key if compareElement(nextNode, startNextKey) == 0 { + t.hasChanges = true t.startLevels[index] = nextNode.Next[index] // This was our currently highest node! if t.startLevels[index] == nil { @@ -240,6 +253,7 @@ func (t *SkipList) Delete(key []byte) (err error) { // Link from end needs readjustments. if nextNode.Next[index] == nil { t.endLevels[index] = currentNode.Reference() + t.hasChanges = true } nextNode.Next[index] = nil } @@ -260,7 +274,7 @@ func (t *SkipList) Delete(key []byte) (err error) { // Insert inserts the given ListElement into the skiplist. // Insert runs in approx. O(log(n)) -func (t *SkipList) Insert(key, value []byte) (err error){ +func (t *SkipList) Insert(key, value []byte) (err error) { if t == nil || key == nil { return @@ -272,6 +286,7 @@ func (t *SkipList) Insert(key, value []byte) (err error){ if level > t.maxLevel { level = t.maxLevel + 1 t.maxLevel = level + t.hasChanges = true } elem := &SkipListElement{ @@ -326,9 +341,11 @@ func (t *SkipList) Insert(key, value []byte) (err error){ if nextNode, err = t.loadElement(nextNodeRef); err != nil { return } - nextNode.Prev = elem.Reference() - if err = t.saveElement(nextNode); err != nil { - return + if nextNode != nil { + nextNode.Prev = elem.Reference() + if err = t.saveElement(nextNode); err != nil { + return + } } } } @@ -343,6 +360,9 @@ func (t *SkipList) Insert(key, value []byte) (err error){ } } currentNode = nextNode + if currentNode == nil { + return + } } else { // Go down index-- @@ -366,18 +386,22 @@ func (t *SkipList) Insert(key, value []byte) (err error){ if err != nil { return err } - startLevelElement.Prev = elem.Reference() - if err = t.saveElement(startLevelElement); err != nil { - return err + if startLevelElement != nil { + startLevelElement.Prev = elem.Reference() + if err = t.saveElement(startLevelElement); err != nil { + return err + } } } elem.Next[i] = t.startLevels[i] t.startLevels[i] = elem.Reference() + t.hasChanges = true } // link the endLevels to this element! if elem.Next[i] == nil { t.endLevels[i] = elem.Reference() + t.hasChanges = true } didSomething = true @@ -392,20 +416,24 @@ func (t *SkipList) Insert(key, value []byte) (err error){ if err != nil { return err } - endLevelElement.Next[i] = elem.Reference() - if err = t.saveElement(endLevelElement); err != nil { - return err + if endLevelElement != nil { + endLevelElement.Next[i] = elem.Reference() + if err = t.saveElement(endLevelElement); err != nil { + return err + } } } if i == 0 { elem.Prev = t.endLevels[i] } t.endLevels[i] = elem.Reference() + t.hasChanges = true } // Link the startLevels to this element! if t.startLevels[i] == nil || bytes.Compare(t.startLevels[i].Key, key) > 0 { t.startLevels[i] = elem.Reference() + t.hasChanges = true } didSomething = true @@ -486,6 +514,9 @@ func (t *SkipList) println() { for nodeRef != nil { print(fmt.Sprintf("%v: ", string(nodeRef.Key))) node, _ := t.loadElement(nodeRef) + if node == nil { + break + } for i := 0; i <= int(node.Level); i++ { l := node.Next[i] @@ -510,8 +541,8 @@ func (t *SkipList) println() { } } - println() nodeRef = node.Next[0] + println() } print("end --> ") From 2b9aab344228cfbb0ba3da5f410eb76bd41d18ea Mon Sep 17 00:00:00 2001 From: Chris Lu Date: Mon, 4 Oct 2021 01:03:40 -0700 Subject: [PATCH 092/130] use 1000 per batch --- weed/filer/redis3/kv_directory_children.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/weed/filer/redis3/kv_directory_children.go b/weed/filer/redis3/kv_directory_children.go index 5465a833d..16d921d03 100644 --- a/weed/filer/redis3/kv_directory_children.go +++ b/weed/filer/redis3/kv_directory_children.go @@ -8,7 +8,7 @@ import ( "github.com/go-redis/redis/v8" ) -const maxNameBatchSizeLimit = 5 +const maxNameBatchSizeLimit = 1000 func insertChild(ctx context.Context, client redis.UniversalClient, key string, name string) error { data, err := client.Get(ctx, key).Result() From 04662126bb9374b355d4bf85ae8a04548b2e9283 Mon Sep 17 00:00:00 2001 From: Chris Lu Date: Mon, 4 Oct 2021 01:04:27 -0700 Subject: [PATCH 093/130] add redis3 --- weed/command/scaffold/filer.toml | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/weed/command/scaffold/filer.toml b/weed/command/scaffold/filer.toml index caf9d173d..aeb8a5b67 100644 --- a/weed/command/scaffold/filer.toml +++ b/weed/command/scaffold/filer.toml @@ -185,6 +185,28 @@ routeByLatency = false # This changes the data layout. Only add new directories. Removing/Updating will cause data loss. superLargeDirectories = [] +[redis3] # beta +enabled = false +address = "localhost:6379" +password = "" +database = 0 + +[redis_cluster3] # beta +enabled = false +addresses = [ + "localhost:30001", + "localhost:30002", + "localhost:30003", + "localhost:30004", + "localhost:30005", + "localhost:30006", +] +password = "" +# allows reads from slave servers or the master, but all writes still go to the master +readOnly = false +# automatically use the closest Redis server for reads +routeByLatency = false + [etcd] enabled = false servers = "localhost:2379" From 280ab7f95cdbbaf2fee4a49d10d944e2865829fc Mon Sep 17 00:00:00 2001 From: Chris Lu Date: Mon, 4 Oct 2021 02:30:24 -0700 Subject: [PATCH 094/130] add test --- weed/util/skiplist/skiplist_test.go | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/weed/util/skiplist/skiplist_test.go b/weed/util/skiplist/skiplist_test.go index 115656cd9..a35bef6f3 100644 --- a/weed/util/skiplist/skiplist_test.go +++ b/weed/util/skiplist/skiplist_test.go @@ -16,6 +16,21 @@ var ( memStore = newMemStore() ) +func TestReverseInsert(t *testing.T) { + list := NewSeed(100, memStore) + + list.Insert([]byte("zzz"), []byte("zzz")) + list.Delete([]byte("zzz")) + + list.Insert([]byte("aaa"), []byte("aaa")) + + if list.IsEmpty() { + t.Fail() + } + +} + + func TestInsertAndFind(t *testing.T) { k0 := []byte("0") From 513fed323a86f30996adc8e66f6cf6641b40e77a Mon Sep 17 00:00:00 2001 From: Chris Lu Date: Mon, 4 Oct 2021 02:30:44 -0700 Subject: [PATCH 095/130] SkipListElementReference can be an empty object --- weed/util/skiplist/skiplist_serde.go | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/weed/util/skiplist/skiplist_serde.go b/weed/util/skiplist/skiplist_serde.go index 5b7089e80..e528b8a3d 100644 --- a/weed/util/skiplist/skiplist_serde.go +++ b/weed/util/skiplist/skiplist_serde.go @@ -34,8 +34,18 @@ func (t *SkipList) deleteElement(element *SkipListElement) error { } func (t *SkipList) loadElement(ref *SkipListElementReference) (*SkipListElement, error) { - if ref == nil { + if ref.IsNil() { return nil, nil } return t.listStore.LoadElement(ref.ElementPointer) } + +func (ref *SkipListElementReference) IsNil() bool { + if ref == nil { + return true + } + if len(ref.Key) == 0 { + return true + } + return false +} \ No newline at end of file From 947add39e6b2bca54ddc04fd33be28868cae8a0f Mon Sep 17 00:00:00 2001 From: Chris Lu Date: Mon, 4 Oct 2021 02:31:38 -0700 Subject: [PATCH 096/130] clean up *SkipListElementReference loaded from Redis --- weed/filer/redis3/skiplist_element_store.go | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/weed/filer/redis3/skiplist_element_store.go b/weed/filer/redis3/skiplist_element_store.go index fa13d35e9..66a5408d6 100644 --- a/weed/filer/redis3/skiplist_element_store.go +++ b/weed/filer/redis3/skiplist_element_store.go @@ -48,5 +48,15 @@ func (m *SkipListElementStore) LoadElement(id int64) (*skiplist.SkipListElement, } t := &skiplist.SkipListElement{} err = proto.Unmarshal([]byte(data), t) + if err == nil { + for i:=0;i Date: Mon, 4 Oct 2021 02:51:26 -0700 Subject: [PATCH 097/130] add back logic to check master peers fix https://github.com/chrislusf/seaweedfs/issues/2352 --- weed/command/server.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/weed/command/server.go b/weed/command/server.go index 9f402c9f8..7ffabb321 100644 --- a/weed/command/server.go +++ b/weed/command/server.go @@ -168,6 +168,12 @@ func runServer(cmd *Command, args []string) bool { *isStartingFiler = true } + if *isStartingMasterServer { + _, peerList := checkPeers(*serverIp, *masterOptions.port, *masterOptions.portGrpc, *masterOptions.peers) + peers := strings.Join(pb.ToAddressStrings(peerList), ",") + masterOptions.peers = &peers + } + // ip address masterOptions.ip = serverIp masterOptions.ipBind = serverBindIp From 6a030547a2efa3efe38491aa8fc06f3b7d3acc75 Mon Sep 17 00:00:00 2001 From: Chris Lu Date: Mon, 4 Oct 2021 03:27:10 -0700 Subject: [PATCH 098/130] server: remove peer check if not starting master more fix https://github.com/chrislusf/seaweedfs/issues/2352 --- weed/command/server.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/weed/command/server.go b/weed/command/server.go index 7ffabb321..a80ef6796 100644 --- a/weed/command/server.go +++ b/weed/command/server.go @@ -177,13 +177,12 @@ func runServer(cmd *Command, args []string) bool { // ip address masterOptions.ip = serverIp masterOptions.ipBind = serverBindIp - _, masters := checkPeers(*masterOptions.ip, *masterOptions.port, *masterOptions.portGrpc, *masterOptions.peers) - filerOptions.masters = masters + filerOptions.masters = pb.ServerAddresses(*masterOptions.peers).ToAddresses() filerOptions.ip = serverIp filerOptions.bindIp = serverBindIp serverOptions.v.ip = serverIp serverOptions.v.bindIp = serverBindIp - serverOptions.v.masters = masters + serverOptions.v.masters = pb.ServerAddresses(*masterOptions.peers).ToAddresses() serverOptions.v.idleConnectionTimeout = serverTimeout serverOptions.v.dataCenter = serverDataCenter serverOptions.v.rack = serverRack From 4ed2994555a441eafab1d24cd03c9de2a89de5a0 Mon Sep 17 00:00:00 2001 From: Chris Lu Date: Mon, 4 Oct 2021 16:02:56 -0700 Subject: [PATCH 099/130] use tsMemory to determine whether read from disk or memory remove lastFlushTime --- weed/util/log_buffer/log_buffer.go | 39 +++++++++++++++++------------- 1 file changed, 22 insertions(+), 17 deletions(-) diff --git a/weed/util/log_buffer/log_buffer.go b/weed/util/log_buffer/log_buffer.go index 3dcea147f..ed56a710e 100644 --- a/weed/util/log_buffer/log_buffer.go +++ b/weed/util/log_buffer/log_buffer.go @@ -29,7 +29,6 @@ type LogBuffer struct { pos int startTime time.Time stopTime time.Time - lastFlushTime time.Time sizeBuf []byte flushInterval time.Duration flushFn func(startTime, stopTime time.Time, buf []byte) @@ -133,8 +132,6 @@ func (m *LogBuffer) loopFlush() { // glog.V(4).Infof("%s flush [%v, %v] size %d", m.name, d.startTime, d.stopTime, len(d.data.Bytes())) m.flushFn(d.startTime, d.stopTime, d.data.Bytes()) d.releaseMemory() - // local logbuffer is different from aggregate logbuffer here - m.lastFlushTime = d.stopTime } } } @@ -165,9 +162,6 @@ func (m *LogBuffer) copyToFlush() *dataToFlush { data: copiedBytes(m.buf[:m.pos]), } // glog.V(4).Infof("%s flushing [0,%d) with %d entries [%v, %v]", m.name, m.pos, len(m.idx), m.startTime, m.stopTime) - } else { - // glog.V(4).Infof("%s removed from memory [0,%d) with %d entries [%v, %v]", m.name, m.pos, len(m.idx), m.startTime, m.stopTime) - m.lastFlushTime = m.stopTime } m.buf = m.prevBuffers.SealBuffer(m.startTime, m.stopTime, m.buf, m.pos) m.startTime = time.Unix(0, 0) @@ -188,20 +182,31 @@ func (m *LogBuffer) ReadFromBuffer(lastReadTime time.Time) (bufferCopy *bytes.Bu m.RLock() defer m.RUnlock() - if !m.lastFlushTime.IsZero() && m.lastFlushTime.After(lastReadTime) { - if time.Now().Sub(m.lastFlushTime) < m.flushInterval*2 { - diff := m.lastFlushTime.Sub(lastReadTime) - glog.V(4).Infof("lastFlush:%v lastRead:%v diff:%v", m.lastFlushTime, lastReadTime, diff) - return nil, ResumeFromDiskError + // Read from disk and memory + // 1. read from disk, last time is = td + // 2. in memory, the earliest time = tm + // if tm <= td, case 2.1 + // read from memory + // if tm is empty, case 2.2 + // read from memory + // if td < tm, case 2.3 + // read from disk again + var tsMemory time.Time + if !m.startTime.IsZero() { + tsMemory = m.startTime + } + for _, prevBuf := range m.prevBuffers.buffers { + if !prevBuf.startTime.IsZero() && prevBuf.startTime.Before(tsMemory) { + tsMemory = prevBuf.startTime } } + if tsMemory.IsZero() { // case 2.2 + return nil, nil + } else if lastReadTime.Before(tsMemory) { // case 2.3 + return nil, ResumeFromDiskError + } - /* - fmt.Printf("read buffer %p: %v last stop time: [%v,%v], pos %d, entries:%d, prevBufs:%d\n", m, lastReadTime, m.startTime, m.stopTime, m.pos, len(m.idx), len(m.prevBuffers.buffers)) - for i, prevBuf := range m.prevBuffers.buffers { - fmt.Printf(" prev %d : %s\n", i, prevBuf.String()) - } - */ + // the following is case 2.1 if lastReadTime.Equal(m.stopTime) { return nil, nil From 8a663060640a499f8a37a7f60e3a7c5a2f95fe05 Mon Sep 17 00:00:00 2001 From: Chris Lu Date: Mon, 4 Oct 2021 23:32:07 -0700 Subject: [PATCH 100/130] calculate disk usage in case of race condition related to https://github.com/chrislusf/seaweedfs/issues/2357 --- weed/topology/data_node.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/weed/topology/data_node.go b/weed/topology/data_node.go index 9f868681e..6bdbd965f 100644 --- a/weed/topology/data_node.go +++ b/weed/topology/data_node.go @@ -110,6 +110,9 @@ func (dn *DataNode) DeltaUpdateVolumes(newVolumes, deletedVolumes []storage.Volu for _, v := range deletedVolumes { disk := dn.getOrCreateDisk(v.DiskType) + if _, found := disk.volumes[v.Id]; !found { + continue + } delete(disk.volumes, v.Id) deltaDiskUsages := newDiskUsages() From 96119eab00e13ff056f3058b9032d12c3758cb35 Mon Sep 17 00:00:00 2001 From: Chris Lu Date: Tue, 5 Oct 2021 00:40:04 -0700 Subject: [PATCH 101/130] refactor --- weed/server/master_grpc_server_volume.go | 9 +++++++-- weed/server/master_server_handlers.go | 4 +++- weed/server/master_server_handlers_admin.go | 7 ------- weed/topology/volume_layout.go | 6 ++++++ 4 files changed, 16 insertions(+), 10 deletions(-) diff --git a/weed/server/master_grpc_server_volume.go b/weed/server/master_grpc_server_volume.go index 4048225f3..95fdc3fd1 100644 --- a/weed/server/master_grpc_server_volume.go +++ b/weed/server/master_grpc_server_volume.go @@ -42,8 +42,11 @@ func (ms *MasterServer) ProcessGrowRequest() { return !found }) + option := req.Option + vl := ms.Topo.GetVolumeLayout(option.Collection, option.ReplicaPlacement, option.Ttl, option.DiskType) + // not atomic but it's okay - if !found && ms.shouldVolumeGrow(req.Option) { + if !found && vl.ShouldGrowVolumes(option) { filter.Store(req, nil) // we have lock called inside vg go func() { @@ -130,7 +133,9 @@ func (ms *MasterServer) Assign(ctx context.Context, req *master_pb.AssignRequest MemoryMapMaxSizeMb: req.MemoryMapMaxSizeMb, } - if ms.shouldVolumeGrow(option) { + vl := ms.Topo.GetVolumeLayout(option.Collection, option.ReplicaPlacement, option.Ttl, option.DiskType) + + if vl.ShouldGrowVolumes(option) { if ms.Topo.AvailableSpaceFor(option) <= 0 { return nil, fmt.Errorf("no free volumes left for " + option.String()) } diff --git a/weed/server/master_server_handlers.go b/weed/server/master_server_handlers.go index 36c4239fb..8187ca21f 100644 --- a/weed/server/master_server_handlers.go +++ b/weed/server/master_server_handlers.go @@ -113,7 +113,9 @@ func (ms *MasterServer) dirAssignHandler(w http.ResponseWriter, r *http.Request) return } - if ms.shouldVolumeGrow(option) { + vl := ms.Topo.GetVolumeLayout(option.Collection, option.ReplicaPlacement, option.Ttl, option.DiskType) + + if vl.ShouldGrowVolumes(option) { glog.V(0).Infof("dirAssign volume growth %v from %v", option.String(), r.RemoteAddr) if ms.Topo.AvailableSpaceFor(option) <= 0 { writeJsonQuiet(w, r, http.StatusNotFound, operation.AssignResult{Error: "No free volumes left for " + option.String()}) diff --git a/weed/server/master_server_handlers_admin.go b/weed/server/master_server_handlers_admin.go index 549ea86dc..41a2b570b 100644 --- a/weed/server/master_server_handlers_admin.go +++ b/weed/server/master_server_handlers_admin.go @@ -132,13 +132,6 @@ func (ms *MasterServer) submitFromMasterServerHandler(w http.ResponseWriter, r * } } -func (ms *MasterServer) shouldVolumeGrow(option *topology.VolumeGrowOption) bool { - vl := ms.Topo.GetVolumeLayout(option.Collection, option.ReplicaPlacement, option.Ttl, option.DiskType) - active, high := vl.GetActiveVolumeCount(option) - //glog.V(0).Infof("active volume: %d, high usage volume: %d\n", active, high) - return active <= high -} - func (ms *MasterServer) getVolumeGrowOption(r *http.Request) (*topology.VolumeGrowOption, error) { replicationString := r.FormValue("replication") if replicationString == "" { diff --git a/weed/topology/volume_layout.go b/weed/topology/volume_layout.go index db05102fe..3e487fb2f 100644 --- a/weed/topology/volume_layout.go +++ b/weed/topology/volume_layout.go @@ -310,6 +310,12 @@ func (vl *VolumeLayout) PickForWrite(count uint64, option *VolumeGrowOption) (*n return &vid, count, locationList, nil } +func (vl *VolumeLayout) ShouldGrowVolumes(option *VolumeGrowOption) bool { + active, crowded := vl.GetActiveVolumeCount(option) + //glog.V(0).Infof("active volume: %d, high usage volume: %d\n", active, high) + return active <= crowded +} + func (vl *VolumeLayout) GetActiveVolumeCount(option *VolumeGrowOption) (active, crowded int) { vl.accessLock.RLock() defer vl.accessLock.RUnlock() From 332d49432dfc5328b803ddbe540704d303500cfd Mon Sep 17 00:00:00 2001 From: Chris Lu Date: Tue, 5 Oct 2021 01:58:30 -0700 Subject: [PATCH 102/130] reduce concurrent volume grow requests --- weed/server/master_grpc_server_volume.go | 4 +++- weed/server/master_server_handlers.go | 3 ++- weed/topology/volume_layout.go | 17 +++++++++++++++++ 3 files changed, 22 insertions(+), 2 deletions(-) diff --git a/weed/server/master_grpc_server_volume.go b/weed/server/master_grpc_server_volume.go index 95fdc3fd1..551e59990 100644 --- a/weed/server/master_grpc_server_volume.go +++ b/weed/server/master_grpc_server_volume.go @@ -54,6 +54,7 @@ func (ms *MasterServer) ProcessGrowRequest() { start := time.Now() _, err := ms.vg.AutomaticGrowByType(req.Option, ms.grpcDialOption, ms.Topo, req.Count) glog.V(1).Infoln("finished automatic volume grow, cost ", time.Now().Sub(start)) + vl.DoneGrowRequest() if req.ErrCh != nil { req.ErrCh <- err @@ -135,10 +136,11 @@ func (ms *MasterServer) Assign(ctx context.Context, req *master_pb.AssignRequest vl := ms.Topo.GetVolumeLayout(option.Collection, option.ReplicaPlacement, option.Ttl, option.DiskType) - if vl.ShouldGrowVolumes(option) { + if !vl.HasGrowRequest() && vl.ShouldGrowVolumes(option) { if ms.Topo.AvailableSpaceFor(option) <= 0 { return nil, fmt.Errorf("no free volumes left for " + option.String()) } + vl.AddGrowRequest() ms.vgCh <- &topology.VolumeGrowRequest{ Option: option, Count: int(req.WritableVolumeCount), diff --git a/weed/server/master_server_handlers.go b/weed/server/master_server_handlers.go index 8187ca21f..50a3f12f6 100644 --- a/weed/server/master_server_handlers.go +++ b/weed/server/master_server_handlers.go @@ -115,13 +115,14 @@ func (ms *MasterServer) dirAssignHandler(w http.ResponseWriter, r *http.Request) vl := ms.Topo.GetVolumeLayout(option.Collection, option.ReplicaPlacement, option.Ttl, option.DiskType) - if vl.ShouldGrowVolumes(option) { + if !vl.HasGrowRequest() && vl.ShouldGrowVolumes(option) { glog.V(0).Infof("dirAssign volume growth %v from %v", option.String(), r.RemoteAddr) if ms.Topo.AvailableSpaceFor(option) <= 0 { writeJsonQuiet(w, r, http.StatusNotFound, operation.AssignResult{Error: "No free volumes left for " + option.String()}) return } errCh := make(chan error, 1) + vl.AddGrowRequest() ms.vgCh <- &topology.VolumeGrowRequest{ Option: option, Count: writableVolumeCount, diff --git a/weed/topology/volume_layout.go b/weed/topology/volume_layout.go index 3e487fb2f..dfc3957f8 100644 --- a/weed/topology/volume_layout.go +++ b/weed/topology/volume_layout.go @@ -114,6 +114,8 @@ type VolumeLayout struct { volumeSizeLimit uint64 replicationAsMin bool accessLock sync.RWMutex + growRequestCount int + growRequestTime time.Time } type VolumeLayoutStats struct { @@ -310,6 +312,21 @@ func (vl *VolumeLayout) PickForWrite(count uint64, option *VolumeGrowOption) (*n return &vid, count, locationList, nil } +func (vl *VolumeLayout) HasGrowRequest() bool { + if vl.growRequestCount > 0 && vl.growRequestTime.Add(time.Minute).After(time.Now()) { + return true + } + return false +} +func (vl *VolumeLayout) AddGrowRequest() { + vl.growRequestTime = time.Now() + vl.growRequestCount++ +} +func (vl *VolumeLayout) DoneGrowRequest() { + vl.growRequestTime = time.Unix(0,0) + vl.growRequestCount = 0 +} + func (vl *VolumeLayout) ShouldGrowVolumes(option *VolumeGrowOption) bool { active, crowded := vl.GetActiveVolumeCount(option) //glog.V(0).Infof("active volume: %d, high usage volume: %d\n", active, high) From f0d1e7bd05e962e1f432cb1699229de8d194714a Mon Sep 17 00:00:00 2001 From: Chris Lu Date: Tue, 5 Oct 2021 02:31:44 -0700 Subject: [PATCH 103/130] skip ec volumes when loading normal volumes --- weed/storage/disk_location.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/weed/storage/disk_location.go b/weed/storage/disk_location.go index f16963356..c6fceb2c2 100644 --- a/weed/storage/disk_location.go +++ b/weed/storage/disk_location.go @@ -95,6 +95,11 @@ func (l *DiskLocation) loadExistingVolume(fileInfo os.FileInfo, needleMapKind Ne return false } + // skip ec volumes + if util.FileExists(l.Directory + "/" + volumeName + ".ecx") { + return false + } + // check for incomplete volume noteFile := l.Directory + "/" + volumeName + ".note" if util.FileExists(noteFile) { From 893f0587b17c0e30e8e3e7eb4b3c486546f41651 Mon Sep 17 00:00:00 2001 From: Chris Lu Date: Wed, 6 Oct 2021 00:03:54 -0700 Subject: [PATCH 104/130] redis3 adds distributed locking --- go.mod | 4 +- go.sum | 15 ++++ weed/filer/redis3/kv_directory_children.go | 42 +++++++++-- .../redis3/kv_directory_children_test.go | 75 +++++++++++++++++++ weed/filer/redis3/redis_cluster_store.go | 3 + weed/filer/redis3/redis_store.go | 3 + weed/filer/redis3/universal_redis_store.go | 12 +-- weed/util/skiplist/name_list.go | 2 +- 8 files changed, 142 insertions(+), 14 deletions(-) create mode 100644 weed/filer/redis3/kv_directory_children_test.go diff --git a/go.mod b/go.mod index 3b0582c52..a0907ac00 100644 --- a/go.mod +++ b/go.mod @@ -58,7 +58,7 @@ require ( github.com/grpc-ecosystem/go-grpc-middleware v1.1.0 github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed // indirect github.com/hashicorp/errwrap v1.0.0 // indirect - github.com/hashicorp/go-multierror v1.0.0 // indirect + github.com/hashicorp/go-multierror v1.1.0 // indirect github.com/hashicorp/go-uuid v1.0.2 // indirect github.com/hashicorp/hcl v1.0.0 // indirect github.com/jcmturner/gofork v1.0.0 // indirect @@ -165,6 +165,7 @@ require ( require ( github.com/coreos/etcd v3.3.10+incompatible // indirect + github.com/go-redsync/redsync/v4 v4.4.1 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/jcmturner/aescts/v2 v2.0.0 // indirect github.com/jcmturner/dnsutils/v2 v2.0.0 // indirect @@ -176,6 +177,7 @@ require ( github.com/miekg/dns v1.1.25-0.20191211073109-8ebf2e419df7 // indirect github.com/opentracing/opentracing-go v1.2.0 // indirect github.com/spf13/pflag v1.0.5 // indirect + github.com/stvp/tempredis v0.0.0-20181119212430-b82af8480203 // indirect go.etcd.io/etcd/api/v3 v3.5.0 // indirect go.etcd.io/etcd/client/pkg/v3 v3.5.0 // indirect go.etcd.io/etcd/client/v3 v3.5.0 // indirect diff --git a/go.sum b/go.sum index abd342f0c..232d788b3 100644 --- a/go.sum +++ b/go.sum @@ -342,8 +342,13 @@ github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+ github.com/go-playground/validator/v10 v10.2.0 h1:KgJ0snyC2R9VXYN2rneOtQcw5aHQB1Vv0sFl1UcHBOY= github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI= github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4= +github.com/go-redis/redis v6.15.9+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA= +github.com/go-redis/redis/v7 v7.4.0/go.mod h1:JDNMw23GTyLNC4GZu9njt15ctBQVn7xjRfnwdHj/Dcg= +github.com/go-redis/redis/v8 v8.1.1/go.mod h1:ysgGY09J/QeDYbu3HikWEIPCwaeOkuNoTgKayTEaEOw= github.com/go-redis/redis/v8 v8.4.4 h1:fGqgxCTR1sydaKI00oQf3OmkU/DIe/I/fYXvGklCIuc= github.com/go-redis/redis/v8 v8.4.4/go.mod h1:nA0bQuF0i5JFx4Ta9RZxGKXFrQ8cRWntra97f0196iY= +github.com/go-redsync/redsync/v4 v4.4.1 h1:Z0AaOpoLvzfZwLK+3uCDHcTxOXck2juzumu1EPJwCUI= +github.com/go-redsync/redsync/v4 v4.4.1/go.mod h1:QBOJAs1k8O6Eyrre4a++pxQgHe5eQ+HF56KuTVv+8Bs= github.com/go-sql-driver/mysql v1.3.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= @@ -441,6 +446,7 @@ github.com/golang/snappy v0.0.2-0.20190904063534-ff6b7dc882cf/go.mod h1:/XxbfmMg github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/gomodule/redigo v1.8.2/go.mod h1:P9dn9mFrCBvWhGE1wpxx6fgq7BAeLBk+UUUzlpkBYO0= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0 h1:0udJVsspx3VBr5FwtLhQQtuAsVc79tTq0ocGIPAU6qo= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= @@ -542,6 +548,8 @@ github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjh github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= github.com/hashicorp/go-multierror v1.0.0 h1:iVjPR7a6H0tWELX5NxNe7bYopibicUzc7uPribsnS6o= github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= +github.com/hashicorp/go-multierror v1.1.0 h1:B9UzwGQJehnUY1yNrnwREHc3fGbC2xefo8g4TbElacI= +github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA= github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= @@ -751,12 +759,16 @@ github.com/olivere/elastic/v7 v7.0.19 h1:w4F6JpqOISadhYf/n0NR1cNj73xHqh4pzPwD1Gk github.com/olivere/elastic/v7 v7.0.19/go.mod h1:4Jqt5xvjqpjCqgnTcHwl3j8TLs8mvoOK8NYgo/qEOu4= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= +github.com/onsi/ginkgo v1.14.1/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= github.com/onsi/ginkgo v1.14.2 h1:8mVmC9kjFFmA8H4pKMUhcblgifdkOIXPvbhN1T36q1M= github.com/onsi/ginkgo v1.14.2/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= +github.com/onsi/gomega v1.10.2/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= github.com/onsi/gomega v1.10.4 h1:NiTx7EEvBzu9sFOD1zORteLSt3o8gnlvZZwSE9TnY9U= github.com/onsi/gomega v1.10.4/go.mod h1:g/HbgYopi++010VEqkFgJHKC09uJiW9UkXvMUuKHUCQ= github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk= @@ -959,6 +971,8 @@ github.com/stretchr/testify v1.6.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stvp/tempredis v0.0.0-20181119212430-b82af8480203 h1:QVqDTf3h2WHt08YuiTGPZLls0Wq99X9bWd0Q5ZSBesM= +github.com/stvp/tempredis v0.0.0-20181119212430-b82af8480203/go.mod h1:oqN97ltKNihBbwlX8dLpwxCl3+HnXKV/R0e+sRLd9C8= github.com/swaggo/files v0.0.0-20190704085106-630677cd5c14/go.mod h1:gxQT6pBGRuIGunNf/+tSOB5OHvguWi8Tbt82WOkf35E= github.com/swaggo/gin-swagger v1.2.0/go.mod h1:qlH2+W7zXGZkczuL+r2nEBR2JTT+/lX05Nn6vPhc7OI= github.com/swaggo/http-swagger v0.0.0-20200308142732-58ac5e232fba/go.mod h1:O1lAbCgAAX/KZ80LM/OXwtWFI/5TvZlwxSg8Cq08PV0= @@ -1073,6 +1087,7 @@ go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= go.opencensus.io v0.23.0 h1:gqCw0LfLxScz8irSi8exQc7fyQ0fKQU/qnC/X8+V/1M= go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= +go.opentelemetry.io/otel v0.11.0/go.mod h1:G8UCk+KooF2HLkgo8RHX9epABH/aRGYET7gQOqBVdB0= go.opentelemetry.io/otel v0.15.0 h1:CZFy2lPhxd4HlhZnYK8gRyDotksO3Ip9rBweY1vVYJw= go.opentelemetry.io/otel v0.15.0/go.mod h1:e4GKElweB8W2gWUqbghw0B8t5MCTccc9212eNHnOHwA= go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= diff --git a/weed/filer/redis3/kv_directory_children.go b/weed/filer/redis3/kv_directory_children.go index 16d921d03..797e7797c 100644 --- a/weed/filer/redis3/kv_directory_children.go +++ b/weed/filer/redis3/kv_directory_children.go @@ -10,7 +10,18 @@ import ( const maxNameBatchSizeLimit = 1000 -func insertChild(ctx context.Context, client redis.UniversalClient, key string, name string) error { +func insertChild(ctx context.Context, redisStore *UniversalRedis3Store, key string, name string) error { + + // lock and unlock + mutex := redisStore.redsync.NewMutex(key+"lock") + if err := mutex.Lock(); err != nil { + return fmt.Errorf("lock %s: %v", key, err) + } + defer func() { + mutex.Unlock() + }() + + client := redisStore.Client data, err := client.Get(ctx, key).Result() if err != nil { if err != redis.Nil { @@ -20,11 +31,11 @@ func insertChild(ctx context.Context, client redis.UniversalClient, key string, store := newSkipListElementStore(key, client) nameList := skiplist.LoadNameList([]byte(data), store, maxNameBatchSizeLimit) - // println("add", key, name) if err := nameList.WriteName(name); err != nil { glog.Errorf("add %s %s: %v", key, name, err) return err } + if !nameList.HasChanges() { return nil } @@ -36,7 +47,16 @@ func insertChild(ctx context.Context, client redis.UniversalClient, key string, return nil } -func removeChild(ctx context.Context, client redis.UniversalClient, key string, name string) error { +func removeChild(ctx context.Context, redisStore *UniversalRedis3Store, key string, name string) error { + + // lock and unlock + mutex := redisStore.redsync.NewMutex(key+"lock") + if err := mutex.Lock(); err != nil { + return fmt.Errorf("lock %s: %v", key, err) + } + defer mutex.Unlock() + + client := redisStore.Client data, err := client.Get(ctx, key).Result() if err != nil { if err != redis.Nil { @@ -60,8 +80,16 @@ func removeChild(ctx context.Context, client redis.UniversalClient, key string, return nil } -func removeChildren(ctx context.Context, client redis.UniversalClient, key string, onDeleteFn func(name string) error) error { +func removeChildren(ctx context.Context, redisStore *UniversalRedis3Store, key string, onDeleteFn func(name string) error) error { + // lock and unlock + mutex := redisStore.redsync.NewMutex(key+"lock") + if err := mutex.Lock(); err != nil { + return fmt.Errorf("lock %s: %v", key, err) + } + defer mutex.Unlock() + + client := redisStore.Client data, err := client.Get(ctx, key).Result() if err != nil { if err != redis.Nil { @@ -84,13 +112,13 @@ func removeChildren(ctx context.Context, client redis.UniversalClient, key strin if err = nameList.RemoteAllListElement(); err != nil { return err } - + return nil } -func listChildren(ctx context.Context, client redis.UniversalClient, key string, startFileName string, eachFn func(name string) bool) error { - +func listChildren(ctx context.Context, redisStore *UniversalRedis3Store, key string, startFileName string, eachFn func(name string) bool) error { + client := redisStore.Client data, err := client.Get(ctx, key).Result() if err != nil { if err != redis.Nil { diff --git a/weed/filer/redis3/kv_directory_children_test.go b/weed/filer/redis3/kv_directory_children_test.go new file mode 100644 index 000000000..7c086bdfb --- /dev/null +++ b/weed/filer/redis3/kv_directory_children_test.go @@ -0,0 +1,75 @@ +package redis3 + +import ( + "github.com/chrislusf/seaweedfs/weed/util/skiplist" + goredislib "github.com/go-redis/redis/v8" + "github.com/stvp/tempredis" + "testing" +) + +var names = []string{ + "cassandra.in.sh", + "cassandra", + "debug-cql.bat", + "nodetool", + "nodetool.bat", + "source-conf.ps1", + "sstableloader", + "sstableloader.bat", + "sstablescrub", + "sstablescrub.bat", + "sstableupgrade", + "sstableupgrade.bat", + "sstableutil", + "sstableutil.bat", + "sstableverify", + "sstableverify.bat", + "stop-server", + "stop-server.bat", + "stop-server.ps1", + "cassandra.in.bat", + "cqlsh.py", + "cqlsh", + "cassandra.ps1", + "cqlsh.bat", + "debug-cql", + "cassandra.bat", +} + +func TestNameList(t *testing.T) { + server, err := tempredis.Start(tempredis.Config{}) + if err != nil { + panic(err) + } + defer server.Term() + + client := goredislib.NewClient(&goredislib.Options{ + Network: "unix", + Addr: server.Socket(), + }) + + store := newSkipListElementStore("/yyy/bin", client) + var data []byte + for _, name := range names { + nameList := skiplist.LoadNameList(data, store, maxNameBatchSizeLimit) + nameList.WriteName(name) + + nameList.ListNames("", func(name string) bool { + println(" * ", name) + return true + }) + + if nameList.HasChanges() { + println("has some changes") + data = nameList.ToBytes() + } + println() + } + + nameList := skiplist.LoadNameList(data, store, maxNameBatchSizeLimit) + nameList.ListNames("", func(name string) bool { + println(name) + return true + }) + +} \ No newline at end of file diff --git a/weed/filer/redis3/redis_cluster_store.go b/weed/filer/redis3/redis_cluster_store.go index e0c620450..73fc0dd20 100644 --- a/weed/filer/redis3/redis_cluster_store.go +++ b/weed/filer/redis3/redis_cluster_store.go @@ -4,6 +4,8 @@ import ( "github.com/chrislusf/seaweedfs/weed/filer" "github.com/chrislusf/seaweedfs/weed/util" "github.com/go-redis/redis/v8" + "github.com/go-redsync/redsync/v4" + "github.com/go-redsync/redsync/v4/redis/goredis/v8" ) func init() { @@ -38,5 +40,6 @@ func (store *RedisCluster3Store) initialize(addresses []string, password string, ReadOnly: readOnly, RouteByLatency: routeByLatency, }) + store.redsync = redsync.New(goredis.NewPool(store.Client)) return } diff --git a/weed/filer/redis3/redis_store.go b/weed/filer/redis3/redis_store.go index fdbf994ec..2eec3d37a 100644 --- a/weed/filer/redis3/redis_store.go +++ b/weed/filer/redis3/redis_store.go @@ -4,6 +4,8 @@ import ( "github.com/chrislusf/seaweedfs/weed/filer" "github.com/chrislusf/seaweedfs/weed/util" "github.com/go-redis/redis/v8" + "github.com/go-redsync/redsync/v4" + "github.com/go-redsync/redsync/v4/redis/goredis/v8" ) func init() { @@ -32,5 +34,6 @@ func (store *Redis3Store) initialize(hostPort string, password string, database Password: password, DB: database, }) + store.redsync = redsync.New(goredis.NewPool(store.Client)) return } diff --git a/weed/filer/redis3/universal_redis_store.go b/weed/filer/redis3/universal_redis_store.go index 8a89e7c48..f04ee493d 100644 --- a/weed/filer/redis3/universal_redis_store.go +++ b/weed/filer/redis3/universal_redis_store.go @@ -3,6 +3,7 @@ package redis3 import ( "context" "fmt" + "github.com/go-redsync/redsync/v4" "time" "github.com/go-redis/redis/v8" @@ -18,7 +19,8 @@ const ( ) type UniversalRedis3Store struct { - Client redis.UniversalClient + Client redis.UniversalClient + redsync *redsync.Redsync } func (store *UniversalRedis3Store) BeginTransaction(ctx context.Context) (context.Context, error) { @@ -49,7 +51,7 @@ func (store *UniversalRedis3Store) InsertEntry(ctx context.Context, entry *filer dir, name := entry.FullPath.DirAndName() if name != "" { - if err = insertChild(ctx, store.Client, genDirectoryListKey(dir), name); err != nil { + if err = insertChild(ctx, store, genDirectoryListKey(dir), name); err != nil { return fmt.Errorf("persisting %s in parent dir: %v", entry.FullPath, err) } } @@ -99,7 +101,7 @@ func (store *UniversalRedis3Store) DeleteEntry(ctx context.Context, fullpath uti dir, name := fullpath.DirAndName() if name != "" { - if err = removeChild(ctx, store.Client, genDirectoryListKey(dir), name); err != nil { + if err = removeChild(ctx, store, genDirectoryListKey(dir), name); err != nil { return fmt.Errorf("DeleteEntry %s in parent dir: %v", fullpath, err) } } @@ -109,7 +111,7 @@ func (store *UniversalRedis3Store) DeleteEntry(ctx context.Context, fullpath uti func (store *UniversalRedis3Store) DeleteFolderChildren(ctx context.Context, fullpath util.FullPath) (err error) { - return removeChildren(ctx, store.Client, genDirectoryListKey(string(fullpath)), func(name string) error { + return removeChildren(ctx, store, genDirectoryListKey(string(fullpath)), func(name string) error { path := util.NewFullPath(string(fullpath), name) _, err = store.Client.Del(ctx, string(path)).Result() if err != nil { @@ -131,7 +133,7 @@ func (store *UniversalRedis3Store) ListDirectoryEntries(ctx context.Context, dir dirListKey := genDirectoryListKey(string(dirPath)) counter := int64(0) - err = listChildren(ctx, store.Client, dirListKey, startFileName, func(fileName string) bool { + err = listChildren(ctx, store, dirListKey, startFileName, func(fileName string) bool { if startFileName != "" { if !includeStartFile && startFileName == fileName { return true diff --git a/weed/util/skiplist/name_list.go b/weed/util/skiplist/name_list.go index 4ba26665a..c5cbf3f87 100644 --- a/weed/util/skiplist/name_list.go +++ b/weed/util/skiplist/name_list.go @@ -300,7 +300,7 @@ func (nl *NameList) ListNames(startFrom string, visitNamesFn func(name string) b } } - return nil + return nil } func (nl *NameList) RemoteAllListElement() error { From 6b31f3c97a5d12b29e93182db10fdddd82e198b5 Mon Sep 17 00:00:00 2001 From: Chris Lu Date: Wed, 6 Oct 2021 00:37:57 -0700 Subject: [PATCH 105/130] add benchmark test --- .../redis3/kv_directory_children_test.go | 54 +++++++++++++++++-- 1 file changed, 49 insertions(+), 5 deletions(-) diff --git a/weed/filer/redis3/kv_directory_children_test.go b/weed/filer/redis3/kv_directory_children_test.go index 7c086bdfb..1b97d8aaf 100644 --- a/weed/filer/redis3/kv_directory_children_test.go +++ b/weed/filer/redis3/kv_directory_children_test.go @@ -1,9 +1,11 @@ package redis3 import ( + "context" "github.com/chrislusf/seaweedfs/weed/util/skiplist" - goredislib "github.com/go-redis/redis/v8" + "github.com/go-redis/redis/v8" "github.com/stvp/tempredis" + "strconv" "testing" ) @@ -43,7 +45,7 @@ func TestNameList(t *testing.T) { } defer server.Term() - client := goredislib.NewClient(&goredislib.Options{ + client := redis.NewClient(&redis.Options{ Network: "unix", Addr: server.Socket(), }) @@ -55,12 +57,10 @@ func TestNameList(t *testing.T) { nameList.WriteName(name) nameList.ListNames("", func(name string) bool { - println(" * ", name) return true }) if nameList.HasChanges() { - println("has some changes") data = nameList.ToBytes() } println() @@ -72,4 +72,48 @@ func TestNameList(t *testing.T) { return true }) -} \ No newline at end of file +} + +func BenchmarkNameList(b *testing.B) { + + server, err := tempredis.Start(tempredis.Config{}) + if err != nil { + panic(err) + } + defer server.Term() + + client := redis.NewClient(&redis.Options{ + Network: "unix", + Addr: server.Socket(), + }) + + store := newSkipListElementStore("/yyy/bin", client) + var data []byte + for i := 0; i < b.N; i++ { + nameList := skiplist.LoadNameList(data, store, maxNameBatchSizeLimit) + + nameList.WriteName("name"+strconv.Itoa(i)) + + if nameList.HasChanges() { + data = nameList.ToBytes() + } + } +} + +func BenchmarkRedis(b *testing.B) { + + server, err := tempredis.Start(tempredis.Config{}) + if err != nil { + panic(err) + } + defer server.Term() + + client := redis.NewClient(&redis.Options{ + Network: "unix", + Addr: server.Socket(), + }) + + for i := 0; i < b.N; i++ { + client.ZAddNX(context.Background(),"/yyy/bin", &redis.Z{Score: 0, Member: "name"+strconv.Itoa(i)}) + } +} From 8668d49c9d14b063d8c363e0abd3e0bf7e0120e8 Mon Sep 17 00:00:00 2001 From: Chris Lu Date: Wed, 6 Oct 2021 01:25:37 -0700 Subject: [PATCH 106/130] test with real redis cpu: Intel(R) Core(TM) i7-9750H CPU @ 2.60GHz BenchmarkNameList-12 1789 760599 ns/op BenchmarkRedis-12 17539 64122 ns/op PASS --- .../redis3/kv_directory_children_test.go | 41 +++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/weed/filer/redis3/kv_directory_children_test.go b/weed/filer/redis3/kv_directory_children_test.go index 1b97d8aaf..5c1cff2bb 100644 --- a/weed/filer/redis3/kv_directory_children_test.go +++ b/weed/filer/redis3/kv_directory_children_test.go @@ -117,3 +117,44 @@ func BenchmarkRedis(b *testing.B) { client.ZAddNX(context.Background(),"/yyy/bin", &redis.Z{Score: 0, Member: "name"+strconv.Itoa(i)}) } } + + +func xBenchmarkNameList(b *testing.B) { + + server, err := tempredis.Start(tempredis.Config{}) + if err != nil { + panic(err) + } + defer server.Term() + + client := redis.NewClient(&redis.Options{ + Addr: "localhost:6379", + Password: "", + DB: 0, + }) + + store := newSkipListElementStore("/yyy/bin", client) + var data []byte + for i := 0; i < b.N; i++ { + nameList := skiplist.LoadNameList(data, store, maxNameBatchSizeLimit) + + nameList.WriteName("name"+strconv.Itoa(i)) + + if nameList.HasChanges() { + data = nameList.ToBytes() + } + } +} + +func xBenchmarkRedis(b *testing.B) { + + client := redis.NewClient(&redis.Options{ + Addr: "localhost:6379", + Password: "", + DB: 0, + }) + + for i := 0; i < b.N; i++ { + client.ZAddNX(context.Background(),"/xxx/bin", &redis.Z{Score: 0, Member: "name"+strconv.Itoa(i)}) + } +} From 371fead8a5c7453ff5c49a01cfbe2bdcb46bb8b8 Mon Sep 17 00:00:00 2001 From: Chris Lu Date: Wed, 6 Oct 2021 18:18:24 -0700 Subject: [PATCH 107/130] redis3 using redis native sorted set --- weed/filer/redis3/ItemList.go | 483 ++++++++++++++++++ weed/filer/redis3/item_list_serde.go | 75 +++ weed/filer/redis3/kv_directory_children.go | 11 +- .../redis3/kv_directory_children_test.go | 160 ++++-- weed/filer/redis3/skiplist_element_store.go | 10 +- weed/util/skiplist/name_list.go | 30 +- weed/util/skiplist/name_list_serde.go | 18 +- weed/util/skiplist/skiplist.go | 181 +++---- weed/util/skiplist/skiplist_serde.go | 12 +- weed/util/skiplist/skiplist_test.go | 42 +- 10 files changed, 817 insertions(+), 205 deletions(-) create mode 100644 weed/filer/redis3/ItemList.go create mode 100644 weed/filer/redis3/item_list_serde.go diff --git a/weed/filer/redis3/ItemList.go b/weed/filer/redis3/ItemList.go new file mode 100644 index 000000000..ae4e61cfb --- /dev/null +++ b/weed/filer/redis3/ItemList.go @@ -0,0 +1,483 @@ +package redis3 + +import ( + "bytes" + "context" + "fmt" + "github.com/chrislusf/seaweedfs/weed/util/skiplist" + "github.com/go-redis/redis/v8" +) + +type ItemList struct { + skipList *skiplist.SkipList + batchSize int + client redis.UniversalClient + prefix string +} + +func newItemList(client redis.UniversalClient, prefix string, store skiplist.ListStore, batchSize int) *ItemList { + return &ItemList{ + skipList: skiplist.New(store), + batchSize: batchSize, + client: client, + prefix: prefix, + } +} + +/* +Be reluctant to create new nodes. Try to fit into either previous node or next node. +Prefer to add to previous node. + +There are multiple cases after finding the name for greater or equal node + 1. found and node.Key == name + The node contains a batch with leading key the same as the name + nothing to do + 2. no such node found or node.Key > name + + if no such node found + prevNode = list.LargestNode + + // case 2.1 + if previousNode contains name + nothing to do + + // prefer to add to previous node + if prevNode != nil { + // case 2.2 + if prevNode has capacity + prevNode.add name, and save + return + // case 2.3 + split prevNode by name + } + + // case 2.4 + // merge into next node. Avoid too many nodes if adding data in reverse order. + if nextNode is not nil and nextNode has capacity + delete nextNode.Key + nextNode.Key = name + nextNode.batch.add name + insert nodeNode.Key + return + + // case 2.5 + if prevNode is nil + insert new node with key = name, value = batch{name} + return + +*/ +func (nl *ItemList) WriteName(name string) error { + + lookupKey := []byte(name) + prevNode, nextNode, found, err := nl.skipList.FindGreaterOrEqual(lookupKey) + if err != nil { + return err + } + // case 1: the name already exists as one leading key in the batch + if found && bytes.Compare(nextNode.Key, lookupKey) == 0 { + return nil + } + + if !found { + prevNode, err = nl.skipList.GetLargestNode() + if err != nil { + return err + } + } + + if nextNode != nil && prevNode == nil { + prevNode, err = nl.skipList.LoadElement(nextNode.Prev) + if err != nil { + return err + } + } + + if prevNode != nil { + // case 2.1 + if nl.NodeContainsItem(prevNode.Reference(), name) { + return nil + } + + // case 2.2 + nodeSize := nl.NodeSize(prevNode.Reference()) + if nodeSize < nl.batchSize { + return nl.NodeAddMember(prevNode.Reference(), name) + } + + // case 2.3 + x := nl.NodeInnerPosition(prevNode.Reference(), name) + y := nodeSize - x + addToX := x <= y + // add to a new node + if x == 0 || y == 0 { + if err := nl.ItemAdd(lookupKey, 0, name); err != nil { + return err + } + return nil + } + if addToX { + // collect names before name, add them to X + namesToX, err := nl.NodeRangeBeforeExclusive(prevNode.Reference(), name) + if err != nil { + return nil + } + // delete skiplist reference to old node + if _, err := nl.skipList.DeleteByKey(prevNode.Key); err != nil { + return err + } + // add namesToY and name to a new X + namesToX = append(namesToX, name) + if err := nl.ItemAdd([]byte(namesToX[0]), 0, namesToX...); err != nil { + return nil + } + // remove names less than name from current Y + if err := nl.NodeDeleteBeforeExclusive(prevNode.Reference(), name); err != nil { + return nil + } + + // point skip list to current Y + if err := nl.ItemAdd(lookupKey, prevNode.Id); err != nil { + return nil + } + return nil + } else { + // collect names after name, add them to Y + namesToY, err := nl.NodeRangeAfterExclusive(prevNode.Reference(), name) + if err != nil { + return nil + } + // add namesToY and name to a new Y + namesToY = append(namesToY, name) + if err := nl.ItemAdd(lookupKey, 0, namesToY...); err != nil { + return nil + } + // remove names after name from current X + if err := nl.NodeDeleteAfterExclusive(prevNode.Reference(), name); err != nil { + return nil + } + return nil + } + + } + + // case 2.4 + if nextNode != nil { + nodeSize := nl.NodeSize(nextNode.Reference()) + if nodeSize < nl.batchSize { + if id, err := nl.skipList.DeleteByKey(nextNode.Key); err != nil { + return err + } else { + if err := nl.ItemAdd(lookupKey, id, name); err != nil { + return err + } + } + return nil + } + } + + // case 2.5 + // now prevNode is nil + return nl.ItemAdd(lookupKey, 0, name) +} + +/* +// case 1: exists in nextNode +if nextNode != nil && nextNode.Key == name { + remove from nextNode, update nextNode + // TODO: merge with prevNode if possible? + return +} +if nextNode is nil + prevNode = list.Largestnode +if prevNode == nil and nextNode.Prev != nil + prevNode = load(nextNode.Prev) + +// case 2: does not exist +// case 2.1 +if prevNode == nil { + return +} +// case 2.2 +if prevNameBatch does not contain name { + return +} + +// case 3 +delete from prevNameBatch +if prevNameBatch + nextNode < capacityList + // case 3.1 + merge +else + // case 3.2 + update prevNode + + +*/ +func (nl *ItemList) DeleteName(name string) error { + lookupKey := []byte(name) + prevNode, nextNode, found, err := nl.skipList.FindGreaterOrEqual(lookupKey) + if err != nil { + return err + } + + // case 1 + if found && bytes.Compare(nextNode.Key, lookupKey) == 0 { + if _, err := nl.skipList.DeleteByKey(nextNode.Key); err != nil { + return err + } + if err := nl.NodeDeleteMember(nextNode.Reference(), name); err != nil { + return err + } + minName := nl.NodeMin(nextNode.Reference()) + if minName == "" { + return nl.NodeDelete(nextNode.Reference()) + } + return nl.ItemAdd([]byte(minName), nextNode.Id) + } + + if !found { + prevNode, err = nl.skipList.GetLargestNode() + if err != nil { + return err + } + } + + if nextNode != nil && prevNode == nil { + prevNode, err = nl.skipList.LoadElement(nextNode.Prev) + if err != nil { + return err + } + } + + // case 2 + if prevNode == nil { + // case 2.1 + return nil + } + if !nl.NodeContainsItem(prevNode.Reference(), name) { + return nil + } + + // case 3 + if err := nl.NodeDeleteMember(prevNode.Reference(), name); err != nil { + return err + } + prevSize := nl.NodeSize(prevNode.Reference()) + if prevSize == 0 { + if _, err := nl.skipList.DeleteByKey(prevNode.Key); err != nil { + return err + } + return nil + } + nextSize := nl.NodeSize(nextNode.Reference()) + if nextSize > 0 && prevSize + nextSize < nl.batchSize { + // case 3.1 merge nextNode and prevNode + if _, err := nl.skipList.DeleteByKey(nextNode.Key); err != nil { + return err + } + nextNames, err := nl.NodeRangeBeforeExclusive(nextNode.Reference(), "") + if err != nil { + return err + } + if err := nl.NodeAddMember(prevNode.Reference(), nextNames...); err != nil { + return err + } + return nl.NodeDelete(nextNode.Reference()) + } else { + // case 3.2 update prevNode + // no action to take + return nil + } + + return nil +} + +func (nl *ItemList) ListNames(startFrom string, visitNamesFn func(name string) bool) error { + lookupKey := []byte(startFrom) + prevNode, nextNode, found, err := nl.skipList.FindGreaterOrEqual(lookupKey) + if err != nil { + return err + } + if found && bytes.Compare(nextNode.Key, lookupKey) == 0 { + prevNode = nil + } + if !found { + prevNode, err = nl.skipList.GetLargestNode() + if err != nil { + return err + } + } + + if prevNode != nil { + if !nl.NodeScanIncluseiveAfter(prevNode.Reference(), startFrom, visitNamesFn) { + return nil + } + } + + for nextNode != nil { + if !nl.NodeScanIncluseiveAfter(nextNode.Reference(), startFrom, visitNamesFn) { + return nil + } + nextNode, err = nl.skipList.LoadElement(nextNode.Next[0]) + if err != nil { + return err + } + } + + return nil +} + +func (nl *ItemList) RemoteAllListElement() error { + + t := nl.skipList + + nodeRef := t.StartLevels[0] + for nodeRef != nil { + node, err := t.LoadElement(nodeRef) + if err != nil { + return err + } + if node == nil { + return nil + } + if err := t.DeleteElement(node); err != nil { + return err + } + if err := nl.NodeDelete(node.Reference()); err != nil { + return err + } + nodeRef = node.Next[0] + } + return nil + +} + +func (nl *ItemList) NodeContainsItem(node *skiplist.SkipListElementReference, item string) bool { + key := fmt.Sprintf("%s%dm", nl.prefix, node.ElementPointer) + _, err := nl.client.ZScore(context.Background(), key, item).Result() + if err == redis.Nil { + return false + } + if err == nil { + return true + } + return false +} + +func (nl *ItemList) NodeSize(node *skiplist.SkipListElementReference) int { + key := fmt.Sprintf("%s%dm", nl.prefix, node.ElementPointer) + return int(nl.client.ZLexCount(context.Background(), key, "-", "+").Val()) +} + +func (nl *ItemList) NodeAddMember(node *skiplist.SkipListElementReference, names ...string) error { + key := fmt.Sprintf("%s%dm", nl.prefix, node.ElementPointer) + var members []*redis.Z + for _, name := range names { + members = append(members, &redis.Z{ + Score: 0, + Member: name, + }) + } + return nl.client.ZAddNX(context.Background(), key, members...).Err() +} +func (nl *ItemList) NodeDeleteMember(node *skiplist.SkipListElementReference, name string) error { + key := fmt.Sprintf("%s%dm", nl.prefix, node.ElementPointer) + return nl.client.ZRem(context.Background(), key, name).Err() +} + +func (nl *ItemList) NodeDelete(node *skiplist.SkipListElementReference) error { + key := fmt.Sprintf("%s%dm", nl.prefix, node.ElementPointer) + return nl.client.Del(context.Background(), key).Err() +} + +func (nl *ItemList) NodeInnerPosition(node *skiplist.SkipListElementReference, name string) int { + key := fmt.Sprintf("%s%dm", nl.prefix, node.ElementPointer) + return int(nl.client.ZLexCount(context.Background(), key, "-", "("+name).Val()) +} + +func (nl *ItemList) NodeMin(node *skiplist.SkipListElementReference) string { + key := fmt.Sprintf("%s%dm", nl.prefix, node.ElementPointer) + slice := nl.client.ZPopMin(context.Background(), key).Val() + if len(slice)>0{ + s := slice[0].Member.(string) + return s + } + return "" +} + +func (nl *ItemList) NodeScanIncluseiveAfter(node *skiplist.SkipListElementReference, startFrom string, visitNamesFn func(name string) bool) bool { + key := fmt.Sprintf("%s%dm", nl.prefix, node.ElementPointer) + if startFrom == "" { + startFrom = "-" + } else { + startFrom = "[" + startFrom + } + names := nl.client.ZRangeByLex(context.Background(), key, &redis.ZRangeBy{ + Min: startFrom, + Max: "+", + }).Val() + for _, n := range names { + if !visitNamesFn(n) { + return false + } + } + return true +} + +func (nl *ItemList) NodeRangeBeforeExclusive(node *skiplist.SkipListElementReference, stopAt string) ([]string, error) { + key := fmt.Sprintf("%s%dm", nl.prefix, node.ElementPointer) + if stopAt == "" { + stopAt = "+" + } else { + stopAt = "(" + stopAt + } + return nl.client.ZRangeByLex(context.Background(), key, &redis.ZRangeBy{ + Min: "-", + Max: stopAt, + }).Result() +} +func (nl *ItemList) NodeRangeAfterExclusive(node *skiplist.SkipListElementReference, startFrom string) ([]string, error) { + key := fmt.Sprintf("%s%dm", nl.prefix, node.ElementPointer) + if startFrom == "" { + startFrom = "-" + } else { + startFrom = "(" + startFrom + } + return nl.client.ZRangeByLex(context.Background(), key, &redis.ZRangeBy{ + Min: startFrom, + Max: "+", + }).Result() +} + +func (nl *ItemList) NodeDeleteBeforeExclusive(node *skiplist.SkipListElementReference, stopAt string) error { + key := fmt.Sprintf("%s%dm", nl.prefix, node.ElementPointer) + if stopAt == "" { + stopAt = "+" + } else { + stopAt = "(" + stopAt + } + return nl.client.ZRemRangeByLex(context.Background(), key, "-", stopAt).Err() +} +func (nl *ItemList) NodeDeleteAfterExclusive(node *skiplist.SkipListElementReference, startFrom string) error { + key := fmt.Sprintf("%s%dm", nl.prefix, node.ElementPointer) + if startFrom == "" { + startFrom = "-" + } else { + startFrom = "(" + startFrom + } + return nl.client.ZRemRangeByLex(context.Background(), key, startFrom, "+").Err() +} + +func (nl *ItemList) ItemAdd(lookupKey []byte, idIfKnown int64, names ...string) error { + if id, err := nl.skipList.InsertByKey(lookupKey, idIfKnown, nil); err != nil { + return err + } else { + if len(names) > 0 { + return nl.NodeAddMember(&skiplist.SkipListElementReference{ + ElementPointer: id, + Key: lookupKey, + }, names...) + } + } + return nil +} diff --git a/weed/filer/redis3/item_list_serde.go b/weed/filer/redis3/item_list_serde.go new file mode 100644 index 000000000..d0310ce40 --- /dev/null +++ b/weed/filer/redis3/item_list_serde.go @@ -0,0 +1,75 @@ +package redis3 + +import ( + "github.com/chrislusf/seaweedfs/weed/glog" + "github.com/chrislusf/seaweedfs/weed/util/skiplist" + "github.com/go-redis/redis/v8" + "github.com/golang/protobuf/proto" +) + +func LoadItemList(data []byte, prefix string, client redis.UniversalClient, store skiplist.ListStore, batchSize int) *ItemList { + + nl := &ItemList{ + skipList: skiplist.New(store), + batchSize: batchSize, + client: client, + prefix: prefix, + } + + if len(data) == 0 { + return nl + } + + message := &skiplist.SkipListProto{} + if err := proto.Unmarshal(data, message); err != nil { + glog.Errorf("loading skiplist: %v", err) + } + nl.skipList.MaxNewLevel = int(message.MaxNewLevel) + nl.skipList.MaxLevel = int(message.MaxLevel) + for i, ref := range message.StartLevels { + nl.skipList.StartLevels[i] = &skiplist.SkipListElementReference{ + ElementPointer: ref.ElementPointer, + Key: ref.Key, + } + } + for i, ref := range message.EndLevels { + nl.skipList.EndLevels[i] = &skiplist.SkipListElementReference{ + ElementPointer: ref.ElementPointer, + Key: ref.Key, + } + } + return nl +} + +func (nl *ItemList) HasChanges() bool { + return nl.skipList.HasChanges +} + +func (nl *ItemList) ToBytes() []byte { + message := &skiplist.SkipListProto{} + message.MaxNewLevel = int32(nl.skipList.MaxNewLevel) + message.MaxLevel = int32(nl.skipList.MaxLevel) + for _, ref := range nl.skipList.StartLevels { + if ref == nil { + break + } + message.StartLevels = append(message.StartLevels, &skiplist.SkipListElementReference{ + ElementPointer: ref.ElementPointer, + Key: ref.Key, + }) + } + for _, ref := range nl.skipList.EndLevels { + if ref == nil { + break + } + message.EndLevels = append(message.EndLevels, &skiplist.SkipListElementReference{ + ElementPointer: ref.ElementPointer, + Key: ref.Key, + }) + } + data, err := proto.Marshal(message) + if err != nil { + glog.Errorf("marshal skiplist: %v", err) + } + return data +} diff --git a/weed/filer/redis3/kv_directory_children.go b/weed/filer/redis3/kv_directory_children.go index 797e7797c..624d17374 100644 --- a/weed/filer/redis3/kv_directory_children.go +++ b/weed/filer/redis3/kv_directory_children.go @@ -4,11 +4,10 @@ import ( "context" "fmt" "github.com/chrislusf/seaweedfs/weed/glog" - "github.com/chrislusf/seaweedfs/weed/util/skiplist" "github.com/go-redis/redis/v8" ) -const maxNameBatchSizeLimit = 1000 +const maxNameBatchSizeLimit = 1000000 func insertChild(ctx context.Context, redisStore *UniversalRedis3Store, key string, name string) error { @@ -29,7 +28,7 @@ func insertChild(ctx context.Context, redisStore *UniversalRedis3Store, key stri } } store := newSkipListElementStore(key, client) - nameList := skiplist.LoadNameList([]byte(data), store, maxNameBatchSizeLimit) + nameList := LoadItemList([]byte(data), key, client, store, maxNameBatchSizeLimit) if err := nameList.WriteName(name); err != nil { glog.Errorf("add %s %s: %v", key, name, err) @@ -64,7 +63,7 @@ func removeChild(ctx context.Context, redisStore *UniversalRedis3Store, key stri } } store := newSkipListElementStore(key, client) - nameList := skiplist.LoadNameList([]byte(data), store, maxNameBatchSizeLimit) + nameList := LoadItemList([]byte(data), key, client, store, maxNameBatchSizeLimit) if err := nameList.DeleteName(name); err != nil { return err @@ -97,7 +96,7 @@ func removeChildren(ctx context.Context, redisStore *UniversalRedis3Store, key s } } store := newSkipListElementStore(key, client) - nameList := skiplist.LoadNameList([]byte(data), store, maxNameBatchSizeLimit) + nameList := LoadItemList([]byte(data), key, client, store, maxNameBatchSizeLimit) if err = nameList.ListNames("", func(name string) bool { if err := onDeleteFn(name); err != nil { @@ -126,7 +125,7 @@ func listChildren(ctx context.Context, redisStore *UniversalRedis3Store, key str } } store := newSkipListElementStore(key, client) - nameList := skiplist.LoadNameList([]byte(data), store, maxNameBatchSizeLimit) + nameList := LoadItemList([]byte(data), key, client, store, maxNameBatchSizeLimit) if err = nameList.ListNames(startFileName, func(name string) bool { return eachFn(name) diff --git a/weed/filer/redis3/kv_directory_children_test.go b/weed/filer/redis3/kv_directory_children_test.go index 5c1cff2bb..77988e1a3 100644 --- a/weed/filer/redis3/kv_directory_children_test.go +++ b/weed/filer/redis3/kv_directory_children_test.go @@ -2,11 +2,12 @@ package redis3 import ( "context" - "github.com/chrislusf/seaweedfs/weed/util/skiplist" + "fmt" "github.com/go-redis/redis/v8" "github.com/stvp/tempredis" "strconv" "testing" + "time" ) var names = []string{ @@ -53,20 +54,21 @@ func TestNameList(t *testing.T) { store := newSkipListElementStore("/yyy/bin", client) var data []byte for _, name := range names { - nameList := skiplist.LoadNameList(data, store, maxNameBatchSizeLimit) + nameList := LoadItemList(data, "/yyy/bin", client, store, maxNameBatchSizeLimit) nameList.WriteName(name) nameList.ListNames("", func(name string) bool { + // println(name) return true }) if nameList.HasChanges() { data = nameList.ToBytes() } - println() + // println() } - nameList := skiplist.LoadNameList(data, store, maxNameBatchSizeLimit) + nameList := LoadItemList(data, "/yyy/bin", client, store, maxNameBatchSizeLimit) nameList.ListNames("", func(name string) bool { println(name) return true @@ -74,6 +76,99 @@ func TestNameList(t *testing.T) { } +func xBenchmarkNameList(b *testing.B) { + + server, err := tempredis.Start(tempredis.Config{}) + if err != nil { + panic(err) + } + defer server.Term() + + client := redis.NewClient(&redis.Options{ + Network: "unix", + Addr: server.Socket(), + }) + + store := newSkipListElementStore("/yyy/bin", client) + var data []byte + for i := 0; i < b.N; i++ { + nameList := LoadItemList(data, "/yyy/bin", client, store, maxNameBatchSizeLimit) + + nameList.WriteName(strconv.Itoa(i)+"namexxxxxxxxxxxxxxxxxxx") + + if nameList.HasChanges() { + data = nameList.ToBytes() + } + } +} + +func xBenchmarkRedis(b *testing.B) { + + server, err := tempredis.Start(tempredis.Config{}) + if err != nil { + panic(err) + } + defer server.Term() + + client := redis.NewClient(&redis.Options{ + Network: "unix", + Addr: server.Socket(), + }) + + for i := 0; i < b.N; i++ { + client.ZAddNX(context.Background(),"/yyy/bin", &redis.Z{Score: 0, Member: strconv.Itoa(i)+"namexxxxxxxxxxxxxxxxxxx"}) + } +} + +func TestNameListAdd(t *testing.T) { + + server, err := tempredis.Start(tempredis.Config{}) + if err != nil { + panic(err) + } + defer server.Term() + + client := redis.NewClient(&redis.Options{ + Addr: "localhost:6379", + Password: "", + DB: 0, + }) + + client.FlushAll(context.Background()) + + N := 364800 + + ts0 := time.Now() + store := newSkipListElementStore("/y", client) + var data []byte + nameList := LoadItemList(data, "/y", client, store, 100000) + for i := 0; i < N; i++ { + nameList.WriteName(fmt.Sprintf("%8d", i)) + } + + ts1 := time.Now() + + for i := 0; i < N; i++ { + client.ZAddNX(context.Background(),"/x", &redis.Z{Score: 0, Member: fmt.Sprintf("name %8d", i)}) + } + ts2 := time.Now() + + fmt.Printf("%v %v", ts1.Sub(ts0), ts2.Sub(ts1)) + + /* + keys := client.Keys(context.Background(), "/*m").Val() + for _, k := range keys { + println("key", k) + for i, v := range client.ZRangeByLex(context.Background(), k, &redis.ZRangeBy{ + Min: "-", + Max: "+", + }).Val() { + println(" ", i, v) + } + } + */ +} + func BenchmarkNameList(b *testing.B) { server, err := tempredis.Start(tempredis.Config{}) @@ -83,16 +178,17 @@ func BenchmarkNameList(b *testing.B) { defer server.Term() client := redis.NewClient(&redis.Options{ - Network: "unix", - Addr: server.Socket(), + Addr: "localhost:6379", + Password: "", + DB: 0, }) store := newSkipListElementStore("/yyy/bin", client) var data []byte for i := 0; i < b.N; i++ { - nameList := skiplist.LoadNameList(data, store, maxNameBatchSizeLimit) + nameList := LoadItemList(data, "/yyy/bin", client, store, maxNameBatchSizeLimit) - nameList.WriteName("name"+strconv.Itoa(i)) + nameList.WriteName(fmt.Sprintf("name %8d", i)) if nameList.HasChanges() { data = nameList.ToBytes() @@ -102,52 +198,6 @@ func BenchmarkNameList(b *testing.B) { func BenchmarkRedis(b *testing.B) { - server, err := tempredis.Start(tempredis.Config{}) - if err != nil { - panic(err) - } - defer server.Term() - - client := redis.NewClient(&redis.Options{ - Network: "unix", - Addr: server.Socket(), - }) - - for i := 0; i < b.N; i++ { - client.ZAddNX(context.Background(),"/yyy/bin", &redis.Z{Score: 0, Member: "name"+strconv.Itoa(i)}) - } -} - - -func xBenchmarkNameList(b *testing.B) { - - server, err := tempredis.Start(tempredis.Config{}) - if err != nil { - panic(err) - } - defer server.Term() - - client := redis.NewClient(&redis.Options{ - Addr: "localhost:6379", - Password: "", - DB: 0, - }) - - store := newSkipListElementStore("/yyy/bin", client) - var data []byte - for i := 0; i < b.N; i++ { - nameList := skiplist.LoadNameList(data, store, maxNameBatchSizeLimit) - - nameList.WriteName("name"+strconv.Itoa(i)) - - if nameList.HasChanges() { - data = nameList.ToBytes() - } - } -} - -func xBenchmarkRedis(b *testing.B) { - client := redis.NewClient(&redis.Options{ Addr: "localhost:6379", Password: "", @@ -155,6 +205,6 @@ func xBenchmarkRedis(b *testing.B) { }) for i := 0; i < b.N; i++ { - client.ZAddNX(context.Background(),"/xxx/bin", &redis.Z{Score: 0, Member: "name"+strconv.Itoa(i)}) + client.ZAddNX(context.Background(),"/xxx/bin", &redis.Z{Score: 0, Member: fmt.Sprintf("name %8d", i)}) } } diff --git a/weed/filer/redis3/skiplist_element_store.go b/weed/filer/redis3/skiplist_element_store.go index 66a5408d6..bcad356dd 100644 --- a/weed/filer/redis3/skiplist_element_store.go +++ b/weed/filer/redis3/skiplist_element_store.go @@ -10,7 +10,7 @@ import ( ) type SkipListElementStore struct { - prefix string + Prefix string client redis.UniversalClient } @@ -18,13 +18,13 @@ var _ = skiplist.ListStore(&SkipListElementStore{}) func newSkipListElementStore(prefix string, client redis.UniversalClient) *SkipListElementStore { return &SkipListElementStore{ - prefix: prefix, + Prefix: prefix, client: client, } } func (m *SkipListElementStore) SaveElement(id int64, element *skiplist.SkipListElement) error { - key := fmt.Sprintf("%s%d", m.prefix, id) + key := fmt.Sprintf("%s%d", m.Prefix, id) data, err := proto.Marshal(element) if err != nil { glog.Errorf("marshal %s: %v", key, err) @@ -33,12 +33,12 @@ func (m *SkipListElementStore) SaveElement(id int64, element *skiplist.SkipListE } func (m *SkipListElementStore) DeleteElement(id int64) error { - key := fmt.Sprintf("%s%d", m.prefix, id) + key := fmt.Sprintf("%s%d", m.Prefix, id) return m.client.Del(context.Background(), key).Err() } func (m *SkipListElementStore) LoadElement(id int64) (*skiplist.SkipListElement, error) { - key := fmt.Sprintf("%s%d", m.prefix, id) + key := fmt.Sprintf("%s%d", m.Prefix, id) data, err := m.client.Get(context.Background(), key).Result() if err != nil { if err == redis.Nil { diff --git a/weed/util/skiplist/name_list.go b/weed/util/skiplist/name_list.go index c5cbf3f87..19e8e6b49 100644 --- a/weed/util/skiplist/name_list.go +++ b/weed/util/skiplist/name_list.go @@ -78,7 +78,7 @@ func (nl *NameList) WriteName(name string) error { } if nextNode != nil && prevNode == nil { - prevNode, err = nl.skipList.loadElement(nextNode.Prev) + prevNode, err = nl.skipList.LoadElement(nextNode.Prev) if err != nil { return err } @@ -109,7 +109,7 @@ func (nl *NameList) WriteName(name string) error { return err } } else { - if err := nl.skipList.Insert([]byte(x.key), x.ToBytes()); err != nil { + if _, err := nl.skipList.InsertByKey([]byte(x.key), 0, x.ToBytes()); err != nil { return err } } @@ -123,7 +123,7 @@ func (nl *NameList) WriteName(name string) error { return err } } else { - if err := nl.skipList.Insert([]byte(y.key), y.ToBytes()); err != nil { + if _, err := nl.skipList.InsertByKey([]byte(y.key), 0, y.ToBytes()); err != nil { return err } } @@ -136,11 +136,11 @@ func (nl *NameList) WriteName(name string) error { if nextNode != nil { nextNameBatch := LoadNameBatch(nextNode.Value) if len(nextNameBatch.names) < nl.batchSize { - if err := nl.skipList.Delete(nextNode.Key); err != nil { + if _, err := nl.skipList.DeleteByKey(nextNode.Key); err != nil { return err } nextNameBatch.WriteName(name) - if err := nl.skipList.Insert([]byte(nextNameBatch.key), nextNameBatch.ToBytes()); err != nil { + if _, err := nl.skipList.InsertByKey([]byte(nextNameBatch.key), 0, nextNameBatch.ToBytes()); err != nil { return err } return nil @@ -151,7 +151,7 @@ func (nl *NameList) WriteName(name string) error { // now prevNode is nil newNameBatch := NewNameBatch() newNameBatch.WriteName(name) - if err := nl.skipList.Insert([]byte(newNameBatch.key), newNameBatch.ToBytes()); err != nil { + if _, err := nl.skipList.InsertByKey([]byte(newNameBatch.key), 0, newNameBatch.ToBytes()); err != nil { return err } @@ -204,12 +204,12 @@ func (nl *NameList) DeleteName(name string) error { nextNameBatch = LoadNameBatch(nextNode.Value) } if found && bytes.Compare(nextNode.Key, lookupKey) == 0 { - if err := nl.skipList.Delete(nextNode.Key); err != nil { + if _, err := nl.skipList.DeleteByKey(nextNode.Key); err != nil { return err } nextNameBatch.DeleteName(name) if len(nextNameBatch.names) > 0 { - if err := nl.skipList.Insert([]byte(nextNameBatch.key), nextNameBatch.ToBytes()); err != nil { + if _, err := nl.skipList.InsertByKey([]byte(nextNameBatch.key), 0, nextNameBatch.ToBytes()); err != nil { return err } } @@ -224,7 +224,7 @@ func (nl *NameList) DeleteName(name string) error { } if nextNode != nil && prevNode == nil { - prevNode, err = nl.skipList.loadElement(nextNode.Prev) + prevNode, err = nl.skipList.LoadElement(nextNode.Prev) if err != nil { return err } @@ -244,14 +244,14 @@ func (nl *NameList) DeleteName(name string) error { // case 3 prevNameBatch.DeleteName(name) if len(prevNameBatch.names) == 0 { - if err := nl.skipList.Delete(prevNode.Key); err != nil { + if _, err := nl.skipList.DeleteByKey(prevNode.Key); err != nil { return err } return nil } if nextNameBatch != nil && len(nextNameBatch.names) + len(prevNameBatch.names) < nl.batchSize { // case 3.1 merge nextNode and prevNode - if err := nl.skipList.Delete(nextNode.Key); err != nil { + if _, err := nl.skipList.DeleteByKey(nextNode.Key); err != nil { return err } for nextName := range nextNameBatch.names { @@ -294,7 +294,7 @@ func (nl *NameList) ListNames(startFrom string, visitNamesFn func(name string) b if !nextNameBatch.ListNames(startFrom, visitNamesFn) { return nil } - nextNode, err = nl.skipList.loadElement(nextNode.Next[0]) + nextNode, err = nl.skipList.LoadElement(nextNode.Next[0]) if err != nil { return err } @@ -307,16 +307,16 @@ func (nl *NameList) RemoteAllListElement() error { t := nl.skipList - nodeRef := t.startLevels[0] + nodeRef := t.StartLevels[0] for nodeRef != nil { - node, err := t.loadElement(nodeRef) + node, err := t.LoadElement(nodeRef) if err != nil { return err } if node == nil { return nil } - if err := t.deleteElement(node); err != nil { + if err := t.DeleteElement(node); err != nil { return err } nodeRef = node.Next[0] diff --git a/weed/util/skiplist/name_list_serde.go b/weed/util/skiplist/name_list_serde.go index be9f06698..397dfd432 100644 --- a/weed/util/skiplist/name_list_serde.go +++ b/weed/util/skiplist/name_list_serde.go @@ -20,16 +20,16 @@ func LoadNameList(data []byte, store ListStore, batchSize int) *NameList { if err := proto.Unmarshal(data, message); err != nil { glog.Errorf("loading skiplist: %v", err) } - nl.skipList.maxNewLevel = int(message.MaxNewLevel) - nl.skipList.maxLevel = int(message.MaxLevel) + nl.skipList.MaxNewLevel = int(message.MaxNewLevel) + nl.skipList.MaxLevel = int(message.MaxLevel) for i, ref := range message.StartLevels { - nl.skipList.startLevels[i] = &SkipListElementReference{ + nl.skipList.StartLevels[i] = &SkipListElementReference{ ElementPointer: ref.ElementPointer, Key: ref.Key, } } for i, ref := range message.EndLevels { - nl.skipList.endLevels[i] = &SkipListElementReference{ + nl.skipList.EndLevels[i] = &SkipListElementReference{ ElementPointer: ref.ElementPointer, Key: ref.Key, } @@ -38,14 +38,14 @@ func LoadNameList(data []byte, store ListStore, batchSize int) *NameList { } func (nl *NameList) HasChanges() bool { - return nl.skipList.hasChanges + return nl.skipList.HasChanges } func (nl *NameList) ToBytes() []byte { message := &SkipListProto{} - message.MaxNewLevel = int32(nl.skipList.maxNewLevel) - message.MaxLevel = int32(nl.skipList.maxLevel) - for _, ref := range nl.skipList.startLevels { + message.MaxNewLevel = int32(nl.skipList.MaxNewLevel) + message.MaxLevel = int32(nl.skipList.MaxLevel) + for _, ref := range nl.skipList.StartLevels { if ref == nil { break } @@ -54,7 +54,7 @@ func (nl *NameList) ToBytes() []byte { Key: ref.Key, }) } - for _, ref := range nl.skipList.endLevels { + for _, ref := range nl.skipList.EndLevels { if ref == nil { break } diff --git a/weed/util/skiplist/skiplist.go b/weed/util/skiplist/skiplist.go index 52e6c606a..f42ec23cd 100644 --- a/weed/util/skiplist/skiplist.go +++ b/weed/util/skiplist/skiplist.go @@ -17,12 +17,12 @@ const ( ) type SkipList struct { - startLevels [maxLevel]*SkipListElementReference - endLevels [maxLevel]*SkipListElementReference - maxNewLevel int - maxLevel int - listStore ListStore - hasChanges bool + StartLevels [maxLevel]*SkipListElementReference + EndLevels [maxLevel]*SkipListElementReference + MaxNewLevel int + MaxLevel int + ListStore ListStore + HasChanges bool // elementCount int } @@ -36,9 +36,9 @@ func NewSeed(seed int64, listStore ListStore) *SkipList { //fmt.Printf("SkipList seed: %v\n", seed) list := &SkipList{ - maxNewLevel: maxLevel, - maxLevel: 0, - listStore: listStore, + MaxNewLevel: maxLevel, + MaxLevel: 0, + ListStore: listStore, // elementCount: 0, } @@ -52,7 +52,7 @@ func New(listStore ListStore) *SkipList { // IsEmpty checks, if the skiplist is empty. func (t *SkipList) IsEmpty() bool { - return t.startLevels[0] == nil + return t.StartLevels[0] == nil } func (t *SkipList) generateLevel(maxLevel int) int { @@ -70,8 +70,8 @@ func (t *SkipList) generateLevel(maxLevel int) int { func (t *SkipList) findEntryIndex(key []byte, minLevel int) int { // Find good entry point so we don't accidentally skip half the list. - for i := t.maxLevel; i >= 0; i-- { - if t.startLevels[i] != nil && bytes.Compare(t.startLevels[i].Key, key) < 0 || i <= minLevel { + for i := t.MaxLevel; i >= 0; i-- { + if t.StartLevels[i] != nil && bytes.Compare(t.StartLevels[i].Key, key) < 0 || i <= minLevel { return i } } @@ -90,7 +90,7 @@ func (t *SkipList) findExtended(key []byte, findGreaterOrEqual bool) (prevElemen index := t.findEntryIndex(key, 0) var currentNode *SkipListElement - currentNode, err = t.loadElement(t.startLevels[index]) + currentNode, err = t.LoadElement(t.StartLevels[index]) if err != nil { return } @@ -115,7 +115,7 @@ func (t *SkipList) findExtended(key []byte, findGreaterOrEqual bool) (prevElemen // Which direction are we continuing next time? if currentNode.Next[index] != nil && bytes.Compare(currentNode.Next[index].Key, key) <= 0 { // Go right - currentNode, err = t.loadElement(currentNode.Next[index]) + currentNode, err = t.LoadElement(currentNode.Next[index]) if err != nil { return } @@ -129,7 +129,7 @@ func (t *SkipList) findExtended(key []byte, findGreaterOrEqual bool) (prevElemen if currentNode.Next[0] != nil && bytes.Compare(currentNode.Next[0].Key, key) == 0 { prevElementIfVisited = currentNode var currentNodeNext *SkipListElement - currentNodeNext, err = t.loadElement(currentNode.Next[0]) + currentNodeNext, err = t.LoadElement(currentNode.Next[0]) if err != nil { return } @@ -145,7 +145,7 @@ func (t *SkipList) findExtended(key []byte, findGreaterOrEqual bool) (prevElemen } else { // Element is not found and we reached the bottom. if findGreaterOrEqual { - foundElem, err = t.loadElement(currentNode.Next[index]) + foundElem, err = t.LoadElement(currentNode.Next[index]) if err != nil { return } @@ -188,13 +188,13 @@ func (t *SkipList) FindGreaterOrEqual(key []byte) (prevIfVisited *SkipListElemen // If there are multiple entries with the same value, Delete will remove one of them // (Which one will change based on the actual skiplist layout) // Delete runs in approx. O(log(n)) -func (t *SkipList) Delete(key []byte) (err error) { +func (t *SkipList) DeleteByKey(key []byte) (id int64, err error) { if t == nil || t.IsEmpty() || key == nil { return } - index := t.findEntryIndex(key, t.maxLevel) + index := t.findEntryIndex(key, t.MaxLevel) var currentNode *SkipListElement var nextNode *SkipListElement @@ -202,12 +202,12 @@ func (t *SkipList) Delete(key []byte) (err error) { for { if currentNode == nil { - nextNode, err = t.loadElement(t.startLevels[index]) + nextNode, err = t.LoadElement(t.StartLevels[index]) } else { - nextNode, err = t.loadElement(currentNode.Next[index]) + nextNode, err = t.LoadElement(currentNode.Next[index]) } if err != nil { - return err + return id, err } // Found and remove! @@ -215,45 +215,46 @@ func (t *SkipList) Delete(key []byte) (err error) { if currentNode != nil { currentNode.Next[index] = nextNode.Next[index] - if err = t.saveElement(currentNode); err != nil { - return err + if err = t.SaveElement(currentNode); err != nil { + return id, err } } if index == 0 { if nextNode.Next[index] != nil { - nextNextNode, err := t.loadElement(nextNode.Next[index]) + nextNextNode, err := t.LoadElement(nextNode.Next[index]) if err != nil { - return err + return id, err } if nextNextNode != nil { nextNextNode.Prev = currentNode.Reference() - if err = t.saveElement(nextNextNode); err != nil { - return err + if err = t.SaveElement(nextNextNode); err != nil { + return id, err } } } // t.elementCount-- - if err = t.deleteElement(nextNode); err != nil { - return err + id = nextNode.Id + if err = t.DeleteElement(nextNode); err != nil { + return id, err } } // Link from start needs readjustments. - startNextKey := t.startLevels[index].Key + startNextKey := t.StartLevels[index].Key if compareElement(nextNode, startNextKey) == 0 { - t.hasChanges = true - t.startLevels[index] = nextNode.Next[index] + t.HasChanges = true + t.StartLevels[index] = nextNode.Next[index] // This was our currently highest node! - if t.startLevels[index] == nil { - t.maxLevel = index - 1 + if t.StartLevels[index] == nil { + t.MaxLevel = index - 1 } } // Link from end needs readjustments. if nextNode.Next[index] == nil { - t.endLevels[index] = currentNode.Reference() - t.hasChanges = true + t.EndLevels[index] = currentNode.Reference() + t.HasChanges = true } nextNode.Next[index] = nil } @@ -274,24 +275,28 @@ func (t *SkipList) Delete(key []byte) (err error) { // Insert inserts the given ListElement into the skiplist. // Insert runs in approx. O(log(n)) -func (t *SkipList) Insert(key, value []byte) (err error) { +func (t *SkipList) InsertByKey(key []byte, idIfKnown int64, value []byte) (id int64, err error) { if t == nil || key == nil { return } - level := t.generateLevel(t.maxNewLevel) + level := t.generateLevel(t.MaxNewLevel) // Only grow the height of the skiplist by one at a time! - if level > t.maxLevel { - level = t.maxLevel + 1 - t.maxLevel = level - t.hasChanges = true + if level > t.MaxLevel { + level = t.MaxLevel + 1 + t.MaxLevel = level + t.HasChanges = true } + id = idIfKnown + if id == 0 { + id = rand.Int63() + } elem := &SkipListElement{ - Id: rand.Int63(), - Next: make([]*SkipListElementReference, t.maxNewLevel, t.maxNewLevel), + Id: id, + Next: make([]*SkipListElementReference, t.MaxNewLevel, t.MaxNewLevel), Level: int32(level), Key: key, Value: value, @@ -302,8 +307,8 @@ func (t *SkipList) Insert(key, value []byte) (err error) { newFirst := true newLast := true if !t.IsEmpty() { - newFirst = compareElement(elem, t.startLevels[0].Key) < 0 - newLast = compareElement(elem, t.endLevels[0].Key) > 0 + newFirst = compareElement(elem, t.StartLevels[0].Key) < 0 + newLast = compareElement(elem, t.EndLevels[0].Key) > 0 } normallyInserted := false @@ -319,7 +324,7 @@ func (t *SkipList) Insert(key, value []byte) (err error) { for { if currentNode == nil { - nextNodeRef = t.startLevels[index] + nextNodeRef = t.StartLevels[index] } else { nextNodeRef = currentNode.Next[index] } @@ -331,19 +336,19 @@ func (t *SkipList) Insert(key, value []byte) (err error) { elem.Next[index] = nextNodeRef if currentNode != nil { currentNode.Next[index] = elem.Reference() - if err = t.saveElement(currentNode); err != nil { + if err = t.SaveElement(currentNode); err != nil { return } } if index == 0 { elem.Prev = currentNode.Reference() if nextNodeRef != nil { - if nextNode, err = t.loadElement(nextNodeRef); err != nil { + if nextNode, err = t.LoadElement(nextNodeRef); err != nil { return } if nextNode != nil { nextNode.Prev = elem.Reference() - if err = t.saveElement(nextNode); err != nil { + if err = t.SaveElement(nextNode); err != nil { return } } @@ -355,7 +360,7 @@ func (t *SkipList) Insert(key, value []byte) (err error) { // Go right if nextNode == nil { // reuse nextNode when index == 0 - if nextNode, err = t.loadElement(nextNodeRef); err != nil { + if nextNode, err = t.LoadElement(nextNodeRef); err != nil { return } } @@ -380,28 +385,28 @@ func (t *SkipList) Insert(key, value []byte) (err error) { if newFirst || normallyInserted { - if t.startLevels[i] == nil || bytes.Compare(t.startLevels[i].Key, key) > 0 { - if i == 0 && t.startLevels[i] != nil { - startLevelElement, err := t.loadElement(t.startLevels[i]) + if t.StartLevels[i] == nil || bytes.Compare(t.StartLevels[i].Key, key) > 0 { + if i == 0 && t.StartLevels[i] != nil { + startLevelElement, err := t.LoadElement(t.StartLevels[i]) if err != nil { - return err + return id, err } if startLevelElement != nil { startLevelElement.Prev = elem.Reference() - if err = t.saveElement(startLevelElement); err != nil { - return err + if err = t.SaveElement(startLevelElement); err != nil { + return id, err } } } - elem.Next[i] = t.startLevels[i] - t.startLevels[i] = elem.Reference() - t.hasChanges = true + elem.Next[i] = t.StartLevels[i] + t.StartLevels[i] = elem.Reference() + t.HasChanges = true } - // link the endLevels to this element! + // link the EndLevels to this element! if elem.Next[i] == nil { - t.endLevels[i] = elem.Reference() - t.hasChanges = true + t.EndLevels[i] = elem.Reference() + t.HasChanges = true } didSomething = true @@ -411,29 +416,29 @@ func (t *SkipList) Insert(key, value []byte) (err error) { // Places the element after the very last element on this level! // This is very important, so we are not linking the very first element (newFirst AND newLast) to itself! if !newFirst { - if t.endLevels[i] != nil { - endLevelElement, err := t.loadElement(t.endLevels[i]) + if t.EndLevels[i] != nil { + endLevelElement, err := t.LoadElement(t.EndLevels[i]) if err != nil { - return err + return id, err } if endLevelElement != nil { endLevelElement.Next[i] = elem.Reference() - if err = t.saveElement(endLevelElement); err != nil { - return err + if err = t.SaveElement(endLevelElement); err != nil { + return id, err } } } if i == 0 { - elem.Prev = t.endLevels[i] + elem.Prev = t.EndLevels[i] } - t.endLevels[i] = elem.Reference() - t.hasChanges = true + t.EndLevels[i] = elem.Reference() + t.HasChanges = true } // Link the startLevels to this element! - if t.startLevels[i] == nil || bytes.Compare(t.startLevels[i].Key, key) > 0 { - t.startLevels[i] = elem.Reference() - t.hasChanges = true + if t.StartLevels[i] == nil || bytes.Compare(t.StartLevels[i].Key, key) > 0 { + t.StartLevels[i] = elem.Reference() + t.HasChanges = true } didSomething = true @@ -444,41 +449,41 @@ func (t *SkipList) Insert(key, value []byte) (err error) { } } - if err = t.saveElement(elem); err != nil { - return err + if err = t.SaveElement(elem); err != nil { + return id, err } - return nil + return id, nil } // GetSmallestNode returns the very first/smallest node in the skiplist. // GetSmallestNode runs in O(1) func (t *SkipList) GetSmallestNode() (*SkipListElement, error) { - return t.loadElement(t.startLevels[0]) + return t.LoadElement(t.StartLevels[0]) } // GetLargestNode returns the very last/largest node in the skiplist. // GetLargestNode runs in O(1) func (t *SkipList) GetLargestNode() (*SkipListElement, error) { - return t.loadElement(t.endLevels[0]) + return t.LoadElement(t.EndLevels[0]) } // Next returns the next element based on the given node. // Next will loop around to the first node, if you call it on the last! func (t *SkipList) Next(e *SkipListElement) (*SkipListElement, error) { if e.Next[0] == nil { - return t.loadElement(t.startLevels[0]) + return t.LoadElement(t.StartLevels[0]) } - return t.loadElement(e.Next[0]) + return t.LoadElement(e.Next[0]) } // Prev returns the previous element based on the given node. // Prev will loop around to the last node, if you call it on the first! func (t *SkipList) Prev(e *SkipListElement) (*SkipListElement, error) { if e.Prev == nil { - return t.loadElement(t.endLevels[0]) + return t.LoadElement(t.EndLevels[0]) } - return t.loadElement(e.Prev) + return t.LoadElement(e.Prev) } // ChangeValue can be used to change the actual value of a node in the skiplist @@ -488,14 +493,14 @@ func (t *SkipList) Prev(e *SkipListElement) (*SkipListElement, error) { func (t *SkipList) ChangeValue(e *SkipListElement, newValue []byte) (err error) { // The key needs to stay correct, so this is very important! e.Value = newValue - return t.saveElement(e) + return t.SaveElement(e) } // String returns a string format of the skiplist. Useful to get a graphical overview and/or debugging. func (t *SkipList) println() { print("start --> ") - for i, l := range t.startLevels { + for i, l := range t.StartLevels { if l == nil { break } @@ -510,10 +515,10 @@ func (t *SkipList) println() { } println() - nodeRef := t.startLevels[0] + nodeRef := t.StartLevels[0] for nodeRef != nil { print(fmt.Sprintf("%v: ", string(nodeRef.Key))) - node, _ := t.loadElement(nodeRef) + node, _ := t.LoadElement(nodeRef) if node == nil { break } @@ -546,7 +551,7 @@ func (t *SkipList) println() { } print("end --> ") - for i, l := range t.endLevels { + for i, l := range t.EndLevels { if l == nil { break } diff --git a/weed/util/skiplist/skiplist_serde.go b/weed/util/skiplist/skiplist_serde.go index e528b8a3d..f3b81b7fc 100644 --- a/weed/util/skiplist/skiplist_serde.go +++ b/weed/util/skiplist/skiplist_serde.go @@ -19,25 +19,25 @@ func (node *SkipListElement) Reference() *SkipListElementReference { } } -func (t *SkipList) saveElement(element *SkipListElement) error { +func (t *SkipList) SaveElement(element *SkipListElement) error { if element == nil { return nil } - return t.listStore.SaveElement(element.Id, element) + return t.ListStore.SaveElement(element.Id, element) } -func (t *SkipList) deleteElement(element *SkipListElement) error { +func (t *SkipList) DeleteElement(element *SkipListElement) error { if element == nil { return nil } - return t.listStore.DeleteElement(element.Id) + return t.ListStore.DeleteElement(element.Id) } -func (t *SkipList) loadElement(ref *SkipListElementReference) (*SkipListElement, error) { +func (t *SkipList) LoadElement(ref *SkipListElementReference) (*SkipListElement, error) { if ref.IsNil() { return nil, nil } - return t.listStore.LoadElement(ref.ElementPointer) + return t.ListStore.LoadElement(ref.ElementPointer) } func (ref *SkipListElementReference) IsNil() bool { diff --git a/weed/util/skiplist/skiplist_test.go b/weed/util/skiplist/skiplist_test.go index a35bef6f3..69b38012f 100644 --- a/weed/util/skiplist/skiplist_test.go +++ b/weed/util/skiplist/skiplist_test.go @@ -19,10 +19,10 @@ var ( func TestReverseInsert(t *testing.T) { list := NewSeed(100, memStore) - list.Insert([]byte("zzz"), []byte("zzz")) - list.Delete([]byte("zzz")) + list.InsertByKey([]byte("zzz"), 0, []byte("zzz")) + list.DeleteByKey([]byte("zzz")) - list.Insert([]byte("aaa"), []byte("aaa")) + list.InsertByKey([]byte("aaa"), 0, []byte("aaa")) if list.IsEmpty() { t.Fail() @@ -37,7 +37,7 @@ func TestInsertAndFind(t *testing.T) { var list *SkipList var listPointer *SkipList - listPointer.Insert(k0, k0) + listPointer.InsertByKey(k0, 0, k0) if _, _, ok, _ := listPointer.Find(k0); ok { t.Fail() } @@ -53,7 +53,7 @@ func TestInsertAndFind(t *testing.T) { // Test at the beginning of the list. for i := 0; i < maxN; i++ { key := []byte(strconv.Itoa(maxN - i)) - list.Insert(key, key) + list.InsertByKey(key, 0, key) } for i := 0; i < maxN; i++ { key := []byte(strconv.Itoa(maxN - i)) @@ -66,7 +66,7 @@ func TestInsertAndFind(t *testing.T) { // Test at the end of the list. for i := 0; i < maxN; i++ { key := []byte(strconv.Itoa(i)) - list.Insert(key, key) + list.InsertByKey(key, 0, key) } for i := 0; i < maxN; i++ { key := []byte(strconv.Itoa(i)) @@ -81,7 +81,7 @@ func TestInsertAndFind(t *testing.T) { for _, e := range rList { key := []byte(strconv.Itoa(e)) // println("insert", e) - list.Insert(key, key) + list.InsertByKey(key, 0, key) } for _, e := range rList { key := []byte(strconv.Itoa(e)) @@ -106,27 +106,27 @@ func TestDelete(t *testing.T) { var list *SkipList // Delete on empty list - list.Delete(k0) + list.DeleteByKey(k0) list = New(memStore) - list.Delete(k0) + list.DeleteByKey(k0) if !list.IsEmpty() { t.Fail() } - list.Insert(k0, k0) - list.Delete(k0) + list.InsertByKey(k0, 0, k0) + list.DeleteByKey(k0) if !list.IsEmpty() { t.Fail() } // Delete elements at the beginning of the list. for i := 0; i < maxN; i++ { - list.Insert(Element(i), Element(i)) + list.InsertByKey(Element(i), 0, Element(i)) } for i := 0; i < maxN; i++ { - list.Delete(Element(i)) + list.DeleteByKey(Element(i)) } if !list.IsEmpty() { t.Fail() @@ -135,10 +135,10 @@ func TestDelete(t *testing.T) { list = New(memStore) // Delete elements at the end of the list. for i := 0; i < maxN; i++ { - list.Insert(Element(i), Element(i)) + list.InsertByKey(Element(i), 0, Element(i)) } for i := 0; i < maxN; i++ { - list.Delete(Element(maxN - i - 1)) + list.DeleteByKey(Element(maxN - i - 1)) } if !list.IsEmpty() { t.Fail() @@ -148,10 +148,10 @@ func TestDelete(t *testing.T) { // Delete elements at random positions in the list. rList := rand.Perm(maxN) for _, e := range rList { - list.Insert(Element(e), Element(e)) + list.InsertByKey(Element(e), 0, Element(e)) } for _, e := range rList { - list.Delete(Element(e)) + list.DeleteByKey(Element(e)) } if !list.IsEmpty() { t.Fail() @@ -162,7 +162,7 @@ func TestNext(t *testing.T) { list := New(memStore) for i := 0; i < maxN; i++ { - list.Insert(Element(i), Element(i)) + list.InsertByKey(Element(i), 0, Element(i)) } smallest, _ := list.GetSmallestNode() @@ -194,7 +194,7 @@ func TestPrev(t *testing.T) { list := New(memStore) for i := 0; i < maxN; i++ { - list.Insert(Element(i), Element(i)) + list.InsertByKey(Element(i), 0, Element(i)) } smallest, _ := list.GetSmallestNode() @@ -237,7 +237,7 @@ func TestFindGreaterOrEqual(t *testing.T) { list = New(memStore) for i := 0; i < maxN; i++ { - list.Insert(Element(rand.Intn(maxNumber)), Element(i)) + list.InsertByKey(Element(rand.Intn(maxNumber)), 0, Element(i)) } for i := 0; i < maxN; i++ { @@ -271,7 +271,7 @@ func TestChangeValue(t *testing.T) { list := New(memStore) for i := 0; i < maxN; i++ { - list.Insert(Element(i), []byte("value")) + list.InsertByKey(Element(i), 0, []byte("value")) } for i := 0; i < maxN; i++ { From b08fac00147471c9b586630ba7d2226ef98873c7 Mon Sep 17 00:00:00 2001 From: Chris Lu Date: Wed, 6 Oct 2021 20:28:08 -0700 Subject: [PATCH 108/130] Added SeaweedFS_Gateway_RemoteObjectStore.png --- note/SeaweedFS_Gateway_RemoteObjectStore.png | Bin 0 -> 101086 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 note/SeaweedFS_Gateway_RemoteObjectStore.png diff --git a/note/SeaweedFS_Gateway_RemoteObjectStore.png b/note/SeaweedFS_Gateway_RemoteObjectStore.png new file mode 100644 index 0000000000000000000000000000000000000000..949b8554be8d5fe21f72607e7f510e5b00384acf GIT binary patch literal 101086 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 Per)$R?k?Qvb?(0aOMiMj literal 0 HcmV?d00001 From f14be41439146edb3a5a828a9282ba214352fde7 Mon Sep 17 00:00:00 2001 From: Chris Lu Date: Wed, 6 Oct 2021 20:35:04 -0700 Subject: [PATCH 109/130] Update SeaweedFS_Gateway_RemoteObjectStore.png --- note/SeaweedFS_Gateway_RemoteObjectStore.png | Bin 101086 -> 112366 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/note/SeaweedFS_Gateway_RemoteObjectStore.png b/note/SeaweedFS_Gateway_RemoteObjectStore.png index 949b8554be8d5fe21f72607e7f510e5b00384acf..8b726953aeeae8927af5f2508b52d04725aafc44 100644 GIT binary patch literal 112366 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 z z>tg8X;_=f4$A$NUJIy3LtAfgy+RiqGP5vnXpWI~DT-^NB+~4em<%EBEyt2NokTN

))CIi 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 Yz9ZAo#m^p@%7MrIpPt`V9l)pl4;-Ou^8f$< literal 101086 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 Per)$R?k?Qvb?(0aOMiMj From ed456253d23a3ca8e9c83ac83fce0cc54b504561 Mon Sep 17 00:00:00 2001 From: Chris Lu Date: Wed, 6 Oct 2021 20:36:55 -0700 Subject: [PATCH 110/130] Update SeaweedFS_Gateway_RemoteObjectStore.png --- note/SeaweedFS_Gateway_RemoteObjectStore.png | Bin 112366 -> 112660 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/note/SeaweedFS_Gateway_RemoteObjectStore.png b/note/SeaweedFS_Gateway_RemoteObjectStore.png index 8b726953aeeae8927af5f2508b52d04725aafc44..85f6cc1b57ce552cb7601faeb1aeb7f8689ffabb 100644 GIT binary patch delta 63784 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 Zh~iaS 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

;-G}YszKbm(*)9W_>Z9X#>K^; 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�Z_Ju_Fy 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

Y8t`tfqTK=@%8q(H#teluYU%P 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 delta 63629 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 z z$|xgJn4C>M3WbXO_YE!ss1O;ucHyg3A0HppXryne4=|>_mzI`JFg*?PGipMjkfx@K zQm|9B+SLp?uOR2@@ceD*hYZB;9%8P(D1246175HYsf#zy%pptrp>WjMzjYAM3mBQh zN)|5DwT>8omQ-=7z3B|0IA|x73M@SXQ|F_Ld4CGS7UvimGCu$v;{(-dj zd0rb2KMdI=yR{g&0PI>wlsllKT3bG1>_jmQJ-P}aW&6a4a@ibh1pqeyRB%jH_TXz{ zRW>?M1DGHvCEOk*WDfOWF<$~kOeNqeF`7Qtco)jqPJalD_)*iVU@7Z-O7Gh+`}dU_i=833;Fb?F1iRH3Pz!gPJkNN?;1D6KHeNeEQJ;y; zc=Rg1r`x%qu0yn&F&<-6+UJ=$A;vy7QNH}t8~P*7V^PXPCbfssn3OwJx!};Y&@R^B zcz<%F^h((&PZkk%36#x117PcF-n0XKJK%I@$_(sUlW;0<%*NN6pI`1Z%N(?Uf1UQl zSWcDQls3}qXjQE&3`kiSGQLJV$OQ;27O%x{66GG*>9;^TiuvXiOq({y4V4{*VR3j$Q}0x(0I5d5fL;7-3~ z1;y>V5z=oY9w0Mf8;NvqaL6#XeM0>O%!Cb9Z-&1ev8ks_L?|{KQ za;n7cUUXsvYTl@PZ}^-&SID%Oz{WBd);gx^@s(g+4Ti64S^3PKN?8;lfMY!3^i?hm z9pHu;6#ccu+{g0K>fMTlV!YjO8G^j+ck+)y_0U>^(J-*w!1fceVW4ioyn9?49Xit9 z->kc7!`YM5-2i_0PnAulNm;fRz_<0&xdA91x#3}@)1mTdo-HC=?6zrmorMkchig5a z_|-<_6<9Pgz;gUAJXTI|y}!_I`Ze@nI(8~CjNbb^gJA|@o&&t8Oe|-Ai*IfEyyw#} zTH=mtFy)X@(07N^Q^ZpO>fi19WW4O%LYw*j-R*fkk#lLM17z-7+CN_P-y-fh=f(CA zBhU&g{R-w7JIg)Mrl~1}WlndNSQLSFN)jCvMOl4Y!qu-k$bUxkjw-6;sli<2!UY+$ zVxymCq0JKqcyIcYIWRfiNII=i0is$B&9*GF9xk==qDUj7Ez_X>eV(Xge?k;M1`Qi} za-`M3?z_0Ov;gGm8?7Mt?O0RS-qWMGy7Y&hsTyFQTNVS}r$K zKb_Q^!qO>Dg}uFX#@btK5hi<#Iz|JArJhC z`t%yy`0L+qM1?X)o-z+JFL7s4kdiXV{!%{VHK=D++6$!;cx3nY34~Aem{~0f@mO`K z8UKte(sEtm^5^<*y*;69g~Jkq9|7R|%;qW+?lIfo?zlTGdO!`B7y^$~_e7bY#*4Fy zHh(-*JCNz9Q{pqOf@P!a?^U`Sqw#T4il%!U(tE5kzO#jA8ot?ITmqi%eG2yDzazhcV<>@;@Xb7 z92}N8X`5s1ahR&!$&~ascFP4*FJr&a@@GnKhTk3sML~_vE=D}Q?rDA%5fORiry39t z&;>-%jZ=E)%F!7ZK#{;xU6ue49n1L{xhX^plS0u#Xzylm5g$85Ly{NMozpreYwz^r z#NSG?$e%D3kRM#~Cn*AP0*mYMwe?vTrU+QbBC5f5chIr9F9Z-_y zfX9h2^rc2mBksvzOG$MOC@9E=udzoBy{QH2e9l9Zs$9B`fyyC3u|si5Sp$!KLCiu0 za$6}{Q))8=)FT<^3#H|K2(=R5V<#∨voM>%Do1g<^!A=Y8FR%7J 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 From 7584f758b80077f0b4d1f6bc000b98b74258f034 Mon Sep 17 00:00:00 2001 From: Chris Lu Date: Wed, 6 Oct 2021 20:37:37 -0700 Subject: [PATCH 111/130] Update SeaweedFS_Gateway_RemoteObjectStore.png --- note/SeaweedFS_Gateway_RemoteObjectStore.png | Bin 112660 -> 112660 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/note/SeaweedFS_Gateway_RemoteObjectStore.png b/note/SeaweedFS_Gateway_RemoteObjectStore.png index 85f6cc1b57ce552cb7601faeb1aeb7f8689ffabb..c39cdc909c078b3c11a5a6e330045b6922b1177e 100644 GIT binary patch delta 62 zcmbR8fo;kMwh0|v=Bmby#%6k^=4KNoZ4vSJxA2Yh%#V*MF4Z-w%r&jbHb~kW#rT<< SrK5qXpgE9tdmu04;Z*>(8x`vS delta 62 zcmV-E0Kxy1@CKCd29SsfHYGDbH8d_XF*%Wvv=nGbL1=bPPeM>(by{#|W^Xn)Lu<2I U0rLw4>%FzigHa8)Q4ImZs?Spwx&QzG From 2336a397dc4e1e22c2ddb76edf8fe0406ce37aa9 Mon Sep 17 00:00:00 2001 From: Chris Lu Date: Wed, 6 Oct 2021 20:40:16 -0700 Subject: [PATCH 112/130] use pipeline to save some time --- weed/filer/redis3/ItemList.go | 27 ++++++++++++++++--- .../redis3/kv_directory_children_test.go | 14 +++++----- 2 files changed, 31 insertions(+), 10 deletions(-) diff --git a/weed/filer/redis3/ItemList.go b/weed/filer/redis3/ItemList.go index ae4e61cfb..bad3d2efb 100644 --- a/weed/filer/redis3/ItemList.go +++ b/weed/filer/redis3/ItemList.go @@ -66,6 +66,24 @@ There are multiple cases after finding the name for greater or equal node return */ + +func (nl *ItemList) canAddMember(node *skiplist.SkipListElementReference, name string) (alreadyContains bool, nodeSize int, err error) { + ctx := context.Background() + pipe := nl.client.TxPipeline() + key := fmt.Sprintf("%s%dm", nl.prefix, node.ElementPointer) + countOperation := pipe.ZLexCount(ctx, key, "-", "+") + scoreOperationt := pipe.ZScore(ctx, key, name) + if _, err = pipe.Exec(ctx); err != nil && err != redis.Nil{ + return false, 0, err + } + if err == redis.Nil { + err = nil + } + alreadyContains = scoreOperationt.Err() == nil + nodeSize = int(countOperation.Val()) + return +} + func (nl *ItemList) WriteName(name string) error { lookupKey := []byte(name) @@ -93,13 +111,16 @@ func (nl *ItemList) WriteName(name string) error { } if prevNode != nil { - // case 2.1 - if nl.NodeContainsItem(prevNode.Reference(), name) { + alreadyContains, nodeSize, err := nl.canAddMember(prevNode.Reference(), name) + if err != nil { + return err + } + if alreadyContains { + // case 2.1 return nil } // case 2.2 - nodeSize := nl.NodeSize(prevNode.Reference()) if nodeSize < nl.batchSize { return nl.NodeAddMember(prevNode.Reference(), name) } diff --git a/weed/filer/redis3/kv_directory_children_test.go b/weed/filer/redis3/kv_directory_children_test.go index 77988e1a3..03c15ec35 100644 --- a/weed/filer/redis3/kv_directory_children_test.go +++ b/weed/filer/redis3/kv_directory_children_test.go @@ -58,14 +58,14 @@ func TestNameList(t *testing.T) { nameList.WriteName(name) nameList.ListNames("", func(name string) bool { - // println(name) + println(name) return true }) if nameList.HasChanges() { data = nameList.ToBytes() } - // println() + println() } nameList := LoadItemList(data, "/yyy/bin", client, store, maxNameBatchSizeLimit) @@ -76,7 +76,7 @@ func TestNameList(t *testing.T) { } -func xBenchmarkNameList(b *testing.B) { +func BenchmarkNameList(b *testing.B) { server, err := tempredis.Start(tempredis.Config{}) if err != nil { @@ -102,7 +102,7 @@ func xBenchmarkNameList(b *testing.B) { } } -func xBenchmarkRedis(b *testing.B) { +func BenchmarkRedis(b *testing.B) { server, err := tempredis.Start(tempredis.Config{}) if err != nil { @@ -120,7 +120,7 @@ func xBenchmarkRedis(b *testing.B) { } } -func TestNameListAdd(t *testing.T) { +func xTestNameListAdd(t *testing.T) { server, err := tempredis.Start(tempredis.Config{}) if err != nil { @@ -169,7 +169,7 @@ func TestNameListAdd(t *testing.T) { */ } -func BenchmarkNameList(b *testing.B) { +func xBenchmarkNameList(b *testing.B) { server, err := tempredis.Start(tempredis.Config{}) if err != nil { @@ -196,7 +196,7 @@ func BenchmarkNameList(b *testing.B) { } } -func BenchmarkRedis(b *testing.B) { +func xBenchmarkRedis(b *testing.B) { client := redis.NewClient(&redis.Options{ Addr: "localhost:6379", From 03bb82043c3fd7be6ee419a443081524809eb5d0 Mon Sep 17 00:00:00 2001 From: Chris Lu Date: Wed, 6 Oct 2021 20:43:02 -0700 Subject: [PATCH 113/130] Update SeaweedFS_Gateway_RemoteObjectStore.png --- note/SeaweedFS_Gateway_RemoteObjectStore.png | Bin 112660 -> 130257 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/note/SeaweedFS_Gateway_RemoteObjectStore.png b/note/SeaweedFS_Gateway_RemoteObjectStore.png index c39cdc909c078b3c11a5a6e330045b6922b1177e..82c90e369f18391a2ef3be6ee9e4116b262e429c 100644 GIT binary patch delta 57910 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 Cj#zF0 delta 40174 zcmYg$2UHWm_ID@>f>IQfB1KeCX$hf9hlHMl8X)wRkWL7Y5XyqsXab^wqJUViAgHK- zR6&Z;M5=VDDjlS^FTD58cm6rM$?oiC=FXj4e|Oq9cy2EoOgSkL%zF?70$H3It7QLh z(Cy5OK?SX+{(wM7PNu3l@)>lh4ELXtHEuwgU+d%n&Hl=8Z!|Pf+oVwGL(I-YUk|i@8gHmQo~r%&2Wt1Fc)^K zf2cEqNVLXLeC)C2XjKz-lbNk8%bsPXg(BG6YWq0^V4?OtD3?%|Ky9+Qi=(O?yK@K{ z73!}>HL~A zNzp`*i~>nmGK?IIF!84#wES%-CPcEfhBpOfX`yb1B>;9~ARhq*;-CY}D0V?5t5_Yi}n_ zJF*3lfXDdT!5Bp65Soh(#mNPR^GBJgsiUYYG=WBD`59?3&3ui_!J2pzQ#cT8ghOEi z)YPmQU^8F#05BsAlWgXTF=3&>{sCsest_%dBifQZmXU1`8p?9;BRhwgBeg9t+9Vi4 zL*3ZS+S1I?j%BPy)F8vdSV$v#En6!Z5{rPFGSK#>c1C_s6MrLhJ31YMR5f)X+qeWe zIe6PpSQ^eDI7a{yNLLH;H}&(er8?t%OoN!#B!43qEfDSO4JQyxVfJP$0t(I^o=iY^ z+XljX;BXWUN;cD=1^VNS7$z`OND#s@z>JLYF*89pv%;uOHg=}wP+vP5&ceh6>1fTM zLQsBc4%!HWu^rmf!N&sY6KJ8WX%gtCj>G`wXp}#L2qrTu@Kj&0jk+(3X@tj6f=w;0 ztPnOZ_Q3XmMn*oGcn2dkci>dwev_ZM8J39h4K>12y|HRoO>bXgRWl<P-fB9284P(!HOKruK!Q*%=js2!8OuSjTL zfanwG?~f%S!8mWak&T@N!X8Ft%bPJ+&Cb?EBNVOe3h|~mL(J%47t%fl zEubW4B+QbA(1cM5&Q1WBVM|mC3vdY_kj%_*W;U2mhJ}@luZxzm0~F~*Az}h;P1(zD z98ALo;Iu=bCK?VX6STD{%*L2#VTJ~q`!MiOD~Of0i4_H^O7YROvPU>;6EP;LM&>X| zkRvhF!i5e~^|P@w$I)Oe+Qd+6Gc_x`i=QLL39f1ku*p!jQfE;doP3Q#(AxHXXd;b_ zBw2*nL)8cx!FFc$WFML_*xS$B+>(kiN7$>ISU@eEwVgE_g2H?)QRbF#BU|?JsiPQ{ z>Qp8YPk|Z3g4AJtBuApEHUs8}QlmSX8>_j5YU02SC_kJ!TQck{z3E^xOT4Ks;OIp6 zMjNrLP1M0)m=D8`MpDJ&kz^-ifStOrou56KNwHw!>$&&%r}HhmV)-={2+Cz|Z2aLL1ppzY7WrD;w2HAxKlFYU30yOAWEEXJ3alm>QwVkqBh;!#4!wJs)a>5S(E^ok7gT$U~P;OS=9v4fPyu(y`dD=zS6=1 z*d*`UR%0qo-8U4;Vw3I102}%HIuZ!RaIn9Iw=v0uZfk5E;71Q45y0wnA3GDYlNLRc zMl_+BQ1LViOR@_}4M77ltjxmvur78uIv5{Db+o|-1YphWv6@hV8p;Xch_>;eJ3DFt zA+}}?L`VqDG|-W53NbWrzln00h!t7+Z`Bo2N`$hAqsQ1~bPZZJZ$1j1U~l*TE`?Om#7JvUFmT z40SZdX$HZxDNsfL+j^LyH0g97%Y8$pp^Zd_Id~hxtc|gbM5Z4Sjb{?za171T(wqI* z#oR%g0;aQkXmk=5VrON9HX;#e7Bo6E7_d|IHwOnm$W~_lU|6W74-o^wXd->kaE!M( z1&p)8neBT#JB$5qZ+~<3AhcsBmSJj21N#MOse%KI9KE&C2y1_Eh@FN5)d*{C22r!8 z_^RX3Fmsd^gR0Gf+Cc(?G_kg9>thN=yFjQurcf6o#Ds=5u>=DF8bmNn!-nOIq+rn6 zER3p|xgUuVs_Emy<~xjn4l>86YB`#*Iliw>bSBI`&@5D&sG*G@Xfsq*_ZvwJJBkg1 zgbD~VCYag=_|OP}>g;B$BiaNG#i<3^Y8#=gz*KV$Un3%dVCENq-uEF+T6RbR3P71* z!d&bE?Sp*+agY#mhI%N~glGb`#A&e|CX@jQrGrfvs%8!*&g#}qA!;VpL7KrVb5$Rz zV;~0U9YUoeeb^R0Oq1q|h4?s!L2R^Z&wu%NSl zjHv_^1WXRHqrxBn#D+w(WEf%W1KF#qY5^hP$ljJ}RuDU9AAb@&7)7QAJ7Rsc8TLm0 zM%HvPlVyxBrNDeO%~-0=27Qr^u#b1kT6->gTO|{8pcv^@#!U+j?HnlOMu?KTD z4Gpmgr3Vu%sjA=*KT84<0;OQW*iU@X)|LR>#2SW$;wk>nFtDW~6U8*4Yhu~_F^2jF zJ2|2lX!fN&gB;+5_Hm#wRWW`fi(mrE9&O=IG11U6F;UY-Sy2eaRO?_gjDTgoj4*Ss zV+Fu`F%UW)38iRgW5clcU^TT6Y>*}#VG{_&+923dW16uEq1rLo zahj=(#=e)vp)}dn01Kts8ao7=k=ZR43}+me{oKOG(T0eihlbgcC_yY6#yKb~?TCOZ z_l;=2z*wk2F;sw~P8?VUCBN(|UcYa_VO(u z)@8S@GfMs@PlJVkbf=|%_mSM|RDS5Q9}j1K32?p6Hfi6+WGwT?w7i;<0UA_K^{<$# zQGRf{gnyJVkZ&S>RXMSOR49Al={fUpepx56tD^B`0`45JBH+75my4a~jvu+;7gnLm ztw8r)Jm6jl!!$pri6$1*uIQguTY;oZ@?f zo-$JNZ-A#%++B{dE6vc-@>}8~lk1_8BQZRGVMjUg8gbV8z7tlKrcSrihc_-=r6*x5 z)+6iBl7>q-$*yW9#!2_L1d0;k4PQ%3pS-?ZZk@kFl=$f1^+lbFYSMI9srFj^g{42_ zhDl;v@tXTB9B?_!%E#vjssEpRNK2LmYXm!VD z6a_Eu;&F{g6x=B4W^q(Sk@XPA&xN(p?vrId z)BV2|U;77O=Jc{SnTp=o$u%_*O~mTVoMg(TE^pvx*ZSV#zQnaRIp`q0PW*RHI*^&P z@#>?J`=--odR3t%t~6ugbj9fj3F`+<4X;WV8ChF0PLG>lIekY1j!8>b`ECCDqmIdwvQwh9UOjPp;QwfKcg8fLe2oFT$`1Dot0-A#HeITmS(9F97`P{@ z@h`Mf>-oKq60)Rnc)Zs`+wl02_=M}Po(t*hdR&f6t2E8bN_YOcSJU-CHrvCCbhoj% z`+--a_|lZ>T0p_quLDVk=BuuJ=2A9|t-W-{psM=RQ2$qYg$}K3?AO2D;19%b#-uRDX2=R86TI;ySwVdEj%9oBehYy-u{LZ#AXq_;u`H zR_b>Zo-gLpmN9#6+rQA;D)ad3F-)x=-OpDX1OZO(qdFqlWFA4Ofvd7rj<3)b9eLq{ zD|;B#;{}6Fv^kfGl+5(lf#$)x>)8JE^KCBmt+suwyrIdNFgxVM(6@T;oy10gR5`_OcKb}cNUB6wOQq5CONxtUG+^tbS3 zjXVH<5PL=X*_3``*Kbwf=iS_!nv{X{m2*~iJIMoDsxSX;9NQ47I;_xN5-O~!s+ux_ z&81Pz)7K(;$vf#k{oSlt6pv!r_NCQabgsn7e`TT@+G4#$k+0X5TU+-=*Hsis)ky7< z^Eac8w3NBuP$+n_;_*9MIIl5VKl0OrZF&lja$``Z*r#K7I`#KANZJzpdxXcQ@{quC zp0VwF5=zFl-pWUHU9{%b$J*b8$UphZ@?_&HG9U|>k;85U??&mpNy9;3NgomE@v>;F zB(hezNY1DJo%P|0HHcb3^{Jrtem;JFS|2`-d{3R&%>W&A@RCpbn#G-Sy-l>AV|^Eh zDd(eBus-!|#YcFwLVkse@)8p15y>V0Cf1$iJsO*v zVMZPw&sG0?aBeC7YWarhuaGr5hHD)tqp{8Dp2x5Gq^ULL>os&lh45-!?vTPP7v3#R zN1sET*aeSB>RXT8{gFFAMcD}b+^3g4H;~yT*oKHnJoZd~^wim2u-=+cfoXc2;fkD1 zp+^O-ub67UT%GwTqUbmj0pBeTTdsCqo><1^`1@)i^>>}m%1MrQy*?}qbp*Uco#s~# zkMZ1ZU}e`o5Vtszn&NP>Ohd`Edrs+DUqW%RyJY>AnA4TZ1BcZpz@?$RVb|Y5^&wh& z5iI-bdD`&Z_xO&HpoY_s)+7ULRN~%r_(41IQ=BEEuXm2SoE{w7)>KYz==<>Mh70v~ z(uk3pF}d@32k2*bv2RcPxyfbVrzOFD`yMGSLQrpdDbRe&ez70r>0P&azvRzNo_y8I z?S1G%j~2f;9j5}fuiyWkKHbYh+hPp)mVcbeXbGZW!B~FqpZ`tgcYgLXlm|9_;7tq{+9am#~7W;{Mh@z-y`oXGUhMz zCOr73KBTz0bzgDaG0a}A7%pS&GiA8AF@`c9E9VfKzpRKeGyDm!6=LF9!wr|0>(6x>`n2?Q zV*uCp9|M;PGYv8;6q79008uFJ9Q<{zT&vM#!4A6gI3f5R?+wGT`Xqyyfzsul2HOiJ zcecn|$2PXzetwGJD=7OV=Qs27>cE2v52JZ4zagjAF5BF$F{7wCk8%HJF4;s6osy3D zdA-}Kd?Ut%Q_f>I*ME1Y1nzdNJ;I^nYy?pf>I4xDQm>bPPhMj(3doOvcMQSjha$fh zMDl#!$=V?1V~q#S|9CyQm~-RT!AW|C1+lb&t7g90=-PaiAQ?ESFTSO5OVHqelUw+) zTWM!TBk~4v8g5E91x1IImX@x8n2!dyZbxy{?F!4$JnTU_AQE?V@5WO16M3&szl)*h zn-idn+bTL=h~0q`GQddBfS=Dl7oNp;L5WWbQjNSGG$l5cYmL;oN;1h?CBM-mm-*-Ah0E^YO1Ut@VkAan_U3A6bLGT^lKG~q zj+3qSyx%2<#oPPKX}iaX9;u$k4yhA9+h=&HQGeFY6h;j$)d4dT`1!Z89H8YR&wZA; z!#-&798$J!i%fad?k_9DdDe#Q+LOco{igMGzOaAQNgckhFsbAIT2LD&l>xRIyT?c3Pxa50xqoW_8cgwXio~h`u-|F)6FJ}o&UJ%`eMac=8xb&mZRPI zM{)y;agASFRuo;!{Cmc=`{kNF?rNNDsERKUq!(hKAPp&+9~t#i}_6Mkd{6T^i$UEE`Q2)4lgV# z(;OvbZ*4|N;YD_{BF<`(Vw7BqeZT$C)!pp0^8OI01K;g@(UJ1-BZtBi>a&ujTT-VI zaH6a)++?hrw$cqC;`$>CH=0yNj<0__0C>8%2@Wi(1K*!p&~>+$aH8OU+W-D`y(+ow zH8DK9)Y3T5EvWbJd#ikQ#5)Q*44u{qo$)%Vt1iqNZ&-)0HA1{ffEjYbWCA$#M~gy! z{JU4>)3Ee4Fi*+EvPD5SKMnE-Gv{rG8RWsbnQ(`OXQyy`YPfvAk)$nE^6c*Sg|uH%p|rOjJ;0&H1qH?5ux zH5htgk@oqRob_|Y04@KwW9cj?b+6%Qe)h63QfGqsWrB3ANzzkse66%E+^VEc+dKaC zW?jJs4`x-&1D%mgF8u=4JhIpSdqLT`oC(oj0+)V0lJ`(VGEWL{B2x|g|EiHZH3dWB z?Pxy^xu<4rCSR~9Lsj>OaaoG&Y}F0V>wqpa<6iWUs7tP>(?afg zVk^e4UJvtzClkAMCp`yN`-p|-j*Eyyq-SO}9S!mCdMo%xT+Mk#IHfVUBK)OfoMHYk zEx;zCb@NF7p6GS)d#`K?Kk|P_Be-baNa&C9|GQS`Qe~i+4IGW#Ir**x=6R^N=5~kg zkF&*1SB;Z-{)E$)(_at24cZ)a&u;d(zj`kEsbj>My3%-R^s|#3!n_hOM0e$${xfDb zn@h%S>ReR+bGxw7vm`(ajSN3N^)T{M&?@O2@NMenhvA3aeWzyIbrwp})+hXAT6tzc9l=MUVFyT&rdDkx~Ff3S>UF#fuD zi16d3^+}leb@OehisLzqwVzj!Ep~QCSZvqINc8^`r!+o0e?= z-RS(6o=?$2R@`ToeE(1gDzY|uyud)i;euEm!8uj0>gev*m=DPNyUT7f4|MpxZcN+G z=k%eiJtB_uYaP2WB5+PiN7(LyFXk?fCg1YAd=;hOSJrLc+Ooj(+S})*q52-BkhFU% zuiiySom6Rk6rregm+i@)92NZ)R4P@mx4Ts&#rplIk5a6YKRw_%Ui^6`JI@!WO>WCw z!^^wyR1S%5N7B}LN`7Ys6{5v@wof%71|?EFH=S5cZkz%ydw30+hp!De>0V1iUb>LK z6&$}!t=bWoo+`gy6fSi$f5xrwRb@k_P)Z!fL&(aO0JMCx5^NxtRpa)m50q?R*)Lyu zu8VxbpQw3eDBx&0FM7+v=tJzq4^{H4 z)D7=?={SeT)Ob2RH5EQUS*sZsaGUJkI8YsYCy9UhjPoo{dSl^gay00M;W}@I;#i(4 zl*IPf^YZ$x9|>t{vkfl2WdUlp_%(7V5B)?+M{TEb#qUQrMFEE6b?T* z&hu5}OxV3L#nd*ZHS9@0eRayl`iIOSeTz0lFWo4D|Lc+~VPCWxC4~NytZ+xTxZCgE z7_Q#qOp#i+^Y=VwJ1PgUv$>MJ%G9keOBA&oTkWT!5|eRP-+nLNfkkD0%z*qMi%{0Z zUnd7#zFhA1rN{ajKLtoTUN4UwJlgP{FctgDBT8xYoZ`Jh9>v3K(({kP8)8$lKGpR-*11{AlaIpNuhi4teAW{eyt*`#n*B z&HX$fVk z&&-L}suO=|zPZIFM5jf=DzkXlYW_x;*S;dqDq<+O1L0mE3C;;=y8AlqS;X48B{Y!e z*M{!Qca(s`mCwrcn7KtD6e`=f&s`&DPl)wy9Z(X?-*QdKiCEmXkT8)g4{3rJOR-PD=WB~eJn>Qr^5`CA7p5#3~Z)h*k^Y&nM z7T@7DWzgNsB{H2O@!$gbK>cdPzlbqx_}OTGqnl0hUhW*E1W?;UnPMS&x@6CD+s8am zD739);;t*fEu_owZVtbH5q#GL_t3HPPh)EVeoV^5at^TTEOg?@;>336z3wBwtX?RN z9P%E5Mfi)&x+r)5Q}8I^pDWQCA3yHpJ0z^N_o?AK+bY^nrla3Jklma9dd%j{lHRHI z_X0~n&X*pB`&5GJdzTGsubdN+ZkA}f^1~tijCaA2t79c&N8ftmg#U(A2&cORSv0Qo zJ~m4xFHg1r;R4l-7Ym|~Emh#Fdu3%(rCrzT%YI(EXkhmACT9lk$aB84U#+TmaBT+MC9T(!4Fzs=)rBlOoEKFubcp}bPJg>l>&1%;D3X~_)-oQ7jflf)gF zFMk{ijNdMlyJ=SQiSd0T$FGEVHf;Hz!~2b!pY@hNdIl#YRHw(D1A}pCDE-phq8Y|c zt)Rh!82fhKNgFT;9_>GuZ}Sd=!eSY$z9!7f;NG21&(?BdO;GyCd`7RT;h$A?7kBrc%^H~YCwW^^x zIp1RuA)C2->ajKAuUfArOX-PE_Y@W%osZn9GczAr*S|%Oe~cZBO?bt_{nm&c*QSpM z-Fc8G8J#E8wEEF_m}r`VkjQem_AGL1w)l5{@%$W{+*Z)8K+AJ+ICl+TR?QBIHZ(q+ z3kB$6nS_E{Z?9m;~l^H(ZSxg zI&1SM>s9s$8}-c>!i)HTdnq_GP!m7nBu(H};D0bg`%(XoZ+3YN0fUO02Mp6Y*Ffdu z{FZ;p_s9e5e`rgWKnImO_z>+)YTX@)60eHtp?U`k)b4K1J`*g&WkWf1@dMo_xkQQO zd?jZwBRrfV=>#eBK*?s9yIqmeMy0I5XXn+hXj7E~lhW<AQ=7et3CaV%;8g+1R9Z zgak?~o=`u0$Qi#n{eHZIdrLHW4x}ZJMz~WyDRfMZb_t~Fy=Nn7cxBV2ow>yu%n?X% za~n9s{tVLT^kj1+5?RPb=|dwTB5pQ}U1)4+NoxD{&GOr~Z-n2WWtzuNoKOaV8Nt4)8Xnz;4qgh@Exs;qqEk$0i0PENG&=e35qZc3nlG-0cK zutW>g&{gQUcg1RV{)e*Q&f4w?sKKe!g|+*21v4|vM+YSF>TA~~T{-)=fas=|pNONJ z6||cGdNh1mUFY_2=Ws}v$=Ql?VQUqWvs1svM2Dl=?ti1b?nm%f^2Z&bHL57~?o2)V zcdjkb6ACG;hC(!ZEW1rk4!xv|o*I|r{g3~$EKf9Ebp-7s_J_HQbv&%h6#g8}u^@D# zVE(;j7Ed&1^Q3qtReeM${7!rIDe39`CsV9r-pH};mctkE>vp*zC==ZeIT+-5d z>{E&l8qz2U5%uF$heu9SZw`E@GPzTKZRpa2%q!cGs2#&CoY_k<*8N% z(wHs7RTC|sfnBzp{%4TKXnWQqH*ocemQHQ?8P)oWk|!H!?@u~g=v8EgDO+tnJE36| zThQ~X85;KS#L((o+5F|K!;Hga$Dl(J*Dh(l0xVv{dc=P5>rzk#|OZ$tm%zzSf5KjZGzGlZq2NsWwfS zk1$G0hsJSKc;BYrTjGvZ_}l;BoT*houfga;>_G3mXe+OG=nf0yyfsxq87wO9Rr4{{MiW&%QLB&$;jfA2vyMLO*Vt8dkmfr=wmn>%<(j`J^Xi zd1FBJU>0Zh$NR$sZgA~x2H>2WSeif4h=|)wWJ#y$cCHsA)GU|}F z0x5N#s`CDcs$u#NZ_JGdzEvmCH1xK`R`2@q`C~QJOjdlv+%FbrK>p{#NVJlWM*LTm z=@$ttYk2jt^@UnN>Gc&Hb!~n1wa(b`((+pWi=EQZpyOc%zWHr(^MLoeHNCCn#B9Co zipHZQTl$ZkpLT7PerYSe!qhxUl=C*(jw@NMfAribd9VC>u@dGzZ(@^l#In%#YP)1_ zrGKpBrEbbrB&F~A1eV%8Rrpgy@JmH@?X{~rE^ZM?=@t>?5+9C^z14hs?$X}0K(O{T z+~wm`c9^_u{Xi>~C$|`Ij8IFG;k;v(<2>GfA|hQupyiWZ?0BrpilvhHq5ay^UbFO^ zQXyw!u5^;7Em1iE#5H?~Q)@J*ZxPX^Q()27_w|E7P}$g+Ew|I7w|HmI8>^8|&!1R~ zh{JJE6v_&(6NbNGNf!t1shAFVbnoFd3w&ZI6Wkw3ibK1~UlxJm(+M|HwI`ddpWCj8 zTPg2~tl;6ys=2(q?T-JWqd{qMuKJsuKhdP&x9$B!!2R+Wqmlm3FQxhN5Y}*>mG;%J zoS(nQsi>HmsoS&XKqjCE6G96S4^M-vEs(`b%p5UeJW@%Nz^;f2uWp~H{GQxcB~(3C zFN=ItIe5NY%#sPbr>(3#r6xpRI_@!(lYZ7AAs!&##M5VlQVl*cwNH(omp8YX#n;!M z1i9lQCY48i#Y8^$F1#S5ade}7Yt6p>&hl_KfB5^?JuCm_T3B`L&q-E%ew)JM*ujmf z@DM_mc~DP*d(&oMTtX4N%>G~5>+#njr8MPbv3e66D**^Yapz~RO`hXixiq$IRZ#cn zx+*3osLxC~l3Pw9zX*48;*O2;7tGou{U}Fu_uw_a? zP%%F{>^?Q@OPIm4vC+{KSCC)5Wc)|HRyMF(k(yEsur78hSN!|YjMCp2=RZbqc>6i{ z1FwTB$26euGVXJptE9lJz8z=PEvm8Bv&Y|u)uiNlMix@hpx1~21H^k@K!XNYI5zIDpM@YBX_7e9VdE$|i zMa)>sv7<*lDmNxGk8Q0?H|QQSQkPQhdF^`gx^$G5o!$DBbC{C6d_#N_3W2!VaBN6c zl3fu3zI^79POKHsIjpPoJfQ4%ZbB$B0~}MTYV3KvFgKxNMOFyJG0ndi59xlf=RUl{twzy3+qF#)M?1{q6i8X!O zeTDdONp48NVjPNmIPIhLS;TSacV30Lu7kNU&r%bsPX=lzZaH(1K&GN zN%c)A;=|^>2z~T6cI={;{Y$`e!*5>jN|NHn+l}Jm-`qp2tgRnxvK>ul$**e?Owdy_ zaEO~-r!DgT+xf!af*AH(b+?H3nUQzj#!D-tTVEshEFtUOChi+tP}9A(KZ-hs&$3c- zmsocILAA%cH==oA=Q;SSt;6L8Z)MwC|~Zku;! z?8Y1akV3~--3*l8io>^*(NkPiz&y_JzV`_l|t9Ie~dwAOxI6YKIAeUzgo9w-i! ziU-HJ{L5VP(--^TEW5Dwz-TCcOZma@FP8Pqrxkb=HuTCqj>{Sf@0E$g_|F8SV(Y^( zKQ8RzuU0jb&!fhnbiG$b;4$5_R>&`@x~(b>aJWI8u2rkMui|b*w(@fGf$GrXAZ)ze zw;ROx;JdTFahEVZO7#n3I3H#K2NJ)oB}Fng-@tc|-Q+u-_+p;CS{oGY#xF6rc&{h6 z^w0^8lAeEW;rcyao%z82X&f9H|*J<4u3u`OfBYo_?!#h&4R^5 z`C^H&rau?kG9q<^&nj)bU+DNdPb$wcet4ZL)ORw9&lfXy;ckOCN6Xru0+dnH+pr^6 zon-EX%(pZB#t$quMJ>&N^sutGU!TDY$gXn-e(Z`6F`J(>FU5yQ^npEyXLrl30*h=QrZ@v2;H=#_d7+@#O2o zSVJ;3_=kn{kw+&KWB&aM{x{u2?aFs4dbhN^TnO#bzbuLDi8F-lBkRxmg*WTW$FFC- zFg^UhP$d3gwPs$HtPLlJ-Wp4dU9qUK9(lRGzHZAH3d3!tp#c30f+;dyRck}xu2i<| zywcok%@VvY%@jHuV&Qj;-QmoZ{jV;IaVOD72cLsD;vVuz_{naye7__vf>^l{szAMm}La5cGNqJ*#F?&zNbDe_Ux- zB2;p}d-o0;vA$5{3GA%Y#>dBtzTAF(KF&~apVWymenVMDgZ@qSm=yQ;8dZ29KjSK2C->#_=0tbuQ> zt=Q6LgWT<1!iKC6m?LgG+7^y8i`)KPPQ`rKx3T{{xGi!bJDwq=dq0It`}erf5ue#I ze)OhZck!lw${DkDe>S*K^Sk}=azm2D$(|D07|rz~DU7pviP!VvU3t#a@@(3Jo}{Yh zz@q#H1z($Tvt>c{1_-bk+E9>eQ2A2D$^2rwr2O3<#bv`|m=k|F(k1ui)3wVNY;lFt z;-L9WmvMr9+b##FG=`F!7wirQ`cpUe?C)R1c*)|EG$C z1}R6gWeT*!DP|H0%lRb4)`~?PrBvxLP3Qh&`$eOXL$~FYwEyBj%AA3G(Jlb?erxmS zksO}rBcMvqf^4+$a%Ev-vgX~tm7x2aIXZjhAS=*=Ln9VrA1g(xok4B)X}errGN9Hq ziRj{_{z!|m5Bs1>_KvoEkE=AuM5Lr}ttDcWY5fhZaxJrk-))T-l%5%q%(IXx)|A~? zB~lFn4U0;hdiF%`l#hVTZ@~YJWOXno{Qw{l@|2ewks};UzpsX00{;B@Bd@nHZv(ucQ2y2xg8W?#;z zSqlNN|6CWnDGlPg0&oSgIM?I1EkLbYhQbENs?UMMGczJL4-MmnrcSOVC;bm9pp#mg z)>nS`jO@fkVCKTPatx<7O9fqXmUAv^^UT!8f>w)b4>NfKp9&vUEQYro)VUpVT>{C7 z|*@J&rdW$bz7YTNMsgg%df$(VC)>*&0&NRyiyA0H2HYihD$ z%L5skuI%pSUKWa=uoDYo_MS$i1i;wXIK_Sj_{rXk@W#9L;P%E;w)01lOKZ(l{-Sh6 zmr;+ZsK`M!db{K3?5stcYJC{|dCKU_-YPr)H{|A&VcQ1KlYlwdROuFm%4Sai>sLD< z_#t-b*p1w&N_ItNJnVgLKW(f^B7q47WgfYg4m!*c`2KAxyX5|}{vBdkcdYf#5(@B& z$MRA6qJ}df>f5Ot;D+)BzcBR{4vv=i_w`#vLw9a7lbZ(kbGV`bPLYj<4DbQW9I%xc zDTQA(OEAzACZ%R$1>_%a8l>oz98dGytx|Hg{lm>nX{aLW@68g~@_op#X<%UB*#}}z ztEGj-*88%a~@Ex_Qs*VU>Mv-|Hmtpwe8%i_d3WIMK`A6AX9;j1r|)(UDvCT|tLKPRXqg zg}IJb;d~Z0^p)n##Hd_yC;d(UnCT1Ot%}C!1x}!alu?}q7!H*4e6K&zsPNjcqy{5? zkjpcwOt@E&p_JG8MGx-XBSZ@hVXyU;&-IV<;rP^VU(zvMUHnsdueImvZ=Pji zneZH^E3l}>-M?xn|Mi?{hMhOI>yJ#2T?Q7b_o`PL-DBTicZRh#E>AE1+W}0#1IdjB zyX8a(Zuh7NISY4Wp$a88)~6$7+VD|{RtLvDwl70FzwtXKC8gcR0w(g?+6(SzHl2q@ z;g{F6C$!umlm*w%6k3d%4e7l=D#XjawSP!SSQW=|+Ws3zKer^N(0V28eK+K0*4L$g zt9cn~WNR5S{!;t#B1LKUCE&t1W6RC3C$j$W7G_B-519;#Cjn32bn7Tiww|^+wvhw2iBw590;+O zHE0GB#69+%{7Z$H=fWF#*_~V+`V&)?d#lUyz-_DXj(2-a18{S|UE|1+zi;2&EKb_l zmT(K3j@^gXipa!;|fLH_L<+a1jhR7BSz z*wyN~_4TpsQ=)HB0A4n`U?X_X;JSrji#{_Kcq~@_LHMuYN%PdUu#7+1JVyp3Bo65M zUVX@Oz)y`2xo7y{_y&)3%QwC1&0)FIFHX=m znf*hFif)=S!VjW+1P_0TkGooNHzID^c^CS9s0Lo`CzkgVpj>-5eLdk_eUjS#diBd@ zhm0Gjow_htCVFxQ0m`_d+*NKStTXUsr09uc(+`M8Hsqsqe1i#yy%YU~mR#B6JJ~!WnON*1kjd_$gYc>}uHvXSxqCS)!#tvGFEqio`1V_qJ|*x=9gU#%4j zGM6D6)|CytfS24_u45M4I6UBLHt4|I&Qf%hLMe5B-7h-Qe=CCLEZ27r`Nlu8da_Bi>QtZmw^Q zj@-DfuM+ca`s1>y!QUquNey=gR?gEuFgfe9jkPHUCr4^sek31S7wrMs zq6Wfc!kIsucf)vVQh3$(11I*uduU&32gncoqNIOTRp>1lZBLaCoi8lg?W@ul{jywG zyV~hq$CcGJ&0GMSJcE?~=7Ikd7XOS}+iq!FE8bqzBgijS8!X))SUEGF6#(C7l+XmX zvuwc~i+ZzH>SNPZ#JKt&ZGa%HzHo~#1oPp-n~j)e@nup(5@#m8{c*J?XL-EW_%F_@ zT2>rCxITOOzIb>h@mx0XJeA*1F!AKLZlyN=H=Xp&g#Sk9hLC-NMS6FS&JQvt;976Z zrh*b8jyAS5)pp*i^6hJ5pZTTe)yT4))s=9qRZ(j1qle6SpoY#|2fTbWcE#hi1&)Ow zWDS<)EJEBZM(qI)VIRjx+hCWk+Pv-iP$O*H2nmjeT%Y_K$PQ@Z~w?(kMsNZ%yWT~u9))TUE zXD^Td)c4Acn43xPG>FDfy4P_diJdZ&ie1%;kij zvOw)KC9Tok3cLg@@@nW;_&z~ zs&o4h;nDvDT!zyYC4+@Uu`~Dk!TL?{Z-?KHNT?CtsWh}Tcp-fbJF2S-CI1W82jHo;ysHzu9!MA@_d#^M8+* z2VydWH_Bb5BUX zGkhQ&om(MM@cVkL0G^Y@RjxI8W?Pk!8X{V4!r?kD-S-}{>DhQum*?Y|{^FYBC*N*U z#?|Zg9Y#A4|9?l{zm{R=)TZNG^x8EZ|IV#!-*sOVmzBBM?vMWTMCs-C69v@b#AZd^ zh34~CojMYNTyv!p;scs~o>ygb%AzbZf8AQ%8cDOwzw$p>ibJ2>hp&V!qkoRvqh(DD zWFWHC=Ukff?z_f+oylgGxmIR(X7jFn&1t(8_5bb33U{vYIFHFjVN&Mnl1=0+CU?ka z>)Wr5n|D6SOA>piHLdRlwVqg>D^?|Uotgmq&7DXB`n>F6&W{r9D=&lDSd87HQ zK(!Lc`LX^$lK=RAmM9>4@zU@;kO#;2Zk$Zk%rH0Ll^j+f8GQ$&%(Xx}lqSd3%RgcS zN=65JPi|fUX=yfX_7V@Ze;5BxzRFH$j@@uc^&F(;R{(JldWZfi9Z!XFN_J1N!)Sg` zqUy?DvDo_N_acw;KpDre@%bj1{3Bu@hErgH0!KNuo+)0f3p&ygh2(i7ZYT``0EtKL zcN)ydn|CfEAGT#Rlviah+MXNsdW$m{*Bvw~( zMEK{goaAT>J=`NvEdru*oN|4O|5kpk#PfB`o&t;1p;;V1)c^>X6NN zdg;2%CG&9}4n#V;7l3_oo3c9xBW?SwWDN+^hbNTx7wnFamt1rjM=;ZxBnBd)f`X!SDGCZw(s_nhC<=lSA`A;ux=YwBLcpnX59!>S^xrW!y6Zdn+@2&@7=Q=U~!ja$zVumxh6u@|+N0XG<#4=XbU+HK2!F|FI#tUH)G z*eVt})`NzE&CT;E-HN0Sg(B$%UCp2WF^&LSfU@JgaAnl75u>@9?_V}J8qW5Tsq6BQ z2>^oz`T{FI$^;;z5zF|0lCfK~7GsfS+b5{JYt z-O%h`^imDoEhC}kowgjKfo!YjQH=$PmsPk>*VCOBq@1JAf3RXWH=pO=-3w|k!hx@< z5>VigM^ScKVj}#vs*{E4uOd?JJg<&A65%(9%-z6J5My^thy#0=G?1v^s}FI_)GfUw zht-u637SXkRP$>003mE8tY(8&RrjnWw_?w^NJt2nX~eR6vjb2%10%{A0l7~t{)#YR zIR5gk=ix~w!S^SdLK{T_Yi{!{nc?5{F&XMIA2JXKQsVP^#~)<&eT!bEb+*C+S|yn2 z@-q73R_|wx9@v)=VxiX?ef8XR$DLlt0OKP&^tXK;VI_Wgb8mxR^~@lrH|oPyoox^* z#nt>c0Tp*1$%tncLpTMBB>IfULmACx6|Z}}x5L1k?>_Z*#BbQ%6jkqOwvk{j#5~P|7}+D5^+^nZ1^@;ApZvDWJ2b#@78vZGI?{bgUDc zqwq>T#C*8}O2E5zQz{}Pwszg#s@+Cxxr9CVkd;?&0`}}v=wlkz{m?prU+-Uc%W0>K z!F8{^V`ZI9GAFmA`ilriyDaVSK~59LXjG4T2NGODDux8>Ba20Nyh^78)W zlvwQ6m9<;0>9AV@-wZySnj6nY3lP9_GnMV_fAq+Dw4~l(J%m_LCbKSuLc5e&q+_){ z+zB82e!0};gU+5D=MNfnBdWmz<(V&LSHD(-x%`9>{WYV`Fq_d%n6S&2@5;OY@3yfd zr^a&U9_!8lbh7JpU5+b*E}TJ*-#tmgx)od1LO2ho??iXdEybbApEMJdpm?A1z`Bt| z*RQjRpCa?1&=3& z-Sy!BUH1HR9s{eDn~=rRi{6ntyUUe%u^fWVxYF)I+RV60_uO7Qff?Ji8gO)^ce!71r`j#K$faReYyS{+8=-yG$$3&Y?{-IMh#HYEDw-l-k_@LRRSrDfV+wO#-n}5V zGBf4&ORS=4QxvXaBbGR7p8Yy-(gQWwl!C=nYg7|-jO)LjIGGe^Gd^iysvgd94XU{) zUH>e{Xss(v^I47YDIadNWwLS>peYBSl_9LP#OKs^=<|V(EE6oa7<$ZT7F53>>Un{< zH(itUv!?Uh-mzdde13Q5Y1E<~>N(oNZ>~@hB~r(MtLff}9xjOK@7x1sBWqZXrWAnz z@p@YG>9b#OrRLuj^?rJOjP_aOg!Ns{8y{lnjJVa<>}5-W0{fwt+JO)*dAWM}8OuMK z;xS5|qP&JWwuzO0B9c+D@Wkf>-NN@GYWT@vY4gh^7qk>8VKvM^ReLLQzo*}LR(nVm z82tY0F)xg%e%ht6Qs+)hmyh$W?@|}Voyq`_C1a1LN7m$A(hC7!b%flZY zy02Cvns!&UjqR6Aw;0X4K9Hu`HJ5lJKwp6JZ1m0dbycctK-zRR|Dx9Y?x zFKp!byK2Lv19Bzn75cl+OC7EIF;KGYH?#cmZq}cXboL61ci_!Mvm3{cb633xHch;7 z?%r-Vh!eSyleU3r81uPCZ zxW0ME4zMICT`79w!;nH5$yF)&*4Uqj+f84p6x;dwH&%0!gmuJyzN<`T!(O`99h+

b6~;#)0XTm>Ut5N=GeRIcVS*uhD6c|0CbL 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 From f3d8232e1424e2e1ff9b2de695a05b58ef3f92b7 Mon Sep 17 00:00:00 2001 From: Chris Lu Date: Wed, 6 Oct 2021 22:01:17 -0700 Subject: [PATCH 114/130] reduce one redis lookup on hot path --- weed/filer/redis3/ItemList.go | 31 +++++++++++++------------------ weed/util/skiplist/skiplist.go | 3 +++ 2 files changed, 16 insertions(+), 18 deletions(-) diff --git a/weed/filer/redis3/ItemList.go b/weed/filer/redis3/ItemList.go index bad3d2efb..33855d22d 100644 --- a/weed/filer/redis3/ItemList.go +++ b/weed/filer/redis3/ItemList.go @@ -96,22 +96,17 @@ func (nl *ItemList) WriteName(name string) error { return nil } + var prevNodeReference *skiplist.SkipListElementReference if !found { - prevNode, err = nl.skipList.GetLargestNode() - if err != nil { - return err - } + prevNodeReference = nl.skipList.GetLargestNodeReference() } if nextNode != nil && prevNode == nil { - prevNode, err = nl.skipList.LoadElement(nextNode.Prev) - if err != nil { - return err - } + prevNodeReference = nextNode.Prev } - if prevNode != nil { - alreadyContains, nodeSize, err := nl.canAddMember(prevNode.Reference(), name) + if prevNodeReference != nil { + alreadyContains, nodeSize, err := nl.canAddMember(prevNodeReference, name) if err != nil { return err } @@ -122,11 +117,11 @@ func (nl *ItemList) WriteName(name string) error { // case 2.2 if nodeSize < nl.batchSize { - return nl.NodeAddMember(prevNode.Reference(), name) + return nl.NodeAddMember(prevNodeReference, name) } // case 2.3 - x := nl.NodeInnerPosition(prevNode.Reference(), name) + x := nl.NodeInnerPosition(prevNodeReference, name) y := nodeSize - x addToX := x <= y // add to a new node @@ -138,12 +133,12 @@ func (nl *ItemList) WriteName(name string) error { } if addToX { // collect names before name, add them to X - namesToX, err := nl.NodeRangeBeforeExclusive(prevNode.Reference(), name) + namesToX, err := nl.NodeRangeBeforeExclusive(prevNodeReference, name) if err != nil { return nil } // delete skiplist reference to old node - if _, err := nl.skipList.DeleteByKey(prevNode.Key); err != nil { + if _, err := nl.skipList.DeleteByKey(prevNodeReference.Key); err != nil { return err } // add namesToY and name to a new X @@ -152,18 +147,18 @@ func (nl *ItemList) WriteName(name string) error { return nil } // remove names less than name from current Y - if err := nl.NodeDeleteBeforeExclusive(prevNode.Reference(), name); err != nil { + if err := nl.NodeDeleteBeforeExclusive(prevNodeReference, name); err != nil { return nil } // point skip list to current Y - if err := nl.ItemAdd(lookupKey, prevNode.Id); err != nil { + if err := nl.ItemAdd(lookupKey, prevNodeReference.ElementPointer); err != nil { return nil } return nil } else { // collect names after name, add them to Y - namesToY, err := nl.NodeRangeAfterExclusive(prevNode.Reference(), name) + namesToY, err := nl.NodeRangeAfterExclusive(prevNodeReference, name) if err != nil { return nil } @@ -173,7 +168,7 @@ func (nl *ItemList) WriteName(name string) error { return nil } // remove names after name from current X - if err := nl.NodeDeleteAfterExclusive(prevNode.Reference(), name); err != nil { + if err := nl.NodeDeleteAfterExclusive(prevNodeReference, name); err != nil { return nil } return nil diff --git a/weed/util/skiplist/skiplist.go b/weed/util/skiplist/skiplist.go index f42ec23cd..2a0262c69 100644 --- a/weed/util/skiplist/skiplist.go +++ b/weed/util/skiplist/skiplist.go @@ -467,6 +467,9 @@ func (t *SkipList) GetSmallestNode() (*SkipListElement, error) { func (t *SkipList) GetLargestNode() (*SkipListElement, error) { return t.LoadElement(t.EndLevels[0]) } +func (t *SkipList) GetLargestNodeReference() (*SkipListElementReference) { + return t.EndLevels[0] +} // Next returns the next element based on the given node. // Next will loop around to the first node, if you call it on the last! From 332f5ad3a82343c1a63e96a05cda09c7a71b581f Mon Sep 17 00:00:00 2001 From: Chris Lu Date: Thu, 7 Oct 2021 13:24:16 -0700 Subject: [PATCH 115/130] revert temporary changes that disabled compression fix https://github.com/chrislusf/seaweedfs/issues/2362 --- weed/storage/needle/needle_parse_upload.go | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/weed/storage/needle/needle_parse_upload.go b/weed/storage/needle/needle_parse_upload.go index 0888c6b7a..fe6b7d740 100644 --- a/weed/storage/needle/needle_parse_upload.go +++ b/weed/storage/needle/needle_parse_upload.go @@ -75,16 +75,14 @@ func ParseUpload(r *http.Request, sizeLimit int64, bytesBuffer *bytes.Buffer) (p if mimeType == "application/octet-stream" { mimeType = "" } - if false { - if shouldBeCompressed, iAmSure := util.IsCompressableFileType(ext, mimeType); mimeType == "" && !iAmSure || shouldBeCompressed && iAmSure { - // println("ext", ext, "iAmSure", iAmSure, "shouldBeCompressed", shouldBeCompressed, "mimeType", pu.MimeType) - if compressedData, err := util.GzipData(pu.Data); err == nil { - if len(compressedData)*10 < len(pu.Data)*9 { - pu.Data = compressedData - pu.IsGzipped = true - } - // println("gzipped data size", len(compressedData)) + if shouldBeCompressed, iAmSure := util.IsCompressableFileType(ext, mimeType); mimeType == "" && !iAmSure || shouldBeCompressed && iAmSure { + // println("ext", ext, "iAmSure", iAmSure, "shouldBeCompressed", shouldBeCompressed, "mimeType", pu.MimeType) + if compressedData, err := util.GzipData(pu.Data); err == nil { + if len(compressedData)*10 < len(pu.Data)*9 { + pu.Data = compressedData + pu.IsGzipped = true } + // println("gzipped data size", len(compressedData)) } } } From d688e10ed14502bfb3141c39d837c81e9853fe96 Mon Sep 17 00:00:00 2001 From: Chris Lu Date: Thu, 7 Oct 2021 13:29:00 -0700 Subject: [PATCH 116/130] do not try to compress if not sure about the file content --- weed/storage/needle/needle_parse_upload.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/weed/storage/needle/needle_parse_upload.go b/weed/storage/needle/needle_parse_upload.go index fe6b7d740..bda58fbc3 100644 --- a/weed/storage/needle/needle_parse_upload.go +++ b/weed/storage/needle/needle_parse_upload.go @@ -75,7 +75,7 @@ func ParseUpload(r *http.Request, sizeLimit int64, bytesBuffer *bytes.Buffer) (p if mimeType == "application/octet-stream" { mimeType = "" } - if shouldBeCompressed, iAmSure := util.IsCompressableFileType(ext, mimeType); mimeType == "" && !iAmSure || shouldBeCompressed && iAmSure { + if shouldBeCompressed, iAmSure := util.IsCompressableFileType(ext, mimeType); shouldBeCompressed && iAmSure { // println("ext", ext, "iAmSure", iAmSure, "shouldBeCompressed", shouldBeCompressed, "mimeType", pu.MimeType) if compressedData, err := util.GzipData(pu.Data); err == nil { if len(compressedData)*10 < len(pu.Data)*9 { From 0a856241fe2ac9492229ff9c63b6879d04d69185 Mon Sep 17 00:00:00 2001 From: Chris Lu Date: Thu, 7 Oct 2021 21:12:57 -0700 Subject: [PATCH 117/130] avoid int bigger than math.MaxInt32 fix https://github.com/chrislusf/seaweedfs/issues/2363 --- weed/filer/filer_search.go | 8 ++++---- weed/filer/filerstore_translate_path.go | 5 +++++ weed/filer/filerstore_wrapper.go | 4 ++++ weed/pb/filer_pb/filer_client.go | 6 +++++- 4 files changed, 18 insertions(+), 5 deletions(-) diff --git a/weed/filer/filer_search.go b/weed/filer/filer_search.go index 2e0336da8..112df7984 100644 --- a/weed/filer/filer_search.go +++ b/weed/filer/filer_search.go @@ -23,15 +23,15 @@ func splitPattern(pattern string) (prefix string, restPattern string) { // For now, prefix and namePattern are mutually exclusive func (f *Filer) ListDirectoryEntries(ctx context.Context, p util.FullPath, startFileName string, inclusive bool, limit int64, prefix string, namePattern string, namePatternExclude string) (entries []*Entry, hasMore bool, err error) { + if limit > math.MaxInt32-1 { + limit = math.MaxInt32 - 1 + } + _, err = f.StreamListDirectoryEntries(ctx, p, startFileName, inclusive, limit+1, prefix, namePattern, namePatternExclude, func(entry *Entry) bool { entries = append(entries, entry) return true }) - if limit == math.MaxInt64 { - limit = math.MaxInt64 - 1 - } - hasMore = int64(len(entries)) >= limit+1 if hasMore { entries = entries[:limit] diff --git a/weed/filer/filerstore_translate_path.go b/weed/filer/filerstore_translate_path.go index 00bf82ed4..55a8bc929 100644 --- a/weed/filer/filerstore_translate_path.go +++ b/weed/filer/filerstore_translate_path.go @@ -3,6 +3,7 @@ package filer import ( "context" "github.com/chrislusf/seaweedfs/weed/util" + "math" "strings" ) @@ -120,6 +121,10 @@ func (t *FilerStorePathTranlator) ListDirectoryPrefixedEntries(ctx context.Conte newFullPath := t.translatePath(dirPath) + if limit > math.MaxInt32-1 { + limit = math.MaxInt32 - 1 + } + return t.actualStore.ListDirectoryPrefixedEntries(ctx, newFullPath, startFileName, includeStartFile, limit, prefix, func(entry *Entry) bool { entry.FullPath = dirPath[:len(t.storeRoot)-1] + entry.FullPath return eachEntryFunc(entry) diff --git a/weed/filer/filerstore_wrapper.go b/weed/filer/filerstore_wrapper.go index 2470f340c..4ded40fb1 100644 --- a/weed/filer/filerstore_wrapper.go +++ b/weed/filer/filerstore_wrapper.go @@ -4,6 +4,7 @@ import ( "context" "github.com/chrislusf/seaweedfs/weed/glog" "github.com/viant/ptrie" + "math" "strings" "time" @@ -248,6 +249,9 @@ func (fsw *FilerStoreWrapper) ListDirectoryPrefixedEntries(ctx context.Context, defer func() { stats.FilerStoreHistogram.WithLabelValues(actualStore.GetName(), "prefixList").Observe(time.Since(start).Seconds()) }() + if limit > math.MaxInt32-1 { + limit = math.MaxInt32 - 1 + } glog.V(4).Infof("ListDirectoryPrefixedEntries %s from %s prefix %s limit %d", dirPath, startFileName, prefix, limit) lastFileName, err = actualStore.ListDirectoryPrefixedEntries(ctx, dirPath, startFileName, includeStartFile, limit, prefix, eachEntryFunc) if err == ErrUnsupportedListDirectoryPrefixed { diff --git a/weed/pb/filer_pb/filer_client.go b/weed/pb/filer_pb/filer_client.go index 6a74e3c4c..719039d88 100644 --- a/weed/pb/filer_pb/filer_client.go +++ b/weed/pb/filer_pb/filer_client.go @@ -101,9 +101,13 @@ func SeaweedList(client SeaweedFilerClient, parentDirectoryPath, prefix string, func doSeaweedList(client SeaweedFilerClient, fullDirPath util.FullPath, prefix string, fn EachEntryFunciton, startFrom string, inclusive bool, limit uint32) (err error) { // Redundancy limit to make it correctly judge whether it is the last file. redLimit := limit - if limit != math.MaxInt32 && limit != 0 { + + if limit < math.MaxInt32 && limit != 0 { redLimit = limit + 1 } + if redLimit > math.MaxInt32 { + redLimit = math.MaxInt32 + } request := &ListEntriesRequest{ Directory: string(fullDirPath), Prefix: prefix, From e4830bd93dd18eface5a0590e3e18672bd7d30ef Mon Sep 17 00:00:00 2001 From: Chris Lu Date: Thu, 7 Oct 2021 21:13:31 -0700 Subject: [PATCH 118/130] go fmt --- weed/filer/redis3/ItemList.go | 6 ++-- weed/filer/redis3/kv_directory_children.go | 8 +++--- .../redis3/kv_directory_children_test.go | 28 +++++++++---------- weed/filer/redis3/skiplist_element_store.go | 2 +- weed/topology/volume_layout.go | 2 +- weed/util/skiplist/name_list.go | 6 ++-- weed/util/skiplist/name_list_serde.go | 2 +- weed/util/skiplist/name_list_test.go | 2 +- weed/util/skiplist/skiplist.go | 2 +- weed/util/skiplist/skiplist_serde.go | 2 +- weed/util/skiplist/skiplist_test.go | 1 - 11 files changed, 30 insertions(+), 31 deletions(-) diff --git a/weed/filer/redis3/ItemList.go b/weed/filer/redis3/ItemList.go index 33855d22d..9f84d57f6 100644 --- a/weed/filer/redis3/ItemList.go +++ b/weed/filer/redis3/ItemList.go @@ -73,7 +73,7 @@ func (nl *ItemList) canAddMember(node *skiplist.SkipListElementReference, name s key := fmt.Sprintf("%s%dm", nl.prefix, node.ElementPointer) countOperation := pipe.ZLexCount(ctx, key, "-", "+") scoreOperationt := pipe.ZScore(ctx, key, name) - if _, err = pipe.Exec(ctx); err != nil && err != redis.Nil{ + if _, err = pipe.Exec(ctx); err != nil && err != redis.Nil { return false, 0, err } if err == redis.Nil { @@ -286,7 +286,7 @@ func (nl *ItemList) DeleteName(name string) error { return nil } nextSize := nl.NodeSize(nextNode.Reference()) - if nextSize > 0 && prevSize + nextSize < nl.batchSize { + if nextSize > 0 && prevSize+nextSize < nl.batchSize { // case 3.1 merge nextNode and prevNode if _, err := nl.skipList.DeleteByKey(nextNode.Key); err != nil { return err @@ -414,7 +414,7 @@ func (nl *ItemList) NodeInnerPosition(node *skiplist.SkipListElementReference, n func (nl *ItemList) NodeMin(node *skiplist.SkipListElementReference) string { key := fmt.Sprintf("%s%dm", nl.prefix, node.ElementPointer) slice := nl.client.ZPopMin(context.Background(), key).Val() - if len(slice)>0{ + if len(slice) > 0 { s := slice[0].Member.(string) return s } diff --git a/weed/filer/redis3/kv_directory_children.go b/weed/filer/redis3/kv_directory_children.go index 624d17374..d92dddfe6 100644 --- a/weed/filer/redis3/kv_directory_children.go +++ b/weed/filer/redis3/kv_directory_children.go @@ -12,7 +12,7 @@ const maxNameBatchSizeLimit = 1000000 func insertChild(ctx context.Context, redisStore *UniversalRedis3Store, key string, name string) error { // lock and unlock - mutex := redisStore.redsync.NewMutex(key+"lock") + mutex := redisStore.redsync.NewMutex(key + "lock") if err := mutex.Lock(); err != nil { return fmt.Errorf("lock %s: %v", key, err) } @@ -49,7 +49,7 @@ func insertChild(ctx context.Context, redisStore *UniversalRedis3Store, key stri func removeChild(ctx context.Context, redisStore *UniversalRedis3Store, key string, name string) error { // lock and unlock - mutex := redisStore.redsync.NewMutex(key+"lock") + mutex := redisStore.redsync.NewMutex(key + "lock") if err := mutex.Lock(); err != nil { return fmt.Errorf("lock %s: %v", key, err) } @@ -82,7 +82,7 @@ func removeChild(ctx context.Context, redisStore *UniversalRedis3Store, key stri func removeChildren(ctx context.Context, redisStore *UniversalRedis3Store, key string, onDeleteFn func(name string) error) error { // lock and unlock - mutex := redisStore.redsync.NewMutex(key+"lock") + mutex := redisStore.redsync.NewMutex(key + "lock") if err := mutex.Lock(); err != nil { return fmt.Errorf("lock %s: %v", key, err) } @@ -111,7 +111,7 @@ func removeChildren(ctx context.Context, redisStore *UniversalRedis3Store, key s if err = nameList.RemoteAllListElement(); err != nil { return err } - + return nil } diff --git a/weed/filer/redis3/kv_directory_children_test.go b/weed/filer/redis3/kv_directory_children_test.go index 03c15ec35..78979cb21 100644 --- a/weed/filer/redis3/kv_directory_children_test.go +++ b/weed/filer/redis3/kv_directory_children_test.go @@ -94,7 +94,7 @@ func BenchmarkNameList(b *testing.B) { for i := 0; i < b.N; i++ { nameList := LoadItemList(data, "/yyy/bin", client, store, maxNameBatchSizeLimit) - nameList.WriteName(strconv.Itoa(i)+"namexxxxxxxxxxxxxxxxxxx") + nameList.WriteName(strconv.Itoa(i) + "namexxxxxxxxxxxxxxxxxxx") if nameList.HasChanges() { data = nameList.ToBytes() @@ -116,7 +116,7 @@ func BenchmarkRedis(b *testing.B) { }) for i := 0; i < b.N; i++ { - client.ZAddNX(context.Background(),"/yyy/bin", &redis.Z{Score: 0, Member: strconv.Itoa(i)+"namexxxxxxxxxxxxxxxxxxx"}) + client.ZAddNX(context.Background(), "/yyy/bin", &redis.Z{Score: 0, Member: strconv.Itoa(i) + "namexxxxxxxxxxxxxxxxxxx"}) } } @@ -149,24 +149,24 @@ func xTestNameListAdd(t *testing.T) { ts1 := time.Now() for i := 0; i < N; i++ { - client.ZAddNX(context.Background(),"/x", &redis.Z{Score: 0, Member: fmt.Sprintf("name %8d", i)}) + client.ZAddNX(context.Background(), "/x", &redis.Z{Score: 0, Member: fmt.Sprintf("name %8d", i)}) } ts2 := time.Now() fmt.Printf("%v %v", ts1.Sub(ts0), ts2.Sub(ts1)) /* - keys := client.Keys(context.Background(), "/*m").Val() - for _, k := range keys { - println("key", k) - for i, v := range client.ZRangeByLex(context.Background(), k, &redis.ZRangeBy{ - Min: "-", - Max: "+", - }).Val() { - println(" ", i, v) + keys := client.Keys(context.Background(), "/*m").Val() + for _, k := range keys { + println("key", k) + for i, v := range client.ZRangeByLex(context.Background(), k, &redis.ZRangeBy{ + Min: "-", + Max: "+", + }).Val() { + println(" ", i, v) + } } - } - */ + */ } func xBenchmarkNameList(b *testing.B) { @@ -205,6 +205,6 @@ func xBenchmarkRedis(b *testing.B) { }) for i := 0; i < b.N; i++ { - client.ZAddNX(context.Background(),"/xxx/bin", &redis.Z{Score: 0, Member: fmt.Sprintf("name %8d", i)}) + client.ZAddNX(context.Background(), "/xxx/bin", &redis.Z{Score: 0, Member: fmt.Sprintf("name %8d", i)}) } } diff --git a/weed/filer/redis3/skiplist_element_store.go b/weed/filer/redis3/skiplist_element_store.go index bcad356dd..8c101d006 100644 --- a/weed/filer/redis3/skiplist_element_store.go +++ b/weed/filer/redis3/skiplist_element_store.go @@ -49,7 +49,7 @@ func (m *SkipListElementStore) LoadElement(id int64) (*skiplist.SkipListElement, t := &skiplist.SkipListElement{} err = proto.Unmarshal([]byte(data), t) if err == nil { - for i:=0;i Date: Sat, 9 Oct 2021 04:54:14 -0700 Subject: [PATCH 119/130] adds more error message --- weed/storage/volume_backup.go | 3 +- weed/storage/volume_write_test.go | 69 +++++++++++++++++++++++++++++++ 2 files changed, 71 insertions(+), 1 deletion(-) create mode 100644 weed/storage/volume_write_test.go diff --git a/weed/storage/volume_backup.go b/weed/storage/volume_backup.go index 7fadd6fef..500f48b23 100644 --- a/weed/storage/volume_backup.go +++ b/weed/storage/volume_backup.go @@ -191,12 +191,13 @@ func (v *Volume) BinarySearchByAppendAtNs(sinceNs uint64) (offset Offset, isLast // read the appendAtNs for entry m offset, err = v.readOffsetFromIndex(m) if err != nil { + err = fmt.Errorf("read entry %d: %v", m, err) return } mNs, nsReadErr := v.readAppendAtNs(offset) if nsReadErr != nil { - err = nsReadErr + err = fmt.Errorf("read entry %d offset %d: %v", m, offset, nsReadErr) return } diff --git a/weed/storage/volume_write_test.go b/weed/storage/volume_write_test.go new file mode 100644 index 000000000..e0ee3dac7 --- /dev/null +++ b/weed/storage/volume_write_test.go @@ -0,0 +1,69 @@ +package storage + +import ( + "fmt" + "github.com/chrislusf/seaweedfs/weed/storage/needle" + "github.com/chrislusf/seaweedfs/weed/storage/super_block" + "io/ioutil" + "os" + "testing" + "time" +) + +func TestSearchVolumesWithDeletedNeedles(t *testing.T) { + dir, err := ioutil.TempDir("", "example") + if err != nil { + t.Fatalf("temp dir creation: %v", err) + } + defer os.RemoveAll(dir) // clean up + + v, err := NewVolume(dir, dir, "", 1, NeedleMapInMemory, &super_block.ReplicaPlacement{}, &needle.TTL{}, 0, 0) + if err != nil { + t.Fatalf("volume creation: %v", err) + } + + count := 10 + + for i:=1;i Date: Sat, 9 Oct 2021 04:54:48 -0700 Subject: [PATCH 120/130] fix redis3 deletion --- weed/filer/redis3/ItemList.go | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/weed/filer/redis3/ItemList.go b/weed/filer/redis3/ItemList.go index 9f84d57f6..af3b8ae5a 100644 --- a/weed/filer/redis3/ItemList.go +++ b/weed/filer/redis3/ItemList.go @@ -381,6 +381,9 @@ func (nl *ItemList) NodeContainsItem(node *skiplist.SkipListElementReference, it } func (nl *ItemList) NodeSize(node *skiplist.SkipListElementReference) int { + if node == nil { + return 0 + } key := fmt.Sprintf("%s%dm", nl.prefix, node.ElementPointer) return int(nl.client.ZLexCount(context.Background(), key, "-", "+").Val()) } @@ -413,9 +416,14 @@ func (nl *ItemList) NodeInnerPosition(node *skiplist.SkipListElementReference, n func (nl *ItemList) NodeMin(node *skiplist.SkipListElementReference) string { key := fmt.Sprintf("%s%dm", nl.prefix, node.ElementPointer) - slice := nl.client.ZPopMin(context.Background(), key).Val() + slice := nl.client.ZRangeByLex(context.Background(), key, &redis.ZRangeBy{ + Min: "-", + Max: "+", + Offset: 0, + Count: 1, + }).Val() if len(slice) > 0 { - s := slice[0].Member.(string) + s := slice[0] return s } return "" From 5ca0a551acd89932bdb79871682527186ed28f40 Mon Sep 17 00:00:00 2001 From: Chris Lu Date: Sat, 9 Oct 2021 05:38:15 -0700 Subject: [PATCH 121/130] java: adjust cache expiration policy for long running java processes --- .../java/client/src/main/java/seaweedfs/client/ChunkCache.java | 2 +- .../client/src/main/java/seaweedfs/client/VolumeIdCache.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/other/java/client/src/main/java/seaweedfs/client/ChunkCache.java b/other/java/client/src/main/java/seaweedfs/client/ChunkCache.java index 58870d742..84b2beeb1 100644 --- a/other/java/client/src/main/java/seaweedfs/client/ChunkCache.java +++ b/other/java/client/src/main/java/seaweedfs/client/ChunkCache.java @@ -15,7 +15,7 @@ public class ChunkCache { } this.cache = CacheBuilder.newBuilder() .maximumSize(maxEntries) - .expireAfterAccess(1, TimeUnit.HOURS) + .expireAfterWrite(1, TimeUnit.HOURS) .build(); } diff --git a/other/java/client/src/main/java/seaweedfs/client/VolumeIdCache.java b/other/java/client/src/main/java/seaweedfs/client/VolumeIdCache.java index 86263fff9..6f3d9d8c1 100644 --- a/other/java/client/src/main/java/seaweedfs/client/VolumeIdCache.java +++ b/other/java/client/src/main/java/seaweedfs/client/VolumeIdCache.java @@ -15,7 +15,7 @@ public class VolumeIdCache { } this.cache = CacheBuilder.newBuilder() .maximumSize(maxEntries) - .expireAfterAccess(5, TimeUnit.MINUTES) + .expireAfterWrite(5, TimeUnit.MINUTES) .build(); } From bf218cd59d0a7db1a8b5c55ccc93989b02a35738 Mon Sep 17 00:00:00 2001 From: Chris Lu Date: Sun, 10 Oct 2021 19:10:46 -0700 Subject: [PATCH 122/130] removing etcd sequencer causing go mod tidy problem. If anyone wants this, please help to resolve this first. github.com/chrislusf/seaweedfs/weed/sequence imports go.etcd.io/etcd/client tested by go.etcd.io/etcd/client.test imports github.com/coreos/etcd/integration imports github.com/coreos/etcd/proxy/grpcproxy imports google.golang.org/grpc/naming: module google.golang.org/grpc@latest found (v1.41.0), but does not contain package google.golang.org/grpc/naming --- weed/command/scaffold/master.toml | 5 +- weed/sequence/etcd_sequencer.go | 296 ------------------------------ weed/server/master_server.go | 10 - 3 files changed, 1 insertion(+), 310 deletions(-) delete mode 100644 weed/sequence/etcd_sequencer.go diff --git a/weed/command/scaffold/master.toml b/weed/command/scaffold/master.toml index 020f48e36..363493db3 100644 --- a/weed/command/scaffold/master.toml +++ b/weed/command/scaffold/master.toml @@ -23,10 +23,7 @@ default = "localhost:8888" # used by maintenance scripts if the scripts needs [master.sequencer] -type = "raft" # Choose [raft|etcd|snowflake] type for storing the file id sequence -# when sequencer.type = etcd, set listen client urls of etcd cluster that store file id sequence -# example : http://127.0.0.1:2379,http://127.0.0.1:2389 -sequencer_etcd_urls = "http://127.0.0.1:2379" +type = "raft" # Choose [raft|snowflake] type for storing the file id sequence # when sequencer.type = snowflake, the snowflake id must be different from other masters sequencer_snowflake_id = 0 # any number between 1~1023 diff --git a/weed/sequence/etcd_sequencer.go b/weed/sequence/etcd_sequencer.go deleted file mode 100644 index a9f2bb97f..000000000 --- a/weed/sequence/etcd_sequencer.go +++ /dev/null @@ -1,296 +0,0 @@ -package sequence - -/* -Note : -(1) store the sequence in the ETCD cluster, and local file(sequence.dat) -(2) batch get the sequences from ETCD cluster, and store the max sequence id in the local file -(3) the sequence range is : [currentSeqId, maxSeqId), when the currentSeqId >= maxSeqId, fetch the new maxSeqId. -*/ - -import ( - "context" - "fmt" - "go.etcd.io/etcd/client" - "sync" - "time" - - "io" - "os" - "strconv" - "strings" - - "github.com/chrislusf/seaweedfs/weed/glog" -) - -const ( - // EtcdKeyPrefix = "/seaweedfs" - EtcdKeySequence = "/master/sequence" - EtcdContextTimeoutSecond = 100 * time.Second - DefaultEtcdSteps uint64 = 500 // internal counter - SequencerFileName = "sequencer.dat" - FileMaxSequenceLength = 128 -) - -type EtcdSequencer struct { - sequenceLock sync.Mutex - - // available sequence range : [currentSeqId, maxSeqId) - currentSeqId uint64 - maxSeqId uint64 - - keysAPI client.KeysAPI - seqFile *os.File -} - -func NewEtcdSequencer(etcdUrls string, metaFolder string) (*EtcdSequencer, error) { - file, err := openSequenceFile(metaFolder + "/" + SequencerFileName) - if nil != err { - return nil, fmt.Errorf("open sequence file fialed, %v", err) - } - - cli, err := client.New(client.Config{ - Endpoints: strings.Split(etcdUrls, ","), - Username: "", - Password: "", - }) - if err != nil { - return nil, err - } - keysApi := client.NewKeysAPI(cli) - - // TODO: the current sequence id in local file is not used - maxValue, _, err := readSequenceFile(file) - if err != nil { - return nil, fmt.Errorf("read sequence from file failed, %v", err) - } - glog.V(4).Infof("read sequence from file : %d", maxValue) - - newSeq, err := setMaxSequenceToEtcd(keysApi, maxValue) - if err != nil { - return nil, err - } - - sequencer := &EtcdSequencer{maxSeqId: newSeq, - currentSeqId: newSeq, - keysAPI: keysApi, - seqFile: file, - } - return sequencer, nil -} - -func (es *EtcdSequencer) NextFileId(count uint64) uint64 { - es.sequenceLock.Lock() - defer es.sequenceLock.Unlock() - - if (es.currentSeqId + count) >= es.maxSeqId { - reqSteps := DefaultEtcdSteps - if count > DefaultEtcdSteps { - reqSteps += count - } - maxId, err := batchGetSequenceFromEtcd(es.keysAPI, reqSteps) - glog.V(4).Infof("get max sequence id from etcd, %d", maxId) - if err != nil { - glog.Error(err) - return 0 - } - es.currentSeqId, es.maxSeqId = maxId-reqSteps, maxId - glog.V(4).Infof("current id : %d, max id : %d", es.currentSeqId, es.maxSeqId) - - if err := writeSequenceFile(es.seqFile, es.maxSeqId, es.currentSeqId); err != nil { - glog.Errorf("flush sequence to file failed, %v", err) - } - } - - ret := es.currentSeqId - es.currentSeqId += count - return ret -} - -/** -instead of collecting the max value from volume server, -the max value should be saved in local config file and ETCD cluster -*/ -func (es *EtcdSequencer) SetMax(seenValue uint64) { - es.sequenceLock.Lock() - defer es.sequenceLock.Unlock() - if seenValue > es.maxSeqId { - maxId, err := setMaxSequenceToEtcd(es.keysAPI, seenValue) - if err != nil { - glog.Errorf("set Etcd Max sequence failed : %v", err) - return - } - es.currentSeqId, es.maxSeqId = maxId, maxId - - if err := writeSequenceFile(es.seqFile, maxId, maxId); err != nil { - glog.Errorf("flush sequence to file failed, %v", err) - } - } -} - -func (es *EtcdSequencer) GetMax() uint64 { - return es.maxSeqId -} - -func (es *EtcdSequencer) Peek() uint64 { - return es.currentSeqId -} - -func batchGetSequenceFromEtcd(kvApi client.KeysAPI, step uint64) (uint64, error) { - if step <= 0 { - return 0, fmt.Errorf("the step must be large than 1") - } - - ctx, cancel := context.WithTimeout(context.Background(), EtcdContextTimeoutSecond) - var endSeqValue uint64 = 0 - defer cancel() - for { - getResp, err := kvApi.Get(ctx, EtcdKeySequence, &client.GetOptions{Recursive: false, Quorum: true}) - if err != nil { - return 0, err - } - if getResp.Node == nil { - continue - } - - prevValue := getResp.Node.Value - prevSeqValue, err := strconv.ParseUint(prevValue, 10, 64) - if err != nil { - return 0, fmt.Errorf("get sequence from etcd failed, %v", err) - } - endSeqValue = prevSeqValue + step - endSeqStr := strconv.FormatUint(endSeqValue, 10) - - _, err = kvApi.Set(ctx, EtcdKeySequence, endSeqStr, &client.SetOptions{PrevValue: prevValue}) - if err == nil { - break - } - glog.Error(err) - } - - return endSeqValue, nil -} - -/** -update the value of the key EtcdKeySequence in ETCD cluster with the parameter of maxSeq, -when the value of the key EtcdKeySequence is equal to or large than the parameter maxSeq, -return the value of EtcdKeySequence in the ETCD cluster; -when the value of the EtcdKeySequence is less than the parameter maxSeq, -return the value of the parameter maxSeq -*/ -func setMaxSequenceToEtcd(kvApi client.KeysAPI, maxSeq uint64) (uint64, error) { - maxSeqStr := strconv.FormatUint(maxSeq, 10) - ctx, cancel := context.WithTimeout(context.Background(), EtcdContextTimeoutSecond) - defer cancel() - - for { - getResp, err := kvApi.Get(ctx, EtcdKeySequence, &client.GetOptions{Recursive: false, Quorum: true}) - if err != nil { - if ce, ok := err.(client.Error); ok && (ce.Code == client.ErrorCodeKeyNotFound) { - _, err := kvApi.Create(ctx, EtcdKeySequence, maxSeqStr) - if err == nil { - continue - } - if ce, ok = err.(client.Error); ok && (ce.Code == client.ErrorCodeNodeExist) { - continue - } - return 0, err - } else { - return 0, err - } - } - - if getResp.Node == nil { - continue - } - prevSeqStr := getResp.Node.Value - prevSeq, err := strconv.ParseUint(prevSeqStr, 10, 64) - if err != nil { - return 0, err - } - if prevSeq >= maxSeq { - return prevSeq, nil - } - - _, err = kvApi.Set(ctx, EtcdKeySequence, maxSeqStr, &client.SetOptions{PrevValue: prevSeqStr}) - if err != nil { - return 0, err - } - } -} - -func openSequenceFile(file string) (*os.File, error) { - _, err := os.Stat(file) - if os.IsNotExist(err) { - fid, err := os.OpenFile(file, os.O_RDWR|os.O_CREATE, 0644) - if err != nil { - return nil, err - } - if err := writeSequenceFile(fid, 1, 0); err != nil { - return nil, err - } - return fid, nil - } else { - return os.OpenFile(file, os.O_RDWR|os.O_CREATE, 0644) - } -} - -/* -read sequence and step from sequence file -*/ -func readSequenceFile(file *os.File) (uint64, uint64, error) { - sequence := make([]byte, FileMaxSequenceLength) - size, err := file.ReadAt(sequence, 0) - if (err != nil) && (err != io.EOF) { - err := fmt.Errorf("cannot read file %s, %v", file.Name(), err) - return 0, 0, err - } - sequence = sequence[0:size] - seqs := strings.Split(string(sequence), ":") - maxId, err := strconv.ParseUint(seqs[0], 10, 64) - if err != nil { - return 0, 0, fmt.Errorf("parse sequence from file failed, %v", err) - } - - if len(seqs) > 1 { - step, err := strconv.ParseUint(seqs[1], 10, 64) - if err != nil { - return 0, 0, fmt.Errorf("parse sequence from file failed, %v", err) - } - return maxId, step, nil - } - - return maxId, 0, nil -} - -/** -write the sequence and step to sequence file -*/ -func writeSequenceFile(file *os.File, sequence, step uint64) error { - _ = step - seqStr := fmt.Sprintf("%d:%d", sequence, sequence) - if _, err := file.Seek(0, 0); err != nil { - err = fmt.Errorf("cannot seek to the beginning of %s: %v", file.Name(), err) - return err - } - if err := file.Truncate(0); err != nil { - return fmt.Errorf("truncate sequence file faield : %v", err) - } - if _, err := file.WriteString(seqStr); err != nil { - return fmt.Errorf("write file %s failed, %v", file.Name(), err) - } - if err := file.Sync(); err != nil { - return fmt.Errorf("flush file %s failed, %v", file.Name(), err) - } - return nil -} - -// the UT helper method -// func deleteEtcdKey(kvApi client.KeysAPI, key string) error { -// ctx, cancel := context.WithTimeout(context.Background(), EtcdContextTimeoutSecond) -// defer cancel() -// _, err := kvApi.Delete(ctx, key, &client.DeleteOptions{Dir: false}) -// if err != nil { -// return err -// } -// return nil -// } diff --git a/weed/server/master_server.go b/weed/server/master_server.go index 8de01abf7..3b3b1c94b 100644 --- a/weed/server/master_server.go +++ b/weed/server/master_server.go @@ -28,7 +28,6 @@ import ( const ( SequencerType = "master.sequencer.type" - SequencerEtcdUrls = "master.sequencer.sequencer_etcd_urls" SequencerSnowflakeId = "master.sequencer.sequencer_snowflake_id" ) @@ -286,15 +285,6 @@ func (ms *MasterServer) createSequencer(option *MasterOption) sequence.Sequencer seqType := strings.ToLower(v.GetString(SequencerType)) glog.V(1).Infof("[%s] : [%s]", SequencerType, seqType) switch strings.ToLower(seqType) { - case "etcd": - var err error - urls := v.GetString(SequencerEtcdUrls) - glog.V(0).Infof("[%s] : [%s]", SequencerEtcdUrls, urls) - seq, err = sequence.NewEtcdSequencer(urls, option.MetaFolder) - if err != nil { - glog.Error(err) - seq = nil - } case "snowflake": var err error snowflakeId := v.GetInt(SequencerSnowflakeId) From 3e2acf677c423982516d413196e77c92e604e864 Mon Sep 17 00:00:00 2001 From: Chris Lu Date: Sun, 10 Oct 2021 19:27:02 -0700 Subject: [PATCH 123/130] removing tikv to resolve "go mod tidy" problem tikv is causing "go mod tidy" problem. Need to resolve this before adding tikv back. go mod tidy go: finding module for package github.com/coreos/etcd/clientv3/balancer/picker go: finding module for package cloud.google.com/go/kms/apiv1 go: finding module for package github.com/coreos/etcd/clientv3/balancer/resolver/endpoint go: finding module for package google.golang.org/grpc/naming go: finding module for package github.com/coreos/etcd/clientv3/credentials go: finding module for package github.com/coreos/etcd/clientv3/balancer go: finding module for package github.com/d4l3k/messagediff go: found github.com/coreos/etcd/clientv3/balancer in github.com/coreos/etcd v3.3.26+incompatible go: found github.com/coreos/etcd/clientv3/balancer/picker in github.com/coreos/etcd v3.3.26+incompatible go: found github.com/coreos/etcd/clientv3/balancer/resolver/endpoint in github.com/coreos/etcd v3.3.26+incompatible go: found github.com/coreos/etcd/clientv3/credentials in github.com/coreos/etcd v3.3.26+incompatible go: found cloud.google.com/go/kms/apiv1 in cloud.google.com/go/kms v1.0.0 go: found github.com/d4l3k/messagediff in github.com/d4l3k/messagediff v1.2.1 go: finding module for package google.golang.org/grpc/naming github.com/chrislusf/seaweedfs/weed/filer/tikv imports github.com/tikv/client-go/v2/tikv imports go.etcd.io/etcd/clientv3 tested by go.etcd.io/etcd/clientv3.test imports github.com/coreos/etcd/integration imports github.com/coreos/etcd/proxy/grpcproxy imports google.golang.org/grpc/naming: module google.golang.org/grpc@latest found (v1.41.0), but does not contain package google.golang.org/grpc/naming --- go.mod | 27 +-- go.sum | 343 ++------------------------- weed/command/imports.go | 1 - weed/command/scaffold/filer.toml | 8 - weed/filer/tikv/tikv.go | 5 - weed/filer/tikv/tikv_store.go | 389 ------------------------------- weed/filer/tikv/tikv_store_kv.go | 50 ---- 7 files changed, 26 insertions(+), 797 deletions(-) delete mode 100644 weed/filer/tikv/tikv.go delete mode 100644 weed/filer/tikv/tikv_store.go delete mode 100644 weed/filer/tikv/tikv_store_kv.go diff --git a/go.mod b/go.mod index a0907ac00..0651f0a25 100644 --- a/go.mod +++ b/go.mod @@ -8,7 +8,6 @@ require ( cloud.google.com/go/storage v1.16.1 github.com/Azure/azure-pipeline-go v0.2.3 github.com/Azure/azure-storage-blob-go v0.14.0 - github.com/BurntSushi/toml v0.3.1 // indirect github.com/DataDog/zstd v1.3.6-0.20190409195224-796139022798 // indirect github.com/OneOfOne/xxhash v1.2.2 github.com/Shopify/sarama v1.23.1 @@ -39,6 +38,7 @@ require ( github.com/fsnotify/fsnotify v1.4.9 // indirect github.com/go-errors/errors v1.1.1 // indirect github.com/go-redis/redis/v8 v8.4.4 + github.com/go-redsync/redsync/v4 v4.4.1 github.com/go-sql-driver/mysql v1.5.0 github.com/go-stack/stack v1.8.0 // indirect github.com/go-zookeeper/zk v1.0.2 // indirect @@ -54,7 +54,6 @@ require ( github.com/googleapis/gax-go v2.0.2+incompatible // indirect github.com/googleapis/gax-go/v2 v2.1.0 // indirect github.com/gorilla/mux v1.7.4 - github.com/gorilla/websocket v1.4.1 // indirect github.com/grpc-ecosystem/go-grpc-middleware v1.1.0 github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed // indirect github.com/hashicorp/errwrap v1.0.0 // indirect @@ -74,6 +73,7 @@ require ( github.com/konsorten/go-windows-terminal-sequences v1.0.3 // indirect github.com/kurin/blazer v0.5.3 github.com/lib/pq v1.10.0 + github.com/linxGnu/grocksdb v1.6.38 github.com/magiconair/properties v1.8.1 // indirect github.com/mailru/easyjson v0.7.1 // indirect github.com/mattn/go-ieproxy v0.0.1 // indirect @@ -110,11 +110,11 @@ require ( github.com/spf13/viper v1.4.0 github.com/streadway/amqp v0.0.0-20200108173154-1c71cc93ed71 github.com/stretchr/testify v1.7.0 + github.com/stvp/tempredis v0.0.0-20181119212430-b82af8480203 github.com/syndtr/goleveldb v1.0.1-0.20190318030020-c3a204f8e965 github.com/tidwall/gjson v1.8.1 github.com/tidwall/match v1.0.3 github.com/tidwall/pretty v1.1.0 // indirect - github.com/tikv/client-go/v2 v2.0.0-alpha.0.20210824090536-16d902a3c7e5 github.com/tsuna/gohbase v0.0.0-20201125011725-348991136365 github.com/tylertreat/BoomFilters v0.0.0-20210315201527-1a82519a3e43 github.com/valyala/bytebufferpool v1.0.0 @@ -125,7 +125,7 @@ require ( github.com/xdg-go/scram v1.0.2 // indirect github.com/xdg-go/stringprep v1.0.2 // indirect github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d // indirect - go.etcd.io/etcd v3.3.25+incompatible + go.etcd.io/etcd/client/v3 v3.5.0 go.mongodb.org/mongo-driver v1.7.0 go.opencensus.io v0.23.0 // indirect go.opentelemetry.io/otel v0.15.0 // indirect @@ -136,13 +136,13 @@ require ( golang.org/x/image v0.0.0-20200119044424-58c23975cae1 golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f // indirect - golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf + golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365 golang.org/x/text v0.3.6 // indirect golang.org/x/tools v0.1.5 golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect - google.golang.org/api v0.56.0 + google.golang.org/api v0.57.0 google.golang.org/appengine v1.6.7 // indirect - google.golang.org/genproto v0.0.0-20210903162649-d08c68adba83 // indirect + google.golang.org/genproto v0.0.0-20210921142501-181ce0d877f6 // indirect google.golang.org/grpc v1.40.0 google.golang.org/protobuf v1.27.1 gopkg.in/inf.v0 v0.9.1 // indirect @@ -164,28 +164,25 @@ require ( ) require ( - github.com/coreos/etcd v3.3.10+incompatible // indirect - github.com/go-redsync/redsync/v4 v4.4.1 // indirect + cloud.google.com/go/kms v1.0.0 // indirect + github.com/d4l3k/messagediff v1.2.1 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/jcmturner/aescts/v2 v2.0.0 // indirect github.com/jcmturner/dnsutils/v2 v2.0.0 // indirect github.com/jcmturner/goidentity/v6 v6.0.1 // indirect github.com/jcmturner/rpc/v2 v2.0.2 // indirect - github.com/leodido/go-urn v1.2.0 // indirect - github.com/linxGnu/grocksdb v1.6.38 // indirect github.com/mattn/go-runewidth v0.0.7 // indirect - github.com/miekg/dns v1.1.25-0.20191211073109-8ebf2e419df7 // indirect - github.com/opentracing/opentracing-go v1.2.0 // indirect + github.com/mattn/go-sqlite3 v2.0.1+incompatible // indirect github.com/spf13/pflag v1.0.5 // indirect - github.com/stvp/tempredis v0.0.0-20181119212430-b82af8480203 // indirect go.etcd.io/etcd/api/v3 v3.5.0 // indirect go.etcd.io/etcd/client/pkg/v3 v3.5.0 // indirect - go.etcd.io/etcd/client/v3 v3.5.0 // indirect go.uber.org/atomic v1.7.0 // indirect go.uber.org/multierr v1.7.0 // indirect go.uber.org/zap v1.17.0 // indirect + golang.org/x/mod v0.4.2 // indirect golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect gopkg.in/yaml.v2 v2.4.0 // indirect + gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect ) // replace github.com/seaweedfs/fuse => /Users/chris/go/src/github.com/seaweedfs/fuse diff --git a/go.sum b/go.sum index 232d788b3..f049edbae 100644 --- a/go.sum +++ b/go.sum @@ -15,7 +15,6 @@ cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bP cloud.google.com/go v0.55.0/go.mod h1:ZHmoY+/lIMNkN2+fBmuTiqZ4inFhvQad8ft7MT8IV5Y= cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= -cloud.google.com/go v0.58.0 h1:vtAfVc723K3xKq1BQydk/FyCldnaNFhGhpJxaJzgRMQ= cloud.google.com/go v0.58.0/go.mod h1:W+9FnSUw6nhVwXlFcp1eL+krq5+HQUJeUogSeJZZiWg= cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= @@ -36,12 +35,12 @@ cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNF cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= -cloud.google.com/go/bigquery v1.8.0 h1:PQcPefKFdaIzjQFbiyOgAqyx8q5djaE7x9Sqe712DPA= cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= -cloud.google.com/go/datastore v1.1.0 h1:/May9ojXjRkPBNVrq+oWLqmWCkr4OU5uRY29bu0mRyQ= cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= cloud.google.com/go/firestore v1.2.0/go.mod h1:iISCjWnTpnoJT1R287xRdjvQHJrxQOpeah4phb5D3h0= +cloud.google.com/go/kms v1.0.0 h1:YkIeqPXqTAlwXk3Z2/WG0d6h1tqJQjU354WftjEoP9E= +cloud.google.com/go/kms v1.0.0/go.mod h1:nhUehi+w7zht2XrUfvTRNpxrfayBHqP4lu2NSywui/0= cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= @@ -51,7 +50,6 @@ cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiy cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= -cloud.google.com/go/storage v1.9.0 h1:oXnZyBjHB6hC8TnSle0AWW6pGJ29EuSo5ww+SFmdNBg= cloud.google.com/go/storage v1.9.0/go.mod h1:m+/etGaqZbylxaNT876QGXqEHp4PR2Rq5GMqICWb9bU= cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= cloud.google.com/go/storage v1.16.1 h1:sMEIc4wxvoY3NXG7Rn9iP7jb/2buJgWR1vNXCR/UPfs= @@ -109,35 +107,23 @@ github.com/DataDog/zstd v1.3.6-0.20190409195224-796139022798 h1:2T/jmrHeTezcCM58 github.com/DataDog/zstd v1.3.6-0.20190409195224-796139022798/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo= github.com/GoogleCloudPlatform/cloudsql-proxy v0.0.0-20191009163259-e802c2cb94ae/go.mod h1:mjwGPas4yKduTyubHvD1Atl9r1rUq8DfVy+gkVvZ+oo= github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= -github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE= github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= -github.com/PuerkitoBio/purell v1.1.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= -github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= -github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo= github.com/Shopify/sarama v1.23.1 h1:XxJBCZEoWJtoWjf/xRbmGUpAmTZGnuuF0ON0EvxxBrs= github.com/Shopify/sarama v1.23.1/go.mod h1:XLH1GYJnLVE0XCr6KdJGVJRTwY30moWNJ4sERjXX6fs= github.com/Shopify/toxiproxy v2.1.4+incompatible h1:TKdv8HiTLgE5wdJuEML90aBgNWsokNbMijUGhmcoBJc= github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI= -github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg= github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g= -github.com/VividCortex/mysqlerr v0.0.0-20200629151747-c28746d985dd/go.mod h1:f3HiCrHjHBdcm6E83vGaXh1KomZMA2P6aeo3hKx/wg0= -github.com/Xeoncross/go-aesctr-with-hmac v0.0.0-20200623134604-12b17a7ff502/go.mod h1:pmnBM9bxWSiHvC/gSWunUIyDvGn33EkP2CUjxFKtTTM= github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= -github.com/antihax/optional v0.0.0-20180407024304-ca021399b1a6/go.mod h1:V8iCPQYkqmusNa815XgQio277wI47sdRh1dUOLdyC6Q= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= -github.com/apache/thrift v0.14.0 h1:vqZ2DP42i8th2OsgCcYZkirtbzvpZEFx53LiWDJXIAs= -github.com/apache/thrift v0.14.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= -github.com/appleboy/gin-jwt/v2 v2.6.3/go.mod h1:MfPYA4ogzvOcVkRwAxT7quHOtQmVKDpTwxyUrC2DNw0= -github.com/appleboy/gofight/v2 v2.1.2/go.mod h1:frW+U1QZEdDgixycTj4CygQ48yLTUhplt43+Wczp3rw= github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= @@ -165,16 +151,12 @@ github.com/buraksezer/consistent v0.0.0-20191006190839-693edf70fd72 h1:fUmDBbSvv github.com/buraksezer/consistent v0.0.0-20191006190839-693edf70fd72/go.mod h1:OEE5igu/CDjGegM1Jn6ZMo7R6LlV/JChAkjfQQIRLpg= github.com/bwmarrin/snowflake v0.3.0 h1:xm67bEhkKh6ij1790JB83OujPR5CzNe8QuQqAgISZN0= github.com/bwmarrin/snowflake v0.3.0/go.mod h1:NdZxfVWX+oR6y2K0o6qAYv6gIOP9rjG0/E9WsDpxqwE= -github.com/cakturk/go-netstat v0.0.0-20200220111822-e5b49efee7a5 h1:BjkPE3785EwPhhyuFkbINB+2a1xATwk8SNDWnJiD41g= -github.com/cakturk/go-netstat v0.0.0-20200220111822-e5b49efee7a5/go.mod h1:jtAfVaU/2cu1+wdSRPWE2c1N2qeAA3K4RH9pYgqwets= github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ= github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= -github.com/cenkalti/backoff/v4 v4.0.2/go.mod h1:eEew/i+1Q6OrCDZh3WiXYv3+nJwBASZ8Bog/87DQnVg= github.com/census-instrumentation/opencensus-proto v0.2.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= -github.com/cespare/xxhash/v2 v2.1.0/go.mod h1:dgIUBU3pDso/gPgZ1osOZ0iQf77oPR28Tjxl5dIMyVM= github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/chrislusf/raft v1.0.7 h1:reybAIwnQOTSgTj1YgflbJFWLSN0KVQSxe8gDZYa04o= @@ -188,13 +170,11 @@ github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGX github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa h1:OaNxuTZr7kxeODyLWsRMC+OD03aFUH+mW6r2d+MWa5Y= github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI= github.com/colinmarc/hdfs/v2 v2.2.0 h1:4AaIlTq+/sWmeqYhI0dX8bD4YrMQM990tRjm636FkGM= github.com/colinmarc/hdfs/v2 v2.2.0/go.mod h1:Wss6n3mtaZyRwWaqtSH+6ge01qT0rw9dJJmvoUnIQ/E= github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= -github.com/coreos/etcd v3.3.10+incompatible h1:jFneRYjIvLMLhDLCzuTuU4rSJUjRplcJQ7pD7MnhC04= github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-semver v0.3.0 h1:wkHLiw0WNATZnSG7epLsujiMCgPAc9xhjJ4tgnAxmfM= @@ -202,43 +182,25 @@ github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3Ee github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e h1:Wf6HqHfScWJN9/ZjdUKyjop4mf3Qdd+1TvvltAvM3m8= github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= -github.com/coreos/go-systemd/v22 v22.0.0 h1:XJIw/+VlJ+87J+doOxznsAWIdmWuViOVhkQamW5YV28= -github.com/coreos/go-systemd/v22 v22.0.0/go.mod h1:xO0FLkIi5MaZafQlIrOotqXZ90ih+1atmu1JpKERPPk= github.com/coreos/go-systemd/v22 v22.3.2 h1:D9/bQk5vlXQFZ6Kwuu6zaiXJ9oTPe68++AzAJc1DzSI= github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= -github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f h1:lBNOc5arjvs8E5mO2tbpBpLoyyu8B6e44T7hJy6potg= github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= -github.com/corona10/goimagehash v1.0.2/go.mod h1:/l9umBhvcHQXVtQO1V6Gp1yD20STawkhRnnX0D1bvVI= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= -github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= -github.com/cznic/golex v0.0.0-20181122101858-9c343928389c/go.mod h1:+bmmJDNmKlhWNG+gwWCkaBoTy39Fs+bzRxVBzoTQbIc= -github.com/cznic/mathutil v0.0.0-20181122101859-297441e03548 h1:iwZdTE0PVqJCos1vaoKsclOGD3ADKpshg3SRtYBbwso= -github.com/cznic/mathutil v0.0.0-20181122101859-297441e03548/go.mod h1:e6NPNENfs9mPDVNRekM7lKScauxd5kXTr1Mfyig6TDM= -github.com/cznic/parser v0.0.0-20160622100904-31edd927e5b1/go.mod h1:2B43mz36vGZNZEwkWi8ayRSSUXLfjL8OkbzwW4NcPMM= -github.com/cznic/sortutil v0.0.0-20181122101858-f5f958428db8/go.mod h1:q2w6Bg5jeox1B+QkJ6Wp/+Vn0G/bo3f1uY7Fn3vivIQ= -github.com/cznic/strutil v0.0.0-20171016134553-529a34b1c186/go.mod h1:AHHPPPXTw0h6pVabbcbyGRK1DckRn7r/STdZEeIDzZc= -github.com/cznic/y v0.0.0-20170802143616-045f81c6662a/go.mod h1:1rk5VM7oSnA4vjp+hrLQ3HWHa+Y4yPCa3/CsJrcNnvs= github.com/d4l3k/messagediff v1.2.1 h1:ZcAIMYsUg0EAp9X+tt8/enBE/Q8Yd5kzPynLyKptt9U= github.com/d4l3k/messagediff v1.2.1/go.mod h1:Oozbb1TVXFac9FtSIxHBMnBCq2qeH/2KkEQxENCrlLo= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/denisenkom/go-mssqldb v0.0.0-20191124224453-732737034ffd/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU= github.com/devigned/tab v0.1.1/go.mod h1:XG9mPq0dFghrYvoBF3xdRrJzSTX1b7IQrvaL9mzjeJY= -github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= -github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2 h1:tdlZCpZ/P9DhczCTSixgIKmwPv6+wP5DGjqLYw5SUiA= -github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= github.com/dimchansky/utfbom v1.1.0/go.mod h1:rO41eb7gLfo8SF1jd9F8HplJm1Fewwi4mQvIirEdv+8= github.com/disintegration/imaging v1.6.2 h1:w1LecBlG2Lnp8B3jk5zSuNqd7b4DXhcjwek1ei82L+c= github.com/disintegration/imaging v1.6.2/go.mod h1:44/5580QXChDfwIclfc/PCwrr44amcmDAg8hxG0Ewe4= -github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw= -github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= @@ -250,7 +212,6 @@ github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1 github.com/eapache/queue v1.1.0 h1:YOEu7KNc61ntiQlcEeUIoDTJ2o8mQznoNvUhiigpIqc= github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I= github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= -github.com/eknkc/amber v0.0.0-20171010120322-cdade1c07385/go.mod h1:0vRUJqYpeSZifjYj7uP3BG/gKcuzL9xWVV/Y+cK33KM= github.com/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4safvEdbitLhGGK48rN6g= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= @@ -260,7 +221,6 @@ github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.m github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5/go.mod h1:a2zkGnVExMxdzMo3M0Hi/3sEU+cWnZpSni0O6/Yb/P0= github.com/facebookgo/clock v0.0.0-20150410010913-600d898af40a h1:yDWHCSQ40h88yih2JAcL6Ls/kVkSE8GFACTGVnMPruw= github.com/facebookgo/clock v0.0.0-20150410010913-600d898af40a/go.mod h1:7Ga40egUymuWXxAe151lTNnCv97MddSOVsjpPPkityA= github.com/facebookgo/ensure v0.0.0-20200202191622-63f1cf65ac4c h1:8ISkoahWXwZR41ois5lSJBSVw4D0OV19Ht/JSTzvSv0= @@ -272,11 +232,8 @@ github.com/facebookgo/stats v0.0.0-20151006221625-1b76add642e4/go.mod h1:vsJz7uE github.com/facebookgo/subset v0.0.0-20200203212716-c811ad88dec4 h1:7HZCaLC5+BZpmbhCOZJ293Lz68O7PYrF2EzeiFMwCLk= github.com/facebookgo/subset v0.0.0-20200203212716-c811ad88dec4/go.mod h1:5tD+neXqOorC30/tWg0LCSkrqj/AR6gu8yY8/fpw1q0= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= -github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= -github.com/fatih/structtag v1.2.0/go.mod h1:mBJUNpUnHmRKrKlQQlmCrh5PuhftFbNv8Ys4/aAZl94= github.com/fclairamb/ftpserverlib v0.8.0 h1:ZsWUQ8Vg3Y8LIWRUAzVnFXY982Yztz2odDdK/UVJtik= github.com/fclairamb/ftpserverlib v0.8.0/go.mod h1:xF4cy07oCHA9ZorKehsFGqA/1UHYaonmqHK2g3P1X8U= -github.com/fogleman/gg v1.3.0/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= github.com/form3tech-oss/jwt-go v3.2.2+incompatible h1:TcekIExNqud5crz4xD2pavyTgWiPvpYe4Xau31I0PRk= github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw= @@ -289,19 +246,6 @@ github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMo github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= -github.com/gin-contrib/gzip v0.0.1/go.mod h1:fGBJBCdt6qCZuCAOwWuFhBB4OOq9EFqlo5dEaFhhu5w= -github.com/gin-contrib/sse v0.0.0-20170109093832-22d885f9ecc7/go.mod h1:VJ0WA2NBN22VlZ2dKZQPAPnyWw5XTlK1KymzLKsr59s= -github.com/gin-contrib/sse v0.0.0-20190301062529-5545eab6dad3/go.mod h1:VJ0WA2NBN22VlZ2dKZQPAPnyWw5XTlK1KymzLKsr59s= -github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= -github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= -github.com/gin-gonic/gin v1.3.0/go.mod h1:7cKuhb5qV2ggCFctp2fJQ+ErvciLZrIeoOSOm6mUr7Y= -github.com/gin-gonic/gin v1.4.0/go.mod h1:OW2EZn3DO8Ln9oIKOvM++LBO+5UPHJJDH72/q/3rZdM= -github.com/gin-gonic/gin v1.5.0/go.mod h1:Nd6IXA8m5kNZdNEHMBd93KT+mdY3+bewLgRvmCsR2Do= -github.com/gin-gonic/gin v1.6.0 h1:Lb3veSYoGaNck69fV2+Vf2juLSsHpMTf3Vk5+X+EDJg= -github.com/gin-gonic/gin v1.6.0/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M= -github.com/gin-gonic/gin v1.7.0/go.mod h1:jD2toBW3GZUr5UMcdrwQA10I7RuaFOl/SGeDjXkfUtY= -github.com/go-chi/chi v4.0.2+incompatible/go.mod h1:eB3wogJHnLi3x/kFX2A+IbTBlXxmMeXJVKy9tTv1XzQ= -github.com/go-echarts/go-echarts v1.0.0/go.mod h1:qbmyAb/Rl1f2w7wKba1D4LoNq4U164yO4/wedFbcWyo= github.com/go-errors/errors v1.1.1 h1:ljK/pL5ltg3qoN+OtN6yCv9HWSfMwxSx90GJCZQxYNg= github.com/go-errors/errors v1.1.1/go.mod h1:psDX2osz5VnTOnFWbDeWwS7yejl+uV3FEWEp4lssFEs= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= @@ -317,41 +261,16 @@ github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9 github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-logfmt/logfmt v0.5.0 h1:TrB8swr/68K7m9CcGut2g3UOihhbcbiMAYiuTXdEih4= github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= -github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas= -github.com/go-ole/go-ole v1.2.4/go.mod h1:XCwSNxSkXRo4vlyPy93sltvi/qJq0jqQhjqQNIwKuxM= -github.com/go-openapi/jsonpointer v0.17.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M= -github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg= -github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= -github.com/go-openapi/jsonreference v0.17.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I= -github.com/go-openapi/jsonreference v0.19.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I= -github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc= -github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8= -github.com/go-openapi/spec v0.19.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI= -github.com/go-openapi/spec v0.19.4/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8Lj9mJglo= -github.com/go-openapi/swag v0.17.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg= -github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= -github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= -github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= -github.com/go-playground/locales v0.12.1/go.mod h1:IUMDtCfWo/w/mtMfIE/IG2K+Ey3ygWanZIBtBW0W2TM= -github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q= -github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8= -github.com/go-playground/overalls v0.0.0-20180201144345-22ec1a223b7c/go.mod h1:UqxAgEOt89sCiXlrc/ycnx00LVvUO/eS8tMUkWX4R7w= -github.com/go-playground/universal-translator v0.16.0/go.mod h1:1AnU7NaIRDWWzGEKwgtJRd2xk99HeFyHw3yid4rvQIY= -github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no= -github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA= -github.com/go-playground/validator/v10 v10.2.0 h1:KgJ0snyC2R9VXYN2rneOtQcw5aHQB1Vv0sFl1UcHBOY= -github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI= -github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4= +github.com/go-redis/redis v6.15.9+incompatible h1:K0pv1D7EQUjfyoMql+r/jZqCLizCGKFlFgcHWWmHQjg= github.com/go-redis/redis v6.15.9+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA= +github.com/go-redis/redis/v7 v7.4.0 h1:7obg6wUoj05T0EpY0o8B59S9w5yeMWql7sw2kwNW1x4= github.com/go-redis/redis/v7 v7.4.0/go.mod h1:JDNMw23GTyLNC4GZu9njt15ctBQVn7xjRfnwdHj/Dcg= github.com/go-redis/redis/v8 v8.1.1/go.mod h1:ysgGY09J/QeDYbu3HikWEIPCwaeOkuNoTgKayTEaEOw= github.com/go-redis/redis/v8 v8.4.4 h1:fGqgxCTR1sydaKI00oQf3OmkU/DIe/I/fYXvGklCIuc= github.com/go-redis/redis/v8 v8.4.4/go.mod h1:nA0bQuF0i5JFx4Ta9RZxGKXFrQ8cRWntra97f0196iY= github.com/go-redsync/redsync/v4 v4.4.1 h1:Z0AaOpoLvzfZwLK+3uCDHcTxOXck2juzumu1EPJwCUI= github.com/go-redsync/redsync/v4 v4.4.1/go.mod h1:QBOJAs1k8O6Eyrre4a++pxQgHe5eQ+HF56KuTVv+8Bs= -github.com/go-sql-driver/mysql v1.3.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= -github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs= github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk= @@ -382,29 +301,22 @@ github.com/gobuffalo/packd v0.1.0/go.mod h1:M2Juc+hhDXf/PnmBANFCqx4DM3wRbgDvnVWe github.com/gobuffalo/packr/v2 v2.0.9/go.mod h1:emmyGweYTm6Kdper+iywB6YK5YzuKchGtJQZ0Odn4pQ= github.com/gobuffalo/packr/v2 v2.2.0/go.mod h1:CaAwI0GPIAv+5wKLtv8Afwl+Cm78K/I/VCm/3ptBN+0= github.com/gobuffalo/syncx v0.0.0-20190224160051-33c29581e754/go.mod h1:HhnNqWY95UYwwW3uSASeV7vtgYkT2t16hJgV3AEPUpw= -github.com/goccy/go-graphviz v0.0.5/go.mod h1:wXVsXxmyMQU6TN3zGRttjNn3h+iCAS7xQFC6TlNvLhk= github.com/gocql/gocql v0.0.0-20210707082121-9a3953d1826d h1:k544nNVphXK4Yt0FTduvOvCfJabEY/DMkdNw0zpCwBE= github.com/gocql/gocql v0.0.0-20210707082121-9a3953d1826d/go.mod h1:3gM2c4D3AnkISwBxGnMMsS8Oy4y2lhbPRsH4xnJrHG8= -github.com/godbus/dbus/v5 v5.0.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s= -github.com/gogo/protobuf v0.0.0-20180717141946-636bf0302bc9/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= -github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/golang-jwt/jwt v3.2.1+incompatible h1:73Z+4BJcrTC+KczS6WvTPvRGOp1WmfEP4Q1lOd9Z/+c= github.com/golang-jwt/jwt v3.2.1+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= -github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0= -github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e h1:1r7pUrabqp18hOBcwBwiTsbnFeTZHV9eER/QT5JVZxY= github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= @@ -413,13 +325,11 @@ github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfb github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.3 h1:GV+pQPG/EUUbkh47niozDcADz6go/dUwhVzdUQHIVRw= github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8= github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= -github.com/golang/protobuf v0.0.0-20180814211427-aa810b61a9c7/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.1.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= @@ -434,7 +344,6 @@ github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:W github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/golang/protobuf v1.4.3 h1:JjCZWpVbqXDqFVmTfYWEVTMIYrL/NPdPSCHPJ0T/raM= github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM= @@ -442,10 +351,10 @@ github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/golang/snappy v0.0.2-0.20190904063534-ff6b7dc882cf/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/gomodule/redigo v1.8.2 h1:H5XSIre1MB5NbPYFp+i1NBbb5qN1W8Y8YAQoAYbkm8k= github.com/gomodule/redigo v1.8.2/go.mod h1:P9dn9mFrCBvWhGE1wpxx6fgq7BAeLBk+UUUzlpkBYO0= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0 h1:0udJVsspx3VBr5FwtLhQQtuAsVc79tTq0ocGIPAU6qo= @@ -460,7 +369,6 @@ github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= @@ -474,13 +382,13 @@ github.com/google/martian v2.1.1-0.20190517191504-25dcb96d9e51+incompatible h1:x github.com/google/martian v2.1.1-0.20190517191504-25dcb96d9e51+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= +github.com/google/martian/v3 v3.2.1 h1:d8MncMlErDFTwQGBK1xhv026j9kqhvw1Qv9IbWT1VLQ= github.com/google/martian/v3 v3.2.1/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200407044318-7d83b28da2e9/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20200507031123-427632fa3b1c/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= @@ -492,12 +400,10 @@ github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLe github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= -github.com/google/shlex v0.0.0-20181106134648-c34317bd91bf/go.mod h1:RpwtwJQFrIEPstU94h88MWPXP2ektJZ8cZ0YntAmXiE= github.com/google/subcommands v1.0.1/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk= github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.2.0 h1:qJYtXnJRWmpe7m/3XlyhrsLrEURqHRM2kxzoxXqyUDs= github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= @@ -506,7 +412,6 @@ github.com/google/wire v0.4.0/go.mod h1:ngWDr9Qvq3yZA10YrxfyGELY/AFWGVpy9c1LTRi1 github.com/googleapis/gax-go v2.0.2+incompatible h1:silFMLAnr330+NRuag/VjIGF7TLp/LBrV2CJKFLWEww= github.com/googleapis/gax-go v2.0.2+incompatible/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= -github.com/googleapis/gax-go/v2 v2.0.5 h1:sjZBwGj9Jlw33ImPtvFviGYvseOtDM7hkSKB7+Tv3SM= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/googleapis/gax-go/v2 v2.1.0 h1:6DWmvNpomjL1+3liNSZbVns3zsYzzCjm6pRBO1tLeso= github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0= @@ -522,21 +427,14 @@ github.com/gorilla/sessions v1.2.0 h1:S7P+1Hm5V/AT9cjEcUD5uDaQSX0OE577aCXgoaKpYb github.com/gorilla/sessions v1.2.0/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM= github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= -github.com/gorilla/websocket v1.4.1 h1:q7AeDBpnBk8AogcD4DSag/Ukw/KV+YhzLj2bP5HvKCM= -github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= github.com/grpc-ecosystem/go-grpc-middleware v1.1.0 h1:THDBEeQ9xZ8JEaCLyLQqXMMdRqNr0QAUJTIkQAUtFjg= github.com/grpc-ecosystem/go-grpc-middleware v1.1.0/go.mod h1:f5nM7jw/oeRSadq3xCzHAvxcr8HZnzsqU6ILg/0NiiE= -github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 h1:Ovs26xHkKqVztRpIrF/92BcuyuQ/YW4NSIpoGtfXNho= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= -github.com/grpc-ecosystem/grpc-gateway v1.12.1 h1:zCy2xE9ablevUOrUZc3Dl72Dt+ya2FNAvC2yLYMHzi4= -github.com/grpc-ecosystem/grpc-gateway v1.12.1/go.mod h1:8XEsbTttt/W+VvjtQhLACqCisSPWTxCZ7sBRjU6iH9c= -github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= -github.com/gtank/cryptopasta v0.0.0-20170601214702-1f550f6f2f69/go.mod h1:YLEMZOtU+AZ7dhN9T/IpGhXVGly2bvkJQ+zxj3WeVQo= github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed h1:5upAirOpQc1Q53c0bnx2ufif5kANL7bfZWcc6VJWJd8= github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed/go.mod h1:tMWxXQ9wFIaZeTI9F+hmhFiGpFmhOHzyShyFUhRm0H4= github.com/hashicorp/consul/api v1.3.0/go.mod h1:MmDNSzIMUjNpY/mQ398R4bk2FnqQLoPndWW5VkKPlCE= @@ -546,7 +444,6 @@ github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brv github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= -github.com/hashicorp/go-multierror v1.0.0 h1:iVjPR7a6H0tWELX5NxNe7bYopibicUzc7uPribsnS6o= github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= github.com/hashicorp/go-multierror v1.1.0 h1:B9UzwGQJehnUY1yNrnwREHc3fGbC2xefo8g4TbElacI= github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA= @@ -569,7 +466,6 @@ github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2p github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg= -github.com/hypnoglow/gormzap v0.3.0/go.mod h1:5Wom8B7Jl2oK0Im9hs6KQ+Kl92w4Y7gKCrj66rhyvw0= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= @@ -587,12 +483,8 @@ github.com/jcmturner/gokrb5/v8 v8.4.1 h1:IGSJfqBzMS6TA0oJ7DxXdyzPK563QHa8T2IqER2 github.com/jcmturner/gokrb5/v8 v8.4.1/go.mod h1:T1hnNppQsBtxW0tCHMHTkAt8n/sABdzZgZdoFrZaZNM= github.com/jcmturner/rpc/v2 v2.0.2 h1:gMB4IwRXYsWw4Bc6o/az2HJgFUA1ffSh90i26ZJ6Xl0= github.com/jcmturner/rpc/v2 v2.0.2/go.mod h1:VUJYCIDm3PVOEHw8sgt091/20OJjskO/YJki3ELg/Hc= -github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jinzhu/copier v0.2.8 h1:N8MbL5niMwE3P4dOwurJixz5rMkKfujmMRFmAanSzWE= github.com/jinzhu/copier v0.2.8/go.mod h1:24xnZezI2Yqac9J61UC6/dG/k76ttpq0DdJI3QmUvro= -github.com/jinzhu/gorm v1.9.12/go.mod h1:vhTjlKSJUTWNtcbQtrMBFCxy7eXTzeCAzfL5fBZT/Qs= -github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= -github.com/jinzhu/now v1.0.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= github.com/jmespath/go-jmespath v0.3.0/go.mod h1:9QtRXoHjLGCJ5IBSaohpXITPlowMeeYCZ7fLUTSywik= @@ -601,24 +493,17 @@ github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHW github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg= -github.com/jonboulle/clockwork v0.1.0 h1:VKV+ZcuP6l3yW9doeqz6ziZGgcynBVQO+obU0+0hcPo= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= -github.com/joomcode/errorx v1.0.1/go.mod h1:kgco15ekB6cs+4Xjzo7SPeXzx38PbJzBwbnu9qfVNHQ= github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= -github.com/json-iterator/go v1.1.5/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.11 h1:uVUAXhF2To8cbw/3xN3pxj6kk7TYKs98NIrTqPlMWAQ= github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= -github.com/jstemmer/go-junit-report v0.9.1 h1:6QPYqodiu3GuPL+7mfx+NwDdp2eTkp9IfEUpgAwUN0o= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= -github.com/juju/ratelimit v1.0.1 h1:+7AIFJVQ0EQgq/K9+0Krm7m530Du7tIz0METWzN0RgY= -github.com/juju/ratelimit v1.0.1/go.mod h1:qapgC/Gy+xNh9UxzV13HGGl/6UXNN+ct+vwSgWNm/qk= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= github.com/karlseguin/ccache/v2 v2.0.7 h1:y5Pfi4eiyYCOD6LS/Kj+o6Nb4M5Ngpw9qFQs+v44ZYM= @@ -630,7 +515,6 @@ github.com/karrick/godirwalk v1.10.3/go.mod h1:RoGL9dQei4vP9ilrpETWE8CLOZ1kiN0Lh github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs= github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= -github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/klauspost/compress v1.9.5/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= @@ -650,14 +534,10 @@ github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORN github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kurin/blazer v0.5.3 h1:SAgYv0TKU0kN/ETfO5ExjNAPyMt2FocO2s/UlCHfjAk= github.com/kurin/blazer v0.5.3/go.mod h1:4FCXMUWo9DllR2Do4TtBd377ezyAJ51vB5uTBjt0pGU= -github.com/leodido/go-urn v1.1.0/go.mod h1:+cyI34gQWZcE1eQU7NVgKkkzdXDQHr1dBMtdAPozLkw= -github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y= -github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= github.com/lib/pq v1.1.1/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.10.0 h1:Zx5DJFEYQXio93kgXnQ09fXNiUKsqv4OUEu2UtGcB1E= github.com/lib/pq v1.10.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= @@ -669,42 +549,29 @@ github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0Q github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/magiconair/properties v1.8.1 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzRKO2BQ4= github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= -github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= -github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= -github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.7.1 h1:mdxE1MF9o53iCb2Ghj1VfWvh7ZOwHpnVG/xwXrV90U8= github.com/mailru/easyjson v0.7.1/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs= github.com/markbates/oncer v0.0.0-20181203154359-bf2de49a0be2/go.mod h1:Ld9puTsIW75CHf65OeIOkyKbteujpZVXDpWK6YGZbxE= github.com/markbates/safe v1.0.1/go.mod h1:nAqgmRi7cY2nqMc92/bSEeQA+R4OheNU2T1kNSCBdG0= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= -github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-ieproxy v0.0.0-20190610004146-91bb50d98149/go.mod h1:31jz6HNzdxOmlERGGEc4v/dMssOfmp2p5bT/okiKFFc= github.com/mattn/go-ieproxy v0.0.0-20190702010315-6dee0af9227d/go.mod h1:31jz6HNzdxOmlERGGEc4v/dMssOfmp2p5bT/okiKFFc= github.com/mattn/go-ieproxy v0.0.1 h1:qiyop7gCflfhwCzGyeT0gro3sF9AIg9HU98JORTkqfI= github.com/mattn/go-ieproxy v0.0.1/go.mod h1:pYabZ6IHcRpFh7vIaLfK7rdcWgFEb3SFJ6/gNWuh88E= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= -github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= -github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= -github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ= -github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-runewidth v0.0.3/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-runewidth v0.0.7 h1:Ei8KR0497xHyKJPAv59M1dkC+rOZCMBJ+t3fZ+twI54= github.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= -github.com/mattn/go-shellwords v1.0.3/go.mod h1:3xCvwCdWdlDJUrvuMn7Wuy9eWs4pE8vqg+NOMyg4B2o= github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= github.com/mattn/go-sqlite3 v2.0.1+incompatible h1:xQ15muvnzGBHpIpdrNi1DA5x0+TcBZzsIDwmw9uTHzw= github.com/mattn/go-sqlite3 v2.0.1+incompatible/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= -github.com/mgechev/dots v0.0.0-20190921121421-c36f7dcfbb81/go.mod h1:KQ7+USdGKfpPjXk4Ga+5XxQM4Lm4e3gAogrreFAYpOg= -github.com/mgechev/revive v1.0.2/go.mod h1:rb0dQy1LVAxW9SWy5R3LPUjevzUbUS316U5MFySA2lo= github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= -github.com/miekg/dns v1.1.25-0.20191211073109-8ebf2e419df7 h1:AnRQ1PsgPhnGl4fv0/MDlbTxSBGeWXtDZPmbahfcHTM= -github.com/miekg/dns v1.1.25-0.20191211073109-8ebf2e419df7/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso= github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= @@ -721,8 +588,6 @@ github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lN github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc= -github.com/montanaflynn/stats v0.5.0 h1:2EkzeTSqBB4V4bJwWrt5gIIrZmpJBcoIRGS2kWLgzmk= -github.com/montanaflynn/stats v0.5.0/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/nats-io/jwt v0.2.6/go.mod h1:mQxQ0uHQ9FhEVPIcTSKwx2lqZEpXWWcCgA7R6NrWvvY= @@ -745,16 +610,12 @@ github.com/nats-io/nkeys v0.2.0 h1:WXKF7diOaPU9cJdLD7nuzwasQy9vT1tBqzXZZf3AMJM= github.com/nats-io/nkeys v0.2.0/go.mod h1:XdZpAbhgyyODYqjTawOnIOI7VlbKSarI9Gfy1tqEu/s= github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw= github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= -github.com/nfnt/resize v0.0.0-20160724205520-891127d8d1b5/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8= -github.com/nicksnyder/go-i18n v1.10.0/go.mod h1:HrK7VCrbOvQoUAQ7Vpy7i87N7JZZZ7R2xBGjv0j365Q= github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/oklog/oklog v0.3.2/go.mod h1:FCV+B7mhrz4o+ueLpx+KqkyXRGMWOYEvfiXtdGtbWGs= github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= -github.com/oleiade/reflections v1.0.1/go.mod h1:rdFxbxq4QXVZWj0F+e9jqjDkc7dbp97vkRixKo2JR60= github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= -github.com/olekukonko/tablewriter v0.0.4/go.mod h1:zq6QwlOf5SlnkVbMSr5EoBv3636FWnp+qbPhuoO21uA= github.com/olivere/elastic/v7 v7.0.19 h1:w4F6JpqOISadhYf/n0NR1cNj73xHqh4pzPwD1Gkidts= github.com/olivere/elastic/v7 v7.0.19/go.mod h1:4Jqt5xvjqpjCqgnTcHwl3j8TLs8mvoOK8NYgo/qEOu4= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= @@ -776,7 +637,6 @@ github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492/go github.com/opentracing/basictracer-go v1.0.0/go.mod h1:QfBfYuafItcjQuMwinw9GhYKwFXS9KnPs5lxoYwgW74= github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= -github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs= github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc= github.com/openzipkin-contrib/zipkin-go-opentracing v0.4.5/go.mod h1:/wsWhb9smxSfWAKL3wpBW7V8scJMt8N8gnaMCS9E/cA= github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw= @@ -787,54 +647,16 @@ github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FI github.com/pborman/getopt v0.0.0-20180729010549-6fdd0a2c7117/go.mod h1:85jBQOZwpVEaDAr341tbn15RS4fCAsIst0qp7i8ex1o= github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= -github.com/pelletier/go-toml v1.3.0/go.mod h1:PN7xzY2wHTK0K9p34ErDQMlFxa51Fk0OUruD3k1mMwo= github.com/pelletier/go-toml v1.7.0 h1:7utD74fnzVc/cpcyy8sjrlFr5vYpypUixARcHIMIGuI= github.com/pelletier/go-toml v1.7.0/go.mod h1:vwGMzjaWMwyfHwgIBhI2YUM4fB6nL6lVAvS1LBMMhTE= github.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9iaPbIdPPGyKcA8hKdoy6hAWba7Yac= github.com/peterh/liner v1.1.0 h1:f+aAedNJA6uk7+6rXsYBnhdo4Xux7ESLe+kcuVUF5os= github.com/peterh/liner v1.1.0/go.mod h1:CRroGNssyjTd/qIG2FyxByd2S8JEAZXBl4qUrZf8GS0= -github.com/petermattis/goid v0.0.0-20180202154549-b0b1615b78e5/go.mod h1:jvVRKCrJTQWu0XVbaOlby/2lO20uSCHEMzzplHXte1o= -github.com/phf/go-queue v0.0.0-20170504031614-9abe38d0371d h1:U+PMnTlV2tu7RuMK5etusZG3Cf+rpow5hqQByeCzJ2g= -github.com/phf/go-queue v0.0.0-20170504031614-9abe38d0371d/go.mod h1:lXfE4PvvTW5xOjO6Mba8zDPyw8M93B6AQ7frTGnMlA8= github.com/pierrec/lz4 v0.0.0-20190327172049-315a67e90e41/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc= github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc= github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= github.com/pierrec/lz4 v2.2.7+incompatible h1:Eerk9aiqeZo2QzsbWOAsELUf9ddvAxEdMY9LYze/DEc= github.com/pierrec/lz4 v2.2.7+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= -github.com/pingcap/check v0.0.0-20190102082844-67f458068fc8/go.mod h1:B1+S9LNcuMyLH/4HMTViQOJevkGiik3wW2AN9zb2fNQ= -github.com/pingcap/check v0.0.0-20191107115940-caf2b9e6ccf4/go.mod h1:PYMCGwN0JHjoqGr3HrZoD+b8Tgx8bKnArhSq8YVzUMc= -github.com/pingcap/check v0.0.0-20191216031241-8a5a85928f12/go.mod h1:PYMCGwN0JHjoqGr3HrZoD+b8Tgx8bKnArhSq8YVzUMc= -github.com/pingcap/check v0.0.0-20200212061837-5e12011dc712 h1:R8gStypOBmpnHEx1qi//SaqxJVI4inOqljg/Aj5/390= -github.com/pingcap/check v0.0.0-20200212061837-5e12011dc712/go.mod h1:PYMCGwN0JHjoqGr3HrZoD+b8Tgx8bKnArhSq8YVzUMc= -github.com/pingcap/errcode v0.3.0/go.mod h1:4b2X8xSqxIroj/IZ9MX/VGZhAwc11wB9wRIzHvz6SeM= -github.com/pingcap/errors v0.11.0/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8= -github.com/pingcap/errors v0.11.4/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8= -github.com/pingcap/errors v0.11.5-0.20190809092503-95897b64e011/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8= -github.com/pingcap/errors v0.11.5-0.20200917111840-a15ef68f753d/go.mod h1:g4vx//d6VakjJ0mk7iLBlKA8LFavV/sAVINT/1PFxeQ= -github.com/pingcap/errors v0.11.5-0.20201029093017-5a7df2af2ac7/go.mod h1:G7x87le1poQzLB/TqvTJI2ILrSgobnq4Ut7luOwvfvI= -github.com/pingcap/errors v0.11.5-0.20201126102027-b0a155152ca3 h1:LllgC9eGfqzkfubMgjKIDyZYaa609nNWAyNZtpy2B3M= -github.com/pingcap/errors v0.11.5-0.20201126102027-b0a155152ca3/go.mod h1:G7x87le1poQzLB/TqvTJI2ILrSgobnq4Ut7luOwvfvI= -github.com/pingcap/failpoint v0.0.0-20191029060244-12f4ac2fd11d/go.mod h1:DNS3Qg7bEDhU6EXNHF+XSv/PGznQaMJ5FWvctpm6pQI= -github.com/pingcap/failpoint v0.0.0-20200702092429-9f69995143ce/go.mod h1:w4PEZ5y16LeofeeGwdgZB4ddv9bLyDuIX+ljstgKZyk= -github.com/pingcap/failpoint v0.0.0-20210316064728-7acb0f0a3dfd h1:I8IeI8MNiZVKnwuXhcIIzz6pREcOSbq18Q31KYIzFVM= -github.com/pingcap/failpoint v0.0.0-20210316064728-7acb0f0a3dfd/go.mod h1:IVF+ijPSMZVtx2oIqxAg7ur6EyixtTYfOHwpfmlhqI4= -github.com/pingcap/goleveldb v0.0.0-20191226122134-f82aafb29989 h1:surzm05a8C9dN8dIUmo4Be2+pMRb6f55i+UIYrluu2E= -github.com/pingcap/goleveldb v0.0.0-20191226122134-f82aafb29989/go.mod h1:O17XtbryoCJhkKGbT62+L2OlrniwqiGLSqrmdHCMzZw= -github.com/pingcap/kvproto v0.0.0-20191211054548-3c6b38ea5107/go.mod h1:WWLmULLO7l8IOcQG+t+ItJ3fEcrL5FxF0Wu+HrMy26w= -github.com/pingcap/kvproto v0.0.0-20200411081810-b85805c9476c/go.mod h1:IOdRDPLyda8GX2hE/jO7gqaCV/PNFh8BZQCQZXfIOqI= -github.com/pingcap/kvproto v0.0.0-20210219064844-c1844a4775d6/go.mod h1:IOdRDPLyda8GX2hE/jO7gqaCV/PNFh8BZQCQZXfIOqI= -github.com/pingcap/kvproto v0.0.0-20210806074406-317f69fb54b4 h1:4EUpHzPFHwleKkVALyMqQbQcNziPZvU+vhUT9Wzj93E= -github.com/pingcap/kvproto v0.0.0-20210806074406-317f69fb54b4/go.mod h1:IOdRDPLyda8GX2hE/jO7gqaCV/PNFh8BZQCQZXfIOqI= -github.com/pingcap/log v0.0.0-20191012051959-b742a5d432e9/go.mod h1:4rbK1p9ILyIfb6hU7OG2CiWSqMXnp3JMbiaVJ6mvoY8= -github.com/pingcap/log v0.0.0-20200511115504-543df19646ad/go.mod h1:4rbK1p9ILyIfb6hU7OG2CiWSqMXnp3JMbiaVJ6mvoY8= -github.com/pingcap/log v0.0.0-20201112100606-8f1e84a3abc8/go.mod h1:4rbK1p9ILyIfb6hU7OG2CiWSqMXnp3JMbiaVJ6mvoY8= -github.com/pingcap/log v0.0.0-20210317133921-96f4fcab92a4 h1:ERrF0fTuIOnwfGbt71Ji3DKbOEaP189tjym50u8gpC8= -github.com/pingcap/log v0.0.0-20210317133921-96f4fcab92a4/go.mod h1:4rbK1p9ILyIfb6hU7OG2CiWSqMXnp3JMbiaVJ6mvoY8= -github.com/pingcap/parser v0.0.0-20210525032559-c37778aff307 h1:v7SipssMu4X1tVQOe3PIVE73keJNHCFXe4Cza5uNDZ8= -github.com/pingcap/parser v0.0.0-20210525032559-c37778aff307/go.mod h1:xZC8I7bug4GJ5KtHhgAikjTfU4kBv1Sbo3Pf1MZ6lVw= -github.com/pingcap/sysutil v0.0.0-20200206130906-2bfa6dc40bcd/go.mod h1:EB/852NMQ+aRKioCpToQ94Wl7fktV+FNnxf3CX/TTXI= -github.com/pingcap/sysutil v0.0.0-20210315073920-cc0985d983a3/go.mod h1:tckvA041UWP+NqYzrJ3fMgC/Hw9wnmQ/tUkp/JaHly8= -github.com/pingcap/tidb-dashboard v0.0.0-20210312062513-eef5d6404638/go.mod h1:OzFN8H0EDMMqeulPhPMw2i2JaiZWOKFQ7zdRPhENNgo= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= @@ -852,9 +674,7 @@ github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXP github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs= github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= -github.com/prometheus/client_golang v1.2.1/go.mod h1:XMU6Z2MjaRKVu/dC1qupJI9SiNkDYzz3xecMgSW/F+U= github.com/prometheus/client_golang v1.3.0/go.mod h1:hJaj2vgQTGQmVCsAACORcieXFeDPbaTKGT+JTgUa3og= -github.com/prometheus/client_golang v1.5.1/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU= github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= github.com/prometheus/client_golang v1.11.0 h1:HNkLOAEQMIDv/K+04rukrLx6ch7msSRwf3/SASFAGtQ= github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= @@ -870,7 +690,6 @@ github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y8 github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA= -github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4= github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= github.com/prometheus/common v0.26.0 h1:iMAkS2TDoNWnKM+Kopnx/8tnEStIfpYA0ur0xQzzhMQ= github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= @@ -878,7 +697,6 @@ github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= -github.com/prometheus/procfs v0.0.5/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ= github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= github.com/prometheus/procfs v0.6.0 h1:mxy4L2jP6qMonqmq+aTtOx1ifVWUgG/TAmntgbh3xv4= @@ -887,7 +705,6 @@ github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40T github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= github.com/rcrowley/go-metrics v0.0.0-20190826022208-cac0b30c2563 h1:dY6ETXrvDG7Sa4vE8ZQG4yqWg6UnOcbqTAahkV813vQ= github.com/rcrowley/go-metrics v0.0.0-20190826022208-cac0b30c2563/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= -github.com/remyoudompheng/bigfft v0.0.0-20190728182440-6a916e37a237/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 h1:OdAsTTz6OkFY5QxjkYwrChwuRruF69c169dPK26NUlk= github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= @@ -895,14 +712,9 @@ github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6L github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.2.2/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E= -github.com/sasha-s/go-deadlock v0.2.0/go.mod h1:StQn567HiB1fF2yJ44N9au7wOhrPS3iZqiDbRupzT10= -github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= -github.com/satori/go.uuid v1.2.1-0.20181016170032-d91630c85102 h1:WAQaHPfnpevd8SKXCcy5nk3JzEv2h5Q0kSwvoMqXiZs= -github.com/satori/go.uuid v1.2.1-0.20181016170032-d91630c85102/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= github.com/seaweedfs/fuse v1.2.0 h1:m4Ar3I0TY/Tzyb80lfJiXUKFcMBHqA5e/cZwxQtilZo= github.com/seaweedfs/fuse v1.2.0/go.mod h1:iwbDQv5BZACY54r6AO/6xsLNuMaYcBKSkLTZVfmK594= @@ -910,15 +722,7 @@ github.com/seaweedfs/goexif v1.0.2 h1:p+rTXYdQ2mgxd+1JaTrQ9N8DvYuw9UH9xgYmJ+Bb29 github.com/seaweedfs/goexif v1.0.2/go.mod h1:MrKs5LK0HXdffrdCZrW3OIMegL2xXpC6ThLyXMyjdrk= github.com/secsy/goftp v0.0.0-20190720192957-f31499d7c79a h1:C6IhVTxNkhlb0tlCB6JfHOUv1f0xHPK7V8X4HlJZEJw= github.com/secsy/goftp v0.0.0-20190720192957-f31499d7c79a/go.mod h1:MnkX001NG75g3p8bhFycnyIjeQoOjGL6CEIsdE/nKSY= -github.com/sergi/go-diff v1.0.1-0.20180205163309-da645544ed44/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= -github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= -github.com/shirou/gopsutil v2.19.10+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= -github.com/shirou/gopsutil v3.21.2+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= -github.com/shirou/w32 v0.0.0-20160930032740-bb4de0191aa4/go.mod h1:qsXQc7+bwAM3Q1u/4XEfrquwF8Lw7D7y5cD8CuHnfIc= -github.com/shurcooL/httpfs v0.0.0-20190707220628-8d4bc4ba7749/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg= -github.com/shurcooL/httpgzip v0.0.0-20190720172056-320755c1c1b0/go.mod h1:919LwcH0M7/W4fcZ0/jy0qGght1GIhqyS/EgWGH2j5Q= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= -github.com/shurcooL/vfsgen v0.0.0-20181202132449-6a9ea43bcacd/go.mod h1:TrYk7fJVaAttu97ZZKrO9UbRa8izdowaMIZcxYMbVaw= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= @@ -931,7 +735,6 @@ github.com/smartystreets/assertions v1.1.1/go.mod h1:tcbTF8ujkAEcZ8TElKY+i30BzYl github.com/smartystreets/go-aws-auth v0.0.0-20180515143844-0c1422d1fdb9/go.mod h1:SnhjPscd9TpLiy1LpzGSKh3bXCfxxXuqd9xmQJy3slM= github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/smartystreets/gunit v1.3.4/go.mod h1:ZjM1ozSIMJlAz/ay4SG8PeKF00ckUp+zMHZXV9/bvak= -github.com/soheilhy/cmux v0.1.4 h1:0HKaf1o97UwFjHH9o5XsHUOF+tqmdA7KEzXLpiyaw0E= github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= github.com/sony/gobreaker v0.4.1/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= @@ -944,7 +747,6 @@ github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= -github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk= github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= @@ -967,63 +769,29 @@ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXf github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= -github.com/stretchr/testify v1.6.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stvp/tempredis v0.0.0-20181119212430-b82af8480203 h1:QVqDTf3h2WHt08YuiTGPZLls0Wq99X9bWd0Q5ZSBesM= github.com/stvp/tempredis v0.0.0-20181119212430-b82af8480203/go.mod h1:oqN97ltKNihBbwlX8dLpwxCl3+HnXKV/R0e+sRLd9C8= -github.com/swaggo/files v0.0.0-20190704085106-630677cd5c14/go.mod h1:gxQT6pBGRuIGunNf/+tSOB5OHvguWi8Tbt82WOkf35E= -github.com/swaggo/gin-swagger v1.2.0/go.mod h1:qlH2+W7zXGZkczuL+r2nEBR2JTT+/lX05Nn6vPhc7OI= -github.com/swaggo/http-swagger v0.0.0-20200308142732-58ac5e232fba/go.mod h1:O1lAbCgAAX/KZ80LM/OXwtWFI/5TvZlwxSg8Cq08PV0= -github.com/swaggo/swag v1.5.1/go.mod h1:1Bl9F/ZBpVWh22nY0zmYyASPO1lI/zIwRDrpZU+tv8Y= -github.com/swaggo/swag v1.6.3/go.mod h1:wcc83tB4Mb2aNiL/HP4MFeQdpHUrca+Rp/DRNgWAUio= -github.com/swaggo/swag v1.6.6-0.20200529100950-7c765ddd0476/go.mod h1:xDhTyuFIujYiN3DKWC/H/83xcfHp+UE/IzWWampG7Zc= github.com/syndtr/goleveldb v1.0.1-0.20190318030020-c3a204f8e965 h1:1oFLiOyVl+W7bnBzGhf7BbIv9loSFQcieWWYIjLqcAw= github.com/syndtr/goleveldb v1.0.1-0.20190318030020-c3a204f8e965/go.mod h1:9OrXJhf154huy1nPWmuSrkgjPUtUNhA+Zmy+6AESzuA= -github.com/tecbot/gorocksdb v0.0.0-20191217155057-f0fad39f321c h1:g+WoO5jjkqGAzHWCjJB1zZfXPIAaDpzXIEJ0eS6B5Ok= -github.com/tecbot/gorocksdb v0.0.0-20191217155057-f0fad39f321c/go.mod h1:ahpPrc7HpcfEWDQRZEmnXMzHY03mLDYMCxeDzy46i+8= -github.com/thoas/go-funk v0.7.0/go.mod h1:+IWnUfUmFO1+WVYQWQtIJHeRRdaIyyYglZN7xzUPe4Q= -github.com/tidwall/gjson v1.3.5/go.mod h1:P256ACg0Mn+j1RXIDXoss50DeIABTYK1PULOJHhxOls= -github.com/tidwall/gjson v1.6.6 h1:Gh0D/kZV+L9rcuE2hE8Hn2dTYe2L6j6SKwcPlKpXAcs= -github.com/tidwall/gjson v1.6.6/go.mod h1:zeFuBCIqD4sN/gmqBzZ4j7Jd6UcA2Fc56x7QFsv+8fI= github.com/tidwall/gjson v1.8.1 h1:8j5EE9Hrh3l9Od1OIEDAb7IpezNA20UdRngNAj5N0WU= github.com/tidwall/gjson v1.8.1/go.mod h1:5/xDoumyyDNerp2U36lyolv46b3uF/9Bu6OfyQ9GImk= -github.com/tidwall/match v1.0.1/go.mod h1:LujAq0jyVjBy028G1WhWfIzbpQfMO8bBZ6Tyb0+pL9E= github.com/tidwall/match v1.0.3 h1:FQUVvBImDutD8wJLN6c5eMzWtjgONK9MwIBCOrUJKeE= github.com/tidwall/match v1.0.3/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= -github.com/tidwall/pretty v1.0.2/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= github.com/tidwall/pretty v1.1.0 h1:K3hMW5epkdAVwibsQEfR/7Zj0Qgt4DxtNumTq/VloO8= github.com/tidwall/pretty v1.1.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= -github.com/tikv/client-go/v2 v2.0.0-alpha.0.20210824090536-16d902a3c7e5 h1:7CJYiW8gKiI3IQOQSAZyqZq0GxB+bmrnZgk9QNZ1cPo= -github.com/tikv/client-go/v2 v2.0.0-alpha.0.20210824090536-16d902a3c7e5/go.mod h1:KwtZXt0JD+bP9bWW2ka0ir3Wp3oTEfZUTh22bs2sI4o= -github.com/tikv/pd v1.1.0-beta.0.20210323121136-78679e5e209d h1:K0XnvsnT6ofLDuM8Rt3PuFQO4p8bNraeHYstspD316g= -github.com/tikv/pd v1.1.0-beta.0.20210323121136-78679e5e209d/go.mod h1:Jw9KG11C/23Rr7DW4XWQ7H5xOgGZo6DFL1OKAF4+Igw= -github.com/tklauser/go-sysconf v0.3.4/go.mod h1:Cl2c8ZRWfHD5IrfHo9VN+FX9kCFjIOyVklgXycLB6ek= -github.com/tklauser/numcpus v0.2.1/go.mod h1:9aU+wOc6WjUIZEwWMP62PL/41d65P+iks1gBkr4QyP8= github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= -github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5 h1:LnC5Kc/wtumK+WB441p7ynQJzVuNRJiqddSIE3IlSEQ= github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/tsuna/gohbase v0.0.0-20201125011725-348991136365 h1:6iRwZdrFUzbcVYZwa8dXTIILGIxmmhjyUPJEcwzPGaU= github.com/tsuna/gohbase v0.0.0-20201125011725-348991136365/go.mod h1:zj0GJHGvyf1ed3Jm/Tb4830c/ZKDq+YoLsCt2rGQuT0= -github.com/twmb/murmur3 v1.1.3 h1:D83U0XYKcHRYwYIpBKf3Pks91Z0Byda/9SJ8B6EMRcA= -github.com/twmb/murmur3 v1.1.3/go.mod h1:Qq/R7NUyOfr65zD+6Q5IHKsJLwP7exErjN6lyyq3OSQ= github.com/tylertreat/BoomFilters v0.0.0-20210315201527-1a82519a3e43 h1:QEePdg0ty2r0t1+qwfZmQ4OOl/MB2UXIeJSpIZv56lg= github.com/tylertreat/BoomFilters v0.0.0-20210315201527-1a82519a3e43/go.mod h1:OYRfF6eb5wY9VRFkXJH8FFBi3plw2v+giaIu7P054pM= github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= -github.com/ugorji/go v1.1.5-pre/go.mod h1:FwP/aQVg39TXzItUBMwnWp9T9gPQnXw4Poh4/oBQZ/0= -github.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo= -github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= -github.com/ugorji/go/codec v0.0.0-20181022190402-e5e69e061d4f/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= -github.com/ugorji/go/codec v1.1.5-pre/go.mod h1:tULtS6Gy1AE1yCENaw4Vb//HLH5njI2tfCQDUqRd8fI= -github.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs= -github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= -github.com/unrolled/render v1.0.1/go.mod h1:gN9T0NhL4Bfbwu8ann7Ry/TGHYfosul+J0obPf6NBdM= github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= -github.com/urfave/cli/v2 v2.1.1/go.mod h1:SE9GqnLQmjVa0iPEY0f1w3ygNIYcIJ0OKPMoW2caLfQ= -github.com/urfave/negroni v0.3.0/go.mod h1:Meg73S6kFm/4PpbYdq35yYWoCZ9mS/YSx+lKnmiohz4= github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= github.com/viant/assertly v0.5.4 h1:5Hh4U3pLZa6uhCFAGpYOxck/8l9TZczEzoHNfJAhHEQ= @@ -1032,9 +800,6 @@ github.com/viant/ptrie v0.3.0 h1:SDaRd7Gqr1+ItCNz0GpTxRdK21nOfqjV6YtBm9jGlMY= github.com/viant/ptrie v0.3.0/go.mod h1:VguMnbGfz95Zw+V5VarYSqtqslDxJbOv++xLzxkMhec= github.com/viant/toolbox v0.33.2 h1:Av844IIeGz81gT672qZemyptGfbrcxqGymA5RFnIPjE= github.com/viant/toolbox v0.33.2/go.mod h1:OxMCG57V0PXuIP2HNQrtJf2CjqdmbrOx5EkMILuUhzM= -github.com/vmihailenco/msgpack/v4 v4.3.11/go.mod h1:gborTTJjAo/GWTqqRjrLCn9pgNN+NXzzngzBKDPIqw4= -github.com/vmihailenco/msgpack/v5 v5.0.0-beta.1/go.mod h1:xlngVLeyQ/Qi05oQxhQ+oTuqa03RjMwMfk/7/TCs+QI= -github.com/vmihailenco/tagparser v0.1.1/go.mod h1:OeAg3pn3UbLjkWt+rN9oFYB6u/cQgqMEUPoW2WPyhdI= github.com/wsxiaoys/terminal v0.0.0-20160513160801-0940f3fc43a0 h1:3UeQBvD0TFrlVjOeLOBz+CPAI8dnbqNSVwUwRrkp7vQ= github.com/wsxiaoys/terminal v0.0.0-20160513160801-0940f3fc43a0/go.mod h1:IXCdmsXIht47RaVFLEdVnh1t+pgYtTAhQGj73kz+2DM= github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c= @@ -1045,10 +810,8 @@ github.com/xdg-go/stringprep v1.0.2 h1:6iq84/ryjjeRmMJwxutI51F2GIPlP5BfTvXHeYjyh github.com/xdg-go/stringprep v1.0.2/go.mod h1:8F9zXuvzgwmyT5DUm4GUfZGDdT3W+LCvS6+da4O5kxM= github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c/go.mod h1:lB8K/P019DLNhemzwFU4jHLhdvlE6uDZjXFejJXr49I= github.com/xdg/stringprep v1.0.0/go.mod h1:Jhud4/sHMO4oL310DaZAKk9ZaJ08SJfe+sJh0HrGL1Y= -github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 h1:eY9dn8+vbi4tKz5Qo6v2eYzo7kUS51QINcR5jNpbZS8= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= -github.com/yookoala/realpath v1.0.0/go.mod h1:gJJMA9wuX7AcqLy1+ffPatSCySA1FQ2S8Ya9AIoYBpE= github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d h1:splanxYIlg+5LfHAM6xpdFEAYOk8iySO56hMFq6uLyA= github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= @@ -1058,15 +821,8 @@ github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9dec github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= -go.etcd.io/bbolt v1.3.4 h1:hi1bXHMVrlQh6WwxAy+qZCV/SYIlqo+Ushwdpa4tAKg= -go.etcd.io/bbolt v1.3.4/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ= +go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738 h1:VcrIfasaLFkyjk6KNlXQSzO+B0fZcnECiDrKJsfxka0= go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg= -go.etcd.io/etcd v0.5.0-alpha.5.0.20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg= -go.etcd.io/etcd v0.5.0-alpha.5.0.20200425165423-262c93980547 h1:s71VGheLtWmCYsnNjf+s7XE8HsrZnd3EYGrLGWVm7nY= -go.etcd.io/etcd v0.5.0-alpha.5.0.20200425165423-262c93980547/go.mod h1:YoUyTScD3Vcv2RBm3eGVOq7i1ULiz3OuXoQFWOirmAM= -go.etcd.io/etcd v0.5.0-alpha.5.0.20200824191128-ae9734ed278b/go.mod h1:yVHk9ub3CSBatqGNg7GRmsnfLWtoW60w4eDYfh7vHDg= -go.etcd.io/etcd v3.3.25+incompatible h1:V1RzkZJj9LqsJRy+TUBgpWSbZXITLB819lstuTFoZOY= -go.etcd.io/etcd v3.3.25+incompatible/go.mod h1:yaeTdrJi5lOmYerz05bd8+V7KubZs8YSFZfzsF9A6aI= go.etcd.io/etcd/api/v3 v3.5.0 h1:GsV3S+OfZEOCNXdtNkBSR7kgLobAa/SO6tCxRa0GAYw= go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs= go.etcd.io/etcd/client/pkg/v3 v3.5.0 h1:2aQv6F436YnN7I4VbI8PPYrBhu+SmrTaADcf8Mi/6PU= @@ -1082,7 +838,6 @@ go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.4 h1:LYy1Hy3MJdrCdMwwzxA/dRok4ejH+RwNGbuoD9fCjto= go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= go.opencensus.io v0.23.0 h1:gqCw0LfLxScz8irSi8exQc7fyQ0fKQU/qnC/X8+V/1M= @@ -1094,31 +849,16 @@ go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqe go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= -go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= -go.uber.org/dig v1.8.0/go.mod h1:X34SnWGr8Fyla9zQNO2GSO2D+TIuqB14OS8JhYocIyw= -go.uber.org/fx v1.10.0/go.mod h1:vLRicqpG/qQEzno4SYU86iCwfT95EZza+Eba0ItuxqY= -go.uber.org/goleak v0.10.0/go.mod h1:VCZuO8V8mFPlL0F5J5GK1rtHV3DrFcQ1R8ryq7FK0aI= -go.uber.org/goleak v1.1.10 h1:z+mqJhf6ss6BSfSM671tgKyZBFPTTJM+HLxnhPC3wu0= -go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= -go.uber.org/multierr v1.4.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= -go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU= go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= go.uber.org/multierr v1.7.0 h1:zaiO/rmgFjbmCXdSYJWQcdvOCsthmdaHfr3Gm2Kx4Ec= go.uber.org/multierr v1.7.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak= go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= -go.uber.org/zap v1.8.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= -go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= -go.uber.org/zap v1.12.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= -go.uber.org/zap v1.14.1/go.mod h1:Mb2vm2krFEG5DV0W9qcHBYFtp/Wku1cvYaqPsS/WYfc= -go.uber.org/zap v1.15.0/go.mod h1:Mb2vm2krFEG5DV0W9qcHBYFtp/Wku1cvYaqPsS/WYfc= -go.uber.org/zap v1.16.0 h1:uFRZXykJGK9lLY4HtgSw44DnIcAM+kRBP7x5m+NpAOM= -go.uber.org/zap v1.16.0/go.mod h1:MA8QOfq0BHJwdXa996Y4dYkAqRKB8/1K1QMMZVaNZjQ= go.uber.org/zap v1.17.0 h1:MTjgFu6ZLKvY6Pvaqk97GlxNBuMpV4Hy/3P6tRGlI2U= go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo= gocloud.dev v0.20.0 h1:mbEKMfnyPV7W1Rj35R1xXfjszs9dXkwSOq2KoFr25g8= @@ -1131,28 +871,20 @@ golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnf golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190404164418-38d8ce5564a5/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= golang.org/x/crypto v0.0.0-20190422162423-af44ce270edf/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190530122614-20be4c3c3ed5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY= -golang.org/x/crypto v0.0.0-20191002192127-34f69633bfdc/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20191205180655-e7c4368fe9dd/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200117160349-530e935923ad/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20200204104054-c9f3fb736b72/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200604202706-70a84ac30bf9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0 h1:hb9wdF1z5waM+dSIICn1l0DkLVDT3hqhhQsDNUmHPRE= golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201216223049-8b5274cf687f h1:aZp0e2vLN4MToVqnjNEYEtrEA8RH8U8FN1CU7JgqsPU= golang.org/x/crypto v0.0.0-20201216223049-8b5274cf687f/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= @@ -1182,7 +914,6 @@ golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRu golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/lint v0.0.0-20210508222113-6edffad5e616 h1:VLliZ0d+/avPrXXH+OakdXhpJuEoBZuwh1m2j7U6Iug= golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= @@ -1199,7 +930,6 @@ golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181005035420-146acd28ed58/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -1213,16 +943,13 @@ golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= -golang.org/x/net v0.0.0-20190611141213-3f473d35a33a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190619014844-b5b0513f8c1b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20191002035440-2ec189313ef0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191112182307-2180aed22343/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -1257,7 +984,6 @@ golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4Iltr golang.org/x/oauth2 v0.0.0-20190402181905-9f3314589c9a/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d h1:TzXSXBo42m9gQenoE3b9BGiEpg5IG2JkU5FkPIawgtw= golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= @@ -1291,9 +1017,7 @@ golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181228144115-9a3f9b0469bb/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -1303,15 +1027,10 @@ golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190531175056-4c3a928424d2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190610200419-93c9922d18ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191010194322-b09406accb47/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -1350,7 +1069,6 @@ golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210217105451-b926d437f341/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -1365,10 +1083,10 @@ golang.org/x/sys v0.0.0-20210603125802-9665404d3644/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210817142637-7d9622a276b7 h1:lQ8Btl/sJr2+f4ql7ffKUKfnV0BsgsICvm0oEeINAQY= golang.org/x/sys v0.0.0-20210817142637-7d9622a276b7/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf h1:2ucpDCmfkl8Bd/FsLtiD653Wf96cW37s+iGx93zsu4k= golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365 h1:6wSTsvPddg9gc/mVEEyk9oOAoxn+bT4Z9q1zx+4RwA4= +golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -1383,12 +1101,10 @@ golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20191024005414-555d28b269f0 h1:/5xXl8Y5W96D+TtHSlonuFqGHIWVuyCkGJLwGh9JJFs= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= @@ -1403,23 +1119,15 @@ golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBn golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190531172133-b3315ee88b7d/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190606050223-4d9ae51c2468/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190611222205-d73e1c7e250b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20190907020128-2ca718005c18/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191030062658-86caa796c7ab/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191107010934-f79515f33823/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191114200427-caa0b0f7d508/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= @@ -1434,7 +1142,6 @@ golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapK golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200225230052-807dcd883420/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= @@ -1444,7 +1151,6 @@ golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWc golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200527183253-8e7acdbce89d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200601175630-2caf76543d99/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200606014950-c42cb6316fb6/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200608174601-1b747fd94509/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= @@ -1464,8 +1170,8 @@ golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.4 h1:cVngSRcfgyZCzys3KYOpCFa+4dqX/Oub9tAq00ttGVs= golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.5 h1:ouewzE6p+/VEB31YYnTbEJdi8pFqKp4P4n85vwo3DHA= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -1487,7 +1193,6 @@ google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/ google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= -google.golang.org/api v0.26.0 h1:VJZ8h6E8ip82FRpQl848c5vAadxlTXrUh8RzQzSRm08= google.golang.org/api v0.26.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= @@ -1503,20 +1208,18 @@ google.golang.org/api v0.50.0/go.mod h1:4bNT5pAuq5ji4SRZm+5QIkjny9JAyVD/3gaSihNe google.golang.org/api v0.51.0/go.mod h1:t4HdrdoNgyN5cbEfm7Lum0lcLDLiise1F8qDKX00sOU= google.golang.org/api v0.54.0/go.mod h1:7C4bFFOvVDGXjfDTAsgGwDgAxRDeQ4X8NvUedIt6z3k= google.golang.org/api v0.55.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE= -google.golang.org/api v0.56.0 h1:08F9XVYTLOGeSQb3xI9C0gXMuQanhdGed0cWFhDozbI= -google.golang.org/api v0.56.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE= +google.golang.org/api v0.57.0 h1:4t9zuDlHLcIx0ZEhmXEeFVCRsiOgpgn2QOH9N0MNjPI= +google.golang.org/api v0.57.0/go.mod h1:dVPlbZyBo2/OjBpmvNdpn2GRm6rPy75jyU7bmhdrMgI= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/appengine v1.6.6 h1:lMO5rYAqUxkmaj76jAkRUvt5JZgFymx/+Q5Mzfivuhc= google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/genproto v0.0.0-20181004005441-af9cb2a35e7f/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= @@ -1526,7 +1229,6 @@ google.golang.org/genproto v0.0.0-20190530194941-fb225487d101/go.mod h1:z3L6/3dT google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= -google.golang.org/genproto v0.0.0-20190927181202-20e1ac93f88c/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= @@ -1548,7 +1250,6 @@ google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfG google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= google.golang.org/genproto v0.0.0-20200603110839-e855014d5736/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= -google.golang.org/genproto v0.0.0-20200608115520-7c474a2e3482 h1:i+Aiej6cta/Frzp13/swvwz5O00kYcSe0A/C5Wd7zX8= google.golang.org/genproto v0.0.0-20200608115520-7c474a2e3482/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= @@ -1578,9 +1279,9 @@ google.golang.org/genproto v0.0.0-20210821163610-241b8fcbd6c8/go.mod h1:eFjDcFEc google.golang.org/genproto v0.0.0-20210825212027-de86158e7fda/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= google.golang.org/genproto v0.0.0-20210828152312-66f60bf46e71/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= google.golang.org/genproto v0.0.0-20210831024726-fe130286e0e2/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20210903162649-d08c68adba83 h1:3V2dxSZpz4zozWWUq36vUxXEKnSYitEH2LdsAx+RUmg= google.golang.org/genproto v0.0.0-20210903162649-d08c68adba83/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/grpc v0.0.0-20180607172857-7a6a684ca69e/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= +google.golang.org/genproto v0.0.0-20210921142501-181ce0d877f6 h1:2ncG/LajxmrclaZH+ppVi02rQxz4eXYJzGHdFN4Y9UA= +google.golang.org/genproto v0.0.0-20210921142501-181ce0d877f6/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.0/go.mod h1:chYK+tFQF0nDUGJgXMSgLCQk3phJEuONr2DCgLDdAQM= @@ -1590,13 +1291,11 @@ google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ij google.golang.org/grpc v1.22.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= -google.golang.org/grpc v1.24.0/go.mod h1:XDChyiUovWa60DnaeDeZmSW86xtLtjtZbwvSiRnRtcA= google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= -google.golang.org/grpc v1.29.1 h1:EC2SB8S04d2r73uptxphDSUG+kTKVgjRPF+N3xpxRB4= google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= @@ -1625,14 +1324,11 @@ google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2 google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= -google.golang.org/protobuf v1.26.0-rc.1 h1:7QnIQpGRHE5RnLKnESfDoxm2dTapTZua5a0kS0A+VXQ= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ= google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -gopkg.in/alecthomas/gometalinter.v2 v2.0.12/go.mod h1:NDRytsqEZyolNuAgTzJkZMkSQM7FIKyzVzGhjB/qfYo= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= -gopkg.in/alecthomas/kingpin.v3-unstable v3.0.0-20180810215634-df19058c872c/go.mod h1:3HH7i1SgMqlzxCcBmUHW657sD4Kvv9sC3HpL3YukzwA= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= @@ -1644,9 +1340,6 @@ gopkg.in/dutchcoders/goftp.v1 v1.0.0-20170301105846-ed59a591ce14/go.mod h1:nzmlZ gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o= -gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE= -gopkg.in/go-playground/validator.v8 v8.18.2/go.mod h1:RX2a/7Ha8BgOhfk7j780h4/u/RRjR0eouCJSH80/M2Y= -gopkg.in/go-playground/validator.v9 v9.29.1/go.mod h1:+c9/zcJMFNgbLvly1L1V+PpxWdVbfP1avr/N00E2vyQ= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/jcmturner/aescts.v1 v1.0.1 h1:cVVZBK2b1zY26haWB4vbBiZrfFQnfbTVrE3xZq6hrEw= @@ -1660,8 +1353,6 @@ gopkg.in/jcmturner/gokrb5.v7 v7.3.0 h1:0709Jtq/6QXEuWRfAm260XqlpcwL1vxtO1tUE2qK8 gopkg.in/jcmturner/gokrb5.v7 v7.3.0/go.mod h1:l8VISx+WGYp+Fp7KRbsiUuXTTOnxIc3Tuvyavf11/WM= gopkg.in/jcmturner/rpc.v1 v1.1.0 h1:QHIUxTX1ISuAv9dD2wJ9HWQVuWDX/Zc0PfeC2tjc4rU= gopkg.in/jcmturner/rpc.v1 v1.1.0/go.mod h1:YIdkC4XfD6GXbzje11McwsDuOlZQSb9W4vfLvuNnlv8= -gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8= -gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= @@ -1672,7 +1363,6 @@ gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= @@ -1688,9 +1378,6 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -honnef.co/go/tools v0.2.0 h1:ws8AfbgTX3oIczLPNPCu5166oBg9ST2vNs0rcht+mDE= -honnef.co/go/tools v0.2.0/go.mod h1:lPVVZ2BS5TfnjLyizF7o7hv7j9/L+8cZY2hLyjP9cGY= -k8s.io/klog v1.0.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I= modernc.org/b v1.0.0 h1:vpvqeyp17ddcQWF29Czawql4lDdABCDRbXRAS4+aF2o= modernc.org/b v1.0.0/go.mod h1:uZWcZfRj1BpYzfN9JTerzlNUnnPsV9O2ZA8JsRcubNg= modernc.org/cc/v3 v3.32.4/go.mod h1:0R6jl1aZlIl2avnYfbfHBS1QB6/f+16mihBObaBC878= @@ -1725,8 +1412,6 @@ modernc.org/z v1.0.1/go.mod h1:8/SRk5C/HgiQWCgXdfpb+1RvhORdkz5sw72d3jjtyqA= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= -sigs.k8s.io/yaml v1.1.0 h1:4A07+ZFc2wgJwo8YNlQpr1rVlgUDlxXHhPJciaPY5gs= sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= -sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q= sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= sourcegraph.com/sourcegraph/appdash v0.0.0-20190731080439-ebfcffb1b5c0/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU= diff --git a/weed/command/imports.go b/weed/command/imports.go index 48cda5f90..3792c45c4 100644 --- a/weed/command/imports.go +++ b/weed/command/imports.go @@ -31,5 +31,4 @@ import ( _ "github.com/chrislusf/seaweedfs/weed/filer/redis2" _ "github.com/chrislusf/seaweedfs/weed/filer/redis3" _ "github.com/chrislusf/seaweedfs/weed/filer/sqlite" - _ "github.com/chrislusf/seaweedfs/weed/filer/tikv" ) diff --git a/weed/command/scaffold/filer.toml b/weed/command/scaffold/filer.toml index aeb8a5b67..aec409408 100644 --- a/weed/command/scaffold/filer.toml +++ b/weed/command/scaffold/filer.toml @@ -252,11 +252,3 @@ location = "/tmp/" address = "localhost:6379" password = "" database = 1 - -[tikv] -enabled = false -# If you have many pd address, use ',' split then: -# pdaddrs = "pdhost1:2379, pdhost2:2379, pdhost3:2379" -pdaddrs = "localhost:2379" -# Concurrency for TiKV delete range -deleterange_concurrency = 1 diff --git a/weed/filer/tikv/tikv.go b/weed/filer/tikv/tikv.go deleted file mode 100644 index 8bb5dc577..000000000 --- a/weed/filer/tikv/tikv.go +++ /dev/null @@ -1,5 +0,0 @@ -package tikv - -/* - * This empty file is let go build can work without tikv tag - */ diff --git a/weed/filer/tikv/tikv_store.go b/weed/filer/tikv/tikv_store.go deleted file mode 100644 index 561f23910..000000000 --- a/weed/filer/tikv/tikv_store.go +++ /dev/null @@ -1,389 +0,0 @@ -//go:build tikv -// +build tikv - -package tikv - -import ( - "bytes" - "context" - "crypto/sha1" - "fmt" - "io" - "strings" - - "github.com/chrislusf/seaweedfs/weed/filer" - "github.com/chrislusf/seaweedfs/weed/glog" - "github.com/chrislusf/seaweedfs/weed/pb/filer_pb" - "github.com/chrislusf/seaweedfs/weed/util" - "github.com/tikv/client-go/v2/tikv" - "github.com/tikv/client-go/v2/txnkv" -) - -var ( - _ filer.FilerStore = ((*TikvStore)(nil)) -) - -func init() { - filer.Stores = append(filer.Stores, &TikvStore{}) -} - -type TikvStore struct { - client *tikv.KVStore - deleteRangeConcurrency int -} - -// Basic APIs -func (store *TikvStore) GetName() string { - return "tikv" -} - -func (store *TikvStore) Initialize(config util.Configuration, prefix string) error { - pdAddrs := []string{} - pdAddrsStr := config.GetString(prefix + "pdaddrs") - for _, item := range strings.Split(pdAddrsStr, ",") { - pdAddrs = append(pdAddrs, strings.TrimSpace(item)) - } - drc := config.GetInt(prefix + "deleterange_concurrency") - if drc <= 0 { - drc = 1 - } - store.deleteRangeConcurrency = drc - return store.initialize(pdAddrs) -} - -func (store *TikvStore) initialize(pdAddrs []string) error { - client, err := tikv.NewTxnClient(pdAddrs) - store.client = client - return err -} - -func (store *TikvStore) Shutdown() { - err := store.client.Close() - if err != nil { - glog.V(0).Infof("Shutdown TiKV client got error: %v", err) - } -} - -// ~ Basic APIs - -// Entry APIs -func (store *TikvStore) InsertEntry(ctx context.Context, entry *filer.Entry) error { - dir, name := entry.DirAndName() - key := generateKey(dir, name) - - value, err := entry.EncodeAttributesAndChunks() - if err != nil { - return fmt.Errorf("encoding %s %+v: %v", entry.FullPath, entry.Attr, err) - } - txn, err := store.getTxn(ctx) - if err != nil { - return err - } - err = txn.RunInTxn(func(txn *txnkv.KVTxn) error { - return txn.Set(key, value) - }) - if err != nil { - return fmt.Errorf("persisting %s : %v", entry.FullPath, err) - } - return nil -} - -func (store *TikvStore) UpdateEntry(ctx context.Context, entry *filer.Entry) error { - return store.InsertEntry(ctx, entry) -} - -func (store *TikvStore) FindEntry(ctx context.Context, path util.FullPath) (*filer.Entry, error) { - dir, name := path.DirAndName() - key := generateKey(dir, name) - - txn, err := store.getTxn(ctx) - if err != nil { - return nil, err - } - var value []byte = nil - err = txn.RunInTxn(func(txn *txnkv.KVTxn) error { - val, err := txn.Get(context.TODO(), key) - if err == nil { - value = val - } - return err - }) - - if isNotExists(err) || value == nil { - return nil, filer_pb.ErrNotFound - } - - if err != nil { - return nil, fmt.Errorf("get %s : %v", path, err) - } - - entry := &filer.Entry{ - FullPath: path, - } - err = entry.DecodeAttributesAndChunks(value) - if err != nil { - return entry, fmt.Errorf("decode %s : %v", entry.FullPath, err) - } - return entry, nil -} - -func (store *TikvStore) DeleteEntry(ctx context.Context, path util.FullPath) error { - dir, name := path.DirAndName() - key := generateKey(dir, name) - - txn, err := store.getTxn(ctx) - if err != nil { - return err - } - - err = txn.RunInTxn(func(txn *txnkv.KVTxn) error { - return txn.Delete(key) - }) - if err != nil { - return fmt.Errorf("delete %s : %v", path, err) - } - return nil -} - -// ~ Entry APIs - -// Directory APIs -func (store *TikvStore) DeleteFolderChildren(ctx context.Context, path util.FullPath) error { - directoryPrefix := genDirectoryKeyPrefix(path, "") - - txn, err := store.getTxn(ctx) - if err != nil { - return err - } - var ( - startKey []byte = nil - endKey []byte = nil - ) - err = txn.RunInTxn(func(txn *txnkv.KVTxn) error { - iter, err := txn.Iter(directoryPrefix, nil) - if err != nil { - return err - } - defer iter.Close() - for iter.Valid() { - key := iter.Key() - endKey = key - if !bytes.HasPrefix(key, directoryPrefix) { - break - } - if startKey == nil { - startKey = key - } - - err = iter.Next() - if err != nil { - return err - } - } - // Only one Key matched just delete it. - if startKey != nil && bytes.Equal(startKey, endKey) { - return txn.Delete(startKey) - } - return nil - }) - if err != nil { - return fmt.Errorf("delete %s : %v", path, err) - } - - if startKey != nil && endKey != nil && !bytes.Equal(startKey, endKey) { - // has startKey and endKey and they are not equals, so use delete range - _, err = store.client.DeleteRange(context.Background(), startKey, endKey, store.deleteRangeConcurrency) - if err != nil { - return fmt.Errorf("delete %s : %v", path, err) - } - } - return err -} - -func (store *TikvStore) ListDirectoryEntries(ctx context.Context, dirPath util.FullPath, startFileName string, includeStartFile bool, limit int64, eachEntryFunc filer.ListEachEntryFunc) (string, error) { - return store.ListDirectoryPrefixedEntries(ctx, dirPath, startFileName, includeStartFile, limit, "", eachEntryFunc) -} - -func (store *TikvStore) ListDirectoryPrefixedEntries(ctx context.Context, dirPath util.FullPath, startFileName string, includeStartFile bool, limit int64, prefix string, eachEntryFunc filer.ListEachEntryFunc) (string, error) { - lastFileName := "" - directoryPrefix := genDirectoryKeyPrefix(dirPath, prefix) - lastFileStart := directoryPrefix - if startFileName != "" { - lastFileStart = genDirectoryKeyPrefix(dirPath, startFileName) - } - - txn, err := store.getTxn(ctx) - if err != nil { - return lastFileName, err - } - err = txn.RunInTxn(func(txn *txnkv.KVTxn) error { - iter, err := txn.Iter(lastFileStart, nil) - if err != nil { - return err - } - defer iter.Close() - i := int64(0) - first := true - for iter.Valid() { - if first { - first = false - if !includeStartFile { - if iter.Valid() { - // Check first item is lastFileStart - if bytes.Equal(iter.Key(), lastFileStart) { - // Is lastFileStart and not include start file, just - // ignore it. - err = iter.Next() - if err != nil { - return err - } - continue - } - } - } - } - // Check for limitation - if limit > 0 { - i++ - if i > limit { - break - } - } - // Validate key prefix - key := iter.Key() - if !bytes.HasPrefix(key, directoryPrefix) { - break - } - value := iter.Value() - - // Start process - fileName := getNameFromKey(key) - if fileName != "" { - // Got file name, then generate the Entry - entry := &filer.Entry{ - FullPath: util.NewFullPath(string(dirPath), fileName), - } - // Update lastFileName - lastFileName = fileName - // Check for decode value. - if decodeErr := entry.DecodeAttributesAndChunks(value); decodeErr != nil { - // Got error just return the error - glog.V(0).Infof("list %s : %v", entry.FullPath, err) - return err - } - // Run for each callback if return false just break the iteration - if !eachEntryFunc(entry) { - break - } - } - // End process - - err = iter.Next() - if err != nil { - return err - } - } - return nil - }) - if err != nil { - return lastFileName, fmt.Errorf("prefix list %s : %v", dirPath, err) - } - return lastFileName, nil -} - -// ~ Directory APIs - -// Transaction Related APIs -func (store *TikvStore) BeginTransaction(ctx context.Context) (context.Context, error) { - tx, err := store.client.Begin() - if err != nil { - return ctx, err - } - return context.WithValue(ctx, "tx", tx), nil -} - -func (store *TikvStore) CommitTransaction(ctx context.Context) error { - if tx, ok := ctx.Value("tx").(*txnkv.KVTxn); ok { - return tx.Commit(context.Background()) - } - return nil -} - -func (store *TikvStore) RollbackTransaction(ctx context.Context) error { - if tx, ok := ctx.Value("tx").(*txnkv.KVTxn); ok { - return tx.Rollback() - } - return nil -} - -// ~ Transaction Related APIs - -// Transaction Wrapper -type TxnWrapper struct { - *txnkv.KVTxn - inContext bool -} - -func (w *TxnWrapper) RunInTxn(f func(txn *txnkv.KVTxn) error) error { - err := f(w.KVTxn) - if !w.inContext { - if err != nil { - w.KVTxn.Rollback() - return err - } - w.KVTxn.Commit(context.Background()) - return nil - } - return err -} - -func (store *TikvStore) getTxn(ctx context.Context) (*TxnWrapper, error) { - if tx, ok := ctx.Value("tx").(*txnkv.KVTxn); ok { - return &TxnWrapper{tx, true}, nil - } - txn, err := store.client.Begin() - if err != nil { - return nil, err - } - return &TxnWrapper{txn, false}, nil -} - -// ~ Transaction Wrapper - -// Encoding Functions -func hashToBytes(dir string) []byte { - h := sha1.New() - io.WriteString(h, dir) - b := h.Sum(nil) - return b -} - -func generateKey(dirPath, fileName string) []byte { - key := hashToBytes(dirPath) - key = append(key, []byte(fileName)...) - return key -} - -func getNameFromKey(key []byte) string { - return string(key[sha1.Size:]) -} - -func genDirectoryKeyPrefix(fullpath util.FullPath, startFileName string) (keyPrefix []byte) { - keyPrefix = hashToBytes(string(fullpath)) - if len(startFileName) > 0 { - keyPrefix = append(keyPrefix, []byte(startFileName)...) - } - return keyPrefix -} - -func isNotExists(err error) bool { - if err == nil { - return false - } - if err.Error() == "not exist" { - return true - } - return false -} - -// ~ Encoding Functions diff --git a/weed/filer/tikv/tikv_store_kv.go b/weed/filer/tikv/tikv_store_kv.go deleted file mode 100644 index 1d9428c69..000000000 --- a/weed/filer/tikv/tikv_store_kv.go +++ /dev/null @@ -1,50 +0,0 @@ -//go:build tikv -// +build tikv - -package tikv - -import ( - "context" - - "github.com/chrislusf/seaweedfs/weed/filer" - "github.com/tikv/client-go/v2/txnkv" -) - -func (store *TikvStore) KvPut(ctx context.Context, key []byte, value []byte) error { - tw, err := store.getTxn(ctx) - if err != nil { - return err - } - return tw.RunInTxn(func(txn *txnkv.KVTxn) error { - return txn.Set(key, value) - }) -} - -func (store *TikvStore) KvGet(ctx context.Context, key []byte) ([]byte, error) { - tw, err := store.getTxn(ctx) - if err != nil { - return nil, err - } - var data []byte = nil - err = tw.RunInTxn(func(txn *txnkv.KVTxn) error { - val, err := txn.Get(context.TODO(), key) - if err == nil { - data = val - } - return err - }) - if isNotExists(err) { - return data, filer.ErrKvNotFound - } - return data, err -} - -func (store *TikvStore) KvDelete(ctx context.Context, key []byte) error { - tw, err := store.getTxn(ctx) - if err != nil { - return err - } - return tw.RunInTxn(func(txn *txnkv.KVTxn) error { - return txn.Delete(key) - }) -} From 8205166009df4d5a1f2eaa623214913b3ce5c429 Mon Sep 17 00:00:00 2001 From: Chris Lu Date: Sun, 10 Oct 2021 21:57:55 -0700 Subject: [PATCH 124/130] disable testing with redis server --- FAIL: TestNameList (0.00s) panic: exec: "redis-server": executable file not found in $PATH [recovered] panic: exec: "redis-server": executable file not found in $PATH goroutine 37 [running]: testing.tRunner.func1.2({0xde2f80, 0xc0003da160}) /opt/hostedtoolcache/go/1.17.1/x64/src/testing/testing.go:1209 +0x24e testing.tRunner.func1() /opt/hostedtoolcache/go/1.17.1/x64/src/testing/testing.go:1212 +0x218 panic({0xde2f80, 0xc0003da160}) /opt/hostedtoolcache/go/1.17.1/x64/src/runtime/panic.go:1038 +0x215 github.com/chrislusf/seaweedfs/weed/filer/redis3.TestNameList(0x407c59) /home/runner/work/seaweedfs/seaweedfs/weed/filer/redis3/kv_directory_children_test.go:45 +0x376 testing.tRunner(0xc0003e2680, 0xf57f48) /opt/hostedtoolcache/go/1.17.1/x64/src/testing/testing.go:1259 +0x102 created by testing.(*T).Run /opt/hostedtoolcache/go/1.17.1/x64/src/testing/testing.go:1306 +0x35a --- weed/filer/redis3/kv_directory_children_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/weed/filer/redis3/kv_directory_children_test.go b/weed/filer/redis3/kv_directory_children_test.go index 78979cb21..b5c3cd391 100644 --- a/weed/filer/redis3/kv_directory_children_test.go +++ b/weed/filer/redis3/kv_directory_children_test.go @@ -39,7 +39,7 @@ var names = []string{ "cassandra.bat", } -func TestNameList(t *testing.T) { +func xTestNameList(t *testing.T) { server, err := tempredis.Start(tempredis.Config{}) if err != nil { panic(err) @@ -76,7 +76,7 @@ func TestNameList(t *testing.T) { } -func BenchmarkNameList(b *testing.B) { +func xBenchmarkNameList(b *testing.B) { server, err := tempredis.Start(tempredis.Config{}) if err != nil { From f4676824a7c72f7d0392f65da5e3123f86c5d479 Mon Sep 17 00:00:00 2001 From: Chris Lu Date: Sun, 10 Oct 2021 22:14:13 -0700 Subject: [PATCH 125/130] fix test code compilation --- weed/filer/redis3/kv_directory_children_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/weed/filer/redis3/kv_directory_children_test.go b/weed/filer/redis3/kv_directory_children_test.go index b5c3cd391..9d7acacf1 100644 --- a/weed/filer/redis3/kv_directory_children_test.go +++ b/weed/filer/redis3/kv_directory_children_test.go @@ -39,7 +39,7 @@ var names = []string{ "cassandra.bat", } -func xTestNameList(t *testing.T) { +func yTestNameList(t *testing.T) { server, err := tempredis.Start(tempredis.Config{}) if err != nil { panic(err) @@ -76,7 +76,7 @@ func xTestNameList(t *testing.T) { } -func xBenchmarkNameList(b *testing.B) { +func yBenchmarkNameList(b *testing.B) { server, err := tempredis.Start(tempredis.Config{}) if err != nil { From 3d586be552e9cab6aee69bd07fa3808238eaa1e3 Mon Sep 17 00:00:00 2001 From: Chris Lu Date: Sun, 10 Oct 2021 22:40:44 -0700 Subject: [PATCH 126/130] 2.71 --- k8s/helm_charts2/Chart.yaml | 4 ++-- weed/util/constants.go | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/k8s/helm_charts2/Chart.yaml b/k8s/helm_charts2/Chart.yaml index 14952e3e8..45ff4bcb3 100644 --- a/k8s/helm_charts2/Chart.yaml +++ b/k8s/helm_charts2/Chart.yaml @@ -1,5 +1,5 @@ apiVersion: v1 description: SeaweedFS name: seaweedfs -appVersion: "2.70" -version: "2.70" +appVersion: "2.71" +version: "2.71" diff --git a/weed/util/constants.go b/weed/util/constants.go index 95c8086e5..b4df3ea9a 100644 --- a/weed/util/constants.go +++ b/weed/util/constants.go @@ -5,7 +5,7 @@ import ( ) var ( - VERSION_NUMBER = fmt.Sprintf("%.02f", 2.70) + VERSION_NUMBER = fmt.Sprintf("%.02f", 2.71) VERSION = sizeLimit + " " + VERSION_NUMBER COMMIT = "" ) From bde2fc65b0839f10f32ea62a6cf000027430a447 Mon Sep 17 00:00:00 2001 From: Chris Lu Date: Mon, 11 Oct 2021 00:35:22 -0700 Subject: [PATCH 127/130] separate into multiple jobs for better execution --- .github/workflows/binaries_release.yml | 135 ++++++++++++++++++++++--- 1 file changed, 123 insertions(+), 12 deletions(-) diff --git a/.github/workflows/binaries_release.yml b/.github/workflows/binaries_release.yml index a093f0a3e..084b34255 100644 --- a/.github/workflows/binaries_release.yml +++ b/.github/workflows/binaries_release.yml @@ -13,21 +13,132 @@ on: # A workflow run is made up of one or more jobs that can run sequentially or in parallel jobs: - build-release-binaries: + build-release-binaries_windows: runs-on: ubuntu-latest strategy: matrix: - goos: [linux, windows, darwin, freebsd] - goarch: [amd64, arm, arm64] - exclude: - - goarch: arm - goos: darwin - - goarch: 386 - goos: darwin - - goarch: arm - goos: windows - - goarch: arm64 - goos: windows + goos: [windows] + goarch: [amd64] + + # Steps represent a sequence of tasks that will be executed as part of the job + steps: + # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it + - uses: actions/checkout@v2 + - name: Go Release Binaries Normal Volume Size + uses: wangyoucao577/go-release-action@v1.20 + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + goos: ${{ matrix.goos }} + goarch: ${{ matrix.goarch }} + overwrite: true + pre_command: export CGO_ENABLED=0 + # build_flags: -tags 5BytesOffset # optional, default is + ldflags: -extldflags -static -X github.com/chrislusf/seaweedfs/weed/util.COMMIT=${{github.sha}} + # Where to run `go build .` + project_path: weed + binary_name: weed + asset_name: "${{ matrix.goos }}_${{ matrix.goarch }}" + - name: Go Release Large Disk Binaries + uses: wangyoucao577/go-release-action@v1.20 + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + goos: ${{ matrix.goos }} + goarch: ${{ matrix.goarch }} + overwrite: true + pre_command: export CGO_ENABLED=0 + build_flags: -tags 5BytesOffset # optional, default is + ldflags: -extldflags -static -X github.com/chrislusf/seaweedfs/weed/util.COMMIT=${{github.sha}} + # Where to run `go build .` + project_path: weed + binary_name: weed + asset_name: "${{ matrix.goos }}_${{ matrix.goarch }}_large_disk" + + build-release-binaries_linux: + runs-on: ubuntu-latest + strategy: + matrix: + goos: [linux] + goarch: [amd64, arm, arm64] + + # Steps represent a sequence of tasks that will be executed as part of the job + steps: + # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it + - uses: actions/checkout@v2 + - name: Go Release Binaries Normal Volume Size + uses: wangyoucao577/go-release-action@v1.20 + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + goos: ${{ matrix.goos }} + goarch: ${{ matrix.goarch }} + overwrite: true + pre_command: export CGO_ENABLED=0 + # build_flags: -tags 5BytesOffset # optional, default is + ldflags: -extldflags -static -X github.com/chrislusf/seaweedfs/weed/util.COMMIT=${{github.sha}} + # Where to run `go build .` + project_path: weed + binary_name: weed + asset_name: "${{ matrix.goos }}_${{ matrix.goarch }}" + - name: Go Release Large Disk Binaries + uses: wangyoucao577/go-release-action@v1.20 + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + goos: ${{ matrix.goos }} + goarch: ${{ matrix.goarch }} + overwrite: true + pre_command: export CGO_ENABLED=0 + build_flags: -tags 5BytesOffset # optional, default is + ldflags: -extldflags -static -X github.com/chrislusf/seaweedfs/weed/util.COMMIT=${{github.sha}} + # Where to run `go build .` + project_path: weed + binary_name: weed + asset_name: "${{ matrix.goos }}_${{ matrix.goarch }}_large_disk" + + build-release-binaries_darwin: + runs-on: ubuntu-latest + strategy: + matrix: + goos: [darwin] + goarch: [amd64, arm64] + + # Steps represent a sequence of tasks that will be executed as part of the job + steps: + # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it + - uses: actions/checkout@v2 + - name: Go Release Binaries Normal Volume Size + uses: wangyoucao577/go-release-action@v1.20 + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + goos: ${{ matrix.goos }} + goarch: ${{ matrix.goarch }} + overwrite: true + pre_command: export CGO_ENABLED=0 + # build_flags: -tags 5BytesOffset # optional, default is + ldflags: -extldflags -static -X github.com/chrislusf/seaweedfs/weed/util.COMMIT=${{github.sha}} + # Where to run `go build .` + project_path: weed + binary_name: weed + asset_name: "${{ matrix.goos }}_${{ matrix.goarch }}" + - name: Go Release Large Disk Binaries + uses: wangyoucao577/go-release-action@v1.20 + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + goos: ${{ matrix.goos }} + goarch: ${{ matrix.goarch }} + overwrite: true + pre_command: export CGO_ENABLED=0 + build_flags: -tags 5BytesOffset # optional, default is + ldflags: -extldflags -static -X github.com/chrislusf/seaweedfs/weed/util.COMMIT=${{github.sha}} + # Where to run `go build .` + project_path: weed + binary_name: weed + asset_name: "${{ matrix.goos }}_${{ matrix.goarch }}_large_disk" + + build-release-binaries_freebsd: + runs-on: ubuntu-latest + strategy: + matrix: + goos: [freebsd] + goarch: [amd64, arm, arm64] # Steps represent a sequence of tasks that will be executed as part of the job steps: From 4800d0ce262ab4084a181ccde36b52b6961568ac Mon Sep 17 00:00:00 2001 From: Chris Lu Date: Mon, 11 Oct 2021 00:56:35 -0700 Subject: [PATCH 128/130] separate into multiple actions --- .github/workflows/binaries_release.yml | 174 ----------------------- .github/workflows/binaries_release0.yml | 54 +++++++ .github/workflows/binaries_release1.yml | 54 +++++++ .github/workflows/binaries_release2.yml | 54 +++++++ .github/workflows/binaries_release3.yml | 54 +++++++ .github/workflows/container_release.yml | 152 -------------------- .github/workflows/container_release1.yml | 55 +++++++ .github/workflows/container_release2.yml | 56 ++++++++ .github/workflows/container_release3.yml | 55 +++++++ 9 files changed, 382 insertions(+), 326 deletions(-) delete mode 100644 .github/workflows/binaries_release.yml create mode 100644 .github/workflows/binaries_release0.yml create mode 100644 .github/workflows/binaries_release1.yml create mode 100644 .github/workflows/binaries_release2.yml create mode 100644 .github/workflows/binaries_release3.yml delete mode 100644 .github/workflows/container_release.yml create mode 100644 .github/workflows/container_release1.yml create mode 100644 .github/workflows/container_release2.yml create mode 100644 .github/workflows/container_release3.yml diff --git a/.github/workflows/binaries_release.yml b/.github/workflows/binaries_release.yml deleted file mode 100644 index 084b34255..000000000 --- a/.github/workflows/binaries_release.yml +++ /dev/null @@ -1,174 +0,0 @@ -# This is a basic workflow to help you get started with Actions - -name: "go: build versioned binaries" - -on: - push: - tags: - - '*' - - # Allows you to run this workflow manually from the Actions tab - workflow_dispatch: - -# A workflow run is made up of one or more jobs that can run sequentially or in parallel -jobs: - - build-release-binaries_windows: - runs-on: ubuntu-latest - strategy: - matrix: - goos: [windows] - goarch: [amd64] - - # Steps represent a sequence of tasks that will be executed as part of the job - steps: - # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it - - uses: actions/checkout@v2 - - name: Go Release Binaries Normal Volume Size - uses: wangyoucao577/go-release-action@v1.20 - with: - github_token: ${{ secrets.GITHUB_TOKEN }} - goos: ${{ matrix.goos }} - goarch: ${{ matrix.goarch }} - overwrite: true - pre_command: export CGO_ENABLED=0 - # build_flags: -tags 5BytesOffset # optional, default is - ldflags: -extldflags -static -X github.com/chrislusf/seaweedfs/weed/util.COMMIT=${{github.sha}} - # Where to run `go build .` - project_path: weed - binary_name: weed - asset_name: "${{ matrix.goos }}_${{ matrix.goarch }}" - - name: Go Release Large Disk Binaries - uses: wangyoucao577/go-release-action@v1.20 - with: - github_token: ${{ secrets.GITHUB_TOKEN }} - goos: ${{ matrix.goos }} - goarch: ${{ matrix.goarch }} - overwrite: true - pre_command: export CGO_ENABLED=0 - build_flags: -tags 5BytesOffset # optional, default is - ldflags: -extldflags -static -X github.com/chrislusf/seaweedfs/weed/util.COMMIT=${{github.sha}} - # Where to run `go build .` - project_path: weed - binary_name: weed - asset_name: "${{ matrix.goos }}_${{ matrix.goarch }}_large_disk" - - build-release-binaries_linux: - runs-on: ubuntu-latest - strategy: - matrix: - goos: [linux] - goarch: [amd64, arm, arm64] - - # Steps represent a sequence of tasks that will be executed as part of the job - steps: - # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it - - uses: actions/checkout@v2 - - name: Go Release Binaries Normal Volume Size - uses: wangyoucao577/go-release-action@v1.20 - with: - github_token: ${{ secrets.GITHUB_TOKEN }} - goos: ${{ matrix.goos }} - goarch: ${{ matrix.goarch }} - overwrite: true - pre_command: export CGO_ENABLED=0 - # build_flags: -tags 5BytesOffset # optional, default is - ldflags: -extldflags -static -X github.com/chrislusf/seaweedfs/weed/util.COMMIT=${{github.sha}} - # Where to run `go build .` - project_path: weed - binary_name: weed - asset_name: "${{ matrix.goos }}_${{ matrix.goarch }}" - - name: Go Release Large Disk Binaries - uses: wangyoucao577/go-release-action@v1.20 - with: - github_token: ${{ secrets.GITHUB_TOKEN }} - goos: ${{ matrix.goos }} - goarch: ${{ matrix.goarch }} - overwrite: true - pre_command: export CGO_ENABLED=0 - build_flags: -tags 5BytesOffset # optional, default is - ldflags: -extldflags -static -X github.com/chrislusf/seaweedfs/weed/util.COMMIT=${{github.sha}} - # Where to run `go build .` - project_path: weed - binary_name: weed - asset_name: "${{ matrix.goos }}_${{ matrix.goarch }}_large_disk" - - build-release-binaries_darwin: - runs-on: ubuntu-latest - strategy: - matrix: - goos: [darwin] - goarch: [amd64, arm64] - - # Steps represent a sequence of tasks that will be executed as part of the job - steps: - # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it - - uses: actions/checkout@v2 - - name: Go Release Binaries Normal Volume Size - uses: wangyoucao577/go-release-action@v1.20 - with: - github_token: ${{ secrets.GITHUB_TOKEN }} - goos: ${{ matrix.goos }} - goarch: ${{ matrix.goarch }} - overwrite: true - pre_command: export CGO_ENABLED=0 - # build_flags: -tags 5BytesOffset # optional, default is - ldflags: -extldflags -static -X github.com/chrislusf/seaweedfs/weed/util.COMMIT=${{github.sha}} - # Where to run `go build .` - project_path: weed - binary_name: weed - asset_name: "${{ matrix.goos }}_${{ matrix.goarch }}" - - name: Go Release Large Disk Binaries - uses: wangyoucao577/go-release-action@v1.20 - with: - github_token: ${{ secrets.GITHUB_TOKEN }} - goos: ${{ matrix.goos }} - goarch: ${{ matrix.goarch }} - overwrite: true - pre_command: export CGO_ENABLED=0 - build_flags: -tags 5BytesOffset # optional, default is - ldflags: -extldflags -static -X github.com/chrislusf/seaweedfs/weed/util.COMMIT=${{github.sha}} - # Where to run `go build .` - project_path: weed - binary_name: weed - asset_name: "${{ matrix.goos }}_${{ matrix.goarch }}_large_disk" - - build-release-binaries_freebsd: - runs-on: ubuntu-latest - strategy: - matrix: - goos: [freebsd] - goarch: [amd64, arm, arm64] - - # Steps represent a sequence of tasks that will be executed as part of the job - steps: - # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it - - uses: actions/checkout@v2 - - name: Go Release Binaries Normal Volume Size - uses: wangyoucao577/go-release-action@v1.20 - with: - github_token: ${{ secrets.GITHUB_TOKEN }} - goos: ${{ matrix.goos }} - goarch: ${{ matrix.goarch }} - overwrite: true - pre_command: export CGO_ENABLED=0 - # build_flags: -tags 5BytesOffset # optional, default is - ldflags: -extldflags -static -X github.com/chrislusf/seaweedfs/weed/util.COMMIT=${{github.sha}} - # Where to run `go build .` - project_path: weed - binary_name: weed - asset_name: "${{ matrix.goos }}_${{ matrix.goarch }}" - - name: Go Release Large Disk Binaries - uses: wangyoucao577/go-release-action@v1.20 - with: - github_token: ${{ secrets.GITHUB_TOKEN }} - goos: ${{ matrix.goos }} - goarch: ${{ matrix.goarch }} - overwrite: true - pre_command: export CGO_ENABLED=0 - build_flags: -tags 5BytesOffset # optional, default is - ldflags: -extldflags -static -X github.com/chrislusf/seaweedfs/weed/util.COMMIT=${{github.sha}} - # Where to run `go build .` - project_path: weed - binary_name: weed - asset_name: "${{ matrix.goos }}_${{ matrix.goarch }}_large_disk" diff --git a/.github/workflows/binaries_release0.yml b/.github/workflows/binaries_release0.yml new file mode 100644 index 000000000..622fe5dd6 --- /dev/null +++ b/.github/workflows/binaries_release0.yml @@ -0,0 +1,54 @@ +# This is a basic workflow to help you get started with Actions + +name: "go: build versioned binaries for windows" + +on: + push: + tags: + - '*' + + # Allows you to run this workflow manually from the Actions tab + workflow_dispatch: + +# A workflow run is made up of one or more jobs that can run sequentially or in parallel +jobs: + + build-release-binaries_windows: + runs-on: ubuntu-latest + strategy: + matrix: + goos: [windows] + goarch: [amd64] + + # Steps represent a sequence of tasks that will be executed as part of the job + steps: + # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it + - uses: actions/checkout@v2 + - name: Go Release Binaries Normal Volume Size + uses: wangyoucao577/go-release-action@v1.20 + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + goos: ${{ matrix.goos }} + goarch: ${{ matrix.goarch }} + overwrite: true + pre_command: export CGO_ENABLED=0 + # build_flags: -tags 5BytesOffset # optional, default is + ldflags: -extldflags -static -X github.com/chrislusf/seaweedfs/weed/util.COMMIT=${{github.sha}} + # Where to run `go build .` + project_path: weed + binary_name: weed + asset_name: "${{ matrix.goos }}_${{ matrix.goarch }}" + - name: Go Release Large Disk Binaries + uses: wangyoucao577/go-release-action@v1.20 + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + goos: ${{ matrix.goos }} + goarch: ${{ matrix.goarch }} + overwrite: true + pre_command: export CGO_ENABLED=0 + build_flags: -tags 5BytesOffset # optional, default is + ldflags: -extldflags -static -X github.com/chrislusf/seaweedfs/weed/util.COMMIT=${{github.sha}} + # Where to run `go build .` + project_path: weed + binary_name: weed + asset_name: "${{ matrix.goos }}_${{ matrix.goarch }}_large_disk" diff --git a/.github/workflows/binaries_release1.yml b/.github/workflows/binaries_release1.yml new file mode 100644 index 000000000..a0cc2c74c --- /dev/null +++ b/.github/workflows/binaries_release1.yml @@ -0,0 +1,54 @@ +# This is a basic workflow to help you get started with Actions + +name: "go: build versioned binaries for linux" + +on: + push: + tags: + - '*' + + # Allows you to run this workflow manually from the Actions tab + workflow_dispatch: + +# A workflow run is made up of one or more jobs that can run sequentially or in parallel +jobs: + + build-release-binaries_linux: + runs-on: ubuntu-latest + strategy: + matrix: + goos: [linux] + goarch: [amd64, arm, arm64] + + # Steps represent a sequence of tasks that will be executed as part of the job + steps: + # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it + - uses: actions/checkout@v2 + - name: Go Release Binaries Normal Volume Size + uses: wangyoucao577/go-release-action@v1.20 + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + goos: ${{ matrix.goos }} + goarch: ${{ matrix.goarch }} + overwrite: true + pre_command: export CGO_ENABLED=0 + # build_flags: -tags 5BytesOffset # optional, default is + ldflags: -extldflags -static -X github.com/chrislusf/seaweedfs/weed/util.COMMIT=${{github.sha}} + # Where to run `go build .` + project_path: weed + binary_name: weed + asset_name: "${{ matrix.goos }}_${{ matrix.goarch }}" + - name: Go Release Large Disk Binaries + uses: wangyoucao577/go-release-action@v1.20 + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + goos: ${{ matrix.goos }} + goarch: ${{ matrix.goarch }} + overwrite: true + pre_command: export CGO_ENABLED=0 + build_flags: -tags 5BytesOffset # optional, default is + ldflags: -extldflags -static -X github.com/chrislusf/seaweedfs/weed/util.COMMIT=${{github.sha}} + # Where to run `go build .` + project_path: weed + binary_name: weed + asset_name: "${{ matrix.goos }}_${{ matrix.goarch }}_large_disk" diff --git a/.github/workflows/binaries_release2.yml b/.github/workflows/binaries_release2.yml new file mode 100644 index 000000000..95cde0fdd --- /dev/null +++ b/.github/workflows/binaries_release2.yml @@ -0,0 +1,54 @@ +# This is a basic workflow to help you get started with Actions + +name: "go: build versioned binaries for darwin" + +on: + push: + tags: + - '*' + + # Allows you to run this workflow manually from the Actions tab + workflow_dispatch: + +# A workflow run is made up of one or more jobs that can run sequentially or in parallel +jobs: + + build-release-binaries_darwin: + runs-on: ubuntu-latest + strategy: + matrix: + goos: [darwin] + goarch: [amd64, arm64] + + # Steps represent a sequence of tasks that will be executed as part of the job + steps: + # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it + - uses: actions/checkout@v2 + - name: Go Release Binaries Normal Volume Size + uses: wangyoucao577/go-release-action@v1.20 + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + goos: ${{ matrix.goos }} + goarch: ${{ matrix.goarch }} + overwrite: true + pre_command: export CGO_ENABLED=0 + # build_flags: -tags 5BytesOffset # optional, default is + ldflags: -extldflags -static -X github.com/chrislusf/seaweedfs/weed/util.COMMIT=${{github.sha}} + # Where to run `go build .` + project_path: weed + binary_name: weed + asset_name: "${{ matrix.goos }}_${{ matrix.goarch }}" + - name: Go Release Large Disk Binaries + uses: wangyoucao577/go-release-action@v1.20 + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + goos: ${{ matrix.goos }} + goarch: ${{ matrix.goarch }} + overwrite: true + pre_command: export CGO_ENABLED=0 + build_flags: -tags 5BytesOffset # optional, default is + ldflags: -extldflags -static -X github.com/chrislusf/seaweedfs/weed/util.COMMIT=${{github.sha}} + # Where to run `go build .` + project_path: weed + binary_name: weed + asset_name: "${{ matrix.goos }}_${{ matrix.goarch }}_large_disk" diff --git a/.github/workflows/binaries_release3.yml b/.github/workflows/binaries_release3.yml new file mode 100644 index 000000000..0bbfa3953 --- /dev/null +++ b/.github/workflows/binaries_release3.yml @@ -0,0 +1,54 @@ +# This is a basic workflow to help you get started with Actions + +name: "go: build versioned binaries for freebsd" + +on: + push: + tags: + - '*' + + # Allows you to run this workflow manually from the Actions tab + workflow_dispatch: + +# A workflow run is made up of one or more jobs that can run sequentially or in parallel +jobs: + + build-release-binaries_freebsd: + runs-on: ubuntu-latest + strategy: + matrix: + goos: [freebsd] + goarch: [amd64, arm, arm64] + + # Steps represent a sequence of tasks that will be executed as part of the job + steps: + # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it + - uses: actions/checkout@v2 + - name: Go Release Binaries Normal Volume Size + uses: wangyoucao577/go-release-action@v1.20 + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + goos: ${{ matrix.goos }} + goarch: ${{ matrix.goarch }} + overwrite: true + pre_command: export CGO_ENABLED=0 + # build_flags: -tags 5BytesOffset # optional, default is + ldflags: -extldflags -static -X github.com/chrislusf/seaweedfs/weed/util.COMMIT=${{github.sha}} + # Where to run `go build .` + project_path: weed + binary_name: weed + asset_name: "${{ matrix.goos }}_${{ matrix.goarch }}" + - name: Go Release Large Disk Binaries + uses: wangyoucao577/go-release-action@v1.20 + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + goos: ${{ matrix.goos }} + goarch: ${{ matrix.goarch }} + overwrite: true + pre_command: export CGO_ENABLED=0 + build_flags: -tags 5BytesOffset # optional, default is + ldflags: -extldflags -static -X github.com/chrislusf/seaweedfs/weed/util.COMMIT=${{github.sha}} + # Where to run `go build .` + project_path: weed + binary_name: weed + asset_name: "${{ matrix.goos }}_${{ matrix.goarch }}_large_disk" diff --git a/.github/workflows/container_release.yml b/.github/workflows/container_release.yml deleted file mode 100644 index f008f0e70..000000000 --- a/.github/workflows/container_release.yml +++ /dev/null @@ -1,152 +0,0 @@ -name: "docker: build release containers" - -on: - push: - tags: - - '*' - workflow_dispatch: [] - -jobs: - build-default-release-container: - runs-on: [ubuntu-latest] - - steps: - - - name: Checkout - uses: actions/checkout@v2 - - - name: Docker meta - id: docker_meta - uses: docker/metadata-action@v3 - with: - images: | - chrislusf/seaweedfs - ghcr.io/chrislusf/seaweedfs - tags: | - type=ref,event=tag - flavor: | - latest=false - labels: | - org.opencontainers.image.title=seaweedfs - org.opencontainers.image.description=SeaweedFS is a distributed storage system for blobs, objects, files, and data lake, to store and serve billions of files fast! - org.opencontainers.image.vendor=Chris Lu - - - name: Set up QEMU - uses: docker/setup-qemu-action@v1 - - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v1 - with: - buildkitd-flags: "--debug" - - - name: Login to Docker Hub - if: github.event_name != 'pull_request' - uses: docker/login-action@v1 - with: - username: ${{ secrets.DOCKER_USERNAME }} - password: ${{ secrets.DOCKER_PASSWORD }} - - - name: Build - uses: docker/build-push-action@v2 - with: - context: ./docker - push: ${{ github.event_name != 'pull_request' }} - file: ./docker/Dockerfile.go_build - platforms: linux/amd64, linux/arm, linux/arm64, linux/386 - tags: ${{ steps.docker_meta.outputs.tags }} - labels: ${{ steps.docker_meta.outputs.labels }} - - build-large-release-container: - runs-on: [ubuntu-latest] - - steps: - - - name: Checkout - uses: actions/checkout@v2 - - - name: Docker meta - id: docker_meta - uses: docker/metadata-action@v3 - with: - images: | - chrislusf/seaweedfs - ghcr.io/chrislusf/seaweedfs - tags: | - type=ref,event=tag,suffix=_large_disk - flavor: | - latest=false - labels: | - org.opencontainers.image.title=seaweedfs - org.opencontainers.image.description=SeaweedFS is a distributed storage system for blobs, objects, files, and data lake, to store and serve billions of files fast! - org.opencontainers.image.vendor=Chris Lu - - - name: Set up QEMU - uses: docker/setup-qemu-action@v1 - - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v1 - with: - buildkitd-flags: "--debug" - - - name: Login to Docker Hub - if: github.event_name != 'pull_request' - uses: docker/login-action@v1 - with: - username: ${{ secrets.DOCKER_USERNAME }} - password: ${{ secrets.DOCKER_PASSWORD }} - - - name: Build - uses: docker/build-push-action@v2 - with: - context: ./docker - push: ${{ github.event_name != 'pull_request' }} - file: ./docker/Dockerfile.go_build_large - platforms: linux/amd64, linux/arm, linux/arm64, linux/386 - tags: ${{ steps.docker_meta.outputs.tags }} - labels: ${{ steps.docker_meta.outputs.labels }} - - build-large-release-container_rocksdb: - runs-on: [ubuntu-latest] - - steps: - - - name: Checkout - uses: actions/checkout@v2 - - - name: Docker meta - id: docker_meta - uses: docker/metadata-action@v3 - with: - images: | - chrislusf/seaweedfs - tags: | - type=ref,event=tag,suffix=_large_disk_rocksdb - flavor: | - latest=false - labels: | - org.opencontainers.image.title=seaweedfs - org.opencontainers.image.description=SeaweedFS is a distributed storage system for blobs, objects, files, and data lake, to store and serve billions of files fast! - org.opencontainers.image.vendor=Chris Lu - - - name: Set up QEMU - uses: docker/setup-qemu-action@v1 - - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v1 - - - name: Login to Docker Hub - if: github.event_name != 'pull_request' - uses: docker/login-action@v1 - with: - username: ${{ secrets.DOCKER_USERNAME }} - password: ${{ secrets.DOCKER_PASSWORD }} - - - name: Build - uses: docker/build-push-action@v2 - with: - context: ./docker - push: ${{ github.event_name != 'pull_request' }} - file: ./docker/Dockerfile.rocksdb_large - platforms: linux/amd64 - tags: ${{ steps.docker_meta.outputs.tags }} - labels: ${{ steps.docker_meta.outputs.labels }} diff --git a/.github/workflows/container_release1.yml b/.github/workflows/container_release1.yml new file mode 100644 index 000000000..1738b8825 --- /dev/null +++ b/.github/workflows/container_release1.yml @@ -0,0 +1,55 @@ +name: "docker: build release containers for normal volume" + +on: + push: + tags: + - '*' + workflow_dispatch: [] + +jobs: + build-default-release-container: + runs-on: [ubuntu-latest] + + steps: + - + name: Checkout + uses: actions/checkout@v2 + - + name: Docker meta + id: docker_meta + uses: docker/metadata-action@v3 + with: + images: | + chrislusf/seaweedfs + ghcr.io/chrislusf/seaweedfs + tags: | + type=ref,event=tag + flavor: | + latest=false + labels: | + org.opencontainers.image.title=seaweedfs + org.opencontainers.image.description=SeaweedFS is a distributed storage system for blobs, objects, files, and data lake, to store and serve billions of files fast! + org.opencontainers.image.vendor=Chris Lu + - + name: Set up QEMU + uses: docker/setup-qemu-action@v1 + - + name: Set up Docker Buildx + uses: docker/setup-buildx-action@v1 + - + name: Login to Docker Hub + if: github.event_name != 'pull_request' + uses: docker/login-action@v1 + with: + username: ${{ secrets.DOCKER_USERNAME }} + password: ${{ secrets.DOCKER_PASSWORD }} + - + name: Build + uses: docker/build-push-action@v2 + with: + context: ./docker + push: ${{ github.event_name != 'pull_request' }} + file: ./docker/Dockerfile.go_build + platforms: linux/amd64, linux/arm, linux/arm64, linux/386 + tags: ${{ steps.docker_meta.outputs.tags }} + labels: ${{ steps.docker_meta.outputs.labels }} diff --git a/.github/workflows/container_release2.yml b/.github/workflows/container_release2.yml new file mode 100644 index 000000000..8106f7a9f --- /dev/null +++ b/.github/workflows/container_release2.yml @@ -0,0 +1,56 @@ +name: "docker: build release containers for large volume" + +on: + push: + tags: + - '*' + workflow_dispatch: [] + +jobs: + + build-large-release-container: + runs-on: [ubuntu-latest] + + steps: + - + name: Checkout + uses: actions/checkout@v2 + - + name: Docker meta + id: docker_meta + uses: docker/metadata-action@v3 + with: + images: | + chrislusf/seaweedfs + ghcr.io/chrislusf/seaweedfs + tags: | + type=ref,event=tag,suffix=_large_disk + flavor: | + latest=false + labels: | + org.opencontainers.image.title=seaweedfs + org.opencontainers.image.description=SeaweedFS is a distributed storage system for blobs, objects, files, and data lake, to store and serve billions of files fast! + org.opencontainers.image.vendor=Chris Lu + - + name: Set up QEMU + uses: docker/setup-qemu-action@v1 + - + name: Set up Docker Buildx + uses: docker/setup-buildx-action@v1 + - + name: Login to Docker Hub + if: github.event_name != 'pull_request' + uses: docker/login-action@v1 + with: + username: ${{ secrets.DOCKER_USERNAME }} + password: ${{ secrets.DOCKER_PASSWORD }} + - + name: Build + uses: docker/build-push-action@v2 + with: + context: ./docker + push: ${{ github.event_name != 'pull_request' }} + file: ./docker/Dockerfile.go_build_large + platforms: linux/amd64, linux/arm, linux/arm64, linux/386 + tags: ${{ steps.docker_meta.outputs.tags }} + labels: ${{ steps.docker_meta.outputs.labels }} diff --git a/.github/workflows/container_release3.yml b/.github/workflows/container_release3.yml new file mode 100644 index 000000000..93cada734 --- /dev/null +++ b/.github/workflows/container_release3.yml @@ -0,0 +1,55 @@ +name: "docker: build release containers for rocksdb" + +on: + push: + tags: + - '*' + workflow_dispatch: [] + +jobs: + + build-large-release-container_rocksdb: + runs-on: [ubuntu-latest] + + steps: + - + name: Checkout + uses: actions/checkout@v2 + - + name: Docker meta + id: docker_meta + uses: docker/metadata-action@v3 + with: + images: | + chrislusf/seaweedfs + tags: | + type=ref,event=tag,suffix=_large_disk_rocksdb + flavor: | + latest=false + labels: | + org.opencontainers.image.title=seaweedfs + org.opencontainers.image.description=SeaweedFS is a distributed storage system for blobs, objects, files, and data lake, to store and serve billions of files fast! + org.opencontainers.image.vendor=Chris Lu + - + name: Set up QEMU + uses: docker/setup-qemu-action@v1 + - + name: Set up Docker Buildx + uses: docker/setup-buildx-action@v1 + - + name: Login to Docker Hub + if: github.event_name != 'pull_request' + uses: docker/login-action@v1 + with: + username: ${{ secrets.DOCKER_USERNAME }} + password: ${{ secrets.DOCKER_PASSWORD }} + - + name: Build + uses: docker/build-push-action@v2 + with: + context: ./docker + push: ${{ github.event_name != 'pull_request' }} + file: ./docker/Dockerfile.rocksdb_large + platforms: linux/amd64 + tags: ${{ steps.docker_meta.outputs.tags }} + labels: ${{ steps.docker_meta.outputs.labels }} From e6ef7b238793901ac2603d5adfb5383b0a3e9846 Mon Sep 17 00:00:00 2001 From: Chris Lu Date: Mon, 11 Oct 2021 01:24:30 -0700 Subject: [PATCH 129/130] return error early fix https://github.com/chrislusf/seaweedfs/issues/2370 --- weed/operation/assign_file_id.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/weed/operation/assign_file_id.go b/weed/operation/assign_file_id.go index 9eac69631..b4d44eccf 100644 --- a/weed/operation/assign_file_id.go +++ b/weed/operation/assign_file_id.go @@ -66,6 +66,10 @@ func Assign(masterFn GetMasterFn, grpcDialOption grpc.DialOption, primaryRequest return grpcErr } + if resp.Error != "" { + return fmt.Errorf("assignRequest: %v", resp.Error) + } + ret.Count = resp.Count ret.Fid = resp.Fid ret.Url = resp.Location.Url @@ -80,10 +84,6 @@ func Assign(masterFn GetMasterFn, grpcDialOption grpc.DialOption, primaryRequest }) } - if resp.Error != "" { - return fmt.Errorf("assignRequest: %v", resp.Error) - } - return nil }) From 84d2e1bdd099550aaba494c88324c8c0dbc08776 Mon Sep 17 00:00:00 2001 From: Chris Lu Date: Mon, 11 Oct 2021 01:29:05 -0700 Subject: [PATCH 130/130] fix container building, make it same as rocksdb image --- .github/workflows/container_release1.yml | 1 - .github/workflows/container_release2.yml | 1 - 2 files changed, 2 deletions(-) diff --git a/.github/workflows/container_release1.yml b/.github/workflows/container_release1.yml index 1738b8825..4b0ff16e3 100644 --- a/.github/workflows/container_release1.yml +++ b/.github/workflows/container_release1.yml @@ -21,7 +21,6 @@ jobs: with: images: | chrislusf/seaweedfs - ghcr.io/chrislusf/seaweedfs tags: | type=ref,event=tag flavor: | diff --git a/.github/workflows/container_release2.yml b/.github/workflows/container_release2.yml index 8106f7a9f..e62401e7f 100644 --- a/.github/workflows/container_release2.yml +++ b/.github/workflows/container_release2.yml @@ -22,7 +22,6 @@ jobs: with: images: | chrislusf/seaweedfs - ghcr.io/chrislusf/seaweedfs tags: | type=ref,event=tag,suffix=_large_disk flavor: |