github.com/Cloud-Foundations/Dominator@v0.3.4/lib/objectserver/cachingreader/get.go (about) 1 package cachingreader 2 3 import ( 4 "io" 5 "io/ioutil" 6 "os" 7 "path/filepath" 8 "runtime" 9 "syscall" 10 "time" 11 12 "github.com/Cloud-Foundations/Dominator/lib/format" 13 "github.com/Cloud-Foundations/Dominator/lib/fsutil" 14 "github.com/Cloud-Foundations/Dominator/lib/hash" 15 "github.com/Cloud-Foundations/Dominator/lib/objectcache" 16 "github.com/Cloud-Foundations/Dominator/lib/objectserver" 17 "github.com/Cloud-Foundations/Dominator/lib/objectserver/client" 18 ) 19 20 const ( 21 dirPerms = syscall.S_IRWXU | syscall.S_IRGRP | syscall.S_IXGRP | 22 syscall.S_IROTH | syscall.S_IXOTH 23 ) 24 25 type objectsReader struct { 26 downloadedObjects uint 27 downloadedBytes uint64 28 completionChannels map[<-chan struct{}]chan<- struct{} 29 objectsToRead []*objectType // nil: read from upstream, no caching. 30 objectClient *client.ObjectClient 31 objectsReader objectserver.FullObjectsReader 32 objSrv *ObjectServer 33 totalBytes uint64 34 totalObjects uint 35 waitedObjects uint 36 waitedBytes uint64 37 } 38 39 type readerObject struct { 40 object *objectType 41 file *os.File 42 objSrv *ObjectServer 43 } 44 45 func saveObject(filename string, 46 objectsReader objectserver.ObjectsReader) error { 47 if size, reader, err := objectsReader.NextObject(); err != nil { 48 return err 49 } else { 50 defer reader.Close() 51 err := fsutil.CopyToFile(filename, privateFilePerms, reader, size) 52 if err == nil { 53 return nil 54 } 55 if !os.IsNotExist(err) { 56 return err 57 } 58 if err := os.MkdirAll(filepath.Dir(filename), dirPerms); err != nil { 59 return err 60 } 61 return fsutil.CopyToFile(filename, privateFilePerms, reader, size) 62 } 63 } 64 65 func (objSrv *ObjectServer) fetchObjects(hashes []hash.Hash) error { 66 or, err := objSrv.getObjects(hashes) 67 if err != nil { 68 return err 69 } 70 for range hashes { 71 length, reader, err := or.nextObject(true) 72 if err != nil { 73 return err 74 } 75 if reader != nil { 76 _, err := io.CopyN(ioutil.Discard, reader, int64(length)) 77 if err != nil { 78 reader.Close() 79 return err 80 } 81 if err := reader.Close(); err != nil { 82 return err 83 } 84 } 85 } 86 return nil 87 } 88 89 func (objSrv *ObjectServer) getObjects(hashes []hash.Hash) ( 90 *objectsReader, error) { 91 or := objectsReader{ 92 completionChannels: make(map[<-chan struct{}]chan<- struct{}, 93 len(hashes)), 94 objSrv: objSrv, 95 objectsToRead: make([]*objectType, len(hashes)), 96 } 97 var hashesToFetch []hash.Hash 98 var fetchToReadIndex []int 99 objSrv.rwLock.Lock() 100 defer objSrv.rwLock.Unlock() 101 for index, hashVal := range hashes { 102 if object := objSrv.getObjectWithLock(hashVal); object == nil { 103 fetchToReadIndex = append(fetchToReadIndex, index) 104 hashesToFetch = append(hashesToFetch, hashVal) 105 } else { 106 or.objectsToRead[index] = object 107 } 108 } 109 if len(hashesToFetch) < 1 { 110 return &or, nil 111 } 112 or.objectClient = client.NewObjectClient(objSrv.objectServerAddress) 113 if realOR, err := or.objectClient.GetObjects(hashesToFetch); err != nil { 114 or.Close() 115 return nil, err 116 } else { 117 or.objectsReader = realOR.(objectserver.FullObjectsReader) 118 } 119 sizes := or.objectsReader.ObjectSizes() 120 for index, hashVal := range hashesToFetch { 121 size := sizes[index] 122 if !objSrv.releaseSpaceWithLock(size) { 123 continue // Too large: read directly without caching. 124 } 125 if _, ok := objSrv.objects[hashVal]; ok { 126 // Duplicate fetch of same hash: read directly since we're committed 127 // to read it. 128 or.objectsToRead[fetchToReadIndex[index]] = nil 129 continue 130 } 131 completionChannel := make(chan struct{}) 132 or.completionChannels[completionChannel] = completionChannel 133 object := &objectType{ 134 hash: hashVal, 135 size: size, 136 downloadingChannel: completionChannel, 137 usageCount: 1, 138 } 139 or.objectsToRead[fetchToReadIndex[index]] = object 140 objSrv.data.DownloadingBytes += size 141 objSrv.objects[hashVal] = object 142 } 143 return &or, nil 144 } 145 146 func (objSrv *ObjectServer) getStats(grabLock bool) Stats { 147 if grabLock { 148 objSrv.rwLock.RLock() 149 defer objSrv.rwLock.RUnlock() 150 } 151 return objSrv.data 152 } 153 154 func (or *objectsReader) Close() error { 155 or.objSrv.logger.Printf( 156 "objectcache: total: %d (%s), downloaded: %d (%s), waited: %d (%s)\n", 157 or.totalObjects, format.FormatBytes(or.totalBytes), 158 or.downloadedObjects, format.FormatBytes(or.downloadedBytes), 159 or.waitedObjects, format.FormatBytes(or.waitedBytes)) 160 timeoutFunction(or.objSrv.rwLock.Lock, time.Second*10) 161 for _, object := range or.objectsToRead { 162 if object != nil { 163 or.objSrv.putObjectWithLock(object) 164 } 165 } 166 or.objSrv.rwLock.Unlock() 167 if or.objectClient == nil { 168 return nil 169 } 170 var err error 171 if e := or.objectsReader.Close(); err == nil && e != nil { 172 err = e 173 } 174 if e := or.objectClient.Close(); err == nil && e != nil { 175 err = e 176 } 177 if err != nil { 178 return err 179 } 180 return nil 181 } 182 183 func (or *objectsReader) NextObject() (uint64, io.ReadCloser, error) { 184 return or.nextObject(false) 185 } 186 187 func (or *objectsReader) nextObject(skipOpen bool) ( 188 uint64, io.ReadCloser, error) { 189 if len(or.objectsToRead) < 1 { 190 return 0, nil, io.EOF 191 } 192 object := or.objectsToRead[0] 193 or.objectsToRead = or.objectsToRead[1:] 194 if object == nil { // No caching. 195 return or.objectsReader.NextObject() 196 } 197 filename := filepath.Join(or.objSrv.baseDir, 198 objectcache.HashToFilename(object.hash)) 199 or.objSrv.rwLock.RLock() 200 downloadingChannel := object.downloadingChannel 201 or.objSrv.rwLock.RUnlock() 202 if downloadingChannel != nil { 203 completionChannel := or.completionChannels[downloadingChannel] 204 if completionChannel != nil { // I am the downloader. 205 err := saveObject(filename, or.objectsReader) 206 or.objSrv.rwLock.Lock() 207 object.downloadingChannel = nil 208 if err == nil { 209 or.objSrv.data.CachedBytes += object.size 210 } else { 211 delete(or.objSrv.objects, object.hash) 212 object.usageCount-- 213 } 214 or.objSrv.data.DownloadingBytes -= object.size 215 or.objSrv.rwLock.Unlock() 216 close(completionChannel) 217 if err != nil { 218 return 0, nil, err 219 } 220 or.downloadedObjects++ 221 or.downloadedBytes += object.size 222 } else { // Someone else is the downloader. 223 <-downloadingChannel // It's still downloading: wait. 224 or.waitedObjects++ 225 or.waitedBytes += object.size 226 } 227 } 228 if skipOpen { 229 or.totalObjects++ 230 or.totalBytes += object.size 231 return object.size, nil, nil 232 } 233 if file, err := os.Open(filename); err != nil { 234 return 0, nil, err 235 } else { 236 or.totalObjects++ 237 or.totalBytes += object.size 238 return object.size, &readerObject{object, file, or.objSrv}, nil 239 } 240 } 241 242 func (ro *readerObject) Close() error { 243 err := ro.file.Close() 244 ro.objSrv.rwLock.Lock() 245 ro.objSrv.putObjectWithLock(ro.object) 246 ro.objSrv.rwLock.Unlock() 247 return err 248 } 249 250 func (ro *readerObject) Read(p []byte) (int, error) { 251 return ro.file.Read(p) 252 } 253 254 func timeoutFunction(f func(), timeout time.Duration) { 255 if timeout < 0 { 256 f() 257 return 258 } 259 completionChannel := make(chan struct{}, 1) 260 go func() { 261 f() 262 completionChannel <- struct{}{} 263 }() 264 timer := time.NewTimer(timeout) 265 select { 266 case <-completionChannel: 267 if !timer.Stop() { 268 <-timer.C 269 } 270 return 271 case <-timer.C: 272 os.Stderr.Write([]byte("lock timeout. Full stack trace follows:\n")) 273 buf := make([]byte, 1024*1024) 274 nBytes := runtime.Stack(buf, true) 275 os.Stderr.Write(buf[0:nBytes]) 276 os.Stderr.Write([]byte("\n")) 277 panic("timeout") 278 } 279 }