Skip to content

Commit 21eb716

Browse files
committed
change of key and value
1 parent 14d40a4 commit 21eb716

4 files changed

Lines changed: 108 additions & 27 deletions

File tree

pkg/resourceinterpreter/customized/declarative/configurable.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -193,9 +193,9 @@ func (c *ConfigurableInterpreter) GetDependencies(object *unstructured.Unstructu
193193
klog.V(4).Infof("Running operation %s for object: %v %s/%s with configurable interpreter.",
194194
configv1alpha1.InterpreterOperationInterpretDependency, object.GroupVersionKind(), object.GetNamespace(), object.GetName())
195195
refs := sets.New[configv1alpha1.DependentObjectReference]()
196-
for _, luaScript := range scripts {
196+
for i, luaScript := range scripts {
197197
var references []configv1alpha1.DependentObjectReference
198-
references, err = c.luaVM.GetDependencies(object, luaScript)
198+
references, err = c.luaVM.GetDependencies(object, luaScript, i)
199199
if err != nil {
200200
klog.Errorf("Failed to get DependentObjectReferences from object: %v %s/%s, error: %v",
201201
object.GroupVersionKind(), object.GetNamespace(), object.GetName(), err)

pkg/resourceinterpreter/customized/declarative/luavm/lua.go

Lines changed: 46 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -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.
4255
type 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.
160176
func (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.
189206
func (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.
216234
func (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.
289308
func (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.
308328
func (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.
327348
func (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.
342364
func (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
}

pkg/resourceinterpreter/customized/declarative/luavm/lua_test.go

Lines changed: 58 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -190,13 +190,17 @@ end`,
190190
func TestLoadOrCompileScriptCaching(t *testing.T) {
191191
vm := New(false, 1)
192192
script := `function TestFunc(obj) return 42 end`
193+
key := ScriptCacheKey{
194+
GVK: schema.GroupVersionKind{Group: "test", Version: "v1", Kind: "Test"},
195+
Operation: configv1alpha1.InterpreterOperationInterpretHealth,
196+
}
193197

194-
proto1, err := vm.loadOrCompileScript(script)
198+
proto1, err := vm.loadOrCompileScript(key, script)
195199
if err != nil {
196200
t.Fatal(err)
197201
}
198202

199-
proto2, err := vm.loadOrCompileScript(script)
203+
proto2, err := vm.loadOrCompileScript(key, script)
200204
if err != nil {
201205
t.Fatal(err)
202206
}
@@ -215,11 +219,60 @@ func TestLoadOrCompileScriptCaching(t *testing.T) {
215219
}
216220
}
217221

222+
func TestLoadOrCompileScriptRecompileOnChange(t *testing.T) {
223+
vm := New(false, 1)
224+
key := ScriptCacheKey{
225+
GVK: schema.GroupVersionKind{Group: "test", Version: "v1", Kind: "Test"},
226+
Operation: configv1alpha1.InterpreterOperationInterpretHealth,
227+
}
228+
scriptV1 := `function TestFunc(obj) return 42 end`
229+
scriptV2 := `function TestFunc(obj) return 43 end`
230+
231+
proto1, err := vm.loadOrCompileScript(key, scriptV1)
232+
if err != nil {
233+
t.Fatal(err)
234+
}
235+
236+
proto2, err := vm.loadOrCompileScript(key, scriptV2)
237+
if err != nil {
238+
t.Fatal(err)
239+
}
240+
241+
if proto1 == proto2 {
242+
t.Fatalf("expected a new proto after script change")
243+
}
244+
245+
entry, ok := vm.funcCache.Load(key)
246+
if !ok {
247+
t.Fatal("expected cache entry to exist")
248+
}
249+
cached := entry.(*scriptCacheEntry)
250+
if cached.script != scriptV2 {
251+
t.Fatalf("expected cached script to be updated, got %q", cached.script)
252+
}
253+
if cached.proto != proto2 {
254+
t.Fatalf("expected cached proto to match latest compile")
255+
}
256+
257+
count := 0
258+
vm.funcCache.Range(func(_, _ any) bool {
259+
count++
260+
return true
261+
})
262+
if count != 1 {
263+
t.Fatalf("expected 1 entry in funcCache after script update, got %d", count)
264+
}
265+
}
266+
218267
func TestRunScriptCaching(t *testing.T) {
219268
vm := New(false, 1)
220269
script := `function TestFunc() return 42 end`
270+
key := ScriptCacheKey{
271+
GVK: schema.GroupVersionKind{Group: "test", Version: "v1", Kind: "Test"},
272+
Operation: configv1alpha1.InterpreterOperationInterpretHealth,
273+
}
221274

222-
results1, err := vm.RunScript(script, "TestFunc", 1)
275+
results1, err := vm.RunScript(key, script, "TestFunc", 1)
223276
if err != nil {
224277
t.Fatal(err)
225278
}
@@ -234,7 +287,7 @@ func TestRunScriptCaching(t *testing.T) {
234287
t.Fatalf("expected 42, got %v", value1)
235288
}
236289

237-
results2, err := vm.RunScript(script, "TestFunc", 1)
290+
results2, err := vm.RunScript(key, script, "TestFunc", 1)
238291
if err != nil {
239292
t.Fatal(err)
240293
}
@@ -901,7 +954,7 @@ func TestGetDeployPodDependencies(t *testing.T) {
901954
vm := New(false, 1)
902955

903956
for _, tt := range tests {
904-
res, err := vm.GetDependencies(tt.curObj, tt.luaScript)
957+
res, err := vm.GetDependencies(tt.curObj, tt.luaScript, 0)
905958
if err != nil {
906959
t.Errorf("GetDependencies err %v", err)
907960
}

pkg/resourceinterpreter/default/thirdparty/thirdparty.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -179,9 +179,9 @@ func (p *ConfigurableInterpreter) GetDependencies(object *unstructured.Unstructu
179179
klog.V(4).Infof("Running operation %s for object: %v %s/%s with thirdparty configurable interpreter.",
180180
configv1alpha1.InterpreterOperationInterpretDependency, object.GroupVersionKind(), object.GetNamespace(), object.GetName())
181181
refs := sets.New[configv1alpha1.DependentObjectReference]()
182-
for _, luaScript := range scripts {
182+
for i, luaScript := range scripts {
183183
var references []configv1alpha1.DependentObjectReference
184-
references, err = p.luaVM.GetDependencies(object, luaScript)
184+
references, err = p.luaVM.GetDependencies(object, luaScript, i)
185185
if err != nil {
186186
klog.Errorf("Failed to get DependentObjectReferences from object: %v %s/%s, error: %v",
187187
object.GroupVersionKind(), object.GetNamespace(), object.GetName(), err)

0 commit comments

Comments
 (0)