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