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  }