Compare commits

...
Sign in to create a new pull request.

24 commits

Author SHA1 Message Date
8822aedbbb [#360] bearer: Change APEOverride method prototype
* Make `APEOverride` also return flag that indicates whether bearer token
  sets ape override;
* Fix unit-tests.

Signed-off-by: Airat Arifullin <a.arifullin@yadro.com>
2025-04-28 16:47:06 +03:00
Aleksey Kravchenko
715b1e4100 [#358] pool: Add LatestReceivedEpoch to track latest response epoch
Signed-off-by: Aleksey Kravchenko <al.kravchenko@yadro.com>
2025-04-19 22:23:33 +03:00
971e740ea3
[#35] network: Use AppendComponent() for TLS encapsulation
As advocated explicitly in https://github.com/multiformats/go-multiaddr/blob/master/v015-MIGRATION.md

Answering a question about safety:
AppendComponent() appends to `a.ma`` in place.
`a.ma` is created by FromString() function,
so there is only a single goroutine appending to it.
Thus, assuming `FromString()` itself is called from a single goroutine,
no data race is introduced.

Signed-off-by: Evgenii Stratonikov <e.stratonikov@yadro.com>
2025-04-12 21:19:30 +03:00
fc8f9637fe
[#35] go.mod: Update go-multiaddr to v0.15.0
New multiaddr release is incompatible with the old one:
https://github.com/multiformats/go-multiaddr/releases/tag/v0.15.0
https://github.com/multiformats/go-multiaddr/blob/master/v015-MIGRATION.md

Thankfully, it seems we are not affected by any breaking changes.

Signed-off-by: Evgenii Stratonikov <e.stratonikov@yadro.com>
2025-04-12 21:19:30 +03:00
d282cd094f
[#355] netmap: Cache price and capacity attributes
They are used by HRW sorting and it makes sense to store them separately
instead of iterating over all attributes each time we need them.
It also simplifies code: we already parse them in NodeInfo.readFromV2(),
so just save the result.

```
goos: linux
goarch: amd64
pkg: git.frostfs.info/TrueCloudLab/frostfs-sdk-go/netmap
cpu: 11th Gen Intel(R) Core(TM) i5-1135G7 @ 2.40GHz
                                                              │     old     │                 new                 │
                                                              │   sec/op    │   sec/op     vs base                │
Netmap_ContainerNodes/REP_2-8                                   5.923µ ± 0%   5.209µ ± 1%  -12.05% (p=0.000 n=10)
Netmap_ContainerNodes/REP_2_IN_X_CBF_2_SELECT_2_FROM_*_AS_X-8   5.931µ ± 7%   5.088µ ± 1%  -14.22% (p=0.000 n=10)
geomean                                                         5.927µ        5.148µ       -13.14%

                                                              │     old      │                 new                 │
                                                              │     B/op     │     B/op      vs base               │
Netmap_ContainerNodes/REP_2-8                                   7.609Ki ± 0%   8.172Ki ± 0%  +7.39% (p=0.000 n=10)
Netmap_ContainerNodes/REP_2_IN_X_CBF_2_SELECT_2_FROM_*_AS_X-8   7.031Ki ± 0%   7.469Ki ± 0%  +6.22% (p=0.000 n=10)
geomean                                                         7.315Ki        7.812Ki       +6.81%

                                                              │    old     │                 new                 │
                                                              │ allocs/op  │ allocs/op   vs base                 │
Netmap_ContainerNodes/REP_2-8                                   77.00 ± 0%   77.00 ± 0%       ~ (p=1.000 n=10) ¹
Netmap_ContainerNodes/REP_2_IN_X_CBF_2_SELECT_2_FROM_*_AS_X-8   77.00 ± 0%   77.00 ± 0%       ~ (p=1.000 n=10) ¹
geomean                                                         77.00        77.00       +0.00%
¹ all samples are equal
```

Signed-off-by: Evgenii Stratonikov <e.stratonikov@yadro.com>
2025-04-07 17:47:08 +03:00
338faaf308
[#355] netmap: Remove unnecessary err declaration
Signed-off-by: Evgenii Stratonikov <e.stratonikov@yadro.com>
2025-04-07 17:47:08 +03:00
16fd3bafe0 [#354] api/netmap: Return a slice of parameters directly
`IterateParameters` does a poor job:
- it doesn't encapsulate well, because it returns a pointer,
- it has a clunky interface, compared to range loop.

I have decided to return parameter slice and not `iter.Seq` for 2
reasons:
1. There already is `SetParameters`, so `NetworkConfig` struct is
   expected to be modified.
2. This iterator uses pointers, so even with this interface the slice
   can already be changed.

Signed-off-by: Evgenii Stratonikov <e.stratonikov@yadro.com>
2025-04-07 14:39:47 +00:00
661adf17bb [#353] Format deprecated notices properly
They should be in a separate paragraph, otherwise they are not
recognized.

Signed-off-by: Evgenii Stratonikov <e.stratonikov@yadro.com>
2025-04-07 09:12:27 +00:00
f158a6b2e1 [#353] pool: Remove deprecated IterateNetworkEndpoints()
Signed-off-by: Evgenii Stratonikov <e.stratonikov@yadro.com>
2025-04-07 09:12:27 +00:00
a0e4d16dbb [#352] container: Implement iterators over attributes
Signed-off-by: Evgenii Stratonikov <e.stratonikov@yadro.com>
2025-04-07 09:12:13 +00:00
88bc9eeb26 [#356] netmap: Check for attribute duplicates in NodeInfo.readFromV2()
Signed-off-by: Evgenii Stratonikov <e.stratonikov@yadro.com>
2025-04-07 09:10:32 +00:00
684050b570 [#356] netmap/test: Cover NodeInfo.ReadFromV2() with tests
Untested code never works.
Qed.

Signed-off-by: Evgenii Stratonikov <e.stratonikov@yadro.com>
2025-04-07 09:10:32 +00:00
1b12c9beae [#356] netmap: Fix typo in NodeInfo.readFromV2()
Signed-off-by: Evgenii Stratonikov <e.stratonikov@yadro.com>
2025-04-07 09:10:32 +00:00
fb999fecac
[#357] netmap: Provide slice to append to in flattenNodes()
```
goos: linux
goarch: amd64
pkg: git.frostfs.info/TrueCloudLab/frostfs-sdk-go/netmap
cpu: 11th Gen Intel(R) Core(TM) i5-1135G7 @ 2.40GHz
                                                              │     old     │                new                 │
                                                              │   sec/op    │   sec/op     vs base               │
Netmap_ContainerNodes/REP_2-8                                   5.867µ ± 1%   5.821µ ± 0%  -0.79% (p=0.000 n=10)
Netmap_ContainerNodes/REP_2_IN_X_CBF_2_SELECT_2_FROM_*_AS_X-8   5.786µ ± 2%   5.810µ ± 1%       ~ (p=1.000 n=10)
geomean                                                         5.826µ        5.815µ       -0.19%

                                                              │     old      │                 new                 │
                                                              │     B/op     │     B/op      vs base               │
Netmap_ContainerNodes/REP_2-8                                   7.609Ki ± 0%   7.328Ki ± 0%  -3.70% (p=0.000 n=10)
Netmap_ContainerNodes/REP_2_IN_X_CBF_2_SELECT_2_FROM_*_AS_X-8   7.031Ki ± 0%   6.859Ki ± 0%  -2.44% (p=0.000 n=10)
geomean                                                         7.315Ki        7.090Ki       -3.07%

                                                              │    old     │                 new                 │
                                                              │ allocs/op  │ allocs/op   vs base                 │
Netmap_ContainerNodes/REP_2-8                                   77.00 ± 0%   77.00 ± 0%       ~ (p=1.000 n=10) ¹
Netmap_ContainerNodes/REP_2_IN_X_CBF_2_SELECT_2_FROM_*_AS_X-8   77.00 ± 0%   77.00 ± 0%       ~ (p=1.000 n=10) ¹
geomean                                                         77.00        77.00       +0.00%
¹ all samples are equal
```

Signed-off-by: Evgenii Stratonikov <e.stratonikov@yadro.com>
2025-04-05 11:36:33 +03:00
6458c11e83
[#345] api/netmap: Drop deprecated single-address methods
Signed-off-by: Evgenii Stratonikov <e.stratonikov@yadro.com>
2025-04-04 18:22:10 +03:00
4b9b54a901
[#345] netmap: Implement an iterator over node attributes
Signed-off-by: Evgenii Stratonikov <e.stratonikov@yadro.com>
2025-04-04 18:22:10 +03:00
b27f172de9
[#345] netmap: Implement an iterator over node endpoints
Signed-off-by: Evgenii Stratonikov <e.stratonikov@yadro.com>
2025-04-04 18:22:09 +03:00
5be3415961
[#345] go.mod: Bump min go version to 1.23
Signed-off-by: Evgenii Stratonikov <e.stratonikov@yadro.com>
2025-04-04 17:26:35 +03:00
4d36a49d39 [#349] object: Make patcher apply patching for split header
* Change `PatchApplier` interface: `ApplyAttributesPatch` -> `ApplyHeaderPatch`.
  Make `ApplyHeaderPatch` receive `ApplyHeaderPatchPrm` as parameter;
* Fix `patcher`: apply patch for split header;
* Fix `patcher` unit-tests. Add test-case for split header;
* Extend `Patch` struct with `NewSplitHeader`;
* Change `ObjectPatcher` interface for client: `PatchAttributes` -> `PatchHeader`.
  Fix `objectPatcher`.
* Fix object transformer: since object header sets `SplitHeader` if it's passed.

Signed-off-by: Airat Arifullin <a.arifullin@yadro.com>
2025-03-26 13:17:39 +03:00
76265fe9be [#349] object: Introduce SplitHeader type
* Also introduce `SplitHeader` getter and `SetSplitHeader` setter
  for `Object` type.

Signed-off-by: Airat Arifullin <a.arifullin@yadro.com>
2025-03-26 13:17:37 +03:00
826df9303c [#349] object: Regenerate protobuf for Patch method
* `PatchRequestBody` got `NewSplitHeader` field
* Introduce `SetNewSplitHeader`, `GetNewSplitHeader`
* Fix converter and marshaler

Signed-off-by: Airat Arifullin <a.arifullin@yadro.com>
2025-03-26 13:17:24 +03:00
87bb55f992 [#51] add address to logs
Signed-off-by: Pavel Pogodaev <p.pogodaev@yadro.com>
2025-03-17 11:28:14 +03:00
a262a0038f [#343] pool: Fix Yoda condition
go-staticcheck recommends not to use Yoda conditions.

Signed-off-by: Ekaterina Lebedeva <ekaterina.lebedeva@yadro.com>
2025-03-10 19:24:58 +03:00
fe5b28e6bf [#338] pool: Support avg request time for ListContainerStream
Signed-off-by: Ekaterina Lebedeva <ekaterina.lebedeva@yadro.com>
2025-03-10 19:24:58 +03:00
39 changed files with 1253 additions and 466 deletions

View file

@ -2,6 +2,7 @@ package netmap
import (
"bytes"
"iter"
"slices"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/api/refs"
@ -409,25 +410,6 @@ func (ni *NodeInfo) SetPublicKey(v []byte) {
ni.publicKey = v
}
// GetAddress returns node's network address.
//
// Deprecated: use IterateAddresses.
func (ni *NodeInfo) GetAddress() (addr string) {
ni.IterateAddresses(func(s string) bool {
addr = s
return true
})
return
}
// SetAddress sets node's network address.
//
// Deprecated: use SetAddresses.
func (ni *NodeInfo) SetAddress(v string) {
ni.SetAddresses(v)
}
// SetAddresses sets list of network addresses of the node.
func (ni *NodeInfo) SetAddresses(v ...string) {
ni.addresses = v
@ -442,10 +424,23 @@ func (ni *NodeInfo) NumberOfAddresses() int {
return 0
}
// Addresses returns an iterator over network addresses of the node.
func (ni NodeInfo) Addresses() iter.Seq[string] {
return func(yield func(string) bool) {
for i := range ni.addresses {
if !yield(ni.addresses[i]) {
break
}
}
}
}
// IterateAddresses iterates over network addresses of the node.
// Breaks iteration on f's true return.
//
// Handler should not be nil.
//
// Deprecated: use [NodeInfo.Addresses] instead.
func (ni *NodeInfo) IterateAddresses(f func(string) bool) {
if ni != nil {
for i := range ni.addresses {
@ -582,6 +577,8 @@ type NetworkConfig struct {
}
// NumberOfParameters returns number of network parameters.
//
// Deprecated: use [NetworkConfig.Parameters] instead.
func (x *NetworkConfig) NumberOfParameters() int {
if x != nil {
return len(x.ps)
@ -590,10 +587,20 @@ func (x *NetworkConfig) NumberOfParameters() int {
return 0
}
// Parameters returns an iterator over network parameters.
func (x *NetworkConfig) Parameters() []NetworkParameter {
if x != nil {
return x.ps
}
return nil
}
// IterateParameters iterates over network parameters.
// Breaks iteration on f's true return.
//
// Handler must not be nil.
//
// Deprecated: use [NetworkConfig.Parameters] instead.
func (x *NetworkConfig) IterateParameters(f func(*NetworkParameter) bool) {
if x != nil {
for i := range x.ps {

View file

@ -2389,6 +2389,7 @@ func (r *PatchRequestBody) ToGRPCMessage() grpc.Message {
m.SetNewAttributes(AttributesToGRPC(r.newAttributes))
m.SetReplaceAttributes(r.replaceAttributes)
m.SetPatch(r.patch.ToGRPCMessage().(*object.PatchRequest_Body_Patch))
m.SetNewSplitHeader(r.newSplitHeader.ToGRPCMessage().(*object.Header_Split))
}
return m
@ -2437,6 +2438,20 @@ func (r *PatchRequestBody) FromGRPCMessage(m grpc.Message) error {
}
}
newSplitHeader := v.GetNewSplitHeader()
if newSplitHeader == nil {
r.newSplitHeader = nil
} else {
if r.newSplitHeader == nil {
r.newSplitHeader = new(SplitHeader)
}
err = r.newSplitHeader.FromGRPCMessage(newSplitHeader)
if err != nil {
return err
}
}
return nil
}

View file

@ -5181,6 +5181,9 @@ type PatchRequest_Body struct {
// merged. If the incoming `new_attributes` list contains already existing
// key, then it just replaces it while merging the lists.
ReplaceAttributes *bool `protobuf:"varint,3,opt,name=replace_attributes,json=replaceAttributes" json:"replace_attributes,omitempty"`
// New split header for the object. This defines how the object will relate
// to other objects in a split operation.
NewSplitHeader *Header_Split `protobuf:"bytes,5,opt,name=new_split_header,json=newSplitHeader" json:"new_split_header,omitempty"`
// The patch that is applied for the object.
Patch *PatchRequest_Body_Patch `protobuf:"bytes,4,opt,name=patch" json:"patch,omitempty"`
unknownFields protoimpl.UnknownFields
@ -5233,6 +5236,13 @@ func (x *PatchRequest_Body) GetReplaceAttributes() bool {
return false
}
func (x *PatchRequest_Body) GetNewSplitHeader() *Header_Split {
if x != nil {
return x.NewSplitHeader
}
return nil
}
func (x *PatchRequest_Body) GetPatch() *PatchRequest_Body_Patch {
if x != nil {
return x.Patch
@ -5252,6 +5262,10 @@ func (x *PatchRequest_Body) SetReplaceAttributes(v bool) {
x.ReplaceAttributes = &v
}
func (x *PatchRequest_Body) SetNewSplitHeader(v *Header_Split) {
x.NewSplitHeader = v
}
func (x *PatchRequest_Body) SetPatch(v *PatchRequest_Body_Patch) {
x.Patch = v
}
@ -5270,6 +5284,13 @@ func (x *PatchRequest_Body) HasReplaceAttributes() bool {
return x.ReplaceAttributes != nil
}
func (x *PatchRequest_Body) HasNewSplitHeader() bool {
if x == nil {
return false
}
return x.NewSplitHeader != nil
}
func (x *PatchRequest_Body) HasPatch() bool {
if x == nil {
return false
@ -5285,6 +5306,10 @@ func (x *PatchRequest_Body) ClearReplaceAttributes() {
x.ReplaceAttributes = nil
}
func (x *PatchRequest_Body) ClearNewSplitHeader() {
x.NewSplitHeader = nil
}
func (x *PatchRequest_Body) ClearPatch() {
x.Patch = nil
}
@ -5305,6 +5330,9 @@ type PatchRequest_Body_builder struct {
// merged. If the incoming `new_attributes` list contains already existing
// key, then it just replaces it while merging the lists.
ReplaceAttributes *bool
// New split header for the object. This defines how the object will relate
// to other objects in a split operation.
NewSplitHeader *Header_Split
// The patch that is applied for the object.
Patch *PatchRequest_Body_Patch
}
@ -5316,6 +5344,7 @@ func (b0 PatchRequest_Body_builder) Build() *PatchRequest_Body {
x.Address = b.Address
x.NewAttributes = b.NewAttributes
x.ReplaceAttributes = b.ReplaceAttributes
x.NewSplitHeader = b.NewSplitHeader
x.Patch = b.Patch
return m0
}
@ -5902,7 +5931,7 @@ var file_api_object_grpc_service_proto_rawDesc = []byte{
0x73, 0x69, 0x6f, 0x6e, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x56, 0x65, 0x72,
0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x52,
0x0c, 0x76, 0x65, 0x72, 0x69, 0x66, 0x79, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x1a, 0x06, 0x0a,
0x04, 0x42, 0x6f, 0x64, 0x79, 0x22, 0xb3, 0x04, 0x0a, 0x0c, 0x50, 0x61, 0x74, 0x63, 0x68, 0x52,
0x04, 0x42, 0x6f, 0x64, 0x79, 0x22, 0xfd, 0x04, 0x0a, 0x0c, 0x50, 0x61, 0x74, 0x63, 0x68, 0x52,
0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x37, 0x0a, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x18, 0x01,
0x20, 0x01, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x6e, 0x65, 0x6f, 0x2e, 0x66, 0x73, 0x2e, 0x76, 0x32,
0x2e, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x2e, 0x50, 0x61, 0x74, 0x63, 0x68, 0x52, 0x65, 0x71,
@ -5916,7 +5945,7 @@ var file_api_object_grpc_service_proto_rawDesc = []byte{
0x6e, 0x65, 0x6f, 0x2e, 0x66, 0x73, 0x2e, 0x76, 0x32, 0x2e, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f,
0x6e, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x56, 0x65, 0x72, 0x69, 0x66, 0x69, 0x63,
0x61, 0x74, 0x69, 0x6f, 0x6e, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x52, 0x0c, 0x76, 0x65, 0x72,
0x69, 0x66, 0x79, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x1a, 0xcf, 0x02, 0x0a, 0x04, 0x42, 0x6f,
0x69, 0x66, 0x79, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x1a, 0x99, 0x03, 0x0a, 0x04, 0x42, 0x6f,
0x64, 0x79, 0x12, 0x31, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20,
0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x6e, 0x65, 0x6f, 0x2e, 0x66, 0x73, 0x2e, 0x76, 0x32, 0x2e,
0x72, 0x65, 0x66, 0x73, 0x2e, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x52, 0x07, 0x61, 0x64,
@ -5928,87 +5957,92 @@ var file_api_object_grpc_service_proto_rawDesc = []byte{
0x12, 0x2d, 0x0a, 0x12, 0x72, 0x65, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x5f, 0x61, 0x74, 0x74, 0x72,
0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x11, 0x72, 0x65,
0x70, 0x6c, 0x61, 0x63, 0x65, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x12,
0x3f, 0x0a, 0x05, 0x70, 0x61, 0x74, 0x63, 0x68, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x29,
0x2e, 0x6e, 0x65, 0x6f, 0x2e, 0x66, 0x73, 0x2e, 0x76, 0x32, 0x2e, 0x6f, 0x62, 0x6a, 0x65, 0x63,
0x74, 0x2e, 0x50, 0x61, 0x74, 0x63, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x42,
0x6f, 0x64, 0x79, 0x2e, 0x50, 0x61, 0x74, 0x63, 0x68, 0x52, 0x05, 0x70, 0x61, 0x74, 0x63, 0x68,
0x1a, 0x59, 0x0a, 0x05, 0x50, 0x61, 0x74, 0x63, 0x68, 0x12, 0x3a, 0x0a, 0x0c, 0x73, 0x6f, 0x75,
0x72, 0x63, 0x65, 0x5f, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32,
0x17, 0x2e, 0x6e, 0x65, 0x6f, 0x2e, 0x66, 0x73, 0x2e, 0x76, 0x32, 0x2e, 0x6f, 0x62, 0x6a, 0x65,
0x63, 0x74, 0x2e, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x0b, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65,
0x52, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x63, 0x68, 0x75, 0x6e, 0x6b, 0x18, 0x02,
0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x63, 0x68, 0x75, 0x6e, 0x6b, 0x22, 0xa4, 0x02, 0x0a, 0x0d,
0x50, 0x61, 0x74, 0x63, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x38, 0x0a,
0x04, 0x62, 0x6f, 0x64, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x6e, 0x65,
0x6f, 0x2e, 0x66, 0x73, 0x2e, 0x76, 0x32, 0x2e, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x2e, 0x50,
0x61, 0x74, 0x63, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x42, 0x6f, 0x64,
0x79, 0x52, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x12, 0x46, 0x0a, 0x0b, 0x6d, 0x65, 0x74, 0x61, 0x5f,
0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x6e,
0x65, 0x6f, 0x2e, 0x66, 0x73, 0x2e, 0x76, 0x32, 0x2e, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e,
0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x48, 0x65, 0x61,
0x64, 0x65, 0x72, 0x52, 0x0a, 0x6d, 0x65, 0x74, 0x61, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x12,
0x52, 0x0a, 0x0d, 0x76, 0x65, 0x72, 0x69, 0x66, 0x79, 0x5f, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72,
0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2d, 0x2e, 0x6e, 0x65, 0x6f, 0x2e, 0x66, 0x73, 0x2e,
0x48, 0x0a, 0x10, 0x6e, 0x65, 0x77, 0x5f, 0x73, 0x70, 0x6c, 0x69, 0x74, 0x5f, 0x68, 0x65, 0x61,
0x64, 0x65, 0x72, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x6e, 0x65, 0x6f, 0x2e,
0x66, 0x73, 0x2e, 0x76, 0x32, 0x2e, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x2e, 0x48, 0x65, 0x61,
0x64, 0x65, 0x72, 0x2e, 0x53, 0x70, 0x6c, 0x69, 0x74, 0x52, 0x0e, 0x6e, 0x65, 0x77, 0x53, 0x70,
0x6c, 0x69, 0x74, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x12, 0x3f, 0x0a, 0x05, 0x70, 0x61, 0x74,
0x63, 0x68, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x6e, 0x65, 0x6f, 0x2e, 0x66,
0x73, 0x2e, 0x76, 0x32, 0x2e, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x2e, 0x50, 0x61, 0x74, 0x63,
0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x42, 0x6f, 0x64, 0x79, 0x2e, 0x50, 0x61,
0x74, 0x63, 0x68, 0x52, 0x05, 0x70, 0x61, 0x74, 0x63, 0x68, 0x1a, 0x59, 0x0a, 0x05, 0x50, 0x61,
0x74, 0x63, 0x68, 0x12, 0x3a, 0x0a, 0x0c, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x72, 0x61,
0x6e, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x6e, 0x65, 0x6f, 0x2e,
0x66, 0x73, 0x2e, 0x76, 0x32, 0x2e, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x2e, 0x52, 0x61, 0x6e,
0x67, 0x65, 0x52, 0x0b, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x12,
0x14, 0x0a, 0x05, 0x63, 0x68, 0x75, 0x6e, 0x6b, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05,
0x63, 0x68, 0x75, 0x6e, 0x6b, 0x22, 0xa4, 0x02, 0x0a, 0x0d, 0x50, 0x61, 0x74, 0x63, 0x68, 0x52,
0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x38, 0x0a, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x18,
0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x6e, 0x65, 0x6f, 0x2e, 0x66, 0x73, 0x2e, 0x76,
0x32, 0x2e, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x2e, 0x50, 0x61, 0x74, 0x63, 0x68, 0x52, 0x65,
0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x42, 0x6f, 0x64, 0x79, 0x52, 0x04, 0x62, 0x6f, 0x64,
0x79, 0x12, 0x46, 0x0a, 0x0b, 0x6d, 0x65, 0x74, 0x61, 0x5f, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72,
0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x6e, 0x65, 0x6f, 0x2e, 0x66, 0x73, 0x2e,
0x76, 0x32, 0x2e, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f,
0x6e, 0x73, 0x65, 0x56, 0x65, 0x72, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x48,
0x65, 0x61, 0x64, 0x65, 0x72, 0x52, 0x0c, 0x76, 0x65, 0x72, 0x69, 0x66, 0x79, 0x48, 0x65, 0x61,
0x64, 0x65, 0x72, 0x1a, 0x3d, 0x0a, 0x04, 0x42, 0x6f, 0x64, 0x79, 0x12, 0x35, 0x0a, 0x09, 0x6f,
0x62, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18,
0x2e, 0x6e, 0x65, 0x6f, 0x2e, 0x66, 0x73, 0x2e, 0x76, 0x32, 0x2e, 0x72, 0x65, 0x66, 0x73, 0x2e,
0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x49, 0x44, 0x52, 0x08, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74,
0x49, 0x64, 0x32, 0xd4, 0x05, 0x0a, 0x0d, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x53, 0x65, 0x72,
0x76, 0x69, 0x63, 0x65, 0x12, 0x44, 0x0a, 0x03, 0x47, 0x65, 0x74, 0x12, 0x1c, 0x2e, 0x6e, 0x65,
0x6f, 0x2e, 0x66, 0x73, 0x2e, 0x76, 0x32, 0x2e, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x2e, 0x47,
0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x6e, 0x65, 0x6f, 0x2e,
0x66, 0x73, 0x2e, 0x76, 0x32, 0x2e, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x2e, 0x47, 0x65, 0x74,
0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x30, 0x01, 0x12, 0x44, 0x0a, 0x03, 0x50, 0x75,
0x74, 0x12, 0x1c, 0x2e, 0x6e, 0x65, 0x6f, 0x2e, 0x66, 0x73, 0x2e, 0x76, 0x32, 0x2e, 0x6f, 0x62,
0x6a, 0x65, 0x63, 0x74, 0x2e, 0x50, 0x75, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a,
0x6e, 0x73, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x52, 0x0a, 0x6d,
0x65, 0x74, 0x61, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x12, 0x52, 0x0a, 0x0d, 0x76, 0x65, 0x72,
0x69, 0x66, 0x79, 0x5f, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b,
0x32, 0x2d, 0x2e, 0x6e, 0x65, 0x6f, 0x2e, 0x66, 0x73, 0x2e, 0x76, 0x32, 0x2e, 0x73, 0x65, 0x73,
0x73, 0x69, 0x6f, 0x6e, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x56, 0x65, 0x72,
0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x52,
0x0c, 0x76, 0x65, 0x72, 0x69, 0x66, 0x79, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x1a, 0x3d, 0x0a,
0x04, 0x42, 0x6f, 0x64, 0x79, 0x12, 0x35, 0x0a, 0x09, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x5f,
0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x6e, 0x65, 0x6f, 0x2e, 0x66,
0x73, 0x2e, 0x76, 0x32, 0x2e, 0x72, 0x65, 0x66, 0x73, 0x2e, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74,
0x49, 0x44, 0x52, 0x08, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x49, 0x64, 0x32, 0xd4, 0x05, 0x0a,
0x0d, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x44,
0x0a, 0x03, 0x47, 0x65, 0x74, 0x12, 0x1c, 0x2e, 0x6e, 0x65, 0x6f, 0x2e, 0x66, 0x73, 0x2e, 0x76,
0x32, 0x2e, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75,
0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x6e, 0x65, 0x6f, 0x2e, 0x66, 0x73, 0x2e, 0x76, 0x32, 0x2e,
0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e,
0x73, 0x65, 0x30, 0x01, 0x12, 0x44, 0x0a, 0x03, 0x50, 0x75, 0x74, 0x12, 0x1c, 0x2e, 0x6e, 0x65,
0x6f, 0x2e, 0x66, 0x73, 0x2e, 0x76, 0x32, 0x2e, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x2e, 0x50,
0x75, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x6e, 0x65, 0x6f, 0x2e,
0x66, 0x73, 0x2e, 0x76, 0x32, 0x2e, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x2e, 0x50, 0x75, 0x74,
0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x28, 0x01, 0x12, 0x4b, 0x0a, 0x06, 0x44, 0x65,
0x6c, 0x65, 0x74, 0x65, 0x12, 0x1f, 0x2e, 0x6e, 0x65, 0x6f, 0x2e, 0x66, 0x73, 0x2e, 0x76, 0x32,
0x2e, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65,
0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x6e, 0x65, 0x6f, 0x2e, 0x66, 0x73, 0x2e, 0x76,
0x32, 0x2e, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52,
0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x45, 0x0a, 0x04, 0x48, 0x65, 0x61, 0x64, 0x12,
0x1d, 0x2e, 0x6e, 0x65, 0x6f, 0x2e, 0x66, 0x73, 0x2e, 0x76, 0x32, 0x2e, 0x6f, 0x62, 0x6a, 0x65,
0x63, 0x74, 0x2e, 0x50, 0x75, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x28, 0x01,
0x12, 0x4b, 0x0a, 0x06, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x12, 0x1f, 0x2e, 0x6e, 0x65, 0x6f,
0x2e, 0x66, 0x73, 0x2e, 0x76, 0x32, 0x2e, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x2e, 0x44, 0x65,
0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x6e, 0x65,
0x6f, 0x2e, 0x66, 0x73, 0x2e, 0x76, 0x32, 0x2e, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x2e, 0x44,
0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x45, 0x0a,
0x04, 0x48, 0x65, 0x61, 0x64, 0x12, 0x1d, 0x2e, 0x6e, 0x65, 0x6f, 0x2e, 0x66, 0x73, 0x2e, 0x76,
0x32, 0x2e, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x2e, 0x48, 0x65, 0x61, 0x64, 0x52, 0x65, 0x71,
0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x6e, 0x65, 0x6f, 0x2e, 0x66, 0x73, 0x2e, 0x76, 0x32,
0x2e, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x2e, 0x48, 0x65, 0x61, 0x64, 0x52, 0x65, 0x73, 0x70,
0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4d, 0x0a, 0x06, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x12, 0x1f,
0x63, 0x74, 0x2e, 0x48, 0x65, 0x61, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e,
0x2e, 0x6e, 0x65, 0x6f, 0x2e, 0x66, 0x73, 0x2e, 0x76, 0x32, 0x2e, 0x6f, 0x62, 0x6a, 0x65, 0x63,
0x74, 0x2e, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a,
0x20, 0x2e, 0x6e, 0x65, 0x6f, 0x2e, 0x66, 0x73, 0x2e, 0x76, 0x32, 0x2e, 0x6f, 0x62, 0x6a, 0x65,
0x63, 0x74, 0x2e, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73,
0x65, 0x30, 0x01, 0x12, 0x53, 0x0a, 0x08, 0x47, 0x65, 0x74, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x12,
0x21, 0x2e, 0x6e, 0x65, 0x6f, 0x2e, 0x66, 0x73, 0x2e, 0x76, 0x32, 0x2e, 0x6f, 0x62, 0x6a, 0x65,
0x63, 0x74, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65,
0x73, 0x74, 0x1a, 0x22, 0x2e, 0x6e, 0x65, 0x6f, 0x2e, 0x66, 0x73, 0x2e, 0x76, 0x32, 0x2e, 0x6f,
0x62, 0x6a, 0x65, 0x63, 0x74, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x65,
0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x30, 0x01, 0x12, 0x5d, 0x0a, 0x0c, 0x47, 0x65, 0x74, 0x52,
0x61, 0x6e, 0x67, 0x65, 0x48, 0x61, 0x73, 0x68, 0x12, 0x25, 0x2e, 0x6e, 0x65, 0x6f, 0x2e, 0x66,
0x73, 0x2e, 0x76, 0x32, 0x2e, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x2e, 0x47, 0x65, 0x74, 0x52,
0x61, 0x6e, 0x67, 0x65, 0x48, 0x61, 0x73, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a,
0x26, 0x2e, 0x6e, 0x65, 0x6f, 0x2e, 0x66, 0x73, 0x2e, 0x76, 0x32, 0x2e, 0x6f, 0x62, 0x6a, 0x65,
0x63, 0x74, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x48, 0x61, 0x73, 0x68, 0x52,
0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x54, 0x0a, 0x09, 0x50, 0x75, 0x74, 0x53, 0x69,
0x6e, 0x67, 0x6c, 0x65, 0x12, 0x22, 0x2e, 0x6e, 0x65, 0x6f, 0x2e, 0x66, 0x73, 0x2e, 0x76, 0x32,
0x2e, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x2e, 0x50, 0x75, 0x74, 0x53, 0x69, 0x6e, 0x67, 0x6c,
0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x6e, 0x65, 0x6f, 0x2e, 0x66,
0x73, 0x2e, 0x76, 0x32, 0x2e, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x2e, 0x50, 0x75, 0x74, 0x53,
0x69, 0x6e, 0x67, 0x6c, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4a, 0x0a,
0x05, 0x50, 0x61, 0x74, 0x63, 0x68, 0x12, 0x1e, 0x2e, 0x6e, 0x65, 0x6f, 0x2e, 0x66, 0x73, 0x2e,
0x76, 0x32, 0x2e, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x2e, 0x50, 0x61, 0x74, 0x63, 0x68, 0x52,
0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x6e, 0x65, 0x6f, 0x2e, 0x66, 0x73, 0x2e,
0x76, 0x32, 0x2e, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x2e, 0x50, 0x61, 0x74, 0x63, 0x68, 0x52,
0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x28, 0x01, 0x42, 0x62, 0x5a, 0x43, 0x67, 0x69, 0x74,
0x2e, 0x66, 0x72, 0x6f, 0x73, 0x74, 0x66, 0x73, 0x2e, 0x69, 0x6e, 0x66, 0x6f, 0x2f, 0x54, 0x72,
0x75, 0x65, 0x43, 0x6c, 0x6f, 0x75, 0x64, 0x4c, 0x61, 0x62, 0x2f, 0x66, 0x72, 0x6f, 0x73, 0x74,
0x66, 0x73, 0x2d, 0x73, 0x64, 0x6b, 0x2d, 0x67, 0x6f, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x6f, 0x62,
0x6a, 0x65, 0x63, 0x74, 0x2f, 0x67, 0x72, 0x70, 0x63, 0x3b, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74,
0xaa, 0x02, 0x1a, 0x4e, 0x65, 0x6f, 0x2e, 0x46, 0x69, 0x6c, 0x65, 0x53, 0x74, 0x6f, 0x72, 0x61,
0x67, 0x65, 0x2e, 0x41, 0x50, 0x49, 0x2e, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x62, 0x08, 0x65,
0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x70, 0xe8, 0x07,
0x74, 0x2e, 0x48, 0x65, 0x61, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4d,
0x0a, 0x06, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x12, 0x1f, 0x2e, 0x6e, 0x65, 0x6f, 0x2e, 0x66,
0x73, 0x2e, 0x76, 0x32, 0x2e, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x2e, 0x53, 0x65, 0x61, 0x72,
0x63, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x6e, 0x65, 0x6f, 0x2e,
0x66, 0x73, 0x2e, 0x76, 0x32, 0x2e, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x2e, 0x53, 0x65, 0x61,
0x72, 0x63, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x30, 0x01, 0x12, 0x53, 0x0a,
0x08, 0x47, 0x65, 0x74, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x21, 0x2e, 0x6e, 0x65, 0x6f, 0x2e,
0x66, 0x73, 0x2e, 0x76, 0x32, 0x2e, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x2e, 0x47, 0x65, 0x74,
0x52, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x6e,
0x65, 0x6f, 0x2e, 0x66, 0x73, 0x2e, 0x76, 0x32, 0x2e, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x2e,
0x47, 0x65, 0x74, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
0x30, 0x01, 0x12, 0x5d, 0x0a, 0x0c, 0x47, 0x65, 0x74, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x48, 0x61,
0x73, 0x68, 0x12, 0x25, 0x2e, 0x6e, 0x65, 0x6f, 0x2e, 0x66, 0x73, 0x2e, 0x76, 0x32, 0x2e, 0x6f,
0x62, 0x6a, 0x65, 0x63, 0x74, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x48, 0x61,
0x73, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x26, 0x2e, 0x6e, 0x65, 0x6f, 0x2e,
0x66, 0x73, 0x2e, 0x76, 0x32, 0x2e, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x2e, 0x47, 0x65, 0x74,
0x52, 0x61, 0x6e, 0x67, 0x65, 0x48, 0x61, 0x73, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73,
0x65, 0x12, 0x54, 0x0a, 0x09, 0x50, 0x75, 0x74, 0x53, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x12, 0x22,
0x2e, 0x6e, 0x65, 0x6f, 0x2e, 0x66, 0x73, 0x2e, 0x76, 0x32, 0x2e, 0x6f, 0x62, 0x6a, 0x65, 0x63,
0x74, 0x2e, 0x50, 0x75, 0x74, 0x53, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65,
0x73, 0x74, 0x1a, 0x23, 0x2e, 0x6e, 0x65, 0x6f, 0x2e, 0x66, 0x73, 0x2e, 0x76, 0x32, 0x2e, 0x6f,
0x62, 0x6a, 0x65, 0x63, 0x74, 0x2e, 0x50, 0x75, 0x74, 0x53, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x52,
0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4a, 0x0a, 0x05, 0x50, 0x61, 0x74, 0x63, 0x68,
0x12, 0x1e, 0x2e, 0x6e, 0x65, 0x6f, 0x2e, 0x66, 0x73, 0x2e, 0x76, 0x32, 0x2e, 0x6f, 0x62, 0x6a,
0x65, 0x63, 0x74, 0x2e, 0x50, 0x61, 0x74, 0x63, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
0x1a, 0x1f, 0x2e, 0x6e, 0x65, 0x6f, 0x2e, 0x66, 0x73, 0x2e, 0x76, 0x32, 0x2e, 0x6f, 0x62, 0x6a,
0x65, 0x63, 0x74, 0x2e, 0x50, 0x61, 0x74, 0x63, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73,
0x65, 0x28, 0x01, 0x42, 0x62, 0x5a, 0x43, 0x67, 0x69, 0x74, 0x2e, 0x66, 0x72, 0x6f, 0x73, 0x74,
0x66, 0x73, 0x2e, 0x69, 0x6e, 0x66, 0x6f, 0x2f, 0x54, 0x72, 0x75, 0x65, 0x43, 0x6c, 0x6f, 0x75,
0x64, 0x4c, 0x61, 0x62, 0x2f, 0x66, 0x72, 0x6f, 0x73, 0x74, 0x66, 0x73, 0x2d, 0x73, 0x64, 0x6b,
0x2d, 0x67, 0x6f, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x67,
0x72, 0x70, 0x63, 0x3b, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0xaa, 0x02, 0x1a, 0x4e, 0x65, 0x6f,
0x2e, 0x46, 0x69, 0x6c, 0x65, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x41, 0x50, 0x49,
0x2e, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x62, 0x08, 0x65, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e,
0x73, 0x70, 0xe8, 0x07,
}
var file_api_object_grpc_service_proto_msgTypes = make([]protoimpl.MessageInfo, 42)
@ -6071,6 +6105,7 @@ var file_api_object_grpc_service_proto_goTypes = []any{
(grpc1.ChecksumType)(0), // 55: neo.fs.v2.refs.ChecksumType
(*Object)(nil), // 56: neo.fs.v2.object.Object
(*Header_Attribute)(nil), // 57: neo.fs.v2.object.Header.Attribute
(*Header_Split)(nil), // 58: neo.fs.v2.object.Header.Split
}
var file_api_object_grpc_service_proto_depIdxs = []int32{
20, // 0: neo.fs.v2.object.GetRequest.body:type_name -> neo.fs.v2.object.GetRequest.Body
@ -6163,32 +6198,33 @@ var file_api_object_grpc_service_proto_depIdxs = []int32{
56, // 87: neo.fs.v2.object.PutSingleRequest.Body.object:type_name -> neo.fs.v2.object.Object
48, // 88: neo.fs.v2.object.PatchRequest.Body.address:type_name -> neo.fs.v2.refs.Address
57, // 89: neo.fs.v2.object.PatchRequest.Body.new_attributes:type_name -> neo.fs.v2.object.Header.Attribute
40, // 90: neo.fs.v2.object.PatchRequest.Body.patch:type_name -> neo.fs.v2.object.PatchRequest.Body.Patch
11, // 91: neo.fs.v2.object.PatchRequest.Body.Patch.source_range:type_name -> neo.fs.v2.object.Range
51, // 92: neo.fs.v2.object.PatchResponse.Body.object_id:type_name -> neo.fs.v2.refs.ObjectID
0, // 93: neo.fs.v2.object.ObjectService.Get:input_type -> neo.fs.v2.object.GetRequest
2, // 94: neo.fs.v2.object.ObjectService.Put:input_type -> neo.fs.v2.object.PutRequest
4, // 95: neo.fs.v2.object.ObjectService.Delete:input_type -> neo.fs.v2.object.DeleteRequest
6, // 96: neo.fs.v2.object.ObjectService.Head:input_type -> neo.fs.v2.object.HeadRequest
9, // 97: neo.fs.v2.object.ObjectService.Search:input_type -> neo.fs.v2.object.SearchRequest
12, // 98: neo.fs.v2.object.ObjectService.GetRange:input_type -> neo.fs.v2.object.GetRangeRequest
14, // 99: neo.fs.v2.object.ObjectService.GetRangeHash:input_type -> neo.fs.v2.object.GetRangeHashRequest
16, // 100: neo.fs.v2.object.ObjectService.PutSingle:input_type -> neo.fs.v2.object.PutSingleRequest
18, // 101: neo.fs.v2.object.ObjectService.Patch:input_type -> neo.fs.v2.object.PatchRequest
1, // 102: neo.fs.v2.object.ObjectService.Get:output_type -> neo.fs.v2.object.GetResponse
3, // 103: neo.fs.v2.object.ObjectService.Put:output_type -> neo.fs.v2.object.PutResponse
5, // 104: neo.fs.v2.object.ObjectService.Delete:output_type -> neo.fs.v2.object.DeleteResponse
8, // 105: neo.fs.v2.object.ObjectService.Head:output_type -> neo.fs.v2.object.HeadResponse
10, // 106: neo.fs.v2.object.ObjectService.Search:output_type -> neo.fs.v2.object.SearchResponse
13, // 107: neo.fs.v2.object.ObjectService.GetRange:output_type -> neo.fs.v2.object.GetRangeResponse
15, // 108: neo.fs.v2.object.ObjectService.GetRangeHash:output_type -> neo.fs.v2.object.GetRangeHashResponse
17, // 109: neo.fs.v2.object.ObjectService.PutSingle:output_type -> neo.fs.v2.object.PutSingleResponse
19, // 110: neo.fs.v2.object.ObjectService.Patch:output_type -> neo.fs.v2.object.PatchResponse
102, // [102:111] is the sub-list for method output_type
93, // [93:102] is the sub-list for method input_type
93, // [93:93] is the sub-list for extension type_name
93, // [93:93] is the sub-list for extension extendee
0, // [0:93] is the sub-list for field type_name
58, // 90: neo.fs.v2.object.PatchRequest.Body.new_split_header:type_name -> neo.fs.v2.object.Header.Split
40, // 91: neo.fs.v2.object.PatchRequest.Body.patch:type_name -> neo.fs.v2.object.PatchRequest.Body.Patch
11, // 92: neo.fs.v2.object.PatchRequest.Body.Patch.source_range:type_name -> neo.fs.v2.object.Range
51, // 93: neo.fs.v2.object.PatchResponse.Body.object_id:type_name -> neo.fs.v2.refs.ObjectID
0, // 94: neo.fs.v2.object.ObjectService.Get:input_type -> neo.fs.v2.object.GetRequest
2, // 95: neo.fs.v2.object.ObjectService.Put:input_type -> neo.fs.v2.object.PutRequest
4, // 96: neo.fs.v2.object.ObjectService.Delete:input_type -> neo.fs.v2.object.DeleteRequest
6, // 97: neo.fs.v2.object.ObjectService.Head:input_type -> neo.fs.v2.object.HeadRequest
9, // 98: neo.fs.v2.object.ObjectService.Search:input_type -> neo.fs.v2.object.SearchRequest
12, // 99: neo.fs.v2.object.ObjectService.GetRange:input_type -> neo.fs.v2.object.GetRangeRequest
14, // 100: neo.fs.v2.object.ObjectService.GetRangeHash:input_type -> neo.fs.v2.object.GetRangeHashRequest
16, // 101: neo.fs.v2.object.ObjectService.PutSingle:input_type -> neo.fs.v2.object.PutSingleRequest
18, // 102: neo.fs.v2.object.ObjectService.Patch:input_type -> neo.fs.v2.object.PatchRequest
1, // 103: neo.fs.v2.object.ObjectService.Get:output_type -> neo.fs.v2.object.GetResponse
3, // 104: neo.fs.v2.object.ObjectService.Put:output_type -> neo.fs.v2.object.PutResponse
5, // 105: neo.fs.v2.object.ObjectService.Delete:output_type -> neo.fs.v2.object.DeleteResponse
8, // 106: neo.fs.v2.object.ObjectService.Head:output_type -> neo.fs.v2.object.HeadResponse
10, // 107: neo.fs.v2.object.ObjectService.Search:output_type -> neo.fs.v2.object.SearchResponse
13, // 108: neo.fs.v2.object.ObjectService.GetRange:output_type -> neo.fs.v2.object.GetRangeResponse
15, // 109: neo.fs.v2.object.ObjectService.GetRangeHash:output_type -> neo.fs.v2.object.GetRangeHashResponse
17, // 110: neo.fs.v2.object.ObjectService.PutSingle:output_type -> neo.fs.v2.object.PutSingleResponse
19, // 111: neo.fs.v2.object.ObjectService.Patch:output_type -> neo.fs.v2.object.PatchResponse
103, // [103:112] is the sub-list for method output_type
94, // [94:103] is the sub-list for method input_type
94, // [94:94] is the sub-list for extension type_name
94, // [94:94] is the sub-list for extension extendee
0, // [0:94] is the sub-list for field type_name
}
func init() { file_api_object_grpc_service_proto_init() }

View file

@ -5050,6 +5050,7 @@ type PatchRequest_Body struct {
xxx_hidden_Address *grpc1.Address `protobuf:"bytes,1,opt,name=address" json:"address,omitempty"`
xxx_hidden_NewAttributes *[]*Header_Attribute `protobuf:"bytes,2,rep,name=new_attributes,json=newAttributes" json:"new_attributes,omitempty"`
xxx_hidden_ReplaceAttributes bool `protobuf:"varint,3,opt,name=replace_attributes,json=replaceAttributes" json:"replace_attributes,omitempty"`
xxx_hidden_NewSplitHeader *Header_Split `protobuf:"bytes,5,opt,name=new_split_header,json=newSplitHeader" json:"new_split_header,omitempty"`
xxx_hidden_Patch *PatchRequest_Body_Patch `protobuf:"bytes,4,opt,name=patch" json:"patch,omitempty"`
XXX_raceDetectHookData protoimpl.RaceDetectHookData
XXX_presence [1]uint32
@ -5105,6 +5106,13 @@ func (x *PatchRequest_Body) GetReplaceAttributes() bool {
return false
}
func (x *PatchRequest_Body) GetNewSplitHeader() *Header_Split {
if x != nil {
return x.xxx_hidden_NewSplitHeader
}
return nil
}
func (x *PatchRequest_Body) GetPatch() *PatchRequest_Body_Patch {
if x != nil {
return x.xxx_hidden_Patch
@ -5122,7 +5130,11 @@ func (x *PatchRequest_Body) SetNewAttributes(v []*Header_Attribute) {
func (x *PatchRequest_Body) SetReplaceAttributes(v bool) {
x.xxx_hidden_ReplaceAttributes = v
protoimpl.X.SetPresent(&(x.XXX_presence[0]), 2, 4)
protoimpl.X.SetPresent(&(x.XXX_presence[0]), 2, 5)
}
func (x *PatchRequest_Body) SetNewSplitHeader(v *Header_Split) {
x.xxx_hidden_NewSplitHeader = v
}
func (x *PatchRequest_Body) SetPatch(v *PatchRequest_Body_Patch) {
@ -5143,6 +5155,13 @@ func (x *PatchRequest_Body) HasReplaceAttributes() bool {
return protoimpl.X.Present(&(x.XXX_presence[0]), 2)
}
func (x *PatchRequest_Body) HasNewSplitHeader() bool {
if x == nil {
return false
}
return x.xxx_hidden_NewSplitHeader != nil
}
func (x *PatchRequest_Body) HasPatch() bool {
if x == nil {
return false
@ -5159,6 +5178,10 @@ func (x *PatchRequest_Body) ClearReplaceAttributes() {
x.xxx_hidden_ReplaceAttributes = false
}
func (x *PatchRequest_Body) ClearNewSplitHeader() {
x.xxx_hidden_NewSplitHeader = nil
}
func (x *PatchRequest_Body) ClearPatch() {
x.xxx_hidden_Patch = nil
}
@ -5179,6 +5202,9 @@ type PatchRequest_Body_builder struct {
// merged. If the incoming `new_attributes` list contains already existing
// key, then it just replaces it while merging the lists.
ReplaceAttributes *bool
// New split header for the object. This defines how the object will relate
// to other objects in a split operation.
NewSplitHeader *Header_Split
// The patch that is applied for the object.
Patch *PatchRequest_Body_Patch
}
@ -5190,9 +5216,10 @@ func (b0 PatchRequest_Body_builder) Build() *PatchRequest_Body {
x.xxx_hidden_Address = b.Address
x.xxx_hidden_NewAttributes = &b.NewAttributes
if b.ReplaceAttributes != nil {
protoimpl.X.SetPresentNonAtomic(&(x.XXX_presence[0]), 2, 4)
protoimpl.X.SetPresentNonAtomic(&(x.XXX_presence[0]), 2, 5)
x.xxx_hidden_ReplaceAttributes = *b.ReplaceAttributes
}
x.xxx_hidden_NewSplitHeader = b.NewSplitHeader
x.xxx_hidden_Patch = b.Patch
return m0
}
@ -5779,7 +5806,7 @@ var file_api_object_grpc_service_proto_rawDesc = []byte{
0x73, 0x69, 0x6f, 0x6e, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x56, 0x65, 0x72,
0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x52,
0x0c, 0x76, 0x65, 0x72, 0x69, 0x66, 0x79, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x1a, 0x06, 0x0a,
0x04, 0x42, 0x6f, 0x64, 0x79, 0x22, 0xb3, 0x04, 0x0a, 0x0c, 0x50, 0x61, 0x74, 0x63, 0x68, 0x52,
0x04, 0x42, 0x6f, 0x64, 0x79, 0x22, 0xfd, 0x04, 0x0a, 0x0c, 0x50, 0x61, 0x74, 0x63, 0x68, 0x52,
0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x37, 0x0a, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x18, 0x01,
0x20, 0x01, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x6e, 0x65, 0x6f, 0x2e, 0x66, 0x73, 0x2e, 0x76, 0x32,
0x2e, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x2e, 0x50, 0x61, 0x74, 0x63, 0x68, 0x52, 0x65, 0x71,
@ -5793,7 +5820,7 @@ var file_api_object_grpc_service_proto_rawDesc = []byte{
0x6e, 0x65, 0x6f, 0x2e, 0x66, 0x73, 0x2e, 0x76, 0x32, 0x2e, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f,
0x6e, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x56, 0x65, 0x72, 0x69, 0x66, 0x69, 0x63,
0x61, 0x74, 0x69, 0x6f, 0x6e, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x52, 0x0c, 0x76, 0x65, 0x72,
0x69, 0x66, 0x79, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x1a, 0xcf, 0x02, 0x0a, 0x04, 0x42, 0x6f,
0x69, 0x66, 0x79, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x1a, 0x99, 0x03, 0x0a, 0x04, 0x42, 0x6f,
0x64, 0x79, 0x12, 0x31, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20,
0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x6e, 0x65, 0x6f, 0x2e, 0x66, 0x73, 0x2e, 0x76, 0x32, 0x2e,
0x72, 0x65, 0x66, 0x73, 0x2e, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x52, 0x07, 0x61, 0x64,
@ -5805,87 +5832,92 @@ var file_api_object_grpc_service_proto_rawDesc = []byte{
0x12, 0x2d, 0x0a, 0x12, 0x72, 0x65, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x5f, 0x61, 0x74, 0x74, 0x72,
0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x11, 0x72, 0x65,
0x70, 0x6c, 0x61, 0x63, 0x65, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x12,
0x3f, 0x0a, 0x05, 0x70, 0x61, 0x74, 0x63, 0x68, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x29,
0x2e, 0x6e, 0x65, 0x6f, 0x2e, 0x66, 0x73, 0x2e, 0x76, 0x32, 0x2e, 0x6f, 0x62, 0x6a, 0x65, 0x63,
0x74, 0x2e, 0x50, 0x61, 0x74, 0x63, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x42,
0x6f, 0x64, 0x79, 0x2e, 0x50, 0x61, 0x74, 0x63, 0x68, 0x52, 0x05, 0x70, 0x61, 0x74, 0x63, 0x68,
0x1a, 0x59, 0x0a, 0x05, 0x50, 0x61, 0x74, 0x63, 0x68, 0x12, 0x3a, 0x0a, 0x0c, 0x73, 0x6f, 0x75,
0x72, 0x63, 0x65, 0x5f, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32,
0x17, 0x2e, 0x6e, 0x65, 0x6f, 0x2e, 0x66, 0x73, 0x2e, 0x76, 0x32, 0x2e, 0x6f, 0x62, 0x6a, 0x65,
0x63, 0x74, 0x2e, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x0b, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65,
0x52, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x63, 0x68, 0x75, 0x6e, 0x6b, 0x18, 0x02,
0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x63, 0x68, 0x75, 0x6e, 0x6b, 0x22, 0xa4, 0x02, 0x0a, 0x0d,
0x50, 0x61, 0x74, 0x63, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x38, 0x0a,
0x04, 0x62, 0x6f, 0x64, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x6e, 0x65,
0x6f, 0x2e, 0x66, 0x73, 0x2e, 0x76, 0x32, 0x2e, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x2e, 0x50,
0x61, 0x74, 0x63, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x42, 0x6f, 0x64,
0x79, 0x52, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x12, 0x46, 0x0a, 0x0b, 0x6d, 0x65, 0x74, 0x61, 0x5f,
0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x6e,
0x65, 0x6f, 0x2e, 0x66, 0x73, 0x2e, 0x76, 0x32, 0x2e, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e,
0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x48, 0x65, 0x61,
0x64, 0x65, 0x72, 0x52, 0x0a, 0x6d, 0x65, 0x74, 0x61, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x12,
0x52, 0x0a, 0x0d, 0x76, 0x65, 0x72, 0x69, 0x66, 0x79, 0x5f, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72,
0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2d, 0x2e, 0x6e, 0x65, 0x6f, 0x2e, 0x66, 0x73, 0x2e,
0x48, 0x0a, 0x10, 0x6e, 0x65, 0x77, 0x5f, 0x73, 0x70, 0x6c, 0x69, 0x74, 0x5f, 0x68, 0x65, 0x61,
0x64, 0x65, 0x72, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x6e, 0x65, 0x6f, 0x2e,
0x66, 0x73, 0x2e, 0x76, 0x32, 0x2e, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x2e, 0x48, 0x65, 0x61,
0x64, 0x65, 0x72, 0x2e, 0x53, 0x70, 0x6c, 0x69, 0x74, 0x52, 0x0e, 0x6e, 0x65, 0x77, 0x53, 0x70,
0x6c, 0x69, 0x74, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x12, 0x3f, 0x0a, 0x05, 0x70, 0x61, 0x74,
0x63, 0x68, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x6e, 0x65, 0x6f, 0x2e, 0x66,
0x73, 0x2e, 0x76, 0x32, 0x2e, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x2e, 0x50, 0x61, 0x74, 0x63,
0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x42, 0x6f, 0x64, 0x79, 0x2e, 0x50, 0x61,
0x74, 0x63, 0x68, 0x52, 0x05, 0x70, 0x61, 0x74, 0x63, 0x68, 0x1a, 0x59, 0x0a, 0x05, 0x50, 0x61,
0x74, 0x63, 0x68, 0x12, 0x3a, 0x0a, 0x0c, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x72, 0x61,
0x6e, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x6e, 0x65, 0x6f, 0x2e,
0x66, 0x73, 0x2e, 0x76, 0x32, 0x2e, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x2e, 0x52, 0x61, 0x6e,
0x67, 0x65, 0x52, 0x0b, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x12,
0x14, 0x0a, 0x05, 0x63, 0x68, 0x75, 0x6e, 0x6b, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05,
0x63, 0x68, 0x75, 0x6e, 0x6b, 0x22, 0xa4, 0x02, 0x0a, 0x0d, 0x50, 0x61, 0x74, 0x63, 0x68, 0x52,
0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x38, 0x0a, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x18,
0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x6e, 0x65, 0x6f, 0x2e, 0x66, 0x73, 0x2e, 0x76,
0x32, 0x2e, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x2e, 0x50, 0x61, 0x74, 0x63, 0x68, 0x52, 0x65,
0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x42, 0x6f, 0x64, 0x79, 0x52, 0x04, 0x62, 0x6f, 0x64,
0x79, 0x12, 0x46, 0x0a, 0x0b, 0x6d, 0x65, 0x74, 0x61, 0x5f, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72,
0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x6e, 0x65, 0x6f, 0x2e, 0x66, 0x73, 0x2e,
0x76, 0x32, 0x2e, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f,
0x6e, 0x73, 0x65, 0x56, 0x65, 0x72, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x48,
0x65, 0x61, 0x64, 0x65, 0x72, 0x52, 0x0c, 0x76, 0x65, 0x72, 0x69, 0x66, 0x79, 0x48, 0x65, 0x61,
0x64, 0x65, 0x72, 0x1a, 0x3d, 0x0a, 0x04, 0x42, 0x6f, 0x64, 0x79, 0x12, 0x35, 0x0a, 0x09, 0x6f,
0x62, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18,
0x2e, 0x6e, 0x65, 0x6f, 0x2e, 0x66, 0x73, 0x2e, 0x76, 0x32, 0x2e, 0x72, 0x65, 0x66, 0x73, 0x2e,
0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x49, 0x44, 0x52, 0x08, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74,
0x49, 0x64, 0x32, 0xd4, 0x05, 0x0a, 0x0d, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x53, 0x65, 0x72,
0x76, 0x69, 0x63, 0x65, 0x12, 0x44, 0x0a, 0x03, 0x47, 0x65, 0x74, 0x12, 0x1c, 0x2e, 0x6e, 0x65,
0x6f, 0x2e, 0x66, 0x73, 0x2e, 0x76, 0x32, 0x2e, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x2e, 0x47,
0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x6e, 0x65, 0x6f, 0x2e,
0x66, 0x73, 0x2e, 0x76, 0x32, 0x2e, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x2e, 0x47, 0x65, 0x74,
0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x30, 0x01, 0x12, 0x44, 0x0a, 0x03, 0x50, 0x75,
0x74, 0x12, 0x1c, 0x2e, 0x6e, 0x65, 0x6f, 0x2e, 0x66, 0x73, 0x2e, 0x76, 0x32, 0x2e, 0x6f, 0x62,
0x6a, 0x65, 0x63, 0x74, 0x2e, 0x50, 0x75, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a,
0x6e, 0x73, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x52, 0x0a, 0x6d,
0x65, 0x74, 0x61, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x12, 0x52, 0x0a, 0x0d, 0x76, 0x65, 0x72,
0x69, 0x66, 0x79, 0x5f, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b,
0x32, 0x2d, 0x2e, 0x6e, 0x65, 0x6f, 0x2e, 0x66, 0x73, 0x2e, 0x76, 0x32, 0x2e, 0x73, 0x65, 0x73,
0x73, 0x69, 0x6f, 0x6e, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x56, 0x65, 0x72,
0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x52,
0x0c, 0x76, 0x65, 0x72, 0x69, 0x66, 0x79, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x1a, 0x3d, 0x0a,
0x04, 0x42, 0x6f, 0x64, 0x79, 0x12, 0x35, 0x0a, 0x09, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x5f,
0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x6e, 0x65, 0x6f, 0x2e, 0x66,
0x73, 0x2e, 0x76, 0x32, 0x2e, 0x72, 0x65, 0x66, 0x73, 0x2e, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74,
0x49, 0x44, 0x52, 0x08, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x49, 0x64, 0x32, 0xd4, 0x05, 0x0a,
0x0d, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x44,
0x0a, 0x03, 0x47, 0x65, 0x74, 0x12, 0x1c, 0x2e, 0x6e, 0x65, 0x6f, 0x2e, 0x66, 0x73, 0x2e, 0x76,
0x32, 0x2e, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75,
0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x6e, 0x65, 0x6f, 0x2e, 0x66, 0x73, 0x2e, 0x76, 0x32, 0x2e,
0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e,
0x73, 0x65, 0x30, 0x01, 0x12, 0x44, 0x0a, 0x03, 0x50, 0x75, 0x74, 0x12, 0x1c, 0x2e, 0x6e, 0x65,
0x6f, 0x2e, 0x66, 0x73, 0x2e, 0x76, 0x32, 0x2e, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x2e, 0x50,
0x75, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x6e, 0x65, 0x6f, 0x2e,
0x66, 0x73, 0x2e, 0x76, 0x32, 0x2e, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x2e, 0x50, 0x75, 0x74,
0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x28, 0x01, 0x12, 0x4b, 0x0a, 0x06, 0x44, 0x65,
0x6c, 0x65, 0x74, 0x65, 0x12, 0x1f, 0x2e, 0x6e, 0x65, 0x6f, 0x2e, 0x66, 0x73, 0x2e, 0x76, 0x32,
0x2e, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65,
0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x6e, 0x65, 0x6f, 0x2e, 0x66, 0x73, 0x2e, 0x76,
0x32, 0x2e, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52,
0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x45, 0x0a, 0x04, 0x48, 0x65, 0x61, 0x64, 0x12,
0x1d, 0x2e, 0x6e, 0x65, 0x6f, 0x2e, 0x66, 0x73, 0x2e, 0x76, 0x32, 0x2e, 0x6f, 0x62, 0x6a, 0x65,
0x63, 0x74, 0x2e, 0x50, 0x75, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x28, 0x01,
0x12, 0x4b, 0x0a, 0x06, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x12, 0x1f, 0x2e, 0x6e, 0x65, 0x6f,
0x2e, 0x66, 0x73, 0x2e, 0x76, 0x32, 0x2e, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x2e, 0x44, 0x65,
0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x6e, 0x65,
0x6f, 0x2e, 0x66, 0x73, 0x2e, 0x76, 0x32, 0x2e, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x2e, 0x44,
0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x45, 0x0a,
0x04, 0x48, 0x65, 0x61, 0x64, 0x12, 0x1d, 0x2e, 0x6e, 0x65, 0x6f, 0x2e, 0x66, 0x73, 0x2e, 0x76,
0x32, 0x2e, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x2e, 0x48, 0x65, 0x61, 0x64, 0x52, 0x65, 0x71,
0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x6e, 0x65, 0x6f, 0x2e, 0x66, 0x73, 0x2e, 0x76, 0x32,
0x2e, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x2e, 0x48, 0x65, 0x61, 0x64, 0x52, 0x65, 0x73, 0x70,
0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4d, 0x0a, 0x06, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x12, 0x1f,
0x63, 0x74, 0x2e, 0x48, 0x65, 0x61, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e,
0x2e, 0x6e, 0x65, 0x6f, 0x2e, 0x66, 0x73, 0x2e, 0x76, 0x32, 0x2e, 0x6f, 0x62, 0x6a, 0x65, 0x63,
0x74, 0x2e, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a,
0x20, 0x2e, 0x6e, 0x65, 0x6f, 0x2e, 0x66, 0x73, 0x2e, 0x76, 0x32, 0x2e, 0x6f, 0x62, 0x6a, 0x65,
0x63, 0x74, 0x2e, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73,
0x65, 0x30, 0x01, 0x12, 0x53, 0x0a, 0x08, 0x47, 0x65, 0x74, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x12,
0x21, 0x2e, 0x6e, 0x65, 0x6f, 0x2e, 0x66, 0x73, 0x2e, 0x76, 0x32, 0x2e, 0x6f, 0x62, 0x6a, 0x65,
0x63, 0x74, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65,
0x73, 0x74, 0x1a, 0x22, 0x2e, 0x6e, 0x65, 0x6f, 0x2e, 0x66, 0x73, 0x2e, 0x76, 0x32, 0x2e, 0x6f,
0x62, 0x6a, 0x65, 0x63, 0x74, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x65,
0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x30, 0x01, 0x12, 0x5d, 0x0a, 0x0c, 0x47, 0x65, 0x74, 0x52,
0x61, 0x6e, 0x67, 0x65, 0x48, 0x61, 0x73, 0x68, 0x12, 0x25, 0x2e, 0x6e, 0x65, 0x6f, 0x2e, 0x66,
0x73, 0x2e, 0x76, 0x32, 0x2e, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x2e, 0x47, 0x65, 0x74, 0x52,
0x61, 0x6e, 0x67, 0x65, 0x48, 0x61, 0x73, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a,
0x26, 0x2e, 0x6e, 0x65, 0x6f, 0x2e, 0x66, 0x73, 0x2e, 0x76, 0x32, 0x2e, 0x6f, 0x62, 0x6a, 0x65,
0x63, 0x74, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x48, 0x61, 0x73, 0x68, 0x52,
0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x54, 0x0a, 0x09, 0x50, 0x75, 0x74, 0x53, 0x69,
0x6e, 0x67, 0x6c, 0x65, 0x12, 0x22, 0x2e, 0x6e, 0x65, 0x6f, 0x2e, 0x66, 0x73, 0x2e, 0x76, 0x32,
0x2e, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x2e, 0x50, 0x75, 0x74, 0x53, 0x69, 0x6e, 0x67, 0x6c,
0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x6e, 0x65, 0x6f, 0x2e, 0x66,
0x73, 0x2e, 0x76, 0x32, 0x2e, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x2e, 0x50, 0x75, 0x74, 0x53,
0x69, 0x6e, 0x67, 0x6c, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4a, 0x0a,
0x05, 0x50, 0x61, 0x74, 0x63, 0x68, 0x12, 0x1e, 0x2e, 0x6e, 0x65, 0x6f, 0x2e, 0x66, 0x73, 0x2e,
0x76, 0x32, 0x2e, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x2e, 0x50, 0x61, 0x74, 0x63, 0x68, 0x52,
0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x6e, 0x65, 0x6f, 0x2e, 0x66, 0x73, 0x2e,
0x76, 0x32, 0x2e, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x2e, 0x50, 0x61, 0x74, 0x63, 0x68, 0x52,
0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x28, 0x01, 0x42, 0x62, 0x5a, 0x43, 0x67, 0x69, 0x74,
0x2e, 0x66, 0x72, 0x6f, 0x73, 0x74, 0x66, 0x73, 0x2e, 0x69, 0x6e, 0x66, 0x6f, 0x2f, 0x54, 0x72,
0x75, 0x65, 0x43, 0x6c, 0x6f, 0x75, 0x64, 0x4c, 0x61, 0x62, 0x2f, 0x66, 0x72, 0x6f, 0x73, 0x74,
0x66, 0x73, 0x2d, 0x73, 0x64, 0x6b, 0x2d, 0x67, 0x6f, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x6f, 0x62,
0x6a, 0x65, 0x63, 0x74, 0x2f, 0x67, 0x72, 0x70, 0x63, 0x3b, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74,
0xaa, 0x02, 0x1a, 0x4e, 0x65, 0x6f, 0x2e, 0x46, 0x69, 0x6c, 0x65, 0x53, 0x74, 0x6f, 0x72, 0x61,
0x67, 0x65, 0x2e, 0x41, 0x50, 0x49, 0x2e, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x62, 0x08, 0x65,
0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x70, 0xe8, 0x07,
0x74, 0x2e, 0x48, 0x65, 0x61, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4d,
0x0a, 0x06, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x12, 0x1f, 0x2e, 0x6e, 0x65, 0x6f, 0x2e, 0x66,
0x73, 0x2e, 0x76, 0x32, 0x2e, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x2e, 0x53, 0x65, 0x61, 0x72,
0x63, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x6e, 0x65, 0x6f, 0x2e,
0x66, 0x73, 0x2e, 0x76, 0x32, 0x2e, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x2e, 0x53, 0x65, 0x61,
0x72, 0x63, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x30, 0x01, 0x12, 0x53, 0x0a,
0x08, 0x47, 0x65, 0x74, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x21, 0x2e, 0x6e, 0x65, 0x6f, 0x2e,
0x66, 0x73, 0x2e, 0x76, 0x32, 0x2e, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x2e, 0x47, 0x65, 0x74,
0x52, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x6e,
0x65, 0x6f, 0x2e, 0x66, 0x73, 0x2e, 0x76, 0x32, 0x2e, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x2e,
0x47, 0x65, 0x74, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
0x30, 0x01, 0x12, 0x5d, 0x0a, 0x0c, 0x47, 0x65, 0x74, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x48, 0x61,
0x73, 0x68, 0x12, 0x25, 0x2e, 0x6e, 0x65, 0x6f, 0x2e, 0x66, 0x73, 0x2e, 0x76, 0x32, 0x2e, 0x6f,
0x62, 0x6a, 0x65, 0x63, 0x74, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x48, 0x61,
0x73, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x26, 0x2e, 0x6e, 0x65, 0x6f, 0x2e,
0x66, 0x73, 0x2e, 0x76, 0x32, 0x2e, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x2e, 0x47, 0x65, 0x74,
0x52, 0x61, 0x6e, 0x67, 0x65, 0x48, 0x61, 0x73, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73,
0x65, 0x12, 0x54, 0x0a, 0x09, 0x50, 0x75, 0x74, 0x53, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x12, 0x22,
0x2e, 0x6e, 0x65, 0x6f, 0x2e, 0x66, 0x73, 0x2e, 0x76, 0x32, 0x2e, 0x6f, 0x62, 0x6a, 0x65, 0x63,
0x74, 0x2e, 0x50, 0x75, 0x74, 0x53, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65,
0x73, 0x74, 0x1a, 0x23, 0x2e, 0x6e, 0x65, 0x6f, 0x2e, 0x66, 0x73, 0x2e, 0x76, 0x32, 0x2e, 0x6f,
0x62, 0x6a, 0x65, 0x63, 0x74, 0x2e, 0x50, 0x75, 0x74, 0x53, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x52,
0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4a, 0x0a, 0x05, 0x50, 0x61, 0x74, 0x63, 0x68,
0x12, 0x1e, 0x2e, 0x6e, 0x65, 0x6f, 0x2e, 0x66, 0x73, 0x2e, 0x76, 0x32, 0x2e, 0x6f, 0x62, 0x6a,
0x65, 0x63, 0x74, 0x2e, 0x50, 0x61, 0x74, 0x63, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
0x1a, 0x1f, 0x2e, 0x6e, 0x65, 0x6f, 0x2e, 0x66, 0x73, 0x2e, 0x76, 0x32, 0x2e, 0x6f, 0x62, 0x6a,
0x65, 0x63, 0x74, 0x2e, 0x50, 0x61, 0x74, 0x63, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73,
0x65, 0x28, 0x01, 0x42, 0x62, 0x5a, 0x43, 0x67, 0x69, 0x74, 0x2e, 0x66, 0x72, 0x6f, 0x73, 0x74,
0x66, 0x73, 0x2e, 0x69, 0x6e, 0x66, 0x6f, 0x2f, 0x54, 0x72, 0x75, 0x65, 0x43, 0x6c, 0x6f, 0x75,
0x64, 0x4c, 0x61, 0x62, 0x2f, 0x66, 0x72, 0x6f, 0x73, 0x74, 0x66, 0x73, 0x2d, 0x73, 0x64, 0x6b,
0x2d, 0x67, 0x6f, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x67,
0x72, 0x70, 0x63, 0x3b, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0xaa, 0x02, 0x1a, 0x4e, 0x65, 0x6f,
0x2e, 0x46, 0x69, 0x6c, 0x65, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x41, 0x50, 0x49,
0x2e, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x62, 0x08, 0x65, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e,
0x73, 0x70, 0xe8, 0x07,
}
var file_api_object_grpc_service_proto_msgTypes = make([]protoimpl.MessageInfo, 42)
@ -5948,6 +5980,7 @@ var file_api_object_grpc_service_proto_goTypes = []any{
(grpc1.ChecksumType)(0), // 55: neo.fs.v2.refs.ChecksumType
(*Object)(nil), // 56: neo.fs.v2.object.Object
(*Header_Attribute)(nil), // 57: neo.fs.v2.object.Header.Attribute
(*Header_Split)(nil), // 58: neo.fs.v2.object.Header.Split
}
var file_api_object_grpc_service_proto_depIdxs = []int32{
20, // 0: neo.fs.v2.object.GetRequest.body:type_name -> neo.fs.v2.object.GetRequest.Body
@ -6040,32 +6073,33 @@ var file_api_object_grpc_service_proto_depIdxs = []int32{
56, // 87: neo.fs.v2.object.PutSingleRequest.Body.object:type_name -> neo.fs.v2.object.Object
48, // 88: neo.fs.v2.object.PatchRequest.Body.address:type_name -> neo.fs.v2.refs.Address
57, // 89: neo.fs.v2.object.PatchRequest.Body.new_attributes:type_name -> neo.fs.v2.object.Header.Attribute
40, // 90: neo.fs.v2.object.PatchRequest.Body.patch:type_name -> neo.fs.v2.object.PatchRequest.Body.Patch
11, // 91: neo.fs.v2.object.PatchRequest.Body.Patch.source_range:type_name -> neo.fs.v2.object.Range
51, // 92: neo.fs.v2.object.PatchResponse.Body.object_id:type_name -> neo.fs.v2.refs.ObjectID
0, // 93: neo.fs.v2.object.ObjectService.Get:input_type -> neo.fs.v2.object.GetRequest
2, // 94: neo.fs.v2.object.ObjectService.Put:input_type -> neo.fs.v2.object.PutRequest
4, // 95: neo.fs.v2.object.ObjectService.Delete:input_type -> neo.fs.v2.object.DeleteRequest
6, // 96: neo.fs.v2.object.ObjectService.Head:input_type -> neo.fs.v2.object.HeadRequest
9, // 97: neo.fs.v2.object.ObjectService.Search:input_type -> neo.fs.v2.object.SearchRequest
12, // 98: neo.fs.v2.object.ObjectService.GetRange:input_type -> neo.fs.v2.object.GetRangeRequest
14, // 99: neo.fs.v2.object.ObjectService.GetRangeHash:input_type -> neo.fs.v2.object.GetRangeHashRequest
16, // 100: neo.fs.v2.object.ObjectService.PutSingle:input_type -> neo.fs.v2.object.PutSingleRequest
18, // 101: neo.fs.v2.object.ObjectService.Patch:input_type -> neo.fs.v2.object.PatchRequest
1, // 102: neo.fs.v2.object.ObjectService.Get:output_type -> neo.fs.v2.object.GetResponse
3, // 103: neo.fs.v2.object.ObjectService.Put:output_type -> neo.fs.v2.object.PutResponse
5, // 104: neo.fs.v2.object.ObjectService.Delete:output_type -> neo.fs.v2.object.DeleteResponse
8, // 105: neo.fs.v2.object.ObjectService.Head:output_type -> neo.fs.v2.object.HeadResponse
10, // 106: neo.fs.v2.object.ObjectService.Search:output_type -> neo.fs.v2.object.SearchResponse
13, // 107: neo.fs.v2.object.ObjectService.GetRange:output_type -> neo.fs.v2.object.GetRangeResponse
15, // 108: neo.fs.v2.object.ObjectService.GetRangeHash:output_type -> neo.fs.v2.object.GetRangeHashResponse
17, // 109: neo.fs.v2.object.ObjectService.PutSingle:output_type -> neo.fs.v2.object.PutSingleResponse
19, // 110: neo.fs.v2.object.ObjectService.Patch:output_type -> neo.fs.v2.object.PatchResponse
102, // [102:111] is the sub-list for method output_type
93, // [93:102] is the sub-list for method input_type
93, // [93:93] is the sub-list for extension type_name
93, // [93:93] is the sub-list for extension extendee
0, // [0:93] is the sub-list for field type_name
58, // 90: neo.fs.v2.object.PatchRequest.Body.new_split_header:type_name -> neo.fs.v2.object.Header.Split
40, // 91: neo.fs.v2.object.PatchRequest.Body.patch:type_name -> neo.fs.v2.object.PatchRequest.Body.Patch
11, // 92: neo.fs.v2.object.PatchRequest.Body.Patch.source_range:type_name -> neo.fs.v2.object.Range
51, // 93: neo.fs.v2.object.PatchResponse.Body.object_id:type_name -> neo.fs.v2.refs.ObjectID
0, // 94: neo.fs.v2.object.ObjectService.Get:input_type -> neo.fs.v2.object.GetRequest
2, // 95: neo.fs.v2.object.ObjectService.Put:input_type -> neo.fs.v2.object.PutRequest
4, // 96: neo.fs.v2.object.ObjectService.Delete:input_type -> neo.fs.v2.object.DeleteRequest
6, // 97: neo.fs.v2.object.ObjectService.Head:input_type -> neo.fs.v2.object.HeadRequest
9, // 98: neo.fs.v2.object.ObjectService.Search:input_type -> neo.fs.v2.object.SearchRequest
12, // 99: neo.fs.v2.object.ObjectService.GetRange:input_type -> neo.fs.v2.object.GetRangeRequest
14, // 100: neo.fs.v2.object.ObjectService.GetRangeHash:input_type -> neo.fs.v2.object.GetRangeHashRequest
16, // 101: neo.fs.v2.object.ObjectService.PutSingle:input_type -> neo.fs.v2.object.PutSingleRequest
18, // 102: neo.fs.v2.object.ObjectService.Patch:input_type -> neo.fs.v2.object.PatchRequest
1, // 103: neo.fs.v2.object.ObjectService.Get:output_type -> neo.fs.v2.object.GetResponse
3, // 104: neo.fs.v2.object.ObjectService.Put:output_type -> neo.fs.v2.object.PutResponse
5, // 105: neo.fs.v2.object.ObjectService.Delete:output_type -> neo.fs.v2.object.DeleteResponse
8, // 106: neo.fs.v2.object.ObjectService.Head:output_type -> neo.fs.v2.object.HeadResponse
10, // 107: neo.fs.v2.object.ObjectService.Search:output_type -> neo.fs.v2.object.SearchResponse
13, // 108: neo.fs.v2.object.ObjectService.GetRange:output_type -> neo.fs.v2.object.GetRangeResponse
15, // 109: neo.fs.v2.object.ObjectService.GetRangeHash:output_type -> neo.fs.v2.object.GetRangeHashResponse
17, // 110: neo.fs.v2.object.ObjectService.PutSingle:output_type -> neo.fs.v2.object.PutSingleResponse
19, // 111: neo.fs.v2.object.ObjectService.Patch:output_type -> neo.fs.v2.object.PatchResponse
103, // [103:112] is the sub-list for method output_type
94, // [94:103] is the sub-list for method input_type
94, // [94:94] is the sub-list for extension type_name
94, // [94:94] is the sub-list for extension extendee
0, // [0:94] is the sub-list for field type_name
}
func init() { file_api_object_grpc_service_proto_init() }

View file

@ -136,10 +136,11 @@ const (
patchRequestBodyPatchRangeField = 1
patchRequestBodyPatchChunkField = 2
patchRequestBodyAddrField = 1
patchRequestBodyNewAttrsField = 2
patchRequestBodyReplaceAttrField = 3
patchRequestBodyPatchField = 4
patchRequestBodyAddrField = 1
patchRequestBodyNewAttrsField = 2
patchRequestBodyReplaceAttrField = 3
patchRequestBodyPatchField = 4
patchRequestBodyNewSplitHeaderField = 5
patchResponseBodyObjectIDField = 1
)
@ -1372,7 +1373,8 @@ func (r *PatchRequestBody) StableMarshal(buf []byte) []byte {
offset += proto.NestedStructureMarshal(patchRequestBodyNewAttrsField, buf[offset:], &r.newAttributes[i])
}
offset += proto.BoolMarshal(patchRequestBodyReplaceAttrField, buf[offset:], r.replaceAttributes)
proto.NestedStructureMarshal(patchRequestBodyPatchField, buf[offset:], r.patch)
offset += proto.NestedStructureMarshal(patchRequestBodyPatchField, buf[offset:], r.patch)
proto.NestedStructureMarshal(patchRequestBodyNewSplitHeaderField, buf[offset:], r.newSplitHeader)
return buf
}
@ -1389,6 +1391,7 @@ func (r *PatchRequestBody) StableSize() int {
}
size += proto.BoolSize(patchRequestBodyReplaceAttrField, r.replaceAttributes)
size += proto.NestedStructureSize(patchRequestBodyPatchField, r.patch)
size += proto.NestedStructureSize(patchRequestBodyNewSplitHeaderField, r.newSplitHeader)
return size
}

View file

@ -360,6 +360,8 @@ type PatchRequestBody struct {
newAttributes []Attribute
newSplitHeader *SplitHeader
replaceAttributes bool
patch *PatchRequestBodyPatch
@ -1591,6 +1593,14 @@ func (r *PatchRequestBody) SetReplaceAttributes(replace bool) {
r.replaceAttributes = replace
}
func (r *PatchRequestBody) SetNewSplitHeader(newSplitHeader *SplitHeader) {
r.newSplitHeader = newSplitHeader
}
func (r *PatchRequestBody) GetNewSplitHeader() *SplitHeader {
return r.newSplitHeader
}
func (r *PatchRequestBody) GetPatch() *PatchRequestBodyPatch {
if r != nil {
return r.patch

View file

@ -313,15 +313,10 @@ func (b *Token) SetAPEOverride(v APEOverride) {
b.apeOverrideSet = true
}
// APEOverride returns APE override set by SetAPEOverride.
//
// Zero Token has zero APEOverride.
func (b *Token) APEOverride() APEOverride {
if b.apeOverrideSet {
return b.apeOverride
}
return APEOverride{}
// APEOverride returns APE override set by SetAPEOverride and a flag that indicates whether override
// is set for the token.
func (b *Token) APEOverride() (override APEOverride, isSet bool) {
return b.apeOverride, b.apeOverrideSet
}
// SetImpersonate mark token as impersonate to consider token signer as request owner.

View file

@ -93,7 +93,8 @@ func TestToken_SetAPEOverrides(t *testing.T) {
val2 := filled
require.NoError(t, val2.Unmarshal(val.Marshal()))
require.Zero(t, val2.APEOverride())
_, isSet := val2.APEOverride()
require.False(t, isSet)
val2 = filled
@ -101,14 +102,16 @@ func TestToken_SetAPEOverrides(t *testing.T) {
require.NoError(t, err)
require.NoError(t, val2.UnmarshalJSON(jd))
require.Zero(t, val2.APEOverride())
_, isSet = val2.APEOverride()
require.False(t, isSet)
// set value
tApe := bearertest.APEOverride()
val.SetAPEOverride(tApe)
require.Equal(t, tApe, val.APEOverride())
_, isSet = val.APEOverride()
require.True(t, isSet)
val.WriteToV2(&m)
require.NotNil(t, m.GetBody().GetAPEOverride())
@ -117,7 +120,8 @@ func TestToken_SetAPEOverrides(t *testing.T) {
val2 = filled
require.NoError(t, val2.Unmarshal(val.Marshal()))
apeOverride := val2.APEOverride()
apeOverride, isSet := val2.APEOverride()
require.True(t, isSet)
require.True(t, tokenAPEOverridesEqual(tApe.ToV2(), apeOverride.ToV2()))
val2 = filled
@ -126,7 +130,8 @@ func TestToken_SetAPEOverrides(t *testing.T) {
require.NoError(t, err)
require.NoError(t, val2.UnmarshalJSON(jd))
apeOverride = val.APEOverride()
apeOverride, isSet = val.APEOverride()
require.True(t, isSet)
require.True(t, tokenAPEOverridesEqual(tApe.ToV2(), apeOverride.ToV2()))
}

View file

@ -26,11 +26,19 @@ import (
// usage is unsafe.
type ObjectPatcher interface {
// PatchAttributes patches attributes. Attributes can be patched no more than once,
// otherwise, the server returns an error.
// otherwise, the server returns an error. `PatchAttributes` and `PatchHeader` are mutually
// exclusive - only one method can be used.
//
// Result means success. Failure reason can be received via Close.
PatchAttributes(ctx context.Context, newAttrs []object.Attribute, replace bool) bool
// PatchHeader patches object's header. Header can be patched no more than once,
// otherwise, the server returns an error. `PatchAttributes` and `PatchHeader` are mutually
// exclusive - only one method can be used.
//
// Result means success. Failure reason can be received via Close.
PatchHeader(ctx context.Context, prm PatchHeaderPrm) bool
// PatchPayload patches the object's payload.
//
// PatchPayload receives `payloadReader` and thus the payload of the patch is read and sent by chunks of
@ -60,6 +68,14 @@ type ObjectPatcher interface {
Close(_ context.Context) (*ResObjectPatch, error)
}
type PatchHeaderPrm struct {
NewSplitHeader *object.SplitHeader
NewAttributes []object.Attribute
ReplaceAttributes bool
}
// ResObjectPatch groups resulting values of ObjectPatch operation.
type ResObjectPatch struct {
statusRes
@ -163,6 +179,15 @@ func (x *objectPatcher) PatchAttributes(_ context.Context, newAttrs []object.Att
})
}
func (x *objectPatcher) PatchHeader(_ context.Context, prm PatchHeaderPrm) bool {
return x.patch(&object.Patch{
Address: x.addr,
NewAttributes: prm.NewAttributes,
ReplaceAttributes: prm.ReplaceAttributes,
NewSplitHeader: prm.NewSplitHeader,
})
}
func (x *objectPatcher) PatchPayload(_ context.Context, rng *object.Range, payloadReader io.Reader) bool {
offset := rng.GetOffset()

View file

@ -177,7 +177,7 @@ func TestObjectPatcher(t *testing.T) {
maxChunkLen: test.maxChunkLen,
}
success := patcher.PatchAttributes(context.Background(), nil, false)
success := patcher.PatchHeader(context.Background(), PatchHeaderPrm{})
require.True(t, success)
success = patcher.PatchPayload(context.Background(), test.rng, bytes.NewReader([]byte(test.patchPayload)))

View file

@ -5,6 +5,7 @@ import (
"crypto/sha256"
"errors"
"fmt"
"iter"
"strconv"
"strings"
"time"
@ -337,10 +338,41 @@ func (x Container) Attribute(key string) string {
return ""
}
// Attributes returns an iterator over all Container attributes.
//
// See also [Container.SetAttribute], [Container.UserAttributes].
func (x Container) Attributes() iter.Seq2[string, string] {
return func(yield func(string, string) bool) {
attrs := x.v2.GetAttributes()
for i := range attrs {
if !yield(attrs[i].GetKey(), attrs[i].GetValue()) {
return
}
}
}
}
// Attributes returns an iterator over all non-system Container attributes.
//
// See also [Container.SetAttribute], [Container.Attributes].
func (x Container) UserAttributes() iter.Seq2[string, string] {
return func(yield func(string, string) bool) {
for key, value := range x.Attributes() {
if !strings.HasPrefix(key, container.SysAttributePrefix) {
if !yield(key, value) {
return
}
}
}
}
}
// IterateAttributes iterates over all Container attributes and passes them
// into f. The handler MUST NOT be nil.
//
// See also SetAttribute, Attribute.
//
// Deprecated: use [Container.Attributes] instead.
func (x Container) IterateAttributes(f func(key, val string)) {
attrs := x.v2.GetAttributes()
for i := range attrs {
@ -352,6 +384,8 @@ func (x Container) IterateAttributes(f func(key, val string)) {
// into f. The handler MUST NOT be nil.
//
// See also SetAttribute, Attribute.
//
// Deprecated: use [Container.UserAttributes] instead.
func (x Container) IterateUserAttributes(f func(key, val string)) {
attrs := x.v2.GetAttributes()
for _, attr := range attrs {

View file

@ -2,6 +2,7 @@ package container_test
import (
"crypto/sha256"
"maps"
"strconv"
"testing"
"time"
@ -159,9 +160,9 @@ func TestContainer_Attribute(t *testing.T) {
val.SetAttribute(attrKey2, attrVal2)
var i int
val.IterateUserAttributes(func(key, val string) {
for range val.UserAttributes() {
i++
})
}
require.Equal(t, 1, i)
var msg v2container.Container
@ -177,11 +178,7 @@ func TestContainer_Attribute(t *testing.T) {
require.Equal(t, attrVal1, val2.Attribute(attrKey1))
require.Equal(t, attrVal2, val2.Attribute(attrKey2))
m := map[string]string{}
val2.IterateAttributes(func(key, val string) {
m[key] = val
})
m := maps.Collect(val2.Attributes())
require.GreaterOrEqual(t, len(m), 2)
require.Equal(t, attrVal1, m[attrKey1])

View file

@ -0,0 +1,95 @@
package container_test
import (
"testing"
containerAPI "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/api/container"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container"
"github.com/stretchr/testify/require"
)
func TestContainer_Attributes(t *testing.T) {
t.Run("empty", func(t *testing.T) {
var n container.Container
t.Run("attributes", func(t *testing.T) {
for range n.Attributes() {
t.Fatalf("handler is called, but it shouldn't")
}
})
t.Run("user attributes", func(t *testing.T) {
for range n.UserAttributes() {
t.Fatalf("handler is called, but it shouldn't")
}
})
})
var n container.Container
n.SetAttribute(containerAPI.SysAttributeName, "myname")
n.SetAttribute("key1", "value1")
n.SetAttribute("key2", "value2")
n.SetAttribute(containerAPI.SysAttributeZone, "test")
t.Run("break", func(t *testing.T) {
t.Run("attributes", func(t *testing.T) {
var res [][2]string
for key, value := range n.Attributes() {
if key == "key2" {
break
}
res = append(res, [2]string{key, value})
}
require.Equal(t, [][2]string{{containerAPI.SysAttributeName, "myname"}, {"key1", "value1"}}, res)
})
t.Run("user attributes", func(t *testing.T) {
var res [][2]string
for key, value := range n.UserAttributes() {
if key == "key2" {
break
}
res = append(res, [2]string{key, value})
}
require.Equal(t, [][2]string{{"key1", "value1"}}, res)
})
})
t.Run("continue", func(t *testing.T) {
t.Run("attributes", func(t *testing.T) {
var res [][2]string
for key, value := range n.Attributes() {
if key == "key2" {
continue
}
res = append(res, [2]string{key, value})
}
require.Equal(t, [][2]string{{containerAPI.SysAttributeName, "myname"}, {"key1", "value1"}, {containerAPI.SysAttributeZone, "test"}}, res)
})
t.Run("user attributes", func(t *testing.T) {
var res [][2]string
for key, value := range n.UserAttributes() {
if key == "key2" {
continue
}
res = append(res, [2]string{key, value})
}
require.Equal(t, [][2]string{{"key1", "value1"}}, res)
})
})
t.Run("attributes", func(t *testing.T) {
var res [][2]string
for key, value := range n.Attributes() {
res = append(res, [2]string{key, value})
}
require.Equal(t, [][2]string{
{containerAPI.SysAttributeName, "myname"},
{"key1", "value1"},
{"key2", "value2"},
{containerAPI.SysAttributeZone, "test"},
}, res)
})
t.Run("user attributes", func(t *testing.T) {
var res [][2]string
for key, value := range n.UserAttributes() {
res = append(res, [2]string{key, value})
}
require.Equal(t, [][2]string{{"key1", "value1"}, {"key2", "value2"}}, res)
})
}

20
go.mod
View file

@ -1,6 +1,6 @@
module git.frostfs.info/TrueCloudLab/frostfs-sdk-go
go 1.22
go 1.23.0
require (
git.frostfs.info/TrueCloudLab/frostfs-contract v0.19.3-0.20240621131249-49e5270f673e
@ -14,11 +14,11 @@ require (
github.com/klauspost/reedsolomon v1.12.1
github.com/mailru/easyjson v0.7.7
github.com/mr-tron/base58 v1.2.0
github.com/multiformats/go-multiaddr v0.14.0
github.com/multiformats/go-multiaddr v0.15.0
github.com/nspcc-dev/neo-go v0.106.2
github.com/stretchr/testify v1.9.0
go.uber.org/zap v1.27.0
golang.org/x/sync v0.10.0
golang.org/x/sync v0.12.0
google.golang.org/grpc v1.69.2
google.golang.org/protobuf v1.36.1
gopkg.in/yaml.v3 v3.0.1
@ -30,9 +30,9 @@ require (
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 // indirect
github.com/golang/snappy v0.0.1 // indirect
github.com/gorilla/websocket v1.5.1 // indirect
github.com/ipfs/go-cid v0.0.7 // indirect
github.com/ipfs/go-cid v0.5.0 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/klauspost/cpuid/v2 v2.2.6 // indirect
github.com/klauspost/cpuid/v2 v2.2.10 // indirect
github.com/kr/pretty v0.3.1 // indirect
github.com/minio/sha256-simd v1.0.1 // indirect
github.com/multiformats/go-base32 v0.1.0 // indirect
@ -49,12 +49,12 @@ require (
github.com/twmb/murmur3 v1.1.8 // indirect
go.etcd.io/bbolt v1.3.9 // indirect
go.uber.org/multierr v1.11.0 // indirect
golang.org/x/crypto v0.31.0 // indirect
golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 // indirect
golang.org/x/crypto v0.36.0 // indirect
golang.org/x/exp v0.0.0-20250305212735-054e65f0b394 // indirect
golang.org/x/net v0.30.0 // indirect
golang.org/x/sys v0.28.0 // indirect
golang.org/x/text v0.21.0 // indirect
golang.org/x/sys v0.31.0 // indirect
golang.org/x/text v0.23.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20241015192408-796eee8c2d53 // indirect
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect
lukechampine.com/blake3 v1.2.1 // indirect
lukechampine.com/blake3 v1.4.0 // indirect
)

59
go.sum
View file

@ -62,12 +62,12 @@ github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyf
github.com/holiman/uint256 v1.2.4 h1:jUc4Nk8fm9jZabQuqr2JzednajVmBpC+oiTiXZJEApU=
github.com/holiman/uint256 v1.2.4/go.mod h1:EOMSn4q6Nyt9P6efbI3bueV4e1b3dGlUCXeiRV4ng7E=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/ipfs/go-cid v0.0.7 h1:ysQJVJA3fNDF1qigJbsSQOdjhVLsOEoPdh0+R97k3jY=
github.com/ipfs/go-cid v0.0.7/go.mod h1:6Ux9z5e+HpkQdckYoX1PG/6xqKspzlEIR5SDmgqgC/I=
github.com/ipfs/go-cid v0.5.0 h1:goEKKhaGm0ul11IHA7I6p1GmKz8kEYniqFopaB5Otwg=
github.com/ipfs/go-cid v0.5.0/go.mod h1:0L7vmeNXpQpUS9vt+yEARkJ8rOg43DF3iPgn4GIN0mk=
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
github.com/klauspost/cpuid/v2 v2.2.6 h1:ndNyv040zDGIDh8thGkXYjnFtiN02M1PVVF+JE/48xc=
github.com/klauspost/cpuid/v2 v2.2.6/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
github.com/klauspost/cpuid/v2 v2.2.10 h1:tBs3QSyvjDyFTq3uoc/9xFpCuOsJQFNPiAhYdw2skhE=
github.com/klauspost/cpuid/v2 v2.2.10/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
github.com/klauspost/reedsolomon v1.12.1 h1:NhWgum1efX1x58daOBGCFWcxtEhOhXKKl1HAPQUp03Q=
github.com/klauspost/reedsolomon v1.12.1/go.mod h1:nEi5Kjb6QqtbofI6s+cbG/j1da11c96IBYBSnVGtuBs=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
@ -76,31 +76,22 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
github.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1/go.mod h1:pD8RvIylQ358TN4wwqatJ8rNavkEINozVn9DtGI3dfQ=
github.com/minio/sha256-simd v0.1.1-0.20190913151208-6de447530771/go.mod h1:B5e1o+1/KgNmWrSQK08Y6Z1Vb5pwIktudl0J58iy0KM=
github.com/minio/sha256-simd v1.0.1 h1:6kaan5IFmwTNynnKKpDHe6FWHohJOHhCPchzK49dzMM=
github.com/minio/sha256-simd v1.0.1/go.mod h1:Pz6AKMiUdngCLpeTL/RJY1M9rUuPMYujV5xJjtbRSN8=
github.com/mmcloughlin/addchain v0.4.0 h1:SobOdjm2xLj1KkXN5/n0xTIWyZA2+s99UCY1iPfkHRY=
github.com/mmcloughlin/addchain v0.4.0/go.mod h1:A86O+tHqZLMNO4w6ZZ4FlVQEadcoqkyU72HC5wJ4RlU=
github.com/mr-tron/base58 v1.1.0/go.mod h1:xcD2VGqlgYjBdcBLw+TuYLr8afG+Hj8g2eTVqeSzSU8=
github.com/mr-tron/base58 v1.1.3/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc=
github.com/mr-tron/base58 v1.2.0 h1:T/HDJBh4ZCPbU39/+c3rRvE0uKBQlU27+QI8LJ4t64o=
github.com/mr-tron/base58 v1.2.0/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc=
github.com/multiformats/go-base32 v0.0.3/go.mod h1:pLiuGC8y0QR3Ue4Zug5UzK9LjgbkL8NSQj0zQ5Nz/AA=
github.com/multiformats/go-base32 v0.1.0 h1:pVx9xoSPqEIQG8o+UbAe7DNi51oej1NtK+aGkbLYxPE=
github.com/multiformats/go-base32 v0.1.0/go.mod h1:Kj3tFY6zNr+ABYMqeUNeGvkIC/UYgtWibDcT0rExnbI=
github.com/multiformats/go-base36 v0.1.0/go.mod h1:kFGE83c6s80PklsHO9sRn2NCoffoRdUUOENyW/Vv6sM=
github.com/multiformats/go-base36 v0.2.0 h1:lFsAbNOGeKtuKozrtBsAkSVhv1p9D0/qedU9rQyccr0=
github.com/multiformats/go-base36 v0.2.0/go.mod h1:qvnKE++v+2MWCfePClUEjE78Z7P2a1UV0xHgWc0hkp4=
github.com/multiformats/go-multiaddr v0.14.0 h1:bfrHrJhrRuh/NXH5mCnemjpbGjzRw/b+tJFOD41g2tU=
github.com/multiformats/go-multiaddr v0.14.0/go.mod h1:6EkVAxtznq2yC3QT5CM1UTAwG0GTP3EWAIcjHuzQ+r4=
github.com/multiformats/go-multibase v0.0.3/go.mod h1:5+1R4eQrT3PkYZ24C3W2Ue2tPwIdYQD509ZjSb5y9Oc=
github.com/multiformats/go-multiaddr v0.15.0 h1:zB/HeaI/apcZiTDwhY5YqMvNVl/oQYvs3XySU+qeAVo=
github.com/multiformats/go-multiaddr v0.15.0/go.mod h1:JSVUmXDjsVFiW7RjIFMP7+Ev+h1DTbiJgVeTV/tcmP0=
github.com/multiformats/go-multibase v0.2.0 h1:isdYCVLvksgWlMW9OZRYJEa9pZETFivncJHmHnnd87g=
github.com/multiformats/go-multibase v0.2.0/go.mod h1:bFBZX4lKCA/2lyOFSAoKH5SS6oPyjtnzK/XTFDPkNuk=
github.com/multiformats/go-multihash v0.0.13/go.mod h1:VdAWLKTwram9oKAatUcLxBNUjdtcVwxObEQBtRfuyjc=
github.com/multiformats/go-multihash v0.2.3 h1:7Lyc8XfX/IY2jWb/gI7JP+o7JEq9hOa7BFvVU9RSh+U=
github.com/multiformats/go-multihash v0.2.3/go.mod h1:dXgKXCXjBzdscBLk9JkjINiEsCKRVch90MdaGiKsvSM=
github.com/multiformats/go-varint v0.0.5/go.mod h1:3Ls8CIEsrijN6+B7PbrXRPxHRPuXSrVKRY101jdMZYE=
github.com/multiformats/go-varint v0.0.7 h1:sWSGR+f/eu5ABZA2ZpYKBILXTTs9JWpdEM/nEGOHFS8=
github.com/multiformats/go-varint v0.0.7/go.mod h1:r8PUYw/fD/SjBCiKOoDlGF6QawOELpZAu9eioSos/OU=
github.com/nspcc-dev/go-ordered-json v0.0.0-20240301084351-0246b013f8b2 h1:mD9hU3v+zJcnHAVmHnZKt3I++tvn30gBj2rP2PocZMk=
@ -167,14 +158,13 @@ go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN8
go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U=
golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 h1:vr/HnozRka3pE4EsMEg1lgkXJkTFJCVUX+S/ZT6wYzM=
golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842/go.mod h1:XtvwrStGgqGPLc4cjQfWqZHG1YFdYs6swckp8vpsjnc=
golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA=
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34=
golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc=
golang.org/x/exp v0.0.0-20250305212735-054e65f0b394 h1:nDVHiLt8aIbd/VzvPWN6kSOPE7+F/fNFDSXLVYkE/Iw=
golang.org/x/exp v0.0.0-20250305212735-054e65f0b394/go.mod h1:sIifuuw/Yco/y6yb6+bDNfyeQ/MdPUy/hKEMYQV17cM=
golang.org/x/mod v0.24.0 h1:ZfthKaKaT4NrhGVZHO1/WDTwGES4De8KtWO0SIbNJMU=
golang.org/x/mod v0.24.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
@ -182,8 +172,8 @@ golang.org/x/net v0.0.0-20200813134508-3edf25e44fcc/go.mod h1:/O7V0waA8r7cgGh81R
golang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4=
golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw=
golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/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-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@ -194,19 +184,18 @@ golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200814200057-3d37ad5750ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA=
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.27.0 h1:WP60Sv1nlK1T6SupCHbXzSaN0b9wUmsPoRS9b61A23Q=
golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM=
golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik=
golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/term v0.30.0 h1:PQ39fJZ+mfadBm0y5WlL4vlM7Sx1Hgf13sMIY2+QS9Y=
golang.org/x/term v0.30.0/go.mod h1:NYYFdzHoI5wRh/h5tDMdMqCqPJZEuNqVR5xJLd/n67g=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY=
golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg=
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
golang.org/x/tools v0.31.0 h1:0EedkvKDbh+qistFTd0Bcwe/YLh4vHwWEkiI0toFIBU=
golang.org/x/tools v0.31.0/go.mod h1:naFTU+Cev749tSJRXJlna0T3WxKvb1kWEx15xA4SdmQ=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@ -235,7 +224,7 @@ gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
lukechampine.com/blake3 v1.2.1 h1:YuqqRuaqsGV71BV/nm9xlI0MKUv4QC54jQnBChWbGnI=
lukechampine.com/blake3 v1.2.1/go.mod h1:0OFRp7fBtAylGVCO40o87sbupkyIGgbpv1+M1k1LM6k=
lukechampine.com/blake3 v1.4.0 h1:xDbKOZCVbnZsfzM6mHSYcGRHZ3YrLDzqz8XnV4uaD5w=
lukechampine.com/blake3 v1.4.0/go.mod h1:MQJNQCTnR+kwOP/JEZSxj3MaQjp80FOFSNMMHXcSeX0=
rsc.io/tmplfunc v0.0.3 h1:53XFQh69AfOa8Tw0Jm7t+GV7KZhOi6jzsCzTtKbMvzU=
rsc.io/tmplfunc v0.0.3/go.mod h1:AG3sTPzElb1Io3Yg4voV9AGZJuleGAwaVRxL9M49PhA=

View file

@ -54,7 +54,7 @@ var (
// capacity and price.
func newWeightFunc(capNorm, priceNorm normalizer) weightFunc {
return func(n NodeInfo) float64 {
return capNorm.Normalize(float64(n.capacity())) * priceNorm.Normalize(float64(n.Price()))
return capNorm.Normalize(float64(n.capacity)) * priceNorm.Normalize(float64(n.price))
}
}

View file

@ -97,8 +97,8 @@ func defaultWeightFunc(ns nodes) weightFunc {
minV := newMinAgg()
for i := range ns {
mean.Add(float64(ns[i].capacity()))
minV.Add(float64(ns[i].Price()))
mean.Add(float64(ns[i].capacity))
minV.Add(float64(ns[i].price))
}
return newWeightFunc(

View file

@ -133,9 +133,9 @@ func (c *context) matchKeyValue(f *netmap.Filter, b NodeInfo) bool {
switch f.GetKey() {
case attrPrice:
attr = b.Price()
attr = b.price
case attrCapacity:
attr = b.capacity()
attr = b.capacity
default:
var err error

View file

@ -139,20 +139,12 @@ func (n nodes) appendWeightsTo(wf weightFunc, w []float64) []float64 {
return w
}
func flattenNodes(ns []nodes) nodes {
var sz, i int
for i = range ns {
sz += len(ns[i])
}
result := make(nodes, 0, sz)
// flattenNodes flattens ns nested list and appends the result to the target slice.
func flattenNodes(target nodes, ns []nodes) nodes {
for i := range ns {
result = append(result, ns[i]...)
target = append(target, ns[i]...)
}
return result
return target
}
// PlacementVectors sorts container nodes returned by ContainerNodes method
@ -287,7 +279,7 @@ func (m NetMap) ContainerNodes(p PlacementPolicy, pivot []byte) ([][]NodeInfo, e
return nil, err
}
result[i] = append(result[i], flattenNodes(nodes)...)
result[i] = flattenNodes(result[i], nodes)
if unique {
c.addUsedNodes(result[i]...)
@ -304,14 +296,14 @@ func (m NetMap) ContainerNodes(p PlacementPolicy, pivot []byte) ([][]NodeInfo, e
if err != nil {
return nil, err
}
result[i] = append(result[i], flattenNodes(nodes)...)
result[i] = flattenNodes(result[i], nodes)
c.addUsedNodes(result[i]...)
} else {
nodes, ok := c.selections[sName]
if !ok {
return nil, fmt.Errorf("selector not found: REPLICA '%s'", sName)
}
result[i] = append(result[i], flattenNodes(nodes)...)
result[i] = flattenNodes(result[i], nodes)
}
}

View file

@ -30,20 +30,19 @@ func (x *NetworkInfo) readFromV2(m netmap.NetworkInfo, checkFieldPresence bool)
return errors.New("missing network config")
}
if checkFieldPresence && c.NumberOfParameters() <= 0 {
if checkFieldPresence && len(c.Parameters()) == 0 {
return errors.New("missing network parameters")
}
var err error
mNames := make(map[string]struct{}, c.NumberOfParameters())
mNames := make(map[string]struct{}, len(c.Parameters()))
c.IterateParameters(func(prm *netmap.NetworkParameter) bool {
for _, prm := range c.Parameters() {
name := string(prm.GetKey())
_, was := mNames[name]
if was {
err = fmt.Errorf("duplicated parameter name: %s", name)
return true
return fmt.Errorf("duplicated parameter name: %s", name)
}
mNames[name] = struct{}{}
@ -67,14 +66,8 @@ func (x *NetworkInfo) readFromV2(m netmap.NetworkInfo, checkFieldPresence bool)
}
if err != nil {
err = fmt.Errorf("invalid %s parameter: %w", name, err)
return fmt.Errorf("invalid %s parameter: %w", name, err)
}
return err != nil
})
if err != nil {
return err
}
x.m = m
@ -152,41 +145,29 @@ func (x *NetworkInfo) setConfig(name string, val []byte) {
return
}
found := false
prms := make([]netmap.NetworkParameter, 0, c.NumberOfParameters())
c.IterateParameters(func(prm *netmap.NetworkParameter) bool {
found = bytes.Equal(prm.GetKey(), []byte(name))
if found {
prm.SetValue(val)
} else {
prms = append(prms, *prm)
prms := c.Parameters()
for i := range prms {
if bytes.Equal(prms[i].GetKey(), []byte(name)) {
prms[i].SetValue(val)
return
}
return found
})
if !found {
prms = append(prms, netmap.NetworkParameter{})
prms[len(prms)-1].SetKey([]byte(name))
prms[len(prms)-1].SetValue(val)
c.SetParameters(prms...)
}
prms = append(prms, netmap.NetworkParameter{})
prms[len(prms)-1].SetKey([]byte(name))
prms[len(prms)-1].SetValue(val)
c.SetParameters(prms...)
}
func (x NetworkInfo) configValue(name string) (res []byte) {
x.m.GetNetworkConfig().IterateParameters(func(prm *netmap.NetworkParameter) bool {
for _, prm := range x.m.GetNetworkConfig().Parameters() {
if string(prm.GetKey()) == name {
res = prm.GetValue()
return true
return prm.GetValue()
}
}
return false
})
return
return nil
}
// SetRawNetworkParameter sets named FrostFS network parameter whose value is
@ -218,7 +199,7 @@ func (x *NetworkInfo) RawNetworkParameter(name string) []byte {
func (x *NetworkInfo) IterateRawNetworkParameters(f func(name string, value []byte)) {
c := x.m.GetNetworkConfig()
c.IterateParameters(func(prm *netmap.NetworkParameter) bool {
for _, prm := range c.Parameters() {
name := string(prm.GetKey())
switch name {
default:
@ -237,9 +218,7 @@ func (x *NetworkInfo) IterateRawNetworkParameters(f func(name string, value []by
configHomomorphicHashingDisabled,
configMaintenanceModeAllowed:
}
return false
})
}
}
func (x *NetworkInfo) setConfigUint64(name string, num uint64) {

View file

@ -76,16 +76,10 @@ func testConfigValue(t *testing.T,
var m netmap.NetworkInfo
x.WriteToV2(&m)
require.EqualValues(t, 1, m.GetNetworkConfig().NumberOfParameters())
found := false
m.GetNetworkConfig().IterateParameters(func(prm *netmap.NetworkParameter) bool {
require.False(t, found)
require.Equal(t, []byte(v2Key), prm.GetKey())
require.Equal(t, v2Val(exp), prm.GetValue())
found = true
return false
})
require.True(t, found)
var p netmap.NetworkParameter
p.SetKey([]byte(v2Key))
p.SetValue(v2Val(exp))
require.Equal(t, []netmap.NetworkParameter{p}, m.GetNetworkConfig().Parameters())
}
setter(&x, val1)

View file

@ -3,6 +3,7 @@ package netmap
import (
"errors"
"fmt"
"iter"
"slices"
"strconv"
"strings"
@ -25,6 +26,9 @@ import (
type NodeInfo struct {
m netmap.NodeInfo
hash uint64
capacity uint64
price uint64
}
// reads NodeInfo from netmap.NodeInfo message. If checkFieldPresence is set,
@ -32,6 +36,7 @@ type NodeInfo struct {
// presented field according to FrostFS API V2 protocol.
func (x *NodeInfo) readFromV2(m netmap.NodeInfo, checkFieldPresence bool) error {
var err error
var capacity, price uint64
binPublicKey := m.GetPublicKey()
if checkFieldPresence && len(binPublicKey) == 0 {
@ -49,18 +54,18 @@ func (x *NodeInfo) readFromV2(m netmap.NodeInfo, checkFieldPresence bool) error
if key == "" {
return fmt.Errorf("empty key of the attribute #%d", i)
} else if _, ok := mAttr[key]; ok {
return fmt.Errorf("duplicated attbiuted %s", key)
return fmt.Errorf("duplicate attributes %s", key)
}
mAttr[key] = struct{}{}
switch {
case key == attrCapacity:
_, err = strconv.ParseUint(attributes[i].GetValue(), 10, 64)
capacity, err = strconv.ParseUint(attributes[i].GetValue(), 10, 64)
if err != nil {
return fmt.Errorf("invalid %s attribute: %w", attrCapacity, err)
}
case key == attrPrice:
var err error
_, err = strconv.ParseUint(attributes[i].GetValue(), 10, 64)
price, err = strconv.ParseUint(attributes[i].GetValue(), 10, 64)
if err != nil {
return fmt.Errorf("invalid %s attribute: %w", attrPrice, err)
}
@ -73,6 +78,8 @@ func (x *NodeInfo) readFromV2(m netmap.NodeInfo, checkFieldPresence bool) error
x.m = m
x.hash = hrw.Hash(binPublicKey)
x.capacity = capacity
x.price = price
return nil
}
@ -200,12 +207,28 @@ func (x NodeInfo) NumberOfNetworkEndpoints() int {
// FrostFS system requirements.
//
// See also SetNetworkEndpoints.
//
// Deprecated: use [NodeInfo.NetworkEndpoints] instead.
func (x NodeInfo) IterateNetworkEndpoints(f func(string) bool) {
x.m.IterateAddresses(f)
for s := range x.NetworkEndpoints() {
if f(s) {
return
}
}
}
// NetworkEndpoints returns an iterator over network endpoints announced by the
// node.
//
// See also SetNetworkEndpoints.
func (x NodeInfo) NetworkEndpoints() iter.Seq[string] {
return x.m.Addresses()
}
// IterateNetworkEndpoints is an extra-sugared function over IterateNetworkEndpoints
// method which allows to unconditionally iterate over all node's network endpoints.
//
// Deprecated: use [NodeInfo.NetworkEndpoints] instead.
func IterateNetworkEndpoints(node NodeInfo, f func(string)) {
node.IterateNetworkEndpoints(func(addr string) bool {
f(addr)
@ -235,46 +258,21 @@ func (x *NodeInfo) setNumericAttribute(key string, num uint64) {
// price is announced.
func (x *NodeInfo) SetPrice(price uint64) {
x.setNumericAttribute(attrPrice, price)
x.price = price
}
// Price returns price set using SetPrice.
//
// Zero NodeInfo has zero price.
func (x NodeInfo) Price() uint64 {
val := x.Attribute(attrPrice)
if val == "" {
return 0
}
price, err := strconv.ParseUint(val, 10, 64)
if err != nil {
panic(fmt.Sprintf("unexpected price parsing error %s: %v", val, err))
}
return price
return x.price
}
// SetCapacity sets the storage capacity declared by the node. By default, zero
// capacity is announced.
func (x *NodeInfo) SetCapacity(capacity uint64) {
x.setNumericAttribute(attrCapacity, capacity)
}
// capacity returns capacity set using SetCapacity.
//
// Zero NodeInfo has zero capacity.
func (x NodeInfo) capacity() uint64 {
val := x.Attribute(attrCapacity)
if val == "" {
return 0
}
capacity, err := strconv.ParseUint(val, 10, 64)
if err != nil {
panic(fmt.Sprintf("unexpected capacity parsing error %s: %v", val, err))
}
return capacity
x.capacity = capacity
}
const attrUNLOCODE = "UN-LOCODE"
@ -393,8 +391,22 @@ func (x NodeInfo) NumberOfAttributes() int {
return len(x.m.GetAttributes())
}
// Attributes returns an iterator over node attributes.
func (x NodeInfo) Attributes() iter.Seq2[string, string] {
return func(yield func(string, string) bool) {
a := x.m.GetAttributes()
for i := range a {
if !yield(a[i].GetKey(), a[i].GetValue()) {
break
}
}
}
}
// IterateAttributes iterates over all node attributes and passes the into f.
// Handler MUST NOT be nil.
//
// Deprecated: use [NodeInfo.Attributes] instead.
func (x NodeInfo) IterateAttributes(f func(key, value string)) {
a := x.m.GetAttributes()
for i := range a {
@ -411,6 +423,17 @@ func (x *NodeInfo) SetAttribute(key, value string) {
panic("empty value in SetAttribute")
}
// NodeInfo with non-numeric `Price`` or `Capacity` attributes
// is considered invalid by NodeInfo.readFromV2().
// Here we have no way to signal an error, and panic seems an overkill.
// So, set cached fields only if we can parse the value and 0 parsing fails.
switch key {
case attrPrice:
x.price, _ = strconv.ParseUint(value, 10, 64)
case attrCapacity:
x.capacity, _ = strconv.ParseUint(value, 10, 64)
}
a := x.m.GetAttributes()
for i := range a {
if a[i].GetKey() == key {

View file

@ -1,12 +1,94 @@
package netmap
import (
"fmt"
"testing"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/api/netmap"
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
"github.com/stretchr/testify/require"
)
func TestNodeInfo_NetworkEndpoints(t *testing.T) {
t.Run("empty", func(t *testing.T) {
var n NodeInfo
for range n.NetworkEndpoints() {
t.Fatalf("handler is called, but it shouldn't")
}
})
var n NodeInfo
n.SetNetworkEndpoints("1", "2", "3")
t.Run("break", func(t *testing.T) {
var res []string
for s := range n.NetworkEndpoints() {
if s == "2" {
break
}
res = append(res, s)
}
require.Equal(t, []string{"1"}, res)
})
t.Run("continue", func(t *testing.T) {
var res []string
for s := range n.NetworkEndpoints() {
if s == "2" {
continue
}
res = append(res, s)
}
require.Equal(t, []string{"1", "3"}, res)
})
var res []string
for s := range n.NetworkEndpoints() {
res = append(res, s)
}
require.Equal(t, []string{"1", "2", "3"}, res)
}
func TestNodeInfo_Attributes(t *testing.T) {
t.Run("empty", func(t *testing.T) {
var n NodeInfo
for range n.Attributes() {
t.Fatalf("handler is called, but it shouldn't")
}
})
var n NodeInfo
n.SetAttribute("key1", "value1")
n.SetAttribute("key2", "value2")
n.SetAttribute("key3", "value3")
t.Run("break", func(t *testing.T) {
var res [][2]string
for k, v := range n.Attributes() {
if k == "key2" {
break
}
res = append(res, [2]string{k, v})
}
require.Equal(t, [][2]string{{"key1", "value1"}}, res)
})
t.Run("continue", func(t *testing.T) {
var res [][2]string
for k, v := range n.Attributes() {
if k == "key2" {
continue
}
res = append(res, [2]string{k, v})
}
require.Equal(t, [][2]string{{"key1", "value1"}, {"key3", "value3"}}, res)
})
var res [][2]string
for k, v := range n.Attributes() {
res = append(res, [2]string{k, v})
}
require.Equal(t, [][2]string{{"key1", "value1"}, {"key2", "value2"}, {"key3", "value3"}}, res)
}
func TestNodeInfo_SetAttribute(t *testing.T) {
var n NodeInfo
@ -117,3 +199,85 @@ func TestNodeInfo_Clone(t *testing.T) {
require.True(t, c != &ni)
require.True(t, &(c.PublicKey()[0]) != &(ni.PublicKey()[0]))
}
func TestNodeInfo_Unmarshal(t *testing.T) {
pk, err := keys.NewPrivateKey()
require.NoError(t, err)
attrs := make([]netmap.Attribute, 2)
for i := range attrs {
attrs[i].SetKey(fmt.Sprintf("key%d", i))
attrs[i].SetValue(fmt.Sprintf("value%d", i))
}
goodNodeInfo := func() netmap.NodeInfo {
var nodev2 netmap.NodeInfo
nodev2.SetPublicKey(pk.PublicKey().Bytes())
nodev2.SetAddresses("127.0.0.1:2025")
nodev2.SetState(netmap.Online)
nodev2.SetAttributes(attrs)
return nodev2
}
// Check that goodNodeInfo indeed returns good node.
// Otherwise, the whole test is garbage.
require.NoError(t, new(NodeInfo).ReadFromV2(goodNodeInfo()))
t.Run("empty public key", func(t *testing.T) {
n := goodNodeInfo()
n.SetPublicKey(nil)
require.ErrorContains(t, new(NodeInfo).ReadFromV2(n), "missing public key")
})
t.Run("missing addresses", func(t *testing.T) {
n := goodNodeInfo()
n.SetAddresses()
require.ErrorContains(t, new(NodeInfo).ReadFromV2(n), "missing network endpoints")
})
t.Run("empty attribute key", func(t *testing.T) {
n := goodNodeInfo()
var a netmap.Attribute
a.SetValue("non-empty")
n.SetAttributes(append(attrs, a))
require.ErrorContains(t, new(NodeInfo).ReadFromV2(n),
fmt.Sprintf("empty key of the attribute #%d", len(attrs)))
})
t.Run("empty attribute value", func(t *testing.T) {
n := goodNodeInfo()
var a netmap.Attribute
a.SetKey("non-empty-key")
n.SetAttributes(append(attrs, a))
require.ErrorContains(t, new(NodeInfo).ReadFromV2(n),
"empty value of the attribute non-empty-key")
})
t.Run("invalid price attribute", func(t *testing.T) {
n := goodNodeInfo()
var a netmap.Attribute
a.SetKey(attrPrice)
a.SetValue("not a number")
n.SetAttributes(append(attrs, a))
require.ErrorContains(t, new(NodeInfo).ReadFromV2(n),
fmt.Sprintf("invalid %s attribute", attrPrice))
})
t.Run("invalid capacity attribute", func(t *testing.T) {
n := goodNodeInfo()
var a netmap.Attribute
a.SetKey(attrCapacity)
a.SetValue("not a number")
n.SetAttributes(append(attrs, a))
require.ErrorContains(t, new(NodeInfo).ReadFromV2(n),
fmt.Sprintf("invalid %s attribute", attrCapacity))
})
t.Run("duplicate attributes", func(t *testing.T) {
n := goodNodeInfo()
var a netmap.Attribute
a.SetKey("key1")
a.SetValue("value3")
n.SetAttributes(append(attrs, a))
require.ErrorContains(t, new(NodeInfo).ReadFromV2(n),
"duplicate attributes key1")
})
}

View file

@ -191,7 +191,7 @@ func TestPlacementPolicy_DeterministicOrder(t *testing.T) {
nss[i] = v[i]
}
ns := flattenNodes(nss)
ns := flattenNodes(nil, nss)
require.Equal(t, 2, len(ns))
return ns[0].Hash(), ns[1].Hash()
}

View file

@ -345,6 +345,32 @@ func (o *Object) SetAttributes(v ...Attribute) {
})
}
// SplitHeader returns split header of the object. If it's set, then split header
// defines how the object relates to other objects in a split operation.
func (o *Object) SplitHeader() (splitHeader *SplitHeader) {
if v2 := (*object.Object)(o).
GetHeader().
GetSplit(); v2 != nil {
splitHeader = NewSplitHeaderFromV2(v2)
}
return
}
// SetSplitHeader sets split header.
func (o *Object) SetSplitHeader(v *SplitHeader) {
o.setSplitFields(func(sh *object.SplitHeader) {
v2 := v.ToV2()
sh.SetParent(v2.GetParent())
sh.SetPrevious(v2.GetPrevious())
sh.SetParentHeader(v2.GetParentHeader())
sh.SetParentSignature(v2.GetParentSignature())
sh.SetChildren(v2.GetChildren())
sh.SetSplitID(v2.GetSplitID())
})
}
// PreviousID returns identifier of the previous sibling object.
func (o *Object) PreviousID() (v oid.ID, isSet bool) {
v2 := (*object.Object)(o)

View file

@ -18,6 +18,10 @@ type Patch struct {
// filled with NewAttributes. Otherwise, the attributes are just merged.
ReplaceAttributes bool
// A new split header which is set to object's header. If `nil`, then split header patching
// is ignored.
NewSplitHeader *SplitHeader
// Payload patch. If this field is not set, then it assumed such Patch patches only
// header (see NewAttributes, ReplaceAttributes).
PayloadPatch *PayloadPatch
@ -41,6 +45,8 @@ func (p *Patch) ToV2() *v2object.PatchRequestBody {
v2.SetNewAttributes(attrs)
v2.SetReplaceAttributes(p.ReplaceAttributes)
v2.SetNewSplitHeader(p.NewSplitHeader.ToV2())
v2.SetPatch(p.PayloadPatch.ToV2())
return v2
@ -63,6 +69,8 @@ func (p *Patch) FromV2(patch *v2object.PatchRequestBody) {
p.ReplaceAttributes = patch.GetReplaceAttributes()
p.NewSplitHeader = NewSplitHeaderFromV2(patch.GetNewSplitHeader())
if v2patch := patch.GetPatch(); v2patch != nil {
p.PayloadPatch = new(PayloadPatch)
p.PayloadPatch.FromV2(v2patch)

View file

@ -11,10 +11,12 @@ import (
)
var (
ErrOffsetExceedsSize = errors.New("patch offset exceeds object size")
ErrInvalidPatchOffsetOrder = errors.New("invalid patch offset order")
ErrPayloadPatchIsNil = errors.New("nil payload patch")
ErrAttrPatchAlreadyApplied = errors.New("attribute patch already applied")
ErrOffsetExceedsSize = errors.New("patch offset exceeds object size")
ErrInvalidPatchOffsetOrder = errors.New("invalid patch offset order")
ErrPayloadPatchIsNil = errors.New("nil payload patch")
ErrAttrPatchAlreadyApplied = errors.New("attribute patch already applied")
ErrHeaderPatchAlreadyApplied = errors.New("header patch already applied")
ErrSplitHeaderPatchAppliedWithPayloadPatch = errors.New("split header patch applied with payload patch")
)
// PatchRes is the result of patch application.
@ -27,13 +29,24 @@ type PatchApplier interface {
// ApplyAttributesPatch applies the patch only for the object's attributes.
//
// ApplyAttributesPatch can't be invoked few times, otherwise it returns `ErrAttrPatchAlreadyApplied` error.
// `ApplyHeaderPatch` and `ApplyAttributesPatch` are mutually exclusive - only one method can be used.
//
// The call is idempotent for the original header if it's invoked with empty `newAttrs` and
// `replaceAttrs = false`.
ApplyAttributesPatch(ctx context.Context, newAttrs []objectSDK.Attribute, replaceAttrs bool) error
// ApplyHeaderPatch applies the patch only for the object's attributes.
//
// ApplyHeaderPatch can't be invoked few times, otherwise it returns `ErrHeaderPatchAlreadyApplied` error.
// `ApplyHeaderPatch` and `ApplyAttributesPatch` are mutually exclusive - only one method can be used.
//
// The call is idempotent for the original header if it's invoked with `ApplyHeaderPatchPrm` with not set fields.
ApplyHeaderPatch(ctx context.Context, prm ApplyHeaderPatchPrm) error
// ApplyPayloadPatch applies the patch for the object's payload.
//
// ApplyPayloadPatch returns `ErrSplitHeaderPatchAppliedWithPayloadPatch` when attempting to apply it with a split header patch.
//
// ApplyPayloadPatch returns `ErrPayloadPatchIsNil` error if patch is nil.
ApplyPayloadPatch(ctx context.Context, payloadPatch *objectSDK.PayloadPatch) error
@ -41,6 +54,14 @@ type PatchApplier interface {
Close(context.Context) (PatchRes, error)
}
type ApplyHeaderPatchPrm struct {
NewSplitHeader *objectSDK.SplitHeader
NewAttributes []objectSDK.Attribute
ReplaceAttributes bool
}
// RangeProvider is the interface that provides a method to get original object payload
// by a given range.
type RangeProvider interface {
@ -61,7 +82,9 @@ type patcher struct {
hdr *objectSDK.Object
attrPatchAlreadyApplied bool
hdrPatchAlreadyApplied bool
splitHeaderPatchAlreadyApplied bool
readerBuffSize int
}
@ -107,10 +130,10 @@ func New(prm Params) PatchApplier {
func (p *patcher) ApplyAttributesPatch(ctx context.Context, newAttrs []objectSDK.Attribute, replaceAttrs bool) error {
defer func() {
p.attrPatchAlreadyApplied = true
p.hdrPatchAlreadyApplied = true
}()
if p.attrPatchAlreadyApplied {
if p.hdrPatchAlreadyApplied {
return ErrAttrPatchAlreadyApplied
}
@ -127,7 +150,38 @@ func (p *patcher) ApplyAttributesPatch(ctx context.Context, newAttrs []objectSDK
return nil
}
func (p *patcher) ApplyHeaderPatch(ctx context.Context, prm ApplyHeaderPatchPrm) error {
defer func() {
p.hdrPatchAlreadyApplied = true
}()
if p.hdrPatchAlreadyApplied {
return ErrHeaderPatchAlreadyApplied
}
if prm.NewSplitHeader != nil {
p.hdr.SetSplitHeader(prm.NewSplitHeader)
p.splitHeaderPatchAlreadyApplied = true
}
if prm.ReplaceAttributes {
p.hdr.SetAttributes(prm.NewAttributes...)
} else if len(prm.NewAttributes) > 0 {
mergedAttrs := mergeAttributes(prm.NewAttributes, p.hdr.Attributes())
p.hdr.SetAttributes(mergedAttrs...)
}
if err := p.objectWriter.WriteHeader(ctx, p.hdr); err != nil {
return fmt.Errorf("writer header: %w", err)
}
return nil
}
func (p *patcher) ApplyPayloadPatch(ctx context.Context, payloadPatch *objectSDK.PayloadPatch) error {
if p.splitHeaderPatchAlreadyApplied {
return ErrSplitHeaderPatchAppliedWithPayloadPatch
}
if payloadPatch == nil {
return ErrPayloadPatchIsNil
}

View file

@ -106,7 +106,11 @@ func TestPatchRevert(t *testing.T) {
patcher := New(prm)
err := patcher.ApplyAttributesPatch(context.Background(), modifPatch.NewAttributes, modifPatch.ReplaceAttributes)
err := patcher.ApplyHeaderPatch(context.Background(), ApplyHeaderPatchPrm{
NewSplitHeader: modifPatch.NewSplitHeader,
NewAttributes: modifPatch.NewAttributes,
ReplaceAttributes: modifPatch.ReplaceAttributes,
})
require.NoError(t, err)
err = patcher.ApplyPayloadPatch(context.Background(), modifPatch.PayloadPatch)
@ -145,7 +149,11 @@ func TestPatchRevert(t *testing.T) {
patcher = New(prm)
err = patcher.ApplyAttributesPatch(context.Background(), revertPatch.NewAttributes, revertPatch.ReplaceAttributes)
err = patcher.ApplyHeaderPatch(context.Background(), ApplyHeaderPatchPrm{
NewSplitHeader: revertPatch.NewSplitHeader,
NewAttributes: revertPatch.NewAttributes,
ReplaceAttributes: revertPatch.ReplaceAttributes,
})
require.NoError(t, err)
err = patcher.ApplyPayloadPatch(context.Background(), revertPatch.PayloadPatch)
@ -157,7 +165,7 @@ func TestPatchRevert(t *testing.T) {
require.Equal(t, originalObjectPayload, patchedPatchedObj.Payload())
}
func TestPatchRepeatAttributePatch(t *testing.T) {
func TestPatchRepeatHeaderPatch(t *testing.T) {
obj, _ := newTestObject()
modifPatch := &objectSDK.Patch{}
@ -187,11 +195,142 @@ func TestPatchRepeatAttributePatch(t *testing.T) {
patcher := New(prm)
err := patcher.ApplyAttributesPatch(context.Background(), modifPatch.NewAttributes, modifPatch.ReplaceAttributes)
err := patcher.ApplyHeaderPatch(context.Background(), ApplyHeaderPatchPrm{
NewSplitHeader: modifPatch.NewSplitHeader,
NewAttributes: modifPatch.NewAttributes,
ReplaceAttributes: modifPatch.ReplaceAttributes,
})
require.NoError(t, err)
err = patcher.ApplyAttributesPatch(context.Background(), modifPatch.NewAttributes, modifPatch.ReplaceAttributes)
require.ErrorIs(t, err, ErrAttrPatchAlreadyApplied)
err = patcher.ApplyHeaderPatch(context.Background(), ApplyHeaderPatchPrm{
NewSplitHeader: modifPatch.NewSplitHeader,
NewAttributes: modifPatch.NewAttributes,
ReplaceAttributes: modifPatch.ReplaceAttributes,
})
require.ErrorIs(t, err, ErrHeaderPatchAlreadyApplied)
}
func TestPatchSplitHeader(t *testing.T) {
obj, _ := newTestObject()
const (
splitIDStr = "a59c9f87-14bc-4a61-95d1-7eb10f036163"
parentStr = "9cRjAaPqUt5zaDAjBkSCqFfPdkE8dHJ7mtRupRjPWp6E"
previosStr = "6WaTd9HobT4Z52NnKWHAtjqtQu2Ww5xZwNdT4ptshkKE"
)
splitID := objectSDK.NewSplitID()
require.NoError(t, splitID.Parse(splitIDStr))
var par, prev oid.ID
require.NoError(t, par.DecodeString(parentStr))
require.NoError(t, prev.DecodeString(previosStr))
splitHdr := objectSDK.NewSplitHeader()
splitHdr.SetSplitID(splitID)
splitHdr.SetParentID(par)
splitHdr.SetPreviousID(prev)
originalObjectPayload := []byte("*******************")
obj.SetPayload(originalObjectPayload)
obj.SetPayloadSize(uint64(len(originalObjectPayload)))
rangeProvider := &mockRangeProvider{
originalObjectPayload: originalObjectPayload,
}
t.Run("no payload patch", func(t *testing.T) {
patchedObj, _ := newTestObject()
wr := &mockPatchedObjectWriter{
obj: patchedObj,
}
modifPatch := &objectSDK.Patch{
NewSplitHeader: splitHdr,
}
prm := Params{
Header: obj.CutPayload(),
RangeProvider: rangeProvider,
ObjectWriter: wr,
}
patcher := New(prm)
err := patcher.ApplyHeaderPatch(context.Background(), ApplyHeaderPatchPrm{
NewSplitHeader: modifPatch.NewSplitHeader,
NewAttributes: modifPatch.NewAttributes,
ReplaceAttributes: modifPatch.ReplaceAttributes,
})
require.NoError(t, err)
splitHdrFromPatchedObj := patchedObj.SplitHeader()
require.NotNil(t, splitHdrFromPatchedObj)
patchObjParID, isSet := splitHdrFromPatchedObj.ParentID()
require.True(t, isSet)
require.True(t, patchObjParID.Equals(par))
patchObjPrevID, isSet := splitHdrFromPatchedObj.PreviousID()
require.True(t, isSet)
require.True(t, patchObjPrevID.Equals(prev))
require.Equal(t, splitHdrFromPatchedObj.SplitID().String(), splitID.String())
})
t.Run("with payload patch", func(t *testing.T) {
patchedObj, _ := newTestObject()
wr := &mockPatchedObjectWriter{
obj: patchedObj,
}
modifPatch := &objectSDK.Patch{
NewSplitHeader: splitHdr,
PayloadPatch: &objectSDK.PayloadPatch{
Range: rangeWithOffestWithLength(10, 0),
Chunk: []byte(""),
},
}
prm := Params{
Header: obj.CutPayload(),
RangeProvider: rangeProvider,
ObjectWriter: wr,
}
patcher := New(prm)
err := patcher.ApplyHeaderPatch(context.Background(), ApplyHeaderPatchPrm{
NewSplitHeader: modifPatch.NewSplitHeader,
NewAttributes: modifPatch.NewAttributes,
ReplaceAttributes: modifPatch.ReplaceAttributes,
})
require.NoError(t, err)
splitHdrFromPatchedObj := patchedObj.SplitHeader()
require.NotNil(t, splitHdrFromPatchedObj)
patchObjParID, isSet := splitHdrFromPatchedObj.ParentID()
require.True(t, isSet)
require.True(t, patchObjParID.Equals(par))
patchObjPrevID, isSet := splitHdrFromPatchedObj.PreviousID()
require.True(t, isSet)
require.True(t, patchObjPrevID.Equals(prev))
require.Equal(t, splitHdrFromPatchedObj.SplitID().String(), splitID.String())
err = patcher.ApplyPayloadPatch(context.Background(), modifPatch.PayloadPatch)
require.Error(t, err, ErrSplitHeaderPatchAppliedWithPayloadPatch)
})
}
func TestPatchEmptyPayloadPatch(t *testing.T) {
@ -224,7 +363,11 @@ func TestPatchEmptyPayloadPatch(t *testing.T) {
patcher := New(prm)
err := patcher.ApplyAttributesPatch(context.Background(), modifPatch.NewAttributes, modifPatch.ReplaceAttributes)
err := patcher.ApplyHeaderPatch(context.Background(), ApplyHeaderPatchPrm{
NewSplitHeader: modifPatch.NewSplitHeader,
NewAttributes: modifPatch.NewAttributes,
ReplaceAttributes: modifPatch.ReplaceAttributes,
})
require.NoError(t, err)
err = patcher.ApplyPayloadPatch(context.Background(), nil)
@ -599,7 +742,11 @@ func TestPatch(t *testing.T) {
for i, patch := range test.patches {
if i == 0 {
_ = patcher.ApplyAttributesPatch(context.Background(), patch.NewAttributes, patch.ReplaceAttributes)
_ = patcher.ApplyHeaderPatch(context.Background(), ApplyHeaderPatchPrm{
NewSplitHeader: patch.NewSplitHeader,
NewAttributes: patch.NewAttributes,
ReplaceAttributes: patch.ReplaceAttributes,
})
}
if patch.PayloadPatch == nil {

124
object/split_header.go Normal file
View file

@ -0,0 +1,124 @@
package object
import (
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/api/object"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/api/refs"
frostfscrypto "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/crypto"
oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
)
// SplitHeader is an object's header component that defines the relationship
// between this object and other objects if the object is part of a split operation.
type SplitHeader object.SplitHeader
// NewSplitHeaderFromV2 wraps v2 SplitHeader message to SplitHeader.
func NewSplitHeaderFromV2(v2 *object.SplitHeader) *SplitHeader {
return (*SplitHeader)(v2)
}
// NewSplitHeader creates blank SplitHeader instance.
func NewSplitHeader() *SplitHeader {
return NewSplitHeaderFromV2(new(object.SplitHeader))
}
func (sh *SplitHeader) ToV2() *object.SplitHeader {
return (*object.SplitHeader)(sh)
}
func (sh *SplitHeader) ParentID() (v oid.ID, isSet bool) {
v2 := (*object.SplitHeader)(sh)
if id := v2.GetParent(); id != nil {
_ = v.ReadFromV2(*id)
isSet = true
}
return
}
func (sh *SplitHeader) SetParentID(v oid.ID) {
v2 := new(refs.ObjectID)
v.WriteToV2(v2)
(*object.SplitHeader)(sh).SetParent(v2)
}
func (sh *SplitHeader) PreviousID() (v oid.ID, isSet bool) {
v2 := (*object.SplitHeader)(sh)
if id := v2.GetPrevious(); id != nil {
_ = v.ReadFromV2(*id)
isSet = true
}
return
}
func (sh *SplitHeader) SetPreviousID(v oid.ID) {
v2 := new(refs.ObjectID)
v.WriteToV2(v2)
(*object.SplitHeader)(sh).SetPrevious(v2)
}
func (sh *SplitHeader) ParentSignature() *frostfscrypto.Signature {
v2 := (*object.SplitHeader)(sh)
if parSigV2 := v2.GetParentSignature(); parSigV2 != nil {
parSig := new(frostfscrypto.Signature)
_ = parSig.ReadFromV2(*parSigV2)
}
return nil
}
func (sh *SplitHeader) SetParentSignature(v *frostfscrypto.Signature) {
var parSigV2 *refs.Signature
if v != nil {
parSigV2 = new(refs.Signature)
v.WriteToV2(parSigV2)
}
(*object.SplitHeader)(sh).SetParentSignature(parSigV2)
}
func (sh *SplitHeader) ParentHeader() (parentHeader *Object) {
v2 := (*object.SplitHeader)(sh)
if parHdr := v2.GetParentHeader(); parHdr != nil {
parentHeader = New()
parentHeader.setHeaderField(func(h *object.Header) {
*h = *v2.GetParentHeader()
})
}
return
}
func (sh *SplitHeader) SetParentHeader(parentHeader *Object) {
(*object.SplitHeader)(sh).SetParentHeader(parentHeader.ToV2().GetHeader())
}
func (sh *SplitHeader) Children() (res []oid.ID) {
v2 := (*object.SplitHeader)(sh)
if children := v2.GetChildren(); len(children) > 0 {
res = make([]oid.ID, len(children))
for i := range children {
_ = res[i].ReadFromV2(children[i])
}
}
return
}
func (sh *SplitHeader) SetChildren(children []oid.ID) {
v2Children := make([]refs.ObjectID, len(children))
for i := range children {
children[i].WriteToV2(&v2Children[i])
}
(*object.SplitHeader)(sh).SetChildren(v2Children)
}
func (sh *SplitHeader) SplitID() *SplitID {
return NewSplitIDFromV2((*object.SplitHeader)(sh).GetSplitID())
}
func (sh *SplitHeader) SetSplitID(v *SplitID) {
(*object.SplitHeader)(sh).SetSplitID(v.ToV2())
}

View file

@ -116,9 +116,14 @@ func fromObject(obj *object.Object) *object.Object {
res.SetAttributes(obj.Attributes()...)
res.SetType(obj.Type())
// obj.SetSplitID creates splitHeader but we don't need to do it in case
// of small objects, so we should make nil check.
if obj.SplitID() != nil {
// There are two ways to specify split information:
// 1. Using explicit SplitHeader. Thus, we only propagate whole split information
// if it's already set in the source object (use-case: Patch method).
// 2. Using SplitID - will automatically generate a SplitHeader, but this is not requiered for
// small objects.
if obj.SplitHeader() != nil {
res.SetSplitHeader(obj.SplitHeader())
} else if obj.SplitID() != nil {
res.SetSplitID(obj.SplitID())
}

View file

@ -66,7 +66,7 @@ func (a *Address) FromString(s string) error {
if err == nil {
a.ma, err = multiaddr.NewMultiaddr(s)
if err == nil && hasTLS {
a.ma = a.ma.Encapsulate(tls)
a.ma = a.ma.AppendComponent(tls)
}
}
}

View file

@ -9,7 +9,7 @@ const (
)
// tls var is used for (un)wrapping other multiaddrs around TLS multiaddr.
var tls, _ = multiaddr.NewMultiaddr("/" + tlsProtocolName)
var tls, _ = multiaddr.NewComponent(tlsProtocolName, "")
// IsTLSEnabled searches for wrapped TLS protocol in multiaddr.
func (a Address) IsTLSEnabled() bool {

View file

@ -90,6 +90,8 @@ func (m MethodIndex) String() string {
return "containerGet"
case methodContainerList:
return "containerList"
case methodContainerListStream:
return "containerListStream"
case methodContainerDelete:
return "containerDelete"
case methodEndpointInfo:
@ -457,13 +459,16 @@ type PrmListStream struct {
//
// Must be initialized using Pool.ListContainersStream, any other usage is unsafe.
type ResListStream struct {
r *sdkClient.ContainerListReader
handleError func(context.Context, apistatus.Status, error) error
r *sdkClient.ContainerListReader
elapsedTimeCallback func(time.Duration)
handleError func(context.Context, apistatus.Status, error) error
}
// Read reads another list of the container identifiers.
func (x *ResListStream) Read(buf []cid.ID) (int, error) {
start := time.Now()
n, ok := x.r.Read(buf)
x.elapsedTimeCallback(time.Since(start))
if !ok {
res, err := x.r.Close()
if err == nil {
@ -487,7 +492,14 @@ func (x *ResListStream) Read(buf []cid.ID) (int, error) {
//
// Returns an error if container can't be read.
func (x *ResListStream) Iterate(f func(cid.ID) bool) error {
return x.r.Iterate(f)
start := time.Now()
err := x.r.Iterate(func(id cid.ID) bool {
x.elapsedTimeCallback(time.Since(start))
stop := f(id)
start = time.Now()
return stop
})
return err
}
// Close ends reading list of the matched containers and returns the result of the operation
@ -508,11 +520,19 @@ func (c *clientWrapper) containerListStream(ctx context.Context, prm PrmListStre
Session: prm.Session,
}
res, err := cl.ContainerListInit(ctx, cliPrm)
start := time.Now()
cnrRdr, err := cl.ContainerListInit(ctx, cliPrm)
c.incRequests(time.Since(start), methodContainerListStream)
if err = c.handleError(ctx, nil, err); err != nil {
return ResListStream{}, fmt.Errorf("init container listing on client: %w", err)
}
return ResListStream{r: res, handleError: c.handleError}, nil
return ResListStream{
r: cnrRdr,
elapsedTimeCallback: func(elapsed time.Duration) {
c.incRequests(elapsed, methodContainerListStream)
},
handleError: c.handleError,
}, nil
}
// containerDelete invokes sdkClient.ContainerDelete parse response status to error.

View file

@ -2,6 +2,7 @@ package pool
import (
"context"
"fmt"
sdkClient "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client"
apistatus "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client/status"
@ -134,7 +135,7 @@ func (it *internalTarget) putAsStream(ctx context.Context, o *object.Object) err
it.res.OID = res.StoredObjectID()
it.res.Epoch = res.StoredEpoch()
}
return err
return fmt.Errorf("put as stream '%s': %w", it.address, err)
}
func (it *internalTarget) tryPutSingle(ctx context.Context, o *object.Object) (bool, error) {
@ -151,7 +152,7 @@ func (it *internalTarget) tryPutSingle(ctx context.Context, o *object.Object) (b
res, err := it.client.ObjectPutSingle(ctx, cliPrm)
if err != nil && status.Code(err) == codes.Unimplemented {
return false, err
return false, fmt.Errorf("address '%s': %w", it.address, err)
}
if err == nil {
@ -166,5 +167,5 @@ func (it *internalTarget) tryPutSingle(ctx context.Context, o *object.Object) (b
}
return true, nil
}
return true, err
return true, fmt.Errorf("try put single '%s': %w", it.address, err)
}

View file

@ -1110,6 +1110,12 @@ func (p *Pool) PatchObject(ctx context.Context, prm PrmObjectPatch) (ResPatchObj
return res, nil
}
// LatestReceivedEpoch returns the epoch number extracted from the metadata
// of responses received from the client pool's most recent request.
func (p *Pool) LatestReceivedEpoch() uint64 {
return p.cache.Epoch()
}
// PutObject writes an object through a remote server using FrostFS API protocol.
func (p *Pool) PutObject(ctx context.Context, prm PrmObjectPut) (ResPutObject, error) {
cnr, _ := prm.hdr.ContainerID()
@ -1646,7 +1652,7 @@ func (p *Pool) GetSplitInfo(ctx context.Context, cnrID cid.ID, objID oid.ID, tok
case errors.As(err, &errSplit):
return errSplit.SplitInfo(), nil
case err == nil || errors.As(err, &errECInfo):
return nil, relations.ErrNoSplitInfo
return nil, fmt.Errorf("failed to get raw object header %w", relations.ErrNoSplitInfo)
default:
return nil, fmt.Errorf("failed to get raw object header: %w", err)
}

View file

@ -372,7 +372,7 @@ func TestUpdateNodesHealth(t *testing.T) {
changed := tc.wasHealthy != tc.willHealthy
require.Equalf(t, tc.willHealthy, cli.isHealthy(), "healthy status should be: %v", tc.willHealthy)
require.Equalf(t, changed, 1 == log.Len(), "healthy status should be changed: %v", changed)
require.Equalf(t, changed, log.Len() == 1, "healthy status should be changed: %v", changed)
})
}
}

View file

@ -97,6 +97,11 @@ func (n NodeStatistic) AverageListContainer() time.Duration {
return n.averageTime(methodContainerList)
}
// AverageListContainerStream returns average time to perform ContainerListStream request.
func (n NodeStatistic) AverageListContainerStream() time.Duration {
return n.averageTime(methodContainerListStream)
}
// AverageDeleteContainer returns average time to perform ContainerDelete request.
func (n NodeStatistic) AverageDeleteContainer() time.Duration {
return n.averageTime(methodContainerDelete)

View file

@ -49,11 +49,11 @@ func (c *treeClient) dial(ctx context.Context) error {
var err error
c.client, err = c.createClient()
if err != nil {
return err
return fmt.Errorf("couldn't dial '%s': %w", c.address, err)
}
if _, err = rpcapi.Healthcheck(c.client, &tree.HealthcheckRequest{}, rpcclient.WithContext(ctx)); err != nil {
return fmt.Errorf("healthcheck tree service: %w", err)
return fmt.Errorf("healthcheck tree service '%s': %w", c.address, err)
}
c.healthy = true
@ -127,5 +127,9 @@ func (c *treeClient) close() error {
if c.client == nil || c.client.Conn() == nil {
return nil
}
return c.client.Conn().Close()
err := c.client.Conn().Close()
if err != nil {
return fmt.Errorf("address '%s': %w", c.address, err)
}
return nil
}

View file

@ -295,7 +295,7 @@ func (p *Pool) Dial(ctx context.Context) error {
for j, node := range nodes {
clients[j] = newTreeClient(node.Address(), p.dialOptions, p.nodeDialTimeout, p.streamTimeout)
if err := clients[j].dial(ctx); err != nil {
p.log(zap.WarnLevel, "failed to dial tree client", zap.String("address", node.Address()), zap.Error(err))
p.log(zap.WarnLevel, "failed to dial tree client", zap.Error(err))
continue
}
@ -1081,21 +1081,16 @@ func (p *Pool) deleteClientFromMap(hash uint64) {
}
func (p *Pool) getNewTreeClient(ctx context.Context, node netmap.NodeInfo) (*treeClient, error) {
var (
treeCl *treeClient
err error
)
node.IterateNetworkEndpoints(func(endpoint string) bool {
for endpoint := range node.NetworkEndpoints() {
var addr network.Address
if err = addr.FromString(endpoint); err != nil {
if err := addr.FromString(endpoint); err != nil {
p.log(zap.WarnLevel, "can't parse endpoint", zap.String("endpoint", endpoint), zap.Error(err))
return false
continue
}
newTreeCl := newTreeClient(addr.URIAddr(), p.dialOptions, p.nodeDialTimeout, p.streamTimeout)
if err = newTreeCl.dial(ctx); err != nil {
p.log(zap.WarnLevel, "failed to dial tree client", zap.String("address", addr.URIAddr()), zap.Error(err))
if err := newTreeCl.dial(ctx); err != nil {
p.log(zap.WarnLevel, "failed to dial tree client", zap.Error(err))
// We have to close connection here after failed `dial()`.
// This is NOT necessary in object pool and regular tree pool without netmap support, because:
@ -1103,21 +1098,16 @@ func (p *Pool) getNewTreeClient(ctx context.Context, node netmap.NodeInfo) (*tre
// - regular tree pool is going to reuse connection by calling `redialIfNecessary()`.
// Tree pool with netmap support does not operate with background goroutine, so we have to close connection immediately.
if err = newTreeCl.close(); err != nil {
p.log(zap.WarnLevel, "failed to close recently dialed tree client", zap.String("address", addr.URIAddr()), zap.Error(err))
p.log(zap.WarnLevel, "failed to close recently dialed tree client", zap.Error(err))
}
return false
continue
}
treeCl = newTreeCl
return true
})
if treeCl == nil {
return nil, fmt.Errorf("tree client wasn't initialized")
return newTreeCl, nil
}
return treeCl, nil
return nil, fmt.Errorf("tree client wasn't initialized")
}
func shouldTryAgain(err error) bool {