Skip to content

Commit 0e69b60

Browse files
committed
node/metabase: Bump version and fill metadata bucket on upgrade
Closes #3117. Signed-off-by: Leonard Lyubich <[email protected]>
1 parent ef63ec1 commit 0e69b60

File tree

4 files changed

+323
-1
lines changed

4 files changed

+323
-1
lines changed

pkg/local_object_storage/metabase/VERSION.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,9 +99,21 @@ The lowest not used bucket index: 20.
9999
- Name: `19` + container ID
100100
- Key: first object ID
101101
- Value: objects for corresponding split chain
102+
- Metadata bucket
103+
- Name: `255` + container ID
104+
- Keys without values
105+
- `0` + object ID
106+
- `1` + attribute + `0xFF` + `0|1` + fixed256(value) + object ID: integer attributes. \
107+
Sign byte is 0 for negatives, 1 otherwise. Bits are inverted for negatives also.
108+
- `2` + attribute + `0xFF` + value + object ID: plain non-integer attributes
109+
- `3` + object ID + attribute + `0xFF` + value
102110

103111
# History
104112

113+
## Version 3
114+
115+
Last version without metadata bucket introduced with `ObjectService.SearchV2` API.
116+
105117
## Version 2
106118

107119
- Container ID is encoded as 32-byte slice

pkg/local_object_storage/metabase/metadata.go

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,27 @@ func invalidMetaBucketKeyErr(key []byte, cause error) error {
4646
return fmt.Errorf("invalid meta bucket key (prefix 0x%X): %w", key[0], cause)
4747
}
4848

49+
func putMetadataForObject(tx *bbolt.Tx, hdr object.Object, root, phy bool) error {
50+
owner := hdr.Owner()
51+
if owner.IsZero() {
52+
return fmt.Errorf("invalid owner: %w", user.ErrZeroID)
53+
}
54+
pldHash, ok := hdr.PayloadChecksum()
55+
if !ok {
56+
return errors.New("missing payload checksum")
57+
}
58+
var ver version.Version
59+
if v := hdr.Version(); v != nil {
60+
ver = *v
61+
}
62+
var pldHmmHash []byte
63+
if h, ok := hdr.PayloadHomomorphicHash(); ok {
64+
pldHmmHash = h.Value()
65+
}
66+
return putMetadata(tx, hdr.GetContainerID(), hdr.GetID(), ver, owner, hdr.Type(), hdr.CreationEpoch(), hdr.PayloadSize(), pldHash.Value(),
67+
pldHmmHash, hdr.SplitID().ToV2(), hdr.GetParentID(), hdr.GetFirstID(), hdr.Attributes(), root, phy)
68+
}
69+
4970
// TODO: fill on migration.
5071
// TODO: cleaning on obj removal.
5172
func putMetadata(tx *bbolt.Tx, cnr cid.ID, id oid.ID, ver version.Version, owner user.ID, typ object.Type, creationEpoch uint64,

pkg/local_object_storage/metabase/version.go

Lines changed: 53 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,21 @@
11
package meta
22

33
import (
4+
"bytes"
45
"encoding/binary"
56
"errors"
67
"fmt"
78

89
objectconfig "github.com/nspcc-dev/neofs-node/cmd/neofs-node/config/object"
910
"github.com/nspcc-dev/neofs-node/pkg/local_object_storage/util/logicerr"
11+
cid "github.com/nspcc-dev/neofs-sdk-go/container/id"
12+
"github.com/nspcc-dev/neofs-sdk-go/object"
13+
oid "github.com/nspcc-dev/neofs-sdk-go/object/id"
1014
"go.etcd.io/bbolt"
1115
)
1216

1317
// currentMetaVersion contains current metabase version.
14-
const currentMetaVersion = 3
18+
const currentMetaVersion = 4
1519

1620
var versionKey = []byte("version")
1721

@@ -74,6 +78,7 @@ func getVersion(tx *bbolt.Tx) (uint64, bool) {
7478

7579
var migrateFrom = map[uint64]func(*DB, *bbolt.Tx) error{
7680
2: migrateFrom2Version,
81+
3: migrateFrom3Version,
7782
}
7883

7984
func migrateFrom2Version(db *DB, tx *bbolt.Tx) error {
@@ -105,3 +110,50 @@ func migrateFrom2Version(db *DB, tx *bbolt.Tx) error {
105110

106111
return updateVersion(tx, 3)
107112
}
113+
114+
func migrateFrom3Version(_ *DB, tx *bbolt.Tx) error {
115+
c := tx.Cursor()
116+
pref := []byte{metadataPrefix}
117+
if k, _ := c.Seek(pref); bytes.HasPrefix(k, pref) {
118+
return fmt.Errorf("key with prefix 0x%X detected", pref)
119+
}
120+
err := tx.ForEach(func(name []byte, b *bbolt.Bucket) error {
121+
switch name[0] {
122+
default:
123+
return nil
124+
case primaryPrefix, tombstonePrefix, storageGroupPrefix, lockersPrefix, linkObjectsPrefix:
125+
}
126+
if len(name[1:]) != cid.Size {
127+
return fmt.Errorf("invalid container bucket with prefix 0x%X: wrong CID len %d", name[0], len(name[1:]))
128+
}
129+
cnr := cid.ID(name[1:])
130+
err := b.ForEach(func(k, v []byte) error {
131+
if len(k) != oid.Size {
132+
return fmt.Errorf("wrong OID key len %d", len(k))
133+
}
134+
id := oid.ID(k)
135+
var hdr object.Object
136+
if err := hdr.Unmarshal(v); err != nil {
137+
return fmt.Errorf("decode header of object %s from bucket value: %w", id, err)
138+
}
139+
par := hdr.Parent()
140+
if err := putMetadataForObject(tx, hdr, par == nil, true); err != nil {
141+
return fmt.Errorf("put metadata for object %s: %w", id, err)
142+
}
143+
if par != nil {
144+
if err := putMetadataForObject(tx, *par, true, false); err != nil {
145+
return fmt.Errorf("put metadata for parent of object %s: %w", id, err)
146+
}
147+
}
148+
return nil
149+
})
150+
if err != nil {
151+
return fmt.Errorf("process container 0x%X%s bucket: %w", name[0], cnr, err)
152+
}
153+
return nil
154+
})
155+
if err != nil {
156+
return err
157+
}
158+
return updateVersion(tx, 4)
159+
}

pkg/local_object_storage/metabase/version_test.go

Lines changed: 237 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,19 +3,31 @@ package meta
33
import (
44
"bytes"
55
"encoding/binary"
6+
"encoding/hex"
67
"errors"
78
"fmt"
9+
"math/rand"
810
"os"
911
"path"
1012
"path/filepath"
13+
"slices"
14+
"strconv"
1115
"testing"
1216

1317
objectconfig "github.com/nspcc-dev/neofs-node/cmd/neofs-node/config/object"
1418
"github.com/nspcc-dev/neofs-node/pkg/local_object_storage/shard/mode"
19+
"github.com/nspcc-dev/neofs-sdk-go/checksum"
20+
"github.com/nspcc-dev/neofs-sdk-go/client"
1521
apistatus "github.com/nspcc-dev/neofs-sdk-go/client/status"
22+
cid "github.com/nspcc-dev/neofs-sdk-go/container/id"
23+
cidtest "github.com/nspcc-dev/neofs-sdk-go/container/id/test"
1624
"github.com/nspcc-dev/neofs-sdk-go/object"
1725
oid "github.com/nspcc-dev/neofs-sdk-go/object/id"
1826
oidtest "github.com/nspcc-dev/neofs-sdk-go/object/id/test"
27+
objecttest "github.com/nspcc-dev/neofs-sdk-go/object/test"
28+
usertest "github.com/nspcc-dev/neofs-sdk-go/user/test"
29+
"github.com/nspcc-dev/neofs-sdk-go/version"
30+
"github.com/nspcc-dev/tzhash/tz"
1931
"github.com/stretchr/testify/require"
2032
"go.etcd.io/bbolt"
2133
)
@@ -344,3 +356,228 @@ func TestMigrate2to3(t *testing.T) {
344356
})
345357
require.NoError(t, err)
346358
}
359+
360+
func TestMigrate3to4(t *testing.T) {
361+
db := newDB(t)
362+
363+
typs := []object.Type{object.TypeRegular, object.TypeTombstone, object.TypeStorageGroup, object.TypeLock, object.TypeLink}
364+
objs := make([]object.Object, len(typs))
365+
var css, hcss [][]byte
366+
for i := range objs {
367+
objs[i].SetContainerID(cidtest.ID())
368+
id := oidtest.ID()
369+
objs[i].SetID(id)
370+
ver := version.New(uint32(100*i), uint32(100*i+1))
371+
objs[i].SetVersion(&ver)
372+
objs[i].SetOwner(usertest.ID())
373+
objs[i].SetType(typs[i])
374+
objs[i].SetCreationEpoch(rand.Uint64())
375+
objs[i].SetPayloadSize(rand.Uint64())
376+
objs[i].SetPayloadChecksum(checksum.NewSHA256(id))
377+
css = append(css, id[:])
378+
var tzh [tz.Size]byte
379+
rand.Read(tzh[:]) //nolint:staticcheck
380+
objs[i].SetPayloadHomomorphicHash(checksum.NewTillichZemor(tzh))
381+
hcss = append(hcss, tzh[:])
382+
sid := objecttest.SplitID()
383+
objs[i].SetSplitID(&sid)
384+
objs[i].SetParentID(oidtest.ID())
385+
objs[i].SetFirstID(oidtest.ID())
386+
objs[i].SetAttributes(*object.NewAttribute("Index", strconv.Itoa(i)))
387+
}
388+
389+
var par object.Object
390+
par.SetContainerID(objs[0].GetContainerID())
391+
par.SetID(oidtest.ID())
392+
ver := version.New(1000, 1001)
393+
par.SetVersion(&ver)
394+
par.SetOwner(usertest.ID())
395+
par.SetType(typs[0])
396+
par.SetCreationEpoch(rand.Uint64())
397+
par.SetPayloadSize(rand.Uint64())
398+
pcs := oidtest.ID()
399+
par.SetPayloadChecksum(checksum.NewSHA256(pcs))
400+
var phcs [tz.Size]byte
401+
rand.Read(phcs[:]) //nolint:staticcheck
402+
par.SetPayloadHomomorphicHash(checksum.NewTillichZemor(phcs))
403+
sid := objecttest.SplitID()
404+
par.SetSplitID(&sid)
405+
par.SetParentID(oidtest.ID())
406+
par.SetFirstID(oidtest.ID())
407+
par.SetAttributes(*object.NewAttribute("Index", "9999"))
408+
409+
objs[0].SetParent(&par)
410+
411+
for _, item := range []struct {
412+
pref byte
413+
hdr *object.Object
414+
}{
415+
{pref: 0x06, hdr: &objs[0]},
416+
{pref: 0x06, hdr: &par},
417+
{pref: 0x09, hdr: &objs[1]},
418+
{pref: 0x08, hdr: &objs[2]},
419+
{pref: 0x07, hdr: &objs[3]},
420+
{pref: 0x12, hdr: &objs[4]},
421+
} {
422+
err := db.boltDB.Update(func(tx *bbolt.Tx) error {
423+
cnr := item.hdr.GetContainerID()
424+
bkt, err := tx.CreateBucketIfNotExists(slices.Concat([]byte{item.pref}, cnr[:]))
425+
require.NoError(t, err)
426+
id := item.hdr.GetID()
427+
return bkt.Put(id[:], item.hdr.Marshal())
428+
})
429+
require.NoError(t, err)
430+
}
431+
432+
// force old version
433+
err := db.boltDB.Update(func(tx *bbolt.Tx) error {
434+
if err := tx.ForEach(func(name []byte, b *bbolt.Bucket) error {
435+
if name[0] == 0xFF {
436+
return tx.DeleteBucket(name)
437+
}
438+
return nil
439+
}); err != nil {
440+
return err
441+
}
442+
443+
bkt := tx.Bucket([]byte{0x05})
444+
require.NotNil(t, bkt)
445+
return bkt.Put([]byte("version"), []byte{0x03, 0, 0, 0, 0, 0, 0, 0})
446+
})
447+
require.NoError(t, err)
448+
// migrate
449+
require.NoError(t, db.Init())
450+
// check
451+
err = db.boltDB.View(func(tx *bbolt.Tx) error {
452+
bkt := tx.Bucket([]byte{0x05})
453+
require.NotNil(t, bkt)
454+
require.Equal(t, []byte{0x04, 0, 0, 0, 0, 0, 0, 0}, bkt.Get([]byte("version")))
455+
return nil
456+
})
457+
require.NoError(t, err)
458+
459+
res, _, err := db.Search(objs[0].GetContainerID(), nil, nil, nil, 1000)
460+
require.NoError(t, err)
461+
require.Len(t, res, 2)
462+
require.True(t, slices.ContainsFunc(res, func(r client.SearchResultItem) bool { return r.ID == objs[0].GetID() }))
463+
require.True(t, slices.ContainsFunc(res, func(r client.SearchResultItem) bool { return r.ID == par.GetID() }))
464+
465+
for i := range objs[1:] {
466+
res, _, err := db.Search(objs[1+i].GetContainerID(), nil, nil, nil, 1000)
467+
require.NoError(t, err, i)
468+
require.Len(t, res, 1, i)
469+
require.Equal(t, objs[1+i].GetID(), res[0].ID, i)
470+
}
471+
472+
for _, tc := range []struct {
473+
attr string
474+
val string
475+
cnr cid.ID
476+
exp oid.ID
477+
par bool
478+
}{
479+
{attr: "$Object:version", val: "v0.1", cnr: objs[0].GetContainerID(), exp: objs[0].GetID()},
480+
{attr: "$Object:version", val: "v100.101", cnr: objs[1].GetContainerID(), exp: objs[1].GetID()},
481+
{attr: "$Object:version", val: "v200.201", cnr: objs[2].GetContainerID(), exp: objs[2].GetID()},
482+
{attr: "$Object:version", val: "v300.301", cnr: objs[3].GetContainerID(), exp: objs[3].GetID()},
483+
{attr: "$Object:version", val: "v400.401", cnr: objs[4].GetContainerID(), exp: objs[4].GetID()},
484+
{attr: "$Object:version", val: "v1000.1001", cnr: par.GetContainerID(), exp: par.GetID()},
485+
{attr: "$Object:ownerID", val: objs[0].Owner().String(), cnr: objs[0].GetContainerID(), exp: objs[0].GetID()},
486+
{attr: "$Object:ownerID", val: objs[1].Owner().String(), cnr: objs[1].GetContainerID(), exp: objs[1].GetID()},
487+
{attr: "$Object:ownerID", val: objs[2].Owner().String(), cnr: objs[2].GetContainerID(), exp: objs[2].GetID()},
488+
{attr: "$Object:ownerID", val: objs[3].Owner().String(), cnr: objs[3].GetContainerID(), exp: objs[3].GetID()},
489+
{attr: "$Object:ownerID", val: objs[4].Owner().String(), cnr: objs[4].GetContainerID(), exp: objs[4].GetID()},
490+
{attr: "$Object:ownerID", val: par.Owner().String(), cnr: par.GetContainerID(), exp: par.GetID()},
491+
{attr: "$Object:objectType", val: "REGULAR", cnr: objs[0].GetContainerID(), par: true},
492+
{attr: "$Object:objectType", val: "TOMBSTONE", cnr: objs[1].GetContainerID(), exp: objs[1].GetID()},
493+
{attr: "$Object:objectType", val: "STORAGE_GROUP", cnr: objs[2].GetContainerID(), exp: objs[2].GetID()},
494+
{attr: "$Object:objectType", val: "LOCK", cnr: objs[3].GetContainerID(), exp: objs[3].GetID()},
495+
{attr: "$Object:objectType", val: "LINK", cnr: objs[4].GetContainerID(), exp: objs[4].GetID()},
496+
{attr: "$Object:creationEpoch", val: strconv.FormatUint(objs[0].CreationEpoch(), 10), cnr: objs[0].GetContainerID(), exp: objs[0].GetID()},
497+
{attr: "$Object:creationEpoch", val: strconv.FormatUint(objs[1].CreationEpoch(), 10), cnr: objs[1].GetContainerID(), exp: objs[1].GetID()},
498+
{attr: "$Object:creationEpoch", val: strconv.FormatUint(objs[2].CreationEpoch(), 10), cnr: objs[2].GetContainerID(), exp: objs[2].GetID()},
499+
{attr: "$Object:creationEpoch", val: strconv.FormatUint(objs[3].CreationEpoch(), 10), cnr: objs[3].GetContainerID(), exp: objs[3].GetID()},
500+
{attr: "$Object:creationEpoch", val: strconv.FormatUint(objs[4].CreationEpoch(), 10), cnr: objs[4].GetContainerID(), exp: objs[4].GetID()},
501+
{attr: "$Object:creationEpoch", val: strconv.FormatUint(par.CreationEpoch(), 10), cnr: par.GetContainerID(), exp: par.GetID()},
502+
{attr: "$Object:payloadLength", val: strconv.FormatUint(objs[0].PayloadSize(), 10), cnr: objs[0].GetContainerID(), exp: objs[0].GetID()},
503+
{attr: "$Object:payloadLength", val: strconv.FormatUint(objs[1].PayloadSize(), 10), cnr: objs[1].GetContainerID(), exp: objs[1].GetID()},
504+
{attr: "$Object:payloadLength", val: strconv.FormatUint(objs[2].PayloadSize(), 10), cnr: objs[2].GetContainerID(), exp: objs[2].GetID()},
505+
{attr: "$Object:payloadLength", val: strconv.FormatUint(objs[3].PayloadSize(), 10), cnr: objs[3].GetContainerID(), exp: objs[3].GetID()},
506+
{attr: "$Object:payloadLength", val: strconv.FormatUint(objs[4].PayloadSize(), 10), cnr: objs[4].GetContainerID(), exp: objs[4].GetID()},
507+
{attr: "$Object:payloadLength", val: strconv.FormatUint(par.PayloadSize(), 10), cnr: par.GetContainerID(), exp: par.GetID()},
508+
{attr: "$Object:payloadHash", val: hex.EncodeToString(css[0]), cnr: objs[0].GetContainerID(), exp: objs[0].GetID()},
509+
{attr: "$Object:payloadHash", val: hex.EncodeToString(css[1]), cnr: objs[1].GetContainerID(), exp: objs[1].GetID()},
510+
{attr: "$Object:payloadHash", val: hex.EncodeToString(css[2]), cnr: objs[2].GetContainerID(), exp: objs[2].GetID()},
511+
{attr: "$Object:payloadHash", val: hex.EncodeToString(css[3]), cnr: objs[3].GetContainerID(), exp: objs[3].GetID()},
512+
{attr: "$Object:payloadHash", val: hex.EncodeToString(css[4]), cnr: objs[4].GetContainerID(), exp: objs[4].GetID()},
513+
{attr: "$Object:payloadHash", val: hex.EncodeToString(pcs[:]), cnr: par.GetContainerID(), exp: par.GetID()},
514+
{attr: "$Object:homomorphicHash", val: hex.EncodeToString(hcss[0]), cnr: objs[0].GetContainerID(), exp: objs[0].GetID()},
515+
{attr: "$Object:homomorphicHash", val: hex.EncodeToString(hcss[1]), cnr: objs[1].GetContainerID(), exp: objs[1].GetID()},
516+
{attr: "$Object:homomorphicHash", val: hex.EncodeToString(hcss[2]), cnr: objs[2].GetContainerID(), exp: objs[2].GetID()},
517+
{attr: "$Object:homomorphicHash", val: hex.EncodeToString(hcss[3]), cnr: objs[3].GetContainerID(), exp: objs[3].GetID()},
518+
{attr: "$Object:homomorphicHash", val: hex.EncodeToString(hcss[4]), cnr: objs[4].GetContainerID(), exp: objs[4].GetID()},
519+
{attr: "$Object:homomorphicHash", val: hex.EncodeToString(phcs[:]), cnr: par.GetContainerID(), exp: par.GetID()},
520+
{attr: "$Object:split.splitID", val: objs[0].SplitID().String(), cnr: objs[0].GetContainerID(), exp: objs[0].GetID()},
521+
{attr: "$Object:split.splitID", val: objs[1].SplitID().String(), cnr: objs[1].GetContainerID(), exp: objs[1].GetID()},
522+
{attr: "$Object:split.splitID", val: objs[2].SplitID().String(), cnr: objs[2].GetContainerID(), exp: objs[2].GetID()},
523+
{attr: "$Object:split.splitID", val: objs[3].SplitID().String(), cnr: objs[3].GetContainerID(), exp: objs[3].GetID()},
524+
{attr: "$Object:split.splitID", val: objs[4].SplitID().String(), cnr: objs[4].GetContainerID(), exp: objs[4].GetID()},
525+
{attr: "$Object:split.splitID", val: par.SplitID().String(), cnr: par.GetContainerID(), exp: par.GetID()},
526+
{attr: "$Object:split.parent", val: objs[0].GetParentID().String(), cnr: objs[0].GetContainerID(), exp: objs[0].GetID()},
527+
{attr: "$Object:split.parent", val: objs[1].GetParentID().String(), cnr: objs[1].GetContainerID(), exp: objs[1].GetID()},
528+
{attr: "$Object:split.parent", val: objs[2].GetParentID().String(), cnr: objs[2].GetContainerID(), exp: objs[2].GetID()},
529+
{attr: "$Object:split.parent", val: objs[3].GetParentID().String(), cnr: objs[3].GetContainerID(), exp: objs[3].GetID()},
530+
{attr: "$Object:split.parent", val: objs[4].GetParentID().String(), cnr: objs[4].GetContainerID(), exp: objs[4].GetID()},
531+
{attr: "$Object:split.parent", val: par.GetParentID().String(), cnr: par.GetContainerID(), exp: par.GetID()},
532+
{attr: "$Object:split.first", val: objs[0].GetFirstID().String(), cnr: objs[0].GetContainerID(), exp: objs[0].GetID()},
533+
{attr: "$Object:split.first", val: objs[1].GetFirstID().String(), cnr: objs[1].GetContainerID(), exp: objs[1].GetID()},
534+
{attr: "$Object:split.first", val: objs[2].GetFirstID().String(), cnr: objs[2].GetContainerID(), exp: objs[2].GetID()},
535+
{attr: "$Object:split.first", val: objs[3].GetFirstID().String(), cnr: objs[3].GetContainerID(), exp: objs[3].GetID()},
536+
{attr: "$Object:split.first", val: objs[4].GetFirstID().String(), cnr: objs[4].GetContainerID(), exp: objs[4].GetID()},
537+
{attr: "$Object:split.first", val: par.GetFirstID().String(), cnr: par.GetContainerID(), exp: par.GetID()},
538+
{attr: "Index", val: "0", cnr: objs[0].GetContainerID(), exp: objs[0].GetID()},
539+
{attr: "Index", val: "1", cnr: objs[1].GetContainerID(), exp: objs[1].GetID()},
540+
{attr: "Index", val: "2", cnr: objs[2].GetContainerID(), exp: objs[2].GetID()},
541+
{attr: "Index", val: "3", cnr: objs[3].GetContainerID(), exp: objs[3].GetID()},
542+
{attr: "Index", val: "4", cnr: objs[4].GetContainerID(), exp: objs[4].GetID()},
543+
{attr: "Index", val: "9999", cnr: par.GetContainerID(), exp: par.GetID()},
544+
} {
545+
var fs object.SearchFilters
546+
fs.AddFilter(tc.attr, tc.val, object.MatchStringEqual)
547+
res, _, err := db.Search(tc.cnr, fs, nil, nil, 1000)
548+
require.NoError(t, err, tc)
549+
if !tc.par {
550+
require.Len(t, res, 1, tc)
551+
require.Equal(t, tc.exp, res[0].ID, tc)
552+
} else {
553+
require.Len(t, res, 2, tc)
554+
require.True(t, slices.ContainsFunc(res, func(r client.SearchResultItem) bool { return r.ID == objs[0].GetID() }))
555+
require.True(t, slices.ContainsFunc(res, func(r client.SearchResultItem) bool { return r.ID == par.GetID() }))
556+
}
557+
}
558+
559+
for i := range objs {
560+
var fs object.SearchFilters
561+
fs.AddRootFilter()
562+
res, _, err = db.Search(objs[i].GetContainerID(), fs, nil, nil, 1000)
563+
require.NoError(t, err, i)
564+
require.Len(t, res, 1, i)
565+
if i == 0 {
566+
require.Equal(t, par.GetID(), res[0].ID)
567+
} else {
568+
require.Equal(t, objs[i].GetID(), res[0].ID, i)
569+
}
570+
fs = fs[:0]
571+
fs.AddPhyFilter()
572+
res, _, err = db.Search(objs[i].GetContainerID(), fs, nil, nil, 1000)
573+
require.NoError(t, err, i)
574+
if i == 0 {
575+
require.Len(t, res, 2)
576+
require.True(t, slices.ContainsFunc(res, func(r client.SearchResultItem) bool { return r.ID == objs[0].GetID() }))
577+
require.True(t, slices.ContainsFunc(res, func(r client.SearchResultItem) bool { return r.ID == par.GetID() }))
578+
} else {
579+
require.Len(t, res, 1)
580+
require.Equal(t, objs[i].GetID(), res[0].ID, i)
581+
}
582+
}
583+
}

0 commit comments

Comments
 (0)