Stroika Library 3.0d16
 
Loading...
Searching...
No Matches
Stroika::Foundation::Cache::TimedCache< KEY, VALUE, TRAITS > Class Template Reference

Keep track of a bunch of objects, each with an associated 'freshness' which meet a TimedCache-associated minimal reshness limit. More...

#include <TimedCache.h>

Inheritance diagram for Stroika::Foundation::Cache::TimedCache< KEY, VALUE, TRAITS >:
Stroika::Foundation::Cache::SynchronizedTimedCache< KEY, VALUE, TRAITS >

Public Member Functions

nonvirtual Time::Duration GetMinimumAllowedFreshness () const
 
nonvirtual void SetMinimumAllowedFreshness (Time::Duration minimumAllowedFreshness)
 
nonvirtual Traversal::Iterable< CacheElement > Elements () const
 
nonvirtual optional< VALUELookup (typename Common::ArgByValueType< KEY > key, Time::TimePointSeconds *lastRefreshedAt=nullptr) const
 Returns the value associated with argument 'key', or nullopt, if its missing (missing same as expired). Can be used to retrieve lastRefreshedAt.
 
nonvirtual optional< VALUELookup (typename Common::ArgByValueType< KEY > key, LookupMarksDataAsRefreshed successfulLookupRefreshesAcceesFlag)
 
nonvirtual VALUE LookupValue (typename Common::ArgByValueType< KEY > key, const function< VALUE(typename Common::ArgByValueType< KEY >)> &cacheFiller, LookupMarksDataAsRefreshed successfulLookupRefreshesAcceesFlag=LookupMarksDataAsRefreshed::eDontTreatFoundThroughLookupAsRefreshed, PurgeSpoiledDataFlagType purgeSpoiledData=PurgeSpoiledDataFlagType::eAutomaticallyPurgeSpoiledData)
 
nonvirtual void Add (typename Common::ArgByValueType< KEY > key, typename Common::ArgByValueType< VALUE > result, PurgeSpoiledDataFlagType purgeSpoiledData=PurgeSpoiledDataFlagType::eAutomaticallyPurgeSpoiledData)
 
nonvirtual void clear ()
 
nonvirtual void PurgeSpoiledData ()
 

Detailed Description

template<typename KEY, typename VALUE, typename TRAITS = TimedCacheSupport::DefaultTraits<KEY, VALUE>>
class Stroika::Foundation::Cache::TimedCache< KEY, VALUE, TRAITS >

Keep track of a bunch of objects, each with an associated 'freshness' which meet a TimedCache-associated minimal reshness limit.

We define 'fresheness' somewhat arbitrarily, but by default, this means since the item was added. However, the TimedCache also provides other apis to update the 'freshness' of a stored object, depending on application needs.

Keeps track of all items - indexed by Key - but throws away items which are any more stale than given by the staleness limit.

Note
Comparison with LRUCache The main difference beweeen an LRUCache and TimedCache has to do with when an element is evicted from the Cache. With a TimedCache, its evicted only when its overly aged. With an LRUCache, its more random, and depends a bit on luck (when using hashing) and how recently an item was last accessed.
Principal difference between CallerStalenessCache and TimedCache lies in where you specify the max-age for an item: with CallerStalenessCache, its specified on each lookup call (ie with the caller), and with TimedCache, the expiry is stored with each cached item.

Because of this, when you use either of these caches with a KEY=void (essentially to cache a single thing) they become indistinguishable.

N.B. the KEY=void functionality is NYI for TimedCache, so best to use CallerStalenessCache for that, at least for now.

Example Usage
Use TimedCache to avoid needlessly redundant lookups
{
return sCache_.LookupValue (inetAddr, [] (const InternetAddress& inetAddr) {
return DNS::kThe.ReverseLookup (inetAddr);
});
}
LRUCache implements a simple least-recently-used caching strategy, with optional hashing (of keys) to...
Definition LRUCache.h:94
nonvirtual VALUE LookupValue(typename Common::ArgByValueType< KEY > key, const function< VALUE(typename Common::ArgByValueType< KEY >)> &valueFetcher)
Definition LRUCache.inl:302
Duration is a chrono::duration<double> (=.
Definition Duration.h:96
Example Usage
Assume 'LookupDiskStats_' returns DiskSpaceUsageType, but its expensive, and the results change only slowly...
int size;
};
// do the actual lookup part which maybe slow
auto LookupDiskStats_ ([[maybe_unused]] const String& filename) -> DiskSpaceUsageType { return DiskSpaceUsageType{33}; };
Cache::TimedCache<String, DiskSpaceUsageType> sDiskUsageCache_{5.0_duration};
// explicitly caller maintaining the cache
optional<DiskSpaceUsageType> LookupDiskStats_Try1 (String diskName)
{
optional<DiskSpaceUsageType> o = sDiskUsageCache_.Lookup (diskName); // maybe use eTreatFoundThroughLookupAsRefreshed depending on your application
if (not o.has_value ()) {
o = LookupDiskStats_ (diskName);
if (o) {
sDiskUsageCache_.Add (diskName, *o);
}
}
return o;
}
// more automatic maintainance of that update pattern
DiskSpaceUsageType LookupDiskStats_Try2 (String diskName)
{
// maybe use eTreatFoundThroughLookupAsRefreshed depending on your application
return sDiskUsageCache_.LookupValue (diskName,
[](String diskName) -> DiskSpaceUsageType {
return LookupDiskStats_ (diskName);
});
}
// or still simpler
DiskSpaceUsageType LookupDiskStats_Try3 (String diskName)
{
// maybe use eTreatFoundThroughLookupAsRefreshed depending on your application
return sDiskUsageCache_.LookupValue (diskName, LookupDiskStats_);
}
void DoIt ()
{
// example usage
EXPECT_TRUE (Memory::NullCoalesce (LookupDiskStats_Try1 ("xx")).size == 33);
EXPECT_TRUE (LookupDiskStats_Try2 ("xx").size == 33);
EXPECT_TRUE (LookupDiskStats_Try3 ("xx").size == 33);
}
nonvirtual optional< VALUE > Lookup(typename Common::ArgByValueType< KEY > key)
Definition LRUCache.inl:253
Note
Only calls to @Add, @Lookup (...,eTreatFoundThroughLookupAsRefreshed), and @LookupValue (on a cache miss when updating) update the lastRefreshed time.

For most use cases (when caching something) - the default behavior of only updating the last-access time on Add makes sense. But for the case where this class is used to OWN an object (see shared_ptr example below) - then treating a read asccess as a refresh can be helpful.

Before Stroika 3.0d1, there was a template parameter which allowed treating Lookup () this way. But that has significant downsides (making lookup non-const which has threading implications). Instead now, we have non-const methods you can use todo this instead of Lookup, and then there is no need for special template paremeters, or non-cost Lookup.

Example Usage
To use TimedCache<> to 'own' a set of objects (say a set caches where we are the only possible updater) - you can make the 'VALUE' type a shared_ptr<X>, and use Lookup (...,eTreatFoundThroughLookupAsRefreshed) instead of Lookup ().

In this example, there is a set of files on disk in a folder, which is complex to analyze but once analyzed, lots of calls come in at once to read (and maybe update) the set of files and once nobody has asked for a while, we throw that cache away, and rebuild it as needed.

This example ALSO shows how to wrap a cache object in 'Synchronized' for thread safety.

using ScanFolderKey_ = String;
int size; // ...info to cache about a folder
};
Synchronized<Cache::TimedCache<
ScanFolderKey_,
shared_ptr<FolderDetails_>,
shared_ptr<FolderDetails_>>>
sCachedScanFoldersDetails_{kAgeForScanPersistenceCache_};
shared_ptr<FolderDetails_> AccessFolder_ (const ScanFolderKey_& folder)
{
auto lockedCache = sCachedScanFoldersDetails_.rwget ();
if (optional<shared_ptr<FolderDetails_>> o = lockedCache->Lookup (folder, eTreatFoundThroughLookupAsRefreshed)) {
return *o;
}
else {
shared_ptr<FolderDetails_> fd = make_shared<FolderDetails_> (); // and fill in default values looking at disk
lockedCache->Add (folder, fd);
return fd;
}
}
void DoIt ()
{
auto f1 = AccessFolder_ ("folder1"_k);
auto f2 = AccessFolder_ ("folder2"_k);
auto f1again = AccessFolder_ ("folder1"); // if you trace through the debug code you'll see this is a cache hit
}
chrono::duration< double > DurationSeconds
chrono::duration<double> - a time span (length of time) measured in seconds, but high precision.
Definition Realtime.h:57
Keep track of a bunch of objects, each with an associated 'freshness' which meet a TimedCache-associa...
Definition TimedCache.h:257
Note
This cache will keep using more and more memory until the cached items become out of date. For a cache that limits the max number of entries, use the
See also
LRUCache.
Note
This cache assumes one timeout for all items. To have timeouts vary by item,
See also
CallerStalenessCache.
Note
Thread-Safety C++-Standard-Thread-Safety
we REQUIRE (without a way to enforce) - that the STATS object be internally synchronized, so that we can maintain statistics, without requiring the lookup method be non-const; this is only for tuning/debugging, anyhow...
See also
SynchronizedTimedCache<> - for internally synchonized implementation
SyncrhonizedCallerStalenessCache
CallerStalenessCache
LRUCache

Definition at line 257 of file TimedCache.h.

Member Function Documentation

◆ GetMinimumAllowedFreshness()

Time::Duration Stroika::Foundation::Cache::TimedCache< KEY, VALUE, TRAITS >::GetMinimumAllowedFreshness ( ) const

When items are added to the timed cache, there is a universal (for the entire cache) minimum allowed freshness (how old item allowed to be before thrown away).

Definition at line 23 of file TimedCache.inl.

◆ SetMinimumAllowedFreshness()

void Stroika::Foundation::Cache::TimedCache< KEY, VALUE, TRAITS >::SetMinimumAllowedFreshness ( Time::Duration  minimumAllowedFreshness)
See also
GetMinimumAllowedFreshness ()

Definition at line 29 of file TimedCache.inl.

◆ Elements()

Note
This returns the non-expired elements of the current cache object.

Definition at line 39 of file TimedCache.inl.

◆ Lookup() [1/2]

Returns the value associated with argument 'key', or nullopt, if its missing (missing same as expired). Can be used to retrieve lastRefreshedAt.

If lastRefreshedAt is provided, it is ignored, except if Lookup returns true, the value pointed to will contain the last time the data was refreshed.

Occasionally, a caller might want to have the ACT of doing a lookup mark the item as fresh, in which case call Lookup (..., eTreatFoundThroughLookupAsRefreshed) instead.

Occasionally, a caller might want to ASSURE it gets data, and just use the cached value if fresh enuf, and specify a lookup lambda to fetch the actual data if its not fresh, in which case call LookupValue ().

Note
Before Stroika 3.0d1, this used to support TraitsType::kTrackReadAccess, and if it was true did the same as the newer Lookup (..., eTreatFoundThroughLookupAsRefreshed)

Before Stroika 3.0d1, we used to remove the entry from the list (an optimization). But that required Lookup to be non-const (with synchronization in mind probably a pessimization). So instead, count on PurgeUnusedData being called automatically on future adds, explicit user calls to purge unused data.

 i = fMap_.erase (i);

Definition at line 52 of file TimedCache.inl.

◆ Lookup() [2/2]

optional< VALUE > Stroika::Foundation::Cache::TimedCache< KEY, VALUE, TRAITS >::Lookup ( typename Common::ArgByValueType< KEY key,
LookupMarksDataAsRefreshed  successfulLookupRefreshesAcceesFlag 
)

Before Stroika 3.0d1, we used to remove the entry from the list (an optimization). But that required Lookup to be non-const (with synchronization in mind probably a pessimization). So instead, count on PurgeUnusedData being called automatically on future adds, explicit user calls to purge unused data.

 i = fMap_.erase (i);

Definition at line 83 of file TimedCache.inl.

◆ LookupValue()

VALUE Stroika::Foundation::Cache::TimedCache< KEY, VALUE, TRAITS >::LookupValue ( typename Common::ArgByValueType< KEY key,
const function< VALUE(typename Common::ArgByValueType< KEY >)> &  cacheFiller,
LookupMarksDataAsRefreshed  successfulLookupRefreshesAcceesFlag = LookupMarksDataAsRefreshed::eDontTreatFoundThroughLookupAsRefreshed,
PurgeSpoiledDataFlagType  purgeSpoiledData = PurgeSpoiledDataFlagType::eAutomaticallyPurgeSpoiledData 
)

Usually one will use this as (cacheFiller overload):

VALUE v = cache.Lookup (key, ts, [this] () -> VALUE {return this->realLookup(key); });

However, the method (Lookup) returing an optional is occasionally useful, if you don't want to fill the cache but just see if a value is present.

The overload with cacheFiller, will update the 'time stored' for the argument key if a new value is fetched.

Note
This function may update the TimedCache (which is why it is non-const).

Definition at line 114 of file TimedCache.inl.

◆ Add()

void Stroika::Foundation::Cache::TimedCache< KEY, VALUE, TRAITS >::Add ( typename Common::ArgByValueType< KEY key,
typename Common::ArgByValueType< VALUE result,
PurgeSpoiledDataFlagType  purgeSpoiledData = PurgeSpoiledDataFlagType::eAutomaticallyPurgeSpoiledData 
)

Updates/adds the given value associated with key, and updates the last-access date to now (or argument freshAsOf).

The parameter PurgeSpoiledData defaults to eAutomaticallyPurgeSpoiledData; this allows the accumulated data to automatically be purged as it becomes irrelevant (

See also
PurgeSpoiledData). But for performance sake, callers may call Add (..., eDontAutomaticallyPurgeSpoiledData)

Definition at line 129 of file TimedCache.inl.

◆ clear()

Remove everything from the cache

Definition at line 158 of file TimedCache.inl.

◆ PurgeSpoiledData()

May be called occasionally to free resources used by cached items that are out of date. Not necessary to call - but can save memory.

Can be triggered automatically (so not explicitly) by passing eAutomaticallyPurgeSpoiledData to Add ()

Definition at line 164 of file TimedCache.inl.


The documentation for this class was generated from the following files: