github.com/choria-io/go-choria@v0.28.1-0.20240416190746-b3bf9c7d5a45/broker/federation/choria_nats_egest.go (about)

     1  // Copyright (c) 2017-2021, R.I. Pienaar and the Choria Project contributors
     2  //
     3  // SPDX-License-Identifier: Apache-2.0
     4  
     5  package federation
     6  
     7  import (
     8  	"context"
     9  	"fmt"
    10  	"strings"
    11  
    12  	"github.com/choria-io/go-choria/broker/federation/stats"
    13  	"github.com/choria-io/go-choria/inter"
    14  	"github.com/prometheus/client_golang/prometheus"
    15  	log "github.com/sirupsen/logrus"
    16  )
    17  
    18  func NewChoriaNatsEgest(workers int, mode int, capacity int, broker *FederationBroker, logger *log.Entry) (*pooledWorker, error) {
    19  	worker, err := PooledWorkerFactory("choria_nats_egest", workers, mode, capacity, broker, logger, func(ctx context.Context, self *pooledWorker, i int, logger *log.Entry) {
    20  		defer self.wg.Done()
    21  
    22  		var nc inter.Connector
    23  		var err error
    24  
    25  		nc, err = self.connection.NewConnector(ctx, self.servers, self.Name(), logger)
    26  		if err != nil {
    27  			logger.Errorf("Could not start NATS connection for worker %d: %s", i, err)
    28  			return
    29  		}
    30  
    31  		workeri := fmt.Sprintf("%d", i)
    32  		mname := nameForConnectionMode(mode)
    33  		rctr := stats.ReceivedMsgsCtr.WithLabelValues("nats_egest", workeri, mname, self.broker.Name, self.broker.identity)
    34  		pctr := stats.PublishedMsgsCtr.WithLabelValues("nats_egest", workeri, mname, self.broker.Name, self.broker.identity)
    35  		ectr := stats.ErrorCtr.WithLabelValues("nats_egest", workeri, mname, self.broker.Name, self.broker.identity)
    36  		timer := stats.ProcessTime.WithLabelValues("nats_egest", workeri, mname, self.broker.Name, self.broker.identity)
    37  
    38  		handler := func(cm chainmessage) {
    39  			obs := prometheus.NewTimer(timer)
    40  			defer obs.ObserveDuration()
    41  
    42  			if len(cm.Targets) == 0 {
    43  				logger.Errorf("Received message '%s' with no targets, discarding: %#v", cm.RequestID, cm)
    44  				ectr.Inc()
    45  				return
    46  			}
    47  
    48  			rctr.Inc()
    49  
    50  			logger.Debugf("Publishing message '%s' to %d target(s)", cm.RequestID, len(cm.Targets))
    51  
    52  			cm.Seen = append(cm.Seen, fmt.Sprintf("%s:%d", self.Name(), i))
    53  			cm.Seen = append(cm.Seen, nc.ConnectedServer())
    54  
    55  			if len(cm.Seen) >= 3 {
    56  				mid := fmt.Sprintf("%s (%s)", self.choria.Configuration().Identity, strings.Join(cm.Seen[1:len(cm.Seen)-1], ", "))
    57  				cm.Message.RecordNetworkHop(cm.Seen[0], mid, cm.Seen[len(cm.Seen)-1])
    58  			}
    59  
    60  			j, err := cm.Message.JSON()
    61  			if err != nil {
    62  				logger.Errorf("Could not JSON encode message '%s': %s", cm.RequestID, err)
    63  				ectr.Inc()
    64  				return
    65  			}
    66  
    67  			for _, target := range cm.Targets {
    68  				if err = nc.PublishRaw(target, []byte(j)); err != nil {
    69  					logger.Errorf("Could not publish message '%s' to '%s': %s", cm.RequestID, target, err)
    70  					ectr.Inc()
    71  					continue
    72  				}
    73  				pctr.Inc()
    74  			}
    75  		}
    76  
    77  		for {
    78  			var cm chainmessage
    79  
    80  			select {
    81  			case cm = <-self.in:
    82  				handler(cm)
    83  			case <-ctx.Done():
    84  				logger.Infof("Worker routine %s exiting", self.Name())
    85  				return
    86  			}
    87  		}
    88  	})
    89  
    90  	return worker, err
    91  }