@@ -3,19 +3,31 @@ package meta
3
3
import (
4
4
"bytes"
5
5
"encoding/binary"
6
+ "encoding/hex"
6
7
"errors"
7
8
"fmt"
9
+ "math/rand"
8
10
"os"
9
11
"path"
10
12
"path/filepath"
13
+ "slices"
14
+ "strconv"
11
15
"testing"
12
16
13
17
objectconfig "github.com/nspcc-dev/neofs-node/cmd/neofs-node/config/object"
14
18
"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"
15
21
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"
16
24
"github.com/nspcc-dev/neofs-sdk-go/object"
17
25
oid "github.com/nspcc-dev/neofs-sdk-go/object/id"
18
26
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"
19
31
"github.com/stretchr/testify/require"
20
32
"go.etcd.io/bbolt"
21
33
)
@@ -344,3 +356,228 @@ func TestMigrate2to3(t *testing.T) {
344
356
})
345
357
require .NoError (t , err )
346
358
}
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