github.com/ydb-platform/ydb-go-sdk/v3@v3.89.2/internal/ratelimiter/client.go (about) 1 package ratelimiter 2 3 import ( 4 "context" 5 "errors" 6 "fmt" 7 8 "github.com/ydb-platform/ydb-go-genproto/Ydb_RateLimiter_V1" 9 "github.com/ydb-platform/ydb-go-genproto/protos/Ydb" 10 "github.com/ydb-platform/ydb-go-genproto/protos/Ydb_RateLimiter" 11 "google.golang.org/grpc" 12 13 "github.com/ydb-platform/ydb-go-sdk/v3/internal/operation" 14 "github.com/ydb-platform/ydb-go-sdk/v3/internal/ratelimiter/config" 15 ratelimiterErrors "github.com/ydb-platform/ydb-go-sdk/v3/internal/ratelimiter/errors" 16 "github.com/ydb-platform/ydb-go-sdk/v3/internal/ratelimiter/options" 17 "github.com/ydb-platform/ydb-go-sdk/v3/internal/xerrors" 18 "github.com/ydb-platform/ydb-go-sdk/v3/ratelimiter" 19 "github.com/ydb-platform/ydb-go-sdk/v3/retry" 20 ) 21 22 var ( 23 errUnknownAcquireType = xerrors.Wrap(errors.New("unknown acquire type")) 24 errNilClient = xerrors.Wrap(errors.New("ratelimiter client is not initialized")) 25 ) 26 27 type Client struct { 28 config config.Config 29 service Ydb_RateLimiter_V1.RateLimiterServiceClient 30 } 31 32 func (c *Client) Close(ctx context.Context) error { 33 if c == nil { 34 return xerrors.WithStackTrace(errNilClient) 35 } 36 37 return nil 38 } 39 40 func New(ctx context.Context, cc grpc.ClientConnInterface, config config.Config) *Client { 41 return &Client{ 42 config: config, 43 service: Ydb_RateLimiter_V1.NewRateLimiterServiceClient(cc), 44 } 45 } 46 47 func (c *Client) CreateResource( 48 ctx context.Context, 49 coordinationNodePath string, 50 resource ratelimiter.Resource, 51 ) (err error) { 52 if c == nil { 53 return xerrors.WithStackTrace(errNilClient) 54 } 55 call := func(ctx context.Context) error { 56 return xerrors.WithStackTrace(c.createResource(ctx, coordinationNodePath, resource)) 57 } 58 if !c.config.AutoRetry() { 59 return call(ctx) 60 } 61 62 return retry.Retry(ctx, call, 63 retry.WithStackTrace(), 64 retry.WithIdempotent(true), 65 retry.WithTrace(c.config.TraceRetry()), 66 retry.WithBudget(c.config.RetryBudget()), 67 ) 68 } 69 70 func (c *Client) createResource( 71 ctx context.Context, 72 coordinationNodePath string, 73 resource ratelimiter.Resource, 74 ) (err error) { 75 _, err = c.service.CreateResource(ctx, &Ydb_RateLimiter.CreateResourceRequest{ 76 CoordinationNodePath: coordinationNodePath, 77 Resource: &Ydb_RateLimiter.Resource{ 78 ResourcePath: resource.ResourcePath, 79 Type: &Ydb_RateLimiter.Resource_HierarchicalDrr{HierarchicalDrr: &Ydb_RateLimiter.HierarchicalDrrSettings{ 80 MaxUnitsPerSecond: resource.HierarchicalDrr.MaxUnitsPerSecond, 81 MaxBurstSizeCoefficient: resource.HierarchicalDrr.MaxBurstSizeCoefficient, 82 PrefetchCoefficient: resource.HierarchicalDrr.PrefetchCoefficient, 83 PrefetchWatermark: resource.HierarchicalDrr.PrefetchWatermark, 84 }}, 85 }, 86 OperationParams: operation.Params( 87 ctx, 88 c.config.OperationTimeout(), 89 c.config.OperationCancelAfter(), 90 operation.ModeSync, 91 ), 92 }) 93 94 return 95 } 96 97 func (c *Client) AlterResource( 98 ctx context.Context, 99 coordinationNodePath string, 100 resource ratelimiter.Resource, 101 ) (err error) { 102 if c == nil { 103 return xerrors.WithStackTrace(errNilClient) 104 } 105 call := func(ctx context.Context) error { 106 return xerrors.WithStackTrace(c.alterResource(ctx, coordinationNodePath, resource)) 107 } 108 if !c.config.AutoRetry() { 109 return call(ctx) 110 } 111 112 return retry.Retry(ctx, call, 113 retry.WithStackTrace(), 114 retry.WithIdempotent(true), 115 retry.WithTrace(c.config.TraceRetry()), 116 retry.WithBudget(c.config.RetryBudget()), 117 ) 118 } 119 120 func (c *Client) alterResource( 121 ctx context.Context, 122 coordinationNodePath string, 123 resource ratelimiter.Resource, 124 ) (err error) { 125 _, err = c.service.AlterResource(ctx, &Ydb_RateLimiter.AlterResourceRequest{ 126 CoordinationNodePath: coordinationNodePath, 127 Resource: &Ydb_RateLimiter.Resource{ 128 ResourcePath: resource.ResourcePath, 129 Type: &Ydb_RateLimiter.Resource_HierarchicalDrr{HierarchicalDrr: &Ydb_RateLimiter.HierarchicalDrrSettings{ 130 MaxUnitsPerSecond: resource.HierarchicalDrr.MaxUnitsPerSecond, 131 MaxBurstSizeCoefficient: resource.HierarchicalDrr.MaxBurstSizeCoefficient, 132 PrefetchCoefficient: resource.HierarchicalDrr.PrefetchCoefficient, 133 PrefetchWatermark: resource.HierarchicalDrr.PrefetchWatermark, 134 }}, 135 }, 136 OperationParams: operation.Params( 137 ctx, 138 c.config.OperationTimeout(), 139 c.config.OperationCancelAfter(), 140 operation.ModeSync, 141 ), 142 }) 143 144 return 145 } 146 147 func (c *Client) DropResource( 148 ctx context.Context, 149 coordinationNodePath string, 150 resourcePath string, 151 ) (err error) { 152 if c == nil { 153 return xerrors.WithStackTrace(errNilClient) 154 } 155 call := func(ctx context.Context) error { 156 return xerrors.WithStackTrace(c.dropResource(ctx, coordinationNodePath, resourcePath)) 157 } 158 if !c.config.AutoRetry() { 159 return call(ctx) 160 } 161 162 return retry.Retry(ctx, call, 163 retry.WithStackTrace(), 164 retry.WithIdempotent(true), 165 retry.WithTrace(c.config.TraceRetry()), 166 retry.WithBudget(c.config.RetryBudget()), 167 ) 168 } 169 170 func (c *Client) dropResource( 171 ctx context.Context, 172 coordinationNodePath string, 173 resourcePath string, 174 ) (err error) { 175 _, err = c.service.DropResource(ctx, &Ydb_RateLimiter.DropResourceRequest{ 176 CoordinationNodePath: coordinationNodePath, 177 ResourcePath: resourcePath, 178 OperationParams: operation.Params( 179 ctx, 180 c.config.OperationTimeout(), 181 c.config.OperationCancelAfter(), 182 operation.ModeSync, 183 ), 184 }) 185 186 return 187 } 188 189 func (c *Client) ListResource( 190 ctx context.Context, 191 coordinationNodePath string, 192 resourcePath string, 193 recursive bool, 194 ) (list []string, _ error) { 195 if c == nil { 196 return list, xerrors.WithStackTrace(errNilClient) 197 } 198 call := func(ctx context.Context) (err error) { 199 list, err = c.listResource(ctx, coordinationNodePath, resourcePath, recursive) 200 201 return xerrors.WithStackTrace(err) 202 } 203 if !c.config.AutoRetry() { 204 err := call(ctx) 205 206 return list, err 207 } 208 err := retry.Retry(ctx, call, 209 retry.WithIdempotent(true), 210 retry.WithStackTrace(), 211 retry.WithTrace(c.config.TraceRetry()), 212 retry.WithBudget(c.config.RetryBudget()), 213 ) 214 215 return list, err 216 } 217 218 func (c *Client) listResource( 219 ctx context.Context, 220 coordinationNodePath string, 221 resourcePath string, 222 recursive bool, 223 ) (_ []string, err error) { 224 var ( 225 response *Ydb_RateLimiter.ListResourcesResponse 226 result Ydb_RateLimiter.ListResourcesResult 227 ) 228 response, err = c.service.ListResources(ctx, &Ydb_RateLimiter.ListResourcesRequest{ 229 CoordinationNodePath: coordinationNodePath, 230 ResourcePath: resourcePath, 231 Recursive: recursive, 232 OperationParams: operation.Params( 233 ctx, 234 c.config.OperationTimeout(), 235 c.config.OperationCancelAfter(), 236 operation.ModeSync, 237 ), 238 }) 239 if err != nil { 240 return nil, xerrors.WithStackTrace(err) 241 } 242 err = response.GetOperation().GetResult().UnmarshalTo(&result) 243 if err != nil { 244 return nil, xerrors.WithStackTrace(err) 245 } 246 247 return result.GetResourcePaths(), nil 248 } 249 250 func (c *Client) DescribeResource( 251 ctx context.Context, 252 coordinationNodePath string, 253 resourcePath string, 254 ) (resource *ratelimiter.Resource, err error) { 255 if c == nil { 256 return resource, xerrors.WithStackTrace(errNilClient) 257 } 258 call := func(ctx context.Context) error { 259 resource, err = c.describeResource(ctx, coordinationNodePath, resourcePath) 260 261 return xerrors.WithStackTrace(err) 262 } 263 if !c.config.AutoRetry() { 264 err = call(ctx) 265 266 return 267 } 268 err = retry.Retry(ctx, call, 269 retry.WithIdempotent(true), 270 retry.WithStackTrace(), 271 retry.WithTrace(c.config.TraceRetry()), 272 retry.WithBudget(c.config.RetryBudget()), 273 ) 274 275 return 276 } 277 278 func (c *Client) describeResource( 279 ctx context.Context, 280 coordinationNodePath string, 281 resourcePath string, 282 ) (_ *ratelimiter.Resource, err error) { 283 var ( 284 response *Ydb_RateLimiter.DescribeResourceResponse 285 result Ydb_RateLimiter.DescribeResourceResult 286 ) 287 response, err = c.service.DescribeResource(ctx, &Ydb_RateLimiter.DescribeResourceRequest{ 288 CoordinationNodePath: coordinationNodePath, 289 ResourcePath: resourcePath, 290 OperationParams: operation.Params( 291 ctx, 292 c.config.OperationTimeout(), 293 c.config.OperationCancelAfter(), 294 operation.ModeSync, 295 ), 296 }) 297 if err != nil { 298 return nil, xerrors.WithStackTrace(err) 299 } 300 err = response.GetOperation().GetResult().UnmarshalTo(&result) 301 if err != nil { 302 return nil, xerrors.WithStackTrace(err) 303 } 304 305 resource := &ratelimiter.Resource{ 306 ResourcePath: result.GetResource().GetResourcePath(), 307 } 308 309 if result.GetResource().GetHierarchicalDrr() != nil { 310 resource.HierarchicalDrr = ratelimiter.HierarchicalDrrSettings{ 311 MaxUnitsPerSecond: result.GetResource().GetHierarchicalDrr().GetMaxUnitsPerSecond(), 312 MaxBurstSizeCoefficient: result.GetResource().GetHierarchicalDrr().GetMaxBurstSizeCoefficient(), 313 PrefetchCoefficient: result.GetResource().GetHierarchicalDrr().GetPrefetchCoefficient(), 314 PrefetchWatermark: result.GetResource().GetHierarchicalDrr().GetPrefetchWatermark(), 315 } 316 } 317 318 return resource, nil 319 } 320 321 func (c *Client) AcquireResource( 322 ctx context.Context, 323 coordinationNodePath string, 324 resourcePath string, 325 amount uint64, 326 opts ...options.AcquireOption, 327 ) (err error) { 328 if c == nil { 329 return xerrors.WithStackTrace(errNilClient) 330 } 331 call := func(ctx context.Context) error { 332 return xerrors.WithStackTrace(c.acquireResource(ctx, coordinationNodePath, resourcePath, amount, opts...)) 333 } 334 if !c.config.AutoRetry() { 335 return call(ctx) 336 } 337 338 return retry.Retry(ctx, call, 339 retry.WithStackTrace(), 340 retry.WithTrace(c.config.TraceRetry()), 341 retry.WithBudget(c.config.RetryBudget()), 342 ) 343 } 344 345 func (c *Client) acquireResource( 346 ctx context.Context, 347 coordinationNodePath string, 348 resourcePath string, 349 amount uint64, 350 opts ...options.AcquireOption, 351 ) (err error) { 352 acquireOptions := options.NewAcquire( 353 append( 354 []options.AcquireOption{ 355 options.WithOperationTimeout(c.config.OperationTimeout()), 356 options.WithOperationCancelAfter(c.config.OperationCancelAfter()), 357 }, 358 opts..., 359 )..., 360 ) 361 362 switch acquireOptions.Type() { 363 case options.AcquireTypeAcquire: 364 _, err = c.service.AcquireResource( 365 ctx, 366 &Ydb_RateLimiter.AcquireResourceRequest{ 367 CoordinationNodePath: coordinationNodePath, 368 ResourcePath: resourcePath, 369 Units: &Ydb_RateLimiter.AcquireResourceRequest_Required{ 370 Required: amount, 371 }, 372 OperationParams: operation.Params( 373 ctx, 374 acquireOptions.OperationTimeout(), 375 acquireOptions.OperationCancelAfter(), 376 operation.ModeSync, 377 ), 378 }, 379 ) 380 case options.AcquireTypeReport: 381 _, err = c.service.AcquireResource( 382 ctx, 383 &Ydb_RateLimiter.AcquireResourceRequest{ 384 CoordinationNodePath: coordinationNodePath, 385 ResourcePath: resourcePath, 386 Units: &Ydb_RateLimiter.AcquireResourceRequest_Used{ 387 Used: amount, 388 }, 389 OperationParams: operation.Params( 390 ctx, 391 acquireOptions.OperationTimeout(), 392 acquireOptions.OperationCancelAfter(), 393 operation.ModeSync, 394 ), 395 }, 396 ) 397 default: 398 return xerrors.WithStackTrace(fmt.Errorf("%w: %d", errUnknownAcquireType, acquireOptions.Type())) 399 } 400 401 if xerrors.IsOperationError(err, Ydb.StatusIds_TIMEOUT, Ydb.StatusIds_CANCELLED) { 402 return xerrors.WithStackTrace(ratelimiterErrors.NewAcquire(amount, err)) 403 } 404 405 return xerrors.WithStackTrace(err) 406 }