github.com/koko1123/flow-go-1@v0.29.6/engine/access/rpc/engine.go (about)

     1  package rpc
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  	"fmt"
     7  	"net"
     8  	"net/http"
     9  	"sync"
    10  	"time"
    11  
    12  	grpc_prometheus "github.com/grpc-ecosystem/go-grpc-prometheus"
    13  	lru "github.com/hashicorp/golang-lru"
    14  	accessproto "github.com/onflow/flow/protobuf/go/flow/access"
    15  	"github.com/rs/zerolog"
    16  	"google.golang.org/grpc"
    17  	"google.golang.org/grpc/credentials"
    18  
    19  	"github.com/koko1123/flow-go-1/engine"
    20  	"github.com/koko1123/flow-go-1/engine/access/rest"
    21  	"github.com/koko1123/flow-go-1/engine/access/rpc/backend"
    22  	"github.com/koko1123/flow-go-1/engine/common/rpc"
    23  	"github.com/koko1123/flow-go-1/model/flow"
    24  	"github.com/koko1123/flow-go-1/module"
    25  	"github.com/koko1123/flow-go-1/state/protocol"
    26  	"github.com/koko1123/flow-go-1/storage"
    27  	"github.com/koko1123/flow-go-1/utils/grpcutils"
    28  )
    29  
    30  // Config defines the configurable options for the access node server
    31  // A secure GRPC server here implies a server that presents a self-signed TLS certificate and a client that authenticates
    32  // the server via a pre-shared public key
    33  type Config struct {
    34  	UnsecureGRPCListenAddr    string                           // the non-secure GRPC server address as ip:port
    35  	SecureGRPCListenAddr      string                           // the secure GRPC server address as ip:port
    36  	StateStreamListenAddr     string                           // the state stream GRPC server address as ip:port
    37  	TransportCredentials      credentials.TransportCredentials // the secure GRPC credentials
    38  	HTTPListenAddr            string                           // the HTTP web proxy address as ip:port
    39  	RESTListenAddr            string                           // the REST server address as ip:port (if empty the REST server will not be started)
    40  	CollectionAddr            string                           // the address of the upstream collection node
    41  	HistoricalAccessAddrs     string                           // the list of all access nodes from previous spork
    42  	MaxMsgSize                int                              // GRPC max message size
    43  	MaxExecutionDataMsgSize   int                              // GRPC max message size for block execution data
    44  	ExecutionClientTimeout    time.Duration                    // execution API GRPC client timeout
    45  	CollectionClientTimeout   time.Duration                    // collection API GRPC client timeout
    46  	ConnectionPoolSize        uint                             // size of the cache for storing collection and execution connections
    47  	MaxHeightRange            uint                             // max size of height range requests
    48  	PreferredExecutionNodeIDs []string                         // preferred list of upstream execution node IDs
    49  	FixedExecutionNodeIDs     []string                         // fixed list of execution node IDs to choose from if no node node ID can be chosen from the PreferredExecutionNodeIDs
    50  }
    51  
    52  // Engine exposes the server with a simplified version of the Access API.
    53  // An unsecured GRPC server (default port 9000), a secure GRPC server (default port 9001) and an HTTP Web proxy (default
    54  // port 8000) are brought up.
    55  type Engine struct {
    56  	unit               *engine.Unit
    57  	log                zerolog.Logger
    58  	backend            *backend.Backend // the gRPC service implementation
    59  	unsecureGrpcServer *grpc.Server     // the unsecure gRPC server
    60  	secureGrpcServer   *grpc.Server     // the secure gRPC server
    61  	httpServer         *http.Server
    62  	restServer         *http.Server
    63  	config             Config
    64  	chain              flow.Chain
    65  
    66  	addrLock            sync.RWMutex
    67  	unsecureGrpcAddress net.Addr
    68  	secureGrpcAddress   net.Addr
    69  	restAPIAddress      net.Addr
    70  }
    71  
    72  // NewBuilder returns a new RPC engine builder.
    73  func NewBuilder(log zerolog.Logger,
    74  	state protocol.State,
    75  	config Config,
    76  	collectionRPC accessproto.AccessAPIClient,
    77  	historicalAccessNodes []accessproto.AccessAPIClient,
    78  	blocks storage.Blocks,
    79  	headers storage.Headers,
    80  	collections storage.Collections,
    81  	transactions storage.Transactions,
    82  	executionReceipts storage.ExecutionReceipts,
    83  	executionResults storage.ExecutionResults,
    84  	chainID flow.ChainID,
    85  	transactionMetrics module.TransactionMetrics,
    86  	accessMetrics module.AccessMetrics,
    87  	collectionGRPCPort uint,
    88  	executionGRPCPort uint,
    89  	retryEnabled bool,
    90  	rpcMetricsEnabled bool,
    91  	apiRatelimits map[string]int, // the api rate limit (max calls per second) for each of the Access API e.g. Ping->100, GetTransaction->300
    92  	apiBurstLimits map[string]int, // the api burst limit (max calls at the same time) for each of the Access API e.g. Ping->50, GetTransaction->10
    93  ) (*RPCEngineBuilder, error) {
    94  
    95  	log = log.With().Str("engine", "rpc").Logger()
    96  
    97  	if config.MaxMsgSize == 0 {
    98  		config.MaxMsgSize = grpcutils.DefaultMaxMsgSize
    99  	}
   100  
   101  	// create a GRPC server to serve GRPC clients
   102  	grpcOpts := []grpc.ServerOption{
   103  		grpc.MaxRecvMsgSize(config.MaxMsgSize),
   104  		grpc.MaxSendMsgSize(config.MaxMsgSize),
   105  	}
   106  
   107  	var interceptors []grpc.UnaryServerInterceptor // ordered list of interceptors
   108  	// if rpc metrics is enabled, first create the grpc metrics interceptor
   109  	if rpcMetricsEnabled {
   110  		interceptors = append(interceptors, grpc_prometheus.UnaryServerInterceptor)
   111  	}
   112  
   113  	if len(apiRatelimits) > 0 {
   114  		// create a rate limit interceptor
   115  		rateLimitInterceptor := rpc.NewRateLimiterInterceptor(log, apiRatelimits, apiBurstLimits).UnaryServerInterceptor
   116  		// append the rate limit interceptor to the list of interceptors
   117  		interceptors = append(interceptors, rateLimitInterceptor)
   118  	}
   119  
   120  	// add the logging interceptor, ensure it is innermost wrapper
   121  	interceptors = append(interceptors, rpc.LoggingInterceptor(log)...)
   122  
   123  	// create a chained unary interceptor
   124  	chainedInterceptors := grpc.ChainUnaryInterceptor(interceptors...)
   125  	grpcOpts = append(grpcOpts, chainedInterceptors)
   126  
   127  	// create an unsecured grpc server
   128  	unsecureGrpcServer := grpc.NewServer(grpcOpts...)
   129  
   130  	// create a secure server server by using the secure grpc credentials that are passed in as part of config
   131  	grpcOpts = append(grpcOpts, grpc.Creds(config.TransportCredentials))
   132  	secureGrpcServer := grpc.NewServer(grpcOpts...)
   133  
   134  	// wrap the unsecured server with an HTTP proxy server to serve HTTP clients
   135  	httpServer := NewHTTPServer(unsecureGrpcServer, config.HTTPListenAddr)
   136  
   137  	var cache *lru.Cache
   138  	cacheSize := config.ConnectionPoolSize
   139  	if cacheSize > 0 {
   140  		// TODO: remove this fallback after fixing issues with evictions
   141  		// It was observed that evictions cause connection errors for in flight requests. This works around
   142  		// the issue by forcing hte pool size to be greater than the number of ENs + LNs
   143  		if cacheSize < backend.DefaultConnectionPoolSize {
   144  			log.Warn().Msg("connection pool size below threshold, setting pool size to default value ")
   145  			cacheSize = backend.DefaultConnectionPoolSize
   146  		}
   147  		var err error
   148  		cache, err = lru.NewWithEvict(int(cacheSize), func(_, evictedValue interface{}) {
   149  			store := evictedValue.(*backend.CachedClient)
   150  			store.Close()
   151  			log.Debug().Str("grpc_conn_evicted", store.Address).Msg("closing grpc connection evicted from pool")
   152  			if accessMetrics != nil {
   153  				accessMetrics.ConnectionFromPoolEvicted()
   154  			}
   155  		})
   156  		if err != nil {
   157  			return nil, fmt.Errorf("could not initialize connection pool cache: %w", err)
   158  		}
   159  	}
   160  
   161  	connectionFactory := &backend.ConnectionFactoryImpl{
   162  		CollectionGRPCPort:        collectionGRPCPort,
   163  		ExecutionGRPCPort:         executionGRPCPort,
   164  		CollectionNodeGRPCTimeout: config.CollectionClientTimeout,
   165  		ExecutionNodeGRPCTimeout:  config.ExecutionClientTimeout,
   166  		ConnectionsCache:          cache,
   167  		CacheSize:                 cacheSize,
   168  		AccessMetrics:             accessMetrics,
   169  		Log:                       log,
   170  	}
   171  
   172  	backend := backend.New(state,
   173  		collectionRPC,
   174  		historicalAccessNodes,
   175  		blocks,
   176  		headers,
   177  		collections,
   178  		transactions,
   179  		executionReceipts,
   180  		executionResults,
   181  		chainID,
   182  		transactionMetrics,
   183  		connectionFactory,
   184  		retryEnabled,
   185  		config.MaxHeightRange,
   186  		config.PreferredExecutionNodeIDs,
   187  		config.FixedExecutionNodeIDs,
   188  		log,
   189  		backend.DefaultSnapshotHistoryLimit,
   190  	)
   191  
   192  	eng := &Engine{
   193  		log:                log,
   194  		unit:               engine.NewUnit(),
   195  		backend:            backend,
   196  		unsecureGrpcServer: unsecureGrpcServer,
   197  		secureGrpcServer:   secureGrpcServer,
   198  		httpServer:         httpServer,
   199  		config:             config,
   200  		chain:              chainID.Chain(),
   201  	}
   202  
   203  	builder := NewRPCEngineBuilder(eng)
   204  	if rpcMetricsEnabled {
   205  		builder.WithMetrics()
   206  	}
   207  
   208  	return builder, nil
   209  }
   210  
   211  // Ready returns a ready channel that is closed once the engine has fully
   212  // started. The RPC engine is ready when the gRPC server has successfully
   213  // started.
   214  func (e *Engine) Ready() <-chan struct{} {
   215  	e.unit.Launch(e.serveUnsecureGRPC)
   216  	e.unit.Launch(e.serveSecureGRPC)
   217  	e.unit.Launch(e.serveGRPCWebProxy)
   218  	if e.config.RESTListenAddr != "" {
   219  		e.unit.Launch(e.serveREST)
   220  	}
   221  	return e.unit.Ready()
   222  }
   223  
   224  // Done returns a done channel that is closed once the engine has fully stopped.
   225  // It sends a signal to stop the gRPC server, then closes the channel.
   226  func (e *Engine) Done() <-chan struct{} {
   227  	return e.unit.Done(
   228  		e.unsecureGrpcServer.GracefulStop,
   229  		e.secureGrpcServer.GracefulStop,
   230  		func() {
   231  			err := e.httpServer.Shutdown(context.Background())
   232  			if err != nil {
   233  				e.log.Error().Err(err).Msg("error stopping http server")
   234  			}
   235  		},
   236  		func() {
   237  			if e.restServer != nil {
   238  				err := e.restServer.Shutdown(context.Background())
   239  				if err != nil {
   240  					e.log.Error().Err(err).Msg("error stopping http REST server")
   241  				}
   242  			}
   243  		})
   244  }
   245  
   246  // SubmitLocal submits an event originating on the local node.
   247  func (e *Engine) SubmitLocal(event interface{}) {
   248  	e.unit.Launch(func() {
   249  		err := e.process(event)
   250  		if err != nil {
   251  			e.log.Error().Err(err).Msg("could not process submitted event")
   252  		}
   253  	})
   254  }
   255  
   256  func (e *Engine) UnsecureGRPCAddress() net.Addr {
   257  	e.addrLock.RLock()
   258  	defer e.addrLock.RUnlock()
   259  	return e.unsecureGrpcAddress
   260  }
   261  
   262  func (e *Engine) SecureGRPCAddress() net.Addr {
   263  	e.addrLock.RLock()
   264  	defer e.addrLock.RUnlock()
   265  	return e.secureGrpcAddress
   266  }
   267  
   268  func (e *Engine) RestApiAddress() net.Addr {
   269  	e.addrLock.RLock()
   270  	defer e.addrLock.RUnlock()
   271  	return e.restAPIAddress
   272  }
   273  
   274  // process processes the given ingestion engine event. Events that are given
   275  // to this function originate within the expulsion engine on the node with the
   276  // given origin ID.
   277  func (e *Engine) process(event interface{}) error {
   278  	switch entity := event.(type) {
   279  	case *flow.Block:
   280  		e.backend.NotifyFinalizedBlockHeight(entity.Header.Height)
   281  		return nil
   282  	default:
   283  		return fmt.Errorf("invalid event type (%T)", event)
   284  	}
   285  }
   286  
   287  // serveUnsecureGRPC starts the unsecure gRPC server
   288  // When this function returns, the server is considered ready.
   289  func (e *Engine) serveUnsecureGRPC() {
   290  
   291  	e.log.Info().Str("grpc_address", e.config.UnsecureGRPCListenAddr).Msg("starting grpc server on address")
   292  
   293  	l, err := net.Listen("tcp", e.config.UnsecureGRPCListenAddr)
   294  	if err != nil {
   295  		e.log.Err(err).Msg("failed to start the grpc server")
   296  		return
   297  	}
   298  
   299  	// save the actual address on which we are listening (may be different from e.config.UnsecureGRPCListenAddr if not port
   300  	// was specified)
   301  	e.addrLock.Lock()
   302  	e.unsecureGrpcAddress = l.Addr()
   303  	e.addrLock.Unlock()
   304  
   305  	e.log.Debug().Str("unsecure_grpc_address", e.unsecureGrpcAddress.String()).Msg("listening on port")
   306  
   307  	err = e.unsecureGrpcServer.Serve(l) // blocking call
   308  	if err != nil {
   309  		e.log.Fatal().Err(err).Msg("fatal error in unsecure grpc server")
   310  	}
   311  }
   312  
   313  // serveSecureGRPC starts the secure gRPC server
   314  // When this function returns, the server is considered ready.
   315  func (e *Engine) serveSecureGRPC() {
   316  
   317  	e.log.Info().Str("secure_grpc_address", e.config.SecureGRPCListenAddr).Msg("starting grpc server on address")
   318  
   319  	l, err := net.Listen("tcp", e.config.SecureGRPCListenAddr)
   320  	if err != nil {
   321  		e.log.Err(err).Msg("failed to start the grpc server")
   322  		return
   323  	}
   324  
   325  	e.addrLock.Lock()
   326  	e.secureGrpcAddress = l.Addr()
   327  	e.addrLock.Unlock()
   328  
   329  	e.log.Debug().Str("secure_grpc_address", e.secureGrpcAddress.String()).Msg("listening on port")
   330  
   331  	err = e.secureGrpcServer.Serve(l) // blocking call
   332  	if err != nil {
   333  		e.log.Fatal().Err(err).Msg("fatal error in secure grpc server")
   334  	}
   335  }
   336  
   337  // serveGRPCWebProxy starts the gRPC web proxy server
   338  func (e *Engine) serveGRPCWebProxy() {
   339  	log := e.log.With().Str("http_proxy_address", e.config.HTTPListenAddr).Logger()
   340  
   341  	log.Info().Msg("starting http proxy server on address")
   342  
   343  	err := e.httpServer.ListenAndServe()
   344  	if errors.Is(err, http.ErrServerClosed) {
   345  		return
   346  	}
   347  	if err != nil {
   348  		e.log.Err(err).Msg("failed to start the http proxy server")
   349  	}
   350  }
   351  
   352  // serveREST starts the HTTP REST server
   353  func (e *Engine) serveREST() {
   354  
   355  	e.log.Info().Str("rest_api_address", e.config.RESTListenAddr).Msg("starting REST server on address")
   356  
   357  	r, err := rest.NewServer(e.backend, e.config.RESTListenAddr, e.log, e.chain)
   358  	if err != nil {
   359  		e.log.Err(err).Msg("failed to initialize the REST server")
   360  		return
   361  	}
   362  	e.restServer = r
   363  
   364  	l, err := net.Listen("tcp", e.config.RESTListenAddr)
   365  	if err != nil {
   366  		e.log.Err(err).Msg("failed to start the REST server")
   367  		return
   368  	}
   369  
   370  	e.addrLock.Lock()
   371  	e.restAPIAddress = l.Addr()
   372  	e.addrLock.Unlock()
   373  
   374  	e.log.Debug().Str("rest_api_address", e.restAPIAddress.String()).Msg("listening on port")
   375  
   376  	err = e.restServer.Serve(l) // blocking call
   377  	if err != nil {
   378  		if errors.Is(err, http.ErrServerClosed) {
   379  			return
   380  		}
   381  		e.log.Error().Err(err).Msg("fatal error in REST server")
   382  	}
   383  }