Commit Graph

27430 Commits

Author SHA1 Message Date
Fredrik Adelöw fbae43b4d0 catalog-backend: use index-only GROUP BY + LATERAL for dedup
Replace the window-function full-table scan with a two-phase approach
that leverages search_key_value_entity_idx (key, value, entity_id):

  Phase 1: GROUP BY entity_id, key, value with HAVING COUNT(*) > 1
           resolves as a pure index-only scan (Heap Fetches: 0).
           Stores only the duplicate groups in a temp table (~8s for
           a 14M-row table with 700k dupes).

  Phase 2: CROSS JOIN LATERAL back into search using the same covering
           index (Nested Loop + Index Scan). row_number() runs per-group
           over the 2-3 matching rows, so there is no global external
           sort. A single DELETE removes all extras in one statement
           (~16s).

NULL values get a separate UNION ALL arm so the index equality
condition stays usable (value = NULL is always false in SQL).

Benchmarked on a 14.4M-row production-like staging master with
700k injected duplicates, post-VACUUM:
  Old (seq scan + external merge sort): ~101s
  New (index-only + index scan):         ~25s  (~4× faster)

Clean second run (no dupes, fast path skips dedup): <50ms.

Signed-off-by: Fredrik Adelöw <freben@gmail.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-11 18:27:11 +02:00
Fredrik Adelöw c7706249ba fix(catalog-backend): use explicit null sentinel in dedup keys and add buildEntitySearch dedup tests
Use `=== null ? '\x01' : value` instead of `?? ''` in both buildEntitySearch
and syncSearchRows dedup maps, so that null and empty-string values are
treated as distinct keys. In theory an entity could produce both value=null
and value='' for the same key (e.g. spec.foo: [null, '']), and the old
encoding would silently drop one of the two distinct rows.

Also adds two focused unit tests to buildEntitySearch.test.ts: one covering
deduplication of duplicate array values (e.g. tags: ['java', 'java', 'Java']),
and one confirming that null and empty-string are kept as separate rows.

Signed-off-by: Fredrik Adelöw <freben@gmail.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-11 17:07:49 +02:00
Fredrik Adelöw 36514a23d6 fix(catalog-backend): use two-phase dedup in search migration to avoid full-table re-scans
The original single-pass CTE window-function DELETE re-scanned every row
on each invocation — including the idempotent "nothing to do" case which
took over 4 minutes on a 14 M-row table, and timed out the DB proxy on
the dirty case (~217 s for 722 k duplicates).

Replace it with two improvements:

1. Fast path: if the UNIQUE index already exists and is valid, skip dedup
   entirely. A valid unique index is a proof-of-no-duplicates. This makes
   migration startup essentially free for installations that prepare the
   index manually beforehand (as documented in the migration comment).

2. Two-phase dedup when dedup is needed: Phase 1 does one full-table scan
   to collect all duplicate ctids into a temp table (~60 s on 14 M rows).
   Phase 2 drains that temp table in 10 k-row batches via cheap ctid
   lookups with no further full-table scans (~5 s for 722 k rows). Total
   ~65 s dirty vs >217 s+ before, and ~35 ms clean vs 4+ minutes before.

Signed-off-by: Fredrik Adelöw <freben@gmail.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-11 16:52:44 +02:00
Fredrik Adelöw f503fc935b fix(catalog-backend): scope pg_class lookups to relkind='i' in migration helpers
Prevents a false match if any non-index object (table, sequence, view)
shares a name with one of the search indices.

Signed-off-by: Fredrik Adelöw <freben@spotify.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-11 13:42:06 +02:00
Fredrik Adelöw 63036124cc test(catalog-backend): add ON CONFLICT DO UPDATE coverage for original_value overwrite
Verifies that a conflicting insert with a different original_value casing
updates the stored value, and documents that DO UPDATE requires explicit
conflict columns unlike DO NOTHING.

Signed-off-by: Fredrik Adelöw <freben@spotify.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-11 13:33:01 +02:00
Fredrik Adelöw 930c575e58 test(catalog-backend): simplify onConflict().ignore() and add UNIQUE constraint test
Remove the explicit column list from .onConflict() calls in test setup —
ON CONFLICT DO NOTHING without a target is equivalent since (entity_id,
key, value) is the only unique constraint on the search table.

Add a dedicated test in syncSearchRows that directly inserts a duplicate
row and verifies it is silently rejected via the UNIQUE constraint.

Signed-off-by: Fredrik Adelöw <freben@spotify.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-11 12:55:10 +02:00
Fredrik Adelöw bb6c46adc0 fix(catalog-backend): create replacement indices before dropping in down migration
Avoids a window with no coverage on (key, value) during rollback.

Signed-off-by: Fredrik Adelöw <freben@spotify.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-11 12:50:02 +02:00
Fredrik Adelöw c8efd9dda5 clarify death loop risk in migration comments and changeset
Be explicit that interrupted index builds do not leave partial progress —
each retry starts from scratch. On large tables with short liveness
probe timeouts this repeats indefinitely. Recommend running the SQL
commands manually before deploying.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Signed-off-by: Fredrik Adelöw <freben@spotify.com>
2026-05-10 19:16:32 +02:00
Fredrik Adelöw 27d2d3f0a2 fix down migration + update SQL report
The down migration now restores the previous index state (drops new
indices, recreates the old search_key_value_idx and
search_key_original_value_idx). Updated the SQL report to reflect the
new index set.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Signed-off-by: Fredrik Adelöw <freben@spotify.com>
2026-05-10 14:21:19 +02:00
Fredrik Adelöw 7445f0f5bc draft(catalog-backend): search table dedup, covering indices, UNIQUE constraint
Single knex migration that cleans up duplicate search rows and creates
covering indices including a UNIQUE constraint on (entity_id, key, value).
Each step is idempotent and handles INVALID indices from interrupted runs.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Signed-off-by: Fredrik Adelöw <freben@spotify.com>
2026-05-10 14:16:03 +02:00
Fredrik Adelöw 95f31b18ec Merge pull request #34160 from backstage/freben/catalog-metrics-cache
fix(catalog-backend): cache and cheapen catalog_entities_count metric
2026-05-08 14:51:08 +03:00
Fredrik Adelöw 8f7f591c46 coalesce overlapping callers via single-flight in-flight promise
Hold a shared promise rather than just a resolved value. Concurrent
callers awaiting a fresh count get the same in-flight promise back, so
the underlying query is never overlapped by a duplicate. The TTL is now
the minimum gap between the resolution of one query and the start of
the next, rather than a hard bound on cache age.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Signed-off-by: Fredrik Adelöw <freben@spotify.com>
2026-05-08 11:55:26 +02:00
Fredrik Adelöw ccbad9d892 fix(catalog-backend): cache and cheapen catalog_entities_count metric
The legacy Prometheus and OpenTelemetry observable gauges previously each
ran the per-kind count query against the search table on every metrics
scrape. With multiple pods and short scrape intervals, identical
sequential scans piled up faster than they completed, contending for
buffers in the database.

Extract a shared helper that wraps a 30-second TTL cache around a single
query, and have both gauges read from it. The query itself moves from
the (large) search table to final_entities, parsing kind out of
entity_ref via per-engine substring functions. The emitted labels and
values are unchanged.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Signed-off-by: Fredrik Adelöw <freben@spotify.com>
2026-05-08 11:48:58 +02:00
Fredrik Adelöw 3f55b73e32 fix(catalog-backend): use INNER JOIN for filtered entity facets
Replace the `WHERE search.entity_id IN (...)` form with an INNER JOIN
against the filtered final_entities subquery in DefaultEntitiesCatalog#facets.
Results are unchanged; the planner gets more freedom to pick cheaper plans,
which on large catalogs leads to substantial speedups (1.2× to 7×+ in
adversarial testing on a ~13.8M-row search table) and avoids the
materialize-then-spill pattern of the IN form.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Signed-off-by: Fredrik Adelöw <freben@spotify.com>
2026-05-08 11:17:08 +02:00
Fredrik Adelöw 213c6807d9 Merge pull request #33991 from jtbry/master
fix(SecureTemplater): return dispose function to clean up secure temp…
2026-05-07 17:15:23 +03:00
Fredrik Adelöw 5ebd1a14a0 plugin-app: fix param substitution to use word boundary
Use a word-boundary regex (:name\b) instead of a plain string replace
so that a shorter param like :a doesn't corrupt a longer param :ab
when both are present in the route.

Signed-off-by: Patrik Oldsberg <rugvip@backstage.io>
Signed-off-by: Fredrik Adelöw <freben@spotify.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-07 14:03:07 +02:00
Fredrik Adelöw a3458208a5 plugin-app: substitute path params in app/routes redirect targets
The app/routes redirect config now performs the same :param and *
substitution that the legacy Redirect component did before navigating.
Named params captured by the `from` pattern are replaced in the `to`
string, enabling redirects like /users/:userId → /profile/:userId and
/old-docs → /docs/* (with splat forwarding).

Adds tests for both named-param and splat substitution.

Signed-off-by: Patrik Oldsberg <rugvip@backstage.io>
Signed-off-by: Fredrik Adelöw <freben@spotify.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-07 13:57:23 +02:00
Andre Wanlin fe697792c5 Merge pull request #34105 from awanlin/topic/non-breaking-typos
Fixes for non-breaking typos and typos configuration
2026-05-06 13:44:11 -05:00
Justin Bryant 964bc8694f fix: run prettier
Signed-off-by: Justin Bryant <justintbry@gmail.com>
2026-05-06 10:04:32 -04:00
Justin Bryant 4bcbd8245b Merge branch 'master' into master
Signed-off-by: Justin Bryant <justintbry@gmail.com>
2026-05-06 09:49:24 -04:00
Rickard Dybeck e68cb8ac0f feat(kubernetes-react): add optional getClusters() cache to KubernetesBackendClient (#34136)
fix(kubernetes-react): coalesce concurrent getClusters() fetches and validate TTL input

Signed-off-by: Rickard Dybeck <dybeck@spotify.com>
2026-05-06 09:16:24 -04:00
Fredrik Adelöw a590d4ad96 Merge pull request #34130 from backstage/codex/turtle395-improve-mcp-auth-dialog
Show MCP client metadata in auth consent dialog
2026-05-06 14:56:34 +03:00
Djam 9d6edc0622 Potential fix for pull request finding
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Signed-off-by: Djam <rdjamaile@gmail.com>
2026-05-06 13:02:43 +02:00
djamaile 4f62755eed feat(auth): TURTLE-395: show MCP client metadata
Signed-off-by: djamaile <rdjamaile@gail.com>
2026-05-06 11:19:17 +02:00
github-actions[bot] b0bc1e5cc9 Version Packages (next) 2026-05-05 14:57:07 +00:00
Fredrik Adelöw f3aebf577d Merge pull request #34053 from sriharsha9618/msgraph-incremental
feat(catalog): add plugin-catalog-backend-module-msgraph-incremental
2026-05-05 16:21:37 +03:00
Andre Wanlin 50758120ce Merge pull request #31010 from drodil/search_action
feat(search): add actions to query search engine
2026-05-05 06:04:06 -05:00
Andre Wanlin 5e53254fe0 Merge pull request #33370 from rtar/master
fix(plugin-techdocs-node): Move docs directory validation to after copying README.md
2026-05-05 06:02:44 -05:00
Andre Wanlin bb7febddd6 Merge pull request #33252 from AdityaK60/devtools-bui-config
Migrate ConfigContent Component to Backstage UI
2026-04-30 07:02:45 -05:00
Ruslans Tarasovs a27a24fd5a Implemented a fix preventing to write outside of current directory
Signed-off-by: Ruslans Tarasovs <ruslan@tarasovs.com>
2026-04-30 13:09:39 +03:00
Ruslans Tarasovs 47c1ca0613 Add a test checking that it is not possible to write outside of a current directory
Signed-off-by: Ruslans Tarasovs <ruslan@tarasovs.com>
2026-04-30 13:09:39 +03:00
Ruslans Tarasovs 0db7b1b163 Removed accidential console.log
Signed-off-by: Ruslans Tarasovs <ruslan@tarasovs.com>
2026-04-30 13:09:39 +03:00
Ruslans Tarasovs a30dbc500b Made the test more generic
Signed-off-by: Ruslans Tarasovs <ruslan@tarasovs.com>
2026-04-30 13:09:39 +03:00
Ruslans Tarasovs 5d36b961e3 Validate that symlink to README.md does not escape the directory
Signed-off-by: Ruslans Tarasovs <ruslan@tarasovs.com>
2026-04-30 13:09:39 +03:00
Ruslans Tarasovs 6ce84626ab Move docs directory validation to after copying README.md
Signed-off-by: Ruslans Tarasovs <ruslan@tarasovs.com>
2026-04-30 13:09:38 +03:00
Andre Wanlin 2f33a9f63f Fixes for non-breaking typos and typos configuration
Signed-off-by: Andre Wanlin <awanlin@spotify.com>

More

Signed-off-by: Andre Wanlin <awanlin@spotify.com>
2026-04-29 16:54:05 -05:00
Andre Wanlin 84913005fd Merge pull request #31838 from karthikjeeyar/mkdocs-patch
feat(techdocs): add app-config option to disable external font download
2026-04-29 12:47:12 -05:00
Ben Lambert a0f58971a7 chore: fix issue with types (#34104)
Signed-off-by: benjdlambert <ben@blam.sh>
2026-04-29 15:50:39 +00:00
Ben Lambert fa06df607e Merge commit from fork
Signed-off-by: Benjamin Lambert <benjdlambert@gmail.com>
Signed-off-by: benjdlambert <ben@blam.sh>
2026-04-29 15:51:19 +02:00
Karthik 610b42f72f update documentation and improve types
Signed-off-by: Karthik <karthik.jk11@gmail.com>
2026-04-29 00:19:03 +05:30
github-actions[bot] 7295193bb6 Version Packages (next) 2026-04-28 15:53:09 +00:00
pillaris fec21115a3 revert: keep alpha entrypoint empty per maintainer guidance
Net-new package does not need alpha exports per awanlin's review.
Revert alpha.ts to export {}, remove default alias from module/index.ts,
and restore empty report-alpha.api.md.

Signed-off-by: pillaris <pillaris@adobe.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-28 15:09:26 +05:30
Ferin Patel 07e08beac1 feat: Add status check functions for scaffolder steps (#32890)
* feat: Add status check functions for scaffolder steps

- Introduced `always()` and `failure()` functions to control step execution after failures.
- Updated documentation to explain usage of new status check functions.
- Enhanced NunjucksWorkflowRunner to process these functions in step conditions.
- Added tests to verify behavior of steps using `always()` and `failure()`.

Signed-off-by: ferin79 <ferinpatel79@gmail.com>

* feat: Enhance status check functions in scaffolder steps

- Updated documentation to clarify usage of status check functions with template expressions.
- Modified tests to reflect changes in syntax for status checks.
- Refactored NunjucksWorkflowRunner to ensure proper handling of status check functions in step conditions.

Signed-off-by: ferin79 <ferinpatel79@gmail.com>

* docs: Clarify usage of status check functions in writing templates

- Removed redundant explanation about truthy conditions after step failure.
- Streamlined the description for better clarity on status check functions.

Signed-off-by: ferin79 <ferinpatel79@gmail.com>

---------

Signed-off-by: ferin79 <ferinpatel79@gmail.com>
2026-04-28 11:16:59 +02:00
pillaris 3a0dfd0031 fix: export BackendFeature from alpha entrypoint and regenerate API report
Re-export catalogModuleMicrosoftGraphIncrementalEntityProvider as @alpha
default from src/alpha.ts, matching the pattern of catalog-backend-module-msgraph.
Add default export alias in module/index.ts and regenerate report-alpha.api.md.

Signed-off-by: pillaris <pillaris@adobe.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-28 11:07:45 +05:30
pillaris fbc2c76d39 fix: update alpha API report to match API Extractor output
The manually-written empty report-alpha.api.md didn't match what API
Extractor generates for an empty alpha entry point. Use the correct
generated output to pass the CI api-reports check.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: pillaris <pillaris@adobe.com>
2026-04-28 10:14:28 +05:30
pillaris ff199fef07 fix: address Andre's review comments
- Update copyright year from 2024 to 2026 across all new source files
- Clear alpha entry point (no alpha exports needed for a net-new package)
  and regenerate report-alpha.api.md accordingly
- Delete CHANGELOG.md (auto-generated by the release process)
- Change changeset bump from patch to minor so the first release is 0.1.0,
  and remove the redundant "New package:" title line
- Reset package version to 0.0.0 (release process sets the real version)
- Add incremental ingestion section to docs/integrations/azure/org.md

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: pillaris <pillaris@adobe.com>
2026-04-28 09:54:40 +05:30
pillaris 1b5e83401f fix: wrap child group transformer in try/catch to keep burst resilient
A throwing custom groupTransformer on a child group member would fail
the entire groups-phase burst. Apply the same try/catch + debug/warn
logging pattern already used for user member transformation.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: pillaris <pillaris@adobe.com>
2026-04-27 15:43:33 +05:30
pillaris 69cf79ea00 chore: fix prettier formatting in provider test file
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: pillaris <pillaris@adobe.com>
2026-04-27 15:28:39 +05:30
pillaris e1714a861c fix: guard user.id before photo fetch, warn on groupIncludeSubGroups, skip dangling child refs
- Guard getUserPhotoGated with user.id presence to prevent requesting
  users/undefined/photo when Graph omits the id field
- Log a warning when groupIncludeSubGroups is configured, matching the
  existing warning for unsupported userGroupMember* options
- Skip spec.children population when groupFilter/groupSearch is active,
  since child groups may not pass the filter and would create dangling
  references in the catalog

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: pillaris <pillaris@adobe.com>
2026-04-27 15:10:25 +05:30
pillaris 9e9eca49c7 docs: fix page-size docs and note groupIncludeSubGroups unsupported
Update all references to "up to 999 items" to accurately reflect that
users are fetched in pages of 999 while groups use a smaller page of
100. Also document groupIncludeSubGroups as unsupported in the JSDoc
@remarks, README, and comparison table.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: pillaris <pillaris@adobe.com>
2026-04-27 14:44:07 +05:30