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  }