124 lines
5.3 KiB
Plaintext
124 lines
5.3 KiB
Plaintext
NuCache Documentation
|
|
======================
|
|
|
|
NOTE - RENAMING
|
|
Facade = PublishedSnapshot
|
|
and everything needs to be renamed accordingly
|
|
|
|
HOW IT WORKS
|
|
-------------
|
|
|
|
NuCache uses a ContentStore to keep content - basically, a dictionary of int => content,
|
|
and some logic to maintain it up-to-date. In order to provide immutable content to
|
|
pages rendering, a ContentStore can create ContentViews. A ContentView basically is
|
|
another int => content dictionary, containing entries only for things that have changed
|
|
in the ContentStore - so the ContentStore changes, but it updates the views so that
|
|
they
|
|
|
|
Views are chained, ie each new view is the parent of previously existing views. A view
|
|
knows its parent but not the other way round, so views just disappear when they are GC.
|
|
|
|
When reading the cache, we read views up the chain until we find a value (which may be
|
|
null) for the given id, and finally we read the store itself.
|
|
|
|
|
|
The FacadeService manages a ContentStore for content, and another for media.
|
|
When a Facade is created, the FacadeService gets ContentView objects from the stores.
|
|
Views provide an immutable snapshot of the content and media.
|
|
|
|
When the FacadeService is notified of changes, it notifies the stores.
|
|
Then it resync the current Facade, so that it requires new views, etc.
|
|
|
|
Whenever a content, media or member is modified or removed, the cmsContentNu table
|
|
is updated with a json dictionary of alias => property value, so that a content,
|
|
media or member can be loaded with one database row - this is what is used to populate
|
|
the in-memory cache.
|
|
|
|
|
|
A ContentStore actually stores ContentNode instances, which contain what's common to
|
|
both the published and draft version of content + the actual published and/or draft
|
|
content.
|
|
|
|
|
|
LOCKS
|
|
------
|
|
|
|
Each ContentStore is protected by a reader/writer lock 'Locker' that is used both by
|
|
the store and its views to ensure that the store remains consistent.
|
|
|
|
Each ContentStore has a _freezeLock object used to protect the 'Frozen'
|
|
state of the store. It's a disposable object that releases the lock when disposed,
|
|
so usage would be: using (store.Frozen) { ... }.
|
|
|
|
The FacadeService has a _storesLock object used to guarantee atomic access to the
|
|
set of content, media stores.
|
|
|
|
|
|
CACHE
|
|
------
|
|
|
|
For each set of views, the FacadeService creates a SnapshotCache. So a SnapshotCache
|
|
is valid until anything changes in the content or media trees. In other words, things
|
|
that go in the SnapshotCache stay until a content or media is modified.
|
|
|
|
For each Facade, the FacadeService creates a FacadeCache. So a FacadeCache is valid
|
|
for the duration of the Facade (usually, the request). In other words, things that go
|
|
in the FacadeCache stay (and are visible to) for the duration of the request only.
|
|
|
|
The FacadeService defines a static constant FullCacheWhenPreviewing, that defines
|
|
how caches operate when previewing:
|
|
- when true, the caches in preview mode work normally.
|
|
- when false, everything that would go to the SnapshotCache goes to the FacadeCache.
|
|
At the moment it is true in the code, which means that eg converted values for
|
|
previewed content will go in the SnapshotCache. Makes for faster preview, but uses
|
|
more memory on the long term... would need some benchmarking to figure out what is
|
|
best.
|
|
|
|
Members only live for the duration of the Facade. So, for members SnapshotCache is
|
|
never used, and everything goes to the FacadeCache.
|
|
|
|
All cache keys are computed in the CacheKeys static class.
|
|
|
|
|
|
TESTS
|
|
-----
|
|
|
|
For testing purposes the following mechanisms exist:
|
|
|
|
The Facade type has a static Current property that is used to obtain the 'current'
|
|
facade in many places, going through the PublishedCachesServiceResolver to get the
|
|
current service, and asking the current service for the current facade, which by
|
|
default relies on UmbracoContext. For test purposes, it is possible to override the
|
|
entire mechanism by defining Facade.GetCurrentFacadeFunc which should return a facade.
|
|
|
|
A PublishedContent keeps only id-references to its parent and children, and needs a
|
|
way to retrieve the actual objects from the cache - which depends on the current
|
|
facade. It is possible to override the entire mechanism by defining PublishedContent.
|
|
GetContentByIdFunc or .GetMediaByIdFunc.
|
|
|
|
Setting these functions must be done before Resolution is frozen.
|
|
|
|
|
|
STATUS
|
|
------
|
|
|
|
"Detached" contents & properties, which need to be refactored anyway, are not supported
|
|
by NuCache - throwing NotImplemented in ContentCache.
|
|
|
|
All the cached elements rely on guids for the cache key, and not ints, so it could be
|
|
possible to support detached contents & properties, even those that do not have an actual
|
|
int id, but again this should be refactored entirely anyway.
|
|
|
|
Not doing any row-version checks (see XmlStore) when reloading from database, though it
|
|
is maintained in the database. Two FIXME in FacadeService. Should we do it?
|
|
|
|
There is no on-disk cache at all so everything is reloaded from the cmsContentNu table
|
|
when the site restarts. This is pretty fast, but we should experiment with solutions to
|
|
store things locally (and deal with the sync issues, see XmlStore...).
|
|
|
|
Doing our best with PublishedMember but the whole thing should be refactored, because
|
|
PublishedMember exposes properties that IPublishedContent does not, and that are going
|
|
to be lost soon as the member is wrapped (content set, model...) - so we probably need
|
|
some sort of IPublishedMember.
|
|
|
|
/ |