github.com/Cloud-Foundations/Dominator@v0.3.4/lib/objectserver/filesystem/refcount.go (about) 1 package filesystem 2 3 import ( 4 "fmt" 5 "time" 6 7 "github.com/Cloud-Foundations/Dominator/lib/format" 8 "github.com/Cloud-Foundations/Dominator/lib/hash" 9 "github.com/Cloud-Foundations/Dominator/lib/objectserver" 10 ) 11 12 func (objSrv *ObjectServer) adjustRefcounts(increment bool, 13 iterator objectserver.ObjectsIterator) error { 14 var count, size uint64 15 var adjustedObjects []*objectType 16 objSrv.rwLock.Lock() 17 defer objSrv.rwLock.Unlock() 18 startTime := time.Now() 19 err := iterator.ForEachObject(func(hashVal hash.Hash) error { 20 object := objSrv.objects[hashVal] 21 if object == nil { 22 return fmt.Errorf("unknown object: %x", hashVal) 23 } 24 if increment { 25 if err := objSrv.incrementRefcount(object); err != nil { 26 return err 27 } 28 } else { 29 if err := objSrv.decrementRefcount(object); err != nil { 30 return err 31 } 32 } 33 size += object.size 34 count++ 35 adjustedObjects = append(adjustedObjects, object) 36 return nil 37 }) 38 if err == nil { 39 if increment { 40 objSrv.Logger.Debugf(0, 41 "Incremented refcounts, counted: %d (%s) in %s\n", 42 count, format.FormatBytes(size), 43 format.Duration(time.Since(startTime))) 44 } else { 45 objSrv.Logger.Debugf(0, 46 "Decremented refcounts, counted: %d (%s) in %s\n", 47 count, format.FormatBytes(size), 48 format.Duration(time.Since(startTime))) 49 } 50 return nil 51 } 52 // Undo what was done so far. 53 if increment { 54 for _, object := range adjustedObjects { 55 if err := objSrv.decrementRefcount(object); err != nil { 56 panic(err) 57 } 58 } 59 } else { 60 for _, object := range adjustedObjects { 61 if err := objSrv.incrementRefcount(object); err != nil { 62 panic(err) 63 } 64 } 65 } 66 objSrv.Logger.Printf("Adjusted&reverted: %d (%s) in %s\n", 67 count, format.FormatBytes(size), 68 format.Duration(time.Since(startTime))) 69 return err 70 } 71 72 // Add object to unreferenced list, at newest (front) position. 73 func (objSrv *ObjectServer) addUnreferenced(object *objectType) { 74 object.olderUnreferenced = objSrv.newestUnreferenced 75 if objSrv.oldestUnreferenced == nil { 76 objSrv.oldestUnreferenced = object 77 } else { 78 objSrv.newestUnreferenced.newerUnreferenced = object 79 } 80 objSrv.newestUnreferenced = object 81 objSrv.numUnreferenced++ 82 object.newerUnreferenced = nil 83 objSrv.unreferencedBytes += object.size 84 } 85 86 // Decrement refcount and possibly add to list of unreferenced objects. 87 func (objSrv *ObjectServer) decrementRefcount(object *objectType) error { 88 if object.refcount < 1 { 89 return fmt.Errorf("cannot decrement zero refcount, object: %x", 90 object.hash) 91 } 92 objSrv.duplicatedBytes -= object.size 93 objSrv.numDuplicated-- 94 object.refcount-- 95 if object.refcount > 0 { 96 return nil 97 } 98 objSrv.addUnreferenced(object) 99 objSrv.numReferenced-- 100 objSrv.referencedBytes -= object.size 101 return nil 102 } 103 104 // This must be called without the lock being held. 105 func (objSrv *ObjectServer) deleteOldestUnreferenced(lastPauseTime *time.Time) ( 106 uint64, error) { 107 lockWatcherOptions := objSrv.lockWatcher.GetOptions() 108 // Inject periodic pauses so that the write lockwatcher is not starved out. 109 if time.Since(*lastPauseTime) > lockWatcherOptions.LogTimeout>>1 { 110 time.Sleep(lockWatcherOptions.MaximumTryInterval << 1) 111 *lastPauseTime = time.Now() 112 } 113 objSrv.rwLock.Lock() 114 object := objSrv.oldestUnreferenced 115 if object == nil { 116 objSrv.rwLock.Unlock() 117 return 0, fmt.Errorf("no more objects to delete") 118 } 119 // deleteObject() will release the lock. 120 if err := objSrv.deleteObject(object.hash, true); err != nil { 121 return 0, err 122 } 123 return object.size, nil 124 } 125 126 // This must be called without the lock being held. 127 func (objSrv *ObjectServer) deleteUnreferenced(percentage uint8, 128 bytesToDelete uint64) (uint64, uint64, error) { 129 startTime := time.Now() 130 var bytesDeleted, objectsDeleted uint64 131 objSrv.rwLock.RLock() 132 objectsToDelete := uint64(percentage) * objSrv.numUnreferenced / 100 133 objSrv.rwLock.RUnlock() 134 lastPauseTime := time.Now() 135 for bytesDeleted < bytesToDelete || objectsDeleted < objectsToDelete { 136 size, err := objSrv.deleteOldestUnreferenced(&lastPauseTime) 137 if err != nil { 138 return bytesDeleted, objectsDeleted, err 139 } 140 bytesDeleted += size 141 objectsDeleted++ 142 } 143 objSrv.Logger.Printf("Garbage collector deleted: %s in: %d objects in %s\n", 144 format.FormatBytes(bytesDeleted), objectsDeleted, 145 format.Duration(time.Since(startTime))) 146 return bytesDeleted, objectsDeleted, nil 147 } 148 149 // Increment refcount and possibly remove from list of unreferenced objects. 150 func (objSrv *ObjectServer) incrementRefcount(object *objectType) error { 151 if object.refcount < 1 { 152 objSrv.numReferenced++ 153 objSrv.referencedBytes += object.size 154 objSrv.removeUnreferenced(object) 155 } 156 objSrv.duplicatedBytes += object.size 157 objSrv.numDuplicated++ 158 object.refcount++ 159 return nil 160 } 161 162 func (objSrv *ObjectServer) listUnreferenced() map[hash.Hash]uint64 { 163 objSrv.rwLock.RLock() 164 defer objSrv.rwLock.RUnlock() 165 objects := make(map[hash.Hash]uint64, objSrv.numUnreferenced) 166 for ob := objSrv.oldestUnreferenced; ob != nil; ob = ob.newerUnreferenced { 167 objects[ob.hash] = ob.size 168 } 169 return objects 170 } 171 172 // Remove object from list if present, else do nothing. 173 func (objSrv *ObjectServer) removeUnreferenced(object *objectType) { 174 var removed bool 175 if object.olderUnreferenced == nil { 176 if objSrv.oldestUnreferenced == object { 177 objSrv.oldestUnreferenced = object.newerUnreferenced 178 removed = true 179 } 180 } else { 181 object.olderUnreferenced.newerUnreferenced = object.newerUnreferenced 182 removed = true 183 } 184 if object.newerUnreferenced == nil { 185 if objSrv.newestUnreferenced == object { 186 objSrv.newestUnreferenced = object.olderUnreferenced 187 removed = true 188 } 189 } else { 190 object.newerUnreferenced.olderUnreferenced = object.olderUnreferenced 191 removed = true 192 } 193 object.olderUnreferenced = nil 194 if removed { 195 objSrv.numUnreferenced-- 196 objSrv.unreferencedBytes -= object.size 197 } 198 object.newerUnreferenced = nil 199 }