Skip to content

BE-782: Fit 2D semantic maps over graph entities and serve them as tiles - #9305

Open
indietyp wants to merge 6 commits into
bm/be-785-caller-identified-workflow-starts-and-owned-buffer-accessorsfrom
bm/be-782-atlas-fit-2d-semantic-maps-over-graph-entities-and-serve
Open

BE-782: Fit 2D semantic maps over graph entities and serve them as tiles#9305
indietyp wants to merge 6 commits into
bm/be-785-caller-identified-workflow-starts-and-owned-buffer-accessorsfrom
bm/be-782-atlas-fit-2d-semantic-maps-over-graph-entities-and-serve

Conversation

@indietyp

@indietyp indietyp commented Aug 24, 2026

Copy link
Copy Markdown
Member

🌟 What is the purpose of this PR?

The new hash-graph-atlas crate fits 2D semantic maps over the entity embeddings stored in the graph, blending semantic similarity (what entities mean) with relational structure (how they connect), and distills each fitted map into a small encoder that places new entities on the existing map without refitting. Serving reads the fitted artifacts from disk and answers tile queries over them.

The crate arrives whole and inert. Nothing links it until #9306 wires the CLI, the API proxy, and the compose service, so this layer reviews as a self-contained library plus its own cli module.

Review guidance for 627 files: line-reading is the wrong mode. The contract is at the boundaries, and they are small. The cli module is the only public one (everything else is pub(crate)), the on-disk artifact formats live in the file module with SHA-256 content identity in integrity, and the wire format is documented in wire.md. The module docs top-down from lib.rs give the architecture: the SALT pipeline sits on domain-independent foundation modules (math, random, bitset, identity, morton, runs, progress, file).

Review focus: determinism and cross-machine identity of the artifacts. x86-64 builds now assume the x86-64-v3 baseline (AVX2, FMA, BMI2) because the math kernels pin fused-multiply-add semantics, and the crate verifies the baseline at process entry so that a mismatched machine fails diagnosably. That .cargo/config.toml change applies workspace-wide and is the one hunk here whose effect reaches beyond the crate.


For an authorisation-focused review, one wants to take a look at (in order of building up on each other):

  • src/api/visibility.rs - how we authenticate and retrieve the information. This will undoubtedly change once the Kratos changes are in / will need to be retrofitted.
  • src/serve/authorization.rs - this is the Atlas-Authority token that we send over the wire to authenticate
  • src/api/authorization.rs - is the bridge between the API and the actual serve layer

The part to get right here is the cache semantics, as well as the cascading effect: we cannot backfill tiles, because that would trivially expose information about which tiles actually have occupancy. Everything revolves around that fact. The module enforcing it is serve/schedule/: a restricted response delivers from its own first-occupant cascade over the visible rows alone, so a hidden row contributes to no output at all rather than leaving a hole.
The fitting process (so everything in salt/) is authorisation-blind. It only does Postgres queries (these are in the postgres/ folders respectively). The serve layer applies authorisation, supplied via the API layer.

We can model it through the following diagram:

flowchart TD
    G["Gateway authenticates the session,<br/>states the actor in X-Authenticated-User-Actor-Id"] --> R{Route}

    R -->|manifest| P["Presented: reads the token if any, never rejects<br/>(api/authorization.rs)"]
    P --> MJ["Judge the generation, bind the filter,<br/>seal Scope: actor, filter digest, delivery cut<br/>XChaCha20-Poly1305 under a per-generation HKDF key"]
    MJ --> T[Atlas-Authority header back to the client]

    R -->|data route| AD["admit (api/authorization.rs)"]
    AD --> Q{"tag valid? actor matches the header?<br/>issued_at inside the window? epoch held?"}
    Q -->|any refusal| U["uniform 401, oracle-free:<br/>client re-fetches the manifest"]
    Q -->|ok| S["sealed Scope enters the visibility cache<br/>key = generation + actor + filter digest<br/>soft 8 min refresh-behind, hard 10 min ceiling<br/>(hard = revocation lag = token-age bound)"]
    S -->|hit| V[VisibilityProof]
    S -->|miss, one resolve per scope| C["compile policy + caller filter<br/>(serve/hydrate/compile.rs)"]
    C --> E{"admits_every_row?<br/>no caller filter AND policy filter = empty Filter::All"}
    E -->|yes| F["full_visibility: every row of every domain"]
    E -->|no| M["from_masks: fail-closed masks"]
    F --> V
    M --> V
    V --> W["assembly answers under the proof:<br/>every corpus-bearing response is masked"]
    W --> D["delivery from a per-scope schedule at cut k<br/>(serve/schedule/): built over visible rows alone,<br/>hidden rows contribute to nothing - no tile backfill"]
Loading

The crate layout is roughly:

- api: layer between HTTP and the actual serving (`serve`)
- bitset: domain-typed membership sets and persistable bit frames (the generic in-memory dense forms already come from `hashql_core`)
- cli: cli components, a superset of what either entry point exposes (the standalone binary lacks serve, the graph binary lacks dump/reports), to be reworked into a single binary endpoint
- dataset: ways to get the data out of the graph, and also to adapt to other systems (used for e.g. memory based testing)
- file: file formats (dumps and archives) that atlas uses
- identity: identity information, such as ids used throughout the system
- integrity: security relevant information, such as hashing infrastructure, secrets, etc.
- math: what the name says - strongly typed math primitives with a SIMD first approach
- morton: implementation of [z-order keys](https://en.wikipedia.org/wiki/Z-order_curve)
- postgres: well postgres
- random: random utilities, primarily adapters / additional functions not exposed by rand
- runs: fencepost-compressed per-key item lists, supplementing sprs
- salt: ML
- serve: taking what was generated by salt, and turning it in something that is consumed through api
- allocator: live-byte accounting at the allocator boundary, to be potentially subsumed into hashql_core
- bench: benchmarking infrastructure + entrypoint
- offload: rayon infrastructure to bridge async + sync
- device: chooses which device to run on training and inference
- progress: progress support for ML

🔍 What does this change?

  • libs/@local/graph/atlas/: the crate (614 files), its docs, benches, and tests.
  • .cargo/config.toml: the x86-64-v3 baseline for x86-64 targets, workspace-wide.
  • .clippy.toml: core::fmt::LowerExp joins allow-renamed-params-for.
  • .dockerignore: excludes wire.md.
  • .github/actions/prune-repository/prune.py: learns the atlas edge (inert until the frontend merges).
  • Workspace Cargo.toml, Cargo.lock, yarn.lock: crate and package registration.
  • Dependency diagrams regenerated.

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:

  • are internal and do not require a docs change

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

The changes in this PR:

  • affected the execution graph, and the turbo.json's have been updated to reflect this

⚠️ Known issues

🛡 What tests cover this?

  • The crate's unit tests and doctests cover the fit and serve paths.
  • The workspace battery: cargo check, clippy at zero warnings across targets and features, fmt clean.

❓ How to test this?

cargo nextest run -p hash-graph-atlas --lib
cargo test -p hash-graph-atlas --doc

@vercel

vercel Bot commented Aug 24, 2026

Copy link
Copy Markdown

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

Project Deployment Actions Updated (UTC)
hash Ready Ready Preview Aug 27, 2026 3:28pm
petrinaut Ready Ready Preview Aug 27, 2026 3:28pm
petrinaut-docs Ready Ready Preview Aug 27, 2026 3:28pm
1 Skipped Deployment
Project Deployment Actions Updated (UTC)
hashdotdesign-tokens Ignored Ignored Preview Aug 27, 2026 3:28pm

Request Review

@github-actions github-actions Bot added area/deps Relates to third-party dependencies (area) area/infra Relates to version control, CI, CD or IaC (area) area/libs Relates to first-party libraries/crates/packages (area) type/eng > backend Owned by the @backend team area/tests New or updated tests type/legal Owned by the @legal team labels Aug 24, 2026
Comment thread libs/@local/graph/atlas/src/salt/fit/prepare/instance.rs
Comment thread libs/@local/graph/atlas/src/file/region/mod.rs
Comment thread libs/@local/graph/atlas/src/file/generation/mod.rs
Comment thread libs/@local/graph/atlas/src/file/generation/mod.rs
Comment thread libs/@local/graph/atlas/src/file/generation/mod.rs
@codecov

codecov Bot commented Aug 24, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 60.76%. Comparing base (411c437) to head (d969b6c).

Additional details and impacted files
@@                                           Coverage Diff                                           @@
##           bm/be-785-caller-identified-workflow-starts-and-owned-buffer-accessors    #9305   +/-   ##
=======================================================================================================
  Coverage                                                                   60.76%   60.76%           
=======================================================================================================
  Files                                                                        1441     1441           
  Lines                                                                      143607   143607           
  Branches                                                                     6662     6662           
=======================================================================================================
  Hits                                                                        87261    87261           
  Misses                                                                      55255    55255           
  Partials                                                                     1091     1091           
Flag Coverage Δ
apps.hash-ai-worker-ts 1.99% <ø> (ø)
apps.hash-api 14.68% <ø> (ø)
local.hash-backend-utils 3.27% <ø> (ø)
local.hash-graph-sdk 10.02% <ø> (ø)
local.hash-isomorphic-utils 12.22% <ø> (ø)
rust.hash-graph-api 24.95% <ø> (ø)
rust.hash-graph-postgres-store 32.02% <ø> (ø)
rust.hashql-compiletest 28.39% <ø> (ø)
rust.hashql-eval 79.82% <ø> (ø)

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

☔ View full report in Codecov by Harness.
📢 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

codspeed-hq Bot commented Aug 24, 2026

Copy link
Copy Markdown

Merging this PR will regress 2 benchmarks

⚡ 15 improved benchmarks
❌ 2 regressed benchmarks
✅ 81 untouched benchmarks

Warning

Please fix the performance issues or acknowledge them on CodSpeed.

Performance Changes

Benchmark BASE HEAD Efficiency
bit_matrix/dense/union_rows[1000] 102.4 µs 119.3 µs -14.14%
bit_matrix/sparse/union_rows[1000] 126.3 µs 144.7 µs -12.76%
n50000_d256[32] 1,468.2 ms 696.5 ms ×2.1
n10000_d256[128] 4 s 1.9 s ×2.1
n10000_d256[32] 1,033.7 ms 517.7 ms +99.68%
n10000_d256[8] 94.3 ms 47.9 ms +96.68%
d256[64] 68.3 µs 40.7 µs +67.87%
d1536[16] 111.6 µs 70.6 µs +57.99%
d3072[16] 221.2 µs 140.2 µs +57.8%
d256[16] 19.5 µs 12.4 µs +57.12%
d256[15] 18.6 µs 12.3 µs +50.58%
kernel/micro_4x2[3072] 46.7 µs 36.9 µs +26.81%
kernel/micro_4x2[1536] 23.9 µs 18.9 µs +26.66%
kernel/micro_4x2[256] 4.9 µs 3.9 µs +26.17%
bit_matrix/dense/iter_row[64] 170 ns 137.2 ns +23.89%
bit_matrix/dense/iter_row[200] 215 ns 174.7 ns +23.05%
bit_matrix/dense/iter_row[1000] 525.6 ns 440.8 ns +19.22%

Tip

Investigate this regression by commenting @codspeedbot fix this regression on this PR, or directly use the CodSpeed MCP with your agent.


Comparing bm/be-782-atlas-fit-2d-semantic-maps-over-graph-entities-and-serve (d969b6c) with bm/be-785-caller-identified-workflow-starts-and-owned-buffer-accessors (411c437)

Open in CodSpeed

Comment thread libs/@local/graph/atlas/src/serve/authorization.rs Dismissed

@TimDiekmann TimDiekmann left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Could you please give instructions on how to review this?

"lint:clippy": "just clippy",
"start": "cargo run -p hash-graph-atlas --bin hash-graph-atlas --",
"test:integration": "cargo nextest run --package hash-graph-atlas --all-features --filterset 'kind(test)'",
"test:miri": "cargo miri nextest run -- vec2::tests::from_slice_reinterprets_in_place vec2::tests::natural_from_slice_splits_and_rejoins vec2::tests::layout_conversions_round_trip vec2::tests::simd_conversions_round_trip vec2::tests::from_lanes_inverts_lane_extraction vec2::tests::into_lanes_extracts_axis_groups vec2::tests::natural_from_lanes_interleaves vec2::tests::batch_getters_agree_across_layouts vec2::tests::natural_min_max_and_reductions bounds::tests::from_slice_matches_from_points bounds::tests::from_slice_rejects_non_finite dvec2::tests::dvec2x4t vecn::tests::boxed vecn::tests::lanes_ vecn::tests::vecn_wraps_in_place vecn::tests::vecn_wrap_slice dvecn::tests::wrapping_is_in_place vecn::tests::aligned_vecn_rejects dvecn::tests::aligned_accumulators dvecn::tests::aligned_from_mut dvecn::tests::aligned_from_ref dvecn::tests::boxed_dvecn_clone_from vecn::tests::try_as_aligned vecn::tests::from_slice_yields_every_row vecn::tests::from_slice_rejects_what matrixn::tests:: dataset::tests::archived_ dataset::auxiliary::tests:: random::tests::compat_ sprs::tests::unit_values adjacency::tests::writing_to_memory bitset::tests::dense_bit_slice_frames_round_trip bitset::tests::dense_bit_slice_frames_mutate_in_place bitset::tests::dense_bit_slice_zero_domain_frames_parse bitset::tests::dense_bit_slice_polices_the_final_word bitset::tests::dense_bit_slice_refuses_misshapen_frames bitset::tests::dense_bit_slice_zerocopy_doors_carry_the_frame_invariant bitset::tests::dense_bit_slice_prefix_reads_take_the_header_count bitset::tests::dense_bit_slice_array_starts_as_empty_frames bitset::tests::dense_bit_slice_array_indexes_independent_frames bitset::tests::dense_bit_slice_array_round_trips_through_its_region bitset::tests::dense_bit_slice_array_of_no_frames bitset::tests::dense_bit_slice_array_refuses_misshapen_regions bitset::tests::dense_bit_slice_array_zerocopy_doors_carry_the_region_invariant allocator::tests:: dsquare::tests::block_height_invariance dsquare::tests::the_solution_reproduces_the_right_hand_side dsquare::tests::both_dots_reduce_equal_inputs_to_identical_bits dsquare::tests::the_row_dot_and_dvecn_dot_reduce_equal_bytes_to_identical_bits dsquare::tests::every_row_is_aligned_for_simd dsquare::tests::writes_through_rows_land_at_their_offsets dsquare::tests::the_zero_matrix_reads_zero_everywhere dsquare::tests::the_empty_matrix_factors_and_solves dsquare::tests::a_row_past_the_order_panics dsquare::tests::a_mismatched_right_hand_side_panics dsquare::tests::matrix_drop_returns_the_buffer_to_its_allocator dsquare::tests::factor_drop_returns_the_moved_buffer_to_its_allocator runs::tests::structure_view scalar::tests::try_from_bytes morton::tests::try_from_ref authorization::tests::archived_actor_entity_uuid",

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

It would be good if this would be more maintainable. This way, it's very easy to miss.

Comment on lines +417 to +426
impl FromRequestParts<AppState> for Actor {
type Rejection = Problem<'static>;

fn from_request_parts(
parts: &mut Parts,
_state: &AppState,
) -> impl Future<Output = Result<Self, Self::Rejection>> + Send {
future::ready(actor(parts).map(Self))
}
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This reinvents the existing Authentication setup, please use already existing authentication we have in hash-graph-api, so we don't have multiple locations where we define how we get the AuthenticatedActor.
The auth-setup itself lives in hash-graph-authentication. We may need to move some middleware from the api-crate to the authN-crate to re-use it here. This should reduce the amount of work being required.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Yes, I understand, but I deliberately didn't want to depend on hash-graph-api since it doesn't make sense to do so just for the middleware. I tracked all of this in BE-805; I'm happy to do it as part of this PR or as a follow-up. Still, I didn't want to make this already massive PR bigger by moving things into, e.g. hash-graph-authentication, especially considering that before yesterday the way I was doing this was basically just 10 lines of code that were the same between them (which is no longer true), so it's just unfortunate timing^^. Several adjustments are needed here, including removing the hash-api proxy, moving to kratos, and moving the middleware into hash-graph-authentication, which I didn't want to do as part of this. I can prepare this under this PR and then adjust this PR, making it reviewable (for each), or you can take it up if you'd like. This should then also make the authentication wiring straightforward.

Comment on lines +10 to +11
//! The `X-Authenticated-User-Actor-Id` header names the actor, and that header is the trust
//! boundary the graph's REST API stands on. The gateway authenticates the session and states the

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This is not true (anymore): #9233

Comment on lines +56 to +58
/// Parity with the graph's REST API, which accepts this name, fixes the spelling. Headers this
/// crate introduces carry no prefix.
const ACTOR_HEADER: &str = "X-Authenticated-User-Actor-Id";

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

See above

extract::FromRequestParts,
http::{StatusCode, request::Parts},
};
use hash_graph_authorization::policies::principal::actor::AuthenticatedActor;
extract::FromRequestParts,
http::{StatusCode, request::Parts},
};
use hash_graph_authorization::policies::principal::actor::AuthenticatedActor;
use hash_graph_authorization::policies::{
MergePolicies, PolicyComponents,
action::ActionName,
principal::actor::AuthenticatedActor,
use hash_graph_authorization::policies::{
MergePolicies, PolicyComponents,
action::ActionName,
principal::actor::AuthenticatedActor,
Comment on lines +204 to +205
let policy_components = PolicyComponents::builder(store)
.with_actor(actor)
let temporal_axes = QueryTemporalAxesUnresolved::live_only().resolve();
let mut compiler = SelectCompiler::new(Some(&temporal_axes), false);

let policy_components = PolicyComponents::builder(store)
let temporal_axes = QueryTemporalAxesUnresolved::live_only().resolve();
let mut compiler = SelectCompiler::new(Some(&temporal_axes), false);

let policy_components = PolicyComponents::builder(store)
let mut compiler = SelectCompiler::new(Some(&temporal_axes), false);

let policy_components = PolicyComponents::builder(store)
.with_actor(actor)
*indices.last()?
};

self.examples.remove(index);
};
if single_group {
let index = *indices.last().expect("an example group is never empty");
self.examples.remove(index);
}

for index in indices.iter().rev() {
self.examples.remove(*index);
.as_bytes()
.windows(8)
.any(|candidate| candidate == window),
"the rendering carries held material at position {start}: {rendered}"
Comment on lines +547 to +549
"the rebuilt canonical frame does not reproduce the published coordinate column (max \
component error {}, bound {CERTIFICATE_TOLERANCE})",
certificate.max_absolute_error,
.as_bytes()
.windows(8)
.any(|candidate| candidate == window),
"the rendering carries key material at position {start}: {rendered}"
// Redaction that hid the type would trade one diagnostic failure for another.
assert!(
rendered.contains("WireSecret"),
"the rendering still names the type: {rendered}"
let connection = DatabaseConnectionInfo::new(
DatabaseType::Postgres,
store("USER", "postgres"),
store("PASSWORD", "postgres"),
@github-actions

Copy link
Copy Markdown
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 $$28.5 \mathrm{ms} \pm 207 \mathrm{μs}\left({\color{gray}-0.831 \mathrm{\%}}\right) $$ Flame Graph
resolve_policies_for_actor user: empty, selectivity: low, policies: 1 $$3.56 \mathrm{ms} \pm 24.1 \mathrm{μs}\left({\color{gray}-0.490 \mathrm{\%}}\right) $$ Flame Graph
resolve_policies_for_actor user: empty, selectivity: medium, policies: 1002 $$13.8 \mathrm{ms} \pm 93.3 \mathrm{μs}\left({\color{gray}-2.012 \mathrm{\%}}\right) $$ Flame Graph
resolve_policies_for_actor user: seeded, selectivity: high, policies: 3314 $$44.1 \mathrm{ms} \pm 440 \mathrm{μs}\left({\color{gray}-4.827 \mathrm{\%}}\right) $$ Flame Graph
resolve_policies_for_actor user: seeded, selectivity: low, policies: 1 $$16.4 \mathrm{ms} \pm 186 \mathrm{μs}\left({\color{red}14.3 \mathrm{\%}}\right) $$ Flame Graph
resolve_policies_for_actor user: seeded, selectivity: medium, policies: 1527 $$25.3 \mathrm{ms} \pm 268 \mathrm{μs}\left({\color{gray}1.23 \mathrm{\%}}\right) $$ Flame Graph
resolve_policies_for_actor user: system, selectivity: high, policies: 2078 $$29.7 \mathrm{ms} \pm 315 \mathrm{μs}\left({\color{gray}-1.496 \mathrm{\%}}\right) $$ Flame Graph
resolve_policies_for_actor user: system, selectivity: low, policies: 1 $$3.89 \mathrm{ms} \pm 26.4 \mathrm{μs}\left({\color{gray}-1.502 \mathrm{\%}}\right) $$ Flame Graph
resolve_policies_for_actor user: system, selectivity: medium, policies: 1033 $$15.5 \mathrm{ms} \pm 157 \mathrm{μs}\left({\color{gray}0.994 \mathrm{\%}}\right) $$ Flame Graph

policy_resolution_medium

Function Value Mean Flame graphs
resolve_policies_for_actor user: empty, selectivity: high, policies: 102 $$3.98 \mathrm{ms} \pm 32.3 \mathrm{μs}\left({\color{gray}-0.744 \mathrm{\%}}\right) $$ Flame Graph
resolve_policies_for_actor user: empty, selectivity: low, policies: 1 $$3.13 \mathrm{ms} \pm 19.8 \mathrm{μs}\left({\color{gray}-0.360 \mathrm{\%}}\right) $$ Flame Graph
resolve_policies_for_actor user: empty, selectivity: medium, policies: 52 $$3.53 \mathrm{ms} \pm 27.7 \mathrm{μs}\left({\color{gray}-0.903 \mathrm{\%}}\right) $$ Flame Graph
resolve_policies_for_actor user: seeded, selectivity: high, policies: 269 $$5.45 \mathrm{ms} \pm 40.4 \mathrm{μs}\left({\color{gray}-1.213 \mathrm{\%}}\right) $$ Flame Graph
resolve_policies_for_actor user: seeded, selectivity: low, policies: 1 $$3.68 \mathrm{ms} \pm 21.5 \mathrm{μs}\left({\color{gray}0.926 \mathrm{\%}}\right) $$ Flame Graph
resolve_policies_for_actor user: seeded, selectivity: medium, policies: 108 $$4.33 \mathrm{ms} \pm 35.9 \mathrm{μs}\left({\color{gray}-1.327 \mathrm{\%}}\right) $$ Flame Graph
resolve_policies_for_actor user: system, selectivity: high, policies: 133 $$4.52 \mathrm{ms} \pm 36.2 \mathrm{μs}\left({\color{lightgreen}-7.666 \mathrm{\%}}\right) $$ Flame Graph
resolve_policies_for_actor user: system, selectivity: low, policies: 1 $$3.62 \mathrm{ms} \pm 26.4 \mathrm{μs}\left({\color{gray}-0.900 \mathrm{\%}}\right) $$ Flame Graph
resolve_policies_for_actor user: system, selectivity: medium, policies: 63 $$4.62 \mathrm{ms} \pm 40.5 \mathrm{μs}\left({\color{red}5.47 \mathrm{\%}}\right) $$ Flame Graph

policy_resolution_none

Function Value Mean Flame graphs
resolve_policies_for_actor user: empty, selectivity: high, policies: 2 $$2.71 \mathrm{ms} \pm 18.4 \mathrm{μs}\left({\color{lightgreen}-18.425 \mathrm{\%}}\right) $$ Flame Graph
resolve_policies_for_actor user: empty, selectivity: low, policies: 1 $$2.60 \mathrm{ms} \pm 15.0 \mathrm{μs}\left({\color{gray}-2.413 \mathrm{\%}}\right) $$ Flame Graph
resolve_policies_for_actor user: empty, selectivity: medium, policies: 2 $$2.70 \mathrm{ms} \pm 21.5 \mathrm{μs}\left({\color{gray}-3.565 \mathrm{\%}}\right) $$ Flame Graph
resolve_policies_for_actor user: system, selectivity: high, policies: 8 $$2.97 \mathrm{ms} \pm 21.3 \mathrm{μs}\left({\color{gray}-1.434 \mathrm{\%}}\right) $$ Flame Graph
resolve_policies_for_actor user: system, selectivity: low, policies: 1 $$2.75 \mathrm{ms} \pm 18.7 \mathrm{μs}\left({\color{lightgreen}-10.685 \mathrm{\%}}\right) $$ Flame Graph
resolve_policies_for_actor user: system, selectivity: medium, policies: 3 $$3.00 \mathrm{ms} \pm 25.7 \mathrm{μs}\left({\color{gray}-0.391 \mathrm{\%}}\right) $$ Flame Graph

policy_resolution_small

Function Value Mean Flame graphs
resolve_policies_for_actor user: empty, selectivity: high, policies: 52 $$3.18 \mathrm{ms} \pm 21.2 \mathrm{μs}\left({\color{gray}-0.327 \mathrm{\%}}\right) $$ Flame Graph
resolve_policies_for_actor user: empty, selectivity: low, policies: 1 $$2.88 \mathrm{ms} \pm 22.0 \mathrm{μs}\left({\color{gray}2.20 \mathrm{\%}}\right) $$ Flame Graph
resolve_policies_for_actor user: empty, selectivity: medium, policies: 26 $$3.13 \mathrm{ms} \pm 25.9 \mathrm{μs}\left({\color{gray}2.08 \mathrm{\%}}\right) $$ Flame Graph
resolve_policies_for_actor user: seeded, selectivity: high, policies: 94 $$3.60 \mathrm{ms} \pm 28.1 \mathrm{μs}\left({\color{gray}3.05 \mathrm{\%}}\right) $$ Flame Graph
resolve_policies_for_actor user: seeded, selectivity: low, policies: 1 $$3.02 \mathrm{ms} \pm 19.0 \mathrm{μs}\left({\color{lightgreen}-5.124 \mathrm{\%}}\right) $$ Flame Graph
resolve_policies_for_actor user: seeded, selectivity: medium, policies: 27 $$3.42 \mathrm{ms} \pm 21.1 \mathrm{μs}\left({\color{lightgreen}-5.160 \mathrm{\%}}\right) $$ Flame Graph
resolve_policies_for_actor user: system, selectivity: high, policies: 66 $$3.52 \mathrm{ms} \pm 24.4 \mathrm{μs}\left({\color{gray}-0.839 \mathrm{\%}}\right) $$ Flame Graph
resolve_policies_for_actor user: system, selectivity: low, policies: 1 $$3.09 \mathrm{ms} \pm 20.4 \mathrm{μs}\left({\color{gray}-0.587 \mathrm{\%}}\right) $$ Flame Graph
resolve_policies_for_actor user: system, selectivity: medium, policies: 29 $$3.47 \mathrm{ms} \pm 27.9 \mathrm{μs}\left({\color{gray}0.777 \mathrm{\%}}\right) $$ Flame Graph

read_scaling_complete

Function Value Mean Flame graphs
entity_by_id;one_depth 1 entities $$44.1 \mathrm{ms} \pm 289 \mathrm{μs}\left({\color{gray}-0.670 \mathrm{\%}}\right) $$ Flame Graph
entity_by_id;one_depth 10 entities $$33.2 \mathrm{ms} \pm 197 \mathrm{μs}\left({\color{lightgreen}-6.726 \mathrm{\%}}\right) $$ Flame Graph
entity_by_id;one_depth 25 entities $$39.9 \mathrm{ms} \pm 590 \mathrm{μs}\left({\color{gray}4.04 \mathrm{\%}}\right) $$ Flame Graph
entity_by_id;one_depth 5 entities $$33.7 \mathrm{ms} \pm 290 \mathrm{μs}\left({\color{gray}0.350 \mathrm{\%}}\right) $$ Flame Graph
entity_by_id;one_depth 50 entities $$45.1 \mathrm{ms} \pm 363 \mathrm{μs}\left({\color{gray}1.24 \mathrm{\%}}\right) $$ Flame Graph
entity_by_id;two_depth 1 entities $$52.2 \mathrm{ms} \pm 388 \mathrm{μs}\left({\color{gray}1.86 \mathrm{\%}}\right) $$ Flame Graph
entity_by_id;two_depth 10 entities $$42.3 \mathrm{ms} \pm 344 \mathrm{μs}\left({\color{lightgreen}-8.757 \mathrm{\%}}\right) $$ Flame Graph
entity_by_id;two_depth 25 entities $$97.1 \mathrm{ms} \pm 606 \mathrm{μs}\left({\color{gray}1.29 \mathrm{\%}}\right) $$ Flame Graph
entity_by_id;two_depth 5 entities $$35.3 \mathrm{ms} \pm 226 \mathrm{μs}\left({\color{gray}4.06 \mathrm{\%}}\right) $$ Flame Graph
entity_by_id;two_depth 50 entities $$312 \mathrm{ms} \pm 1.25 \mathrm{ms}\left({\color{gray}-0.602 \mathrm{\%}}\right) $$ Flame Graph
entity_by_id;zero_depth 1 entities $$11.9 \mathrm{ms} \pm 233 \mathrm{μs}\left({\color{red}5.31 \mathrm{\%}}\right) $$ Flame Graph
entity_by_id;zero_depth 10 entities $$11.3 \mathrm{ms} \pm 79.8 \mathrm{μs}\left({\color{red}5.52 \mathrm{\%}}\right) $$ Flame Graph
entity_by_id;zero_depth 25 entities $$11.3 \mathrm{ms} \pm 71.5 \mathrm{μs}\left({\color{lightgreen}-6.171 \mathrm{\%}}\right) $$ Flame Graph
entity_by_id;zero_depth 5 entities $$11.2 \mathrm{ms} \pm 75.5 \mathrm{μs}\left({\color{gray}1.21 \mathrm{\%}}\right) $$ Flame Graph
entity_by_id;zero_depth 50 entities $$11.1 \mathrm{ms} \pm 65.3 \mathrm{μs}\left({\color{lightgreen}-9.599 \mathrm{\%}}\right) $$ Flame Graph

read_scaling_linkless

Function Value Mean Flame graphs
entity_by_id 1 entities $$11.1 \mathrm{ms} \pm 73.3 \mathrm{μs}\left({\color{gray}-4.093 \mathrm{\%}}\right) $$ Flame Graph
entity_by_id 10 entities $$11.1 \mathrm{ms} \pm 74.9 \mathrm{μs}\left({\color{gray}-1.706 \mathrm{\%}}\right) $$ Flame Graph
entity_by_id 100 entities $$13.2 \mathrm{ms} \pm 588 \mathrm{μs}\left({\color{red}15.7 \mathrm{\%}}\right) $$ Flame Graph
entity_by_id 1000 entities $$11.3 \mathrm{ms} \pm 74.9 \mathrm{μs}\left({\color{gray}-2.811 \mathrm{\%}}\right) $$ Flame Graph
entity_by_id 10000 entities $$11.7 \mathrm{ms} \pm 78.7 \mathrm{μs}\left({\color{gray}-1.059 \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 $$11.6 \mathrm{ms} \pm 78.3 \mathrm{μs}\left({\color{gray}2.36 \mathrm{\%}}\right) $$ Flame Graph
entity_by_id entity type ID: https://blockprotocol.org/@alice/types/entity-type/book/v/1 $$11.7 \mathrm{ms} \pm 94.1 \mathrm{μs}\left({\color{gray}-0.676 \mathrm{\%}}\right) $$ Flame Graph
entity_by_id entity type ID: https://blockprotocol.org/@alice/types/entity-type/building/v/1 $$12.4 \mathrm{ms} \pm 129 \mathrm{μs}\left({\color{red}12.1 \mathrm{\%}}\right) $$ Flame Graph
entity_by_id entity type ID: https://blockprotocol.org/@alice/types/entity-type/organization/v/1 $$11.6 \mathrm{ms} \pm 93.1 \mathrm{μs}\left({\color{gray}0.820 \mathrm{\%}}\right) $$ Flame Graph
entity_by_id entity type ID: https://blockprotocol.org/@alice/types/entity-type/page/v/2 $$11.7 \mathrm{ms} \pm 99.5 \mathrm{μs}\left({\color{gray}-0.156 \mathrm{\%}}\right) $$ Flame Graph
entity_by_id entity type ID: https://blockprotocol.org/@alice/types/entity-type/person/v/1 $$11.5 \mathrm{ms} \pm 89.7 \mathrm{μs}\left({\color{gray}-1.687 \mathrm{\%}}\right) $$ Flame Graph
entity_by_id entity type ID: https://blockprotocol.org/@alice/types/entity-type/playlist/v/1 $$11.3 \mathrm{ms} \pm 93.6 \mathrm{μs}\left({\color{gray}-2.651 \mathrm{\%}}\right) $$ Flame Graph
entity_by_id entity type ID: https://blockprotocol.org/@alice/types/entity-type/song/v/1 $$11.4 \mathrm{ms} \pm 56.3 \mathrm{μs}\left({\color{lightgreen}-9.058 \mathrm{\%}}\right) $$ Flame Graph
entity_by_id entity type ID: https://blockprotocol.org/@alice/types/entity-type/uk-address/v/1 $$11.0 \mathrm{ms} \pm 75.7 \mathrm{μs}\left({\color{gray}-4.993 \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.59 \mathrm{ms} \pm 56.4 \mathrm{μs}\left({\color{gray}-2.309 \mathrm{\%}}\right) $$ Flame Graph

representative_read_multiple_entities

Function Value Mean Flame graphs
entity_by_property traversal_paths=0 0 $$60.3 \mathrm{ms} \pm 501 \mathrm{μs}\left({\color{gray}-0.635 \mathrm{\%}}\right) $$
entity_by_property traversal_paths=255 1,resolve_depths=inherit:1;values:255;properties:255;links:127;link_dests:126;type:true $$111 \mathrm{ms} \pm 595 \mathrm{μs}\left({\color{gray}-4.289 \mathrm{\%}}\right) $$
entity_by_property traversal_paths=2 1,resolve_depths=inherit:0;values:0;properties:0;links:0;link_dests:0;type:false $$67.5 \mathrm{ms} \pm 495 \mathrm{μs}\left({\color{gray}-1.270 \mathrm{\%}}\right) $$
entity_by_property traversal_paths=2 1,resolve_depths=inherit:0;values:0;properties:0;links:1;link_dests:0;type:true $$77.6 \mathrm{ms} \pm 499 \mathrm{μs}\left({\color{gray}-0.892 \mathrm{\%}}\right) $$
entity_by_property traversal_paths=2 1,resolve_depths=inherit:0;values:0;properties:2;links:1;link_dests:0;type:true $$85.8 \mathrm{ms} \pm 482 \mathrm{μs}\left({\color{gray}-3.391 \mathrm{\%}}\right) $$
entity_by_property traversal_paths=2 1,resolve_depths=inherit:0;values:2;properties:2;links:1;link_dests:0;type:true $$91.7 \mathrm{ms} \pm 740 \mathrm{μs}\left({\color{lightgreen}-5.491 \mathrm{\%}}\right) $$
link_by_source_by_property traversal_paths=0 0 $$49.0 \mathrm{ms} \pm 432 \mathrm{μs}\left({\color{red}5.92 \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 $$75.8 \mathrm{ms} \pm 509 \mathrm{μs}\left({\color{gray}-0.433 \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 $$55.3 \mathrm{ms} \pm 386 \mathrm{μs}\left({\color{gray}4.26 \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 $$63.2 \mathrm{ms} \pm 565 \mathrm{μs}\left({\color{gray}0.633 \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 $$64.9 \mathrm{ms} \pm 515 \mathrm{μs}\left({\color{gray}-0.528 \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 $$65.0 \mathrm{ms} \pm 683 \mathrm{μs}\left({\color{gray}0.792 \mathrm{\%}}\right) $$

scenarios

Function Value Mean Flame graphs
full_test query-limited $$116 \mathrm{ms} \pm 1.07 \mathrm{ms}\left({\color{lightgreen}-5.051 \mathrm{\%}}\right) $$ Flame Graph
full_test query-unlimited $$125 \mathrm{ms} \pm 872 \mathrm{μs}\left({\color{lightgreen}-8.751 \mathrm{\%}}\right) $$ Flame Graph
linked_queries query-limited $$19.4 \mathrm{ms} \pm 165 \mathrm{μs}\left({\color{gray}-2.214 \mathrm{\%}}\right) $$ Flame Graph
linked_queries query-unlimited $$534 \mathrm{ms} \pm 3.01 \mathrm{ms}\left({\color{lightgreen}-8.110 \mathrm{\%}}\right) $$ Flame Graph

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

Labels

area/deps Relates to third-party dependencies (area) area/infra Relates to version control, CI, CD or IaC (area) area/libs Relates to first-party libraries/crates/packages (area) area/tests New or updated tests type/eng > backend Owned by the @backend team type/legal Owned by the @legal team

Development

Successfully merging this pull request may close these issues.

3 participants