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 intellistream_datahub_sdk

client = intellistream_datahub_sdk.DataHubClient.from_env()

client.resources.create(
[intellistream_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"]],
[intellistream_datahub_sdk.RelForm.by_external_ids("lot_acme_8842", "part_bearing_55", "supplied"),
intellistream_datahub_sdk.RelForm.by_external_ids("part_bearing_55", "product_gearbox_910", "built_into"),
intellistream_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​