diff options
author | John Lee <64482439+algojohnlee@users.noreply.github.com> | 2022-09-09 10:05:53 -0400 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-09-09 10:05:53 -0400 |
commit | d178342371f9d3f50ef0fa983b9e40a9687a5dac (patch) | |
tree | 18ffa8aab6616d59cc865710534986c9c854acaf | |
parent | a32df45b722509aa9c2fd3e2278457ec47e0477e (diff) | |
parent | b11d6032200b5d3a13e2b44759d39bbe600d7976 (diff) |
Merge pull request #4521 from Algo-devops-service/relstable3.9.3v3.9.3-stable
-rw-r--r-- | buildnumber.dat | 2 | ||||
-rw-r--r-- | daemon/algod/api/algod.oas2.json | 12 | ||||
-rw-r--r-- | daemon/algod/api/algod.oas3.yml | 20 | ||||
-rw-r--r-- | daemon/algod/api/server/v2/generated/routes.go | 418 | ||||
-rw-r--r-- | daemon/algod/api/server/v2/handlers.go | 56 | ||||
-rw-r--r-- | daemon/algod/api/server/v2/test/handlers_resources_test.go | 15 | ||||
-rw-r--r-- | daemon/algod/api/server/v2/test/handlers_test.go | 85 | ||||
-rw-r--r-- | daemon/algod/api/server/v2/utils.go | 4 | ||||
-rw-r--r-- | installer/external/node_exporter-stable-darwin-x86_64.tar.gz | bin | 6901398 -> 6901768 bytes | |||
-rw-r--r-- | installer/external/node_exporter-stable-linux-x86_64.tar.gz | bin | 7142051 -> 7142297 bytes | |||
-rw-r--r-- | network/wsPeer.go | 17 | ||||
-rw-r--r-- | protocol/tags.go | 19 | ||||
-rw-r--r-- | util/metrics/tagcounter.go | 27 | ||||
-rw-r--r-- | util/metrics/tagcounter_test.go | 78 |
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 Binary files differindex 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 diff --git a/installer/external/node_exporter-stable-linux-x86_64.tar.gz b/installer/external/node_exporter-stable-linux-x86_64.tar.gz Binary files differindex 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 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) |