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  }