# How CFnew Implements Configuration Caching with Cloudflare KV

> Learn how CFnew drastically reduces Cloudflare KV read operations by implementing a two-level configuration caching strategy with in-memory storage and 30-second validation.

- Repository: [byJoey/cfnew](https://github.com/byJoey/cfnew)
- Tags: internals
- Published: 2026-05-23

---

**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.

```javascript
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

```javascript
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.

```javascript
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)

```javascript
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:

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

```

Updating configuration from an API endpoint:

```javascript
// 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:

```javascript
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.