github.com/Cloud-Foundations/Dominator@v0.3.4/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.params.Logger.Println(txt)
    36  		return errors.New(txt)
    37  	}
    38  	if err := t.getFetchLock(conn, request); err != nil {
    39  		return err
    40  	}
    41  	if request.Wait {
    42  		return t.fetchAndUnlock(conn, request, conn.Username())
    43  	}
    44  	go t.fetchAndUnlock(conn, request, conn.Username())
    45  	return nil
    46  }
    47  
    48  func (t *rpcType) getFetchLock(conn *srpc.Conn,
    49  	request sub.FetchRequest) error {
    50  	t.rwLock.Lock()
    51  	defer t.rwLock.Unlock()
    52  	if err := t.getClientLock(conn, request.LockFor); err != nil {
    53  		t.params.Logger.Printf("Error: %s\n", err)
    54  		return err
    55  	}
    56  	if t.fetchInProgress {
    57  		t.params.Logger.Println("Error: fetch already in progress")
    58  		return errors.New("fetch already in progress")
    59  	}
    60  	if t.updateInProgress {
    61  		t.params.Logger.Println("Error: update in progress")
    62  		return errors.New("update in progress")
    63  	}
    64  	t.fetchInProgress = true
    65  	return nil
    66  }
    67  
    68  func (t *rpcType) fetchAndUnlock(conn *srpc.Conn, request sub.FetchRequest,
    69  	username string) error {
    70  	err := t.doFetch(request, username)
    71  	if err != nil && *exitOnFetchFailure {
    72  		os.Exit(1)
    73  	}
    74  	t.rwLock.Lock()
    75  	defer t.rwLock.Unlock()
    76  	t.fetchInProgress = false
    77  	t.lastFetchError = err
    78  	if err := t.getClientLock(conn, request.LockFor); err != nil {
    79  		return err
    80  	}
    81  	return err
    82  }
    83  
    84  func (t *rpcType) doFetch(request sub.FetchRequest, username string) error {
    85  	objectServer := objectclient.NewObjectClient(request.ServerAddress)
    86  	defer objectServer.Close()
    87  	defer t.params.ScannerConfiguration.BoostCpuLimit(t.params.Logger)
    88  	benchmark := false
    89  	linkSpeed, haveLinkSpeed := netspeed.GetSpeedToAddress(
    90  		request.ServerAddress)
    91  	if haveLinkSpeed {
    92  		t.logFetch(request, linkSpeed, username)
    93  	} else {
    94  		if t.params.NetworkReaderContext.MaximumSpeed() < 1 {
    95  			benchmark = enoughBytesForBenchmark(objectServer, request)
    96  			if benchmark {
    97  				objectServer.SetExclusiveGetObjects(true)
    98  				var suffix string
    99  				if username != "" {
   100  					suffix = " by " + username
   101  				}
   102  				t.params.Logger.Printf(
   103  					"Fetch(%s) %d objects and benchmark speed%s\n",
   104  					request.ServerAddress, len(request.Hashes), suffix)
   105  			} else {
   106  				t.logFetch(request, 0, username)
   107  			}
   108  		} else {
   109  			t.logFetch(request, t.params.NetworkReaderContext.MaximumSpeed(),
   110  				username)
   111  		}
   112  	}
   113  	objectsReader, err := objectServer.GetObjects(request.Hashes)
   114  	if err != nil {
   115  		t.params.Logger.Printf("Error getting object reader: %s\n", err.Error())
   116  		return err
   117  	}
   118  	defer objectsReader.Close()
   119  	var totalLength uint64
   120  	defer t.params.WorkdirGoroutine.Run(t.params.RescanObjectCacheFunction)
   121  	timeStart := time.Now()
   122  	for _, hash := range request.Hashes {
   123  		length, reader, err := objectsReader.NextObject()
   124  		if err != nil {
   125  			t.params.Logger.Println(err)
   126  			return err
   127  		}
   128  		r := io.Reader(reader)
   129  		if haveLinkSpeed {
   130  			if linkSpeed > 0 {
   131  				r = rateio.NewReaderContext(linkSpeed,
   132  					uint64(t.params.NetworkReaderContext.SpeedPercent()),
   133  					&rateio.ReadMeasurer{}).NewReader(reader)
   134  			}
   135  		} else if !benchmark {
   136  			r = t.params.NetworkReaderContext.NewReader(reader)
   137  		}
   138  		t.params.WorkdirGoroutine.Run(func() {
   139  			err = readOne(t.config.ObjectsDirectoryName, hash, length, r)
   140  		})
   141  		reader.Close()
   142  		if err != nil {
   143  			t.params.Logger.Println(err)
   144  			return err
   145  		}
   146  		totalLength += length
   147  	}
   148  	duration := time.Since(timeStart)
   149  	speed := uint64(float64(totalLength) / duration.Seconds())
   150  	if benchmark {
   151  		file, err := os.Create(t.config.NetworkBenchmarkFilename)
   152  		if err == nil {
   153  			fmt.Fprintf(file, "%d\n", speed)
   154  			file.Close()
   155  		}
   156  		t.params.NetworkReaderContext.InitialiseMaximumSpeed(speed)
   157  	}
   158  	t.params.Logger.Printf("Fetch() complete. Read: %s in %s (%s/s)\n",
   159  		format.FormatBytes(totalLength), format.Duration(duration),
   160  		format.FormatBytes(speed))
   161  	return nil
   162  }
   163  
   164  func (t *rpcType) logFetch(request sub.FetchRequest, speed uint64,
   165  	username string) {
   166  	speedString := "unlimited speed"
   167  	if speed > 0 {
   168  		speedString = format.FormatBytes(
   169  			speed*uint64(
   170  				t.params.NetworkReaderContext.SpeedPercent())/100) + "/s"
   171  	}
   172  	var suffix string
   173  	if username != "" {
   174  		suffix = " by " + username
   175  	}
   176  	t.params.Logger.Printf("Fetch(%s) %d objects at %s%s\n",
   177  		request.ServerAddress, len(request.Hashes), speedString, suffix)
   178  }
   179  
   180  func enoughBytesForBenchmark(objectServer *objectclient.ObjectClient,
   181  	request sub.FetchRequest) bool {
   182  	lengths, err := objectServer.CheckObjects(request.Hashes)
   183  	if err != nil {
   184  		return false
   185  	}
   186  	var totalLength uint64
   187  	for _, length := range lengths {
   188  		totalLength += length
   189  	}
   190  	if totalLength > 1024*1024*64 {
   191  		return true
   192  	}
   193  	return false
   194  }
   195  
   196  func readOne(objectsDir string, hash hash.Hash, length uint64,
   197  	reader io.Reader) error {
   198  	filename := path.Join(objectsDir, objectcache.HashToFilename(hash))
   199  	dirname := path.Dir(filename)
   200  	if err := os.MkdirAll(dirname, syscall.S_IRWXU); err != nil {
   201  		return err
   202  	}
   203  	return fsutil.CopyToFile(filename, filePerms, reader, length)
   204  }