Select Git revision
caching_middleware.go

Semyon Krestyaninov authored and
Pavel Antonov
committed
caching_middleware.go 6.67 KiB
package middleware
import (
"context"
"strings"
"git.perx.ru/perxis/perxis-go/pkg/cache"
envService "git.perx.ru/perxis/perxis-go/pkg/environments"
"git.perx.ru/perxis/perxis-go/pkg/errors"
service "git.perx.ru/perxis/perxis-go/pkg/items"
"git.perx.ru/perxis/perxis-go/pkg/locales"
)
func makeKey(ss ...string) string {
return strings.Join(ss, "-")
}
func CachingMiddleware(cache, cachePublished cache.Cache, envs envService.Environments) Middleware {
return func(next service.Items) service.Items {
return &cachingMiddleware{
cache: cache,
cachePublished: cachePublished,
Items: next,
envs: envs,
}
}
}
type cachingMiddleware struct {
cache cache.Cache
cachePublished cache.Cache
envs envService.Environments
service.Items
}
func (m cachingMiddleware) Get(ctx context.Context, spaceId, envId, collectionId, id string, options ...*service.GetOptions) (itm *service.Item, err error) {
opts := service.MergeGetOptions(options...)
localeID := opts.LocaleID
if localeID == "" {
localeID = locales.DefaultID
}
// Значение из кэша можно достать только в случае, когда не запрашиваются переводы. Для
// списка переводов (`item.Translations`) кэш не предусмотрен: предполагается, что это
// нечастый запрос и содержание кэша с разными переводами себя не оправдывает
var value = make(map[string]*service.Item)
if len(opts.TranslationsIDs) == 0 {
val, e := m.cache.Get(makeKey(spaceId, envId, collectionId, id))
if e == nil {
value = val.(map[string]*service.Item)
if i, ok := value[localeID]; ok {
return i.Clone(), nil
}
}
}
itm, err = m.Items.Get(ctx, spaceId, envId, collectionId, id, options...)
if err == nil {
env, err := m.envs.Get(ctx, itm.SpaceID, itm.EnvID)
if err != nil {
return nil, errors.Wrapf(err, "failed to get environment %q", itm.EnvID)
}
// Сохраняем в кэш запись без Translations, поскольку значение из кэша также
// возвращается только если переводы не запрашиваются
itmCached := *itm
itmCached.Translations = nil
value[localeID] = &itmCached
for _, envID := range append(env.Aliases, env.ID) {
_ = m.cache.Set(makeKey(itm.SpaceID, envID, itm.CollectionID, itm.ID), value)
}
return itm.Clone(), err
}
return nil, err
}
func (m cachingMiddleware) invalidateCache(ctx context.Context, item *service.Item) (err error) {
env, err := m.envs.Get(ctx, item.SpaceID, item.EnvID)
if err != nil {
return errors.Wrapf(err, "failed to get environment %q", item.EnvID)
}
for _, a := range append(env.Aliases, env.ID) {
key := makeKey(item.SpaceID, a, item.CollectionID, item.ID)
_ = m.cache.Remove(key)
_ = m.cachePublished.Remove(key)
}
return nil
}
// Update вызывает удаление всех сохраненных в кэше значений для записи:
// - Каждую из локализаций записи
// - С ключами, составленных из разных алиасов окружения
func (m cachingMiddleware) Update(ctx context.Context, item *service.Item, options ...*service.UpdateOptions) (err error) {
err = m.Items.Update(ctx, item, options...)
if err != nil {
return err
}
return m.invalidateCache(ctx, item)
}
func (m cachingMiddleware) Delete(ctx context.Context, del *service.Item, options ...*service.DeleteOptions) (err error) {
err = m.Items.Delete(ctx, del, options...)
if err != nil {
return err
}
return m.invalidateCache(ctx, del)
}
func (m cachingMiddleware) Publish(ctx context.Context, item *service.Item, options ...*service.PublishOptions) (err error) {
err = m.Items.Publish(ctx, item, options...)
if err != nil {
return err
}
return m.invalidateCache(ctx, item)
}
func (m cachingMiddleware) Unpublish(ctx context.Context, item *service.Item, options ...*service.UnpublishOptions) (err error) {
err = m.Items.Unpublish(ctx, item, options...)
if err != nil {
return err
}
return m.invalidateCache(ctx, item)
}
func (m cachingMiddleware) GetPublished(ctx context.Context, spaceId, envId, collectionId, id string, options ...*service.GetPublishedOptions) (itm *service.Item, err error) {
opts := service.MergeGetPublishedOptions(options...)
localeID := opts.LocaleID
if localeID == "" {
localeID = locales.DefaultID
}
// Значение из кэша можно достать только в случае, когда не запрашиваются переводы. Для
// списка переводов (`item.Translations`) кэш не предусмотрен: предполагается, что это
// нечастый запрос и содержание кэша с разными переводами себя не оправдывает
var value = make(map[string]*service.Item)
if len(opts.TranslationsIDs) == 0 {
val, e := m.cachePublished.Get(makeKey(spaceId, envId, collectionId, id))
if e == nil {
value = val.(map[string]*service.Item)
if i, ok := value[localeID]; ok {
return i.Clone(), nil
}
}
}
itm, err = m.Items.GetPublished(ctx, spaceId, envId, collectionId, id, opts)
if err == nil {
env, err := m.envs.Get(ctx, itm.SpaceID, itm.EnvID)
if err != nil {
return nil, errors.Wrapf(err, "failed to get environment %q", itm.EnvID)
}
// Сохраняем в кэш запись без Translations, поскольку значение из кэша также
// возвращается только если переводы не запрашиваются
itmCached := *itm
itmCached.Translations = nil
value[localeID] = &itmCached
for _, envID := range append(env.Aliases, env.ID) {
_ = m.cachePublished.Set(makeKey(itm.SpaceID, envID, itm.CollectionID, itm.ID), value)
}
return itm.Clone(), err
}
return nil, err
}
func (m cachingMiddleware) CheckoutRevision(
ctx context.Context,
spaceId string,
envId string,
collId string,
id string,
revId string,
options ...*service.CheckoutRevisionsOptions,
) (storedRevId string, err error) {
storedRevId, err = m.Items.CheckoutRevision(ctx, spaceId, envId, collId, id, revId, options...)
if err != nil {
return "", err
}
err = m.invalidateCache(ctx, &service.Item{ID: id, SpaceID: spaceId, EnvID: envId, CollectionID: collId})
if err != nil {
return "", err
}
return
}
func (m cachingMiddleware) Archive(ctx context.Context, item *service.Item, options ...*service.ArchiveOptions) (err error) {
err = m.Items.Archive(ctx, item, options...)
if err != nil {
return err
}
return m.invalidateCache(ctx, item)
}