-
Notifications
You must be signed in to change notification settings - Fork 15
Caching
Because building/minifying a module is computationally expensive, the Aggregator maintains per-servlet caches of previously built modules and previously assembled layers on the server's file system so that subsequent requests may be serviced more efficiently by reusing modules or layers from cache.
The Aggregator maintains a two tiered cache for each servlet on the server's file system, The first tier is a cache of built modules and the second tier is a cache of built layers. The module cache contains previously built/minified JavaScript, CSS and other resources. The same source module may have multiple built/minified versions in the module cache, each version having been built using different request parameters and/or has.js features. The layer cache is a cache of assembled module builds that have been gzip encoded. As with the module cache, the layer cache can contain multiple cached versions of a layer that have been built using different request parameters and features. The cache meta-data, which contains the information that organizes the individual cache files, is periodically serialized to disk, and then de-serialized on server restarts, allowing the cache files to be reused across server restarts.
When responding to a request, the servlet first searches the layer cache to see if a layer build has already been assembled for the requested modules, with the requested parameters and features. If one is found, the the cached layer build is returned as the response. Otherwise, the module cache is searched to find any needed modules that have been previously built using the requested parameters and features. Any modules not found in the module cache are built using the requested parameters and features, and the newly built modules are added to the module cache and to the layer being assembled. The newly assembled layer build is then gzip encoded, added to the layer cache, and then returned as the response to the request.
The set of has.js features specified in a request may be large and vary widely from client to client. An optimization performed by the Aggregator to associate with each cached module or layer build only those features which actually make a difference to the module or layer (i.e. only those features for which the has function is called in a module). This allows the Aggregator to reuse cached module or layer builds that were built for requests specifying different features than the current request as long as those differences are not relevant to the module or layer. In order to maximize the effectiveness of the Aggregator cache, follow the best-practice of isolating feature detection within platform library modules and avoid sprinkling feature detection calls throughout your application.
Aggregator caches are maintained on the server's file system in the directory returned by the Plugin.getStateLocation() method for the servlet. The physical location of this directory varies between OSGi implementations and is platform specific. Within this directory, there is a separate working directory for each servlet that has been activated. The name of the directory is derived from the servlet name, which is the value of the alias attribute specified in the element in the plugin.xml for the bundle defining the servlet. Within each servlet's working directory is a "cache" directory, which contains the built module and layer cache files as well as the serialized cache metadata, and a "deps" directory which contains the serialized dependency map used for require list expansion.
Caching is great for improving performance, but it can get in the way when developing applications. It also needs to be factored in when upgrading an application, to prevent stale cached responses from being returned for new requests. The Aggregator provides various URL query args, options and console commands that enable the developer to manage the cache, and the behavior of the Aggregator as it relates to the cache. These include:
- The
developmentMode
option - The
noCache
URL query arg - The
clearcache
console command - The
dumpcache
console command
Another way of clearing the cache is to clean out the cache directory for the servlet. This technique should be used only when the server is down, and is an effective way to make sure the cache is cleared when upgrading an application. Clearing the cache is a necessary step of the application upgrade process because, for performance reasons, the Aggregator does not automatically detect changes in module sources unless it is running in development mode.
The aggregator supports the use of a cache-primer bundle in order to avoid most of the delays associated with populating the Aggregator cache during the initial application requests following an application deployment or upgrade. To use this feature, the application is deployed by build automation to a test server (e.g. Pax Exam
) and application requests are issued to the Aggregator, ether over HTTP or via the processrequesturl
console command, in order to populate the Aggregator cache on the test server. You can use the includeUndefinedFeatureDeps
and includeRequireDeps
query args to cause the Aggregator to generate a 'whole-app' response that will cause all of the modules used by the application to be minified and cached with a single request.
Once the test server's Aggregtor cache has been populated, you then issue the createcachebundle
console command. This will cause the Aggregator to create an OSGi bundle with the specified name containing the contents of the Aggregator cache. The bundle's manifest will contain a cacheBust
header who's value is that of the cacheBust
config property for the application and is used for validation.
This cache-primer bundle can then be deployed to production servers along with the other application bundles. On server startup, if the Aggregator cache is empty or stale, the Aggregator will try to load the cache primer bundle with the name specified by the cachePrimerBundleName
config property. If the bundle can be loaded and the cacheBust
bundle header value matches the cacheBust
config property for the deployed application, then the Aggregator cache will be primed using the contents of the cache-primer bundle. With the Aggregator cache primed in this way, the initial requests for application pages will complete much faster.
This feature was introduced in version 1.3
The Aggregator supports maximizing the use of third-party caches such as browser and proxy caches through the use of the expires
property specified in the server-side AMD config file and the cacheBust
loader extension config property. The expires config property controls the value of the Cache-Control:max-age response header for non-error responses. This header determines how long a third-party cache considers a cached response to be valid before the cache will attempt to validate the cached response with the server. The expires config property specifies the value of the Cache-Control:max-age header, which indicates the number of seconds in the future that the cached response should be considered valid. By setting this value sufficiently high (e.g. one year), you can effectively cause the cached response to always be considered valid. This works great until the application needs to be updated. For this situation, the Aggregator supports a cacheBust loader extension property which allows the application to specify an arbitrary string that is included as the value of the cb=
query argument in all Aggregator requests. By changing the value of the cacheBust property when the application is updated, all previously cached responses will be invalidated and new requests will be sent.