Compare commits

..

1 commit

Author SHA1 Message Date
b464c4c3cf Use netmap in tree pool requests
Signed-off-by: Marina Biryukova <m.biryukova@yadro.com>
2024-09-04 15:41:46 +03:00
52 changed files with 332 additions and 1061 deletions

2
.gitignore vendored
View file

@ -23,7 +23,7 @@ coverage.txt
coverage.html coverage.html
# antlr tool jar # antlr tool jar
antlr*.jar antlr-*.jar
# tempfiles # tempfiles
.cache .cache

View file

@ -63,6 +63,5 @@ linters:
- funlen - funlen
- gocognit - gocognit
- contextcheck - contextcheck
- protogetter
disable-all: true disable-all: true
fast: false fast: false

View file

@ -1,6 +1,6 @@
#!/usr/bin/make -f #!/usr/bin/make -f
ANTLR_VERSION=4.13.1 ANTLR_VERSION="4.13.0"
TMP_DIR := .cache TMP_DIR := .cache
LINT_VERSION ?= 1.60.1 LINT_VERSION ?= 1.60.1
TRUECLOUDLAB_LINT_VERSION ?= 0.0.6 TRUECLOUDLAB_LINT_VERSION ?= 0.0.6
@ -53,8 +53,7 @@ format:
policy: policy:
@wget -q https://www.antlr.org/download/antlr-${ANTLR_VERSION}-complete.jar -O antlr4-tool.jar @wget -q https://www.antlr.org/download/antlr-${ANTLR_VERSION}-complete.jar -O antlr4-tool.jar
@java -Xmx500M -cp antlr4-tool.jar org.antlr.v4.Tool -Dlanguage=Go \ @java -Xmx500M -cp "`pwd`/antlr4-tool.jar" "org.antlr.v4.Tool" -o `pwd`/netmap/parser/ -Dlanguage=Go -no-listener -visitor `pwd`/netmap/parser/Query.g4 `pwd`/netmap/parser/QueryLexer.g4
-no-listener -visitor netmap/parser/Query.g4 netmap/parser/QueryLexer.g4
# Run `make %` in truecloudlab/frostfs-sdk-go container(Golang+Java) # Run `make %` in truecloudlab/frostfs-sdk-go container(Golang+Java)
docker/%: docker/%:
@ -78,15 +77,3 @@ help:
@echo ' Targets:' @echo ' Targets:'
@echo '' @echo ''
@awk '/^#/{ comment = substr($$0,3) } comment && /^[a-zA-Z][a-zA-Z0-9_-]+ ?:/{ print " ", $$1, comment }' $(MAKEFILE_LIST) | column -t -s ':' | grep -v 'IGNORE' | sort -u @awk '/^#/{ comment = substr($$0,3) } comment && /^[a-zA-Z][a-zA-Z0-9_-]+ ?:/{ print " ", $$1, comment }' $(MAKEFILE_LIST) | column -t -s ':' | grep -v 'IGNORE' | sort -u
# Activate pre-commit hooks
pre-commit:
pre-commit install --hook-type pre-commit
# Deactivate pre-commit hooks
unpre-commit:
pre-commit uninstall --hook-type pre-commit
# Run pre-commit hooks
pre-commit-run:
@pre-commit run --all-files --hook-stage manual

View file

@ -65,7 +65,7 @@ func (c *Client) APEManagerListChains(ctx context.Context, prm PrmAPEManagerList
var res ResAPEManagerListChains var res ResAPEManagerListChains
res.st, err = c.processResponse(resp) res.st, err = c.processResponse(resp)
if err != nil || !apistatus.IsSuccessful(res.st) { if err != nil || !apistatus.IsSuccessful(res.st) {
return &res, err return nil, err
} }
for _, ch := range resp.GetBody().GetChains() { for _, ch := range resp.GetBody().GetChains() {

View file

@ -11,8 +11,6 @@ import (
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/rpc" "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/rpc"
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/rpc/client" "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/rpc/client"
"google.golang.org/grpc" "google.golang.org/grpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
) )
// Client represents virtual connection to the FrostFS network to communicate // Client represents virtual connection to the FrostFS network to communicate
@ -100,28 +98,13 @@ func (c *Client) Dial(ctx context.Context, prm PrmDial) error {
c.setFrostFSAPIServer((*coreServer)(&c.c)) c.setFrostFSAPIServer((*coreServer)(&c.c))
ctx, cancel := context.WithTimeout(ctx, prm.DialTimeout) // TODO: (neofs-api-go#382) perform generic dial stage of the client.Client
defer cancel()
_, err := rpc.Balance(&c.c, new(v2accounting.BalanceRequest), _, err := rpc.Balance(&c.c, new(v2accounting.BalanceRequest),
client.WithContext(ctx), client.WithContext(ctx),
) )
if err != nil {
var ctxErr error
// return context errors since they signal about dial problem // return context errors since they signal about dial problem
if errors.Is(err, context.Canceled) || errors.Is(err, context.DeadlineExceeded) { if errors.Is(err, context.Canceled) || errors.Is(err, context.DeadlineExceeded) {
ctxErr = err return err
} else if st, ok := status.FromError(err); ok && st.Code() == codes.Canceled {
ctxErr = context.Canceled
} else if ok && st.Code() == codes.DeadlineExceeded {
ctxErr = context.DeadlineExceeded
}
if ctxErr != nil {
if conn := c.c.Conn(); conn != nil {
_ = conn.Close()
}
return ctxErr
}
} }
return nil return nil

View file

@ -239,8 +239,12 @@ func (c *Client) NetMapSnapshot(ctx context.Context, _ PrmNetMapSnapshot) (*ResN
var res ResNetMapSnapshot var res ResNetMapSnapshot
res.st, err = c.processResponse(resp) res.st, err = c.processResponse(resp)
if err != nil || !apistatus.IsSuccessful(res.st) { if err != nil {
return &res, err return nil, err
}
if !apistatus.IsSuccessful(res.st) {
return &res, nil
} }
const fieldNetMap = "network map" const fieldNetMap = "network map"

View file

@ -148,8 +148,12 @@ func (c *Client) ObjectDelete(ctx context.Context, prm PrmObjectDelete) (*ResObj
var res ResObjectDelete var res ResObjectDelete
res.st, err = c.processResponse(resp) res.st, err = c.processResponse(resp)
if err != nil || !apistatus.IsSuccessful(res.st) { if err != nil {
return &res, err return nil, err
}
if !apistatus.IsSuccessful(res.st) {
return &res, nil
} }
const fieldTombstone = "tombstone" const fieldTombstone = "tombstone"

View file

@ -492,8 +492,12 @@ func (c *Client) ObjectHead(ctx context.Context, prm PrmObjectHead) (*ResObjectH
var res ResObjectHead var res ResObjectHead
res.st, err = c.processResponse(resp) res.st, err = c.processResponse(resp)
if err != nil || !apistatus.IsSuccessful(res.st) { if err != nil {
return &res, err return nil, err
}
if !apistatus.IsSuccessful(res.st) {
return &res, nil
} }
res.idObj = *prm.ObjectID res.idObj = *prm.ObjectID

View file

@ -189,8 +189,12 @@ func (c *Client) ObjectHash(ctx context.Context, prm PrmObjectHash) (*ResObjectH
var res ResObjectHash var res ResObjectHash
res.st, err = c.processResponse(resp) res.st, err = c.processResponse(resp)
if err != nil || !apistatus.IsSuccessful(res.st) { if err != nil {
return &res, err return nil, err
}
if !apistatus.IsSuccessful(res.st) {
return &res, nil
} }
res.checksums = resp.GetBody().GetHashList() res.checksums = resp.GetBody().GetHashList()

View file

@ -106,6 +106,7 @@ func (c *Client) ObjectPatchInit(ctx context.Context, prm PrmObjectPatch) (Objec
} }
objectPatcher.client = c objectPatcher.client = c
objectPatcher.stream = stream objectPatcher.stream = stream
objectPatcher.firstPatchPayload = true
if prm.MaxChunkLength > 0 { if prm.MaxChunkLength > 0 {
objectPatcher.maxChunkLen = prm.MaxChunkLength objectPatcher.maxChunkLen = prm.MaxChunkLength
@ -153,6 +154,8 @@ type objectPatcher struct {
respV2 v2object.PatchResponse respV2 v2object.PatchResponse
maxChunkLen int maxChunkLen int
firstPatchPayload bool
} }
func (x *objectPatcher) PatchAttributes(_ context.Context, newAttrs []object.Attribute, replace bool) bool { func (x *objectPatcher) PatchAttributes(_ context.Context, newAttrs []object.Attribute, replace bool) bool {
@ -168,33 +171,19 @@ func (x *objectPatcher) PatchPayload(_ context.Context, rng *object.Range, paylo
buf := make([]byte, x.maxChunkLen) buf := make([]byte, x.maxChunkLen)
for patchIter := 0; ; patchIter++ { for {
n, err := payloadReader.Read(buf) n, err := payloadReader.Read(buf)
if err != nil && err != io.EOF { if err != nil && err != io.EOF {
x.err = fmt.Errorf("read payload: %w", err) x.err = fmt.Errorf("read payload: %w", err)
return false return false
} }
if n == 0 { if n == 0 {
if patchIter == 0 {
if rng.GetLength() == 0 {
x.err = errors.New("zero-length empty payload patch can't be applied")
return false
}
if !x.patch(&object.Patch{
Address: x.addr,
PayloadPatch: &object.PayloadPatch{
Range: rng,
Chunk: []byte{},
},
}) {
return false
}
}
break break
} }
rngPart := object.NewRange() rngPart := object.NewRange()
if patchIter == 0 { if x.firstPatchPayload {
x.firstPatchPayload = false
rngPart.SetOffset(offset) rngPart.SetOffset(offset)
rngPart.SetLength(rng.GetLength()) rngPart.SetLength(rng.GetLength())
} else { } else {
@ -246,8 +235,12 @@ func (x *objectPatcher) Close(_ context.Context) (*ResObjectPatch, error) {
} }
x.res.st, x.err = x.client.processResponse(&x.respV2) x.res.st, x.err = x.client.processResponse(&x.respV2)
if x.err != nil || !apistatus.IsSuccessful(x.res.st) { if x.err != nil {
return &x.res, x.err return nil, x.err
}
if !apistatus.IsSuccessful(x.res.st) {
return &x.res, nil
} }
const fieldID = "ID" const fieldID = "ID"

View file

@ -175,6 +175,7 @@ func TestObjectPatcher(t *testing.T) {
addr: oidtest.Address(), addr: oidtest.Address(),
key: pk, key: pk,
maxChunkLen: test.maxChunkLen, maxChunkLen: test.maxChunkLen,
firstPatchPayload: true,
} }
success := patcher.PatchAttributes(context.Background(), nil, false) success := patcher.PatchAttributes(context.Background(), nil, false)
@ -193,93 +194,6 @@ func TestObjectPatcher(t *testing.T) {
} }
} }
func TestRepeatPayloadPatch(t *testing.T) {
t.Run("no payload patch partioning", func(t *testing.T) {
m := &mockPatchStream{}
pk, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
const maxChunkLen = 20
patcher := objectPatcher{
client: &Client{},
stream: m,
addr: oidtest.Address(),
key: pk,
maxChunkLen: maxChunkLen,
}
for _, pp := range []struct {
patchPayload string
rng *object.Range
}{
{
patchPayload: "xxxxxxxxxx",
rng: newRange(1, 6),
},
{
patchPayload: "yyyyyyyyyy",
rng: newRange(5, 9),
},
{
patchPayload: "zzzzzzzzzz",
rng: newRange(10, 0),
},
} {
success := patcher.PatchPayload(context.Background(), pp.rng, bytes.NewReader([]byte(pp.patchPayload)))
require.True(t, success)
}
requireRangeChunk(t, m.streamedPayloadPatches[0], 1, 6, "xxxxxxxxxx")
requireRangeChunk(t, m.streamedPayloadPatches[1], 5, 9, "yyyyyyyyyy")
requireRangeChunk(t, m.streamedPayloadPatches[2], 10, 0, "zzzzzzzzzz")
})
t.Run("payload patch partioning", func(t *testing.T) {
m := &mockPatchStream{}
pk, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
const maxChunkLen = 5
patcher := objectPatcher{
client: &Client{},
stream: m,
addr: oidtest.Address(),
key: pk,
maxChunkLen: maxChunkLen,
}
for _, pp := range []struct {
patchPayload string
rng *object.Range
}{
{
patchPayload: "xxxxxxxxxx",
rng: newRange(1, 6),
},
{
patchPayload: "yyyyyyyyyy",
rng: newRange(5, 9),
},
{
patchPayload: "zzzzzzzzzz",
rng: newRange(10, 0),
},
} {
success := patcher.PatchPayload(context.Background(), pp.rng, bytes.NewReader([]byte(pp.patchPayload)))
require.True(t, success)
}
requireRangeChunk(t, m.streamedPayloadPatches[0], 1, 6, "xxxxx")
requireRangeChunk(t, m.streamedPayloadPatches[1], 7, 0, "xxxxx")
requireRangeChunk(t, m.streamedPayloadPatches[2], 5, 9, "yyyyy")
requireRangeChunk(t, m.streamedPayloadPatches[3], 14, 0, "yyyyy")
requireRangeChunk(t, m.streamedPayloadPatches[4], 10, 0, "zzzzz")
requireRangeChunk(t, m.streamedPayloadPatches[5], 10, 0, "zzzzz")
})
}
func requireRangeChunk(t *testing.T, pp *object.PayloadPatch, offset, length int, chunk string) { func requireRangeChunk(t *testing.T, pp *object.PayloadPatch, offset, length int, chunk string) {
require.NotNil(t, pp) require.NotNil(t, pp)
require.Equal(t, uint64(offset), pp.Range.GetOffset()) require.Equal(t, uint64(offset), pp.Range.GetOffset())

View file

@ -156,8 +156,12 @@ func (x *objectWriterRaw) Close(_ context.Context) (*ResObjectPut, error) {
} }
x.res.st, x.err = x.client.processResponse(&x.respV2) x.res.st, x.err = x.client.processResponse(&x.respV2)
if x.err != nil || !apistatus.IsSuccessful(x.res.st) { if x.err != nil {
return &x.res, x.err return nil, x.err
}
if !apistatus.IsSuccessful(x.res.st) {
return &x.res, nil
} }
const fieldID = "ID" const fieldID = "ID"

View file

@ -167,7 +167,7 @@ func (c *Client) ObjectPutSingle(ctx context.Context, prm PrmObjectPutSingle) (*
var res ResObjectPutSingle var res ResObjectPutSingle
res.st, err = c.processResponse(resp) res.st, err = c.processResponse(resp)
if err != nil { if err != nil {
return &res, err return nil, err
} }
res.epoch = resp.GetMetaHeader().GetEpoch() res.epoch = resp.GetMetaHeader().GetEpoch()

View file

@ -220,7 +220,7 @@ func (x *ObjectListReader) Close() (*ResObjectSearch, error) {
defer x.cancelCtxStream() defer x.cancelCtxStream()
if x.err != nil && !errors.Is(x.err, io.EOF) { if x.err != nil && !errors.Is(x.err, io.EOF) {
return &x.res, x.err return nil, x.err
} }
return &x.res, nil return &x.res, nil

View file

@ -238,61 +238,3 @@ func (x *NodeUnderMaintenance) SetMessage(v string) {
func (x NodeUnderMaintenance) Message() string { func (x NodeUnderMaintenance) Message() string {
return x.v2.Message() return x.v2.Message()
} }
// InvalidArgument describes failure status related to invalid argument.
// Instances provide Status and StatusV2 interfaces.
type InvalidArgument struct {
v2 status.Status
}
const defaultInvalidArgumentMsg = "argument is invalid"
// Error implements the error interface.
func (x *InvalidArgument) Error() string {
msg := x.v2.Message()
if msg == "" {
msg = defaultInvalidArgumentMsg
}
return errMessageStatusV2(
globalizeCodeV2(status.InvalidArgument, status.GlobalizeCommonFail),
msg,
)
}
// implements local interface defined in FromStatusV2 func.
func (x *InvalidArgument) fromStatusV2(st *status.Status) {
x.v2 = *st
}
// ToStatusV2 implements StatusV2 interface method.
// If the value was returned by FromStatusV2, returns the source message.
// Otherwise, returns message with
// - code: INVALID_ARGUMENT;
// - string message: written message via SetMessage or
// "argument is invalid" as a default message;
// - details: empty.
func (x InvalidArgument) ToStatusV2() *status.Status {
x.v2.SetCode(globalizeCodeV2(status.InvalidArgument, status.GlobalizeCommonFail))
if x.v2.Message() == "" {
x.v2.SetMessage(defaultInvalidArgumentMsg)
}
return &x.v2
}
// SetMessage writes invalid argument failure message.
// Message should be used for debug purposes only.
//
// See also Message.
func (x *InvalidArgument) SetMessage(v string) {
x.v2.SetMessage(v)
}
// Message returns status message. Zero status returns empty message.
// Message should be used for debug purposes only.
//
// See also SetMessage.
func (x InvalidArgument) Message() string {
return x.v2.Message()
}

View file

@ -114,7 +114,7 @@ func TestNodeUnderMaintenance(t *testing.T) {
stV2 := st.ToStatusV2() stV2 := st.ToStatusV2()
require.Equal(t, "node is under maintenance", stV2.Message()) require.Empty(t, "", stV2.Message())
}) })
t.Run("non-empty to V2", func(t *testing.T) { t.Run("non-empty to V2", func(t *testing.T) {
@ -128,42 +128,3 @@ func TestNodeUnderMaintenance(t *testing.T) {
require.Equal(t, msg, stV2.Message()) require.Equal(t, msg, stV2.Message())
}) })
} }
func TestInvalidArgument(t *testing.T) {
t.Run("default", func(t *testing.T) {
var st apistatus.InvalidArgument
require.Empty(t, st.Message())
})
t.Run("custom message", func(t *testing.T) {
var st apistatus.InvalidArgument
msg := "some message"
st.SetMessage(msg)
stV2 := st.ToStatusV2()
require.Equal(t, msg, st.Message())
require.Equal(t, msg, stV2.Message())
})
t.Run("empty to V2", func(t *testing.T) {
var st apistatus.InvalidArgument
stV2 := st.ToStatusV2()
require.Equal(t, "argument is invalid", stV2.Message())
})
t.Run("non-empty to V2", func(t *testing.T) {
var st apistatus.InvalidArgument
msg := "some other msg"
st.SetMessage(msg)
stV2 := st.ToStatusV2()
require.Equal(t, msg, stV2.Message())
})
}

View file

@ -33,29 +33,12 @@ type StatusV2 interface {
// //
// Common failures: // Common failures:
// - status.Internal: *ServerInternal; // - status.Internal: *ServerInternal;
// - status.WrongMagicNumber: *WrongMagicNumber; // - status.SignatureVerificationFail: *SignatureVerification.
// - status.SignatureVerificationFail: *SignatureVerification;
// - status.NodeUnderMaintenance: *NodeUnderMaintenance;
// - status.InvalidArgument: *InvalidArgument.
// //
// Object failures: // Object failures:
// - object.StatusLocked: *ObjectLocked; // - object.StatusLocked: *ObjectLocked;
// - object.StatusLockNonRegularObject: *LockNonRegularObject; // - object.StatusLockNonRegularObject: *LockNonRegularObject.
// - object.StatusAccessDenied: *ObjectAccessDenied; // - object.StatusAccessDenied: *ObjectAccessDenied.
// - object.StatusNotFound: *ObjectNotFound;
// - object.StatusAlreadyRemoved: *ObjectAlreadyRemoved;
// - object.StatusOutOfRange: *ObjectOutOfRange.
//
// Container failures:
// - container.StatusNotFound: *ContainerNotFound;
// - container.StatusEACLNotFound: *EACLNotFound.
//
// Session failures:
// - session.StatusTokenNotFound: *SessionTokenNotFound;
// - session.StatusTokenExpired: *SessionTokenExpired.
//
// APE Manager failures
// - apemanager.StatusAPEManagerAccessDenied: *APEManagerAccessDenied.
func FromStatusV2(st *status.Status) Status { func FromStatusV2(st *status.Status) Status {
var decoder interface { var decoder interface {
fromStatusV2(*status.Status) fromStatusV2(*status.Status)
@ -78,8 +61,6 @@ func FromStatusV2(st *status.Status) Status {
decoder = new(SignatureVerification) decoder = new(SignatureVerification)
case status.NodeUnderMaintenance: case status.NodeUnderMaintenance:
decoder = new(NodeUnderMaintenance) decoder = new(NodeUnderMaintenance)
case status.InvalidArgument:
decoder = new(InvalidArgument)
} }
case object.LocalizeFailStatus(&code): case object.LocalizeFailStatus(&code):
switch code { switch code {

View file

@ -61,24 +61,6 @@ func TestToStatusV2(t *testing.T) {
}), }),
codeV2: 1025, codeV2: 1025,
}, },
{
status: (statusConstructor)(func() apistatus.Status {
return new(apistatus.SignatureVerification)
}),
codeV2: 1026,
},
{
status: (statusConstructor)(func() apistatus.Status {
return new(apistatus.NodeUnderMaintenance)
}),
codeV2: 1027,
},
{
status: (statusConstructor)(func() apistatus.Status {
return new(apistatus.InvalidArgument)
}),
codeV2: 1028,
},
{ {
status: (statusConstructor)(func() apistatus.Status { status: (statusConstructor)(func() apistatus.Status {
return new(apistatus.ObjectLocked) return new(apistatus.ObjectLocked)
@ -149,6 +131,12 @@ func TestToStatusV2(t *testing.T) {
}), }),
codeV2: 5120, codeV2: 5120,
}, },
{
status: (statusConstructor)(func() apistatus.Status {
return new(apistatus.NodeUnderMaintenance)
}),
codeV2: 1027,
},
} { } {
var st apistatus.Status var st apistatus.Status

View file

@ -308,7 +308,7 @@ func (x *Container) SetAttribute(key, value string) {
attrs := x.v2.GetAttributes() attrs := x.v2.GetAttributes()
ln := len(attrs) ln := len(attrs)
for i := range ln { for i := 0; i < ln; i++ {
if attrs[i].GetKey() == key { if attrs[i].GetKey() == key {
attrs[i].SetValue(value) attrs[i].SetValue(value)
return return
@ -356,7 +356,8 @@ func (x Container) IterateUserAttributes(f func(key, val string)) {
attrs := x.v2.GetAttributes() attrs := x.v2.GetAttributes()
for _, attr := range attrs { for _, attr := range attrs {
key := attr.GetKey() key := attr.GetKey()
if !strings.HasPrefix(key, container.SysAttributePrefix) { if !strings.HasPrefix(key, container.SysAttributePrefix) &&
!strings.HasPrefix(key, container.SysAttributePrefixNeoFS) {
f(key, attr.GetValue()) f(key, attr.GetValue())
} }
} }
@ -416,7 +417,8 @@ func DisableHomomorphicHashing(cnr *Container) {
// //
// Zero Container has enabled hashing. // Zero Container has enabled hashing.
func IsHomomorphicHashingDisabled(cnr Container) bool { func IsHomomorphicHashingDisabled(cnr Container) bool {
return cnr.Attribute(container.SysAttributeHomomorphicHashing) == attributeHomoHashEnabled return cnr.Attribute(container.SysAttributeHomomorphicHashing) == attributeHomoHashEnabled ||
cnr.Attribute(container.SysAttributeHomomorphicHashingNeoFS) == attributeHomoHashEnabled
} }
// Domain represents information about container domain registered in the NNS // Domain represents information about container domain registered in the NNS
@ -465,6 +467,9 @@ func ReadDomain(cnr Container) (res Domain) {
if name := cnr.Attribute(container.SysAttributeName); name != "" { if name := cnr.Attribute(container.SysAttributeName); name != "" {
res.SetName(name) res.SetName(name)
res.SetZone(cnr.Attribute(container.SysAttributeZone)) res.SetZone(cnr.Attribute(container.SysAttributeZone))
} else if name = cnr.Attribute(container.SysAttributeNameNeoFS); name != "" {
res.SetName(name)
res.SetZone(cnr.Attribute(container.SysAttributeZoneNeoFS))
} }
return return

View file

@ -150,7 +150,7 @@ func assertContainsAttribute(t *testing.T, m v2container.Container, key, val str
} }
func TestContainer_Attribute(t *testing.T) { func TestContainer_Attribute(t *testing.T) {
const attrKey1, attrKey2 = v2container.SysAttributePrefix + "key1", v2container.SysAttributePrefix + "key2" const attrKey1, attrKey2 = v2container.SysAttributePrefix + "key1", v2container.SysAttributePrefixNeoFS + "key2"
const attrVal1, attrVal2 = "val1", "val2" const attrVal1, attrVal2 = "val1", "val2"
val := containertest.Container() val := containertest.Container()

View file

@ -286,13 +286,13 @@ func equalRecords(r1, r2 Record) bool {
return false return false
} }
for i := range len(fs1) { for i := 0; i < len(fs1); i++ {
if !equalFilters(fs1[i], fs2[i]) { if !equalFilters(fs1[i], fs2[i]) {
return false return false
} }
} }
for i := range len(ts1) { for i := 0; i < len(ts1); i++ {
if !equalTargets(ts1[i], ts2[i]) { if !equalTargets(ts1[i], ts2[i]) {
return false return false
} }

View file

@ -212,7 +212,7 @@ func EqualTables(t1, t2 Table) bool {
return false return false
} }
for i := range len(rs1) { for i := 0; i < len(rs1); i++ {
if !equalRecords(rs1[i], rs2[i]) { if !equalRecords(rs1[i], rs2[i]) {
return false return false
} }

View file

@ -51,7 +51,7 @@ func SetTargetECDSAKeys(t *Target, pubs ...*ecdsa.PublicKey) {
binKeys = make([][]byte, 0, ln) binKeys = make([][]byte, 0, ln)
} }
for i := range ln { for i := 0; i < ln; i++ {
binKeys = append(binKeys, (*keys.PublicKey)(pubs[i]).Bytes()) binKeys = append(binKeys, (*keys.PublicKey)(pubs[i]).Bytes())
} }
@ -67,7 +67,7 @@ func TargetECDSAKeys(t *Target) []*ecdsa.PublicKey {
pubs := make([]*ecdsa.PublicKey, ln) pubs := make([]*ecdsa.PublicKey, ln)
for i := range ln { for i := 0; i < ln; i++ {
p := new(keys.PublicKey) p := new(keys.PublicKey)
if p.DecodeBytes(binKeys[i]) == nil { if p.DecodeBytes(binKeys[i]) == nil {
pubs[i] = (*ecdsa.PublicKey)(p) pubs[i] = (*ecdsa.PublicKey)(p)
@ -169,7 +169,7 @@ func equalTargets(t1, t2 Target) bool {
return false return false
} }
for i := range len(keys1) { for i := 0; i < len(keys1); i++ {
if !bytes.Equal(keys1[i], keys2[i]) { if !bytes.Equal(keys1[i], keys2[i]) {
return false return false
} }

View file

@ -19,7 +19,7 @@ func baseBenchmarkTableBinaryComparison(b *testing.B, factor int) {
b.StopTimer() b.StopTimer()
b.ResetTimer() b.ResetTimer()
b.StartTimer() b.StartTimer()
for range b.N { for i := 0; i < b.N; i++ {
got, _ := t.Marshal() got, _ := t.Marshal()
if !bytes.Equal(exp, got) { if !bytes.Equal(exp, got) {
b.Fail() b.Fail()
@ -38,7 +38,7 @@ func baseBenchmarkTableEqualsComparison(b *testing.B, factor int) {
b.StopTimer() b.StopTimer()
b.ResetTimer() b.ResetTimer()
b.StartTimer() b.StartTimer()
for range b.N { for i := 0; i < b.N; i++ {
if !eacl.EqualTables(*t, *t2) { if !eacl.EqualTables(*t, *t2) {
b.Fail() b.Fail()
} }
@ -76,7 +76,7 @@ func TargetN(n int) *eacl.Target {
x.SetRole(eacl.RoleSystem) x.SetRole(eacl.RoleSystem)
keys := make([][]byte, n) keys := make([][]byte, n)
for i := range n { for i := 0; i < n; i++ {
keys[i] = make([]byte, 32) keys[i] = make([]byte, 32)
rand.Read(keys[i]) rand.Read(keys[i])
} }
@ -94,7 +94,7 @@ func RecordN(n int) *eacl.Record {
x.SetOperation(eacl.OperationRangeHash) x.SetOperation(eacl.OperationRangeHash)
x.SetTargets(*TargetN(n)) x.SetTargets(*TargetN(n))
for range n { for i := 0; i < n; i++ {
x.AddFilter(eacl.HeaderFromObject, eacl.MatchStringEqual, "", cidtest.ID().EncodeToString()) x.AddFilter(eacl.HeaderFromObject, eacl.MatchStringEqual, "", cidtest.ID().EncodeToString())
} }
@ -106,7 +106,7 @@ func TableN(n int) *eacl.Table {
x.SetCID(cidtest.ID()) x.SetCID(cidtest.ID())
for range n { for i := 0; i < n; i++ {
x.AddRecord(RecordN(n)) x.AddRecord(RecordN(n))
} }

22
go.mod
View file

@ -3,11 +3,11 @@ module git.frostfs.info/TrueCloudLab/frostfs-sdk-go
go 1.22 go 1.22
require ( require (
git.frostfs.info/TrueCloudLab/frostfs-api-go/v2 v2.16.1-0.20241011114054-f0fc40e116d1 git.frostfs.info/TrueCloudLab/frostfs-api-go/v2 v2.16.1-0.20240902111049-c11f50efeccb
git.frostfs.info/TrueCloudLab/frostfs-contract v0.19.3-0.20240621131249-49e5270f673e git.frostfs.info/TrueCloudLab/frostfs-contract v0.19.3-0.20240621131249-49e5270f673e
git.frostfs.info/TrueCloudLab/hrw v1.2.1 git.frostfs.info/TrueCloudLab/hrw v1.2.1
git.frostfs.info/TrueCloudLab/tzhash v1.8.0 git.frostfs.info/TrueCloudLab/tzhash v1.8.0
github.com/antlr4-go/antlr/v4 v4.13.1 github.com/antlr4-go/antlr/v4 v4.13.0
github.com/google/uuid v1.6.0 github.com/google/uuid v1.6.0
github.com/hashicorp/golang-lru/v2 v2.0.7 github.com/hashicorp/golang-lru/v2 v2.0.7
github.com/klauspost/reedsolomon v1.12.1 github.com/klauspost/reedsolomon v1.12.1
@ -16,8 +16,8 @@ require (
github.com/nspcc-dev/neo-go v0.106.2 github.com/nspcc-dev/neo-go v0.106.2
github.com/stretchr/testify v1.9.0 github.com/stretchr/testify v1.9.0
go.uber.org/zap v1.27.0 go.uber.org/zap v1.27.0
google.golang.org/grpc v1.66.2 google.golang.org/grpc v1.63.2
google.golang.org/protobuf v1.34.1 google.golang.org/protobuf v1.33.0
gopkg.in/yaml.v3 v3.0.1 gopkg.in/yaml.v3 v3.0.1
) )
@ -49,11 +49,11 @@ require (
github.com/twmb/murmur3 v1.1.8 // indirect github.com/twmb/murmur3 v1.1.8 // indirect
go.etcd.io/bbolt v1.3.9 // indirect go.etcd.io/bbolt v1.3.9 // indirect
go.uber.org/multierr v1.11.0 // indirect go.uber.org/multierr v1.11.0 // indirect
golang.org/x/crypto v0.24.0 // indirect golang.org/x/crypto v0.21.0 // indirect
golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 // indirect golang.org/x/exp v0.0.0-20240222234643-814bf88cf225 // indirect
golang.org/x/net v0.26.0 // indirect golang.org/x/net v0.23.0 // indirect
golang.org/x/sync v0.7.0 // indirect golang.org/x/sync v0.6.0 // indirect
golang.org/x/sys v0.21.0 // indirect golang.org/x/sys v0.18.0 // indirect
golang.org/x/text v0.16.0 // indirect golang.org/x/text v0.14.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240604185151-ef581f913117 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20240227224415-6ceb2ff114de // indirect
) )

60
go.sum
View file

@ -1,5 +1,5 @@
git.frostfs.info/TrueCloudLab/frostfs-api-go/v2 v2.16.1-0.20241011114054-f0fc40e116d1 h1:ivcdxQeQDnx4srF2ezoaeVlF0FAycSAztwfIUJnUI4s= git.frostfs.info/TrueCloudLab/frostfs-api-go/v2 v2.16.1-0.20240902111049-c11f50efeccb h1:p9ByDsw+H6p6LyYSx8LKFtAG/oPKQpDVMNfjPqdevTw=
git.frostfs.info/TrueCloudLab/frostfs-api-go/v2 v2.16.1-0.20241011114054-f0fc40e116d1/go.mod h1:F5GS7hRb62PUy5sTYDC4ajVdeffoAfjHSSHTKUJEaYU= git.frostfs.info/TrueCloudLab/frostfs-api-go/v2 v2.16.1-0.20240902111049-c11f50efeccb/go.mod h1:BDnEpkKMykCS8u1nLzR6SgNzCv6885RWlo5TnravQuI=
git.frostfs.info/TrueCloudLab/frostfs-contract v0.19.3-0.20240621131249-49e5270f673e h1:kcBqZBiFIUBATUqEuvVigtkJJWQ2Gug/eYXn967o3M4= git.frostfs.info/TrueCloudLab/frostfs-contract v0.19.3-0.20240621131249-49e5270f673e h1:kcBqZBiFIUBATUqEuvVigtkJJWQ2Gug/eYXn967o3M4=
git.frostfs.info/TrueCloudLab/frostfs-contract v0.19.3-0.20240621131249-49e5270f673e/go.mod h1:F/fe1OoIDKr5Bz99q4sriuHDuf3aZefZy9ZsCqEtgxc= git.frostfs.info/TrueCloudLab/frostfs-contract v0.19.3-0.20240621131249-49e5270f673e/go.mod h1:F/fe1OoIDKr5Bz99q4sriuHDuf3aZefZy9ZsCqEtgxc=
git.frostfs.info/TrueCloudLab/frostfs-crypto v0.6.0 h1:FxqFDhQYYgpe41qsIHVOcdzSVCB8JNSfPG7Uk4r2oSk= git.frostfs.info/TrueCloudLab/frostfs-crypto v0.6.0 h1:FxqFDhQYYgpe41qsIHVOcdzSVCB8JNSfPG7Uk4r2oSk=
@ -12,14 +12,14 @@ git.frostfs.info/TrueCloudLab/tzhash v1.8.0 h1:UFMnUIk0Zh17m8rjGHJMqku2hCgaXDqjq
git.frostfs.info/TrueCloudLab/tzhash v1.8.0/go.mod h1:dhY+oy274hV8wGvGL4MwwMpdL3GYvaX1a8GQZQHvlF8= git.frostfs.info/TrueCloudLab/tzhash v1.8.0/go.mod h1:dhY+oy274hV8wGvGL4MwwMpdL3GYvaX1a8GQZQHvlF8=
github.com/VictoriaMetrics/easyproto v0.1.4 h1:r8cNvo8o6sR4QShBXQd1bKw/VVLSQma/V2KhTBPf+Sc= github.com/VictoriaMetrics/easyproto v0.1.4 h1:r8cNvo8o6sR4QShBXQd1bKw/VVLSQma/V2KhTBPf+Sc=
github.com/VictoriaMetrics/easyproto v0.1.4/go.mod h1:QlGlzaJnDfFd8Lk6Ci/fuLxfTo3/GThPs2KH23mv710= github.com/VictoriaMetrics/easyproto v0.1.4/go.mod h1:QlGlzaJnDfFd8Lk6Ci/fuLxfTo3/GThPs2KH23mv710=
github.com/antlr4-go/antlr/v4 v4.13.1 h1:SqQKkuVZ+zWkMMNkjy5FZe5mr5WURWnlpmOuzYWrPrQ= github.com/antlr4-go/antlr/v4 v4.13.0 h1:lxCg3LAv+EUK6t1i0y1V6/SLeUi0eKEKdhQAlS8TVTI=
github.com/antlr4-go/antlr/v4 v4.13.1/go.mod h1:GKmUxMtwp6ZgGwZSva4eWPC5mS6vUAmOABFgjdkM7Nw= github.com/antlr4-go/antlr/v4 v4.13.0/go.mod h1:pfChB/xh/Unjila75QW7+VU4TSnWnnk9UTnmpPaOR2g=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/bits-and-blooms/bitset v1.8.0 h1:FD+XqgOZDUxxZ8hzoBFuV9+cGWY9CslN6d5MS5JVb4c= github.com/bits-and-blooms/bitset v1.8.0 h1:FD+XqgOZDUxxZ8hzoBFuV9+cGWY9CslN6d5MS5JVb4c=
github.com/bits-and-blooms/bitset v1.8.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8= github.com/bits-and-blooms/bitset v1.8.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8=
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/consensys/bavard v0.1.13 h1:oLhMLOFGTLdlda/kma4VOJazblc7IM5y5QPd2A/YjhQ= github.com/consensys/bavard v0.1.13 h1:oLhMLOFGTLdlda/kma4VOJazblc7IM5y5QPd2A/YjhQ=
github.com/consensys/bavard v0.1.13/go.mod h1:9ItSMtA/dXMAiL7BG6bqW2m3NdSEObYWoH223nGHukI= github.com/consensys/bavard v0.1.13/go.mod h1:9ItSMtA/dXMAiL7BG6bqW2m3NdSEObYWoH223nGHukI=
github.com/consensys/gnark-crypto v0.12.2-0.20231013160410-1f65e75b6dfb h1:f0BMgIjhZy4lSRHCXFbQst85f5agZAjtDMixQqBWNpc= github.com/consensys/gnark-crypto v0.12.2-0.20231013160410-1f65e75b6dfb h1:f0BMgIjhZy4lSRHCXFbQst85f5agZAjtDMixQqBWNpc=
@ -148,21 +148,21 @@ 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-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-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI= golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA=
golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM= golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs=
golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 h1:vr/HnozRka3pE4EsMEg1lgkXJkTFJCVUX+S/ZT6wYzM= golang.org/x/exp v0.0.0-20240222234643-814bf88cf225 h1:LfspQV/FYTatPTr/3HzIcmiUFH7PGP+OQ6mgDYo3yuQ=
golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842/go.mod h1:XtvwrStGgqGPLc4cjQfWqZHG1YFdYs6swckp8vpsjnc= golang.org/x/exp v0.0.0-20240222234643-814bf88cf225/go.mod h1:CxmFvTBINI24O/j8iY7H1xHzx2i4OsyguNBmN/uPtqc=
golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA= golang.org/x/mod v0.16.0 h1:QX4fJ0Rr5cPQCF7O9lh9Se4pmwfwskqZfq5moyldzic=
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/mod v0.16.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 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= golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200813134508-3edf25e44fcc/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200813134508-3edf25e44fcc/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ= golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs=
golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE= golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ=
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 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-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@ -174,33 +174,33 @@ golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20200814200057-3d37ad5750ed/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.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.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws= golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4=
golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.21.0 h1:WVXCp+/EBEHOj53Rvu+7KiT/iElMrO8ACK16SMZ3jaA= golang.org/x/term v0.18.0 h1:FcHjZXDMxI8mM3nwhX9HlKop4C0YQvCVCdwYl2wOtE8=
golang.org/x/term v0.21.0/go.mod h1:ooXLefLobQVslOqselCNF4SxFAaoS6KujMbsGzSDmX0= golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 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.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.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 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.19.0 h1:tfGCXNR1OsFG+sVdLAitlpjAvD/I6dHDKnYrpEZUHkw=
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= golang.org/x/tools v0.19.0/go.mod h1:qoJWxmGSIBmAeriMx19ogtrEPrGtDbPK634QFIcLAhc=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 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 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240604185151-ef581f913117 h1:1GBuWVLM/KMVUv1t1En5Gs+gFZCNd360GGb4sSxtrhU= google.golang.org/genproto/googleapis/rpc v0.0.0-20240227224415-6ceb2ff114de h1:cZGRis4/ot9uVm639a+rHCUaG0JJHEsdyzSQTMX+suY=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240604185151-ef581f913117/go.mod h1:EfXuqaE1J41VCDicxHzUDm+8rk+7ZdXzHV0IhO/I6s0= google.golang.org/genproto/googleapis/rpc v0.0.0-20240227224415-6ceb2ff114de/go.mod h1:H4O17MA/PE9BsGx3w+a+W2VOLLD1Qf7oJneAoU6WktY=
google.golang.org/grpc v1.66.2 h1:3QdXkuq3Bkh7w+ywLdLvM56cmGvQHUMZpiCzt6Rqaoo= google.golang.org/grpc v1.63.2 h1:MUeiw1B2maTVZthpU5xvASfTh3LDbxHd6IJ6QQVU+xM=
google.golang.org/grpc v1.66.2/go.mod h1:s3/l6xSSCURdVfAnL+TqCNMyTDAGN6+lZeVxnZR128Y= google.golang.org/grpc v1.63.2/go.mod h1:WAX/8DgncnokcFUldAxq7GeB5DXHDbMF+lLvDomNkRA=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg= google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=

View file

@ -47,7 +47,7 @@ func BenchmarkNetmap_ContainerNodes(b *testing.B) {
b.ResetTimer() b.ResetTimer()
b.ReportAllocs() b.ReportAllocs()
for range b.N { for i := 0; i < b.N; i++ {
_, err := nm.ContainerNodes(p, pivot) _, err := nm.ContainerNodes(p, pivot)
if err != nil { if err != nil {
b.Fatal(err) b.Fatal(err)

View file

@ -460,10 +460,6 @@ func (x *NodeInfo) SortAttributes() {
// SetOffline sets the state of the node to "offline". When a node updates // SetOffline sets the state of the node to "offline". When a node updates
// information about itself in the network map, this action is interpreted as // information about itself in the network map, this action is interpreted as
// an intention to leave the network. // an intention to leave the network.
//
// See also IsOffline.
//
// Deprecated: use SetStatus instead.
func (x *NodeInfo) SetOffline() { func (x *NodeInfo) SetOffline() {
x.m.SetState(netmap.Offline) x.m.SetState(netmap.Offline)
} }
@ -474,8 +470,6 @@ func (x *NodeInfo) SetOffline() {
// mean online). // mean online).
// //
// See also SetOffline. // See also SetOffline.
//
// Deprecated: use Status instead.
func (x NodeInfo) IsOffline() bool { func (x NodeInfo) IsOffline() bool {
return x.m.GetState() == netmap.Offline return x.m.GetState() == netmap.Offline
} }
@ -485,8 +479,6 @@ func (x NodeInfo) IsOffline() bool {
// action is interpreted as an intention to enter the network. // action is interpreted as an intention to enter the network.
// //
// See also IsOnline. // See also IsOnline.
//
// Deprecated: use SetStatus instead.
func (x *NodeInfo) SetOnline() { func (x *NodeInfo) SetOnline() {
x.m.SetState(netmap.Online) x.m.SetState(netmap.Online)
} }
@ -497,8 +489,6 @@ func (x *NodeInfo) SetOnline() {
// mean offline). // mean offline).
// //
// See also SetOnline. // See also SetOnline.
//
// Deprecated: use Status instead.
func (x NodeInfo) IsOnline() bool { func (x NodeInfo) IsOnline() bool {
return x.m.GetState() == netmap.Online return x.m.GetState() == netmap.Online
} }
@ -508,8 +498,6 @@ func (x NodeInfo) IsOnline() bool {
// state declares temporal unavailability for a node. // state declares temporal unavailability for a node.
// //
// See also IsMaintenance. // See also IsMaintenance.
//
// Deprecated: use SetStatus instead.
func (x *NodeInfo) SetMaintenance() { func (x *NodeInfo) SetMaintenance() {
x.m.SetState(netmap.Maintenance) x.m.SetState(netmap.Maintenance)
} }
@ -519,63 +507,6 @@ func (x *NodeInfo) SetMaintenance() {
// Zero NodeInfo has undefined state. // Zero NodeInfo has undefined state.
// //
// See also SetMaintenance. // See also SetMaintenance.
//
// Deprecated: use Status instead.
func (x NodeInfo) IsMaintenance() bool { func (x NodeInfo) IsMaintenance() bool {
return x.m.GetState() == netmap.Maintenance return x.m.GetState() == netmap.Maintenance
} }
type NodeState netmap.NodeState
const (
UnspecifiedState = NodeState(netmap.UnspecifiedState)
Online = NodeState(netmap.Online)
Offline = NodeState(netmap.Offline)
Maintenance = NodeState(netmap.Maintenance)
)
// ToV2 converts NodeState to v2.
func (ns NodeState) ToV2() netmap.NodeState {
return netmap.NodeState(ns)
}
// FromV2 reads NodeState to v2.
func (ns *NodeState) FromV2(state netmap.NodeState) {
*ns = NodeState(state)
}
// Status returns the current state of the node in the network map.
//
// Zero NodeInfo has an undefined state, neither online nor offline.
func (x NodeInfo) Status() NodeState {
return NodeState(x.m.GetState())
}
// SetState updates the state of the node in the network map.
//
// The state determines the node's current status within the network:
// - "online": Indicates the node intends to enter the network.
// - "offline": Indicates the node intends to leave the network.
// - "maintenance": Indicates the node is temporarily unavailable.
//
// See also Status.
func (x *NodeInfo) SetStatus(state NodeState) {
x.m.SetState(netmap.NodeState(state))
}
// String implements fmt.Stringer.
//
// String is designed to be human-readable, and its format MAY differ between
// SDK versions.
func (ns NodeState) String() string {
return netmap.NodeState(ns).String()
}
// IsOnline checks if the current state is "online".
func (ns NodeState) IsOnline() bool { return ns == Online }
// IsOffline checks if the current state is "offline".
func (ns NodeState) IsOffline() bool { return ns == Offline }
// IsMaintenance checks if the current state is "maintenance".
func (ns NodeState) IsMaintenance() bool { return ns == Maintenance }

View file

@ -3,7 +3,6 @@ package netmap
import ( import (
"testing" "testing"
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/netmap"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
@ -24,31 +23,7 @@ func TestNodeInfo_SetAttribute(t *testing.T) {
require.Equal(t, val, n.Attribute(key)) require.Equal(t, val, n.Attribute(key))
} }
func TestNodeState(t *testing.T) {
m := map[NodeState]netmap.NodeState{
UnspecifiedState: netmap.UnspecifiedState,
Online: netmap.Online,
Offline: netmap.Offline,
Maintenance: netmap.Maintenance,
}
t.Run("from sdk to v2", func(t *testing.T) {
for stateSDK, stateV2 := range m {
require.Equal(t, stateV2, stateSDK.ToV2())
}
})
t.Run("from v2 to sdk", func(t *testing.T) {
for stateSDK, stateV2 := range m {
var state NodeState
state.FromV2(stateV2)
require.Equal(t, stateSDK, state)
}
})
}
func TestNodeInfo_Status(t *testing.T) { func TestNodeInfo_Status(t *testing.T) {
t.Run("deprecated getters/setters", func(t *testing.T) {
var n NodeInfo var n NodeInfo
require.False(t, n.IsOnline()) require.False(t, n.IsOnline())
@ -69,30 +44,6 @@ func TestNodeInfo_Status(t *testing.T) {
require.True(t, n.IsMaintenance()) require.True(t, n.IsMaintenance())
require.False(t, n.IsOnline()) require.False(t, n.IsOnline())
require.False(t, n.IsOffline()) require.False(t, n.IsOffline())
})
t.Run("brand new getters/setters", func(t *testing.T) {
var n NodeInfo
require.False(t, n.Status().IsOnline())
require.False(t, n.Status().IsOffline())
require.False(t, n.Status().IsMaintenance())
n.SetStatus(Online)
require.True(t, n.Status().IsOnline())
require.False(t, n.Status().IsOffline())
require.False(t, n.Status().IsMaintenance())
n.SetStatus(Offline)
require.False(t, n.Status().IsOnline())
require.True(t, n.Status().IsOffline())
require.False(t, n.Status().IsMaintenance())
n.SetStatus(Maintenance)
require.False(t, n.Status().IsOnline())
require.False(t, n.Status().IsOffline())
require.True(t, n.Status().IsMaintenance())
})
} }
func TestNodeInfo_ExternalAddr(t *testing.T) { func TestNodeInfo_ExternalAddr(t *testing.T) {

View file

@ -20,7 +20,7 @@ cbfStmt: CBF BackupFactor = NUMBER1; // container backup factor
selectStmt: selectStmt:
SELECT Count = NUMBER1 // number of nodes to select without container backup factor *) SELECT Count = NUMBER1 // number of nodes to select without container backup factor *)
(IN clause? Bucket = filterKey)? // bucket name (IN clause? Bucket = ident)? // bucket name
FROM Filter = identWC // filter reference or whole netmap FROM Filter = identWC // filter reference or whole netmap
(AS Name = ident)? // optional selector name (AS Name = ident)? // optional selector name
; ;

File diff suppressed because one or more lines are too long

View file

@ -1,5 +1,4 @@
package parser package parser
// You can download ANTLR from https://www.antlr.org/download/antlr-4.13.1-complete.jar, // ANTLR can be downloaded from https://www.antlr.org/download/antlr-4.13.0-complete.jar
// then run generate or simply run the dedicated Makefile target like this `make policy`. //go:generate java -Xmx500M -cp "./antlr-4.13.0-complete.jar:$CLASSPATH" org.antlr.v4.Tool -Dlanguage=Go -no-listener -visitor QueryLexer.g4 Query.g4
//go:generate java -Xmx500M -cp "./antlr-4.13.1-complete.jar:$CLASSPATH" org.antlr.v4.Tool -Dlanguage=Go -no-listener -visitor QueryLexer.g4 Query.g4

View file

@ -1,4 +1,4 @@
// Code generated from netmap/parser/Query.g4 by ANTLR 4.13.1. DO NOT EDIT. // Code generated from /repo/frostfs/sdk-go/netmap/parser/Query.g4 by ANTLR 4.13.0. DO NOT EDIT.
package parser // Query package parser // Query

View file

@ -1,4 +1,4 @@
// Code generated from netmap/parser/QueryLexer.g4 by ANTLR 4.13.1. DO NOT EDIT. // Code generated from /repo/frostfs/sdk-go/netmap/parser/QueryLexer.g4 by ANTLR 4.13.0. DO NOT EDIT.
package parser package parser

View file

@ -1,4 +1,4 @@
// Code generated from netmap/parser/Query.g4 by ANTLR 4.13.1. DO NOT EDIT. // Code generated from /repo/frostfs/sdk-go/netmap/parser/Query.g4 by ANTLR 4.13.0. DO NOT EDIT.
package parser // Query package parser // Query
@ -93,7 +93,7 @@ func queryParserInit() {
85, 1, 0, 0, 0, 85, 7, 1, 0, 0, 0, 86, 87, 5, 10, 0, 0, 87, 88, 5, 22, 85, 1, 0, 0, 0, 85, 7, 1, 0, 0, 0, 86, 87, 5, 10, 0, 0, 87, 88, 5, 22,
0, 0, 88, 9, 1, 0, 0, 0, 89, 90, 5, 11, 0, 0, 90, 96, 5, 22, 0, 0, 91, 0, 0, 88, 9, 1, 0, 0, 0, 89, 90, 5, 11, 0, 0, 90, 96, 5, 22, 0, 0, 91,
93, 5, 8, 0, 0, 92, 94, 3, 12, 6, 0, 93, 92, 1, 0, 0, 0, 93, 94, 1, 0, 93, 5, 8, 0, 0, 92, 94, 3, 12, 6, 0, 93, 92, 1, 0, 0, 0, 93, 94, 1, 0,
0, 0, 94, 95, 1, 0, 0, 0, 95, 97, 3, 20, 10, 0, 96, 91, 1, 0, 0, 0, 96, 0, 0, 94, 95, 1, 0, 0, 0, 95, 97, 3, 28, 14, 0, 96, 91, 1, 0, 0, 0, 96,
97, 1, 0, 0, 0, 97, 98, 1, 0, 0, 0, 98, 99, 5, 12, 0, 0, 99, 102, 3, 30, 97, 1, 0, 0, 0, 97, 98, 1, 0, 0, 0, 98, 99, 5, 12, 0, 0, 99, 102, 3, 30,
15, 0, 100, 101, 5, 9, 0, 0, 101, 103, 3, 28, 14, 0, 102, 100, 1, 0, 0, 15, 0, 100, 101, 5, 9, 0, 0, 101, 103, 3, 28, 14, 0, 102, 100, 1, 0, 0,
0, 102, 103, 1, 0, 0, 0, 103, 11, 1, 0, 0, 0, 104, 105, 7, 0, 0, 0, 105, 0, 102, 103, 1, 0, 0, 0, 103, 11, 1, 0, 0, 0, 104, 105, 7, 0, 0, 0, 105,
@ -1364,7 +1364,7 @@ type ISelectStmtContext interface {
SetCount(antlr.Token) SetCount(antlr.Token)
// GetBucket returns the Bucket rule contexts. // GetBucket returns the Bucket rule contexts.
GetBucket() IFilterKeyContext GetBucket() IIdentContext
// GetFilter returns the Filter rule contexts. // GetFilter returns the Filter rule contexts.
GetFilter() IIdentWCContext GetFilter() IIdentWCContext
@ -1373,7 +1373,7 @@ type ISelectStmtContext interface {
GetName() IIdentContext GetName() IIdentContext
// SetBucket sets the Bucket rule contexts. // SetBucket sets the Bucket rule contexts.
SetBucket(IFilterKeyContext) SetBucket(IIdentContext)
// SetFilter sets the Filter rule contexts. // SetFilter sets the Filter rule contexts.
SetFilter(IIdentWCContext) SetFilter(IIdentWCContext)
@ -1388,8 +1388,8 @@ type ISelectStmtContext interface {
IdentWC() IIdentWCContext IdentWC() IIdentWCContext
IN() antlr.TerminalNode IN() antlr.TerminalNode
AS() antlr.TerminalNode AS() antlr.TerminalNode
FilterKey() IFilterKeyContext AllIdent() []IIdentContext
Ident() IIdentContext Ident(i int) IIdentContext
Clause() IClauseContext Clause() IClauseContext
// IsSelectStmtContext differentiates from other interfaces. // IsSelectStmtContext differentiates from other interfaces.
@ -1400,7 +1400,7 @@ type SelectStmtContext struct {
antlr.BaseParserRuleContext antlr.BaseParserRuleContext
parser antlr.Parser parser antlr.Parser
Count antlr.Token Count antlr.Token
Bucket IFilterKeyContext Bucket IIdentContext
Filter IIdentWCContext Filter IIdentWCContext
Name IIdentContext Name IIdentContext
} }
@ -1436,13 +1436,13 @@ func (s *SelectStmtContext) GetCount() antlr.Token { return s.Count }
func (s *SelectStmtContext) SetCount(v antlr.Token) { s.Count = v } func (s *SelectStmtContext) SetCount(v antlr.Token) { s.Count = v }
func (s *SelectStmtContext) GetBucket() IFilterKeyContext { return s.Bucket } func (s *SelectStmtContext) GetBucket() IIdentContext { return s.Bucket }
func (s *SelectStmtContext) GetFilter() IIdentWCContext { return s.Filter } func (s *SelectStmtContext) GetFilter() IIdentWCContext { return s.Filter }
func (s *SelectStmtContext) GetName() IIdentContext { return s.Name } func (s *SelectStmtContext) GetName() IIdentContext { return s.Name }
func (s *SelectStmtContext) SetBucket(v IFilterKeyContext) { s.Bucket = v } func (s *SelectStmtContext) SetBucket(v IIdentContext) { s.Bucket = v }
func (s *SelectStmtContext) SetFilter(v IIdentWCContext) { s.Filter = v } func (s *SelectStmtContext) SetFilter(v IIdentWCContext) { s.Filter = v }
@ -1484,29 +1484,38 @@ func (s *SelectStmtContext) AS() antlr.TerminalNode {
return s.GetToken(QueryAS, 0) return s.GetToken(QueryAS, 0)
} }
func (s *SelectStmtContext) FilterKey() IFilterKeyContext { func (s *SelectStmtContext) AllIdent() []IIdentContext {
var t antlr.RuleContext children := s.GetChildren()
for _, ctx := range s.GetChildren() { len := 0
if _, ok := ctx.(IFilterKeyContext); ok { for _, ctx := range children {
t = ctx.(antlr.RuleContext) if _, ok := ctx.(IIdentContext); ok {
break len++
} }
} }
if t == nil { tst := make([]IIdentContext, len)
return nil i := 0
for _, ctx := range children {
if t, ok := ctx.(IIdentContext); ok {
tst[i] = t.(IIdentContext)
i++
}
} }
return t.(IFilterKeyContext) return tst
} }
func (s *SelectStmtContext) Ident() IIdentContext { func (s *SelectStmtContext) Ident(i int) IIdentContext {
var t antlr.RuleContext var t antlr.RuleContext
j := 0
for _, ctx := range s.GetChildren() { for _, ctx := range s.GetChildren() {
if _, ok := ctx.(IIdentContext); ok { if _, ok := ctx.(IIdentContext); ok {
if j == i {
t = ctx.(antlr.RuleContext) t = ctx.(antlr.RuleContext)
break break
} }
j++
}
} }
if t == nil { if t == nil {
@ -1608,7 +1617,7 @@ func (p *Query) SelectStmt() (localctx ISelectStmtContext) {
{ {
p.SetState(95) p.SetState(95)
var _x = p.FilterKey() var _x = p.Ident()
localctx.(*SelectStmtContext).Bucket = _x localctx.(*SelectStmtContext).Bucket = _x
} }

View file

@ -1,4 +1,4 @@
// Code generated from netmap/parser/Query.g4 by ANTLR 4.13.1. DO NOT EDIT. // Code generated from /repo/frostfs/sdk-go/netmap/parser/Query.g4 by ANTLR 4.13.0. DO NOT EDIT.
package parser // Query package parser // Query

View file

@ -82,13 +82,6 @@ CBF 1
SELECT 1 FROM Color SELECT 1 FROM Color
FILTER (Color EQ Red OR Color EQ Blue OR Color EQ Yellow) AND Color NE Green AS Color`, FILTER (Color EQ Red OR Color EQ Blue OR Color EQ Yellow) AND Color NE Green AS Color`,
}, },
{
name: "non-ascii attributes in SELECT IN",
input: `REP 1
CBF 1
SELECT 1 IN SAME 'Цвет' FROM Colorful
FILTER 'Цвет' EQ 'Красный' OR 'Цвет' EQ 'Синий' AS Colorful`,
},
} }
for _, tc := range testCases { for _, tc := range testCases {
@ -134,11 +127,6 @@ func TestDecodeSelectFilterExpr(t *testing.T) {
SELECT 1 FROM R SELECT 1 FROM R
FILTER Color LIKE 'R' AS R FILTER Color LIKE 'R' AS R
`, `,
`
CBF 1
SELECT 1 IN SAME 'Цвет' FROM Colorful
FILTER 'Цвет' EQ 'Красный' OR 'Цвет' EQ 'Синий' AS Colorful
`,
} { } {
_, err := DecodeSelectFilterString(s) _, err := DecodeSelectFilterString(s)
require.NoError(t, err) require.NoError(t, err)

View file

@ -6,7 +6,6 @@ import (
"encoding/binary" "encoding/binary"
"fmt" "fmt"
mrand "math/rand" mrand "math/rand"
"reflect"
"slices" "slices"
"strconv" "strconv"
"strings" "strings"
@ -14,7 +13,6 @@ import (
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/netmap" "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/netmap"
"git.frostfs.info/TrueCloudLab/hrw" "git.frostfs.info/TrueCloudLab/hrw"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
@ -40,7 +38,7 @@ func BenchmarkHRWSort(b *testing.B) {
b.Run("sort by index, no weight", func(b *testing.B) { b.Run("sort by index, no weight", func(b *testing.B) {
realNodes := make([]nodes, netmapSize) realNodes := make([]nodes, netmapSize)
b.ResetTimer() b.ResetTimer()
for range b.N { for i := 0; i < b.N; i++ {
b.StopTimer() b.StopTimer()
copy(realNodes, vectors) copy(realNodes, vectors)
b.StartTimer() b.StartTimer()
@ -51,7 +49,7 @@ func BenchmarkHRWSort(b *testing.B) {
b.Run("sort by value, no weight", func(b *testing.B) { b.Run("sort by value, no weight", func(b *testing.B) {
realNodes := make([]nodes, netmapSize) realNodes := make([]nodes, netmapSize)
b.ResetTimer() b.ResetTimer()
for range b.N { for i := 0; i < b.N; i++ {
b.StopTimer() b.StopTimer()
copy(realNodes, vectors) copy(realNodes, vectors)
b.StartTimer() b.StartTimer()
@ -62,7 +60,7 @@ func BenchmarkHRWSort(b *testing.B) {
b.Run("only sort by index", func(b *testing.B) { b.Run("only sort by index", func(b *testing.B) {
realNodes := make([]nodes, netmapSize) realNodes := make([]nodes, netmapSize)
b.ResetTimer() b.ResetTimer()
for range b.N { for i := 0; i < b.N; i++ {
b.StopTimer() b.StopTimer()
copy(realNodes, vectors) copy(realNodes, vectors)
b.StartTimer() b.StartTimer()
@ -73,7 +71,7 @@ func BenchmarkHRWSort(b *testing.B) {
b.Run("sort by value", func(b *testing.B) { b.Run("sort by value", func(b *testing.B) {
realNodes := make([]nodes, netmapSize) realNodes := make([]nodes, netmapSize)
b.ResetTimer() b.ResetTimer()
for range b.N { for i := 0; i < b.N; i++ {
b.StopTimer() b.StopTimer()
copy(realNodes, vectors) copy(realNodes, vectors)
b.StartTimer() b.StartTimer()
@ -84,7 +82,7 @@ func BenchmarkHRWSort(b *testing.B) {
b.Run("sort by ID, then by index (deterministic)", func(b *testing.B) { b.Run("sort by ID, then by index (deterministic)", func(b *testing.B) {
realNodes := make([]nodes, netmapSize) realNodes := make([]nodes, netmapSize)
b.ResetTimer() b.ResetTimer()
for range b.N { for i := 0; i < b.N; i++ {
b.StopTimer() b.StopTimer()
copy(realNodes, vectors) copy(realNodes, vectors)
b.StartTimer() b.StartTimer()
@ -136,7 +134,7 @@ func BenchmarkPolicyHRWType(b *testing.B) {
nm.SetNodes(nodes) nm.SetNodes(nodes)
b.ResetTimer() b.ResetTimer()
for range b.N { for i := 0; i < b.N; i++ {
_, err := nm.ContainerNodes(p, []byte{1}) _, err := nm.ContainerNodes(p, []byte{1})
if err != nil { if err != nil {
b.Fatal() b.Fatal()
@ -197,7 +195,7 @@ func TestPlacementPolicy_DeterministicOrder(t *testing.T) {
} }
a, b := getIndices(t) a, b := getIndices(t)
for range 10 { for i := 0; i < 10; i++ {
x, y := getIndices(t) x, y := getIndices(t)
require.Equal(t, a, x) require.Equal(t, a, x)
require.Equal(t, b, y) require.Equal(t, b, y)
@ -354,7 +352,7 @@ func TestPlacementPolicy_Unique(t *testing.T) {
var nodes []NodeInfo var nodes []NodeInfo
for i, city := range []string{"Moscow", "Berlin", "Shenzhen"} { for i, city := range []string{"Moscow", "Berlin", "Shenzhen"} {
for j := range 3 { for j := 0; j < 3; j++ {
node := nodeInfoFromAttributes("City", city) node := nodeInfoFromAttributes("City", city)
node.SetPublicKey(binary.BigEndian.AppendUint16(nil, uint16(i*4+j))) node.SetPublicKey(binary.BigEndian.AppendUint16(nil, uint16(i*4+j)))
nodes = append(nodes, node) nodes = append(nodes, node)
@ -368,7 +366,7 @@ func TestPlacementPolicy_Unique(t *testing.T) {
require.NoError(t, err) require.NoError(t, err)
for i, vi := range v { for i, vi := range v {
for _, ni := range vi { for _, ni := range vi {
for j := range i { for j := 0; j < i; j++ {
for _, nj := range v[j] { for _, nj := range v[j] {
require.NotEqual(t, ni.hash, nj.hash) require.NotEqual(t, ni.hash, nj.hash)
} }
@ -457,7 +455,7 @@ func TestPlacementPolicy_MultiREP(t *testing.T) {
for _, additional := range []int{0, 1, 2} { for _, additional := range []int{0, 1, 2} {
t.Run(fmt.Sprintf("unique=%t, additional=%d", unique, additional), func(t *testing.T) { t.Run(fmt.Sprintf("unique=%t, additional=%d", unique, additional), func(t *testing.T) {
rs := []ReplicaDescriptor{newReplica(1, "SameRU")} rs := []ReplicaDescriptor{newReplica(1, "SameRU")}
for range additional { for i := 0; i < additional; i++ {
rs = append(rs, newReplica(1, "")) rs = append(rs, newReplica(1, ""))
} }
@ -544,66 +542,6 @@ func TestPlacementPolicy_ProcessSelectorsExceptForNodes(t *testing.T) {
} }
} }
func TestPlacementPolicy_NonAsciiAttributes(t *testing.T) {
p := newPlacementPolicy(
1,
[]ReplicaDescriptor{
newReplica(2, "Nodes"),
newReplica(2, "Nodes"),
},
[]Selector{
newSelector("Nodes", "Цвет", 2, "Colorful", (*Selector).SelectSame),
},
[]Filter{
newFilter("Colorful", "", "", netmap.OR,
newFilter("", "Цвет", "Красный", netmap.EQ),
newFilter("", "Цвет", "Синий", netmap.EQ),
),
},
)
p.SetUnique(true)
nodes := []NodeInfo{
nodeInfoFromAttributes("Цвет", "Красный", "Форма", "Треугольник"),
nodeInfoFromAttributes("Цвет", "Красный", "Форма", "Круг"),
nodeInfoFromAttributes("Цвет", "Синий", "Форма", "Треугольник"),
nodeInfoFromAttributes("Цвет", "Синий", "Форма", "Круг"),
nodeInfoFromAttributes("Свойство", "Мягкий", "Форма", "Треугольник"),
nodeInfoFromAttributes("Свойство", "Теплый", "Форма", "Круг"),
}
for i := range nodes {
nodes[i].SetPublicKey([]byte{byte(i)})
}
redNodes := nodes[:2]
blueNodes := nodes[2:4]
var nm NetMap
nm.SetNodes(nodes)
pivot := make([]byte, 42)
_, _ = rand.Read(pivot)
nodesPerReplica, err := nm.ContainerNodes(p, pivot)
require.NoError(t, err)
require.Len(t, nodesPerReplica, 2)
for i := range nodesPerReplica {
slices.SortFunc(nodesPerReplica[i], func(n1, n2 NodeInfo) int {
pk1, pk2 := string(n1.PublicKey()), string(n2.PublicKey())
return cmp.Compare(pk1, pk2)
})
}
redMatchFirst := reflect.DeepEqual(redNodes, nodesPerReplica[0])
blueMatchFirst := reflect.DeepEqual(blueNodes, nodesPerReplica[0])
redMatchSecond := reflect.DeepEqual(redNodes, nodesPerReplica[1])
blueMatchSecond := reflect.DeepEqual(blueNodes, nodesPerReplica[1])
assert.True(t, redMatchFirst && blueMatchSecond || blueMatchFirst && redMatchSecond)
}
func TestSelector_SetName(t *testing.T) { func TestSelector_SetName(t *testing.T) {
const name = "some name" const name = "some name"
var s Selector var s Selector

View file

@ -130,7 +130,7 @@ func BenchmarkPlacementPolicyInteropability(b *testing.B) {
b.Run(name, func(b *testing.B) { b.Run(name, func(b *testing.B) {
b.ReportAllocs() b.ReportAllocs()
b.ResetTimer() b.ResetTimer()
for range b.N { for i := 0; i < b.N; i++ {
b.StartTimer() b.StartTimer()
v, err := nm.ContainerNodes(tt.Policy, tt.Pivot) v, err := nm.ContainerNodes(tt.Policy, tt.Pivot)
b.StopTimer() b.StopTimer()
@ -173,7 +173,7 @@ func BenchmarkManySelects(b *testing.B) {
b.ResetTimer() b.ResetTimer()
b.ReportAllocs() b.ReportAllocs()
for range b.N { for i := 0; i < b.N; i++ {
_, err = nm.ContainerNodes(tt.Policy, tt.Pivot) _, err = nm.ContainerNodes(tt.Policy, tt.Pivot)
if err != nil { if err != nil {
b.FailNow() b.FailNow()

View file

@ -53,7 +53,7 @@ func TestErasureCodeReconstruct(t *testing.T) {
}) })
t.Run("from parity", func(t *testing.T) { t.Run("from parity", func(t *testing.T) {
parts := cloneSlice(parts) parts := cloneSlice(parts)
for i := range parityCount { for i := 0; i < parityCount; i++ {
parts[i] = nil parts[i] = nil
} }
reconstructed, err := c.ReconstructHeader(parts) reconstructed, err := c.ReconstructHeader(parts)
@ -138,7 +138,7 @@ func TestErasureCodeReconstruct(t *testing.T) {
}) })
t.Run("from parity", func(t *testing.T) { t.Run("from parity", func(t *testing.T) {
parts := cloneSlice(parts) parts := cloneSlice(parts)
for i := range parityCount { for i := 0; i < parityCount; i++ {
parts[i] = nil parts[i] = nil
} }
reconstructed, err := c.Reconstruct(parts) reconstructed, err := c.Reconstruct(parts)
@ -180,7 +180,7 @@ func TestErasureCodeReconstruct(t *testing.T) {
t.Run("from parity", func(t *testing.T) { t.Run("from parity", func(t *testing.T) {
oldParts := parts oldParts := parts
parts := cloneSlice(parts) parts := cloneSlice(parts)
for i := range parityCount { for i := 0; i < parityCount; i++ {
parts[i] = nil parts[i] = nil
} }

View file

@ -61,7 +61,7 @@ func TestID_Equal(t *testing.T) {
func TestID_Parse(t *testing.T) { func TestID_Parse(t *testing.T) {
t.Run("should parse successful", func(t *testing.T) { t.Run("should parse successful", func(t *testing.T) {
for i := range 10 { for i := 0; i < 10; i++ {
t.Run(strconv.Itoa(i), func(t *testing.T) { t.Run(strconv.Itoa(i), func(t *testing.T) {
cs := randSHA256Checksum(t) cs := randSHA256Checksum(t)
str := base58.Encode(cs[:]) str := base58.Encode(cs[:])
@ -78,7 +78,7 @@ func TestID_Parse(t *testing.T) {
}) })
t.Run("should failure on parse", func(t *testing.T) { t.Run("should failure on parse", func(t *testing.T) {
for i := range 10 { for i := 0; i < 10; i++ {
j := i j := i
t.Run(strconv.Itoa(j), func(t *testing.T) { t.Run(strconv.Itoa(j), func(t *testing.T) {
cs := []byte{1, 2, 3, 4, 5, byte(j)} cs := []byte{1, 2, 3, 4, 5, byte(j)}
@ -98,7 +98,7 @@ func TestID_String(t *testing.T) {
}) })
t.Run("should be equal", func(t *testing.T) { t.Run("should be equal", func(t *testing.T) {
for i := range 10 { for i := 0; i < 10; i++ {
t.Run(strconv.Itoa(i), func(t *testing.T) { t.Run(strconv.Itoa(i), func(t *testing.T) {
cs := randSHA256Checksum(t) cs := randSHA256Checksum(t)
str := base58.Encode(cs[:]) str := base58.Encode(cs[:])

View file

@ -3,10 +3,7 @@ package object
import ( import (
"errors" "errors"
"fmt" "fmt"
"slices"
"strings"
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/container"
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/object" "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/object"
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs" "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs"
v2session "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/session" v2session "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/session"
@ -315,23 +312,6 @@ func (o *Object) Attributes() []Attribute {
return res return res
} }
// UserAttributes returns object user attributes.
func (o *Object) UserAttributes() []Attribute {
attrs := (*object.Object)(o).
GetHeader().
GetAttributes()
res := make([]Attribute, 0, len(attrs))
for _, attr := range attrs {
if !strings.HasPrefix(attr.GetKey(), container.SysAttributePrefix) {
res = append(res, *NewAttributeFromV2(&attr))
}
}
return slices.Clip(res)
}
// SetAttributes sets object attributes. // SetAttributes sets object attributes.
func (o *Object) SetAttributes(v ...Attribute) { func (o *Object) SetAttributes(v ...Attribute) {
attrs := make([]object.Attribute, len(v)) attrs := make([]object.Attribute, len(v))

View file

@ -3,10 +3,8 @@ package object_test
import ( import (
"testing" "testing"
v2container "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/container"
cidtest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id/test" cidtest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id/test"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object" "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object"
objecttest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/test"
usertest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/user/test" usertest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/user/test"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
@ -26,26 +24,3 @@ func TestInitCreation(t *testing.T) {
require.Equal(t, cnr, cID) require.Equal(t, cnr, cID)
require.Equal(t, own, o.OwnerID()) require.Equal(t, own, o.OwnerID())
} }
func Test_Attributes(t *testing.T) {
obj := objecttest.Object()
t.Run("get user attributes", func(t *testing.T) {
// See how we create a test object. It's created with two attributes.
require.Len(t, obj.UserAttributes(), 2)
})
userAttrs := obj.UserAttributes()
sysAttr := *object.NewAttribute()
sysAttr.SetKey(v2container.SysAttributePrefix + "key")
sysAttr.SetValue("value")
attr := append(userAttrs, sysAttr)
obj.SetAttributes(attr...)
t.Run("get attributes", func(t *testing.T) {
require.ElementsMatch(t, obj.UserAttributes(), userAttrs)
require.ElementsMatch(t, obj.Attributes(), attr)
})
}

View file

@ -14,7 +14,7 @@ func generateIDList(sz int) []oid.ID {
res := make([]oid.ID, sz) res := make([]oid.ID, sz)
cs := [sha256.Size]byte{} cs := [sha256.Size]byte{}
for i := range sz { for i := 0; i < sz; i++ {
var oID oid.ID var oID oid.ID
res[i] = oID res[i] = oID

View file

@ -72,7 +72,7 @@ func TestTransformer(t *testing.T) {
require.Equal(t, ids.ParentID, &parID) require.Equal(t, ids.ParentID, &parID)
children := tt.objects[i].Children() children := tt.objects[i].Children()
for j := range i { for j := 0; j < i; j++ {
id, ok := tt.objects[j].ID() id, ok := tt.objects[j].ID()
require.True(t, ok) require.True(t, ok)
require.Equal(t, id, children[j]) require.Equal(t, id, children[j])
@ -152,7 +152,7 @@ func benchmarkTransformer(b *testing.B, header *objectSDK.Object, payloadSize, s
b.ReportAllocs() b.ReportAllocs()
b.ResetTimer() b.ResetTimer()
for range b.N { for i := 0; i < b.N; i++ {
f, _ := newPayloadSizeLimiter(maxSize, uint64(sizeHint), func() ObjectWriter { return benchTarget{} }) f, _ := newPayloadSizeLimiter(maxSize, uint64(sizeHint), func() ObjectWriter { return benchTarget{} })
if err := f.WriteHeader(ctx, header); err != nil { if err := f.WriteHeader(ctx, header); err != nil {
b.Fatalf("write header: %v", err) b.Fatalf("write header: %v", err)

View file

@ -26,15 +26,11 @@ type mockClient struct {
errorOnDial bool errorOnDial bool
errorOnCreateSession bool errorOnCreateSession bool
errorOnEndpointInfo error errorOnEndpointInfo bool
resOnEndpointInfo netmap.NodeInfo
healthcheckFn func()
errorOnNetworkInfo bool errorOnNetworkInfo bool
stOnGetObject apistatus.Status stOnGetObject apistatus.Status
} }
var _ client = (*mockClient)(nil)
func newMockClient(addr string, key ecdsa.PrivateKey) *mockClient { func newMockClient(addr string, key ecdsa.PrivateKey) *mockClient {
return &mockClient{ return &mockClient{
key: key, key: key,
@ -42,16 +38,6 @@ func newMockClient(addr string, key ecdsa.PrivateKey) *mockClient {
} }
} }
func newMockClientHealthy(addr string, key ecdsa.PrivateKey, healthy bool) *mockClient {
m := newMockClient(addr, key)
if healthy {
m.setHealthy()
} else {
m.setUnhealthy()
}
return m
}
func (m *mockClient) setThreshold(threshold uint32) { func (m *mockClient) setThreshold(threshold uint32) {
m.errorThreshold = threshold m.errorThreshold = threshold
} }
@ -61,11 +47,11 @@ func (m *mockClient) errOnCreateSession() {
} }
func (m *mockClient) errOnEndpointInfo() { func (m *mockClient) errOnEndpointInfo() {
m.errorOnEndpointInfo = errors.New("error") m.errorOnEndpointInfo = true
} }
func (m *mockClient) errOnNetworkInfo() { func (m *mockClient) errOnNetworkInfo() {
m.errorOnEndpointInfo = errors.New("error") m.errorOnEndpointInfo = true
} }
func (m *mockClient) errOnDial() { func (m *mockClient) errOnDial() {
@ -108,32 +94,27 @@ func (m *mockClient) containerDelete(context.Context, PrmContainerDelete) error
return nil return nil
} }
func (m *mockClient) apeManagerAddChain(context.Context, PrmAddAPEChain) error { func (c *mockClient) apeManagerAddChain(ctx context.Context, prm PrmAddAPEChain) error {
return nil return nil
} }
func (m *mockClient) apeManagerRemoveChain(context.Context, PrmRemoveAPEChain) error { func (c *mockClient) apeManagerRemoveChain(ctx context.Context, prm PrmRemoveAPEChain) error {
return nil return nil
} }
func (m *mockClient) apeManagerListChains(context.Context, PrmListAPEChains) ([]ape.Chain, error) { func (c *mockClient) apeManagerListChains(ctx context.Context, prm PrmListAPEChains) ([]ape.Chain, error) {
return []ape.Chain{}, nil return []ape.Chain{}, nil
} }
func (m *mockClient) endpointInfo(ctx context.Context, _ prmEndpointInfo) (netmap.NodeInfo, error) { func (m *mockClient) endpointInfo(ctx context.Context, _ prmEndpointInfo) (netmap.NodeInfo, error) {
if m.errorOnEndpointInfo != nil { var ni netmap.NodeInfo
return netmap.NodeInfo{}, m.handleError(ctx, nil, m.errorOnEndpointInfo)
if m.errorOnEndpointInfo {
return ni, m.handleError(ctx, nil, errors.New("error"))
} }
m.resOnEndpointInfo.SetNetworkEndpoints(m.addr) ni.SetNetworkEndpoints(m.addr)
return m.resOnEndpointInfo, nil return ni, nil
}
func (m *mockClient) healthcheck(ctx context.Context) (netmap.NodeInfo, error) {
if m.healthcheckFn != nil {
m.healthcheckFn()
}
return m.endpointInfo(ctx, prmEndpointInfo{})
} }
func (m *mockClient) networkInfo(ctx context.Context, _ prmNetworkInfo) (netmap.NetworkInfo, error) { func (m *mockClient) networkInfo(ctx context.Context, _ prmNetworkInfo) (netmap.NetworkInfo, error) {
@ -209,12 +190,16 @@ func (m *mockClient) dial(context.Context) error {
return nil return nil
} }
func (m *mockClient) restart(context.Context) error { func (m *mockClient) restartIfUnhealthy(ctx context.Context) (changed bool, err error) {
if m.errorOnDial { _, err = m.endpointInfo(ctx, prmEndpointInfo{})
return errors.New("restart dial error") healthy := err == nil
changed = healthy != m.isHealthy()
if healthy {
m.setHealthy()
} else {
m.setUnhealthy()
} }
return
return nil
} }
func (m *mockClient) close() error { func (m *mockClient) close() error {

View file

@ -57,8 +57,6 @@ type client interface {
apeManagerListChains(context.Context, PrmListAPEChains) ([]ape.Chain, error) apeManagerListChains(context.Context, PrmListAPEChains) ([]ape.Chain, error)
// see clientWrapper.endpointInfo. // see clientWrapper.endpointInfo.
endpointInfo(context.Context, prmEndpointInfo) (netmap.NodeInfo, error) endpointInfo(context.Context, prmEndpointInfo) (netmap.NodeInfo, error)
// see clientWrapper.healthcheck.
healthcheck(ctx context.Context) (netmap.NodeInfo, error)
// see clientWrapper.networkInfo. // see clientWrapper.networkInfo.
networkInfo(context.Context, prmNetworkInfo) (netmap.NetworkInfo, error) networkInfo(context.Context, prmNetworkInfo) (netmap.NetworkInfo, error)
// see clientWrapper.netMapSnapshot // see clientWrapper.netMapSnapshot
@ -84,8 +82,8 @@ type client interface {
// see clientWrapper.dial. // see clientWrapper.dial.
dial(ctx context.Context) error dial(ctx context.Context) error
// see clientWrapper.restart. // see clientWrapper.restartIfUnhealthy.
restart(ctx context.Context) error restartIfUnhealthy(ctx context.Context) (bool, error)
// see clientWrapper.close. // see clientWrapper.close.
close() error close() error
} }
@ -94,10 +92,10 @@ type client interface {
type clientStatus interface { type clientStatus interface {
// isHealthy checks if the connection can handle requests. // isHealthy checks if the connection can handle requests.
isHealthy() bool isHealthy() bool
// isDialed checks if the connection was created.
isDialed() bool
// setUnhealthy marks client as unhealthy. // setUnhealthy marks client as unhealthy.
setUnhealthy() setUnhealthy()
// setHealthy marks client as healthy.
setHealthy()
// address return address of endpoint. // address return address of endpoint.
address() string address() string
// currentErrorRate returns current errors rate. // currentErrorRate returns current errors rate.
@ -128,10 +126,15 @@ type clientStatusMonitor struct {
// values for healthy status of clientStatusMonitor. // values for healthy status of clientStatusMonitor.
const ( const (
// statusUnhealthyOnDial is set when dialing to the endpoint is failed,
// so there is no connection to the endpoint, and pool should not close it
// before re-establishing connection once again.
statusUnhealthyOnDial = iota
// statusUnhealthyOnRequest is set when communication after dialing to the // statusUnhealthyOnRequest is set when communication after dialing to the
// endpoint is failed due to immediate or accumulated errors, connection is // endpoint is failed due to immediate or accumulated errors, connection is
// available and pool should close it before re-establishing connection once again. // available and pool should close it before re-establishing connection once again.
statusUnhealthyOnRequest = iota statusUnhealthyOnRequest
// statusHealthy is set when connection is ready to be used by the pool. // statusHealthy is set when connection is ready to be used by the pool.
statusHealthy statusHealthy
@ -230,7 +233,6 @@ func newClientStatusMonitor(logger *zap.Logger, addr string, errorThreshold uint
type clientWrapper struct { type clientWrapper struct {
clientMutex sync.RWMutex clientMutex sync.RWMutex
client *sdkClient.Client client *sdkClient.Client
dialed bool
prm wrapperPrm prm wrapperPrm
clientStatusMonitor clientStatusMonitor
@ -340,17 +342,30 @@ func (c *clientWrapper) dial(ctx context.Context) error {
GRPCDialOptions: c.prm.dialOptions, GRPCDialOptions: c.prm.dialOptions,
} }
err = cl.Dial(ctx, prmDial) if err = cl.Dial(ctx, prmDial); err != nil {
c.setDialed(err == nil) c.setUnhealthyOnDial()
if err != nil {
return err return err
} }
return nil return nil
} }
// restart recreates and redial inner sdk client. // restartIfUnhealthy checks healthy status of client and recreate it if status is unhealthy.
func (c *clientWrapper) restart(ctx context.Context) error { // Indicating if status was changed by this function call and returns error that caused unhealthy status.
func (c *clientWrapper) restartIfUnhealthy(ctx context.Context) (changed bool, err error) {
var wasHealthy bool
if _, err = c.endpointInfo(ctx, prmEndpointInfo{}); err == nil {
return false, nil
} else if !errors.Is(err, errPoolClientUnhealthy) {
wasHealthy = true
}
// if connection is dialed before, to avoid routine / connection leak,
// pool has to close it and then initialize once again.
if c.isDialed() {
c.scheduleGracefulClose()
}
var cl sdkClient.Client var cl sdkClient.Client
prmInit := sdkClient.PrmInit{ prmInit := sdkClient.PrmInit{
Key: c.prm.key, Key: c.prm.key,
@ -366,35 +381,22 @@ func (c *clientWrapper) restart(ctx context.Context) error {
GRPCDialOptions: c.prm.dialOptions, GRPCDialOptions: c.prm.dialOptions,
} }
// if connection is dialed before, to avoid routine / connection leak, if err = cl.Dial(ctx, prmDial); err != nil {
// pool has to close it and then initialize once again. c.setUnhealthyOnDial()
if c.isDialed() { return wasHealthy, err
c.scheduleGracefulClose()
}
err := cl.Dial(ctx, prmDial)
c.setDialed(err == nil)
if err != nil {
return err
} }
c.clientMutex.Lock() c.clientMutex.Lock()
c.client = &cl c.client = &cl
c.clientMutex.Unlock() c.clientMutex.Unlock()
return nil if _, err = cl.EndpointInfo(ctx, sdkClient.PrmEndpointInfo{}); err != nil {
} c.setUnhealthy()
return wasHealthy, err
}
func (c *clientWrapper) isDialed() bool { c.setHealthy()
c.mu.RLock() return !wasHealthy, nil
defer c.mu.RUnlock()
return c.dialed
}
func (c *clientWrapper) setDialed(dialed bool) {
c.mu.Lock()
c.dialed = dialed
c.mu.Unlock()
} }
func (c *clientWrapper) getClient() (*sdkClient.Client, error) { func (c *clientWrapper) getClient() (*sdkClient.Client, error) {
@ -652,15 +654,6 @@ func (c *clientWrapper) endpointInfo(ctx context.Context, _ prmEndpointInfo) (ne
return netmap.NodeInfo{}, err return netmap.NodeInfo{}, err
} }
return c.endpointInfoRaw(ctx, cl)
}
func (c *clientWrapper) healthcheck(ctx context.Context) (netmap.NodeInfo, error) {
cl := c.getClientRaw()
return c.endpointInfoRaw(ctx, cl)
}
func (c *clientWrapper) endpointInfoRaw(ctx context.Context, cl *sdkClient.Client) (netmap.NodeInfo, error) {
start := time.Now() start := time.Now()
res, err := cl.EndpointInfo(ctx, sdkClient.PrmEndpointInfo{}) res, err := cl.EndpointInfo(ctx, sdkClient.PrmEndpointInfo{})
c.incRequests(time.Since(start), methodEndpointInfo) c.incRequests(time.Since(start), methodEndpointInfo)
@ -1092,7 +1085,7 @@ func (c *clientWrapper) objectSearch(ctx context.Context, prm PrmObjectSearch) (
return ResObjectSearch{}, fmt.Errorf("init object searching on client: %w", err) return ResObjectSearch{}, fmt.Errorf("init object searching on client: %w", err)
} }
return ResObjectSearch{r: res, handleError: c.handleError}, nil return ResObjectSearch{r: res}, nil
} }
// sessionCreate invokes sdkClient.SessionCreate parse response status to error and return result as is. // sessionCreate invokes sdkClient.SessionCreate parse response status to error and return result as is.
@ -1128,6 +1121,10 @@ func (c *clientStatusMonitor) isHealthy() bool {
return c.healthy.Load() == statusHealthy return c.healthy.Load() == statusHealthy
} }
func (c *clientStatusMonitor) isDialed() bool {
return c.healthy.Load() != statusUnhealthyOnDial
}
func (c *clientStatusMonitor) setHealthy() { func (c *clientStatusMonitor) setHealthy() {
c.healthy.Store(statusHealthy) c.healthy.Store(statusHealthy)
} }
@ -1136,6 +1133,10 @@ func (c *clientStatusMonitor) setUnhealthy() {
c.healthy.Store(statusUnhealthyOnRequest) c.healthy.Store(statusUnhealthyOnRequest)
} }
func (c *clientStatusMonitor) setUnhealthyOnDial() {
c.healthy.Store(statusUnhealthyOnDial)
}
func (c *clientStatusMonitor) address() string { func (c *clientStatusMonitor) address() string {
return c.addr return c.addr
} }
@ -1158,16 +1159,6 @@ func (c *clientStatusMonitor) incErrorRate() {
} }
} }
func (c *clientStatusMonitor) incErrorRateToUnhealthy(err error) {
c.mu.Lock()
c.currentErrorCount = 0
c.overallErrorCount++
c.setUnhealthy()
c.mu.Unlock()
c.log(zapcore.WarnLevel, "explicitly mark node unhealthy", zap.String("address", c.addr), zap.Error(err))
}
func (c *clientStatusMonitor) log(level zapcore.Level, msg string, fields ...zap.Field) { func (c *clientStatusMonitor) log(level zapcore.Level, msg string, fields ...zap.Field) {
if c.logger == nil { if c.logger == nil {
return return
@ -1210,9 +1201,6 @@ func (c *clientWrapper) incRequests(elapsed time.Duration, method MethodIndex) {
} }
func (c *clientWrapper) close() error { func (c *clientWrapper) close() error {
if !c.isDialed() {
return nil
}
if cl := c.getClientRaw(); cl != nil { if cl := c.getClientRaw(); cl != nil {
return cl.Close() return cl.Close()
} }
@ -1237,10 +1225,9 @@ func (c *clientStatusMonitor) handleError(ctx context.Context, st apistatus.Stat
switch stErr.(type) { switch stErr.(type) {
case *apistatus.ServerInternal, case *apistatus.ServerInternal,
*apistatus.WrongMagicNumber, *apistatus.WrongMagicNumber,
*apistatus.SignatureVerification: *apistatus.SignatureVerification,
*apistatus.NodeUnderMaintenance:
c.incErrorRate() c.incErrorRate()
case *apistatus.NodeUnderMaintenance:
c.incErrorRateToUnhealthy(stErr)
} }
if err == nil { if err == nil {
@ -1252,12 +1239,8 @@ func (c *clientStatusMonitor) handleError(ctx context.Context, st apistatus.Stat
if err != nil { if err != nil {
if needCountError(ctx, err) { if needCountError(ctx, err) {
if sdkClient.IsErrNodeUnderMaintenance(err) {
c.incErrorRateToUnhealthy(err)
} else {
c.incErrorRate() c.incErrorRate()
} }
}
return err return err
} }
@ -1278,7 +1261,7 @@ func needCountError(ctx context.Context, err error) bool {
return false return false
} }
if ctx != nil && errors.Is(ctx.Err(), context.Canceled) { if errors.Is(ctx.Err(), context.Canceled) {
return false return false
} }
@ -2155,9 +2138,7 @@ func adjustNodeParams(nodeParams []NodeParam) ([]*nodesParam, error) {
// startRebalance runs loop to monitor connection healthy status. // startRebalance runs loop to monitor connection healthy status.
func (p *Pool) startRebalance(ctx context.Context) { func (p *Pool) startRebalance(ctx context.Context) {
ticker := time.NewTicker(p.rebalanceParams.clientRebalanceInterval) ticker := time.NewTimer(p.rebalanceParams.clientRebalanceInterval)
defer ticker.Stop()
buffers := make([][]float64, len(p.rebalanceParams.nodesParams)) buffers := make([][]float64, len(p.rebalanceParams.nodesParams))
for i, params := range p.rebalanceParams.nodesParams { for i, params := range p.rebalanceParams.nodesParams {
buffers[i] = make([]float64, len(params.weights)) buffers[i] = make([]float64, len(params.weights))
@ -2207,7 +2188,7 @@ func (p *Pool) updateInnerNodesHealth(ctx context.Context, i int, bufferWeights
tctx, c := context.WithTimeout(ctx, options.nodeRequestTimeout) tctx, c := context.WithTimeout(ctx, options.nodeRequestTimeout)
defer c() defer c()
changed, err := restartIfUnhealthy(tctx, cli) changed, err := cli.restartIfUnhealthy(tctx)
healthy := err == nil healthy := err == nil
if healthy { if healthy {
bufferWeights[j] = options.nodesParams[i].weights[j] bufferWeights[j] = options.nodesParams[i].weights[j]
@ -2238,43 +2219,6 @@ func (p *Pool) updateInnerNodesHealth(ctx context.Context, i int, bufferWeights
} }
} }
// restartIfUnhealthy checks healthy status of client and recreate it if status is unhealthy.
// Indicating if status was changed by this function call and returns error that caused unhealthy status.
func restartIfUnhealthy(ctx context.Context, c client) (changed bool, err error) {
defer func() {
if err != nil {
c.setUnhealthy()
} else {
c.setHealthy()
}
}()
wasHealthy := c.isHealthy()
if res, err := c.healthcheck(ctx); err == nil {
if res.Status().IsMaintenance() {
return wasHealthy, new(apistatus.NodeUnderMaintenance)
}
return !wasHealthy, nil
}
if err = c.restart(ctx); err != nil {
return wasHealthy, err
}
res, err := c.healthcheck(ctx)
if err != nil {
return wasHealthy, err
}
if res.Status().IsMaintenance() {
return wasHealthy, new(apistatus.NodeUnderMaintenance)
}
return !wasHealthy, nil
}
func adjustWeights(weights []float64) []float64 { func adjustWeights(weights []float64) []float64 {
adjusted := make([]float64, len(weights)) adjusted := make([]float64, len(weights))
sum := 0.0 sum := 0.0
@ -2312,7 +2256,7 @@ func (p *innerPool) connection() (client, error) {
return nil, errors.New("no healthy client") return nil, errors.New("no healthy client")
} }
attempts := 3 * len(p.clients) attempts := 3 * len(p.clients)
for range attempts { for k := 0; k < attempts; k++ {
i := p.sampler.Next() i := p.sampler.Next()
if cp := p.clients[i]; cp.isHealthy() { if cp := p.clients[i]; cp.isHealthy() {
return cp, nil return cp, nil
@ -2765,24 +2709,17 @@ func (p *Pool) ObjectRange(ctx context.Context, prm PrmObjectRange) (ResObjectRa
// Must be initialized using Pool.SearchObjects, any other usage is unsafe. // Must be initialized using Pool.SearchObjects, any other usage is unsafe.
type ResObjectSearch struct { type ResObjectSearch struct {
r *sdkClient.ObjectListReader r *sdkClient.ObjectListReader
handleError func(context.Context, apistatus.Status, error) error
} }
// Read reads another list of the object identifiers. // Read reads another list of the object identifiers.
func (x *ResObjectSearch) Read(buf []oid.ID) (int, error) { func (x *ResObjectSearch) Read(buf []oid.ID) (int, error) {
n, ok := x.r.Read(buf) n, ok := x.r.Read(buf)
if !ok { if !ok {
res, err := x.r.Close() _, err := x.r.Close()
if err == nil { if err == nil {
return n, io.EOF return n, io.EOF
} }
var status apistatus.Status
if res != nil {
status = res.Status()
}
err = x.handleError(nil, status, err)
return n, err return n, err
} }
@ -3080,9 +3017,11 @@ func (p *Pool) Close() {
// close all clients // close all clients
for _, pools := range p.innerPools { for _, pools := range p.innerPools {
for _, cli := range pools.clients { for _, cli := range pools.clients {
if cli.isDialed() {
_ = cli.close() _ = cli.close()
} }
} }
}
} }
// SyncContainerWithNetwork applies network configuration received via // SyncContainerWithNetwork applies network configuration received via

View file

@ -4,13 +4,12 @@ import (
"context" "context"
"crypto/ecdsa" "crypto/ecdsa"
"errors" "errors"
"math/rand" "strconv"
"testing" "testing"
"time" "time"
apistatus "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client/status" apistatus "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client/status"
frostfsecdsa "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/crypto/ecdsa" frostfsecdsa "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/crypto/ecdsa"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/netmap"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object" "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object"
oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id" oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/session" "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/session"
@ -18,8 +17,6 @@ import (
"github.com/nspcc-dev/neo-go/pkg/crypto/keys" "github.com/nspcc-dev/neo-go/pkg/crypto/keys"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"go.uber.org/zap" "go.uber.org/zap"
"go.uber.org/zap/zaptest"
"go.uber.org/zap/zaptest/observer"
) )
func TestBuildPoolClientFailed(t *testing.T) { func TestBuildPoolClientFailed(t *testing.T) {
@ -225,7 +222,7 @@ func TestOneOfTwoFailed(t *testing.T) {
time.Sleep(2 * time.Second) time.Sleep(2 * time.Second)
for range 5 { for i := 0; i < 5; i++ {
cp, err := pool.connection() cp, err := pool.connection()
require.NoError(t, err) require.NoError(t, err)
st, _ := pool.cache.Get(formCacheKey(cp.address(), pool.key, false)) st, _ := pool.cache.Get(formCacheKey(cp.address(), pool.key, false))
@ -233,179 +230,6 @@ func TestOneOfTwoFailed(t *testing.T) {
} }
} }
func TestUpdateNodesHealth(t *testing.T) {
ctx := context.Background()
key := newPrivateKey(t)
for _, tc := range []struct {
name string
wasHealthy bool
willHealthy bool
prepareCli func(*mockClient)
}{
{
name: "healthy, maintenance, unhealthy",
wasHealthy: true,
willHealthy: false,
prepareCli: func(c *mockClient) { c.resOnEndpointInfo.SetStatus(netmap.Maintenance) },
},
{
name: "unhealthy, maintenance, unhealthy",
wasHealthy: false,
willHealthy: false,
prepareCli: func(c *mockClient) { c.resOnEndpointInfo.SetStatus(netmap.Maintenance) },
},
{
name: "healthy, no error, healthy",
wasHealthy: true,
willHealthy: true,
prepareCli: func(c *mockClient) { c.resOnEndpointInfo.SetStatus(netmap.Online) },
},
{
name: "unhealthy, no error, healthy",
wasHealthy: false,
willHealthy: true,
prepareCli: func(c *mockClient) { c.resOnEndpointInfo.SetStatus(netmap.Online) },
},
{
name: "healthy, error, failed restart, unhealthy",
wasHealthy: true,
willHealthy: false,
prepareCli: func(c *mockClient) {
c.errOnEndpointInfo()
c.errorOnDial = true
},
},
{
name: "unhealthy, error, failed restart, unhealthy",
wasHealthy: false,
willHealthy: false,
prepareCli: func(c *mockClient) {
c.errOnEndpointInfo()
c.errorOnDial = true
},
},
{
name: "healthy, error, restart, error, unhealthy",
wasHealthy: true,
willHealthy: false,
prepareCli: func(c *mockClient) { c.errOnEndpointInfo() },
},
{
name: "unhealthy, error, restart, error, unhealthy",
wasHealthy: false,
willHealthy: false,
prepareCli: func(c *mockClient) { c.errOnEndpointInfo() },
},
{
name: "healthy, error, restart, maintenance, unhealthy",
wasHealthy: true,
willHealthy: false,
prepareCli: func(c *mockClient) {
healthError := true
c.healthcheckFn = func() {
if healthError {
c.errorOnEndpointInfo = errors.New("error")
} else {
c.errorOnEndpointInfo = nil
c.resOnEndpointInfo.SetStatus(netmap.Maintenance)
}
healthError = !healthError
}
},
},
{
name: "unhealthy, error, restart, maintenance, unhealthy",
wasHealthy: false,
willHealthy: false,
prepareCli: func(c *mockClient) {
healthError := true
c.healthcheckFn = func() {
if healthError {
c.errorOnEndpointInfo = errors.New("error")
} else {
c.errorOnEndpointInfo = nil
c.resOnEndpointInfo.SetStatus(netmap.Maintenance)
}
healthError = !healthError
}
},
},
{
name: "healthy, error, restart, healthy",
wasHealthy: true,
willHealthy: true,
prepareCli: func(c *mockClient) {
healthError := true
c.healthcheckFn = func() {
if healthError {
c.errorOnEndpointInfo = errors.New("error")
} else {
c.errorOnEndpointInfo = nil
}
healthError = !healthError
}
},
},
{
name: "unhealthy, error, restart, healthy",
wasHealthy: false,
willHealthy: true,
prepareCli: func(c *mockClient) {
healthError := true
c.healthcheckFn = func() {
if healthError {
c.errorOnEndpointInfo = errors.New("error")
} else {
c.errorOnEndpointInfo = nil
}
healthError = !healthError
}
},
},
} {
t.Run(tc.name, func(t *testing.T) {
cli := newMockClientHealthy("peer0", *key, tc.wasHealthy)
tc.prepareCli(cli)
p, log := newPool(t, cli)
p.updateNodesHealth(ctx, [][]float64{{1}})
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)
})
}
}
func newPool(t *testing.T, cli *mockClient) (*Pool, *observer.ObservedLogs) {
log, observedLog := getObservedLogger()
cache, err := newCache(0)
require.NoError(t, err)
return &Pool{
innerPools: []*innerPool{{
sampler: newSampler([]float64{1}, rand.NewSource(0)),
clients: []client{cli},
}},
cache: cache,
key: newPrivateKey(t),
closedCh: make(chan struct{}),
rebalanceParams: rebalanceParameters{
nodesParams: []*nodesParam{{1, []string{"peer0"}, []float64{1}}},
nodeRequestTimeout: time.Second,
clientRebalanceInterval: 200 * time.Millisecond,
},
logger: log,
}, observedLog
}
func getObservedLogger() (*zap.Logger, *observer.ObservedLogs) {
loggerCore, observedLog := observer.New(zap.DebugLevel)
return zap.New(loggerCore), observedLog
}
func TestTwoFailed(t *testing.T) { func TestTwoFailed(t *testing.T) {
var clientKeys []*ecdsa.PrivateKey var clientKeys []*ecdsa.PrivateKey
mockClientBuilder := func(addr string) client { mockClientBuilder := func(addr string) client {
@ -690,7 +514,7 @@ func TestStatusMonitor(t *testing.T) {
monitor.errorThreshold = 3 monitor.errorThreshold = 3
count := 10 count := 10
for range count { for i := 0; i < count; i++ {
monitor.incErrorRate() monitor.incErrorRate()
} }
@ -705,6 +529,13 @@ func TestStatusMonitor(t *testing.T) {
isHealthy bool isHealthy bool
description string description string
}{ }{
{
action: func(m *clientStatusMonitor) { m.setUnhealthyOnDial() },
status: statusUnhealthyOnDial,
isDialed: false,
isHealthy: false,
description: "set unhealthy on dial",
},
{ {
action: func(m *clientStatusMonitor) { m.setUnhealthy() }, action: func(m *clientStatusMonitor) { m.setUnhealthy() },
status: statusUnhealthyOnRequest, status: statusUnhealthyOnRequest,
@ -723,6 +554,7 @@ func TestStatusMonitor(t *testing.T) {
for _, tc := range cases { for _, tc := range cases {
tc.action(&monitor) tc.action(&monitor)
require.Equal(t, tc.status, monitor.healthy.Load()) require.Equal(t, tc.status, monitor.healthy.Load())
require.Equal(t, tc.isDialed, monitor.isDialed())
require.Equal(t, tc.isHealthy, monitor.isHealthy()) require.Equal(t, tc.isHealthy, monitor.isHealthy())
} }
}) })
@ -730,22 +562,19 @@ func TestStatusMonitor(t *testing.T) {
func TestHandleError(t *testing.T) { func TestHandleError(t *testing.T) {
ctx := context.Background() ctx := context.Background()
log := zaptest.NewLogger(t) monitor := newClientStatusMonitor(zap.NewExample(), "", 10)
canceledCtx, cancel := context.WithCancel(context.Background()) canceledCtx, cancel := context.WithCancel(context.Background())
cancel() cancel()
for _, tc := range []struct { for i, tc := range []struct {
name string
ctx context.Context ctx context.Context
status apistatus.Status status apistatus.Status
err error err error
expectedError bool expectedError bool
countError bool countError bool
markedUnhealthy bool
}{ }{
{ {
name: "no error, no status",
ctx: ctx, ctx: ctx,
status: nil, status: nil,
err: nil, err: nil,
@ -753,7 +582,6 @@ func TestHandleError(t *testing.T) {
countError: false, countError: false,
}, },
{ {
name: "no error, success status",
ctx: ctx, ctx: ctx,
status: new(apistatus.SuccessDefaultV2), status: new(apistatus.SuccessDefaultV2),
err: nil, err: nil,
@ -761,7 +589,6 @@ func TestHandleError(t *testing.T) {
countError: false, countError: false,
}, },
{ {
name: "error, success status",
ctx: ctx, ctx: ctx,
status: new(apistatus.SuccessDefaultV2), status: new(apistatus.SuccessDefaultV2),
err: errors.New("error"), err: errors.New("error"),
@ -769,7 +596,6 @@ func TestHandleError(t *testing.T) {
countError: true, countError: true,
}, },
{ {
name: "error, no status",
ctx: ctx, ctx: ctx,
status: nil, status: nil,
err: errors.New("error"), err: errors.New("error"),
@ -777,7 +603,6 @@ func TestHandleError(t *testing.T) {
countError: true, countError: true,
}, },
{ {
name: "no error, object not found status",
ctx: ctx, ctx: ctx,
status: new(apistatus.ObjectNotFound), status: new(apistatus.ObjectNotFound),
err: nil, err: nil,
@ -785,7 +610,6 @@ func TestHandleError(t *testing.T) {
countError: false, countError: false,
}, },
{ {
name: "object not found error, object not found status",
ctx: ctx, ctx: ctx,
status: new(apistatus.ObjectNotFound), status: new(apistatus.ObjectNotFound),
err: &apistatus.ObjectNotFound{}, err: &apistatus.ObjectNotFound{},
@ -793,7 +617,6 @@ func TestHandleError(t *testing.T) {
countError: false, countError: false,
}, },
{ {
name: "eacl not found error, no status",
ctx: ctx, ctx: ctx,
status: nil, status: nil,
err: &apistatus.EACLNotFound{}, err: &apistatus.EACLNotFound{},
@ -804,7 +627,6 @@ func TestHandleError(t *testing.T) {
countError: true, countError: true,
}, },
{ {
name: "no error, internal status",
ctx: ctx, ctx: ctx,
status: new(apistatus.ServerInternal), status: new(apistatus.ServerInternal),
err: nil, err: nil,
@ -812,7 +634,6 @@ func TestHandleError(t *testing.T) {
countError: true, countError: true,
}, },
{ {
name: "no error, wrong magic status",
ctx: ctx, ctx: ctx,
status: new(apistatus.WrongMagicNumber), status: new(apistatus.WrongMagicNumber),
err: nil, err: nil,
@ -820,7 +641,6 @@ func TestHandleError(t *testing.T) {
countError: true, countError: true,
}, },
{ {
name: "no error, signature verification status",
ctx: ctx, ctx: ctx,
status: new(apistatus.SignatureVerification), status: new(apistatus.SignatureVerification),
err: nil, err: nil,
@ -828,33 +648,20 @@ func TestHandleError(t *testing.T) {
countError: true, countError: true,
}, },
{ {
name: "no error, maintenance status", ctx: ctx,
status: new(apistatus.SignatureVerification),
err: nil,
expectedError: true,
countError: true,
},
{
ctx: ctx, ctx: ctx,
status: new(apistatus.NodeUnderMaintenance), status: new(apistatus.NodeUnderMaintenance),
err: nil, err: nil,
expectedError: true, expectedError: true,
countError: true, countError: true,
markedUnhealthy: true,
}, },
{ {
name: "maintenance error, no status",
ctx: ctx,
status: nil,
err: &apistatus.NodeUnderMaintenance{},
expectedError: true,
countError: true,
markedUnhealthy: true,
},
{
name: "no error, invalid argument status",
ctx: ctx,
status: new(apistatus.InvalidArgument),
err: nil,
expectedError: true,
countError: false,
},
{
name: "context canceled error, no status",
ctx: canceledCtx, ctx: canceledCtx,
status: nil, status: nil,
err: errors.New("error"), err: errors.New("error"),
@ -862,9 +669,8 @@ func TestHandleError(t *testing.T) {
countError: false, countError: false,
}, },
} { } {
t.Run(tc.name, func(t *testing.T) { t.Run(strconv.Itoa(i), func(t *testing.T) {
monitor := newClientStatusMonitor(log, "", 10) errCount := monitor.currentErrorRate()
errCount := monitor.overallErrorRate()
err := monitor.handleError(tc.ctx, tc.status, tc.err) err := monitor.handleError(tc.ctx, tc.status, tc.err)
if tc.expectedError { if tc.expectedError {
require.Error(t, err) require.Error(t, err)
@ -874,10 +680,7 @@ func TestHandleError(t *testing.T) {
if tc.countError { if tc.countError {
errCount++ errCount++
} }
require.Equal(t, errCount, monitor.overallErrorRate()) require.Equal(t, errCount, monitor.currentErrorRate())
if tc.markedUnhealthy {
require.False(t, monitor.isHealthy())
}
}) })
} }
} }
@ -921,7 +724,7 @@ func TestSwitchAfterErrorThreshold(t *testing.T) {
require.NoError(t, err) require.NoError(t, err)
t.Cleanup(pool.Close) t.Cleanup(pool.Close)
for range errorThreshold { for i := 0; i < errorThreshold; i++ {
conn, err := pool.connection() conn, err := pool.connection()
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, nodes[0].address, conn.address()) require.Equal(t, nodes[0].address, conn.address())

View file

@ -30,7 +30,7 @@ func newSampler(probabilities []float64, source rand.Source) *sampler {
sampler.alias = make([]int, n) sampler.alias = make([]int, n)
// Compute scaled probabilities. // Compute scaled probabilities.
p := make([]float64, n) p := make([]float64, n)
for i := range n { for i := 0; i < n; i++ {
p[i] = probabilities[i] * float64(n) p[i] = probabilities[i] * float64(n)
} }
for i, pi := range p { for i, pi := range p {

View file

@ -32,7 +32,7 @@ func TestSamplerStability(t *testing.T) {
for _, tc := range cases { for _, tc := range cases {
sampler := newSampler(tc.probabilities, rand.NewSource(0)) sampler := newSampler(tc.probabilities, rand.NewSource(0))
res := make([]int, len(tc.probabilities)) res := make([]int, len(tc.probabilities))
for range COUNT { for i := 0; i < COUNT; i++ {
res[sampler.Next()]++ res[sampler.Next()]++
} }

View file

@ -43,7 +43,7 @@ func (c *treeClient) dial(ctx context.Context) error {
} }
var err error var err error
if c.conn, c.service, err = createClient(c.address, c.opts...); err != nil { if c.conn, c.service, err = dialClient(ctx, c.address, c.opts...); err != nil {
return err return err
} }
@ -61,7 +61,7 @@ func (c *treeClient) redialIfNecessary(ctx context.Context) (healthHasChanged bo
defer c.mu.Unlock() defer c.mu.Unlock()
if c.conn == nil { if c.conn == nil {
if c.conn, c.service, err = createClient(c.address, c.opts...); err != nil { if c.conn, c.service, err = dialClient(ctx, c.address, c.opts...); err != nil {
return false, err return false, err
} }
} }
@ -77,7 +77,7 @@ func (c *treeClient) redialIfNecessary(ctx context.Context) (healthHasChanged bo
return !wasHealthy, nil return !wasHealthy, nil
} }
func createClient(addr string, clientOptions ...grpc.DialOption) (*grpc.ClientConn, grpcService.TreeServiceClient, error) { func dialClient(ctx context.Context, addr string, clientOptions ...grpc.DialOption) (*grpc.ClientConn, grpcService.TreeServiceClient, error) {
host, tlsEnable, err := apiClient.ParseURI(addr) host, tlsEnable, err := apiClient.ParseURI(addr)
if err != nil { if err != nil {
return nil, nil, fmt.Errorf("parse address: %w", err) return nil, nil, fmt.Errorf("parse address: %w", err)
@ -93,9 +93,9 @@ func createClient(addr string, clientOptions ...grpc.DialOption) (*grpc.ClientCo
// the order is matter, we want client to be able to overwrite options. // the order is matter, we want client to be able to overwrite options.
opts := append(options, clientOptions...) opts := append(options, clientOptions...)
conn, err := grpc.NewClient(host, opts...) conn, err := grpc.DialContext(ctx, host, opts...)
if err != nil { if err != nil {
return nil, nil, fmt.Errorf("grpc create node tree service: %w", err) return nil, nil, fmt.Errorf("grpc dial node tree service: %w", err)
} }
return conn, grpcService.NewTreeServiceClient(conn), nil return conn, grpcService.NewTreeServiceClient(conn), nil

View file

@ -415,7 +415,7 @@ func (p *Pool) GetNodes(ctx context.Context, prm GetNodesParams) ([]*grpcService
// Empty result is expected due to delayed tree service sync. // Empty result is expected due to delayed tree service sync.
// Return an error there to trigger retry and ignore it after, // Return an error there to trigger retry and ignore it after,
// to keep compatibility with 'GetNodeByPath' implementation. // to keep compatibility with 'GetNodeByPath' implementation.
if inErr == nil && len(resp.GetBody().GetNodes()) == 0 { if inErr == nil && len(resp.Body.Nodes) == 0 {
return errNodeEmptyResult return errNodeEmptyResult
} }
return handleError("failed to get node by path", inErr) return handleError("failed to get node by path", inErr)
@ -437,14 +437,14 @@ type SubTreeReader struct {
// Read reads another list of the subtree nodes. // Read reads another list of the subtree nodes.
func (x *SubTreeReader) Read(buf []*grpcService.GetSubTreeResponse_Body) (int, error) { func (x *SubTreeReader) Read(buf []*grpcService.GetSubTreeResponse_Body) (int, error) {
for i := range len(buf) { for i := 0; i < len(buf); i++ {
resp, err := x.cli.Recv() resp, err := x.cli.Recv()
if err == io.EOF { if err == io.EOF {
return i, io.EOF return i, io.EOF
} else if err != nil { } else if err != nil {
return i, handleError("failed to get sub tree", err) return i, handleError("failed to get sub tree", err)
} }
buf[i] = resp.GetBody() buf[i] = resp.Body
} }
return len(buf), nil return len(buf), nil
@ -460,7 +460,7 @@ func (x *SubTreeReader) ReadAll() ([]*grpcService.GetSubTreeResponse_Body, error
} else if err != nil { } else if err != nil {
return nil, handleError("failed to get sub tree", err) return nil, handleError("failed to get sub tree", err)
} }
res = append(res, resp.GetBody()) res = append(res, resp.Body)
} }
return res, nil return res, nil
@ -476,7 +476,7 @@ func (x *SubTreeReader) Next() (*grpcService.GetSubTreeResponse_Body, error) {
return nil, handleError("failed to get sub tree", err) return nil, handleError("failed to get sub tree", err)
} }
return resp.GetBody(), nil return resp.Body, nil
} }
// GetSubTree invokes eponymous method from TreeServiceClient. // GetSubTree invokes eponymous method from TreeServiceClient.
@ -590,12 +590,12 @@ func (p *Pool) AddNodeByPath(ctx context.Context, prm AddNodeByPathParams) (uint
body := resp.GetBody() body := resp.GetBody()
if body == nil { if body == nil {
return 0, errors.New("nil body in tree service response") return 0, errors.New("nil body in tree service response")
} else if len(body.GetNodes()) == 0 { } else if len(body.Nodes) == 0 {
return 0, errors.New("empty list of added nodes in tree service response") return 0, errors.New("empty list of added nodes in tree service response")
} }
// The first node is the leaf that we add, according to tree service docs. // The first node is the leaf that we add, according to tree service docs.
return body.GetNodes()[0], nil return body.Nodes[0], nil
} }
// MoveNode invokes eponymous method from TreeServiceClient. // MoveNode invokes eponymous method from TreeServiceClient.