Skip to content

Commit 61fba7b

Browse files
craig[bot]tbgnormanchenn
committed
143673: kvserver: tolerate missing store in raft transport r=tbg a=tbg In a multi-store cluster, if a node (say n1) is restarted with fewer stores than initially, it will take some time for replicas to be moved off of it. Previously, while in this state, the raft transport from other nodes to n1 would frequently be reconnected. Concretely, this would happen every time a leader on n2 would try to reach a follower on a now-missing store on n1. n2 would respond with a StoreNotFoundError, and the original sender would tear down the raft transport stream to n1 and reconnect, dropping messages directed at other valid stores on n1 in the process. We now no longer emit StoreNotFoundError from n1, and in mixed clusters such messages are now handled as no-ops, leaving the raft transport intact and thus preventing interruption to raft message flows to stores still present on n1. Epic: CRDB-49133 Release note: None 144247: jsonpath: validate array indices are within int32 range r=normanchenn a=normanchenn This commit adds validation to ensure JSONPath array indices are within the int32 range, matching Postgres' behaviour. Now, we return an error for indices outside the int32 range. Epic: None Release note: None Co-authored-by: Tobias Grieger <[email protected]> Co-authored-by: Norman Chen <[email protected]>
3 parents 8a20084 + eb6ca16 + 5764611 commit 61fba7b

File tree

5 files changed

+51
-11
lines changed

5 files changed

+51
-11
lines changed

pkg/kv/kvserver/raft_transport.go

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -428,6 +428,7 @@ func (t *RaftTransport) getOutgoingMessageHandler(
428428
func (t *RaftTransport) handleRaftRequest(
429429
ctx context.Context, req *kvserverpb.RaftMessageRequest, respStream RaftMessageResponseStream,
430430
) *kvpb.Error {
431+
isV1 := log.V(1)
431432
for i := range req.AdmittedRaftLogEntries {
432433
// Process any flow tokens that were returned over the RaftTransport. Do
433434
// this first thing, before these requests enter the receive queues
@@ -444,7 +445,7 @@ func (t *RaftTransport) handleRaftRequest(
444445
)
445446
}
446447

447-
if log.V(1) {
448+
if isV1 {
448449
log.Infof(ctx, "informed of below-raft %s", admittedEntries)
449450
}
450451
}
@@ -457,9 +458,16 @@ func (t *RaftTransport) handleRaftRequest(
457458

458459
incomingMessageHandler, ok := t.getIncomingRaftMessageHandler(req.ToReplica.StoreID)
459460
if !ok {
460-
log.Warningf(ctx, "unable to accept Raft message from %+v: no handler registered for %+v",
461-
req.FromReplica, req.ToReplica)
462-
return kvpb.NewError(kvpb.NewStoreNotFoundError(req.ToReplica.StoreID))
461+
if isV1 {
462+
log.Warningf(ctx, "unable to accept Raft message from %+v: no handler registered for %+v",
463+
req.FromReplica, req.ToReplica)
464+
}
465+
// We don't return an error to the client. If this node restarted with fewer
466+
// stores than it had before (think: hardware failure), then it is expected
467+
// for remote ranges to think there should be a replica on this store. We
468+
// could still send an error back here, but then it would have to be dropped
469+
// at the other node - not a good use of bandwidth.
470+
return nil
463471
}
464472

465473
return incomingMessageHandler.HandleRaftRequest(ctx, req, respStream)

pkg/kv/kvserver/store_raft.go

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -662,7 +662,13 @@ func (s *Store) HandleRaftResponse(
662662
case *kvpb.StoreNotFoundError:
663663
log.Warningf(ctx, "raft error: node %d claims to not contain store %d for replica %s: %s",
664664
resp.FromReplica.NodeID, resp.FromReplica.StoreID, resp.FromReplica, val)
665-
return val.GetDetail() // close Raft connection
665+
// This error is expected if the remote node restarted with fewer stores
666+
// (before rebalancing off that now dead store is complete).
667+
//
668+
// Fall through intentionally.
669+
//
670+
// NB: as of v25.2, receivers no longer return this error in this situation
671+
// and eventually, this case can be removed.
666672
default:
667673
log.Warningf(ctx, "got error from r%d, replica %s: %s",
668674
resp.RangeID, resp.FromReplica, val)

pkg/sql/logictest/testdata/logic_test/jsonb_path_query

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1406,3 +1406,25 @@ query T
14061406
SELECT jsonb_path_query('{"a": {"b": [{"c": 1}, {"c": 2}]}}', '($.a.b[1]).c');
14071407
----
14081408
2
1409+
1410+
statement error pgcode 22033 pq: jsonpath array subscript is out of integer range
1411+
SELECT jsonb_path_query('[1]', 'lax $[10000000000000000]');
1412+
1413+
statement error pgcode 22033 pq: jsonpath array subscript is out of integer range
1414+
SELECT jsonb_path_query('[1]', 'lax $[-10000000000000000]');
1415+
1416+
# MaxInt32
1417+
query empty
1418+
SELECT jsonb_path_query('[1]', '$[2147483647]');
1419+
1420+
# MaxInt32 + 1
1421+
statement error pgcode 22033 pq: jsonpath array subscript is out of integer range
1422+
SELECT jsonb_path_query('[1]', '$[2147483648]');
1423+
1424+
# MinInt32
1425+
query empty
1426+
SELECT jsonb_path_query('[1]', '$[-2147483648]');
1427+
1428+
# MinInt32 - 1
1429+
statement error pgcode 22033 pq: jsonpath array subscript is out of integer range
1430+
SELECT jsonb_path_query('[1]', '$[-2147483649]');

pkg/util/jsonpath/eval/array.go

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
package eval
77

88
import (
9+
"math"
10+
911
"github.com/cockroachdb/cockroach/pkg/sql/pgwire/pgcode"
1012
"github.com/cockroachdb/cockroach/pkg/sql/pgwire/pgerror"
1113
"github.com/cockroachdb/cockroach/pkg/util/json"
@@ -18,6 +20,7 @@ var (
1820
errIndexOnNonArray = pgerror.Newf(pgcode.SQLJSONArrayNotFound, "jsonpath array accessor can only be applied to an array")
1921
errIndexOutOfBounds = pgerror.Newf(pgcode.InvalidSQLJSONSubscript, "jsonpath array subscript is out of bounds")
2022
errIndexNotSingleNumValue = pgerror.Newf(pgcode.InvalidSQLJSONSubscript, "jsonpath array subscript is not a single numeric value")
23+
errInvalidSubscript = pgerror.Newf(pgcode.InvalidSQLJSONSubscript, "jsonpath array subscript is out of integer range")
2124
)
2225

2326
func (ctx *jsonpathCtx) evalArrayWildcard(jsonValue json.JSON) ([]json.JSON, error) {
@@ -110,23 +113,25 @@ func (ctx *jsonpathCtx) resolveArrayIndex(
110113
if len(evalResults) != 1 || evalResults[0].Type() != json.NumberJSONType {
111114
return -1, errIndexNotSingleNumValue
112115
}
113-
// TODO(normanchenn): Postgres returns an error if the index is outside int32
114-
// range. (ex. `select jsonb_path_query('[1]', 'lax $[10000000000000000]');
115116
i, err := asInt(evalResults[0])
116117
if err != nil {
117-
return -1, errIndexNotSingleNumValue
118+
return -1, err
118119
}
119120
return i, nil
120121
}
121122

122123
func asInt(j json.JSON) (int, error) {
123124
d, ok := j.AsDecimal()
124125
if !ok {
125-
return 0, errInternal
126+
return 0, errIndexNotSingleNumValue
126127
}
127128
i64, err := d.Int64()
128129
if err != nil {
129-
return 0, err
130+
return 0, errIndexNotSingleNumValue
131+
}
132+
// Postgres returns an error if the index is outside int32 range.
133+
if i64 < math.MinInt32 || i64 > math.MaxInt32 {
134+
return 0, errInvalidSubscript
130135
}
131136
return int(i64), nil
132137
}

pkg/util/jsonpath/eval/eval.go

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ import (
1818

1919
var (
2020
errUnimplemented = unimplemented.NewWithIssue(22513, "unimplemented")
21-
errInternal = errors.New("internal error")
2221
errSingleBooleanRequired = pgerror.Newf(pgcode.SingletonSQLJSONItemRequired, "single boolean result is expected")
2322
)
2423

0 commit comments

Comments
 (0)