Skip to main content
pub struct BulkInserter<T>  /* private fields */ 

Shard-aware high-performance bulk inserter.

Routes records to the correct worker based on shard key computation, enabling true multi-head ingest. Uses serde_avro_fast for fast Avro encoding and rayon for parallel processing.

It also provides:

  • Support for insert options (update_on_existing_pk, etc.)
  • Permission checking before insertion
  • Table replication detection (replicated tables use head node only)
  • Multi-head ingest configuration
  • Retry logic with configurable max retries

HA Failover Safety

This implementation uses ArcSwap for lock-free atomic updates of routing state during HA failover. This ensures:

  • No race conditions when routing table changes during insert operations
  • No out-of-bounds indexing from stale routing data
  • Records are always routed using consistent, up-to-date routing information

Implementations

Source

pub async fn new( kinetica: Arc<Kinetica>, table_name: String, batch_size: usize, num_workers: usize, ) -> Result<Arc<Self>, KineticaError>

Creates a new shard-aware BulkInserter instance with default options.

This is a convenience method that calls with_options with None for options. For more control over insert behavior, use with_options directly.

  • kinetica - Shared Kinetica client
  • table_name - Target table for inserts (including schema, e.g., “my_schema.my_table”)
  • batch_size - Records per batch (recommended: 50000)
  • num_workers - Number of flush workers, i.e. of batches sent concurrently; used exactly as given. 0 sizes the pool at one worker per available CPU.
Source

pub async fn with_options( kinetica: Arc<Kinetica>, table_name: String, batch_size: usize, num_workers: usize, options: Option<BulkInserterOptions>, ) -> Result<Arc<Self>, KineticaError>

Creates a new shard-aware BulkInserter instance with the specified options.

This constructor:

  1. Validates user permissions for the table
  2. Validates batch size
  3. Checks if the table is replicated
  4. Configures multi-head ingest (if available and table is not replicated)
  5. Sets up worker queues and routing
  • kinetica - Shared Kinetica client
  • table_name - Target table for inserts (including schema, e.g., “my_schema.my_table”)
  • batch_size - Records per batch (recommended: 50000); must be > 0
  • num_workers - Number of flush workers, i.e. of batches sent concurrently; used exactly as given. 0 sizes the pool at one worker per available CPU.
  • options - Optional BulkInserterOptions for configuring insert behavior

Returns an error if:

  • User doesn’t have required permissions (insert, and update if using upsert)
  • Table doesn’t exist
  • Batch size is less than 1
  • Unable to configure multi-head ingest
Source

pub async fn with_schema( kinetica: Arc<Kinetica>, table_name: String, batch_size: usize, num_workers: usize, options: Option<BulkInserterOptions>, schema_str: &str, ) -> Result<Arc<Self>, KineticaError>

Creates a new shard-aware BulkInserter instance with an explicit schema.

This constructor is useful for types that cannot implement Default, such as GenericRecord which requires a Type reference at construction time.

  • kinetica - Shared Kinetica client
  • table_name - Target table for inserts (including schema, e.g., “my_schema.my_table”)
  • batch_size - Records per batch (recommended: 50000); must be > 0
  • num_workers - Number of flush workers, i.e. of batches sent concurrently; used exactly as given. 0 sizes the pool at one worker per available CPU.
  • options - Optional BulkInserterOptions for configuring insert behavior
  • schema_str - The Avro schema JSON string for the record type
Source

impl BulkInserter<String>

An inserter of JSON records.

Each record is one JSON object, as text. Records are sent as JSON arrays to the ranks’ /insert/records/json, each to the rank of a random shard: the client does not read their keys. A batch the server fails is reported as any other, through drain_errors, and one that meets a connection failure is retried and failed over like any other.

Source

pub async fn new_json( kinetica: Arc<Kinetica>, table_name: String, batch_size: usize, num_workers: usize, ) -> Result<Arc<Self>, KineticaError>

Creates a JSON inserter with the default JsonOptions and options.

num_workers is as for BulkInserter::with_schema.

Source

pub async fn with_json_options( kinetica: Arc<Kinetica>, table_name: String, batch_size: usize, num_workers: usize, options: Option<BulkInserterOptions>, json_options: JsonOptions, ) -> Result<Arc<Self>, KineticaError>

Creates a JSON inserter. With JsonOptions::is_validate_json, each record inserted must be a JSON object.

Source

pub fn insert(&self, record: Arc<T>) -> Result<(), QueueError<T>>

Inserts a single record with shard-aware routing.

The record’s shard key is computed using ShardKeyExtractor::shard_key_values(), and the record is routed to the worker that owns the corresponding shard.

Returns an error if the record cannot be routed — see Self::route_to_worker. The record is not queued in that case, and comes back in the QueueError.

Also returns an error after close when this record fills a batch: the batch cannot be sent, and its records come back.

This method uses ArcSwap::load() to get the current routing state, which provides:

  • Lock-free atomic read of the routing configuration
  • Consistent routing even during HA failover (no torn reads)
  • No race conditions with concurrent routing updates

A JSON inserter whose JsonOptions ask for it first checks that the record is a JSON object.

Source

pub async fn insert_with_backpressure( &self, record: Arc<T>, ) -> Result<(), KineticaError>

Inserts a single record with backpressure control.

This async method will wait if the maximum in-flight batches limit is reached, providing memory-bounded operation for high-throughput scenarios.

This method uses ArcSwap::load() to get the current routing state atomically.

Source

pub fn insert_batch(&self, records: Vec<T>) -> Result<(), QueueError<T>>

Inserts a batch of records with shard-aware routing.

This method is more efficient than calling insert() in a loop because:

  1. Worker indices are computed in parallel using rayon
  2. Records are grouped by destination before any lock is taken
  3. Each destination’s queue is locked once per call, not once per record

The records go into the calling thread’s stripe of each queue, as insert puts them, and a batch is sent each time that stripe reaches the batch size. No batch is larger than the batch size.

  • records - A vector of records to insert. Each record will be wrapped in Arc internally.

The records before the first that cannot be routed are queued, and that record and every one after it come back in the QueueError with its error. A list whose JSON does not validate is not queued at all, and comes back whole.

Source

pub fn insert_batch_arc( &self, records: Vec<Arc<T>>, ) -> Result<(), QueueError<T>>

Inserts a batch of records that are already wrapped in Arc.

This is useful when records are shared or when the caller already has Arc-wrapped records.

  • records - A vector of Arc-wrapped records to insert.
Source

pub fn count_inserted(&self) -> i64

Returns the total number of records successfully inserted.

Source

pub fn count_updated(&self) -> i64

Returns the total number of records successfully updated (for upserts).

Source

pub fn table_name(&self) -> &str

Returns the table name.

Source

pub fn batch_size(&self) -> usize

Returns the batch size.

Returns whether the target table is replicated.

Returns whether multi-head ingest is enabled.

Returns whether the head node is being used (replicated table or multi-head disabled).

Source

pub fn max_retries(&self) -> usize

Returns the current maximum retries for failed insertions.

Source

pub fn set_max_retries(&self, max_retries: usize)

Sets the maximum retries for failed insertions.

Returns a reference to the insert options.

The JSON options of a JSON inserter, or those set since.

Source

pub fn set_json_options(&self, json_options: JsonOptions)

Replaces the JSON options, which a JSON inserter reads for each record it is given. They do not make an inserter of Avro records a JSON one.

Returns the flush options.

The timed flush options.

Source

pub fn set_timed_flush_options( self: &Arc<Self>, flush_options: Option<FlushOptions>, )

Replaces the timed flush options, None restoring the defaults (no timed flush), and restarts the timed flush with them. A scheduled timed flush is stopped first, and a new one starts if the new interval is positive, its first run one interval from now.

Returns whether return_individual_errors mode is active.

Returns the number of records currently buffered in worker queues.

These are records that have been queued via insert() but not yet sent to the flush workers. Call flush() to send these records.

Returns the estimated number of records in-flight (sent but not yet confirmed).

This is an estimate based on pending_batches * batch_size. The actual number may be slightly different due to partial batches.

Source

pub fn kinetica(&self) -> &Arc<Kinetica>

The connection this inserter sends through.

Drains and returns all queued errors. This clears the error queue, so subsequent calls return new errors only.

Source

pub fn error_count(&self) -> usize

Returns the number of queued errors without draining.

Returns the total number of records that failed to insert. This sums the record_count from all errors in the queue.

Drains and returns all queued warnings.

Source

pub fn warning_count(&self) -> usize

Returns the number of queued warnings without draining.

Returns whether timed flush is currently running.

Stops the timed flush task.

Returns the maximum number of in-flight batches configured.

Returns the number of available in-flight permits.

Returns backpressure metrics for monitoring.

This provides visibility into the backpressure state of the inserter, useful for monitoring and tuning max_in_flight_batches.

Source

pub async fn flush(&self) -> Result<(), KineticaError>

Flushes all buffered records to the server and waits until they have been acknowledged.

When this returns Ok(()), every record queued before the call has either been inserted or recorded in the error queue — so a subsequent read is guaranteed to see them.

Check error_count or drain_errors afterwards to see whether any records failed; a returned Ok means “all batches finished”, not “all batches succeeded”.

Use flush_no_wait if you deliberately want to hand the batches off without waiting.

Hands all buffered records to the flush workers without waiting for them to complete.

Prefer flush — after this returns, the records have not necessarily reached the server, so reading the table back may not see them. This exists for background flushing (the timed-flush task uses it) where blocking would stall the caller.

Source

pub fn pending_batches(&self) -> i64

Returns the number of pending batches (sent but not yet completed).

Source

pub async fn close(&self) -> Result<(), KineticaError>

Closes the BulkInserter, flushing all records and waiting for completion.

This method:

  1. Flushes all buffered records to the flush workers
  2. Waits for all pending batches to complete
  3. Stops the flush workers and the timed flush

The inserter cannot send anything afterwards: an insert still buffers, but one that fills a batch, or a flush() with records buffered, fails.

IMPORTANT: This method will wait indefinitely until all pending batches complete. It only returns when:

  1. All records have been successfully inserted, OR
  2. All records have either succeeded or failed (with errors queued)

Failed records are tracked in the error queue and can be retrieved via drain_errors(). The method guarantees that inserted + updated + failed = queued.

Returns Ok(()) when all batches have completed (success or failure). Check error_count() or drain_errors() to see if any records failed.

Source

pub async fn close_with_timeout( &self, timeout: Duration, ) -> Result<(), KineticaError>

Closes the BulkInserter with an optional timeout.

If timeout is reached, returns an error but records may still be in-flight. Prefer using close() without timeout to ensure all records are flushed.

  • timeout - Maximum time to wait for pending batches to complete

Returns Ok(()) if all batches completed, or Err if timeout was reached.

Returns the number of shard key columns used for routing.

Returns the names of shard key columns.

Returns the routing table size (number of shards).

Source

pub fn worker_count(&self) -> usize

Returns the number of worker queues.

Returns the number of times the connection has switched clusters due to failover.

Source

pub fn ha_ring_size(&self) -> usize

Returns the current HA ring size (number of clusters).

Returns the current head node URL.

Trait Implementations

Source

impl<T> Drop for BulkInserter<T>

Dropping an inserter stops its background tasks (they hold only Weak references, and the shutdown sender goes with it), so nothing leaks. It cannot flush, since that is asynchronous: records still buffered, or queued but not yet sent, are lost. Say so, rather than lose them silently.

Source

fn drop(&mut self)

Executes the destructor for this type. Read more
Source

fn pin_drop(self: Pin<&mut Self>)

🔬This is a nightly-only experimental API. (pin_ergonomics)
Execute the destructor for this type, but different to Drop::drop, it requires self to be pinned. Read more