diff --git a/apis/grpc/v1/agent/core/agent.pb.go b/apis/grpc/v1/agent/core/agent.pb.go index 950cab5b6da..9b9ff65eed0 100644 --- a/apis/grpc/v1/agent/core/agent.pb.go +++ b/apis/grpc/v1/agent/core/agent.pb.go @@ -17,7 +17,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.28.1 -// protoc v3.21.9 +// protoc v3.21.11 // source: apis/proto/v1/agent/core/agent.proto package core diff --git a/apis/grpc/v1/agent/sidecar/sidecar.pb.go b/apis/grpc/v1/agent/sidecar/sidecar.pb.go index 1826acdd65b..6ba405acee7 100644 --- a/apis/grpc/v1/agent/sidecar/sidecar.pb.go +++ b/apis/grpc/v1/agent/sidecar/sidecar.pb.go @@ -17,7 +17,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.28.1 -// protoc v3.21.9 +// protoc v3.21.11 // source: apis/proto/v1/agent/sidecar/sidecar.proto package sidecar diff --git a/apis/grpc/v1/discoverer/discoverer.pb.go b/apis/grpc/v1/discoverer/discoverer.pb.go index a87f8b93888..f36874a8d0d 100644 --- a/apis/grpc/v1/discoverer/discoverer.pb.go +++ b/apis/grpc/v1/discoverer/discoverer.pb.go @@ -17,7 +17,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.28.1 -// protoc v3.21.9 +// protoc v3.21.11 // source: apis/proto/v1/discoverer/discoverer.proto package discoverer diff --git a/apis/grpc/v1/filter/egress/egress_filter.pb.go b/apis/grpc/v1/filter/egress/egress_filter.pb.go index 1a167e6ffa7..a736aaa267e 100644 --- a/apis/grpc/v1/filter/egress/egress_filter.pb.go +++ b/apis/grpc/v1/filter/egress/egress_filter.pb.go @@ -17,7 +17,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.28.1 -// protoc v3.21.9 +// protoc v3.21.11 // source: apis/proto/v1/filter/egress/egress_filter.proto package egress diff --git a/apis/grpc/v1/filter/ingress/ingress_filter.pb.go b/apis/grpc/v1/filter/ingress/ingress_filter.pb.go index 2cdb078d95a..4b23bcebbb1 100644 --- a/apis/grpc/v1/filter/ingress/ingress_filter.pb.go +++ b/apis/grpc/v1/filter/ingress/ingress_filter.pb.go @@ -17,7 +17,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.28.1 -// protoc v3.21.9 +// protoc v3.21.11 // source: apis/proto/v1/filter/ingress/ingress_filter.proto package ingress diff --git a/apis/grpc/v1/manager/index/index_manager.pb.go b/apis/grpc/v1/manager/index/index_manager.pb.go index 78f2d636623..3453a8956c3 100644 --- a/apis/grpc/v1/manager/index/index_manager.pb.go +++ b/apis/grpc/v1/manager/index/index_manager.pb.go @@ -17,7 +17,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.28.1 -// protoc v3.21.9 +// protoc v3.21.11 // source: apis/proto/v1/manager/index/index_manager.proto package index diff --git a/apis/grpc/v1/payload/payload.pb.go b/apis/grpc/v1/payload/payload.pb.go index 2e35b671a96..f7a3a020f53 100644 --- a/apis/grpc/v1/payload/payload.pb.go +++ b/apis/grpc/v1/payload/payload.pb.go @@ -17,7 +17,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.28.1 -// protoc v3.21.9 +// protoc v3.21.11 // source: apis/proto/v1/payload/payload.proto package payload diff --git a/apis/grpc/v1/vald/filter.pb.go b/apis/grpc/v1/vald/filter.pb.go index f96eab20045..54e25fcd931 100644 --- a/apis/grpc/v1/vald/filter.pb.go +++ b/apis/grpc/v1/vald/filter.pb.go @@ -17,7 +17,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.28.1 -// protoc v3.21.9 +// protoc v3.21.11 // source: apis/proto/v1/vald/filter.proto package vald diff --git a/apis/grpc/v1/vald/insert.pb.go b/apis/grpc/v1/vald/insert.pb.go index ccf2f30dafd..9f2e363c539 100644 --- a/apis/grpc/v1/vald/insert.pb.go +++ b/apis/grpc/v1/vald/insert.pb.go @@ -17,7 +17,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.28.1 -// protoc v3.21.9 +// protoc v3.21.11 // source: apis/proto/v1/vald/insert.proto package vald diff --git a/apis/grpc/v1/vald/object.pb.go b/apis/grpc/v1/vald/object.pb.go index cd1c3f90ded..5cc3983af9f 100644 --- a/apis/grpc/v1/vald/object.pb.go +++ b/apis/grpc/v1/vald/object.pb.go @@ -17,7 +17,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.28.1 -// protoc v3.21.9 +// protoc v3.21.11 // source: apis/proto/v1/vald/object.proto package vald diff --git a/apis/grpc/v1/vald/remove.pb.go b/apis/grpc/v1/vald/remove.pb.go index 682806bbde5..c84c84f2a07 100644 --- a/apis/grpc/v1/vald/remove.pb.go +++ b/apis/grpc/v1/vald/remove.pb.go @@ -17,7 +17,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.28.1 -// protoc v3.21.9 +// protoc v3.21.11 // source: apis/proto/v1/vald/remove.proto package vald diff --git a/apis/grpc/v1/vald/search.pb.go b/apis/grpc/v1/vald/search.pb.go index aadaf00c3f5..2a5dbacbb13 100644 --- a/apis/grpc/v1/vald/search.pb.go +++ b/apis/grpc/v1/vald/search.pb.go @@ -17,7 +17,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.28.1 -// protoc v3.21.9 +// protoc v3.21.11 // source: apis/proto/v1/vald/search.proto package vald diff --git a/apis/grpc/v1/vald/update.pb.go b/apis/grpc/v1/vald/update.pb.go index cd0d4cab7d3..fb43e1eed22 100644 --- a/apis/grpc/v1/vald/update.pb.go +++ b/apis/grpc/v1/vald/update.pb.go @@ -17,7 +17,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.28.1 -// protoc v3.21.9 +// protoc v3.21.11 // source: apis/proto/v1/vald/update.proto package vald diff --git a/apis/grpc/v1/vald/upsert.pb.go b/apis/grpc/v1/vald/upsert.pb.go index 0b207b11c5b..721e9aee448 100644 --- a/apis/grpc/v1/vald/upsert.pb.go +++ b/apis/grpc/v1/vald/upsert.pb.go @@ -17,7 +17,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.28.1 -// protoc v3.21.9 +// protoc v3.21.11 // source: apis/proto/v1/vald/upsert.proto package vald diff --git a/example/client/go.mod b/example/client/go.mod index 7d5b8d2e51f..24ddb7eb9d0 100644 --- a/example/client/go.mod +++ b/example/client/go.mod @@ -8,10 +8,10 @@ replace ( github.com/golang/protobuf => github.com/golang/protobuf v1.5.2 github.com/kpango/glg => github.com/kpango/glg v1.6.14 github.com/pkg/sftp => github.com/pkg/sftp v1.13.5 - golang.org/x/crypto => golang.org/x/crypto v0.3.0 - golang.org/x/net => golang.org/x/net v0.3.0 + golang.org/x/crypto => golang.org/x/crypto v0.4.0 + golang.org/x/net => golang.org/x/net v0.4.0 golang.org/x/text => golang.org/x/text v0.5.0 - google.golang.org/genproto => google.golang.org/genproto v0.0.0-20221205194025-8222ab48f5fc + google.golang.org/genproto => google.golang.org/genproto v0.0.0-20221207170731-23e4bf6bdc37 google.golang.org/grpc => google.golang.org/grpc v1.51.0 google.golang.org/protobuf => google.golang.org/protobuf v1.28.1 gopkg.in/yaml.v2 => gopkg.in/yaml.v2 v2.4.0 @@ -23,7 +23,7 @@ require ( github.com/kpango/glg v1.6.14 github.com/vdaas/vald-client-go v1.6.3 gonum.org/v1/hdf5 v0.0.0-20210714002203-8c5d23bc6946 - google.golang.org/grpc v1.50.1 + google.golang.org/grpc v1.51.0 ) require ( diff --git a/example/client/go.sum b/example/client/go.sum index 07d4abf3372..8902f8b2458 100644 --- a/example/client/go.sum +++ b/example/client/go.sum @@ -18,8 +18,8 @@ github.com/vdaas/vald-client-go v1.6.3/go.mod h1:WiE3uVM1gjAEi4wbQi3S7lwfASR4BMi go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw= go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4= go.uber.org/zap v1.23.0 h1:OjGQ5KQDEUawVHxNwQgPpiypGHOxo2mNZsOqTak4fFY= -golang.org/x/net v0.3.0 h1:VWL6FNY2bEEmsGVKabSlHu5Irp34xmMRoqb/9lF9lxk= -golang.org/x/net v0.3.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE= +golang.org/x/net v0.4.0 h1:Q5QPcMlvfxFTAPV0+07Xz/MpK9NTXu2VDUuy0FeMfaU= +golang.org/x/net v0.4.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE= golang.org/x/sys v0.3.0 h1:w8ZOecv6NaNa/zC8944JTU3vz4u6Lagfk4RPQxv92NQ= golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/text v0.5.0 h1:OLmvp0KP+FVG99Ct/qFiL/Fhk4zp4QQnZ7b2U+5piUM= @@ -28,8 +28,8 @@ golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 h1:H2TDz8ibqkAF6YGhCdN3jS9O0/s90v0rJh3X/OLHEUk= gonum.org/v1/hdf5 v0.0.0-20210714002203-8c5d23bc6946 h1:vJpL69PeUullhJyKtTjHjENEmZU3BkO4e+fod7nKzgM= gonum.org/v1/hdf5 v0.0.0-20210714002203-8c5d23bc6946/go.mod h1:BQUWDHIAygjdt1HnUPQ0eWqLN2n5FwJycrpYUVUOx2I= -google.golang.org/genproto v0.0.0-20221205194025-8222ab48f5fc h1:nUKKji0AarrQKh6XpFEpG3p1TNztxhe7C8TcUvDgXqw= -google.golang.org/genproto v0.0.0-20221205194025-8222ab48f5fc/go.mod h1:1dOng4TWOomJrDGhpXjfCD35wQC6jnC7HpRmOFRqEV0= +google.golang.org/genproto v0.0.0-20221207170731-23e4bf6bdc37 h1:jmIfw8+gSvXcZSgaFAGyInDXeWzUhvYH57G/5GKMn70= +google.golang.org/genproto v0.0.0-20221207170731-23e4bf6bdc37/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= google.golang.org/grpc v1.51.0 h1:E1eGv1FTqoLIdnBCZufiSHgKjlqG6fKFf6pPWtMTh8U= google.golang.org/grpc v1.51.0/go.mod h1:wgNDFcnuBGmxLKI/qn4T+m5BtEBYXJPvibbUPsAIPww= google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w= diff --git a/go.mod b/go.mod index 2829c098c86..b67e7caee67 100755 --- a/go.mod +++ b/go.mod @@ -486,8 +486,8 @@ replace ( go4.org/unsafe/assume-no-moving-gc => go4.org/unsafe/assume-no-moving-gc v0.0.0-20220617031537-928513b29760 gocloud.dev => gocloud.dev v0.27.0 golang.org/x/crypto => golang.org/x/crypto v0.4.0 - golang.org/x/exp => golang.org/x/exp v0.0.0-20221215174704-0915cd710c24 - golang.org/x/exp/typeparams => golang.org/x/exp/typeparams v0.0.0-20221215174704-0915cd710c24 + golang.org/x/exp => golang.org/x/exp v0.0.0-20221217163422-3c43f8badb15 + golang.org/x/exp/typeparams => golang.org/x/exp/typeparams v0.0.0-20221217163422-3c43f8badb15 golang.org/x/image => golang.org/x/image v0.2.0 golang.org/x/lint => golang.org/x/lint v0.0.0-20210508222113-6edffad5e616 golang.org/x/mobile => golang.org/x/mobile v0.0.0-20221110043201-43a038452099 @@ -563,6 +563,7 @@ require ( github.com/gocql/gocql v0.0.0-20200131111108-92af2e088537 github.com/gocraft/dbr/v2 v2.0.0-00010101000000-000000000000 github.com/google/go-cmp v0.5.9 + github.com/google/uuid v1.3.0 github.com/gorilla/mux v0.0.0-00010101000000-000000000000 github.com/hashicorp/go-version v0.0.0-00010101000000-000000000000 github.com/klauspost/compress v1.15.1 @@ -647,7 +648,6 @@ require ( github.com/google/gnostic v0.5.7-v3refs // indirect github.com/google/gofuzz v1.1.0 // indirect github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect - github.com/google/uuid v1.3.0 // indirect github.com/google/wire v0.5.0 // indirect github.com/googleapis/enterprise-certificate-proxy v0.2.0 // indirect github.com/googleapis/gax-go/v2 v2.7.0 // indirect diff --git a/go.sum b/go.sum index 9f9065e6224..575e1a88ff6 100644 --- a/go.sum +++ b/go.sum @@ -628,10 +628,10 @@ gocloud.dev v0.27.0 h1:j0WTUsnKTxCsWO7y8T+YCiBZUmLl9w/WIowqAY3yo0g= gocloud.dev v0.27.0/go.mod h1:YlYKhYsY5/1JdHGWQDkAuqkezVKowu7qbe9aIeUF6p0= golang.org/x/crypto v0.4.0 h1:UVQgzMY87xqpKNgb+kDsll2Igd33HszWHFLmpaRMq/8= golang.org/x/crypto v0.4.0/go.mod h1:3quD/ATkf6oY+rnes5c3ExXTbLc8mueNue5/DoinL80= -golang.org/x/exp v0.0.0-20221215174704-0915cd710c24 h1:6w3iSY8IIkp5OQtbYj8NeuKG1jS9d+kYaubXqsoOiQ8= -golang.org/x/exp v0.0.0-20221215174704-0915cd710c24/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= -golang.org/x/exp/typeparams v0.0.0-20221215174704-0915cd710c24 h1:+iZuikSm1jIhtO1dsw9jQcYCoGFEDDVXp236qRsnqK4= -golang.org/x/exp/typeparams v0.0.0-20221215174704-0915cd710c24/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk= +golang.org/x/exp v0.0.0-20221217163422-3c43f8badb15 h1:5oN1Pz/eDhCpbMbLstvIPa0b/BEQo6g6nwV3pLjfM6w= +golang.org/x/exp v0.0.0-20221217163422-3c43f8badb15/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= +golang.org/x/exp/typeparams v0.0.0-20221217163422-3c43f8badb15 h1:WumQqbro49zP5y7xSPDDdBZBwiUrWNZ7ZbKUQst9RiA= +golang.org/x/exp/typeparams v0.0.0-20221217163422-3c43f8badb15/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk= golang.org/x/image v0.2.0 h1:/DcQ0w3VHKCC5p0/P2B0JpAZ9Z++V2KOo2fyU89CXBQ= golang.org/x/image v0.2.0/go.mod h1:la7oBXb9w3YFjBqaAwtynVioc1ZvOnNteUNrifGNmAI= golang.org/x/lint v0.0.0-20210508222113-6edffad5e616 h1:VLliZ0d+/avPrXXH+OakdXhpJuEoBZuwh1m2j7U6Iug= diff --git a/internal/test/data/vector/gen.go b/internal/test/data/vector/gen.go index f7c461f822e..35462c4a502 100644 --- a/internal/test/data/vector/gen.go +++ b/internal/test/data/vector/gen.go @@ -16,6 +16,7 @@ package vector import ( "math" "math/rand" + "time" "github.com/vdaas/vald/internal/errors" irand "github.com/vdaas/vald/internal/rand" @@ -30,6 +31,7 @@ type ( const ( Gaussian Distribution = iota Uniform + NegativeUniform // NOTE: mean:128, sigma:128/3, all of 99.7% are in [0, 255]. gaussianMean float64 = 128 @@ -46,6 +48,8 @@ func Float32VectorGenerator(d Distribution) (Float32VectorGeneratorFunc, error) return GaussianDistributedFloat32VectorGenerator, nil case Uniform: return UniformDistributedFloat32VectorGenerator, nil + case NegativeUniform: + return NegativeUniformDistributedFloat32VectorGenerator, nil default: return nil, ErrUnknownDistribution } @@ -82,6 +86,23 @@ func UniformDistributedFloat32VectorGenerator(n, dim int) [][]float32 { return genF32Slice(n, dim, rand.Float32) } +// NegativeUniformDistributedFloat32VectorGenerator returns n float32 vectors with dim dimension and their values under Uniform distribution +func NegativeUniformDistributedFloat32VectorGenerator(n, dim int) (vecs [][]float32) { + left, right := dim/2, dim-dim/2 + lvs := genF32Slice(n, left, func() float32 { + return -rand.Float32() + }) + rvs := UniformDistributedFloat32VectorGenerator(n, right) + vecs = make([][]float32, 0, n) + rand.Seed(time.Now().UnixNano()) + for i := 0; i < n; i++ { + vs := append(lvs[i], rvs[i]...) + rand.Shuffle(len(vs), func(i, j int) { vs[i], vs[j] = vs[j], vs[i] }) + vecs = append(vecs, vs) + } + return vecs +} + // GaussianDistributedFloat32VectorGenerator returns n float32 vectors with dim dimension and their values under Gaussian distribution func GaussianDistributedFloat32VectorGenerator(n, dim int) [][]float32 { return genF32Slice(n, dim, func() float32 { diff --git a/pkg/agent/core/ngt/handler/grpc/index.go b/pkg/agent/core/ngt/handler/grpc/index.go index f97ad336f1a..28f20c5bb39 100644 --- a/pkg/agent/core/ngt/handler/grpc/index.go +++ b/pkg/agent/core/ngt/handler/grpc/index.go @@ -21,6 +21,7 @@ import ( "github.com/vdaas/vald/internal/errors" "github.com/vdaas/vald/internal/info" "github.com/vdaas/vald/internal/log" + "github.com/vdaas/vald/internal/net/grpc/codes" "github.com/vdaas/vald/internal/net/grpc/errdetails" "github.com/vdaas/vald/internal/net/grpc/status" "github.com/vdaas/vald/internal/observability/trace" @@ -36,8 +37,9 @@ func (s *server) CreateIndex(ctx context.Context, c *payload.Control_CreateIndex res = new(payload.Empty) err = s.ngt.CreateIndex(ctx, c.GetPoolSize()) if err != nil { - if errors.Is(err, errors.ErrUncommittedIndexNotFound) { - err = status.WrapWithFailedPrecondition(fmt.Sprintf("CreateIndex API failed to create indexes pool_size = %d", c.GetPoolSize()), err, + var ( + code codes.Code + details = []interface{}{ &errdetails.RequestInfo{ ServingData: errdetails.Serialize(c), }, @@ -45,34 +47,35 @@ func (s *server) CreateIndex(ctx context.Context, c *payload.Control_CreateIndex ResourceType: ngtResourceType + "/ngt.CreateIndex", ResourceName: fmt.Sprintf("%s: %s(%s)", apiName, s.name, s.ip), }, - &errdetails.PreconditionFailure{ + } + ) + + switch { + case errors.Is(err, errors.ErrUncommittedIndexNotFound): + err = status.WrapWithFailedPrecondition(fmt.Sprintf("CreateIndex API failed to create indexes pool_size = %d due to the precondition failure, error: %v", c.GetPoolSize(), err), err, + append(details, &errdetails.PreconditionFailure{ Violations: []*errdetails.PreconditionFailureViolation{ { Type: "uncommitted index is empty", Subject: "failed to CreateIndex operation caused by empty uncommitted indices", }, }, - }, info.Get()) - if span != nil { - span.RecordError(err) - span.SetAttributes(trace.StatusCodeFailedPrecondition(err.Error())...) - span.SetStatus(trace.StatusError, err.Error()) - } - return nil, err + }, info.Get())...) + code = codes.FailedPrecondition + case errors.Is(err, context.Canceled): + err = status.WrapWithCanceled(fmt.Sprintf("CreateIndex API canceled to create indexes pool_size = %d, error: %v", c.GetPoolSize(), err), err, details...) + code = codes.Canceled + case errors.Is(err, context.DeadlineExceeded): + err = status.WrapWithDeadlineExceeded(fmt.Sprintf("CreateIndex API deadline exceeded to create indexes pool_size = %d, error: %v", c.GetPoolSize(), err), err, details...) + code = codes.DeadlineExceeded + default: + err = status.WrapWithInternal(fmt.Sprintf("CreateIndex API failed to create indexes pool_size = %d, error: %v", c.GetPoolSize(), err), err, append(details, info.Get())...) + code = codes.Internal } log.Error(err) - err = status.WrapWithInternal(fmt.Sprintf("CreateIndex API failed to create indexes pool_size = %d", c.GetPoolSize()), err, - &errdetails.RequestInfo{ - ServingData: errdetails.Serialize(c), - }, - &errdetails.ResourceInfo{ - ResourceType: ngtResourceType + "/ngt.CreateIndex", - ResourceName: fmt.Sprintf("%s: %s(%s)", apiName, s.name, s.ip), - }, info.Get()) - log.Error(err) if span != nil { span.RecordError(err) - span.SetAttributes(trace.StatusCodeInternal(err.Error())...) + span.SetAttributes(trace.FromGRPCStatus(code, err.Error())...) span.SetStatus(trace.StatusError, err.Error()) } return nil, err @@ -117,8 +120,9 @@ func (s *server) CreateAndSaveIndex(ctx context.Context, c *payload.Control_Crea res = new(payload.Empty) err = s.ngt.CreateAndSaveIndex(ctx, c.GetPoolSize()) if err != nil { - if errors.Is(err, errors.ErrUncommittedIndexNotFound) { - err = status.WrapWithFailedPrecondition(fmt.Sprintf("CreateAndSaveIndex API failed to create indexes pool_size = %d", c.GetPoolSize()), err, + var ( + code codes.Code + details = []interface{}{ &errdetails.RequestInfo{ ServingData: errdetails.Serialize(c), }, @@ -126,36 +130,37 @@ func (s *server) CreateAndSaveIndex(ctx context.Context, c *payload.Control_Crea ResourceType: ngtResourceType + "/ngt.CreateAndSaveIndex", ResourceName: fmt.Sprintf("%s: %s(%s)", apiName, s.name, s.ip), }, - &errdetails.PreconditionFailure{ + } + ) + + switch { + case errors.Is(err, errors.ErrUncommittedIndexNotFound): + err = status.WrapWithFailedPrecondition(fmt.Sprintf("CreateAndSaveIndex API failed to create indexes pool_size = %d due to the precondition failure, error: %v", c.GetPoolSize(), err), err, + append(details, &errdetails.PreconditionFailure{ Violations: []*errdetails.PreconditionFailureViolation{ { Type: "uncommitted index is empty", Subject: "failed to CreateAndSaveIndex operation caused by empty uncommitted indices", }, }, - }, info.Get()) - if span != nil { - span.RecordError(err) - span.SetAttributes(trace.StatusCodeFailedPrecondition(err.Error())...) - span.SetStatus(trace.StatusError, err.Error()) - } - return nil, err + }, info.Get())...) + code = codes.FailedPrecondition + case errors.Is(err, context.Canceled): + err = status.WrapWithCanceled(fmt.Sprintf("CreateAndSaveIndex API canceled to create indexes pool_size = %d, error: %v", c.GetPoolSize(), err), err, details...) + code = codes.Canceled + case errors.Is(err, context.DeadlineExceeded): + err = status.WrapWithDeadlineExceeded(fmt.Sprintf("CreateAndSaveIndex API deadline exceeded to create indexes pool_size = %d, error: %v", c.GetPoolSize(), err), err, details...) + code = codes.DeadlineExceeded + default: + err = status.WrapWithInternal(fmt.Sprintf("CreateAndSaveIndex API failed to create indexes pool_size = %d, error: %v", c.GetPoolSize(), err), err, append(details, info.Get())...) + code = codes.Internal } - err = status.WrapWithInternal(fmt.Sprintf("CreateAndSaveIndex API failed to create indexes pool_size = %d", c.GetPoolSize()), err, - &errdetails.RequestInfo{ - ServingData: errdetails.Serialize(c), - }, - &errdetails.ResourceInfo{ - ResourceType: ngtResourceType + "/ngt.CreateAndSaveIndex", - ResourceName: fmt.Sprintf("%s: %s(%s)", apiName, s.name, s.ip), - }, info.Get()) log.Error(err) if span != nil { span.RecordError(err) - span.SetAttributes(trace.StatusCodeInternal(err.Error())...) + span.SetAttributes(trace.FromGRPCStatus(code, err.Error())...) span.SetStatus(trace.StatusError, err.Error()) } - return nil, err } return res, nil } diff --git a/pkg/agent/core/ngt/service/kvs/kvs_test.go b/pkg/agent/core/ngt/service/kvs/kvs_test.go index 44c230d9526..9cec77373b7 100644 --- a/pkg/agent/core/ngt/service/kvs/kvs_test.go +++ b/pkg/agent/core/ngt/service/kvs/kvs_test.go @@ -1773,3 +1773,56 @@ func Test_bidi_Close(t *testing.T) { }) } } + +func Test_set_get_delete(t *testing.T) { + // ids := readFileByLine("/tmp/20221026_ids") + ids := []string{ + // ids here + } + + b := New().(*bidi) + for i, id := range ids { + b.Set(id, uint32(i)) + } + for i, id := range ids { + j, ok := b.Get(id) + if !ok { + t.Fatal("get not ok") + } + if uint32(i) != j { + t.Fatal("get not match") + } + } + + wg := sync.WaitGroup{} + for idx, uuid := range ids { + i := idx + id := uuid + + wg.Add(1) + go func() { + defer wg.Done() + + j, ok := b.Delete(id) + if !ok { + t.Fatal("delete not ok") + } + if uint32(i) != j { + t.Fatal("delete not match") + } + + b.Set(id, uint32(i)) + + j, ok = b.Get(id) + if !ok { + t.Fatal("get not ok") + } + if uint32(i) != j { + t.Fatal("get not match") + } + }() + } + + wg.Wait() + // t.Fatalf("id length: %d, ou length: %d, uo length: %d", len(ids), len(b.ou), len(b.uo)) +} diff --git a/pkg/agent/core/ngt/service/ngt.go b/pkg/agent/core/ngt/service/ngt.go index 15e2b24a602..a50835a7ffa 100644 --- a/pkg/agent/core/ngt/service/ngt.go +++ b/pkg/agent/core/ngt/service/ngt.go @@ -99,6 +99,7 @@ type ngt struct { // counters nocie uint64 // number of create index execution nogce uint64 // number of proactive GC execution + wfci uint64 // wait for create indexing // configurations inMem bool // in-memory mode @@ -827,6 +828,12 @@ func (n *ngt) CreateIndex(ctx context.Context, poolSize uint32) (err error) { if ic == 0 { return errors.ErrUncommittedIndexNotFound } + wf := atomic.AddUint64(&n.wfci, 1) + if wf > 1 { + atomic.AddUint64(&n.wfci, ^uint64(0)) + // log.Debugf("concurrent create index waiting detected this request will be ignored, concurrent: %d", wf) + return nil + } err = func() error { ticker := time.NewTicker(time.Millisecond * 100) defer ticker.Stop() @@ -835,10 +842,12 @@ func (n *ngt) CreateIndex(ctx context.Context, poolSize uint32) (err error) { runtime.Gosched() select { case <-ctx.Done(): + atomic.AddUint64(&n.wfci, ^uint64(0)) return ctx.Err() case <-ticker.C: } } + atomic.AddUint64(&n.wfci, ^uint64(0)) return nil }() if err != nil { @@ -857,17 +866,20 @@ func (n *ngt) CreateIndex(ctx context.Context, poolSize uint32) (err error) { log.Infof("create index operation started, uncommitted indexes = %d", ic) log.Debug("create index delete phase started") n.vq.RangePopDelete(ctx, now, func(uuid string) bool { + log.Debugf("start delete operation for kvsdb id: %s", uuid) oid, ok := n.kvs.Delete(uuid) if !ok { log.Warn(errors.ErrObjectIDNotFound(uuid)) return true } + log.Debugf("start remove operation for ngt index id: %s, oid: %d", uuid, oid) if err := n.core.Remove(uint(oid)); err != nil { log.Errorf("failed to remove oid: %d from ngt index. error: %v", oid, err) n.fmu.Lock() n.fmap[uuid] = oid n.fmu.Unlock() } + log.Debugf("removed from ngt index and kvsdb id: %s, oid: %d", uuid, oid) return true }) log.Debug("create index delete phase finished") @@ -875,28 +887,31 @@ func (n *ngt) CreateIndex(ctx context.Context, poolSize uint32) (err error) { log.Debug("create index insert phase started") var icnt uint32 n.vq.RangePopInsert(ctx, now, func(uuid string, vector []float32) bool { + log.Debugf("start insert operation for ngt index id: %s", uuid) oid, err := n.core.Insert(vector) if err != nil { log.Warnf("failed to insert vector uuid: %s vec: %v to ngt index. error: %v", uuid, vector, err) - if !errors.Is(err, errors.ErrIncompatibleDimensionSize(len(vector), n.dim)) { - oid, err = n.core.Insert(vector) - if err != nil { - log.Errorf("failed to retry insert vector uuid: %s vec: %v to ngt index. error: %v", uuid, vector, err) - return true - } - n.kvs.Set(uuid, uint32(oid)) - atomic.AddUint32(&icnt, 1) + if errors.Is(err, errors.ErrIncompatibleDimensionSize(len(vector), n.dim)) { + log.Error(err) + return true + } + oid, err = n.core.Insert(vector) + if err != nil { + log.Errorf("failed to retry insert vector uuid: %s vec: %v to ngt index. error: %v", uuid, vector, err) + return true } - } else { - n.kvs.Set(uuid, uint32(oid)) - atomic.AddUint32(&icnt, 1) } + log.Debugf("start insert operation for kvsdb id: %s, oid: %d", uuid, oid) + n.kvs.Set(uuid, uint32(oid)) + atomic.AddUint32(&icnt, 1) + n.fmu.Lock() _, ok := n.fmap[uuid] if ok { delete(n.fmap, uuid) } n.fmu.Unlock() + log.Debugf("inserted to ngt index and kvsdb id: %s, oid: %d", uuid, oid) return true }) if poolSize <= 0 { @@ -1183,12 +1198,15 @@ func (n *ngt) Exists(uuid string) (oid uint32, ok bool) { if !ok { oid, ok = n.kvs.Get(uuid) if !ok { - log.Debugf("Exists\tuuid: %s's data not found in kvsdb and insert vqueue\terror: %v", uuid, errors.ErrObjectIDNotFound(uuid)) + // log.Debugf("Exists\tuuid: %s's data not found in kvsdb and insert vqueue\terror: %v", uuid, errors.ErrObjectIDNotFound(uuid)) return 0, false } if n.vq.DVExists(uuid) { - log.Debugf("Exists\tuuid: %s's data found in kvsdb and not found in insert vqueue, but delete vqueue data exists. the object will be delete soon\terror: %v", - uuid, errors.ErrObjectIDNotFound(uuid)) + // log.Debugf( + // "Exists\tuuid: %s's data found in kvsdb and not found in insert vqueue, but delete vqueue data exists. the object will be delete soon\terror: %v", + // uuid, + // errors.ErrObjectIDNotFound(uuid), + // ) return 0, false } } diff --git a/pkg/agent/core/ngt/service/ngt_test.go b/pkg/agent/core/ngt/service/ngt_test.go index efcb994eadf..7048a202936 100644 --- a/pkg/agent/core/ngt/service/ngt_test.go +++ b/pkg/agent/core/ngt/service/ngt_test.go @@ -19,15 +19,21 @@ package service import ( "context" + "fmt" + "math" "reflect" + "sync" "sync/atomic" "testing" "time" + "github.com/google/uuid" "github.com/vdaas/vald/internal/config" core "github.com/vdaas/vald/internal/core/algorithm/ngt" "github.com/vdaas/vald/internal/errgroup" "github.com/vdaas/vald/internal/errors" + "github.com/vdaas/vald/internal/log" + "github.com/vdaas/vald/internal/test/data/vector" "github.com/vdaas/vald/internal/test/goleak" "github.com/vdaas/vald/pkg/agent/core/ngt/model" "github.com/vdaas/vald/pkg/agent/core/ngt/service/kvs" @@ -48,8 +54,8 @@ func TestNew(t *testing.T) { args args want want checkFunc func(want, NGT, error) error - beforeFunc func(args) - afterFunc func(args) + beforeFunc func(*testing.T, args) + afterFunc func(*testing.T, args) } defaultCheckFunc := func(w want, gotNn NGT, err error) error { if !errors.Is(err, w.err) { @@ -71,6 +77,12 @@ func TestNew(t *testing.T) { }, want: want{}, checkFunc: defaultCheckFunc, + beforeFunc: func(t *testing.T, args args) { + t.Helper() + }, + afterFunc: func(t *testing.T, args args) { + t.Helper() + }, }, */ @@ -85,6 +97,12 @@ func TestNew(t *testing.T) { }, want: want{}, checkFunc: defaultCheckFunc, + beforeFunc: func(t *testing.T, args args) { + t.Helper() + }, + afterFunc: func(t *testing.T, args args) { + t.Helper() + }, } }(), */ @@ -96,10 +114,10 @@ func TestNew(t *testing.T) { tt.Parallel() defer goleak.VerifyNone(tt, goleak.IgnoreCurrent()) if test.beforeFunc != nil { - test.beforeFunc(test.args) + test.beforeFunc(tt, test.args) } if test.afterFunc != nil { - defer test.afterFunc(test.args) + defer test.afterFunc(tt, test.args) } checkFunc := test.checkFunc if test.checkFunc == nil { @@ -163,8 +181,8 @@ func Test_ngt_load(t *testing.T) { fields fields want want checkFunc func(want, error) error - beforeFunc func(args) - afterFunc func(args) + beforeFunc func(*testing.T, args) + afterFunc func(*testing.T, args) } defaultCheckFunc := func(w want, err error) error { if !errors.Is(err, w.err) { @@ -218,6 +236,12 @@ func Test_ngt_load(t *testing.T) { }, want: want{}, checkFunc: defaultCheckFunc, + beforeFunc: func(t *testing.T, args args) { + t.Helper() + }, + afterFunc: func(t *testing.T, args args) { + t.Helper() + }, }, */ @@ -267,6 +291,12 @@ func Test_ngt_load(t *testing.T) { }, want: want{}, checkFunc: defaultCheckFunc, + beforeFunc: func(t *testing.T, args args) { + t.Helper() + }, + afterFunc: func(t *testing.T, args args) { + t.Helper() + }, } }(), */ @@ -278,10 +308,10 @@ func Test_ngt_load(t *testing.T) { tt.Parallel() defer goleak.VerifyNone(tt, goleak.IgnoreCurrent()) if test.beforeFunc != nil { - test.beforeFunc(test.args) + test.beforeFunc(tt, test.args) } if test.afterFunc != nil { - defer test.afterFunc(test.args) + defer test.afterFunc(tt, test.args) } checkFunc := test.checkFunc if test.checkFunc == nil { @@ -377,8 +407,8 @@ func Test_ngt_initNGT(t *testing.T) { fields fields want want checkFunc func(want, error) error - beforeFunc func(args) - afterFunc func(args) + beforeFunc func(*testing.T, args) + afterFunc func(*testing.T, args) } defaultCheckFunc := func(w want, err error) error { if !errors.Is(err, w.err) { @@ -430,6 +460,12 @@ func Test_ngt_initNGT(t *testing.T) { }, want: want{}, checkFunc: defaultCheckFunc, + beforeFunc: func(t *testing.T, args args) { + t.Helper() + }, + afterFunc: func(t *testing.T, args args) { + t.Helper() + }, }, */ @@ -477,6 +513,12 @@ func Test_ngt_initNGT(t *testing.T) { }, want: want{}, checkFunc: defaultCheckFunc, + beforeFunc: func(t *testing.T, args args) { + t.Helper() + }, + afterFunc: func(t *testing.T, args args) { + t.Helper() + }, } }(), */ @@ -488,10 +530,10 @@ func Test_ngt_initNGT(t *testing.T) { tt.Parallel() defer goleak.VerifyNone(tt, goleak.IgnoreCurrent()) if test.beforeFunc != nil { - test.beforeFunc(test.args) + test.beforeFunc(tt, test.args) } if test.afterFunc != nil { - defer test.afterFunc(test.args) + defer test.afterFunc(tt, test.args) } checkFunc := test.checkFunc if test.checkFunc == nil { @@ -587,8 +629,8 @@ func Test_ngt_loadKVS(t *testing.T) { fields fields want want checkFunc func(want, error) error - beforeFunc func(args) - afterFunc func(args) + beforeFunc func(*testing.T, args) + afterFunc func(*testing.T, args) } defaultCheckFunc := func(w want, err error) error { if !errors.Is(err, w.err) { @@ -640,6 +682,12 @@ func Test_ngt_loadKVS(t *testing.T) { }, want: want{}, checkFunc: defaultCheckFunc, + beforeFunc: func(t *testing.T, args args) { + t.Helper() + }, + afterFunc: func(t *testing.T, args args) { + t.Helper() + }, }, */ @@ -687,6 +735,12 @@ func Test_ngt_loadKVS(t *testing.T) { }, want: want{}, checkFunc: defaultCheckFunc, + beforeFunc: func(t *testing.T, args args) { + t.Helper() + }, + afterFunc: func(t *testing.T, args args) { + t.Helper() + }, } }(), */ @@ -698,10 +752,10 @@ func Test_ngt_loadKVS(t *testing.T) { tt.Parallel() defer goleak.VerifyNone(tt, goleak.IgnoreCurrent()) if test.beforeFunc != nil { - test.beforeFunc(test.args) + test.beforeFunc(tt, test.args) } if test.afterFunc != nil { - defer test.afterFunc(test.args) + defer test.afterFunc(tt, test.args) } checkFunc := test.checkFunc if test.checkFunc == nil { @@ -797,8 +851,8 @@ func Test_ngt_Start(t *testing.T) { fields fields want want checkFunc func(want, <-chan error) error - beforeFunc func(args) - afterFunc func(args) + beforeFunc func(*testing.T, args) + afterFunc func(*testing.T, args) } defaultCheckFunc := func(w want, got <-chan error) error { if !reflect.DeepEqual(got, w.want) { @@ -850,6 +904,12 @@ func Test_ngt_Start(t *testing.T) { }, want: want{}, checkFunc: defaultCheckFunc, + beforeFunc: func(t *testing.T, args args) { + t.Helper() + }, + afterFunc: func(t *testing.T, args args) { + t.Helper() + }, }, */ @@ -897,6 +957,12 @@ func Test_ngt_Start(t *testing.T) { }, want: want{}, checkFunc: defaultCheckFunc, + beforeFunc: func(t *testing.T, args args) { + t.Helper() + }, + afterFunc: func(t *testing.T, args args) { + t.Helper() + }, } }(), */ @@ -908,10 +974,10 @@ func Test_ngt_Start(t *testing.T) { tt.Parallel() defer goleak.VerifyNone(tt, goleak.IgnoreCurrent()) if test.beforeFunc != nil { - test.beforeFunc(test.args) + test.beforeFunc(tt, test.args) } if test.afterFunc != nil { - defer test.afterFunc(test.args) + defer test.afterFunc(tt, test.args) } checkFunc := test.checkFunc if test.checkFunc == nil { @@ -1011,8 +1077,8 @@ func Test_ngt_Search(t *testing.T) { fields fields want want checkFunc func(want, []model.Distance, error) error - beforeFunc func(args) - afterFunc func(args) + beforeFunc func(*testing.T, args) + afterFunc func(*testing.T, args) } defaultCheckFunc := func(w want, got []model.Distance, err error) error { if !errors.Is(err, w.err) { @@ -1070,6 +1136,12 @@ func Test_ngt_Search(t *testing.T) { }, want: want{}, checkFunc: defaultCheckFunc, + beforeFunc: func(t *testing.T, args args) { + t.Helper() + }, + afterFunc: func(t *testing.T, args args) { + t.Helper() + }, }, */ @@ -1120,6 +1192,12 @@ func Test_ngt_Search(t *testing.T) { }, want: want{}, checkFunc: defaultCheckFunc, + beforeFunc: func(t *testing.T, args args) { + t.Helper() + }, + afterFunc: func(t *testing.T, args args) { + t.Helper() + }, } }(), */ @@ -1131,10 +1209,10 @@ func Test_ngt_Search(t *testing.T) { tt.Parallel() defer goleak.VerifyNone(tt, goleak.IgnoreCurrent()) if test.beforeFunc != nil { - test.beforeFunc(test.args) + test.beforeFunc(tt, test.args) } if test.afterFunc != nil { - defer test.afterFunc(test.args) + defer test.afterFunc(tt, test.args) } checkFunc := test.checkFunc if test.checkFunc == nil { @@ -1235,8 +1313,8 @@ func Test_ngt_SearchByID(t *testing.T) { fields fields want want checkFunc func(want, []float32, []model.Distance, error) error - beforeFunc func(args) - afterFunc func(args) + beforeFunc func(*testing.T, args) + afterFunc func(*testing.T, args) } defaultCheckFunc := func(w want, gotVec []float32, gotDst []model.Distance, err error) error { if !errors.Is(err, w.err) { @@ -1297,6 +1375,12 @@ func Test_ngt_SearchByID(t *testing.T) { }, want: want{}, checkFunc: defaultCheckFunc, + beforeFunc: func(t *testing.T, args args) { + t.Helper() + }, + afterFunc: func(t *testing.T, args args) { + t.Helper() + }, }, */ @@ -1347,6 +1431,12 @@ func Test_ngt_SearchByID(t *testing.T) { }, want: want{}, checkFunc: defaultCheckFunc, + beforeFunc: func(t *testing.T, args args) { + t.Helper() + }, + afterFunc: func(t *testing.T, args args) { + t.Helper() + }, } }(), */ @@ -1358,10 +1448,10 @@ func Test_ngt_SearchByID(t *testing.T) { tt.Parallel() defer goleak.VerifyNone(tt, goleak.IgnoreCurrent()) if test.beforeFunc != nil { - test.beforeFunc(test.args) + test.beforeFunc(tt, test.args) } if test.afterFunc != nil { - defer test.afterFunc(test.args) + defer test.afterFunc(tt, test.args) } checkFunc := test.checkFunc if test.checkFunc == nil { @@ -1459,8 +1549,8 @@ func Test_ngt_LinearSearch(t *testing.T) { fields fields want want checkFunc func(want, []model.Distance, error) error - beforeFunc func(args) - afterFunc func(args) + beforeFunc func(*testing.T, args) + afterFunc func(*testing.T, args) } defaultCheckFunc := func(w want, got []model.Distance, err error) error { if !errors.Is(err, w.err) { @@ -1516,6 +1606,12 @@ func Test_ngt_LinearSearch(t *testing.T) { }, want: want{}, checkFunc: defaultCheckFunc, + beforeFunc: func(t *testing.T, args args) { + t.Helper() + }, + afterFunc: func(t *testing.T, args args) { + t.Helper() + }, }, */ @@ -1564,6 +1660,12 @@ func Test_ngt_LinearSearch(t *testing.T) { }, want: want{}, checkFunc: defaultCheckFunc, + beforeFunc: func(t *testing.T, args args) { + t.Helper() + }, + afterFunc: func(t *testing.T, args args) { + t.Helper() + }, } }(), */ @@ -1575,10 +1677,10 @@ func Test_ngt_LinearSearch(t *testing.T) { tt.Parallel() defer goleak.VerifyNone(tt, goleak.IgnoreCurrent()) if test.beforeFunc != nil { - test.beforeFunc(test.args) + test.beforeFunc(tt, test.args) } if test.afterFunc != nil { - defer test.afterFunc(test.args) + defer test.afterFunc(tt, test.args) } checkFunc := test.checkFunc if test.checkFunc == nil { @@ -1677,8 +1779,8 @@ func Test_ngt_LinearSearchByID(t *testing.T) { fields fields want want checkFunc func(want, []float32, []model.Distance, error) error - beforeFunc func(args) - afterFunc func(args) + beforeFunc func(*testing.T, args) + afterFunc func(*testing.T, args) } defaultCheckFunc := func(w want, gotVec []float32, gotDst []model.Distance, err error) error { if !errors.Is(err, w.err) { @@ -1737,6 +1839,12 @@ func Test_ngt_LinearSearchByID(t *testing.T) { }, want: want{}, checkFunc: defaultCheckFunc, + beforeFunc: func(t *testing.T, args args) { + t.Helper() + }, + afterFunc: func(t *testing.T, args args) { + t.Helper() + }, }, */ @@ -1785,6 +1893,12 @@ func Test_ngt_LinearSearchByID(t *testing.T) { }, want: want{}, checkFunc: defaultCheckFunc, + beforeFunc: func(t *testing.T, args args) { + t.Helper() + }, + afterFunc: func(t *testing.T, args args) { + t.Helper() + }, } }(), */ @@ -1796,10 +1910,10 @@ func Test_ngt_LinearSearchByID(t *testing.T) { tt.Parallel() defer goleak.VerifyNone(tt, goleak.IgnoreCurrent()) if test.beforeFunc != nil { - test.beforeFunc(test.args) + test.beforeFunc(tt, test.args) } if test.afterFunc != nil { - defer test.afterFunc(test.args) + defer test.afterFunc(tt, test.args) } checkFunc := test.checkFunc if test.checkFunc == nil { @@ -1896,8 +2010,8 @@ func Test_ngt_Insert(t *testing.T) { fields fields want want checkFunc func(want, error) error - beforeFunc func(args) - afterFunc func(args) + beforeFunc func(*testing.T, args) + afterFunc func(*testing.T, args) } defaultCheckFunc := func(w want, err error) error { if !errors.Is(err, w.err) { @@ -1950,6 +2064,12 @@ func Test_ngt_Insert(t *testing.T) { }, want: want{}, checkFunc: defaultCheckFunc, + beforeFunc: func(t *testing.T, args args) { + t.Helper() + }, + afterFunc: func(t *testing.T, args args) { + t.Helper() + }, }, */ @@ -1998,6 +2118,12 @@ func Test_ngt_Insert(t *testing.T) { }, want: want{}, checkFunc: defaultCheckFunc, + beforeFunc: func(t *testing.T, args args) { + t.Helper() + }, + afterFunc: func(t *testing.T, args args) { + t.Helper() + }, } }(), */ @@ -2009,10 +2135,10 @@ func Test_ngt_Insert(t *testing.T) { tt.Parallel() defer goleak.VerifyNone(tt, goleak.IgnoreCurrent()) if test.beforeFunc != nil { - test.beforeFunc(test.args) + test.beforeFunc(tt, test.args) } if test.afterFunc != nil { - defer test.afterFunc(test.args) + defer test.afterFunc(tt, test.args) } checkFunc := test.checkFunc if test.checkFunc == nil { @@ -2110,8 +2236,8 @@ func Test_ngt_InsertWithTime(t *testing.T) { fields fields want want checkFunc func(want, error) error - beforeFunc func(args) - afterFunc func(args) + beforeFunc func(*testing.T, args) + afterFunc func(*testing.T, args) } defaultCheckFunc := func(w want, err error) error { if !errors.Is(err, w.err) { @@ -2165,6 +2291,12 @@ func Test_ngt_InsertWithTime(t *testing.T) { }, want: want{}, checkFunc: defaultCheckFunc, + beforeFunc: func(t *testing.T, args args) { + t.Helper() + }, + afterFunc: func(t *testing.T, args args) { + t.Helper() + }, }, */ @@ -2214,6 +2346,12 @@ func Test_ngt_InsertWithTime(t *testing.T) { }, want: want{}, checkFunc: defaultCheckFunc, + beforeFunc: func(t *testing.T, args args) { + t.Helper() + }, + afterFunc: func(t *testing.T, args args) { + t.Helper() + }, } }(), */ @@ -2225,10 +2363,10 @@ func Test_ngt_InsertWithTime(t *testing.T) { tt.Parallel() defer goleak.VerifyNone(tt, goleak.IgnoreCurrent()) if test.beforeFunc != nil { - test.beforeFunc(test.args) + test.beforeFunc(tt, test.args) } if test.afterFunc != nil { - defer test.afterFunc(test.args) + defer test.afterFunc(tt, test.args) } checkFunc := test.checkFunc if test.checkFunc == nil { @@ -2327,8 +2465,8 @@ func Test_ngt_insert(t *testing.T) { fields fields want want checkFunc func(want, error) error - beforeFunc func(args) - afterFunc func(args) + beforeFunc func(*testing.T, args) + afterFunc func(*testing.T, args) } defaultCheckFunc := func(w want, err error) error { if !errors.Is(err, w.err) { @@ -2383,6 +2521,12 @@ func Test_ngt_insert(t *testing.T) { }, want: want{}, checkFunc: defaultCheckFunc, + beforeFunc: func(t *testing.T, args args) { + t.Helper() + }, + afterFunc: func(t *testing.T, args args) { + t.Helper() + }, }, */ @@ -2433,6 +2577,12 @@ func Test_ngt_insert(t *testing.T) { }, want: want{}, checkFunc: defaultCheckFunc, + beforeFunc: func(t *testing.T, args args) { + t.Helper() + }, + afterFunc: func(t *testing.T, args args) { + t.Helper() + }, } }(), */ @@ -2444,10 +2594,10 @@ func Test_ngt_insert(t *testing.T) { tt.Parallel() defer goleak.VerifyNone(tt, goleak.IgnoreCurrent()) if test.beforeFunc != nil { - test.beforeFunc(test.args) + test.beforeFunc(tt, test.args) } if test.afterFunc != nil { - defer test.afterFunc(test.args) + defer test.afterFunc(tt, test.args) } checkFunc := test.checkFunc if test.checkFunc == nil { @@ -2543,8 +2693,8 @@ func Test_ngt_InsertMultiple(t *testing.T) { fields fields want want checkFunc func(want, error) error - beforeFunc func(args) - afterFunc func(args) + beforeFunc func(*testing.T, args) + afterFunc func(*testing.T, args) } defaultCheckFunc := func(w want, err error) error { if !errors.Is(err, w.err) { @@ -2596,6 +2746,12 @@ func Test_ngt_InsertMultiple(t *testing.T) { }, want: want{}, checkFunc: defaultCheckFunc, + beforeFunc: func(t *testing.T, args args) { + t.Helper() + }, + afterFunc: func(t *testing.T, args args) { + t.Helper() + }, }, */ @@ -2643,6 +2799,12 @@ func Test_ngt_InsertMultiple(t *testing.T) { }, want: want{}, checkFunc: defaultCheckFunc, + beforeFunc: func(t *testing.T, args args) { + t.Helper() + }, + afterFunc: func(t *testing.T, args args) { + t.Helper() + }, } }(), */ @@ -2654,10 +2816,10 @@ func Test_ngt_InsertMultiple(t *testing.T) { tt.Parallel() defer goleak.VerifyNone(tt, goleak.IgnoreCurrent()) if test.beforeFunc != nil { - test.beforeFunc(test.args) + test.beforeFunc(tt, test.args) } if test.afterFunc != nil { - defer test.afterFunc(test.args) + defer test.afterFunc(tt, test.args) } checkFunc := test.checkFunc if test.checkFunc == nil { @@ -2754,8 +2916,8 @@ func Test_ngt_InsertMultipleWithTime(t *testing.T) { fields fields want want checkFunc func(want, error) error - beforeFunc func(args) - afterFunc func(args) + beforeFunc func(*testing.T, args) + afterFunc func(*testing.T, args) } defaultCheckFunc := func(w want, err error) error { if !errors.Is(err, w.err) { @@ -2808,6 +2970,12 @@ func Test_ngt_InsertMultipleWithTime(t *testing.T) { }, want: want{}, checkFunc: defaultCheckFunc, + beforeFunc: func(t *testing.T, args args) { + t.Helper() + }, + afterFunc: func(t *testing.T, args args) { + t.Helper() + }, }, */ @@ -2856,6 +3024,12 @@ func Test_ngt_InsertMultipleWithTime(t *testing.T) { }, want: want{}, checkFunc: defaultCheckFunc, + beforeFunc: func(t *testing.T, args args) { + t.Helper() + }, + afterFunc: func(t *testing.T, args args) { + t.Helper() + }, } }(), */ @@ -2867,10 +3041,10 @@ func Test_ngt_InsertMultipleWithTime(t *testing.T) { tt.Parallel() defer goleak.VerifyNone(tt, goleak.IgnoreCurrent()) if test.beforeFunc != nil { - test.beforeFunc(test.args) + test.beforeFunc(tt, test.args) } if test.afterFunc != nil { - defer test.afterFunc(test.args) + defer test.afterFunc(tt, test.args) } checkFunc := test.checkFunc if test.checkFunc == nil { @@ -2968,8 +3142,8 @@ func Test_ngt_insertMultiple(t *testing.T) { fields fields want want checkFunc func(want, error) error - beforeFunc func(args) - afterFunc func(args) + beforeFunc func(*testing.T, args) + afterFunc func(*testing.T, args) } defaultCheckFunc := func(w want, err error) error { if !errors.Is(err, w.err) { @@ -3023,6 +3197,12 @@ func Test_ngt_insertMultiple(t *testing.T) { }, want: want{}, checkFunc: defaultCheckFunc, + beforeFunc: func(t *testing.T, args args) { + t.Helper() + }, + afterFunc: func(t *testing.T, args args) { + t.Helper() + }, }, */ @@ -3072,6 +3252,12 @@ func Test_ngt_insertMultiple(t *testing.T) { }, want: want{}, checkFunc: defaultCheckFunc, + beforeFunc: func(t *testing.T, args args) { + t.Helper() + }, + afterFunc: func(t *testing.T, args args) { + t.Helper() + }, } }(), */ @@ -3083,10 +3269,10 @@ func Test_ngt_insertMultiple(t *testing.T) { tt.Parallel() defer goleak.VerifyNone(tt, goleak.IgnoreCurrent()) if test.beforeFunc != nil { - test.beforeFunc(test.args) + test.beforeFunc(tt, test.args) } if test.afterFunc != nil { - defer test.afterFunc(test.args) + defer test.afterFunc(tt, test.args) } checkFunc := test.checkFunc if test.checkFunc == nil { @@ -3183,8 +3369,8 @@ func Test_ngt_Update(t *testing.T) { fields fields want want checkFunc func(want, error) error - beforeFunc func(args) - afterFunc func(args) + beforeFunc func(*testing.T, args) + afterFunc func(*testing.T, args) } defaultCheckFunc := func(w want, err error) error { if !errors.Is(err, w.err) { @@ -3237,6 +3423,12 @@ func Test_ngt_Update(t *testing.T) { }, want: want{}, checkFunc: defaultCheckFunc, + beforeFunc: func(t *testing.T, args args) { + t.Helper() + }, + afterFunc: func(t *testing.T, args args) { + t.Helper() + }, }, */ @@ -3285,6 +3477,12 @@ func Test_ngt_Update(t *testing.T) { }, want: want{}, checkFunc: defaultCheckFunc, + beforeFunc: func(t *testing.T, args args) { + t.Helper() + }, + afterFunc: func(t *testing.T, args args) { + t.Helper() + }, } }(), */ @@ -3296,10 +3494,10 @@ func Test_ngt_Update(t *testing.T) { tt.Parallel() defer goleak.VerifyNone(tt, goleak.IgnoreCurrent()) if test.beforeFunc != nil { - test.beforeFunc(test.args) + test.beforeFunc(tt, test.args) } if test.afterFunc != nil { - defer test.afterFunc(test.args) + defer test.afterFunc(tt, test.args) } checkFunc := test.checkFunc if test.checkFunc == nil { @@ -3397,8 +3595,8 @@ func Test_ngt_UpdateWithTime(t *testing.T) { fields fields want want checkFunc func(want, error) error - beforeFunc func(args) - afterFunc func(args) + beforeFunc func(*testing.T, args) + afterFunc func(*testing.T, args) } defaultCheckFunc := func(w want, err error) error { if !errors.Is(err, w.err) { @@ -3452,6 +3650,12 @@ func Test_ngt_UpdateWithTime(t *testing.T) { }, want: want{}, checkFunc: defaultCheckFunc, + beforeFunc: func(t *testing.T, args args) { + t.Helper() + }, + afterFunc: func(t *testing.T, args args) { + t.Helper() + }, }, */ @@ -3501,6 +3705,12 @@ func Test_ngt_UpdateWithTime(t *testing.T) { }, want: want{}, checkFunc: defaultCheckFunc, + beforeFunc: func(t *testing.T, args args) { + t.Helper() + }, + afterFunc: func(t *testing.T, args args) { + t.Helper() + }, } }(), */ @@ -3512,10 +3722,10 @@ func Test_ngt_UpdateWithTime(t *testing.T) { tt.Parallel() defer goleak.VerifyNone(tt, goleak.IgnoreCurrent()) if test.beforeFunc != nil { - test.beforeFunc(test.args) + test.beforeFunc(tt, test.args) } if test.afterFunc != nil { - defer test.afterFunc(test.args) + defer test.afterFunc(tt, test.args) } checkFunc := test.checkFunc if test.checkFunc == nil { @@ -3613,8 +3823,8 @@ func Test_ngt_update(t *testing.T) { fields fields want want checkFunc func(want, error) error - beforeFunc func(args) - afterFunc func(args) + beforeFunc func(*testing.T, args) + afterFunc func(*testing.T, args) } defaultCheckFunc := func(w want, err error) error { if !errors.Is(err, w.err) { @@ -3668,6 +3878,12 @@ func Test_ngt_update(t *testing.T) { }, want: want{}, checkFunc: defaultCheckFunc, + beforeFunc: func(t *testing.T, args args) { + t.Helper() + }, + afterFunc: func(t *testing.T, args args) { + t.Helper() + }, }, */ @@ -3717,6 +3933,12 @@ func Test_ngt_update(t *testing.T) { }, want: want{}, checkFunc: defaultCheckFunc, + beforeFunc: func(t *testing.T, args args) { + t.Helper() + }, + afterFunc: func(t *testing.T, args args) { + t.Helper() + }, } }(), */ @@ -3728,10 +3950,10 @@ func Test_ngt_update(t *testing.T) { tt.Parallel() defer goleak.VerifyNone(tt, goleak.IgnoreCurrent()) if test.beforeFunc != nil { - test.beforeFunc(test.args) + test.beforeFunc(tt, test.args) } if test.afterFunc != nil { - defer test.afterFunc(test.args) + defer test.afterFunc(tt, test.args) } checkFunc := test.checkFunc if test.checkFunc == nil { @@ -3827,8 +4049,8 @@ func Test_ngt_UpdateMultiple(t *testing.T) { fields fields want want checkFunc func(want, error) error - beforeFunc func(args) - afterFunc func(args) + beforeFunc func(*testing.T, args) + afterFunc func(*testing.T, args) } defaultCheckFunc := func(w want, err error) error { if !errors.Is(err, w.err) { @@ -3880,6 +4102,12 @@ func Test_ngt_UpdateMultiple(t *testing.T) { }, want: want{}, checkFunc: defaultCheckFunc, + beforeFunc: func(t *testing.T, args args) { + t.Helper() + }, + afterFunc: func(t *testing.T, args args) { + t.Helper() + }, }, */ @@ -3927,6 +4155,12 @@ func Test_ngt_UpdateMultiple(t *testing.T) { }, want: want{}, checkFunc: defaultCheckFunc, + beforeFunc: func(t *testing.T, args args) { + t.Helper() + }, + afterFunc: func(t *testing.T, args args) { + t.Helper() + }, } }(), */ @@ -3938,10 +4172,10 @@ func Test_ngt_UpdateMultiple(t *testing.T) { tt.Parallel() defer goleak.VerifyNone(tt, goleak.IgnoreCurrent()) if test.beforeFunc != nil { - test.beforeFunc(test.args) + test.beforeFunc(tt, test.args) } if test.afterFunc != nil { - defer test.afterFunc(test.args) + defer test.afterFunc(tt, test.args) } checkFunc := test.checkFunc if test.checkFunc == nil { @@ -4038,8 +4272,8 @@ func Test_ngt_UpdateMultipleWithTime(t *testing.T) { fields fields want want checkFunc func(want, error) error - beforeFunc func(args) - afterFunc func(args) + beforeFunc func(*testing.T, args) + afterFunc func(*testing.T, args) } defaultCheckFunc := func(w want, err error) error { if !errors.Is(err, w.err) { @@ -4092,6 +4326,12 @@ func Test_ngt_UpdateMultipleWithTime(t *testing.T) { }, want: want{}, checkFunc: defaultCheckFunc, + beforeFunc: func(t *testing.T, args args) { + t.Helper() + }, + afterFunc: func(t *testing.T, args args) { + t.Helper() + }, }, */ @@ -4140,6 +4380,12 @@ func Test_ngt_UpdateMultipleWithTime(t *testing.T) { }, want: want{}, checkFunc: defaultCheckFunc, + beforeFunc: func(t *testing.T, args args) { + t.Helper() + }, + afterFunc: func(t *testing.T, args args) { + t.Helper() + }, } }(), */ @@ -4151,10 +4397,10 @@ func Test_ngt_UpdateMultipleWithTime(t *testing.T) { tt.Parallel() defer goleak.VerifyNone(tt, goleak.IgnoreCurrent()) if test.beforeFunc != nil { - test.beforeFunc(test.args) + test.beforeFunc(tt, test.args) } if test.afterFunc != nil { - defer test.afterFunc(test.args) + defer test.afterFunc(tt, test.args) } checkFunc := test.checkFunc if test.checkFunc == nil { @@ -4251,8 +4497,8 @@ func Test_ngt_updateMultiple(t *testing.T) { fields fields want want checkFunc func(want, error) error - beforeFunc func(args) - afterFunc func(args) + beforeFunc func(*testing.T, args) + afterFunc func(*testing.T, args) } defaultCheckFunc := func(w want, err error) error { if !errors.Is(err, w.err) { @@ -4305,6 +4551,12 @@ func Test_ngt_updateMultiple(t *testing.T) { }, want: want{}, checkFunc: defaultCheckFunc, + beforeFunc: func(t *testing.T, args args) { + t.Helper() + }, + afterFunc: func(t *testing.T, args args) { + t.Helper() + }, }, */ @@ -4353,6 +4605,12 @@ func Test_ngt_updateMultiple(t *testing.T) { }, want: want{}, checkFunc: defaultCheckFunc, + beforeFunc: func(t *testing.T, args args) { + t.Helper() + }, + afterFunc: func(t *testing.T, args args) { + t.Helper() + }, } }(), */ @@ -4364,10 +4622,10 @@ func Test_ngt_updateMultiple(t *testing.T) { tt.Parallel() defer goleak.VerifyNone(tt, goleak.IgnoreCurrent()) if test.beforeFunc != nil { - test.beforeFunc(test.args) + test.beforeFunc(tt, test.args) } if test.afterFunc != nil { - defer test.afterFunc(test.args) + defer test.afterFunc(tt, test.args) } checkFunc := test.checkFunc if test.checkFunc == nil { @@ -4463,8 +4721,8 @@ func Test_ngt_Delete(t *testing.T) { fields fields want want checkFunc func(want, error) error - beforeFunc func(args) - afterFunc func(args) + beforeFunc func(*testing.T, args) + afterFunc func(*testing.T, args) } defaultCheckFunc := func(w want, err error) error { if !errors.Is(err, w.err) { @@ -4516,6 +4774,12 @@ func Test_ngt_Delete(t *testing.T) { }, want: want{}, checkFunc: defaultCheckFunc, + beforeFunc: func(t *testing.T, args args) { + t.Helper() + }, + afterFunc: func(t *testing.T, args args) { + t.Helper() + }, }, */ @@ -4563,6 +4827,12 @@ func Test_ngt_Delete(t *testing.T) { }, want: want{}, checkFunc: defaultCheckFunc, + beforeFunc: func(t *testing.T, args args) { + t.Helper() + }, + afterFunc: func(t *testing.T, args args) { + t.Helper() + }, } }(), */ @@ -4574,10 +4844,10 @@ func Test_ngt_Delete(t *testing.T) { tt.Parallel() defer goleak.VerifyNone(tt, goleak.IgnoreCurrent()) if test.beforeFunc != nil { - test.beforeFunc(test.args) + test.beforeFunc(tt, test.args) } if test.afterFunc != nil { - defer test.afterFunc(test.args) + defer test.afterFunc(tt, test.args) } checkFunc := test.checkFunc if test.checkFunc == nil { @@ -4674,8 +4944,8 @@ func Test_ngt_DeleteWithTime(t *testing.T) { fields fields want want checkFunc func(want, error) error - beforeFunc func(args) - afterFunc func(args) + beforeFunc func(*testing.T, args) + afterFunc func(*testing.T, args) } defaultCheckFunc := func(w want, err error) error { if !errors.Is(err, w.err) { @@ -4728,6 +4998,12 @@ func Test_ngt_DeleteWithTime(t *testing.T) { }, want: want{}, checkFunc: defaultCheckFunc, + beforeFunc: func(t *testing.T, args args) { + t.Helper() + }, + afterFunc: func(t *testing.T, args args) { + t.Helper() + }, }, */ @@ -4776,6 +5052,12 @@ func Test_ngt_DeleteWithTime(t *testing.T) { }, want: want{}, checkFunc: defaultCheckFunc, + beforeFunc: func(t *testing.T, args args) { + t.Helper() + }, + afterFunc: func(t *testing.T, args args) { + t.Helper() + }, } }(), */ @@ -4787,10 +5069,10 @@ func Test_ngt_DeleteWithTime(t *testing.T) { tt.Parallel() defer goleak.VerifyNone(tt, goleak.IgnoreCurrent()) if test.beforeFunc != nil { - test.beforeFunc(test.args) + test.beforeFunc(tt, test.args) } if test.afterFunc != nil { - defer test.afterFunc(test.args) + defer test.afterFunc(tt, test.args) } checkFunc := test.checkFunc if test.checkFunc == nil { @@ -4888,8 +5170,8 @@ func Test_ngt_delete(t *testing.T) { fields fields want want checkFunc func(want, error) error - beforeFunc func(args) - afterFunc func(args) + beforeFunc func(*testing.T, args) + afterFunc func(*testing.T, args) } defaultCheckFunc := func(w want, err error) error { if !errors.Is(err, w.err) { @@ -4943,6 +5225,12 @@ func Test_ngt_delete(t *testing.T) { }, want: want{}, checkFunc: defaultCheckFunc, + beforeFunc: func(t *testing.T, args args) { + t.Helper() + }, + afterFunc: func(t *testing.T, args args) { + t.Helper() + }, }, */ @@ -4992,6 +5280,12 @@ func Test_ngt_delete(t *testing.T) { }, want: want{}, checkFunc: defaultCheckFunc, + beforeFunc: func(t *testing.T, args args) { + t.Helper() + }, + afterFunc: func(t *testing.T, args args) { + t.Helper() + }, } }(), */ @@ -5003,10 +5297,10 @@ func Test_ngt_delete(t *testing.T) { tt.Parallel() defer goleak.VerifyNone(tt, goleak.IgnoreCurrent()) if test.beforeFunc != nil { - test.beforeFunc(test.args) + test.beforeFunc(tt, test.args) } if test.afterFunc != nil { - defer test.afterFunc(test.args) + defer test.afterFunc(tt, test.args) } checkFunc := test.checkFunc if test.checkFunc == nil { @@ -5102,8 +5396,8 @@ func Test_ngt_DeleteMultiple(t *testing.T) { fields fields want want checkFunc func(want, error) error - beforeFunc func(args) - afterFunc func(args) + beforeFunc func(*testing.T, args) + afterFunc func(*testing.T, args) } defaultCheckFunc := func(w want, err error) error { if !errors.Is(err, w.err) { @@ -5155,6 +5449,12 @@ func Test_ngt_DeleteMultiple(t *testing.T) { }, want: want{}, checkFunc: defaultCheckFunc, + beforeFunc: func(t *testing.T, args args) { + t.Helper() + }, + afterFunc: func(t *testing.T, args args) { + t.Helper() + }, }, */ @@ -5202,6 +5502,12 @@ func Test_ngt_DeleteMultiple(t *testing.T) { }, want: want{}, checkFunc: defaultCheckFunc, + beforeFunc: func(t *testing.T, args args) { + t.Helper() + }, + afterFunc: func(t *testing.T, args args) { + t.Helper() + }, } }(), */ @@ -5213,10 +5519,10 @@ func Test_ngt_DeleteMultiple(t *testing.T) { tt.Parallel() defer goleak.VerifyNone(tt, goleak.IgnoreCurrent()) if test.beforeFunc != nil { - test.beforeFunc(test.args) + test.beforeFunc(tt, test.args) } if test.afterFunc != nil { - defer test.afterFunc(test.args) + defer test.afterFunc(tt, test.args) } checkFunc := test.checkFunc if test.checkFunc == nil { @@ -5313,8 +5619,8 @@ func Test_ngt_DeleteMultipleWithTime(t *testing.T) { fields fields want want checkFunc func(want, error) error - beforeFunc func(args) - afterFunc func(args) + beforeFunc func(*testing.T, args) + afterFunc func(*testing.T, args) } defaultCheckFunc := func(w want, err error) error { if !errors.Is(err, w.err) { @@ -5367,6 +5673,12 @@ func Test_ngt_DeleteMultipleWithTime(t *testing.T) { }, want: want{}, checkFunc: defaultCheckFunc, + beforeFunc: func(t *testing.T, args args) { + t.Helper() + }, + afterFunc: func(t *testing.T, args args) { + t.Helper() + }, }, */ @@ -5415,6 +5727,12 @@ func Test_ngt_DeleteMultipleWithTime(t *testing.T) { }, want: want{}, checkFunc: defaultCheckFunc, + beforeFunc: func(t *testing.T, args args) { + t.Helper() + }, + afterFunc: func(t *testing.T, args args) { + t.Helper() + }, } }(), */ @@ -5426,10 +5744,10 @@ func Test_ngt_DeleteMultipleWithTime(t *testing.T) { tt.Parallel() defer goleak.VerifyNone(tt, goleak.IgnoreCurrent()) if test.beforeFunc != nil { - test.beforeFunc(test.args) + test.beforeFunc(tt, test.args) } if test.afterFunc != nil { - defer test.afterFunc(test.args) + defer test.afterFunc(tt, test.args) } checkFunc := test.checkFunc if test.checkFunc == nil { @@ -5527,8 +5845,8 @@ func Test_ngt_deleteMultiple(t *testing.T) { fields fields want want checkFunc func(want, error) error - beforeFunc func(args) - afterFunc func(args) + beforeFunc func(*testing.T, args) + afterFunc func(*testing.T, args) } defaultCheckFunc := func(w want, err error) error { if !errors.Is(err, w.err) { @@ -5582,6 +5900,12 @@ func Test_ngt_deleteMultiple(t *testing.T) { }, want: want{}, checkFunc: defaultCheckFunc, + beforeFunc: func(t *testing.T, args args) { + t.Helper() + }, + afterFunc: func(t *testing.T, args args) { + t.Helper() + }, }, */ @@ -5631,6 +5955,12 @@ func Test_ngt_deleteMultiple(t *testing.T) { }, want: want{}, checkFunc: defaultCheckFunc, + beforeFunc: func(t *testing.T, args args) { + t.Helper() + }, + afterFunc: func(t *testing.T, args args) { + t.Helper() + }, } }(), */ @@ -5642,10 +5972,10 @@ func Test_ngt_deleteMultiple(t *testing.T) { tt.Parallel() defer goleak.VerifyNone(tt, goleak.IgnoreCurrent()) if test.beforeFunc != nil { - test.beforeFunc(test.args) + test.beforeFunc(tt, test.args) } if test.afterFunc != nil { - defer test.afterFunc(test.args) + defer test.afterFunc(tt, test.args) } checkFunc := test.checkFunc if test.checkFunc == nil { @@ -5742,8 +6072,8 @@ func Test_ngt_CreateIndex(t *testing.T) { fields fields want want checkFunc func(want, error) error - beforeFunc func(args) - afterFunc func(args) + beforeFunc func(*testing.T, args) + afterFunc func(*testing.T, args) } defaultCheckFunc := func(w want, err error) error { if !errors.Is(err, w.err) { @@ -5796,6 +6126,12 @@ func Test_ngt_CreateIndex(t *testing.T) { }, want: want{}, checkFunc: defaultCheckFunc, + beforeFunc: func(t *testing.T, args args) { + t.Helper() + }, + afterFunc: func(t *testing.T, args args) { + t.Helper() + }, }, */ @@ -5844,6 +6180,12 @@ func Test_ngt_CreateIndex(t *testing.T) { }, want: want{}, checkFunc: defaultCheckFunc, + beforeFunc: func(t *testing.T, args args) { + t.Helper() + }, + afterFunc: func(t *testing.T, args args) { + t.Helper() + }, } }(), */ @@ -5855,10 +6197,10 @@ func Test_ngt_CreateIndex(t *testing.T) { tt.Parallel() defer goleak.VerifyNone(tt, goleak.IgnoreCurrent()) if test.beforeFunc != nil { - test.beforeFunc(test.args) + test.beforeFunc(tt, test.args) } if test.afterFunc != nil { - defer test.afterFunc(test.args) + defer test.afterFunc(tt, test.args) } checkFunc := test.checkFunc if test.checkFunc == nil { @@ -5952,8 +6294,8 @@ func Test_ngt_removeInvalidIndex(t *testing.T) { fields fields want want checkFunc func(want) error - beforeFunc func(args) - afterFunc func(args) + beforeFunc func(*testing.T, args) + afterFunc func(*testing.T, args) } defaultCheckFunc := func(w want) error { return nil @@ -6002,6 +6344,12 @@ func Test_ngt_removeInvalidIndex(t *testing.T) { }, want: want{}, checkFunc: defaultCheckFunc, + beforeFunc: func(t *testing.T, args args) { + t.Helper() + }, + afterFunc: func(t *testing.T, args args) { + t.Helper() + }, }, */ @@ -6049,6 +6397,12 @@ func Test_ngt_removeInvalidIndex(t *testing.T) { }, want: want{}, checkFunc: defaultCheckFunc, + beforeFunc: func(t *testing.T, args args) { + t.Helper() + }, + afterFunc: func(t *testing.T, args args) { + t.Helper() + }, } }(), */ @@ -6060,10 +6414,10 @@ func Test_ngt_removeInvalidIndex(t *testing.T) { tt.Parallel() defer goleak.VerifyNone(tt, goleak.IgnoreCurrent()) if test.beforeFunc != nil { - test.beforeFunc(test.args) + test.beforeFunc(tt, test.args) } if test.afterFunc != nil { - defer test.afterFunc(test.args) + defer test.afterFunc(tt, test.args) } checkFunc := test.checkFunc if test.checkFunc == nil { @@ -6159,8 +6513,8 @@ func Test_ngt_SaveIndex(t *testing.T) { fields fields want want checkFunc func(want, error) error - beforeFunc func(args) - afterFunc func(args) + beforeFunc func(*testing.T, args) + afterFunc func(*testing.T, args) } defaultCheckFunc := func(w want, err error) error { if !errors.Is(err, w.err) { @@ -6212,6 +6566,12 @@ func Test_ngt_SaveIndex(t *testing.T) { }, want: want{}, checkFunc: defaultCheckFunc, + beforeFunc: func(t *testing.T, args args) { + t.Helper() + }, + afterFunc: func(t *testing.T, args args) { + t.Helper() + }, }, */ @@ -6259,6 +6619,12 @@ func Test_ngt_SaveIndex(t *testing.T) { }, want: want{}, checkFunc: defaultCheckFunc, + beforeFunc: func(t *testing.T, args args) { + t.Helper() + }, + afterFunc: func(t *testing.T, args args) { + t.Helper() + }, } }(), */ @@ -6270,10 +6636,10 @@ func Test_ngt_SaveIndex(t *testing.T) { tt.Parallel() defer goleak.VerifyNone(tt, goleak.IgnoreCurrent()) if test.beforeFunc != nil { - test.beforeFunc(test.args) + test.beforeFunc(tt, test.args) } if test.afterFunc != nil { - defer test.afterFunc(test.args) + defer test.afterFunc(tt, test.args) } checkFunc := test.checkFunc if test.checkFunc == nil { @@ -6369,8 +6735,8 @@ func Test_ngt_saveIndex(t *testing.T) { fields fields want want checkFunc func(want, error) error - beforeFunc func(args) - afterFunc func(args) + beforeFunc func(*testing.T, args) + afterFunc func(*testing.T, args) } defaultCheckFunc := func(w want, err error) error { if !errors.Is(err, w.err) { @@ -6422,6 +6788,12 @@ func Test_ngt_saveIndex(t *testing.T) { }, want: want{}, checkFunc: defaultCheckFunc, + beforeFunc: func(t *testing.T, args args) { + t.Helper() + }, + afterFunc: func(t *testing.T, args args) { + t.Helper() + }, }, */ @@ -6469,6 +6841,12 @@ func Test_ngt_saveIndex(t *testing.T) { }, want: want{}, checkFunc: defaultCheckFunc, + beforeFunc: func(t *testing.T, args args) { + t.Helper() + }, + afterFunc: func(t *testing.T, args args) { + t.Helper() + }, } }(), */ @@ -6480,10 +6858,10 @@ func Test_ngt_saveIndex(t *testing.T) { tt.Parallel() defer goleak.VerifyNone(tt, goleak.IgnoreCurrent()) if test.beforeFunc != nil { - test.beforeFunc(test.args) + test.beforeFunc(tt, test.args) } if test.afterFunc != nil { - defer test.afterFunc(test.args) + defer test.afterFunc(tt, test.args) } checkFunc := test.checkFunc if test.checkFunc == nil { @@ -6580,8 +6958,8 @@ func Test_ngt_CreateAndSaveIndex(t *testing.T) { fields fields want want checkFunc func(want, error) error - beforeFunc func(args) - afterFunc func(args) + beforeFunc func(*testing.T, args) + afterFunc func(*testing.T, args) } defaultCheckFunc := func(w want, err error) error { if !errors.Is(err, w.err) { @@ -6634,6 +7012,12 @@ func Test_ngt_CreateAndSaveIndex(t *testing.T) { }, want: want{}, checkFunc: defaultCheckFunc, + beforeFunc: func(t *testing.T, args args) { + t.Helper() + }, + afterFunc: func(t *testing.T, args args) { + t.Helper() + }, }, */ @@ -6682,6 +7066,12 @@ func Test_ngt_CreateAndSaveIndex(t *testing.T) { }, want: want{}, checkFunc: defaultCheckFunc, + beforeFunc: func(t *testing.T, args args) { + t.Helper() + }, + afterFunc: func(t *testing.T, args args) { + t.Helper() + }, } }(), */ @@ -6693,10 +7083,10 @@ func Test_ngt_CreateAndSaveIndex(t *testing.T) { tt.Parallel() defer goleak.VerifyNone(tt, goleak.IgnoreCurrent()) if test.beforeFunc != nil { - test.beforeFunc(test.args) + test.beforeFunc(tt, test.args) } if test.afterFunc != nil { - defer test.afterFunc(test.args) + defer test.afterFunc(tt, test.args) } checkFunc := test.checkFunc if test.checkFunc == nil { @@ -6792,8 +7182,8 @@ func Test_ngt_moveAndSwitchSavedData(t *testing.T) { fields fields want want checkFunc func(want, error) error - beforeFunc func(args) - afterFunc func(args) + beforeFunc func(*testing.T, args) + afterFunc func(*testing.T, args) } defaultCheckFunc := func(w want, err error) error { if !errors.Is(err, w.err) { @@ -6845,6 +7235,12 @@ func Test_ngt_moveAndSwitchSavedData(t *testing.T) { }, want: want{}, checkFunc: defaultCheckFunc, + beforeFunc: func(t *testing.T, args args) { + t.Helper() + }, + afterFunc: func(t *testing.T, args args) { + t.Helper() + }, }, */ @@ -6892,6 +7288,12 @@ func Test_ngt_moveAndSwitchSavedData(t *testing.T) { }, want: want{}, checkFunc: defaultCheckFunc, + beforeFunc: func(t *testing.T, args args) { + t.Helper() + }, + afterFunc: func(t *testing.T, args args) { + t.Helper() + }, } }(), */ @@ -6903,10 +7305,10 @@ func Test_ngt_moveAndSwitchSavedData(t *testing.T) { tt.Parallel() defer goleak.VerifyNone(tt, goleak.IgnoreCurrent()) if test.beforeFunc != nil { - test.beforeFunc(test.args) + test.beforeFunc(tt, test.args) } if test.afterFunc != nil { - defer test.afterFunc(test.args) + defer test.afterFunc(tt, test.args) } checkFunc := test.checkFunc if test.checkFunc == nil { @@ -6998,8 +7400,8 @@ func Test_ngt_mktmp(t *testing.T) { fields fields want want checkFunc func(want, error) error - beforeFunc func() - afterFunc func() + beforeFunc func(*testing.T) + afterFunc func(*testing.T) } defaultCheckFunc := func(w want, err error) error { if !errors.Is(err, w.err) { @@ -7048,6 +7450,12 @@ func Test_ngt_mktmp(t *testing.T) { }, want: want{}, checkFunc: defaultCheckFunc, + beforeFunc: func(t *testing.T,) { + t.Helper() + }, + afterFunc: func(t *testing.T,) { + t.Helper() + }, }, */ @@ -7092,6 +7500,12 @@ func Test_ngt_mktmp(t *testing.T) { }, want: want{}, checkFunc: defaultCheckFunc, + beforeFunc: func(t *testing.T,) { + t.Helper() + }, + afterFunc: func(t *testing.T,) { + t.Helper() + }, } }(), */ @@ -7103,10 +7517,10 @@ func Test_ngt_mktmp(t *testing.T) { tt.Parallel() defer goleak.VerifyNone(tt, goleak.IgnoreCurrent()) if test.beforeFunc != nil { - test.beforeFunc() + test.beforeFunc(tt) } if test.afterFunc != nil { - defer test.afterFunc() + defer test.afterFunc(tt) } checkFunc := test.checkFunc if test.checkFunc == nil { @@ -7203,8 +7617,8 @@ func Test_ngt_Exists(t *testing.T) { fields fields want want checkFunc func(want, uint32, bool) error - beforeFunc func(args) - afterFunc func(args) + beforeFunc func(*testing.T, args) + afterFunc func(*testing.T, args) } defaultCheckFunc := func(w want, gotOid uint32, gotOk bool) error { if !reflect.DeepEqual(gotOid, w.wantOid) { @@ -7259,6 +7673,12 @@ func Test_ngt_Exists(t *testing.T) { }, want: want{}, checkFunc: defaultCheckFunc, + beforeFunc: func(t *testing.T, args args) { + t.Helper() + }, + afterFunc: func(t *testing.T, args args) { + t.Helper() + }, }, */ @@ -7306,6 +7726,12 @@ func Test_ngt_Exists(t *testing.T) { }, want: want{}, checkFunc: defaultCheckFunc, + beforeFunc: func(t *testing.T, args args) { + t.Helper() + }, + afterFunc: func(t *testing.T, args args) { + t.Helper() + }, } }(), */ @@ -7317,10 +7743,10 @@ func Test_ngt_Exists(t *testing.T) { tt.Parallel() defer goleak.VerifyNone(tt, goleak.IgnoreCurrent()) if test.beforeFunc != nil { - test.beforeFunc(test.args) + test.beforeFunc(tt, test.args) } if test.afterFunc != nil { - defer test.afterFunc(test.args) + defer test.afterFunc(tt, test.args) } checkFunc := test.checkFunc if test.checkFunc == nil { @@ -7417,8 +7843,8 @@ func Test_ngt_GetObject(t *testing.T) { fields fields want want checkFunc func(want, []float32, error) error - beforeFunc func(args) - afterFunc func(args) + beforeFunc func(*testing.T, args) + afterFunc func(*testing.T, args) } defaultCheckFunc := func(w want, gotVec []float32, err error) error { if !errors.Is(err, w.err) { @@ -7473,6 +7899,12 @@ func Test_ngt_GetObject(t *testing.T) { }, want: want{}, checkFunc: defaultCheckFunc, + beforeFunc: func(t *testing.T, args args) { + t.Helper() + }, + afterFunc: func(t *testing.T, args args) { + t.Helper() + }, }, */ @@ -7520,6 +7952,12 @@ func Test_ngt_GetObject(t *testing.T) { }, want: want{}, checkFunc: defaultCheckFunc, + beforeFunc: func(t *testing.T, args args) { + t.Helper() + }, + afterFunc: func(t *testing.T, args args) { + t.Helper() + }, } }(), */ @@ -7531,10 +7969,10 @@ func Test_ngt_GetObject(t *testing.T) { tt.Parallel() defer goleak.VerifyNone(tt, goleak.IgnoreCurrent()) if test.beforeFunc != nil { - test.beforeFunc(test.args) + test.beforeFunc(tt, test.args) } if test.afterFunc != nil { - defer test.afterFunc(test.args) + defer test.afterFunc(tt, test.args) } checkFunc := test.checkFunc if test.checkFunc == nil { @@ -7631,8 +8069,8 @@ func Test_ngt_readyForUpdate(t *testing.T) { fields fields want want checkFunc func(want, error) error - beforeFunc func(args) - afterFunc func(args) + beforeFunc func(*testing.T, args) + afterFunc func(*testing.T, args) } defaultCheckFunc := func(w want, err error) error { if !errors.Is(err, w.err) { @@ -7685,6 +8123,12 @@ func Test_ngt_readyForUpdate(t *testing.T) { }, want: want{}, checkFunc: defaultCheckFunc, + beforeFunc: func(t *testing.T, args args) { + t.Helper() + }, + afterFunc: func(t *testing.T, args args) { + t.Helper() + }, }, */ @@ -7733,6 +8177,12 @@ func Test_ngt_readyForUpdate(t *testing.T) { }, want: want{}, checkFunc: defaultCheckFunc, + beforeFunc: func(t *testing.T, args args) { + t.Helper() + }, + afterFunc: func(t *testing.T, args args) { + t.Helper() + }, } }(), */ @@ -7744,10 +8194,10 @@ func Test_ngt_readyForUpdate(t *testing.T) { tt.Parallel() defer goleak.VerifyNone(tt, goleak.IgnoreCurrent()) if test.beforeFunc != nil { - test.beforeFunc(test.args) + test.beforeFunc(tt, test.args) } if test.afterFunc != nil { - defer test.afterFunc(test.args) + defer test.afterFunc(tt, test.args) } checkFunc := test.checkFunc if test.checkFunc == nil { @@ -7839,8 +8289,8 @@ func Test_ngt_IsSaving(t *testing.T) { fields fields want want checkFunc func(want, bool) error - beforeFunc func() - afterFunc func() + beforeFunc func(*testing.T) + afterFunc func(*testing.T) } defaultCheckFunc := func(w want, got bool) error { if !reflect.DeepEqual(got, w.want) { @@ -7889,6 +8339,12 @@ func Test_ngt_IsSaving(t *testing.T) { }, want: want{}, checkFunc: defaultCheckFunc, + beforeFunc: func(t *testing.T,) { + t.Helper() + }, + afterFunc: func(t *testing.T,) { + t.Helper() + }, }, */ @@ -7933,6 +8389,12 @@ func Test_ngt_IsSaving(t *testing.T) { }, want: want{}, checkFunc: defaultCheckFunc, + beforeFunc: func(t *testing.T,) { + t.Helper() + }, + afterFunc: func(t *testing.T,) { + t.Helper() + }, } }(), */ @@ -7944,10 +8406,10 @@ func Test_ngt_IsSaving(t *testing.T) { tt.Parallel() defer goleak.VerifyNone(tt, goleak.IgnoreCurrent()) if test.beforeFunc != nil { - test.beforeFunc() + test.beforeFunc(tt) } if test.afterFunc != nil { - defer test.afterFunc() + defer test.afterFunc(tt) } checkFunc := test.checkFunc if test.checkFunc == nil { @@ -8039,8 +8501,8 @@ func Test_ngt_IsIndexing(t *testing.T) { fields fields want want checkFunc func(want, bool) error - beforeFunc func() - afterFunc func() + beforeFunc func(*testing.T) + afterFunc func(*testing.T) } defaultCheckFunc := func(w want, got bool) error { if !reflect.DeepEqual(got, w.want) { @@ -8089,6 +8551,12 @@ func Test_ngt_IsIndexing(t *testing.T) { }, want: want{}, checkFunc: defaultCheckFunc, + beforeFunc: func(t *testing.T,) { + t.Helper() + }, + afterFunc: func(t *testing.T,) { + t.Helper() + }, }, */ @@ -8133,6 +8601,12 @@ func Test_ngt_IsIndexing(t *testing.T) { }, want: want{}, checkFunc: defaultCheckFunc, + beforeFunc: func(t *testing.T,) { + t.Helper() + }, + afterFunc: func(t *testing.T,) { + t.Helper() + }, } }(), */ @@ -8144,10 +8618,10 @@ func Test_ngt_IsIndexing(t *testing.T) { tt.Parallel() defer goleak.VerifyNone(tt, goleak.IgnoreCurrent()) if test.beforeFunc != nil { - test.beforeFunc() + test.beforeFunc(tt) } if test.afterFunc != nil { - defer test.afterFunc() + defer test.afterFunc(tt) } checkFunc := test.checkFunc if test.checkFunc == nil { @@ -8243,8 +8717,8 @@ func Test_ngt_UUIDs(t *testing.T) { fields fields want want checkFunc func(want, []string) error - beforeFunc func(args) - afterFunc func(args) + beforeFunc func(*testing.T, args) + afterFunc func(*testing.T, args) } defaultCheckFunc := func(w want, gotUuids []string) error { if !reflect.DeepEqual(gotUuids, w.wantUuids) { @@ -8296,6 +8770,12 @@ func Test_ngt_UUIDs(t *testing.T) { }, want: want{}, checkFunc: defaultCheckFunc, + beforeFunc: func(t *testing.T, args args) { + t.Helper() + }, + afterFunc: func(t *testing.T, args args) { + t.Helper() + }, }, */ @@ -8343,6 +8823,12 @@ func Test_ngt_UUIDs(t *testing.T) { }, want: want{}, checkFunc: defaultCheckFunc, + beforeFunc: func(t *testing.T, args args) { + t.Helper() + }, + afterFunc: func(t *testing.T, args args) { + t.Helper() + }, } }(), */ @@ -8354,10 +8840,10 @@ func Test_ngt_UUIDs(t *testing.T) { tt.Parallel() defer goleak.VerifyNone(tt, goleak.IgnoreCurrent()) if test.beforeFunc != nil { - test.beforeFunc(test.args) + test.beforeFunc(tt, test.args) } if test.afterFunc != nil { - defer test.afterFunc(test.args) + defer test.afterFunc(tt, test.args) } checkFunc := test.checkFunc if test.checkFunc == nil { @@ -8449,8 +8935,8 @@ func Test_ngt_NumberOfCreateIndexExecution(t *testing.T) { fields fields want want checkFunc func(want, uint64) error - beforeFunc func() - afterFunc func() + beforeFunc func(*testing.T) + afterFunc func(*testing.T) } defaultCheckFunc := func(w want, got uint64) error { if !reflect.DeepEqual(got, w.want) { @@ -8499,6 +8985,12 @@ func Test_ngt_NumberOfCreateIndexExecution(t *testing.T) { }, want: want{}, checkFunc: defaultCheckFunc, + beforeFunc: func(t *testing.T,) { + t.Helper() + }, + afterFunc: func(t *testing.T,) { + t.Helper() + }, }, */ @@ -8543,6 +9035,12 @@ func Test_ngt_NumberOfCreateIndexExecution(t *testing.T) { }, want: want{}, checkFunc: defaultCheckFunc, + beforeFunc: func(t *testing.T,) { + t.Helper() + }, + afterFunc: func(t *testing.T,) { + t.Helper() + }, } }(), */ @@ -8554,10 +9052,10 @@ func Test_ngt_NumberOfCreateIndexExecution(t *testing.T) { tt.Parallel() defer goleak.VerifyNone(tt, goleak.IgnoreCurrent()) if test.beforeFunc != nil { - test.beforeFunc() + test.beforeFunc(tt) } if test.afterFunc != nil { - defer test.afterFunc() + defer test.afterFunc(tt) } checkFunc := test.checkFunc if test.checkFunc == nil { @@ -8649,8 +9147,8 @@ func Test_ngt_NumberOfProactiveGCExecution(t *testing.T) { fields fields want want checkFunc func(want, uint64) error - beforeFunc func() - afterFunc func() + beforeFunc func(*testing.T) + afterFunc func(*testing.T) } defaultCheckFunc := func(w want, got uint64) error { if !reflect.DeepEqual(got, w.want) { @@ -8699,6 +9197,12 @@ func Test_ngt_NumberOfProactiveGCExecution(t *testing.T) { }, want: want{}, checkFunc: defaultCheckFunc, + beforeFunc: func(t *testing.T,) { + t.Helper() + }, + afterFunc: func(t *testing.T,) { + t.Helper() + }, }, */ @@ -8743,6 +9247,12 @@ func Test_ngt_NumberOfProactiveGCExecution(t *testing.T) { }, want: want{}, checkFunc: defaultCheckFunc, + beforeFunc: func(t *testing.T,) { + t.Helper() + }, + afterFunc: func(t *testing.T,) { + t.Helper() + }, } }(), */ @@ -8754,10 +9264,10 @@ func Test_ngt_NumberOfProactiveGCExecution(t *testing.T) { tt.Parallel() defer goleak.VerifyNone(tt, goleak.IgnoreCurrent()) if test.beforeFunc != nil { - test.beforeFunc() + test.beforeFunc(tt) } if test.afterFunc != nil { - defer test.afterFunc() + defer test.afterFunc(tt) } checkFunc := test.checkFunc if test.checkFunc == nil { @@ -8847,8 +9357,8 @@ func Test_ngt_gc(t *testing.T) { fields fields want want checkFunc func(want) error - beforeFunc func() - afterFunc func() + beforeFunc func(*testing.T) + afterFunc func(*testing.T) } defaultCheckFunc := func(w want) error { return nil @@ -8894,6 +9404,12 @@ func Test_ngt_gc(t *testing.T) { }, want: want{}, checkFunc: defaultCheckFunc, + beforeFunc: func(t *testing.T,) { + t.Helper() + }, + afterFunc: func(t *testing.T,) { + t.Helper() + }, }, */ @@ -8938,6 +9454,12 @@ func Test_ngt_gc(t *testing.T) { }, want: want{}, checkFunc: defaultCheckFunc, + beforeFunc: func(t *testing.T,) { + t.Helper() + }, + afterFunc: func(t *testing.T,) { + t.Helper() + }, } }(), */ @@ -8949,10 +9471,10 @@ func Test_ngt_gc(t *testing.T) { tt.Parallel() defer goleak.VerifyNone(tt, goleak.IgnoreCurrent()) if test.beforeFunc != nil { - test.beforeFunc() + test.beforeFunc(tt) } if test.afterFunc != nil { - defer test.afterFunc() + defer test.afterFunc(tt) } checkFunc := test.checkFunc if test.checkFunc == nil { @@ -9044,8 +9566,8 @@ func Test_ngt_Len(t *testing.T) { fields fields want want checkFunc func(want, uint64) error - beforeFunc func() - afterFunc func() + beforeFunc func(*testing.T) + afterFunc func(*testing.T) } defaultCheckFunc := func(w want, got uint64) error { if !reflect.DeepEqual(got, w.want) { @@ -9094,6 +9616,12 @@ func Test_ngt_Len(t *testing.T) { }, want: want{}, checkFunc: defaultCheckFunc, + beforeFunc: func(t *testing.T,) { + t.Helper() + }, + afterFunc: func(t *testing.T,) { + t.Helper() + }, }, */ @@ -9138,6 +9666,12 @@ func Test_ngt_Len(t *testing.T) { }, want: want{}, checkFunc: defaultCheckFunc, + beforeFunc: func(t *testing.T,) { + t.Helper() + }, + afterFunc: func(t *testing.T,) { + t.Helper() + }, } }(), */ @@ -9149,10 +9683,10 @@ func Test_ngt_Len(t *testing.T) { tt.Parallel() defer goleak.VerifyNone(tt, goleak.IgnoreCurrent()) if test.beforeFunc != nil { - test.beforeFunc() + test.beforeFunc(tt) } if test.afterFunc != nil { - defer test.afterFunc() + defer test.afterFunc(tt) } checkFunc := test.checkFunc if test.checkFunc == nil { @@ -9244,8 +9778,8 @@ func Test_ngt_InsertVQueueBufferLen(t *testing.T) { fields fields want want checkFunc func(want, uint64) error - beforeFunc func() - afterFunc func() + beforeFunc func(*testing.T) + afterFunc func(*testing.T) } defaultCheckFunc := func(w want, got uint64) error { if !reflect.DeepEqual(got, w.want) { @@ -9294,6 +9828,12 @@ func Test_ngt_InsertVQueueBufferLen(t *testing.T) { }, want: want{}, checkFunc: defaultCheckFunc, + beforeFunc: func(t *testing.T,) { + t.Helper() + }, + afterFunc: func(t *testing.T,) { + t.Helper() + }, }, */ @@ -9338,6 +9878,12 @@ func Test_ngt_InsertVQueueBufferLen(t *testing.T) { }, want: want{}, checkFunc: defaultCheckFunc, + beforeFunc: func(t *testing.T,) { + t.Helper() + }, + afterFunc: func(t *testing.T,) { + t.Helper() + }, } }(), */ @@ -9349,10 +9895,10 @@ func Test_ngt_InsertVQueueBufferLen(t *testing.T) { tt.Parallel() defer goleak.VerifyNone(tt, goleak.IgnoreCurrent()) if test.beforeFunc != nil { - test.beforeFunc() + test.beforeFunc(tt) } if test.afterFunc != nil { - defer test.afterFunc() + defer test.afterFunc(tt) } checkFunc := test.checkFunc if test.checkFunc == nil { @@ -9444,8 +9990,8 @@ func Test_ngt_DeleteVQueueBufferLen(t *testing.T) { fields fields want want checkFunc func(want, uint64) error - beforeFunc func() - afterFunc func() + beforeFunc func(*testing.T) + afterFunc func(*testing.T) } defaultCheckFunc := func(w want, got uint64) error { if !reflect.DeepEqual(got, w.want) { @@ -9494,6 +10040,12 @@ func Test_ngt_DeleteVQueueBufferLen(t *testing.T) { }, want: want{}, checkFunc: defaultCheckFunc, + beforeFunc: func(t *testing.T,) { + t.Helper() + }, + afterFunc: func(t *testing.T,) { + t.Helper() + }, }, */ @@ -9538,6 +10090,12 @@ func Test_ngt_DeleteVQueueBufferLen(t *testing.T) { }, want: want{}, checkFunc: defaultCheckFunc, + beforeFunc: func(t *testing.T,) { + t.Helper() + }, + afterFunc: func(t *testing.T,) { + t.Helper() + }, } }(), */ @@ -9549,10 +10107,10 @@ func Test_ngt_DeleteVQueueBufferLen(t *testing.T) { tt.Parallel() defer goleak.VerifyNone(tt, goleak.IgnoreCurrent()) if test.beforeFunc != nil { - test.beforeFunc() + test.beforeFunc(tt) } if test.afterFunc != nil { - defer test.afterFunc() + defer test.afterFunc(tt) } checkFunc := test.checkFunc if test.checkFunc == nil { @@ -9644,8 +10202,8 @@ func Test_ngt_GetDimensionSize(t *testing.T) { fields fields want want checkFunc func(want, int) error - beforeFunc func() - afterFunc func() + beforeFunc func(*testing.T) + afterFunc func(*testing.T) } defaultCheckFunc := func(w want, got int) error { if !reflect.DeepEqual(got, w.want) { @@ -9694,6 +10252,12 @@ func Test_ngt_GetDimensionSize(t *testing.T) { }, want: want{}, checkFunc: defaultCheckFunc, + beforeFunc: func(t *testing.T,) { + t.Helper() + }, + afterFunc: func(t *testing.T,) { + t.Helper() + }, }, */ @@ -9738,6 +10302,12 @@ func Test_ngt_GetDimensionSize(t *testing.T) { }, want: want{}, checkFunc: defaultCheckFunc, + beforeFunc: func(t *testing.T,) { + t.Helper() + }, + afterFunc: func(t *testing.T,) { + t.Helper() + }, } }(), */ @@ -9749,10 +10319,10 @@ func Test_ngt_GetDimensionSize(t *testing.T) { tt.Parallel() defer goleak.VerifyNone(tt, goleak.IgnoreCurrent()) if test.beforeFunc != nil { - test.beforeFunc() + test.beforeFunc(tt) } if test.afterFunc != nil { - defer test.afterFunc() + defer test.afterFunc(tt) } checkFunc := test.checkFunc if test.checkFunc == nil { @@ -9848,8 +10418,8 @@ func Test_ngt_Close(t *testing.T) { fields fields want want checkFunc func(want, error) error - beforeFunc func(args) - afterFunc func(args) + beforeFunc func(*testing.T, args) + afterFunc func(*testing.T, args) } defaultCheckFunc := func(w want, err error) error { if !errors.Is(err, w.err) { @@ -9901,6 +10471,12 @@ func Test_ngt_Close(t *testing.T) { }, want: want{}, checkFunc: defaultCheckFunc, + beforeFunc: func(t *testing.T, args args) { + t.Helper() + }, + afterFunc: func(t *testing.T, args args) { + t.Helper() + }, }, */ @@ -9948,6 +10524,12 @@ func Test_ngt_Close(t *testing.T) { }, want: want{}, checkFunc: defaultCheckFunc, + beforeFunc: func(t *testing.T, args args) { + t.Helper() + }, + afterFunc: func(t *testing.T, args args) { + t.Helper() + }, } }(), */ @@ -9959,10 +10541,10 @@ func Test_ngt_Close(t *testing.T) { tt.Parallel() defer goleak.VerifyNone(tt, goleak.IgnoreCurrent()) if test.beforeFunc != nil { - test.beforeFunc(test.args) + test.beforeFunc(tt, test.args) } if test.afterFunc != nil { - defer test.afterFunc(test.args) + defer test.afterFunc(tt, test.args) } checkFunc := test.checkFunc if test.checkFunc == nil { @@ -10010,3 +10592,579 @@ func Test_ngt_Close(t *testing.T) { }) } } + +type index struct { + uuid string + vec []float32 +} + +func Test_ngt_InsertUpsert(t *testing.T) { + type args struct { + idxes []index + poolSize uint32 + bulkSize int + } + type fields struct { + svcCfg *config.NGT + svcOpts []Option + + core core.NGT + eg errgroup.Group + kvs kvs.BidiMap + fmu sync.Mutex + fmap map[string]uint32 + vq vqueue.Queue + indexing atomic.Value + saving atomic.Value + cimu sync.Mutex + lastNocie uint64 + nocie uint64 + nogce uint64 + inMem bool + dim int + alen int + lim time.Duration + dur time.Duration + sdur time.Duration + minLit time.Duration + maxLit time.Duration + litFactor time.Duration + enableProactiveGC bool + enableCopyOnWrite bool + path string + smu sync.Mutex + tmpPath atomic.Value + oldPath string + basePath string + cowmu sync.Mutex + backupGen uint64 + poolSize uint32 + radius float32 + epsilon float32 + idelay time.Duration + dcd bool + kvsdbConcurrency int + } + type want struct { + err error + } + type test struct { + name string + args args + fields fields + want want + checkFunc func(want, error) error + beforeFunc func(args) + afterFunc func(args) + } + defaultCheckFunc := func(w want, err error) error { + if !errors.Is(err, w.err) { + return errors.Errorf("got_error: \"%#v\",\n\t\t\t\twant: \"%#v\"", err, w.err) + } + return nil + } + var ( + // default NGT configuration for test + kvsdbCfg = &config.KVSDB{} + vqueueCfg = &config.VQueue{} + ) + tests := []test{ + // { + // name: "insert & upsert 1", + // args: args{ + // idxes: createRandomData(1, new(createRandomDataConfig)), + // }, + // fields: fields{ + // svcCfg: &config.NGT{ + // Dimension: 128, + // DistanceType: core.Cosine.String(), + // ObjectType: core.Uint8.String(), + // KVSDB: kvsdbCfg, + // VQueue: vqueueCfg, + // }, + // svcOpts: []Option{ + // WithEnableInMemoryMode(true), + // }, + // }, + // }, + { + name: "insert & upsert 100 random", + args: args{ + idxes: createRandomData(10000000, new(createRandomDataConfig)), + poolSize: 100000, + bulkSize: 100000, + }, + fields: fields{ + svcCfg: &config.NGT{ + Dimension: 128, + DistanceType: core.Cosine.String(), + ObjectType: core.Uint8.String(), + KVSDB: kvsdbCfg, + VQueue: vqueueCfg, + }, + svcOpts: []Option{ + WithEnableInMemoryMode(true), + }, + }, + }, + } + for _, tc := range tests { + test := tc + t.Run(test.name, func(tt *testing.T) { + tt.Parallel() + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + // defer goleak.VerifyNone(tt, goleak.IgnoreCurrent()) + if test.beforeFunc != nil { + test.beforeFunc(test.args) + } + if test.afterFunc != nil { + defer test.afterFunc(test.args) + } + checkFunc := test.checkFunc + if test.checkFunc == nil { + checkFunc = defaultCheckFunc + } + + eg, _ := errgroup.New(ctx) + n, err := New(test.fields.svcCfg, append(test.fields.svcOpts, WithErrGroup(eg))...) + if err != nil { + tt.Errorf("failed to init ngt service, error = %v", err) + } + var wg sync.WaitGroup + count := 0 + for _, idx := range test.args.idxes { + count++ + err = n.Insert(idx.uuid, idx.vec) + if err := checkFunc(test.want, err); err != nil { + tt.Errorf("error = %v", err) + } + + if count >= test.args.bulkSize { + wg.Add(1) + eg.Go(func() error { + defer wg.Done() + err = n.CreateAndSaveIndex(ctx, test.args.poolSize) + if err != nil { + tt.Errorf("error creating index: %v", err) + } + return nil + }) + count = 0 + } + } + wg.Wait() + + log.Warn("start create index operation") + err = n.CreateAndSaveIndex(ctx, test.args.poolSize) + if err != nil { + tt.Errorf("error creating index: %v", err) + } + log.Warn("start update operation") + for i := 0; i < 100; i++ { + idx := i + eg.Go(func() error { + log.Warnf("started %d-1", idx) + var wgu sync.WaitGroup + count = 0 + for _, idx := range test.args.idxes[:len(test.args.idxes)/3] { + count++ + err = n.Delete(idx.uuid) + if err != nil { + tt.Errorf("delete error = %v", err) + } + err = n.Insert(idx.uuid, idx.vec) + if err := checkFunc(test.want, err); err != nil { + tt.Errorf("error = %v", err) + } + + // if count >= test.args.bulkSize { + // wgu.Add(1) + // eg.Go(func() error { + // defer wgu.Done() + // err = n.CreateAndSaveIndex(ctx, test.args.poolSize) + // if err != nil { + // tt.Errorf("error creating index: %v", err) + // } + // return nil + // }) + // count = 0 + // } + } + wgu.Wait() + log.Warnf("finished %d-1", idx) + return nil + }) + + eg.Go(func() error { + log.Warnf("started %d-2", idx) + var wgu sync.WaitGroup + count = 0 + for _, idx := range test.args.idxes[len(test.args.idxes)/3 : 2*len(test.args.idxes)/3] { + count++ + err = n.Delete(idx.uuid) + if err != nil { + tt.Errorf("delete error = %v", err) + } + err = n.Insert(idx.uuid, idx.vec) + if err := checkFunc(test.want, err); err != nil { + tt.Errorf("error = %v", err) + } + + // if count >= test.args.bulkSize { + // wgu.Add(1) + // eg.Go(func() error { + // defer wgu.Done() + // err = n.CreateAndSaveIndex(ctx, test.args.poolSize) + // if err != nil { + // tt.Errorf("error creating index: %v", err) + // } + // return nil + // }) + // count = 0 + // } + } + wgu.Wait() + log.Warnf("finished %d-2", idx) + return nil + }) + + eg.Go(func() error { + log.Warnf("started %d-3", idx) + var wgu sync.WaitGroup + count = 0 + for _, idx := range test.args.idxes[2*len(test.args.idxes)/3:] { + count++ + err = n.Delete(idx.uuid) + if err != nil { + tt.Errorf("delete error = %v", err) + } + err = n.Insert(idx.uuid, idx.vec) + if err := checkFunc(test.want, err); err != nil { + tt.Errorf("error = %v", err) + } + + // if count >= test.args.bulkSize { + // wgu.Add(1) + // eg.Go(func() error { + // defer wgu.Done() + // err = n.CreateAndSaveIndex(ctx, test.args.poolSize) + // if err != nil { + // tt.Errorf("error creating index: %v", err) + // } + // return nil + // }) + // count = 0 + // } + } + wgu.Wait() + log.Warnf("finished %d-3", idx) + return nil + }) + } + eg.Wait() + + log.Warn("start final create index operation") + err = n.CreateAndSaveIndex(ctx, test.args.poolSize) + if err != nil { + tt.Errorf("error creating index: %v", err) + } + }) + } +} + +func Test_ngt_InsertUpsert_with_additional_digits_for_each_vector_element(t *testing.T) { + type args struct { + idxes []index + poolSize uint32 + bulkSize int + } + type fields struct { + svcCfg *config.NGT + svcOpts []Option + + core core.NGT + eg errgroup.Group + kvs kvs.BidiMap + fmu sync.Mutex + fmap map[string]uint32 + vq vqueue.Queue + indexing atomic.Value + saving atomic.Value + cimu sync.Mutex + lastNocie uint64 + nocie uint64 + nogce uint64 + inMem bool + dim int + alen int + lim time.Duration + dur time.Duration + sdur time.Duration + minLit time.Duration + maxLit time.Duration + litFactor time.Duration + enableProactiveGC bool + enableCopyOnWrite bool + path string + smu sync.Mutex + tmpPath atomic.Value + oldPath string + basePath string + cowmu sync.Mutex + backupGen uint64 + poolSize uint32 + radius float32 + epsilon float32 + idelay time.Duration + dcd bool + kvsdbConcurrency int + } + type want struct { + err error + } + type test struct { + name string + args args + fields fields + want want + checkFunc func(want, error) error + beforeFunc func(args) + afterFunc func(args) + } + defaultCheckFunc := func(w want, err error) error { + if !errors.Is(err, w.err) { + return errors.Errorf("got_error: \"%#v\",\n\t\t\t\twant: \"%#v\"", err, w.err) + } + return nil + } + var ( + // default NGT configuration for test + kvsdbCfg = &config.KVSDB{} + vqueueCfg = &config.VQueue{} + ) + tests := []test{ + { + name: "insert & upsert 100 random", + args: args{ + idxes: createRandomData(10000000, &createRandomDataConfig{ + additionaldigits: 11, + }), + }, + fields: fields{ + svcCfg: &config.NGT{ + Dimension: 128, + DistanceType: core.Cosine.String(), + ObjectType: core.Uint8.String(), + KVSDB: kvsdbCfg, + VQueue: vqueueCfg, + }, + svcOpts: []Option{ + WithEnableInMemoryMode(true), + }, + }, + }, + } + for _, tc := range tests { + test := tc + t.Run(test.name, func(tt *testing.T) { + tt.Parallel() + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + // defer goleak.VerifyNone(tt, goleak.IgnoreCurrent()) + if test.beforeFunc != nil { + test.beforeFunc(test.args) + } + if test.afterFunc != nil { + defer test.afterFunc(test.args) + } + checkFunc := test.checkFunc + if test.checkFunc == nil { + checkFunc = defaultCheckFunc + } + + eg, _ := errgroup.New(ctx) + n, err := New(test.fields.svcCfg, append(test.fields.svcOpts, WithErrGroup(eg))...) + if err != nil { + tt.Errorf("failed to init ngt service, error = %v", err) + } + var wg sync.WaitGroup + count := 0 + for _, idx := range test.args.idxes { + count++ + err = n.Insert(idx.uuid, idx.vec) + if err := checkFunc(test.want, err); err != nil { + tt.Errorf("error = %v", err) + } + + if count >= test.args.bulkSize { + wg.Add(1) + eg.Go(func() error { + defer wg.Done() + err = n.CreateAndSaveIndex(ctx, test.args.poolSize) + if err != nil { + tt.Errorf("error creating index: %v", err) + } + return nil + }) + count = 0 + } + } + wg.Wait() + + err = n.CreateAndSaveIndex(ctx, test.args.poolSize) + if err != nil { + tt.Errorf("error creating index: %v", err) + } + + eg.Go(func() error { + var wgu sync.WaitGroup + count = 0 + for _, idx := range test.args.idxes[:len(test.args.idxes)/3] { + count++ + err = n.Delete(idx.uuid) + if err != nil { + tt.Errorf("delete error = %v", err) + } + err = n.Insert(idx.uuid, idx.vec) + if err := checkFunc(test.want, err); err != nil { + tt.Errorf("error = %v", err) + } + + if count >= test.args.bulkSize { + wgu.Add(1) + eg.Go(func() error { + defer wgu.Done() + err = n.CreateAndSaveIndex(ctx, test.args.poolSize) + if err != nil { + tt.Errorf("error creating index: %v", err) + } + return nil + }) + count = 0 + } + } + wgu.Wait() + return nil + }) + + eg.Go(func() error { + var wgu sync.WaitGroup + count = 0 + for _, idx := range test.args.idxes[len(test.args.idxes)/3 : 2*len(test.args.idxes)/3] { + count++ + err = n.Delete(idx.uuid) + if err != nil { + tt.Errorf("delete error = %v", err) + } + err = n.Insert(idx.uuid, idx.vec) + if err := checkFunc(test.want, err); err != nil { + tt.Errorf("error = %v", err) + } + + if count >= test.args.bulkSize { + wgu.Add(1) + eg.Go(func() error { + defer wgu.Done() + err = n.CreateAndSaveIndex(ctx, test.args.poolSize) + if err != nil { + tt.Errorf("error creating index: %v", err) + } + return nil + }) + count = 0 + } + } + wgu.Wait() + return nil + }) + + eg.Go(func() error { + var wgu sync.WaitGroup + count = 0 + for _, idx := range test.args.idxes[2*len(test.args.idxes)/3:] { + count++ + err = n.Delete(idx.uuid) + if err != nil { + tt.Errorf("delete error = %v", err) + } + err = n.Insert(idx.uuid, idx.vec) + if err := checkFunc(test.want, err); err != nil { + tt.Errorf("error = %v", err) + } + + if count >= test.args.bulkSize { + wgu.Add(1) + eg.Go(func() error { + defer wgu.Done() + err = n.CreateAndSaveIndex(ctx, test.args.poolSize) + if err != nil { + tt.Errorf("error creating index: %v", err) + } + return nil + }) + count = 0 + } + } + wgu.Wait() + return nil + }) + + err = n.CreateAndSaveIndex(ctx, test.args.poolSize) + if err != nil { + tt.Errorf("error creating index: %v", err) + } + eg.Wait() + }) + } +} + +type createRandomDataConfig struct { + additionaldigits int +} + +func (cfg *createRandomDataConfig) verify() *createRandomDataConfig { + if cfg == nil { + cfg = new(createRandomDataConfig) + } + if cfg.additionaldigits < 0 { + cfg.additionaldigits = 0 + } + return cfg +} + +func createRandomData(num int, cfg *createRandomDataConfig) []index { + cfg = cfg.verify() + + var ad float32 = 1.0 + for i := 0; i < cfg.additionaldigits; i++ { + ad = ad * 0.1 + } + + result := make([]index, 0) + f32s, _ := vector.GenF32Vec(vector.NegativeUniform, num, 128) + + for idx, vec := range f32s { + for i := range vec { + if f := vec[i] * ad; f == 0.0 { + if vec[i] > 0.0 { + vec[i] = math.MaxFloat32 + } else if vec[i] < 0.0 { + vec[i] = math.SmallestNonzeroFloat32 + } + continue + } + vec[i] = vec[i] * ad + } + result = append(result, index{ + uuid: fmt.Sprintf("%s_%s-%s:%d:%d,%d", uuid.New().String(), uuid.New().String(), uuid.New().String(), idx, idx/100, idx%100), + vec: vec, + }) + } + + return result +} diff --git a/pkg/agent/core/ngt/service/vqueue/indexmap.go b/pkg/agent/core/ngt/service/vqueue/indexmap.go new file mode 100644 index 00000000000..1b426b0f308 --- /dev/null +++ b/pkg/agent/core/ngt/service/vqueue/indexmap.go @@ -0,0 +1,404 @@ +// Copyright (C) 2019-2022 vdaas.org vald team +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package vqueue + +import ( + "sync" + "sync/atomic" + "unsafe" +) + +// Map is like a Go map[interface{}]interface{} but is safe for concurrent use +// by multiple goroutines without additional locking or coordination. +// Loads, stores, and deletes run in amortized constant time. +// +// The Map type is specialized. Most code should use a plain Go map instead, +// with separate locking or coordination, for better type safety and to make it +// easier to maintain other invariants along with the map content. +// +// The Map type is optimized for two common use cases: (1) when the entry for a given +// key is only ever written once but read many times, as in caches that only grow, +// or (2) when multiple goroutines read, write, and overwrite entries for disjoint +// sets of keys. In these two cases, use of a Map may significantly reduce lock +// contention compared to a Go map paired with a separate Mutex or RWMutex. +// +// The zero Map is empty and ready for use. A Map must not be copied after first use. +// +// In the terminology of the Go memory model, Map arranges that a write operation +// “synchronizes before” any read operation that observes the effect of the write, where +// read and write operations are defined as follows. +// Load, LoadAndDelete, LoadOrStore are read operations; +// Delete, LoadAndDelete, and Store are write operations; +// and LoadOrStore is a write operation when it returns loaded set to false. +type indexMap struct { + mu sync.Mutex + + // read contains the portion of the map's contents that are safe for + // concurrent access (with or without mu held). + // + // The read field itself is always safe to load, but must only be stored with + // mu held. + // + // Entries stored in read may be updated concurrently without mu, but updating + // a previously-expunged entry requires that the entry be copied to the dirty + // map and unexpunged with mu held. + read atomic.Value // readOnly + + // dirty contains the portion of the map's contents that require mu to be + // held. To ensure that the dirty map can be promoted to the read map quickly, + // it also includes all of the non-expunged entries in the read map. + // + // Expunged entries are not stored in the dirty map. An expunged entry in the + // clean map must be unexpunged and added to the dirty map before a new value + // can be stored to it. + // + // If the dirty map is nil, the next write to the map will initialize it by + // making a shallow copy of the clean map, omitting stale entries. + dirty map[string]*entryIndexMap + + // misses counts the number of loads since the read map was last updated that + // needed to lock mu to determine whether the key was present. + // + // Once enough misses have occurred to cover the cost of copying the dirty + // map, the dirty map will be promoted to the read map (in the unamended + // state) and the next store to the map will make a new dirty copy. + misses int +} + +// readOnly is an immutable struct stored atomically in the Map.read field. +type readOnlyIndexMap struct { + m map[string]*entryIndexMap + amended bool // true if the dirty map contains some key not in m. +} + +// expunged is an arbitrary pointer that marks entries which have been deleted +// from the dirty map. +var expungedIndexMap = unsafe.Pointer(new(index)) + +// An entry is a slot in the map corresponding to a particular key. +type entryIndexMap struct { + // p points to the interface{} value stored for the entry. + // + // If p == nil, the entry has been deleted, and either m.dirty == nil or + // m.dirty[key] is e. + // + // If p == expunged, the entry has been deleted, m.dirty != nil, and the entry + // is missing from m.dirty. + // + // Otherwise, the entry is valid and recorded in m.read.m[key] and, if m.dirty + // != nil, in m.dirty[key]. + // + // An entry can be deleted by atomic replacement with nil: when m.dirty is + // next created, it will atomically replace nil with expunged and leave + // m.dirty[key] unset. + // + // An entry's associated value can be updated by atomic replacement, provided + // p != expunged. If p == expunged, an entry's associated value can be updated + // only after first setting m.dirty[key] = e so that lookups using the dirty + // map find the entry. + p unsafe.Pointer // *interface{} +} + +func newEntryIndexMap(i index) *entryIndexMap { + return &entryIndexMap{p: unsafe.Pointer(&i)} +} + +// Load returns the value stored in the map for a key, or nil if no +// value is present. +// The ok result indicates whether value was found in the map. +func (m *indexMap) Load(key string) (value index, ok bool) { + read, _ := m.read.Load().(readOnlyIndexMap) + e, ok := read.m[key] + if !ok && read.amended { + m.mu.Lock() + // Avoid reporting a spurious miss if m.dirty got promoted while we were + // blocked on m.mu. (If further loads of the same key will not miss, it's + // not worth copying the dirty map for this key.) + read, _ = m.read.Load().(readOnlyIndexMap) + e, ok = read.m[key] + if !ok && read.amended { + e, ok = m.dirty[key] + // Regardless of whether the entry was present, record a miss: this key + // will take the slow path until the dirty map is promoted to the read + // map. + m.missLocked() + } + m.mu.Unlock() + } + if !ok { + return value, false + } + return e.load() +} + +func (e *entryIndexMap) load() (value index, ok bool) { + p := atomic.LoadPointer(&e.p) + if p == nil || p == expungedIndexMap { + return value, false + } + return *(*index)(p), true +} + +// Store sets the value for a key. +func (m *indexMap) Store(key string, value index) { + read, _ := m.read.Load().(readOnlyIndexMap) + if e, ok := read.m[key]; ok && e.tryStore(&value) { + return + } + + m.mu.Lock() + read, _ = m.read.Load().(readOnlyIndexMap) + if e, ok := read.m[key]; ok { + if e.unexpungeLocked() { + // The entry was previously expunged, which implies that there is a + // non-nil dirty map and this entry is not in it. + m.dirty[key] = e + } + e.storeLocked(&value) + } else if e, ok := m.dirty[key]; ok { + e.storeLocked(&value) + } else { + if !read.amended { + // We're adding the first new key to the dirty map. + // Make sure it is allocated and mark the read-only map as incomplete. + m.dirtyLocked() + m.read.Store(readOnlyIndexMap{m: read.m, amended: true}) + } + m.dirty[key] = newEntryIndexMap(value) + } + m.mu.Unlock() +} + +// tryStore stores a value if the entry has not been expunged. +// +// If the entry is expunged, tryStore returns false and leaves the entry +// unchanged. +func (e *entryIndexMap) tryStore(i *index) bool { + for { + p := atomic.LoadPointer(&e.p) + if p == expungedIndexMap { + return false + } + if atomic.CompareAndSwapPointer(&e.p, p, unsafe.Pointer(i)) { + return true + } + } +} + +// unexpungeLocked ensures that the entry is not marked as expunged. +// +// If the entry was previously expunged, it must be added to the dirty map +// before m.mu is unlocked. +func (e *entryIndexMap) unexpungeLocked() (wasExpunged bool) { + return atomic.CompareAndSwapPointer(&e.p, expungedIndexMap, nil) +} + +// storeLocked unconditionally stores a value to the entry. +// +// The entry must be known not to be expunged. +func (e *entryIndexMap) storeLocked(i *index) { + atomic.StorePointer(&e.p, unsafe.Pointer(i)) +} + +// LoadOrStore returns the existing value for the key if present. +// Otherwise, it stores and returns the given value. +// The loaded result is true if the value was loaded, false if stored. +func (m *indexMap) LoadOrStore(key string, value index) (actual index, loaded bool) { + // Avoid locking if it's a clean hit. + read, _ := m.read.Load().(readOnlyIndexMap) + if e, ok := read.m[key]; ok { + actual, loaded, ok := e.tryLoadOrStore(value) + if ok { + return actual, loaded + } + } + + m.mu.Lock() + read, _ = m.read.Load().(readOnlyIndexMap) + if e, ok := read.m[key]; ok { + if e.unexpungeLocked() { + m.dirty[key] = e + } + actual, loaded, _ = e.tryLoadOrStore(value) + } else if e, ok := m.dirty[key]; ok { + actual, loaded, _ = e.tryLoadOrStore(value) + m.missLocked() + } else { + if !read.amended { + // We're adding the first new key to the dirty map. + // Make sure it is allocated and mark the read-only map as incomplete. + m.dirtyLocked() + m.read.Store(readOnlyIndexMap{m: read.m, amended: true}) + } + m.dirty[key] = newEntryIndexMap(value) + actual, loaded = value, false + } + m.mu.Unlock() + + return actual, loaded +} + +// tryLoadOrStore atomically loads or stores a value if the entry is not +// expunged. +// +// If the entry is expunged, tryLoadOrStore leaves the entry unchanged and +// returns with ok==false. +func (e *entryIndexMap) tryLoadOrStore(i index) (actual index, loaded, ok bool) { + p := atomic.LoadPointer(&e.p) + if p == expungedIndexMap { + return actual, false, false + } + if p != nil { + return *(*index)(p), true, true + } + + // Copy the interface after the first load to make this method more amenable + // to escape analysis: if we hit the "load" path or the entry is expunged, we + // shouldn't bother heap-allocating. + ic := i + for { + if atomic.CompareAndSwapPointer(&e.p, nil, unsafe.Pointer(&ic)) { + return i, false, true + } + p = atomic.LoadPointer(&e.p) + if p == expungedIndexMap { + return actual, false, false + } + if p != nil { + return *(*index)(p), true, true + } + } +} + +// LoadAndDelete deletes the value for a key, returning the previous value if any. +// The loaded result reports whether the key was present. +func (m *indexMap) LoadAndDelete(key string) (value index, loaded bool) { + read, _ := m.read.Load().(readOnlyIndexMap) + e, ok := read.m[key] + if !ok && read.amended { + m.mu.Lock() + read, _ = m.read.Load().(readOnlyIndexMap) + e, ok = read.m[key] + if !ok && read.amended { + e, ok = m.dirty[key] + delete(m.dirty, key) + // Regardless of whether the entry was present, record a miss: this key + // will take the slow path until the dirty map is promoted to the read + // map. + m.missLocked() + } + m.mu.Unlock() + } + if ok { + return e.delete() + } + return value, false +} + +// Delete deletes the value for a key. +func (m *indexMap) Delete(key string) { + m.LoadAndDelete(key) +} + +func (e *entryIndexMap) delete() (value index, ok bool) { + for { + p := atomic.LoadPointer(&e.p) + if p == nil || p == expungedIndexMap { + return value, false + } + if atomic.CompareAndSwapPointer(&e.p, p, nil) { + return *(*index)(p), true + } + } +} + +// Range calls f sequentially for each key and value present in the map. +// If f returns false, range stops the iteration. +// +// Range does not necessarily correspond to any consistent snapshot of the Map's +// contents: no key will be visited more than once, but if the value for any key +// is stored or deleted concurrently (including by f), Range may reflect any +// mapping for that key from any point during the Range call. Range does not +// block other methods on the receiver; even f itself may call any method on m. +// +// Range may be O(N) with the number of elements in the map even if f returns +// false after a constant number of calls. +func (m *indexMap) Range(f func(key string, value index) bool) { + // We need to be able to iterate over all of the keys that were already + // present at the start of the call to Range. + // If read.amended is false, then read.m satisfies that property without + // requiring us to hold m.mu for a long time. + read, _ := m.read.Load().(readOnlyIndexMap) + if read.amended { + // m.dirty contains keys not in read.m. Fortunately, Range is already O(N) + // (assuming the caller does not break out early), so a call to Range + // amortizes an entire copy of the map: we can promote the dirty copy + // immediately! + m.mu.Lock() + read, _ = m.read.Load().(readOnlyIndexMap) + if read.amended { + read = readOnlyIndexMap{m: m.dirty} + m.read.Store(read) + m.dirty = nil + m.misses = 0 + } + m.mu.Unlock() + } + + for k, e := range read.m { + v, ok := e.load() + if !ok { + continue + } + if !f(k, v) { + break + } + } +} + +func (m *indexMap) missLocked() { + m.misses++ + if m.misses < len(m.dirty) { + return + } + m.read.Store(readOnlyIndexMap{m: m.dirty}) + m.dirty = nil + m.misses = 0 +} + +func (m *indexMap) dirtyLocked() { + if m.dirty != nil { + return + } + + read, _ := m.read.Load().(readOnlyIndexMap) + m.dirty = make(map[string]*entryIndexMap, len(read.m)) + for k, e := range read.m { + if !e.tryExpungeLocked() { + m.dirty[k] = e + } + } +} + +func (e *entryIndexMap) tryExpungeLocked() (isExpunged bool) { + p := atomic.LoadPointer(&e.p) + for p == nil { + if atomic.CompareAndSwapPointer(&e.p, nil, expungedIndexMap) { + return true + } + p = atomic.LoadPointer(&e.p) + } + return p == expungedIndexMap +} diff --git a/pkg/agent/core/ngt/service/vqueue/option.go b/pkg/agent/core/ngt/service/vqueue/option.go index ba8efff6b77..feedad1c33c 100644 --- a/pkg/agent/core/ngt/service/vqueue/option.go +++ b/pkg/agent/core/ngt/service/vqueue/option.go @@ -49,8 +49,6 @@ func WithInsertBufferPoolSize(size int) Option { if size <= 0 { return errors.NewErrInvalidOption("insertBufferPoolSize", size) } - v.iBufSize = size - return nil } } @@ -61,8 +59,6 @@ func WithDeleteBufferPoolSize(size int) Option { if size <= 0 { return errors.NewErrInvalidOption("deleteBufferPoolSize", size) } - v.dBufSize = size - return nil } } diff --git a/pkg/agent/core/ngt/service/vqueue/queue.go b/pkg/agent/core/ngt/service/vqueue/queue.go index cd761e7051e..21d0dc27f4f 100644 --- a/pkg/agent/core/ngt/service/vqueue/queue.go +++ b/pkg/agent/core/ngt/service/vqueue/queue.go @@ -19,14 +19,9 @@ package vqueue import ( "context" - "reflect" "sort" - "sync" + "sync/atomic" "time" - - "github.com/vdaas/vald/internal/errgroup" - "github.com/vdaas/vald/internal/errors" - "github.com/vdaas/vald/internal/log" ) // Queue @@ -43,17 +38,8 @@ type Queue interface { } type vqueue struct { - uii []index // uii is un inserted index - imu sync.RWMutex // insert mutex - uiim uiim // uiim is un inserted index map (this value is used for GetVector operation to return queued vector cache data) - udk []key // udk is un deleted key - dmu sync.RWMutex // delete mutex - udim udim // udim is un deleted index map (this value is used for Exists operation to return cache data existence) - eg errgroup.Group - - // buffer config - iBufSize int - dBufSize int + il, dl indexMap + ic, dc uint64 } type index struct { @@ -62,27 +48,20 @@ type index struct { uuid string } -type key struct { - date int64 - uuid string -} - func New(opts ...Option) (Queue, error) { vq := new(vqueue) for _, opt := range append(defaultOptions, opts...) { if err := opt(vq); err != nil { - werr := errors.ErrOptionFailed(err, reflect.ValueOf(opt)) - - e := new(errors.ErrCriticalOption) - if errors.As(err, &e) { - log.Error(werr) - return nil, werr - } - log.Warn(werr) + // werr := errors.ErrOptionFailed(err, reflect.ValueOf(opt)) + // + // e := new(errors.ErrCriticalOption) + // if errors.As(err, &e) { + // log.Error(werr) + // return nil, werr + // } + // log.Warn(werr) } } - vq.uii = make([]index, 0, vq.iBufSize) - vq.udk = make([]key, 0, vq.dBufSize) return vq, nil } @@ -90,23 +69,18 @@ func (v *vqueue) PushInsert(uuid string, vector []float32, date int64) error { if date == 0 { date = time.Now().UnixNano() } - ddate, ok := v.udim.Load(uuid) - if ok && ddate > date { - return nil - } idx := index{ uuid: uuid, vector: vector, date: date, } - oidx, loaded := v.uiim.LoadOrStore(uuid, idx) - if loaded { - if oidx.date > idx.date { - return nil - } - v.uiim.Store(uuid, idx) + oidx, loaded := v.il.LoadOrStore(uuid, idx) + if loaded && date > oidx.date { // if data already exists and existing index is older than new one + v.il.Delete(uuid) + v.il.Store(uuid, idx) + } else { + _ = atomic.AddUint64(&v.ic, 1) } - v.addInsert(idx) return nil } @@ -114,17 +88,17 @@ func (v *vqueue) PushDelete(uuid string, date int64) error { if date == 0 { date = time.Now().UnixNano() } - odate, loaded := v.udim.LoadOrStore(uuid, date) - if loaded { - if odate > date { - return nil - } - v.udim.Store(uuid, date) - } - v.addDelete(key{ + idx := index{ uuid: uuid, date: date, - }) + } + oidx, loaded := v.dl.LoadOrStore(uuid, idx) + if loaded && date > oidx.date { // if data already exists and existing index is older than new one + v.dl.Delete(uuid) + v.dl.Store(uuid, idx) + } else { + _ = atomic.AddUint64(&v.dc, 1) + } return nil } @@ -132,19 +106,19 @@ func (v *vqueue) PushDelete(uuid string, date int64) error { // If the same UUID exists in the insert queue and the delete queue, the timestamp is compared. // And the vector is returned if the timestamp in the insert queue is newer than the delete queue. func (v *vqueue) GetVector(uuid string) ([]float32, bool) { - vec, ok := v.uiim.Load(uuid) + idx, ok := v.il.Load(uuid) if !ok { // data not in the insert queue then return not exists(false) return nil, false } - di, ok := v.udim.Load(uuid) + didx, ok := v.dl.Load(uuid) if !ok { // data not in the delete queue but exists in insert queue then return exists(true) - return vec.vector, true + return idx.vector, true } // data exists both queue, compare data timestamp if insert queue timestamp is newer than delete one, this function returns exists(true) - if di <= vec.date { - return vec.vector, true + if didx.date <= idx.date { + return idx.vector, true } return nil, false } @@ -153,181 +127,97 @@ func (v *vqueue) GetVector(uuid string) ([]float32, bool) { // If the same UUID exists in the insert queue and the delete queue, the timestamp is compared. // And the true is returned if the timestamp in the insert queue is newer than the delete queue. func (v *vqueue) IVExists(uuid string) bool { - vec, ok := v.uiim.Load(uuid) + idx, ok := v.il.Load(uuid) if !ok { // data not in the insert queue then return not exists(false) return false } - di, ok := v.udim.Load(uuid) + didx, ok := v.dl.Load(uuid) if !ok { // data not in the delete queue but exists in insert queue then return exists(true) return true } // data exists both queue, compare data timestamp if insert queue timestamp is newer than delete one, this function returns exists(true) // However, if insert and delete are sent by the update instruction, the timestamp will be the same - return di <= vec.date + return didx.date <= idx.date } // DVExists returns true if there is the UUID in the delete queue. // If the same UUID exists in the insert queue and the delete queue, the timestamp is compared. // And the true is returned if the timestamp in the delete queue is newer than the insert queue. func (v *vqueue) DVExists(uuid string) bool { - di, ok := v.udim.Load(uuid) + didx, ok := v.dl.Load(uuid) if !ok { return false } - vec, ok := v.uiim.Load(uuid) + idx, ok := v.il.Load(uuid) if !ok { // data not in the insert queue then return not exists(false) return true } // data exists both queue, compare data timestamp if insert queue timestamp is newer than delete one, this function returns exists(true) - return di > vec.date -} - -func (v *vqueue) addInsert(i index) { - date, ok := v.udim.Load(i.uuid) - if ok && i.date < date { - return - } - idx, ok := v.uiim.Load(i.uuid) - if ok && i.date < idx.date { - return - } - v.imu.Lock() - v.uii = append(v.uii, i) - v.imu.Unlock() -} - -func (v *vqueue) addDelete(d key) { - date, ok := v.udim.Load(d.uuid) - if ok && d.date < date { - return - } - v.dmu.Lock() - v.udk = append(v.udk, d) - v.dmu.Unlock() + return didx.date > idx.date } func (v *vqueue) RangePopInsert(_ context.Context, now int64, f func(uuid string, vector []float32) bool) { - v.imu.Lock() - uii := make([]index, len(v.uii)) - copy(uii, v.uii) - v.uii = v.uii[:0] - v.imu.Unlock() + uii := make([]index, 0, atomic.LoadUint64(&v.ic)) + v.il.Range(func(uuid string, idx index) bool { + v.il.Delete(uuid) + atomic.AddUint64(&v.ic, ^uint64(0)) + didx, ok := v.dl.Load(uuid) + if ok { // if delete map exists + if idx.date < didx.date { // if delete data is newer than insert data then ignore insert + return true + } + v.dl.Delete(uuid) // if insert data is newer than delete data then we can remove data from delete map + atomic.AddUint64(&v.dc, ^uint64(0)) + } + uii = append(uii, idx) + return true + }) sort.Slice(uii, func(i, j int) bool { // sort by latest unix time order return uii[i].date > uii[j].date }) - dup := make(map[string]bool, len(uii)/2) for i, idx := range uii { - if idx.date > now { - v.imu.Lock() - v.uii = append(v.uii, idx) - v.imu.Unlock() - continue - } - - // if the same uuid is detected in the delete map during insert phase, which means the data is not processed in the delete phase. - // we need to add it back to insert map to process it in next create index process. - if _, ok := v.udim.Load(idx.uuid); ok { - v.imu.Lock() - v.uii = append(v.uii, idx) - v.imu.Unlock() - continue - } - - // if duplicated data exists current loop's data is old due to the uii's sort order - if !dup[idx.uuid] { - dup[idx.uuid] = true - if !f(idx.uuid, idx.vector) { - v.imu.Lock() - v.uii = append(uii[i:], v.uii...) - v.imu.Unlock() - return + if !f(idx.uuid, idx.vector) { + for _, idx = range uii[i:] { + v.PushInsert(idx.uuid, idx.vector, idx.date) } - v.uiim.Delete(idx.uuid) + return } } } func (v *vqueue) RangePopDelete(_ context.Context, now int64, f func(uuid string) bool) { - v.dmu.Lock() - udk := make([]key, len(v.udk)) - copy(udk, v.udk) - v.udk = v.udk[:0] - v.dmu.Unlock() - sort.Slice(udk, func(i, j int) bool { - return udk[i].date > udk[j].date + udi := make([]index, 0, atomic.LoadUint64(&v.dc)) + v.dl.Range(func(uuid string, idx index) bool { + v.dl.Delete(uuid) + atomic.AddUint64(&v.dc, ^uint64(0)) + udi = append(udi, idx) + return true }) - dup := make(map[string]bool, len(udk)/2) - udm := make(map[string]int64, len(udk)) - for i, idx := range udk { - if idx.date > now { - v.dmu.Lock() - v.udk = append(v.udk, idx) - v.dmu.Unlock() - continue - } - if !dup[idx.uuid] { - dup[idx.uuid] = true - if !f(idx.uuid) { - v.dmu.Lock() - v.udk = append(udk[i:], v.udk...) - v.dmu.Unlock() - return + sort.Slice(udi, func(i, j int) bool { + // sort by latest unix time order + return udi[i].date > udi[j].date + }) + for i, idx := range udi { + if !f(idx.uuid) { + for _, idx = range udi[i:] { + v.PushDelete(idx.uuid, idx.date) } - v.udim.Delete(idx.uuid) - udm[idx.uuid] = idx.date + return } } - - dl := make([]int, 0, len(udk)/2) - - // In the CreateIndex operation of the NGT Service, the Delete Queue is processed first, and then the Insert Queue is processed, - // so the Insert Queue still contains the old Insert Operation older than the Delete Queue, - // and it is possible that data that was intended to be deleted is registered again. - // For this reason, the data is deleted from the Insert Queue only when retrieving data from the Delete Queue. - // we should check insert vqueue if insert vqueue exists and delete operation date is newer than insert operation date then we should remove insert vqueue's data. - v.imu.RLock() - for i, idx := range v.uii { - if idx.date > now { - continue - } - // check same uuid & operation date - // if date is equal, it may update operation we shouldn't remove at that time - date, exists := udm[idx.uuid] - if exists && date <= now && date > idx.date { - dl = append(dl, i) - } - } - v.imu.RUnlock() - sort.Sort(sort.Reverse(sort.IntSlice(dl))) - for _, i := range dl { - v.imu.Lock() - // load removal target uuid - uuid := v.uii[i].uuid - // remove unnecessary insert vector queue data - v.uii = append(v.uii[:i], v.uii[i+1:]...) - v.imu.Unlock() - // remove from existing map - v.uiim.Delete(uuid) - } } // IVQLen returns the number of uninserted indexes stored in the insert queue. func (v *vqueue) IVQLen() (l int) { - v.imu.RLock() - l = len(v.uii) - v.imu.RUnlock() - return l + return int(atomic.LoadUint64(&v.ic)) } // DVQLen returns the number of undeleted keys stored in the delete queue. func (v *vqueue) DVQLen() (l int) { - v.dmu.RLock() - l = len(v.udk) - v.dmu.RUnlock() - return l + return int(atomic.LoadUint64(&v.dc)) } diff --git a/pkg/agent/core/ngt/service/vqueue/undeleted_index_map.go b/pkg/agent/core/ngt/service/vqueue/undeleted_index_map.go deleted file mode 100644 index 0b83a13da98..00000000000 --- a/pkg/agent/core/ngt/service/vqueue/undeleted_index_map.go +++ /dev/null @@ -1,274 +0,0 @@ -// Copyright (C) 2019-2022 vdaas.org vald team -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -package vqueue - -import ( - "sync" - "sync/atomic" - "unsafe" -) - -type udim struct { - mu sync.Mutex - read atomic.Value - dirty map[string]*entryUdim - misses int -} - -type readOnlyUdim struct { - m map[string]*entryUdim - amended bool -} - -// skipcq: GSC-G103 -var expungedUdim = unsafe.Pointer(new(int64)) - -type entryUdim struct { - p unsafe.Pointer -} - -func newEntryUdim(i int64) *entryUdim { - // skipcq: GSC-G103 - return &entryUdim{p: unsafe.Pointer(&i)} -} - -func (m *udim) Load(key string) (value int64, ok bool) { - read, _ := m.read.Load().(readOnlyUdim) - e, ok := read.m[key] - if !ok && read.amended { - m.mu.Lock() - read, _ = m.read.Load().(readOnlyUdim) - e, ok = read.m[key] - if !ok && read.amended { - e, ok = m.dirty[key] - m.missLocked() - } - m.mu.Unlock() - } - if !ok { - return value, false - } - return e.load() -} - -func (e *entryUdim) load() (value int64, ok bool) { - p := atomic.LoadPointer(&e.p) - if p == nil || p == expungedUdim { - return value, false - } - return *(*int64)(p), true -} - -func (m *udim) Store(key string, value int64) { - read, _ := m.read.Load().(readOnlyUdim) - if e, ok := read.m[key]; ok && e.tryStore(&value) { - return - } - - m.mu.Lock() - read, _ = m.read.Load().(readOnlyUdim) - if e, ok := read.m[key]; ok { - if e.unexpungeLocked() { - m.dirty[key] = e - } - e.storeLocked(&value) - } else if e, ok := m.dirty[key]; ok { - e.storeLocked(&value) - } else { - if !read.amended { - m.dirtyLocked() - m.read.Store(readOnlyUdim{m: read.m, amended: true}) - } - m.dirty[key] = newEntryUdim(value) - } - m.mu.Unlock() -} - -func (e *entryUdim) tryStore(i *int64) bool { - for { - p := atomic.LoadPointer(&e.p) - if p == expungedUdim { - return false - } - // skipcq: GSC-G103 - if atomic.CompareAndSwapPointer(&e.p, p, unsafe.Pointer(i)) { - return true - } - } -} - -func (e *entryUdim) unexpungeLocked() (wasExpunged bool) { - return atomic.CompareAndSwapPointer(&e.p, expungedUdim, nil) -} - -func (e *entryUdim) storeLocked(i *int64) { - // skipcq: GSC-G103 - atomic.StorePointer(&e.p, unsafe.Pointer(i)) -} - -func (m *udim) LoadOrStore(key string, value int64) (actual int64, loaded bool) { - read, _ := m.read.Load().(readOnlyUdim) - if e, ok := read.m[key]; ok { - actual, loaded, ok := e.tryLoadOrStore(value) - if ok { - return actual, loaded - } - } - - m.mu.Lock() - read, _ = m.read.Load().(readOnlyUdim) - if e, ok := read.m[key]; ok { - if e.unexpungeLocked() { - m.dirty[key] = e - } - actual, loaded, _ = e.tryLoadOrStore(value) - } else if e, ok := m.dirty[key]; ok { - actual, loaded, _ = e.tryLoadOrStore(value) - m.missLocked() - } else { - if !read.amended { - - m.dirtyLocked() - m.read.Store(readOnlyUdim{m: read.m, amended: true}) - } - m.dirty[key] = newEntryUdim(value) - actual, loaded = value, false - } - m.mu.Unlock() - - return actual, loaded -} - -func (e *entryUdim) tryLoadOrStore(i int64) (actual int64, loaded, ok bool) { - p := atomic.LoadPointer(&e.p) - if p == expungedUdim { - return actual, false, false - } - if p != nil { - return *(*int64)(p), true, true - } - - ic := i - for { - // skipcq: GSC-G103 - if atomic.CompareAndSwapPointer(&e.p, nil, unsafe.Pointer(&ic)) { - return i, false, true - } - p = atomic.LoadPointer(&e.p) - if p == expungedUdim { - return actual, false, false - } - if p != nil { - return *(*int64)(p), true, true - } - } -} - -func (m *udim) LoadAndDelete(key string) (value int64, loaded bool) { - read, _ := m.read.Load().(readOnlyUdim) - e, ok := read.m[key] - if !ok && read.amended { - m.mu.Lock() - read, _ = m.read.Load().(readOnlyUdim) - e, ok = read.m[key] - if !ok && read.amended { - e, ok = m.dirty[key] - delete(m.dirty, key) - - m.missLocked() - } - m.mu.Unlock() - } - if ok { - return e.delete() - } - return value, false -} - -func (m *udim) Delete(key string) { - m.LoadAndDelete(key) -} - -func (e *entryUdim) delete() (value int64, ok bool) { - for { - p := atomic.LoadPointer(&e.p) - if p == nil || p == expungedUdim { - return value, false - } - if atomic.CompareAndSwapPointer(&e.p, p, nil) { - return *(*int64)(p), true - } - } -} - -func (m *udim) Range(f func(key string, value int64) bool) { - read, _ := m.read.Load().(readOnlyUdim) - if read.amended { - - m.mu.Lock() - read, _ = m.read.Load().(readOnlyUdim) - if read.amended { - read = readOnlyUdim{m: m.dirty} - m.read.Store(read) - m.dirty = nil - m.misses = 0 - } - m.mu.Unlock() - } - - for k, e := range read.m { - v, ok := e.load() - if !ok { - continue - } - if !f(k, v) { - break - } - } -} - -func (m *udim) missLocked() { - m.misses++ - if m.misses < len(m.dirty) { - return - } - m.read.Store(readOnlyUdim{m: m.dirty}) - m.dirty = nil - m.misses = 0 -} - -func (m *udim) dirtyLocked() { - if m.dirty != nil { - return - } - - read, _ := m.read.Load().(readOnlyUdim) - m.dirty = make(map[string]*entryUdim, len(read.m)) - for k, e := range read.m { - if !e.tryExpungeLocked() { - m.dirty[k] = e - } - } -} - -func (e *entryUdim) tryExpungeLocked() (isExpunged bool) { - p := atomic.LoadPointer(&e.p) - for p == nil { - if atomic.CompareAndSwapPointer(&e.p, nil, expungedUdim) { - return true - } - p = atomic.LoadPointer(&e.p) - } - return p == expungedUdim -} diff --git a/pkg/agent/core/ngt/service/vqueue/undeleted_index_map_test.go b/pkg/agent/core/ngt/service/vqueue/undeleted_index_map_test.go deleted file mode 100644 index f583164bf02..00000000000 --- a/pkg/agent/core/ngt/service/vqueue/undeleted_index_map_test.go +++ /dev/null @@ -1,1395 +0,0 @@ -// Copyright (C) 2019-2022 vdaas.org vald team -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -package vqueue - -import ( - "reflect" - "sync/atomic" - "testing" - "unsafe" - - "github.com/vdaas/vald/internal/errors" - "github.com/vdaas/vald/internal/test/goleak" -) - -func Test_newEntryUdim(t *testing.T) { - type args struct { - i int64 - } - type want struct { - want *entryUdim - } - type test struct { - name string - args args - want want - checkFunc func(want, *entryUdim) error - beforeFunc func(args) - afterFunc func(args) - } - defaultCheckFunc := func(w want, got *entryUdim) error { - if !reflect.DeepEqual(got, w.want) { - return errors.Errorf("got: \"%#v\",\n\t\t\t\twant: \"%#v\"", got, w.want) - } - return nil - } - tests := []test{ - // TODO test cases - /* - { - name: "test_case_1", - args: args { - i: 0, - }, - want: want{}, - checkFunc: defaultCheckFunc, - }, - */ - - // TODO test cases - /* - func() test { - return test { - name: "test_case_2", - args: args { - i: 0, - }, - want: want{}, - checkFunc: defaultCheckFunc, - } - }(), - */ - } - - for _, tc := range tests { - test := tc - t.Run(test.name, func(tt *testing.T) { - tt.Parallel() - defer goleak.VerifyNone(tt, goleak.IgnoreCurrent()) - if test.beforeFunc != nil { - test.beforeFunc(test.args) - } - if test.afterFunc != nil { - defer test.afterFunc(test.args) - } - checkFunc := test.checkFunc - if test.checkFunc == nil { - checkFunc = defaultCheckFunc - } - - got := newEntryUdim(test.args.i) - if err := checkFunc(test.want, got); err != nil { - tt.Errorf("error = %v", err) - } - }) - } -} - -func Test_udim_Load(t *testing.T) { - type args struct { - key string - } - type fields struct { - read atomic.Value - dirty map[string]*entryUdim - misses int - } - type want struct { - wantValue int64 - wantOk bool - } - type test struct { - name string - args args - fields fields - want want - checkFunc func(want, int64, bool) error - beforeFunc func(args) - afterFunc func(args) - } - defaultCheckFunc := func(w want, gotValue int64, gotOk bool) error { - if !reflect.DeepEqual(gotValue, w.wantValue) { - return errors.Errorf("got: \"%#v\",\n\t\t\t\twant: \"%#v\"", gotValue, w.wantValue) - } - if !reflect.DeepEqual(gotOk, w.wantOk) { - return errors.Errorf("got: \"%#v\",\n\t\t\t\twant: \"%#v\"", gotOk, w.wantOk) - } - return nil - } - tests := []test{ - // TODO test cases - /* - { - name: "test_case_1", - args: args { - key: "", - }, - fields: fields { - read: nil, - dirty: nil, - misses: 0, - }, - want: want{}, - checkFunc: defaultCheckFunc, - }, - */ - - // TODO test cases - /* - func() test { - return test { - name: "test_case_2", - args: args { - key: "", - }, - fields: fields { - read: nil, - dirty: nil, - misses: 0, - }, - want: want{}, - checkFunc: defaultCheckFunc, - } - }(), - */ - } - - for _, tc := range tests { - test := tc - t.Run(test.name, func(tt *testing.T) { - tt.Parallel() - defer goleak.VerifyNone(tt, goleak.IgnoreCurrent()) - if test.beforeFunc != nil { - test.beforeFunc(test.args) - } - if test.afterFunc != nil { - defer test.afterFunc(test.args) - } - checkFunc := test.checkFunc - if test.checkFunc == nil { - checkFunc = defaultCheckFunc - } - m := &udim{ - read: test.fields.read, - dirty: test.fields.dirty, - misses: test.fields.misses, - } - - gotValue, gotOk := m.Load(test.args.key) - if err := checkFunc(test.want, gotValue, gotOk); err != nil { - tt.Errorf("error = %v", err) - } - }) - } -} - -func Test_entryUdim_load(t *testing.T) { - type fields struct { - p unsafe.Pointer - } - type want struct { - wantValue int64 - wantOk bool - } - type test struct { - name string - fields fields - want want - checkFunc func(want, int64, bool) error - beforeFunc func() - afterFunc func() - } - defaultCheckFunc := func(w want, gotValue int64, gotOk bool) error { - if !reflect.DeepEqual(gotValue, w.wantValue) { - return errors.Errorf("got: \"%#v\",\n\t\t\t\twant: \"%#v\"", gotValue, w.wantValue) - } - if !reflect.DeepEqual(gotOk, w.wantOk) { - return errors.Errorf("got: \"%#v\",\n\t\t\t\twant: \"%#v\"", gotOk, w.wantOk) - } - return nil - } - tests := []test{ - // TODO test cases - /* - { - name: "test_case_1", - fields: fields { - p: nil, - }, - want: want{}, - checkFunc: defaultCheckFunc, - }, - */ - - // TODO test cases - /* - func() test { - return test { - name: "test_case_2", - fields: fields { - p: nil, - }, - want: want{}, - checkFunc: defaultCheckFunc, - } - }(), - */ - } - - for _, tc := range tests { - test := tc - t.Run(test.name, func(tt *testing.T) { - tt.Parallel() - defer goleak.VerifyNone(tt, goleak.IgnoreCurrent()) - if test.beforeFunc != nil { - test.beforeFunc() - } - if test.afterFunc != nil { - defer test.afterFunc() - } - checkFunc := test.checkFunc - if test.checkFunc == nil { - checkFunc = defaultCheckFunc - } - e := &entryUdim{ - p: test.fields.p, - } - - gotValue, gotOk := e.load() - if err := checkFunc(test.want, gotValue, gotOk); err != nil { - tt.Errorf("error = %v", err) - } - }) - } -} - -func Test_udim_Store(t *testing.T) { - type args struct { - key string - value int64 - } - type fields struct { - read atomic.Value - dirty map[string]*entryUdim - misses int - } - type want struct{} - type test struct { - name string - args args - fields fields - want want - checkFunc func(want) error - beforeFunc func(args) - afterFunc func(args) - } - defaultCheckFunc := func(w want) error { - return nil - } - tests := []test{ - // TODO test cases - /* - { - name: "test_case_1", - args: args { - key: "", - value: 0, - }, - fields: fields { - read: nil, - dirty: nil, - misses: 0, - }, - want: want{}, - checkFunc: defaultCheckFunc, - }, - */ - - // TODO test cases - /* - func() test { - return test { - name: "test_case_2", - args: args { - key: "", - value: 0, - }, - fields: fields { - read: nil, - dirty: nil, - misses: 0, - }, - want: want{}, - checkFunc: defaultCheckFunc, - } - }(), - */ - } - - for _, tc := range tests { - test := tc - t.Run(test.name, func(tt *testing.T) { - tt.Parallel() - defer goleak.VerifyNone(tt, goleak.IgnoreCurrent()) - if test.beforeFunc != nil { - test.beforeFunc(test.args) - } - if test.afterFunc != nil { - defer test.afterFunc(test.args) - } - checkFunc := test.checkFunc - if test.checkFunc == nil { - checkFunc = defaultCheckFunc - } - m := &udim{ - read: test.fields.read, - dirty: test.fields.dirty, - misses: test.fields.misses, - } - - m.Store(test.args.key, test.args.value) - if err := checkFunc(test.want); err != nil { - tt.Errorf("error = %v", err) - } - }) - } -} - -func Test_entryUdim_tryStore(t *testing.T) { - type args struct { - i *int64 - } - type fields struct { - p unsafe.Pointer - } - type want struct { - want bool - } - type test struct { - name string - args args - fields fields - want want - checkFunc func(want, bool) error - beforeFunc func(args) - afterFunc func(args) - } - defaultCheckFunc := func(w want, got bool) error { - if !reflect.DeepEqual(got, w.want) { - return errors.Errorf("got: \"%#v\",\n\t\t\t\twant: \"%#v\"", got, w.want) - } - return nil - } - tests := []test{ - // TODO test cases - /* - { - name: "test_case_1", - args: args { - i: nil, - }, - fields: fields { - p: nil, - }, - want: want{}, - checkFunc: defaultCheckFunc, - }, - */ - - // TODO test cases - /* - func() test { - return test { - name: "test_case_2", - args: args { - i: nil, - }, - fields: fields { - p: nil, - }, - want: want{}, - checkFunc: defaultCheckFunc, - } - }(), - */ - } - - for _, tc := range tests { - test := tc - t.Run(test.name, func(tt *testing.T) { - tt.Parallel() - defer goleak.VerifyNone(tt, goleak.IgnoreCurrent()) - if test.beforeFunc != nil { - test.beforeFunc(test.args) - } - if test.afterFunc != nil { - defer test.afterFunc(test.args) - } - checkFunc := test.checkFunc - if test.checkFunc == nil { - checkFunc = defaultCheckFunc - } - e := &entryUdim{ - p: test.fields.p, - } - - got := e.tryStore(test.args.i) - if err := checkFunc(test.want, got); err != nil { - tt.Errorf("error = %v", err) - } - }) - } -} - -func Test_entryUdim_unexpungeLocked(t *testing.T) { - type fields struct { - p unsafe.Pointer - } - type want struct { - wantWasExpunged bool - } - type test struct { - name string - fields fields - want want - checkFunc func(want, bool) error - beforeFunc func() - afterFunc func() - } - defaultCheckFunc := func(w want, gotWasExpunged bool) error { - if !reflect.DeepEqual(gotWasExpunged, w.wantWasExpunged) { - return errors.Errorf("got: \"%#v\",\n\t\t\t\twant: \"%#v\"", gotWasExpunged, w.wantWasExpunged) - } - return nil - } - tests := []test{ - // TODO test cases - /* - { - name: "test_case_1", - fields: fields { - p: nil, - }, - want: want{}, - checkFunc: defaultCheckFunc, - }, - */ - - // TODO test cases - /* - func() test { - return test { - name: "test_case_2", - fields: fields { - p: nil, - }, - want: want{}, - checkFunc: defaultCheckFunc, - } - }(), - */ - } - - for _, tc := range tests { - test := tc - t.Run(test.name, func(tt *testing.T) { - tt.Parallel() - defer goleak.VerifyNone(tt, goleak.IgnoreCurrent()) - if test.beforeFunc != nil { - test.beforeFunc() - } - if test.afterFunc != nil { - defer test.afterFunc() - } - checkFunc := test.checkFunc - if test.checkFunc == nil { - checkFunc = defaultCheckFunc - } - e := &entryUdim{ - p: test.fields.p, - } - - gotWasExpunged := e.unexpungeLocked() - if err := checkFunc(test.want, gotWasExpunged); err != nil { - tt.Errorf("error = %v", err) - } - }) - } -} - -func Test_entryUdim_storeLocked(t *testing.T) { - type args struct { - i *int64 - } - type fields struct { - p unsafe.Pointer - } - type want struct{} - type test struct { - name string - args args - fields fields - want want - checkFunc func(want) error - beforeFunc func(args) - afterFunc func(args) - } - defaultCheckFunc := func(w want) error { - return nil - } - tests := []test{ - // TODO test cases - /* - { - name: "test_case_1", - args: args { - i: nil, - }, - fields: fields { - p: nil, - }, - want: want{}, - checkFunc: defaultCheckFunc, - }, - */ - - // TODO test cases - /* - func() test { - return test { - name: "test_case_2", - args: args { - i: nil, - }, - fields: fields { - p: nil, - }, - want: want{}, - checkFunc: defaultCheckFunc, - } - }(), - */ - } - - for _, tc := range tests { - test := tc - t.Run(test.name, func(tt *testing.T) { - tt.Parallel() - defer goleak.VerifyNone(tt, goleak.IgnoreCurrent()) - if test.beforeFunc != nil { - test.beforeFunc(test.args) - } - if test.afterFunc != nil { - defer test.afterFunc(test.args) - } - checkFunc := test.checkFunc - if test.checkFunc == nil { - checkFunc = defaultCheckFunc - } - e := &entryUdim{ - p: test.fields.p, - } - - e.storeLocked(test.args.i) - if err := checkFunc(test.want); err != nil { - tt.Errorf("error = %v", err) - } - }) - } -} - -func Test_udim_LoadOrStore(t *testing.T) { - type args struct { - key string - value int64 - } - type fields struct { - read atomic.Value - dirty map[string]*entryUdim - misses int - } - type want struct { - wantActual int64 - wantLoaded bool - } - type test struct { - name string - args args - fields fields - want want - checkFunc func(want, int64, bool) error - beforeFunc func(args) - afterFunc func(args) - } - defaultCheckFunc := func(w want, gotActual int64, gotLoaded bool) error { - if !reflect.DeepEqual(gotActual, w.wantActual) { - return errors.Errorf("got: \"%#v\",\n\t\t\t\twant: \"%#v\"", gotActual, w.wantActual) - } - if !reflect.DeepEqual(gotLoaded, w.wantLoaded) { - return errors.Errorf("got: \"%#v\",\n\t\t\t\twant: \"%#v\"", gotLoaded, w.wantLoaded) - } - return nil - } - tests := []test{ - // TODO test cases - /* - { - name: "test_case_1", - args: args { - key: "", - value: 0, - }, - fields: fields { - read: nil, - dirty: nil, - misses: 0, - }, - want: want{}, - checkFunc: defaultCheckFunc, - }, - */ - - // TODO test cases - /* - func() test { - return test { - name: "test_case_2", - args: args { - key: "", - value: 0, - }, - fields: fields { - read: nil, - dirty: nil, - misses: 0, - }, - want: want{}, - checkFunc: defaultCheckFunc, - } - }(), - */ - } - - for _, tc := range tests { - test := tc - t.Run(test.name, func(tt *testing.T) { - tt.Parallel() - defer goleak.VerifyNone(tt, goleak.IgnoreCurrent()) - if test.beforeFunc != nil { - test.beforeFunc(test.args) - } - if test.afterFunc != nil { - defer test.afterFunc(test.args) - } - checkFunc := test.checkFunc - if test.checkFunc == nil { - checkFunc = defaultCheckFunc - } - m := &udim{ - read: test.fields.read, - dirty: test.fields.dirty, - misses: test.fields.misses, - } - - gotActual, gotLoaded := m.LoadOrStore(test.args.key, test.args.value) - if err := checkFunc(test.want, gotActual, gotLoaded); err != nil { - tt.Errorf("error = %v", err) - } - }) - } -} - -func Test_entryUdim_tryLoadOrStore(t *testing.T) { - type args struct { - i int64 - } - type fields struct { - p unsafe.Pointer - } - type want struct { - wantActual int64 - wantLoaded bool - wantOk bool - } - type test struct { - name string - args args - fields fields - want want - checkFunc func(want, int64, bool, bool) error - beforeFunc func(args) - afterFunc func(args) - } - defaultCheckFunc := func(w want, gotActual int64, gotLoaded bool, gotOk bool) error { - if !reflect.DeepEqual(gotActual, w.wantActual) { - return errors.Errorf("got: \"%#v\",\n\t\t\t\twant: \"%#v\"", gotActual, w.wantActual) - } - if !reflect.DeepEqual(gotLoaded, w.wantLoaded) { - return errors.Errorf("got: \"%#v\",\n\t\t\t\twant: \"%#v\"", gotLoaded, w.wantLoaded) - } - if !reflect.DeepEqual(gotOk, w.wantOk) { - return errors.Errorf("got: \"%#v\",\n\t\t\t\twant: \"%#v\"", gotOk, w.wantOk) - } - return nil - } - tests := []test{ - // TODO test cases - /* - { - name: "test_case_1", - args: args { - i: 0, - }, - fields: fields { - p: nil, - }, - want: want{}, - checkFunc: defaultCheckFunc, - }, - */ - - // TODO test cases - /* - func() test { - return test { - name: "test_case_2", - args: args { - i: 0, - }, - fields: fields { - p: nil, - }, - want: want{}, - checkFunc: defaultCheckFunc, - } - }(), - */ - } - - for _, tc := range tests { - test := tc - t.Run(test.name, func(tt *testing.T) { - tt.Parallel() - defer goleak.VerifyNone(tt, goleak.IgnoreCurrent()) - if test.beforeFunc != nil { - test.beforeFunc(test.args) - } - if test.afterFunc != nil { - defer test.afterFunc(test.args) - } - checkFunc := test.checkFunc - if test.checkFunc == nil { - checkFunc = defaultCheckFunc - } - e := &entryUdim{ - p: test.fields.p, - } - - gotActual, gotLoaded, gotOk := e.tryLoadOrStore(test.args.i) - if err := checkFunc(test.want, gotActual, gotLoaded, gotOk); err != nil { - tt.Errorf("error = %v", err) - } - }) - } -} - -func Test_udim_LoadAndDelete(t *testing.T) { - type args struct { - key string - } - type fields struct { - read atomic.Value - dirty map[string]*entryUdim - misses int - } - type want struct { - wantValue int64 - wantLoaded bool - } - type test struct { - name string - args args - fields fields - want want - checkFunc func(want, int64, bool) error - beforeFunc func(args) - afterFunc func(args) - } - defaultCheckFunc := func(w want, gotValue int64, gotLoaded bool) error { - if !reflect.DeepEqual(gotValue, w.wantValue) { - return errors.Errorf("got: \"%#v\",\n\t\t\t\twant: \"%#v\"", gotValue, w.wantValue) - } - if !reflect.DeepEqual(gotLoaded, w.wantLoaded) { - return errors.Errorf("got: \"%#v\",\n\t\t\t\twant: \"%#v\"", gotLoaded, w.wantLoaded) - } - return nil - } - tests := []test{ - // TODO test cases - /* - { - name: "test_case_1", - args: args { - key: "", - }, - fields: fields { - read: nil, - dirty: nil, - misses: 0, - }, - want: want{}, - checkFunc: defaultCheckFunc, - }, - */ - - // TODO test cases - /* - func() test { - return test { - name: "test_case_2", - args: args { - key: "", - }, - fields: fields { - read: nil, - dirty: nil, - misses: 0, - }, - want: want{}, - checkFunc: defaultCheckFunc, - } - }(), - */ - } - - for _, tc := range tests { - test := tc - t.Run(test.name, func(tt *testing.T) { - tt.Parallel() - defer goleak.VerifyNone(tt, goleak.IgnoreCurrent()) - if test.beforeFunc != nil { - test.beforeFunc(test.args) - } - if test.afterFunc != nil { - defer test.afterFunc(test.args) - } - checkFunc := test.checkFunc - if test.checkFunc == nil { - checkFunc = defaultCheckFunc - } - m := &udim{ - read: test.fields.read, - dirty: test.fields.dirty, - misses: test.fields.misses, - } - - gotValue, gotLoaded := m.LoadAndDelete(test.args.key) - if err := checkFunc(test.want, gotValue, gotLoaded); err != nil { - tt.Errorf("error = %v", err) - } - }) - } -} - -func Test_udim_Delete(t *testing.T) { - type args struct { - key string - } - type fields struct { - read atomic.Value - dirty map[string]*entryUdim - misses int - } - type want struct{} - type test struct { - name string - args args - fields fields - want want - checkFunc func(want) error - beforeFunc func(args) - afterFunc func(args) - } - defaultCheckFunc := func(w want) error { - return nil - } - tests := []test{ - // TODO test cases - /* - { - name: "test_case_1", - args: args { - key: "", - }, - fields: fields { - read: nil, - dirty: nil, - misses: 0, - }, - want: want{}, - checkFunc: defaultCheckFunc, - }, - */ - - // TODO test cases - /* - func() test { - return test { - name: "test_case_2", - args: args { - key: "", - }, - fields: fields { - read: nil, - dirty: nil, - misses: 0, - }, - want: want{}, - checkFunc: defaultCheckFunc, - } - }(), - */ - } - - for _, tc := range tests { - test := tc - t.Run(test.name, func(tt *testing.T) { - tt.Parallel() - defer goleak.VerifyNone(tt, goleak.IgnoreCurrent()) - if test.beforeFunc != nil { - test.beforeFunc(test.args) - } - if test.afterFunc != nil { - defer test.afterFunc(test.args) - } - checkFunc := test.checkFunc - if test.checkFunc == nil { - checkFunc = defaultCheckFunc - } - m := &udim{ - read: test.fields.read, - dirty: test.fields.dirty, - misses: test.fields.misses, - } - - m.Delete(test.args.key) - if err := checkFunc(test.want); err != nil { - tt.Errorf("error = %v", err) - } - }) - } -} - -func Test_entryUdim_delete(t *testing.T) { - type fields struct { - p unsafe.Pointer - } - type want struct { - wantValue int64 - wantOk bool - } - type test struct { - name string - fields fields - want want - checkFunc func(want, int64, bool) error - beforeFunc func() - afterFunc func() - } - defaultCheckFunc := func(w want, gotValue int64, gotOk bool) error { - if !reflect.DeepEqual(gotValue, w.wantValue) { - return errors.Errorf("got: \"%#v\",\n\t\t\t\twant: \"%#v\"", gotValue, w.wantValue) - } - if !reflect.DeepEqual(gotOk, w.wantOk) { - return errors.Errorf("got: \"%#v\",\n\t\t\t\twant: \"%#v\"", gotOk, w.wantOk) - } - return nil - } - tests := []test{ - // TODO test cases - /* - { - name: "test_case_1", - fields: fields { - p: nil, - }, - want: want{}, - checkFunc: defaultCheckFunc, - }, - */ - - // TODO test cases - /* - func() test { - return test { - name: "test_case_2", - fields: fields { - p: nil, - }, - want: want{}, - checkFunc: defaultCheckFunc, - } - }(), - */ - } - - for _, tc := range tests { - test := tc - t.Run(test.name, func(tt *testing.T) { - tt.Parallel() - defer goleak.VerifyNone(tt, goleak.IgnoreCurrent()) - if test.beforeFunc != nil { - test.beforeFunc() - } - if test.afterFunc != nil { - defer test.afterFunc() - } - checkFunc := test.checkFunc - if test.checkFunc == nil { - checkFunc = defaultCheckFunc - } - e := &entryUdim{ - p: test.fields.p, - } - - gotValue, gotOk := e.delete() - if err := checkFunc(test.want, gotValue, gotOk); err != nil { - tt.Errorf("error = %v", err) - } - }) - } -} - -func Test_udim_Range(t *testing.T) { - type args struct { - f func(key string, value int64) bool - } - type fields struct { - read atomic.Value - dirty map[string]*entryUdim - misses int - } - type want struct{} - type test struct { - name string - args args - fields fields - want want - checkFunc func(want) error - beforeFunc func(args) - afterFunc func(args) - } - defaultCheckFunc := func(w want) error { - return nil - } - tests := []test{ - // TODO test cases - /* - { - name: "test_case_1", - args: args { - f: nil, - }, - fields: fields { - read: nil, - dirty: nil, - misses: 0, - }, - want: want{}, - checkFunc: defaultCheckFunc, - }, - */ - - // TODO test cases - /* - func() test { - return test { - name: "test_case_2", - args: args { - f: nil, - }, - fields: fields { - read: nil, - dirty: nil, - misses: 0, - }, - want: want{}, - checkFunc: defaultCheckFunc, - } - }(), - */ - } - - for _, tc := range tests { - test := tc - t.Run(test.name, func(tt *testing.T) { - tt.Parallel() - defer goleak.VerifyNone(tt, goleak.IgnoreCurrent()) - if test.beforeFunc != nil { - test.beforeFunc(test.args) - } - if test.afterFunc != nil { - defer test.afterFunc(test.args) - } - checkFunc := test.checkFunc - if test.checkFunc == nil { - checkFunc = defaultCheckFunc - } - m := &udim{ - read: test.fields.read, - dirty: test.fields.dirty, - misses: test.fields.misses, - } - - m.Range(test.args.f) - if err := checkFunc(test.want); err != nil { - tt.Errorf("error = %v", err) - } - }) - } -} - -func Test_udim_missLocked(t *testing.T) { - type fields struct { - read atomic.Value - dirty map[string]*entryUdim - misses int - } - type want struct{} - type test struct { - name string - fields fields - want want - checkFunc func(want) error - beforeFunc func() - afterFunc func() - } - defaultCheckFunc := func(w want) error { - return nil - } - tests := []test{ - // TODO test cases - /* - { - name: "test_case_1", - fields: fields { - read: nil, - dirty: nil, - misses: 0, - }, - want: want{}, - checkFunc: defaultCheckFunc, - }, - */ - - // TODO test cases - /* - func() test { - return test { - name: "test_case_2", - fields: fields { - read: nil, - dirty: nil, - misses: 0, - }, - want: want{}, - checkFunc: defaultCheckFunc, - } - }(), - */ - } - - for _, tc := range tests { - test := tc - t.Run(test.name, func(tt *testing.T) { - tt.Parallel() - defer goleak.VerifyNone(tt, goleak.IgnoreCurrent()) - if test.beforeFunc != nil { - test.beforeFunc() - } - if test.afterFunc != nil { - defer test.afterFunc() - } - checkFunc := test.checkFunc - if test.checkFunc == nil { - checkFunc = defaultCheckFunc - } - m := &udim{ - read: test.fields.read, - dirty: test.fields.dirty, - misses: test.fields.misses, - } - - m.missLocked() - if err := checkFunc(test.want); err != nil { - tt.Errorf("error = %v", err) - } - }) - } -} - -func Test_udim_dirtyLocked(t *testing.T) { - type fields struct { - read atomic.Value - dirty map[string]*entryUdim - misses int - } - type want struct{} - type test struct { - name string - fields fields - want want - checkFunc func(want) error - beforeFunc func() - afterFunc func() - } - defaultCheckFunc := func(w want) error { - return nil - } - tests := []test{ - // TODO test cases - /* - { - name: "test_case_1", - fields: fields { - read: nil, - dirty: nil, - misses: 0, - }, - want: want{}, - checkFunc: defaultCheckFunc, - }, - */ - - // TODO test cases - /* - func() test { - return test { - name: "test_case_2", - fields: fields { - read: nil, - dirty: nil, - misses: 0, - }, - want: want{}, - checkFunc: defaultCheckFunc, - } - }(), - */ - } - - for _, tc := range tests { - test := tc - t.Run(test.name, func(tt *testing.T) { - tt.Parallel() - defer goleak.VerifyNone(tt, goleak.IgnoreCurrent()) - if test.beforeFunc != nil { - test.beforeFunc() - } - if test.afterFunc != nil { - defer test.afterFunc() - } - checkFunc := test.checkFunc - if test.checkFunc == nil { - checkFunc = defaultCheckFunc - } - m := &udim{ - read: test.fields.read, - dirty: test.fields.dirty, - misses: test.fields.misses, - } - - m.dirtyLocked() - if err := checkFunc(test.want); err != nil { - tt.Errorf("error = %v", err) - } - }) - } -} - -func Test_entryUdim_tryExpungeLocked(t *testing.T) { - type fields struct { - p unsafe.Pointer - } - type want struct { - wantIsExpunged bool - } - type test struct { - name string - fields fields - want want - checkFunc func(want, bool) error - beforeFunc func() - afterFunc func() - } - defaultCheckFunc := func(w want, gotIsExpunged bool) error { - if !reflect.DeepEqual(gotIsExpunged, w.wantIsExpunged) { - return errors.Errorf("got: \"%#v\",\n\t\t\t\twant: \"%#v\"", gotIsExpunged, w.wantIsExpunged) - } - return nil - } - tests := []test{ - // TODO test cases - /* - { - name: "test_case_1", - fields: fields { - p: nil, - }, - want: want{}, - checkFunc: defaultCheckFunc, - }, - */ - - // TODO test cases - /* - func() test { - return test { - name: "test_case_2", - fields: fields { - p: nil, - }, - want: want{}, - checkFunc: defaultCheckFunc, - } - }(), - */ - } - - for _, tc := range tests { - test := tc - t.Run(test.name, func(tt *testing.T) { - tt.Parallel() - defer goleak.VerifyNone(tt, goleak.IgnoreCurrent()) - if test.beforeFunc != nil { - test.beforeFunc() - } - if test.afterFunc != nil { - defer test.afterFunc() - } - checkFunc := test.checkFunc - if test.checkFunc == nil { - checkFunc = defaultCheckFunc - } - e := &entryUdim{ - p: test.fields.p, - } - - gotIsExpunged := e.tryExpungeLocked() - if err := checkFunc(test.want, gotIsExpunged); err != nil { - tt.Errorf("error = %v", err) - } - }) - } -} diff --git a/pkg/agent/core/ngt/service/vqueue/uninserted_index_map.go b/pkg/agent/core/ngt/service/vqueue/uninserted_index_map.go deleted file mode 100644 index a0a6a80f85d..00000000000 --- a/pkg/agent/core/ngt/service/vqueue/uninserted_index_map.go +++ /dev/null @@ -1,280 +0,0 @@ -// Copyright (C) 2019-2022 vdaas.org vald team -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -package vqueue - -import ( - "sync" - "sync/atomic" - "unsafe" -) - -type uiim struct { - mu sync.Mutex - - read atomic.Value - - dirty map[string]*entryUiim - - misses int -} - -type readOnlyUiim struct { - m map[string]*entryUiim - amended bool -} - -// skipcq: GSC-G103 -var expungedUiim = unsafe.Pointer(new(index)) - -type entryUiim struct { - p unsafe.Pointer -} - -func newEntryUiim(i index) *entryUiim { - // skipcq: GSC-G103 - return &entryUiim{p: unsafe.Pointer(&i)} -} - -func (m *uiim) Load(key string) (value index, ok bool) { - read, _ := m.read.Load().(readOnlyUiim) - e, ok := read.m[key] - if !ok && read.amended { - m.mu.Lock() - - read, _ = m.read.Load().(readOnlyUiim) - e, ok = read.m[key] - if !ok && read.amended { - e, ok = m.dirty[key] - - m.missLocked() - } - m.mu.Unlock() - } - if !ok { - return value, false - } - return e.load() -} - -func (e *entryUiim) load() (value index, ok bool) { - p := atomic.LoadPointer(&e.p) - if p == nil || p == expungedUiim { - return value, false - } - return *(*index)(p), true -} - -func (m *uiim) Store(key string, value index) { - read, _ := m.read.Load().(readOnlyUiim) - if e, ok := read.m[key]; ok && e.tryStore(&value) { - return - } - - m.mu.Lock() - read, _ = m.read.Load().(readOnlyUiim) - if e, ok := read.m[key]; ok { - if e.unexpungeLocked() { - m.dirty[key] = e - } - e.storeLocked(&value) - } else if e, ok := m.dirty[key]; ok { - e.storeLocked(&value) - } else { - if !read.amended { - - m.dirtyLocked() - m.read.Store(readOnlyUiim{m: read.m, amended: true}) - } - m.dirty[key] = newEntryUiim(value) - } - m.mu.Unlock() -} - -func (e *entryUiim) tryStore(i *index) bool { - for { - p := atomic.LoadPointer(&e.p) - if p == expungedUiim { - return false - } - // skipcq: GSC-G103 - if atomic.CompareAndSwapPointer(&e.p, p, unsafe.Pointer(i)) { - return true - } - } -} - -func (e *entryUiim) unexpungeLocked() (wasExpunged bool) { - return atomic.CompareAndSwapPointer(&e.p, expungedUiim, nil) -} - -func (e *entryUiim) storeLocked(i *index) { - // skipcq: GSC-G103 - atomic.StorePointer(&e.p, unsafe.Pointer(i)) -} - -func (m *uiim) LoadOrStore(key string, value index) (actual index, loaded bool) { - read, _ := m.read.Load().(readOnlyUiim) - if e, ok := read.m[key]; ok { - actual, loaded, ok := e.tryLoadOrStore(value) - if ok { - return actual, loaded - } - } - - m.mu.Lock() - read, _ = m.read.Load().(readOnlyUiim) - if e, ok := read.m[key]; ok { - if e.unexpungeLocked() { - m.dirty[key] = e - } - actual, loaded, _ = e.tryLoadOrStore(value) - } else if e, ok := m.dirty[key]; ok { - actual, loaded, _ = e.tryLoadOrStore(value) - m.missLocked() - } else { - if !read.amended { - - m.dirtyLocked() - m.read.Store(readOnlyUiim{m: read.m, amended: true}) - } - m.dirty[key] = newEntryUiim(value) - actual, loaded = value, false - } - m.mu.Unlock() - - return actual, loaded -} - -func (e *entryUiim) tryLoadOrStore(i index) (actual index, loaded, ok bool) { - p := atomic.LoadPointer(&e.p) - if p == expungedUiim { - return actual, false, false - } - if p != nil { - return *(*index)(p), true, true - } - - ic := i - for { - // skipcq: GSC-G103 - if atomic.CompareAndSwapPointer(&e.p, nil, unsafe.Pointer(&ic)) { - return i, false, true - } - p = atomic.LoadPointer(&e.p) - if p == expungedUiim { - return actual, false, false - } - if p != nil { - return *(*index)(p), true, true - } - } -} - -func (m *uiim) LoadAndDelete(key string) (value index, loaded bool) { - read, _ := m.read.Load().(readOnlyUiim) - e, ok := read.m[key] - if !ok && read.amended { - m.mu.Lock() - read, _ = m.read.Load().(readOnlyUiim) - e, ok = read.m[key] - if !ok && read.amended { - e, ok = m.dirty[key] - delete(m.dirty, key) - - m.missLocked() - } - m.mu.Unlock() - } - if ok { - return e.delete() - } - return value, false -} - -func (m *uiim) Delete(key string) { - m.LoadAndDelete(key) -} - -func (e *entryUiim) delete() (value index, ok bool) { - for { - p := atomic.LoadPointer(&e.p) - if p == nil || p == expungedUiim { - return value, false - } - if atomic.CompareAndSwapPointer(&e.p, p, nil) { - return *(*index)(p), true - } - } -} - -func (m *uiim) Range(f func(key string, value index) bool) { - read, _ := m.read.Load().(readOnlyUiim) - if read.amended { - - m.mu.Lock() - read, _ = m.read.Load().(readOnlyUiim) - if read.amended { - read = readOnlyUiim{m: m.dirty} - m.read.Store(read) - m.dirty = nil - m.misses = 0 - } - m.mu.Unlock() - } - - for k, e := range read.m { - v, ok := e.load() - if !ok { - continue - } - if !f(k, v) { - break - } - } -} - -func (m *uiim) missLocked() { - m.misses++ - if m.misses < len(m.dirty) { - return - } - m.read.Store(readOnlyUiim{m: m.dirty}) - m.dirty = nil - m.misses = 0 -} - -func (m *uiim) dirtyLocked() { - if m.dirty != nil { - return - } - - read, _ := m.read.Load().(readOnlyUiim) - m.dirty = make(map[string]*entryUiim, len(read.m)) - for k, e := range read.m { - if !e.tryExpungeLocked() { - m.dirty[k] = e - } - } -} - -func (e *entryUiim) tryExpungeLocked() (isExpunged bool) { - p := atomic.LoadPointer(&e.p) - for p == nil { - if atomic.CompareAndSwapPointer(&e.p, nil, expungedUiim) { - return true - } - p = atomic.LoadPointer(&e.p) - } - return p == expungedUiim -} diff --git a/pkg/agent/core/ngt/service/vqueue/uninserted_index_map_test.go b/pkg/agent/core/ngt/service/vqueue/uninserted_index_map_test.go deleted file mode 100644 index b38df48df27..00000000000 --- a/pkg/agent/core/ngt/service/vqueue/uninserted_index_map_test.go +++ /dev/null @@ -1,1395 +0,0 @@ -// Copyright (C) 2019-2022 vdaas.org vald team -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -package vqueue - -import ( - "reflect" - "sync/atomic" - "testing" - "unsafe" - - "github.com/vdaas/vald/internal/errors" - "github.com/vdaas/vald/internal/test/goleak" -) - -func Test_newEntryUiim(t *testing.T) { - type args struct { - i index - } - type want struct { - want *entryUiim - } - type test struct { - name string - args args - want want - checkFunc func(want, *entryUiim) error - beforeFunc func(args) - afterFunc func(args) - } - defaultCheckFunc := func(w want, got *entryUiim) error { - if !reflect.DeepEqual(got, w.want) { - return errors.Errorf("got: \"%#v\",\n\t\t\t\twant: \"%#v\"", got, w.want) - } - return nil - } - tests := []test{ - // TODO test cases - /* - { - name: "test_case_1", - args: args { - i: index{}, - }, - want: want{}, - checkFunc: defaultCheckFunc, - }, - */ - - // TODO test cases - /* - func() test { - return test { - name: "test_case_2", - args: args { - i: index{}, - }, - want: want{}, - checkFunc: defaultCheckFunc, - } - }(), - */ - } - - for _, tc := range tests { - test := tc - t.Run(test.name, func(tt *testing.T) { - tt.Parallel() - defer goleak.VerifyNone(tt, goleak.IgnoreCurrent()) - if test.beforeFunc != nil { - test.beforeFunc(test.args) - } - if test.afterFunc != nil { - defer test.afterFunc(test.args) - } - checkFunc := test.checkFunc - if test.checkFunc == nil { - checkFunc = defaultCheckFunc - } - - got := newEntryUiim(test.args.i) - if err := checkFunc(test.want, got); err != nil { - tt.Errorf("error = %v", err) - } - }) - } -} - -func Test_uiim_Load(t *testing.T) { - type args struct { - key string - } - type fields struct { - read atomic.Value - dirty map[string]*entryUiim - misses int - } - type want struct { - wantValue index - wantOk bool - } - type test struct { - name string - args args - fields fields - want want - checkFunc func(want, index, bool) error - beforeFunc func(args) - afterFunc func(args) - } - defaultCheckFunc := func(w want, gotValue index, gotOk bool) error { - if !reflect.DeepEqual(gotValue, w.wantValue) { - return errors.Errorf("got: \"%#v\",\n\t\t\t\twant: \"%#v\"", gotValue, w.wantValue) - } - if !reflect.DeepEqual(gotOk, w.wantOk) { - return errors.Errorf("got: \"%#v\",\n\t\t\t\twant: \"%#v\"", gotOk, w.wantOk) - } - return nil - } - tests := []test{ - // TODO test cases - /* - { - name: "test_case_1", - args: args { - key: "", - }, - fields: fields { - read: nil, - dirty: nil, - misses: 0, - }, - want: want{}, - checkFunc: defaultCheckFunc, - }, - */ - - // TODO test cases - /* - func() test { - return test { - name: "test_case_2", - args: args { - key: "", - }, - fields: fields { - read: nil, - dirty: nil, - misses: 0, - }, - want: want{}, - checkFunc: defaultCheckFunc, - } - }(), - */ - } - - for _, tc := range tests { - test := tc - t.Run(test.name, func(tt *testing.T) { - tt.Parallel() - defer goleak.VerifyNone(tt, goleak.IgnoreCurrent()) - if test.beforeFunc != nil { - test.beforeFunc(test.args) - } - if test.afterFunc != nil { - defer test.afterFunc(test.args) - } - checkFunc := test.checkFunc - if test.checkFunc == nil { - checkFunc = defaultCheckFunc - } - m := &uiim{ - read: test.fields.read, - dirty: test.fields.dirty, - misses: test.fields.misses, - } - - gotValue, gotOk := m.Load(test.args.key) - if err := checkFunc(test.want, gotValue, gotOk); err != nil { - tt.Errorf("error = %v", err) - } - }) - } -} - -func Test_entryUiim_load(t *testing.T) { - type fields struct { - p unsafe.Pointer - } - type want struct { - wantValue index - wantOk bool - } - type test struct { - name string - fields fields - want want - checkFunc func(want, index, bool) error - beforeFunc func() - afterFunc func() - } - defaultCheckFunc := func(w want, gotValue index, gotOk bool) error { - if !reflect.DeepEqual(gotValue, w.wantValue) { - return errors.Errorf("got: \"%#v\",\n\t\t\t\twant: \"%#v\"", gotValue, w.wantValue) - } - if !reflect.DeepEqual(gotOk, w.wantOk) { - return errors.Errorf("got: \"%#v\",\n\t\t\t\twant: \"%#v\"", gotOk, w.wantOk) - } - return nil - } - tests := []test{ - // TODO test cases - /* - { - name: "test_case_1", - fields: fields { - p: nil, - }, - want: want{}, - checkFunc: defaultCheckFunc, - }, - */ - - // TODO test cases - /* - func() test { - return test { - name: "test_case_2", - fields: fields { - p: nil, - }, - want: want{}, - checkFunc: defaultCheckFunc, - } - }(), - */ - } - - for _, tc := range tests { - test := tc - t.Run(test.name, func(tt *testing.T) { - tt.Parallel() - defer goleak.VerifyNone(tt, goleak.IgnoreCurrent()) - if test.beforeFunc != nil { - test.beforeFunc() - } - if test.afterFunc != nil { - defer test.afterFunc() - } - checkFunc := test.checkFunc - if test.checkFunc == nil { - checkFunc = defaultCheckFunc - } - e := &entryUiim{ - p: test.fields.p, - } - - gotValue, gotOk := e.load() - if err := checkFunc(test.want, gotValue, gotOk); err != nil { - tt.Errorf("error = %v", err) - } - }) - } -} - -func Test_uiim_Store(t *testing.T) { - type args struct { - key string - value index - } - type fields struct { - read atomic.Value - dirty map[string]*entryUiim - misses int - } - type want struct{} - type test struct { - name string - args args - fields fields - want want - checkFunc func(want) error - beforeFunc func(args) - afterFunc func(args) - } - defaultCheckFunc := func(w want) error { - return nil - } - tests := []test{ - // TODO test cases - /* - { - name: "test_case_1", - args: args { - key: "", - value: index{}, - }, - fields: fields { - read: nil, - dirty: nil, - misses: 0, - }, - want: want{}, - checkFunc: defaultCheckFunc, - }, - */ - - // TODO test cases - /* - func() test { - return test { - name: "test_case_2", - args: args { - key: "", - value: index{}, - }, - fields: fields { - read: nil, - dirty: nil, - misses: 0, - }, - want: want{}, - checkFunc: defaultCheckFunc, - } - }(), - */ - } - - for _, tc := range tests { - test := tc - t.Run(test.name, func(tt *testing.T) { - tt.Parallel() - defer goleak.VerifyNone(tt, goleak.IgnoreCurrent()) - if test.beforeFunc != nil { - test.beforeFunc(test.args) - } - if test.afterFunc != nil { - defer test.afterFunc(test.args) - } - checkFunc := test.checkFunc - if test.checkFunc == nil { - checkFunc = defaultCheckFunc - } - m := &uiim{ - read: test.fields.read, - dirty: test.fields.dirty, - misses: test.fields.misses, - } - - m.Store(test.args.key, test.args.value) - if err := checkFunc(test.want); err != nil { - tt.Errorf("error = %v", err) - } - }) - } -} - -func Test_entryUiim_tryStore(t *testing.T) { - type args struct { - i *index - } - type fields struct { - p unsafe.Pointer - } - type want struct { - want bool - } - type test struct { - name string - args args - fields fields - want want - checkFunc func(want, bool) error - beforeFunc func(args) - afterFunc func(args) - } - defaultCheckFunc := func(w want, got bool) error { - if !reflect.DeepEqual(got, w.want) { - return errors.Errorf("got: \"%#v\",\n\t\t\t\twant: \"%#v\"", got, w.want) - } - return nil - } - tests := []test{ - // TODO test cases - /* - { - name: "test_case_1", - args: args { - i: index{}, - }, - fields: fields { - p: nil, - }, - want: want{}, - checkFunc: defaultCheckFunc, - }, - */ - - // TODO test cases - /* - func() test { - return test { - name: "test_case_2", - args: args { - i: index{}, - }, - fields: fields { - p: nil, - }, - want: want{}, - checkFunc: defaultCheckFunc, - } - }(), - */ - } - - for _, tc := range tests { - test := tc - t.Run(test.name, func(tt *testing.T) { - tt.Parallel() - defer goleak.VerifyNone(tt, goleak.IgnoreCurrent()) - if test.beforeFunc != nil { - test.beforeFunc(test.args) - } - if test.afterFunc != nil { - defer test.afterFunc(test.args) - } - checkFunc := test.checkFunc - if test.checkFunc == nil { - checkFunc = defaultCheckFunc - } - e := &entryUiim{ - p: test.fields.p, - } - - got := e.tryStore(test.args.i) - if err := checkFunc(test.want, got); err != nil { - tt.Errorf("error = %v", err) - } - }) - } -} - -func Test_entryUiim_unexpungeLocked(t *testing.T) { - type fields struct { - p unsafe.Pointer - } - type want struct { - wantWasExpunged bool - } - type test struct { - name string - fields fields - want want - checkFunc func(want, bool) error - beforeFunc func() - afterFunc func() - } - defaultCheckFunc := func(w want, gotWasExpunged bool) error { - if !reflect.DeepEqual(gotWasExpunged, w.wantWasExpunged) { - return errors.Errorf("got: \"%#v\",\n\t\t\t\twant: \"%#v\"", gotWasExpunged, w.wantWasExpunged) - } - return nil - } - tests := []test{ - // TODO test cases - /* - { - name: "test_case_1", - fields: fields { - p: nil, - }, - want: want{}, - checkFunc: defaultCheckFunc, - }, - */ - - // TODO test cases - /* - func() test { - return test { - name: "test_case_2", - fields: fields { - p: nil, - }, - want: want{}, - checkFunc: defaultCheckFunc, - } - }(), - */ - } - - for _, tc := range tests { - test := tc - t.Run(test.name, func(tt *testing.T) { - tt.Parallel() - defer goleak.VerifyNone(tt, goleak.IgnoreCurrent()) - if test.beforeFunc != nil { - test.beforeFunc() - } - if test.afterFunc != nil { - defer test.afterFunc() - } - checkFunc := test.checkFunc - if test.checkFunc == nil { - checkFunc = defaultCheckFunc - } - e := &entryUiim{ - p: test.fields.p, - } - - gotWasExpunged := e.unexpungeLocked() - if err := checkFunc(test.want, gotWasExpunged); err != nil { - tt.Errorf("error = %v", err) - } - }) - } -} - -func Test_entryUiim_storeLocked(t *testing.T) { - type args struct { - i *index - } - type fields struct { - p unsafe.Pointer - } - type want struct{} - type test struct { - name string - args args - fields fields - want want - checkFunc func(want) error - beforeFunc func(args) - afterFunc func(args) - } - defaultCheckFunc := func(w want) error { - return nil - } - tests := []test{ - // TODO test cases - /* - { - name: "test_case_1", - args: args { - i: index{}, - }, - fields: fields { - p: nil, - }, - want: want{}, - checkFunc: defaultCheckFunc, - }, - */ - - // TODO test cases - /* - func() test { - return test { - name: "test_case_2", - args: args { - i: index{}, - }, - fields: fields { - p: nil, - }, - want: want{}, - checkFunc: defaultCheckFunc, - } - }(), - */ - } - - for _, tc := range tests { - test := tc - t.Run(test.name, func(tt *testing.T) { - tt.Parallel() - defer goleak.VerifyNone(tt, goleak.IgnoreCurrent()) - if test.beforeFunc != nil { - test.beforeFunc(test.args) - } - if test.afterFunc != nil { - defer test.afterFunc(test.args) - } - checkFunc := test.checkFunc - if test.checkFunc == nil { - checkFunc = defaultCheckFunc - } - e := &entryUiim{ - p: test.fields.p, - } - - e.storeLocked(test.args.i) - if err := checkFunc(test.want); err != nil { - tt.Errorf("error = %v", err) - } - }) - } -} - -func Test_uiim_LoadOrStore(t *testing.T) { - type args struct { - key string - value index - } - type fields struct { - read atomic.Value - dirty map[string]*entryUiim - misses int - } - type want struct { - wantActual index - wantLoaded bool - } - type test struct { - name string - args args - fields fields - want want - checkFunc func(want, index, bool) error - beforeFunc func(args) - afterFunc func(args) - } - defaultCheckFunc := func(w want, gotActual index, gotLoaded bool) error { - if !reflect.DeepEqual(gotActual, w.wantActual) { - return errors.Errorf("got: \"%#v\",\n\t\t\t\twant: \"%#v\"", gotActual, w.wantActual) - } - if !reflect.DeepEqual(gotLoaded, w.wantLoaded) { - return errors.Errorf("got: \"%#v\",\n\t\t\t\twant: \"%#v\"", gotLoaded, w.wantLoaded) - } - return nil - } - tests := []test{ - // TODO test cases - /* - { - name: "test_case_1", - args: args { - key: "", - value: index{}, - }, - fields: fields { - read: nil, - dirty: nil, - misses: 0, - }, - want: want{}, - checkFunc: defaultCheckFunc, - }, - */ - - // TODO test cases - /* - func() test { - return test { - name: "test_case_2", - args: args { - key: "", - value: index{}, - }, - fields: fields { - read: nil, - dirty: nil, - misses: 0, - }, - want: want{}, - checkFunc: defaultCheckFunc, - } - }(), - */ - } - - for _, tc := range tests { - test := tc - t.Run(test.name, func(tt *testing.T) { - tt.Parallel() - defer goleak.VerifyNone(tt, goleak.IgnoreCurrent()) - if test.beforeFunc != nil { - test.beforeFunc(test.args) - } - if test.afterFunc != nil { - defer test.afterFunc(test.args) - } - checkFunc := test.checkFunc - if test.checkFunc == nil { - checkFunc = defaultCheckFunc - } - m := &uiim{ - read: test.fields.read, - dirty: test.fields.dirty, - misses: test.fields.misses, - } - - gotActual, gotLoaded := m.LoadOrStore(test.args.key, test.args.value) - if err := checkFunc(test.want, gotActual, gotLoaded); err != nil { - tt.Errorf("error = %v", err) - } - }) - } -} - -func Test_entryUiim_tryLoadOrStore(t *testing.T) { - type args struct { - i index - } - type fields struct { - p unsafe.Pointer - } - type want struct { - wantActual index - wantLoaded bool - wantOk bool - } - type test struct { - name string - args args - fields fields - want want - checkFunc func(want, index, bool, bool) error - beforeFunc func(args) - afterFunc func(args) - } - defaultCheckFunc := func(w want, gotActual index, gotLoaded bool, gotOk bool) error { - if !reflect.DeepEqual(gotActual, w.wantActual) { - return errors.Errorf("got: \"%#v\",\n\t\t\t\twant: \"%#v\"", gotActual, w.wantActual) - } - if !reflect.DeepEqual(gotLoaded, w.wantLoaded) { - return errors.Errorf("got: \"%#v\",\n\t\t\t\twant: \"%#v\"", gotLoaded, w.wantLoaded) - } - if !reflect.DeepEqual(gotOk, w.wantOk) { - return errors.Errorf("got: \"%#v\",\n\t\t\t\twant: \"%#v\"", gotOk, w.wantOk) - } - return nil - } - tests := []test{ - // TODO test cases - /* - { - name: "test_case_1", - args: args { - i: index{}, - }, - fields: fields { - p: nil, - }, - want: want{}, - checkFunc: defaultCheckFunc, - }, - */ - - // TODO test cases - /* - func() test { - return test { - name: "test_case_2", - args: args { - i: index{}, - }, - fields: fields { - p: nil, - }, - want: want{}, - checkFunc: defaultCheckFunc, - } - }(), - */ - } - - for _, tc := range tests { - test := tc - t.Run(test.name, func(tt *testing.T) { - tt.Parallel() - defer goleak.VerifyNone(tt, goleak.IgnoreCurrent()) - if test.beforeFunc != nil { - test.beforeFunc(test.args) - } - if test.afterFunc != nil { - defer test.afterFunc(test.args) - } - checkFunc := test.checkFunc - if test.checkFunc == nil { - checkFunc = defaultCheckFunc - } - e := &entryUiim{ - p: test.fields.p, - } - - gotActual, gotLoaded, gotOk := e.tryLoadOrStore(test.args.i) - if err := checkFunc(test.want, gotActual, gotLoaded, gotOk); err != nil { - tt.Errorf("error = %v", err) - } - }) - } -} - -func Test_uiim_LoadAndDelete(t *testing.T) { - type args struct { - key string - } - type fields struct { - read atomic.Value - dirty map[string]*entryUiim - misses int - } - type want struct { - wantValue index - wantLoaded bool - } - type test struct { - name string - args args - fields fields - want want - checkFunc func(want, index, bool) error - beforeFunc func(args) - afterFunc func(args) - } - defaultCheckFunc := func(w want, gotValue index, gotLoaded bool) error { - if !reflect.DeepEqual(gotValue, w.wantValue) { - return errors.Errorf("got: \"%#v\",\n\t\t\t\twant: \"%#v\"", gotValue, w.wantValue) - } - if !reflect.DeepEqual(gotLoaded, w.wantLoaded) { - return errors.Errorf("got: \"%#v\",\n\t\t\t\twant: \"%#v\"", gotLoaded, w.wantLoaded) - } - return nil - } - tests := []test{ - // TODO test cases - /* - { - name: "test_case_1", - args: args { - key: "", - }, - fields: fields { - read: nil, - dirty: nil, - misses: 0, - }, - want: want{}, - checkFunc: defaultCheckFunc, - }, - */ - - // TODO test cases - /* - func() test { - return test { - name: "test_case_2", - args: args { - key: "", - }, - fields: fields { - read: nil, - dirty: nil, - misses: 0, - }, - want: want{}, - checkFunc: defaultCheckFunc, - } - }(), - */ - } - - for _, tc := range tests { - test := tc - t.Run(test.name, func(tt *testing.T) { - tt.Parallel() - defer goleak.VerifyNone(tt, goleak.IgnoreCurrent()) - if test.beforeFunc != nil { - test.beforeFunc(test.args) - } - if test.afterFunc != nil { - defer test.afterFunc(test.args) - } - checkFunc := test.checkFunc - if test.checkFunc == nil { - checkFunc = defaultCheckFunc - } - m := &uiim{ - read: test.fields.read, - dirty: test.fields.dirty, - misses: test.fields.misses, - } - - gotValue, gotLoaded := m.LoadAndDelete(test.args.key) - if err := checkFunc(test.want, gotValue, gotLoaded); err != nil { - tt.Errorf("error = %v", err) - } - }) - } -} - -func Test_uiim_Delete(t *testing.T) { - type args struct { - key string - } - type fields struct { - read atomic.Value - dirty map[string]*entryUiim - misses int - } - type want struct{} - type test struct { - name string - args args - fields fields - want want - checkFunc func(want) error - beforeFunc func(args) - afterFunc func(args) - } - defaultCheckFunc := func(w want) error { - return nil - } - tests := []test{ - // TODO test cases - /* - { - name: "test_case_1", - args: args { - key: "", - }, - fields: fields { - read: nil, - dirty: nil, - misses: 0, - }, - want: want{}, - checkFunc: defaultCheckFunc, - }, - */ - - // TODO test cases - /* - func() test { - return test { - name: "test_case_2", - args: args { - key: "", - }, - fields: fields { - read: nil, - dirty: nil, - misses: 0, - }, - want: want{}, - checkFunc: defaultCheckFunc, - } - }(), - */ - } - - for _, tc := range tests { - test := tc - t.Run(test.name, func(tt *testing.T) { - tt.Parallel() - defer goleak.VerifyNone(tt, goleak.IgnoreCurrent()) - if test.beforeFunc != nil { - test.beforeFunc(test.args) - } - if test.afterFunc != nil { - defer test.afterFunc(test.args) - } - checkFunc := test.checkFunc - if test.checkFunc == nil { - checkFunc = defaultCheckFunc - } - m := &uiim{ - read: test.fields.read, - dirty: test.fields.dirty, - misses: test.fields.misses, - } - - m.Delete(test.args.key) - if err := checkFunc(test.want); err != nil { - tt.Errorf("error = %v", err) - } - }) - } -} - -func Test_entryUiim_delete(t *testing.T) { - type fields struct { - p unsafe.Pointer - } - type want struct { - wantValue index - wantOk bool - } - type test struct { - name string - fields fields - want want - checkFunc func(want, index, bool) error - beforeFunc func() - afterFunc func() - } - defaultCheckFunc := func(w want, gotValue index, gotOk bool) error { - if !reflect.DeepEqual(gotValue, w.wantValue) { - return errors.Errorf("got: \"%#v\",\n\t\t\t\twant: \"%#v\"", gotValue, w.wantValue) - } - if !reflect.DeepEqual(gotOk, w.wantOk) { - return errors.Errorf("got: \"%#v\",\n\t\t\t\twant: \"%#v\"", gotOk, w.wantOk) - } - return nil - } - tests := []test{ - // TODO test cases - /* - { - name: "test_case_1", - fields: fields { - p: nil, - }, - want: want{}, - checkFunc: defaultCheckFunc, - }, - */ - - // TODO test cases - /* - func() test { - return test { - name: "test_case_2", - fields: fields { - p: nil, - }, - want: want{}, - checkFunc: defaultCheckFunc, - } - }(), - */ - } - - for _, tc := range tests { - test := tc - t.Run(test.name, func(tt *testing.T) { - tt.Parallel() - defer goleak.VerifyNone(tt, goleak.IgnoreCurrent()) - if test.beforeFunc != nil { - test.beforeFunc() - } - if test.afterFunc != nil { - defer test.afterFunc() - } - checkFunc := test.checkFunc - if test.checkFunc == nil { - checkFunc = defaultCheckFunc - } - e := &entryUiim{ - p: test.fields.p, - } - - gotValue, gotOk := e.delete() - if err := checkFunc(test.want, gotValue, gotOk); err != nil { - tt.Errorf("error = %v", err) - } - }) - } -} - -func Test_uiim_Range(t *testing.T) { - type args struct { - f func(key string, value index) bool - } - type fields struct { - read atomic.Value - dirty map[string]*entryUiim - misses int - } - type want struct{} - type test struct { - name string - args args - fields fields - want want - checkFunc func(want) error - beforeFunc func(args) - afterFunc func(args) - } - defaultCheckFunc := func(w want) error { - return nil - } - tests := []test{ - // TODO test cases - /* - { - name: "test_case_1", - args: args { - f: nil, - }, - fields: fields { - read: nil, - dirty: nil, - misses: 0, - }, - want: want{}, - checkFunc: defaultCheckFunc, - }, - */ - - // TODO test cases - /* - func() test { - return test { - name: "test_case_2", - args: args { - f: nil, - }, - fields: fields { - read: nil, - dirty: nil, - misses: 0, - }, - want: want{}, - checkFunc: defaultCheckFunc, - } - }(), - */ - } - - for _, tc := range tests { - test := tc - t.Run(test.name, func(tt *testing.T) { - tt.Parallel() - defer goleak.VerifyNone(tt, goleak.IgnoreCurrent()) - if test.beforeFunc != nil { - test.beforeFunc(test.args) - } - if test.afterFunc != nil { - defer test.afterFunc(test.args) - } - checkFunc := test.checkFunc - if test.checkFunc == nil { - checkFunc = defaultCheckFunc - } - m := &uiim{ - read: test.fields.read, - dirty: test.fields.dirty, - misses: test.fields.misses, - } - - m.Range(test.args.f) - if err := checkFunc(test.want); err != nil { - tt.Errorf("error = %v", err) - } - }) - } -} - -func Test_uiim_missLocked(t *testing.T) { - type fields struct { - read atomic.Value - dirty map[string]*entryUiim - misses int - } - type want struct{} - type test struct { - name string - fields fields - want want - checkFunc func(want) error - beforeFunc func() - afterFunc func() - } - defaultCheckFunc := func(w want) error { - return nil - } - tests := []test{ - // TODO test cases - /* - { - name: "test_case_1", - fields: fields { - read: nil, - dirty: nil, - misses: 0, - }, - want: want{}, - checkFunc: defaultCheckFunc, - }, - */ - - // TODO test cases - /* - func() test { - return test { - name: "test_case_2", - fields: fields { - read: nil, - dirty: nil, - misses: 0, - }, - want: want{}, - checkFunc: defaultCheckFunc, - } - }(), - */ - } - - for _, tc := range tests { - test := tc - t.Run(test.name, func(tt *testing.T) { - tt.Parallel() - defer goleak.VerifyNone(tt, goleak.IgnoreCurrent()) - if test.beforeFunc != nil { - test.beforeFunc() - } - if test.afterFunc != nil { - defer test.afterFunc() - } - checkFunc := test.checkFunc - if test.checkFunc == nil { - checkFunc = defaultCheckFunc - } - m := &uiim{ - read: test.fields.read, - dirty: test.fields.dirty, - misses: test.fields.misses, - } - - m.missLocked() - if err := checkFunc(test.want); err != nil { - tt.Errorf("error = %v", err) - } - }) - } -} - -func Test_uiim_dirtyLocked(t *testing.T) { - type fields struct { - read atomic.Value - dirty map[string]*entryUiim - misses int - } - type want struct{} - type test struct { - name string - fields fields - want want - checkFunc func(want) error - beforeFunc func() - afterFunc func() - } - defaultCheckFunc := func(w want) error { - return nil - } - tests := []test{ - // TODO test cases - /* - { - name: "test_case_1", - fields: fields { - read: nil, - dirty: nil, - misses: 0, - }, - want: want{}, - checkFunc: defaultCheckFunc, - }, - */ - - // TODO test cases - /* - func() test { - return test { - name: "test_case_2", - fields: fields { - read: nil, - dirty: nil, - misses: 0, - }, - want: want{}, - checkFunc: defaultCheckFunc, - } - }(), - */ - } - - for _, tc := range tests { - test := tc - t.Run(test.name, func(tt *testing.T) { - tt.Parallel() - defer goleak.VerifyNone(tt, goleak.IgnoreCurrent()) - if test.beforeFunc != nil { - test.beforeFunc() - } - if test.afterFunc != nil { - defer test.afterFunc() - } - checkFunc := test.checkFunc - if test.checkFunc == nil { - checkFunc = defaultCheckFunc - } - m := &uiim{ - read: test.fields.read, - dirty: test.fields.dirty, - misses: test.fields.misses, - } - - m.dirtyLocked() - if err := checkFunc(test.want); err != nil { - tt.Errorf("error = %v", err) - } - }) - } -} - -func Test_entryUiim_tryExpungeLocked(t *testing.T) { - type fields struct { - p unsafe.Pointer - } - type want struct { - wantIsExpunged bool - } - type test struct { - name string - fields fields - want want - checkFunc func(want, bool) error - beforeFunc func() - afterFunc func() - } - defaultCheckFunc := func(w want, gotIsExpunged bool) error { - if !reflect.DeepEqual(gotIsExpunged, w.wantIsExpunged) { - return errors.Errorf("got: \"%#v\",\n\t\t\t\twant: \"%#v\"", gotIsExpunged, w.wantIsExpunged) - } - return nil - } - tests := []test{ - // TODO test cases - /* - { - name: "test_case_1", - fields: fields { - p: nil, - }, - want: want{}, - checkFunc: defaultCheckFunc, - }, - */ - - // TODO test cases - /* - func() test { - return test { - name: "test_case_2", - fields: fields { - p: nil, - }, - want: want{}, - checkFunc: defaultCheckFunc, - } - }(), - */ - } - - for _, tc := range tests { - test := tc - t.Run(test.name, func(tt *testing.T) { - tt.Parallel() - defer goleak.VerifyNone(tt, goleak.IgnoreCurrent()) - if test.beforeFunc != nil { - test.beforeFunc() - } - if test.afterFunc != nil { - defer test.afterFunc() - } - checkFunc := test.checkFunc - if test.checkFunc == nil { - checkFunc = defaultCheckFunc - } - e := &entryUiim{ - p: test.fields.p, - } - - gotIsExpunged := e.tryExpungeLocked() - if err := checkFunc(test.want, gotIsExpunged); err != nil { - tt.Errorf("error = %v", err) - } - }) - } -}