Each worker shares the same util.PathEval, which is not thread-safe.
To fix this issue, each worker should have their own util.PathEval or util.PathEval should be made thread-safe. Here is an example of how util.PathEval can be made thread-safe using a lock:
diff --git a/util/json.go b/util/json.go
index f66ab86..c8ffbca 100644
--- a/util/json.go
+++ b/util/json.go
@@ -13,6 +13,7 @@ import (
"encoding/json"
"errors"
"fmt"
+ "sync"
"time"
"github.com/Intevation/gval"
@@ -32,6 +33,7 @@ func ReMarshalJSON(dst, src any) error {
type PathEval struct {
builder gval.Language
exprs map[string]gval.Evaluable
+ exprsMU sync.RWMutex
}
// NewPathEval creates a new PathEval.
@@ -45,14 +47,22 @@ func NewPathEval() *PathEval {
// Compile compiles an expression and stores it in the
// internal cache on success.
func (pe *PathEval) Compile(expr string) (gval.Evaluable, error) {
- if eval := pe.exprs[expr]; eval != nil {
+ pe.exprsMU.RLock()
+ eval := pe.exprs[expr]
+ pe.exprsMU.RUnlock()
+
+ if eval != nil {
return eval, nil
}
eval, err := pe.builder.NewEvaluable(expr)
if err != nil {
return nil, err
}
+
+ pe.exprsMU.Lock()
pe.exprs[expr] = eval
+ pe.exprsMU.Unlock()
+
return eval, nil
}
@@ -62,13 +72,17 @@ func (pe *PathEval) Eval(expr string, doc any) (any, error) {
if doc == nil {
return nil, errors.New("no document to extract data from")
}
+ pe.exprsMU.RLock()
eval := pe.exprs[expr]
+ pe.exprsMU.RUnlock()
if eval == nil {
var err error
if eval, err = pe.builder.NewEvaluable(expr); err != nil {
return nil, err
}
+ pe.exprsMU.Lock()
pe.exprs[expr] = eval
+ pe.exprsMU.Unlock()
}
return eval(context.Background(), doc)
}
To reproduce, run the following command:
go run -race ./cmd/csaf_downloader -d ~/Documents/csaf_json_files https://cert-portal.siemens.com/productcert/csaf/provider-metadata.json
PR #547 addresses this. Instead of using a mutex around the global one its easier to use a PathEval per worker.
The Aggregator already does this, too.
With the PR the downloader runs fine under the race detector.
Each worker shares the same
util.PathEval
, which is not thread-safe. To fix this issue, each worker should have their ownutil.PathEval
orutil.PathEval
should be made thread-safe. Here is an example of howutil.PathEval
can be made thread-safe using a lock:To reproduce, run the following command:
go run -race ./cmd/csaf_downloader -d ~/Documents/csaf_json_files https://cert-portal.siemens.com/productcert/csaf/provider-metadata.json
Which results in this output: