Changelog Connectors Performance ~14 min read

CocoIndex Changelog 1.0.8 - 1.0.16

Changelog for CocoIndex 1.0.8-1.0.16: persistent per-component state, LiveMap, rate limiting, batched target writes, BigQuery and Snowflake connectors.


Updated Jul 9, 2026

This is the second changelog since the v1.0 launch, covering nine releases (1.0.8–1.0.16). The focus shifts from initial hardening into new engine primitives for stateful, production-grade pipelines, a major expansion of cloud and enterprise connectors, and continued depth on agent ergonomics.

CocoIndex builds fresh, structured context for AI from sources such as PDFs, codebases, emails, screenshots, and meeting notes, and keeps it up to date with an incremental engine. The source is on GitHub.

Why this release cycle matters

For developers building retrieval, extraction, and knowledge-graph pipelines, the most significant changes are:

  • Persistent per-component state. coco.use_state() lets pipeline components carry state that survives across runs, opening the door to accumulators, counters, and stateful deduplication without an external store.
  • In-memory intermediate collections. LiveMap is a live, in-memory keyed collection that bridges a producing and a consuming stage of the same run: one side declares entries, the other consumes them through mount_each, which grows and shrinks one live component per entry as the data changes — a producer/consumer hand-off that pure declarative pipelines can’t express.
  • Batched target writes. Row-level writes to the same target from different components can now be coalesced into one apply, collapsing N write transactions into one — a direct win for connectors like LanceDB and cloud warehouses where per-transaction cost dominates.
  • Token-bucket rate limiting. RateLimiter is now a first-class engine primitive: a consistent, cooperative throttle shared across pipeline components, replacing hand-rolled sleep pacing at each call site with one fair, budgeted rate.
  • Enterprise cloud connectors. BigQuery, Snowflake, and Azure Blob Storage land alongside sparse-vector Qdrant support and a new Valkey vector store connector, completing coverage of the major cloud data platforms.

Engine

coco.use_state(): persistent per-component state

coco.use_state() gives any pipeline component a persistent, typed state slot that survives across update cycles. The state is stored in the LMDB state store alongside other engine metadata, keyed by a StableKey (not just a plain string, as of #2085). A type_hint parameter added in v1.0.15 enables typed deserialization so components can round-trip structured state objects cleanly.

Why it matters: the incremental engine already knows what changed between runs. use_state lets components know what they have already done. This enables accumulators (running totals, seen-entity sets), watermark trackers, and stateful deduplication, patterns that previously required an external Redis or Postgres sidecar. The net effect is that a wider class of stateful logic can now live inside the pipeline declaration, subject to the same incremental reconciliation semantics as the rest of CocoIndex.

Two update cycles of a component calling use_state. In cycle 1 the state prefetch misses and returns the initial value 0, then commits 1 to the LMDB state store. In cycle 2 the prefetch hits and returns 1, then commits 2. The counter value persists across cycles in the store, so the component remembers what it already did.

LiveMap: in-memory intermediate collections

LiveMap is a new in-memory keyed collection that bridges a producing and a consuming half of the same live run. Producing components declare entries into it as target states (LiveMap.declare_entry), and the consuming side reads that same in-memory dict as a live source through mount_each. Where coco.stats_group provides observability over a data slice, LiveMap provides computation over one: a mutable intermediate collection the pipeline both writes and reads, decoupled through the shared map.

Because LiveComponent classes can now be used inside mount_each(), the consuming side is a dynamic live component tree — one live component per entry, mounted and dropped as producers add and stop declaring keys.

How LiveMap bridges producing and consuming components. On the left, producing components declare entries into LiveMap — adding and updating keys, and dropping a key when its producer stops declaring it. LiveMap, a keyed collection held in memory for the live run, exposes those entries as a live source. On the right, each key drives one live component through mount_each, so the component tree grows and shrinks with the data: components are mounted, re-run, or dropped as keys appear, change, and vanish.

Token-bucket RateLimiter

RateLimiter is now a first-class engine primitive implementing a token-bucket algorithm. It provides a cooperative throttle shared across pipeline components: any component that acquires a token from the same RateLimiter instance participates in the same budget, and concurrent callers are served FIFO so none starve. This replaces hand-rolled sleep pacing at each call site with one budgeted, coordinated rate.

Combined with the existing retry-on-429 (HTTP rate limit) behavior added in earlier cycles, RateLimiter gives pipelines deterministic, budget-aware flow control rather than reactive backpressure alone.

Before and after the token-bucket RateLimiter. Before, each integration hand-rolls its own sleep loops and 429 retries, producing uncoordinated bursts and reactive backpressure against the external API. After, every component calls acquire() on one shared token bucket that refills at max_rows_per_second up to a burst_window capacity, so requests leave at a steady, budgeted rate; components wait when the budget is empty, and retry-on-429 remains only for the unexpected.

Preview mode for update actions

Update actions can now be previewed before they are applied. A pipeline run can describe the adds, reprocesses, and deletes it would perform, without writing to any target, enabling dry-run validation and change auditing before committing to a production store.

Preview mode as a dry run. An update() call with preview enabled produces a plan listing 3 adds, 2 reprocesses, and 1 delete, computed but not applied. A blocked, dashed arrow marked "no write" leads to a dashed, untouched target store, so a run reports what it would change without writing a single row to production.

Batched target writes across components

Row-level writes to the same target from different components can now be coalesced into a single batched apply. The common shape is mount_each(..., table_target), where many child components each declare one row into the same target — but the coalescing is keyed by the target sink, not by any sibling relationship, so any components whose writes land on the same sink concurrently are batched together. Previously each component submitted its leaf actions directly to the sink after precommit, so the connector saw a stream of single-row calls even though its API already accepts a batch. In practice a 50-row ingest into LanceDB produced roughly 50 merge_insert commits — and 50 table versions — instead of one (#2219).

An app-scoped batcher, keyed by the target sink, merges concurrent commits that hit the same sink before the connector’s apply call (#2221). It reuses CocoIndex’s existing Batcher primitive rather than a bespoke queue, so leaf-target batching shares the same path as the engine’s other batched work and adds no debounce latency to a lone write. Batching preserves order, each output mapping back to its input; container targets, which return per-action child handlers, stay on the direct apply path, while the batched path handles leaf writes. Sink lifetime drives cleanup: a keeper owns the batcher one-to-one and drops its registry entry the moment the sink goes away, so a pipeline with an unbounded number of sink functions cannot leak batcher entries.

Why it matters: for connectors with real per-operation cost, such as LanceDB and cloud warehouses, write cost is dominated by the number of write transactions — round-trips and commits — not by row count. Coalescing N single-row applies into one batched apply collapses N write transactions into one, cutting that overhead directly. For a store like LanceDB it also collapses N table versions into one, which keeps version history and compaction manageable under heavy fan-out.

Batched target writes across mount_each children. Before, three child components each apply a single row to LanceDB, producing three commits and three table versions. After, the same three rows pass through a batcher and land as one apply, producing a single commit and one table version.

Engine performance improvements

  • Prefetch fn-memos and user-states in one read transaction: the engine now batches all LMDB reads for function memos and user states into a single read transaction per cycle, replacing N individual reads (#2076).
  • Minimize serialization for UserStateCache: reduces round-trip cost for state reads during hot-path execution (#2127).
  • LMDB map auto-resize: the state store now automatically resizes the LMDB map when it fills, eliminating the manual LMDB_MAP_SIZE tuning that previously required operator intervention (#2231).
  • LMDB map size rounded to page size: map sizes are now aligned to the OS page boundary, preventing subtle allocation failures on some Linux kernels (#2109).

Other engine fixes

  • coco.fn callable outside a component context: coco.fn-decorated functions can now be called directly in scripts and tests without being inside a mounted pipeline component (#2101).
  • !-prefixed negation in excluded_patterns: source file filters now support !pattern to explicitly un-exclude a path that matched an earlier pattern, following the .gitignore-style rule ordering (#1901).
  • Retry transient LiteLLM embedding errors: transient failures (network blips, upstream 5xx) during embedding calls are retried automatically (#2090).
  • Reject rows with missing or null primary keys: the engine now fails fast rather than silently writing corrupt rows (#2239).

Connectors

Target: Snowflake (v1.0.15)

A native Snowflake target connector lands, bringing CocoIndex’s declare-target-state model to one of the most widely deployed cloud data warehouses. Snowflake targets participate in the same incremental reconciliation as all other CocoIndex targets: rows for deleted sources are removed automatically, and only changed rows are upserted.

Target: BigQuery (v1.0.16)

A Google BigQuery target connector was added alongside the Snowflake connector in the same cycle. For organizations already running Google Cloud infrastructure, this completes cloud data warehouse coverage across the major platforms.

Source: Azure Blob Storage (v1.0.16)

An Azure Blob Storage source connector mirrors the OCI and S3 source APIs. For teams on Azure infrastructure, this enables the same live-mode, incremental bucket-watching behavior available to AWS and OCI users.

Target: Valkey vector store (v1.0.12)

Valkey, the open-source Redis fork under Linux Foundation governance, gains a target connector with vector store support. For organizations that have standardized on Valkey over Redis, this provides a fully supported vector index target without requiring a separate vector database.

Qdrant: native sparse vector support (v1.0.16)

The Qdrant connector was upgraded with native sparse vector support and eager point-ID validation. Sparse vectors are essential for hybrid search pipelines that combine dense (semantic) and sparse (BM25/TF-IDF) representations, making CocoIndex’s Qdrant integration first-class for hybrid retrieval.

LanceDB: vector and FTS index support (v1.0.11)

LanceDB gained explicit vector and full-text search (FTS) index management. The drop_index operation was added alongside a dependency bump to LanceDB 0.33.0 and PyArrow 23.0.0. Nullable schema-only row rewrites (a correctness bug that appeared on partial schema changes) were fixed in both the Python and Rust layers. In v1.0.15, LanceDB table maintenance was tied to LanceDB’s own stats API, making compaction scheduling driven by actual table metrics rather than a fixed interval.

zvec: FTS field support (v1.0.14)

The zvec target connector gained full-text search field support, allowing pipelines to write both vector and FTS fields to zvec in a single target export.

OCI: skip startup scan on unchanged logic (v1.0.11)

The OCI Object Storage source now detects when pipeline logic is unchanged since the last run and skips the expensive full-bucket startup scan, going directly to live-stream event processing. For large buckets, this can reduce cold-start time from minutes to seconds.

Local file system: periodic rescan in live mode (v1.0.15)

The local file system source in live mode now periodically rescans and recreates the watcher if it becomes stale. OS file-system watchers (inotify, kqueue) have known limitations under high churn, network mounts, and certain editors; periodic rescan provides a safety net that catches any events the watcher missed.

New connector summary

ConnectorTypeRelease
SnowflakeData warehouse targetv1.0.15
BigQueryData warehouse targetv1.0.16
Azure Blob StorageObject store sourcev1.0.16
ValkeyVector store targetv1.0.12
Qdrant sparse vectorsEnhanced vector targetv1.0.16
LanceDB vector + FTS indexesEnhanced vector targetv1.0.11
zvec FTS fieldsEnhanced targetv1.0.14

Agent & developer experience

cocoindex show: full-fledged inspection tool (v1.0.12)

cocoindex show was upgraded from a basic state dump into a full inspection tool for pipeline debugging. It provides structured, human-readable output of pipeline state, component configuration, and target schema: the production debugging workflow that previously required querying the LMDB or Postgres state store directly.

Python error messages

Runtime errors crossing the Rust-to-Python boundary now use Display (human-readable) rather than Debug (Rust internals) formatting, making error messages that surface in Python stack traces significantly more actionable.

Multiple GPU and fractional GPU allocation (v1.0.15)

GPUPool was added to support multiple GPUs and fractional GPU allocations within a pipeline. For embedding-heavy pipelines running on multi-GPU infrastructure, this allows work to be distributed across available GPU resources without hand-partitioning the pipeline.

Example walkthroughs

v1.0.13 shipped a major documentation expansion: 17 new example walkthroughs (10 full + 7 variants) covering the breadth of CocoIndex use cases:

Each walkthrough was structured for agent followability, with consistent heading patterns, copyable commands, and explicit prerequisites, so AI coding agents can execute them as step-by-step procedures.

Documentation Every example, ready to run. Browse all CocoIndex example walkthroughs: knowledge graphs, streaming pipelines, multimodal search, and PDF extraction. Each ships with copyable commands and explicit prerequisites.

Bug fixes and correctness

IssueImpactRelease
S3 ETag encoded to bytes for content fingerprintPrevents false cache invalidation on binary ETagsv1.0.10
StableKey::Bytes serde round-trip via msgpackCorrect key serialization for binary primary keysv1.0.14
Postgres: preserve data and NOT NULL on column type evolutionSchema evolution no longer drops non-null constraintsv1.0.15
Postgres: schema-qualify DROP INDEX for vector indexesAvoids dropping wrong-schema indexes in multi-schema deploymentsv1.0.15
Logic deps propagated across mount boundaryDependency tracking correct for nested mounted componentsv1.0.12
Orphaned child-existence rows when Directory→ComponentPrevents stale rows when a directory node becomes a component nodev1.0.15
LanceDB: escape single quotes in delete filter predicatesPrevents filter injection on document paths containing apostrophesv1.0.11
LanceDB: nullable schema-only row rewritesCorrect behavior on partial schema updatesv1.0.15
Idle batchers cleared to avoid stale accumulationPrevents write amplification on quiet pipelinesv1.0.8
PEP 695 type aliases in numpy 2.5 NDArrayFixes analyze_type_info crash on numpy 2.5+v1.0.13

Summary

This cycle’s theme is depth and breadth: depth in the engine (stateful components, in-memory intermediate collections, GPU pools, performance batching) and breadth in connectors (completing cloud warehouse coverage with Snowflake, BigQuery, and Azure Blob, plus Valkey and enhanced Qdrant). The through-line is the incremental engine growing new primitives — persistent state, live intermediate collections, cooperative rate limiting, batched writes — that bring a wider class of stateful, production-grade pipelines inside a single declarative CocoIndex app.

For the complete list of changes, see the GitHub releases. If CocoIndex is useful to you, consider starring the repository.

Thanks to the community

Thanks to everyone who shipped this cycle, core maintainers and community alike, including twelve first-time contributors.

@georgeh0

georgeh0

Thanks @georgeh0 for the bulk of this cycle’s engine work: LiveMap, the token-bucket RateLimiter, LiveComponent support inside mount_each, and the shared-parse code infrastructure behind cocoindex-code.

@badmonster0

badmonster0

Thanks @badmonster0 for the documentation updates.

@hakuuww

hakuuww

Thanks @hakuuww for building coco.use_state(), persistent per-component state, then following it with engine-side performance work: prefetching fn-memos and user states in one read transaction and minimizing UserStateCache serialization.

@sdhilip200

sdhilip200

Thanks @sdhilip200 for a prolific connector cycle: the Snowflake and BigQuery targets, the Azure Blob source, and Mermaid diagram rendering in the docs.

@Sujit-1509

@prrao87

@lichuang

lichuang

Thanks @lichuang for GPUPool, supporting multiple and fractional GPU allocations, and for adding type_hint to use_state for typed state deserialization.

@Haleshot

@nuthalapativarun

@daric93

daric93

Thanks @daric93 for the Valkey vector store target connector, a fully supported vector index target for teams standardized on Valkey.

@prithvi-moonshot

@MrAnayDongre

MrAnayDongre

Thanks @MrAnayDongre for preview mode for update actions, letting a run describe the adds, reprocesses, and deletes it would perform before touching a target.

@pyjuan91

pyjuan91

Thanks @pyjuan91 for making the OCI source skip its full startup scan when pipeline logic is unchanged, cutting cold-start time on large buckets.

@tomz-alt

@jordigilh

@MaxRong

MaxRong

Thanks @MaxRong for making the engine reject rows with missing or null primary keys instead of silently writing corrupt rows.

@slliland

slliland

Thanks @slliland for making the state store automatically resize the LMDB map when it fills, removing the manual LMDB_MAP_SIZE tuning operators used to need.

@SuperMarioYL

SuperMarioYL

Thanks @SuperMarioYL for !-prefixed negation in excluded_patterns, so a path matched by an earlier rule can be explicitly un-excluded.

@davidmyriel

@dashitongzhi

CocoIndex

Fresh context for long-horizon agents.