Skip to main content

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.

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;
}

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.

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());
});

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