Recipe 37 — Data-Affinity Placement
Problem: You’re running a compute tasklet that reads from a leased memory region. If the tasklet lands on a different node than the data, every read crosses the fabric — adding latency and consuming bandwidth.
Solution: Use Affinity::new(Strength::Preferred, Target::node(data_node))
to tell the scheduler to prefer the node that holds your data lease.
Affinity is not the same thing as replicated-resource placement policy. Placement defines the legal failure-domain envelope for a logical resource. Affinity then helps the scheduler choose a specific node, rack, GPU, or data-local lease inside that envelope. Affinity must not expand placement into a provider, region, or availability zone that the resource policy did not authorize.
Core grafOS API Path
The direct grafos-std path is: acquire the data lease, read the node that
owns it, then request compute with an affinity hint to that node.
use grafos_std::cpu::CpuBuilder;use grafos_std::mem::MemBuilder;use grafos_std::affinity::{Affinity, Strength, Target};
// Step 1: Allocate a memory lease — note which node it lands on.let mem_lease = MemBuilder::new().bytes(1024 * 1024).lease_secs(300).acquire()?;let data_node_id = mem_lease.node_id(); // u128
// Step 2: Allocate a CPU lease with preferred data affinity.let cpu_lease = CpuBuilder::new() .single_core() .affinity(Affinity::new(Strength::Preferred, Target::node(data_node_id))) .lease_secs(60) .acquire()?;
// The scheduler will prefer placing the CPU lease on the same node as// the memory lease, reducing cross-fabric reads.# let _ = cpu_lease;# Ok::<(), grafos_std::error::GrafosError>(())When to use Required instead of Preferred:
Use Strength::Required only when the workload cannot function if
the data is remote — for example, a shared-memory tasklet where workers
access the memory region via direct pointers. If the scheduler can’t
find capacity on the data node, the request fails rather than silently
degrading to remote access.
// Required: fail if we can't co-locate.let cpu_lease = CpuBuilder::new() .single_core() .affinity(Affinity::new(Strength::Required, Target::node(data_node_id))) .lease_secs(60) .acquire()?;# let _ = cpu_lease;# Ok::<(), grafos_std::error::GrafosError>(())When NOT to use data affinity:
- If your workload does a single small read at startup and then computes independently — the fabric read cost is amortized over the compute time, and affinity over-constrains placement for no benefit.
- If you need fault tolerance more than locality — over-constraining to a single node means you can’t fail over. Use replicated-resource placement policy to express the failure-domain envelope, then use affinity as a local preference within each allowed domain.
Multi-Cloud Placement Variant
For a replicated resource, start with placement and then layer data affinity underneath it:
use grafos_replicated::{ FailureDomain, FailureDomainLevel, PlacementPolicy, PlacementPreference, ReplicatedResourceSpec, ResourceKind,};use grafos_std::affinity::{Affinity, Strength, Target};
let placement = PlacementPolicy::new() .allow(FailureDomain::region("aws", "us-east-1")) .allow(FailureDomain::region("gcp", "us-central1")) .require_distinct(FailureDomainLevel::CloudProvider) .prefer(PlacementPreference::PreferLowerLatency);
let orders = ReplicatedResourceSpec::builder("orders", ResourceKind::Queue) .placement(placement) .build()?;
// Within whichever allowed domain the scheduler is evaluating, prefer the// node that already hosts the hot data lease. This preference cannot move// the queue outside the replicated-resource placement envelope above.let data_node_id: u128 = 42; // node holding the data leaselet local_affinity = Affinity::new(Strength::Preferred, Target::node(data_node_id));# let _ = (orders, local_affinity);# Ok::<(), grafos_replicated::PolicyError>(())This is the intended boundary: placement carries the availability promise; affinity carries the locality hint.
See also:
docs/spec/affinity-request-model.md— wire formatdocs/grafos/affinity-taxonomy.md§5.2 — state/data affinity categorydocs/grafos-std-guide.md— affinity constraints subsection