@@ -5,6 +5,7 @@ import { Promise } from 'bluebird';
5
5
import { compareUnsorted } from 'js-deep-equals' ;
6
6
import { SEMATTRS_DB_NAME , SEMATTRS_DB_OPERATION } from '@opentelemetry/semantic-conventions' ;
7
7
import * as jsonpatch from 'fast-json-patch' ;
8
+ import nconf from 'nconf' ;
8
9
import {
9
10
ALREADY_DELETED_ERROR ,
10
11
AlreadyDeletedError ,
@@ -211,7 +212,8 @@ import { getDraftContext } from '../utils/draftContext';
211
212
import { getDraftChanges , isDraftSupportedEntity } from './draft-utils' ;
212
213
213
214
// region global variables
214
- const MAX_BATCH_SIZE = 300 ;
215
+ const MAX_BATCH_SIZE = nconf . get ( 'elasticsearch:batch_loader_max_size' ) ?? 300 ;
216
+ const MAX_EXPLANATIONS_PER_RULE = nconf . get ( 'rule_engine:max_explanations_per_rule' ) ?? 100 ;
215
217
// endregion
216
218
217
219
// region Loader common
@@ -2332,31 +2334,52 @@ const createRuleDataPatch = (instance) => {
2332
2334
}
2333
2335
return patch ;
2334
2336
} ;
2335
- const upsertEntityRule = async ( context , user , instance , input , opts = { } ) => {
2336
- logApp . info ( 'Upsert inferred entity' , { input } ) ;
2337
- const { fromRule } = opts ;
2338
- const updatedRule = input [ fromRule ] ;
2337
+
2338
+ const getRuleExplanationsSize = ( fromRule , instance ) => {
2339
+ return ( instance [ fromRule ] ?? [ ] ) . flat ( ) . length ;
2340
+ } ;
2341
+
2342
+ const createUpsertRulePatch = async ( instance , input , opts = { } ) => {
2343
+ const { fromRule, fromRuleDeletion = false } = opts ;
2344
+ // 01 - Limit the number of element for the rule
2345
+ const updatedRule = fromRuleDeletion ? input [ fromRule ] : ( input [ fromRule ] ?? [ ] ) . slice ( - MAX_EXPLANATIONS_PER_RULE ) ;
2339
2346
const rulePatch = { [ fromRule ] : updatedRule } ;
2340
2347
const ruleInstance = R . mergeRight ( instance , rulePatch ) ;
2348
+ // 02 - Create the patch
2341
2349
const innerPatch = createRuleDataPatch ( ruleInstance ) ;
2342
- const patch = { ...rulePatch , ...innerPatch } ;
2350
+ return { ...rulePatch , ...innerPatch } ;
2351
+ } ;
2352
+ const upsertEntityRule = async ( context , user , instance , input , opts = { } ) => {
2353
+ const { fromRule } = opts ;
2354
+ // 01. If relation already have max explanation, don't do anything
2355
+ // Strict equals to clean existing element with too many explanations
2356
+ const ruleExplanationsSize = getRuleExplanationsSize ( fromRule , instance ) ;
2357
+ if ( ruleExplanationsSize === MAX_EXPLANATIONS_PER_RULE ) {
2358
+ return instance ;
2359
+ }
2360
+ logApp . debug ( 'Upsert inferred entity' , { input } ) ;
2361
+ const patch = await createUpsertRulePatch ( instance , input , opts ) ;
2343
2362
const element = await storeLoadByIdWithRefs ( context , user , instance . internal_id , { type : instance . entity_type } ) ;
2344
2363
return await patchAttributeFromLoadedWithRefs ( context , RULE_MANAGER_USER , element , patch , opts ) ;
2345
2364
} ;
2346
2365
const upsertRelationRule = async ( context , user , instance , input , opts = { } ) => {
2347
- const { fromRule, ruleOverride = false } = opts ;
2348
- // 01 - Update the rule
2366
+ const { fromRule, fromRuleDeletion = false } = opts ;
2367
+ // 01. If relation already have max explanation, don't do anything
2368
+ // Strict equals to clean existing element with too many explanations
2369
+ const ruleExplanationsSize = getRuleExplanationsSize ( fromRule , instance ) ;
2370
+ if ( ! fromRuleDeletion && ruleExplanationsSize === MAX_EXPLANATIONS_PER_RULE ) {
2371
+ return instance ;
2372
+ }
2373
+ logApp . debug ( 'Upsert inferred relation' , { input } ) ;
2374
+ // 02 - Update the rule
2349
2375
const updatedRule = input [ fromRule ] ;
2350
- if ( ! ruleOverride ) {
2376
+ if ( ! fromRuleDeletion ) {
2351
2377
const keepRuleHashes = input [ fromRule ] . map ( ( i ) => i . hash ) ;
2352
2378
const instanceRuleToKeep = ( instance [ fromRule ] ?? [ ] ) . filter ( ( i ) => ! keepRuleHashes . includes ( i . hash ) ) ;
2353
2379
updatedRule . push ( ...instanceRuleToKeep ) ;
2354
2380
}
2355
- const rulePatch = { [ fromRule ] : updatedRule } ;
2356
- const ruleInstance = R . mergeRight ( instance , rulePatch ) ;
2357
- const innerPatch = createRuleDataPatch ( ruleInstance ) ;
2358
- const patch = { ...rulePatch , ...innerPatch } ;
2359
- logApp . info ( 'Upsert inferred relation' , { id : instance . id , relation : patch } ) ;
2381
+ // 03 - Create the patch
2382
+ const patch = await createUpsertRulePatch ( instance , input , opts ) ;
2360
2383
const element = await storeLoadByIdWithRefs ( context , user , instance . internal_id , { type : instance . entity_type } ) ;
2361
2384
return await patchAttributeFromLoadedWithRefs ( context , RULE_MANAGER_USER , element , patch , opts ) ;
2362
2385
} ;
@@ -3316,13 +3339,13 @@ export const deleteInferredRuleElement = async (rule, instance, deletedDependenc
3316
3339
// If not we need to clean the rule and keep the element for other rules.
3317
3340
logApp . info ( 'Cleanup inferred element' , { rule, id : instance . id } ) ;
3318
3341
const input = { [ completeRuleName ] : null } ;
3319
- const upsertOpts = { fromRule, ruleOverride : true } ;
3342
+ const upsertOpts = { fromRule, fromRuleDeletion : true } ;
3320
3343
await upsertRelationRule ( context , RULE_MANAGER_USER , instance , input , upsertOpts ) ;
3321
3344
} else {
3322
3345
logApp . info ( 'Upsert inferred element' , { rule, id : instance . id } ) ;
3323
3346
// Rule still have other explanation, update the rule
3324
3347
const input = { [ completeRuleName ] : rebuildRuleContent } ;
3325
- const ruleOpts = { fromRule, ruleOverride : true } ;
3348
+ const ruleOpts = { fromRule, fromRuleDeletion : true } ;
3326
3349
await upsertRelationRule ( context , RULE_MANAGER_USER , instance , input , ruleOpts ) ;
3327
3350
}
3328
3351
} catch ( err ) {
0 commit comments