-
Notifications
You must be signed in to change notification settings - Fork 2
adapter: Fence Redis reads before snapshots #1167
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 10 commits
cbc99dd
0ae2c34
a171f46
a9b26b9
86cd4f0
257d4b5
09a0e49
cf73c96
8496aa7
b36da7a
94fb695
d2951fb
a5a5520
4ed6cae
ae80151
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -50,6 +50,18 @@ func (r *RedisServer) proxyTransactionToLeader(conn redcon.Conn, queue []redcon. | |
| if !ok { | ||
| return | ||
| } | ||
| r.proxyTransactionToLeaderAddr(conn, queue, leaderAddr) | ||
| } | ||
|
|
||
| func (r *RedisServer) proxyTransactionToLeaderForKey(conn redcon.Conn, routingKey []byte, queue []redcon.Command) { | ||
| leaderAddr, ok := r.resolveLeaderRedisAddrForKey(conn, routingKey) | ||
| if !ok { | ||
| return | ||
| } | ||
| r.proxyTransactionToLeaderAddr(conn, queue, leaderAddr) | ||
| } | ||
|
|
||
| func (r *RedisServer) proxyTransactionToLeaderAddr(conn redcon.Conn, queue []redcon.Command, leaderAddr string) { | ||
| cli := r.getOrCreateLeaderClient(leaderAddr) | ||
|
|
||
| ctx, cancel := context.WithTimeout(r.handlerContext(), redisDispatchTimeout) | ||
|
|
@@ -81,6 +93,20 @@ func (r *RedisServer) resolveLeaderRedisAddr(conn redcon.Conn) (string, bool) { | |
| return leaderAddr, true | ||
| } | ||
|
|
||
| func (r *RedisServer) resolveLeaderRedisAddrForKey(conn redcon.Conn, key []byte) (string, bool) { | ||
| leader := r.coordinator.RaftLeaderForKey(key) | ||
| if leader == "" { | ||
| writeRedisError(conn, ErrLeaderNotFound) | ||
| return "", false | ||
| } | ||
| leaderAddr, ok := r.leaderRedis[leader] | ||
| if !ok || leaderAddr == "" { | ||
| conn.WriteError(fmt.Sprintf("ERR leader redis address unknown for raft address %s", leader)) | ||
| return "", false | ||
| } | ||
| return leaderAddr, true | ||
| } | ||
|
|
||
| // execTxPipeline sends queue as a single TxPipelined batch and returns the | ||
| // per-command result handles together with any pipeline-level error. | ||
| func (r *RedisServer) execTxPipeline(ctx context.Context, cli *redis.Client, queue []redcon.Command) ([]*redis.Cmd, error) { | ||
|
|
@@ -115,13 +141,7 @@ func handleProxyTxnError(conn redcon.Conn, err error) bool { | |
| conn.WriteError(errRedisHeavyCommandPoolFull.Error()) | ||
| return true | ||
| } | ||
| var netErr net.Error | ||
| if isTransientLeaderRedisError(err) || | ||
| errors.Is(err, context.DeadlineExceeded) || | ||
| errors.Is(err, context.Canceled) || | ||
| errors.Is(err, io.EOF) || | ||
| errors.Is(err, io.ErrUnexpectedEOF) || | ||
| errors.As(err, &netErr) { | ||
| if isTerminalProxyTxnError(err) { | ||
| writeRedisError(conn, err) | ||
| return true | ||
| } | ||
|
|
@@ -143,6 +163,10 @@ func handleProxyTxnCommandError(conn redcon.Conn, cmds []*redis.Cmd) bool { | |
| conn.WriteError(errRedisHeavyCommandPoolFull.Error()) | ||
| return true | ||
| } | ||
| if isRedisExecTerminalProxyError(err) { | ||
| writeRedisError(conn, err) | ||
| return true | ||
| } | ||
| if isTransientLeaderRedisError(err) { | ||
| writeRedisError(conn, err) | ||
| return true | ||
|
|
@@ -151,6 +175,23 @@ func handleProxyTxnCommandError(conn redcon.Conn, cmds []*redis.Cmd) bool { | |
| return false | ||
| } | ||
|
|
||
| func isTerminalProxyTxnError(err error) bool { | ||
| var netErr net.Error | ||
| return isRedisExecTerminalProxyError(err) || | ||
| isTransientLeaderRedisError(err) || | ||
| errors.Is(err, context.DeadlineExceeded) || | ||
| errors.Is(err, context.Canceled) || | ||
| errors.Is(err, io.EOF) || | ||
| errors.Is(err, io.ErrUnexpectedEOF) || | ||
| errors.As(err, &netErr) | ||
| } | ||
|
|
||
| func isRedisExecTerminalProxyError(err error) bool { | ||
| return err != nil && | ||
| (errors.Is(err, errRedisExecRouteChangedAfterAmbiguousAttempt) || | ||
| err.Error() == errRedisExecRouteChangedAfterAmbiguousAttempt.Error()) | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
If leadership changes after one node chooses a common remote Redis target but before that target resolves its own read-fence groups, the target can return Useful? React with 👍 / 👎. |
||
| } | ||
|
|
||
| // writeProxyCmdsResult writes an EXEC-style array reply for the given pipeline | ||
| // command handles. For any other non-nil per-command errors, each cmd carries | ||
| // its own result, which is the correct Redis EXEC semantics. | ||
|
|
@@ -161,8 +202,8 @@ func writeProxyCmdsResult(conn redcon.Conn, cmds []*redis.Cmd) { | |
| } | ||
| } | ||
|
|
||
| func (r *RedisServer) proxyLRange(key []byte, startRaw, endRaw []byte) ([]string, error) { | ||
| leader := r.coordinator.RaftLeaderForKey(key) | ||
| func (r *RedisServer) proxyLRange(key, routingKey []byte, startRaw, endRaw []byte) ([]string, error) { | ||
| leader := r.coordinator.RaftLeaderForKey(routingKey) | ||
| if leader == "" { | ||
| return nil, ErrLeaderNotFound | ||
| } | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When this new per-shard proxy path targets a node whose EXEC returns the newly introduced
errRedisExecRouteChangedAfterAmbiguousAttempt, go-redis reports the top-level non-array EXEC error as the pipeline error and copies it onto the queued command handles. Neither proxy error handler recognizes this error, soproxyTransactionToLeaderAddrwrites an EXEC result array containing per-command errors instead of preserving the top-level ambiguous-transaction failure; clients can therefore treat the transaction as having executed with ordinary command failures. Promote this EXEC-level error in the proxy handlers before writing the result array.Useful? React with 👍 / 👎.