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
|
// Middleware tracking for path hierarchy
|
||||||
middlewareFiles map[string][]string // path -> middleware file paths
|
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
|
// 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")
|
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{
|
r := &LuaRouter{
|
||||||
routesDir: routesDir,
|
routesDir: routesDir,
|
||||||
routes: make(map[string]*node),
|
routes: make(map[string]*node),
|
||||||
@ -82,6 +97,10 @@ func NewLuaRouter(routesDir string) (*LuaRouter, error) {
|
|||||||
middlewareFiles: make(map[string][]string),
|
middlewareFiles: make(map[string][]string),
|
||||||
routeCache: fastcache.New(defaultRouteMaxBytes),
|
routeCache: fastcache.New(defaultRouteMaxBytes),
|
||||||
bytecodeCache: fastcache.New(defaultBytecodeMaxBytes),
|
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"}
|
methods := []string{"GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS", "HEAD"}
|
||||||
@ -221,18 +240,86 @@ func (r *LuaRouter) compileWithMiddleware(n *node, urlPath, scriptPath string) e
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Collect middleware for this path (cascading from root)
|
// Check if we need to recompile by comparing modification times
|
||||||
middlewareChain := r.getMiddlewareChain(urlPath)
|
sourceKey := r.getSourceCacheKey(urlPath, scriptPath)
|
||||||
|
needsRecompile := false
|
||||||
|
|
||||||
// Read and combine all source files
|
// 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
|
var combinedSource strings.Builder
|
||||||
|
|
||||||
|
// Get middleware chain
|
||||||
|
middlewareChain := r.getMiddlewareChain(urlPath)
|
||||||
|
|
||||||
// Add middleware in order
|
// Add middleware in order
|
||||||
for _, mwPath := range middlewareChain {
|
for _, mwPath := range middlewareChain {
|
||||||
content, err := os.ReadFile(mwPath)
|
content, err := r.getFileContent(mwPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
n.err = err
|
return "", err
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
combinedSource.WriteString("-- Middleware: ")
|
combinedSource.WriteString("-- Middleware: ")
|
||||||
combinedSource.WriteString(mwPath)
|
combinedSource.WriteString(mwPath)
|
||||||
@ -242,36 +329,51 @@ func (r *LuaRouter) compileWithMiddleware(n *node, urlPath, scriptPath string) e
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Add main handler
|
// Add main handler
|
||||||
content, err := os.ReadFile(scriptPath)
|
content, err := r.getFileContent(scriptPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
n.err = err
|
return "", err
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
combinedSource.WriteString("-- Handler: ")
|
combinedSource.WriteString("-- Handler: ")
|
||||||
combinedSource.WriteString(scriptPath)
|
combinedSource.WriteString(scriptPath)
|
||||||
combinedSource.WriteString("\n")
|
combinedSource.WriteString("\n")
|
||||||
combinedSource.Write(content)
|
combinedSource.Write(content)
|
||||||
|
|
||||||
// Compile combined source
|
return combinedSource.String(), nil
|
||||||
state := luajit.New()
|
}
|
||||||
if state == nil {
|
|
||||||
compileErr := errors.New("failed to create Lua state")
|
|
||||||
n.err = compileErr
|
|
||||||
return compileErr
|
|
||||||
}
|
|
||||||
defer state.Close()
|
|
||||||
|
|
||||||
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 {
|
if err != nil {
|
||||||
n.err = err
|
return nil, err
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bytecodeKey := getBytecodeKey(scriptPath)
|
// Cache it
|
||||||
r.bytecodeCache.Set(bytecodeKey, bytecode)
|
r.middlewareCache[path] = content
|
||||||
|
r.sourceMtimes[path] = time.Now()
|
||||||
|
|
||||||
n.err = nil
|
return content, nil
|
||||||
return 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
|
// 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.failedRoutes = make(map[string]*RouteError)
|
||||||
r.middlewareFiles = make(map[string][]string)
|
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()
|
err := r.buildRoutes()
|
||||||
|
|
||||||
@ -587,10 +692,23 @@ func (r *LuaRouter) ReportFailedRoutes() []*RouteError {
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
// ClearCache clears both the route and bytecode caches
|
// ClearCache clears all caches
|
||||||
func (r *LuaRouter) ClearCache() {
|
func (r *LuaRouter) ClearCache() {
|
||||||
r.routeCache.Reset()
|
r.routeCache.Reset()
|
||||||
r.bytecodeCache.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
|
// GetCacheStats returns statistics about the cache
|
||||||
|
@ -298,7 +298,7 @@ func BenchmarkRouteCompilation(b *testing.B) {
|
|||||||
routesDir := filepath.Join(tempDir, "routes")
|
routesDir := filepath.Join(tempDir, "routes")
|
||||||
os.MkdirAll(routesDir, 0755)
|
os.MkdirAll(routesDir, 0755)
|
||||||
|
|
||||||
for b.Loop() {
|
for i := 0; i < b.N; i++ {
|
||||||
b.StopTimer()
|
b.StopTimer()
|
||||||
os.RemoveAll(routesDir)
|
os.RemoveAll(routesDir)
|
||||||
os.MkdirAll(routesDir, 0755)
|
os.MkdirAll(routesDir, 0755)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user