Supply chain — traceability & recall
The problem. A defect is found in a component from one supplier lot. The clock starts immediately: which finished products contain a part from that lot, which shipments carried them, and which customers received them? Answering it by querying flat tables is slow and error-prone — but the supply chain is a graph (supplier → part → product → shipment), and one traversal turns a recalled lot into the exact, minimal recall list.
This scenario centres on graph traceability: model the chain of custody as relationships, and trace a recall downstream from a single bad lot.
Set up demo data
New workspace? Run this once (Python) to create the chain of custody — a supplier lot built into a product, shipped out — so the downstream recall trace returns the affected items. Safe to re-run.
import datahub_sdk
client = datahub_sdk.DataHubClient.from_env()
client.resources.create(
[datahub_sdk.Resource(external_id=x, name=x, labels=[x]) for x in
["lot_acme_8842", "part_bearing_55", "product_gearbox_910", "shipment_eu_2204"]],
[datahub_sdk.RelForm.by_external_ids("lot_acme_8842", "part_bearing_55", "supplied"),
datahub_sdk.RelForm.by_external_ids("part_bearing_55", "product_gearbox_910", "built_into"),
datahub_sdk.RelForm.by_external_ids("product_gearbox_910", "shipment_eu_2204", "shipped_in")])
1. Model the chain of custody
Each hop is a relationship: a supplier lot supplied a part, a part built_into a
product, a product shipped_in a shipment.
- Java
- Python
- Rust
List<RelForm> chain = List.of(
rel("supplied", "lot_acme_8842", "part_bearing_55"),
rel("built_into", "part_bearing_55", "product_gearbox_910"),
rel("shipped_in", "product_gearbox_910", "shipment_eu_2204"));
client.resources().create(nodes, chain);
static RelForm rel(String type, String from, String to) {
RelForm r = new RelForm();
r.setName(type);
r.setFromExternalId(from);
r.setToExternalId(to);
return r;
}
import datahub_sdk
chain = [
datahub_sdk.RelForm.by_external_ids("lot_acme_8842", "part_bearing_55", "supplied"),
datahub_sdk.RelForm.by_external_ids("part_bearing_55", "product_gearbox_910", "built_into"),
datahub_sdk.RelForm.by_external_ids("product_gearbox_910", "shipment_eu_2204", "shipped_in"),
]
client.resources.create(nodes, chain)
use dataplatform_rust_sdk::relations::RelForm;
let chain = vec![
RelForm::by_external_ids("lot_acme_8842", "part_bearing_55", "supplied"),
RelForm::by_external_ids("part_bearing_55", "product_gearbox_910", "built_into"),
RelForm::by_external_ids("product_gearbox_910", "shipment_eu_2204", "shipped_in"),
];
api.resources.create(nodes, chain).await?;
2. Trace a recall downstream
A bad lot is found. Walk the graph out from lot_acme_8842 and everything reachable
— parts, products, shipments — is exactly what the recall must cover. Nothing more,
nothing missed.
- Java
- Python
- Rust
ResourceNetwork impacted = client.resources().fetchRelated("lot_acme_8842", 10);
impacted.nodes().forEach(n -> {
if (n.getExternalId().startsWith("shipment_"))
System.out.println("recall shipment: " + n.getExternalId());
if (n.getExternalId().startsWith("product_"))
System.out.println("affected product: " + n.getExternalId());
});
impacted = client.resources.fetch_related(external_id="lot_acme_8842", depth=10)
for node in impacted.nodes:
if node.external_id.startswith("shipment_"):
print("recall shipment:", node.external_id)
elif node.external_id.startswith("product_"):
print("affected product:", node.external_id)
use dataplatform_rust_sdk::resources::RelatedResourcesForm;
let impacted = api.resources.fetch_related(
&RelatedResourcesForm::from_external_id("lot_acme_8842").with_depth(10)).await?;
for node in impacted.nodes() {
if node.external_id.starts_with("shipment_") {
println!("recall shipment: {}", node.external_id);
}
if node.external_id.starts_with("product_") {
println!("affected product: {}", node.external_id);
}
}
A generous depth follows the chain as far as it goes; filtering on the supply
relationship types keeps the walk to the chain of custody. See
Correlate alarms with the graph for the traversal pattern.
3. Track flow, segment by facility
Throughput and inventory are series (dc_oslo_throughput_units,
sku_4471_on_hand); group each distribution centre's data with a
dataset so each site sees only its own. A stockout becomes a
stockout event for the planning team.
See the result
Tracing downstream from the bad lot returns exactly what to recall:
affected product: product_gearbox_910
recall shipment: shipment_eu_2204
See also
- Model assets as a graph — modeling the chain of custody.
- Correlate alarms with the graph — downstream traversal in detail.
- Datasets reference — segment data per facility.