You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Read, admin and health paths treat server error replies as connection loss (read-side counterpart of #105) #123
main at ff2d5b8. Unchanged at the #108 → #109 → #111 stack head 0c3b0e6; #108 adds getStreamSnapshot() with the same pattern.
Redis version and deployment mode
Redis 7.4.11 standalone (TCP).
Observed behavior
#109 separates server rejection from transport failure for writes. Every other path still treats a ReplyError from a healthy server as a lost connection:
RedisConnection::xrange/xrevrange/ping/del/exists/rename/copy/publish/hexpire return their failure value for any sw::redis::Error, ReplyError included.
Their callers pass that result to reconnect(). This covers every get* helper (RedisAdapterTempl.hpp), Add owned stream subscriptions and safe payload decoding #108's getStreamSnapshot(), connected() (reconnect(_redis.ping())), rename/del/exists/publish, copy(), and petWatchdog().
So on a healthy server:
getSingleValue/getSingleList on a key holding a string, or on a key outside the user's ACL pattern, return RA_NOT_CONNECTED, and getValues/getLists return an empty list.
getStreamSnapshot() on such a key reports connected=false, id="0-0".
Each such call launches the full reconnect: a new client and pool (3 new server connections, 4 with an ACL user) and a stop/restart of every reader bucket.
A 10 Hz poll of a wrong-type key produces 10 reader restarts and about 30 new TCP connections per second, plus one LOG_ERR per read. On loopback this caused no measurable subscriber loss, only churn and false outage reports.
rename() of a missing key, NOPERM on del/exists/copy/publish, and a HEXPIRE refused by ACL in the watchdog (+7 reconnects in 5 s) do the same.
At the stack head, reads and writes of the same wrong-type key are now classified inconsistently: the write returns RA_REJECTED with no reconnect, and the read returns RA_NOT_CONNECTED plus a reconnect.
Expected: as #105 states for writes, "do not reconnect for server-side ReplyError"; report rejection distinctly from unavailable transport.
Reproduction
control.set("{S8}:wrong", "not-a-stream");
int v;
auto t = adapter.getSingleValue<int>("wrong", v); // err()==1 (RA_NOT_CONNECTED)auto s = adapter.getStreamSnapshot("wrong"); // connected=false, id="0-0"// INFO stats total_connections_received: +3 per call; every reader bucket restarted
Give the read/admin helpers and ping() a status (a trailing CommandStatus* as Separate rejected stream writes from transport failures #109 did for xtrim, or result structs). Catch sw::redis::ReplyError separately as Rejected, and keep other errors as Unavailable.
Call reconnect(0) only for Unavailable. Return RA_REJECTED from getSingleValue/getSingleList on Rejected.
Give StreamSnapshot a rejection flag or status, and never return a valid replay cursor ("0-0") on failure.
Let connected() distinguish "server refuses commands" from "no transport".
Add the read counterpart of write_error_test.cpp: set a wrong-type key, call getSingleValue/getStreamSnapshot, and assert that no reconnect occurred and that connected() stays true.
RedisAdapter version or revision
mainatff2d5b8. Unchanged at the #108 → #109 → #111 stack head0c3b0e6; #108 addsgetStreamSnapshot()with the same pattern.Redis version and deployment mode
Redis 7.4.11 standalone (TCP).
Observed behavior
#109 separates server rejection from transport failure for writes. Every other path still treats a
ReplyErrorfrom a healthy server as a lost connection:RedisConnection::xrange/xrevrange/ping/del/exists/rename/copy/publish/hexpirereturn their failure value for anysw::redis::Error,ReplyErrorincluded.reconnect(). This covers everyget*helper (RedisAdapterTempl.hpp), Add owned stream subscriptions and safe payload decoding #108'sgetStreamSnapshot(),connected()(reconnect(_redis.ping())),rename/del/exists/publish,copy(), andpetWatchdog().So on a healthy server:
getSingleValue/getSingleListon a key holding a string, or on a key outside the user's ACL pattern, returnRA_NOT_CONNECTED, andgetValues/getListsreturn an empty list.getStreamSnapshot()on such a key reportsconnected=false, id="0-0".rename()of a missing key, NOPERM ondel/exists/copy/publish, and a HEXPIRE refused by ACL in the watchdog (+7 reconnects in 5 s) do the same.connected()probe therefore triggers a reconnect whose own PING fails, and together with A failed reconnect() replaces the working client with nothing; subscriptions stall and the first write after recovery fails #117 every read then returnsRA_NOT_CONNECTEDwhile the server is still serving reads.At the stack head, reads and writes of the same wrong-type key are now classified inconsistently: the write returns
RA_REJECTEDwith no reconnect, and the read returnsRA_NOT_CONNECTEDplus a reconnect.Expected: as #105 states for writes, "do not reconnect for server-side ReplyError"; report rejection distinctly from unavailable transport.
Reproduction
Build and runtime environment
Suggested fix
Mirror #109 on the read side:
ping()a status (a trailingCommandStatus*as Separate rejected stream writes from transport failures #109 did forxtrim, or result structs). Catchsw::redis::ReplyErrorseparately asRejected, and keep other errors asUnavailable.reconnect(0)only forUnavailable. ReturnRA_REJECTEDfromgetSingleValue/getSingleListonRejected.StreamSnapshota rejection flag or status, and never return a valid replay cursor ("0-0") on failure.connected()distinguish "server refuses commands" from "no transport".write_error_test.cpp: set a wrong-type key, callgetSingleValue/getStreamSnapshot, and assert that no reconnect occurred and thatconnected()stays true.Related: #105, #109, #108 (
getStreamSnapshot), #117.