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