Repository navigation
Conversation
Sentinel replies to SENTINEL CKQUORUM with a status line such as "OK 3 usable Sentinels. Quorum and failover authorization can be reached", so the bool_ok callback never matched and the method always returned False. When the quorum cannot be reached Sentinel replies with a NOQUORUM error, which surfaced as a plain ResponseError. Parse the reply with a dedicated callback that accepts any status starting with "OK", and make sentinel_ckquorum() return False when the server replies NOQUORUM. Other response errors are still raised. The async mixin gets its own override so it can await the command before inspecting the error. Fixes valkey-io#307 Signed-off-by: Dmitriy Troyan <dtroyan@ozon.ru>
mkmkme
left a comment
There was a problem hiding this comment.
Thanks for your submission, but this is not the right way to fix it I'm afraid
|
|
||
| from valkey.exceptions import ResponseError | ||
|
|
||
| NOQUORUM_ERROR_PREFIX = "NOQUORUM" |
There was a problem hiding this comment.
Honestly I don't see why this should be its own constant. It's used only once 4 lines below. It should be inlined.
| """Valkey Sentinel's SENTINEL command.""" | ||
| super().sentinel(*args) | ||
|
|
||
| async def sentinel_ckquorum(self, new_master_name): |
There was a problem hiding this comment.
This is wrong. This will make the async pipeline commands stop queuing. Previously, this code would immediately add the command to the pipeline:
pipe = client.pipeline(transaction=False)
pipe.sentinel_ckquorum("mymaster")
results = await pipe.execute()Now it will only create a coroutine. This also breaks the chaining for the pipeline:
pipe.sentinel_ckquorum("master-a").sentinel_ckquorum("master-b")
# AttributeError: 'coroutine' object has no attribute 'sentinel_ckquorum'| ``False`` when Sentinel replies with ``NOQUORUM``. | ||
| """ | ||
| return self.execute_command("SENTINEL CKQUORUM", new_master_name, once=True) | ||
| try: |
There was a problem hiding this comment.
This will not work in the pipelines. It only works for direct client. Consider this code:
pipe = client.pipeline(transaction=False)
pipe.sentinel_ckquorum("mymaster") # No response is read here.
results = pipe.execute() # NOQUORUM is received here.By the time execute() receives NOQUORUM, sentinel_ckquorum() has already returned. Its exception handler is no longer active.
Instead, pipe.execute() will raise an exception, or return the plain error when raise_on_error=False is passed.
The same applies for async part.
Pull Request check-list
Description of change
Fixes #307.
sentinel_ckquorum()always returnedFalsewhen the quorum was reachable,and raised
ResponseErrorwhen it was not.Sentinel replies to
SENTINEL CKQUORUMwith a status line such asOK 3 usable Sentinels. Quorum and failover authorization can be reached,so the
bool_okresponse callback (== "OK") never matched. When the quorumcannot be reached, Sentinel replies with an error
(
NOQUORUM 1 usable Sentinels. ...), which the parser turned into a plainResponseError.As agreed in #307, the method now returns
Truewhen the quorum and themajority can be reached and
Falsewhen Sentinel repliesNOQUORUM:parse_sentinel_ckquorumcallback accepts any reply thatstarts with
OK.sentinel_ckquorum()catchesResponseErrorwhose message starts withNOQUORUMand returnsFalse. Other response errors (for exampleERR No such master with that name) are still raised.The sync and async clients share the callback; the async mixin needs its own
override to
awaitthe command before inspecting the error, soAsyncSentinelCommands.sentinel_ckquorumis added. This applies both whencalling the method on a
Valkeyclient connected to a Sentinel and whencalling it through the
Sentinelhelper.Testing
tests/test_sentinel.pyandtests/test_asyncio/test_sentinel.pycover theOK ...reply (bytes anddecoded), the
NOQUORUMerror, other response errors, and theSentinelhelper path.
compose.yamlsentinels with both thepure-Python and libvalkey parsers, RESP2 and RESP3, sync and async:
Truewith all three sentinels up,Falseafter stopping two of them.make linterspasses locally.make standalone-tests(PROTOCOL=2) passes except forthe multiprocessing, UDS and network-failure tests, which fail identically on
unmodified
mainin my environment (macOS + Docker Desktop).