Skip to content

Commit

Permalink
BAAS-28597: Add counter for limiter wait calls (#117)
Browse files Browse the repository at this point in the history
  • Loading branch information
Calvinnix authored Mar 11, 2024
1 parent acea50a commit 06a8a67
Show file tree
Hide file tree
Showing 2 changed files with 45 additions and 10 deletions.
13 changes: 13 additions & 0 deletions runtime.go
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,9 @@ type Runtime struct {
limiterTicksLeft int
ticks uint64

limiterWaitCount int64
limiterWaitTotalTime time.Duration

tickMetricTrackingEnabled bool
tickMetrics map[string]uint64
}
Expand All @@ -213,6 +216,16 @@ func (self *Runtime) Ticks() uint64 {
return self.ticks
}

// LimiterWaitCount tracks the amount of times the rate limiter throttles the execution.
func (self *Runtime) LimiterWaitCount() int64 {
return self.limiterWaitCount
}

// LimiterWaitTotalTime tracks the total amount of time that the execution was throttled.
func (self *Runtime) LimiterWaitTotalTime() time.Duration {
return self.limiterWaitTotalTime
}

// SetStackTraceLimit sets an upper limit to the number of stack frames that
// goja will use when formatting an error's stack trace. By default, the limit
// is 10. This is consistent with V8 and SpiderMonkey.
Expand Down
42 changes: 32 additions & 10 deletions vm.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"strings"
"sync"
"sync/atomic"
"time"

"github.com/dop251/goja/unistring"
)
Expand Down Expand Up @@ -617,17 +618,26 @@ func (vm *vm) run() {
ctx = context.Background()
}

if waitErr := vm.r.limiter.WaitN(ctx, vm.r.limiterTicksLeft); waitErr != nil {
if vm.r.vm.ctx == nil {
panic(waitErr)
}
if ctxErr := vm.r.vm.ctx.Err(); ctxErr != nil {
panic(ctxErr)
}
if strings.Contains(waitErr.Error(), "would exceed") {
panic(context.DeadlineExceeded)
select {
case <-ctx.Done():
panic(ctx.Err())
default:
}

now := time.Now()
r := vm.r.limiter.ReserveN(now, vm.r.limiterTicksLeft)
if !r.OK() {
panic(context.DeadlineExceeded)
}

if delay := r.DelayFrom(now); delay > 0 {
vm.r.limiterWaitCount++
vm.r.limiterWaitTotalTime += delay

if err := sleep(ctx, delay); err != nil {
r.Cancel()
panic(err)
}
panic(waitErr)
}
}

Expand Down Expand Up @@ -5606,3 +5616,15 @@ func (s valueStack) MemUsage(ctx *MemUsageContext) (memUsage uint64, newMemUsage

return memUsage, newMemUsage, nil
}

// sleep is equivalent to time.Sleep, but is interruptible via context cancelation.
func sleep(ctx context.Context, duration time.Duration) error {
timer := time.NewTimer(duration)
defer timer.Stop()
select {
case <-ctx.Done():
return ctx.Err()
case <-timer.C:
return nil
}
}

0 comments on commit 06a8a67

Please sign in to comment.