github.com/ydb-platform/ydb-go-sdk/v3@v3.89.2/internal/coordination/client.go (about) 1 package coordination 2 3 import ( 4 "context" 5 "errors" 6 "sync" 7 "time" 8 9 "github.com/ydb-platform/ydb-go-genproto/Ydb_Coordination_V1" 10 "github.com/ydb-platform/ydb-go-genproto/protos/Ydb_Coordination" 11 "github.com/ydb-platform/ydb-go-genproto/protos/Ydb_Operations" 12 "google.golang.org/grpc" 13 14 "github.com/ydb-platform/ydb-go-sdk/v3/coordination" 15 "github.com/ydb-platform/ydb-go-sdk/v3/coordination/options" 16 "github.com/ydb-platform/ydb-go-sdk/v3/internal/coordination/config" 17 "github.com/ydb-platform/ydb-go-sdk/v3/internal/operation" 18 "github.com/ydb-platform/ydb-go-sdk/v3/internal/stack" 19 "github.com/ydb-platform/ydb-go-sdk/v3/internal/xerrors" 20 "github.com/ydb-platform/ydb-go-sdk/v3/retry" 21 "github.com/ydb-platform/ydb-go-sdk/v3/scheme" 22 "github.com/ydb-platform/ydb-go-sdk/v3/trace" 23 ) 24 25 //go:generate mockgen -destination grpc_client_mock_test.go --typed -package coordination -write_package_comment=false github.com/ydb-platform/ydb-go-genproto/Ydb_Coordination_V1 CoordinationServiceClient,CoordinationService_SessionClient 26 27 var errNilClient = xerrors.Wrap(errors.New("coordination client is not initialized")) 28 29 type Client struct { 30 config config.Config 31 client Ydb_Coordination_V1.CoordinationServiceClient 32 33 mutex sync.Mutex // guards the fields below 34 sessions map[*session]struct{} 35 } 36 37 func New(ctx context.Context, cc grpc.ClientConnInterface, config config.Config) *Client { 38 onDone := trace.CoordinationOnNew(config.Trace(), &ctx, 39 stack.FunctionID("github.com/ydb-platform/ydb-go-sdk/v3/internal/coordination.New"), 40 ) 41 defer onDone() 42 43 return &Client{ 44 config: config, 45 client: Ydb_Coordination_V1.NewCoordinationServiceClient(cc), 46 sessions: make(map[*session]struct{}), 47 } 48 } 49 50 func operationParams( 51 ctx context.Context, 52 config interface { 53 OperationTimeout() time.Duration 54 OperationCancelAfter() time.Duration 55 }, 56 mode operation.Mode, 57 ) *Ydb_Operations.OperationParams { 58 return operation.Params( 59 ctx, 60 config.OperationTimeout(), 61 config.OperationCancelAfter(), 62 mode, 63 ) 64 } 65 66 func createNodeRequest( 67 path string, config coordination.NodeConfig, operationParams *Ydb_Operations.OperationParams, 68 ) *Ydb_Coordination.CreateNodeRequest { 69 return &Ydb_Coordination.CreateNodeRequest{ 70 Path: path, 71 Config: &Ydb_Coordination.Config{ 72 Path: config.Path, 73 SelfCheckPeriodMillis: config.SelfCheckPeriodMillis, 74 SessionGracePeriodMillis: config.SessionGracePeriodMillis, 75 ReadConsistencyMode: config.ReadConsistencyMode.To(), 76 AttachConsistencyMode: config.AttachConsistencyMode.To(), 77 RateLimiterCountersMode: config.RatelimiterCountersMode.To(), 78 }, 79 OperationParams: operationParams, 80 } 81 } 82 83 func createNode( 84 ctx context.Context, client Ydb_Coordination_V1.CoordinationServiceClient, request *Ydb_Coordination.CreateNodeRequest, 85 ) error { 86 _, err := client.CreateNode(ctx, request) 87 if err != nil { 88 return xerrors.WithStackTrace(err) 89 } 90 91 return nil 92 } 93 94 func (c *Client) CreateNode(ctx context.Context, path string, config coordination.NodeConfig) (finalErr error) { 95 if c == nil { 96 return xerrors.WithStackTrace(errNilClient) 97 } 98 99 onDone := trace.CoordinationOnCreateNode(c.config.Trace(), &ctx, 100 stack.FunctionID("github.com/ydb-platform/ydb-go-sdk/v3/internal/coordination.(*Client).CreateNode"), 101 path, 102 ) 103 defer func() { 104 onDone(finalErr) 105 }() 106 107 request := createNodeRequest(path, config, operationParams(ctx, &c.config, operation.ModeSync)) 108 109 if !c.config.AutoRetry() { 110 return createNode(ctx, c.client, request) 111 } 112 113 return retry.Retry(ctx, 114 func(ctx context.Context) error { 115 return createNode(ctx, c.client, request) 116 }, 117 retry.WithStackTrace(), 118 retry.WithIdempotent(true), 119 retry.WithTrace(c.config.TraceRetry()), 120 retry.WithBudget(c.config.RetryBudget()), 121 ) 122 } 123 124 func (c *Client) AlterNode(ctx context.Context, path string, config coordination.NodeConfig) (finalErr error) { 125 if c == nil { 126 return xerrors.WithStackTrace(errNilClient) 127 } 128 129 onDone := trace.CoordinationOnAlterNode(c.config.Trace(), &ctx, 130 stack.FunctionID("github.com/ydb-platform/ydb-go-sdk/v3/internal/coordination.(*Client).AlterNode"), 131 path, 132 ) 133 defer func() { 134 onDone(finalErr) 135 }() 136 137 request := alterNodeRequest(path, config, operationParams(ctx, &c.config, operation.ModeSync)) 138 139 call := func(ctx context.Context) error { 140 return alterNode(ctx, c.client, request) 141 } 142 if !c.config.AutoRetry() { 143 return xerrors.WithStackTrace(call(ctx)) 144 } 145 146 return retry.Retry(ctx, 147 func(ctx context.Context) (err error) { 148 return alterNode(ctx, c.client, request) 149 }, 150 retry.WithStackTrace(), 151 retry.WithIdempotent(true), 152 retry.WithTrace(c.config.TraceRetry()), 153 retry.WithBudget(c.config.RetryBudget()), 154 ) 155 } 156 157 func alterNodeRequest( 158 path string, config coordination.NodeConfig, operationParams *Ydb_Operations.OperationParams, 159 ) *Ydb_Coordination.AlterNodeRequest { 160 return &Ydb_Coordination.AlterNodeRequest{ 161 Path: path, 162 Config: &Ydb_Coordination.Config{ 163 Path: config.Path, 164 SelfCheckPeriodMillis: config.SelfCheckPeriodMillis, 165 SessionGracePeriodMillis: config.SessionGracePeriodMillis, 166 ReadConsistencyMode: config.ReadConsistencyMode.To(), 167 AttachConsistencyMode: config.AttachConsistencyMode.To(), 168 RateLimiterCountersMode: config.RatelimiterCountersMode.To(), 169 }, 170 OperationParams: operationParams, 171 } 172 } 173 174 func alterNode( 175 ctx context.Context, client Ydb_Coordination_V1.CoordinationServiceClient, request *Ydb_Coordination.AlterNodeRequest, 176 ) error { 177 _, err := client.AlterNode(ctx, request) 178 if err != nil { 179 return xerrors.WithStackTrace(err) 180 } 181 182 return nil 183 } 184 185 func (c *Client) DropNode(ctx context.Context, path string) (finalErr error) { 186 if c == nil { 187 return xerrors.WithStackTrace(errNilClient) 188 } 189 190 onDone := trace.CoordinationOnDropNode(c.config.Trace(), &ctx, 191 stack.FunctionID("github.com/ydb-platform/ydb-go-sdk/v3/internal/coordination.(*Client).DropNode"), 192 path, 193 ) 194 defer func() { 195 onDone(finalErr) 196 }() 197 198 request := dropNodeRequest(path, operationParams(ctx, &c.config, operation.ModeSync)) 199 200 call := func(ctx context.Context) error { 201 return dropNode(ctx, c.client, request) 202 } 203 if !c.config.AutoRetry() { 204 return xerrors.WithStackTrace(call(ctx)) 205 } 206 207 return retry.Retry(ctx, 208 func(ctx context.Context) (err error) { 209 return dropNode(ctx, c.client, request) 210 }, 211 retry.WithStackTrace(), 212 retry.WithIdempotent(true), 213 retry.WithTrace(c.config.TraceRetry()), 214 retry.WithBudget(c.config.RetryBudget()), 215 ) 216 } 217 218 func dropNodeRequest(path string, operationParams *Ydb_Operations.OperationParams) *Ydb_Coordination.DropNodeRequest { 219 return &Ydb_Coordination.DropNodeRequest{ 220 Path: path, 221 OperationParams: operationParams, 222 } 223 } 224 225 func dropNode( 226 ctx context.Context, client Ydb_Coordination_V1.CoordinationServiceClient, request *Ydb_Coordination.DropNodeRequest, 227 ) error { 228 _, err := client.DropNode(ctx, request) 229 if err != nil { 230 return xerrors.WithStackTrace(err) 231 } 232 233 return nil 234 } 235 236 func (c *Client) DescribeNode( 237 ctx context.Context, 238 path string, 239 ) ( 240 entry *scheme.Entry, 241 config *coordination.NodeConfig, 242 finalErr error, 243 ) { 244 if c == nil { 245 return nil, nil, xerrors.WithStackTrace(errNilClient) 246 } 247 248 onDone := trace.CoordinationOnDescribeNode(c.config.Trace(), &ctx, 249 stack.FunctionID("github.com/ydb-platform/ydb-go-sdk/v3/internal/coordination.(*Client).DescribeNode"), 250 path, 251 ) 252 defer func() { 253 onDone(finalErr) 254 }() 255 256 request := describeNodeRequest(path, operationParams(ctx, &c.config, operation.ModeSync)) 257 258 if !c.config.AutoRetry() { 259 return describeNode(ctx, c.client, request) 260 } 261 262 err := retry.Retry(ctx, 263 func(ctx context.Context) (err error) { 264 entry, config, err = describeNode(ctx, c.client, request) 265 if err != nil { 266 return xerrors.WithStackTrace(err) 267 } 268 269 return nil 270 }, 271 retry.WithStackTrace(), 272 retry.WithIdempotent(true), 273 retry.WithTrace(c.config.TraceRetry()), 274 retry.WithBudget(c.config.RetryBudget()), 275 ) 276 if err != nil { 277 return nil, nil, xerrors.WithStackTrace(err) 278 } 279 280 return entry, config, nil 281 } 282 283 func describeNodeRequest( 284 path string, operationParams *Ydb_Operations.OperationParams, 285 ) *Ydb_Coordination.DescribeNodeRequest { 286 return &Ydb_Coordination.DescribeNodeRequest{ 287 Path: path, 288 OperationParams: operationParams, 289 } 290 } 291 292 // DescribeNode describes a coordination node 293 func describeNode( 294 ctx context.Context, 295 client Ydb_Coordination_V1.CoordinationServiceClient, 296 request *Ydb_Coordination.DescribeNodeRequest, 297 ) ( 298 _ *scheme.Entry, 299 _ *coordination.NodeConfig, 300 err error, 301 ) { 302 var ( 303 response *Ydb_Coordination.DescribeNodeResponse 304 result Ydb_Coordination.DescribeNodeResult 305 ) 306 response, err = client.DescribeNode(ctx, request) 307 if err != nil { 308 return nil, nil, xerrors.WithStackTrace(err) 309 } 310 311 err = response.GetOperation().GetResult().UnmarshalTo(&result) 312 if err != nil { 313 return nil, nil, xerrors.WithStackTrace(err) 314 } 315 316 return scheme.InnerConvertEntry(result.GetSelf()), &coordination.NodeConfig{ 317 Path: result.GetConfig().GetPath(), 318 SelfCheckPeriodMillis: result.GetConfig().GetSelfCheckPeriodMillis(), 319 SessionGracePeriodMillis: result.GetConfig().GetSessionGracePeriodMillis(), 320 ReadConsistencyMode: consistencyMode(result.GetConfig().GetReadConsistencyMode()), 321 AttachConsistencyMode: consistencyMode(result.GetConfig().GetAttachConsistencyMode()), 322 RatelimiterCountersMode: ratelimiterCountersMode(result.GetConfig().GetRateLimiterCountersMode()), 323 }, nil 324 } 325 326 func newCreateSessionConfig(opts ...options.SessionOption) *options.CreateSessionOptions { 327 c := defaultCreateSessionConfig() 328 for _, o := range opts { 329 if o != nil { 330 o(c) 331 } 332 } 333 334 return c 335 } 336 337 func (c *Client) sessionCreated(s *session) { 338 c.mutex.Lock() 339 defer c.mutex.Unlock() 340 341 c.sessions[s] = struct{}{} 342 } 343 344 func (c *Client) sessionClosed(s *session) { 345 c.mutex.Lock() 346 defer c.mutex.Unlock() 347 348 delete(c.sessions, s) 349 } 350 351 func (c *Client) closeSessions(ctx context.Context) { 352 c.mutex.Lock() 353 defer c.mutex.Unlock() 354 355 for s := range c.sessions { 356 s.Close(ctx) 357 } 358 } 359 360 func defaultCreateSessionConfig() *options.CreateSessionOptions { 361 return &options.CreateSessionOptions{ 362 Description: "YDB Go SDK", 363 SessionTimeout: time.Second * 5, //nolint:gomnd 364 SessionStartTimeout: time.Second * 1, 365 SessionStopTimeout: time.Second * 1, 366 SessionKeepAliveTimeout: time.Second * 10, //nolint:gomnd 367 SessionReconnectDelay: time.Millisecond * 500, //nolint:gomnd 368 } 369 } 370 371 func (c *Client) Session( 372 ctx context.Context, 373 path string, 374 opts ...options.SessionOption, 375 ) (_ coordination.Session, finalErr error) { 376 if c == nil { 377 return nil, xerrors.WithStackTrace(errNilClient) 378 } 379 380 onDone := trace.CoordinationOnSession(c.config.Trace(), &ctx, 381 stack.FunctionID("github.com/ydb-platform/ydb-go-sdk/v3/internal/coordination.(*Client).Session"), 382 path, 383 ) 384 defer func() { 385 onDone(finalErr) 386 }() 387 388 return createSession(ctx, c, path, newCreateSessionConfig(opts...)) 389 } 390 391 func (c *Client) Close(ctx context.Context) (finalErr error) { 392 if c == nil { 393 return xerrors.WithStackTrace(errNilClient) 394 } 395 396 onDone := trace.CoordinationOnClose(c.config.Trace(), &ctx, 397 stack.FunctionID("github.com/ydb-platform/ydb-go-sdk/v3/internal/coordination.(*Client).Close"), 398 ) 399 defer func() { 400 onDone(finalErr) 401 }() 402 403 c.closeSessions(ctx) 404 405 return nil 406 } 407 408 func consistencyMode(t Ydb_Coordination.ConsistencyMode) coordination.ConsistencyMode { 409 switch t { 410 case Ydb_Coordination.ConsistencyMode_CONSISTENCY_MODE_STRICT: 411 return coordination.ConsistencyModeStrict 412 case Ydb_Coordination.ConsistencyMode_CONSISTENCY_MODE_RELAXED: 413 return coordination.ConsistencyModeRelaxed 414 default: 415 return coordination.ConsistencyModeUnset 416 } 417 } 418 419 func ratelimiterCountersMode(t Ydb_Coordination.RateLimiterCountersMode) coordination.RatelimiterCountersMode { 420 switch t { 421 case Ydb_Coordination.RateLimiterCountersMode_RATE_LIMITER_COUNTERS_MODE_AGGREGATED: 422 return coordination.RatelimiterCountersModeAggregated 423 case Ydb_Coordination.RateLimiterCountersMode_RATE_LIMITER_COUNTERS_MODE_DETAILED: 424 return coordination.RatelimiterCountersModeDetailed 425 default: 426 return coordination.RatelimiterCountersModeUnset 427 } 428 }