github.com/Cloud-Foundations/Dominator@v0.3.4/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.params.Logger.Println(txt) 36 return errors.New(txt) 37 } 38 if err := t.getFetchLock(conn, request); err != nil { 39 return err 40 } 41 if request.Wait { 42 return t.fetchAndUnlock(conn, request, conn.Username()) 43 } 44 go t.fetchAndUnlock(conn, request, conn.Username()) 45 return nil 46 } 47 48 func (t *rpcType) getFetchLock(conn *srpc.Conn, 49 request sub.FetchRequest) error { 50 t.rwLock.Lock() 51 defer t.rwLock.Unlock() 52 if err := t.getClientLock(conn, request.LockFor); err != nil { 53 t.params.Logger.Printf("Error: %s\n", err) 54 return err 55 } 56 if t.fetchInProgress { 57 t.params.Logger.Println("Error: fetch already in progress") 58 return errors.New("fetch already in progress") 59 } 60 if t.updateInProgress { 61 t.params.Logger.Println("Error: update in progress") 62 return errors.New("update in progress") 63 } 64 t.fetchInProgress = true 65 return nil 66 } 67 68 func (t *rpcType) fetchAndUnlock(conn *srpc.Conn, request sub.FetchRequest, 69 username string) error { 70 err := t.doFetch(request, username) 71 if err != nil && *exitOnFetchFailure { 72 os.Exit(1) 73 } 74 t.rwLock.Lock() 75 defer t.rwLock.Unlock() 76 t.fetchInProgress = false 77 t.lastFetchError = err 78 if err := t.getClientLock(conn, request.LockFor); err != nil { 79 return err 80 } 81 return err 82 } 83 84 func (t *rpcType) doFetch(request sub.FetchRequest, username string) error { 85 objectServer := objectclient.NewObjectClient(request.ServerAddress) 86 defer objectServer.Close() 87 defer t.params.ScannerConfiguration.BoostCpuLimit(t.params.Logger) 88 benchmark := false 89 linkSpeed, haveLinkSpeed := netspeed.GetSpeedToAddress( 90 request.ServerAddress) 91 if haveLinkSpeed { 92 t.logFetch(request, linkSpeed, username) 93 } else { 94 if t.params.NetworkReaderContext.MaximumSpeed() < 1 { 95 benchmark = enoughBytesForBenchmark(objectServer, request) 96 if benchmark { 97 objectServer.SetExclusiveGetObjects(true) 98 var suffix string 99 if username != "" { 100 suffix = " by " + username 101 } 102 t.params.Logger.Printf( 103 "Fetch(%s) %d objects and benchmark speed%s\n", 104 request.ServerAddress, len(request.Hashes), suffix) 105 } else { 106 t.logFetch(request, 0, username) 107 } 108 } else { 109 t.logFetch(request, t.params.NetworkReaderContext.MaximumSpeed(), 110 username) 111 } 112 } 113 objectsReader, err := objectServer.GetObjects(request.Hashes) 114 if err != nil { 115 t.params.Logger.Printf("Error getting object reader: %s\n", err.Error()) 116 return err 117 } 118 defer objectsReader.Close() 119 var totalLength uint64 120 defer t.params.WorkdirGoroutine.Run(t.params.RescanObjectCacheFunction) 121 timeStart := time.Now() 122 for _, hash := range request.Hashes { 123 length, reader, err := objectsReader.NextObject() 124 if err != nil { 125 t.params.Logger.Println(err) 126 return err 127 } 128 r := io.Reader(reader) 129 if haveLinkSpeed { 130 if linkSpeed > 0 { 131 r = rateio.NewReaderContext(linkSpeed, 132 uint64(t.params.NetworkReaderContext.SpeedPercent()), 133 &rateio.ReadMeasurer{}).NewReader(reader) 134 } 135 } else if !benchmark { 136 r = t.params.NetworkReaderContext.NewReader(reader) 137 } 138 t.params.WorkdirGoroutine.Run(func() { 139 err = readOne(t.config.ObjectsDirectoryName, hash, length, r) 140 }) 141 reader.Close() 142 if err != nil { 143 t.params.Logger.Println(err) 144 return err 145 } 146 totalLength += length 147 } 148 duration := time.Since(timeStart) 149 speed := uint64(float64(totalLength) / duration.Seconds()) 150 if benchmark { 151 file, err := os.Create(t.config.NetworkBenchmarkFilename) 152 if err == nil { 153 fmt.Fprintf(file, "%d\n", speed) 154 file.Close() 155 } 156 t.params.NetworkReaderContext.InitialiseMaximumSpeed(speed) 157 } 158 t.params.Logger.Printf("Fetch() complete. Read: %s in %s (%s/s)\n", 159 format.FormatBytes(totalLength), format.Duration(duration), 160 format.FormatBytes(speed)) 161 return nil 162 } 163 164 func (t *rpcType) logFetch(request sub.FetchRequest, speed uint64, 165 username string) { 166 speedString := "unlimited speed" 167 if speed > 0 { 168 speedString = format.FormatBytes( 169 speed*uint64( 170 t.params.NetworkReaderContext.SpeedPercent())/100) + "/s" 171 } 172 var suffix string 173 if username != "" { 174 suffix = " by " + username 175 } 176 t.params.Logger.Printf("Fetch(%s) %d objects at %s%s\n", 177 request.ServerAddress, len(request.Hashes), speedString, suffix) 178 } 179 180 func enoughBytesForBenchmark(objectServer *objectclient.ObjectClient, 181 request sub.FetchRequest) bool { 182 lengths, err := objectServer.CheckObjects(request.Hashes) 183 if err != nil { 184 return false 185 } 186 var totalLength uint64 187 for _, length := range lengths { 188 totalLength += length 189 } 190 if totalLength > 1024*1024*64 { 191 return true 192 } 193 return false 194 } 195 196 func readOne(objectsDir string, hash hash.Hash, length uint64, 197 reader io.Reader) error { 198 filename := path.Join(objectsDir, objectcache.HashToFilename(hash)) 199 dirname := path.Dir(filename) 200 if err := os.MkdirAll(dirname, syscall.S_IRWXU); err != nil { 201 return err 202 } 203 return fsutil.CopyToFile(filename, filePerms, reader, length) 204 }