Skip to content

Performance: repeated .find() calls cause O(n²) when building app location options in packages/app-store/server.ts #29959

Description

@Param141

Issue Summary
While building location options in packages/app-store/server.ts, the code iterates integrations and then checks for duplicates using array.find(...) on the per-category options array. That duplicate-check is done inside the loop and becomes a repeated O(n) scan, producing O(n²) behavior when many apps/credentials exist. This is a performance issue for environments with many installed apps/credentials.

Steps to Reproduce

  1. Run the app locally or use a dev/staging environment of calcom/cal.diy.
  2. Populate the database with many app credentials (e.g., >100 credentials across apps). You can seed or create many credentials via the App Store UI or DB seeds.
  3. Trigger the server flow that returns location options (the function that builds locations in packages/app-store/server.ts). This is the server code that aggregates apps into categories and returns the location options.
  4. Observe the endpoint latency or CPU while building the locations response.
    (If you cannot seed many credentials, you can reproduce logically by mocking integrations with many credentials and calling the same function locally.)

Actual Results
The code uses apps[groupByCategory].find((o) => o.value === option.value) inside a loop to detect duplicates. For N options this yields repeated linear scans producing O(n²) behavior and increased CPU / latency under larger N.

Expected Results
The code should deduplicate options using O(1) membership checks (e.g., a Set or Map maintained per category) so the overall cost is O(n). Output should remain identical, only performance improved.

Technical details
File: packages/app-store/server.ts
Link: https://github.com/calcom/cal.diy/blob/main/packages/app-store/server.ts

Code example (existing pattern):
TypeScript
if (apps[groupByCategory]) {
const existingOption = apps[groupByCategory].find((o) => o.value === option.value);
if (!existingOption) {
apps[groupByCategory] = [...apps[groupByCategory], option];
}
} else {
apps[groupByCategory] = [option];
}

Suggested fix: maintain a per-category Set of seen option values (or an appsByCategory map that stores both options array and a seen Set). When adding an option, check seen.has(option.value) and only push/persist when not seen; add to seen afterwards.

Impact: converts repeated O(n) checks to O(1) membership tests; reduces worst-case operations from O(n²) to O(n) and improves latency/CPU when many apps/credentials are present.
Environment: reproducible locally; server-side change only (packages/app-store).

Evidence

  • Observed repeated .find() use in code and other similar patterns in repo (e.g., typedInput.options?.find(...) in payment handling). No stack trace; this is a static-performance anti-pattern that shows up when inputs grow large.

Metadata

Metadata

Assignees

No one assigned

    Labels

    🐛 bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions