Module - Cache Datasource (Cache Datasource)

Implements a cache filter datasource that is capable of caching results of other datesources and distribute them among connected cluster nodes (= 3rd level distributed cache).

Elements, that are kept in the 3rd level cache are actively managed on updates by monitoring what is stored within one site (using clustering). It is safe to assume that cache prunes on updated data reaches each ACL node as multiple ACL nodes build a large virtual cache using UDP based auto-discovery of cluster nodes. (under the assumption clustering was enabled with -DrunClustered=true)

A restart of an ACL node automatically cleans all locally cached information to ensure no old data is kept (disk persistence is primarily used to construct large in-memory and JDBM based caches that offload the SQL-DB).

By actively managing cache prunes on data updates, it is possible to use quite large TTL on data. With the current configuration there's no definite expiration of elements besides from removing elements that are no longer queried. A cache can grow to any size that is required to service requests and is only limited by the amount of available hard disk storage capacity.

Typical cached requests are 10 to 50 times faster when run within the unit test environment (using an indexed in-memory HSQL DB as backend). With the fully loaded GRID DB the differences should be more radical.

Defined Cache Regions

31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
@CacheRegion(name = CacheSettings.CACHE_REGION_FILE_DETAILS,
        // Using 4% of heap to cache file details (assuming an entry weights 2kb).
        // At least 250 entries are kept in memory.
        maxElementsInMemoryExpression = "${max(250, memoryPercent(5) / kb(2))}",
        persistentMode = TEMPORARY,
        evictionPolicy = LIRS,
        distributionPolicy = DISTRIBUTE_VALUES,
        // Clean entries if not accessed for 6 hours
        idleTime = 6, idleTimeUnit = HOURS,
        // Expire entries after 15 minutes if we're not running clustered or in 'readonly' mode.
        defaultExpirationTimeExpression = "${not runClustered || readonly ? 15 : 6000}", defaultExpirationTimeUnit = MINUTES),
  
@CacheRegion(name = CacheSettings.CACHE_REGION_FILE_INFORMATION,
        // Using 15% of heap to cache file information (assuming an entry weights 256b).
        // At least 250 entries are kept in memory.
        maxElementsInMemoryExpression = "${max(250, memoryPercent(15) / 256)}",
        persistentMode = TEMPORARY,
        evictionPolicy = LIRS,
        distributionPolicy = DISTRIBUTE_VALUES,
        // Clean entries if not accessed for 6 hours
        idleTime = 6, idleTimeUnit = HOURS,
        // Expire entries after 15 minutes if we're not running clustered or in 'readonly' mode.
        defaultExpirationTimeExpression = "${not runClustered || readonly ? 15 : 6000}", defaultExpirationTimeUnit = MINUTES),
  
@CacheRegion(name = CacheSettings.CACHE_REGION_PACKAGE_DETAILS,
        // Using 2% of heap to cache package details (assuming an entry weights 4kb).
        // At least 250 entries are kept in memory.
        maxElementsInMemoryExpression = "${max(250, memoryPercent(3) / kb(4))}",
        persistentMode = TEMPORARY,
        evictionPolicy = LIRS,
        distributionPolicy = DISTRIBUTE_VALUES,
        // Clean entries if not accessed for 6 hours
        idleTime = 6, idleTimeUnit = HOURS,
        // Expire entries after 15 minutes if we're not running clustered or in 'readonly' mode.
        defaultExpirationTimeExpression = "${not runClustered || readonly ? 15 : 6000}", defaultExpirationTimeUnit = MINUTES),
  
@CacheRegion(name = CacheSettings.CACHE_REGION_PACKAGE_INFORMATION,
        // Using 4% of heap to cache package information (assuming an entry weights 512b).
        // At least 250 entries are kept in memory.
        maxElementsInMemoryExpression = "${max(250, memoryPercent(5) / 512)}",
        persistentMode = TEMPORARY,
        evictionPolicy = LIRS,
        distributionPolicy = DISTRIBUTE_VALUES,
        // Clean entries if not accessed for 6 hours
        idleTime = 6, idleTimeUnit = HOURS,
        // Expire entries after 15 minutes if we're not running clustered or in 'readonly' mode.
        defaultExpirationTimeExpression = "${not runClustered || readonly ? 15 : 6000}", defaultExpirationTimeUnit = MINUTES),
  
@CacheRegion(name = CacheSettings.CACHE_REGION_PACKAGE_FILE_IDENTIFIERS,
        // Using 2% of heap to cache package name to id mapping (assuming an entry weights 128b).
        // At least 250 entries are kept in memory.
        maxElementsInMemoryExpression = "${max(250, memoryPercent(2) / 128)}",
        persistentMode = TEMPORARY,
        evictionPolicy = LIRS,
        distributionPolicy = REPLICATE_VALUES,
        // Clean entries if not accessed for 6 hours
        idleTime = 6, idleTimeUnit = HOURS,
        // Expire entries after 15 minutes if we're not running clustered or in 'readonly' mode.
        defaultExpirationTimeExpression = "${not runClustered || readonly ? 15 : 6000}", defaultExpirationTimeUnit = MINUTES),
  
@CacheRegion(name = CacheSettings.CACHE_REGION_UNKNOWN_FILE_IDENTIFIERS,
        // Using 0.5% of heap to cache unknown identifiers (assuming an entry weights 64b).
        // At least 512 entries are kept in memory.
        maxElementsInMemoryExpression = "${max(512, memoryPercent(0.5) / 64)}",
        persistentMode = TEMPORARY,
        evictionPolicy = LIRS,
        distributionPolicy = REPLICATE_VALUES,
        // Clean entries if not accessed for 6 hours
        idleTime = 6, idleTimeUnit = HOURS,
        // Expire entries after 15 minutes if we're not running clustered or in 'readonly' mode.
        defaultExpirationTimeExpression = "${not runClustered || readonly ? 15 : 120}", defaultExpirationTimeUnit = MINUTES),
  
@CacheRegion(name = CacheSettings.CACHE_REGION_UNKNOWN_NAMES,
        // Using 0.5% of heap to cache unknown names (assuming an entry weights 64b).
        // At least 512 entries are kept in memory.
        maxElementsInMemoryExpression = "${max(512, memoryPercent(0.5) / 64)}",
        persistentMode = TEMPORARY,
        evictionPolicy = LIRS,
        distributionPolicy = REPLICATE_VALUES,
        // Clean entries if not accessed for 6 hours
        idleTime = 6, idleTimeUnit = HOURS,
        // Expire entries after 15 minutes if we're not running clustered or in 'readonly' mode.
        defaultExpirationTimeExpression = "${not runClustered || readonly ? 15 : 120}", defaultExpirationTimeUnit = MINUTES)

Low Level Tuning Options

11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
/**
 * Toggles synchronous cache operations (disabled by default, use if timing issues occur).
 */
boolean IS_SYNCHRONOUS = Boolean.getBoolean("gacl.3rdcache.is.synchronous");
  
/**
 * Toggles whether negative ('null') results are cached.
 */
boolean NO_NEGATIVE_CACHING = Boolean.getBoolean("gacl.3rdcache.no.negative.caching");
  
/**
 * Toggles whether the batch interface is used for cache operations.
 */
boolean NO_BATCHING = Boolean.getBoolean("gacl.3rdcache.no.batching");
  
/**
 * Enables the use of hard disk storage for caching (using JDBM btree format).
 */
boolean ENABLE_PERSISTENCE = Boolean.getBoolean("gacl.3rdcache.enable-persistence");
  
/**
 * Toggles whether the 3rd level caching is disabled (temporarily).
 * <p/>
 * Note: Disabling the cache with this option does not remove the aspects.
 * If caching must be disabled permanently to reduce resource usage, the
 * file 'cache.jar' should be removed from the lib directory.
 */
boolean DISABLED = Boolean.getBoolean("gacl.3rdcache.disabled");
  
/**
 * Names the 3rd level cache region that stores FileDetail instances.
 */
String CACHE_REGION_FILE_DETAILS = "3rdLevel.Cache.FileDetails";
/**
 * Names the 3rd level cache region that stores FileInformation instances.
 */
String CACHE_REGION_FILE_INFORMATION = "3rdLevel.Cache.FileInformation";
/**
 * Names the 3rd level cache region that stores PackageDetails instances.
 */
String CACHE_REGION_PACKAGE_DETAILS = "3rdLevel.Cache.PackageDetails";
/**
 * Names the 3rd level cache region that stores PackageInformation instances.
 */
String CACHE_REGION_PACKAGE_INFORMATION = "3rdLevel.Cache.PackageInformation";
/**
 * Names the 3rd level cache region that maps FileIdentifiers against their package names.
 */
String CACHE_REGION_PACKAGE_FILE_IDENTIFIERS = "Internal.3rdLevel.Cache.PackageFileIdentifiers";
/**
 * Names the 3rd level cache region that contains names that are known to be unknown (= negative result caching)
 */
String CACHE_REGION_UNKNOWN_NAMES = "Internal.3rdLevel.Cache.UnknownNames";
/**
 * Names the 3rd level cache region that contains identifiers that are known to be unknown (= negative result caching)
 */
String CACHE_REGION_UNKNOWN_FILE_IDENTIFIERS = "Internal.3rdLevel.Cache.UnknownFileIdentifiers";

Usage of Aspects

3rd level caching is technically implemented using AspectJ as it requires to intercept and listen at various locations. Using aspects avoids that implementations become intrusive.