Use otter for site cache

This commit is contained in:
2026-05-04 18:30:44 +02:00
parent 087aa01876
commit 6b6565caee
4 changed files with 94 additions and 156 deletions
+268
View File
@@ -0,0 +1,268 @@
package cachedrepo
import (
"quay/app/models"
"quay/app/repository"
"github.com/maypok86/otter/v2"
)
type CachedSiteRepository struct {
inner repository.SiteRepository
sites *otter.Cache[string, *models.Site]
list *otter.Cache[string, []models.Site]
forwardRules *otter.Cache[string, *models.ForwardRule]
customHeaders *otter.Cache[string, *models.CustomHeaders]
headers *otter.Cache[string, *models.Header]
}
const siteListKey = "__list__"
func NewCachedSiteRepository(inner repository.SiteRepository) *CachedSiteRepository {
return &CachedSiteRepository{
inner: inner,
sites: otter.Must(&otter.Options[string, *models.Site]{
MaximumSize: 1_000,
}),
list: otter.Must(&otter.Options[string, []models.Site]{
MaximumSize: 1,
}),
forwardRules: otter.Must(&otter.Options[string, *models.ForwardRule]{
MaximumSize: 10_000,
}),
customHeaders: otter.Must(&otter.Options[string, *models.CustomHeaders]{
MaximumSize: 10_000,
}),
headers: otter.Must(&otter.Options[string, *models.Header]{
MaximumSize: 10_000,
}),
}
}
var _ repository.SiteRepository = (*CachedSiteRepository)(nil)
// Sites
func (c *CachedSiteRepository) GetSite(id string) (*models.Site, error) {
if s, ok := c.sites.GetIfPresent(id); ok {
return s, nil
}
s, err := c.inner.GetSite(id)
if err != nil {
return nil, err
}
c.sites.Set(id, s)
return s, nil
}
func (c *CachedSiteRepository) GetSiteByDomain(domain string) (*models.Site, error) {
if s, ok := c.sites.GetIfPresent(domain); ok {
return s, nil
}
s, err := c.inner.GetSiteByDomain(domain)
if err != nil {
return nil, err
}
c.sites.Set(domain, s)
return s, nil
}
func (c *CachedSiteRepository) ListSites() ([]models.Site, error) {
if sites, ok := c.list.GetIfPresent(siteListKey); ok {
return sites, nil
}
sites, err := c.inner.ListSites()
if err != nil {
return nil, err
}
c.list.Set(siteListKey, sites)
for i := range sites {
c.sites.Set(sites[i].ID, &sites[i])
}
return sites, nil
}
func (c *CachedSiteRepository) CreateSite(s *models.Site) error {
if err := c.inner.CreateSite(s); err != nil {
return err
}
c.sites.Set(s.ID, s)
c.list.Invalidate(siteListKey)
return nil
}
func (c *CachedSiteRepository) UpdateSite(s *models.Site) error {
if err := c.inner.UpdateSite(s); err != nil {
return err
}
c.sites.Set(s.ID, s)
c.list.Invalidate(siteListKey)
return nil
}
func (c *CachedSiteRepository) ToggleEnabled(id string) (bool, error) {
enabled, err := c.inner.ToggleEnabled(id)
if err != nil {
return false, err
}
c.sites.Invalidate(id)
c.list.Invalidate(siteListKey)
return enabled, nil
}
func (c *CachedSiteRepository) DeleteSite(id string) error {
if err := c.inner.DeleteSite(id); err != nil {
return err
}
c.sites.Invalidate(id)
c.list.Invalidate(siteListKey)
return nil
}
// Forward Rules
func (c *CachedSiteRepository) GetForwardRule(id string) (*models.ForwardRule, error) {
if fr, ok := c.forwardRules.GetIfPresent(id); ok {
return fr, nil
}
fr, err := c.inner.GetForwardRule(id)
if err != nil {
return nil, err
}
c.forwardRules.Set(id, fr)
return fr, nil
}
func (c *CachedSiteRepository) CreateForwardRule(siteID string, fr *models.ForwardRule) error {
if err := c.inner.CreateForwardRule(siteID, fr); err != nil {
return err
}
c.forwardRules.Set(fr.ID, fr)
c.sites.Invalidate(siteID)
c.list.Invalidate(siteListKey)
return nil
}
func (c *CachedSiteRepository) UpdateForwardRule(siteID string, fr *models.ForwardRule) error {
if err := c.inner.UpdateForwardRule(siteID, fr); err != nil {
return err
}
c.forwardRules.Set(fr.ID, fr)
c.sites.Invalidate(siteID)
c.list.Invalidate(siteListKey)
return nil
}
func (c *CachedSiteRepository) DeleteForwardRule(siteID string, id string) error {
if err := c.inner.DeleteForwardRule(siteID, id); err != nil {
return err
}
c.forwardRules.Invalidate(id)
c.sites.Invalidate(siteID)
c.list.Invalidate(siteListKey)
return nil
}
// Custom Headers
func (c *CachedSiteRepository) GetCustomHeaders(id string) (*models.CustomHeaders, error) {
if ch, ok := c.customHeaders.GetIfPresent(id); ok {
return ch, nil
}
ch, err := c.inner.GetCustomHeaders(id)
if err != nil {
return nil, err
}
c.customHeaders.Set(id, ch)
return ch, nil
}
func (c *CachedSiteRepository) CreateCustomHeaders(siteID string, ch *models.CustomHeaders) error {
if err := c.inner.CreateCustomHeaders(siteID, ch); err != nil {
return err
}
c.customHeaders.Set(ch.ID, ch)
c.sites.Invalidate(siteID)
c.list.Invalidate(siteListKey)
return nil
}
func (c *CachedSiteRepository) UpdateCustomHeaders(siteID string, ch *models.CustomHeaders) error {
if err := c.inner.UpdateCustomHeaders(siteID, ch); err != nil {
return err
}
c.customHeaders.Set(ch.ID, ch)
c.sites.Invalidate(siteID)
c.list.Invalidate(siteListKey)
return nil
}
func (c *CachedSiteRepository) DeleteCustomHeaders(siteID string, id string) error {
if err := c.inner.DeleteCustomHeaders(siteID, id); err != nil {
return err
}
c.customHeaders.Invalidate(id)
c.sites.Invalidate(siteID)
c.list.Invalidate(siteListKey)
return nil
}
// Headers
func (c *CachedSiteRepository) GetHeader(id string) (*models.Header, error) {
if h, ok := c.headers.GetIfPresent(id); ok {
return h, nil
}
h, err := c.inner.GetHeader(id)
if err != nil {
return nil, err
}
c.headers.Set(id, h)
return h, nil
}
func (c *CachedSiteRepository) CreateHeader(siteID string, customHeaderID string, h *models.Header) error {
if err := c.inner.CreateHeader(siteID, customHeaderID, h); err != nil {
return err
}
c.headers.Set(h.ID, h)
c.customHeaders.Invalidate(customHeaderID)
c.sites.Invalidate(siteID)
c.list.Invalidate(siteListKey)
return nil
}
func (c *CachedSiteRepository) UpdateHeader(siteID string, h *models.Header) error {
if err := c.inner.UpdateHeader(siteID, h); err != nil {
return err
}
c.headers.Set(h.ID, h)
c.sites.Invalidate(siteID)
c.list.Invalidate(siteListKey)
return nil
}
func (c *CachedSiteRepository) DeleteHeader(siteID string, id string) error {
if err := c.inner.DeleteHeader(siteID, id); err != nil {
return err
}
c.headers.Invalidate(id)
c.sites.Invalidate(siteID)
c.list.Invalidate(siteListKey)
return nil
}
// Invalidate explicitly evicts all cached data.
func (c *CachedSiteRepository) Invalidate() {
c.sites.InvalidateAll()
c.list.InvalidateAll()
c.forwardRules.InvalidateAll()
c.customHeaders.InvalidateAll()
c.headers.InvalidateAll()
}
// InvalidateSite evicts a single site and marks the list stale.
func (c *CachedSiteRepository) InvalidateSite(id string) {
c.sites.Invalidate(id)
c.list.Invalidate(siteListKey)
}