Summary
The codebase depends on the redis/redis-stack image (RedisJSON module) but uses only a tiny, trivial subset of RedisJSON. Replacing every redis.json().set/get/mget/delete with stringified-JSON over plain Redis would:
- let the conserver run on stock Redis, Valkey, KeyDB, AWS ElastiCache, or any managed Redis-protocol service
- remove the RSALv2 licensing footprint that Redis Stack carries
- eliminate the 4 pre-existing
common/tests/test_api.py failures developers hit on machines without a RedisJSON-enabled local Redis
- shrink the container image and speed up startup
- remove one specialized skill for new contributors
Audit numbers
Grepping for every .json().* call across the codebase (filtered for actual Redis client calls, excluding response.json() etc.):
8 .json().set
5 .json().get
2 .json().mget
1 .json().delete
———
16 total call sites
Distribution: common/redis_mgr.py, common/lib/vcon_redis.py, api/api.py.
Every set uses path "$". Every get uses Path.root_path() / ".". No projection, no subtree extraction.
What is NOT used
- No JSONPath queries (e.g.
JSON.GET key "$.analysis[?(@.type=='summary')]")
- No atomic partial updates:
JSON.ARRAPPEND, JSON.ARRINSERT, JSON.NUMINCRBY, JSON.STRAPPEND, JSON.OBJKEYS, JSON.TYPE
- No conditional sets (
NX/XX on paths)
- No nested-path
JSON.SET
In practice, RedisJSON is being used as "plain Redis with a JSON-shaped value" — equivalent to SET key json.dumps(obj) / GET key → json.loads(...).
Proposed replacement
A ~10-line shim in common/redis_mgr.py:
import json
def json_set(key, value):
return redis.set(key, json.dumps(value))
def json_get(key):
raw = redis.get(key)
return json.loads(raw) if raw else None
def json_mget(keys):
raws = redis.mget(keys)
return [json.loads(r) if r else None for r in raws]
def json_delete(key):
return redis.delete(key)
Then mechanical search-and-replace at the 16 call sites. VconRedis is already the chokepoint for most of them so the change concentrates there.
Trade-offs
Upside: portability, no module licensing, no test-env friction, smaller image.
Downside: loses the theoretical future option to do server-side JSONPath. No current code does that. "We might want to someday" is a weak constraint and easy to reintroduce if a real need appears.
Suggested scope
- Replace the 16 call sites
- Drop
redis/redis-stack:latest in docker-compose.yml in favor of redis:7-alpine (or current stable plain Redis)
- Verify
test_api.py passes locally against plain Redis
- One PR; should be ~50 LOC total
Audit recipe (reusable)
grep -rn "\.json()\." conserver/ common/ api/ \
| grep -v "\.json()\.dumps\|response\.json\|jsonresponse" \
| grep -oE '\.json\(\)\.[a-z_]+' \
| sort | uniq -c | sort -rn
Generalizes to any "is this datastore extension actually used?" question.
Context
Surfaced during the refactor/speckit-compliance-and-redis-abstraction work where every link was migrated off raw redis_mgr.redis access onto VconQueue / VconRedis. With reads/writes now flowing through a single chokepoint, swapping the underlying storage primitive becomes a localized change.
Summary
The codebase depends on the
redis/redis-stackimage (RedisJSON module) but uses only a tiny, trivial subset of RedisJSON. Replacing everyredis.json().set/get/mget/deletewith stringified-JSON over plain Redis would:common/tests/test_api.pyfailures developers hit on machines without a RedisJSON-enabled local RedisAudit numbers
Grepping for every
.json().*call across the codebase (filtered for actual Redis client calls, excludingresponse.json()etc.):Distribution:
common/redis_mgr.py,common/lib/vcon_redis.py,api/api.py.Every
setuses path"$". EverygetusesPath.root_path()/".". No projection, no subtree extraction.What is NOT used
JSON.GET key "$.analysis[?(@.type=='summary')]")JSON.ARRAPPEND,JSON.ARRINSERT,JSON.NUMINCRBY,JSON.STRAPPEND,JSON.OBJKEYS,JSON.TYPENX/XXon paths)JSON.SETIn practice, RedisJSON is being used as "plain Redis with a JSON-shaped value" — equivalent to
SET key json.dumps(obj)/GET key → json.loads(...).Proposed replacement
A ~10-line shim in
common/redis_mgr.py:Then mechanical search-and-replace at the 16 call sites.
VconRedisis already the chokepoint for most of them so the change concentrates there.Trade-offs
Upside: portability, no module licensing, no test-env friction, smaller image.
Downside: loses the theoretical future option to do server-side JSONPath. No current code does that. "We might want to someday" is a weak constraint and easy to reintroduce if a real need appears.
Suggested scope
redis/redis-stack:latestindocker-compose.ymlin favor ofredis:7-alpine(or current stable plain Redis)test_api.pypasses locally against plain RedisAudit recipe (reusable)
Generalizes to any "is this datastore extension actually used?" question.
Context
Surfaced during the
refactor/speckit-compliance-and-redis-abstractionwork where every link was migrated off rawredis_mgr.redisaccess ontoVconQueue/VconRedis. With reads/writes now flowing through a single chokepoint, swapping the underlying storage primitive becomes a localized change.