How CFnew Implements Configuration Caching with Cloudflare KV

CFnew employs a two-level caching strategy that keeps the full configuration object in memory while validating freshness against a 13-byte version key every 30 seconds, drastically reducing Cloudflare KV read operations.

The open-source CFnew project (available at byJoey/cfnew) minimizes latency and KV costs by implementing an intelligent configuration caching layer. Instead of reading the full configuration blob on every request, it uses a tiny version key to determine when to refresh data. This approach balances performance with consistency across Cloudflare Worker isolates.

Two-Level Cache Architecture

CFnew stores all runtime configuration in a Cloudflare KV namespace bound to env.C. To avoid expensive KV reads on every request, the implementation uses a hybrid caching strategy spanning memory and KV storage.

In-Memory Cache Layer

The in-memory cache holds the complete configuration object (kvConfig) and the last load timestamp (kvConfigLastLoad). This data persists for the lifetime of the Worker instance, effectively caching configuration for up to 5 hours without additional KV operations.

Short-Window KV Version Check

A 30-second short-window cache (KV_CACHE_TTL) stores only the version identifier c_ver (approximately 13 bytes). Before reading the full configuration, the Worker checks this lightweight key. If the version matches the cached value, the system skips the heavy KV read and simply updates the in-memory timestamp.

Initializing the KV Store (initKVStore)

When a request arrives, the system calls initKVStore(env) (lines 208–214 in 明文源吗) to bind the KV namespace and perform the initial configuration load.

async function initKVStore(env) {
    if (env.C) {
        try {
            kvStore = env.C;          // ← bind KV namespace
            await loadKVConfig();     // ← initial load
        } catch (error) {
            kvStore = null;
        }
    }
}

This function assigns the KV binding to the module-level variable kvStore and immediately hydrates the cache.

Loading Configuration with Conditional Refresh (loadKVConfig)

The loadKVConfig(force = false) function (lines 220–254 in 明文源吗) implements the core caching logic. It avoids KV reads through three mechanisms:

  1. Time-based short-circuit: Returns early if called within 30 seconds of the last load
  2. Version comparison: Reads only the c_ver key and compares it against kvConfigVersion
  3. Conditional full read: Fetches the complete config blob c only when the version changes
async function loadKVConfig(force = false) {
    if (!kvStore) return;

    // 30 s short‑window cache
    if (!force && kvConfigLastLoad > 0 && (Date.now() - kvConfigLastLoad) < KV_CACHE_TTL) {
        return;
    }

    // ---- version check -------------------------------------------------
    let ver = '';
    try { ver = (await kvStore.get('c_ver')) || ''; } catch (_) {}

    // If version unchanged → only refresh timestamp
    if (!force && ver && ver === kvConfigVersion && kvConfig && Object.keys(kvConfig).length > 0) {
        kvConfigLastLoad = Date.now();
        return;
    }

    // ---- full config read -----------------------------------------------
    const configData = await kvStore.get('c');
    if (configData) kvConfig = JSON.parse(configData);
    kvConfigVersion = ver;
    kvConfigLastLoad = Date.now();
}

This design ensures that after the initial load, subsequent requests within the 30-second window require zero KV operations. After the window expires, only the tiny c_ver key is fetched to validate freshness.

Persisting Updates and Version Bumping (saveKVConfig)

When configuration changes occur, saveKVConfig() (lines 253–269 in 明文源吗) writes the data and increments the version key to invalidate caches across all isolates.

async function saveKVConfig() {
    if (!kvStore) return;
    const configString = JSON.stringify(kvConfig);
    await kvStore.put('c', configString);
    const newVer = String(Date.now());          // tiny version key
    kvConfigVersion = newVer;
    try { await kvStore.put('c_ver', newVer); } catch (_) {}
    kvConfigLastLoad = Date.now();
}

The new version number forces other Worker instances (or subsequent requests after their short-window TTL expires) to reload the full configuration.

Accessing Configuration Values

Two helper functions provide safe access to the cached configuration:

  • getConfigValue(key, defaultValue): Reads from the in-memory cache first (lines 71–76)
  • setConfigValue(key, value): Updates the in-memory object and immediately persists to KV (lines 78–81)
function getConfigValue(key, defaultValue = '') {
    if (kvConfig[key] !== undefined) return kvConfig[key];
    return defaultValue;
}

async function setConfigValue(key, value) {
    kvConfig[key] = value;
    await saveKVConfig();
}

Practical Usage Examples

Reading a configuration value with a fallback:

const dnsEndpoint = getConfigValue('customDNS', 'https://223.5.5.5/dns-query');

Updating configuration from an API endpoint:

// UI sends { key: 'ech', value: 'yes' }
await setConfigValue('ech', 'yes');   // persists to KV and bumps c_ver

Forcing a cache refresh after manual KV edits:

await loadKVConfig(true);   // bypasses the 30 s short‑window cache

Summary

  • Two-level caching: CFnew combines an in-memory object cache with a 30-second version key validation to minimize KV reads.
  • Efficient invalidation: The c_ver key (approximately 13 bytes) acts as a freshness signal, enabling cross-isolate consistency without reading the full configuration blob.
  • Low KV costs: The hot path requires reading only the tiny version key every 30 seconds; full configuration reads occur only when changes are detected.
  • Automatic persistence: The setConfigValue helper ensures immediate consistency by writing to KV and updating the version key atomically.

Frequently Asked Questions

How does CFnew avoid reading the full configuration on every request?

CFnew implements a short-window TTL of 30 seconds that stores only the version key c_ver in KV. The system checks this tiny key to determine if the in-memory cache is still valid. If the version matches, it skips reading the full configuration object entirely, reducing bandwidth and latency.

What happens when configuration is updated in one Worker isolate?

When setConfigValue is called, it executes saveKVConfig, which writes the new configuration to the c key and generates a new timestamp for c_ver. Other Worker isolates will detect this version change on their next 30-second check and reload the configuration, ensuring consistency across instances.

How long does the in-memory cache persist?

The in-memory cache persists for the lifetime of the Worker instance, which can be up to 5 hours on Cloudflare's platform. The cache is only invalidated when the Worker restarts or when the c_ver check reveals a newer version, triggering a full reload via loadKVConfig.

Can I force an immediate configuration reload?

Yes. Pass true to the force parameter in loadKVConfig(true). This bypasses both the 30-second short-window check and the version comparison, immediately fetching the latest configuration and version key from KV regardless of cache state.

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 →