π Optimizing dotCMS: The Importance of Block Caching with $dotcache and #dotcache
In high-performance web application development, efficiency is key. One of the most effective strategies to improve load speed and reduce server load is caching.
π‘ Why is Caching Crucial?
Every time a user requests a webpage, the server needs to perform a series of operations: database queries, business logic, and, in the case of dotCMS, the interpretation and rendering of Velocity templates. If a block of HTML content or the result of a complex operation (such as generating a large list or calculating data) is identical for many requests, recalculating or rendering it on every request is a waste of resources.
HTML block caching in Velocity solves this by storing the final result of a fragment's rendering. Instead of executing the Velocity logic every time, the system simply serves the pre-generated HTML from the cache until it expires or is invalidated. This results in:
Faster Response Time: Pages load significantly quicker.
Lower Server Load: CPU and database resources are freed up.
Better Scalability: The system can handle a higher number of simultaneous users.
π οΈ Caching Tools in dotCMS Velocity
dotCMS offers two primary mechanisms for implementing caching at the Velocity level: the #dotcache directive and the $dotcache ViewTool. While both serve the function of caching, they are designed to operate in different contexts.
1. The #dotcache Directive (Tag-Based Caching)
The #dotcache directive is designed for the Front-End (FE) or the public interface of the site. Its purpose is to wrap a block of Velocity code, whose HTML result is then stored in the cache.
Typical Syntax:
Code snippet
#dotcache("my-unique-key", 3600) ## Key and Time-to-Live in seconds (e.g., 1 hour)
<h1>Page Content</h1>
<p>Render Time: $date.get('HH:mm:ss')</p>
#end
Usage Context: It is intrinsically linked to the process of traditional page rendering and dotCMS's Tag-Based Caching, making it ideal and efficient for content delivery to the end-user.
Key Limitation: It does not work in the Admin UI (Backend). The execution context within the administrator, such as inside a Custom Field of a Content Type, does not activate the directive's caching mechanism because it is decoupled from the FE page rendering request.
2. The $dotcache ViewTool
The $dotcache ViewTool is a cache access utility in dotCMS that offers more granular programmatic control. Unlike the directive, it operates as a key/value API to store any object or stringβin this case, the generated HTML.
Main Methods:
$dotcache.get('key'): Retrieves a value from the cache.$dotcache.put('key', $value, time_to_live): Stores a value with a key and an optional time-to-live.
Usage Context: Due to its programmatic nature,
$dotcacheworks in any Velocity context, including the Admin UI, provided the ViewTool is available.
π‘ Alternative Solution: Caching in the Admin UI with $dotcache
As mentioned, using heavy operations inside a Custom Field in the Admin UI can slow down the editor's experience. Since #dotcache is ineffective here, the solution is to use $dotcache in combination with the Velocity #define directive.
The #define directive allows you to capture the output of a Velocity block into a variable, which is perfect for then storing that variable (containing the rendered HTML) into the cache using $dotcache.
Here is the code snippet implementing this solution:
Code snippet
## 1. Try to get the HTML content from the cache
#set($html = $dotcache.get('html-block'))
## 2. If it is not in the cache (Cache Miss)
#if(!$html)
## Define a code block whose execution result will be captured into $htmlBlock
#define($htmlBlock)
I am cached content or velocity
Timestamp: $date
## Includes calls to macros (e.g., renderGreeting) and complex logic
#renderGreeting( "John Doe" )
#set( $fruits = ["Apple", "Banana", "Cherry"] )
#displayList( $fruits )
#end
## Assign the result of #define to the $html variable
#set($html = $htmlBlock)
## Store the result in the dotCMS cache
$dotcache.put('html-block', $html, 3600) ## 3600 seconds = 1 hour
## Output for the first time (not cached)
html first time: $html
#else
## Output for the cached request (Cache Hit)
html cached: $html
#end
## NOTE: The following block with #dotcache will NOT work in the Admin UI:
## #dotcache("jsanca-test-key",5)
## ## #end
## (Macros used in the example)
#macro( renderGreeting $name )
2Timestamp: $date
Hello, $name! Welcome to our application.
#end
#macro( displayList $items )
<ul>
#foreach( $item in $items )
<li>$item</li>
#end
</ul>
#end
Explanation of the Solution:
#set($html = $dotcache.get('html-block')): Attempts to retrieve the cached HTML.#if(!$html): If the HTML does not exist in the cache (Cache Miss), the main block is executed.#define($htmlBlock) ... #end: All the heavy or dynamic Velocity code we want to cache is wrapped in a#define. This executes the logic, including macros, and stores the HTML output within the$htmlBlockvariable.#set($html = $htmlBlock): The rendered content is assigned to the$htmlvariable.$dotcache.put('html-block', $html, 3600): The ViewTool is used to store the rendered HTML ($html) into the global dotCMS cache with a specific key and a Time-to-Live (TTL).Finally, the
$htmlvariable is printed, whether newly generated (first time) or retrieved from the cache (subsequent times).
π― Conclusion
Caching is an essential optimization tool in dotCMS. While the #dotcache directive is the preferred solution for the Front-End due to its concise syntax, the $dotcache ViewTool provides the programmatic flexibility needed to extend caching functionality to other contexts, such as the administrative layer. By combining $dotcache with #define, developers can ensure a fast end-user experience while also improving the efficiency and performance of the Admin UI for editors.