Skip to content

Commit

Permalink
Simplify ObjectCache to use a cache key per put
Browse files Browse the repository at this point in the history
  • Loading branch information
rob-baillie-ortoo committed Mar 14, 2022
1 parent 7bc1289 commit 72e1eb9
Show file tree
Hide file tree
Showing 6 changed files with 78 additions and 32 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,5 @@ public interface ICacheAdaptor
void put( String key, Object value, Integer lifespan );
Boolean contains( String key );
void remove( String key );
Set<String> getKeys();
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,11 @@ public inherited sharing class NullCache implements ICacheAdaptor
return null;
}

public Set<String> getKeys()
{
return new Set<String>();
}

public void put( String key, Object value, Integer lifespan ) // NOPMD: Intentionally left empty, as this should do nothing in a NullCache
{
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
* Note: The lifespan of a given object is reset whenever any object in that object's list is written.
* That is, either the whole of the list is aged out, or none of it is.
*/
public with sharing class ObjectCache
public inherited sharing class ObjectCache
{
public class InvalidIdentifierException extends Exceptions.DeveloperException {}

Expand Down Expand Up @@ -119,16 +119,9 @@ public with sharing class ObjectCache

CacheRetrieval values = new CacheRetrieval();

Map<String,Object> cachedObjects = (Map<String,Object>)cacheWrapper.get( key );

if ( cachedObjects == null )
{
return values.setCacheMisses( ids );
}

for ( String thisId : ids )
{
Object thisValue = cachedObjects.get( thisId );
Object thisValue = cacheWrapper.get( buildFullKey( key, thisId ) );
if ( thisValue != null )
{
values.addCacheHit( thisId, thisValue );
Expand Down Expand Up @@ -212,6 +205,9 @@ public with sharing class ObjectCache
}

// TODO: implement put( String key, IdGetter idGetter, Object objectToStore )
// TODO: implement put( String key, String idField, Sobject sobjectToStore )
// TODO: implement put( String key, Sobject sobjectToStore )

/**
* Put the given Objects into the cache, using the given base key combined with the value that is returned from
* each object when the IdGetter is called against it
Expand All @@ -227,22 +223,15 @@ public with sharing class ObjectCache
Contract.requires( idGetter != null, 'put called with a null idGetter' );
Contract.requires( objects != null, 'put called with a null objects' );

Map<String,Object> cachedObjects = (Map<String,Object>)cacheWrapper.get( key );

if ( cachedObjects == null )
{
cachedObjects = new Map<String,Object>();
}

for ( Object thisObject : objects )
{
Contract.assert( thisObject != null, 'put called with a null entry in objects' );
String thisId = idGetter.getIdFor( thisObject );

Contract.assert( thisId != null, 'put called with an object that has a null Id: ' + thisObject );
cachedObjects.put( thisId, thisObject );

cacheWrapper.put( buildFullKey( key, thisId ), thisObject, CACHE_LIFESPAN_SECONDS );
}
cacheWrapper.put( key, cachedObjects, CACHE_LIFESPAN_SECONDS );

return this;
}
Expand All @@ -253,10 +242,19 @@ public with sharing class ObjectCache
* @param String The base key to remove the objects from.
* @return ObjectCache Itself, allowing for a fluent interface
*/
public ObjectCache remove( String key )
public ObjectCache remove( String keyToRemove )
{
Contract.requires( key != null, 'remove called with a null key' );
cacheWrapper.remove( key );
Contract.requires( keyToRemove != null, 'remove called with a null keyToRemove' );

Set<String> allExistingKeys = cacheWrapper.getKeys();

for ( String thisExistingKey : allExistingKeys )
{
if ( isForKey( thisExistingKey, keyToRemove ) )
{
cacheWrapper.remove( thisExistingKey );
}
}
return this;
}

Expand Down Expand Up @@ -285,23 +283,34 @@ public with sharing class ObjectCache
Contract.requires( key != null, 'remove called with a null key' );
Contract.requires( ids != null, 'remove called with a null ids' );

Map<String,Object> cachedObjects = (Map<String,Object>)cacheWrapper.get( key );

if ( cachedObjects == null )
// This is two loops because we want to check all the ids before we remove anything
for ( String thisId : ids )
{
return this;
Contract.assert( thisId != null, 'remove called with a null entry in ids' );
}

for ( String thisId : ids )
{
Contract.assert( thisId != null, 'remove called with a null entry in ids' );

cachedObjects.remove( thisId );
cacheWrapper.remove( buildFullKey( key, thisId ) );
}
cacheWrapper.put( key, cachedObjects, CACHE_LIFESPAN_SECONDS );
return this;
}

private String buildFullKey( String key, String id )
{
return buildKeyPrefix( key ) + id;
}

private Boolean isForKey( String fullKey, String key )
{
return fullKey.startsWith( buildKeyPrefix( key ) );
}

private String buildKeyPrefix( String key )
{
return key + 'xXXx';
}

private class SobjectIdGetter implements IdGetter
{
String idField;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,24 @@ public inherited sharing class OrgCache implements ICacheAdaptor
return Cache.Org.get( createFullyQualifiedKey( key ) );
}

/**
* Retrieve all the keys that are currently stored in the cache.
*
* If the user does not have access to the cache, will throw an AccessViolationException.
*
* @return Set<String> The keys for all cached objects
*/
public Set<String> getKeys()
{
if ( ! hasAccessToCache )
{
throw new AccessViolationException( Label.ortoo_core_org_cache_access_violation )
.setErrorCode( FrameworkErrorCodes.CACHE_ACCESS_VIOLATION )
.addContext( 'method', 'getKeys' );
}
return Cache.Org.getKeys();
}

/**
* Put the stated value into the stated key for the specified duration (in seconds)
*
Expand Down Expand Up @@ -122,7 +140,7 @@ public inherited sharing class OrgCache implements ICacheAdaptor
}

/**
* Instucts the cache to remove the object at the given key
* Instructs the cache to remove the object at the given key
*
* If the user does not have access to the cache, will throw an AccessViolationException.
*
Expand All @@ -143,7 +161,7 @@ public inherited sharing class OrgCache implements ICacheAdaptor
}

/**
* Instucts the cache to remove all objects from the cache.
* Instructs the cache to remove all objects from the cache.
*
* Is useful for clearing the cache out completely when the majority of entries would otherwise be dirty.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,16 @@ public inherited sharing class SessionCache implements ICacheAdaptor
return Cache.Session.get( key );
}

/**
* Retrieve all the keys that are currently stored in the cache.
*
* @return Set<String> The keys for all cached objects
*/
public Set<String> getKeys()
{
return Cache.Session.getKeys();
}

/**
* Put the stated value into the stated key for the specified duration (in seconds)
*
Expand Down Expand Up @@ -72,7 +82,7 @@ public inherited sharing class SessionCache implements ICacheAdaptor
}

/**
* Instucts the cache to remove the object at the given key
* Instructs the cache to remove the object at the given key
*
* @param String The key to remove
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -546,6 +546,9 @@ private without sharing class ObjectCacheTest
System.assert( true, 'remove, when given a key and Id combination that does not exist, will not throw an exception' );
}

// TODO: remove called with a null / empty id will throw an exception
// TODO: remove called with a null / empty id will not remove other things

public class StringIdGetter implements ObjectCache.IdGetter
{
public String getIdFor( Object objectToGetIdFor )
Expand Down

0 comments on commit 72e1eb9

Please sign in to comment.