diff --git a/plugin/rewrite/cname_target.go b/plugin/rewrite/cname_target.go index 6b18193e9..87a9cd0bc 100644 --- a/plugin/rewrite/cname_target.go +++ b/plugin/rewrite/cname_target.go @@ -33,9 +33,11 @@ type cnameTargetRule struct { Upstream UpstreamInt // Upstream for looking up external names during the resolution process. } -// cnameTargetRuleWithReqState is cname target rewrite rule state +// cnameTargetRuleWithReqState pairs a request's ctx/state with the rule that +// matched it. rule is a pointer to the shared, Corefile-parsed rule (never +// mutated after newCNAMERule builds it) type cnameTargetRuleWithReqState struct { - rule cnameTargetRule + rule *cnameTargetRule state request.Request ctx context.Context } @@ -179,7 +181,7 @@ func newCNAMERule(nextAction string, args ...string) (Rule, error) { func (r *cnameTargetRule) Rewrite(ctx context.Context, state request.Request) (ResponseRules, Result) { if r != nil && len(r.rewriteType) > 0 && len(r.paramFromTarget) > 0 && len(r.paramToTarget) > 0 { return ResponseRules{&cnameTargetRuleWithReqState{ - rule: *r, + rule: r, state: state, ctx: ctx, }}, RewriteDone diff --git a/plugin/rewrite/cname_target_test.go b/plugin/rewrite/cname_target_test.go index de5101382..dfd3a361b 100644 --- a/plugin/rewrite/cname_target_test.go +++ b/plugin/rewrite/cname_target_test.go @@ -269,7 +269,7 @@ func TestCNAMETargetRewrite_upstreamFailurePaths(t *testing.T) { req.SetQuestion("bad.test.", dns.TypeA) state := request.Request{Req: req} - rrState := &cnameTargetRuleWithReqState{rule: rule, state: state, ctx: context.Background()} + rrState := &cnameTargetRuleWithReqState{rule: &rule, state: state, ctx: context.Background()} res := new(dns.Msg) res.SetReply(req) @@ -287,6 +287,30 @@ func TestCNAMETargetRewrite_upstreamFailurePaths(t *testing.T) { } } +// BenchmarkCNAMETargetRuleRewrite isolates cnameTargetRule.Rewrite - the call +// that used to copy the whole shared rule struct by value into +// cnameTargetRuleWithReqState on every matching request. It deliberately +// excludes RewriteResponse/the upstream lookup, whose own allocations would +// dwarf the struct copy and hide the difference this benchmark exists to show. +func BenchmarkCNAMETargetRuleRewrite(b *testing.B) { + rule, err := newCNAMERule("stop", "exact", "def.example.com", "xyz.example.com") + if err != nil { + b.Fatal(err) + } + rule.(*cnameTargetRule).Upstream = &MockedUpstream{} + + ctx := b.Context() + m := new(dns.Msg) + m.SetQuestion("abc.example.com.", dns.TypeA) + state := request.Request{Req: m} + + b.ReportAllocs() + for b.Loop() { + rules, _ := rule.Rewrite(ctx, state) + rulesSink = rules + } +} + func TestNewCNAMERuleLargeRegex(t *testing.T) { largeRegex := strings.Repeat("a", maxRegexpLen+1) _, err := newCNAMERule("stop", "regex", largeRegex, "replacement")