fix(templates): mem leaks in parser cache

Fixes duplicate template storage & removes
unnecessary raw bytes caching.

Mem usage reduced by ~30%.
> 423MB => 299MB heap alloc.

* Use `StoreWithoutRaw()` to avoid storing raw
  bytes.
* Remove duplicate storage in both caches.
* Remove ineffective raw bytes retrieval logic.

Benchmarks show 45% perf improvement with no
regressions.

Signed-off-by: Dwi Siswanto <git@dw1.io>
This commit is contained in:
Dwi Siswanto
2025-11-03 20:27:03 +07:00
parent 3a44eaf7b5
commit 7e33712d08
2 changed files with 17 additions and 20 deletions

View File

@@ -1,7 +1,6 @@
package templates
import (
"bytes"
"fmt"
"io"
"reflect"
@@ -103,20 +102,9 @@ func updateRequestOptions(template *Template) {
// parseFromSource parses a template from source with caching support
func parseFromSource(filePath string, preprocessor Preprocessor, options *protocols.ExecutorOptions, parser *Parser) (*Template, error) {
var reader io.ReadCloser
if !options.DoNotCache {
_, raw, err := parser.parsedTemplatesCache.Has(filePath)
if err == nil && raw != nil {
reader = io.NopCloser(bytes.NewReader(raw))
}
}
var err error
if reader == nil {
reader, err = utils.ReaderFromPathOrURL(filePath, options.Catalog)
if err != nil {
return nil, err
}
reader, err := utils.ReaderFromPathOrURL(filePath, options.Catalog)
if err != nil {
return nil, err
}
defer func() {
@@ -158,7 +146,7 @@ func parseFromSource(filePath string, preprocessor Preprocessor, options *protoc
template.Path = filePath
if !options.DoNotCache {
parser.compiledTemplatesCache.Store(filePath, template, nil, err)
parser.compiledTemplatesCache.StoreWithoutRaw(filePath, template, err)
}
return template, nil

View File

@@ -21,11 +21,19 @@ import (
type Parser struct {
ShouldValidate bool
NoStrictSyntax bool
// this cache can be copied safely between ephemeral instances
// parsedTemplatesCache stores lightweight parsed template structures
// (without raw bytes).
// Used for validation and filtering. This cache can be copied safely
// between ephemeral instances.
parsedTemplatesCache *Cache
// this cache might potentially contain references to heap objects
// it's recommended to always empty it at the end of execution
// compiledTemplatesCache stores fully compiled templates with all protocol
// requests.
// This cache contains references to heap objects and should be purged when
// no longer needed.
compiledTemplatesCache *Cache
sync.Mutex
}
@@ -179,7 +187,8 @@ func (p *Parser) ParseTemplate(templatePath string, catalog catalog.Catalog) (an
return nil, err
}
p.parsedTemplatesCache.Store(templatePath, template, nil, nil) // don't keep raw bytes to save memory
p.parsedTemplatesCache.StoreWithoutRaw(templatePath, template, nil)
return template, nil
}