5
5
"context"
6
6
"errors"
7
7
"fmt"
8
+ "maps"
8
9
"math/big"
9
10
"os"
10
11
"path"
@@ -21,7 +22,7 @@ import (
21
22
objectsdk "github.com/nspcc-dev/neofs-sdk-go/object"
22
23
oid "github.com/nspcc-dev/neofs-sdk-go/object/id"
23
24
"github.com/nspcc-dev/neofs-sdk-go/version"
24
- "golang. org/x/sync/errgroup "
25
+ "go.uber. org/zap "
25
26
)
26
27
27
28
type containerStorage struct {
@@ -50,11 +51,46 @@ func (s *containerStorage) drop() error {
50
51
return nil
51
52
}
52
53
53
- func (s * containerStorage ) putMPTIndexes (ee []objEvent ) {
54
+ type eventWithMptKVs struct {
55
+ ev objEvent
56
+ additionalKVs map [string ][]byte
57
+ }
58
+
59
+ func (s * containerStorage ) putObjects (ctx context.Context , l * zap.Logger , bInd uint32 , ee []objEvent , net NeoFSNetwork ) {
54
60
s .m .Lock ()
55
61
defer s .m .Unlock ()
56
62
57
- for _ , e := range ee {
63
+ // raw indexes are responsible for object validation and only after
64
+ // object is taken as a valid one, it goes to the slower-on-read MPT
65
+ // storage via objCh
66
+ objCh := make (chan eventWithMptKVs , len (ee ))
67
+
68
+ var wg sync.WaitGroup
69
+ wg .Add (1 )
70
+ go func () {
71
+ defer wg .Done ()
72
+ err := s .putRawIndexes (ctx , l , ee , net , objCh )
73
+ if err != nil {
74
+ l .Error ("failed to put raw indexes" , zap .Error (err ))
75
+ }
76
+ }()
77
+ wg .Add (1 )
78
+ go func () {
79
+ defer wg .Done ()
80
+ err := s .putMPTIndexes (bInd , objCh )
81
+ if err != nil {
82
+ l .Error ("failed to put mpt indexes" , zap .Error (err ))
83
+ }
84
+ }()
85
+ wg .Wait ()
86
+ }
87
+
88
+ // lock should be taken.
89
+ func (s * containerStorage ) putMPTIndexes (bInd uint32 , ch <- chan eventWithMptKVs ) error {
90
+ for evWithKeys := range ch {
91
+ maps .Copy (s .mptOpsBatch , evWithKeys .additionalKVs )
92
+
93
+ e := evWithKeys .ev
58
94
commsuffix := e .oID [:]
59
95
60
96
// batching that is implemented for MPT ignores key's first byte
@@ -77,61 +113,122 @@ func (s *containerStorage) putMPTIndexes(ee []objEvent) {
77
113
s .mptOpsBatch [string (append ([]byte {0 , typeIndex }, commsuffix ... ))] = []byte {byte (e .typ )}
78
114
}
79
115
}
116
+
117
+ root := s .mpt .StateRoot ()
118
+ s .mpt .Store .Put ([]byte {rootKey }, root [:])
119
+
120
+ _ , err := s .mpt .PutBatch (mpt .MapToMPTBatch (s .mptOpsBatch ))
121
+ if err != nil {
122
+ return fmt .Errorf ("put batch to MPT storage: %w" , err )
123
+ }
124
+ clear (s .mptOpsBatch )
125
+
126
+ s .mpt .Flush (bInd )
127
+
128
+ return nil
80
129
}
81
130
82
- func (s * containerStorage ) putRawIndexes (ctx context.Context , ee []objEvent , net NeoFSNetwork ) error {
83
- var wg errgroup.Group
84
- wg .SetLimit (10 )
85
- objects := make ([]objectsdk.Object , len (ee ))
131
+ // lock should be taken.
132
+ func (s * containerStorage ) putRawIndexes (ctx context.Context , l * zap.Logger , ee []objEvent , net NeoFSNetwork , res chan <- eventWithMptKVs ) error {
133
+ batch := make (map [string ][]byte )
134
+ var finalErr error
135
+ defer func () {
136
+ close (res )
86
137
87
- for i , e := range ee {
88
- wg .Go (func () error {
89
- h , err := net .Head (ctx , e .cID , e .oID )
138
+ if len (batch ) > 0 {
139
+ err := s .db .PutChangeSet (batch , nil )
90
140
if err != nil {
91
- // TODO define behavior with status (non-network) errors; maybe it is near #3140
92
- return fmt .Errorf ("HEAD object: %w" , err )
141
+ finalErr = fmt .Errorf ("put change set to DB: %w" , err )
93
142
}
143
+ }
144
+ }()
94
145
95
- objects [i ] = h
96
- return nil
97
- })
98
- }
146
+ for _ , e := range ee {
147
+ err := isOpAllowed (s .db , e )
148
+ if err != nil {
149
+ l .Warn ("skip object" , zap .Stringer ("oid" , e .oID ), zap .String ("reason" , err .Error ()))
150
+ continue
151
+ }
99
152
100
- err := wg .Wait ()
101
- if err != nil {
102
- return err
103
- }
153
+ evWithMtp := eventWithMptKVs {ev : e }
104
154
105
- s .m .Lock ()
106
- defer s .m .Unlock ()
107
- batch := make (map [string ][]byte )
155
+ h , err := net .Head (ctx , e .cID , e .oID )
156
+ if err != nil {
157
+ // TODO define behavior with status (non-network) errors; maybe it is near #3140
158
+ return fmt .Errorf ("HEAD %s object: %w" , e .oID , err )
159
+ }
108
160
109
- for i , e := range ee {
110
161
commsuffix := e .oID [:]
111
162
112
163
batch [string (append ([]byte {oidIndex }, commsuffix ... ))] = []byte {}
113
164
if len (e .deletedObjects ) > 0 {
114
165
batch [string (append ([]byte {deletedIndex }, commsuffix ... ))] = e .deletedObjects
115
- err = deleteObjectsOps (batch , s . mptOpsBatch , s .db , e .deletedObjects )
166
+ evWithMtp . additionalKVs , err = deleteObjectsOps (batch , s .db , e .deletedObjects )
116
167
if err != nil {
117
- return fmt .Errorf ("cleaning operations for %s object: %w" , e .oID , err )
168
+ l .Error ("cleaning deleted object" , zap .Stringer ("oid" , e .oID ), zap .Error (err ))
169
+ continue
118
170
}
119
171
}
120
172
if len (e .lockedObjects ) > 0 {
121
173
batch [string (append ([]byte {lockedIndex }, commsuffix ... ))] = e .lockedObjects
174
+
175
+ for locked := range slices .Chunk (e .lockedObjects , oid .Size ) {
176
+ batch [string (append ([]byte {lockedByIndex }, locked ... ))] = commsuffix
177
+ }
122
178
}
123
179
124
- err = object .VerifyHeaderForMetadata (objects [ i ] )
180
+ err = object .VerifyHeaderForMetadata (h )
125
181
if err != nil {
126
- return fmt .Errorf ("invalid %s header: %w" , e .oID , err )
182
+ l .Error ("header verification" , zap .Stringer ("oid" , e .oID ), zap .Error (err ))
183
+ continue
127
184
}
128
185
129
- fillObjectIndex (batch , objects [i ])
186
+ res <- evWithMtp
187
+
188
+ fillObjectIndex (batch , h )
130
189
}
131
190
132
- err = s .db .PutChangeSet (batch , nil )
133
- if err != nil {
134
- return fmt .Errorf ("put change set to DB: %w" , err )
191
+ return finalErr
192
+ }
193
+
194
+ func isOpAllowed (db storage.Store , e objEvent ) error {
195
+ if len (e .deletedObjects ) == 0 && len (e .lockedObjects ) == 0 {
196
+ return nil
197
+ }
198
+
199
+ key := make ([]byte , 1 + oid .Size )
200
+
201
+ for obj := range slices .Chunk (e .deletedObjects , oid .Size ) {
202
+ copy (key [1 :], obj )
203
+
204
+ // delete object that does not exist
205
+ key [0 ] = oidIndex
206
+ _ , err := db .Get (key )
207
+ if err != nil {
208
+ return fmt .Errorf ("%s object-to-delete's presence check: %w" , oid .ID (obj ), err )
209
+ }
210
+
211
+ // delete object that is locked
212
+ key [0 ] = lockedByIndex
213
+ v , err := db .Get (key )
214
+ if err != nil {
215
+ if errors .Is (err , storage .ErrKeyNotFound ) {
216
+ continue
217
+ }
218
+ return fmt .Errorf ("%s object-to-delete's lock status check: %w" , oid .ID (obj ), err )
219
+ }
220
+ return fmt .Errorf ("%s object-to-delete is locked by %s" , oid .ID (obj ), oid .ID (v ))
221
+ }
222
+
223
+ for obj := range slices .Chunk (e .lockedObjects , oid .Size ) {
224
+ copy (key [1 :], obj )
225
+
226
+ // lock object that does not exist
227
+ key [0 ] = oidIndex
228
+ _ , err := db .Get (key )
229
+ if err != nil {
230
+ return fmt .Errorf ("%s object-to-lock's presence check: %w" , oid .ID (obj ), err )
231
+ }
135
232
}
136
233
137
234
return nil
@@ -192,8 +289,9 @@ func fillObjectIndex(batch map[string][]byte, h objectsdk.Object) {
192
289
}
193
290
}
194
291
195
- func deleteObjectsOps (dbKV , mptKV map [string ][]byte , s storage.Store , objects []byte ) error {
292
+ func deleteObjectsOps (dbKV map [string ][]byte , s storage.Store , objects []byte ) ( map [ string ][] byte , error ) {
196
293
rng := storage.SeekRange {}
294
+ mptKV := make (map [string ][]byte )
197
295
198
296
// nil value means "delete" operation
199
297
@@ -271,11 +369,11 @@ func deleteObjectsOps(dbKV, mptKV map[string][]byte, s storage.Store, objects []
271
369
return true
272
370
})
273
371
if err != nil {
274
- return err
372
+ return nil , err
275
373
}
276
374
}
277
375
278
- return nil
376
+ return mptKV , nil
279
377
}
280
378
281
379
// lastObjectKey returns the least possible key in sorted DB list that
0 commit comments