rp

package
v0.32.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 31, 2023 License: GPL-3.0 Imports: 4 Imported by: 0

Documentation

Overview

Package rp ("resource protector") provides functions that help control access to some limited resource.

You first create a Protector, and then make Request()s for tokens. Requests give you Receipts that you use to WaitUntilGranted() to know when a request succeeded and you "have" the tokens. You can now use whatever the actual resource is. Once you're done with it you Release() the request so that some other request can "use" those tokens.

The Protector offers these guarantees:

# The maximum number of requests that are granted and in play at any one time
  is the lesser of the Protector's maxSimultaneous value or the return value
  of the Protector's AvailabilityCallback (if set).
# Requests (and the calling of the AvailabilityCallback, if set) are granted
  with at least a delay of the Protector's delayBetween value between each
  grant (or call).
# If clients fail to release granted requests, they will be automatically
  released.

  import "github.com/VertebrateResequencing/wr/rp"

  p := rp.New("irods", 2 * time.Second, 20, 5 * time.Minute)

  // now every time you want use the protected resource, make a request:
  receipt, err := p.Request(1)
  p.WaitUntilGranted(receipt)

  // now use the irods resource; if using it will take longer than 5mins,
  // arrange to call p.Touch(receipt) every, say, 2.5mins until you're done

  // once you've finished using the resource:
  p.Release(receipt)

Index

Constants

View Source
const (
	ErrOverMaximumTokens = "more tokens requested than can ever be supplied"
	ErrShutDown          = "the protector has been shut down"
)

rp has some typical errors

#nosec

Variables

This section is empty.

Functions

This section is empty.

Types

type AvailabilityCallback

type AvailabilityCallback func() (numTokens int)

AvailabilityCallback is used as a callback to know how many tokens are currently available for use.

type Error

type Error struct {
	Protector string  // the protector's Name
	Op        string  // name of the method
	Request   Receipt // the request's id
	Err       string  // one of our Err constants
}

Error records an error and the operation, request and protector that caused it.

func (Error) Error

func (e Error) Error() string

type Protector

type Protector struct {
	Name string // Name of the resource being protected.
	// contains filtered or unexported fields
}

Protector struct is used to Protect a particular resource by granting tokens tokens when the resource has capacity.

func New

func New(name string, delayBetween time.Duration, maxSimultaneous int, releaseTimeout time.Duration) *Protector

New creates a new Protector. The name is for your benefit, describing the resource you're protecting.

delayBetween defines the minimum delay between the granting of the tokens for each Request(). This would be used to avoid spamming your resource with too high a frequency of accesses.

maxSimultaneous defines the maximum number of tokens that can be in use concurrently. This would be used to avoid overloading your resource with too much usage.

releaseTimeout is the time after which granted tokens are automatically released for use by other requests if the receiver fails to Touch() or Release() before then (so that a client that starts using tokens but then dies unexpectedly doesn't hold on to those tokens forever).

func (*Protector) Granted

func (p *Protector) Granted(receipt Receipt) (granted, keepChecking bool)

Granted is an alternative to WaitUntilGranted(). Instead of blocking until the request corresponding to the given receipt is granted, you can call Granted() periodically until the first bool return value is true.

Note that if you call Granted after more time than the releaseTimeout supplied to New() has passed since you made the Request(), the request will already have been released, and this function will return false. There is now no point in checking Granted() any more since it will never become true, hence the second bool will be false in this case. (You will also get double false if you supply an invalid Receipt.)

func (*Protector) Release

func (p *Protector) Release(receipt Receipt)

Release will release the tokens of a granted Request(), for use by any other requests. You should always call this when you're done using a resource (unless you use ReleaseAfter() instead).

func (*Protector) ReleaseAfter

func (p *Protector) ReleaseAfter(receipt Receipt, delay time.Duration)

ReleaseAfter is a convenience function that calls Release() after the given delay, but returns immediately.

func (*Protector) Request

func (p *Protector) Request(numTokens int) (Receipt, error)

Request lets you request that a desired number of tokens be granted to you for use.

You immediately get back a Receipt, which you should supply to WaitUntilGranted(), then to Touch() periodically until you're no longer using the resource, then finally to Release().

func (*Protector) SetAvailabilityCallback

func (p *Protector) SetAvailabilityCallback(callback AvailabilityCallback)

SetAvailabilityCallback sets a callback that will be called whenever the Protector checks to see if Request()s can be fulfilled.

The callback should do some kind of check on the resource to see how busy it is, and return a number between 0 (block any additional usage of the resource) and the maxSimultaneous value provided to New() (higher values will be of no benefit).

The callback will only be called at most every delayBetween value supplied to New(), so if checking the resource is an expensive operation, be sure to set that value appropriately high. (Or do appropriate caching of the busyness on your end.)

NB: You'd only set this callback if you have unprotected access to your resource that you need the Protector to take in to account.

func (*Protector) Shutdown

func (p *Protector) Shutdown()

Shutdown will make subsequent Request(), WaitUntilGranted() and Granted() calls fail. Any currently granted requests will be released. Any ungranted requests will be forgotten about.

func (*Protector) Touch

func (p *Protector) Touch(receipts ...Receipt)

Touch for a request (identified by the given receipt) prevents it timing out and releasing the granted tokens. You should call this periodically after WaitUntilGranted() for the same receipt.

Rather than have a goroutine for each of your requests that you use to periodically touch, you could instead have a single goroutine that touches all your granted and active requests. Hence this method can take more than one receipt.

func (*Protector) WaitUntilGranted

func (p *Protector) WaitUntilGranted(receipt Receipt, timeout ...time.Duration) bool

WaitUntilGranted will block until the request corresponding to the given Receipt has been granted its tokens, whereupon you can start using the protected resource.

If you call this after more time than the releaseTimeout supplied to New() has passed since you made the Request(), the request will already have been released, and this function will return false: do not use the resources in that case! (You will also get false if you supply an invalid Receipt.)

You can optionally supply a timeout. If it ends up taking longer that this duration the request will be cancelled, the receipt will no longer be useful, and this function will return false.

type Receipt

type Receipt string

Receipt is the unique id of a request

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL