Skip to content

feat(apisix-standalone): lrucache ttl and active invalidation#460

Merged
bzp2010 merged 6 commits into
mainfrom
bzp/feat-lrucache-ttl-and-invalidate
Jun 17, 2026
Merged

feat(apisix-standalone): lrucache ttl and active invalidation#460
bzp2010 merged 6 commits into
mainfrom
bzp/feat-lrucache-ttl-and-invalidate

Conversation

@bzp2010

@bzp2010 bzp2010 commented Jun 17, 2026

Copy link
Copy Markdown
Collaborator

Description

Replace the cache in the apisix-standalone backend with an LRU cache that supports TTL.

By default, the cache holds 16 cache keys with a TTL of 60 minutes.

The LRU cache supports overriding its size and TTL using the ADC_APISIX_STANDALONE_CACHE_MAX and ADC_APISIX_STANDALONE_CACHE_TTL_MS environment variables.

Additionally, this backend supports active cache invalidation. To enable this, set bypassCache in the task.opts of the sync API. This behavior first deletes the local cache, which triggers the cache initialization process and repopulates the cache. As a result, the sync API will perform a diff against the latest cache.

Fix: apache/apisix-ingress-controller#2774 (comment)

Checklist

  • I have explained the need for this PR and the problem it solves
  • I have explained the changes or the new features added to this PR
  • I have added tests corresponding to this change
  • I have updated the documentation to reflect this change
  • I have verified that this change is backward compatible

Summary by CodeRabbit

Release Notes

  • New Features

    • Added an optional bypassCache flag for sync/dump requests to force fresh configuration retrieval by invalidating the relevant cached entry first.
    • Added TASK_START-level logging for backend task events (event name and request id).
  • Tests

    • Added end-to-end coverage for bypassing cache invalidation, ensuring fresh data is fetched and the cache is repopulated.
  • Chores

    • Improved standalone caching by switching to an LRU cache with configurable max size and TTL via environment variables.

@coderabbitai

coderabbitai Bot commented Jun 17, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: c99a9b8e-f1e3-45d5-8e8d-e676da4d2451

📥 Commits

Reviewing files that changed from the base of the PR and between a200b9f and ed3c837.

📒 Files selected for processing (1)
  • libs/backend-apisix-standalone/src/cache.ts

📝 Walkthrough

Walkthrough

Replaces the four Map-based caches in backend-apisix-standalone with LRUCache instances configured via environment variables, adds an invalidate(key) helper, introduces a bypassCache option on BackendAPISIXStandalone that invalidates cached config and emits a TASK_START event during dump(), updates the CLI schema to accept the bypassCache flag, adds TASK_START logging in syncHandler, and adds lru-cache to the workspace dependency catalog and ESLint configuration.

Changes

LRU Cache and bypassCache Feature

Layer / File(s) Summary
LRU cache infrastructure and invalidate helper
pnpm-workspace.yaml, libs/backend-apisix-standalone/package.json, libs/backend-apisix-standalone/src/cache.ts, libs/backend-apisix-standalone/eslint.config.ts
Adds lru-cache ^11.0.0 to the workspace catalog and package dependencies. Replaces the four Map-based exports (version, latestVersion, config, rawConfig) with LRUCache instances sized and TTL-controlled by ADC_APISIX_STANDALONE_CACHE_MAX and ADC_APISIX_STANDALONE_CACHE_TTL_MS environment variables (defaults 16 and 3_600_000). Exports a new invalidate(key) function that removes the key from all four caches. Updates ESLint configuration to ignore lru-cache in dependency checks due to Nx resolver limitations.
BackendOpts type and bypassCache dump logic
libs/backend-apisix-standalone/src/index.ts
Imports invalidateCache, defines BackendOpts extending ADCSDK.BackendOptions with bypassCache?: boolean, updates the constructor signature, and adds conditional logic in dump() to call invalidateCache and emit a TASK_START event before the normal dump observable when bypassCache is set.
CLI schema input for bypassCache option
apps/cli/src/server/schema.ts
Adds an optional bypassCache boolean field (default false) to SyncTask.opts validation schema, allowing callers to explicitly request cache bypass during sync operations.
TASK_START event logging in syncHandler
apps/cli/src/server/sync.ts
Adds a backend.on('TASK_START', ...) listener in syncHandler that logs the event name at info level with req.requestId.
E2E tests for bypassCache invalidation
libs/backend-apisix-standalone/e2e/cache.e2e-spec.ts
Adds invalidateCache to the cache import and a new Cache - bypassCache invalidation e2e suite. Tests inject stale data into the cache, verify non-bypass dumps return stale data without API calls, confirm bypassCache fetches fresh data via API and repopulates the cache, and assert subsequent non-bypass dumps are served from cache without additional API calls.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes


Important

Pre-merge checks failed

Please resolve all errors before merging. Addressing warnings is optional.

❌ Failed checks (1 error, 1 warning)

Check name Status Explanation Resolution
Security Check ❌ Error Cache key exposed in logs: When bypassCache is true, the cache key is logged unredacted in TASK_START event at INFO level (libs/backend-apisix-standalone/src/index.ts:104, apps/cli/src/server/sync.... Redact cache key in the TASK_START event message before logging, e.g., use Cache invalidated instead of including the key, or hash the key if it must be logged. Additionally, consider adding authorization checks for the bypassCache fea...
E2e Test Quality Review ⚠️ Warning E2E test coverage is incomplete. Tests verify bypassCache works but lack critical scenarios: no tests for cache TTL expiration, max size eviction, env var validation, error handling, or concurrent... Add tests covering: (1) cache TTL expiration, (2) cache eviction when max size exceeded, (3) environment variable configuration, (4) error handling in dump/sync, and (5) concurrent bypassCache operations to ensure robustness against the...
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main changes: introducing LRU cache with TTL and active cache invalidation for the apisix-standalone backend.
Linked Issues check ✅ Passed The PR implements LRU cache with TTL and active cache invalidation via bypassCache flag, addressing the version mismatch and sync failure issues described in #2774.
Out of Scope Changes check ✅ Passed All changes are directly related to implementing LRU cache with TTL, active invalidation, and supporting infrastructure; no unrelated modifications detected.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch bzp/feat-lrucache-ttl-and-invalidate

Comment @coderabbitai help to get the list of available commands and usage tips.

@bzp2010 bzp2010 added the test/apisix-standalone Trigger the APISIX standalone test on the PR label Jun 17, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (2)
libs/backend-apisix-standalone/e2e/cache.e2e-spec.ts (2)

485-485: ⚡ Quick win

Use key invalidation in teardown to fully reset all cache buckets.

Line 485 clears only configCache and rawConfigCache; version and latestVersion can persist and couple later suites that reuse the same cache key.

Proposed fix
-  afterAll(() => (configCache.clear(), rawConfigCache.clear()));
+  afterAll(() => invalidateCache(cacheKey));
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@libs/backend-apisix-standalone/e2e/cache.e2e-spec.ts` at line 485, The
afterAll hook in the test file only clears configCache and rawConfigCache, but
does not clear the version and latestVersion cache buckets. This allows cached
state to persist between test suites, causing potential test coupling. Add calls
to clear the version and latestVersion cache buckets in the afterAll hook on the
same line, using the same pattern as the existing cache.clear() calls for
configCache and rawConfigCache to ensure all cache buckets are fully reset after
each test suite.

510-527: ⚡ Quick win

Assert TASK_START emission in the bypassCache test.

This suite validates data freshness and API activity, but it doesn’t assert the new TASK_START contract introduced for bypass invalidation. Adding that check will prevent silent regressions in the event/logging path.

Proposed enhancement
   it('dump with bypassCache fetches fresh data from APISIX', async () => {
     const freshBackend = new BackendAPISIXStandalone({
       server: server1,
       token: token1,
       tlsSkipVerify: true,
       cacheKey,
       bypassCache: true,
       ...defaultBackendOptions,
     });
 
     let apiCall = 0;
+    let taskStartCount = 0;
     const sub = freshBackend.on('AXIOS_DEBUG', () => apiCall++);
+    const taskSub = freshBackend.on('TASK_START', ({ name }) => {
+      taskStartCount++;
+      expect(name).toContain(`Cache invalidated for key: "${cacheKey}"`);
+    });
     const result = await dumpConfiguration(freshBackend);
     sub.unsubscribe();
+    taskSub.unsubscribe();
 
     expect(apiCall).toBeGreaterThan(0);
+    expect(taskStartCount).toEqual(1);
     expect(result).toMatchObject(syncedConfig);
   });
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@libs/backend-apisix-standalone/e2e/cache.e2e-spec.ts` around lines 510 - 527,
The test for the bypassCache feature in the dumpConfiguration call currently
only validates AXIOS_DEBUG event emissions but doesn't assert the TASK_START
event that was introduced for bypass invalidation. Add a listener for the
TASK_START event on the freshBackend instance similar to the existing
AXIOS_DEBUG listener, initialize a counter to track TASK_START emissions, and
then add an assertion to verify that TASK_START was emitted at least once during
the dumpConfiguration call to ensure the bypass invalidation contract is
properly tested.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@apps/cli/src/server/sync.ts`:
- Line 77: The `any` cast on the `task.opts` expression in the httpsAgent
assignment within sync.ts is masking a missing schema definition. Add
`tlsSkipVerify: z.boolean().optional()` to the `SyncTask.opts` schema definition
in schema.ts under the looseObject, then remove the `(task.opts as any)` cast
from sync.ts line 77 and similarly remove any `any` cast from validate.ts line
76 that accesses this property, allowing TypeScript to properly type-check the
access to the tlsSkipVerify property.

In `@libs/backend-apisix-standalone/src/cache.ts`:
- Around line 7-8: The Number() conversions for the max and ttl environment
variables lack validation, allowing invalid values like NaN, zero, or negative
numbers to pass through and destabilize the cache. Add validation checks after
converting the environment variables for both max and ttl to ensure they are
positive numbers, and apply sensible clamping to keep them within acceptable
ranges (for example, max should be at least 1 and ttl should be greater than
zero). Handle cases where the conversion results in NaN or invalid values by
either using the default fallback values or raising an error to fail fast on
misconfiguration.

---

Nitpick comments:
In `@libs/backend-apisix-standalone/e2e/cache.e2e-spec.ts`:
- Line 485: The afterAll hook in the test file only clears configCache and
rawConfigCache, but does not clear the version and latestVersion cache buckets.
This allows cached state to persist between test suites, causing potential test
coupling. Add calls to clear the version and latestVersion cache buckets in the
afterAll hook on the same line, using the same pattern as the existing
cache.clear() calls for configCache and rawConfigCache to ensure all cache
buckets are fully reset after each test suite.
- Around line 510-527: The test for the bypassCache feature in the
dumpConfiguration call currently only validates AXIOS_DEBUG event emissions but
doesn't assert the TASK_START event that was introduced for bypass invalidation.
Add a listener for the TASK_START event on the freshBackend instance similar to
the existing AXIOS_DEBUG listener, initialize a counter to track TASK_START
emissions, and then add an assertion to verify that TASK_START was emitted at
least once during the dumpConfiguration call to ensure the bypass invalidation
contract is properly tested.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: c785b6c6-83cf-4a62-939d-6bcbe4f1186b

📥 Commits

Reviewing files that changed from the base of the PR and between 4f06782 and 728927d.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (6)
  • apps/cli/src/server/sync.ts
  • libs/backend-apisix-standalone/e2e/cache.e2e-spec.ts
  • libs/backend-apisix-standalone/package.json
  • libs/backend-apisix-standalone/src/cache.ts
  • libs/backend-apisix-standalone/src/index.ts
  • pnpm-workspace.yaml

Comment thread apps/cli/src/server/sync.ts
Comment thread libs/backend-apisix-standalone/src/cache.ts Outdated
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

test/apisix-standalone Trigger the APISIX standalone test on the PR

Projects

None yet

Development

Successfully merging this pull request may close these issues.

bug: Leader pod lease timeout leads to syncing errors that result in single pod deployments not receiving IP updates

2 participants