Try to optimize route compile benchmark, optimize luarouter
This commit is contained in:
parent
13cb83b7a7
commit
82470b35a0
@ -52,6 +52,15 @@ type LuaRouter struct {
|
||||
|
||||
// Middleware tracking for path hierarchy
|
||||
middlewareFiles map[string][]string // path -> middleware file paths
|
||||
|
||||
// New caching fields
|
||||
middlewareCache map[string][]byte // path -> content
|
||||
sourceCache map[string][]byte // combined source cache key -> compiled bytecode
|
||||
sourceMtimes map[string]time.Time // track modification times
|
||||
|
||||
// Shared Lua state for compilation
|
||||
compileState *luajit.State
|
||||
compileStateMu sync.Mutex // Protect concurrent access to Lua state
|
||||
}
|
||||
|
||||
// node represents a node in the routing trie
|
||||
@ -75,6 +84,12 @@ func NewLuaRouter(routesDir string) (*LuaRouter, error) {
|
||||
return nil, errors.New("routes path is not a directory")
|
||||
}
|
||||
|
||||
// Create shared Lua state
|
||||
compileState := luajit.New()
|
||||
if compileState == nil {
|
||||
return nil, errors.New("failed to create Lua compile state")
|
||||
}
|
||||
|
||||
r := &LuaRouter{
|
||||
routesDir: routesDir,
|
||||
routes: make(map[string]*node),
|
||||
@ -82,6 +97,10 @@ func NewLuaRouter(routesDir string) (*LuaRouter, error) {
|
||||
middlewareFiles: make(map[string][]string),
|
||||
routeCache: fastcache.New(defaultRouteMaxBytes),
|
||||
bytecodeCache: fastcache.New(defaultBytecodeMaxBytes),
|
||||
middlewareCache: make(map[string][]byte),
|
||||
sourceCache: make(map[string][]byte),
|
||||
sourceMtimes: make(map[string]time.Time),
|
||||
compileState: compileState,
|
||||
}
|
||||
|
||||
methods := []string{"GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS", "HEAD"}
|
||||
@ -221,19 +240,87 @@ func (r *LuaRouter) compileWithMiddleware(n *node, urlPath, scriptPath string) e
|
||||
return nil
|
||||
}
|
||||
|
||||
// Collect middleware for this path (cascading from root)
|
||||
middlewareChain := r.getMiddlewareChain(urlPath)
|
||||
// Check if we need to recompile by comparing modification times
|
||||
sourceKey := r.getSourceCacheKey(urlPath, scriptPath)
|
||||
needsRecompile := false
|
||||
|
||||
// Read and combine all source files
|
||||
var combinedSource strings.Builder
|
||||
|
||||
// Add middleware in order
|
||||
for _, mwPath := range middlewareChain {
|
||||
content, err := os.ReadFile(mwPath)
|
||||
// Check handler modification time
|
||||
handlerInfo, err := os.Stat(scriptPath)
|
||||
if err != nil {
|
||||
n.err = err
|
||||
return err
|
||||
}
|
||||
|
||||
lastCompiled, exists := r.sourceMtimes[sourceKey]
|
||||
if !exists || handlerInfo.ModTime().After(lastCompiled) {
|
||||
needsRecompile = true
|
||||
}
|
||||
|
||||
// Check middleware modification times
|
||||
if !needsRecompile {
|
||||
middlewareChain := r.getMiddlewareChain(urlPath)
|
||||
for _, mwPath := range middlewareChain {
|
||||
mwInfo, err := os.Stat(mwPath)
|
||||
if err != nil {
|
||||
n.err = err
|
||||
return err
|
||||
}
|
||||
if mwInfo.ModTime().After(lastCompiled) {
|
||||
needsRecompile = true
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Use cached bytecode if available and fresh
|
||||
if !needsRecompile {
|
||||
if bytecode, exists := r.sourceCache[sourceKey]; exists {
|
||||
bytecodeKey := getBytecodeKey(scriptPath)
|
||||
r.bytecodeCache.Set(bytecodeKey, bytecode)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// Build combined source
|
||||
combinedSource, err := r.buildCombinedSource(urlPath, scriptPath)
|
||||
if err != nil {
|
||||
n.err = err
|
||||
return err
|
||||
}
|
||||
|
||||
// Compile combined source using shared state
|
||||
r.compileStateMu.Lock()
|
||||
bytecode, err := r.compileState.CompileBytecode(combinedSource, scriptPath)
|
||||
r.compileStateMu.Unlock()
|
||||
|
||||
if err != nil {
|
||||
n.err = err
|
||||
return err
|
||||
}
|
||||
|
||||
// Cache everything
|
||||
bytecodeKey := getBytecodeKey(scriptPath)
|
||||
r.bytecodeCache.Set(bytecodeKey, bytecode)
|
||||
r.sourceCache[sourceKey] = bytecode
|
||||
r.sourceMtimes[sourceKey] = time.Now()
|
||||
|
||||
n.err = nil
|
||||
return nil
|
||||
}
|
||||
|
||||
// buildCombinedSource builds the combined middleware + handler source
|
||||
func (r *LuaRouter) buildCombinedSource(urlPath, scriptPath string) (string, error) {
|
||||
var combinedSource strings.Builder
|
||||
|
||||
// Get middleware chain
|
||||
middlewareChain := r.getMiddlewareChain(urlPath)
|
||||
|
||||
// Add middleware in order
|
||||
for _, mwPath := range middlewareChain {
|
||||
content, err := r.getFileContent(mwPath)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
combinedSource.WriteString("-- Middleware: ")
|
||||
combinedSource.WriteString(mwPath)
|
||||
combinedSource.WriteString("\n")
|
||||
@ -242,36 +329,51 @@ func (r *LuaRouter) compileWithMiddleware(n *node, urlPath, scriptPath string) e
|
||||
}
|
||||
|
||||
// Add main handler
|
||||
content, err := os.ReadFile(scriptPath)
|
||||
content, err := r.getFileContent(scriptPath)
|
||||
if err != nil {
|
||||
n.err = err
|
||||
return err
|
||||
return "", err
|
||||
}
|
||||
combinedSource.WriteString("-- Handler: ")
|
||||
combinedSource.WriteString(scriptPath)
|
||||
combinedSource.WriteString("\n")
|
||||
combinedSource.Write(content)
|
||||
|
||||
// Compile combined source
|
||||
state := luajit.New()
|
||||
if state == nil {
|
||||
compileErr := errors.New("failed to create Lua state")
|
||||
n.err = compileErr
|
||||
return compileErr
|
||||
}
|
||||
defer state.Close()
|
||||
return combinedSource.String(), nil
|
||||
}
|
||||
|
||||
bytecode, err := state.CompileBytecode(combinedSource.String(), scriptPath)
|
||||
// getFileContent reads file content with caching
|
||||
func (r *LuaRouter) getFileContent(path string) ([]byte, error) {
|
||||
// Check cache first
|
||||
if content, exists := r.middlewareCache[path]; exists {
|
||||
// Verify file hasn't changed
|
||||
info, err := os.Stat(path)
|
||||
if err == nil {
|
||||
if cachedTime, exists := r.sourceMtimes[path]; exists && !info.ModTime().After(cachedTime) {
|
||||
return content, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Read from disk
|
||||
content, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
n.err = err
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
bytecodeKey := getBytecodeKey(scriptPath)
|
||||
r.bytecodeCache.Set(bytecodeKey, bytecode)
|
||||
// Cache it
|
||||
r.middlewareCache[path] = content
|
||||
r.sourceMtimes[path] = time.Now()
|
||||
|
||||
n.err = nil
|
||||
return nil
|
||||
return content, nil
|
||||
}
|
||||
|
||||
// getSourceCacheKey generates a unique key for combined source
|
||||
func (r *LuaRouter) getSourceCacheKey(urlPath, scriptPath string) string {
|
||||
middlewareChain := r.getMiddlewareChain(urlPath)
|
||||
var keyParts []string
|
||||
keyParts = append(keyParts, middlewareChain...)
|
||||
keyParts = append(keyParts, scriptPath)
|
||||
return strings.Join(keyParts, "|")
|
||||
}
|
||||
|
||||
// getMiddlewareChain returns middleware files that apply to the given path
|
||||
@ -564,6 +666,9 @@ func (r *LuaRouter) Refresh() error {
|
||||
|
||||
r.failedRoutes = make(map[string]*RouteError)
|
||||
r.middlewareFiles = make(map[string][]string)
|
||||
r.middlewareCache = make(map[string][]byte)
|
||||
r.sourceCache = make(map[string][]byte)
|
||||
r.sourceMtimes = make(map[string]time.Time)
|
||||
|
||||
err := r.buildRoutes()
|
||||
|
||||
@ -587,10 +692,23 @@ func (r *LuaRouter) ReportFailedRoutes() []*RouteError {
|
||||
return result
|
||||
}
|
||||
|
||||
// ClearCache clears both the route and bytecode caches
|
||||
// ClearCache clears all caches
|
||||
func (r *LuaRouter) ClearCache() {
|
||||
r.routeCache.Reset()
|
||||
r.bytecodeCache.Reset()
|
||||
r.middlewareCache = make(map[string][]byte)
|
||||
r.sourceCache = make(map[string][]byte)
|
||||
r.sourceMtimes = make(map[string]time.Time)
|
||||
}
|
||||
|
||||
// Close cleans up the router and its resources
|
||||
func (r *LuaRouter) Close() {
|
||||
r.compileStateMu.Lock()
|
||||
if r.compileState != nil {
|
||||
r.compileState.Close()
|
||||
r.compileState = nil
|
||||
}
|
||||
r.compileStateMu.Unlock()
|
||||
}
|
||||
|
||||
// GetCacheStats returns statistics about the cache
|
||||
|
@ -298,7 +298,7 @@ func BenchmarkRouteCompilation(b *testing.B) {
|
||||
routesDir := filepath.Join(tempDir, "routes")
|
||||
os.MkdirAll(routesDir, 0755)
|
||||
|
||||
for b.Loop() {
|
||||
for i := 0; i < b.N; i++ {
|
||||
b.StopTimer()
|
||||
os.RemoveAll(routesDir)
|
||||
os.MkdirAll(routesDir, 0755)
|
||||
|
Loading…
x
Reference in New Issue
Block a user