How not to delete cache items referenced by other objects

I assume that an object representing something can have only one instance in memory. I avoid duplication and equal objects.

  • Suppose an object uniquely identified by a New York City type is contained in a cache (which extends System.Runtime.Caching.ObjectCache ). This object is referenced by another object named MyBusinessObject

  • The cache deletes the New York object, but the MyBusinessObject object still refers to New York. The garbage collector does not delete this object from memory, because it still has references.

  • Another object requests New York from the cache. Cache downloads a new instance of City for "New York"

Now there are two instances of New York, one of which MyBusinessObject referencing (which is not valid) and a new instance of New York that is referenced by the cache.

Is there a design template that provides a solution to this problem? I do not want MyBusinessObject use obsolete instances of City .

One of the possible solutions could not exclude references to cache objects, but how to do it?

Here are the UML diagrams for what I am trying to explain above:

Class diagram UML

Objects diagram

+4
source share
1 answer

In this case, your cache should not cache the actual object, but rather a wrapper around the cached object, which also contains information about the state of the object in the cache.

For example, you might have a simple class that represents elements in the cache:

 public class CacheItem<T> { // Since the cache is the only thing // that should be making CacheItems, // make this internal to the assembly // that the cache is implemented in. // This constructor is called before // an add. internal CacheItem(T item) { // Set the property values. Item = item; } // Poor-man immutability. public T Item { get; private set; } // The backing field for IsCached, it // is volatile so that it can serialize // access for single reads/writes, which is // what the property does. // Assume it is being added when constructed. private volatile bool _isCached = true; // Only able to be set by the cache. // The setter is set to false when the item // is stale (not in the cache any longer). public bool IsCached { get { return _isCached; } set { _isCached = value; } } } 

The idea here is simple:

  • When your cache is about to introduce a new instance of an element into the cache, it calls the constructor (the constructor should be accessible only to the cache, if necessary, you can make CacheItem nested class with a private constructor instead of the internal one), which sets the value of the IsCached property to true. Then the item is cached.

  • When an item expires from the cache, the cache sets the IsCached property to false (again, this property should be available only to the cache).

  • Responsibility is transferred to the client to check the CacheItem<T> to make sure it is out of date.

Please note that this is a pull operation. If you want, you can add support for push operations by adding an event to the CacheItem<T> like this:

 public class CacheItem<T> { // Since the cache is the only thing // that should be making CacheItems, // make this internal to the assembly // that the cache is implemented in. // This constructor is called before // an add. internal CacheItem(T item) { // Set the property values. Item = item; } // Poor-man immutability. public T Item { get; private set; } // The lock for the event registrations. // Since everything else is immutable, this needs // to be as well. private readonly object _eventLock = new object(); // The backing field for the Expired // event. Since everything else is immutable // this needs to be as well. private readonly EventHandler _expiredHandlers; // The expires event. public event EventHandler Expired { add { lock (_eventLock) _expiredHandlers += value; } remove { lock (_eventLock) _expiredHandlers -= value; } } // The backing field for IsCached, it // is volatile so that it can serialize // access for single reads/writes, which is // what the property does. // Assume it is being added when constructed. private volatile bool _isCached = true; // The setter is set to false by the // Expire method (called by the cached) // when the item is stale // (not in the cache any longer). public bool IsCached { get { return _isCached; } } // Called internally by the cache. internal void Expire() { // Set _isCached to false. _isCached = false; // Get the event handlers and fire // the event. Getting the handlers // needs to be synchronized. EventHandler handlers; // Synchronize. lock (_eventLock) handlers = _expiredHandlers; // Fire if there are handlers. if (handlers != null) handlers(this, EventArgs.Empty); } } 

Now you can subscribe to Expired subscribers who tell clients when the cache entry is invalid. This is triggered by the cache, triggering an internal Expire event on the element when it is removed from the cache (and it also sets the IsCached property).

+2
source

Source: https://habr.com/ru/post/1445769/


All Articles