github.com/pawelgaczynski/gain@v0.4.0-alpha.0.20230821120126-41f1e60a18da/config.go (about)

     1  // Copyright (c) 2023 Paweł Gaczyński
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package gain
    16  
    17  import (
    18  	"runtime"
    19  	"time"
    20  
    21  	"github.com/rs/zerolog"
    22  )
    23  
    24  const (
    25  	defaultPort           = 8080
    26  	defaultMaxCQEvents    = 16384
    27  	defaultMaxSQEntries   = 16384
    28  	defaultRecvBufferSize = 4096
    29  	defaultSendBufferSize = 4096
    30  )
    31  
    32  type Option[T any] func(*T)
    33  
    34  type ConfigOption Option[Config]
    35  
    36  type ServerArchitecture int
    37  
    38  const (
    39  	// Reactor design pattern has one input called Acceptor,
    40  	// which demultiplexes the handling of incoming connections to Consumer workers.
    41  	// The load balancing algorithm can be selected via configuration option.
    42  	Reactor ServerArchitecture = iota
    43  	// The Socket Sharding  allow multiple workers to listen on the same address and port combination.
    44  	// In this case the kernel distributes incoming requests across all the sockets.
    45  	SocketSharding
    46  )
    47  
    48  // Config is the configuration for the gain engine.
    49  type Config struct {
    50  	// Architecture indicates one of the two available architectures: Reactor and SocketSharding.
    51  	//
    52  	// The Reactor design pattern has one input called Acceptor,
    53  	// which demultiplexes the handling of incoming connections to Consumer workers.
    54  	// The load balancing algorithm can be selected via configuration option.
    55  	//
    56  	// The Socket Sharding allows multiple workers to listen on the same address and port combination.
    57  	// In this case the kernel distributes incoming requests across all the sockets.
    58  	Architecture ServerArchitecture
    59  	// AsyncHandler indicates whether the engine should run the OnRead EventHandler method in a separate goroutines.
    60  	AsyncHandler bool
    61  	// GoroutinePool indicates use of pool of bounded goroutines for OnRead calls.
    62  	// Important: Valid only if AsyncHandler is true
    63  	GoroutinePool bool
    64  	// CPUAffinity determines whether each engine worker is locked to the one CPU.
    65  	CPUAffinity bool
    66  	// ProcessPriority sets the prority of the process to high (-19). Requires root privileges.
    67  	ProcessPriority bool
    68  	// Workers indicates the number of consumers or shard workers. The default is runtime.NumCPU().
    69  	Workers int
    70  	// CBPFilter uses custom BPF filter to improve the performance of the Socket Sharding architecture.
    71  	CBPFilter bool
    72  	// LoadBalancing indicates the load-balancing algorithm to use when assigning a new connection.
    73  	// Important: valid only for Reactor architecture.
    74  	LoadBalancing LoadBalancing
    75  	// SocketRecvBufferSize sets the maximum socket receive buffer in bytes.
    76  	SocketRecvBufferSize int
    77  	// SocketSendBufferSize sets the maximum socket send buffer in bytes.
    78  	SocketSendBufferSize int
    79  	// TCPKeepAlive sets the TCP keep-alive for the socket.
    80  	TCPKeepAlive time.Duration
    81  	// LoggerLevel indicates the logging level.
    82  	LoggerLevel zerolog.Level
    83  	// PrettyLogger sets the pretty-printing zerolog mode.
    84  	// Important: it is inefficient so should be used only for debugging.
    85  	PrettyLogger bool
    86  
    87  	// ==============================
    88  	// io_uring related options
    89  	// ==============================
    90  	// MaxSQEntries sets the maximum number of SQEs that can be submitted in one batch.
    91  	// If the number of SQEs exceeds this value, the io_uring will return a SQE overflow error.
    92  	MaxSQEntries uint
    93  	// MaxCQEvents sets the maximum number of CQEs that can be retrieved in one batch.
    94  	MaxCQEvents uint
    95  }
    96  
    97  // WithArchitecture sets the architecture of gain engine.
    98  func WithArchitecture(architecture ServerArchitecture) ConfigOption {
    99  	return func(c *Config) {
   100  		c.Architecture = architecture
   101  	}
   102  }
   103  
   104  // WithAsyncHandler sets the asynchronous mode for the OnRead callback.
   105  func WithAsyncHandler(asyncHandler bool) ConfigOption {
   106  	return func(c *Config) {
   107  		c.AsyncHandler = asyncHandler
   108  	}
   109  }
   110  
   111  // WithGoroutinePool sets the goroutine pool for asynchronous handler.
   112  func WithGoroutinePool(goroutinePool bool) ConfigOption {
   113  	return func(c *Config) {
   114  		c.GoroutinePool = goroutinePool
   115  	}
   116  }
   117  
   118  // WithCPUAffinity sets the CPU affinity option.
   119  func WithCPUAffinity(cpuAffinity bool) ConfigOption {
   120  	return func(c *Config) {
   121  		c.CPUAffinity = cpuAffinity
   122  	}
   123  }
   124  
   125  // WithProcessPriority sets the high process priority. Note: requires root privileges.
   126  func WithProcessPriority(processPriority bool) ConfigOption {
   127  	return func(c *Config) {
   128  		c.ProcessPriority = processPriority
   129  	}
   130  }
   131  
   132  // WithWorkers sets the number of workers.
   133  func WithWorkers(workers int) ConfigOption {
   134  	return func(c *Config) {
   135  		c.Workers = workers
   136  	}
   137  }
   138  
   139  // WithCBPF sets the CBPF filter for the gain engine.
   140  func WithCBPF(cbpf bool) ConfigOption {
   141  	return func(c *Config) {
   142  		c.CBPFilter = cbpf
   143  	}
   144  }
   145  
   146  // WithLoadBalancing sets the load balancing algorithm.
   147  func WithLoadBalancing(loadBalancing LoadBalancing) ConfigOption {
   148  	return func(c *Config) {
   149  		c.LoadBalancing = loadBalancing
   150  	}
   151  }
   152  
   153  // WithSocketRecvBufferSize sets the maximum socket receive buffer in bytes.
   154  func WithSocketRecvBufferSize(size int) ConfigOption {
   155  	return func(c *Config) {
   156  		c.SocketRecvBufferSize = size
   157  	}
   158  }
   159  
   160  // WithSocketSendBufferSize sets the maximum socket send buffer in bytes.
   161  func WithSocketSendBufferSize(size int) ConfigOption {
   162  	return func(c *Config) {
   163  		c.SocketSendBufferSize = size
   164  	}
   165  }
   166  
   167  // WithTCPKeepAlive sets the TCP keep-alive for the socket.
   168  func WithTCPKeepAlive(tcpKeepAlive time.Duration) ConfigOption {
   169  	return func(c *Config) {
   170  		c.TCPKeepAlive = tcpKeepAlive
   171  	}
   172  }
   173  
   174  // WithLoggerLevel sets the logging level.
   175  func WithLoggerLevel(loggerLevel zerolog.Level) ConfigOption {
   176  	return func(c *Config) {
   177  		c.LoggerLevel = loggerLevel
   178  	}
   179  }
   180  
   181  // WithPrettyLogger sets the pretty-printing zerolog mode.
   182  func WithPrettyLogger(prettyLogger bool) ConfigOption {
   183  	return func(c *Config) {
   184  		c.PrettyLogger = prettyLogger
   185  	}
   186  }
   187  
   188  // WithMaxSQEntries sets the maximum number of entries in the submission queue.
   189  func WithMaxSQEntries(maxSQEntries uint) ConfigOption {
   190  	return func(c *Config) {
   191  		c.MaxSQEntries = maxSQEntries
   192  	}
   193  }
   194  
   195  // WithMaxCQEvents sets the maximum number of entries in the completion queue.
   196  func WithMaxCQEvents(maxCQEvents uint) ConfigOption {
   197  	return func(c *Config) {
   198  		c.MaxCQEvents = maxCQEvents
   199  	}
   200  }
   201  
   202  func NewConfig(opts ...ConfigOption) Config {
   203  	config := Config{
   204  		Architecture:         Reactor,
   205  		AsyncHandler:         false,
   206  		GoroutinePool:        false,
   207  		CPUAffinity:          false,
   208  		ProcessPriority:      false,
   209  		LoggerLevel:          zerolog.ErrorLevel,
   210  		PrettyLogger:         false,
   211  		Workers:              runtime.NumCPU(),
   212  		CBPFilter:            false,
   213  		LoadBalancing:        RoundRobin,
   214  		SocketRecvBufferSize: 0,
   215  		SocketSendBufferSize: 0,
   216  		TCPKeepAlive:         0,
   217  		MaxSQEntries:         defaultMaxSQEntries,
   218  		MaxCQEvents:          defaultMaxCQEvents,
   219  	}
   220  	for _, opt := range opts {
   221  		opt(&config)
   222  	}
   223  
   224  	return config
   225  }