github.com/Cloud-Foundations/Dominator@v0.3.4/lib/objectserver/cachingreader/lru.go (about) 1 package cachingreader 2 3 import ( 4 "bufio" 5 "io" 6 "os" 7 "path/filepath" 8 "syscall" 9 "time" 10 11 "github.com/Cloud-Foundations/Dominator/lib/format" 12 "github.com/Cloud-Foundations/Dominator/lib/fsutil" 13 "github.com/Cloud-Foundations/Dominator/lib/hash" 14 "github.com/Cloud-Foundations/Dominator/lib/objectcache" 15 ) 16 17 const ( 18 privateFilePerms = syscall.S_IRUSR | syscall.S_IWUSR 19 filePerms = privateFilePerms | syscall.S_IRGRP | syscall.S_IROTH 20 ) 21 22 func (objSrv *ObjectServer) addToLruWithLock(object *objectType) { 23 if object.usageCount != 0 { 24 panic("object.usageCount != 0") 25 } 26 if object.newer != nil { 27 panic("object has newer link") 28 } 29 if object.older != nil { 30 panic("object has older link") 31 } 32 if objSrv.oldest == nil { // Empty list: initialise it. 33 if objSrv.newest != nil { 34 panic("LRU has newest but not oldest entry") 35 } 36 objSrv.oldest = object 37 } else { // Update previous newest object. 38 if objSrv.newest == nil { 39 panic("LRU has oldest but not newest entry") 40 } 41 objSrv.newest.newer = object 42 } 43 object.older = objSrv.newest 44 objSrv.newest = object 45 objSrv.data.LruBytes += object.size 46 select { 47 case objSrv.lruUpdateNotifier <- struct{}{}: 48 default: 49 } 50 } 51 52 func (objSrv *ObjectServer) flusher(lruUpdateNotifier <-chan struct{}) { 53 flushTimer := time.NewTimer(time.Minute) 54 flushTimer.Stop() 55 for { 56 select { 57 case <-lruUpdateNotifier: 58 if !flushTimer.Stop() { 59 select { 60 case <-flushTimer.C: 61 default: 62 } 63 } 64 flushTimer.Reset(time.Minute) 65 case <-flushTimer.C: 66 objSrv.saveLru() 67 } 68 } 69 } 70 71 func (objSrv *ObjectServer) getObjectWithLock(hashVal hash.Hash) *objectType { 72 if object, ok := objSrv.objects[hashVal]; !ok { 73 return nil 74 } else { 75 if object.usageCount < 1 { 76 objSrv.removeFromLruWithLock(object) 77 } 78 object.usageCount++ 79 return object 80 } 81 } 82 83 func (objSrv *ObjectServer) linkOrphanedEntries() { 84 for _, object := range objSrv.objects { 85 if objSrv.newest == nil { // Empty list: initialise it. 86 objSrv.newest = object 87 objSrv.oldest = object 88 objSrv.data.LruBytes += object.size 89 } else if object.newer == nil && objSrv.newest != object { 90 // Orphaned object: make it the newest. 91 object.older = objSrv.newest 92 objSrv.newest.newer = object 93 objSrv.newest = object 94 objSrv.data.LruBytes += object.size 95 } 96 } 97 } 98 99 func (objSrv *ObjectServer) loadLru() error { 100 startTime := time.Now() 101 filename := filepath.Join(objSrv.baseDir, ".lru") 102 file, err := os.Open(filename) 103 if err != nil { 104 if os.IsNotExist(err) { 105 return nil 106 } 107 return err 108 } 109 defer file.Close() 110 reader := bufio.NewReader(file) 111 if err := objSrv.readLru(reader); err != nil { 112 return err 113 } 114 objSrv.logger.Printf("Loaded LRU in %s\n", 115 format.Duration(time.Since(startTime))) 116 return nil 117 } 118 119 func (objSrv *ObjectServer) putObjectWithLock(object *objectType) { 120 if object.usageCount < 1 { 121 panic("object.usageCount == 0") 122 } 123 object.usageCount-- 124 if object.usageCount == 0 { 125 objSrv.addToLruWithLock(object) 126 } 127 } 128 129 func (objSrv *ObjectServer) readLru(reader io.Reader) error { 130 var hashVal hash.Hash 131 objSrv.data.LruBytes = 0 132 for { // First object is newest, last object is oldest. 133 if _, err := reader.Read((&hashVal)[:]); err != nil { 134 if err == io.EOF { 135 break 136 } 137 return err 138 } 139 if object, ok := objSrv.objects[hashVal]; ok { 140 objSrv.data.LruBytes += object.size 141 if objSrv.newest == nil { // Empty list: initialise it. 142 objSrv.newest = object 143 } else { // Make object the oldest. 144 object.newer = objSrv.oldest 145 objSrv.oldest.older = object 146 } 147 objSrv.oldest = object 148 } 149 } 150 return nil 151 } 152 153 // Returns true if space is available. 154 func (objSrv *ObjectServer) releaseSpaceWithLock(size uint64) bool { 155 if objSrv.data.CachedBytes+objSrv.data.DownloadingBytes+size <= 156 objSrv.maxCachedBytes { 157 return true 158 } 159 if objSrv.data.CachedBytes- 160 objSrv.data.LruBytes+objSrv.data.DownloadingBytes+size > 161 objSrv.maxCachedBytes { 162 return false // No amount of deleting unused objects will help. 163 } 164 for object := objSrv.oldest; object != nil; object = objSrv.oldest { 165 filename := filepath.Join(objSrv.baseDir, 166 objectcache.HashToFilename(object.hash)) 167 if err := os.Remove(filename); err != nil { 168 objSrv.logger.Println(err) 169 return false 170 } 171 objSrv.removeFromLruWithLock(object) 172 delete(objSrv.objects, object.hash) 173 objSrv.data.CachedBytes -= object.size 174 if objSrv.data.CachedBytes+objSrv.data.DownloadingBytes+size <= 175 objSrv.maxCachedBytes { 176 return true 177 } 178 } 179 panic("not enough space despite freeing unused objects") 180 } 181 182 func (objSrv *ObjectServer) removeFromLruWithLock(object *objectType) { 183 if object.older == nil { // Object is the oldest. 184 if object != objSrv.oldest { // Fuck, it's not actually in the list. 185 panic("object is not in the LRU") 186 } 187 objSrv.oldest = object.newer 188 if objSrv.oldest != nil { 189 objSrv.oldest.older = nil 190 } 191 } else { 192 object.older.newer = object.newer 193 } 194 if object.newer == nil { // Object is the newest. 195 if object != objSrv.newest { // Fuck, it's not actually in the list. 196 panic("object is not in the LRU") 197 } 198 objSrv.newest = object.older 199 if objSrv.newest != nil { 200 objSrv.newest.newer = nil 201 } 202 } else { 203 object.newer.older = object.older 204 } 205 object.newer = nil 206 object.older = nil 207 if objSrv.newest == nil && objSrv.oldest != nil { 208 panic("LRU has oldest but not newest entry") 209 } 210 if objSrv.oldest == nil && objSrv.newest != nil { 211 panic("LRU has newest but not oldest entry") 212 } 213 objSrv.data.LruBytes -= object.size 214 select { 215 case objSrv.lruUpdateNotifier <- struct{}{}: 216 default: 217 } 218 } 219 220 func (objSrv *ObjectServer) saveLru() { 221 objSrv.rwLock.RLock() 222 defer objSrv.rwLock.RUnlock() 223 filename := filepath.Join(objSrv.baseDir, ".lru") 224 writer, err := fsutil.CreateRenamingWriter(filename, filePerms) 225 if err != nil { 226 return 227 } 228 defer writer.Close() 229 w := bufio.NewWriter(writer) 230 defer w.Flush() 231 // Write newest first, oldest last. 232 for object := objSrv.newest; object != nil; object = object.older { 233 if _, err := w.Write(object.hash[:]); err != nil { 234 return 235 } 236 } 237 }