Merge pull request #710 in YUNIONIO/onecloud from ~QIUJIAN/onecloud:hotfix/qj-policy-cache to release/2.4.0

* commit 'c33fb7e283fcde9a17c232110befc9ce1ed877ac':
  updates
  minor fixes
  增加policy缓存,降低日志量和计算量
This commit is contained in:
邱剑
2018-12-11 11:00:05 +08:00
5 changed files with 127 additions and 40 deletions
-24
View File
@@ -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")
}
}
+3 -2
View File
@@ -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 {
+49
View File
@@ -1,6 +1,9 @@
package policy
import (
"fmt"
"sort"
"strings"
"time"
"yunion.io/x/jsonutils"
@@ -12,6 +15,7 @@ import (
"yunion.io/x/onecloud/pkg/mcclient/auth"
"yunion.io/x/onecloud/pkg/mcclient/modules"
"yunion.io/x/onecloud/pkg/util/conditionparser"
"yunion.io/x/onecloud/pkg/util/hashcache"
"yunion.io/x/onecloud/pkg/util/rbacutils"
)
@@ -147,6 +151,8 @@ type SPolicyManager struct {
failedRetryInterval time.Duration
refreshInterval time.Duration
cache *hashcache.Cache // policy cache
}
func parseJsonPolicy(obj jsonutils.JSONObject) (string, rbacutils.SRbacPolicy, error) {
@@ -226,6 +232,8 @@ func (manager *SPolicyManager) start(refreshInterval time.Duration, retryInterva
Rules: rbacutils.CompactRules(defaultRules),
}
}
manager.cache = hashcache.NewCache(2048, manager.refreshInterval/2)
manager.sync()
}
@@ -243,7 +251,48 @@ func (manager *SPolicyManager) sync() {
time.AfterFunc(manager.refreshInterval, manager.sync)
}
func queryKey(isAdmin bool, userCred mcclient.TokenCredential, service string, resource string, action string, extra ...string) string {
queryKeys := []string{fmt.Sprintf("%v", isAdmin)}
queryKeys = append(queryKeys, userCred.GetProjectId(), userCred.GetDomainId(), userCred.GetUserId())
roles := userCred.GetRoles()
if len(roles) > 0 {
sort.Strings(roles)
}
queryKeys = append(queryKeys, strings.Join(roles, ":"))
if rbacutils.WILD_MATCH == service || len(service) == 0 {
service = rbacutils.WILD_MATCH
}
queryKeys = append(queryKeys, service)
if rbacutils.WILD_MATCH == resource || len(resource) == 0 {
resource = rbacutils.WILD_MATCH
}
queryKeys = append(queryKeys, resource)
if rbacutils.WILD_MATCH == action || len(action) == 0 {
action = rbacutils.WILD_MATCH
}
queryKeys = append(queryKeys, action)
if len(extra) > 0 {
queryKeys = append(queryKeys, extra...)
}
return strings.Join(queryKeys, "-")
}
func (manager *SPolicyManager) Allow(isAdmin bool, userCred mcclient.TokenCredential, service string, resource string, action string, extra ...string) rbacutils.TRbacResult {
if manager.cache != nil {
key := queryKey(isAdmin, userCred, service, resource, action, extra...)
val := manager.cache.Get(key)
if val != nil {
return val.(rbacutils.TRbacResult)
}
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
@@ -1,25 +1,28 @@
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
}
@@ -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{}
}
}
+49
View File
@@ -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")
}
}