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