diff --git a/pkg/appsrv/cache_test.go b/pkg/appsrv/cache_test.go deleted file mode 100644 index ffbe68abe5..0000000000 --- a/pkg/appsrv/cache_test.go +++ /dev/null @@ -1,24 +0,0 @@ -package appsrv - -import ( - "testing" -) - -func TestCache(t *testing.T) { - c := NewCache(1024) - c.Set("123", 123) - c.Set("456", 456) - v := c.Get("123") - if v == nil || v.(int) != 123 { - t.Error("Key 123 not found") - } - v = c.Get("456") - if v == nil || v.(int) != 456 { - t.Error("Key 456 not found") - } - c.Set("456", 789) - v = c.Get("456") - if v == nil || v.(int) != 789 { - t.Error("Key 456 not changed") - } -} diff --git a/pkg/appsrv/context.go b/pkg/appsrv/context.go index e82203721b..2df21196f1 100644 --- a/pkg/appsrv/context.go +++ b/pkg/appsrv/context.go @@ -5,6 +5,7 @@ import ( "database/sql" "yunion.io/x/onecloud/pkg/appctx" + "yunion.io/x/onecloud/pkg/util/hashcache" ) func AppContextDB(ctx context.Context) *sql.DB { @@ -15,12 +16,12 @@ func AppContextDB(ctx context.Context) *sql.DB { return val.(*sql.DB) } -func AppContextCache(ctx context.Context) *Cache { +func AppContextCache(ctx context.Context) *hashcache.Cache { val := ctx.Value(appctx.APP_CONTEXT_KEY_CACHE) if val == nil { return nil } - return val.(*Cache) + return val.(*hashcache.Cache) } func AppContextApp(ctx context.Context) *Application { diff --git a/pkg/cloudcommon/policy/policy.go b/pkg/cloudcommon/policy/policy.go index 972461802d..8dd940149a 100644 --- a/pkg/cloudcommon/policy/policy.go +++ b/pkg/cloudcommon/policy/policy.go @@ -2,6 +2,8 @@ package policy import ( "time" + "fmt" + "strings" "yunion.io/x/jsonutils" "yunion.io/x/log" @@ -13,6 +15,7 @@ import ( "yunion.io/x/onecloud/pkg/mcclient/modules" "yunion.io/x/onecloud/pkg/util/conditionparser" "yunion.io/x/onecloud/pkg/util/rbacutils" + "yunion.io/x/onecloud/pkg/util/hashcache" ) const ( @@ -135,6 +138,8 @@ type SPolicyManager struct { failedRetryInterval time.Duration refreshInterval time.Duration + + cache *hashcache.Cache // policy cache } func parseJsonPolicy(obj jsonutils.JSONObject) (string, rbacutils.SRbacPolicy, error) { @@ -214,6 +219,8 @@ func (manager *SPolicyManager) start(refreshInterval time.Duration, retryInterva Rules: rbacutils.CompactRules(defaultRules), } } + + manager.cache = hashcache.NewCache(2048, time.Second*5) manager.sync() } @@ -232,6 +239,38 @@ func (manager *SPolicyManager) sync() { } func (manager *SPolicyManager) Allow(isAdmin bool, userCred mcclient.TokenCredential, service string, resource string, action string, extra ...string) rbacutils.TRbacResult { + if manager.cache != nil { + isAdminStr := fmt.Sprintf("%v", isAdmin) + queryKeys := []string{isAdminStr, userCred.String()} + if rbacutils.WILD_MATCH == service || len(service) == 0 { + queryKeys = append(queryKeys, rbacutils.WILD_MATCH) + } + if rbacutils.WILD_MATCH == resource || len(resource) == 0 { + queryKeys = append(queryKeys, rbacutils.WILD_MATCH) + } + if rbacutils.WILD_MATCH == action || len(action) == 0 { + queryKeys = append(queryKeys, rbacutils.WILD_MATCH) + } + if len(extra) > 0 { + queryKeys = append(queryKeys, extra...) + } + key := strings.Join(queryKeys, "-") + log.Debugf("%s", key) + val := manager.cache.Get(key) + if val != nil { + log.Debugf("cache hit") + return val.(rbacutils.TRbacResult) + } + log.Debugf("cache miss!!") + result := manager.allowWithoutCache(isAdmin, userCred, service, resource, action, extra...) + manager.cache.Set(key, result) + return result + } else { + return manager.allowWithoutCache(isAdmin, userCred, service, resource, action, extra...) + } +} + +func (manager *SPolicyManager) allowWithoutCache(isAdmin bool, userCred mcclient.TokenCredential, service string, resource string, action string, extra ...string) rbacutils.TRbacResult { var policies map[string]rbacutils.SRbacPolicy if isAdmin { policies = manager.adminPolicies diff --git a/pkg/appsrv/cache.go b/pkg/util/hashcache/cache.go similarity index 64% rename from pkg/appsrv/cache.go rename to pkg/util/hashcache/cache.go index dbe97ad19b..369718cf48 100644 --- a/pkg/appsrv/cache.go +++ b/pkg/util/hashcache/cache.go @@ -1,30 +1,33 @@ -package appsrv +package hashcache import ( "crypto/md5" "crypto/sha1" "crypto/sha256" "sync" + "time" ) type cacheNode struct { - key string - value interface{} + key string + expire time.Time + value interface{} } type Cache struct { - table []cacheNode - lock *sync.Mutex - size uint32 + table []cacheNode + lock *sync.Mutex + size uint32 + defaultTtl time.Duration } -func NewCache(size uint32) *Cache { - ca := &Cache{table: make([]cacheNode, size), lock: &sync.Mutex{}, size: size} +func NewCache(size uint32, defaultTTL time.Duration) *Cache { + ca := &Cache{table: make([]cacheNode, size), lock: &sync.Mutex{}, size: size, defaultTtl: defaultTTL} return ca } const ( - HASH_ALG_MD5 int = iota + HASH_ALG_MD5 int = iota HASH_ALG_SHA1 HASH_ALG_SHA256 ) @@ -51,10 +54,14 @@ func checksum(alg int, key string) uint32 { func (c *Cache) find(key string) (bool, uint32) { var idx uint32 + now := time.Now() for _, alg := range []int{HASH_ALG_MD5, HASH_ALG_SHA1, HASH_ALG_SHA256} { idx = checksum(alg, key) % c.size if c.table[idx].key == key { - return true, idx + if c.table[idx].expire.IsZero() || c.table[idx].expire.After(now) { + return true, idx + } + break } } return false, idx @@ -74,13 +81,18 @@ func (c *Cache) AtomicGet(key string) interface{} { return c.Get(key) } -func (c *Cache) Set(key string, val interface{}) { +func (c *Cache) Set(key string, val interface{}, expire ...time.Time) { find, idx := c.find(key) - if find { - c.table[idx].value = val - } else { + if !find { c.table[idx].key = key - c.table[idx].value = val + } + c.table[idx].value = val + if len(expire) > 0 && ! expire[0].IsZero() { + c.table[idx].expire = expire[0] + } else if c.defaultTtl > time.Millisecond { + c.table[idx].expire = time.Now().Add(c.defaultTtl) + } else { + c.table[idx].expire = time.Time{} } } diff --git a/pkg/util/hashcache/cache_test.go b/pkg/util/hashcache/cache_test.go new file mode 100644 index 0000000000..3394e9ed14 --- /dev/null +++ b/pkg/util/hashcache/cache_test.go @@ -0,0 +1,49 @@ +package hashcache + +import ( + "testing" + "time" +) + +func TestCache(t *testing.T) { + c := NewCache(1024, time.Second) + c.Set("123", 123) + c.Set("456", 456) + v := c.Get("123") + if v == nil || v.(int) != 123 { + t.Error("Key 123 not found") + } + v = c.Get("456") + if v == nil || v.(int) != 456 { + t.Error("Key 456 not found") + } + c.Set("456", 789) + v = c.Get("456") + if v == nil || v.(int) != 789 { + t.Error("Key 456 not changed") + } + + time.Sleep(time.Second) + + v = c.Get("123") + if v != nil { + t.Errorf("key 123 shoud expire") + } + c.Set("123", 1234) + c.Set("456", 4567) + v = c.Get("123") + if v == nil || v.(int) != 1234 { + t.Error("Key 123 not found") + } + v = c.Get("456") + if v == nil || v.(int) != 4567 { + t.Error("Key 456 not found") + } + + time.Sleep(time.Second) + + v = c.Get("123") + if v != nil { + t.Errorf("key 123 shoud expire") + } +}