Session and Registries#
A VortexSession is a type-indexed map that holds all the runtime state for the Vortex
ecosystem. Each major component of Vortex defines its own session variable type containing a
registry of plugins. There is typically one session per process, and it is passed explicitly
through the API rather than relying on global or thread-local state.
Plugins are themselves also able to register state in the session, for example to hold caches or other shared resources.
Design#
The session is built on two primitives from the vortex-session crate:
VortexSession– a cloneable, thread-safe map from RustTypeIdto a boxed value. Any type that isSend + Sync + Debug + 'staticcan be stored as a session variable.Registry<T>– a concurrent map from string IDs to values of typeT, used by each component to look up registered plugins at runtime.
Because VortexSession is backed by an Arc<DashMap>, cloning is cheap and all clones share
the same state. This makes it safe to hand the session to multiple threads, tasks, or I/O
operations without coordination.
Component Registries#
Each Vortex crate defines a session variable that holds a registry for its extension points:
Session Variable |
Crate |
Registry Contents |
|---|---|---|
|
|
Extension dtype vtables (Date, Time, …) |
|
|
Array encoding vtables (ALP, FSST, …) |
|
|
Scalar expression vtables |
|
|
Layout encoding vtables (Flat, Chunked, …) |
|
|
Async runtime handle |
|
|
CUDA context, kernels, and stream pool |
Session variables are created lazily on first access with their Default implementation, which
registers the built-in plugins for that component. For example, ArraySession::default()
registers the 14 built-in encodings (Null, Bool, Primitive, Struct, etc.), and
LayoutSession::default() registers the 5 built-in layouts (Flat, Struct, Chunked, Zoned, Dict).
Registering Plugins#
Plugins register with the session by accessing the relevant component and calling register:
// Register a custom array encoding
session.arrays().register(MyEncoding::ID, &MyEncoding);
// Register a custom layout
session.layouts().register(MyLayout::encoding());
// Register a custom expression
session.expressions().register(MyExprVTable);
Crates that bundle multiple plugins typically expose an initialize function that registers
everything at once. The top-level vortex crate calls these during VortexSession::default()
to register all built-in encodings.
Explicit Passing#
Sessions are passed explicitly through constructors and method arguments. This means every API that needs access to registries – file readers, writers, scan builders, layout readers – receives the session directly rather than reaching for global state.
// Opening a file
session.open_options()
.open(reader)
.await?;
// Writing a file
session.write_options()
.write(&mut file, array_stream)
.await?;
// Scanning a layout
ScanBuilder::new(session.clone(), layout_reader)
.with_filter(expr)
.into_array_stream()?;
Many APIs use extension traits to provide ergonomic methods directly on the session. For example,
OpenOptionsSessionExt adds .open_options() to any session that has ArraySession,
LayoutSession, and RuntimeSession registered. This lets the type system enforce that the
required components are present.
Constructing a Session#
The VortexSession::default() provided by the top-level vortex crate constructs a session with
all built-in components and encodings:
let session = VortexSession::default();
For tests or specialized use-cases, sessions can be assembled from individual components using
the .with::<T>() builder:
let session = VortexSession::empty()
.with::<ArraySession>()
.with::<LayoutSession>()
.with::<ExprSession>()
.with::<RuntimeSession>();