github.com/authzed/spicedb@v1.32.1-0.20240520085336-ebda56537386/internal/datastore/crdb/options.go (about) 1 package crdb 2 3 import ( 4 "fmt" 5 "time" 6 7 pgxcommon "github.com/authzed/spicedb/internal/datastore/postgres/common" 8 ) 9 10 type crdbOptions struct { 11 readPoolOpts, writePoolOpts pgxcommon.PoolOptions 12 connectRate time.Duration 13 14 watchBufferLength uint16 15 watchBufferWriteTimeout time.Duration 16 revisionQuantization time.Duration 17 followerReadDelay time.Duration 18 maxRevisionStalenessPercent float64 19 gcWindow time.Duration 20 maxRetries uint8 21 overlapStrategy string 22 overlapKey string 23 enableConnectionBalancing bool 24 analyzeBeforeStatistics bool 25 26 enablePrometheusStats bool 27 } 28 29 const ( 30 errQuantizationTooLarge = "revision quantization (%s) must be less than GC window (%s)" 31 32 overlapStrategyRequest = "request" 33 overlapStrategyPrefix = "prefix" 34 overlapStrategyStatic = "static" 35 overlapStrategyInsecure = "insecure" 36 37 defaultRevisionQuantization = 5 * time.Second 38 defaultFollowerReadDelay = 0 * time.Second 39 defaultMaxRevisionStalenessPercent = 0.1 40 defaultWatchBufferLength = 128 41 defaultWatchBufferWriteTimeout = 1 * time.Second 42 defaultSplitSize = 1024 43 44 defaultMaxRetries = 5 45 defaultOverlapKey = "defaultsynckey" 46 defaultOverlapStrategy = overlapStrategyStatic 47 48 defaultEnablePrometheusStats = false 49 defaultEnableConnectionBalancing = true 50 defaultConnectRate = 100 * time.Millisecond 51 ) 52 53 // Option provides the facility to configure how clients within the CRDB 54 // datastore interact with the running CockroachDB database. 55 type Option func(*crdbOptions) 56 57 func generateConfig(options []Option) (crdbOptions, error) { 58 computed := crdbOptions{ 59 gcWindow: 24 * time.Hour, 60 watchBufferLength: defaultWatchBufferLength, 61 watchBufferWriteTimeout: defaultWatchBufferWriteTimeout, 62 revisionQuantization: defaultRevisionQuantization, 63 followerReadDelay: defaultFollowerReadDelay, 64 maxRevisionStalenessPercent: defaultMaxRevisionStalenessPercent, 65 maxRetries: defaultMaxRetries, 66 overlapKey: defaultOverlapKey, 67 overlapStrategy: defaultOverlapStrategy, 68 enablePrometheusStats: defaultEnablePrometheusStats, 69 enableConnectionBalancing: defaultEnableConnectionBalancing, 70 connectRate: defaultConnectRate, 71 } 72 73 for _, option := range options { 74 option(&computed) 75 } 76 77 // Run any checks on the config that need to be done 78 if computed.revisionQuantization >= computed.gcWindow { 79 return computed, fmt.Errorf( 80 errQuantizationTooLarge, 81 computed.revisionQuantization, 82 computed.gcWindow, 83 ) 84 } 85 86 return computed, nil 87 } 88 89 // ReadConnHealthCheckInterval is the frequency at which both idle and max 90 // lifetime connections are checked, and also the frequency at which the 91 // minimum number of connections is checked. 92 // 93 // This happens asynchronously. 94 // 95 // This is not the only approach to evaluate these counts; "connection idle/max 96 // lifetime" is also checked when connections are released to the pool. 97 // 98 // There is no guarantee connections won't last longer than their specified 99 // idle/max lifetime. It's largely dependent on the health-check goroutine 100 // being able to pull them from the connection pool. 101 // 102 // The health-check may not be able to clean up those connections if they are 103 // held by the application very frequently. 104 // 105 // This value defaults to 30s. 106 func ReadConnHealthCheckInterval(interval time.Duration) Option { 107 return func(po *crdbOptions) { po.readPoolOpts.ConnHealthCheckInterval = &interval } 108 } 109 110 // WriteConnHealthCheckInterval is the frequency at which both idle and max 111 // lifetime connections are checked, and also the frequency at which the 112 // minimum number of connections is checked. 113 // 114 // This happens asynchronously. 115 // 116 // This is not the only approach to evaluate these counts; "connection idle/max 117 // lifetime" is also checked when connections are released to the pool. 118 // 119 // There is no guarantee connections won't last longer than their specified 120 // idle/max lifetime. It's largely dependent on the health-check goroutine 121 // being able to pull them from the connection pool. 122 // 123 // The health-check may not be able to clean up those connections if they are 124 // held by the application very frequently. 125 // 126 // This value defaults to 30s. 127 func WriteConnHealthCheckInterval(interval time.Duration) Option { 128 return func(po *crdbOptions) { po.writePoolOpts.ConnHealthCheckInterval = &interval } 129 } 130 131 // ReadConnMaxIdleTime is the duration after which an idle read connection will 132 // be automatically closed by the health check. 133 // 134 // This value defaults to having no maximum. 135 func ReadConnMaxIdleTime(idle time.Duration) Option { 136 return func(po *crdbOptions) { po.readPoolOpts.ConnMaxIdleTime = &idle } 137 } 138 139 // WriteConnMaxIdleTime is the duration after which an idle write connection 140 // will be automatically closed by the health check. 141 // 142 // This value defaults to having no maximum. 143 func WriteConnMaxIdleTime(idle time.Duration) Option { 144 return func(po *crdbOptions) { po.writePoolOpts.ConnMaxIdleTime = &idle } 145 } 146 147 // ReadConnMaxLifetime is the duration since creation after which a read 148 // connection will be automatically closed. 149 // 150 // This value defaults to having no maximum. 151 func ReadConnMaxLifetime(lifetime time.Duration) Option { 152 return func(po *crdbOptions) { po.readPoolOpts.ConnMaxLifetime = &lifetime } 153 } 154 155 // ReadConnMaxLifetimeJitter is an interval to wait up to after the max lifetime 156 // to close the connection. 157 // 158 // This value defaults to 20% of the max lifetime. 159 func ReadConnMaxLifetimeJitter(jitter time.Duration) Option { 160 return func(po *crdbOptions) { po.readPoolOpts.ConnMaxLifetimeJitter = &jitter } 161 } 162 163 // WriteConnMaxLifetime is the duration since creation after which a write 164 // connection will be automatically closed. 165 // 166 // This value defaults to having no maximum. 167 func WriteConnMaxLifetime(lifetime time.Duration) Option { 168 return func(po *crdbOptions) { po.writePoolOpts.ConnMaxLifetime = &lifetime } 169 } 170 171 // WriteConnMaxLifetimeJitter is an interval to wait up to after the max lifetime 172 // to close the connection. 173 // 174 // This value defaults to 20% of the max lifetime. 175 func WriteConnMaxLifetimeJitter(jitter time.Duration) Option { 176 return func(po *crdbOptions) { po.writePoolOpts.ConnMaxLifetimeJitter = &jitter } 177 } 178 179 // ReadConnsMinOpen is the minimum size of the connection pool used for reads. 180 // 181 // The health check will increase the number of connections to this amount if 182 // it had dropped below. 183 // 184 // This value defaults to the maximum open connections. 185 func ReadConnsMinOpen(conns int) Option { 186 return func(po *crdbOptions) { po.readPoolOpts.MinOpenConns = &conns } 187 } 188 189 // WriteConnsMinOpen is the minimum size of the connection pool used for writes. 190 // 191 // The health check will increase the number of connections to this amount if 192 // it had dropped below. 193 // 194 // This value defaults to the maximum open connections. 195 func WriteConnsMinOpen(conns int) Option { 196 return func(po *crdbOptions) { po.writePoolOpts.MinOpenConns = &conns } 197 } 198 199 // ReadConnsMaxOpen is the maximum size of the connection pool used for reads. 200 // 201 // This value defaults to having no maximum. 202 func ReadConnsMaxOpen(conns int) Option { 203 return func(po *crdbOptions) { po.readPoolOpts.MaxOpenConns = &conns } 204 } 205 206 // WriteConnsMaxOpen is the maximum size of the connection pool used for writes. 207 // 208 // This value defaults to having no maximum. 209 func WriteConnsMaxOpen(conns int) Option { 210 return func(po *crdbOptions) { po.writePoolOpts.MaxOpenConns = &conns } 211 } 212 213 // WatchBufferLength is the number of entries that can be stored in the watch 214 // buffer while awaiting read by the client. 215 // 216 // This value defaults to 128. 217 func WatchBufferLength(watchBufferLength uint16) Option { 218 return func(po *crdbOptions) { po.watchBufferLength = watchBufferLength } 219 } 220 221 // WatchBufferWriteTimeout is the maximum timeout for writing to the watch buffer, 222 // after which the caller to the watch will be disconnected. 223 func WatchBufferWriteTimeout(watchBufferWriteTimeout time.Duration) Option { 224 return func(po *crdbOptions) { po.watchBufferWriteTimeout = watchBufferWriteTimeout } 225 } 226 227 // RevisionQuantization is the time bucket size to which advertised revisions 228 // will be rounded. 229 // 230 // This value defaults to 5 seconds. 231 func RevisionQuantization(bucketSize time.Duration) Option { 232 return func(po *crdbOptions) { po.revisionQuantization = bucketSize } 233 } 234 235 // FollowerReadDelay is the time delay to apply to enable historial reads. 236 // 237 // This value defaults to 0 seconds. 238 func FollowerReadDelay(delay time.Duration) Option { 239 return func(po *crdbOptions) { po.followerReadDelay = delay } 240 } 241 242 // MaxRevisionStalenessPercent is the amount of time, expressed as a percentage of 243 // the revision quantization window, that a previously computed rounded revision 244 // can still be advertised after the next rounded revision would otherwise be ready. 245 // 246 // This value defaults to 0.1 (10%). 247 func MaxRevisionStalenessPercent(stalenessPercent float64) Option { 248 return func(po *crdbOptions) { po.maxRevisionStalenessPercent = stalenessPercent } 249 } 250 251 // GCWindow is the maximum age of a passed revision that will be considered 252 // valid. 253 // 254 // This value defaults to 24 hours. 255 func GCWindow(window time.Duration) Option { 256 return func(po *crdbOptions) { po.gcWindow = window } 257 } 258 259 // ConnectRate is the rate at which new datastore connections can be made. 260 // 261 // This is a duration, the rate is 1/period. 262 func ConnectRate(rate time.Duration) Option { 263 return func(po *crdbOptions) { po.connectRate = rate } 264 } 265 266 // MaxRetries is the maximum number of times a retriable transaction will be 267 // client-side retried. 268 // Default: 5 269 func MaxRetries(maxRetries uint8) Option { 270 return func(po *crdbOptions) { po.maxRetries = maxRetries } 271 } 272 273 // OverlapStrategy is the strategy used to generate overlap keys on write. 274 // Default: 'static' 275 func OverlapStrategy(strategy string) Option { 276 return func(po *crdbOptions) { po.overlapStrategy = strategy } 277 } 278 279 // OverlapKey is a key touched on every write if OverlapStrategy is "static" 280 // Default: 'key' 281 func OverlapKey(key string) Option { 282 return func(po *crdbOptions) { po.overlapKey = key } 283 } 284 285 // WithEnablePrometheusStats marks whether Prometheus metrics provided by the Postgres 286 // clients being used by the datastore are enabled. 287 // 288 // Prometheus metrics are disabled by default. 289 func WithEnablePrometheusStats(enablePrometheusStats bool) Option { 290 return func(po *crdbOptions) { po.enablePrometheusStats = enablePrometheusStats } 291 } 292 293 // WithEnableConnectionBalancing marks whether Prometheus metrics provided by the Postgres 294 // clients being used by the datastore are enabled. 295 // 296 // Prometheus metrics are disabled by default. 297 func WithEnableConnectionBalancing(connectionBalancing bool) Option { 298 return func(po *crdbOptions) { po.enableConnectionBalancing = connectionBalancing } 299 } 300 301 // DebugAnalyzeBeforeStatistics signals to the Statistics method that it should 302 // run Analyze on the database before returning statistics. This should only be 303 // used for debug and testing. 304 // 305 // Disabled by default. 306 func DebugAnalyzeBeforeStatistics() Option { 307 return func(po *crdbOptions) { po.analyzeBeforeStatistics = true } 308 }