github.com/cloud-foundations/dominator@v0.0.0-20221004181915-6e4fee580046/sub/rpcd/fetch.go (about) 1 package rpcd 2 3 import ( 4 "errors" 5 "flag" 6 "fmt" 7 "io" 8 "os" 9 "path" 10 "syscall" 11 "time" 12 13 "github.com/Cloud-Foundations/Dominator/lib/format" 14 "github.com/Cloud-Foundations/Dominator/lib/fsutil" 15 "github.com/Cloud-Foundations/Dominator/lib/hash" 16 "github.com/Cloud-Foundations/Dominator/lib/netspeed" 17 "github.com/Cloud-Foundations/Dominator/lib/objectcache" 18 objectclient "github.com/Cloud-Foundations/Dominator/lib/objectserver/client" 19 "github.com/Cloud-Foundations/Dominator/lib/rateio" 20 "github.com/Cloud-Foundations/Dominator/lib/srpc" 21 "github.com/Cloud-Foundations/Dominator/proto/sub" 22 ) 23 24 const filePerms = syscall.S_IRUSR | syscall.S_IWUSR | syscall.S_IRGRP 25 26 var ( 27 exitOnFetchFailure = flag.Bool("exitOnFetchFailure", false, 28 "If true, exit if there are fetch failures. For debugging only") 29 ) 30 31 func (t *rpcType) Fetch(conn *srpc.Conn, request sub.FetchRequest, 32 reply *sub.FetchResponse) error { 33 if *readOnly { 34 txt := "Fetch() rejected due to read-only mode" 35 t.logger.Println(txt) 36 return errors.New(txt) 37 } 38 if err := t.getFetchLock(); err != nil { 39 return err 40 } 41 if request.Wait { 42 return t.fetchAndUnlock(request) 43 } 44 go t.fetchAndUnlock(request) 45 return nil 46 } 47 48 func (t *rpcType) getFetchLock() error { 49 t.rwLock.Lock() 50 defer t.rwLock.Unlock() 51 if t.fetchInProgress { 52 t.logger.Println("Error: fetch already in progress") 53 return errors.New("fetch already in progress") 54 } 55 if t.updateInProgress { 56 t.logger.Println("Error: update in progress") 57 return errors.New("update in progress") 58 } 59 t.fetchInProgress = true 60 return nil 61 } 62 63 func (t *rpcType) fetchAndUnlock(request sub.FetchRequest) error { 64 err := t.doFetch(request) 65 if err != nil && *exitOnFetchFailure { 66 os.Exit(1) 67 } 68 t.rwLock.Lock() 69 defer t.rwLock.Unlock() 70 t.lastFetchError = err 71 return err 72 } 73 74 func (t *rpcType) doFetch(request sub.FetchRequest) error { 75 defer t.clearFetchInProgress() 76 objectServer := objectclient.NewObjectClient(request.ServerAddress) 77 defer objectServer.Close() 78 defer t.scannerConfiguration.BoostCpuLimit(t.logger) 79 benchmark := false 80 linkSpeed, haveLinkSpeed := netspeed.GetSpeedToAddress( 81 request.ServerAddress) 82 if haveLinkSpeed { 83 t.logFetch(request, linkSpeed) 84 } else { 85 if t.networkReaderContext.MaximumSpeed() < 1 { 86 benchmark = enoughBytesForBenchmark(objectServer, request) 87 if benchmark { 88 objectServer.SetExclusiveGetObjects(true) 89 t.logger.Printf("Fetch(%s) %d objects and benchmark speed\n", 90 request.ServerAddress, len(request.Hashes)) 91 } else { 92 t.logFetch(request, 0) 93 } 94 } else { 95 t.logFetch(request, t.networkReaderContext.MaximumSpeed()) 96 } 97 } 98 objectsReader, err := objectServer.GetObjects(request.Hashes) 99 if err != nil { 100 t.logger.Printf("Error getting object reader: %s\n", err.Error()) 101 return err 102 } 103 defer objectsReader.Close() 104 var totalLength uint64 105 defer t.rescanObjectCacheFunction() 106 timeStart := time.Now() 107 for _, hash := range request.Hashes { 108 length, reader, err := objectsReader.NextObject() 109 if err != nil { 110 t.logger.Println(err) 111 return err 112 } 113 r := io.Reader(reader) 114 if haveLinkSpeed { 115 if linkSpeed > 0 { 116 r = rateio.NewReaderContext(linkSpeed, 117 uint64(t.networkReaderContext.SpeedPercent()), 118 &rateio.ReadMeasurer{}).NewReader(reader) 119 } 120 } else if !benchmark { 121 r = t.networkReaderContext.NewReader(reader) 122 } 123 err = readOne(t.objectsDir, hash, length, r) 124 reader.Close() 125 if err != nil { 126 t.logger.Println(err) 127 return err 128 } 129 totalLength += length 130 } 131 duration := time.Since(timeStart) 132 speed := uint64(float64(totalLength) / duration.Seconds()) 133 if benchmark { 134 file, err := os.Create(t.netbenchFilename) 135 if err == nil { 136 fmt.Fprintf(file, "%d\n", speed) 137 file.Close() 138 } 139 t.networkReaderContext.InitialiseMaximumSpeed(speed) 140 } 141 t.logger.Printf("Fetch() complete. Read: %s in %s (%s/s)\n", 142 format.FormatBytes(totalLength), format.Duration(duration), 143 format.FormatBytes(speed)) 144 return nil 145 } 146 147 func (t *rpcType) logFetch(request sub.FetchRequest, speed uint64) { 148 speedString := "unlimited speed" 149 if speed > 0 { 150 speedString = format.FormatBytes( 151 speed*uint64(t.networkReaderContext.SpeedPercent())/100) + "/s" 152 } 153 t.logger.Printf("Fetch(%s) %d objects at %s\n", 154 request.ServerAddress, len(request.Hashes), speedString) 155 } 156 157 func enoughBytesForBenchmark(objectServer *objectclient.ObjectClient, 158 request sub.FetchRequest) bool { 159 lengths, err := objectServer.CheckObjects(request.Hashes) 160 if err != nil { 161 return false 162 } 163 var totalLength uint64 164 for _, length := range lengths { 165 totalLength += length 166 } 167 if totalLength > 1024*1024*64 { 168 return true 169 } 170 return false 171 } 172 173 func readOne(objectsDir string, hash hash.Hash, length uint64, 174 reader io.Reader) error { 175 filename := path.Join(objectsDir, objectcache.HashToFilename(hash)) 176 dirname := path.Dir(filename) 177 if err := os.MkdirAll(dirname, syscall.S_IRWXU); err != nil { 178 return err 179 } 180 return fsutil.CopyToFile(filename, filePerms, reader, length) 181 } 182 183 func (t *rpcType) clearFetchInProgress() { 184 t.rwLock.Lock() 185 defer t.rwLock.Unlock() 186 t.fetchInProgress = false 187 }