Skip to content

feature: Valkey cache integration#1263

Open
daric93 wants to merge 17 commits intogorse-io:masterfrom
daric93:feature/valkey-cache-integration
Open

feature: Valkey cache integration#1263
daric93 wants to merge 17 commits intogorse-io:masterfrom
daric93:feature/valkey-cache-integration

Conversation

@daric93
Copy link
Copy Markdown

@daric93 daric93 commented May 5, 2026

Summary

Fixes: #1260

Adds Valkey as a cache storage backend using the valkey-go client. Supports both standalone and cluster modes with TLS.

What's included

  • Full Database interface implementation in storage/cache/valkey.go — scores (documents), time series, key-value, and queue operations
  • FT.SEARCH-based document queries using Valkey Search (TAG + NUMERIC fields)
  • Time series storage using sorted set + hash with Go-side bucket aggregation
  • URL schemes: valkey://, valkeys://, valkey+cluster://, valkeys+cluster://
  • Config validation and documentation updates
  • Comprehensive test suite (unit, integration, benchmarks)
  • Docker Compose service using valkey/valkey-bundle:unstable (includes valkey-search)

Design decisions

  • Uses valkey-go (official Valkey client) rather than a Redis client for forward compatibility
  • Cluster mode deletes keys one-at-a-time to avoid CROSSSLOT errors
  • Valkey-specific escapeTag() function escapes all TAG special characters to prevent query injection
  • Thin wrapper methods (vDel, vHSet, vZAdd, vHGetAll) centralize cluster/standalone branching
  • DeleteScores loop has a max iteration guard (100) to prevent infinite loops
  • UpdateScores skips the non-atomic Exists check — HSet directly on keys found via FT.SEARCH

Why valkey-go over valkey-glide

The initial implementation used valkey-glide, but it requires CGo — it wraps a Rust core library via FFI. Gorse builds all default Docker images and release binaries with CGO_ENABLED=0 to produce fully static binaries that run in minimal FROM scratch containers (see every Dockerfile and build_release.yml). Enabling CGo would require a C toolchain in the build environment, a libc at runtime, break the FROM scratch pattern, increase image sizes, and complicate multi-arch cross-compilation.

valkey-go is a pure Go client with no CGo dependency, so it works with CGO_ENABLED=0 out of the box.

Testing

  • Inherits full baseTestSuite (TestDocument, TestPurge, TestTimeSeries, TestScanScores, etc.)
  • Valkey-specific tests: escape characters, pagination with tied scores, URL parsing
  • Benchmarks: document CRUD, time series ingest (single/batch), time series queries (small/large/wide range)
  • Requires Valkey with search module (port 6380 via docker-compose)

Changed files

File Change
storage/cache/valkey.go New — full cache backend implementation
storage/cache/valkey_test.go New — tests and benchmarks
storage/scheme.go Add Valkey URL prefix constants
storage/docker-compose.yml Add Valkey service
config/config.go Accept Valkey prefixes in validation
config/config.toml Document Valkey connection strings
config/config_test.go Validate Valkey prefixes
storage/cache/database_test.go Add time series benchmarks
go.mod / go.sum Add valkey-go dependency

daric93 added 17 commits April 29, 2026 08:22
Implement Valkey as a first-class cache store backend using valkey-glide
Go client. This enables Gorse users to use Valkey as an alternative to
Redis for the cache layer.

Changes:
- Add valkey://, valkeys://, valkey+cluster://, valkeys+cluster:// URL
  prefix constants and config validation
- Implement full cache.Database interface in storage/cache/valkey.go
  using valkey-glide v2 client (standalone + cluster modes)
- Time series implemented via Sorted Set + Hash with Go-side bucket
  aggregation (no TimeSeries module dependency)
- Score/document operations use valkey-search FT.* commands via
  CustomCommand
- Add integration test suite embedding baseTestSuite
- Add integration report with analysis and task breakdown

Signed-off-by: Daria Korenieva <daric2612@gmail.com>
Add valkey/valkey-bundle:unstable (valkey-search 1.2.0+ with full-text
search support) to storage/docker-compose.yml on port 6380.
Update default test URI to valkey://127.0.0.1:6380/.

Signed-off-by: Daria Korenieva <daric2612@gmail.com>
Signed-off-by: Daria Korenieva <daric2612@gmail.com>
…/gorse-io-gorse into feature/valkey-cache-integration
Signed-off-by: Daria Korenieva <daric2612@gmail.com>
Signed-off-by: Daria Korenieva <daric2612@gmail.com>
…er ops, handle username in URL

Signed-off-by: Daria Korenieva <daric2612@gmail.com>
Signed-off-by: Daria Korenieva <daric2612@gmail.com>
…ix TAG escaping: new escapeTag() covers all special chars (| * { } etc.) to prevent query injection via user-controlled values - Fix CROSSSLOT error: delete keys one-at-a-time in cluster mode via vDel() - Fix race condition: remove non-atomic Exists+HSet in UpdateScores - Add max iteration guard (100) to DeleteScores loop - Extract wrapper methods (vDel, vHSet, vZAdd, vHGetAll) to reduce cluster/standalone branching duplication - Extract groupTimeSeriesPoints() helper to DRY AddTimeSeriesPoints

Signed-off-by: Daria Korenieva <daric2612@gmail.com>
…/gorse-io-gorse into feature/valkey-cache-integration

# Conflicts:
#	storage/cache/valkey.go
Signed-off-by: Daria Korenieva <daric2612@gmail.com>
Signed-off-by: Daria Korenieva <daric2612@gmail.com>
…s - Add valkey/valkey-bundle:unstable service on port 6380 to unit_test job - Add VALKEY_URI env var for Linux unit tests - Add TestValkey to -skip pattern for macOS and Windows jobs (no Valkey service available on those runners)

Signed-off-by: Daria Korenieva <daric2612@gmail.com>
…glide requires CGO (Rust FFI core) and cannot build with CGO_ENABLED=0, which gorse uses for static Docker binaries. This is a confirmed upstream limitation (valkey-io/valkey-glide#4253). valkey-go is the official pure-Go Valkey client from the same org. It supports all needed commands (FT.SEARCH, HSET, ZADD, SCAN, etc.), works with CGO_ENABLED=0, and simplifies the code by using a single Client interface for both standalone and cluster modes. Changes: - Replace valkey-glide/go/v2 with valkey-io/valkey-go in go.mod - Rewrite storage/cache/valkey.go using valkey-go command builder API - Eliminate all if-isCluster branching (valkey-go handles routing) - Use DoMulti for pipeline batching instead of standalone batch - Update tests to use valkey-go client for FLUSHDB - Update URL parsers to return plain strings instead of glide types

Signed-off-by: Daria Korenieva <daric2612@gmail.com>
…/gorse-io-gorse into feature/valkey-cache-integration
Signed-off-by: Daria Korenieva <daric2612@gmail.com>
… offset in SearchScores, clarify UpdateScores pagination - Set(): use DoMulti to pipeline all SET commands in a single round-trip - AddScores(): use DoMulti to pipeline all HSET commands - SearchScores(): pass begin as LIMIT offset and (end-begin) as count instead of fetching from 0 and slicing in Go - UpdateScores(): add clarifying comments for the first-page re-fetch optimization that avoids pagination drift

Signed-off-by: Daria Korenieva <daric2612@gmail.com>
Copy link
Copy Markdown
Collaborator

@zhenghaoz zhenghaoz left a comment

Choose a reason for hiding this comment

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

Too many duplicate codes comparing to Redis backend. Please fix all tests. Could you reuse Redis client, but implement special timeseries interface if Valkey is detected?

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.

Add Valkey as a cache storage backend

2 participants