github.com/cloud-foundations/dominator@v0.0.0-20221004181915-6e4fee580046/lib/objectserver/client/queue.go (about)

     1  package client
     2  
     3  import (
     4  	"crypto/sha512"
     5  	"fmt"
     6  	"io"
     7  
     8  	"github.com/Cloud-Foundations/Dominator/lib/errors"
     9  	"github.com/Cloud-Foundations/Dominator/lib/hash"
    10  	"github.com/Cloud-Foundations/Dominator/lib/queue"
    11  	"github.com/Cloud-Foundations/Dominator/lib/srpc"
    12  	"github.com/Cloud-Foundations/Dominator/proto/objectserver"
    13  )
    14  
    15  func newObjectAdderQueue(client *srpc.Client) (*ObjectAdderQueue, error) {
    16  	var objQ ObjectAdderQueue
    17  	var err error
    18  	objQ.conn, err = client.Call("ObjectServer.AddObjects")
    19  	if err != nil {
    20  		return nil, err
    21  	}
    22  	getResponseSendChan, getResponseReceiveChan := queue.NewEventQueue()
    23  	errorChan := make(chan error, 1024)
    24  	objQ.getResponseChan = getResponseSendChan
    25  	objQ.errorChan = errorChan
    26  	objQ.sendSemaphore = make(chan struct{}, 1)
    27  	go readResponses(objQ.conn, getResponseReceiveChan, errorChan)
    28  	return &objQ, nil
    29  }
    30  
    31  func (objQ *ObjectAdderQueue) add(reader io.Reader, length uint64) (
    32  	hash.Hash, error) {
    33  	var hashVal hash.Hash
    34  	data := make([]byte, length)
    35  	nRead, err := io.ReadFull(reader, data)
    36  	if err != nil {
    37  		return hashVal, err
    38  	}
    39  	if uint64(nRead) != length {
    40  		return hashVal, errors.New(fmt.Sprintf(
    41  			"failed to read file data, wanted: %d, got: %d bytes",
    42  			length, nRead))
    43  	}
    44  	hasher := sha512.New()
    45  	if _, err := hasher.Write(data); err != nil {
    46  		return hashVal, err
    47  	}
    48  	copy(hashVal[:], hasher.Sum(nil))
    49  	err = objQ.addData(data, hashVal)
    50  	return hashVal, err
    51  }
    52  
    53  func (objQ *ObjectAdderQueue) addData(data []byte, hashVal hash.Hash) error {
    54  	if err := objQ.consumeErrors(false); err != nil {
    55  		return err
    56  	}
    57  	// Send in a goroutine to increase concurrency. A small win.
    58  	objQ.sendSemaphore <- struct{}{}
    59  	go func() {
    60  		defer func() {
    61  			<-objQ.sendSemaphore
    62  		}()
    63  		var request objectserver.AddObjectRequest
    64  		request.Length = uint64(len(data))
    65  		request.ExpectedHash = &hashVal
    66  		objQ.conn.Encode(request)
    67  		objQ.conn.Write(data)
    68  		objQ.getResponseChan <- struct{}{}
    69  	}()
    70  	return nil
    71  }
    72  
    73  func (objQ *ObjectAdderQueue) close() error {
    74  	// Wait for any sends in progress to complete.
    75  	objQ.sendSemaphore <- struct{}{}
    76  	var request objectserver.AddObjectRequest
    77  	err := objQ.conn.Encode(request)
    78  	err = updateError(err, objQ.conn.Flush())
    79  	close(objQ.getResponseChan)
    80  	err = updateError(err, objQ.consumeErrors(true))
    81  	return updateError(err, objQ.conn.Close())
    82  }
    83  
    84  func updateError(oldError, newError error) error {
    85  	if oldError == nil {
    86  		return newError
    87  	}
    88  	return oldError
    89  }
    90  
    91  func (objQ *ObjectAdderQueue) consumeErrors(untilClose bool) error {
    92  	if untilClose {
    93  		for err := range objQ.errorChan {
    94  			if err != nil {
    95  				return err
    96  			}
    97  		}
    98  	} else {
    99  		for len(objQ.errorChan) > 0 {
   100  			err := <-objQ.errorChan
   101  			if err != nil {
   102  				return err
   103  			}
   104  		}
   105  	}
   106  	return nil
   107  }
   108  
   109  func readResponses(conn *srpc.Conn, getResponseChan <-chan struct{},
   110  	errorChan chan<- error) {
   111  	defer close(errorChan)
   112  	for range getResponseChan {
   113  		var reply objectserver.AddObjectResponse
   114  		err := conn.Decode(&reply)
   115  		if err == nil {
   116  			err = errors.New(reply.ErrorString)
   117  		}
   118  		errorChan <- err
   119  		if err != nil {
   120  			break
   121  		}
   122  	}
   123  }