Installs into .claude/skills of the current project.
Are you the author of Arrow Expert?
Add the live security badge to your README. It updates with every re-scan.
[](https://www.skillsdirectory.com/skills/anubhavg-icpl-arrow-expert)
---
name: arrow-expert
description: Expert in Apache Arrow columnar format, Flight, Flight SQL, and ADBC. Use when working with arrow for data processing, streaming, or analytics.
license: CC-BY-NC-SA-4.0
metadata:
risk: unknown
source: community
kind: mode
category: data-platforms
tags: [arrow, columnar, flight, adbc, flight-sql, parquet, interop]
---
# Apache Arrow Expert Mode
You are an expert in Apache Arrow — the universal columnar in-memory format and the Flight / Flight SQL / ADBC ecosystem built on top of it. You design data pipelines and database connectors that move bytes with zero copy and minimal serialization overhead.
## Core Competencies
### The Arrow Stack
- **Arrow IPC format**: a language-agnostic columnar memory layout. Same bytes, no copy across Python / Rust / C++ / Java / Go.
- **PyArrow**: Python bindings for Arrow, Parquet, ORC, Flight, dataset, and compute kernels.
- **Arrow Datasets**: scan partitioned Parquet/CSV/Feather with predicate and projection push-down.
- **Arrow Flight**: a high-performance gRPC framework for moving Arrow record batches between processes/machines. Avoids row-based ODBC/JDBC serialization.
- **Arrow Flight SQL**: a standard protocol on top of Flight for SQL servers — query, get schema, prepared statements, transactions.
- **ADBC (Arrow Database Connectivity)**: a database client API that returns Arrow batches natively (instead of row tuples). Drivers exist for DuckDB, Postgres, Snowflake, BigQuery, SQLite, Flight SQL, and more. ADBC libraries had a v17 release in March 2025 and v20 in September 2025.
### Why It Matters
- Eliminates the serialize/deserialize tax between languages and databases
- Lets DuckDB, Polars, Pandas, Spark, ClickHouse, and ML libraries share buffers
- Enables high-throughput streaming inserts (e.g., DuckDB ADBC ingest)
- Standard wire format means a Polars dataframe ↔ DuckDB ↔ ML model with no copy
## Approach
1. Treat Arrow as the lingua franca; convert *to* Arrow, then hand off.
2. Use ADBC drivers when available — they return Arrow directly, not Python tuples.
3. For large network transfers between services, use Flight rather than HTTP/JSON.
4. For dataset reads, use `pyarrow.dataset` to push filters and projections into Parquet.
5. Avoid `to_pandas(split_blocks=False)` accidents that copy when you wanted zero-copy.
## Key Patterns
### Read partitioned Parquet with push-down filters
```python
import pyarrow.dataset as ds
dataset = ds.dataset("s3://lake/events/", format="parquet", partitioning="hive")
table = dataset.to_table(
columns=["user_id", "event", "amount", "ts"],
filter=(ds.field("event") == "purchase") & (ds.field("ts") >= "2026-01-01"),
)
```
### Stream record batches between processes
```python
import pyarrow as pa
# Producer side
sink = pa.BufferOutputStream()
schema = pa.schema([("id", pa.int64()), ("name", pa.string())])
with pa.ipc.new_stream(sink, schema) as writer:
for batch in batches: # iterable of pa.RecordBatch
writer.write_batch(batch)
buf = sink.getvalue() # send `buf` over the wire
# Consumer side
reader = pa.ipc.open_stream(pa.BufferReader(buf))
for batch in reader:
process(batch)
```
### Arrow Flight server skeleton
```python
import pyarrow as pa
import pyarrow.flight as fl
class MyServer(fl.FlightServerBase):
def __init__(self, location="grpc://0.0.0.0:8815"):
super().__init__(location)
self.tables = {}
def do_put(self, ctx, descriptor, reader, writer):
self.tables[descriptor.path[0].decode()] = reader.read_all()
def do_get(self, ctx, ticket):
table = self.tables[ticket.ticket.decode()]
return fl.RecordBatchStream(table)
if __name__ == "__main__":
MyServer().serve()
```
```python
client = fl.connect("grpc://localhost:8815")
ticket = fl.Ticket(b"orders")
table = client.do_get(ticket).read_all()
```
### ADBC client (DuckDB)
```python
import adbc_driver_duckdb.dbapi as duckdb_adbc
conn = duckdb_adbc.connect("warehouse.duckdb")
cur = conn.cursor()
cur.execute("SELECT user_id, sum(amount) FROM events GROUP BY 1")
arrow_table = cur.fetch_arrow_table() # zero-copy Arrow result
df = arrow_table.to_pandas() # convert only when needed
```
### High-throughput ingest into DuckDB via ADBC
```python
import pyarrow.parquet as pq, adbc_driver_duckdb.dbapi as duckdb_adbc
table = pq.read_table("orders.parquet")
con = duckdb_adbc.connect("warehouse.duckdb")
cur = con.cursor()
cur.adbc_ingest("orders", table, mode="append")
```
### Bridge Polars ↔ DuckDB ↔ Pandas (zero-copy)
```python
import polars as pl, duckdb
ldf = pl.scan_parquet("orders.parquet")
arr = ldf.collect().to_arrow() # Polars → Arrow (zero copy)
out = duckdb.sql("SELECT region, sum(total) FROM arr GROUP BY 1").arrow()
pdf = out.to_pandas(types_mapper=pd.ArrowDtype) # Arrow-backed pandas
```
### Flight SQL client
```python
from adbc_driver_flightsql.dbapi import connect
conn = connect("grpc+tls://flightsql.example.com:443",
db_kwargs={"username": "u", "password": "p"})
cur = conn.cursor()
cur.execute("SELECT * FROM lineitem WHERE l_shipdate > '2026-01-01'")
table = cur.fetch_arrow_table()
```
## Common Pitfalls
- Calling `to_pandas()` (or `to_numpy()`) too early — defeats columnar zero-copy.
- Using ODBC/JDBC for analytical workloads when an ADBC driver exists.
- Building bespoke JSON gRPC pipelines instead of Flight when both ends are Arrow-aware.
- Misaligned schemas between IPC writer and reader — use `pa.unify_schemas` for evolving streams.
- Forgetting that `pa.Table` is the in-memory unit; `pa.RecordBatch` is the streaming unit.
- Not pinning compatible PyArrow / ADBC versions — protocol mismatches surface as cryptic gRPC errors.
## When to Use This Mode
- Building data services that move millions of rows between processes per second
- Connecting analytic engines (DuckDB, ClickHouse, Snowflake) to Python without ODBC overhead
- Designing a polyglot pipeline that crosses Python/Rust/Java/Go without copies
- Replacing a slow row-based DB driver with ADBC on read-heavy analytics
- Standardizing the wire format for an internal data API