feat: implement caching functionality with Valkey/Redis support in pl…#297
Conversation
There was a problem hiding this comment.
ℹ️ 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, andpaca.cache_deletehost functions inservices/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.Storeinto the plugin runtime viaHostServices.Cacheinservices/api/internal/bootstrap/app.go. - Add integration-level tests in
runtime_cache_test.gousing miniredis to exercise set/get, delete, TTL expiry, nil-backend behavior, and prefix isolation. - Add a WASI-reactor test fixture in
testdata/cacheplugin/main.goto drive the new host imports from real compiled WASM.
Kimi K2 (free via Pullfrog for OSS) | 𝕏
There was a problem hiding this comment.
ℹ️ No new issues in the incremental diff — the prior hardening suggestion about
writeToMemoryerror handling incache_getremains 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
TestCacheHostFunctionssubtests so the expensivewazero.CompileModulepass runs once under the race detector instead of five times. - Reused a single miniredis-backed
cache.StoreandRuntime/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 readingRuntime.serviceslive rather than compiling a second fixture.
Kimi K2 (free via Pullfrog for OSS) | 𝕏
…TL rejection in plugin system
…isterCacheFunctions
There was a problem hiding this comment.
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
cacheKeyPrefixto length-prefixed encoding (plugin:<len>:<name>:) so plugin names containing colons cannot produce colliding Redis keys. - Reject negative
ttlSecondsinpaca.cache_setand add aNegativeTTLRejectedregression test. - Update the prefix tests to expect the new length-prefixed format and add a colon-in-plugin-name collision test.
Kimi K2 (free via Pullfrog for OSS) | 𝕏
There was a problem hiding this comment.
ℹ️ No new issues in this delta — the
writeToMemoryhardening 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
writeToMemoryerrors inpaca.storage_get(registerDBFunctions) andpaca.cache_get(registerCacheFunctions), returning a zero-length response when allocation or an out-of-bounds memory write fails.
Kimi K2 (free via Pullfrog for OSS) | 𝕏
Co-authored-by: pullfrog[bot] <226033991+pullfrog[bot]@users.noreply.github.com>
There was a problem hiding this comment.
✅ No new issues found.
Reviewed changes — the latest commit applies the final documentation fix requested in the previous review.
- Fixed
paca.cache_getsignature in docs —docs/plugins/backend-plugin-system.mdnow listspaca.cache_get(key_ptr, key_len, value_ptr_ptr, value_len_ptr)instead of the olderresp_ptr/max_lenreturn-value form, matching the host-function implementation.
Kimi K2 (free via Pullfrog for OSS) | 𝕏

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/KVavailable via the host-function bridge, neither of which can express "cache this for N minutes." This is the host half of the plugin-sdk-goCacheAPI;paca-plugin-dashboardis the first consumer.Host functions (
services/api/internal/platform/plugin/runtime.go):paca.cache_get,paca.cache_set(with attlSecondsargument, 0 = no expiry), andpaca.cache_deleteto the host module, mirroring the existingstorage_get/storage_set/storage_delete(KV) trio.HostServices.Cache *cache.Storefield — the same Valkey/Redis-backed store already used bycached_service.goet al. — wired through ininternal/bootstrap/app.go(Cache: cacheStore).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.nilHostServices.Cache(not configured) degrades gracefully:cache_getalways misses,cache_set/cache_deleteare 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):testdata/cacheplugin, in the same style as the existingtestdata/poisonplugin) that calls the new host imports directly, so the tests exercise the real WASM↔host boundary rather than mocking it.FastForward, no real sleep), the nil-Cacheno-crash/no-op path, andcacheKeyPrefix's per-plugin uniqueness.cache.Storerather 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 ./...) andgo vet ./...continue to pass.