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

     1  package lib
     2  
     3  import (
     4  	"container/list"
     5  	"encoding/gob"
     6  	"fmt"
     7  	"io"
     8  
     9  	"github.com/Cloud-Foundations/Dominator/lib/errors"
    10  	"github.com/Cloud-Foundations/Dominator/lib/hash"
    11  	"github.com/Cloud-Foundations/Dominator/lib/log"
    12  	"github.com/Cloud-Foundations/Dominator/lib/objectserver"
    13  	oclient "github.com/Cloud-Foundations/Dominator/lib/objectserver/client"
    14  	"github.com/Cloud-Foundations/Dominator/lib/srpc"
    15  	proto "github.com/Cloud-Foundations/Dominator/proto/objectserver"
    16  )
    17  
    18  func addObjectsWithMaster(conn *srpc.Conn, decoder srpc.Decoder,
    19  	encoder srpc.Encoder, objSrv objectserver.StashingObjectServer,
    20  	masterAddress string, logger log.DebugLogger) error {
    21  	logger.Printf("AddObjectsWithMaster(%s) starting\n", conn.RemoteAddr())
    22  	defer conn.Flush()
    23  	numAdded := 0
    24  	numObj := 0
    25  	outgoingQueueSendChan, outgoingQueueReceiveChan := newOutgoingQueue()
    26  	errorChan := make(chan error, 1)
    27  	defer close(outgoingQueueSendChan)
    28  	go processOutgoingQueue(encoder, outgoingQueueReceiveChan, errorChan,
    29  		logger)
    30  	masterQueueSendChan, masterQueueReceiveChan := newMasterQueue()
    31  	defer close(masterQueueSendChan)
    32  	masterDrain := make(chan error, 1)
    33  	masterClient, err := srpc.DialHTTP("tcp", masterAddress, 0)
    34  	if err != nil {
    35  		sendError(outgoingQueueSendChan, err)
    36  		return nil
    37  	}
    38  	defer masterClient.Close()
    39  	var masterConn *srpc.Conn
    40  	var masterEncoder *gob.Encoder
    41  	locallyKnownObjects := make([]hash.Hash, 0)
    42  	// First process the stream of incoming objects, verify or stash+send.
    43  	for ; ; numObj++ {
    44  		if len(errorChan) > 0 {
    45  			break
    46  		}
    47  		var request proto.AddObjectRequest
    48  		var response proto.AddObjectResponse
    49  		err := decoder.Decode(&request)
    50  		if err != nil {
    51  			if err == io.EOF || err == io.ErrUnexpectedEOF {
    52  				break
    53  			}
    54  			return errors.New("error decoding: " + err.Error())
    55  		}
    56  		if request.Length < 1 {
    57  			break
    58  		}
    59  		var data []byte
    60  		response.Hash, data, err = objSrv.StashOrVerifyObject(conn,
    61  			request.Length, request.ExpectedHash)
    62  		if err != nil {
    63  			sendError(outgoingQueueSendChan, err)
    64  			break
    65  		}
    66  		entry := make(chan proto.AddObjectResponse, 1)
    67  		if data == nil { // Object already exists
    68  			entry <- response
    69  			outgoingQueueSendChan <- entry
    70  			locallyKnownObjects = append(locallyKnownObjects, response.Hash)
    71  			continue
    72  		}
    73  		if masterEncoder == nil {
    74  			masterConn, err = masterClient.Call("ObjectServer.AddObjects")
    75  			if err != nil {
    76  				sendError(outgoingQueueSendChan,
    77  					errors.New("error calling master: "+masterAddress+": "+
    78  						err.Error()))
    79  				break
    80  			}
    81  			defer func() {
    82  				if masterConn != nil {
    83  					masterConn.Close()
    84  				}
    85  			}()
    86  			go processResponses(gob.NewDecoder(masterConn),
    87  				masterQueueReceiveChan, masterDrain, objSrv)
    88  			masterEncoder = gob.NewEncoder(masterConn)
    89  			logger.Debugln(0,
    90  				"AddObjectsWithMaster(): called ObjectServer.AddObjects for master")
    91  		}
    92  		err = sendRequest(masterConn, masterEncoder, data, response.Hash)
    93  		if err != nil {
    94  			sendError(outgoingQueueSendChan, err)
    95  			break
    96  		}
    97  		masterQueueSendChan <- entry
    98  		outgoingQueueSendChan <- entry
    99  		numAdded++
   100  	}
   101  	logger.Debugln(0, "AddObjectsWithMaster(): Read all objects from client")
   102  	if masterEncoder != nil {
   103  		err := sendRequest(masterConn, masterEncoder, nil, hash.Hash{})
   104  		if err != nil {
   105  			sendError(outgoingQueueSendChan, err)
   106  			logger.Printf(
   107  				"AddObjectsWithMaster(): failed, %d of %d so far are new objects: %s",
   108  				numAdded, numObj, err)
   109  			return nil
   110  		}
   111  		masterQueueSendChan <- nil
   112  		err = <-masterDrain
   113  		masterConn.Close()
   114  		masterConn = nil
   115  		if err != nil {
   116  			logger.Printf(
   117  				"AddObjectsWithMaster(): failed, %d of %d so far are new objects: %s",
   118  				numAdded, numObj, err)
   119  			return nil
   120  		}
   121  	}
   122  	// Check for objects missing on the master.
   123  	err = sendMissingObjects(objSrv, masterClient, locallyKnownObjects, logger)
   124  	if err != nil {
   125  		sendError(outgoingQueueSendChan, err)
   126  		err = <-errorChan
   127  		logger.Printf(
   128  			"AddObjectsWithMaster(): failed, %d of %d so far are new objects: %s",
   129  			numAdded, numObj, err)
   130  		return nil
   131  	}
   132  	logger.Debugln(0, "Sending flush request for outgoing queue")
   133  	outgoingQueueSendChan <- nil
   134  	logger.Debugln(0, "Sent flush request for outgoing queue")
   135  	if err := <-errorChan; err != nil {
   136  		logger.Printf(
   137  			"AddObjectsWithMaster(): failed, %d of %d so far are new objects: %s",
   138  			numAdded, numObj, err)
   139  	} else {
   140  		logger.Printf("AddObjectsWithMaster(): %d of %d are new objects",
   141  			numAdded, numObj)
   142  	}
   143  	return nil
   144  }
   145  
   146  func processOutgoingQueue(encoder srpc.Encoder,
   147  	queue <-chan <-chan proto.AddObjectResponse, errorChan chan<- error,
   148  	logger log.DebugLogger) {
   149  	// Hold back one response until flush entry. This ensures the client will
   150  	// read an error message before seeing last "OK".
   151  	var previousResponse *proto.AddObjectResponse
   152  	for entry := range queue {
   153  		var response proto.AddObjectResponse
   154  		if entry != nil {
   155  			response = <-entry
   156  			if response.ErrorString != "" { // Preempt previous response.
   157  				previousResponse = &response
   158  			}
   159  		}
   160  		if previousResponse != nil {
   161  			if err := encoder.Encode(*previousResponse); err != nil {
   162  				err = errors.New("error encoding: " + err.Error())
   163  				errorChan <- err
   164  				break
   165  			}
   166  			if err := errors.New(previousResponse.ErrorString); err != nil {
   167  				errorChan <- err
   168  				break
   169  			}
   170  		}
   171  		if entry == nil {
   172  			errorChan <- nil
   173  			break
   174  		}
   175  		previousResponse = &response
   176  	}
   177  	logger.Debugln(0, "Draining outgoing queue")
   178  	for range queue { // Drain the queue.
   179  	}
   180  }
   181  
   182  func processResponses(decoder *gob.Decoder,
   183  	queue <-chan chan<- proto.AddObjectResponse, completed chan<- error,
   184  	objSrv objectserver.StashingObjectServer) {
   185  	var err error
   186  	for entry := range queue {
   187  		if entry == nil {
   188  			break
   189  		}
   190  		var reply proto.AddObjectResponse
   191  		if err = decoder.Decode(&reply); err != nil {
   192  			reply.ErrorString = "error decoding: " + err.Error()
   193  			entry <- reply
   194  			break
   195  		}
   196  		if reply.ErrorString == "" {
   197  			if err = objSrv.CommitObject(reply.Hash); err != nil {
   198  				reply.ErrorString = "commit error: " + err.Error()
   199  				entry <- reply
   200  				break
   201  			}
   202  		}
   203  		entry <- reply
   204  	}
   205  	completed <- err
   206  	for range queue { // Drain the queue.
   207  	}
   208  }
   209  
   210  func sendRequest(conn *srpc.Conn, encoder *gob.Encoder, data []byte,
   211  	hashVal hash.Hash) error {
   212  	request := proto.AddObjectRequest{
   213  		Length:       uint64(len(data)),
   214  		ExpectedHash: &hashVal,
   215  	}
   216  	if err := encoder.Encode(request); err != nil {
   217  		return err
   218  	}
   219  	if len(data) > 0 {
   220  		_, err := conn.Write(data)
   221  		return err
   222  	}
   223  	return conn.Flush()
   224  }
   225  
   226  func sendError(queue chan<- <-chan proto.AddObjectResponse, err error) {
   227  	entry := make(chan proto.AddObjectResponse, 1)
   228  	entry <- proto.AddObjectResponse{ErrorString: err.Error()}
   229  	queue <- entry
   230  }
   231  
   232  func sendMissingObjects(objSrv objectserver.StashingObjectServer,
   233  	masterClient *srpc.Client, locallyKnownObjects []hash.Hash,
   234  	logger log.DebugLogger) error {
   235  	if len(locallyKnownObjects) < 1 {
   236  		return nil
   237  	}
   238  	logger.Debugf(0,
   239  		"Checking %d locally known objects for presence on master\n",
   240  		len(locallyKnownObjects))
   241  	objClient := oclient.AttachObjectClient(masterClient)
   242  	lengths, err := objClient.CheckObjects(locallyKnownObjects)
   243  	objClient.Close()
   244  	if err != nil {
   245  		return fmt.Errorf("error checking master for known objects: %s", err)
   246  	}
   247  	allObjectsKnownOnMaster := true
   248  	for _, lengthOnMaster := range lengths {
   249  		if lengthOnMaster < 1 {
   250  			allObjectsKnownOnMaster = false
   251  			break
   252  		}
   253  	}
   254  	if allObjectsKnownOnMaster {
   255  		return nil
   256  	}
   257  	adderQueue, err := oclient.NewObjectAdderQueue(masterClient)
   258  	if err != nil {
   259  		return err
   260  	}
   261  	defer func() {
   262  		if adderQueue != nil {
   263  			adderQueue.Close()
   264  		}
   265  	}()
   266  	numSent := 0
   267  	for index, lengthOnMaster := range lengths {
   268  		if lengthOnMaster > 0 {
   269  			continue
   270  		}
   271  		hashVal := locallyKnownObjects[index]
   272  		length, reader, err := objectserver.GetObject(objSrv, hashVal)
   273  		if err != nil {
   274  			return err
   275  		}
   276  		data := make([]byte, length)
   277  		nRead, err := io.ReadFull(reader, data)
   278  		reader.Close()
   279  		if err != nil {
   280  			return err
   281  		}
   282  		if uint64(nRead) != length {
   283  			return fmt.Errorf(
   284  				"failed to read file data, wanted: %d, got: %d bytes",
   285  				length, nRead)
   286  		}
   287  		if err := adderQueue.AddData(data, hashVal); err != nil {
   288  			return err
   289  		}
   290  		numSent++
   291  	}
   292  	err = adderQueue.Close()
   293  	adderQueue = nil
   294  	logger.Printf("AddObjectsWithMaster() Sent: %d of %d locally known objects\n",
   295  		numSent, len(locallyKnownObjects))
   296  	return err
   297  }
   298  
   299  func newOutgoingQueue() (chan<- <-chan proto.AddObjectResponse,
   300  	<-chan <-chan proto.AddObjectResponse) {
   301  	send := make(chan (<-chan proto.AddObjectResponse), 1)
   302  	receive := make(chan (<-chan proto.AddObjectResponse), 1)
   303  	go manageOutgoingQueue(send, receive)
   304  	return send, receive
   305  }
   306  
   307  func manageOutgoingQueue(send <-chan <-chan proto.AddObjectResponse,
   308  	receive chan<- <-chan proto.AddObjectResponse) {
   309  	queue := list.New()
   310  	for {
   311  		if front := queue.Front(); front == nil {
   312  			if send == nil {
   313  				close(receive)
   314  				return
   315  			}
   316  			response, ok := <-send
   317  			if !ok {
   318  				close(receive)
   319  				return
   320  			}
   321  			queue.PushBack(response)
   322  		} else {
   323  			select {
   324  			case receive <- front.Value.(<-chan proto.AddObjectResponse):
   325  				queue.Remove(front)
   326  			case response, ok := <-send:
   327  				if ok {
   328  					queue.PushBack(response)
   329  				} else {
   330  					send = nil
   331  				}
   332  			}
   333  		}
   334  	}
   335  }
   336  
   337  func newMasterQueue() (chan<- chan<- proto.AddObjectResponse,
   338  	<-chan chan<- proto.AddObjectResponse) {
   339  	send := make(chan chan<- proto.AddObjectResponse, 1)
   340  	receive := make(chan chan<- proto.AddObjectResponse, 1)
   341  	go manageMasterQueue(send, receive)
   342  	return send, receive
   343  }
   344  
   345  func manageMasterQueue(send <-chan chan<- proto.AddObjectResponse,
   346  	receive chan<- chan<- proto.AddObjectResponse) {
   347  	queue := list.New()
   348  	for {
   349  		if front := queue.Front(); front == nil {
   350  			if send == nil {
   351  				close(receive)
   352  				return
   353  			}
   354  			response, ok := <-send
   355  			if !ok {
   356  				close(receive)
   357  				return
   358  			}
   359  			queue.PushBack(response)
   360  		} else {
   361  			select {
   362  			case receive <- front.Value.(chan<- proto.AddObjectResponse):
   363  				queue.Remove(front)
   364  			case response, ok := <-send:
   365  				if ok {
   366  					queue.PushBack(response)
   367  				} else {
   368  					send = nil
   369  				}
   370  			}
   371  		}
   372  	}
   373  }