Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
560 changes: 560 additions & 0 deletions libs/@local/graph/atlas/src/serve/hydrate/client.rs

Large diffs are not rendered by default.

23 changes: 23 additions & 0 deletions libs/@local/graph/atlas/src/serve/hydrate/columns.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
//! Slot ID domains for one hydration read.
//!
//! [`NodeSlot`] and [`EdgeSlot`] index a response's delivered nodes and edges in delivered order,
//! so hydration, assembly and encoding all address the same point or link by the same number
//! without re-deriving it from an entity id.

hashql_core::id::newtype! {
/// A reference to a delivered node by its slot in one response's delivered order.
///
/// Slots are dense and zero-based over one response's delivered nodes. Every node detail
/// column aligns to this domain. A slot is valid only against the response that delivered it,
/// because two responses share no slot vocabulary.
pub(crate) struct NodeSlot(u32)
}

hashql_core::id::newtype! {
/// A reference to a delivered edge by its slot in one response's edge order.
///
/// Slots are dense and zero-based over one response's delivered edges. Every link detail
/// column aligns to this domain. A slot is valid only against the response that delivered it,
/// because two responses share no slot vocabulary.
pub(crate) struct EdgeSlot(u32)
}
123 changes: 123 additions & 0 deletions libs/@local/graph/atlas/src/serve/hydrate/locate.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
//! Live properties and type URLs for locate documents.
//!
//! Each requested identity retains its slot when the store no longer serves it.

use alloc::sync::Arc;

use error_stack::Report;
use hashql_core::id::{IdSlice, IdVec};
use type_system::ontology::VersionedUrl;

use super::{EdgeSlot, NodeSlot, client::HydrateError, scalar::ScalarProperties};
use crate::{postgres::id::ArchivedEntityId, serve::visibility::VisibilityActor};

/// One requested entity and the details the store still serves for it.
#[derive(Debug, PartialEq)]
pub(crate) struct LocateEntity<T> {
pub identity: ArchivedEntityId,
pub details: Option<T>,
}

impl<T> LocateEntity<T> {
/// Builds an entity requested by `identity`, with no details resolved yet.
pub(crate) const fn new(identity: ArchivedEntityId) -> Self {
Self {
identity,
details: None,
}
}
}

/// The live detail for one delivered node: its direct-type versioned URLs.
#[derive(Debug, PartialEq)]
pub(crate) struct LocateNode {
/// Direct-type versioned URLs in canonical order.
pub type_urls: Vec<VersionedUrl>,
}

/// One entity's resolved scalar properties and whether they are its whole deliverable set.
#[derive(Debug, PartialEq)]
pub(crate) struct LocateProperties {
pub values: ScalarProperties,
/// Whether the map contains the entity's whole deliverable property set.
pub complete: bool,
}

/// The live detail for one delivered link: its capped direct types and scalar properties.
#[derive(Debug, PartialEq)]
pub(crate) struct LocateLink {
/// Capped direct-type versioned URLs in canonical order.
pub type_urls: Vec<VersionedUrl>,
/// Whether the list contains the link's whole direct-type set.
pub type_urls_complete: bool,
pub properties: LocateProperties,
}

/// One locate request's live-detail slots.
///
/// The slots arrive with the caps and the actor a [`LocateResolver`] resolves them under.
#[derive(Debug)]
pub(crate) struct LocateRequest<'doc> {
/// The resolved actor the store masks properties for.
pub actor: VisibilityActor,
/// Delivered nodes, source first.
pub nodes: &'doc mut IdSlice<NodeSlot, LocateEntity<LocateNode>>,
/// Delivered link entities, ascending identity bytes.
pub links: &'doc mut IdSlice<EdgeSlot, LocateEntity<LocateLink>>,
/// Most properties the source's map delivers.
pub properties: u32,
/// Most direct-type URLs each link delivers.
pub link_type_ids: u32,
/// Most properties each link's map delivers.
pub link_properties: u32,
}

/// The resolved live detail for one locate document.
///
/// It holds every requested node and link, plus the source's own properties.
#[derive(Debug, PartialEq)]
pub(crate) struct LocateResponse {
pub nodes: IdVec<NodeSlot, LocateEntity<LocateNode>>,
pub links: IdVec<EdgeSlot, LocateEntity<LocateLink>>,
pub source_properties: Option<LocateProperties>,
}

/// Live detail resolution within the supplied entity slots.
pub(crate) trait LocateResolver {
/// Fills the requested slots and returns the source's capped properties.
///
/// Unavailable entities remain unresolved.
///
/// # Errors
///
/// Returns [`HydrateError`] when a store read fails. Errors may leave partially populated
/// slots. Discard those results. Reset all slot details to [`None`] before retrying.
fn resolve(
&self,
request: LocateRequest<'_>,
) -> Result<Option<LocateProperties>, Report<HydrateError>>;
}

impl<T> LocateResolver for &T
where
T: LocateResolver,
{
fn resolve(
&self,
request: LocateRequest<'_>,
) -> Result<Option<LocateProperties>, Report<HydrateError>> {
T::resolve(self, request)
}
}

impl<T> LocateResolver for Arc<T>
where
T: LocateResolver,
{
fn resolve(
&self,
request: LocateRequest<'_>,
) -> Result<Option<LocateProperties>, Report<HydrateError>> {
T::resolve(self, request)
}
}
43 changes: 43 additions & 0 deletions libs/@local/graph/atlas/src/serve/hydrate/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
//! Request-time store reads for the detail a response's trailer carries.
//!
//! Hydration is graph-store enrichment performed while the request runs: an entity's direct-type
//! URLs and its scalar property values, read from the live store. The edges and locate trailers
//! hydrate. A tile trailer does not, because its labels and icons come from the publication the
//! request captured, as positions do.
//!
//! Hydration is one of the two reasons a detailed response is not reusable as an immutable
//! generation tile, and the two differ in the data lifetime a value follows. A captured label
//! follows the epoch the caller's scope resolved against, and moves when that scope re-resolves -
//! which is why even a tile trailer, reading no store, is not stable across requests. A
//! request-time read follows the read instead, and can observe an edition later than the one the
//! captured scope holds.
//!
//! [`LocateResolver`] fills one locate document's node and link slots and returns the source's
//! capped properties, reading both against the live temporal axes. [`TypeUrlResolver`] resolves
//! ontology type uuids to versioned URLs for the edges trailer, a lookup with no temporal axes
//! and no entity edition in it. A uuid derives from the versioned URL it names, which holds the
//! pair steady once resolved. [`CachedTypeUrlResolver`] answers a repeat from its retained result.
//! [`GraphDatabaseClient`] answers both resolvers against the serving store pool. [`NodeSlot`]
//! and [`EdgeSlot`] are the slot domains a resolver fills in place, and [`scalar`] is the value
//! shape a property read can take.
//!
//! The module also holds [`visibility::visibility_proof`], which resolves the rows an actor may
//! receive before any document gathers from the captured scene.

mod client;
mod columns;
mod locate;
pub(crate) mod scalar;
mod statements;
mod type_urls;
pub(crate) mod visibility;

// Locate document fixtures construct typed resolver answers.
#[cfg(test)]
pub(crate) use self::locate::{LocateLink, LocateNode, LocateProperties};
Comment thread
github-advanced-security[bot] marked this conversation as resolved.
Fixed
Comment thread
github-advanced-security[bot] marked this conversation as resolved.
Fixed
Comment thread
github-advanced-security[bot] marked this conversation as resolved.
Fixed
pub(crate) use self::{
client::{GraphDatabaseClient, HydrateError},
Comment thread
github-advanced-security[bot] marked this conversation as resolved.
Fixed
Comment thread
github-advanced-security[bot] marked this conversation as resolved.
Fixed
Comment thread
github-advanced-security[bot] marked this conversation as resolved.
Fixed
Comment thread
github-advanced-security[bot] marked this conversation as resolved.
Fixed
columns::{EdgeSlot, NodeSlot},
locate::{LocateEntity, LocateRequest, LocateResolver, LocateResponse},
Comment thread
github-advanced-security[bot] marked this conversation as resolved.
Fixed
Comment thread
github-advanced-security[bot] marked this conversation as resolved.
Fixed
Comment thread
github-advanced-security[bot] marked this conversation as resolved.
Fixed
Comment thread
github-advanced-security[bot] marked this conversation as resolved.
Fixed
Comment thread
github-advanced-security[bot] marked this conversation as resolved.
Fixed
Comment thread
github-advanced-security[bot] marked this conversation as resolved.
Fixed
Comment thread
github-advanced-security[bot] marked this conversation as resolved.
Fixed
Comment thread
github-advanced-security[bot] marked this conversation as resolved.
Fixed
type_urls::{CachedTypeUrlResolver, TypeUrlResolver},
Comment thread
github-advanced-security[bot] marked this conversation as resolved.
Fixed
Comment thread
github-advanced-security[bot] marked this conversation as resolved.
Fixed
Comment thread
github-advanced-security[bot] marked this conversation as resolved.
Fixed
};
Loading
Loading