-
Notifications
You must be signed in to change notification settings - Fork 37
ConcurrentTLru
ConcurrentTLru
implements expire after write. Items are evicted a fixed duration after creation or most recent update. As such, ConcurrentTLru
is a time aware least recently used (TLRU) cache.
The ConcurrentTLru
API is identical to ConcurrentLru, but with a couple of small differences to facilitate time-based expiry.
int capacity = 128;
TimeSpan ttl = TimeSpan.FromMinutes(5); // items expire after 5 minutes
var lru = new ConcurrentTLru<int, SomeItem>(capacity, ttl);
lru.Policy.ExpireAfterWrite.Value.TrimExpired(); // remove all expired items
ConcurrentTLru
uses the same core algorithm as ConcurrentLru
, described here. In addition, the TLru item eviction policy evaluates the age of an item on each lookup (i.e. TryGet
/GetOrAdd
/GetOrAddAsync
), and if the item has expired it will be discarded at lookup time. Expired items can also be discarded if the cache is at capacity and new items are added. When a new item is added, existing items may transition from hot to warm to cold, and expired items can be evicted at these transition points.
Note that expired items are not eagerly evicted when the cache is below capacity, since there is no background thread performing cleanup. Thus, TLru provides a mechanism to implement eventual consistency, and there is no forceful eviction of stale items until capacity is reached.
On every lookup, item age is calculated and compared to the time to live (TTL). If the item has expired it is discarded, and in the case of GetOrAdd the value factory is invoked to create a new item. This results in an API call to determine the current time.
To get the lowest lookup latency without sacrificing correctness, the Duration struct internally selects the fastest available time API on the host operating system (Windows/MacOS/Linux). See Time-based eviction for more details.
The time policy can be overridden by implementing your own IItemPolicy
, e.g. MyTicksPolicy
shown below with LongTickCountLruItem
would replicate the existing TLruLongTicksPolicy. This enables use of TimeProvider. TimeProvider is not integrated by default because it is significantly slower.
public sealed class CustomTLru<K, V> : ConcurrentLruCore<K, V, LongTickCountLruItem<K, V>, MyLongTicksPolicy<K, V>, TelemetryPolicy<K, V>>
{
public CustomTLru(int capacity, TimeSpan timeToLive, TimeProvider timeprovider)
: base(concurrencyLevel, capacity, EqualityComparer<K>.Default, new MyLongTicksPolicy<K, V>(timeToLive, timeprovider), default)
{
}
...
}