Please check the build logs for more information.
See Builds for ideas on how to fix a failed build, or Metadata for how to configure docs.rs builds.
If you believe this is docs.rs' fault, open an issue.
xbbg-async
Async worker-pool engine over xbbg-core for Bloomberg API requests and subscriptions.
Architecture
Engine
├── RequestWorkerPool (round-robin dispatch, default 2 workers)
│ └── Worker threads each owns a Session + Slab<UnifiedRequestState>
│ └── 12 state machines: RefData, HistData, BulkData, IntradayBar,
│ IntradayTick, HistDataStream, IntradayBarStream,
│ IntradayTickStream, Generic, Bql, Bsrch, FieldInfo
├── SubscriptionSessionPool (1 pre-warmed session, 32-session cap by default)
│ └── Sub-worker threads each owns a Session + Slab<SubscriptionState>
├── SchemaCache (in-memory + disk-persisted service schemas)
├── FieldCache (global, disk-persisted field type resolution)
└── Tokio Runtime
Each worker thread owns its own Bloomberg Session — no Arc<Session>, no shared
state, no contention. Requests are dispatched round-robin across the pool;
subscriptions are claimed from a separate session pool.
Key modules
| Module | Purpose |
|---|---|
engine/ |
Engine startup, shutdown, command dispatch |
engine/worker.rs |
Per-worker event loop and request lifecycle |
engine/subscription_pool.rs |
Per-session subscription lifecycle and callbacks |
engine/session_lifecycle.rs |
Shared startup latch and coalesced service-open attempts |
engine/subscription_status.rs |
Topic indexes, lifecycle snapshots, events, and warning publication |
engine/request_sharding.rs |
Shard planning, ordered execution, and schema reconciliation |
engine/state/ |
12 state machines for different Bloomberg operations |
schema/ |
Service schema introspection + disk cache |
field_cache.rs |
Global field-type resolver with disk persistence |
errors.rs |
BlpAsyncError — async-layer error type |
Design decisions
- One Session per thread — Bloomberg's
Sessionis notSync. Rather than wrapping it in aMutex, each worker owns its session outright, eliminating contention entirely. - Slab-based correlation — In-flight requests are tracked with a
Slab, giving O(1) insert/remove and compact memory layout. - Schema + field caching — Service schemas and field metadata are cached to disk, avoiding repeated introspection on startup.
- Shared response decoders — Historical and intraday-bar requests use the same extraction and Arrow builders for accumulated responses and streamed chunks.
- Canonical format vocabulary — Request planning uses the generated
Formatparser, including aliases declared indefs/bloomberg.toml.
Request response decoding
Historical streaming honors field-type hints and includes eidData,
securityError, and fieldExceptions metadata just like accumulated responses.
Intraday-bar responses use a non-nullable ticker column in both delivery modes:
each row carries the request ticker, including an empty request string.
Historical and intraday-bar streams never silently drop a chunk when their bounded channel fills. They terminate with an error after previously accepted chunks; the SDK callback does not wait for the consumer to drain the channel.
BQL keeps separate small-payload (at most 32 KiB) and large-payload JSON parsers, but both use the same Arrow materializer. Secondary columns with omitted values are null-padded in either route, and their first occurrence wins when names repeat; large payloads no longer skip those columns.
Subscription deltas and termination
SubscriptionStream::next() yields sparse SubscriptionUpdate values. Missing
UpdateField entries mean unchanged; UpdateValue::Null is an explicit SDK
clear. Requested arrays/complex values and non-null decode failures are errors.
All-fields mode omits unrequested nonscalars without suppressing a later scalar.
Requested-field extraction uses a bounded present-field scan for wide, sparse messages and retains name lookups for narrow or dense messages. Values remain in requested order. Changed immutable layouts are built once per message while preserving every discovery/type-change version increment. These optimizations neither combine messages nor wait for another event before delivering an update.
SubscriptionState emits updates immediately; it has no flush threshold or
flush() operation. SubscriptionArrowBatcher owns Arrow batching. The public
EngineConfig::subscription_flush_threshold and SubscribeRequest::flush_threshold
options remain consumer-side hints used by bindings, not engine buffering settings.
Arrow conversion appends non-null binary __xbbg_present. Bit i, LSB-first,
maps to schema field i + 2 after timestamp and topic. A present-null field
sets its bit; absence does not. Interpret against each batch's current schema;
xbbg.subscription_presence metadata records the encoding and mapping.
The bounded subscription channel reserves terminal errors outside data capacity.
It yields committed data, one terminal error, then EOF even when sender handles
remain. Bloomberg DATALOSS, DropNewest overflow, and Block forwarding
overflow/timeout terminate with BlpError::SubscriptionDataLoss; resubscribe for
a fresh image. Unattributed/session-wide loss uses topic="*". The SDK callback
never waits for consumer space.
Draining unsubscribe propagates unread failures after cleanup rather than returning a successful partial result. Removing every topic does not detach the handle from engine/session termination. Partial-topic failures and ordinary connection-down notifications retain their nonterminal behavior.