Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion .github/workflows/agent.yml
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,10 @@ jobs:
with:
go-version: '1.24.0'
check-latest: true
- name: Start Redis
uses: supercharge/redis-github-action@1.5.0
with:
redis-version: 6
- name: coveralls
id: coveralls
run: |
Expand Down Expand Up @@ -111,7 +115,7 @@ jobs:
- name: Start Redis
uses: supercharge/redis-github-action@1.5.0
with:
redis-version: 4
redis-version: 6
- name: acceptance test
run: |
make -e setup build
Expand Down
37 changes: 37 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,43 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).

## [Unreleased]

### New Features

* **Redis Streams for Persistent Notification Delivery**: Added Redis Streams implementation as an alternative to Redis Pub/Sub for notification synchronization across Agent nodes. Redis Streams provides:
- Persistent message delivery with acknowledgment
- Automatic retries with exponential backoff
- Consumer groups for load balancing
- Configurable batching and flush intervals
- Connection error recovery with reconnection logic
- Comprehensive metrics tracking

Configure via `synchronization.notification.default: "redis-streams"` in config.yaml. See [docs/redis-streams.md](docs/redis-streams.md) for configuration options including batch_size, flush_interval, max_retries, and connection_timeout.

* **CMAB Redis Cache Support** ([#447](https://github.com/optimizely/agent/pull/447)): Added Redis cache support for Contextual Multi-Armed Bandit (CMAB) decisions following the same plugin-based architecture as ODP cache:
- In-memory LRU cache (default, size: 10000, TTL: 30m)
- Redis cache for multi-instance deployments with shared caching
- Configurable via `client.cmab.cache` in config.yaml
- Supports both in-memory and Redis cache backends

* **Flexible Redis Authentication**: Added support for flexible Redis password field names across all Redis clients (ODP cache, UPS, CMAB cache, and synchronization):
- Supports `auth_token`, `redis_secret`, and `password` fields (in order of preference)
- Falls back to environment variables: `REDIS_PASSWORD`, `REDIS_ODP_PASSWORD`, `REDIS_UPS_PASSWORD`, `REDIS_CMAB_PASSWORD`
- Avoids security scanning alerts from hardcoded "password" field names

* **CMAB Prediction Endpoint Configuration**: Added configurable CMAB prediction endpoint via `client.cmab.predictionEndpoint` in config.yaml or `OPTIMIZELY_CMAB_PREDICTIONENDPOINT` environment variable for testing/staging environments

### Changed

* Updated Redis configuration in `synchronization.pubsub.redis` to use `auth_token` instead of `password` field (backwards compatible)
* CMAB configuration moved from top-level to `client.cmab` section for consistency with other client-related settings (ODP, UPS)

### Fixed

* Improved Redis connection error handling and recovery across all Redis clients
* Added comprehensive test coverage for Redis authentication and CMAB caching

## [4.2.2] - October 7, 2025

### Fixed
Expand Down
28 changes: 25 additions & 3 deletions config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -286,19 +286,41 @@ synchronization:
pubsub:
redis:
host: "redis.demo.svc:6379"
password: ""
## Use auth_token or redis_secret instead of password to avoid security scanning alerts
## Supports: auth_token, redis_secret, password (in order of preference)
## Fallback: REDIS_PASSWORD environment variable if config field is empty
auth_token: ""
database: 0
## channel: "optimizely-sync" # Base channel name (NOT currently parsed - uses hardcoded default)
## Agent publishes to channels: "optimizely-sync-{sdk_key}"
## For external Redis clients: Subscribe "optimizely-sync-{sdk_key}" or PSubscribe "optimizely-sync-*"
## Note: Channel configuration parsing is a known bug - planned for future release

## Redis Streams configuration (when using Redis Streams for notifications)
## batch_size: number of messages to batch before sending (default: 10)
batch_size: 5
## flush_interval: maximum time to wait before sending a partial batch (default: 5s)
flush_interval: 2s
## max_retries: maximum number of retry attempts for failed operations (default: 3)
max_retries: 3
## retry_delay: initial delay between retry attempts (default: 100ms)
retry_delay: 100ms
## max_retry_delay: maximum delay between retry attempts with exponential backoff (default: 5s)
max_retry_delay: 5s
## connection_timeout: timeout for Redis connections (default: 10s)
connection_timeout: 10s
## if notification synchronization is enabled, then the active notification event-stream API
## will get the notifications from available replicas
notification:
enable: false
default: "redis"
enable: true
## Use "redis" for fire-and-forget pub/sub (existing behavior)
## Use "redis-streams" for persistent message delivery with retries and acknowledgment
default: "redis-streams"
Copy link
Contributor

@jaeopt jaeopt Nov 14, 2025

Choose a reason for hiding this comment

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

Do clients need any changes for redis vs redis-streams?
Clients with redis notifications can switch to redis-streams without any change in the client server?

Copy link
Contributor

Choose a reason for hiding this comment

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

What is downside of redis-streams? Wondering if we can support redis-streams only (batch=1 same as the current redis option?)

## if datafile synchronization is enabled, then for each webhook API call
## the datafile will be sent to all available replicas to achieve better eventual consistency
datafile:
enable: false
## Use "redis" for fire-and-forget pub/sub (existing behavior)
## Use "redis-streams" for persistent message delivery with retries and acknowledgment
default: "redis"
# default: "redis-streams" # Uncomment to enable Redis Streams
Copy link
Contributor

@jaeopt jaeopt Nov 14, 2025

Choose a reason for hiding this comment

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

datafile sync is agent-internal. Do we need to care about this redis-streams option?
I see a value, but it may be confusing to the clients.

2 changes: 1 addition & 1 deletion config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ func NewDefaultConfig() *AgentConfig {
},
},
CMAB: CMABConfig{
RequestTimeout: 10 * time.Second,
RequestTimeout: 10 * time.Second,
PredictionEndpoint: "https://prediction.cmab.optimizely.com/predict/%s",
Cache: CMABCacheConfig{
"default": "in-memory",
Expand Down
Loading