github.com/authzed/spicedb@v1.32.1-0.20240520085336-ebda56537386/internal/datastore/mysql/options.go (about)

     1  package mysql
     2  
     3  import (
     4  	"fmt"
     5  	"time"
     6  )
     7  
     8  const (
     9  	errQuantizationTooLarge = "revision quantization interval (%s) must be less than GC window (%s)"
    10  
    11  	defaultGarbageCollectionWindow           = 24 * time.Hour
    12  	defaultGarbageCollectionInterval         = time.Minute * 3
    13  	defaultGarbageCollectionMaxOperationTime = time.Minute
    14  	defaultMaxOpenConns                      = 20
    15  	defaultConnMaxIdleTime                   = 30 * time.Minute
    16  	defaultConnMaxLifetime                   = 30 * time.Minute
    17  	defaultWatchBufferLength                 = 128
    18  	defaultWatchBufferWriteTimeout           = 1 * time.Second
    19  	defaultQuantization                      = 5 * time.Second
    20  	defaultMaxRevisionStalenessPercent       = 0.1
    21  	defaultEnablePrometheusStats             = false
    22  	defaultMaxRetries                        = 8
    23  	defaultGCEnabled                         = true
    24  	defaultCredentialsProviderName           = ""
    25  )
    26  
    27  type mysqlOptions struct {
    28  	revisionQuantization        time.Duration
    29  	gcWindow                    time.Duration
    30  	gcInterval                  time.Duration
    31  	gcMaxOperationTime          time.Duration
    32  	maxRevisionStalenessPercent float64
    33  	watchBufferLength           uint16
    34  	watchBufferWriteTimeout     time.Duration
    35  	tablePrefix                 string
    36  	enablePrometheusStats       bool
    37  	maxOpenConns                int
    38  	connMaxIdleTime             time.Duration
    39  	connMaxLifetime             time.Duration
    40  	analyzeBeforeStats          bool
    41  	maxRetries                  uint8
    42  	lockWaitTimeoutSeconds      *uint8
    43  	gcEnabled                   bool
    44  	credentialsProviderName     string
    45  }
    46  
    47  // Option provides the facility to configure how clients within the
    48  // MySQL datastore interact with the running MySQL database.
    49  type Option func(*mysqlOptions)
    50  
    51  func generateConfig(options []Option) (mysqlOptions, error) {
    52  	computed := mysqlOptions{
    53  		gcWindow:                    defaultGarbageCollectionWindow,
    54  		gcInterval:                  defaultGarbageCollectionInterval,
    55  		gcMaxOperationTime:          defaultGarbageCollectionMaxOperationTime,
    56  		watchBufferLength:           defaultWatchBufferLength,
    57  		watchBufferWriteTimeout:     defaultWatchBufferWriteTimeout,
    58  		maxOpenConns:                defaultMaxOpenConns,
    59  		connMaxIdleTime:             defaultConnMaxIdleTime,
    60  		connMaxLifetime:             defaultConnMaxLifetime,
    61  		revisionQuantization:        defaultQuantization,
    62  		maxRevisionStalenessPercent: defaultMaxRevisionStalenessPercent,
    63  		enablePrometheusStats:       defaultEnablePrometheusStats,
    64  		maxRetries:                  defaultMaxRetries,
    65  		gcEnabled:                   defaultGCEnabled,
    66  		credentialsProviderName:     defaultCredentialsProviderName,
    67  	}
    68  
    69  	for _, option := range options {
    70  		option(&computed)
    71  	}
    72  
    73  	// Run any checks on the config that need to be done
    74  	if computed.revisionQuantization >= computed.gcWindow {
    75  		return computed, fmt.Errorf(
    76  			errQuantizationTooLarge,
    77  			computed.revisionQuantization,
    78  			computed.gcWindow,
    79  		)
    80  	}
    81  
    82  	return computed, nil
    83  }
    84  
    85  // WatchBufferLength is the number of entries that can be stored in the watch
    86  // buffer while awaiting read by the client.
    87  //
    88  // This value defaults to 128.
    89  func WatchBufferLength(watchBufferLength uint16) Option {
    90  	return func(mo *mysqlOptions) {
    91  		mo.watchBufferLength = watchBufferLength
    92  	}
    93  }
    94  
    95  // WatchBufferWriteTimeout is the maximum timeout for writing to the watch buffer,
    96  // after which the caller to the watch will be disconnected.
    97  func WatchBufferWriteTimeout(watchBufferWriteTimeout time.Duration) Option {
    98  	return func(mo *mysqlOptions) { mo.watchBufferWriteTimeout = watchBufferWriteTimeout }
    99  }
   100  
   101  // RevisionQuantization is the time bucket size to which advertised
   102  // revisions will be rounded.
   103  //
   104  // This value defaults to 5 seconds.
   105  func RevisionQuantization(quantization time.Duration) Option {
   106  	return func(mo *mysqlOptions) {
   107  		mo.revisionQuantization = quantization
   108  	}
   109  }
   110  
   111  // MaxRevisionStalenessPercent is the amount of time, expressed as a percentage of
   112  // the revision quantization window, that a previously computed rounded revision
   113  // can still be advertised after the next rounded revision would otherwise be ready.
   114  //
   115  // This value defaults to 0.1 (10%).
   116  func MaxRevisionStalenessPercent(stalenessPercent float64) Option {
   117  	return func(mo *mysqlOptions) {
   118  		mo.maxRevisionStalenessPercent = stalenessPercent
   119  	}
   120  }
   121  
   122  // GCWindow is the maximum age of a passed revision that will be considered
   123  // valid.
   124  //
   125  // This value defaults to 24 hours.
   126  func GCWindow(window time.Duration) Option {
   127  	return func(mo *mysqlOptions) {
   128  		mo.gcWindow = window
   129  	}
   130  }
   131  
   132  // GCInterval is the interval at which garbage collection will occur.
   133  //
   134  // This value defaults to 3 minutes.
   135  func GCInterval(interval time.Duration) Option {
   136  	return func(mo *mysqlOptions) {
   137  		mo.gcInterval = interval
   138  	}
   139  }
   140  
   141  // MaxRetries is the maximum number of times a retriable transaction will be
   142  // client-side retried.
   143  //
   144  // Default: 10
   145  func MaxRetries(maxRetries uint8) Option {
   146  	return func(mo *mysqlOptions) {
   147  		mo.maxRetries = maxRetries
   148  	}
   149  }
   150  
   151  // TablePrefix allows defining a MySQL table name prefix.
   152  //
   153  // No prefix is set by default
   154  func TablePrefix(prefix string) Option {
   155  	return func(mo *mysqlOptions) {
   156  		mo.tablePrefix = prefix
   157  	}
   158  }
   159  
   160  // WithEnablePrometheusStats marks whether Prometheus metrics provided by Go's database/sql package
   161  // are enabled.
   162  //
   163  // Prometheus metrics are disabled by default.
   164  func WithEnablePrometheusStats(enablePrometheusStats bool) Option {
   165  	return func(mo *mysqlOptions) {
   166  		mo.enablePrometheusStats = enablePrometheusStats
   167  	}
   168  }
   169  
   170  // ConnMaxIdleTime is the duration after which an idle connection will be
   171  // automatically closed.
   172  // See https://pkg.go.dev/database/sql#DB.SetConnMaxIdleTime/
   173  //
   174  // This value defaults to having no maximum.
   175  func ConnMaxIdleTime(idle time.Duration) Option {
   176  	return func(mo *mysqlOptions) {
   177  		mo.connMaxIdleTime = idle
   178  	}
   179  }
   180  
   181  // ConnMaxLifetime is the duration since creation after which a connection will
   182  // be automatically closed.
   183  // See https://pkg.go.dev/database/sql#DB.SetConnMaxLifetime
   184  //
   185  // This value defaults to having no maximum.
   186  func ConnMaxLifetime(lifetime time.Duration) Option {
   187  	return func(mo *mysqlOptions) {
   188  		mo.connMaxLifetime = lifetime
   189  	}
   190  }
   191  
   192  // MaxOpenConns is the maximum size of the connection pool.
   193  // See https://pkg.go.dev/database/sql#DB.SetMaxOpenConns
   194  //
   195  // This value defaults to having no maximum.
   196  func MaxOpenConns(conns int) Option {
   197  	return func(mo *mysqlOptions) {
   198  		mo.maxOpenConns = conns
   199  	}
   200  }
   201  
   202  // DebugAnalyzeBeforeStatistics signals to the Statistics method that it should
   203  // run Analyze Table on the relationships table before returning statistics.
   204  // This should only be used for debug and testing.
   205  //
   206  // Disabled by default.
   207  func DebugAnalyzeBeforeStatistics() Option {
   208  	return func(mo *mysqlOptions) {
   209  		mo.analyzeBeforeStats = true
   210  	}
   211  }
   212  
   213  // OverrideLockWaitTimeout sets the lock wait timeout on each new connection established
   214  // with the databases. As an OLTP service, the default of 50s is unbearably long to block
   215  // a write for our service, so we suggest setting this value to the minimum of 1 second.
   216  //
   217  // Uses server default by default.
   218  func OverrideLockWaitTimeout(seconds uint8) Option {
   219  	return func(mo *mysqlOptions) {
   220  		mo.lockWaitTimeoutSeconds = &seconds
   221  	}
   222  }
   223  
   224  // GCEnabled indicates whether garbage collection is enabled.
   225  //
   226  // GC is enabled by default.
   227  func GCEnabled(isGCEnabled bool) Option {
   228  	return func(mo *mysqlOptions) {
   229  		mo.gcEnabled = isGCEnabled
   230  	}
   231  }
   232  
   233  // GCMaxOperationTime is the maximum operation time of a garbage collection
   234  // pass before it times out.
   235  //
   236  // This value defaults to 1 minute.
   237  func GCMaxOperationTime(time time.Duration) Option {
   238  	return func(mo *mysqlOptions) {
   239  		mo.gcMaxOperationTime = time
   240  	}
   241  }
   242  
   243  // CredentialsProviderName is the name of the CredentialsProvider implementation to use
   244  // for dynamically retrieving the datastore credentials at runtime
   245  //
   246  // Empty by default.
   247  func CredentialsProviderName(credentialsProviderName string) Option {
   248  	return func(mo *mysqlOptions) { mo.credentialsProviderName = credentialsProviderName }
   249  }