github.com/NVIDIA/aistore@v1.3.23-0.20240517131212-7df6609be51d/transport/stream_bundle_test.go (about) 1 // Package transport provides long-lived http/tcp connections for 2 // intra-cluster communications (see README for details and usage example). 3 /* 4 * Copyright (c) 2018-2024, NVIDIA CORPORATION. All rights reserved. 5 */ 6 package transport_test 7 8 import ( 9 "fmt" 10 "io" 11 "net/http/httptest" 12 "strconv" 13 "testing" 14 "time" 15 16 "github.com/NVIDIA/aistore/api/apc" 17 "github.com/NVIDIA/aistore/cmn" 18 "github.com/NVIDIA/aistore/cmn/atomic" 19 "github.com/NVIDIA/aistore/cmn/cos" 20 "github.com/NVIDIA/aistore/cmn/mono" 21 "github.com/NVIDIA/aistore/core" 22 "github.com/NVIDIA/aistore/core/meta" 23 "github.com/NVIDIA/aistore/core/mock" 24 "github.com/NVIDIA/aistore/memsys" 25 "github.com/NVIDIA/aistore/tools/tassert" 26 "github.com/NVIDIA/aistore/tools/tlog" 27 "github.com/NVIDIA/aistore/transport" 28 "github.com/NVIDIA/aistore/transport/bundle" 29 ) 30 31 type ( 32 sowner struct{} 33 slisteners struct{} 34 ) 35 36 var ( 37 smap meta.Smap 38 listeners slisteners 39 ) 40 41 func (*sowner) Get() *meta.Smap { return &smap } 42 func (*sowner) Listeners() meta.SmapListeners { return &listeners } 43 44 func (*slisteners) Reg(meta.Slistener) {} 45 func (*slisteners) Unreg(meta.Slistener) {} 46 47 func TestBundle(t *testing.T) { 48 tests := []struct { 49 name string 50 nvs cos.StrKVs 51 }{ 52 { 53 name: "not-compressed", 54 nvs: cos.StrKVs{ 55 "compression": apc.CompressNever, 56 }, 57 }, 58 { 59 name: "not-compressed-unsized", 60 nvs: cos.StrKVs{ 61 "compression": apc.CompressNever, 62 "unsized": "yes", 63 }, 64 }, 65 } 66 if !testing.Short() { 67 testsLong := []struct { 68 name string 69 nvs cos.StrKVs 70 }{ 71 { 72 name: "compress-block-1M", 73 nvs: cos.StrKVs{ 74 "compression": apc.CompressAlways, 75 "block": "1MiB", 76 }, 77 }, 78 { 79 name: "compress-block-256K", 80 nvs: cos.StrKVs{ 81 "compression": apc.CompressAlways, 82 "block": "256KiB", 83 }, 84 }, 85 { 86 name: "compress-block-256K-unsized", 87 nvs: cos.StrKVs{ 88 "compression": apc.CompressAlways, 89 "block": "256KiB", 90 "unsized": "yes", 91 }, 92 }, 93 } 94 tests = append(tests, testsLong...) 95 } 96 97 tMock := mock.NewTarget(nil) 98 tMock.SO = &sowner{} 99 core.T = tMock 100 101 for _, test := range tests { 102 t.Run(test.name, func(t *testing.T) { 103 testBundle(t, test.nvs) 104 time.Sleep(time.Second) 105 }) 106 } 107 } 108 109 func testBundle(t *testing.T, nvs cos.StrKVs) { 110 var ( 111 numCompleted atomic.Int64 112 mmsa, _ = memsys.NewMMSA("bundle.test", false) 113 network = cmn.NetIntraData 114 trname = "bundle" + nvs["block"] 115 tss = make([]*httptest.Server, 0, 32) 116 ) 117 118 // init local target 119 tMock := mock.NewTarget(nil) 120 tMock.SO = &sowner{} 121 core.T = tMock 122 lsnode := tMock.Snode() 123 124 // add target nodes 125 smap.Tmap = make(meta.NodeMap, 100) 126 smap.Tmap[lsnode.ID()] = lsnode 127 for i := range 10 { 128 ts := httptest.NewServer(objmux) 129 tss = append(tss, ts) 130 addTarget(&smap, ts, i) 131 } 132 defer func() { 133 for _, ts := range tss { 134 ts.Close() 135 } 136 mmsa.Terminate(false) 137 }() 138 smap.Version = 1 139 140 receive := func(hdr *transport.ObjHdr, objReader io.Reader, err error) error { 141 if err != nil && !cos.IsEOF(err) { 142 tassert.CheckFatal(t, err) 143 } 144 written, _ := io.Copy(io.Discard, objReader) 145 cos.Assert(written == hdr.ObjAttrs.Size || hdr.IsUnsized()) 146 return nil 147 } 148 callback := func(*transport.ObjHdr, io.ReadCloser, any, error) { 149 numCompleted.Inc() 150 } 151 152 err := transport.Handle(trname, receive) // URL = /v1/transport/10G 153 tassert.CheckFatal(t, err) 154 defer transport.Unhandle(trname) 155 156 var ( 157 config = cmn.GCO.Get() 158 httpclient = transport.NewIntraDataClient() 159 random = newRand(mono.NanoTime()) 160 wbuf, slab = mmsa.Alloc() 161 extra = &transport.Extra{Compression: nvs["compression"]} 162 size, prevsize int64 163 multiplier = int(random.Int63()%13) + 4 164 num int 165 usePDU bool 166 ) 167 if nvs["compression"] != apc.CompressNever { 168 v, _ := cos.ParseSize(nvs["block"], cos.UnitsIEC) 169 cos.Assert(v == cos.MiB*4 || v == cos.MiB || v == cos.KiB*256 || v == cos.KiB*64) 170 config = cmn.GCO.BeginUpdate() 171 config.Transport.LZ4BlockMaxSize = cos.SizeIEC(v) 172 cmn.GCO.CommitUpdate(config) 173 if err := config.Transport.Validate(); err != nil { 174 tassert.CheckFatal(t, err) 175 } 176 } 177 if _, usePDU = nvs["unsized"]; usePDU { 178 extra.SizePDU = memsys.DefaultBufSize 179 } 180 extra.Config = config 181 _, _ = random.Read(wbuf) 182 sb := bundle.New(httpclient, 183 bundle.Args{Net: network, Trname: trname, Multiplier: multiplier, Extra: extra}) 184 var numGs int64 = 6 185 if testing.Short() { 186 numGs = 1 187 } 188 for size < cos.GiB*numGs { 189 var err error 190 hdr := genRandomHeader(random, usePDU) 191 objSize := hdr.ObjAttrs.Size 192 if num%7 == 0 { 193 objSize, hdr.ObjAttrs.Size = 0, 0 194 err = sb.Send(&transport.Obj{Hdr: hdr, Callback: callback}, nil) 195 } else { 196 reader := &randReader{buf: wbuf, hdr: hdr, slab: slab, clone: true} // FIXME: multiplier reopen 197 if hdr.IsUnsized() { 198 reader.offEOF = int64(random.Int31()>>1) + 1 199 objSize = reader.offEOF 200 } 201 err = sb.Send(&transport.Obj{Hdr: hdr, Callback: callback}, reader) 202 } 203 if err != nil { 204 t.Fatalf("%s: exiting with err [%v]\n", sb, err) 205 } 206 num++ 207 size += objSize 208 if size-prevsize >= cos.GiB { 209 tlog.Logf("%s: %d GiB\n", sb, size/cos.GiB) 210 prevsize = size 211 } 212 } 213 sb.Close(true /* gracefully */) 214 stats := sb.GetStats() 215 216 slab.Free(wbuf) 217 218 if nvs["compression"] != apc.CompressNever { 219 for id, tstat := range stats { 220 fmt.Printf("send$ %s/%s: offset=%d, num=%d(%d), compression-ratio=%.2f\n", 221 id, trname, tstat.Offset.Load(), tstat.Num.Load(), num, tstat.CompressionRatio()) 222 } 223 } else { 224 for id, tstat := range stats { 225 fmt.Printf("send$ %s/%s: offset=%d, num=%d(%d)\n", 226 id, trname, tstat.Offset.Load(), tstat.Num.Load(), num) 227 } 228 } 229 fmt.Printf("send$: num-sent=%d, num-completed=%d\n", num, numCompleted.Load()) 230 } 231 232 func addTarget(smap *meta.Smap, ts *httptest.Server, i int) { 233 netinfo := meta.NetInfo{URL: ts.URL} 234 tid := "t_" + strconv.FormatInt(int64(i), 10) 235 smap.Tmap[tid] = &meta.Snode{PubNet: netinfo, ControlNet: netinfo, DataNet: netinfo} 236 }