github.com/cloud-foundations/dominator@v0.0.0-20221004181915-6e4fee580046/sub/rpcd/fetch.go (about)

     1  package rpcd
     2  
     3  import (
     4  	"errors"
     5  	"flag"
     6  	"fmt"
     7  	"io"
     8  	"os"
     9  	"path"
    10  	"syscall"
    11  	"time"
    12  
    13  	"github.com/Cloud-Foundations/Dominator/lib/format"
    14  	"github.com/Cloud-Foundations/Dominator/lib/fsutil"
    15  	"github.com/Cloud-Foundations/Dominator/lib/hash"
    16  	"github.com/Cloud-Foundations/Dominator/lib/netspeed"
    17  	"github.com/Cloud-Foundations/Dominator/lib/objectcache"
    18  	objectclient "github.com/Cloud-Foundations/Dominator/lib/objectserver/client"
    19  	"github.com/Cloud-Foundations/Dominator/lib/rateio"
    20  	"github.com/Cloud-Foundations/Dominator/lib/srpc"
    21  	"github.com/Cloud-Foundations/Dominator/proto/sub"
    22  )
    23  
    24  const filePerms = syscall.S_IRUSR | syscall.S_IWUSR | syscall.S_IRGRP
    25  
    26  var (
    27  	exitOnFetchFailure = flag.Bool("exitOnFetchFailure", false,
    28  		"If true, exit if there are fetch failures. For debugging only")
    29  )
    30  
    31  func (t *rpcType) Fetch(conn *srpc.Conn, request sub.FetchRequest,
    32  	reply *sub.FetchResponse) error {
    33  	if *readOnly {
    34  		txt := "Fetch() rejected due to read-only mode"
    35  		t.logger.Println(txt)
    36  		return errors.New(txt)
    37  	}
    38  	if err := t.getFetchLock(); err != nil {
    39  		return err
    40  	}
    41  	if request.Wait {
    42  		return t.fetchAndUnlock(request)
    43  	}
    44  	go t.fetchAndUnlock(request)
    45  	return nil
    46  }
    47  
    48  func (t *rpcType) getFetchLock() error {
    49  	t.rwLock.Lock()
    50  	defer t.rwLock.Unlock()
    51  	if t.fetchInProgress {
    52  		t.logger.Println("Error: fetch already in progress")
    53  		return errors.New("fetch already in progress")
    54  	}
    55  	if t.updateInProgress {
    56  		t.logger.Println("Error: update in progress")
    57  		return errors.New("update in progress")
    58  	}
    59  	t.fetchInProgress = true
    60  	return nil
    61  }
    62  
    63  func (t *rpcType) fetchAndUnlock(request sub.FetchRequest) error {
    64  	err := t.doFetch(request)
    65  	if err != nil && *exitOnFetchFailure {
    66  		os.Exit(1)
    67  	}
    68  	t.rwLock.Lock()
    69  	defer t.rwLock.Unlock()
    70  	t.lastFetchError = err
    71  	return err
    72  }
    73  
    74  func (t *rpcType) doFetch(request sub.FetchRequest) error {
    75  	defer t.clearFetchInProgress()
    76  	objectServer := objectclient.NewObjectClient(request.ServerAddress)
    77  	defer objectServer.Close()
    78  	defer t.scannerConfiguration.BoostCpuLimit(t.logger)
    79  	benchmark := false
    80  	linkSpeed, haveLinkSpeed := netspeed.GetSpeedToAddress(
    81  		request.ServerAddress)
    82  	if haveLinkSpeed {
    83  		t.logFetch(request, linkSpeed)
    84  	} else {
    85  		if t.networkReaderContext.MaximumSpeed() < 1 {
    86  			benchmark = enoughBytesForBenchmark(objectServer, request)
    87  			if benchmark {
    88  				objectServer.SetExclusiveGetObjects(true)
    89  				t.logger.Printf("Fetch(%s) %d objects and benchmark speed\n",
    90  					request.ServerAddress, len(request.Hashes))
    91  			} else {
    92  				t.logFetch(request, 0)
    93  			}
    94  		} else {
    95  			t.logFetch(request, t.networkReaderContext.MaximumSpeed())
    96  		}
    97  	}
    98  	objectsReader, err := objectServer.GetObjects(request.Hashes)
    99  	if err != nil {
   100  		t.logger.Printf("Error getting object reader: %s\n", err.Error())
   101  		return err
   102  	}
   103  	defer objectsReader.Close()
   104  	var totalLength uint64
   105  	defer t.rescanObjectCacheFunction()
   106  	timeStart := time.Now()
   107  	for _, hash := range request.Hashes {
   108  		length, reader, err := objectsReader.NextObject()
   109  		if err != nil {
   110  			t.logger.Println(err)
   111  			return err
   112  		}
   113  		r := io.Reader(reader)
   114  		if haveLinkSpeed {
   115  			if linkSpeed > 0 {
   116  				r = rateio.NewReaderContext(linkSpeed,
   117  					uint64(t.networkReaderContext.SpeedPercent()),
   118  					&rateio.ReadMeasurer{}).NewReader(reader)
   119  			}
   120  		} else if !benchmark {
   121  			r = t.networkReaderContext.NewReader(reader)
   122  		}
   123  		err = readOne(t.objectsDir, hash, length, r)
   124  		reader.Close()
   125  		if err != nil {
   126  			t.logger.Println(err)
   127  			return err
   128  		}
   129  		totalLength += length
   130  	}
   131  	duration := time.Since(timeStart)
   132  	speed := uint64(float64(totalLength) / duration.Seconds())
   133  	if benchmark {
   134  		file, err := os.Create(t.netbenchFilename)
   135  		if err == nil {
   136  			fmt.Fprintf(file, "%d\n", speed)
   137  			file.Close()
   138  		}
   139  		t.networkReaderContext.InitialiseMaximumSpeed(speed)
   140  	}
   141  	t.logger.Printf("Fetch() complete. Read: %s in %s (%s/s)\n",
   142  		format.FormatBytes(totalLength), format.Duration(duration),
   143  		format.FormatBytes(speed))
   144  	return nil
   145  }
   146  
   147  func (t *rpcType) logFetch(request sub.FetchRequest, speed uint64) {
   148  	speedString := "unlimited speed"
   149  	if speed > 0 {
   150  		speedString = format.FormatBytes(
   151  			speed*uint64(t.networkReaderContext.SpeedPercent())/100) + "/s"
   152  	}
   153  	t.logger.Printf("Fetch(%s) %d objects at %s\n",
   154  		request.ServerAddress, len(request.Hashes), speedString)
   155  }
   156  
   157  func enoughBytesForBenchmark(objectServer *objectclient.ObjectClient,
   158  	request sub.FetchRequest) bool {
   159  	lengths, err := objectServer.CheckObjects(request.Hashes)
   160  	if err != nil {
   161  		return false
   162  	}
   163  	var totalLength uint64
   164  	for _, length := range lengths {
   165  		totalLength += length
   166  	}
   167  	if totalLength > 1024*1024*64 {
   168  		return true
   169  	}
   170  	return false
   171  }
   172  
   173  func readOne(objectsDir string, hash hash.Hash, length uint64,
   174  	reader io.Reader) error {
   175  	filename := path.Join(objectsDir, objectcache.HashToFilename(hash))
   176  	dirname := path.Dir(filename)
   177  	if err := os.MkdirAll(dirname, syscall.S_IRWXU); err != nil {
   178  		return err
   179  	}
   180  	return fsutil.CopyToFile(filename, filePerms, reader, length)
   181  }
   182  
   183  func (t *rpcType) clearFetchInProgress() {
   184  	t.rwLock.Lock()
   185  	defer t.rwLock.Unlock()
   186  	t.fetchInProgress = false
   187  }