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

     1  package client
     2  
     3  import (
     4  	"bytes"
     5  	"io"
     6  	"time"
     7  
     8  	"github.com/Cloud-Foundations/Dominator/lib/hash"
     9  	"github.com/Cloud-Foundations/Dominator/lib/log"
    10  	"github.com/Cloud-Foundations/Dominator/lib/objectserver"
    11  	"github.com/Cloud-Foundations/Dominator/lib/srpc"
    12  	proto "github.com/Cloud-Foundations/Dominator/proto/filegenerator"
    13  	"github.com/Cloud-Foundations/tricorder/go/tricorder"
    14  	"github.com/Cloud-Foundations/tricorder/go/tricorder/units"
    15  )
    16  
    17  func newManager(objSrv objectserver.ObjectServer, logger log.Logger) *Manager {
    18  	sourceReconnectChannel := make(chan string)
    19  	m := &Manager{
    20  		sourceMap:              make(map[string]*sourceType),
    21  		objectServer:           objSrv,
    22  		machineMap:             make(map[string]*machineType),
    23  		addMachineChannel:      make(chan *machineType),
    24  		removeMachineChannel:   make(chan string),
    25  		updateMachineChannel:   make(chan *machineType),
    26  		serverMessageChannel:   make(chan *serverMessageType),
    27  		sourceReconnectChannel: sourceReconnectChannel,
    28  		objectWaiters:          make(map[hash.Hash][]chan<- hash.Hash),
    29  		logger:                 logger}
    30  	tricorder.RegisterMetric("filegen/client/num-object-waiters",
    31  		&m.numObjectWaiters.value, units.None,
    32  		"number of goroutines waiting for objects")
    33  	go m.manage(sourceReconnectChannel)
    34  	return m
    35  }
    36  
    37  func (m *Manager) manage(sourceConnectChannel <-chan string) {
    38  	for {
    39  		select {
    40  		case machine := <-m.addMachineChannel:
    41  			m.addMachine(machine)
    42  		case hostname := <-m.removeMachineChannel:
    43  			m.removeMachine(hostname)
    44  		case machine := <-m.updateMachineChannel:
    45  			m.updateMachine(machine)
    46  		case serverMessage := <-m.serverMessageChannel:
    47  			m.processMessage(serverMessage)
    48  		case sourceName := <-sourceConnectChannel:
    49  			m.processSourceConnect(sourceName)
    50  		}
    51  	}
    52  }
    53  
    54  func (m *Manager) processMessage(serverMessage *serverMessageType) {
    55  	if msg := serverMessage.serverMessage.GetObjectResponse; msg != nil {
    56  		if _, _, err := m.objectServer.AddObject(
    57  			bytes.NewReader(msg.Data), 0, &msg.Hash); err != nil {
    58  			m.logger.Println(err)
    59  		} else {
    60  			if waiters, ok := m.objectWaiters[msg.Hash]; ok {
    61  				for _, channel := range waiters {
    62  					channel <- msg.Hash
    63  				}
    64  				delete(m.objectWaiters, msg.Hash)
    65  			}
    66  		}
    67  	}
    68  	if msg := serverMessage.serverMessage.YieldResponse; msg != nil {
    69  		if machine, ok := m.machineMap[msg.Hostname]; ok {
    70  			m.handleYieldResponse(machine, msg.Files)
    71  		} // else machine no longer known. Drop the message.
    72  	}
    73  }
    74  
    75  func (m *Manager) processSourceConnect(sourceName string) {
    76  	source := m.sourceMap[sourceName]
    77  	for _, machine := range m.machineMap {
    78  		if pathnames, ok := machine.sourceToPaths[sourceName]; ok {
    79  			request := &proto.ClientRequest{
    80  				YieldRequest: &proto.YieldRequest{machine.machine, pathnames}}
    81  			source.sendChannel <- request
    82  		}
    83  	}
    84  }
    85  
    86  func (m *Manager) getSource(sourceName string) *sourceType {
    87  	source, ok := m.sourceMap[sourceName]
    88  	if ok {
    89  		return source
    90  	}
    91  	source = new(sourceType)
    92  	sendChannel := make(chan *proto.ClientRequest, 4096)
    93  	source.sendChannel = sendChannel
    94  	m.sourceMap[sourceName] = source
    95  	go manageSource(sourceName, m.sourceReconnectChannel, sendChannel,
    96  		m.serverMessageChannel, m.logger)
    97  	return source
    98  }
    99  
   100  func manageSource(sourceName string, sourceReconnectChannel chan<- string,
   101  	clientRequestChannel <-chan *proto.ClientRequest,
   102  	serverMessageChannel chan<- *serverMessageType, logger log.Logger) {
   103  	closeNotifyChannel := make(chan struct{})
   104  	initialRetryTimeout := time.Millisecond * 100
   105  	retryTimeout := initialRetryTimeout
   106  	reconnect := false
   107  	for ; ; time.Sleep(retryTimeout) {
   108  		if retryTimeout < time.Minute {
   109  			retryTimeout *= 2
   110  		}
   111  		client, err := srpc.DialHTTP("tcp", sourceName, time.Second*15)
   112  		if err != nil {
   113  			logger.Printf("error connecting to: %s: %s\n", sourceName, err)
   114  			continue
   115  		}
   116  		conn, err := client.Call("FileGenerator.Connect")
   117  		if err != nil {
   118  			client.Close()
   119  			logger.Println(err)
   120  			continue
   121  		}
   122  		retryTimeout = initialRetryTimeout
   123  		go handleServerMessages(sourceName, conn, serverMessageChannel,
   124  			closeNotifyChannel, logger)
   125  		if reconnect {
   126  			sourceReconnectChannel <- sourceName
   127  		} else {
   128  			reconnect = true
   129  		}
   130  		sendClientRequests(conn, clientRequestChannel, closeNotifyChannel,
   131  			logger)
   132  		conn.Close()
   133  		client.Close()
   134  	}
   135  }
   136  
   137  func sendClientRequests(conn *srpc.Conn,
   138  	clientRequestChannel <-chan *proto.ClientRequest,
   139  	closeNotifyChannel <-chan struct{}, logger log.Logger) {
   140  	for {
   141  		select {
   142  		case clientRequest := <-clientRequestChannel:
   143  			if err := conn.Encode(clientRequest); err != nil {
   144  				logger.Printf("error encoding client request: %s\n", err)
   145  				return
   146  			}
   147  			if len(clientRequestChannel) < 1 {
   148  				if err := conn.Flush(); err != nil {
   149  					logger.Printf("error flushing: %s\n", err)
   150  					return
   151  				}
   152  			}
   153  		case <-closeNotifyChannel:
   154  			return
   155  		}
   156  	}
   157  }
   158  
   159  func handleServerMessages(sourceName string, decoder srpc.Decoder,
   160  	serverMessageChannel chan<- *serverMessageType,
   161  	closeNotifyChannel chan<- struct{}, logger log.Logger) {
   162  	for {
   163  		var message proto.ServerMessage
   164  		if err := decoder.Decode(&message); err != nil {
   165  			if err == io.EOF {
   166  				logger.Printf("connection to source: %s closed\n", sourceName)
   167  			} else {
   168  				logger.Println(err)
   169  			}
   170  			closeNotifyChannel <- struct{}{}
   171  			return
   172  		}
   173  		serverMessageChannel <- &serverMessageType{sourceName, message}
   174  	}
   175  }