github.com/m3db/m3@v1.5.0/src/dbnode/persist/fs/commitlog/options.go (about)

     1  // Copyright (c) 2016 Uber Technologies, Inc.
     2  //
     3  // Permission is hereby granted, free of charge, to any person obtaining a copy
     4  // of this software and associated documentation files (the "Software"), to deal
     5  // in the Software without restriction, including without limitation the rights
     6  // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     7  // copies of the Software, and to permit persons to whom the Software is
     8  // furnished to do so, subject to the following conditions:
     9  //
    10  // The above copyright notice and this permission notice shall be included in
    11  // all copies or substantial portions of the Software.
    12  //
    13  // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    14  // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    15  // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    16  // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    17  // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    18  // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    19  // THE SOFTWARE.
    20  
    21  package commitlog
    22  
    23  import (
    24  	"errors"
    25  	"fmt"
    26  	"runtime"
    27  	"time"
    28  
    29  	"github.com/m3db/m3/src/dbnode/persist/fs"
    30  	"github.com/m3db/m3/src/x/clock"
    31  	"github.com/m3db/m3/src/x/ident"
    32  	"github.com/m3db/m3/src/x/instrument"
    33  	"github.com/m3db/m3/src/x/pool"
    34  )
    35  
    36  const (
    37  	// defaultStrategy is the default commit log write strategy
    38  	defaultStrategy = StrategyWriteBehind
    39  
    40  	// defaultFailureStrategy is the default commit log failure strategy
    41  	defaultFailureStrategy = FailureStrategyPanic
    42  
    43  	// defaultFlushInterval is the default commit log flush interval
    44  	defaultFlushInterval = time.Second
    45  
    46  	// defaultFlushSize is the default commit log flush size
    47  	defaultFlushSize = 65536
    48  
    49  	// defaultBlockSize is the default commit log block size
    50  	defaultBlockSize = 15 * time.Minute
    51  
    52  	// defaultReadConcurrency is the default read concurrency
    53  	defaultReadConcurrency = 4
    54  
    55  	// MaximumQueueSizeQueueChannelSizeRatio is the maximum ratio between the
    56  	// backlog queue size and backlog queue channel size.
    57  	MaximumQueueSizeQueueChannelSizeRatio = 8.0
    58  )
    59  
    60  var (
    61  	// defaultBacklogQueueSize is the default commit log backlog queue size.
    62  	defaultBacklogQueueSize = 1024 * runtime.GOMAXPROCS(0)
    63  
    64  	// defaultBacklogQueueChannelSize is the default commit log backlog queue channel size.
    65  	defaultBacklogQueueChannelSize = int(float64(defaultBacklogQueueSize) / MaximumQueueSizeQueueChannelSizeRatio)
    66  )
    67  
    68  var (
    69  	errFlushIntervalNonNegative = errors.New("flush interval must be non-negative")
    70  	errBlockSizePositive        = errors.New("block size must be a positive duration")
    71  	errReadConcurrencyPositive  = errors.New("read concurrency must be a positive integer")
    72  	errMissingFailureCallback   = errors.New("failure callback must be non-nil if FailureStrategyCallback is used")
    73  )
    74  
    75  // FailureCallback is used in the FailureStrategyCallback failure mode.
    76  // If this function returns false, the error will be treated as fatal.
    77  // This function MUST exit quickly
    78  type FailureCallback func(err error) bool
    79  
    80  type options struct {
    81  	clockOpts               clock.Options
    82  	instrumentOpts          instrument.Options
    83  	blockSize               time.Duration
    84  	fsOpts                  fs.Options
    85  	strategy                Strategy
    86  	flushSize               int
    87  	flushInterval           time.Duration
    88  	backlogQueueSize        int
    89  	backlogQueueChannelSize int
    90  	bytesPool               pool.CheckedBytesPool
    91  	identPool               ident.Pool
    92  	readConcurrency         int
    93  	failureMode             FailureStrategy
    94  	failureCallback         FailureCallback
    95  }
    96  
    97  type optionsInput struct {
    98  	fsOptions fs.Options
    99  	// allows differentiating between explicitly nil and unset
   100  	fsOptionsSet bool
   101  
   102  	identPoolOpts   ident.PoolOptions
   103  	bytePoolOptions pool.ObjectPoolOptions
   104  }
   105  
   106  // OptionSetter is a function that modifies the behavior of NewOptions
   107  type OptionSetter func(o *optionsInput)
   108  
   109  // WithFileSystemOptions is an OptionsSetter that provides custom fs.Options
   110  // Passing nil will be equivalent to calling Options.SetFilesystemOptions(nil)
   111  func WithFileSystemOptions(o fs.Options) OptionSetter {
   112  	return func(input *optionsInput) {
   113  		input.fsOptions = o
   114  		input.fsOptionsSet = true
   115  	}
   116  }
   117  
   118  // WithIdentPoolOptions is an OptionsSetter that provides options to the IdentifierPool
   119  func WithIdentPoolOptions(o ident.PoolOptions) OptionSetter {
   120  	return func(input *optionsInput) {
   121  		input.identPoolOpts = o
   122  	}
   123  }
   124  
   125  // WithBytesPoolOptions is an OptionsSetter that provides options to BytesPool
   126  func WithBytesPoolOptions(o pool.ObjectPoolOptions) OptionSetter {
   127  	return func(input *optionsInput) {
   128  		input.bytePoolOptions = o
   129  	}
   130  }
   131  
   132  // NewOptions creates new commit log options
   133  func NewOptions(setters ...OptionSetter) Options {
   134  	presetOptions := optionsInput{}
   135  	for _, setter := range setters {
   136  		setter(&presetOptions)
   137  	}
   138  
   139  	if !presetOptions.fsOptionsSet && presetOptions.fsOptions == nil {
   140  		presetOptions.fsOptions = fs.NewOptions()
   141  	}
   142  
   143  	o := &options{
   144  		clockOpts:               clock.NewOptions(),
   145  		instrumentOpts:          instrument.NewOptions(),
   146  		blockSize:               defaultBlockSize,
   147  		fsOpts:                  presetOptions.fsOptions,
   148  		strategy:                defaultStrategy,
   149  		failureMode:             defaultFailureStrategy,
   150  		flushSize:               defaultFlushSize,
   151  		flushInterval:           defaultFlushInterval,
   152  		backlogQueueSize:        defaultBacklogQueueSize,
   153  		backlogQueueChannelSize: defaultBacklogQueueChannelSize,
   154  		bytesPool: pool.NewCheckedBytesPool(nil, presetOptions.bytePoolOptions, func(s []pool.Bucket) pool.BytesPool {
   155  			return pool.NewBytesPool(s, presetOptions.bytePoolOptions)
   156  		}),
   157  		readConcurrency: defaultReadConcurrency,
   158  		failureCallback: nil,
   159  	}
   160  
   161  	o.bytesPool.Init()
   162  	o.identPool = ident.NewPool(o.bytesPool, presetOptions.identPoolOpts)
   163  	return o
   164  }
   165  
   166  func (o *options) Validate() error {
   167  	if o.FlushInterval() < 0 {
   168  		return errFlushIntervalNonNegative
   169  	}
   170  
   171  	if o.BlockSize() <= 0 {
   172  		return errBlockSizePositive
   173  	}
   174  
   175  	if o.ReadConcurrency() <= 0 {
   176  		return errReadConcurrencyPositive
   177  	}
   178  
   179  	if float64(o.BacklogQueueSize())/float64(o.BacklogQueueChannelSize()) > MaximumQueueSizeQueueChannelSizeRatio {
   180  		return fmt.Errorf(
   181  			"BacklogQueueSize / BacklogQueueChannelSize ratio must be at most: %f, but was: %f",
   182  			MaximumQueueSizeQueueChannelSizeRatio, float64(o.BacklogQueueSize())/float64(o.BacklogQueueChannelSize()))
   183  	}
   184  
   185  	if o.FailureStrategy() == FailureStrategyCallback && o.FailureCallback() == nil {
   186  		return errMissingFailureCallback
   187  	}
   188  
   189  	return nil
   190  }
   191  
   192  func (o *options) SetClockOptions(value clock.Options) Options {
   193  	opts := *o
   194  	opts.clockOpts = value
   195  	return &opts
   196  }
   197  
   198  func (o *options) ClockOptions() clock.Options {
   199  	return o.clockOpts
   200  }
   201  
   202  func (o *options) SetInstrumentOptions(value instrument.Options) Options {
   203  	opts := *o
   204  	opts.instrumentOpts = value
   205  	return &opts
   206  }
   207  
   208  func (o *options) InstrumentOptions() instrument.Options {
   209  	return o.instrumentOpts
   210  }
   211  
   212  func (o *options) SetBlockSize(value time.Duration) Options {
   213  	opts := *o
   214  	opts.blockSize = value
   215  	return &opts
   216  }
   217  
   218  func (o *options) BlockSize() time.Duration {
   219  	return o.blockSize
   220  }
   221  
   222  func (o *options) SetFilesystemOptions(value fs.Options) Options {
   223  	opts := *o
   224  	opts.fsOpts = value
   225  	return &opts
   226  }
   227  
   228  func (o *options) FilesystemOptions() fs.Options {
   229  	return o.fsOpts
   230  }
   231  
   232  func (o *options) SetStrategy(value Strategy) Options {
   233  	opts := *o
   234  	opts.strategy = value
   235  	return &opts
   236  }
   237  
   238  func (o *options) Strategy() Strategy {
   239  	return o.strategy
   240  }
   241  
   242  func (o *options) SetFlushSize(value int) Options {
   243  	opts := *o
   244  	opts.flushSize = value
   245  	return &opts
   246  }
   247  
   248  func (o *options) FlushSize() int {
   249  	return o.flushSize
   250  }
   251  
   252  func (o *options) SetFlushInterval(value time.Duration) Options {
   253  	opts := *o
   254  	opts.flushInterval = value
   255  	return &opts
   256  }
   257  
   258  func (o *options) FlushInterval() time.Duration {
   259  	return o.flushInterval
   260  }
   261  
   262  func (o *options) SetBacklogQueueSize(value int) Options {
   263  	opts := *o
   264  	opts.backlogQueueSize = value
   265  	return &opts
   266  }
   267  
   268  func (o *options) BacklogQueueSize() int {
   269  	return o.backlogQueueSize
   270  }
   271  
   272  func (o *options) SetBacklogQueueChannelSize(value int) Options {
   273  	opts := *o
   274  	opts.backlogQueueChannelSize = value
   275  	return &opts
   276  }
   277  
   278  func (o *options) BacklogQueueChannelSize() int {
   279  	return o.backlogQueueChannelSize
   280  }
   281  
   282  func (o *options) SetBytesPool(value pool.CheckedBytesPool) Options {
   283  	opts := *o
   284  	opts.bytesPool = value
   285  	return &opts
   286  }
   287  
   288  func (o *options) BytesPool() pool.CheckedBytesPool {
   289  	return o.bytesPool
   290  }
   291  
   292  func (o *options) SetReadConcurrency(concurrency int) Options {
   293  	opts := *o
   294  	opts.readConcurrency = concurrency
   295  	return &opts
   296  }
   297  
   298  func (o *options) ReadConcurrency() int {
   299  	return o.readConcurrency
   300  }
   301  
   302  func (o *options) SetIdentifierPool(value ident.Pool) Options {
   303  	opts := *o
   304  	opts.identPool = value
   305  	return &opts
   306  }
   307  
   308  func (o *options) IdentifierPool() ident.Pool {
   309  	return o.identPool
   310  }
   311  
   312  func (o *options) SetFailureStrategy(value FailureStrategy) Options {
   313  	opts := *o
   314  	opts.failureMode = value
   315  	return &opts
   316  }
   317  
   318  func (o *options) FailureStrategy() FailureStrategy {
   319  	return o.failureMode
   320  }
   321  
   322  func (o *options) SetFailureCallback(value FailureCallback) Options {
   323  	opts := *o
   324  	opts.failureCallback = value
   325  	return &opts
   326  }
   327  
   328  func (o *options) FailureCallback() FailureCallback {
   329  	return o.failureCallback
   330  }