From 62b5e8b326b76155dcccb4324164645fa9abb846 Mon Sep 17 00:00:00 2001
From: Alex Sarkesian
Date: Tue, 9 Aug 2022 15:52:07 -0400
Subject: [PATCH 1/2] kvserver: track replicate queue metrics by allocator
action
While previously we had metrics within the replicate queue which tracked
the number of particular actions processed by the queue based on a
unique set of categories, this change adds new metrics for tracking the
successes/errors of a replica being processed by the replicate queue,
using the allocator action as a method of categorizing these actions.
With this categorization, we are able to track success and error counts
during rebalancing, upreplicating when we have a dead node, or
decommissioning. The categorization makes no distinction between actions
relatinv to voter replicas vs non-voter replicas, so they are aggregated
across these two types.
Release note (ops change): added new metrics:
```
queue.replicate.addreplica.(success|error)
queue.replicate.removereplica.(success|error)
queue.replicate.replacedeadreplica.(success|error)
queue.replicate.removedeadreplica.(success|error)
queue.replicate.replacedecommissioningreplica.(success|error)
queue.replicate.removedecommissioningreplica.(success|error)
```
---
pkg/kv/kvserver/replicate_queue.go | 182 ++++++++++++++++++++++--
pkg/kv/kvserver/replicate_queue_test.go | 48 +++++++
pkg/ts/catalog/chart_catalog.go | 22 +++
3 files changed, 244 insertions(+), 8 deletions(-)
diff --git a/pkg/kv/kvserver/replicate_queue.go b/pkg/kv/kvserver/replicate_queue.go
index f4178dedbe81..0d8123d9a15b 100644
--- a/pkg/kv/kvserver/replicate_queue.go
+++ b/pkg/kv/kvserver/replicate_queue.go
@@ -186,6 +186,78 @@ var (
Measurement: "Demotions of Voters to Non Voters",
Unit: metric.Unit_COUNT,
}
+ metaReplicateQueueAddReplicaSuccessCount = metric.Metadata{
+ Name: "queue.replicate.addreplica.success",
+ Help: "Number of successful replica additions processed by the replicate queue",
+ Measurement: "Replicas",
+ Unit: metric.Unit_COUNT,
+ }
+ metaReplicateQueueAddReplicaErrorCount = metric.Metadata{
+ Name: "queue.replicate.addreplica.error",
+ Help: "Number of failed replica additions processed by the replicate queue",
+ Measurement: "Replicas",
+ Unit: metric.Unit_COUNT,
+ }
+ metaReplicateQueueRemoveReplicaSuccessCount = metric.Metadata{
+ Name: "queue.replicate.removereplica.success",
+ Help: "Number of successful replica removals processed by the replicate queue",
+ Measurement: "Replicas",
+ Unit: metric.Unit_COUNT,
+ }
+ metaReplicateQueueRemoveReplicaErrorCount = metric.Metadata{
+ Name: "queue.replicate.removereplica.error",
+ Help: "Number of failed replica removals processed by the replicate queue",
+ Measurement: "Replicas",
+ Unit: metric.Unit_COUNT,
+ }
+ metaReplicateQueueReplaceDeadReplicaSuccessCount = metric.Metadata{
+ Name: "queue.replicate.replacedeadreplica.success",
+ Help: "Number of successful dead replica replica replacements processed by the replicate queue",
+ Measurement: "Replicas",
+ Unit: metric.Unit_COUNT,
+ }
+ metaReplicateQueueReplaceDeadReplicaErrorCount = metric.Metadata{
+ Name: "queue.replicate.replacedeadreplica.error",
+ Help: "Number of failed dead replica replica replacements processed by the replicate queue",
+ Measurement: "Replicas",
+ Unit: metric.Unit_COUNT,
+ }
+ metaReplicateQueueReplaceDecommissioningReplicaSuccessCount = metric.Metadata{
+ Name: "queue.replicate.replacedecommissioningreplica.success",
+ Help: "Number of successful decommissioning replica replica replacements processed by the replicate queue",
+ Measurement: "Replicas",
+ Unit: metric.Unit_COUNT,
+ }
+ metaReplicateQueueReplaceDecommissioningReplicaErrorCount = metric.Metadata{
+ Name: "queue.replicate.replacedecommissioningreplica.error",
+ Help: "Number of failed decommissioning replica replica replacements processed by the replicate queue",
+ Measurement: "Replicas",
+ Unit: metric.Unit_COUNT,
+ }
+ metaReplicateQueueRemoveDecommissioningReplicaSuccessCount = metric.Metadata{
+ Name: "queue.replicate.removedecommissioningreplica.success",
+ Help: "Number of successful decommissioning replica replica removals processed by the replicate queue",
+ Measurement: "Replicas",
+ Unit: metric.Unit_COUNT,
+ }
+ metaReplicateQueueRemoveDecommissioningReplicaErrorCount = metric.Metadata{
+ Name: "queue.replicate.removedecommissioningreplica.error",
+ Help: "Number of failed decommissioning replica replica removals processed by the replicate queue",
+ Measurement: "Replicas",
+ Unit: metric.Unit_COUNT,
+ }
+ metaReplicateQueueRemoveDeadReplicaSuccessCount = metric.Metadata{
+ Name: "queue.replicate.removedeadreplica.success",
+ Help: "Number of successful dead replica replica removals processed by the replicate queue",
+ Measurement: "Replicas",
+ Unit: metric.Unit_COUNT,
+ }
+ metaReplicateQueueRemoveDeadReplicaErrorCount = metric.Metadata{
+ Name: "queue.replicate.removedeadreplica.error",
+ Help: "Number of failed dead replica replica removals processed by the replicate queue",
+ Measurement: "Replicas",
+ Unit: metric.Unit_COUNT,
+ }
)
// quorumError indicates a retryable error condition which sends replicas being
@@ -228,6 +300,23 @@ type ReplicateQueueMetrics struct {
TransferLeaseCount *metric.Counter
NonVoterPromotionsCount *metric.Counter
VoterDemotionsCount *metric.Counter
+
+ // Success/error counts by allocator action.
+ RemoveReplicaSuccessCount *metric.Counter
+ RemoveReplicaErrorCount *metric.Counter
+ AddReplicaSuccessCount *metric.Counter
+ AddReplicaErrorCount *metric.Counter
+ ReplaceDeadReplicaSuccessCount *metric.Counter
+ ReplaceDeadReplicaErrorCount *metric.Counter
+ RemoveDeadReplicaSuccessCount *metric.Counter
+ RemoveDeadReplicaErrorCount *metric.Counter
+ ReplaceDecommissioningReplicaSuccessCount *metric.Counter
+ ReplaceDecommissioningReplicaErrorCount *metric.Counter
+ RemoveDecommissioningReplicaSuccessCount *metric.Counter
+ RemoveDecommissioningReplicaErrorCount *metric.Counter
+ // TODO(sarkesian): Consider adding metrics for AllocatorRemoveLearner,
+ // AllocatorConsiderRebalance, and AllocatorFinalizeAtomicReplicationChange
+ // allocator actions.
}
func makeReplicateQueueMetrics() ReplicateQueueMetrics {
@@ -251,6 +340,19 @@ func makeReplicateQueueMetrics() ReplicateQueueMetrics {
TransferLeaseCount: metric.NewCounter(metaReplicateQueueTransferLeaseCount),
NonVoterPromotionsCount: metric.NewCounter(metaReplicateQueueNonVoterPromotionsCount),
VoterDemotionsCount: metric.NewCounter(metaReplicateQueueVoterDemotionsCount),
+
+ RemoveReplicaSuccessCount: metric.NewCounter(metaReplicateQueueRemoveReplicaSuccessCount),
+ RemoveReplicaErrorCount: metric.NewCounter(metaReplicateQueueRemoveReplicaErrorCount),
+ AddReplicaSuccessCount: metric.NewCounter(metaReplicateQueueAddReplicaSuccessCount),
+ AddReplicaErrorCount: metric.NewCounter(metaReplicateQueueAddReplicaErrorCount),
+ ReplaceDeadReplicaSuccessCount: metric.NewCounter(metaReplicateQueueReplaceDeadReplicaSuccessCount),
+ ReplaceDeadReplicaErrorCount: metric.NewCounter(metaReplicateQueueReplaceDeadReplicaErrorCount),
+ RemoveDeadReplicaSuccessCount: metric.NewCounter(metaReplicateQueueRemoveDeadReplicaSuccessCount),
+ RemoveDeadReplicaErrorCount: metric.NewCounter(metaReplicateQueueRemoveDeadReplicaErrorCount),
+ ReplaceDecommissioningReplicaSuccessCount: metric.NewCounter(metaReplicateQueueReplaceDecommissioningReplicaSuccessCount),
+ ReplaceDecommissioningReplicaErrorCount: metric.NewCounter(metaReplicateQueueReplaceDecommissioningReplicaErrorCount),
+ RemoveDecommissioningReplicaSuccessCount: metric.NewCounter(metaReplicateQueueRemoveDecommissioningReplicaSuccessCount),
+ RemoveDecommissioningReplicaErrorCount: metric.NewCounter(metaReplicateQueueRemoveDecommissioningReplicaErrorCount),
}
}
@@ -353,6 +455,50 @@ func (metrics *ReplicateQueueMetrics) trackRebalanceReplicaCount(
}
}
+// trackProcessResult increases the corresponding success/error count metric for
+// processing a particular allocator action through the replicate queue.
+func (metrics *ReplicateQueueMetrics) trackResultByAllocatorAction(
+ action allocatorimpl.AllocatorAction, err error, dryRun bool,
+) {
+ if dryRun {
+ return
+ }
+ switch action {
+ case allocatorimpl.AllocatorRemoveVoter, allocatorimpl.AllocatorRemoveNonVoter:
+ if err == nil {
+ metrics.RemoveReplicaSuccessCount.Inc(1)
+ } else {
+ metrics.RemoveReplicaErrorCount.Inc(1)
+ }
+ case allocatorimpl.AllocatorAddVoter, allocatorimpl.AllocatorAddNonVoter:
+ if err == nil {
+ metrics.AddReplicaSuccessCount.Inc(1)
+ } else {
+ metrics.AddReplicaErrorCount.Inc(1)
+ }
+ case allocatorimpl.AllocatorReplaceDeadVoter, allocatorimpl.AllocatorReplaceDeadNonVoter:
+ if err == nil {
+ metrics.ReplaceDeadReplicaSuccessCount.Inc(1)
+ } else {
+ metrics.ReplaceDeadReplicaErrorCount.Inc(1)
+ }
+ case allocatorimpl.AllocatorReplaceDecommissioningVoter, allocatorimpl.AllocatorReplaceDecommissioningNonVoter:
+ if err == nil {
+ metrics.ReplaceDecommissioningReplicaSuccessCount.Inc(1)
+ } else {
+ metrics.ReplaceDecommissioningReplicaErrorCount.Inc(1)
+ }
+ case allocatorimpl.AllocatorRemoveDecommissioningVoter, allocatorimpl.AllocatorRemoveDecommissioningNonVoter:
+ if err == nil {
+ metrics.RemoveDecommissioningReplicaSuccessCount.Inc(1)
+ } else {
+ metrics.RemoveDecommissioningReplicaErrorCount.Inc(1)
+ }
+ default:
+ panic(fmt.Sprintf("unsupported AllocatorAction: %v", action))
+ }
+}
+
// replicateQueue manages a queue of replicas which may need to add an
// additional replica to their range.
type replicateQueue struct {
@@ -641,19 +787,27 @@ func (rq *replicateQueue) processOneChange(
// Add replicas.
case allocatorimpl.AllocatorAddVoter:
- return rq.addOrReplaceVoters(
+ requeue, err := rq.addOrReplaceVoters(
ctx, repl, liveVoterReplicas, liveNonVoterReplicas, -1 /* removeIdx */, allocatorimpl.Alive, dryRun,
)
+ rq.metrics.trackResultByAllocatorAction(action, err, dryRun)
+ return requeue, err
case allocatorimpl.AllocatorAddNonVoter:
- return rq.addOrReplaceNonVoters(
+ requeue, err := rq.addOrReplaceNonVoters(
ctx, repl, liveVoterReplicas, liveNonVoterReplicas, -1 /* removeIdx */, allocatorimpl.Alive, dryRun,
)
+ rq.metrics.trackResultByAllocatorAction(action, err, dryRun)
+ return requeue, err
// Remove replicas.
case allocatorimpl.AllocatorRemoveVoter:
- return rq.removeVoter(ctx, repl, voterReplicas, nonVoterReplicas, dryRun)
+ requeue, err := rq.removeVoter(ctx, repl, voterReplicas, nonVoterReplicas, dryRun)
+ rq.metrics.trackResultByAllocatorAction(action, err, dryRun)
+ return requeue, err
case allocatorimpl.AllocatorRemoveNonVoter:
- return rq.removeNonVoter(ctx, repl, voterReplicas, nonVoterReplicas, dryRun)
+ requeue, err := rq.removeNonVoter(ctx, repl, voterReplicas, nonVoterReplicas, dryRun)
+ rq.metrics.trackResultByAllocatorAction(action, err, dryRun)
+ return requeue, err
// Replace dead replicas.
case allocatorimpl.AllocatorReplaceDeadVoter:
@@ -667,8 +821,10 @@ func (rq *replicateQueue) processOneChange(
"dead voter %v unexpectedly not found in %v",
deadVoterReplicas[0], voterReplicas)
}
- return rq.addOrReplaceVoters(
+ requeue, err := rq.addOrReplaceVoters(
ctx, repl, liveVoterReplicas, liveNonVoterReplicas, removeIdx, allocatorimpl.Dead, dryRun)
+ rq.metrics.trackResultByAllocatorAction(action, err, dryRun)
+ return requeue, err
case allocatorimpl.AllocatorReplaceDeadNonVoter:
if len(deadNonVoterReplicas) == 0 {
// Nothing to do.
@@ -680,8 +836,10 @@ func (rq *replicateQueue) processOneChange(
"dead non-voter %v unexpectedly not found in %v",
deadNonVoterReplicas[0], nonVoterReplicas)
}
- return rq.addOrReplaceNonVoters(
+ requeue, err := rq.addOrReplaceNonVoters(
ctx, repl, liveVoterReplicas, liveNonVoterReplicas, removeIdx, allocatorimpl.Dead, dryRun)
+ rq.metrics.trackResultByAllocatorAction(action, err, dryRun)
+ return requeue, err
// Replace decommissioning replicas.
case allocatorimpl.AllocatorReplaceDecommissioningVoter:
@@ -698,6 +856,7 @@ func (rq *replicateQueue) processOneChange(
}
requeue, err := rq.addOrReplaceVoters(
ctx, repl, liveVoterReplicas, liveNonVoterReplicas, removeIdx, allocatorimpl.Decommissioning, dryRun)
+ rq.metrics.trackResultByAllocatorAction(action, err, dryRun)
if err != nil {
return requeue, decommissionPurgatoryError{err}
}
@@ -715,6 +874,7 @@ func (rq *replicateQueue) processOneChange(
}
requeue, err := rq.addOrReplaceNonVoters(
ctx, repl, liveVoterReplicas, liveNonVoterReplicas, removeIdx, allocatorimpl.Decommissioning, dryRun)
+ rq.metrics.trackResultByAllocatorAction(action, err, dryRun)
if err != nil {
return requeue, decommissionPurgatoryError{err}
}
@@ -727,12 +887,14 @@ func (rq *replicateQueue) processOneChange(
// AllocatorReplaceDecommissioning{Non}Voter above.
case allocatorimpl.AllocatorRemoveDecommissioningVoter:
requeue, err := rq.removeDecommissioning(ctx, repl, allocatorimpl.VoterTarget, dryRun)
+ rq.metrics.trackResultByAllocatorAction(action, err, dryRun)
if err != nil {
return requeue, decommissionPurgatoryError{err}
}
return requeue, nil
case allocatorimpl.AllocatorRemoveDecommissioningNonVoter:
requeue, err := rq.removeDecommissioning(ctx, repl, allocatorimpl.NonVoterTarget, dryRun)
+ rq.metrics.trackResultByAllocatorAction(action, err, dryRun)
if err != nil {
return requeue, decommissionPurgatoryError{err}
}
@@ -744,9 +906,13 @@ func (rq *replicateQueue) processOneChange(
// over-replicated and has dead replicas; in the common case we'll hit
// AllocatorReplaceDead{Non}Voter above.
case allocatorimpl.AllocatorRemoveDeadVoter:
- return rq.removeDead(ctx, repl, deadVoterReplicas, allocatorimpl.VoterTarget, dryRun)
+ requeue, err := rq.removeDead(ctx, repl, deadVoterReplicas, allocatorimpl.VoterTarget, dryRun)
+ rq.metrics.trackResultByAllocatorAction(action, err, dryRun)
+ return requeue, err
case allocatorimpl.AllocatorRemoveDeadNonVoter:
- return rq.removeDead(ctx, repl, deadNonVoterReplicas, allocatorimpl.NonVoterTarget, dryRun)
+ requeue, err := rq.removeDead(ctx, repl, deadNonVoterReplicas, allocatorimpl.NonVoterTarget, dryRun)
+ rq.metrics.trackResultByAllocatorAction(action, err, dryRun)
+ return requeue, err
case allocatorimpl.AllocatorConsiderRebalance:
return rq.considerRebalance(
diff --git a/pkg/kv/kvserver/replicate_queue_test.go b/pkg/kv/kvserver/replicate_queue_test.go
index b2fd78760175..6c5827963b0b 100644
--- a/pkg/kv/kvserver/replicate_queue_test.go
+++ b/pkg/kv/kvserver/replicate_queue_test.go
@@ -480,6 +480,8 @@ func TestReplicateQueueDecommissioningNonVoters(t *testing.T) {
previousRemovalCount := store.ReplicateQueueMetrics().RemoveNonVoterReplicaCount.Count()
previousDecommRemovals :=
store.ReplicateQueueMetrics().RemoveDecommissioningNonVoterReplicaCount.Count()
+ previousDecommReplacementSuccesses :=
+ store.ReplicateQueueMetrics().ReplaceDecommissioningReplicaSuccessCount.Count()
// Decommission each of the two nodes that have the non-voters and make sure
// that those non-voters are upreplicated elsewhere.
@@ -515,6 +517,8 @@ func TestReplicateQueueDecommissioningNonVoters(t *testing.T) {
currentRemoveCount := store.ReplicateQueueMetrics().RemoveNonVoterReplicaCount.Count()
currentDecommRemovals :=
store.ReplicateQueueMetrics().RemoveDecommissioningNonVoterReplicaCount.Count()
+ currentDecommReplacementSuccesses :=
+ store.ReplicateQueueMetrics().ReplaceDecommissioningReplicaSuccessCount.Count()
require.GreaterOrEqualf(
t, currentAddCount, previousAddCount+2,
@@ -528,6 +532,9 @@ func TestReplicateQueueDecommissioningNonVoters(t *testing.T) {
t, currentDecommRemovals, previousDecommRemovals+2,
"expected decommissioning replica removals to increase by at least 2",
)
+ require.GreaterOrEqualf(t, currentDecommReplacementSuccesses, previousDecommReplacementSuccesses+2,
+ "expected decommissioning replica replacement successes to increase by at least 2",
+ )
})
// Check that when we have more non-voters than needed and some of those
@@ -558,9 +565,22 @@ func TestReplicateQueueDecommissioningNonVoters(t *testing.T) {
if err != nil {
t.Fatal(err)
}
+
+ // Ensure leaseholder has updated span config with 0 non-voters.
+ require.Eventually(t, func() bool {
+ repl, err := store.GetReplica(scratchRange.RangeID)
+ if err != nil {
+ t.Fatal(err)
+ }
+ _, conf := repl.DescAndSpanConfig()
+ return conf.GetNumNonVoters() == 0
+ }, testutils.DefaultSucceedsSoonDuration, 100*time.Millisecond)
+
previousRemovalCount := store.ReplicateQueueMetrics().RemoveNonVoterReplicaCount.Count()
previousDecommRemovals :=
store.ReplicateQueueMetrics().RemoveDecommissioningNonVoterReplicaCount.Count()
+ previousDecommRemovalSuccesses :=
+ store.ReplicateQueueMetrics().RemoveDecommissioningReplicaSuccessCount.Count()
require.NoError(t,
tc.Server(0).Decommission(ctx, livenesspb.MembershipStatus_DECOMMISSIONING, nonVoterNodeIDs))
@@ -581,6 +601,8 @@ func TestReplicateQueueDecommissioningNonVoters(t *testing.T) {
currentRemoveCount := store.ReplicateQueueMetrics().RemoveNonVoterReplicaCount.Count()
currentDecommRemovals :=
store.ReplicateQueueMetrics().RemoveDecommissioningNonVoterReplicaCount.Count()
+ currentDecommRemovalSuccesses :=
+ store.ReplicateQueueMetrics().RemoveDecommissioningReplicaSuccessCount.Count()
require.GreaterOrEqualf(
t, currentRemoveCount, previousRemovalCount+2,
"expected replica removals to increase by at least 2",
@@ -589,6 +611,9 @@ func TestReplicateQueueDecommissioningNonVoters(t *testing.T) {
t, currentDecommRemovals, previousDecommRemovals+2,
"expected replica removals to increase by at least 2",
)
+ require.GreaterOrEqualf(t, currentDecommRemovalSuccesses, previousDecommRemovalSuccesses+2,
+ "expected decommissioning replica removal successes to increase by at least 2",
+ )
})
}
@@ -765,6 +790,7 @@ func TestReplicateQueueDeadNonVoters(t *testing.T) {
prevAdditions := store.ReplicateQueueMetrics().AddNonVoterReplicaCount.Count()
prevRemovals := store.ReplicateQueueMetrics().RemoveNonVoterReplicaCount.Count()
prevDeadRemovals := store.ReplicateQueueMetrics().RemoveDeadNonVoterReplicaCount.Count()
+ prevDeadReplacementSuccesses := store.ReplicateQueueMetrics().ReplaceDeadReplicaSuccessCount.Count()
beforeNodeIDs := getNonVoterNodeIDs(scratchRange)
markDead(beforeNodeIDs)
@@ -794,6 +820,7 @@ func TestReplicateQueueDeadNonVoters(t *testing.T) {
addCount := store.ReplicateQueueMetrics().AddNonVoterReplicaCount.Count()
removeNonVoterCount := store.ReplicateQueueMetrics().RemoveNonVoterReplicaCount.Count()
removeDeadNonVoterCount := store.ReplicateQueueMetrics().RemoveDeadNonVoterReplicaCount.Count()
+ replaceDeadSuccesses := store.ReplicateQueueMetrics().ReplaceDeadReplicaSuccessCount.Count()
require.GreaterOrEqualf(
t, addCount, prevAdditions+2,
@@ -807,6 +834,10 @@ func TestReplicateQueueDeadNonVoters(t *testing.T) {
t, removeDeadNonVoterCount, prevDeadRemovals+2,
"expected replica removals to increase by at least 2",
)
+ require.GreaterOrEqualf(
+ t, replaceDeadSuccesses, prevDeadReplacementSuccesses+2,
+ "expected dead replica replacement successes to increase by at least 2",
+ )
})
// This subtest checks that when we have more non-voters than needed and some
@@ -838,9 +869,21 @@ func TestReplicateQueueDeadNonVoters(t *testing.T) {
if err != nil {
t.Fatal(err)
}
+
+ // Ensure leaseholder has updated span config with 0 non-voters.
+ require.Eventually(t, func() bool {
+ repl, err := store.GetReplica(scratchRange.RangeID)
+ if err != nil {
+ t.Fatal(err)
+ }
+ _, conf := repl.DescAndSpanConfig()
+ return conf.GetNumNonVoters() == 0
+ }, testutils.DefaultSucceedsSoonDuration, 100*time.Millisecond)
+
prevRemovals := store.ReplicateQueueMetrics().RemoveReplicaCount.Count()
prevNonVoterRemovals := store.ReplicateQueueMetrics().RemoveNonVoterReplicaCount.Count()
prevDeadRemovals := store.ReplicateQueueMetrics().RemoveDeadNonVoterReplicaCount.Count()
+ prevDeadRemovalSuccesses := store.ReplicateQueueMetrics().RemoveDeadReplicaSuccessCount.Count()
beforeNodeIDs := getNonVoterNodeIDs(scratchRange)
markDead(beforeNodeIDs)
@@ -858,6 +901,7 @@ func TestReplicateQueueDeadNonVoters(t *testing.T) {
removeCount := store.ReplicateQueueMetrics().RemoveReplicaCount.Count()
removeNonVoterCount := store.ReplicateQueueMetrics().RemoveNonVoterReplicaCount.Count()
removeDeadNonVoterCount := store.ReplicateQueueMetrics().RemoveDeadNonVoterReplicaCount.Count()
+ removeDeadSuccesses := store.ReplicateQueueMetrics().RemoveDeadReplicaSuccessCount.Count()
require.GreaterOrEqualf(
t, removeCount, prevRemovals+2,
"expected replica removals to increase by at least 2",
@@ -870,6 +914,10 @@ func TestReplicateQueueDeadNonVoters(t *testing.T) {
t, removeDeadNonVoterCount, prevDeadRemovals+2,
"expected replica removals to increase by at least 2",
)
+ require.GreaterOrEqualf(
+ t, removeDeadSuccesses, prevDeadRemovalSuccesses+2,
+ "expected dead replica removal successes to increase by at least 2",
+ )
})
}
diff --git a/pkg/ts/catalog/chart_catalog.go b/pkg/ts/catalog/chart_catalog.go
index 3be998121eaf..f37b4e6d758b 100644
--- a/pkg/ts/catalog/chart_catalog.go
+++ b/pkg/ts/catalog/chart_catalog.go
@@ -2020,6 +2020,28 @@ var charts = []sectionDescription{
"queue.replicate.removedecommissioningnonvoterreplica",
},
},
+ {
+ Title: "Successes by Action",
+ Metrics: []string{
+ "queue.replicate.addreplica.success",
+ "queue.replicate.removereplica.success",
+ "queue.replicate.replacedeadreplica.success",
+ "queue.replicate.removedeadreplica.success",
+ "queue.replicate.replacedecommissioningreplica.success",
+ "queue.replicate.removedecommissioningreplica.success",
+ },
+ },
+ {
+ Title: "Errors by Action",
+ Metrics: []string{
+ "queue.replicate.addreplica.error",
+ "queue.replicate.removereplica.error",
+ "queue.replicate.replacedeadreplica.error",
+ "queue.replicate.removedeadreplica.error",
+ "queue.replicate.replacedecommissioningreplica.error",
+ "queue.replicate.removedecommissioningreplica.error",
+ },
+ },
{
Title: "Successes",
Metrics: []string{
From 0268864f3d7548804864c37564533ccfe1489c1c Mon Sep 17 00:00:00 2001
From: Rafi Shamim
Date: Wed, 10 Aug 2022 10:17:37 -0400
Subject: [PATCH 2/2] builtins: add trunc(decimal, int) builtin
Release note (sql change): Added the trunc(decimal, int) builtin
function, which truncates the given decimal value to the specified
number of decimal places. A negative value can be used for the scale
parameter, which will truncate to the left of the decimal point.
Example:
```
> select trunc(541.234, 2), trunc(541.234, 0), trunc(541.234, -1);
trunc | trunc | trunc
---------+-------+---------
541.23 | 541 | 5.4E+2
```
---
docs/generated/sql/functions.md | 2 +
pkg/sql/BUILD.bazel | 1 +
pkg/sql/function_resolver_test.go | 3 ++
.../testdata/logic_test/builtin_function | 17 +++++++++
.../logictest/testdata/logic_test/pg_catalog | 4 +-
.../logictest/testdata/logic_test/pgoidtype | 4 +-
pkg/sql/sem/builtins/math_builtins.go | 38 +++++++++++++++++++
7 files changed, 65 insertions(+), 4 deletions(-)
diff --git a/docs/generated/sql/functions.md b/docs/generated/sql/functions.md
index 8636272ce93b..1da8d2b52fe9 100644
--- a/docs/generated/sql/functions.md
+++ b/docs/generated/sql/functions.md
@@ -871,6 +871,8 @@ available replica will error.
Immutable |
trunc(val: decimal) → decimal | Truncates the decimal values of val .
| Immutable |
+trunc(val: decimal, scale: int) → decimal | Truncate val to scale decimal places
+ | Immutable |
trunc(val: float) → float | Truncates the decimal values of val .
| Immutable |
diff --git a/pkg/sql/BUILD.bazel b/pkg/sql/BUILD.bazel
index 3112e96c38a3..89e1f94ccb4a 100644
--- a/pkg/sql/BUILD.bazel
+++ b/pkg/sql/BUILD.bazel
@@ -671,6 +671,7 @@ go_test(
"//pkg/sql/catalog/descpb",
"//pkg/sql/catalog/descs",
"//pkg/sql/catalog/desctestutils",
+ "//pkg/sql/catalog/funcdesc",
"//pkg/sql/catalog/lease",
"//pkg/sql/catalog/multiregion",
"//pkg/sql/catalog/schemadesc",
diff --git a/pkg/sql/function_resolver_test.go b/pkg/sql/function_resolver_test.go
index ab24df66489e..cf3543c2c841 100644
--- a/pkg/sql/function_resolver_test.go
+++ b/pkg/sql/function_resolver_test.go
@@ -20,6 +20,7 @@ import (
"github.com/cockroachdb/cockroach/pkg/security/username"
"github.com/cockroachdb/cockroach/pkg/sql"
"github.com/cockroachdb/cockroach/pkg/sql/catalog/descs"
+ "github.com/cockroachdb/cockroach/pkg/sql/catalog/funcdesc"
"github.com/cockroachdb/cockroach/pkg/sql/parser"
"github.com/cockroachdb/cockroach/pkg/sql/sem/eval"
"github.com/cockroachdb/cockroach/pkg/sql/sem/tree"
@@ -405,6 +406,8 @@ CREATE FUNCTION sc1.lower(a STRING) RETURNS STRING IMMUTABLE LANGUAGE SQL AS $$
require.False(t, funcExpr.ResolvedOverload().UDFContainsOnlySignature)
if tc.expectedFuncOID > 0 {
require.Equal(t, tc.expectedFuncOID, int(funcExpr.ResolvedOverload().Oid))
+ } else {
+ require.False(t, funcdesc.IsOIDUserDefinedFunc(funcExpr.ResolvedOverload().Oid))
}
require.Equal(t, tc.expectedFuncBody, funcExpr.ResolvedOverload().Body)
})
diff --git a/pkg/sql/logictest/testdata/logic_test/builtin_function b/pkg/sql/logictest/testdata/logic_test/builtin_function
index b759f6114076..43af1a6fa944 100644
--- a/pkg/sql/logictest/testdata/logic_test/builtin_function
+++ b/pkg/sql/logictest/testdata/logic_test/builtin_function
@@ -1249,6 +1249,23 @@ SELECT trunc(-0.0), trunc(0.0), trunc(1.9), trunc(19.5678::decimal)
----
0 0 1 19
+query RRRRRRR
+WITH v(x) AS
+ (VALUES('0'::numeric),('1'::numeric),('-1'::numeric),('4.2'::numeric),
+ ('-7.777'::numeric),('9127.777'::numeric),('inf'::numeric),('-inf'::numeric),('nan'::numeric))
+SELECT x, trunc(x), trunc(x,1), trunc(x,2), trunc(x,0), trunc(x,-1), trunc(x,-2)
+FROM v
+----
+0 0 0.0 0.00 0 0 0
+1 1 1.0 1.00 1 0 0
+-1 -1 -1.0 -1.00 -1 0 0
+4.2 4 4.2 4.20 4 0 0
+-7.777 -7 -7.7 -7.77 -7 0 0
+9127.777 9127 9127.7 9127.77 9127 9.12E+3 9.1E+3
+Infinity Infinity Infinity Infinity Infinity Infinity Infinity
+-Infinity -Infinity -Infinity -Infinity -Infinity -Infinity -Infinity
+NaN NaN NaN NaN NaN NaN NaN
+
query T
SELECT translate('Techonthenet.com', 'e.to', '456')
----
diff --git a/pkg/sql/logictest/testdata/logic_test/pg_catalog b/pkg/sql/logictest/testdata/logic_test/pg_catalog
index 45ac45e5c613..bbc0875b37a4 100644
--- a/pkg/sql/logictest/testdata/logic_test/pg_catalog
+++ b/pkg/sql/logictest/testdata/logic_test/pg_catalog
@@ -4645,7 +4645,7 @@ FROM pg_proc p
JOIN pg_type t ON t.typinput = p.oid
WHERE t.typname = '_int4'
----
-2011 array_in array_in
+2012 array_in array_in
## #16285
## int2vectors should be 0-indexed
@@ -4683,7 +4683,7 @@ SELECT cur_max_builtin_oid FROM [SELECT max(oid) as cur_max_builtin_oid FROM pg_
query TT
SELECT proname, oid FROM pg_catalog.pg_proc WHERE oid = $cur_max_builtin_oid
----
-to_regtype 2031
+to_regtype 2032
## Ensure that unnest works with oid wrapper arrays
diff --git a/pkg/sql/logictest/testdata/logic_test/pgoidtype b/pkg/sql/logictest/testdata/logic_test/pgoidtype
index bc1ce3e7c70e..5f363e125bbc 100644
--- a/pkg/sql/logictest/testdata/logic_test/pgoidtype
+++ b/pkg/sql/logictest/testdata/logic_test/pgoidtype
@@ -83,7 +83,7 @@ WHERE relname = 'pg_constraint'
query OOOO
SELECT 'upper'::REGPROC, 'upper'::REGPROCEDURE, 'pg_catalog.upper'::REGPROCEDURE, 'upper'::REGPROC::OID
----
-upper upper upper 828
+upper upper upper 829
query error invalid function name
SELECT 'invalid.more.pg_catalog.upper'::REGPROCEDURE
@@ -91,7 +91,7 @@ SELECT 'invalid.more.pg_catalog.upper'::REGPROCEDURE
query OOO
SELECT 'upper(int)'::REGPROC, 'upper(int)'::REGPROCEDURE, 'upper(int)'::REGPROC::OID
----
-upper upper 828
+upper upper 829
query error unknown function: blah\(\)
SELECT 'blah(ignored, ignored)'::REGPROC, 'blah(ignored, ignored)'::REGPROCEDURE
diff --git a/pkg/sql/sem/builtins/math_builtins.go b/pkg/sql/sem/builtins/math_builtins.go
index be9267ae97d2..e390731eda95 100644
--- a/pkg/sql/sem/builtins/math_builtins.go
+++ b/pkg/sql/sem/builtins/math_builtins.go
@@ -36,6 +36,8 @@ var (
errAbsOfMinInt64 = pgerror.New(pgcode.NumericValueOutOfRange, "abs of min integer value (-9223372036854775808) not defined")
errLogOfNegNumber = pgerror.New(pgcode.InvalidArgumentForLogarithm, "cannot take logarithm of a negative number")
errLogOfZero = pgerror.New(pgcode.InvalidArgumentForLogarithm, "cannot take logarithm of zero")
+
+ bigTen = apd.NewBigInt(10)
)
const (
@@ -517,10 +519,46 @@ var mathBuiltins = map[string]builtinDefinition{
return tree.NewDFloat(tree.DFloat(math.Trunc(x))), nil
}, "Truncates the decimal values of `val`.", volatility.Immutable),
decimalOverload1(func(x *apd.Decimal) (tree.Datum, error) {
+ if x.Form == apd.NaN || x.Form == apd.Infinite {
+ return &tree.DDecimal{Decimal: *x}, nil
+ }
dd := &tree.DDecimal{}
x.Modf(&dd.Decimal, nil)
return dd, nil
}, "Truncates the decimal values of `val`.", volatility.Immutable),
+ tree.Overload{
+ Types: tree.ArgTypes{{"val", types.Decimal}, {"scale", types.Int}},
+ ReturnType: tree.FixedReturnType(types.Decimal),
+ Fn: func(ctx *eval.Context, args tree.Datums) (tree.Datum, error) {
+ // The algorithm here is also used in shopspring/decimal; see
+ // https://github.com/shopspring/decimal/blob/f55dd564545cec84cf84f7a53fb3025cdbec1c4f/decimal.go#L1315
+ dec := tree.MustBeDDecimal(args[0]).Decimal
+ scale := -int64(tree.MustBeDInt(args[1]))
+ if scale > int64(tree.DecimalCtx.MaxExponent) {
+ scale = int64(tree.DecimalCtx.MaxExponent)
+ } else if scale < int64(tree.DecimalCtx.MinExponent) {
+ scale = int64(tree.DecimalCtx.MinExponent)
+ }
+ if dec.Form == apd.NaN || dec.Form == apd.Infinite || scale == int64(dec.Exponent) {
+ return &tree.DDecimal{Decimal: dec}, nil
+ } else if scale >= (dec.NumDigits() + int64(dec.Exponent)) {
+ return &tree.DDecimal{Decimal: *decimalZero}, nil
+ }
+ ret := &tree.DDecimal{}
+ diff := math.Abs(float64(scale) - float64(dec.Exponent))
+ expScale := apd.NewBigInt(0).Exp(bigTen, apd.NewBigInt(int64(diff)), nil)
+ if scale > int64(dec.Exponent) {
+ _ = ret.Coeff.Quo(&dec.Coeff, expScale)
+ } else if scale < int64(dec.Exponent) {
+ _ = ret.Coeff.Mul(&dec.Coeff, expScale)
+ }
+ ret.Exponent = int32(scale)
+ ret.Negative = dec.Negative
+ return ret, nil
+ },
+ Info: "Truncate `val` to `scale` decimal places",
+ Volatility: volatility.Immutable,
+ },
),
"width_bucket": makeBuiltin(defProps(),