FusionCache with preview functionality #561
Replies: 2 comments 1 reply
-
Eheh, some workplaces ago I worked for years on our very own CMS which I've build from scratch.
Makes sense, one question though: does the preview feature use different data then the live version or exactly the same data but with cache busting applied?
The methods are just shortcuts to set the options. Having said that, if the data is the same as the live one (question above) you may skip reading from L1/L2 but not skip writing to it: in this way you would not "waste" the read you just made, and can keep the data in the cache as fresh as possible. But here's the question: if you want to totally bypass the cache & all its features, wouldn't it make sense to just call the source (database or whatever) directly? Also, while we're on the subject, I'd like to share an extra suggestion: whether with or without going through the caching layer, in preview mode you're always going to the database. Because of this I would suggest to not enable the preview mode via a simple true/false query param, but more with something like a separate preview token: I did exactly this back then, and the token was per-user (so it was not copy-paste-shareable), and it worked really great as a protection mechanism. We also did another step, but I don't know if it's too much overhead, so I'll briefly describe it and you can think about your specific scenario, then you do you. Hope this helps. |
Beta Was this translation helpful? Give feedback.
-
The preview is fueled by the same API under the hood API call in essence but the CMS apis themselves have a preview (true/false) header that we send, so the data would be different (preview is based on edits that are saved, live is based on edits after a publish action, so they can and most likely will be different data outcomes)
Yes, that was basically my idea
It would, on my personal .NET template with the same CMS, etc I make use of this by wrapping the actual factories in local functions that either I can directly if in preview or through the cache if not in preview. However, this project has been built for a good year or so now, so refactoring every cache call, etc into local functions and so on is not something that many get the time privilege to do in the agency world as you might be aware of as well 😄
Yes, we have this or similar in place (great minds think alike), whichever editor generates the preview URL in the CMS will result in a pre-signed URL (with a encoded JWT as the token in the URL with a short validity lifetime) which the API layer analyses and verifies before asserting that we are in preview mode (invalid tokens with preview sent as true are ignored and treated as invalid right away). We have a whole site resolution/tenancy/preview middleware and services handling it all which in turn allows application code to access said middleware results and treat it as a simple "true/false" but it's actually far from being just a simple URL param 😄. The only possibility of being DOS'ed by this preview feature would be if a editor went crazy in sharing a given preview URL with loads of stakeholders for some god-forsaken reason. With all that in mind, we could in theory convert this: var result = await _fusionCache.GetOrSetAsync<SiteDictionaryPropertiesModel?>(
$"content-service:site-dictionary:{instance}:{site.HomepageId}:{site.CultureInfo}:{site.DictionaryId}:{isPreview}",
async (ctx, ct) =>
{
var result = await _umbracoDeliveryApi.GetContentItemById20(
site.DictionaryId,
accept_Language: site.CultureInfo,
preview: isPreview,
cancellationToken: ct);
if (!result.IsSuccessStatusCode)
{
throw new ContentApiException(result.StatusCode, result.ReasonPhrase);
}
var properties = (result.Content as SiteDictionaryContentResponseModel)?.Properties;
if (properties is null)
{
ctx.Options.SetAllDurations(TimeSpan.FromSeconds(_cachingConfiguration.Default.NoResultDuration), jitter: TimeSpan.Zero, failsafePercentage: _cachingConfiguration.Default.FailSafePercentage);
}
var cacheSite = _cachingConfiguration.EnabledSites is null
|| _cachingConfiguration.EnabledSites.Contains(site.HomepageId);
if (isPreview || !cacheSite)
{
ctx.Options.SetAllDurations(TimeSpan.FromSeconds(-1), jitter: TimeSpan.Zero, failsafePercentage: _cachingConfiguration.Default.FailSafePercentage);
}
return properties;
},
tags: [CachingConstants.ContentApi.Tags.Content, CachingConstants.ContentApi.Tags.SiteDictionary, site.HomepageId.ToString(), instance, site.CultureInfo, site.DictionaryId.ToString()]);to this? var result = await _fusionCache.GetOrSetAsync<SiteDictionaryPropertiesModel?>(
$"content-service:site-dictionary:{instance}:{site.HomepageId}:{site.CultureInfo}:{site.DictionaryId}",
async (ctx, ct) =>
{
var result = await _umbracoDeliveryApi.GetContentItemById20(
site.DictionaryId,
accept_Language: site.CultureInfo,
preview: isPreview,
cancellationToken: ct);
if (!result.IsSuccessStatusCode)
{
throw new ContentApiException(result.StatusCode, result.ReasonPhrase);
}
var properties = (result.Content as SiteDictionaryContentResponseModel)?.Properties;
if (properties is null)
{
ctx.Options.SetAllDurations(TimeSpan.FromSeconds(_cachingConfiguration.Default.NoResultDuration), jitter: TimeSpan.Zero, failsafePercentage: _cachingConfiguration.Default.FailSafePercentage);
}
var cacheSite = _cachingConfiguration.EnabledSites is null
|| _cachingConfiguration.EnabledSites.Contains(site.HomepageId);
return properties;
},
options => options.SetSkipDistributedCache(isPreview, isPreview).SetSkipMemoryCache(isPreview),
tags: [CachingConstants.ContentApi.Tags.Content, CachingConstants.ContentApi.Tags.SiteDictionary, site.HomepageId.ToString(), instance, site.CultureInfo, site.DictionaryId.ToString()]);SetAllDurations() is just a custom extension I've created for simplifying some recurring calls we make |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
-
Hi @jodydonetti
So, in a project I'm working on right now, we have an API orchestration layer which serves a large white label style site serving loads of differently configured sites all with their own data, etc all powered by a CMS under the hood.
As usual in CMS software there usually is a "preview" functionality, which is meant to allow editors to see their content edits in real-time. This is all served through the same API layer.
At the moment, this preview functionality is essentially served by doing some adaptive caching where when we detect we are in "preview" mode we set our Durations to TimeSpan.FromSeconds(-1), with things such as Jitter, etc all turns off or set to 0 (with a cache key similar to the live version but with a "preview" true/false appended at the end of the key.
However I was looking at the documentation the other day and noticed a few configuration options/properties and also some methods in the Entry Options, namely:
If we want this preview functionality to basically = "factory always runs", will either setting all of those 4 properties or using the 2 methods (which basically set the properties) with everything set to true provide that exact functionality (bypasses fail-safe, etc as well guaranteed)?
Beta Was this translation helpful? Give feedback.
All reactions