11package shard_test
22
33import (
4+ "encoding/binary"
45 "path/filepath"
56 "testing"
67
@@ -9,9 +10,11 @@ import (
910 meta "github.com/nspcc-dev/neofs-node/pkg/local_object_storage/metabase"
1011 "github.com/nspcc-dev/neofs-node/pkg/local_object_storage/shard"
1112 "github.com/nspcc-dev/neofs-node/pkg/local_object_storage/shard/mode"
13+ cidtest "github.com/nspcc-dev/neofs-sdk-go/container/id/test"
1214 "github.com/nspcc-dev/neofs-sdk-go/object"
1315 oid "github.com/nspcc-dev/neofs-sdk-go/object/id"
1416 "github.com/stretchr/testify/require"
17+ "go.etcd.io/bbolt"
1518)
1619
1720type metricsStore struct {
@@ -149,6 +152,108 @@ func TestCounters(t *testing.T) {
149152 })
150153}
151154
155+ func TestInhumeContainerCounters (t * testing.T ) {
156+ sh , mm := shardWithMetrics (t , t .TempDir ())
157+
158+ c1 := cidtest .ID ()
159+ c2 := cidtest .ID ()
160+
161+ var objsC1 , objsC2 = 5 , 7
162+ var sizeC1 , sizeC2 int64 = 0 , 0
163+
164+ for range objsC1 {
165+ obj := generateObjectWithCID (c1 )
166+ require .NoError (t , sh .Put (obj , nil ))
167+ sizeC1 += int64 (obj .PayloadSize ())
168+ }
169+ for range objsC2 {
170+ obj := generateObjectWithCID (c2 )
171+ require .NoError (t , sh .Put (obj , nil ))
172+ sizeC2 += int64 (obj .PayloadSize ())
173+ }
174+
175+ total := uint64 (objsC1 + objsC2 )
176+ initialPayload := mm .payloadSize
177+ require .Equal (t , sizeC1 + sizeC2 , initialPayload )
178+ require .Equal (t , mm .objectCounters [physical ], total )
179+ require .Equal (t , mm .objectCounters [logical ], total )
180+
181+ require .NoError (t , sh .InhumeContainer (c1 ))
182+
183+ require .Equal (t , mm .objectCounters [physical ], total )
184+ require .Equal (t , mm .objectCounters [logical ], uint64 (objsC2 ))
185+ require .Empty (t , mm .containerSize [c1 .EncodeToString ()])
186+ require .Equal (t , mm .containerSize [c2 .EncodeToString ()], sizeC2 )
187+ // payload size must remain unchanged after logical removal
188+ require .Equal (t , initialPayload , mm .payloadSize )
189+
190+ require .NoError (t , sh .InhumeContainer (c2 ))
191+
192+ require .Equal (t , mm .objectCounters [physical ], total )
193+ require .Empty (t , mm .objectCounters [logical ])
194+ require .Empty (t , mm .containerSize [c1 .EncodeToString ()])
195+ require .Empty (t , mm .containerSize [c2 .EncodeToString ()])
196+ // payload size still unchanged
197+ require .Equal (t , initialPayload , mm .payloadSize )
198+ }
199+
200+ func TestShardCounterMigration (t * testing.T ) {
201+ path := t .TempDir ()
202+ sh , mm := shardWithMetrics (t , path )
203+
204+ const n = 11
205+ for range n {
206+ obj := generateObject ()
207+ require .NoError (t , sh .Put (obj , nil ))
208+ }
209+ require .Equal (t , uint64 (n ), mm .objectCounters [physical ])
210+ require .Equal (t , uint64 (n ), mm .objectCounters [logical ])
211+
212+ // Close shard so we can mutate Bolt file.
213+ require .NoError (t , sh .Close ())
214+
215+ metaPath := filepath .Join (path , "meta" )
216+ mdb , err := bbolt .Open (metaPath , 0o600 , nil )
217+ require .NoError (t , err )
218+
219+ var (
220+ bucketPrefix = []byte {5 } // shardInfoPrefix
221+ objectPhyCounterKey = []byte ("phy_counter" )
222+ objectLogicCounterKey = []byte ("logic_counter" )
223+ corruptPhysical = uint64 (100 )
224+ corruptLogical = uint64 (200 )
225+ )
226+
227+ require .NoError (t , mdb .Update (func (tx * bbolt.Tx ) error {
228+ b := tx .Bucket (bucketPrefix )
229+ require .NotNil (t , b )
230+
231+ require .Equal (t , uint64 (n ), binary .LittleEndian .Uint64 (b .Get (objectPhyCounterKey )))
232+ require .Equal (t , uint64 (n ), binary .LittleEndian .Uint64 (b .Get (objectLogicCounterKey )))
233+
234+ cc := make ([]byte , 8 )
235+ binary .LittleEndian .PutUint64 (cc , corruptPhysical )
236+ require .NoError (t , b .Put (objectPhyCounterKey , cc ))
237+ cc = make ([]byte , 8 )
238+ binary .LittleEndian .PutUint64 (cc , corruptLogical )
239+ require .NoError (t , b .Put (objectLogicCounterKey , cc ))
240+
241+ // force metabase version to 7 to restore counters
242+ bkt := tx .Bucket ([]byte {0x05 })
243+ require .NotNil (t , bkt )
244+ require .NoError (t , bkt .Put ([]byte ("version" ), []byte {0x07 , 0 , 0 , 0 , 0 , 0 , 0 , 0 }))
245+ return nil
246+ }))
247+ require .NoError (t , mdb .Close ())
248+
249+ // re-open shard, initMetrics should force sync counters and restore real values
250+ sh , mm = shardWithMetrics (t , path )
251+
252+ require .Equal (t , uint64 (n ), mm .objectCounters [physical ])
253+ require .Equal (t , uint64 (n ), mm .objectCounters [logical ])
254+ require .NoError (t , sh .Close ())
255+ }
256+
152257func shardWithMetrics (t * testing.T , path string ) (* shard.Shard , * metricsStore ) {
153258 mm := & metricsStore {
154259 objectCounters : map [string ]uint64 {
0 commit comments