package cache import ( "context" "strings" "time" ) type l1Cache struct { inner Cache local Cache ttl time.Duration } func newL1Cache(inner Cache, ttl time.Duration) Cache { if ttl <= 0 { return inner } return &l1Cache{inner: inner, local: newMemoryCache(), ttl: ttl} } func (c *l1Cache) Get(ctx context.Context, key string) ([]byte, error) { if isCacheControlKey(key) { return c.inner.Get(ctx, key) } if value, err := c.local.Get(ctx, key); err == nil { recordCacheL1("hit") return value, nil } recordCacheL1("miss") value, err := c.inner.Get(ctx, key) if err != nil { return nil, err } _ = c.local.Set(ctx, key, value, c.ttl) return value, nil } func (c *l1Cache) GetMulti(ctx context.Context, keys []string) (map[string][]byte, error) { if len(keys) == 0 { return map[string][]byte{}, nil } result := make(map[string][]byte, len(keys)) miss := make([]string, 0, len(keys)) for _, key := range keys { value, err := c.local.Get(ctx, key) if err == nil { recordCacheL1("hit") result[key] = value continue } recordCacheL1("miss") miss = append(miss, key) } if len(miss) == 0 { return result, nil } values, err := c.inner.GetMulti(ctx, miss) if err != nil { return nil, err } for key, value := range values { result[key] = value _ = c.local.Set(ctx, key, value, c.ttl) } return result, nil } func (c *l1Cache) Set(ctx context.Context, key string, value []byte, ttl time.Duration) error { if isCacheControlKey(key) { return c.inner.Set(ctx, key, value, ttl) } if err := c.inner.Set(ctx, key, value, ttl); err != nil { return err } return c.local.Set(ctx, key, value, c.l1TTL(ttl)) } func (c *l1Cache) SetMulti(ctx context.Context, items []Item) error { if err := c.inner.SetMulti(ctx, items); err != nil { return err } for _, item := range items { _ = c.local.Set(ctx, item.Key, item.Value, c.l1TTL(item.TTL)) } return nil } func (c *l1Cache) Delete(ctx context.Context, key string) error { _ = c.local.Delete(ctx, key) return c.inner.Delete(ctx, key) } func (c *l1Cache) DeleteMany(ctx context.Context, keys []string) error { _ = c.local.DeleteMany(ctx, keys) return c.inner.DeleteMany(ctx, keys) } func (c *l1Cache) DeletePrefix(ctx context.Context, prefix string) error { _ = c.local.DeletePrefix(ctx, prefix) return c.inner.DeletePrefix(ctx, prefix) } func (c *l1Cache) l1TTL(ttl time.Duration) time.Duration { if ttl > 0 && ttl < c.ttl { return ttl } return c.ttl } func isCacheControlKey(key string) bool { key = strings.TrimSpace(key) return strings.HasPrefix(key, "cache:version:") || strings.Contains(key, ":cache:version:") }