Galia Guide 1.0

Caching

Galia offers a sophisticated and customizable caching system that is capable of meeting a variety of needs while remaining easy to use. Several tiers of cache are available:

  1. Hints to client-side caches;
  2. A heap info cache, which caches source image characteristics in memory;
  3. An info cache, which caches source image characteristics;
  4. A variant cache, which caches processed images.

Client-side caching

The Cache-Control response header, which is configurable via the cache.client.* keys in the configuration file, can provide caching hints to clients. To enable this header, set the cache.client.enabled key to true.

The default settings look something like this:

cache.client.max_age: 2592000
cache.client.shared_max_age:
cache.client.public: true
cache.client.private: false
cache.client.no_cache: false
cache.client.no_store: false
cache.client.must_revalidate: false
cache.client.proxy_revalidate: false
cache.client.no_transform: true

These settings tell clients they can keep cached images for 2,592,000 seconds (30 days).

The Cache-Control header may be supplied directly to clients, or an application instance may be set up behind a content delivery network (CDN) which relies on this header to control how long it caches the content that it supplies to clients. Use of a CDN is a best practice for high-traffic production servers, as its low latency will outperform any local variant cache.

Server-side caching

Variant cache

The variant cache caches post-processed images. Variant caches are pluggable in order to enable different cache stores. (See Plugins.)

In typical use, variant caching will greatly reduce load on the server and improve response times accordingly. There are other ways of caching variants, such as by using a caching reverse proxy server or CDN, which are both good ideas, and can be useful instead of, or in conjunction with, the built-in variant cache.

Variant caching is disabled by default. To enable it, set cache.server.variant.enabled to true, and set cache.server.variant.implementation to the name of a cache, such as FilesystemCache.

The variant cache is shared across endpoints. Requests for the same image from different endpoints should return the same cached image.

Requests for full-sized, unaltered source images are not cached, and are instead streamed through with no processing.

Info cache

The info cache caches source image information (dimensions, tile sizes, component depth, etc.). These are used primarily by IIIF Image API information endpoints, but also by some other components.

Info caches are pluggable in order to enable different cache stores. (See Plugins.)

The info cache is disabled by default. To enable it, set cache.server.info.enabled to true, and set cache.server.info.implementation to the name of a cache, such as FilesystemCache.

Information response representations are not cached—only their underlying image info. This means that other changes that would affect the contents of information responses can be made without invalidating the cache.
Cached information includes image dimensions, tile dimensions, metadata, and other properties. When the info cache is enabled, source images should not be modified in ways that would change any of these properties without their identifier also changing. If a source image needs to be changed without its identifier changing, any cached info relating to it should be manually evicted.

Heap info cache

The heap info cache caches image info objects in the Java heap independently of the info cache. When both are enabled, the heap info cache acts as a "level 1" cache in front of the "level 2" info cache:

  1. Requested image infos are retrieved from the heap info cache, if available;
  2. If not, they are retrieved from the info cache, if available, and also added to the heap info cache;
  3. If not available in any cache, they are read from the source image, and added to both the heap info and info caches—whichever are enabled.

The info cache can be enabled or disabled via the cache.server.info.enabled configuration key.

The maximum size of the heap info cache is hard-coded to a reasonable percentage of the maximum heap size, and is not configurable. As infos tend to be very small, the maximum size is unlikely to ever be reached, but if it is, the least-recently-accessed infos will be invalidated as necessary to accommodate fresher ones.

The heap info cache's content never expires, but it is not persisted.

Modes of operation

The variant and info caches can be configured to operate in one of two ways:

Conservative (cache.server.resolve_first: true)
Source images are looked up and verified to exist before cached representations are returned. This precludes returning a cached representation when the underlying resource no longer exists, but also impairs response times by a variable amount, depending on the source.
Aggressive (cache.server.resolve_first: false)
Cached content is returned immediately, if available. This is faster, but inconsistencies can develop between the source and the cache if the former ever changes.

Maintenance

Because cached content is not automatically deleted after becoming invalid, there will likely be a certain amount of invalid content taking up space in the cache at any given time. Without periodic maintenance, the amount can only grow. If this is a problem, it can be dealt with manually using the HTTP API, or automatically using the cache worker, which periodically purges invalid items. (See the cache.server.worker.* configuration options.)

When operating a cluster of application instances, consider enabling the cache worker on only one node.

Limiting

Most caches age-limit their content based on last-accessed or last-modified time. Depending on the amount of source content served, the types of variants generated, the time-to-live setting, and how often maintenance is performed, the cache may grow very large. Its size is not tracked, as this would be either expensive, or, for some cache implementations, impossible. Managing the cache size is therefore the responsibility of the administrator, and it can be accomplished by any combination of:

  1. Performing maintenance more often;
  2. Reducing the time-to-live (using the cache.server.variant.ttl_seconds and/or cache.server.info.ttl_seconds configuration keys);
  3. Allocating more storage to the cache.

Bundled implementations

All implementations are thread-, process-, and cluster-safe. Multiple application instances can be pointed at the same cache store without conflicting.

FilesystemCache

FilesystemCache caches content in a filesystem tree. The tree structure looks like:

  • cache.FilesystemCache.pathname/
    • image/
      • Intermediate subdirectories (2)
        • {hashed identifier}_{hashed operation list string representation}.{output format extension} (3)
    • info/
      • Intermediate subdirectories (2)
        • {hashed identifier}.json (3)
  1. Some filesystems have per-directory file count limits, or thresholds beyond which performance starts to degrade. To work around this, cache files are stored in subdirectory trees consisting of leading fragments of identifier MD5 hashes, configurable by cache.FilesystemCache.dir.depth and cache.FilesystemCache.dir.name_length.
  2. Identifier and operation list strings in filenames are MD5-hashed in order to allow for lengths longer than the filesystem's filename length limit.

Cache files are created with a .tmp extension and moved into place when closed for writing.

File last-access times (atime) are used to determine validity. Filesystems used to store cached content should not be mounted with the noatime option, as this will disable recording of last-access times, forcing a fallback to last-modified times (mtime), which aren't ideal for an LRU cache.

HeapCache

HeapCache caches variant images and metadata in the Java heap, which is the main area of memory available to the JVM. This is the fastest cache implementation, but it cannot be shared across instances, nor persisted.

In addition to working as an LRU cache based on last-access time, this cache also supports a target size (cache.HeapCache.target_size). When it has been exceeded, the minimum number of least-recently-accessed items are purged that will reduce it back down to this size. (The configured target size may be safely changed while the application is running.)

When using this cache, ensure that your heap is able to grow large enough to accommodate the desired target size (using the -Xmx VM option), and that you have enough RAM to accommodate this size.

Plugin implementations

See Plugins.