Skip to content

Commit 1bc421e

Browse files
committed
add past and future time throttlers
1 parent e5da1bf commit 1bc421e

File tree

4 files changed

+111
-0
lines changed

4 files changed

+111
-0
lines changed

README.MD

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,8 @@ You can find list of returning error types for all existing throttlers in thrott
149149
| each | `func NewThrottlerEach(threshold uint64) Throttler` | Throttles each periodic *i-th* call defined by the specified threshold.<br> - could return `ErrorThreshold`; |
150150
| before | `func NewThrottlerBefore(threshold uint64) Throttler` | Throttles each call below the *i-th* call defined by the specified threshold.<br> - could return `ErrorThreshold`; |
151151
| after | `func NewThrottlerAfter(threshold uint64) Throttler` | Throttles each call after the *i-th* call defined by the specified threshold.<br> - could return `ErrorThreshold`; |
152+
| past | `func NewThrottlerPast(threshold time.Time) Throttler` | Throttles each call befor timestamp defined by the specified UTC time threshold.<br> - could return `ErrorThreshold`; |
153+
| future | `func NewThrottlerFuture(threshold time.Time) Throttler` | Throttles each call after timestamp defined by the specified UTC time threshold.<br> - could return `ErrorThreshold`; |
152154
| 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`; |
153155
| 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`; |
154156
| 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. |

errors.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,19 @@ func (d strdurations) String() string {
3535
return fmt.Sprintf("%s out of %s", d.current, d.threshold)
3636
}
3737

38+
type strtimes struct {
39+
current time.Time
40+
threshold time.Time
41+
}
42+
43+
func (d strtimes) String() string {
44+
return fmt.Sprintf(
45+
"%s out of %s",
46+
d.current.Format(time.RFC3339Nano),
47+
d.threshold.Format(time.RFC3339Nano),
48+
)
49+
}
50+
3851
type strstats struct {
3952
current Stats
4053
threshold Stats

throttlers.go

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -263,6 +263,56 @@ func (thr *tafter) Release(context.Context) error {
263263
return nil
264264
}
265265

266+
type tpast struct {
267+
threshold time.Time
268+
}
269+
270+
// NewThrottlerPast creates new throttler instance that
271+
// throttles each call before timestamp defined by the specified UTC time threshold.
272+
// - could return `ErrorThreshold`;
273+
func NewThrottlerPast(threshold time.Time) Throttler {
274+
return tpast{threshold: threshold.UTC()}
275+
}
276+
277+
func (thr tpast) Acquire(ctx context.Context) error {
278+
if ts := ctxTimestamp(ctx); ts.UnixNano() <= thr.threshold.UnixNano() {
279+
return ErrorThreshold{
280+
Throttler: "past",
281+
Threshold: strtimes{current: ts, threshold: thr.threshold},
282+
}
283+
}
284+
return nil
285+
}
286+
287+
func (thr tpast) Release(context.Context) error {
288+
return nil
289+
}
290+
291+
type tfuture struct {
292+
threshold time.Time
293+
}
294+
295+
// NewThrottlerFuture creates new throttler instance that
296+
// throttles each call after timestamp defined by the specified UTC time threshold.
297+
// - could return `ErrorThreshold`;
298+
func NewThrottlerFuture(threshold time.Time) Throttler {
299+
return tfuture{threshold: threshold.UTC()}
300+
}
301+
302+
func (thr tfuture) Acquire(ctx context.Context) error {
303+
if ts := ctxTimestamp(ctx); ts.UnixNano() > thr.threshold.UnixNano() {
304+
return ErrorThreshold{
305+
Throttler: "future",
306+
Threshold: strtimes{current: ts, threshold: thr.threshold},
307+
}
308+
}
309+
return nil
310+
}
311+
312+
func (thr tfuture) Release(context.Context) error {
313+
return nil
314+
}
315+
266316
type tchance struct {
267317
threshold float64
268318
}

throttlers_test.go

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -299,6 +299,52 @@ func TestThrottlers(t *testing.T) {
299299
},
300300
},
301301
},
302+
"Throttler past should throttle before time threshold": {
303+
tms: 3,
304+
thr: NewThrottlerPast(time.Date(2000, 1, 1, 10, 0, 0, 0, time.Local)),
305+
ctxs: []context.Context{
306+
WithTimestamp(context.Background(), time.Date(1900, 1, 1, 10, 0, 0, 0, time.UTC)),
307+
WithTimestamp(context.Background(), time.Date(2000, 1, 1, 9, 59, 0, 0, time.Local)),
308+
WithTimestamp(context.Background(), time.Date(2000, 1, 1, 10, 59, 0, 0, time.Local)),
309+
},
310+
errs: []error{
311+
ErrorThreshold{
312+
Throttler: "past",
313+
Threshold: strtimes{
314+
current: time.Date(1900, 1, 1, 10, 0, 0, 0, time.UTC),
315+
threshold: time.Date(2000, 1, 1, 10, 0, 0, 0, time.Local).UTC(),
316+
},
317+
},
318+
ErrorThreshold{
319+
Throttler: "past",
320+
Threshold: strtimes{
321+
current: time.Date(2000, 1, 1, 9, 59, 0, 0, time.Local).UTC(),
322+
threshold: time.Date(2000, 1, 1, 10, 0, 0, 0, time.Local).UTC(),
323+
},
324+
},
325+
nil,
326+
},
327+
},
328+
"Throttler future should throttle before time threshold": {
329+
tms: 3,
330+
thr: NewThrottlerFuture(time.Date(2000, 1, 1, 10, 0, 0, 0, time.Local)),
331+
ctxs: []context.Context{
332+
WithTimestamp(context.Background(), time.Date(1900, 1, 1, 10, 0, 0, 0, time.UTC)),
333+
WithTimestamp(context.Background(), time.Date(2000, 1, 1, 9, 59, 0, 0, time.Local)),
334+
WithTimestamp(context.Background(), time.Date(2000, 1, 1, 10, 59, 0, 0, time.Local)),
335+
},
336+
errs: []error{
337+
nil,
338+
nil,
339+
ErrorThreshold{
340+
Throttler: "future",
341+
Threshold: strtimes{
342+
current: time.Date(2000, 1, 1, 10, 59, 0, 0, time.Local).UTC(),
343+
threshold: time.Date(2000, 1, 1, 10, 0, 0, 0, time.Local).UTC(),
344+
},
345+
},
346+
},
347+
},
302348
"Throttler chance should throttle on 1": {
303349
tms: 3,
304350
thr: NewThrottlerChance(1),

0 commit comments

Comments
 (0)