Skip to content

Commit

Permalink
add past and future time throttlers
Browse files Browse the repository at this point in the history
  • Loading branch information
1pkg committed Jan 24, 2021
1 parent e5da1bf commit 1bc421e
Show file tree
Hide file tree
Showing 4 changed files with 111 additions and 0 deletions.
2 changes: 2 additions & 0 deletions README.MD
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,8 @@ You can find list of returning error types for all existing throttlers in thrott
| each | `func NewThrottlerEach(threshold uint64) Throttler` | Throttles each periodic *i-th* call defined by the specified threshold.<br> - could return `ErrorThreshold`; |
| before | `func NewThrottlerBefore(threshold uint64) Throttler` | Throttles each call below the *i-th* call defined by the specified threshold.<br> - could return `ErrorThreshold`; |
| after | `func NewThrottlerAfter(threshold uint64) Throttler` | Throttles each call after the *i-th* call defined by the specified threshold.<br> - could return `ErrorThreshold`; |
| past | `func NewThrottlerPast(threshold time.Time) Throttler` | Throttles each call befor timestamp defined by the specified UTC time threshold.<br> - could return `ErrorThreshold`; |
| future | `func NewThrottlerFuture(threshold time.Time) Throttler` | Throttles each call after timestamp defined by the specified UTC time threshold.<br> - could return `ErrorThreshold`; |
| chance | `func NewThrottlerChance(threshold float64) Throttler` | Throttles each call with the chance *p* defined by the specified threshold.<br> Chance value is normalized to *[0.0, 1.0]* range.<br> Implementation uses `math/rand` as PRNG function and expects rand seeding by a client.<br> - could return `ErrorThreshold`; |
| running | `func NewThrottlerRunning(threshold uint64) Throttler` | Throttles each call which exeeds the running quota *acquired - release* *q* defined by the specified threshold.<br> - could return `ErrorThreshold`; |
| buffered | `func NewThrottlerBuffered(threshold uint64) Throttler` | Waits on call which exeeds the running quota *acquired - release* *q* defined by the specified threshold until the running quota is available again. |
Expand Down
13 changes: 13 additions & 0 deletions errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,19 @@ func (d strdurations) String() string {
return fmt.Sprintf("%s out of %s", d.current, d.threshold)
}

type strtimes struct {
current time.Time
threshold time.Time
}

func (d strtimes) String() string {
return fmt.Sprintf(
"%s out of %s",
d.current.Format(time.RFC3339Nano),
d.threshold.Format(time.RFC3339Nano),
)
}

type strstats struct {
current Stats
threshold Stats
Expand Down
50 changes: 50 additions & 0 deletions throttlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,56 @@ func (thr *tafter) Release(context.Context) error {
return nil
}

type tpast struct {
threshold time.Time
}

// NewThrottlerPast creates new throttler instance that
// throttles each call before timestamp defined by the specified UTC time threshold.
// - could return `ErrorThreshold`;
func NewThrottlerPast(threshold time.Time) Throttler {
return tpast{threshold: threshold.UTC()}
}

func (thr tpast) Acquire(ctx context.Context) error {
if ts := ctxTimestamp(ctx); ts.UnixNano() <= thr.threshold.UnixNano() {
return ErrorThreshold{
Throttler: "past",
Threshold: strtimes{current: ts, threshold: thr.threshold},
}
}
return nil
}

func (thr tpast) Release(context.Context) error {
return nil
}

type tfuture struct {
threshold time.Time
}

// NewThrottlerFuture creates new throttler instance that
// throttles each call after timestamp defined by the specified UTC time threshold.
// - could return `ErrorThreshold`;
func NewThrottlerFuture(threshold time.Time) Throttler {
return tfuture{threshold: threshold.UTC()}
}

func (thr tfuture) Acquire(ctx context.Context) error {
if ts := ctxTimestamp(ctx); ts.UnixNano() > thr.threshold.UnixNano() {
return ErrorThreshold{
Throttler: "future",
Threshold: strtimes{current: ts, threshold: thr.threshold},
}
}
return nil
}

func (thr tfuture) Release(context.Context) error {
return nil
}

type tchance struct {
threshold float64
}
Expand Down
46 changes: 46 additions & 0 deletions throttlers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -299,6 +299,52 @@ func TestThrottlers(t *testing.T) {
},
},
},
"Throttler past should throttle before time threshold": {
tms: 3,
thr: NewThrottlerPast(time.Date(2000, 1, 1, 10, 0, 0, 0, time.Local)),
ctxs: []context.Context{
WithTimestamp(context.Background(), time.Date(1900, 1, 1, 10, 0, 0, 0, time.UTC)),
WithTimestamp(context.Background(), time.Date(2000, 1, 1, 9, 59, 0, 0, time.Local)),
WithTimestamp(context.Background(), time.Date(2000, 1, 1, 10, 59, 0, 0, time.Local)),
},
errs: []error{
ErrorThreshold{
Throttler: "past",
Threshold: strtimes{
current: time.Date(1900, 1, 1, 10, 0, 0, 0, time.UTC),
threshold: time.Date(2000, 1, 1, 10, 0, 0, 0, time.Local).UTC(),
},
},
ErrorThreshold{
Throttler: "past",
Threshold: strtimes{
current: time.Date(2000, 1, 1, 9, 59, 0, 0, time.Local).UTC(),
threshold: time.Date(2000, 1, 1, 10, 0, 0, 0, time.Local).UTC(),
},
},
nil,
},
},
"Throttler future should throttle before time threshold": {
tms: 3,
thr: NewThrottlerFuture(time.Date(2000, 1, 1, 10, 0, 0, 0, time.Local)),
ctxs: []context.Context{
WithTimestamp(context.Background(), time.Date(1900, 1, 1, 10, 0, 0, 0, time.UTC)),
WithTimestamp(context.Background(), time.Date(2000, 1, 1, 9, 59, 0, 0, time.Local)),
WithTimestamp(context.Background(), time.Date(2000, 1, 1, 10, 59, 0, 0, time.Local)),
},
errs: []error{
nil,
nil,
ErrorThreshold{
Throttler: "future",
Threshold: strtimes{
current: time.Date(2000, 1, 1, 10, 59, 0, 0, time.Local).UTC(),
threshold: time.Date(2000, 1, 1, 10, 0, 0, 0, time.Local).UTC(),
},
},
},
},
"Throttler chance should throttle on 1": {
tms: 3,
thr: NewThrottlerChance(1),
Expand Down

0 comments on commit 1bc421e

Please sign in to comment.