Skip to content

Commit 73fda73

Browse files
sfge optimization
1 parent 972cfe6 commit 73fda73

6 files changed

Lines changed: 46 additions & 15 deletions

File tree

packages/code-analyzer-sfge-engine/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "@salesforce/code-analyzer-sfge-engine",
33
"description": "Plugin package that adds 'Salesforce Graph Engine' as an engine into Salesforce Code Analyzer",
4-
"version": "0.18.0",
4+
"version": "0.19.0-SNAPSHOT",
55
"author": "The Salesforce Code Analyzer Team",
66
"license": "BSD-3-Clause",
77
"homepage": "https://developer.salesforce.com/docs/platform/salesforce-code-analyzer/overview",

packages/code-analyzer-sfge-engine/sfge/src/main/java/com/salesforce/config/EnvUtil.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,11 @@ public final class EnvUtil {
2121
// TODO: These should move to SfgeConfigImpl and this class should return Optionals
2222
@VisibleForTesting
2323
static final int DEFAULT_RULE_THREAD_COUNT =
24-
Math.min(Runtime.getRuntime().availableProcessors(), 4);
24+
Math.min(Runtime.getRuntime().availableProcessors(), 8);
2525

2626
@VisibleForTesting
2727
static final long DEFAULT_RULE_THREAD_TIMEOUT =
28-
TimeUnit.MILLISECONDS.convert(15, TimeUnit.MINUTES);
28+
TimeUnit.MILLISECONDS.convert(30, TimeUnit.SECONDS);
2929

3030
@VisibleForTesting static final boolean DEFAULT_RULE_DISABLE_WARNING_VIOLATION = false;
3131
@VisibleForTesting static final boolean DEFAULT_LOG_WARNINGS_ON_VERBOSE = false;

packages/code-analyzer-sfge-engine/sfge/src/main/java/com/salesforce/rules/AbstractStaticRule.java

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,20 +8,41 @@
88
import org.apache.tinkerpop.gremlin.structure.Vertex;
99

1010
public abstract class AbstractStaticRule extends AbstractRule implements StaticRule {
11+
1112
@Override
1213
public final List<Violation> run(GraphTraversalSource g) {
1314
return run(g, new ArrayList<>());
1415
}
1516

17+
/**
18+
* TinkerGraph's .union() operator degrades non-linearly when given thousands of
19+
* sub-traversals. Splitting targets into batches of this size keeps each .union()
20+
* manageable and avoids the performance cliff.
21+
*/
22+
private static final int BATCH_SIZE = 500;
23+
1624
public final List<Violation> run(
1725
GraphTraversalSource g, List<AbstractRuleRunner.RuleRunnerTarget> targets) {
18-
GraphTraversal<Vertex, Vertex> eligibleVertices = buildBaseTraversal(g, targets);
19-
return _run(g, eligibleVertices);
20-
}
26+
if (targets.size() <= BATCH_SIZE) {
27+
GraphTraversal<Vertex, Vertex> eligibleVertices =
28+
TraversalUtil.ruleTargetTraversal(g, targets);
29+
return _run(g, eligibleVertices);
30+
}
2131

22-
private GraphTraversal<Vertex, Vertex> buildBaseTraversal(
23-
GraphTraversalSource g, List<AbstractRuleRunner.RuleRunnerTarget> targets) {
24-
return TraversalUtil.ruleTargetTraversal(g, targets);
32+
int totalBatches = (targets.size() + BATCH_SIZE - 1) / BATCH_SIZE;
33+
List<Violation> allViolations = new ArrayList<>();
34+
35+
for (int batchNum = 0; batchNum < totalBatches; batchNum++) {
36+
int fromIndex = batchNum * BATCH_SIZE;
37+
int toIndex = Math.min(fromIndex + BATCH_SIZE, targets.size());
38+
List<AbstractRuleRunner.RuleRunnerTarget> batch = targets.subList(fromIndex, toIndex);
39+
40+
GraphTraversal<Vertex, Vertex> batchTraversal =
41+
TraversalUtil.ruleTargetTraversal(g, batch);
42+
List<Violation> batchViolations = _run(g, batchTraversal);
43+
allViolations.addAll(batchViolations);
44+
}
45+
return allViolations;
2546
}
2647

2748
/**

packages/code-analyzer-sfge-engine/src/config.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,8 @@ export const DEFAULT_SFGE_ENGINE_CONFIG: SfgeEngineConfig = {
2727
java_command: DEFAULT_JAVA_COMMAND,
2828
disable_limit_reached_violations: false,
2929
java_max_heap_size: undefined,
30-
java_thread_count: 4,
31-
java_thread_timeout: 900000
30+
java_thread_count: 8,
31+
java_thread_timeout: 30000
3232
};
3333

3434
export const SFGE_ENGINE_CONFIG_DESCRIPTION: ConfigDescription = {

packages/code-analyzer-sfge-engine/src/engine.ts

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,10 @@ import {RuntimeSfgeWrapper, SfgeRuleInfo, SfgeRunOptions, SfgeRunResult} from ".
1919
import {SfgeEngineConfig} from "./config";
2020

2121
const SFGE_RELEVANT_FILE_EXTENSIONS = ['.cls', '.trigger', '-meta.xml', '.page', '.component'];
22+
// SFGE can only create entry points from Apex source files — filtering targets
23+
// to these extensions prevents SFGE from receiving non-Apex files as targets,
24+
// which would cause useless union() branches during static rule graph traversal.
25+
const SFGE_TARGET_FILE_EXTENSIONS = ['.cls', '.trigger'];
2226
const DEV_PREVIEW_TAG: string = 'DevPreview';
2327

2428
export class SfgeEngine extends Engine {
@@ -68,8 +72,10 @@ export class SfgeEngine extends Engine {
6872
return { violations: [] };
6973
}
7074

71-
// Get targeted files and return early if empty - prevents SFGE from analyzing all workspace files
72-
const targetedFiles: string[] = await runOptions.workspace.getTargetedFiles();
75+
// Get targeted files and filter to Apex source only (.cls and .trigger).
76+
// SFGE only creates entry points from Apex files; passing non-Apex targets
77+
// causes useless union() branches during graph traversal with no benefit.
78+
const targetedFiles: string[] = (await runOptions.workspace.getTargetedFiles()).filter(isApexSourceFile);
7379
if (targetedFiles.length === 0) {
7480
this.emitRunRulesProgressEvent(100);
7581
return { violations: [] };
@@ -197,6 +203,10 @@ function isFileRelevantToSfge(fileName: string): boolean {
197203
return SFGE_RELEVANT_FILE_EXTENSIONS.some(extension => fileName.toLowerCase().endsWith(extension));
198204
}
199205

206+
function isApexSourceFile(fileName: string): boolean {
207+
return SFGE_TARGET_FILE_EXTENSIONS.some(extension => fileName.toLowerCase().endsWith(extension));
208+
}
209+
200210
function toRuleDescription(sfgeRuleInfo: SfgeRuleInfo): RuleDescription {
201211
const tags: string[] = [DEV_PREVIEW_TAG, sfgeRuleInfo.category.replaceAll(' ', '')];
202212
tags.push(COMMON_TAGS.LANGUAGES.APEX);

packages/code-analyzer-sfge-engine/test/plugin.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,8 +60,8 @@ describe('SfgeEnginePlugin', () => {
6060
java_command: resolvedConfig.java_command, // We just checked the Java Command above.
6161
disable_limit_reached_violations: false,
6262
java_max_heap_size: undefined,
63-
java_thread_count: 4,
64-
java_thread_timeout: 900000
63+
java_thread_count: 8,
64+
java_thread_timeout: 30000
6565
});
6666
});
6767

0 commit comments

Comments
 (0)