summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJohn Lee <64482439+algojohnlee@users.noreply.github.com>2022-09-09 10:05:53 -0400
committerGitHub <noreply@github.com>2022-09-09 10:05:53 -0400
commitd178342371f9d3f50ef0fa983b9e40a9687a5dac (patch)
tree18ffa8aab6616d59cc865710534986c9c854acaf
parenta32df45b722509aa9c2fd3e2278457ec47e0477e (diff)
parentb11d6032200b5d3a13e2b44759d39bbe600d7976 (diff)
Merge pull request #4521 from Algo-devops-service/relstable3.9.3v3.9.3-stable
-rw-r--r--buildnumber.dat2
-rw-r--r--daemon/algod/api/algod.oas2.json12
-rw-r--r--daemon/algod/api/algod.oas3.yml20
-rw-r--r--daemon/algod/api/server/v2/generated/routes.go418
-rw-r--r--daemon/algod/api/server/v2/handlers.go56
-rw-r--r--daemon/algod/api/server/v2/test/handlers_resources_test.go15
-rw-r--r--daemon/algod/api/server/v2/test/handlers_test.go85
-rw-r--r--daemon/algod/api/server/v2/utils.go4
-rw-r--r--installer/external/node_exporter-stable-darwin-x86_64.tar.gzbin6901398 -> 6901768 bytes
-rw-r--r--installer/external/node_exporter-stable-linux-x86_64.tar.gzbin7142051 -> 7142297 bytes
-rw-r--r--network/wsPeer.go17
-rw-r--r--protocol/tags.go19
-rw-r--r--util/metrics/tagcounter.go27
-rw-r--r--util/metrics/tagcounter_test.go78
14 files changed, 521 insertions, 232 deletions
diff --git a/buildnumber.dat b/buildnumber.dat
index 0cfbf0888..00750edc0 100644
--- a/buildnumber.dat
+++ b/buildnumber.dat
@@ -1 +1 @@
-2
+3
diff --git a/daemon/algod/api/algod.oas2.json b/daemon/algod/api/algod.oas2.json
index 9af8a2aef..f5cd705e5 100644
--- a/daemon/algod/api/algod.oas2.json
+++ b/daemon/algod/api/algod.oas2.json
@@ -1282,6 +1282,12 @@
"$ref": "#/definitions/ErrorResponse"
}
},
+ "408": {
+ "description": "timed out on request",
+ "schema": {
+ "$ref": "#/definitions/ErrorResponse"
+ }
+ },
"500": {
"description": "Internal Error",
"schema": {
@@ -1338,6 +1344,12 @@
"$ref": "#/definitions/ErrorResponse"
}
},
+ "408": {
+ "description": "timed out on request",
+ "schema": {
+ "$ref": "#/definitions/ErrorResponse"
+ }
+ },
"404": {
"description": "Could not create proof since some data is missing",
"schema": {
diff --git a/daemon/algod/api/algod.oas3.yml b/daemon/algod/api/algod.oas3.yml
index 404da3a2e..50bd0494d 100644
--- a/daemon/algod/api/algod.oas3.yml
+++ b/daemon/algod/api/algod.oas3.yml
@@ -2747,6 +2747,16 @@
},
"description": "Could not create proof since some data is missing"
},
+ "408": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/ErrorResponse"
+ }
+ }
+ },
+ "description": "timed out on request"
+ },
"500": {
"content": {
"application/json": {
@@ -3619,6 +3629,16 @@
},
"description": "Could not find a state proof that covers a given round"
},
+ "408": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/ErrorResponse"
+ }
+ }
+ },
+ "description": "timed out on request"
+ },
"500": {
"content": {
"application/json": {
diff --git a/daemon/algod/api/server/v2/generated/routes.go b/daemon/algod/api/server/v2/generated/routes.go
index f8fc80514..3389b370d 100644
--- a/daemon/algod/api/server/v2/generated/routes.go
+++ b/daemon/algod/api/server/v2/generated/routes.go
@@ -856,215 +856,215 @@ func RegisterHandlers(router interface {
// Base64 encoded, gzipped, json marshaled Swagger object
var swaggerSpec = []string{
- "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",
- "7uP/DwAA///T+v88UiwBAA==",
+ "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",
}
// GetSwagger returns the Swagger specification corresponding to the generated code
diff --git a/daemon/algod/api/server/v2/handlers.go b/daemon/algod/api/server/v2/handlers.go
index 918440f19..5898c2835 100644
--- a/daemon/algod/api/server/v2/handlers.go
+++ b/daemon/algod/api/server/v2/handlers.go
@@ -18,6 +18,7 @@ package v2
import (
"bytes"
+ "context"
"encoding/base64"
"errors"
"fmt"
@@ -150,10 +151,33 @@ func convertParticipationRecord(record account.ParticipationRecord) generated.Pa
// ErrNoStateProofForRound returned when a state proof transaction could not be found
var ErrNoStateProofForRound = errors.New("no state proof can be found for that round")
+// ErrTimeout indicates a task took too long, and the server canceled it.
+var ErrTimeout = errors.New("timed out on request")
+
+// ErrShutdown represents the error for the string errServiceShuttingDown
+var ErrShutdown = errors.New(errServiceShuttingDown)
+
// GetStateProofTransactionForRound searches for a state proof transaction that can be used to prove on the given round (i.e the round is within the
// attestation period). the latestRound should be provided as an upper bound for the search
-func GetStateProofTransactionForRound(txnFetcher LedgerForAPI, round basics.Round, latestRound basics.Round) (transactions.Transaction, error) {
+func GetStateProofTransactionForRound(ctx context.Context, txnFetcher LedgerForAPI, round, latestRound basics.Round, stop <-chan struct{}) (transactions.Transaction, error) {
+ hdr, err := txnFetcher.BlockHdr(round)
+ if err != nil {
+ return transactions.Transaction{}, err
+ }
+
+ if config.Consensus[hdr.CurrentProtocol].StateProofInterval == 0 {
+ return transactions.Transaction{}, ErrNoStateProofForRound
+ }
+
for i := round + 1; i <= latestRound; i++ {
+ select {
+ case <-stop:
+ return transactions.Transaction{}, ErrShutdown
+ case <-ctx.Done():
+ return transactions.Transaction{}, ErrTimeout
+ default:
+ }
+
txns, err := txnFetcher.AddressTxns(transactions.StateProofSender, i)
if err != nil {
return transactions.Transaction{}, err
@@ -1241,16 +1265,17 @@ func (v2 *Handlers) TealCompile(ctx echo.Context, params generated.TealCompilePa
// GetStateProof returns the state proof for a given round.
// (GET /v2/stateproofs/{round})
func (v2 *Handlers) GetStateProof(ctx echo.Context, round uint64) error {
+ ctxWithTimeout, cancel := context.WithTimeout(ctx.Request().Context(), time.Minute)
+ defer cancel()
+
ledger := v2.Node.LedgerForAPI()
if ledger.Latest() < basics.Round(round) {
return internalError(ctx, errors.New(errRoundGreaterThanTheLatest), errRoundGreaterThanTheLatest, v2.Log)
}
- tx, err := GetStateProofTransactionForRound(ledger, basics.Round(round), ledger.Latest())
+
+ tx, err := GetStateProofTransactionForRound(ctxWithTimeout, ledger, basics.Round(round), ledger.Latest(), v2.Shutdown)
if err != nil {
- if errors.Is(err, ErrNoStateProofForRound) {
- return notFound(ctx, err, err.Error(), v2.Log)
- }
- return internalError(ctx, err, err.Error(), v2.Log)
+ return v2.wrapStateproofError(ctx, err)
}
response := generated.StateProofResponse{
@@ -1266,20 +1291,29 @@ func (v2 *Handlers) GetStateProof(ctx echo.Context, round uint64) error {
return ctx.JSON(http.StatusOK, response)
}
+func (v2 *Handlers) wrapStateproofError(ctx echo.Context, err error) error {
+ if errors.Is(err, ErrNoStateProofForRound) {
+ return notFound(ctx, err, err.Error(), v2.Log)
+ }
+ if errors.Is(err, ErrTimeout) {
+ return timeout(ctx, err, err.Error(), v2.Log)
+ }
+ return internalError(ctx, err, err.Error(), v2.Log)
+}
+
// GetLightBlockHeaderProof Gets a proof of a light block header for a given round
// (GET /v2/blocks/{round}/lightheader/proof)
func (v2 *Handlers) GetLightBlockHeaderProof(ctx echo.Context, round uint64) error {
+ ctxWithTimeout, cancel := context.WithTimeout(ctx.Request().Context(), time.Minute)
+ defer cancel()
ledger := v2.Node.LedgerForAPI()
if ledger.Latest() < basics.Round(round) {
return internalError(ctx, errors.New(errRoundGreaterThanTheLatest), errRoundGreaterThanTheLatest, v2.Log)
}
- stateProof, err := GetStateProofTransactionForRound(ledger, basics.Round(round), ledger.Latest())
+ stateProof, err := GetStateProofTransactionForRound(ctxWithTimeout, ledger, basics.Round(round), ledger.Latest(), v2.Shutdown)
if err != nil {
- if errors.Is(err, ErrNoStateProofForRound) {
- return notFound(ctx, err, err.Error(), v2.Log)
- }
- return internalError(ctx, err, err.Error(), v2.Log)
+ return v2.wrapStateproofError(ctx, err)
}
lastAttestedRound := stateProof.Message.LastAttestedRound
diff --git a/daemon/algod/api/server/v2/test/handlers_resources_test.go b/daemon/algod/api/server/v2/test/handlers_resources_test.go
index b3062b42b..b7b52eeda 100644
--- a/daemon/algod/api/server/v2/test/handlers_resources_test.go
+++ b/daemon/algod/api/server/v2/test/handlers_resources_test.go
@@ -18,6 +18,7 @@ package test
import (
"encoding/json"
+ "fmt"
"net/http"
"net/http/httptest"
"testing"
@@ -98,8 +99,12 @@ func (l *mockLedger) BlockCert(rnd basics.Round) (blk bookkeeping.Block, cert ag
func (l *mockLedger) LatestTotals() (rnd basics.Round, at ledgercore.AccountTotals, err error) {
panic("not implemented")
}
-func (l *mockLedger) BlockHdr(rnd basics.Round) (blk bookkeeping.BlockHeader, err error) {
- panic("not implemented")
+func (l *mockLedger) BlockHdr(rnd basics.Round) (bookkeeping.BlockHeader, error) {
+ blk, err := l.Block(rnd)
+ if err != nil {
+ return bookkeeping.BlockHeader{}, err
+ }
+ return blk.BlockHeader, nil
}
func (l *mockLedger) Wait(r basics.Round) chan struct{} {
panic("not implemented")
@@ -111,7 +116,11 @@ func (l *mockLedger) EncodedBlockCert(rnd basics.Round) (blk []byte, cert []byte
panic("not implemented")
}
func (l *mockLedger) Block(rnd basics.Round) (blk bookkeeping.Block, err error) {
- panic("not implemented")
+ if len(l.blocks) == 0 {
+ err = fmt.Errorf("mockledger error: no block")
+ return
+ }
+ return l.blocks[0], nil
}
func (l *mockLedger) AddressTxns(id basics.Address, r basics.Round) ([]transactions.SignedTxnWithAD, error) {
diff --git a/daemon/algod/api/server/v2/test/handlers_test.go b/daemon/algod/api/server/v2/test/handlers_test.go
index 789097fda..ad3d2ee69 100644
--- a/daemon/algod/api/server/v2/test/handlers_test.go
+++ b/daemon/algod/api/server/v2/test/handlers_test.go
@@ -18,6 +18,7 @@ package test
import (
"bytes"
+ "context"
"encoding/json"
"errors"
"fmt"
@@ -25,6 +26,7 @@ import (
"net/http"
"net/http/httptest"
"testing"
+ "time"
"github.com/labstack/echo/v4"
"github.com/stretchr/testify/assert"
@@ -1135,25 +1137,100 @@ func TestStateproofTransactionForRound(t *testing.T) {
var blk bookkeeping.Block
blk.BlockHeader = bookkeeping.BlockHeader{
Round: basics.Round(i),
+ UpgradeState: bookkeeping.UpgradeState{
+ CurrentProtocol: protocol.ConsensusCurrentVersion,
+ },
}
blk = addStateProofIfNeeded(blk)
ledger.blocks = append(ledger.blocks, blk)
}
- txn, err := v2.GetStateProofTransactionForRound(&ledger, basics.Round(stateProofIntervalForHandlerTests*2+1), 1000)
+ ctx, cncl := context.WithTimeout(context.Background(), time.Minute*2)
+ defer cncl()
+ txn, err := v2.GetStateProofTransactionForRound(ctx, &ledger, basics.Round(stateProofIntervalForHandlerTests*2+1), 1000, nil)
a.NoError(err)
a.Equal(2*stateProofIntervalForHandlerTests+1, txn.Message.FirstAttestedRound)
a.Equal(3*stateProofIntervalForHandlerTests, txn.Message.LastAttestedRound)
a.Equal([]byte{0x0, 0x1, 0x2}, txn.Message.BlockHeadersCommitment)
- txn, err = v2.GetStateProofTransactionForRound(&ledger, basics.Round(2*stateProofIntervalForHandlerTests), 1000)
+ txn, err = v2.GetStateProofTransactionForRound(ctx, &ledger, basics.Round(2*stateProofIntervalForHandlerTests), 1000, nil)
a.NoError(err)
a.Equal(stateProofIntervalForHandlerTests+1, txn.Message.FirstAttestedRound)
a.Equal(2*stateProofIntervalForHandlerTests, txn.Message.LastAttestedRound)
- txn, err = v2.GetStateProofTransactionForRound(&ledger, 999, 1000)
+ txn, err = v2.GetStateProofTransactionForRound(ctx, &ledger, 999, 1000, nil)
a.ErrorIs(err, v2.ErrNoStateProofForRound)
- txn, err = v2.GetStateProofTransactionForRound(&ledger, basics.Round(2*stateProofIntervalForHandlerTests), basics.Round(2*stateProofIntervalForHandlerTests))
+ txn, err = v2.GetStateProofTransactionForRound(ctx, &ledger, basics.Round(2*stateProofIntervalForHandlerTests), basics.Round(2*stateProofIntervalForHandlerTests), nil)
a.ErrorIs(err, v2.ErrNoStateProofForRound)
}
+
+func TestStateproofTransactionForRoundWithoutStateproofs(t *testing.T) {
+ partitiontest.PartitionTest(t)
+ a := require.New(t)
+
+ ledger := mockLedger{blocks: make([]bookkeeping.Block, 0, 1000)}
+ for i := 0; i <= 1000; i++ {
+ var blk bookkeeping.Block
+ blk.BlockHeader = bookkeeping.BlockHeader{
+ Round: basics.Round(i),
+ UpgradeState: bookkeeping.UpgradeState{
+ CurrentProtocol: protocol.ConsensusV30, // should have StateProofInterval == 0 .
+ },
+ }
+ blk = addStateProofIfNeeded(blk)
+ ledger.blocks = append(ledger.blocks, blk)
+ }
+ ctx, cncl := context.WithTimeout(context.Background(), time.Minute)
+ defer cncl()
+ _, err := v2.GetStateProofTransactionForRound(ctx, &ledger, basics.Round(stateProofIntervalForHandlerTests*2+1), 1000, nil)
+ a.ErrorIs(err, v2.ErrNoStateProofForRound)
+}
+
+func TestStateproofTransactionForRoundTimeouts(t *testing.T) {
+ partitiontest.PartitionTest(t)
+ a := require.New(t)
+
+ ledger := mockLedger{blocks: make([]bookkeeping.Block, 0, 1000)}
+ for i := 0; i <= 1000; i++ {
+ var blk bookkeeping.Block
+ blk.BlockHeader = bookkeeping.BlockHeader{
+ Round: basics.Round(i),
+ UpgradeState: bookkeeping.UpgradeState{
+ CurrentProtocol: protocol.ConsensusCurrentVersion, // should have StateProofInterval != 0 .
+ },
+ }
+ blk = addStateProofIfNeeded(blk)
+ ledger.blocks = append(ledger.blocks, blk)
+ }
+
+ ctx, cncl := context.WithTimeout(context.Background(), time.Nanosecond)
+ defer cncl()
+ _, err := v2.GetStateProofTransactionForRound(ctx, &ledger, basics.Round(stateProofIntervalForHandlerTests*2+1), 1000, nil)
+ a.ErrorIs(err, v2.ErrTimeout)
+}
+
+func TestStateproofTransactionForRoundShutsDown(t *testing.T) {
+ partitiontest.PartitionTest(t)
+ a := require.New(t)
+
+ ledger := mockLedger{blocks: make([]bookkeeping.Block, 0, 1000)}
+ for i := 0; i <= 1000; i++ {
+ var blk bookkeeping.Block
+ blk.BlockHeader = bookkeeping.BlockHeader{
+ Round: basics.Round(i),
+ UpgradeState: bookkeeping.UpgradeState{
+ CurrentProtocol: protocol.ConsensusCurrentVersion, // should have StateProofInterval != 0 .
+ },
+ }
+ blk = addStateProofIfNeeded(blk)
+ ledger.blocks = append(ledger.blocks, blk)
+ }
+
+ stoppedChan := make(chan struct{})
+ close(stoppedChan)
+ ctx, cncl := context.WithTimeout(context.Background(), time.Minute)
+ defer cncl()
+ _, err := v2.GetStateProofTransactionForRound(ctx, &ledger, basics.Round(stateProofIntervalForHandlerTests*2+1), 1000, stoppedChan)
+ a.ErrorIs(err, v2.ErrShutdown)
+}
diff --git a/daemon/algod/api/server/v2/utils.go b/daemon/algod/api/server/v2/utils.go
index 1fa1b1dfa..fc9dbe2f4 100644
--- a/daemon/algod/api/server/v2/utils.go
+++ b/daemon/algod/api/server/v2/utils.go
@@ -49,6 +49,10 @@ func serviceUnavailable(ctx echo.Context, internal error, external string, log l
return returnError(ctx, http.StatusServiceUnavailable, internal, external, log)
}
+func timeout(ctx echo.Context, internal error, external string, log logging.Logger) error {
+ return returnError(ctx, http.StatusRequestTimeout, internal, external, log)
+}
+
func internalError(ctx echo.Context, internal error, external string, log logging.Logger) error {
return returnError(ctx, http.StatusInternalServerError, internal, external, log)
}
diff --git a/installer/external/node_exporter-stable-darwin-x86_64.tar.gz b/installer/external/node_exporter-stable-darwin-x86_64.tar.gz
index 2b25a6e30..f847ea666 100644
--- a/installer/external/node_exporter-stable-darwin-x86_64.tar.gz
+++ b/installer/external/node_exporter-stable-darwin-x86_64.tar.gz
Binary files differ
diff --git a/installer/external/node_exporter-stable-linux-x86_64.tar.gz b/installer/external/node_exporter-stable-linux-x86_64.tar.gz
index c9368910c..ef0da17fd 100644
--- a/installer/external/node_exporter-stable-linux-x86_64.tar.gz
+++ b/installer/external/node_exporter-stable-linux-x86_64.tar.gz
Binary files differ
diff --git a/network/wsPeer.go b/network/wsPeer.go
index 594accb3d..6ae9bc0d2 100644
--- a/network/wsPeer.go
+++ b/network/wsPeer.go
@@ -47,15 +47,24 @@ const averageMessageLength = 2 * 1024 // Most of the messages are smaller tha
// buffer and starve messages from other peers.
const msgsInReadBufferPerPeer = 10
+var tagStringList []string
+
+func init() {
+ tagStringList = make([]string, len(protocol.TagList))
+ for i, t := range protocol.TagList {
+ tagStringList[i] = string(t)
+ }
+}
+
var networkSentBytesTotal = metrics.MakeCounter(metrics.NetworkSentBytesTotal)
-var networkSentBytesByTag = metrics.NewTagCounter("algod_network_sent_bytes_{TAG}", "Number of bytes that were sent over the network for {TAG} messages")
+var networkSentBytesByTag = metrics.NewTagCounterFiltered("algod_network_sent_bytes_{TAG}", "Number of bytes that were sent over the network for {TAG} messages", tagStringList, "UNK")
var networkReceivedBytesTotal = metrics.MakeCounter(metrics.NetworkReceivedBytesTotal)
-var networkReceivedBytesByTag = metrics.NewTagCounter("algod_network_received_bytes_{TAG}", "Number of bytes that were received from the network for {TAG} messages")
+var networkReceivedBytesByTag = metrics.NewTagCounterFiltered("algod_network_received_bytes_{TAG}", "Number of bytes that were received from the network for {TAG} messages", tagStringList, "UNK")
var networkMessageReceivedTotal = metrics.MakeCounter(metrics.NetworkMessageReceivedTotal)
-var networkMessageReceivedByTag = metrics.NewTagCounter("algod_network_message_received_{TAG}", "Number of complete messages that were received from the network for {TAG} messages")
+var networkMessageReceivedByTag = metrics.NewTagCounterFiltered("algod_network_message_received_{TAG}", "Number of complete messages that were received from the network for {TAG} messages", tagStringList, "UNK")
var networkMessageSentTotal = metrics.MakeCounter(metrics.NetworkMessageSentTotal)
-var networkMessageSentByTag = metrics.NewTagCounter("algod_network_message_sent_{TAG}", "Number of complete messages that were sent to the network for {TAG} messages")
+var networkMessageSentByTag = metrics.NewTagCounterFiltered("algod_network_message_sent_{TAG}", "Number of complete messages that were sent to the network for {TAG} messages", tagStringList, "UNK")
var networkConnectionsDroppedTotal = metrics.MakeCounter(metrics.NetworkConnectionsDroppedTotal)
var networkMessageQueueMicrosTotal = metrics.MakeCounter(metrics.MetricName{Name: "algod_network_message_sent_queue_micros_total", Description: "Total microseconds message spent waiting in queue to be sent"})
diff --git a/protocol/tags.go b/protocol/tags.go
index f3bff635b..01aee9126 100644
--- a/protocol/tags.go
+++ b/protocol/tags.go
@@ -42,3 +42,22 @@ const (
//UniCatchupResTag Tag = "UT" was used for wsfetcherservice
VoteBundleTag Tag = "VB"
)
+
+// TagList is a list of all currently used protocol tags.
+// TODO: generate this and/or have a test that it is complete.
+var TagList = []Tag{
+ UnknownMsgTag,
+ AgreementVoteTag,
+ MsgOfInterestTag,
+ MsgDigestSkipTag,
+ NetPrioResponseTag,
+ PingTag,
+ PingReplyTag,
+ ProposalPayloadTag,
+ StateProofSigTag,
+ TopicMsgRespTag,
+ TxnTag,
+ UniCatchupReqTag,
+ UniEnsBlockReqTag,
+ VoteBundleTag,
+}
diff --git a/util/metrics/tagcounter.go b/util/metrics/tagcounter.go
index d110b8d4f..d4de0d53c 100644
--- a/util/metrics/tagcounter.go
+++ b/util/metrics/tagcounter.go
@@ -24,6 +24,22 @@ import (
"github.com/algorand/go-deadlock"
)
+// NewTagCounterFiltered makes a set of metrics under rootName for tagged counting.
+// "{TAG}" in rootName is replaced by the tag, otherwise "_{TAG}" is appended.
+// Tags not in allowedTags will be filtered out and ignored.
+// unknownTag may be "" or a value that will be counted for tags not in allowedTags.
+func NewTagCounterFiltered(rootName, desc string, allowedTags []string, unknownTag string) *TagCounter {
+ tc := &TagCounter{Name: rootName, Description: desc, UnknownTag: unknownTag}
+ if len(allowedTags) != 0 {
+ tc.AllowedTags = make(map[string]bool, len(allowedTags))
+ for _, tag := range allowedTags {
+ tc.AllowedTags[tag] = true
+ }
+ }
+ DefaultRegistry().Register(tc)
+ return tc
+}
+
// NewTagCounter makes a set of metrics under rootName for tagged counting.
// "{TAG}" in rootName is replaced by the tag, otherwise "_{TAG}" is appended.
// Optionally provided declaredTags counters for these names up front (making them easier to discover).
@@ -41,6 +57,10 @@ type TagCounter struct {
Name string
Description string
+ AllowedTags map[string]bool
+
+ UnknownTag string
+
// a read only race-free reference to tags
tagptr atomic.Value
@@ -54,6 +74,13 @@ type TagCounter struct {
// Add t[tag] += val, fast and multithread safe
func (tc *TagCounter) Add(tag string, val uint64) {
+ if (tc.AllowedTags != nil) && (!tc.AllowedTags[tag]) {
+ if len(tc.UnknownTag) != 0 {
+ tag = tc.UnknownTag
+ } else {
+ return
+ }
+ }
for {
var tags map[string]*uint64
tagptr := tc.tagptr.Load()
diff --git a/util/metrics/tagcounter_test.go b/util/metrics/tagcounter_test.go
index feb464a35..9e8a50717 100644
--- a/util/metrics/tagcounter_test.go
+++ b/util/metrics/tagcounter_test.go
@@ -81,6 +81,84 @@ func TestTagCounter(t *testing.T) {
}
}
+func TestTagCounterFilter(t *testing.T) {
+ partitiontest.PartitionTest(t)
+
+ tags := make([]string, 17)
+ for i := range tags {
+ tags[i] = fmt.Sprintf("A%c", 'A'+i)
+ }
+ goodTags := tags[:10]
+ badTags := tags[10:]
+ badCount := uint64(0)
+ //t.Logf("tags %v", tags)
+ countsIn := make([]uint64, len(tags))
+ for i := range countsIn {
+ countsIn[i] = uint64(10 * (i + 1))
+ if i >= 10 {
+ badCount += countsIn[i]
+ }
+ }
+
+ tc := NewTagCounterFiltered("tc", "wat", goodTags, "UNK")
+ DefaultRegistry().Deregister(tc)
+
+ // check that empty TagCounter cleanly returns no results
+ var sb strings.Builder
+ tc.WriteMetric(&sb, "")
+ require.Equal(t, "", sb.String())
+
+ result := make(map[string]float64)
+ tc.AddMetric(result)
+ require.Equal(t, 0, len(result))
+
+ var wg sync.WaitGroup
+ wg.Add(len(tags))
+
+ runf := func(tag string, count uint64) {
+ for i := 0; i < int(count); i++ {
+ tc.Add(tag, 1)
+ }
+ wg.Done()
+ }
+
+ for i, tag := range tags {
+ go runf(tag, countsIn[i])
+ }
+ wg.Wait()
+
+ endtags := tc.tagptr.Load().(map[string]*uint64)
+ for i, tag := range goodTags {
+ countin := countsIn[i]
+ endcountp := endtags[tag]
+ if endcountp == nil {
+ t.Errorf("tag[%d] %s nil counter", i, tag)
+ continue
+ }
+ endcount := *endcountp
+ if endcount != countin {
+ t.Errorf("tag[%d] %v wanted %d got %d", i, tag, countin, endcount)
+ }
+ }
+ for _, tag := range badTags {
+ endcountp := endtags[tag]
+ if endcountp == nil {
+ // best, nil entry, never touched the struct
+ continue
+ }
+ endcount := *endcountp
+ if endcount != 0 {
+ t.Errorf("bad tag %v wanted %d got %d", tag, 0, endcount)
+ }
+ }
+ endcountp := endtags["UNK"]
+ endcount := uint64(0)
+ if endcountp != nil {
+ endcount = *endcountp
+ }
+ require.Equal(t, badCount, endcount)
+}
+
func TestTagCounterWriteMetric(t *testing.T) {
partitiontest.PartitionTest(t)