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  }