feat_: call telemetry upon error pushing envelope (#5430)
* feat_: call telemetry upon error pushing envelope * feat_: call telemetry upon error pushing envelope --------- Co-authored-by: Václav Pavlín <vaclav.pavlin@gmail.com>
This commit is contained in:
parent
bd8aa0dac7
commit
5934233266
|
@ -22,6 +22,7 @@ const APIModulesFlag = "api-modules"
|
||||||
const TelemetryServerURLFlag = "telemetry-server-url"
|
const TelemetryServerURLFlag = "telemetry-server-url"
|
||||||
const KeyUIDFlag = "key-uid"
|
const KeyUIDFlag = "key-uid"
|
||||||
const DebugLevel = "debug"
|
const DebugLevel = "debug"
|
||||||
|
const MessageFailureFlag = "fail"
|
||||||
|
|
||||||
const RetrieveInterval = 300 * time.Millisecond
|
const RetrieveInterval = 300 * time.Millisecond
|
||||||
const SendInterval = 1 * time.Second
|
const SendInterval = 1 * time.Second
|
||||||
|
@ -64,6 +65,12 @@ var SimulateFlags = append([]cli.Flag{
|
||||||
Value: 1,
|
Value: 1,
|
||||||
Usage: "How many messages to sent from each user",
|
Usage: "How many messages to sent from each user",
|
||||||
},
|
},
|
||||||
|
&cli.BoolFlag{
|
||||||
|
Name: MessageFailureFlag,
|
||||||
|
Aliases: []string{"f"},
|
||||||
|
Usage: "Causes messages to fail about 25% of the time",
|
||||||
|
Value: false,
|
||||||
|
},
|
||||||
}, CommonFlags...)
|
}, CommonFlags...)
|
||||||
|
|
||||||
var ServeFlags = append([]cli.Flag{
|
var ServeFlags = append([]cli.Flag{
|
||||||
|
|
|
@ -3,7 +3,9 @@ package main
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
"context"
|
"context"
|
||||||
|
"crypto/rand"
|
||||||
"log/slog"
|
"log/slog"
|
||||||
|
"math/big"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
@ -41,7 +43,30 @@ func (cli *StatusCLI) sendContactRequestAcceptance(ctx context.Context, msgID st
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cli *StatusCLI) sendDirectMessage(ctx context.Context, text string) error {
|
func (cli *StatusCLI) randomFailure() func() {
|
||||||
|
nBig, err := rand.Int(rand.Reader, big.NewInt(100))
|
||||||
|
if err != nil {
|
||||||
|
cli.logger.Error("failed to generate random number", "err", err)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
n := nBig.Int64()
|
||||||
|
if n >= 40 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
cli.backend.StatusNode().WakuV2Service().SkipPublishToTopic(true)
|
||||||
|
|
||||||
|
return func() {
|
||||||
|
cli.backend.StatusNode().WakuV2Service().SkipPublishToTopic(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cli *StatusCLI) sendDirectMessage(ctx context.Context, text string, options ...bool) error {
|
||||||
|
randomFailure := false
|
||||||
|
if len(options) > 0 {
|
||||||
|
randomFailure = options[0]
|
||||||
|
}
|
||||||
|
|
||||||
if len(cli.messenger.MutualContacts()) == 0 {
|
if len(cli.messenger.MutualContacts()) == 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -58,8 +83,20 @@ func (cli *StatusCLI) sendDirectMessage(ctx context.Context, text string) error
|
||||||
inputMessage.ContentType = protobuf.ChatMessage_TEXT_PLAIN
|
inputMessage.ContentType = protobuf.ChatMessage_TEXT_PLAIN
|
||||||
inputMessage.Text = text
|
inputMessage.Text = text
|
||||||
|
|
||||||
|
shouldFail := false
|
||||||
|
if randomFailure {
|
||||||
|
if postFailure := cli.randomFailure(); postFailure != nil {
|
||||||
|
defer postFailure()
|
||||||
|
shouldFail = true
|
||||||
|
}
|
||||||
|
}
|
||||||
resp, err := cli.messenger.SendChatMessage(ctx, inputMessage)
|
resp, err := cli.messenger.SendChatMessage(ctx, inputMessage)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
if shouldFail {
|
||||||
|
cli.logger.Info("simulating message failure")
|
||||||
|
cli.logger.Error("error sending message", "err", err)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -33,6 +33,7 @@ func simulate(cCtx *cli.Context) error {
|
||||||
// Start messengers
|
// Start messengers
|
||||||
apiModules := cCtx.String(APIModulesFlag)
|
apiModules := cCtx.String(APIModulesFlag)
|
||||||
telemetryUrl := cCtx.String(TelemetryServerURLFlag)
|
telemetryUrl := cCtx.String(TelemetryServerURLFlag)
|
||||||
|
failMessages := cCtx.Bool(MessageFailureFlag)
|
||||||
|
|
||||||
alice, err := start("Alice", 0, apiModules, telemetryUrl, "", logger.Named("alice"))
|
alice, err := start("Alice", 0, apiModules, telemetryUrl, "", logger.Named("alice"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -76,13 +77,13 @@ func simulate(cCtx *cli.Context) error {
|
||||||
interactiveSendMessageLoop(ctx, alice, charlie)
|
interactiveSendMessageLoop(ctx, alice, charlie)
|
||||||
} else {
|
} else {
|
||||||
for i := 0; i < cCtx.Int(CountFlag); i++ {
|
for i := 0; i < cCtx.Int(CountFlag); i++ {
|
||||||
err = alice.sendDirectMessage(ctx, fmt.Sprintf("message from alice, number: %d", i+1))
|
err = alice.sendDirectMessage(ctx, fmt.Sprintf("message from alice, number: %d", i+1), failMessages)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
time.Sleep(WaitingInterval)
|
time.Sleep(WaitingInterval)
|
||||||
|
|
||||||
err = charlie.sendDirectMessage(ctx, fmt.Sprintf("message from charlie, number: %d", i+1))
|
err = charlie.sendDirectMessage(ctx, fmt.Sprintf("message from charlie, number: %d", i+1), failMessages)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -208,6 +208,7 @@ type Settings struct {
|
||||||
GifFavorites *json.RawMessage `json:"gifs/favorite-gifs"`
|
GifFavorites *json.RawMessage `json:"gifs/favorite-gifs"`
|
||||||
OpenseaEnabled bool `json:"opensea-enabled?,omitempty"`
|
OpenseaEnabled bool `json:"opensea-enabled?,omitempty"`
|
||||||
TelemetryServerURL string `json:"telemetry-server-url,omitempty"`
|
TelemetryServerURL string `json:"telemetry-server-url,omitempty"`
|
||||||
|
TelemetrySendPeriodMs int `json:"telemetry-send-period-ms,omitempty"`
|
||||||
LastBackup uint64 `json:"last-backup,omitempty"`
|
LastBackup uint64 `json:"last-backup,omitempty"`
|
||||||
BackupEnabled bool `json:"backup-enabled?,omitempty"`
|
BackupEnabled bool `json:"backup-enabled?,omitempty"`
|
||||||
AutoMessageEnabled bool `json:"auto-message-enabled?,omitempty"`
|
AutoMessageEnabled bool `json:"auto-message-enabled?,omitempty"`
|
||||||
|
|
|
@ -112,8 +112,9 @@ type config struct {
|
||||||
|
|
||||||
messengerSignalsHandler MessengerSignalsHandler
|
messengerSignalsHandler MessengerSignalsHandler
|
||||||
|
|
||||||
telemetryServerURL string
|
telemetryServerURL string
|
||||||
wakuService *wakuv2.Waku
|
telemetrySendPeriod time.Duration
|
||||||
|
wakuService *wakuv2.Waku
|
||||||
|
|
||||||
messageResendMinDelay time.Duration
|
messageResendMinDelay time.Duration
|
||||||
messageResendMaxCount int
|
messageResendMaxCount int
|
||||||
|
@ -258,9 +259,10 @@ func WithAnonMetricsServerConfig(anonMetricsServerConfig *anonmetrics.ServerConf
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func WithTelemetry(serverURL string) Option {
|
func WithTelemetry(serverURL string, sendPeriod time.Duration) Option {
|
||||||
return func(c *config) error {
|
return func(c *config) error {
|
||||||
c.telemetryServerURL = serverURL
|
c.telemetryServerURL = serverURL
|
||||||
|
c.telemetrySendPeriod = sendPeriod
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -464,7 +464,7 @@ func buildMessengerOptions(
|
||||||
}
|
}
|
||||||
|
|
||||||
if settings.TelemetryServerURL != "" {
|
if settings.TelemetryServerURL != "" {
|
||||||
options = append(options, protocol.WithTelemetry(settings.TelemetryServerURL))
|
options = append(options, protocol.WithTelemetry(settings.TelemetryServerURL, time.Duration(settings.TelemetrySendPeriodMs)*time.Millisecond))
|
||||||
}
|
}
|
||||||
|
|
||||||
if settings.PushNotificationsServerEnabled {
|
if settings.PushNotificationsServerEnabled {
|
||||||
|
|
|
@ -6,6 +6,7 @@ import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
|
@ -22,11 +23,12 @@ import (
|
||||||
type TelemetryType string
|
type TelemetryType string
|
||||||
|
|
||||||
const (
|
const (
|
||||||
ProtocolStatsMetric TelemetryType = "ProtocolStats"
|
ProtocolStatsMetric TelemetryType = "ProtocolStats"
|
||||||
ReceivedEnvelopeMetric TelemetryType = "ReceivedEnvelope"
|
ReceivedEnvelopeMetric TelemetryType = "ReceivedEnvelope"
|
||||||
SentEnvelopeMetric TelemetryType = "SentEnvelope"
|
SentEnvelopeMetric TelemetryType = "SentEnvelope"
|
||||||
UpdateEnvelopeMetric TelemetryType = "UpdateEnvelope"
|
UpdateEnvelopeMetric TelemetryType = "UpdateEnvelope"
|
||||||
ReceivedMessagesMetric TelemetryType = "ReceivedMessages"
|
ReceivedMessagesMetric TelemetryType = "ReceivedMessages"
|
||||||
|
ErrorSendingEnvelopeMetric TelemetryType = "ErrorSendingEnvelope"
|
||||||
)
|
)
|
||||||
|
|
||||||
type TelemetryRequest struct {
|
type TelemetryRequest struct {
|
||||||
|
@ -36,15 +38,19 @@ type TelemetryRequest struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) PushReceivedMessages(receivedMessages ReceivedMessages) {
|
func (c *Client) PushReceivedMessages(receivedMessages ReceivedMessages) {
|
||||||
c.receivedMessagesCh <- receivedMessages
|
c.processAndPushTelemetry(receivedMessages)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) PushSentEnvelope(sentEnvelope wakuv2.SentEnvelope) {
|
func (c *Client) PushSentEnvelope(sentEnvelope wakuv2.SentEnvelope) {
|
||||||
c.sentEnvelopeCh <- sentEnvelope
|
c.processAndPushTelemetry(sentEnvelope)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) PushReceivedEnvelope(receivedEnvelope *v2protocol.Envelope) {
|
func (c *Client) PushReceivedEnvelope(receivedEnvelope *v2protocol.Envelope) {
|
||||||
c.receivedEnvelopeCh <- receivedEnvelope
|
c.processAndPushTelemetry(receivedEnvelope)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) PushErrorSendingEnvelope(errorSendingEnvelope wakuv2.ErrorSendingEnvelope) {
|
||||||
|
c.processAndPushTelemetry(errorSendingEnvelope)
|
||||||
}
|
}
|
||||||
|
|
||||||
type ReceivedMessages struct {
|
type ReceivedMessages struct {
|
||||||
|
@ -60,50 +66,59 @@ type Client struct {
|
||||||
keyUID string
|
keyUID string
|
||||||
nodeName string
|
nodeName string
|
||||||
version string
|
version string
|
||||||
receivedMessagesCh chan ReceivedMessages
|
|
||||||
receivedEnvelopeCh chan *v2protocol.Envelope
|
|
||||||
sentEnvelopeCh chan wakuv2.SentEnvelope
|
|
||||||
telemetryCh chan TelemetryRequest
|
telemetryCh chan TelemetryRequest
|
||||||
|
telemetryCacheLock sync.Mutex
|
||||||
|
telemetryCache []TelemetryRequest
|
||||||
|
nextIdLock sync.Mutex
|
||||||
nextId int
|
nextId int
|
||||||
sendPeriod time.Duration
|
sendPeriod time.Duration
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewClient(logger *zap.Logger, serverURL string, keyUID string, nodeName string, version string) *Client {
|
type TelemetryClientOption func(*Client)
|
||||||
return &Client{
|
|
||||||
|
func WithSendPeriod(sendPeriod time.Duration) TelemetryClientOption {
|
||||||
|
return func(c *Client) {
|
||||||
|
c.sendPeriod = sendPeriod
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewClient(logger *zap.Logger, serverURL string, keyUID string, nodeName string, version string, opts ...TelemetryClientOption) *Client {
|
||||||
|
client := &Client{
|
||||||
serverURL: serverURL,
|
serverURL: serverURL,
|
||||||
httpClient: &http.Client{Timeout: time.Minute},
|
httpClient: &http.Client{Timeout: time.Minute},
|
||||||
logger: logger,
|
logger: logger,
|
||||||
keyUID: keyUID,
|
keyUID: keyUID,
|
||||||
nodeName: nodeName,
|
nodeName: nodeName,
|
||||||
version: version,
|
version: version,
|
||||||
receivedMessagesCh: make(chan ReceivedMessages),
|
|
||||||
receivedEnvelopeCh: make(chan *v2protocol.Envelope),
|
|
||||||
sentEnvelopeCh: make(chan wakuv2.SentEnvelope),
|
|
||||||
telemetryCh: make(chan TelemetryRequest),
|
telemetryCh: make(chan TelemetryRequest),
|
||||||
|
telemetryCacheLock: sync.Mutex{},
|
||||||
|
telemetryCache: make([]TelemetryRequest, 0),
|
||||||
nextId: 0,
|
nextId: 0,
|
||||||
sendPeriod: 10 * time.Second,
|
nextIdLock: sync.Mutex{},
|
||||||
|
sendPeriod: 10 * time.Second, // default value
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for _, opt := range opts {
|
||||||
|
opt(client)
|
||||||
|
}
|
||||||
|
|
||||||
|
return client
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) CollectAndProcessTelemetry(ctx context.Context) {
|
func (c *Client) Start(ctx context.Context) {
|
||||||
go func() {
|
go func() {
|
||||||
|
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case receivedMessages := <-c.receivedMessagesCh:
|
case telemetryRequest := <-c.telemetryCh:
|
||||||
c.processAndPushTelemetry(receivedMessages)
|
c.telemetryCacheLock.Lock()
|
||||||
case receivedEnvelope := <-c.receivedEnvelopeCh:
|
c.telemetryCache = append(c.telemetryCache, telemetryRequest)
|
||||||
c.processAndPushTelemetry(receivedEnvelope)
|
c.telemetryCacheLock.Unlock()
|
||||||
case sentEnvelope := <-c.sentEnvelopeCh:
|
|
||||||
c.processAndPushTelemetry(sentEnvelope)
|
|
||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Client) Start(ctx context.Context) {
|
|
||||||
go c.CollectAndProcessTelemetry(ctx)
|
|
||||||
go func() {
|
go func() {
|
||||||
ticker := time.NewTicker(c.sendPeriod)
|
ticker := time.NewTicker(c.sendPeriod)
|
||||||
defer ticker.Stop()
|
defer ticker.Stop()
|
||||||
|
@ -111,16 +126,12 @@ func (c *Client) Start(ctx context.Context) {
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case <-ticker.C:
|
case <-ticker.C:
|
||||||
var telemetryRequests []TelemetryRequest
|
c.telemetryCacheLock.Lock()
|
||||||
collecting := true
|
telemetryRequests := make([]TelemetryRequest, len(c.telemetryCache))
|
||||||
for collecting {
|
copy(telemetryRequests, c.telemetryCache)
|
||||||
select {
|
c.telemetryCache = nil
|
||||||
case telemetryRequest := <-c.telemetryCh:
|
c.telemetryCacheLock.Unlock()
|
||||||
telemetryRequests = append(telemetryRequests, telemetryRequest)
|
|
||||||
default:
|
|
||||||
collecting = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if len(telemetryRequests) > 0 {
|
if len(telemetryRequests) > 0 {
|
||||||
c.pushTelemetryRequest(telemetryRequests)
|
c.pushTelemetryRequest(telemetryRequests)
|
||||||
}
|
}
|
||||||
|
@ -128,6 +139,7 @@ func (c *Client) Start(ctx context.Context) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -152,13 +164,21 @@ func (c *Client) processAndPushTelemetry(data interface{}) {
|
||||||
TelemetryType: SentEnvelopeMetric,
|
TelemetryType: SentEnvelopeMetric,
|
||||||
TelemetryData: c.ProcessSentEnvelope(v),
|
TelemetryData: c.ProcessSentEnvelope(v),
|
||||||
}
|
}
|
||||||
|
case wakuv2.ErrorSendingEnvelope:
|
||||||
|
telemetryRequest = TelemetryRequest{
|
||||||
|
Id: c.nextId,
|
||||||
|
TelemetryType: ErrorSendingEnvelopeMetric,
|
||||||
|
TelemetryData: c.ProcessErrorSendingEnvelope(v),
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
c.logger.Error("Unknown telemetry data type")
|
c.logger.Error("Unknown telemetry data type")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
c.nextId++
|
|
||||||
c.telemetryCh <- telemetryRequest
|
c.telemetryCh <- telemetryRequest
|
||||||
|
c.nextIdLock.Lock()
|
||||||
|
c.nextId++
|
||||||
|
c.nextIdLock.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) pushTelemetryRequest(request []TelemetryRequest) {
|
func (c *Client) pushTelemetryRequest(request []TelemetryRequest) {
|
||||||
|
@ -224,6 +244,23 @@ func (c *Client) ProcessSentEnvelope(sentEnvelope wakuv2.SentEnvelope) *json.Raw
|
||||||
return &jsonRawMessage
|
return &jsonRawMessage
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Client) ProcessErrorSendingEnvelope(errorSendingEnvelope wakuv2.ErrorSendingEnvelope) *json.RawMessage {
|
||||||
|
postBody := map[string]interface{}{
|
||||||
|
"messageHash": errorSendingEnvelope.SentEnvelope.Envelope.Hash().String(),
|
||||||
|
"sentAt": uint32(errorSendingEnvelope.SentEnvelope.Envelope.Message().GetTimestamp() / int64(time.Second)),
|
||||||
|
"pubsubTopic": errorSendingEnvelope.SentEnvelope.Envelope.PubsubTopic(),
|
||||||
|
"topic": errorSendingEnvelope.SentEnvelope.Envelope.Message().ContentTopic,
|
||||||
|
"senderKeyUID": c.keyUID,
|
||||||
|
"nodeName": c.nodeName,
|
||||||
|
"publishMethod": errorSendingEnvelope.SentEnvelope.PublishMethod.String(),
|
||||||
|
"statusVersion": c.version,
|
||||||
|
"error": errorSendingEnvelope.Error.Error(),
|
||||||
|
}
|
||||||
|
body, _ := json.Marshal(postBody)
|
||||||
|
jsonRawMessage := json.RawMessage(body)
|
||||||
|
return &jsonRawMessage
|
||||||
|
}
|
||||||
|
|
||||||
func (c *Client) UpdateEnvelopeProcessingError(shhMessage *types.Message, processingError error) {
|
func (c *Client) UpdateEnvelopeProcessingError(shhMessage *types.Message, processingError error) {
|
||||||
c.logger.Debug("Pushing envelope update to telemetry server", zap.String("hash", types.EncodeHex(shhMessage.Hash)))
|
c.logger.Debug("Pushing envelope update to telemetry server", zap.String("hash", types.EncodeHex(shhMessage.Hash)))
|
||||||
url := fmt.Sprintf("%s/update-envelope", c.serverURL)
|
url := fmt.Sprintf("%s/update-envelope", c.serverURL)
|
||||||
|
|
|
@ -1,9 +1,12 @@
|
||||||
package telemetry
|
package telemetry
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
|
"os"
|
||||||
|
"sync"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
@ -13,14 +16,22 @@ import (
|
||||||
v2protocol "github.com/waku-org/go-waku/waku/v2/protocol"
|
v2protocol "github.com/waku-org/go-waku/waku/v2/protocol"
|
||||||
"github.com/waku-org/go-waku/waku/v2/protocol/pb"
|
"github.com/waku-org/go-waku/waku/v2/protocol/pb"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
"github.com/status-im/status-go/eth-node/types"
|
"github.com/status-im/status-go/eth-node/types"
|
||||||
"github.com/status-im/status-go/protocol/transport"
|
"github.com/status-im/status-go/protocol/transport"
|
||||||
v1protocol "github.com/status-im/status-go/protocol/v1"
|
v1protocol "github.com/status-im/status-go/protocol/v1"
|
||||||
"github.com/status-im/status-go/wakuv2"
|
"github.com/status-im/status-go/wakuv2"
|
||||||
)
|
)
|
||||||
|
|
||||||
func createMockServer(t *testing.T) *httptest.Server {
|
var (
|
||||||
|
testContentTopic = "/waku/1/0x12345679/rfc26"
|
||||||
|
)
|
||||||
|
|
||||||
|
func createMockServer(t *testing.T, wg *sync.WaitGroup, expectedType TelemetryType) *httptest.Server {
|
||||||
return httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
return httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
defer wg.Done() // Signal that a request was received
|
||||||
|
|
||||||
if r.Method != "POST" {
|
if r.Method != "POST" {
|
||||||
t.Errorf("Expected 'POST' request, got '%s'", r.Method)
|
t.Errorf("Expected 'POST' request, got '%s'", r.Method)
|
||||||
}
|
}
|
||||||
|
@ -38,121 +49,166 @@ func createMockServer(t *testing.T) *httptest.Server {
|
||||||
if len(received) != 1 {
|
if len(received) != 1 {
|
||||||
t.Errorf("Unexpected data received: %+v", received)
|
t.Errorf("Unexpected data received: %+v", received)
|
||||||
} else {
|
} else {
|
||||||
// If the data is as expected, respond with success
|
if received[0].TelemetryType != expectedType {
|
||||||
t.Log("Responding with success")
|
t.Errorf("Unexpected telemetry type: got %v, want %v", received[0].TelemetryType, expectedType)
|
||||||
w.WriteHeader(http.StatusOK)
|
} else {
|
||||||
|
// If the data is as expected, respond with success
|
||||||
|
t.Log("Responding with success")
|
||||||
|
w.WriteHeader(http.StatusOK)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestClient_ProcessReceivedMessages(t *testing.T) {
|
func createClient(t *testing.T, mockServerURL string) *Client {
|
||||||
// Setup a mock server to handle post requests
|
|
||||||
mockServer := createMockServer(t)
|
|
||||||
defer mockServer.Close()
|
|
||||||
|
|
||||||
// Create a client with the mock server URL
|
|
||||||
config := zap.NewDevelopmentConfig()
|
config := zap.NewDevelopmentConfig()
|
||||||
config.Level = zap.NewAtomicLevelAt(zap.DebugLevel)
|
config.Level = zap.NewAtomicLevelAt(zap.DebugLevel)
|
||||||
logger, err := config.Build()
|
logger, err := config.Build()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed to create logger: %v", err)
|
t.Fatalf("Failed to create logger: %v", err)
|
||||||
}
|
}
|
||||||
client := NewClient(logger, mockServer.URL, "testUID", "testNode", "1.0")
|
return NewClient(logger, mockServerURL, "testUID", "testNode", "1.0", WithSendPeriod(500*time.Millisecond))
|
||||||
|
}
|
||||||
|
|
||||||
// Create a telemetry request to send
|
func withMockServer(t *testing.T, expectedType TelemetryType, testFunc func(t *testing.T, client *Client, wg *sync.WaitGroup)) {
|
||||||
data := ReceivedMessages{
|
var wg sync.WaitGroup
|
||||||
Filter: transport.Filter{
|
wg.Add(1) // Expecting one request
|
||||||
ChatID: "testChat",
|
|
||||||
PubsubTopic: "testTopic",
|
mockServer := createMockServer(t, &wg, expectedType)
|
||||||
ContentTopic: types.StringToTopic("testContentTopic"),
|
defer mockServer.Close()
|
||||||
},
|
|
||||||
SSHMessage: &types.Message{
|
client := createClient(t, mockServer.URL)
|
||||||
Hash: []byte("hash"),
|
|
||||||
Timestamp: uint32(time.Now().Unix()),
|
testFunc(t, client, &wg)
|
||||||
},
|
|
||||||
Messages: []*v1protocol.StatusMessage{
|
// Wait for the request to be received
|
||||||
{
|
wg.Wait()
|
||||||
ApplicationLayer: v1protocol.ApplicationLayer{
|
}
|
||||||
ID: types.HexBytes("123"),
|
|
||||||
Type: 1,
|
func TestClient_ProcessReceivedMessages(t *testing.T) {
|
||||||
|
withMockServer(t, ReceivedMessagesMetric, func(t *testing.T, client *Client, wg *sync.WaitGroup) {
|
||||||
|
// Create a telemetry request to send
|
||||||
|
data := ReceivedMessages{
|
||||||
|
Filter: transport.Filter{
|
||||||
|
ChatID: "testChat",
|
||||||
|
PubsubTopic: "testTopic",
|
||||||
|
ContentTopic: types.StringToTopic(testContentTopic),
|
||||||
|
},
|
||||||
|
SSHMessage: &types.Message{
|
||||||
|
Hash: []byte("hash"),
|
||||||
|
Timestamp: uint32(time.Now().Unix()),
|
||||||
|
},
|
||||||
|
Messages: []*v1protocol.StatusMessage{
|
||||||
|
{
|
||||||
|
ApplicationLayer: v1protocol.ApplicationLayer{
|
||||||
|
ID: types.HexBytes("123"),
|
||||||
|
Type: 1,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
}
|
||||||
}
|
telemetryData := client.ProcessReceivedMessages(data)
|
||||||
telemetryData := client.ProcessReceivedMessages(data)
|
telemetryRequest := TelemetryRequest{
|
||||||
telemetryRequest := TelemetryRequest{
|
Id: 1,
|
||||||
Id: 1,
|
TelemetryType: ReceivedMessagesMetric,
|
||||||
TelemetryType: ReceivedMessagesMetric,
|
TelemetryData: telemetryData,
|
||||||
TelemetryData: telemetryData,
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Send the telemetry request
|
// Send the telemetry request
|
||||||
client.pushTelemetryRequest([]TelemetryRequest{telemetryRequest})
|
client.pushTelemetryRequest([]TelemetryRequest{telemetryRequest})
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestClient_ProcessReceivedEnvelope(t *testing.T) {
|
func TestClient_ProcessReceivedEnvelope(t *testing.T) {
|
||||||
// Setup a mock server to handle post requests
|
withMockServer(t, ReceivedEnvelopeMetric, func(t *testing.T, client *Client, wg *sync.WaitGroup) {
|
||||||
mockServer := createMockServer(t)
|
// Create a telemetry request to send
|
||||||
defer mockServer.Close()
|
envelope := v2protocol.NewEnvelope(&pb.WakuMessage{
|
||||||
|
Payload: []byte{1, 2, 3, 4, 5},
|
||||||
|
ContentTopic: testContentTopic,
|
||||||
|
Version: proto.Uint32(0),
|
||||||
|
Timestamp: proto.Int64(time.Now().Unix()),
|
||||||
|
}, 0, "")
|
||||||
|
telemetryData := client.ProcessReceivedEnvelope(envelope)
|
||||||
|
telemetryRequest := TelemetryRequest{
|
||||||
|
Id: 2,
|
||||||
|
TelemetryType: ReceivedEnvelopeMetric,
|
||||||
|
TelemetryData: telemetryData,
|
||||||
|
}
|
||||||
|
|
||||||
// Create a client with the mock server URL
|
// Send the telemetry request
|
||||||
config := zap.NewDevelopmentConfig()
|
client.pushTelemetryRequest([]TelemetryRequest{telemetryRequest})
|
||||||
config.Level = zap.NewAtomicLevelAt(zap.DebugLevel)
|
})
|
||||||
logger, err := config.Build()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Failed to create logger: %v", err)
|
|
||||||
}
|
|
||||||
client := NewClient(logger, mockServer.URL, "testUID", "testNode", "1.0")
|
|
||||||
|
|
||||||
// Create a telemetry request to send
|
|
||||||
envelope := v2protocol.NewEnvelope(&pb.WakuMessage{
|
|
||||||
Payload: []byte{1, 2, 3, 4, 5},
|
|
||||||
ContentTopic: "testContentTopic",
|
|
||||||
Version: proto.Uint32(0),
|
|
||||||
Timestamp: proto.Int64(time.Now().Unix()),
|
|
||||||
}, 0, "")
|
|
||||||
telemetryData := client.ProcessReceivedEnvelope(envelope)
|
|
||||||
telemetryRequest := TelemetryRequest{
|
|
||||||
Id: 2,
|
|
||||||
TelemetryType: ReceivedEnvelopeMetric,
|
|
||||||
TelemetryData: telemetryData,
|
|
||||||
}
|
|
||||||
|
|
||||||
// Send the telemetry request
|
|
||||||
client.pushTelemetryRequest([]TelemetryRequest{telemetryRequest})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestClient_ProcessSentEnvelope(t *testing.T) {
|
func TestClient_ProcessSentEnvelope(t *testing.T) {
|
||||||
// Setup a mock server to handle post requests
|
withMockServer(t, SentEnvelopeMetric, func(t *testing.T, client *Client, wg *sync.WaitGroup) {
|
||||||
mockServer := createMockServer(t)
|
// Create a telemetry request to send
|
||||||
defer mockServer.Close()
|
sentEnvelope := wakuv2.SentEnvelope{
|
||||||
|
Envelope: v2protocol.NewEnvelope(&pb.WakuMessage{
|
||||||
|
Payload: []byte{1, 2, 3, 4, 5},
|
||||||
|
ContentTopic: testContentTopic,
|
||||||
|
Version: proto.Uint32(0),
|
||||||
|
Timestamp: proto.Int64(time.Now().Unix()),
|
||||||
|
}, 0, ""),
|
||||||
|
PublishMethod: wakuv2.LightPush,
|
||||||
|
}
|
||||||
|
telemetryData := client.ProcessSentEnvelope(sentEnvelope)
|
||||||
|
telemetryRequest := TelemetryRequest{
|
||||||
|
Id: 3,
|
||||||
|
TelemetryType: SentEnvelopeMetric,
|
||||||
|
TelemetryData: telemetryData,
|
||||||
|
}
|
||||||
|
|
||||||
// Create a client with the mock server URL
|
// Send the telemetry request
|
||||||
config := zap.NewDevelopmentConfig()
|
client.pushTelemetryRequest([]TelemetryRequest{telemetryRequest})
|
||||||
config.Level = zap.NewAtomicLevelAt(zap.DebugLevel)
|
})
|
||||||
logger, err := config.Build()
|
}
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Failed to create logger: %v", err)
|
|
||||||
}
|
|
||||||
client := NewClient(logger, mockServer.URL, "testUID", "testNode", "1.0")
|
|
||||||
|
|
||||||
// Create a telemetry request to send
|
var (
|
||||||
sentEnvelope := wakuv2.SentEnvelope{
|
testENRBootstrap = "enrtree://AI4W5N5IFEUIHF5LESUAOSMV6TKWF2MB6GU2YK7PU4TYUGUNOCEPW@store.staging.shards.nodes.status.im"
|
||||||
Envelope: v2protocol.NewEnvelope(&pb.WakuMessage{
|
)
|
||||||
|
|
||||||
|
func TestTelemetryUponPublishError(t *testing.T) {
|
||||||
|
withMockServer(t, ErrorSendingEnvelopeMetric, func(t *testing.T, client *Client, wg *sync.WaitGroup) {
|
||||||
|
enrTreeAddress := testENRBootstrap
|
||||||
|
envEnrTreeAddress := os.Getenv("ENRTREE_ADDRESS")
|
||||||
|
if envEnrTreeAddress != "" {
|
||||||
|
enrTreeAddress = envEnrTreeAddress
|
||||||
|
}
|
||||||
|
|
||||||
|
wakuConfig := &wakuv2.Config{}
|
||||||
|
wakuConfig.Port = 0
|
||||||
|
wakuConfig.EnablePeerExchangeClient = true
|
||||||
|
wakuConfig.LightClient = true
|
||||||
|
wakuConfig.EnableDiscV5 = false
|
||||||
|
wakuConfig.DiscV5BootstrapNodes = []string{enrTreeAddress}
|
||||||
|
wakuConfig.DiscoveryLimit = 20
|
||||||
|
wakuConfig.UseShardAsDefaultTopic = true
|
||||||
|
wakuConfig.ClusterID = 16
|
||||||
|
wakuConfig.WakuNodes = []string{enrTreeAddress}
|
||||||
|
wakuConfig.TelemetryServerURL = client.serverURL
|
||||||
|
wakuConfig.TelemetrySendPeriodMs = 500
|
||||||
|
w, err := wakuv2.New(nil, "", wakuConfig, nil, nil, nil, nil, nil)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
client.Start(context.Background())
|
||||||
|
w.SetStatusTelemetryClient(client)
|
||||||
|
|
||||||
|
// Setting this forces the publish function to fail when sending a message
|
||||||
|
w.SkipPublishToTopic(true)
|
||||||
|
|
||||||
|
err = w.Start()
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
msg := &pb.WakuMessage{
|
||||||
Payload: []byte{1, 2, 3, 4, 5},
|
Payload: []byte{1, 2, 3, 4, 5},
|
||||||
ContentTopic: "testContentTopic",
|
ContentTopic: testContentTopic,
|
||||||
Version: proto.Uint32(0),
|
Version: proto.Uint32(0),
|
||||||
Timestamp: proto.Int64(time.Now().Unix()),
|
Timestamp: proto.Int64(time.Now().Unix()),
|
||||||
}, 0, ""),
|
}
|
||||||
PublishMethod: wakuv2.LightPush,
|
|
||||||
}
|
|
||||||
telemetryData := client.ProcessSentEnvelope(sentEnvelope)
|
|
||||||
telemetryRequest := TelemetryRequest{
|
|
||||||
Id: 3,
|
|
||||||
TelemetryType: SentEnvelopeMetric,
|
|
||||||
TelemetryData: telemetryData,
|
|
||||||
}
|
|
||||||
|
|
||||||
// Send the telemetry request
|
// This should result in a single request sent by the telemetry client
|
||||||
client.pushTelemetryRequest([]TelemetryRequest{telemetryRequest})
|
_, err = w.Send(wakuConfig.DefaultShardPubsubTopic, msg)
|
||||||
|
require.NoError(t, err)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -61,6 +61,7 @@ type Config struct {
|
||||||
StoreCapacity int `toml:",omitempty"`
|
StoreCapacity int `toml:",omitempty"`
|
||||||
StoreSeconds int `toml:",omitempty"`
|
StoreSeconds int `toml:",omitempty"`
|
||||||
TelemetryServerURL string `toml:",omitempty"`
|
TelemetryServerURL string `toml:",omitempty"`
|
||||||
|
TelemetrySendPeriodMs int `toml:",omitempty"` // Number of milliseconds to wait between sending requests to telemetry service
|
||||||
DefaultShardPubsubTopic string `toml:",omitempty"` // Pubsub topic to be used by default for messages that do not have a topic assigned (depending whether sharding is used or not)
|
DefaultShardPubsubTopic string `toml:",omitempty"` // Pubsub topic to be used by default for messages that do not have a topic assigned (depending whether sharding is used or not)
|
||||||
DefaultShardedPubsubTopics []string `toml:", omitempty"`
|
DefaultShardedPubsubTopics []string `toml:", omitempty"`
|
||||||
UseShardAsDefaultTopic bool `toml:",omitempty"`
|
UseShardAsDefaultTopic bool `toml:",omitempty"`
|
||||||
|
|
|
@ -95,9 +95,15 @@ type SentEnvelope struct {
|
||||||
PublishMethod PublishMethod
|
PublishMethod PublishMethod
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ErrorSendingEnvelope struct {
|
||||||
|
Error error
|
||||||
|
SentEnvelope SentEnvelope
|
||||||
|
}
|
||||||
|
|
||||||
type ITelemetryClient interface {
|
type ITelemetryClient interface {
|
||||||
PushReceivedEnvelope(receivedEnvelope *v2protocol.Envelope)
|
PushReceivedEnvelope(receivedEnvelope *v2protocol.Envelope)
|
||||||
PushSentEnvelope(sentEnvelope SentEnvelope)
|
PushSentEnvelope(sentEnvelope SentEnvelope)
|
||||||
|
PushErrorSendingEnvelope(errorSendingEnvelope ErrorSendingEnvelope)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Waku represents a dark communication interface through the Ethereum
|
// Waku represents a dark communication interface through the Ethereum
|
||||||
|
@ -993,6 +999,7 @@ func (w *Waku) broadcast() {
|
||||||
var publishMethod PublishMethod
|
var publishMethod PublishMethod
|
||||||
if w.cfg.SkipPublishToTopic {
|
if w.cfg.SkipPublishToTopic {
|
||||||
// For now only used in testing to simulate going offline
|
// For now only used in testing to simulate going offline
|
||||||
|
publishMethod = LightPush
|
||||||
fn = func(env *protocol.Envelope, logger *zap.Logger) error {
|
fn = func(env *protocol.Envelope, logger *zap.Logger) error {
|
||||||
return errors.New("test send failure")
|
return errors.New("test send failure")
|
||||||
}
|
}
|
||||||
|
@ -1020,11 +1027,9 @@ func (w *Waku) broadcast() {
|
||||||
err := sendFn(env, logger)
|
err := sendFn(env, logger)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
w.statusTelemetryClient.PushSentEnvelope(SentEnvelope{Envelope: env, PublishMethod: publishMethod})
|
w.statusTelemetryClient.PushSentEnvelope(SentEnvelope{Envelope: env, PublishMethod: publishMethod})
|
||||||
|
} else {
|
||||||
|
w.statusTelemetryClient.PushErrorSendingEnvelope(ErrorSendingEnvelope{Error: err, SentEnvelope: SentEnvelope{Envelope: env, PublishMethod: publishMethod}})
|
||||||
}
|
}
|
||||||
// else {
|
|
||||||
// TODO: send error from Relay or LightPush to Telemetry
|
|
||||||
// w.statusTelemetryClient.PushError(err)
|
|
||||||
// }
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue