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  }