chore: initialize from backend template 0a6dd0e
This commit is contained in:
@@ -0,0 +1,75 @@
|
||||
# Operating the Redis SDK
|
||||
|
||||
## What the metrics can and cannot tell you
|
||||
|
||||
Every observation carries the command family, the deployment mode, and latency. None carries a key,
|
||||
a field, a member, or a value — not because they would be large, but because a metric dimension
|
||||
built from caller data is unbounded cardinality and, for most deployments, tenant identity in a
|
||||
dashboard.
|
||||
|
||||
That means you can answer "which command family is slow" and "which one is failing", and you cannot
|
||||
answer "which key is hot" from metrics. Use the admin plane's `SLOWLOG` projection for the first
|
||||
question and `MEMORY USAGE` on a specific key for the second.
|
||||
|
||||
## The failures worth alerting on
|
||||
|
||||
| Signal | What it means | What to do |
|
||||
| --- | --- | --- |
|
||||
| `RedisCommandRejectedException` | the SDK refused before sending | a caller exceeded a declared bound; the reason names which one |
|
||||
| `RedisCrossSlotException` | a multi-key command spans slots | the keys need a shared hash tag |
|
||||
| `RedisAmbiguousExecutionException` | a write may or may not have applied | reconcile; the SDK will not retry it |
|
||||
| `RedisCapabilityUnavailableException` | the server lacks the feature | a capability bean was constructed by hand, or the probe result changed |
|
||||
| `SentinelFailoverObserver.ambiguousWriteCount` | non-idempotent writes lost to a promotion | each one needs reconciling; the count is the workload |
|
||||
| `ClusterTopologyObserver.reshardingObserved` | `ASK`/`TRYAGAIN` seen | a slot migration is in progress; latency will be uneven until it ends |
|
||||
|
||||
## Things the SDK will never do for you
|
||||
|
||||
- Retry a non-idempotent write after a timeout. `ExecutionCertainty.AMBIGUOUS_FAILURE` is reported,
|
||||
not resolved.
|
||||
- Follow a cross-slot multi-key command by splitting it. It is refused instead.
|
||||
- Read a whole collection, stream, or index. Every read declares a bound.
|
||||
- Load a Lua script or a function library at request time. Both are deployment actions.
|
||||
- Send a command it cannot classify.
|
||||
- Tell you that an acknowledged write was lost. See below — this one is not a limitation you can
|
||||
work around in application code.
|
||||
|
||||
## The write loss the client cannot see
|
||||
|
||||
Set these on every Redis node that can ever be a primary:
|
||||
|
||||
```
|
||||
min-replicas-to-write 1
|
||||
min-replicas-max-lag 1
|
||||
```
|
||||
|
||||
Without them a Sentinel promotion silently destroys acknowledged writes, and this is measured, not
|
||||
theoretical. In `LiveRedisSentinelPromotionTest` on the 7.4 lane, Sentinel promoted the replica and
|
||||
did not demote the old primary for **eleven seconds**. The client stayed connected to a primary that
|
||||
had already been replaced, wrote, and was told `+OK` **2,086 times**. Every one of those writes was
|
||||
discarded when the old primary resynced. Exactly one command failed.
|
||||
|
||||
Nothing on the client can detect this. The server answered, so the driver recorded a success, the
|
||||
SDK recorded `CONFIRMED_SUCCESS`, and the caller was told the write landed. No metric here counts
|
||||
it, `SentinelFailoverObserver` cannot count it, and no retry policy helps — there was no failure to
|
||||
react to. A second run of the same promotion produced sixteen thousand writes, **zero** exceptions,
|
||||
and the same silent loss.
|
||||
|
||||
With the two settings, the identical promotion lost **one** write and refused 2,020 with
|
||||
`NOREPLICAS`, which the SDK reports as a definite, non-ambiguous failure the caller can act on. That
|
||||
is the whole difference: an outage you can see instead of data you cannot.
|
||||
|
||||
The residual window is `min-replicas-max-lag` wide and cannot be closed by configuration alone. A
|
||||
write that must survive a promotion under any circumstances needs `WAIT` after it, at the cost of a
|
||||
round trip to the replica — decide that per write, not globally.
|
||||
|
||||
## Blocking work
|
||||
|
||||
Blocking pops and blocking stream reads run on a dedicated connection lane. If those saturate, the
|
||||
symptom is blocking calls timing out while ordinary traffic is healthy — that is the lane doing its
|
||||
job, not a fault. Size the blocking pool to the number of concurrent consumers, not to request rate.
|
||||
|
||||
## Pub/Sub
|
||||
|
||||
At-most-once. A subscriber that reconnects misses whatever arrived while it was gone, and there is
|
||||
no replay. Durable business events belong in a stream with a consumer group, which is at-least-once
|
||||
and therefore requires idempotent consumers.
|
||||
Reference in New Issue
Block a user