@@ -29,6 +29,7 @@ import (
2929 "github.com/yuin/gopher-lua/parse"
3030 "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
3131 "k8s.io/apimachinery/pkg/runtime"
32+ "k8s.io/apimachinery/pkg/runtime/schema"
3233 "k8s.io/klog/v2"
3334 luajson "layeh.com/gopher-json"
3435
@@ -38,13 +39,24 @@ import (
3839 lualifted "github.com/karmada-io/karmada/pkg/util/lifted/lua"
3940)
4041
42+ // ScriptCacheKey identifies a Lua interpreter script slot for cache lookups.
43+ type ScriptCacheKey struct {
44+ GVK schema.GroupVersionKind
45+ Operation configv1alpha1.InterpreterOperation
46+ DependencyIndex int
47+ }
48+
49+ type scriptCacheEntry struct {
50+ script string
51+ proto * lua.FunctionProto
52+ }
53+
4154// VM Defines a struct that implements the luaVM.
4255type VM struct {
4356 // UseOpenLibs flag to enable open libraries. Libraries are disabled by default while running, but enabled during testing to allow the use of print statements.
4457 UseOpenLibs bool
4558 Pool * fixedpool.FixedPool
46- // TODO(user): Implement a garbage collection or eviction policy for funcCache to prevent memory leaks if scripts are updated or dynamically generated.
47- funcCache sync.Map // map[string]*lua.FunctionProto
59+ funcCache sync.Map // map[ScriptCacheKey]*scriptCacheEntry
4860}
4961
5062// New creates a manager for lua VM
@@ -60,10 +72,14 @@ func New(useOpenLibs bool, poolSize int) *VM {
6072 return vm
6173}
6274
63- // loadOrCompileScript returns a cached FunctionProto for script, compiling it on first use.
64- func (vm * VM ) loadOrCompileScript (script string ) (* lua.FunctionProto , error ) {
65- if value , ok := vm .funcCache .Load (script ); ok {
66- return value .(* lua.FunctionProto ), nil
75+ // loadOrCompileScript returns a cached FunctionProto for the given key and script,
76+ // compiling it on first use or when the script content changes.
77+ func (vm * VM ) loadOrCompileScript (key ScriptCacheKey , script string ) (* lua.FunctionProto , error ) {
78+ if value , ok := vm .funcCache .Load (key ); ok {
79+ entry := value .(* scriptCacheEntry )
80+ if entry .script == script {
81+ return entry .proto , nil
82+ }
6783 }
6884
6985 chunk , err := parse .Parse (strings .NewReader (script ), "<string>" )
@@ -76,7 +92,7 @@ func (vm *VM) loadOrCompileScript(script string) (*lua.FunctionProto, error) {
7692 return nil , err
7793 }
7894
79- vm .funcCache .Store (script , proto )
95+ vm .funcCache .Store (key , & scriptCacheEntry { script : script , proto : proto } )
8096 return proto , nil
8197}
8298
@@ -96,7 +112,7 @@ func (vm *VM) NewLuaState() (*lua.LState, error) {
96112}
97113
98114// RunScript got a lua vm from pool, and execute script with given arguments.
99- func (vm * VM ) RunScript (script string , fnName string , nRets int , args ... any ) ([]lua.LValue , error ) {
115+ func (vm * VM ) RunScript (key ScriptCacheKey , script string , fnName string , nRets int , args ... any ) ([]lua.LValue , error ) {
100116 a , err := vm .Pool .Get ()
101117 if err != nil {
102118 return nil , err
@@ -110,7 +126,7 @@ func (vm *VM) RunScript(script string, fnName string, nRets int, args ...any) ([
110126 defer cancel ()
111127 l .SetContext (ctx )
112128
113- proto , err := vm .loadOrCompileScript (script )
129+ proto , err := vm .loadOrCompileScript (key , script )
114130 if err != nil {
115131 return nil , err
116132 }
@@ -158,7 +174,8 @@ func (vm *VM) RunScript(script string, fnName string, nRets int, args ...any) ([
158174
159175// GetReplicas returns the desired replicas of the object as well as the requirements of each replica by lua script.
160176func (vm * VM ) GetReplicas (obj * unstructured.Unstructured , script string ) (replica int32 , requires * workv1alpha2.ReplicaRequirements , err error ) {
161- results , err := vm .RunScript (script , "GetReplicas" , 2 , obj )
177+ key := ScriptCacheKey {GVK : obj .GroupVersionKind (), Operation : configv1alpha1 .InterpreterOperationInterpretReplica }
178+ results , err := vm .RunScript (key , script , "GetReplicas" , 2 , obj )
162179 if err != nil {
163180 return 0 , nil , err
164181 }
@@ -187,7 +204,8 @@ func (vm *VM) GetReplicas(obj *unstructured.Unstructured, script string) (replic
187204
188205// GetComponents returns the desired components of the object by executing a Lua script.
189206func (vm * VM ) GetComponents (obj * unstructured.Unstructured , script string ) ([]workv1alpha2.Component , error ) {
190- results , err := vm .RunScript (script , "GetComponents" , 1 , obj )
207+ key := ScriptCacheKey {GVK : obj .GroupVersionKind (), Operation : configv1alpha1 .InterpreterOperationInterpretComponent }
208+ results , err := vm .RunScript (key , script , "GetComponents" , 1 , obj )
191209 if err != nil {
192210 return nil , fmt .Errorf ("failed to run 'GetComponents' script: %w" , err )
193211 }
@@ -214,7 +232,8 @@ func (vm *VM) GetComponents(obj *unstructured.Unstructured, script string) ([]wo
214232
215233// ReviseReplica revises the replica of the given object by lua.
216234func (vm * VM ) ReviseReplica (object * unstructured.Unstructured , replica int64 , script string ) (* unstructured.Unstructured , error ) {
217- results , err := vm .RunScript (script , "ReviseReplica" , 1 , object , replica )
235+ key := ScriptCacheKey {GVK : object .GroupVersionKind (), Operation : configv1alpha1 .InterpreterOperationReviseReplica }
236+ results , err := vm .RunScript (key , script , "ReviseReplica" , 1 , object , replica )
218237 if err != nil {
219238 return nil , err
220239 }
@@ -287,7 +306,8 @@ func (vm *VM) setLib(l *lua.LState) error {
287306
288307// Retain returns the objects that based on the "desired" object but with values retained from the "observed" object by lua.
289308func (vm * VM ) Retain (desired * unstructured.Unstructured , observed * unstructured.Unstructured , script string ) (retained * unstructured.Unstructured , err error ) {
290- results , err := vm .RunScript (script , "Retain" , 1 , desired , observed )
309+ key := ScriptCacheKey {GVK : desired .GroupVersionKind (), Operation : configv1alpha1 .InterpreterOperationRetain }
310+ results , err := vm .RunScript (key , script , "Retain" , 1 , desired , observed )
291311 if err != nil {
292312 return nil , err
293313 }
@@ -306,7 +326,8 @@ func (vm *VM) Retain(desired *unstructured.Unstructured, observed *unstructured.
306326
307327// AggregateStatus returns the objects that based on the 'object' but with status aggregated by lua.
308328func (vm * VM ) AggregateStatus (object * unstructured.Unstructured , items []workv1alpha2.AggregatedStatusItem , script string ) (* unstructured.Unstructured , error ) {
309- results , err := vm .RunScript (script , "AggregateStatus" , 1 , object , items )
329+ key := ScriptCacheKey {GVK : object .GroupVersionKind (), Operation : configv1alpha1 .InterpreterOperationAggregateStatus }
330+ results , err := vm .RunScript (key , script , "AggregateStatus" , 1 , object , items )
310331 if err != nil {
311332 return nil , err
312333 }
@@ -325,7 +346,8 @@ func (vm *VM) AggregateStatus(object *unstructured.Unstructured, items []workv1a
325346
326347// InterpretHealth returns the health state of the object by lua.
327348func (vm * VM ) InterpretHealth (object * unstructured.Unstructured , script string ) (bool , error ) {
328- results , err := vm .RunScript (script , "InterpretHealth" , 1 , object )
349+ key := ScriptCacheKey {GVK : object .GroupVersionKind (), Operation : configv1alpha1 .InterpreterOperationInterpretHealth }
350+ results , err := vm .RunScript (key , script , "InterpretHealth" , 1 , object )
329351 if err != nil {
330352 return false , err
331353 }
@@ -340,7 +362,8 @@ func (vm *VM) InterpretHealth(object *unstructured.Unstructured, script string)
340362
341363// ReflectStatus returns the status of the object by lua.
342364func (vm * VM ) ReflectStatus (object * unstructured.Unstructured , script string ) (status * runtime.RawExtension , err error ) {
343- results , err := vm .RunScript (script , "ReflectStatus" , 1 , object )
365+ key := ScriptCacheKey {GVK : object .GroupVersionKind (), Operation : configv1alpha1 .InterpreterOperationInterpretStatus }
366+ results , err := vm .RunScript (key , script , "ReflectStatus" , 1 , object )
344367 if err != nil {
345368 return nil , err
346369 }
@@ -356,8 +379,13 @@ func (vm *VM) ReflectStatus(object *unstructured.Unstructured, script string) (s
356379}
357380
358381// GetDependencies returns the dependent resources of the given object by lua.
359- func (vm * VM ) GetDependencies (object * unstructured.Unstructured , script string ) (dependencies []configv1alpha1.DependentObjectReference , err error ) {
360- results , err := vm .RunScript (script , "GetDependencies" , 1 , object )
382+ func (vm * VM ) GetDependencies (object * unstructured.Unstructured , script string , dependencyIndex int ) (dependencies []configv1alpha1.DependentObjectReference , err error ) {
383+ key := ScriptCacheKey {
384+ GVK : object .GroupVersionKind (),
385+ Operation : configv1alpha1 .InterpreterOperationInterpretDependency ,
386+ DependencyIndex : dependencyIndex ,
387+ }
388+ results , err := vm .RunScript (key , script , "GetDependencies" , 1 , object )
361389 if err != nil {
362390 return nil , err
363391 }
0 commit comments