Caching with Google Cloud Datastore
Relevant source files
The following files were used as context for generating this wiki page:Rendertron provides an optional caching layer that uses Google Cloud Datastore to store and serve previously rendered pages. This feature is designed to improve performance and reduce costs by avoiding redundant rendering operations for frequently accessed URLs. When enabled, the caching mechanism intercepts incoming requests, serves content from the cache if available and valid, and stores newly rendered content for future requests.
The caching is implemented as a Koa middleware that integrates into the Rendertron request lifecycle. It uses the full request URL as the cache key, ensuring that different query parameters result in distinct cache entries. This functionality relies on the @google-cloud/datastore package.
Sources: src/datastore-cache.ts, README.md:104-105, package.json:29
Configuration
To enable Datastore caching, you must create a config.json file in the project root and set the datastoreCache property to true.
| Option | Type | Default | Description |
|---|---|---|---|
datastoreCache | boolean | false | Set to true to enable caching with Google Cloud Datastore. |
Sources: src/config.ts:28-35, README.md:104-105
Architecture
The caching system is encapsulated within the DatastoreCache class. This class is responsible for initializing the connection to Datastore, providing the Koa middleware for request handling, and managing cache entries.
DatastoreCache Class
The DatastoreCache class is the central component of the caching feature.
This diagram illustrates the structure of the DatastoreCache class.
*datastore**: An instance of the @google-cloud/datastore client.
clearCache(): A method to purge all 'Page' entities from the Datastore cache.cacheContent(): A method to save a rendered page's headers and payload to Datastore.middleware(): A factory method that returns the Koa middleware for handling caching logic.
Sources: src/datastore-cache.ts:31-131
Request Flow
The caching logic is executed as a Koa middleware. The following diagram shows the sequence of operations for both a cache hit and a cache miss.
This flow demonstrates that only successful renders (HTTP status 200) are cached. Non-200 responses from the renderer are not stored in the cache.
Sources: src/datastore-cache.ts:92-131, src/test/datastore-cache-test.ts:131-138
Datastore Entity Model
Cached items are stored in Google Cloud Datastore as entities of the kind Page. The key for each entity is the full URL of the rendered request.
Page Entity Schema
The diagram below shows the structure of a Page entity.
The following table details the fields stored for each cached page.
| Field | Data Type | Description | Indexed |
|---|---|---|---|
saved | Date | The date and time when the content was cached. | Yes |
expires | Date | The date and time when the cached content expires. The cache duration is hardcoded to 24 hours (1440 minutes). | Yes |
headers | string | A JSON string representation of the HTTP response headers. | No |
payload | string | A JSON string representation of the response body Buffer. | No |
Sources: src/datastore-cache.ts:23-28, 59-81
Caching Behavior
Cache Creation
- A cache entry is created only when a render request is successfully processed and returns an HTTP status of
200. - The full request URL, including query parameters, is used as the cache key.
- The cache lifetime is hardcoded to 24 hours (
60 * 24minutes).
Sources: src/datastore-cache.ts:60, 96, 128-130, src/test/datastore-cache-test.ts:52-66
Cache Retrieval (Cache Hit)
- On a new request, the middleware checks for a valid, non-expired cache entry in Datastore.
- If a valid entry is found, its content and headers are served directly.
- A custom header,
x-rendertron-cached, is added to the response, containing the UTC timestamp of when the item was originally cached. - The original handler/renderer is not invoked, as confirmed by tests where
handlerCalledCountdoes not increment on a cache hit.
Sources: src/datastore-cache.ts:101-122, src/test/datastore-cache-test.ts:52-66
Cache Invalidation
- Expiration: Entries are considered invalid if their
expirestimestamp is in the past. The middleware checks this on every request. - Manual Clearing: The
clearCache()method can be called to programmatically delete allPageentities from Datastore. This is used in tests to ensure a clean state before execution.
Sources: src/datastore-cache.ts:38-47, 103-104, src/test/datastore-cache-test.ts:41-43
Handling Headers and Compression
- The cache stores and restores all response headers, including
Content-Encodingfor compressed responses (e.g.,gzip). - Tests confirm that if a compressed response is cached, subsequent cache hits will also serve the compressed content with the correct headers.
Sources: src/datastore-cache.ts:105-106, src/test/datastore-cache-test.ts:91-112