github.com/NVIDIA/aistore@v1.3.23-0.20240517131212-7df6609be51d/ais/test/cp_multiobj_test.go (about) 1 // Package integration_test. 2 /* 3 * Copyright (c) 2021-2024, NVIDIA CORPORATION. All rights reserved. 4 */ 5 package integration_test 6 7 import ( 8 "fmt" 9 "math/rand" 10 "sync/atomic" 11 "testing" 12 "time" 13 14 "github.com/NVIDIA/aistore/api" 15 "github.com/NVIDIA/aistore/api/apc" 16 "github.com/NVIDIA/aistore/cmn" 17 "github.com/NVIDIA/aistore/cmn/cos" 18 "github.com/NVIDIA/aistore/cmn/mono" 19 "github.com/NVIDIA/aistore/core/meta" 20 "github.com/NVIDIA/aistore/tools" 21 "github.com/NVIDIA/aistore/tools/readers" 22 "github.com/NVIDIA/aistore/tools/tassert" 23 "github.com/NVIDIA/aistore/tools/tlog" 24 "github.com/NVIDIA/aistore/tools/trand" 25 "github.com/NVIDIA/aistore/xact" 26 ) 27 28 // TODO -- FIXME: randomize range, check prefix for `xs.iteratePrefix` 29 // NOTE: `from` is a cloud bucket, if exists 30 func TestCopyMultiObjSimple(t *testing.T) { 31 const ( 32 copyCnt = 20 33 objSize = 128 34 cksumType = cos.ChecksumXXHash 35 ) 36 var ( 37 objCnt = 2345 38 proxyURL = tools.RandomProxyURL(t) 39 bckFrom cmn.Bck 40 bckTo = cmn.Bck{Name: "cp-range-to", Provider: apc.AIS} 41 baseParams = tools.BaseAPIParams(proxyURL) 42 xid string 43 err error 44 exists bool 45 ) 46 if cliBck.IsRemote() { 47 if exists, _ = tools.BucketExists(nil, proxyURL, cliBck); exists { 48 bckFrom = cliBck 49 objCnt = 40 50 } 51 } 52 if !exists { 53 bckFrom = cmn.Bck{Name: "cp-range-from", Provider: apc.AIS} 54 tools.CreateBucket(t, proxyURL, bckFrom, nil, true /*cleanup*/) 55 } 56 objList := make([]string, 0, objCnt) 57 tlog.Logf("exists = %t\n", exists) 58 59 tools.CreateBucket(t, proxyURL, bckTo, nil, true /*cleanup*/) 60 for i := range objCnt { 61 objList = append(objList, fmt.Sprintf("test/a-%04d", i)) 62 } 63 for range 5 { 64 tlog.Logf("PUT %d => %s\n", len(objList), bckFrom.Cname("")) 65 for _, objName := range objList { 66 r, _ := readers.NewRand(objSize, cksumType) 67 _, err := api.PutObject(&api.PutArgs{ 68 BaseParams: baseParams, 69 Bck: bckFrom, 70 ObjName: objName, 71 Reader: r, 72 Size: objSize, 73 }) 74 tassert.CheckFatal(t, err) 75 } 76 77 rangeStart := 10 // rand.Intn(objCnt - copyCnt - 1) 78 template := "test/a-" + fmt.Sprintf("{%04d..%04d}", rangeStart, rangeStart+copyCnt-1) 79 tlog.Logf("[%s] %s => %s\n", template, bckFrom.Cname(""), bckTo.Cname("")) 80 81 msg := cmn.TCObjsMsg{ToBck: bckTo} 82 msg.Template = template 83 xid, err = api.CopyMultiObj(baseParams, bckFrom, &msg) 84 tassert.CheckFatal(t, err) 85 } 86 87 wargs := xact.ArgsMsg{ID: xid, Kind: apc.ActCopyObjects} 88 api.WaitForXactionIdle(baseParams, &wargs) 89 90 tlog.Logln("prefix: test/") 91 msg := &apc.LsoMsg{Prefix: "test/"} 92 lst, err := api.ListObjects(baseParams, bckTo, msg, api.ListArgs{}) 93 tassert.CheckFatal(t, err) 94 tassert.Fatalf(t, len(lst.Entries) == copyCnt, "%d != %d", copyCnt, len(lst.Entries)) 95 rangeStart := 10 // rand.Intn(objCnt - copyCnt - 1) 96 for i := rangeStart; i < rangeStart+copyCnt; i++ { 97 objName := fmt.Sprintf("test/a-%04d", i) 98 err := api.DeleteObject(baseParams, bckTo, objName) 99 tassert.CheckError(t, err) 100 tlog.Logf("%s\n", bckTo.Cname(objName)) 101 } 102 } 103 104 func TestCopyMultiObj(t *testing.T) { 105 runProviderTests(t, func(t *testing.T, bck *meta.Bck) { 106 testCopyMobj(t, bck) 107 }) 108 } 109 110 func testCopyMobj(t *testing.T, bck *meta.Bck) { 111 const objCnt = 200 112 var ( 113 proxyURL = tools.RandomProxyURL(t) 114 baseParams = tools.BaseAPIParams(proxyURL) 115 116 m = ioContext{ 117 t: t, 118 bck: bck.Clone(), 119 num: objCnt, 120 prefix: "copy-multiobj/", 121 ordered: true, 122 } 123 bckTo = cmn.Bck{Name: trand.String(10), Provider: apc.AIS} 124 numToCopy = min(m.num/2, 13) 125 fmtRange = "%s{%d..%d}" 126 // test randomization 127 maybe = mono.NanoTime()&0x1 != 0 128 maybeNot = !maybe 129 // 130 // total tests = (num runProviderTests) * (num subtests) 131 // 132 subtests = []struct { 133 list bool 134 createDst bool 135 evictRemoteSrc bool 136 }{ 137 {list: true, createDst: true}, 138 {list: false, createDst: true}, 139 140 {list: maybe, createDst: false}, 141 {list: maybeNot, createDst: false, evictRemoteSrc: true}, 142 } 143 ) 144 for _, test := range subtests { 145 tname := "list" 146 if !test.list { 147 tname = "range" 148 } 149 if test.createDst { 150 tname += "/dst" 151 } else { 152 tname += "/no-dst" 153 } 154 if m.bck.IsRemote() && test.evictRemoteSrc { 155 tname += "/evict-remote-src" 156 } 157 t.Run(tname, func(t *testing.T) { 158 m.init(true /*cleanup*/) 159 if m.bck.IsCloud() { 160 defer m.del() 161 } 162 if !bckTo.Equal(&m.bck) && bckTo.IsAIS() { 163 if test.createDst { 164 tools.CreateBucket(t, proxyURL, bckTo, nil, true /*cleanup*/) 165 } else { 166 t.Cleanup(func() { 167 tools.DestroyBucket(t, proxyURL, bckTo) 168 }) 169 } 170 } 171 172 m.puts() 173 174 if m.bck.IsRemote() && test.evictRemoteSrc { 175 tlog.Logf("evicting %s\n", m.bck) 176 // 177 // evict all _cached_ data from the "local" cluster 178 // keep the src bucket in the "local" BMD though 179 // 180 err := api.EvictRemoteBucket(baseParams, m.bck, true) 181 tassert.CheckFatal(t, err) 182 } 183 184 tlog.Logf("%s: %s => %s %d objects\n", t.Name(), m.bck, bckTo, numToCopy) 185 var erv atomic.Value 186 if test.list { 187 for i := 0; i < numToCopy && erv.Load() == nil; i++ { 188 list := make([]string, 0, numToCopy) 189 flst := func(i int, list []string) { 190 var ( 191 err error 192 xid string 193 msg = cmn.TCObjsMsg{ToBck: bckTo} 194 ) 195 msg.ObjNames = list 196 if m.bck.IsRemote() && test.evictRemoteSrc { 197 xid, err = api.CopyMultiObj(baseParams, m.bck, &msg, apc.FltExists) 198 } else { 199 xid, err = api.CopyMultiObj(baseParams, m.bck, &msg) 200 } 201 if err != nil { 202 erv.Store(err) 203 } else { 204 tlog.Logf("[%s] %2d: cp list %d objects\n", xid, i, numToCopy) 205 } 206 } 207 for range numToCopy { 208 list = append(list, m.objNames[rand.Intn(m.num)]) 209 } 210 if !test.createDst && i == 0 { 211 // serialize the very first batch as it entails creating destination bucket 212 // behind the scenes 213 flst(i, list) 214 time.Sleep(time.Second) 215 continue 216 } 217 go flst(i, list) 218 } 219 } else { 220 for i := 0; i < numToCopy && erv.Load() == nil; i++ { 221 start := rand.Intn(m.num - numToCopy) 222 ftmpl := func(start int, i int) { 223 var ( 224 err error 225 xid string 226 template = fmt.Sprintf(fmtRange, m.prefix, start, start+numToCopy-1) 227 msg = cmn.TCObjsMsg{ToBck: bckTo} 228 ) 229 msg.Template = template 230 if m.bck.IsRemote() && test.evictRemoteSrc { 231 xid, err = api.CopyMultiObj(baseParams, m.bck, &msg, apc.FltExists) 232 } else { 233 xid, err = api.CopyMultiObj(baseParams, m.bck, &msg) 234 } 235 if err != nil { 236 erv.Store(err) 237 } else { 238 tlog.Logf("[%s] %2d: cp range [%s]\n", xid, i, template) 239 } 240 } 241 if !test.createDst && i == 0 { 242 // (ditto serialize) 243 ftmpl(start, i) 244 time.Sleep(time.Second) 245 continue 246 } 247 go ftmpl(start, i) 248 } 249 } 250 if erv.Load() != nil { 251 tassert.CheckFatal(t, erv.Load().(error)) 252 } 253 wargs := xact.ArgsMsg{Kind: apc.ActCopyObjects, Bck: m.bck} 254 api.WaitForXactionIdle(baseParams, &wargs) 255 256 msg := &apc.LsoMsg{Prefix: m.prefix} 257 msg.AddProps(apc.GetPropsName, apc.GetPropsSize) 258 objList, err := api.ListObjects(baseParams, bckTo, msg, api.ListArgs{}) 259 tassert.CheckFatal(t, err) 260 tlog.Logf("Total (`ls %s/%s*`): %d objects\n", bckTo, m.prefix, len(objList.Entries)) 261 }) 262 } 263 }