github.com/cloud-foundations/dominator@v0.0.0-20221004181915-6e4fee580046/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() 72 if err != nil { 73 return err 74 } 75 if _, ok := reader.(*os.File); !ok { 76 _, err := io.CopyN(ioutil.Discard, reader, int64(length)) 77 if err != nil { 78 reader.Close() 79 return err 80 } 81 } 82 if err := reader.Close(); err != nil { 83 return err 84 } 85 } 86 return nil 87 } 88 89 func (objSrv *ObjectServer) getObjects(hashes []hash.Hash) ( 90 objectserver.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.downloadingBytes += size 141 objSrv.objects[hashVal] = object 142 } 143 return &or, nil 144 } 145 146 func (or *objectsReader) Close() error { 147 or.objSrv.logger.Printf( 148 "objectcache: total: %d (%s), downloaded: %d (%s), waited: %d (%s)\n", 149 or.totalObjects, format.FormatBytes(or.totalBytes), 150 or.downloadedObjects, format.FormatBytes(or.downloadedBytes), 151 or.waitedObjects, format.FormatBytes(or.waitedBytes)) 152 timeoutFunction(or.objSrv.rwLock.Lock, time.Second*10) 153 for _, object := range or.objectsToRead { 154 if object != nil { 155 or.objSrv.putObjectWithLock(object) 156 } 157 } 158 or.objSrv.rwLock.Unlock() 159 if or.objectClient == nil { 160 return nil 161 } 162 var err error 163 if e := or.objectsReader.Close(); err == nil && e != nil { 164 err = e 165 } 166 if e := or.objectClient.Close(); err == nil && e != nil { 167 err = e 168 } 169 if err != nil { 170 return err 171 } 172 return nil 173 } 174 175 func (or *objectsReader) NextObject() (uint64, io.ReadCloser, error) { 176 if len(or.objectsToRead) < 1 { 177 return 0, nil, io.EOF 178 } 179 object := or.objectsToRead[0] 180 or.objectsToRead = or.objectsToRead[1:] 181 if object == nil { // No caching. 182 return or.objectsReader.NextObject() 183 } 184 filename := filepath.Join(or.objSrv.baseDir, 185 objectcache.HashToFilename(object.hash)) 186 or.objSrv.rwLock.RLock() 187 downloadingChannel := object.downloadingChannel 188 or.objSrv.rwLock.RUnlock() 189 if downloadingChannel != nil { 190 completionChannel := or.completionChannels[downloadingChannel] 191 if completionChannel != nil { // I am the downloader. 192 err := saveObject(filename, or.objectsReader) 193 or.objSrv.rwLock.Lock() 194 object.downloadingChannel = nil 195 if err == nil { 196 or.objSrv.cachedBytes += object.size 197 } else { 198 delete(or.objSrv.objects, object.hash) 199 object.usageCount-- 200 } 201 or.objSrv.downloadingBytes -= object.size 202 or.objSrv.rwLock.Unlock() 203 close(completionChannel) 204 if err != nil { 205 return 0, nil, err 206 } 207 or.downloadedObjects++ 208 or.downloadedBytes += object.size 209 } else { // Someone else is the downloader. 210 <-downloadingChannel // It's still downloading: wait. 211 or.waitedObjects++ 212 or.waitedBytes += object.size 213 } 214 } 215 if file, err := os.Open(filename); err != nil { 216 return 0, nil, err 217 } else { 218 or.totalObjects++ 219 or.totalBytes += object.size 220 return object.size, &readerObject{object, file, or.objSrv}, nil 221 } 222 } 223 224 func (ro *readerObject) Close() error { 225 err := ro.file.Close() 226 ro.objSrv.rwLock.Lock() 227 ro.objSrv.putObjectWithLock(ro.object) 228 ro.objSrv.rwLock.Unlock() 229 return err 230 } 231 232 func (ro *readerObject) Read(p []byte) (int, error) { 233 return ro.file.Read(p) 234 } 235 236 func timeoutFunction(f func(), timeout time.Duration) { 237 if timeout < 0 { 238 f() 239 return 240 } 241 completionChannel := make(chan struct{}, 1) 242 go func() { 243 f() 244 completionChannel <- struct{}{} 245 }() 246 timer := time.NewTimer(timeout) 247 select { 248 case <-completionChannel: 249 if !timer.Stop() { 250 <-timer.C 251 } 252 return 253 case <-timer.C: 254 os.Stderr.Write([]byte("lock timeout. Full stack trace follows:\n")) 255 buf := make([]byte, 1024*1024) 256 nBytes := runtime.Stack(buf, true) 257 os.Stderr.Write(buf[0:nBytes]) 258 os.Stderr.Write([]byte("\n")) 259 panic("timeout") 260 } 261 }