-
Notifications
You must be signed in to change notification settings - Fork 337
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Support regex in topic protobuf mappings #584
Changes from all commits
c5cb3eb
0123cd3
c2d55aa
acc2278
94ef19b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -14,6 +14,7 @@ import ( | |
"context" | ||
"encoding/binary" | ||
"fmt" | ||
"regexp" | ||
"strings" | ||
"sync" | ||
"time" | ||
|
@@ -47,17 +48,23 @@ const ( | |
RecordValue | ||
) | ||
|
||
type regexProtoTopicMapping struct { | ||
config.ProtoTopicMapping | ||
r *regexp.Regexp | ||
} | ||
|
||
// Service is in charge of deserializing protobuf encoded payloads. It supports payloads that were | ||
// encoded with involvement of the schema registry as well as plain protobuf-encoded messages. | ||
// This service is also in charge of reading the proto source files from the configured provider. | ||
type Service struct { | ||
cfg config.Proto | ||
logger *zap.Logger | ||
|
||
mappingsByTopic map[string]config.ProtoTopicMapping | ||
gitSvc *git.Service | ||
fsSvc *filesystem.Service | ||
schemaSvc *schema.Service | ||
strictMappingsByTopic map[string]config.ProtoTopicMapping | ||
mappingsRegex []regexProtoTopicMapping | ||
gitSvc *git.Service | ||
fsSvc *filesystem.Service | ||
schemaSvc *schema.Service | ||
|
||
// fileDescriptorsBySchemaID are used to find the right schema type for messages at deserialization time. The type | ||
// index is encoded as part of the serialized message. | ||
|
@@ -112,25 +119,48 @@ func NewService(cfg config.Proto, logger *zap.Logger, schemaSvc *schema.Service) | |
} | ||
} | ||
|
||
mappingsByTopic := make(map[string]config.ProtoTopicMapping) | ||
for _, mapping := range cfg.Mappings { | ||
mappingsByTopic[mapping.TopicName] = mapping | ||
strictMappingsByTopic, mappingsRegex, err := setMappingsByTopic(cfg.Mappings, logger) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
return &Service{ | ||
cfg: cfg, | ||
logger: logger, | ||
|
||
mappingsByTopic: mappingsByTopic, | ||
gitSvc: gitSvc, | ||
fsSvc: fsSvc, | ||
schemaSvc: schemaSvc, | ||
strictMappingsByTopic: strictMappingsByTopic, | ||
mappingsRegex: mappingsRegex, | ||
gitSvc: gitSvc, | ||
fsSvc: fsSvc, | ||
schemaSvc: schemaSvc, | ||
|
||
// registry has to be created afterwards | ||
registry: nil, | ||
}, nil | ||
} | ||
|
||
func setMappingsByTopic(mappings []config.ProtoTopicMapping, logger *zap.Logger) (strictMappingsByTopic map[string]config.ProtoTopicMapping, mappingsRegex []regexProtoTopicMapping, err error) { | ||
strictMappingsByTopic = make(map[string]config.ProtoTopicMapping) | ||
mappingsRegex = make([]regexProtoTopicMapping, 0) | ||
|
||
for _, mapping := range mappings { | ||
r, err := regexp.Compile(mapping.TopicName) | ||
if err != nil { | ||
strictMappingsByTopic[mapping.TopicName] = mapping | ||
logger.Warn("topic name handled as a strict match", zap.String("topic_name", mapping.TopicName)) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So as of today where everyone use "strict matches" this would print one warning message for every single configured topic name. I think we need to change this. I agree that we want to be explicit about what's being used since the regexp compilation can fail. I suggested something in my comment to overcome this issue |
||
continue | ||
} | ||
|
||
mappingsRegex = append(mappingsRegex, regexProtoTopicMapping{ | ||
ProtoTopicMapping: mapping, | ||
r: r, | ||
}) | ||
continue | ||
} | ||
|
||
return strictMappingsByTopic, mappingsRegex, err | ||
} | ||
|
||
// Start polling the prototypes from the configured provider (e.g. filesystem or Git) and sync these | ||
// into our in-memory prototype registry. | ||
func (s *Service) Start() error { | ||
|
@@ -308,12 +338,35 @@ func (s *Service) IsProtobufSchemaRegistryEnabled() bool { | |
return s.cfg.SchemaRegistry.Enabled | ||
} | ||
|
||
func (s *Service) getMatchingMapping(topicName string) (mapping config.ProtoTopicMapping, err error) { | ||
mapping, strictExists := s.strictMappingsByTopic[topicName] | ||
if strictExists { | ||
return mapping, nil | ||
} | ||
|
||
var match bool | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please, validate the complexity with a simple test. Test caseCall
|
||
for _, rMapping := range s.mappingsRegex { | ||
match = rMapping.r.MatchString(topicName) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is a performance degradation for a non-matched topic. The maximum number of searches has to be estimated as total unique topic names O(N). I suggest saving the |
||
if match { | ||
mapping = rMapping.ProtoTopicMapping | ||
s.strictMappingsByTopic[topicName] = mapping | ||
break | ||
} | ||
} | ||
|
||
if !match { | ||
return mapping, fmt.Errorf("no prototype found for the given topic. Check your configured protobuf mappings") | ||
} | ||
|
||
return mapping, nil | ||
} | ||
|
||
// GetMessageDescriptor tries to find the apr | ||
func (s *Service) GetMessageDescriptor(topicName string, property RecordPropertyType) (*desc.MessageDescriptor, error) { | ||
// 1. Otherwise check if the user has configured a mapping to a local proto type for this topic and record type | ||
mapping, exists := s.mappingsByTopic[topicName] | ||
if !exists { | ||
return nil, fmt.Errorf("no prototype found for the given topic '%s'. Check your configured protobuf mappings", topicName) | ||
mapping, err := s.getMatchingMapping(topicName) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
var protoTypeURL string | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not related to this PR, I assume something went wrong when syncing your fork? Please fix this