FIX: Redis to handle event metadata. (#3320)

Signed-off-by: Artur Souza <asouza.pro@gmail.com>
Signed-off-by: Bernd Verst <github@bernd.dev>
Co-authored-by: Bernd Verst <github@bernd.dev>
This commit is contained in:
Artur Souza 2024-01-25 12:25:24 -08:00 committed by GitHub
parent 120a649caa
commit 0c687df973
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 78 additions and 16 deletions

View File

@ -15,6 +15,7 @@ package redis
import ( import (
"context" "context"
"encoding/json"
"errors" "errors"
"fmt" "fmt"
"reflect" "reflect"
@ -101,7 +102,17 @@ func (r *redisStreams) Publish(ctx context.Context, req *pubsub.PublishRequest)
return errors.New("component is closed") return errors.New("component is closed")
} }
_, err := r.client.XAdd(ctx, req.Topic, r.clientSettings.MaxLenApprox, map[string]interface{}{"data": req.Data}) redisPayload := map[string]interface{}{"data": req.Data}
if req.Metadata != nil {
serializedMetadata, err := json.Marshal(req.Metadata)
if err != nil {
return err
}
redisPayload["metadata"] = serializedMetadata
}
_, err := r.client.XAdd(ctx, req.Topic, r.clientSettings.MaxLenApprox, redisPayload)
if err != nil { if err != nil {
return fmt.Errorf("redis streams: error from publish: %s", err) return fmt.Errorf("redis streams: error from publish: %s", err)
} }
@ -157,7 +168,7 @@ func (r *redisStreams) Subscribe(ctx context.Context, req pubsub.SubscribeReques
// pick them up for processing. // pick them up for processing.
func (r *redisStreams) enqueueMessages(ctx context.Context, stream string, handler pubsub.Handler, msgs []rediscomponent.RedisXMessage) { func (r *redisStreams) enqueueMessages(ctx context.Context, stream string, handler pubsub.Handler, msgs []rediscomponent.RedisXMessage) {
for _, msg := range msgs { for _, msg := range msgs {
rmsg := createRedisMessageWrapper(ctx, stream, handler, msg) rmsg := r.createRedisMessageWrapper(ctx, stream, handler, msg)
select { select {
// Might block if the queue is full so we need the ctx.Done below. // Might block if the queue is full so we need the ctx.Done below.
@ -172,7 +183,7 @@ func (r *redisStreams) enqueueMessages(ctx context.Context, stream string, handl
// createRedisMessageWrapper encapsulates the Redis message, message identifier, and handler // createRedisMessageWrapper encapsulates the Redis message, message identifier, and handler
// in `redisMessage` for processing. // in `redisMessage` for processing.
func createRedisMessageWrapper(ctx context.Context, stream string, handler pubsub.Handler, msg rediscomponent.RedisXMessage) redisMessageWrapper { func (r *redisStreams) createRedisMessageWrapper(ctx context.Context, stream string, handler pubsub.Handler, msg rediscomponent.RedisXMessage) redisMessageWrapper {
var data []byte var data []byte
if dataValue, exists := msg.Values["data"]; exists && dataValue != nil { if dataValue, exists := msg.Values["data"]; exists && dataValue != nil {
switch v := dataValue.(type) { switch v := dataValue.(type) {
@ -183,11 +194,21 @@ func createRedisMessageWrapper(ctx context.Context, stream string, handler pubsu
} }
} }
var metadata map[string]string
if metadataValue, exists := msg.Values["metadata"]; exists && metadataValue != nil {
metadataStr := metadataValue.(string)
err := json.Unmarshal([]byte(metadataStr), &metadata)
if err != nil {
r.logger.Warnf("Redis PubSub: Could not extract metadata for Redis message %s: %v", msg.ID, err)
}
}
return redisMessageWrapper{ return redisMessageWrapper{
ctx: ctx, ctx: ctx,
message: pubsub.NewMessage{ message: pubsub.NewMessage{
Topic: stream, Topic: stream,
Data: data, Data: data,
Metadata: metadata,
}, },
messageID: msg.ID, messageID: msg.ID,
handler: handler, handler: handler,

View File

@ -76,9 +76,9 @@ func TestParseRedisMetadata(t *testing.T) {
func TestProcessStreams(t *testing.T) { func TestProcessStreams(t *testing.T) {
fakeConsumerID := "fakeConsumer" fakeConsumerID := "fakeConsumer"
topicCount := 0
messageCount := 0 messageCount := 0
expectedData := "testData" expectedData := "testData"
expectedMetadata := "testMetadata"
var wg sync.WaitGroup var wg sync.WaitGroup
wg.Add(3) wg.Add(3)
@ -87,12 +87,10 @@ func TestProcessStreams(t *testing.T) {
defer wg.Done() defer wg.Done()
messageCount++ messageCount++
if topicCount == 0 {
topicCount = 1
}
// assert // assert
assert.Equal(t, expectedData, string(msg.Data)) assert.Equal(t, expectedData, string(msg.Data))
assert.Equal(t, expectedMetadata, msg.Metadata["mymetadata"])
// return fake error to skip executing redis client command // return fake error to skip executing redis client command
return errors.New("fake error") return errors.New("fake error")
@ -105,23 +103,66 @@ func TestProcessStreams(t *testing.T) {
} }
testRedisStream.queue = make(chan redisMessageWrapper, 10) testRedisStream.queue = make(chan redisMessageWrapper, 10)
go testRedisStream.worker() go testRedisStream.worker()
testRedisStream.enqueueMessages(context.Background(), fakeConsumerID, fakeHandler, generateRedisStreamTestData(2, 3, expectedData)) testRedisStream.enqueueMessages(context.Background(), fakeConsumerID, fakeHandler, generateRedisStreamTestData(3, expectedData, expectedMetadata))
// Wait for the handler to finish processing // Wait for the handler to finish processing
wg.Wait() wg.Wait()
// assert // assert
assert.Equal(t, 1, topicCount)
assert.Equal(t, 3, messageCount) assert.Equal(t, 3, messageCount)
} }
func generateRedisStreamTestData(topicCount, messageCount int, data string) []commonredis.RedisXMessage { func TestProcessStreamsWithoutEventMetadata(t *testing.T) {
fakeConsumerID := "fakeConsumer"
messageCount := 0
expectedData := "testData"
var wg sync.WaitGroup
wg.Add(3)
fakeHandler := func(ctx context.Context, msg *pubsub.NewMessage) error {
defer wg.Done()
messageCount++
// assert
assert.Equal(t, expectedData, string(msg.Data))
assert.Nil(t, msg.Metadata)
// return fake error to skip executing redis client command
return errors.New("fake error")
}
// act
testRedisStream := &redisStreams{
logger: logger.NewLogger("test"),
clientSettings: &commonredis.Settings{},
}
testRedisStream.queue = make(chan redisMessageWrapper, 10)
go testRedisStream.worker()
testRedisStream.enqueueMessages(context.Background(), fakeConsumerID, fakeHandler, generateRedisStreamTestData(3, expectedData, ""))
// Wait for the handler to finish processing
wg.Wait()
// assert
assert.Equal(t, 3, messageCount)
}
func generateRedisStreamTestData(messageCount int, data string, metadata string) []commonredis.RedisXMessage {
generateXMessage := func(id int) commonredis.RedisXMessage { generateXMessage := func(id int) commonredis.RedisXMessage {
values := map[string]interface{}{
"data": data,
}
if metadata != "" {
values["metadata"] = "{\"mymetadata\": \"" + metadata + "\"}"
}
return commonredis.RedisXMessage{ return commonredis.RedisXMessage{
ID: strconv.Itoa(id), ID: strconv.Itoa(id),
Values: map[string]interface{}{ Values: values,
"data": data,
},
} }
} }