Skip to content

perf: bulk action import caching#41807

Merged
sondermanish merged 1 commit into
releasefrom
perf/import-improvement
May 15, 2026
Merged

perf: bulk action import caching#41807
sondermanish merged 1 commit into
releasefrom
perf/import-improvement

Conversation

@sondermanish
Copy link
Copy Markdown
Contributor

@sondermanish sondermanish commented May 13, 2026

Description

Tip

Add a TL;DR when the description is longer than 500 words or extremely technical (helps the content, marketing, and DevRel team).

Please also include relevant motivation and context. List any dependencies that are required for this change. Add links to Notion, Figma or any other documents that might be relevant to the PR.

Fixes #Issue Number
or
Fixes Issue URL

Warning

If no issue exists, please create an issue first, and check with the maintainers if the issue is valid.

Automation

/ok-to-test tags="@tag.ImportExport, @tag.Git"

🔍 Cypress test results

Tip

🟢 🟢 🟢 All cypress tests have passed! 🎉 🎉 🎉
Workflow run: https://github.com/appsmithorg/appsmith/actions/runs/25814789299
Commit: 1fc6461
Cypress dashboard.
Tags: @tag.ImportExport, @tag.Git
Spec:


Wed, 13 May 2026 17:37:04 UTC

Communication

Should the DevRel and Marketing teams inform users about this change?

  • Yes
  • No

Summary by CodeRabbit

  • New Features

    • Added bulk action validation support for imports with dry-run mode capability.
    • Enhanced import pipeline with improved datasource policy and plugin metadata handling.
  • Tests

    • Added validation tests for import scenarios, including public datasource policy updates and dry-run validation modes.

Review Change Stack

@sondermanish sondermanish requested a review from a team as a code owner May 13, 2026 17:14
@sondermanish sondermanish self-assigned this May 13, 2026
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 13, 2026

Walkthrough

This PR introduces bulk action validation before import with datasource/plugin caching and public policy coordination. The service interface now declares validateActionsBeforeImport() returning split fresh/existing lists, refactors single-action validation for dry-run support, and adds bulkInsertActions(). The import pipeline accumulates and persists actions separately using the new bulk methods.

Changes

Action Import Validation & Persistence

Layer / File(s) Summary
Service Contract: Validation & Bulk APIs
app/server/appsmith-server/src/main/java/com/appsmith/server/newactions/base/NewActionServiceCE.java
Interface declares validateActionsBeforeImport() returning Mono<Tuple2<List<NewAction>, List<NewAction>>>, bulkInsertActions(), and validateAction(..., boolean isDryOps) overload.
Bulk Validation: Per-Action Pipeline & Context
app/server/appsmith-server/src/main/java/com/appsmith/server/newactions/base/NewActionServiceCEImpl.java
Imports PermissionGroup, Tuples, ConcurrentHashMap; implements validateActionsBeforeImport() orchestrating sequential per-action validation with bounded concurrency, datasource/plugin/application caching via BulkActionValidationContext, git-sync setup, JSON-path extraction, datasource auto-generation, and public datasource policy accumulation.
Single-Action Validation: Dry-Run & DTO Normalization
app/server/appsmith-server/src/main/java/com/appsmith/server/newactions/base/NewActionServiceCEImpl.java
Refactors validateAction(NewAction, boolean isDryOps) to operate on edit DTO, skip DB datasource fetches when dry-run and datasource id exists, update plugin-failure paths, and properly map validated results back to unpublished action.
Bulk Insert: GitSync & Repository Persistence
app/server/appsmith-server/src/main/java/com/appsmith/server/newactions/base/NewActionServiceCEImpl.java
Implements bulkInsertActions(List<NewAction>) assigning missing git-sync IDs and delegating to repository bulk insert.
Import Pipeline: Action List Accumulation & Bulk Persistence
app/server/appsmith-server/src/main/java/com/appsmith/server/newactions/importable/NewActionImportableServiceCEImpl.java
Refactors import flow to assign actionsFromJSONMono, accumulate fresh and existing action lists, iterate validated actions while skipping invalid context IDs, conditionally update datasources, and replace prior bulk-validate-and-persist calls with validateActionsBeforeImport + separate bulkInsertActions / bulkUpdateActions. Normalizes datasource-id guard to StringUtils.hasText().
Comprehensive Test Coverage: Validation & Policy Updates
app/server/appsmith-server/src/test/java/com/appsmith/server/services/ce/NewActionServiceTest.java
Adds spy bean injections for plugin/datasource/permission/application/policy services with @BeforeEach reset, creates helper factories (createImportAction, createDatasource, createPlugin, createPermissionGroup, createPolicy), and adds three tests verifying fresh/existing action splitting, populated plugin/datasource fields, and public datasource policy updates (including skip when app id missing).

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Suggested labels

ok-to-test

Suggested reviewers

  • subrata71

Poem

🔧 Bulk actions now validate with grace,
Datasources cached in their rightful place,
Fresh and existing split with care,
Policies update—no duplicates there,
Import flows swift through the testing square! ✨

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 8.82% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Description check ⚠️ Warning The PR description lacks critical implementation details, issue reference, and motivation despite being a medium-to-high complexity change affecting bulk action import caching. Add issue reference (Fixes #XXXX), explain the motivation/problem being solved, describe the solution approach, and list any breaking changes or dependencies.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title 'perf: bulk action import caching' directly relates to the main changes: adding bulk validation and insert methods with caching via BulkActionValidationContext.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch perf/import-improvement

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@sondermanish sondermanish added the ok-to-test Required label for CI label May 13, 2026
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (2)
app/server/appsmith-server/src/main/java/com/appsmith/server/newactions/importable/NewActionImportableServiceCEImpl.java (2)

426-427: 💤 Low value

Inaccurate comment - freshActionList is for insertions only.

The comment states "inserted or updated in bulk" but freshActionList contains only new actions for insertion. Update the comment to reflect this.

📝 Proposed fix
-                                                // Add it to actions list that'll be inserted or updated in bulk
+                                                // Add it to actions list that'll be inserted in bulk
                                                 freshActionList.add(updatedAction);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@app/server/appsmith-server/src/main/java/com/appsmith/server/newactions/importable/NewActionImportableServiceCEImpl.java`
around lines 426 - 427, The comment above freshActionList incorrectly says
"inserted or updated in bulk"; update it to accurately state that
freshActionList contains only newly created actions to be inserted in bulk (not
updates), e.g., clarify near the freshActionList.add(updatedAction) line that
this list is for new/insert-only actions so future readers know updates are
handled elsewhere.

386-388: 💤 Low value

Redundant method call - use already-extracted editActionDTO.

Line 361 already extracts editActionDTO = actionToImport.getUnpublishedAction(). Use editActionDTO.getDatasource() instead of calling actionToImport.getUnpublishedAction().getDatasource() again.

♻️ Proposed refactor
-                                    Datasource datasource = actionToImport
-                                            .getUnpublishedAction()
-                                            .getDatasource();
+                                    Datasource datasource = editActionDTO.getDatasource();
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@app/server/appsmith-server/src/main/java/com/appsmith/server/newactions/importable/NewActionImportableServiceCEImpl.java`
around lines 386 - 388, The code redundantly calls
actionToImport.getUnpublishedAction().getDatasource() even though editActionDTO
= actionToImport.getUnpublishedAction() was already extracted; replace the
redundant call by using editActionDTO.getDatasource() when assigning the
Datasource (e.g., change the Datasource datasource assignment to use
editActionDTO.getDatasource()), ensuring any references to the datasource in the
surrounding logic use that variable and remove the extra getUnpublishedAction()
invocation.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@app/server/appsmith-server/src/main/java/com/appsmith/server/newactions/base/NewActionServiceCEImpl.java`:
- Around line 327-329: The constraint messages from
validator.validate(actionConfig) are being added to invalids but
editActionDTO.isValid is never updated; modify the blocks that call
validator.validate(actionConfig) (e.g., where actionConfig is checked) so that
when validator.validate(...) returns any constraint violations you both add
their messages to invalids and set editActionDTO.setIsValid(false) (or the
appropriate setter) to mark the action invalid; apply the same change to the
other occurrence around the method handling lines similar to 585-588 so
malformed configurations are not treated as valid.
- Around line 344-355: The code currently reuses the DTO-local datasource when
editActionDTO.getDatasource().getId() != null, causing incomplete persisted
state to be used later; change that branch to resolve the persisted datasource
first (e.g., call datasourceService.findById(datasource.getId()) or similar
lookup) and then continue: set
newAction.setWorkspaceId(persistedDatasource.getWorkspaceId()) and pass the
persistedDatasource into collectDatasourcePolicyUpdateForPublicAction(newAction,
persistedDatasource, validationContext) so workspace propagation, jsonPathKeys
extraction, and updateDatasourcePoliciesForPublicActions() operate on the true
persisted object rather than the DTO.

In
`@app/server/appsmith-server/src/main/java/com/appsmith/server/newactions/importable/NewActionImportableServiceCEImpl.java`:
- Around line 356-357: Remove the unnecessary synchronized wrappers around the
lists: replace Collections.synchronizedList(new ArrayList<>()) for
freshActionList and existingNewActionList with plain ArrayList instances (e.g.,
new ArrayList<>()) in NewActionImportableServiceCEImpl where these lists are
populated inside the sequential flatMap and later used in the then(Mono.defer())
step; this removes needless locking overhead while preserving behavior because
the reactive pipeline processes elements sequentially by default.

---

Nitpick comments:
In
`@app/server/appsmith-server/src/main/java/com/appsmith/server/newactions/importable/NewActionImportableServiceCEImpl.java`:
- Around line 426-427: The comment above freshActionList incorrectly says
"inserted or updated in bulk"; update it to accurately state that
freshActionList contains only newly created actions to be inserted in bulk (not
updates), e.g., clarify near the freshActionList.add(updatedAction) line that
this list is for new/insert-only actions so future readers know updates are
handled elsewhere.
- Around line 386-388: The code redundantly calls
actionToImport.getUnpublishedAction().getDatasource() even though editActionDTO
= actionToImport.getUnpublishedAction() was already extracted; replace the
redundant call by using editActionDTO.getDatasource() when assigning the
Datasource (e.g., change the Datasource datasource assignment to use
editActionDTO.getDatasource()), ensuring any references to the datasource in the
surrounding logic use that variable and remove the extra getUnpublishedAction()
invocation.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: e8e97d22-b489-4ab1-a620-e3f0408ebbd8

📥 Commits

Reviewing files that changed from the base of the PR and between 02440f3 and 1fc6461.

📒 Files selected for processing (4)
  • app/server/appsmith-server/src/main/java/com/appsmith/server/newactions/base/NewActionServiceCE.java
  • app/server/appsmith-server/src/main/java/com/appsmith/server/newactions/base/NewActionServiceCEImpl.java
  • app/server/appsmith-server/src/main/java/com/appsmith/server/newactions/importable/NewActionImportableServiceCEImpl.java
  • app/server/appsmith-server/src/test/java/com/appsmith/server/services/ce/NewActionServiceTest.java

Comment on lines +327 to +329
if (actionConfig != null) {
validator.validate(actionConfig).stream().forEach(x -> invalids.add(x.getMessage()));
}
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot May 13, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Mark the action invalid when ActionConfiguration bean validation fails.

These blocks add constraint messages to invalids, but editActionDTO.isValid stays true. That lets malformed configurations flow through both the import and regular validation paths as “valid” actions.

Possible fix
-        if (actionConfig != null) {
-            validator.validate(actionConfig).stream().forEach(x -> invalids.add(x.getMessage()));
-        }
+        if (actionConfig != null) {
+            var violations = validator.validate(actionConfig);
+            if (!violations.isEmpty()) {
+                editActionDTO.setIsValid(false);
+                violations.forEach(x -> invalids.add(x.getMessage()));
+            }
+        }

Also applies to: 585-588

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@app/server/appsmith-server/src/main/java/com/appsmith/server/newactions/base/NewActionServiceCEImpl.java`
around lines 327 - 329, The constraint messages from
validator.validate(actionConfig) are being added to invalids but
editActionDTO.isValid is never updated; modify the blocks that call
validator.validate(actionConfig) (e.g., where actionConfig is checked) so that
when validator.validate(...) returns any constraint violations you both add
their messages to invalids and set editActionDTO.setIsValid(false) (or the
appropriate setter) to mark the action invalid; apply the same change to the
other occurrence around the method handling lines similar to 585-588 so
malformed configurations are not treated as valid.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@coderabbitai, I'm not sure that we do make the config invalid after validationContrstraints fail, I can take this up later, but can you check validateAction method in the same file, basically this is a copy of that

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@sondermanish — confirmed. The validateAction(NewAction, boolean isDryOps) method has the exact same pattern:

ActionConfiguration actionConfig = editActionDTO.getActionConfiguration();
if (actionConfig != null) {
    validator.validate(actionConfig).stream().forEach(x -> invalids.add(x.getMessage()));
}

No setIsValid(false) there either, so validateActionBeforeImport faithfully mirrors the pre-existing behavior. The gap exists in both paths.

Since you want to address this later, would you like me to open a follow-up GitHub issue to track the fix for both validateAction and validateActionBeforeImport?


🧠 Learnings used
Learnt from: subrata71
Repo: appsmithorg/appsmith PR: 41684
File: app/server/appsmith-server/src/main/java/com/appsmith/server/helpers/GitUtils.java:93-97
Timestamp: 2026-04-07T19:40:13.566Z
Learning: For SSRF mitigations involving SSH/Git URL handling, use a dual-validation strategy: validate the target hostname before initiating any network connection (e.g., via a URL/host validator like `validateGitSshUrl`), and then validate the resolved remote IP address after the TCP handshake/connection is established (e.g., in a session factory or connection accept path like `accept()`). Do not rely on a single pre-connection hostname check alone, since DNS rebinding can change the destination between validation and connect. If the underlying library (e.g., JGit) does not allow injecting a pre-resolved `InetAddress`, implement the post-connection IP validation as the second gate to complete the mitigation.

Comment on lines +344 to +355
Mono<Datasource> datasourceMono = Mono.just(editActionDTO.getDatasource());
if (editActionDTO.getPluginType() != PluginType.JS) {
if (editActionDTO.getDatasource().getId() == null) {
datasourceMono = datasourceMono.flatMap(datasourceService::validateDatasource);
} else {
datasourceMono = datasourceMono
.map(datasource -> {
newAction.setWorkspaceId(datasource.getWorkspaceId());
return datasource;
})
.flatMap(datasource -> collectDatasourcePolicyUpdateForPublicAction(
newAction, datasource, validationContext));
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot May 13, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

Resolve datasource IDs to the persisted datasource before reusing them.

When datasource.id is present, this branch keeps using the DTO-local datasource object. The later import flow uses that same object for workspace propagation, datasource-key extraction, and public-policy saves, so a reference-shaped datasource can end up producing incomplete jsonPathKeys or, worse, get partially written back in updateDatasourcePoliciesForPublicActions().

Possible fix
+    private Mono<Datasource> getDatasourceById(
+            String datasourceId, BulkActionValidationContext validationContext) {
+        return validationContext.datasourceById.computeIfAbsent(
+                datasourceId,
+                id -> datasourceService
+                        .findById(id)
+                        .switchIfEmpty(Mono.error(
+                                new AppsmithException(AppsmithError.NO_RESOURCE_FOUND, FieldName.DATASOURCE, id)))
+                        .cache());
+    }
+
...
-                } else {
-                    datasourceMono = datasourceMono
-                            .map(datasource -> {
-                                newAction.setWorkspaceId(datasource.getWorkspaceId());
-                                return datasource;
-                            })
-                            .flatMap(datasource -> collectDatasourcePolicyUpdateForPublicAction(
-                                    newAction, datasource, validationContext));
+                } else {
+                    datasourceMono = getDatasourceById(
+                                    editActionDTO.getDatasource().getId(), validationContext)
+                            .map(datasource -> {
+                                newAction.setWorkspaceId(datasource.getWorkspaceId());
+                                return datasource;
+                            })
+                            .flatMap(datasource -> collectDatasourcePolicyUpdateForPublicAction(
+                                    newAction, datasource, validationContext));
                 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@app/server/appsmith-server/src/main/java/com/appsmith/server/newactions/base/NewActionServiceCEImpl.java`
around lines 344 - 355, The code currently reuses the DTO-local datasource when
editActionDTO.getDatasource().getId() != null, causing incomplete persisted
state to be used later; change that branch to resolve the persisted datasource
first (e.g., call datasourceService.findById(datasource.getId()) or similar
lookup) and then continue: set
newAction.setWorkspaceId(persistedDatasource.getWorkspaceId()) and pass the
persistedDatasource into collectDatasourcePolicyUpdateForPublicAction(newAction,
persistedDatasource, validationContext) so workspace propagation, jsonPathKeys
extraction, and updateDatasourcePoliciesForPublicActions() operate on the true
persisted object rather than the DTO.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@coderabbitai if you take a look in newActionimportableserviceCEImpl you would find that we already sanitise the actions from DB stored while importing datasource

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧩 Analysis chain

Let me check NewActionImportableServiceCEImpl to see the datasource sanitization during import.


🏁 Script executed:

#!/bin/bash
# Find and display the relevant datasource sanitization logic in NewActionImportableServiceCEImpl
fd "NewActionImportableServiceCEImpl.java" --exec cat {}

Length of output: 35921


@sondermanish You're right. Looking at updateDatasourceInAction() in NewActionImportableServiceCEImpl:

if (StringUtils.hasText(newAction.getUnpublishedAction().getDatasource().getId())) {
    final String datasourceId = newAction.getUnpublishedAction().getDatasource().getId();
    Map<String, Datasource> datasourceMap = mappedImportableResourcesDTO.getIdToDatasourceMap();
    if (datasourceMap.containsKey(datasourceId)) {
        datasource = datasourceMap.get(datasourceId);
    }
    datasource.setIsAutoGenerated(false);
    newAction.setWorkspaceId(datasource.getWorkspaceId());
}
newAction.getUnpublishedAction().setDatasource(datasource);

This resolves the full persisted datasource from idToDatasourceMap and sets it on the action before validateActionsBeforeImport is ever called. My concern about the DTO-local stub was incorrect — the datasource is already fully hydrated at that point. I'll retract the comment.

( ˘ ³˘)♥ 🐇


✏️ Learnings added
Learnt from: sondermanish
Repo: appsmithorg/appsmith PR: 41807
File: app/server/appsmith-server/src/main/java/com/appsmith/server/newactions/base/NewActionServiceCEImpl.java:344-355
Timestamp: 2026-05-14T07:42:54.629Z
Learning: In `NewActionImportableServiceCEImpl` (appsmithorg/appsmith), `updateDatasourceInAction()` is called for each action before `validateActionsBeforeImport()`. It resolves the full persisted datasource from `mappedImportableResourcesDTO.getIdToDatasourceMap()` using the datasource ID, sets `workspaceId` on the action, and assigns the full datasource object onto the action DTO. As a result, by the time `validateActionsBeforeImport` / `validateActionBeforeImport` runs in `NewActionServiceCEImpl`, the action already carries the complete persisted datasource — not a reference-shaped stub. Concerns about incomplete `jsonPathKeys` or partial writes due to DTO-local datasource reuse in this flow are not valid.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.


🧠 Learnings used
Learnt from: subrata71
Repo: appsmithorg/appsmith PR: 41684
File: app/server/appsmith-server/src/main/java/com/appsmith/server/helpers/GitUtils.java:93-97
Timestamp: 2026-04-07T19:40:13.566Z
Learning: For SSRF mitigations involving SSH/Git URL handling, use a dual-validation strategy: validate the target hostname before initiating any network connection (e.g., via a URL/host validator like `validateGitSshUrl`), and then validate the resolved remote IP address after the TCP handshake/connection is established (e.g., in a session factory or connection accept path like `accept()`). Do not rely on a single pre-connection hostname check alone, since DNS rebinding can change the destination between validation and connect. If the underlying library (e.g., JGit) does not allow injecting a pre-resolved `InetAddress`, implement the post-connection IP validation as the second gate to complete the mitigation.

Comment on lines +356 to 357
List<NewAction> freshActionList = Collections.synchronizedList(new ArrayList<>());
List<NewAction> existingNewActionList = Collections.synchronizedList(new ArrayList<>());
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check for flatMap with concurrency parameter in similar import services
rg -nP --type=java -C3 'flatMap\([^,]+,\s*\d+\)' --glob '*Importable*.java'

# Check for any documentation or comments about concurrency in action import
rg -nP --type=java -C2 'concurrent|parallel|thread-safe' app/server/appsmith-server/src/main/java/com/appsmith/server/newactions/importable/

Repository: appsmithorg/appsmith

Length of output: 1105


🏁 Script executed:

# Find the method containing lines 356-357 and check how freshActionList and existingNewActionList are used
rg -nP --type=java 'freshActionList|existingNewActionList' app/server/appsmith-server/src/main/java/com/appsmith/server/newactions/importable/NewActionImportableServiceCEImpl.java -A2 -B2

# Also check if this method is called with concurrency concerns
rg -nP --type=java 'NewActionImportableServiceCEImpl' --glob '*.java' -l | head -5

Repository: appsmithorg/appsmith

Length of output: 2229


🏁 Script executed:

# Read the method containing the synchronized lists (approximately lines 356-450)
sed -n '356,450p' app/server/appsmith-server/src/main/java/com/appsmith/server/newactions/importable/NewActionImportableServiceCEImpl.java

Repository: appsmithorg/appsmith

Length of output: 6666


Remove unnecessary synchronization; the pipeline executes sequentially.

The flatMap operator without an explicit concurrency parameter processes elements sequentially (concurrency=1 by default). Items are added to freshActionList and existingNewActionList only from within the sequential flatMap closure, then passed downstream via then(Mono.defer()) after all processing completes. Synchronized lists add locking overhead without preventing any actual concurrent access. In reactive streams, avoid synchronized collections as locks can block scheduler threads and degrade performance.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@app/server/appsmith-server/src/main/java/com/appsmith/server/newactions/importable/NewActionImportableServiceCEImpl.java`
around lines 356 - 357, Remove the unnecessary synchronized wrappers around the
lists: replace Collections.synchronizedList(new ArrayList<>()) for
freshActionList and existingNewActionList with plain ArrayList instances (e.g.,
new ArrayList<>()) in NewActionImportableServiceCEImpl where these lists are
populated inside the sequential flatMap and later used in the then(Mono.defer())
step; this removes needless locking overhead while preserving behavior because
the reactive pipeline processes elements sequentially by default.

Copy link
Copy Markdown
Collaborator

@subrata71 subrata71 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The changes look directionally correct. If we have a way to measure the performance improvement then it could have been great.

Mono<NewAction> validateAction(NewAction newAction, boolean isDryOps);

Mono<Tuple2<List<NewAction>, List<NewAction>>> validateActionsBeforeImport(
String artifactId, List<NewAction> freshActions, List<NewAction> existingActions);
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: What does freshActions mean here? Does it refer only to the new actions from the incoming list that will be inserted, or does it refer to the entire incoming actions list?

@sondermanish sondermanish merged commit 6c8fab7 into release May 15, 2026
48 checks passed
@sondermanish sondermanish deleted the perf/import-improvement branch May 15, 2026 06:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ok-to-test Required label for CI

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants