EATT in BLE Products: When Parallel GATT Transactions Help and When They Do Not
EATT is useful when an independent GATT transaction is delayed behind another transaction on the same ATT bearer, and the host stacks can actually schedule the work on separate bearers. It is not an automatic throughput multiplier. Shared application locks, a serialized client wrapper or limited radio service can leave the original bottleneck unchanged.
This guide uses Bluetooth Core 6.3, Volume 3 Parts F and G, for protocol rules and Zephyr v3.7.0 for a concrete host API example. The two references serve different purposes; using this example does not claim that Zephyr 3.7.0 implements every Core 6.3 feature. Qualify the actual peer hosts, controller firmware and transport resources on the intended LE connection.
Identify the operation that is really blocked
Capture application submission, host acceptance, ATT transmission, peer response and application completion for the delayed operation. Find the interval that dominates the wait. EATT is a candidate when the operation is ready but waits for a different transaction’s response on the same bearer.
The Core 6.3 ATT rules make request/response sequencing a per-bearer restriction. A transaction remains on one bearer; its request and response cannot be split across bearers. Multiple bearers therefore provide a way to separate independent transactions, rather than a way to split one transaction arbitrarily.
Check independence at the application level. A health-status read may be independent of a slow history read. A read intended to verify a preceding configuration write is not independent until that write has the required completion semantics. Preserve those dependencies even if the transport offers more channels.

Prove that enhanced channels were established
For LE, enhanced bearers use L2CAP Enhanced Credit Based Flow Control channels. The Core 6.3 GATT requirements require encryption before these channels are created and while they remain in use, apart from key refresh. Preserve the product’s attribute authorization requirements as well; extra bearers do not grant extra access.
Do not infer EATT support from a controller’s advertised Bluetooth version alone. Verify the host implementation, configuration and actual peer behavior. Keep channel establishment and its failure reason in the diagnostic record. A local API accepting a request is weaker evidence than observing connected enhanced channels and traffic using them.
Zephyr v3.7.0 exposes bt_eatt_connect to request channels and bt_eatt_count to inspect the connected count. Its versioned ATT API warns that the peer may create fewer channels than requested. Build policy around the observed count, not the requested count.
Separate channel policy from workload scheduling
The same Zephyr API defines enhanced-only, unenhanced-only and either-channel options. Those options constrain channel selection; they do not prove that the application submits multiple operations concurrently or that the peer can service them independently. Inspect the specific GATT operation API and any wrapper that serializes requests above it.
Give each logical operation its own lifetime, completion state and dependency list. Keep buffers and callback context valid until the documented completion point. A single global “current request” structure can work accidentally with one bearer and fail when independent responses arrive in a different order.
On the server, examine locks and execution context. If every attribute read waits for the same slow peripheral or runs behind one application work queue, bearer separation may not shorten the wait. The v3.7.0 ATT implementation also documents a deadlock risk when callback-context request queuing blocks the thread needed to process responses. Adding channels is not a substitute for fixing that ownership and blocking pattern.
Run an A/B test that isolates the claimed benefit
Use a controlled peer and two documented, harmless operations: one deliberately slow response path and one independent short read. Implement the slow path without blocking the host thread that must process the comparison operation. Keep the offered workload and relevant connection settings equivalent between runs.
- Measure the independent read by itself to establish its baseline path.
- Run both operations with only the unenhanced bearer available. Record whether the short read waits behind the slow transaction.
- Establish enhanced channels, record their actual count, and repeat the same workload.
- Confirm in the trace that the transactions use distinct bearers and overlap as intended.
- Compare application completion latency, failure behavior and resource use, not just total bytes transferred.
- Repeat with the real production wrapper and peer firmware; a synthetic harness pass does not establish application behavior.
These are proposed tests, not reported measurements. A favorable result supports the specific workload and peer combination tested. It does not establish a universal speedup or a guaranteed latency bound.
If the short read still waits, locate the earliest shared bottleneck: application serialization, host queueing, peer execution, L2CAP resource pressure or radio scheduling. Avoid changing MTU and PHY at the same time, because that makes it difficult to attribute an improvement to bearer concurrency.
Make fallback a first-class product behavior
Define what happens when no enhanced bearer is available, fewer than requested are established, or one is lost. If the operation permits the unenhanced bearer, provide a bounded serialized path and an honest degraded-performance state. If a higher-layer requirement genuinely needs EATT, fail that capability check explicitly rather than pretending an equivalent path exists.
Test the following cases separately:
- A peer that supports only the unenhanced path.
- A compatible peer that has insufficient channel resources.
- Channel creation before the required security state is ready.
- Loss of one enhanced channel during an outstanding operation.
- Loss of the underlying connection, followed by reconnection and new channel establishment.
Do not duplicate an uncertain write across another bearer merely because its first transaction stalled. Resolve application-level outcome and retry eligibility first. Also keep dependent operations ordered during fallback; a mode change must not turn a verified write-then-read sequence into two unrelated requests.
Hypothetical example: a status read waits behind history retrieval
Imagine a hypothetical maintenance tool retrieving a device’s history while an operator requests a health-status update. With one outstanding request path, the status query waits for the current history response. Separate enhanced bearers could allow the independent status transaction to progress sooner.
Now suppose the server holds a global data lock throughout history retrieval. Both bearers reach the server, but the status read still waits on that lock. The correct conclusion is that transport concurrency exists and the application remains serialized. A useful next step would be reviewing snapshot or lock scope, then rerunning the same test.
Adopt EATT when the trace demonstrates a relevant blocking reduction and the compatibility, memory and recovery costs are acceptable. Keep the one-bearer behavior tested as deliberately as the multi-bearer case. That produces a defensible concurrency feature instead of a Bluetooth-version claim.