JS_MAX_CONCURRENT

main v0.0.9
parent c0329caa54
commit baa9bc1ecc
  1. 40
      ottomain.go

@ -5,6 +5,7 @@ import (
"errors" "errors"
"fmt" "fmt"
"reflect" "reflect"
"sync"
"time" "time"
"github.com/caarlos0/env" "github.com/caarlos0/env"
@ -12,9 +13,17 @@ import (
) )
type otto_config struct { type otto_config struct {
Timeout int `env:"JS_TIMEOUT" envDefault:"2"` Timeout int `env:"JS_TIMEOUT" envDefault:"2"`
MaxConcurrent int `env:"JS_MAX_CONCURRENT" envDefault:"50"`
} }
var (
once sync.Once
semaphore chan struct{}
configChecked bool
configMutex sync.Mutex
)
func jsBtoa(b string) string { func jsBtoa(b string) string {
return base64.StdEncoding.EncodeToString([]byte(b)) return base64.StdEncoding.EncodeToString([]byte(b))
} }
@ -41,7 +50,34 @@ func jsRegisterFunctions(vm *otto.Otto) (err error) {
return return
} }
func initSemaphore() {
configMutex.Lock()
defer configMutex.Unlock()
if !configChecked {
var cfg otto_config
if err := env.Parse(&cfg); err != nil {
// Use default if env parse fails
cfg.MaxConcurrent = 50
}
semaphore = make(chan struct{}, cfg.MaxConcurrent)
configChecked = true
}
}
func ProcessRequest(script string, params map[string]interface{}) (response map[string]interface{}, err error) { func ProcessRequest(script string, params map[string]interface{}) (response map[string]interface{}, err error) {
// Initialize semaphore once
once.Do(initSemaphore)
// Acquire semaphore to limit concurrent executions
select {
case semaphore <- struct{}{}:
defer func() { <-semaphore }() // Release semaphore when done
case <-time.After(30 * time.Second):
// Timeout if too many concurrent requests
return nil, fmt.Errorf("too many concurrent JavaScript executions, please try again later")
}
var cfg otto_config var cfg otto_config
if err := env.Parse(&cfg); err != nil { if err := env.Parse(&cfg); err != nil {
return nil, fmt.Errorf("env vars parse error: %w", err) return nil, fmt.Errorf("env vars parse error: %w", err)
@ -83,6 +119,8 @@ func ProcessRequest(script string, params map[string]interface{}) (response map[
defer func() { defer func() {
close(timeoutDone) // Signal timeout goroutine to exit close(timeoutDone) // Signal timeout goroutine to exit
// Clean up vm to help with garbage collection
vm = nil
if r := recover(); r != nil { if r := recover(); r != nil {
switch x := r.(type) { switch x := r.(type) {
case error: case error:

Loading…
Cancel
Save