11 — I/O Analysis and Heavy Consumers

I/O analysis and heavy consumers

The I/O tab shows traffic in and out of the org. Inbound = external systems calling Salesforce (API, REST, Bulk, SOAP). Outbound = Salesforce calling external systems (Apex callouts, External Services, External Data Sources, Outbound Messages).

The data comes from ELF — the same event log machinery as the DML tab, but a different set of event types:

Triggered by Analyze I/O in the toolbar. Date range selection mirrors the DML analysis. Output:

Inbound: who calls into Salesforce

The Inbound sub-tab is a 5-level drill:

Caller (Connected App / ECA / unidentified)
└── Endpoint type (sobject / query / composite-sobjects / bulk-query / bulk-ingest / apexrest / connect / limits / soap / analytics / other)
└── Object (the SObject in the request URI — only for object-bearing endpoint types)
└── Endpoint (normalised URL pattern with record IDs collapsed to {id})
└── User (15-char USER_ID with per-user calls + UA + run / DB time)

Each row carries: total calls, rows processed, errors (4xx + 5xx for REST, REQUEST_STATUS = F for SOAP), average run-time (ms), average DB time (ms), backbone DML linkage (rows of DML written in the same REQUEST_IDs as this caller's API rows).

What's a "caller"

Salesforce stamps every API row with a CLIENT_NAME — usually a Connected App label or an External Client App identifier. The caller column shows the raw client ID with the friendly label from app_info.json next to it when known.

Rows with no CLIENT_NAME collapse into a single (unidentified inbound) bucket. Common sources:

The User-Agent breakdown on each unidentified row helps you tell curl/8 from python-requests from Mozilla/5.0.

Endpoint types

REST traffic is classified by the URI pattern:

SOAP API rows land under soap and are object-bearing too (the SOAP request says which sObject it's about).

Backbone DML linkage

ELF includes a REQUEST_ID. Stood Flows joins inbound API rows to DatabaseSave rows on the same REQUEST_ID to figure out "for this caller's POST/PATCH/DELETE on this endpoint, what DML rows were actually written?".

The result lands in the DML rows column on each row of the drill. A REST POST that wrote 200 Account rows shows 200 here; a GET shows 0. Entities breaks down those DML rows by sObject. This is how you spot integrations that purport to "just update one record" but actually trigger 50 secondary writes through cascading triggers.

Filters

Above the tree:

The two filters compose. The active filter is shown above the tree and clearable with one click.

Heavy consumers

There's no explicit "heavy consumer" label — the tree is sorted by total call count descending by default. The top caller is your heaviest. The percentages in the column headers show "X% of all inbound calls" for whatever level you're at.

A useful heuristic: any caller above 20% of total inbound traffic is a primary integration. Anything contributing to the long tail under 1% is noise; group it mentally with the rest. The interesting cases are in the 1-15% band — heavy enough to matter, light enough that they might be runaway scripts or misconfigured polling.

Outbound: where does Salesforce call

The Outbound sub-tab is a 4-level drill:

Destination (host of the callout target, e.g. "stripe.com" or "Reservation__x")
└── Source type (External: Cross-Org / External: Custom Apex / External: OData / Apex / Flow / External Services / Outbound Message)
└── Path (normalised request path)
└── User (the user under whose context the callout ran)

Each row carries: total calls, bytes in / out, errors, total time (ms), per-method breakdown.

Named credentials vs hardcoded URLs

When a callout goes through a Named Credential, its identity is recorded — the destination row shows the credential name in the Named Credentials column. When the URL is hardcoded in Apex / Flow, the credential column is empty.

This is how you find configuration debt: every (none) row is a callout that someone wrote with the URL inline rather than via Named Credentials. Often those are the ones nobody documented.

External Data Sources (Salesforce Connect)

When a destination is an external object (suffix __x) — e.g. PropertyAsset exposed via OData — the drill groups it under a banner that names the ExternalDataSource record it belongs to. The banner shows:

So in one place you see "this OData source has 4 external objects, here's their traffic". Without this grouping the rows would be scattered alphabetically across destinations.

Adapter categories

The source type column carries one of:

Each has a different licensing implication. Cross-Org callouts count differently from OData; OData has its own daily call budget per org. The panel doesn't enforce these — it just shows you the volume.

Heavy callouts and errors

Sort by Errors descending to find destinations failing systematically. A 70%-error destination is almost always a misconfigured callout or a dead endpoint that nobody noticed because the records still update via secondary writes.

Sort by Total time to find destinations that are slowing your integrations down. A destination averaging 4 seconds per call is a candidate for caching, retry-with-backoff, or just removal.

Anchoring

Same right-click pattern as the rest of the app. An issue created from a caller / destination row carries an anchor that survives subsequent I/O re-analyses and brings you straight back to the right node in the tree.

Common questions

No external data sources are showing up. Either the org has no Salesforce Connect, or the sf user can't read the Tooling-API CustomObject.ExternalDataSourceId field. Check the user's perms.

Inbound is empty. Verify View Event Log Files and that API / RestApi events are actually being logged. Some orgs disable specific event types.

A Connected App shows as a 15-char ID with no label.app_info.json couldn't resolve the label. Usually means the connected user can't read ConnectedApplication for that app. Add the label manually by editing the JSON, or run an analysis as a user with broader Setup access.

Outbound shows duplicate destinations with similar hostnames. ELF records hosts exactly as the callout was made. A typo in Apex (api.exmaple.com) creates a distinct row. Worth tagging as a code-cleanup target.

Published with Nuclino