-
Notifications
You must be signed in to change notification settings - Fork 68
Add inmemory cache for env #636
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 4 commits
Commits
Show all changes
6 commits
Select commit
Hold shift + click to select a range
e9bc156
add testing in-memory cache
zhuoyuan-liu 17b0605
Add env cache to more functions
zhuoyuan-liu f63c39a
Merge remote-tracking branch 'upstream/main' into add-inmemory-cache
zhuoyuan-liu 10d667a
Rename to EnvManager
zhuoyuan-liu 7f67c5f
Add prometheus metrics to the cache
zhuoyuan-liu 630c06c
add test to the in memory cache
zhuoyuan-liu File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,158 @@ | ||
| package cache | ||
|
|
||
| import ( | ||
| "context" | ||
| "sync" | ||
| "time" | ||
| ) | ||
|
|
||
| // Item represents a cached item with expiration | ||
| type Item[T any] struct { | ||
| Value T | ||
| Expiration int64 | ||
| } | ||
|
|
||
| // Cache interface defines methods that any cache implementation must provide | ||
| type Cache[T any] interface { | ||
| // Get retrieves an item from the cache by key | ||
| Get(ctx context.Context, key string) (T, bool) | ||
|
|
||
| // Set adds or updates an item in the cache with expiration | ||
| Set(ctx context.Context, key string, value T, duration time.Duration) | ||
|
|
||
| // Delete removes an item from the cache | ||
| Delete(ctx context.Context, key string) | ||
|
|
||
| // Clear removes all items from the cache | ||
| Clear(ctx context.Context) | ||
|
|
||
| // ItemCount returns the number of items in the cache | ||
| ItemCount() int | ||
| } | ||
|
|
||
| // MemoryCacheOption is a function that configures a MemoryCache | ||
| type MemoryCacheOption[T any] func(*MemoryCache[T]) | ||
|
|
||
| // WithCleanupInterval sets the interval for cleaning expired items | ||
| func WithCleanupInterval[T any](interval time.Duration) MemoryCacheOption[T] { | ||
| return func(mc *MemoryCache[T]) { | ||
| mc.cleanupInterval = interval | ||
| } | ||
| } | ||
|
|
||
| // MemoryCache provides an in-memory implementation of the Cache interface | ||
| type MemoryCache[T any] struct { | ||
| items map[string]Item[T] | ||
| mutex sync.RWMutex | ||
| cleanupInterval time.Duration | ||
| stopCleanup chan struct{} | ||
| } | ||
|
|
||
| // NewMemoryCache creates a new in-memory cache with the provided options | ||
| func NewMemoryCache[T any](opts ...MemoryCacheOption[T]) *MemoryCache[T] { | ||
| cache := &MemoryCache[T]{ | ||
| items: make(map[string]Item[T]), | ||
| cleanupInterval: 5 * time.Minute, | ||
| stopCleanup: make(chan struct{}), | ||
| } | ||
|
|
||
| // Apply all provided options | ||
| for _, opt := range opts { | ||
| opt(cache) | ||
| } | ||
|
|
||
| // Start cleanup routine to remove expired items | ||
| go cache.cleanupRoutine() | ||
|
|
||
| return cache | ||
| } | ||
|
|
||
| // Get retrieves an item from the cache | ||
| func (c *MemoryCache[T]) Get(ctx context.Context, key string) (T, bool) { | ||
| c.mutex.RLock() | ||
| defer c.mutex.RUnlock() | ||
|
|
||
| item, found := c.items[key] | ||
| if !found { | ||
| var zero T | ||
| return zero, false | ||
| } | ||
|
|
||
| // Check if item has expired | ||
| if item.Expiration > 0 && item.Expiration < time.Now().UnixNano() { | ||
| var zero T | ||
| return zero, false | ||
| } | ||
|
|
||
| return item.Value, true | ||
| } | ||
|
|
||
| // Set adds an item to the cache with expiration | ||
| func (c *MemoryCache[T]) Set(ctx context.Context, key string, value T, duration time.Duration) { | ||
| var expiration int64 | ||
|
|
||
| if duration > 0 { | ||
| expiration = time.Now().Add(duration).UnixNano() | ||
| } | ||
|
|
||
| c.mutex.Lock() | ||
| defer c.mutex.Unlock() | ||
|
|
||
| c.items[key] = Item[T]{ | ||
| Value: value, | ||
| Expiration: expiration, | ||
| } | ||
| } | ||
|
|
||
| // Delete removes an item from the cache | ||
| func (c *MemoryCache[T]) Delete(ctx context.Context, key string) { | ||
| c.mutex.Lock() | ||
| defer c.mutex.Unlock() | ||
|
|
||
| delete(c.items, key) | ||
| } | ||
|
|
||
| // Clear removes all items from the cache | ||
| func (c *MemoryCache[T]) Clear(ctx context.Context) { | ||
| c.mutex.Lock() | ||
| defer c.mutex.Unlock() | ||
|
|
||
| c.items = make(map[string]Item[T]) | ||
| } | ||
|
|
||
| // ItemCount returns the number of items in the cache | ||
| func (c *MemoryCache[T]) ItemCount() int { | ||
| c.mutex.RLock() | ||
| defer c.mutex.RUnlock() | ||
|
|
||
| return len(c.items) | ||
| } | ||
|
|
||
| // cleanupRoutine periodically cleans up expired items | ||
| func (c *MemoryCache[T]) cleanupRoutine() { | ||
| ticker := time.NewTicker(c.cleanupInterval) | ||
| defer ticker.Stop() | ||
|
|
||
| for { | ||
| select { | ||
| case <-ticker.C: | ||
| c.deleteExpired() | ||
| case <-c.stopCleanup: | ||
| return | ||
| } | ||
| } | ||
| } | ||
|
|
||
| // deleteExpired removes expired items from the cache | ||
| func (c *MemoryCache[T]) deleteExpired() { | ||
| now := time.Now().UnixNano() | ||
|
|
||
| c.mutex.Lock() | ||
| defer c.mutex.Unlock() | ||
|
|
||
| for k, v := range c.items { | ||
| if v.Expiration > 0 && v.Expiration < now { | ||
| delete(c.items, k) | ||
| } | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,63 @@ | ||
| package environments | ||
|
|
||
| import ( | ||
| "context" | ||
| "time" | ||
|
|
||
| "github.com/jmpsec/osctrl/pkg/cache" | ||
| ) | ||
|
|
||
| // EnvCache provides cached access to TLS environments | ||
| type EnvCache struct { | ||
| // The cache itself, storing Environment objects | ||
| cache *cache.MemoryCache[TLSEnvironment] | ||
|
|
||
| // Reference to the environment manager for cache misses | ||
| envs EnvManager | ||
| } | ||
|
|
||
| // NewEnvCache creates a new environment cache | ||
| func NewEnvCache(envs EnvManager) *EnvCache { | ||
| // Create a new cache with a 10-minute cleanup interval | ||
| envCache := cache.NewMemoryCache( | ||
| cache.WithCleanupInterval[TLSEnvironment](2 * time.Hour), | ||
| ) | ||
|
|
||
| return &EnvCache{ | ||
| cache: envCache, | ||
| envs: envs, | ||
| } | ||
| } | ||
|
|
||
| // GetByUUID retrieves an environment by UUID, using cache when available | ||
| func (ec *EnvCache) GetByUUID(ctx context.Context, uuid string) (TLSEnvironment, error) { | ||
| // Try to get from cache first | ||
| if env, found := ec.cache.Get(ctx, uuid); found { | ||
| return env, nil | ||
| } | ||
|
|
||
| // Not in cache, fetch from database | ||
| env, err := ec.envs.GetByUUID(uuid) | ||
| if err != nil { | ||
| return TLSEnvironment{}, err | ||
| } | ||
|
|
||
| ec.cache.Set(ctx, uuid, env, 2*time.Hour) | ||
|
|
||
| return env, nil | ||
| } | ||
|
|
||
| // InvalidateEnv removes a specific environment from the cache | ||
| func (ec *EnvCache) InvalidateEnv(ctx context.Context, uuid string) { | ||
| ec.cache.Delete(ctx, uuid) | ||
| } | ||
|
|
||
| // InvalidateAll clears the entire cache | ||
| func (ec *EnvCache) InvalidateAll(ctx context.Context) { | ||
| ec.cache.Clear(ctx) | ||
| } | ||
|
|
||
| // UpdateEnvInCache updates an environment in the cache | ||
| func (ec *EnvCache) UpdateEnvInCache(ctx context.Context, env TLSEnvironment) { | ||
| ec.cache.Set(ctx, env.UUID, env, 2*time.Hour) | ||
| } | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This value is currently hardcoded, and I think we need a more robust approach to configuration management. Otherwise, the number of command-line flags will quickly become unmanageable and difficult to maintain.