Skip to content

BE-460, BE-461: Add user deletion endpoint to admin API#8530

Open
TimDiekmann wants to merge 11 commits intomainfrom
t/h-460-user-deletion-endpoint
Open

BE-460, BE-461: Add user deletion endpoint to admin API#8530
TimDiekmann wants to merge 11 commits intomainfrom
t/h-460-user-deletion-endpoint

Conversation

@TimDiekmann
Copy link
Member

@TimDiekmann TimDiekmann commented Mar 11, 2026

🌟 What is the purpose of this PR?

Adds a user deletion endpoint to the Graph admin API. When invoked, it purges all entities in the user's personal web, deletes their Kratos identity (PII removal), revokes Hydra OAuth sessions, and removes email subscriptions — while preserving principals and policies for structural integrity.

Also makes entity deletion temporal axes configurable, adds the LinkDeletionBehavior::Archive variant for archiving incoming links during purge, and significantly expands deletion test coverage.

🔗 Related links

🔍 What does this change?

User deletion orchestration

  • New POST /users/delete admin endpoint accepting {"userId": "..."} or {"email": "..."}
  • Orchestration in hash-graph-store::user_deletion::delete_user() — generic over store + external service traits
  • Fatal steps (user lookup, entity purge) cause Err return; non-fatal steps (Kratos, Hydra, Mailchimp) collected via ReportSink into UserDeletionOutcome::errors with full error-stack context
  • HTTP response message differentiates between full success and partial failure

External service integrations

  • IdentityProvider trait + KratosIdentityProvider — deletes Kratos identity (PII removal)
  • OAuthProvider trait + HydraOAuthProvider — revokes login and consent sessions
  • EmailSubscriptionProvider trait + MailchimpSubscriptionProvider — permanently deletes subscribers via Mailchimp Marketing API

Entity deletion enhancements

  • DeleteEntitiesParams now accepts QueryTemporalAxesUnresolved for configurable entity finding
  • LinkDeletionBehavior::Archive — temporally archives incoming link entities before purge instead of rejecting or ignoring them. Uses an atomic CTE that closes decision_time, creates a historical row, and sets archivedById provenance
  • FOR UPDATE locking on entity_ids during erase to close the TOCTOU gap with concurrent link creation
  • New AccountStore methods: get_user_kratos_identity_id, get_user_emails, get_user_id_by_email

Configuration

  • Mailchimp clap requires constraints are now bidirectional (api_key requires list_id)
  • External services config (Kratos URL, Hydra URL, Mailchimp) wired into admin server

Pre-Merge Checklist 🚀

🚢 Has this modified a publishable library?

This PR:

  • does not modify any publishable blocks or libraries, or modifications do not need publishing

📜 Does this require a change to the docs?

The changes in this PR:

🕸️ Does this require a change to the Turbo Graph?

The changes in this PR:

  • do not affect the execution graph

⚠️ Known issues

  • Orphaned tombstones after Purge+Erase (BE-466): entity_ids tombstones from prior Purge operations are invisible to subsequent Erase because SelectCompiler requires entity_temporal_metadata rows. A regression test exists but is #[ignore]d.

🐾 Next steps

  • BE-463: Write operational runbook for user deletion
  • BE-466: Fix orphaned tombstone cleanup
  • Unit tests for delete_user() orchestrator with mocked dependencies

🛡 What tests cover this?

  • 65 Rust integration tests in libs/@local/graph/postgres-store/tests/deletion/ covering purge, erase, archive link behavior, draft handling, temporal axes validation, cross-web batches, and edge cases (self-loops, chains, idempotency)
  • Backend integration tests in user.test.ts covering end-to-end user deletion (by ID and email), org entity survival, link archival provenance, and Kratos identity deletion
  • Admin server tests in admin-server.ts for the deleteUser and resetGraph helpers
  • HTTP test in reset-database.http updated with temporal axes

❓ How to test this?

  1. Start the admin server: cargo run --bin hash-graph --all-features -- admin-server --kratos-admin-url http://localhost:4434 --hydra-admin-url http://localhost:4445
  2. Create a test user via the regular API
  3. Delete via admin API:
    curl -X POST http://localhost:4001/users/delete \
      -H "Content-Type: application/json" \
      -H "X-Authenticated-User-Actor-Id: 00000000-0000-0000-0000-000000000000" \
      -d '{"email": "user@example.com"}'
  4. Verify the response includes UserDeletionReport with entity counts and service status booleans
  5. Run Rust tests: cargo nextest run --package hash-graph-postgres-store --test deletion

Add `POST /users/delete` endpoint that accepts either `{ userId }` or
`{ email }` to delete a user's data while preserving structural references
(principals, policies, web) needed by entity types from other webs.

Orchestrates deletion across multiple services:
1. Purge all entities from the user's personal web
2. Delete Kratos identity (removes PII)
3. Revoke Hydra login and consent sessions (best-effort)
4. Delete email subscriptions via Mailchimp (best-effort)

Introduces provider traits (`IdentityProvider`, `OAuthProvider`,
`EmailSubscriptionProvider`) in the store crate with concrete HTTP
implementations in the API crate, keeping external service dependencies
out of domain logic.

Also fixes `delete_principals` to clean up `entity_ids` tombstones
left by Purge-scoped deletions before removing webs.
…and comprehensive deletion tests

- Make entity deletion temporal axes configurable via `DeleteEntitiesParams` so
  callers control which entities are found (live-only vs all temporal states)
- Use `ReportSink` in user deletion orchestration to collect non-fatal errors
  from external services (Kratos, Hydra, Mailchimp) with full error-stack context
- Return `UserDeletionOutcome` with both the deletion report and collected errors,
  differentiating the HTTP response message on partial failure
- Fix Mailchimp clap `requires` to be bidirectional (api_key now requires server)
- Add comprehensive deletion tests: link archival (basic, multiple, chain, self-loop,
  draft versions, idempotency), temporal axes validation, erase/purge interactions,
  cross-web batch handling, and user-deletion-then-reset scenario (ignored per BE-466)
- Extend integration tests for deletion-by-email, org entity survival, and link
  archival provenance verification
@vercel
Copy link

vercel bot commented Mar 11, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
hash Ready Ready Preview, Comment Mar 12, 2026 3:58pm
petrinaut Ready Ready Preview, Comment Mar 12, 2026 3:58pm
2 Skipped Deployments
Project Deployment Actions Updated (UTC)
hashdotdesign Ignored Ignored Preview Mar 12, 2026 3:58pm
hashdotdesign-tokens Ignored Ignored Preview Mar 12, 2026 3:58pm

@cursor
Copy link

cursor bot commented Mar 11, 2026

PR Summary

High Risk
High risk because it changes core data-deletion semantics (new temporal_axes, new LinkDeletionBehavior::Archive, and new Postgres archival CTE) and adds an admin endpoint that deletes user data and interacts with external identity/OAuth/email services.

Overview
Adds a new admin API endpoint POST /users/delete that deletes a user by ID or email by purging all entities in the user’s personal web, deleting the user’s Kratos identity, revoking Hydra login/consent sessions, and optionally deleting Mailchimp subscriptions (with non-fatal external-service errors reported back).

Extends entity deletion to accept configurable temporal_axes for entity finding and introduces LinkDeletionBehavior::Archive to temporally archive incoming link entities during purge; deletion summaries now include links_archived, and the Postgres deletion flow now validates/handles incoming links per scope/behavior and archives link entities via a single CTE.

Wires new external-service CLI/env config (Kratos/Hydra URLs + optional Mailchimp key/list) into the admin server, adds provider abstractions/implementations for Kratos/Hydra/Mailchimp (including md-5 for Mailchimp subscriber hashing), and updates/reset tooling and extensive Rust/TS integration tests to use the new deletion parameters and cover archive + temporal-axes behavior.

Written by Cursor Bugbot for commit 97eac95. This will update automatically on new commits. Configure here.

@github-actions github-actions bot added area/deps Relates to third-party dependencies (area) area/apps > hash* Affects HASH (a `hash-*` app) area/libs Relates to first-party libraries/crates/packages (area) type/eng > backend Owned by the @backend team area/tests New or updated tests area/tests > integration New or updated integration tests area/apps area/apps > hash-graph labels Mar 11, 2026
@codecov
Copy link

codecov bot commented Mar 11, 2026

Codecov Report

❌ Patch coverage is 0% with 365 lines in your changes missing coverage. Please review.
✅ Project coverage is 62.50%. Comparing base (313a166) to head (97eac95).
⚠️ Report is 26 commits behind head on main.

Files with missing lines Patch % Lines
...tore/src/store/postgres/knowledge/entity/delete.rs 0.00% 181 Missing ⚠️
libs/@local/graph/api/src/rest/admin.rs 0.00% 71 Missing ⚠️
...cal/graph/postgres-store/src/store/postgres/mod.rs 0.00% 37 Missing ⚠️
libs/@local/graph/api/src/oauth_provider.rs 0.00% 36 Missing ⚠️
libs/@local/graph/api/src/email_subscription.rs 0.00% 16 Missing ⚠️
libs/@local/graph/store/src/user_deletion.rs 0.00% 16 Missing ⚠️
libs/@local/graph/api/src/identity_provider.rs 0.00% 8 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #8530      +/-   ##
==========================================
- Coverage   62.65%   62.50%   -0.15%     
==========================================
  Files        1312     1316       +4     
  Lines      133689   134002     +313     
  Branches     5510     5510              
==========================================
- Hits        83757    83756       -1     
- Misses      49018    49332     +314     
  Partials      914      914              
Flag Coverage Δ
apps.hash-ai-worker-ts 1.40% <ø> (ø)
apps.hash-api 0.00% <ø> (ø)
local.hash-graph-sdk 7.73% <ø> (ø)
local.hash-isomorphic-utils 0.00% <ø> (ø)
rust.hash-graph-api 2.56% <0.00%> (-0.08%) ⬇️
rust.hash-graph-postgres-store 26.38% <0.00%> (-0.27%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@codspeed-hq
Copy link

codspeed-hq bot commented Mar 11, 2026

Merging this PR will not alter performance

✅ 80 untouched benchmarks


Comparing t/h-460-user-deletion-endpoint (97eac95) with main (07c2523)

Open in CodSpeed

@augmentcode
Copy link

augmentcode bot commented Mar 11, 2026

🤖 Augment PR Summary

Summary: Adds an admin API endpoint to delete a user and purge their personal web, including cleanup in external identity/OAuth/email systems.

Changes:

  • Adds POST /users/delete (delete by userId or email) and wires external-services config into the admin server/router.
  • Introduces store-level user_deletion::delete_user() orchestrator (fatal: user lookup + entity purge; non-fatal: Kratos/Hydra/Mailchimp cleanup with collected errors).
  • Adds provider abstractions + implementations: Kratos identity deletion, Hydra session revocation, optional Mailchimp subscriber deletion.
  • Makes entity deletion “finding” temporal axes configurable via DeleteEntitiesParams.temporal_axes.
  • Adds LinkDeletionBehavior::Archive and Postgres logic to archive incoming link entities during purge (with provenance updates).
  • Expands Rust deletion integration tests and backend integration tests to cover link-archival and end-to-end user deletion.

Technical Notes: Incoming link handling now distinguishes purge-vs-erase semantics and uses locking to reduce race windows with concurrent edge creation.

🤖 Was this summary useful? React with 👍 or 👎

Copy link

@augmentcode augmentcode bot left a comment

Choose a reason for hiding this comment

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

Review completed. 3 suggestions posted.

Fix All in Augment

Comment augment review to trigger a new review at any time.

Copy link

@cursor cursor bot left a comment

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Autofix Details

Bugbot Autofix prepared a fix for the issue found in the latest run.

  • ✅ Fixed: User deletion temporal axes inconsistent with find-all pattern
    • Changed QueryTemporalAxesUnresolved::TransactionTime to DecisionTime to match the established find_all_axes() pattern, ensuring archived entities are found during user deletion.

Create PR

Or push these changes by commenting:

@cursor push e4cd897e6c
Preview (e4cd897e6c)
diff --git a/libs/@local/graph/store/src/user_deletion.rs b/libs/@local/graph/store/src/user_deletion.rs
--- a/libs/@local/graph/store/src/user_deletion.rs
+++ b/libs/@local/graph/store/src/user_deletion.rs
@@ -143,7 +143,7 @@
             actor,
             DeleteEntitiesParams {
                 filter: web_filter,
-                temporal_axes: QueryTemporalAxesUnresolved::TransactionTime {
+                temporal_axes: QueryTemporalAxesUnresolved::DecisionTime {
                     pinned: PinnedTemporalAxisUnresolved::new(None),
                     variable: VariableTemporalAxisUnresolved::new(
                         Some(TemporalBound::Unbounded),

This Bugbot Autofix run was free. To enable autofix for future PRs, go to the Cursor dashboard.

…n, optional email field

- Use DecisionTime variant (not TransactionTime) for user deletion temporal
  axes so entities with closed decision_time ranges are found
- Treat Kratos 404 as success for idempotency, consistent with Mailchimp
- Make email_subscriptions_deleted Optional<bool> (None when no provider
  configured) to avoid misleading operators
The RevocationFailed variant is used for both login and consent session
revocation, so the display should not specify "consent".
@graphite-app graphite-app bot requested a review from a team March 11, 2026 14:07
@TimDiekmann TimDiekmann enabled auto-merge March 11, 2026 14:11
Mailchimp API keys have the format `<key>-<server>` (e.g. `abc123-us15`).
Extract the server automatically instead of requiring a separate config
parameter, reducing config from 3 to 2 fields and eliminating a source
of misconfiguration.
@TimDiekmann TimDiekmann changed the title BE-460: Add user deletion endpoint to admin API BE-460, BE-461: Add user deletion endpoint to admin API Mar 11, 2026
…dd admin 404 fallback

- Add `links_archived` field to `DeletionSummary` and `UserDeletionReport`
- Treat Hydra 400 (no sessions) as success instead of error
- Add fallback handler for unknown admin API routes
Enables manual cleanup of external services when deletion partially
fails, since the user's data is no longer in the Graph after entity
deletion.
archive_incoming_link_edges counted all closed temporal rows via count(*),
which double-counted link entities that have both published and draft versions.
Changed to count(DISTINCT (web_id, entity_uuid)) to report the actual number
of link entities archived.
Copy link

@cursor cursor bot left a comment

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 2 potential issues.

Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

get_user_emails now returns an error if the email property is not a JSON
array or if an array entry is not a string, instead of silently producing
an empty vector.
@github-actions
Copy link
Contributor

Benchmark results

@rust/hash-graph-benches – Integrations

policy_resolution_large

Function Value Mean Flame graphs
resolve_policies_for_actor user: empty, selectivity: high, policies: 2002 $$27.8 \mathrm{ms} \pm 135 \mathrm{μs}\left({\color{gray}-3.860 \mathrm{\%}}\right) $$ Flame Graph
resolve_policies_for_actor user: empty, selectivity: low, policies: 1 $$3.58 \mathrm{ms} \pm 19.6 \mathrm{μs}\left({\color{gray}-1.991 \mathrm{\%}}\right) $$ Flame Graph
resolve_policies_for_actor user: empty, selectivity: medium, policies: 1001 $$13.1 \mathrm{ms} \pm 87.8 \mathrm{μs}\left({\color{gray}-4.303 \mathrm{\%}}\right) $$ Flame Graph
resolve_policies_for_actor user: seeded, selectivity: high, policies: 3314 $$44.3 \mathrm{ms} \pm 286 \mathrm{μs}\left({\color{gray}-0.564 \mathrm{\%}}\right) $$ Flame Graph
resolve_policies_for_actor user: seeded, selectivity: low, policies: 1 $$15.6 \mathrm{ms} \pm 109 \mathrm{μs}\left({\color{gray}-1.289 \mathrm{\%}}\right) $$ Flame Graph
resolve_policies_for_actor user: seeded, selectivity: medium, policies: 1526 $$25.1 \mathrm{ms} \pm 169 \mathrm{μs}\left({\color{gray}-0.865 \mathrm{\%}}\right) $$ Flame Graph
resolve_policies_for_actor user: system, selectivity: high, policies: 2078 $$29.4 \mathrm{ms} \pm 183 \mathrm{μs}\left({\color{gray}-0.491 \mathrm{\%}}\right) $$ Flame Graph
resolve_policies_for_actor user: system, selectivity: low, policies: 1 $$3.89 \mathrm{ms} \pm 21.3 \mathrm{μs}\left({\color{gray}-1.826 \mathrm{\%}}\right) $$ Flame Graph
resolve_policies_for_actor user: system, selectivity: medium, policies: 1033 $$14.1 \mathrm{ms} \pm 109 \mathrm{μs}\left({\color{gray}-4.904 \mathrm{\%}}\right) $$ Flame Graph

policy_resolution_medium

Function Value Mean Flame graphs
resolve_policies_for_actor user: empty, selectivity: high, policies: 102 $$4.05 \mathrm{ms} \pm 18.3 \mathrm{μs}\left({\color{gray}0.892 \mathrm{\%}}\right) $$ Flame Graph
resolve_policies_for_actor user: empty, selectivity: low, policies: 1 $$3.15 \mathrm{ms} \pm 16.8 \mathrm{μs}\left({\color{gray}-1.536 \mathrm{\%}}\right) $$ Flame Graph
resolve_policies_for_actor user: empty, selectivity: medium, policies: 51 $$3.56 \mathrm{ms} \pm 22.5 \mathrm{μs}\left({\color{gray}-0.201 \mathrm{\%}}\right) $$ Flame Graph
resolve_policies_for_actor user: seeded, selectivity: high, policies: 269 $$5.70 \mathrm{ms} \pm 39.4 \mathrm{μs}\left({\color{gray}3.94 \mathrm{\%}}\right) $$ Flame Graph
resolve_policies_for_actor user: seeded, selectivity: low, policies: 1 $$3.86 \mathrm{ms} \pm 22.6 \mathrm{μs}\left({\color{gray}2.29 \mathrm{\%}}\right) $$ Flame Graph
resolve_policies_for_actor user: seeded, selectivity: medium, policies: 107 $$4.42 \mathrm{ms} \pm 25.3 \mathrm{μs}\left({\color{gray}1.56 \mathrm{\%}}\right) $$ Flame Graph
resolve_policies_for_actor user: system, selectivity: high, policies: 133 $$4.66 \mathrm{ms} \pm 27.7 \mathrm{μs}\left({\color{gray}-0.553 \mathrm{\%}}\right) $$ Flame Graph
resolve_policies_for_actor user: system, selectivity: low, policies: 1 $$3.66 \mathrm{ms} \pm 21.9 \mathrm{μs}\left({\color{gray}-2.098 \mathrm{\%}}\right) $$ Flame Graph
resolve_policies_for_actor user: system, selectivity: medium, policies: 63 $$4.27 \mathrm{ms} \pm 24.8 \mathrm{μs}\left({\color{gray}-3.438 \mathrm{\%}}\right) $$ Flame Graph

policy_resolution_none

Function Value Mean Flame graphs
resolve_policies_for_actor user: empty, selectivity: high, policies: 2 $$2.88 \mathrm{ms} \pm 14.1 \mathrm{μs}\left({\color{gray}-0.191 \mathrm{\%}}\right) $$ Flame Graph
resolve_policies_for_actor user: empty, selectivity: low, policies: 1 $$2.82 \mathrm{ms} \pm 12.5 \mathrm{μs}\left({\color{gray}-0.182 \mathrm{\%}}\right) $$ Flame Graph
resolve_policies_for_actor user: empty, selectivity: medium, policies: 1 $$2.94 \mathrm{ms} \pm 15.2 \mathrm{μs}\left({\color{gray}-0.135 \mathrm{\%}}\right) $$ Flame Graph
resolve_policies_for_actor user: system, selectivity: high, policies: 8 $$3.21 \mathrm{ms} \pm 18.6 \mathrm{μs}\left({\color{gray}-1.908 \mathrm{\%}}\right) $$ Flame Graph
resolve_policies_for_actor user: system, selectivity: low, policies: 1 $$3.02 \mathrm{ms} \pm 15.2 \mathrm{μs}\left({\color{gray}-0.924 \mathrm{\%}}\right) $$ Flame Graph
resolve_policies_for_actor user: system, selectivity: medium, policies: 3 $$3.36 \mathrm{ms} \pm 20.5 \mathrm{μs}\left({\color{gray}-0.501 \mathrm{\%}}\right) $$ Flame Graph

policy_resolution_small

Function Value Mean Flame graphs
resolve_policies_for_actor user: empty, selectivity: high, policies: 52 $$3.27 \mathrm{ms} \pm 16.4 \mathrm{μs}\left({\color{gray}-0.493 \mathrm{\%}}\right) $$ Flame Graph
resolve_policies_for_actor user: empty, selectivity: low, policies: 1 $$2.95 \mathrm{ms} \pm 16.3 \mathrm{μs}\left({\color{gray}-2.041 \mathrm{\%}}\right) $$ Flame Graph
resolve_policies_for_actor user: empty, selectivity: medium, policies: 25 $$3.12 \mathrm{ms} \pm 18.9 \mathrm{μs}\left({\color{gray}-1.493 \mathrm{\%}}\right) $$ Flame Graph
resolve_policies_for_actor user: seeded, selectivity: high, policies: 94 $$3.71 \mathrm{ms} \pm 27.5 \mathrm{μs}\left({\color{gray}-0.021 \mathrm{\%}}\right) $$ Flame Graph
resolve_policies_for_actor user: seeded, selectivity: low, policies: 1 $$3.22 \mathrm{ms} \pm 13.4 \mathrm{μs}\left({\color{gray}-3.027 \mathrm{\%}}\right) $$ Flame Graph
resolve_policies_for_actor user: seeded, selectivity: medium, policies: 26 $$3.49 \mathrm{ms} \pm 21.1 \mathrm{μs}\left({\color{gray}-0.578 \mathrm{\%}}\right) $$ Flame Graph
resolve_policies_for_actor user: system, selectivity: high, policies: 66 $$3.66 \mathrm{ms} \pm 16.6 \mathrm{μs}\left({\color{gray}0.323 \mathrm{\%}}\right) $$ Flame Graph
resolve_policies_for_actor user: system, selectivity: low, policies: 1 $$3.24 \mathrm{ms} \pm 18.0 \mathrm{μs}\left({\color{gray}-3.325 \mathrm{\%}}\right) $$ Flame Graph
resolve_policies_for_actor user: system, selectivity: medium, policies: 29 $$3.48 \mathrm{ms} \pm 15.6 \mathrm{μs}\left({\color{gray}-1.661 \mathrm{\%}}\right) $$ Flame Graph

read_scaling_complete

Function Value Mean Flame graphs
entity_by_id;one_depth 1 entities $$42.7 \mathrm{ms} \pm 236 \mathrm{μs}\left({\color{gray}-4.333 \mathrm{\%}}\right) $$ Flame Graph
entity_by_id;one_depth 10 entities $$80.0 \mathrm{ms} \pm 410 \mathrm{μs}\left({\color{gray}-3.532 \mathrm{\%}}\right) $$ Flame Graph
entity_by_id;one_depth 25 entities $$48.5 \mathrm{ms} \pm 210 \mathrm{μs}\left({\color{gray}-1.539 \mathrm{\%}}\right) $$ Flame Graph
entity_by_id;one_depth 5 entities $$50.2 \mathrm{ms} \pm 267 \mathrm{μs}\left({\color{gray}-1.419 \mathrm{\%}}\right) $$ Flame Graph
entity_by_id;one_depth 50 entities $$60.5 \mathrm{ms} \pm 370 \mathrm{μs}\left({\color{gray}1.89 \mathrm{\%}}\right) $$ Flame Graph
entity_by_id;two_depth 1 entities $$44.0 \mathrm{ms} \pm 207 \mathrm{μs}\left({\color{gray}-3.053 \mathrm{\%}}\right) $$ Flame Graph
entity_by_id;two_depth 10 entities $$419 \mathrm{ms} \pm 929 \mathrm{μs}\left({\color{gray}-1.119 \mathrm{\%}}\right) $$ Flame Graph
entity_by_id;two_depth 25 entities $$99.8 \mathrm{ms} \pm 374 \mathrm{μs}\left({\color{gray}-1.792 \mathrm{\%}}\right) $$ Flame Graph
entity_by_id;two_depth 5 entities $$88.4 \mathrm{ms} \pm 380 \mathrm{μs}\left({\color{gray}-2.732 \mathrm{\%}}\right) $$ Flame Graph
entity_by_id;two_depth 50 entities $$314 \mathrm{ms} \pm 1.18 \mathrm{ms}\left({\color{red}7.62 \mathrm{\%}}\right) $$ Flame Graph
entity_by_id;zero_depth 1 entities $$16.6 \mathrm{ms} \pm 97.1 \mathrm{μs}\left({\color{gray}-1.745 \mathrm{\%}}\right) $$ Flame Graph
entity_by_id;zero_depth 10 entities $$16.3 \mathrm{ms} \pm 99.1 \mathrm{μs}\left({\color{gray}-2.758 \mathrm{\%}}\right) $$ Flame Graph
entity_by_id;zero_depth 25 entities $$16.3 \mathrm{ms} \pm 84.3 \mathrm{μs}\left({\color{gray}-4.797 \mathrm{\%}}\right) $$ Flame Graph
entity_by_id;zero_depth 5 entities $$16.2 \mathrm{ms} \pm 92.8 \mathrm{μs}\left({\color{gray}-0.850 \mathrm{\%}}\right) $$ Flame Graph
entity_by_id;zero_depth 50 entities $$19.6 \mathrm{ms} \pm 147 \mathrm{μs}\left({\color{gray}0.390 \mathrm{\%}}\right) $$ Flame Graph

read_scaling_linkless

Function Value Mean Flame graphs
entity_by_id 1 entities $$16.2 \mathrm{ms} \pm 77.4 \mathrm{μs}\left({\color{gray}-4.018 \mathrm{\%}}\right) $$ Flame Graph
entity_by_id 10 entities $$16.3 \mathrm{ms} \pm 101 \mathrm{μs}\left({\color{gray}-1.728 \mathrm{\%}}\right) $$ Flame Graph
entity_by_id 100 entities $$16.2 \mathrm{ms} \pm 88.7 \mathrm{μs}\left({\color{gray}-2.250 \mathrm{\%}}\right) $$ Flame Graph
entity_by_id 1000 entities $$16.9 \mathrm{ms} \pm 110 \mathrm{μs}\left({\color{gray}-1.455 \mathrm{\%}}\right) $$ Flame Graph
entity_by_id 10000 entities $$24.3 \mathrm{ms} \pm 137 \mathrm{μs}\left({\color{gray}-2.473 \mathrm{\%}}\right) $$ Flame Graph

representative_read_entity

Function Value Mean Flame graphs
entity_by_id entity type ID: https://blockprotocol.org/@alice/types/entity-type/block/v/1 $$31.8 \mathrm{ms} \pm 268 \mathrm{μs}\left({\color{gray}1.20 \mathrm{\%}}\right) $$ Flame Graph
entity_by_id entity type ID: https://blockprotocol.org/@alice/types/entity-type/book/v/1 $$34.0 \mathrm{ms} \pm 343 \mathrm{μs}\left({\color{gray}3.57 \mathrm{\%}}\right) $$ Flame Graph
entity_by_id entity type ID: https://blockprotocol.org/@alice/types/entity-type/building/v/1 $$31.6 \mathrm{ms} \pm 291 \mathrm{μs}\left({\color{gray}-1.340 \mathrm{\%}}\right) $$ Flame Graph
entity_by_id entity type ID: https://blockprotocol.org/@alice/types/entity-type/organization/v/1 $$31.9 \mathrm{ms} \pm 303 \mathrm{μs}\left({\color{gray}-0.126 \mathrm{\%}}\right) $$ Flame Graph
entity_by_id entity type ID: https://blockprotocol.org/@alice/types/entity-type/page/v/2 $$31.7 \mathrm{ms} \pm 301 \mathrm{μs}\left({\color{gray}-0.403 \mathrm{\%}}\right) $$ Flame Graph
entity_by_id entity type ID: https://blockprotocol.org/@alice/types/entity-type/person/v/1 $$32.9 \mathrm{ms} \pm 299 \mathrm{μs}\left({\color{red}5.77 \mathrm{\%}}\right) $$ Flame Graph
entity_by_id entity type ID: https://blockprotocol.org/@alice/types/entity-type/playlist/v/1 $$31.5 \mathrm{ms} \pm 304 \mathrm{μs}\left({\color{gray}-1.173 \mathrm{\%}}\right) $$ Flame Graph
entity_by_id entity type ID: https://blockprotocol.org/@alice/types/entity-type/song/v/1 $$32.4 \mathrm{ms} \pm 271 \mathrm{μs}\left({\color{gray}-0.852 \mathrm{\%}}\right) $$ Flame Graph
entity_by_id entity type ID: https://blockprotocol.org/@alice/types/entity-type/uk-address/v/1 $$32.5 \mathrm{ms} \pm 300 \mathrm{μs}\left({\color{gray}4.87 \mathrm{\%}}\right) $$ Flame Graph

representative_read_entity_type

Function Value Mean Flame graphs
get_entity_type_by_id Account ID: bf5a9ef5-dc3b-43cf-a291-6210c0321eba $$8.96 \mathrm{ms} \pm 46.7 \mathrm{μs}\left({\color{gray}-0.216 \mathrm{\%}}\right) $$ Flame Graph

representative_read_multiple_entities

Function Value Mean Flame graphs
entity_by_property traversal_paths=0 0 $$92.3 \mathrm{ms} \pm 473 \mathrm{μs}\left({\color{gray}0.753 \mathrm{\%}}\right) $$
entity_by_property traversal_paths=255 1,resolve_depths=inherit:1;values:255;properties:255;links:127;link_dests:126;type:true $$144 \mathrm{ms} \pm 552 \mathrm{μs}\left({\color{gray}-1.717 \mathrm{\%}}\right) $$
entity_by_property traversal_paths=2 1,resolve_depths=inherit:0;values:0;properties:0;links:0;link_dests:0;type:false $$98.5 \mathrm{ms} \pm 580 \mathrm{μs}\left({\color{gray}-1.902 \mathrm{\%}}\right) $$
entity_by_property traversal_paths=2 1,resolve_depths=inherit:0;values:0;properties:0;links:1;link_dests:0;type:true $$107 \mathrm{ms} \pm 505 \mathrm{μs}\left({\color{gray}-0.403 \mathrm{\%}}\right) $$
entity_by_property traversal_paths=2 1,resolve_depths=inherit:0;values:0;properties:2;links:1;link_dests:0;type:true $$116 \mathrm{ms} \pm 467 \mathrm{μs}\left({\color{gray}-0.258 \mathrm{\%}}\right) $$
entity_by_property traversal_paths=2 1,resolve_depths=inherit:0;values:2;properties:2;links:1;link_dests:0;type:true $$123 \mathrm{ms} \pm 494 \mathrm{μs}\left({\color{gray}-0.451 \mathrm{\%}}\right) $$
link_by_source_by_property traversal_paths=0 0 $$90.2 \mathrm{ms} \pm 479 \mathrm{μs}\left({\color{gray}0.993 \mathrm{\%}}\right) $$
link_by_source_by_property traversal_paths=255 1,resolve_depths=inherit:1;values:255;properties:255;links:127;link_dests:126;type:true $$118 \mathrm{ms} \pm 582 \mathrm{μs}\left({\color{gray}-3.041 \mathrm{\%}}\right) $$
link_by_source_by_property traversal_paths=2 1,resolve_depths=inherit:0;values:0;properties:0;links:0;link_dests:0;type:false $$96.4 \mathrm{ms} \pm 459 \mathrm{μs}\left({\color{gray}-1.604 \mathrm{\%}}\right) $$
link_by_source_by_property traversal_paths=2 1,resolve_depths=inherit:0;values:0;properties:0;links:1;link_dests:0;type:true $$105 \mathrm{ms} \pm 538 \mathrm{μs}\left({\color{gray}-2.009 \mathrm{\%}}\right) $$
link_by_source_by_property traversal_paths=2 1,resolve_depths=inherit:0;values:0;properties:2;links:1;link_dests:0;type:true $$108 \mathrm{ms} \pm 516 \mathrm{μs}\left({\color{gray}-1.443 \mathrm{\%}}\right) $$
link_by_source_by_property traversal_paths=2 1,resolve_depths=inherit:0;values:2;properties:2;links:1;link_dests:0;type:true $$107 \mathrm{ms} \pm 442 \mathrm{μs}\left({\color{gray}-2.256 \mathrm{\%}}\right) $$

scenarios

Function Value Mean Flame graphs
full_test query-limited $$141 \mathrm{ms} \pm 519 \mathrm{μs}\left({\color{gray}4.86 \mathrm{\%}}\right) $$ Flame Graph
full_test query-unlimited $$142 \mathrm{ms} \pm 530 \mathrm{μs}\left({\color{gray}4.45 \mathrm{\%}}\right) $$ Flame Graph
linked_queries query-limited $$107 \mathrm{ms} \pm 461 \mathrm{μs}\left({\color{gray}-0.058 \mathrm{\%}}\right) $$ Flame Graph
linked_queries query-unlimited $$608 \mathrm{ms} \pm 3.23 \mathrm{ms}\left({\color{gray}4.21 \mathrm{\%}}\right) $$ Flame Graph

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/apps > hash* Affects HASH (a `hash-*` app) area/apps > hash-graph area/apps area/deps Relates to third-party dependencies (area) area/libs Relates to first-party libraries/crates/packages (area) area/tests > integration New or updated integration tests area/tests New or updated tests type/eng > backend Owned by the @backend team

Development

Successfully merging this pull request may close these issues.

1 participant