Skip to content

feat: implement caching functionality with Valkey/Redis support in pl…#297

Merged
pikann merged 5 commits into
masterfrom
feature/implement-caching-functionality-in-plugin-system
Jul 21, 2026
Merged

feat: implement caching functionality with Valkey/Redis support in pl…#297
pikann merged 5 commits into
masterfrom
feature/implement-caching-functionality-in-plugin-system

Conversation

@pikann

@pikann pikann commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

This pull request wires a Valkey/Redis-backed TTL cache into the plugin host runtime, so WASM plugins have somewhere to cache derived data with an expiry — previously plugins only had Postgres-backed DB/KV available via the host-function bridge, neither of which can express "cache this for N minutes." This is the host half of the plugin-sdk-go Cache API; paca-plugin-dashboard is the first consumer.

Host functions (services/api/internal/platform/plugin/runtime.go):

  • Added paca.cache_get, paca.cache_set (with a ttlSeconds argument, 0 = no expiry), and paca.cache_delete to the host module, mirroring the existing storage_get/storage_set/storage_delete (KV) trio.
  • Backed by a new HostServices.Cache *cache.Store field — the same Valkey/Redis-backed store already used by cached_service.go et al. — wired through in internal/bootstrap/app.go (Cache: cacheStore).
  • Namespaced per plugin via cacheKeyPrefix(pluginName) ("plugin:<name>:"), so two plugins can use the same logical key without colliding. Unlike DB/KV, which get physical isolation for free from each plugin's own Postgres schema, Cache has no storage of its own — this prefix is the only thing standing in for that, and it's derived from the caller identity fixed at host-module registration time, never from anything plugin-supplied.
  • A nil HostServices.Cache (not configured) degrades gracefully: cache_get always misses, cache_set/cache_delete are no-ops — a plugin's Cache is meant for recomputable data, so a missing backend means "always recompute," not a failure.

Tests (runtime_cache_test.go, testdata/cacheplugin/main.go):

  • Added a minimal hand-written WASM fixture (testdata/cacheplugin, in the same style as the existing testdata/poisonplugin) that calls the new host imports directly, so the tests exercise the real WASM↔host boundary rather than mocking it.
  • Covers set-then-get (including that the value lands under the expected namespaced key in the store), get-miss, delete, TTL expiry (via miniredis's FastForward, no real sleep), the nil-Cache no-crash/no-op path, and cacheKeyPrefix's per-plugin uniqueness.
  • Uses a real miniredis-backed cache.Store rather than a fake, so the tests confirm the host functions actually reach a Valkey/Redis-protocol server with the right key/value/TTL.

Full existing suite (go test ./...) and go vet ./... continue to pass.

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

ℹ️ No critical issues — one minor hardening suggestion inline.

Reviewed changes — this PR adds Valkey/Redis-backed cache host functions to the plugin WASM runtime and wires the existing application cache store into the plugin HostServices.

  • Add paca.cache_get, paca.cache_set, and paca.cache_delete host functions in services/api/internal/platform/plugin/runtime.go, with per-plugin key namespacing (plugin:<name>:) and graceful degradation when no cache backend is configured.
  • Wire the shared cache.Store into the plugin runtime via HostServices.Cache in services/api/internal/bootstrap/app.go.
  • Add integration-level tests in runtime_cache_test.go using miniredis to exercise set/get, delete, TTL expiry, nil-backend behavior, and prefix isolation.
  • Add a WASI-reactor test fixture in testdata/cacheplugin/main.go to drive the new host imports from real compiled WASM.

Pullfrog  | Fix all ➔Fix 👍s ➔View workflow run | Using Kimi K2 (free via Pullfrog for OSS) | 𝕏

Comment thread services/api/internal/platform/plugin/runtime.go Outdated

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

ℹ️ No new issues in the incremental diff — the prior hardening suggestion about writeToMemory error handling in cache_get remains unresolved.

Reviewed changes — refactored the cache host-function tests to share one WASM fixture compilation and one Runtime instance across subtests, avoiding repeated compile passes that previously timed out CI under -race.

  • Merged the five standalone cache host-function tests into TestCacheHostFunctions subtests so the expensive wazero.CompileModule pass runs once under the race detector instead of five times.
  • Reused a single miniredis-backed cache.Store and Runtime/instance across subtests, with unique keys per scenario keeping assertions independent.
  • Exercised the nil-backend path via a subtest that temporarily clears rt.services.Cache, relying on the registered closures reading Runtime.services live rather than compiling a second fixture.

Pullfrog  | Fix it ➔View workflow run | Using Kimi K2 (free via Pullfrog for OSS) | 𝕏

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Important

This batch hardens key namespacing and rejects invalid TTLs, but the newly added documentation for paca.cache_get does not match the host function it describes.

Reviewed changes — since the last review, the PR added user-facing docs, hardened the per-plugin key prefix against colons in plugin names, and rejected negative TTL values before they reach Redis.

  • Add the Plugin Cache host-function docs to docs/plugins/backend-plugin-system.md, including the TTL behavior and nil-backend fallback.
  • Switch cacheKeyPrefix to length-prefixed encoding (plugin:<len>:<name>:) so plugin names containing colons cannot produce colliding Redis keys.
  • Reject negative ttlSeconds in paca.cache_set and add a NegativeTTLRejected regression test.
  • Update the prefix tests to expect the new length-prefixed format and add a colon-in-plugin-name collision test.

Pullfrog  | Fix all ➔Fix 👍s ➔View workflow run | Using Kimi K2 (free via Pullfrog for OSS) | 𝕏

Comment thread docs/plugins/backend-plugin-system.md Outdated

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

ℹ️ No new issues in this delta — the writeToMemory hardening from the previous review has been applied.

Reviewed changes — the latest commit only touches runtime.go and addresses the allocation-failure panic risk that was flagged earlier.

  • Check writeToMemory errors in paca.storage_get (registerDBFunctions) and paca.cache_get (registerCacheFunctions), returning a zero-length response when allocation or an out-of-bounds memory write fails.

Pullfrog  | Fix it ➔View workflow run | Using Kimi K2 (free via Pullfrog for OSS) | 𝕏

Co-authored-by: pullfrog[bot] <226033991+pullfrog[bot]@users.noreply.github.com>

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

✅ No new issues found.

Reviewed changes — the latest commit applies the final documentation fix requested in the previous review.

  • Fixed paca.cache_get signature in docsdocs/plugins/backend-plugin-system.md now lists paca.cache_get(key_ptr, key_len, value_ptr_ptr, value_len_ptr) instead of the older resp_ptr/max_len return-value form, matching the host-function implementation.

Pullfrog  | View workflow run | Using Kimi K2 (free via Pullfrog for OSS) | 𝕏

@pikann
pikann merged commit fc81182 into master Jul 21, 2026
4 checks passed
@pikann
pikann deleted the feature/implement-caching-functionality-in-plugin-system branch July 21, 2026 09:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant