Reputation: 3312
I need my application to be able to load policies (rego) and then evaluate an input JSON based on defined policies.
I mistakenly used PrepareForEval
in my evaluation API instead of load policy API. The result of this surprised me as the response time kept on increasing exponentially after each evaluation, while the policies were kept unchanged. Although, after this, I've realized and changed my logic to call PrepareForEval
method at the time of loading policy and then store the prepared query instance in my struct instance. But I'm still concerned if prepare method gets executed multiple times while loading policies then it'd still become an expensive operation.
So, it'd be great to be pointed towards the correct way of using the prepare method.
Sample code:
// My provider
func init() {
cachedRego := rego.New(rego.Query("data.report"))
}
// My load policy method
func loadPolicy(ctx context.Context, filename, regoPolicy string) {
mod, err := ast.ParseModule(filename, regoPolicy)
rego.ParsedModule(mod)(cachedRego)
}
// My evaluate method
func eval(ctx context.Context, input interface{}) {
// after loading my policies, the following call took 1s, 2s, 5s, 10s,... respectively on eval calls
preparedQuery, _ := cachedRego.PrepareForEval(ctx) // <- I've moved this to my load policy method and cached preparedQuery
// this doesn’t take much time
rs, _ := preparedQuery.Eval(ctx, rego.EvalInput(input))
}
// My use case
func main() {
// load policies and evaluate inputs
for _, p := range policySet1 {
loadPolicy(context.Background(), p.filename, p.regoPolicy)
}
for _, inp := range inputSet1 {
eval(context.Background(), inp)
}
// load more policies to the earlier set and evaluate another input set
for _, p := range policySet2 {
loadPolicy(context.Background(), p.filename, p.regoPolicy)
}
for _, inp := range inputSet2 {
eval(context.Background(), inp)
}
}
Upvotes: 1
Views: 403
Reputation: 1609
TLDR; this is probably not the right place for this question. File an issue on GitHub if you are seeing behaviour you think is wrong.
To answer your question about how to prepare queries properly, your main function should (minimally) look like this:
func main() {
pq1 := loadPolicies(policySet1)
for _, inp := range inputSet1 {
eval(pq1, inp)
}
pq2 := loadPolicies(policySet2)
for _, inp := range inputSet2 {
eval(pq2, inp)
}
}
The easiest way to implement loadPolicies
for the above example would be something like:
func loadPolicies(policySet) {
opts := []func(*rego.Rego){}
// setup prepared query. load all modules.
for _, p := range policySet {
opts = append(opts, rego.Module(p.filename, p.regoPolicy))
}
// create new prepared query
return rego.New(rego.Query("data.report"), opts...).PrepareForEval()
}
Then your eval function becomes:
func eval(pq, inp) {
rs, err := pq.Eval(rego.EvalInput(inp))
// handle err
// interpret rs
}
Upvotes: 3