Chat2DB Domain Caching Architecture: How Cache Invalidation Works

Chat2DB implements a lightweight domain-level caching layer using Guava Cache with proactive invalidation triggered by DDL operations and fuzzy key matching.

The OtterMind/Chat2DB repository employs a domain-centric caching strategy to minimize expensive database metadata queries. This architecture wraps Guava Cache in a static facade pattern, providing deterministic key generation and explicit invalidation mechanisms that keep metadata fresh while reducing round-trips to underlying data sources.

Core Components of the Domain Caching Layer

MemoryCacheManage (Low-Level Guava Wrapper)

Located in chat2db-community-server/chat2db-community-domain/chat2db-community-domain-core/src/main/java/ai/chat2db/community/domain/core/cache/MemoryCacheManage.java, this class instantiates a Guava Cache<Object, Object> with configurable maximum size and time-to-live policies. It exposes primitive operations including get, put, getList, fuzzyDelete, and close, handling thread-safety internally through Guava's concurrent primitives.

CacheManage (Static Facade)

The CacheManage class in the same package provides a singleton static interface that delegates to MemoryCacheManage. Domain services invoke CacheManage.get(), CacheManage.getList(), and CacheManage.fuzzyDelete() without managing cache instance lifecycle. This design ensures consistent cache access patterns across DbTableServiceImpl, DbViewServiceImpl, and DbSqlParserServiceImpl.

CacheKey (Deterministic Key Generation)

The CacheKey utility generates string-based identifiers following patterns like TABLE_{dataSourceId}_{tableName} or COLUMN_{dataSourceId}_{tableName}_{columnName}. By embedding the data source UUID and entity identifiers, keys guarantee that logical entities map to unique cache entries while enabling prefix-based bulk operations.

Cache Retrieval and Storage Flow

When a service requests metadata, it first constructs a cache key via CacheKey and calls CacheManage.get(). On cache miss, the service executes the database query through DBManager or Chat2DBMetaData, stores the result using CacheManage.put(), and returns the fresh data. The underlying Guava cache enforces a default 30-minute expiration and maximum size bounds to prevent memory exhaustion.

Cache Invalidation Mechanisms

Fuzzy Delete Pattern

The primary invalidation mechanism, fuzzyDelete(String pattern), iterates over cached entries and removes keys whose string representation starts with the supplied prefix. This allows bulk invalidation without enumerating individual keys. For example, CacheManage.fuzzyDelete(CacheKey.getTableKey(dataSourceId, "")) purges all table metadata for a specific data source.

DDL-Triggered Invalidation

Domain services explicitly invalidate cache entries after successful DDL operations. In DbTableServiceImpl, after executing CREATE, DROP, or ALTER statements, the service calls CacheManage.fuzzyDelete() with patterns matching the affected entities. This removes cached table entries and associated column caches, ensuring subsequent reads reflect schema changes immediately.

Connection and Schema Refresh Events

When data source configurations change or users trigger manual refresh via the UI, CacheManage.invalidateAll() clears the entire metadata cache. Because CacheKey embeds data source IDs, connection changes automatically isolate cached data through distinct key namespaces, though explicit clearing ensures no stale cross-contamination.

Implementation Examples

The following patterns demonstrate domain cache usage in Chat2DB:

// Retrieving cached table list with fallback supplier
List<Table> tables = CacheManage.getList(
    CacheKey.getDataBasesKey(dataSourceId),
    Table.class,
    () -> dbManager.showTables(dataSourceId)
);
// Invalidating cache after dropping a table
public void dropTable(DropTableParam param) {
    dbManager.executeDrop(param);
    // Remove all table entries for this datasource
    CacheManage.fuzzyDelete(CacheKey.getTableKey(param.getDataSourceId(), ""));
    // Remove column cache for the specific table
    CacheManage.fuzzyDelete(CacheKey.getColumnKey(
        param.getDataSourceId(), param.getTableName(), ""));
}
// Manual cache refresh for schema updates
public void refreshAllCaches(String dataSourceId) {
    CacheManage.fuzzyDelete(CacheKey.getTableKey(dataSourceId, ""));
    CacheManage.fuzzyDelete(CacheKey.getViewKey(dataSourceId, ""));
    CacheManage.fuzzyDelete(CacheKey.getColumnKey(dataSourceId, "", ""));
}

Summary

  • Guava-based implementation: MemoryCacheManage wraps Guava Cache with size and time-based eviction policies.
  • Static facade pattern: CacheManage provides singleton access to cache operations across domain services.
  • Deterministic key generation: CacheKey creates structured identifiers enabling prefix-based invalidation.
  • Proactive invalidation: DDL operations trigger fuzzyDelete to purge stale metadata immediately.
  • Thread-safe design: Concurrent access is handled internally by Guava's cache implementation.

Frequently Asked Questions

How does Chat2DB handle cache invalidation when table schemas change?

When DDL statements like ALTER or DROP execute successfully, domain services such as DbTableServiceImpl invoke CacheManage.fuzzyDelete() with patterns matching the affected data source and table. This removes all cached metadata for those entities before the next read operation, ensuring consistency without waiting for natural expiration.

What caching library does Chat2DB use for its domain layer?

Chat2DB uses Google's Guava Cache library through the MemoryCacheManage wrapper class. This provides built-in thread safety, configurable expiration policies, and memory bounding through maximum size constraints.

Can cache entries expire automatically in Chat2DB?

Yes, the underlying Guava cache configuration in MemoryCacheManage applies a default 30-minute time-to-live policy alongside maximum size limits. Entries expire automatically after the configured duration, even without explicit invalidation calls.

How does Chat2DB prevent cache key collisions between different data sources?

The CacheKey class embeds the data source UUID into every cache key (e.g., TABLE_{dataSourceId}_{tableName}). This creates isolated namespaces for each connection, preventing collisions while enabling targeted invalidation for specific sources.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →