pools
This commit is contained in:
parent
bc6ef0d882
commit
9b1942903b
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -27,3 +27,4 @@ config.lua
|
|||
routes/
|
||||
static/
|
||||
libs/
|
||||
override/
|
||||
|
|
|
@ -101,7 +101,7 @@ func (s *Server) handleRequest(w http.ResponseWriter, r *http.Request) {
|
|||
bytecode, scriptPath, found := s.luaRouter.GetBytecode(r.Method, r.URL.Path, params)
|
||||
|
||||
// Check if we found a route but it has no valid bytecode (compile error)
|
||||
if found && (bytecode == nil || len(bytecode) == 0) {
|
||||
if found && len(bytecode) == 0 {
|
||||
// Get the actual error from the router - this requires exposing the actual error
|
||||
// from the node in the GetBytecode method
|
||||
errorMsg := "Route exists but failed to compile. Check server logs for details."
|
||||
|
@ -147,6 +147,7 @@ func (s *Server) HandleMethodNotAllowed(w http.ResponseWriter, r *http.Request)
|
|||
// handleLuaRoute executes a Lua route
|
||||
func (s *Server) handleLuaRoute(w http.ResponseWriter, r *http.Request, bytecode []byte, scriptPath string, params *routers.Params) {
|
||||
ctx := runner.NewContext()
|
||||
defer ctx.Release()
|
||||
|
||||
// Log bytecode size
|
||||
s.logger.Debug("Executing Lua route with %d bytes of bytecode", len(bytecode))
|
||||
|
@ -234,6 +235,8 @@ func writeResponse(w http.ResponseWriter, result any, log *logger.Logger) {
|
|||
|
||||
// Check for HTTPResponse type
|
||||
if httpResp, ok := result.(*runner.HTTPResponse); ok {
|
||||
defer runner.ReleaseResponse(httpResp)
|
||||
|
||||
// Set response headers
|
||||
for name, value := range httpResp.Headers {
|
||||
w.Header().Set(name, value)
|
||||
|
|
|
@ -1,16 +1,34 @@
|
|||
package runner
|
||||
|
||||
import "sync"
|
||||
|
||||
// Context represents execution context for a Lua script
|
||||
type Context struct {
|
||||
// Generic map for any context values (route params, HTTP request info, etc.)
|
||||
Values map[string]any
|
||||
}
|
||||
|
||||
// NewContext creates a new context with initialized maps
|
||||
func NewContext() *Context {
|
||||
// Context pool to reduce allocations
|
||||
var contextPool = sync.Pool{
|
||||
New: func() interface{} {
|
||||
return &Context{
|
||||
Values: make(map[string]any),
|
||||
Values: make(map[string]any, 16), // Pre-allocate with reasonable capacity
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
// NewContext creates a new context, potentially reusing one from the pool
|
||||
func NewContext() *Context {
|
||||
return contextPool.Get().(*Context)
|
||||
}
|
||||
|
||||
// Release returns the context to the pool after clearing its values
|
||||
func (c *Context) Release() {
|
||||
// Clear all values to prevent data leakage
|
||||
for k := range c.Values {
|
||||
delete(c.Values, k)
|
||||
}
|
||||
contextPool.Put(c)
|
||||
}
|
||||
|
||||
// Set adds a value to the context
|
||||
|
|
|
@ -2,6 +2,7 @@ package runner
|
|||
|
||||
import (
|
||||
"net/http"
|
||||
"sync"
|
||||
|
||||
luajit "git.sharkk.net/Sky/LuaJIT-to-Go"
|
||||
)
|
||||
|
@ -14,13 +15,43 @@ type HTTPResponse struct {
|
|||
Cookies []*http.Cookie `json:"-"`
|
||||
}
|
||||
|
||||
// NewHTTPResponse creates a default HTTP response
|
||||
func NewHTTPResponse() *HTTPResponse {
|
||||
// Response pool to reduce allocations
|
||||
var responsePool = sync.Pool{
|
||||
New: func() interface{} {
|
||||
return &HTTPResponse{
|
||||
Status: 200,
|
||||
Headers: make(map[string]string),
|
||||
Cookies: []*http.Cookie{},
|
||||
Headers: make(map[string]string, 8), // Pre-allocate with reasonable capacity
|
||||
Cookies: make([]*http.Cookie, 0, 4), // Pre-allocate with reasonable capacity
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
// NewHTTPResponse creates a default HTTP response, potentially reusing one from the pool
|
||||
func NewHTTPResponse() *HTTPResponse {
|
||||
return responsePool.Get().(*HTTPResponse)
|
||||
}
|
||||
|
||||
// ReleaseResponse returns the response to the pool after clearing its values
|
||||
func ReleaseResponse(resp *HTTPResponse) {
|
||||
if resp == nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Clear all values to prevent data leakage
|
||||
resp.Status = 200 // Reset to default
|
||||
|
||||
// Clear headers
|
||||
for k := range resp.Headers {
|
||||
delete(resp.Headers, k)
|
||||
}
|
||||
|
||||
// Clear cookies
|
||||
resp.Cookies = resp.Cookies[:0] // Keep capacity but set length to 0
|
||||
|
||||
// Clear body
|
||||
resp.Body = nil
|
||||
|
||||
responsePool.Put(resp)
|
||||
}
|
||||
|
||||
// LuaHTTPModule is the pure Lua implementation of the HTTP module
|
||||
|
@ -97,7 +128,8 @@ func GetHTTPResponse(state *luajit.State) (*HTTPResponse, bool) {
|
|||
state.GetGlobal("__http_responses")
|
||||
if state.IsNil(-1) {
|
||||
state.Pop(1)
|
||||
return response, false
|
||||
ReleaseResponse(response) // Return unused response to pool
|
||||
return nil, false
|
||||
}
|
||||
|
||||
// Check for response at thread index
|
||||
|
@ -105,7 +137,8 @@ func GetHTTPResponse(state *luajit.State) (*HTTPResponse, bool) {
|
|||
state.GetTable(-2)
|
||||
if state.IsNil(-1) {
|
||||
state.Pop(2)
|
||||
return response, false
|
||||
ReleaseResponse(response) // Return unused response to pool
|
||||
return nil, false
|
||||
}
|
||||
|
||||
// Get status
|
||||
|
|
|
@ -22,6 +22,13 @@ type StateInitFunc func(*luajit.State) error
|
|||
// RunnerOption defines a functional option for configuring the LuaRunner
|
||||
type RunnerOption func(*LuaRunner)
|
||||
|
||||
// Result channel pool to reduce allocations
|
||||
var resultChanPool = sync.Pool{
|
||||
New: func() interface{} {
|
||||
return make(chan JobResult, 1)
|
||||
},
|
||||
}
|
||||
|
||||
// LuaRunner runs Lua scripts using a single Lua state
|
||||
type LuaRunner struct {
|
||||
state *luajit.State // The Lua state
|
||||
|
@ -191,7 +198,18 @@ func (r *LuaRunner) RunWithContext(ctx context.Context, bytecode []byte, execCtx
|
|||
}
|
||||
r.mu.RUnlock()
|
||||
|
||||
resultChan := make(chan JobResult, 1)
|
||||
// Get a result channel from the pool
|
||||
resultChanInterface := resultChanPool.Get()
|
||||
resultChan := resultChanInterface.(chan JobResult)
|
||||
|
||||
// Make sure to clear any previous results
|
||||
select {
|
||||
case <-resultChan:
|
||||
// Drain the channel if it has a value
|
||||
default:
|
||||
// Channel is already empty
|
||||
}
|
||||
|
||||
j := job{
|
||||
Bytecode: bytecode,
|
||||
Context: execCtx,
|
||||
|
@ -204,16 +222,26 @@ func (r *LuaRunner) RunWithContext(ctx context.Context, bytecode []byte, execCtx
|
|||
case r.jobQueue <- j:
|
||||
// Job submitted
|
||||
case <-ctx.Done():
|
||||
// Return the channel to the pool before exiting
|
||||
resultChanPool.Put(resultChan)
|
||||
return nil, ctx.Err()
|
||||
}
|
||||
|
||||
// Wait for result with context
|
||||
var result JobResult
|
||||
select {
|
||||
case result := <-resultChan:
|
||||
return result.Value, result.Error
|
||||
case result = <-resultChan:
|
||||
// Got result
|
||||
case <-ctx.Done():
|
||||
// Return the channel to the pool before exiting
|
||||
resultChanPool.Put(resultChan)
|
||||
return nil, ctx.Err()
|
||||
}
|
||||
|
||||
// Return the channel to the pool
|
||||
resultChanPool.Put(resultChan)
|
||||
|
||||
return result.Value, result.Error
|
||||
}
|
||||
|
||||
// Run executes a Lua script
|
||||
|
|
Loading…
Reference in New Issue
Block a user