github.com/authzed/spicedb@v1.32.1-0.20240520085336-ebda56537386/internal/datastore/spanner/options.go (about) 1 package spanner 2 3 import ( 4 "fmt" 5 "math" 6 "runtime" 7 "time" 8 ) 9 10 type spannerOptions struct { 11 watchBufferLength uint16 12 watchBufferWriteTimeout time.Duration 13 revisionQuantization time.Duration 14 followerReadDelay time.Duration 15 maxRevisionStalenessPercent float64 16 credentialsFilePath string 17 emulatorHost string 18 disableStats bool 19 readMaxOpen int 20 writeMaxOpen int 21 minSessions uint64 22 maxSessions uint64 23 migrationPhase string 24 } 25 26 type migrationPhase uint8 27 28 const ( 29 complete migrationPhase = iota 30 ) 31 32 var migrationPhases = map[string]migrationPhase{ 33 "": complete, 34 } 35 36 const ( 37 errQuantizationTooLarge = "revision quantization (%s) must be less than (%s)" 38 39 defaultRevisionQuantization = 5 * time.Second 40 defaultFollowerReadDelay = 0 * time.Second 41 defaultMaxRevisionStalenessPercent = 0.1 42 defaultWatchBufferLength = 128 43 defaultWatchBufferWriteTimeout = 1 * time.Second 44 defaultDisableStats = false 45 maxRevisionQuantization = 24 * time.Hour 46 ) 47 48 // Option provides the facility to configure how clients within the Spanner 49 // datastore interact with the running Spanner database. 50 type Option func(*spannerOptions) 51 52 func generateConfig(options []Option) (spannerOptions, error) { 53 // originally SpiceDB didn't use connection pools for Spanner SDK, so it opened 1 single connection 54 // This determines if there are more CPU cores to increase the default number of connections 55 defaultNumberConnections := max(1, math.Round(float64(runtime.GOMAXPROCS(0)))) 56 computed := spannerOptions{ 57 watchBufferLength: defaultWatchBufferLength, 58 watchBufferWriteTimeout: defaultWatchBufferWriteTimeout, 59 revisionQuantization: defaultRevisionQuantization, 60 followerReadDelay: defaultFollowerReadDelay, 61 maxRevisionStalenessPercent: defaultMaxRevisionStalenessPercent, 62 disableStats: defaultDisableStats, 63 readMaxOpen: int(defaultNumberConnections), 64 writeMaxOpen: int(defaultNumberConnections), 65 minSessions: 100, 66 maxSessions: 400, 67 migrationPhase: "", // no migration 68 } 69 70 for _, option := range options { 71 option(&computed) 72 } 73 74 // Run any checks on the config that need to be done 75 if computed.revisionQuantization >= maxRevisionQuantization { 76 return computed, fmt.Errorf( 77 errQuantizationTooLarge, 78 computed.revisionQuantization, 79 maxRevisionQuantization, 80 ) 81 } 82 83 if _, ok := migrationPhases[computed.migrationPhase]; !ok { 84 return computed, fmt.Errorf("unknown migration phase: %s", computed.migrationPhase) 85 } 86 87 return computed, nil 88 } 89 90 // WatchBufferLength is the number of entries that can be stored in the watch 91 // buffer while awaiting read by the client. 92 // 93 // This value defaults to 128. 94 func WatchBufferLength(watchBufferLength uint16) Option { 95 return func(so *spannerOptions) { 96 so.watchBufferLength = watchBufferLength 97 } 98 } 99 100 // WatchBufferWriteTimeout is the maximum timeout for writing to the watch buffer, 101 // after which the caller to the watch will be disconnected. 102 func WatchBufferWriteTimeout(watchBufferWriteTimeout time.Duration) Option { 103 return func(so *spannerOptions) { so.watchBufferWriteTimeout = watchBufferWriteTimeout } 104 } 105 106 // RevisionQuantization is the time bucket size to which advertised revisions 107 // will be rounded. 108 // 109 // This value defaults to 5 seconds. 110 func RevisionQuantization(bucketSize time.Duration) Option { 111 return func(so *spannerOptions) { 112 so.revisionQuantization = bucketSize 113 } 114 } 115 116 // FollowerReadDelay is the time delay to apply to enable historial reads. 117 // 118 // This value defaults to 0 seconds. 119 func FollowerReadDelay(delay time.Duration) Option { 120 return func(so *spannerOptions) { 121 so.followerReadDelay = delay 122 } 123 } 124 125 // MaxRevisionStalenessPercent is the amount of time, expressed as a percentage of 126 // the revision quantization window, that a previously computed rounded revision 127 // can still be advertised after the next rounded revision would otherwise be ready. 128 // 129 // This value defaults to 0.1 (10%). 130 func MaxRevisionStalenessPercent(stalenessPercent float64) Option { 131 return func(so *spannerOptions) { 132 so.maxRevisionStalenessPercent = stalenessPercent 133 } 134 } 135 136 // CredentialsFile is the path to a file containing credentials for a service 137 // account that can access the cloud spanner instance 138 func CredentialsFile(path string) Option { 139 return func(so *spannerOptions) { 140 so.credentialsFilePath = path 141 } 142 } 143 144 // EmulatorHost is the URI of a Spanner emulator to connect to for 145 // development and testing use 146 func EmulatorHost(uri string) Option { 147 return func(so *spannerOptions) { 148 so.emulatorHost = uri 149 } 150 } 151 152 // DisableStats disables recording counts to the stats table 153 func DisableStats(disable bool) Option { 154 return func(po *spannerOptions) { 155 po.disableStats = disable 156 } 157 } 158 159 // ReadConnsMaxOpen is the maximum size of the connection pool used for reads. 160 // 161 // This value defaults to having 20 connections. 162 func ReadConnsMaxOpen(conns int) Option { 163 return func(po *spannerOptions) { po.readMaxOpen = conns } 164 } 165 166 // WriteConnsMaxOpen is the maximum size of the connection pool used for writes. 167 // 168 // This value defaults to having 10 connections. 169 func WriteConnsMaxOpen(conns int) Option { 170 return func(po *spannerOptions) { po.writeMaxOpen = conns } 171 } 172 173 // MinSessionCount minimum number of session the Spanner client can have 174 // at a given time. 175 // 176 // Defaults to 100. 177 func MinSessionCount(minSessions uint64) Option { 178 return func(po *spannerOptions) { po.minSessions = minSessions } 179 } 180 181 // MaxSessionCount maximum number of session the Spanner client can have 182 // at a given time. 183 // 184 // Defaults to 400 sessions. 185 func MaxSessionCount(maxSessions uint64) Option { 186 return func(po *spannerOptions) { po.maxSessions = maxSessions } 187 } 188 189 // MigrationPhase configures the spanner driver to the proper state of a 190 // multi-phase migration. 191 // 192 // Steady-state configuration (e.g. fully migrated) by default 193 func MigrationPhase(phase string) Option { 194 return func(po *spannerOptions) { po.migrationPhase = phase } 195 }