harness-drone/service/org/cache.go

99 lines
2.3 KiB
Go

// Copyright 2019 Drone IO, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package orgs
import (
"context"
"fmt"
"sync"
"time"
"github.com/drone/drone/core"
lru "github.com/hashicorp/golang-lru"
)
// content key pattern used in the cache, comprised of the
// organization name and username.
const contentKey = "%s/%s"
// NewCache wraps the service with a simple cache to store
// organization membership.
func NewCache(base core.OrganizationService, size int, ttl time.Duration) core.OrganizationService {
// simple cache prevents the same yaml file from being
// requested multiple times in a short period.
cache, _ := lru.New(25)
return &cacher{
cache: cache,
base: base,
size: size,
ttl: ttl,
}
}
type cacher struct {
mu sync.Mutex
base core.OrganizationService
size int
ttl time.Duration
cache *lru.Cache
}
type item struct {
expiry time.Time
member bool
admin bool
}
func (c *cacher) List(ctx context.Context, user *core.User) ([]*core.Organization, error) {
return c.base.List(ctx, user)
}
func (c *cacher) Membership(ctx context.Context, user *core.User, name string) (bool, bool, error) {
key := fmt.Sprintf(contentKey, user.Login, name)
now := time.Now()
// get the membership details from the cache.
cached, ok := c.cache.Get(key)
if ok {
item := cached.(*item)
// if the item is expired it can be ejected
// from the cache, else if not expired we return
// the cached results.
if now.After(item.expiry) {
c.cache.Remove(cached)
} else {
return item.member, item.admin, nil
}
}
// get up-to-date membership details due to a cache
// miss or expired cache item.
member, admin, err := c.base.Membership(ctx, user, name)
if err != nil {
return false, false, err
}
c.cache.Add(key, &item{
expiry: now.Add(c.ttl),
member: member,
admin: admin,
})
return member, admin, nil
}