πŸš€ Optimizing dotCMS: The Importance of Block Caching with $dotcache and #dotcache

πŸš€ 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, $dotcache works 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:

  1. #set($html = $dotcache.get('html-block')): Attempts to retrieve the cached HTML.

  2. #if(!$html): If the HTML does not exist in the cache (Cache Miss), the main block is executed.

  3. #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 $htmlBlock variable.

  4. #set($html = $htmlBlock): The rendered content is assigned to the $html variable.

  5. $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).

  6. Finally, the $html variable 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.