drone/git/api/last_commit_cache.go

133 lines
3.1 KiB
Go

// Copyright 2023 Harness, 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 api
import (
"context"
"crypto/sha256"
"encoding/gob"
"encoding/hex"
"fmt"
"strings"
"time"
"github.com/harness/gitness/cache"
"github.com/harness/gitness/errors"
"github.com/harness/gitness/git/sha"
"github.com/go-redis/redis/v8"
"github.com/rs/zerolog/log"
)
func NewInMemoryLastCommitCache(
cacheDuration time.Duration,
) cache.Cache[CommitEntryKey, *Commit] {
return cache.New[CommitEntryKey, *Commit](
commitEntryGetter{},
cacheDuration)
}
func logCacheErrFn(ctx context.Context, err error) {
log.Ctx(ctx).Warn().Msgf("failed to use cache: %s", err.Error())
}
func NewRedisLastCommitCache(
redisClient redis.UniversalClient,
cacheDuration time.Duration,
) (cache.Cache[CommitEntryKey, *Commit], error) {
if redisClient == nil {
return nil, errors.New("unable to create redis based LastCommitCache as redis client is nil")
}
return cache.NewRedis[CommitEntryKey, *Commit](
redisClient,
commitEntryGetter{},
func(key CommitEntryKey) string {
h := sha256.New()
h.Write([]byte(key))
return "last_commit:" + hex.EncodeToString(h.Sum(nil))
},
commitValueCodec{},
cacheDuration,
logCacheErrFn,
), nil
}
func NoLastCommitCache() cache.Cache[CommitEntryKey, *Commit] {
return cache.NewNoCache[CommitEntryKey, *Commit](commitEntryGetter{})
}
type CommitEntryKey string
const separatorZero = "\x00"
func makeCommitEntryKey(
repoPath string,
commitSHA sha.SHA,
path string,
) CommitEntryKey {
return CommitEntryKey(repoPath + separatorZero + commitSHA.String() + separatorZero + path)
}
func (c CommitEntryKey) Split() (
repoPath string,
commitSHA string,
path string,
) {
parts := strings.Split(string(c), separatorZero)
if len(parts) != 3 {
return
}
repoPath = parts[0]
commitSHA = parts[1]
path = parts[2]
return
}
type commitValueCodec struct{}
func (c commitValueCodec) Encode(v *Commit) string {
buffer := &strings.Builder{}
_ = gob.NewEncoder(buffer).Encode(v)
return buffer.String()
}
func (c commitValueCodec) Decode(s string) (*Commit, error) {
commit := &Commit{}
if err := gob.NewDecoder(strings.NewReader(s)).Decode(commit); err != nil {
return nil, fmt.Errorf("failed to unpack commit entry value: %w", err)
}
return commit, nil
}
type commitEntryGetter struct{}
// Find implements the cache.Getter interface.
func (c commitEntryGetter) Find(
ctx context.Context,
key CommitEntryKey,
) (*Commit, error) {
repoPath, commitSHA, path := key.Split()
if path == "" {
path = "."
}
return getCommit(ctx, repoPath, commitSHA, path)
}