You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@casbin.apache.org by hs...@apache.org on 2026/04/24 13:53:11 UTC
(casbin) branch master updated: fix: add gFunctionCache boolean option to enforcer
This is an automated email from the ASF dual-hosted git repository.
hsluoyz pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/casbin.git
The following commit(s) were added to refs/heads/master by this push:
new 26288f9 fix: add gFunctionCache boolean option to enforcer
26288f9 is described below
commit 26288f98cc0a1b9418f55ca455776a2d63a1e499
Author: Yang Luo <hs...@gmail.com>
AuthorDate: Fri Apr 24 21:52:57 2026 +0800
fix: add gFunctionCache boolean option to enforcer
---
enforcer.go | 12 +++++++++-
management_api.go | 2 +-
util/builtin_operators.go | 61 ++++++++++++++++++++++++++++++-----------------
3 files changed, 51 insertions(+), 24 deletions(-)
diff --git a/enforcer.go b/enforcer.go
index a6bf174..c78350c 100644
--- a/enforcer.go
+++ b/enforcer.go
@@ -56,6 +56,7 @@ type Enforcer struct {
autoNotifyWatcher bool
autoNotifyDispatcher bool
acceptJsonRequest bool
+ gFunctionCache bool
aiConfig AIConfig
}
@@ -193,6 +194,7 @@ func (e *Enforcer) initialize() {
e.autoBuildRoleLinks = true
e.autoNotifyWatcher = true
e.autoNotifyDispatcher = true
+ e.gFunctionCache = true
e.initRmMap()
// Initialize detectors with default detector if not already set
@@ -637,6 +639,14 @@ func (e *Enforcer) EnableAcceptJsonRequest(acceptJsonRequest bool) {
e.acceptJsonRequest = acceptJsonRequest
}
+// EnableGFunctionCache controls whether to cache g() function results.
+// Disable this when inputs are high-cardinality (e.g. UUIDs or dynamic paths)
+// to prevent unbounded memory growth.
+func (e *Enforcer) EnableGFunctionCache(enabled bool) {
+ e.gFunctionCache = enabled
+ e.invalidateMatcherMap()
+}
+
// BuildRoleLinks manually rebuild the role inheritance relations.
func (e *Enforcer) BuildRoleLinks() error {
e.invalidateMatcherMap()
@@ -704,7 +714,7 @@ func (e *Enforcer) enforce(matcher string, explains *[]string, rvals ...interfac
// or a conditional role definition (ast.CondRM != nil)
// ast.RM and ast.CondRM shouldn't be nil at the same time
if ast.RM != nil {
- functions[key] = util.GenerateGFunction(ast.RM)
+ functions[key] = util.GenerateGFunction(ast.RM, e.gFunctionCache)
}
if ast.CondRM != nil {
functions[key] = util.GenerateConditionalGFunction(ast.CondRM)
diff --git a/management_api.go b/management_api.go
index 7a8f768..d18536c 100644
--- a/management_api.go
+++ b/management_api.go
@@ -145,7 +145,7 @@ func (e *Enforcer) GetFilteredNamedPolicyWithMatcher(ptype string, matcher strin
// or a conditional role definition (ast.CondRM != nil)
// ast.RM and ast.CondRM shouldn't be nil at the same time
if ast.RM != nil {
- functions[key] = util.GenerateGFunction(ast.RM)
+ functions[key] = util.GenerateGFunction(ast.RM, e.gFunctionCache)
}
if ast.CondRM != nil {
functions[key] = util.GenerateConditionalGFunction(ast.CondRM)
diff --git a/util/builtin_operators.go b/util/builtin_operators.go
index 37d9cb5..066a0bd 100644
--- a/util/builtin_operators.go
+++ b/util/builtin_operators.go
@@ -402,34 +402,53 @@ func GlobMatchFunc(args ...interface{}) (interface{}, error) {
}
// GenerateGFunction is the factory method of the g(_, _[, _]) function.
-func GenerateGFunction(rm rbac.RoleManager) govaluate.ExpressionFunction {
+// If useCache is true, results are memoized per unique argument combination for performance.
+// Set useCache to false when inputs are high-cardinality (e.g. UUIDs) to avoid unbounded memory growth.
+func GenerateGFunction(rm rbac.RoleManager, useCache bool) govaluate.ExpressionFunction {
memorized := sync.Map{}
return func(args ...interface{}) (interface{}, error) {
// Like all our other govaluate functions, all args are strings.
- // Allocate and generate a cache key from the arguments...
- total := len(args)
- for _, a := range args {
- aStr := a.(string)
- total += len(aStr)
- }
- builder := strings.Builder{}
- builder.Grow(total)
- for _, arg := range args {
- builder.WriteByte(0)
- builder.WriteString(arg.(string))
- }
- key := builder.String()
-
- // ...and see if we've already calculated this.
- v, found := memorized.Load(key)
- if found {
+ if useCache {
+ // Allocate and generate a cache key from the arguments...
+ total := len(args)
+ for _, a := range args {
+ aStr := a.(string)
+ total += len(aStr)
+ }
+ builder := strings.Builder{}
+ builder.Grow(total)
+ for _, arg := range args {
+ builder.WriteByte(0)
+ builder.WriteString(arg.(string))
+ }
+ key := builder.String()
+
+ // ...and see if we've already calculated this.
+ if v, found := memorized.Load(key); found {
+ return v, nil
+ }
+
+ // If not, do the calculation.
+ // There are guaranteed to be exactly 2 or 3 arguments.
+ name1, name2 := args[0].(string), args[1].(string)
+ var v interface{}
+ if rm == nil {
+ v = name1 == name2
+ } else if len(args) == 2 {
+ v, _ = rm.HasLink(name1, name2)
+ } else {
+ domain := args[2].(string)
+ v, _ = rm.HasLink(name1, name2, domain)
+ }
+
+ memorized.Store(key, v)
return v, nil
}
- // If not, do the calculation.
- // There are guaranteed to be exactly 2 or 3 arguments.
+ // No caching path.
name1, name2 := args[0].(string), args[1].(string)
+ var v interface{}
if rm == nil {
v = name1 == name2
} else if len(args) == 2 {
@@ -438,8 +457,6 @@ func GenerateGFunction(rm rbac.RoleManager) govaluate.ExpressionFunction {
domain := args[2].(string)
v, _ = rm.HasLink(name1, name2, domain)
}
-
- memorized.Store(key, v)
return v, nil
}
}
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@casbin.apache.org
For additional commands, e-mail: commits-help@casbin.apache.org