Centralize DynamicDP and add support in AttributeS

This commit is contained in:
TeoV
2020-10-05 17:09:28 +03:00
committed by Dan Christian Bogos
parent 3e998f9d8e
commit e29f34ac58
10 changed files with 220 additions and 117 deletions

View File

@@ -23,6 +23,9 @@ import "github.com/cgrates/cgrates/utils"
// AttributeSCfg is the configuration of attribute service
type AttributeSCfg struct {
Enabled bool
ResourceSConns []string
StatSConns []string
ApierSConns []string
IndexedSelects bool
StringIndexedFields *[]string
PrefixIndexedFields *[]string
@@ -38,6 +41,39 @@ func (alS *AttributeSCfg) loadFromJsonCfg(jsnCfg *AttributeSJsonCfg) (err error)
if jsnCfg.Enabled != nil {
alS.Enabled = *jsnCfg.Enabled
}
if jsnCfg.Stats_conns != nil {
alS.StatSConns = make([]string, len(*jsnCfg.Stats_conns))
for idx, connID := range *jsnCfg.Stats_conns {
// if we have the connection internal we change the name so we can have internal rpc for each subsystem
if connID == utils.MetaInternal {
alS.StatSConns[idx] = utils.ConcatenatedKey(utils.MetaInternal, utils.MetaStatS)
} else {
alS.StatSConns[idx] = connID
}
}
}
if jsnCfg.Resources_conns != nil {
alS.ResourceSConns = make([]string, len(*jsnCfg.Resources_conns))
for idx, connID := range *jsnCfg.Resources_conns {
// if we have the connection internal we change the name so we can have internal rpc for each subsystem
if connID == utils.MetaInternal {
alS.ResourceSConns[idx] = utils.ConcatenatedKey(utils.MetaInternal, utils.MetaResources)
} else {
alS.ResourceSConns[idx] = connID
}
}
}
if jsnCfg.Apiers_conns != nil {
alS.ApierSConns = make([]string, len(*jsnCfg.Apiers_conns))
for idx, connID := range *jsnCfg.Apiers_conns {
// if we have the connection internal we change the name so we can have internal rpc for each subsystem
if connID == utils.MetaInternal {
alS.ApierSConns[idx] = utils.ConcatenatedKey(utils.MetaInternal, utils.MetaApier)
} else {
alS.ApierSConns[idx] = connID
}
}
}
if jsnCfg.Indexed_selects != nil {
alS.IndexedSelects = *jsnCfg.Indexed_selects
}
@@ -74,6 +110,9 @@ func (alS *AttributeSCfg) loadFromJsonCfg(jsnCfg *AttributeSJsonCfg) (err error)
func (alS *AttributeSCfg) AsMapInterface() (initialMP map[string]interface{}) {
initialMP = map[string]interface{}{
utils.EnabledCfg: alS.Enabled,
utils.StatSConnsCfg: alS.StatSConns,
utils.ResourceSConnsCfg: alS.ResourceSConns,
utils.ApierSConnsCfg: alS.ApierSConns,
utils.IndexedSelectsCfg: alS.IndexedSelects,
utils.ProcessRunsCfg: alS.ProcessRuns,
utils.NestedFieldsCfg: alS.NestedFields,

View File

@@ -36,6 +36,9 @@ func TestAttributeSCfgloadFromJsonCfg(t *testing.T) {
}
expected := &AttributeSCfg{
Enabled: true,
ApierSConns: []string{},
StatSConns: []string{},
ResourceSConns: []string{},
IndexedSelects: false,
StringIndexedFields: &[]string{"*req.index1"},
PrefixIndexedFields: &[]string{"*req.index1", "*req.index2"},
@@ -63,6 +66,9 @@ func TestAttributeSCfgAsMapInterface(t *testing.T) {
}`
eMap := map[string]interface{}{
utils.EnabledCfg: true,
utils.StatSConnsCfg: []string{},
utils.ResourceSConnsCfg: []string{},
utils.ApierSConnsCfg: []string{},
utils.StringIndexedFieldsCfg: []string{"*req.index1"},
utils.PrefixIndexedFieldsCfg: []string{"*req.index1", "*req.index2"},
utils.ProcessRunsCfg: 3,
@@ -88,6 +94,9 @@ func TestAttributeSCfgAsMapInterface2(t *testing.T) {
}`
expectedMap := map[string]interface{}{
utils.EnabledCfg: true,
utils.StatSConnsCfg: []string{},
utils.ResourceSConnsCfg: []string{},
utils.ApierSConnsCfg: []string{},
utils.IndexedSelectsCfg: true,
utils.PrefixIndexedFieldsCfg: []string{},
utils.SuffixIndexedFieldsCfg: []string{"*req.index1", "*req.index2"},
@@ -109,6 +118,9 @@ func TestAttributeSCfgAsMapInterface3(t *testing.T) {
`
expectedMap := map[string]interface{}{
utils.EnabledCfg: false,
utils.StatSConnsCfg: []string{},
utils.ResourceSConnsCfg: []string{},
utils.ApierSConnsCfg: []string{},
utils.IndexedSelectsCfg: true,
utils.PrefixIndexedFieldsCfg: []string{},
utils.SuffixIndexedFieldsCfg: []string{},

View File

@@ -536,7 +536,10 @@ const CGRATES_CFG_JSON = `
"attributes": { // AttributeS config
"enabled": false, // starts attribute service: <true|false>.
"enabled": false, // starts attribute service: <true|false>
"stats_conns": [], // connections to StatS, empty to disable: <""|*internal|$rpc_conns_id>
"resources_conns": [], // connections to ResourceS, empty to disable: <""|*internal|$rpc_conns_id>
"apiers_conns": [], // connections to ApierS, empty to disable: <""|*internal|$rpc_conns_id>
"indexed_selects": true, // enable profile matching exclusively on indexes
//"string_indexed_fields": [], // query indexes based on these fields for faster processing
"prefix_indexed_fields": [], // query indexes based on these fields for faster processing

View File

@@ -134,6 +134,9 @@ func testCGRConfigReloadAttributeS(t *testing.T) {
}
expAttr := &AttributeSCfg{
Enabled: true,
ApierSConns: []string{},
ResourceSConns: []string{},
StatSConns: []string{},
StringIndexedFields: &[]string{utils.MetaReq + utils.NestingSep + utils.Account},
PrefixIndexedFields: &[]string{},
SuffixIndexedFields: &[]string{},

View File

@@ -825,12 +825,15 @@ func TestDNSAgentJsonCfg(t *testing.T) {
func TestDfAttributeServJsonCfg(t *testing.T) {
eCfg := &AttributeSJsonCfg{
Enabled: utils.BoolPointer(false),
Stats_conns: &[]string{},
Resources_conns: &[]string{},
Apiers_conns: &[]string{},
Indexed_selects: utils.BoolPointer(true),
String_indexed_fields: nil,
Prefix_indexed_fields: &[]string{},
Suffix_indexed_fields: &[]string{},
Process_runs: utils.IntPointer(1),
Nested_fields: utils.BoolPointer(false),
Process_runs: utils.IntPointer(1),
}
if cfg, err := dfCgrJSONCfg.AttributeServJsonCfg(); err != nil {
t.Error(err)

View File

@@ -395,6 +395,9 @@ type ReqProcessorJsnCfg struct {
// Attribute service config section
type AttributeSJsonCfg struct {
Enabled *bool
Stats_conns *[]string
Resources_conns *[]string
Apiers_conns *[]string
Indexed_selects *bool
String_indexed_fields *[]string
Prefix_indexed_fields *[]string

View File

@@ -235,18 +235,21 @@ func (alS *AttributeService) processEvent(args *AttrArgsProcessEvent, evNm utils
case utils.META_CONSTANT:
substitute, err = attribute.Value.ParseValue(utils.EmptyString)
case utils.MetaVariable, utils.META_COMPOSED:
substitute, err = attribute.Value.ParseDataProvider(evNm)
substitute, err = attribute.Value.ParseDataProvider(newDynamicDP(alS.cgrcfg.AttributeSCfg().ResourceSConns,
alS.cgrcfg.AttributeSCfg().StatSConns, alS.cgrcfg.AttributeSCfg().ApierSConns, args.Tenant, evNm))
case utils.META_USAGE_DIFFERENCE:
if len(attribute.Value) != 2 {
return nil, fmt.Errorf("invalid arguments <%s>", utils.ToJSON(attribute.Value))
}
var strVal1 string
if strVal1, err = attribute.Value[0].ParseDataProvider(evNm); err != nil {
if strVal1, err = attribute.Value[0].ParseDataProvider(newDynamicDP(alS.cgrcfg.AttributeSCfg().ResourceSConns,
alS.cgrcfg.AttributeSCfg().StatSConns, alS.cgrcfg.AttributeSCfg().ApierSConns, args.Tenant, evNm)); err != nil {
rply = nil
return
}
var strVal2 string
if strVal2, err = attribute.Value[1].ParseDataProvider(evNm); err != nil {
if strVal2, err = attribute.Value[1].ParseDataProvider(newDynamicDP(alS.cgrcfg.AttributeSCfg().ResourceSConns,
alS.cgrcfg.AttributeSCfg().StatSConns, alS.cgrcfg.AttributeSCfg().ApierSConns, args.Tenant, evNm)); err != nil {
rply = nil
return
}
@@ -263,7 +266,8 @@ func (alS *AttributeService) processEvent(args *AttrArgsProcessEvent, evNm utils
substitute = tEnd.Sub(tStart).String()
case utils.MetaSum:
var ifaceVals []interface{}
if ifaceVals, err = attribute.Value.GetIfaceFromValues(evNm); err != nil {
if ifaceVals, err = attribute.Value.GetIfaceFromValues(newDynamicDP(alS.cgrcfg.AttributeSCfg().ResourceSConns,
alS.cgrcfg.AttributeSCfg().StatSConns, alS.cgrcfg.AttributeSCfg().ApierSConns, args.Tenant, evNm)); err != nil {
rply = nil
return
}
@@ -275,7 +279,8 @@ func (alS *AttributeService) processEvent(args *AttrArgsProcessEvent, evNm utils
substitute = utils.IfaceAsString(ifaceSum)
case utils.MetaDifference:
var ifaceVals []interface{}
if ifaceVals, err = attribute.Value.GetIfaceFromValues(evNm); err != nil {
if ifaceVals, err = attribute.Value.GetIfaceFromValues(newDynamicDP(alS.cgrcfg.AttributeSCfg().ResourceSConns,
alS.cgrcfg.AttributeSCfg().StatSConns, alS.cgrcfg.AttributeSCfg().ApierSConns, args.Tenant, evNm)); err != nil {
rply = nil
return
}
@@ -287,7 +292,8 @@ func (alS *AttributeService) processEvent(args *AttrArgsProcessEvent, evNm utils
substitute = utils.IfaceAsString(ifaceSum)
case utils.MetaMultiply:
var ifaceVals []interface{}
if ifaceVals, err = attribute.Value.GetIfaceFromValues(evNm); err != nil {
if ifaceVals, err = attribute.Value.GetIfaceFromValues(newDynamicDP(alS.cgrcfg.AttributeSCfg().ResourceSConns,
alS.cgrcfg.AttributeSCfg().StatSConns, alS.cgrcfg.AttributeSCfg().ApierSConns, args.Tenant, evNm)); err != nil {
rply = nil
return
}
@@ -299,7 +305,8 @@ func (alS *AttributeService) processEvent(args *AttrArgsProcessEvent, evNm utils
substitute = utils.IfaceAsString(ifaceSum)
case utils.MetaDivide:
var ifaceVals []interface{}
if ifaceVals, err = attribute.Value.GetIfaceFromValues(evNm); err != nil {
if ifaceVals, err = attribute.Value.GetIfaceFromValues(newDynamicDP(alS.cgrcfg.AttributeSCfg().ResourceSConns,
alS.cgrcfg.AttributeSCfg().StatSConns, alS.cgrcfg.AttributeSCfg().ApierSConns, args.Tenant, evNm)); err != nil {
rply = nil
return
}
@@ -315,7 +322,8 @@ func (alS *AttributeService) processEvent(args *AttrArgsProcessEvent, evNm utils
utils.ToJSON(attribute.Value), utils.MetaValueExponent)
}
var strVal1 string
if strVal1, err = attribute.Value[0].ParseDataProvider(evNm); err != nil {
if strVal1, err = attribute.Value[0].ParseDataProvider(newDynamicDP(alS.cgrcfg.AttributeSCfg().ResourceSConns,
alS.cgrcfg.AttributeSCfg().StatSConns, alS.cgrcfg.AttributeSCfg().ApierSConns, args.Tenant, evNm)); err != nil {
rply = nil
return
}
@@ -325,7 +333,8 @@ func (alS *AttributeService) processEvent(args *AttrArgsProcessEvent, evNm utils
strVal1, utils.MetaValueExponent)
}
var strVal2 string
if strVal2, err = attribute.Value[1].ParseDataProvider(evNm); err != nil {
if strVal2, err = attribute.Value[1].ParseDataProvider(newDynamicDP(alS.cgrcfg.AttributeSCfg().ResourceSConns,
alS.cgrcfg.AttributeSCfg().StatSConns, alS.cgrcfg.AttributeSCfg().ApierSConns, args.Tenant, evNm)); err != nil {
rply = nil
return
}
@@ -338,7 +347,8 @@ func (alS *AttributeService) processEvent(args *AttrArgsProcessEvent, evNm utils
alS.cgrcfg.GeneralCfg().RoundingDecimals, utils.ROUNDING_MIDDLE), 'f', -1, 64)
case utils.MetaUnixTimestamp:
var val string
if val, err = attribute.Value.ParseDataProvider(evNm); err != nil {
if val, err = attribute.Value.ParseDataProvider(newDynamicDP(alS.cgrcfg.AttributeSCfg().ResourceSConns,
alS.cgrcfg.AttributeSCfg().StatSConns, alS.cgrcfg.AttributeSCfg().ApierSConns, args.Tenant, evNm)); err != nil {
rply = nil
return
}

125
engine/dynamicdp.go Normal file
View File

@@ -0,0 +1,125 @@
/*
Real-time Online/Offline Charging System (OCS) for Telecom & ISP environments
Copyright (C) ITsysCOM GmbH
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>
*/
package engine
import (
"fmt"
"net"
"github.com/cgrates/cgrates/config"
"github.com/cgrates/cgrates/utils"
)
func newDynamicDP(resConns, stsConns, apiConns []string,
tenant string, initialDP utils.DataProvider) *dynamicDP {
return &dynamicDP{
resConns: resConns,
stsConns: stsConns,
apiConns: apiConns,
tenant: tenant,
initialDP: initialDP,
cache: utils.MapStorage{},
}
}
type dynamicDP struct {
resConns []string
stsConns []string
apiConns []string
tenant string
initialDP utils.DataProvider
cache utils.MapStorage
}
func (dDP *dynamicDP) String() string { return dDP.initialDP.String() }
func (dDP *dynamicDP) FieldAsString(fldPath []string) (string, error) {
val, err := dDP.FieldAsInterface(fldPath)
if err != nil {
return "", err
}
return utils.IfaceAsString(val), nil
}
func (dDP *dynamicDP) RemoteHost() net.Addr {
return utils.LocalAddr()
}
var initialDPPrefixes = utils.NewStringSet([]string{utils.MetaReq, utils.MetaVars,
utils.MetaCgreq, utils.MetaCgrep, utils.MetaRep, utils.MetaCGRAReq,
utils.MetaAct, utils.MetaEC, utils.MetaUCH, utils.MetaOpts})
func (dDP *dynamicDP) FieldAsInterface(fldPath []string) (val interface{}, err error) {
if len(fldPath) == 0 {
return nil, utils.ErrNotFound
}
if initialDPPrefixes.Has(fldPath[0]) {
return dDP.initialDP.FieldAsInterface(fldPath)
}
val, err = dDP.cache.FieldAsInterface(fldPath)
if err == utils.ErrNotFound { // in case not found in cache try to populate it
return dDP.fieldAsInterface(fldPath)
}
return
}
func (dDP *dynamicDP) fieldAsInterface(fldPath []string) (val interface{}, err error) {
if len(fldPath) < 2 {
return nil, fmt.Errorf("invalid fieldname <%s>", fldPath)
}
switch fldPath[0] {
case utils.MetaAccounts:
// sample of fieldName : ~*accounts.1001.BalanceMap.*monetary[0].Value
// split the field name in 3 parts
// fieldNameType (~*accounts), accountID(1001) and queried part (BalanceMap.*monetary[0].Value)
var account Account
if err = connMgr.Call(dDP.apiConns, nil, utils.APIerSv2GetAccount,
&utils.AttrGetAccount{Tenant: dDP.tenant, Account: fldPath[1]}, &account); err != nil {
return
}
//construct dataProvider from account and set it further
dp := config.NewObjectDP(account)
dDP.cache.Set(fldPath[:2], dp)
return dp.FieldAsInterface(fldPath[2:])
case utils.MetaResources:
// sample of fieldName : ~*resources.ResourceID.Field
var reply *Resource
if err := connMgr.Call(dDP.resConns, nil, utils.ResourceSv1GetResource,
&utils.TenantID{Tenant: dDP.tenant, ID: fldPath[1]}, &reply); err != nil {
return nil, err
}
dp := config.NewObjectDP(reply)
dDP.cache.Set(fldPath[:2], dp)
return dp.FieldAsInterface(fldPath[2:])
case utils.MetaStats:
// sample of fieldName : ~*stats.StatID.*acd
var statValues map[string]float64
if err := connMgr.Call(dDP.stsConns, nil, utils.StatSv1GetQueueFloatMetrics,
&utils.TenantIDWithOpts{TenantID: &utils.TenantID{Tenant: dDP.tenant, ID: fldPath[1]}},
&statValues); err != nil {
return nil, err
}
for k, v := range statValues {
dDP.cache.Set([]string{utils.MetaStats, fldPath[1], k}, v)
}
return dDP.cache.FieldAsInterface(fldPath)
default: // in case of constant we give an empty DataProvider ( empty navigable map )
}
return nil, utils.ErrNotFound
}

View File

@@ -33,7 +33,6 @@ func NewFilterS(cfg *config.CGRConfig, connMgr *ConnManager, dm *DataManager) (f
cfg: cfg,
connMgr: connMgr,
}
return
}
@@ -53,7 +52,8 @@ func (fS *FilterS) Pass(tenant string, filterIDs []string,
if len(filterIDs) == 0 {
return true, nil
}
dDP := newDynamicDP(fS.cfg, fS.connMgr, tenant, ev)
dDP := newDynamicDP(fS.cfg.FilterSCfg().ResourceSConns, fS.cfg.FilterSCfg().StatSConns,
fS.cfg.FilterSCfg().ApierSConns, tenant, ev)
for _, fltrID := range filterIDs {
f, err := fS.dm.GetFilter(tenant, fltrID,
true, true, utils.NonTransactional)
@@ -117,7 +117,8 @@ func (fS *FilterS) LazyPass(tenant string, filterIDs []string,
return true, nil, nil
}
pass = true
dDP := newDynamicDP(fS.cfg, fS.connMgr, tenant, ev)
dDP := newDynamicDP(fS.cfg.FilterSCfg().ResourceSConns, fS.cfg.FilterSCfg().StatSConns,
fS.cfg.FilterSCfg().ApierSConns, tenant, ev)
for _, fltrID := range filterIDs {
var f *Filter
f, err = fS.dm.GetFilter(tenant, fltrID,
@@ -617,104 +618,6 @@ func (fltr *FilterRule) passAPIBan(dDP utils.DataProvider) (bool, error) {
return dm.GetAPIBan(strVal, config.CgrConfig().APIBanCfg().Keys, fltr.Values[0] != utils.MetaAll, true, true)
}
func newDynamicDP(cfg *config.CGRConfig, connMgr *ConnManager,
tenant string, initialDP utils.DataProvider) *dynamicDP {
return &dynamicDP{
cfg: cfg,
connMgr: connMgr,
tenant: tenant,
initialDP: initialDP,
cache: utils.MapStorage{},
}
}
type dynamicDP struct {
cfg *config.CGRConfig
connMgr *ConnManager
tenant string
initialDP utils.DataProvider
cache utils.MapStorage
}
func (dDP *dynamicDP) String() string { return dDP.initialDP.String() }
func (dDP *dynamicDP) FieldAsString(fldPath []string) (string, error) {
val, err := dDP.FieldAsInterface(fldPath)
if err != nil {
return "", err
}
return utils.IfaceAsString(val), nil
}
func (dDP *dynamicDP) RemoteHost() net.Addr {
return utils.LocalAddr()
}
var initialDPPrefixes = utils.NewStringSet([]string{utils.MetaReq, utils.MetaVars,
utils.MetaCgreq, utils.MetaCgrep, utils.MetaRep, utils.MetaCGRAReq,
utils.MetaAct, utils.MetaEC, utils.MetaUCH, utils.MetaOpts})
func (dDP *dynamicDP) FieldAsInterface(fldPath []string) (val interface{}, err error) {
if len(fldPath) == 0 {
return nil, utils.ErrNotFound
}
if initialDPPrefixes.Has(fldPath[0]) {
return dDP.initialDP.FieldAsInterface(fldPath)
}
val, err = dDP.cache.FieldAsInterface(fldPath)
if err == utils.ErrNotFound { // in case not found in cache try to populate it
return dDP.fieldAsInterface(fldPath)
}
return
}
func (dDP *dynamicDP) fieldAsInterface(fldPath []string) (val interface{}, err error) {
if len(fldPath) < 2 {
return nil, fmt.Errorf("invalid fieldname <%s>", fldPath)
}
switch fldPath[0] {
case utils.MetaAccounts:
// sample of fieldName : ~*accounts.1001.BalanceMap.*monetary[0].Value
// split the field name in 3 parts
// fieldNameType (~*accounts), accountID(1001) and quried part (BalanceMap.*monetary[0].Value)
var account Account
if err = dDP.connMgr.Call(dDP.cfg.FilterSCfg().ApierSConns, nil, utils.APIerSv2GetAccount,
&utils.AttrGetAccount{Tenant: dDP.tenant, Account: fldPath[1]}, &account); err != nil {
return
}
//construct dataProvider from account and set it furthder
dp := config.NewObjectDP(account)
dDP.cache.Set(fldPath[:2], dp)
return dp.FieldAsInterface(fldPath[2:])
case utils.MetaResources:
// sample of fieldName : ~*resources.ResourceID.Field
var reply *Resource
if err := dDP.connMgr.Call(dDP.cfg.FilterSCfg().ResourceSConns, nil, utils.ResourceSv1GetResource,
&utils.TenantID{Tenant: dDP.tenant, ID: fldPath[1]}, &reply); err != nil {
return nil, err
}
dp := config.NewObjectDP(reply)
dDP.cache.Set(fldPath[:2], dp)
return dp.FieldAsInterface(fldPath[2:])
case utils.MetaStats:
// sample of fieldName : ~*stats.StatID.*acd
var statValues map[string]float64
if err := dDP.connMgr.Call(dDP.cfg.FilterSCfg().StatSConns, nil, utils.StatSv1GetQueueFloatMetrics,
&utils.TenantIDWithOpts{TenantID: &utils.TenantID{Tenant: dDP.tenant, ID: fldPath[1]}},
&statValues); err != nil {
return nil, err
}
for k, v := range statValues {
dDP.cache.Set([]string{utils.MetaStats, fldPath[1], k}, v)
}
return dDP.cache.FieldAsInterface(fldPath)
default: // in case of constant we give an empty DataProvider ( empty navigable map )
}
return nil, utils.ErrNotFound
}
func verifyInlineFilterS(fltrs []string) (err error) {
for _, fl := range fltrs {
if strings.HasPrefix(fl, utils.Meta) {

View File

@@ -496,10 +496,12 @@ func (rpS *RouteService) populateSortingData(ev *utils.CGREvent, route *Route,
//filter the route
if len(route.lazyCheckRules) != 0 {
//construct the DP and pass it to filterS
dynDP := newDynamicDP(rpS.cgrcfg, rpS.connMgr, ev.Tenant, utils.MapStorage{
utils.MetaReq: ev.Event,
utils.MetaVars: sortedSpl.SortingData,
})
dynDP := newDynamicDP(rpS.cgrcfg.FilterSCfg().ResourceSConns, rpS.cgrcfg.FilterSCfg().StatSConns,
rpS.cgrcfg.FilterSCfg().ApierSConns,
ev.Tenant, utils.MapStorage{
utils.MetaReq: ev.Event,
utils.MetaVars: sortedSpl.SortingData,
})
for _, rule := range route.lazyCheckRules { // verify the rules remaining from PartialPass
if pass, err = rule.Pass(dynDP); err != nil {