Skip to content

Commit a2866fc

Browse files
committed
core: object chunks; chunk manifest
* move remote metadata from chunk manifest to target-level (`ups` structure) - remove metadata storage from chunk manifest * manifest persistence: add LoadPartial() - as in: StorePartial() vs StoreCompleted() * fix xattr storage asymmetry: _partial_ manifests use chunk #1, and vice versa * add level 1 unit tests for core Ufest functionality * ongoing: error handling; validation and cleanup; other refactoring * tests: - rewrite level-1 unit test - enable multipart test #2: s3cmd with xxhash end-to-end validation ---- * part ten, prev. commit: e2c6a0d Signed-off-by: Alex Aizman <alex.aizman@gmail.com>
1 parent 39c8bed commit a2866fc

7 files changed

Lines changed: 613 additions & 627 deletions

File tree

ais/target.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ type (
7373
res *res.Res
7474
txns txns
7575
regstate regstate
76-
ups uploads
76+
ups ups
7777
}
7878
)
7979

ais/test/scripted_cli_test.go

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,6 @@ func TestCopySyncRemaisUsingScript(t *testing.T) {
148148
// 1. MD5 checksum
149149
// 2. "apc.S3Scheme+apc.BckProviderSeparator+bck.Name"
150150
func TestMPU_1_UsingScript(t *testing.T) {
151-
t.Skipf("skipping %s - requires xxhsum installed", t.Name())
152151
tempdir := t.TempDir()
153152
bck := cmn.Bck{Name: trand.String(10), Provider: apc.AIS}
154153

@@ -174,7 +173,6 @@ func TestMPU_1_UsingScript(t *testing.T) {
174173
}
175174

176175
func TestMPU_2_UsingScript(t *testing.T) {
177-
t.Skipf("skipping %s - requires xxhsum installed", t.Name())
178176
bck := cmn.Bck{Name: trand.String(10), Provider: apc.AIS}
179177
cmd := exec.Command("./scripts/multipart-smoke.sh", "--bucket", bck.Cname(""))
180178

ais/test/scripts/multipart-smoke.sh

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -86,9 +86,9 @@ elif [[ "$bucket" == s3://* ]]; then
8686
bucket_name="${bucket#s3://}"
8787
s3_bucket="$bucket"
8888

89-
# Verify S3 bucket exists (s3cmd will fail if it doesn't)
90-
if ! s3cmd ls "$bucket" >/dev/null 2>&1; then
91-
echo "Error: S3 bucket $bucket does not exist or is not accessible"
89+
# Verify bucket exists
90+
if ! ais show bucket "$bucket" -c >/dev/null 2>&1; then
91+
echo "Error: $bucket does not exist"
9292
exit 1
9393
fi
9494

@@ -128,10 +128,18 @@ h1=$(xxhsum "$src" | awk '{print $1}')
128128
echo " Source hash: $h1"
129129

130130
echo "3. uploading via s3cmd to $s3_bucket..."
131-
s3cmd put "$src" "$s3_bucket/${filename}" || exit $?
131+
s3cmd put "$src" "$s3_bucket/${filename}" \
132+
--no-ssl \
133+
--host=localhost:8080/s3 \
134+
--host-bucket="localhost:8080/s3/%(bucket)" \
135+
|| exit $?
132136

133137
echo "4. downloading via s3cmd from $s3_bucket..."
134-
s3cmd get "$s3_bucket/${filename}" "$dst" --force || exit $?
138+
s3cmd get "$s3_bucket/${filename}" "$dst" --force \
139+
--no-ssl \
140+
--host=localhost:8080/s3 \
141+
--host-bucket="localhost:8080/s3/%(bucket)" \
142+
|| exit $?
135143

136144
echo "5. computing xxhash of downloaded file..."
137145
h2=$(xxhsum "$dst" | awk '{print $1}')

ais/tgtmpt.go

Lines changed: 38 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -28,66 +28,77 @@ const (
2828

2929
// see also: https://docs.aws.amazon.com/AmazonS3/latest/userguide/qfacts.html
3030
const (
31-
maxPartsPerUpload = 10000
31+
maxPartsPerUpload = 9999 // 64K limited (compressed) unless (e.g.) `mkfs.xfs -m attr=128k /dev/sdX`
3232
)
3333

3434
type (
35-
uploads struct {
35+
up struct {
36+
u *core.Ufest
37+
rmd map[string]string
38+
}
39+
ups struct {
3640
t *target
37-
m map[string]*core.Ufest // by upload ID
41+
m map[string]up
3842
sync.RWMutex
3943
}
4044
)
4145

42-
func (ups *uploads) init(id string, lom *core.LOM, metadata map[string]string) error {
46+
func (ups *ups) init(id string, lom *core.LOM, rmd map[string]string) error {
4347
manifest := core.NewUfest(id, lom, false /*must-exist*/)
44-
if err := manifest.SetMeta(metadata); err != nil {
45-
return err
46-
}
4748
ups.Lock()
4849
if ups.m == nil {
49-
ups.m = make(map[string]*core.Ufest, iniCapUploads)
50+
ups.m = make(map[string]up, iniCapUploads)
5051
}
51-
ups._add(id, manifest)
52+
ups._add(id, manifest, rmd)
5253
ups.Unlock()
5354
return nil
5455
}
5556

56-
func (ups *uploads) _add(id string, manifest *core.Ufest) {
57+
func (ups *ups) _add(id string, manifest *core.Ufest, rmd map[string]string) {
5758
debug.Assert(manifest.Lom != nil)
5859
_, ok := ups.m[id]
5960
debug.Assert(!ok, "duplicated upload ID: ", id, " ", manifest.Lom.Cname())
60-
ups.m[id] = manifest
61+
ups.m[id] = up{manifest, rmd}
62+
}
63+
64+
func (ups *ups) get(id string) (manifest *core.Ufest) {
65+
ups.RLock()
66+
manifest = ups.m[id].u
67+
ups.RUnlock()
68+
return
6169
}
6270

63-
func (ups *uploads) get(id string) (manifest *core.Ufest) {
71+
func (ups *ups) getWithMeta(id string) (manifest *core.Ufest, rmd map[string]string) {
6472
ups.RLock()
65-
manifest = ups.m[id]
73+
manifest = ups.m[id].u
74+
rmd = ups.m[id].rmd
6675
ups.RUnlock()
6776
return
6877
}
6978

7079
// NOTE: must be called with ups unlocked
71-
func (ups *uploads) fromFS(id string, lom *core.LOM, add bool) (manifest *core.Ufest, err error) {
80+
func (ups *ups) fromFS(id string, lom *core.LOM, add bool) (manifest *core.Ufest, err error) {
7281
manifest = core.NewUfest(id, lom, true /*must-exist*/)
7382
if err = manifest.Load(lom); err == nil && add {
7483
ups.Lock()
75-
ups._add(id, manifest)
84+
ups._add(id, manifest, nil /*remote metadata <= LOM custom*/)
7685
ups.Unlock()
7786
}
7887
return
7988
}
8089

81-
func (ups *uploads) abort(id string, lom *core.LOM) (ecode int, err error) {
90+
func (ups *ups) abort(id string, lom *core.LOM) (ecode int, err error) {
91+
var manifest *core.Ufest
8292
ups.Lock()
83-
manifest, ok := ups.m[id]
93+
up, ok := ups.m[id]
8494
if !ok {
8595
ups.Unlock()
8696
manifest, err = ups.fromFS(id, lom, false /*add*/)
8797
if err != nil {
8898
return 0, err
8999
}
90100
} else {
101+
manifest = up.u
91102
delete(ups.m, id)
92103
ups.Unlock()
93104
}
@@ -100,17 +111,17 @@ func (ups *uploads) abort(id string, lom *core.LOM) (ecode int, err error) {
100111
return 0, nil
101112
}
102113

103-
func (ups *uploads) toSlice() (all []*core.Ufest) {
114+
func (ups *ups) toSlice() (all []*core.Ufest) {
104115
ups.RLock()
105116
all = make([]*core.Ufest, 0, len(ups.m))
106-
for _, manifest := range ups.m {
107-
all = append(all, manifest)
117+
for _, up := range ups.m {
118+
all = append(all, up.u)
108119
}
109120
ups.RUnlock()
110121
return
111122
}
112123

113-
func (ups *uploads) del(id string) {
124+
func (ups *ups) del(id string) {
114125
ups.Lock()
115126
delete(ups.m, id)
116127
ups.Unlock()
@@ -120,7 +131,7 @@ func (ups *uploads) del(id string) {
120131
// backend operations - encapsulate IsRemoteS3/IsRemoteOCI pattern
121132
//
122133

123-
func (ups *uploads) start(w http.ResponseWriter, r *http.Request, lom *core.LOM, q url.Values) (uploadID string, metadata map[string]string, err error) {
134+
func (ups *ups) start(w http.ResponseWriter, r *http.Request, lom *core.LOM, q url.Values) (uploadID string, metadata map[string]string, err error) {
124135
var (
125136
ecode int
126137
bck = lom.Bck()
@@ -141,7 +152,7 @@ func (ups *uploads) start(w http.ResponseWriter, r *http.Request, lom *core.LOM,
141152
return
142153
}
143154

144-
func (ups *uploads) putPartRemote(lom *core.LOM, reader io.ReadCloser, r *http.Request, q url.Values, uploadID string, expectedSize int64, partNum int32) (etag string, ecode int, err error) {
155+
func (ups *ups) putPartRemote(lom *core.LOM, reader io.ReadCloser, r *http.Request, q url.Values, uploadID string, expectedSize int64, partNum int32) (etag string, ecode int, err error) {
145156
bck := lom.Bck()
146157
if bck.IsRemoteS3() {
147158
etag, ecode, err = backend.PutMptPartAWS(lom, reader, r, q, uploadID, expectedSize, partNum)
@@ -152,7 +163,7 @@ func (ups *uploads) putPartRemote(lom *core.LOM, reader io.ReadCloser, r *http.R
152163
return
153164
}
154165

155-
func (ups *uploads) completeRemote(w http.ResponseWriter, r *http.Request, lom *core.LOM, q url.Values, uploadID string, body []byte, partList *s3.CompleteMptUpload) (etag string, err error) {
166+
func (ups *ups) completeRemote(w http.ResponseWriter, r *http.Request, lom *core.LOM, q url.Values, uploadID string, body []byte, partList *s3.CompleteMptUpload) (etag string, err error) {
156167
var (
157168
ecode int
158169
provider string
@@ -178,7 +189,7 @@ func (ups *uploads) completeRemote(w http.ResponseWriter, r *http.Request, lom *
178189
return
179190
}
180191

181-
func (ups *uploads) abortRemote(w http.ResponseWriter, r *http.Request, lom *core.LOM, q url.Values, uploadID string) (err error) {
192+
func (ups *ups) abortRemote(w http.ResponseWriter, r *http.Request, lom *core.LOM, q url.Values, uploadID string) (err error) {
182193
var (
183194
ecode int
184195
bck = lom.Bck()
@@ -194,7 +205,7 @@ func (ups *uploads) abortRemote(w http.ResponseWriter, r *http.Request, lom *cor
194205
return
195206
}
196207

197-
func (*uploads) encodeRemoteMetadata(lom *core.LOM, metadata map[string]string) (md map[string]string) {
208+
func (*ups) encodeRemoteMetadata(lom *core.LOM, metadata map[string]string) (md map[string]string) {
198209
bck := lom.Bck()
199210
if bck.IsRemoteS3() {
200211
md = cmn.BackendHelpers.Amazon.EncodeMetadata(metadata)
@@ -209,7 +220,7 @@ func (*uploads) encodeRemoteMetadata(lom *core.LOM, metadata map[string]string)
209220
// misc
210221
//
211222

212-
func (*uploads) parsePartNum(s string) (int32, error) {
223+
func (*ups) parsePartNum(s string) (int32, error) {
213224
partNum, err := strconv.ParseInt(s, 10, 32)
214225
if err != nil {
215226
err = fmt.Errorf("invalid part number %q (must be in 1-%d range): %v", s, maxPartsPerUpload, err)

ais/tgts3mpt.go

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -324,7 +324,7 @@ func (t *target) completeMpt(w http.ResponseWriter, r *http.Request, items []str
324324
}
325325

326326
// TODO: compare with listMptUploads (that does fromFS)
327-
manifest := t.ups.get(uploadID)
327+
manifest, metadata := t.ups.getWithMeta(uploadID)
328328
if manifest == nil {
329329
s3.WriteMptErr(w, r, s3.NewErrNoSuchUpload(uploadID, nil), http.StatusNotFound, lom, uploadID)
330330
return
@@ -378,7 +378,6 @@ func (t *target) completeMpt(w http.ResponseWriter, r *http.Request, items []str
378378
// compute "whole" checksum // TODO -- FIXME: sha256 may take precedence when implied by partSHA (see above)
379379
var (
380380
wholeCksum *cos.CksumHash
381-
metadata = manifest.GetMeta()
382381
remote = bck.IsRemoteS3() || bck.IsRemoteOCI()
383382
)
384383
if remote && lom.CksumConf().Type != cos.ChecksumNone {
@@ -395,7 +394,7 @@ func (t *target) completeMpt(w http.ResponseWriter, r *http.Request, items []str
395394
lom.SetCksum(&wholeCksum.Cksum)
396395
}
397396

398-
// compute ETag if need be
397+
// compute multipart-compliant ETag if need be
399398
var etag string
400399
if !remote {
401400
var err error

0 commit comments

Comments
 (0)