Files
lancedb/docs/web/feature-engineering/refresh-column.mdx
T
Jack YeandClaude Sonnet 5 de95ed3f6d docs: overhaul curation, feature engineering, security and deployment guides (#4748)
A broad documentation pass across the areas LanceDB Enterprise and
Embedded
diverge on most:

**Data Curation** -- GPU Function execution, Slurm scheduling (and what
Slurm is, for readers new to it), blob late materialization as its own
page, joins, aggregation/profiling, sampling, near-duplicate detection,
chunking, distributed shuffle, and IVF-partition clustering.

**Feature Engineering** -- computed columns and materialized views,
restructured to lead with the Function-based path and show the SQL form
side by side with Python throughout; a single Auto Refresh page instead
of
duplicating scheduling details on every refresh page; adaptive
execution;
checkpointing; GPU and Slurm placement for refresh workers; a Training
Cache page for the PyTorch data loader's automatic cache benefit.

**Security & Governance** -- an Audit Log reference (schema, access,
grants), Secrets promoted to an ordinary catalog object with their own
SQL
DDL and a vending/rotation guide, and privilege-table fixes verified
against the authorization source.

**Deployment & Maintenance** -- Horizontal Scaling and OpenTelemetry
moved
into a real Deployment area; Automated Cleanup folded into Intelligent
Auto-Maintenance (every other maintenance decision already lived there);
RTE Compaction and Advanced Conflict Resolution documented in depth.

**Landing page** -- the Enterprise feature comparison table reorganized
(several redundant or stale rows removed, several new areas added), and
the
"why Enterprise" bullets replaced with ones tied to a concrete page
(Performance, Scale, Fault tolerance, Multicloud deployment, Simplify
operations) instead of vague promises.

Plus a full pass addressing lancedb-gatekeeper's review: a real
test-safety
bug (a quickstart test could silently overwrite and delete a
deployment's
own `frames` table), a fabricated SQL statement with no backing
implementation, an actual dedup-guarantee bug in `take_row_ids`, and
dozens
of documentation-accuracy fixes verified against source rather than
taken
on faith -- each either fixed, or left with an explicit note on why it
needs a feature or deployment owner's sign-off instead of being guessed
at.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

https://claude.ai/code/session_018PXMCbRH6i5w47TsjeEUMT

---------

Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
2026-11-03 16:17:24 -08:00

230 lines
7.8 KiB
Plaintext

---
title: "Refresh a computed column"
sidebarTitle: "Refresh Computed Column"
description: "Populate a computed column and update it when its inputs change."
icon: arrows-rotate
keywords: ["refresh_column", "backfill", "job", "idempotent", "REFRESH COLUMN"]
availability:
embedded: unavailable
enterprise: available
---
Refresh a [computed column](/feature-engineering/add-computed-column) to fill
new rows and recompute values whose inputs changed. The example below continues
with the `doubled` column from the computed-column guide.
<Note>
Computed columns require LanceDB Enterprise.
</Note>
<CodeGroup>
```python Python icon="python"
job = table.refresh_column_async("doubled")
job.wait()
table.to_arrow().sort_by("x").to_pandas()
# x doubled
# 0 1 2
# 1 2 4
```
```sql SQL icon="database"
REFRESH COLUMN doubled ON my_table
```
</CodeGroup>
<Note>
`table.refresh_column(...)` (sync) is also a local, blocking form, but it
raises on an Enterprise connection -- use `refresh_column_async`, shown
above, or the SQL form instead. See
[Run a refresh on Enterprise](#async) below for what the returned job
supports.
</Note>
The refresh commits ordinary table versions (the values, then a
metadata-only record of the inputs they were computed from), so
[versioning](/management/table#versioning) applies to it like any other write.
<Note>
The SQL form is [`REFRESH COLUMN`](/sql/dml/refresh-column), with the same resource options.
</Note>
## What a refresh recomputes {#idempotent}
A refresh fills rows that have no value and recomputes rows whose inputs
changed since they were computed. Every other row is left alone, so a second
refresh with nothing changed does nothing. This supports two common workflows.
**Appending is cheap.** Rows added since the last refresh are filled by the
next one; filled rows whose inputs are unchanged are skipped. Backfilling a
column and then ingesting more data does not mean recomputing the backfill.
**A filled value catches up with its input at the next refresh.** If `doubled`
derives from `x` and a row's `x` is later updated, `doubled` keeps its old value
until the next refresh recomputes it.
That is what makes the call idempotent and safe to retry. However, unlike a
spreadsheet formula, nothing is recomputed until you refresh, so a derived
column is stale between an update and the next refresh.
## Automatic refresh {#automatic}
A computed column can refresh itself on a schedule instead of waiting for
an explicit call here. See [Auto Refresh](/enterprise/auto-refresh) for
enabling it, the SQL and SDK forms for changing the setting, and how
catch-up works when it's re-enabled after being off.
## Refreshing part of a table {#partial}
<Badge color="red">Enterprise</Badge> In SQL a refresh can be scoped to a
predicate, which is useful for filling a slice first and checking it before
committing to the whole table:
<CodeGroup>
```sql SQL icon="database"
REFRESH COLUMN embedding ON documents WHERE created_at > '2026-01-01'
```
</CodeGroup>
<Note>
**Enterprise job type:** `computed_refresh` for a SQL-expression column,
`function_refresh` for one filled by a registered [function](/management/function).
</Note>
## Run a refresh on Enterprise {#async}
<Badge color="red">Enterprise</Badge>
With an Enterprise cluster that enables computed columns, the refresh runs on the
cluster. `refresh_column` raises
there; use `refresh_column_async`, which returns a [job](/basics/jobs) you can
wait on or poll instead of holding a connection open:
<CodeGroup>
```python Python icon="python"
job = table.refresh_column_async("doubled")
result = job.wait()
result.rows_assigned
# 2
job.status()
# 'finished'
```
</CodeGroup>
Poll with `status()`, resolve with `wait()`. `status()` is a point snapshot and
does not raise, which is what makes it safe in a loop; `wait()` raises
`JobFailedError`, `JobCancelledError` or `TimeoutError`.
A long backfill can therefore be started, left, and checked on from a different
process.
### Configure workers and resources {#resources}
SQL exposes the worker budget directly, which is the lever when a backfill is
too slow or too expensive:
<CodeGroup>
```sql SQL icon="database"
REFRESH COLUMN embedding ON documents
WITH (cpu_millis = 8000, memory_bytes = 17179869184, gpu_count = 2)
```
</CodeGroup>
`num_workers` sets the count instead of a per-worker budget; the two are
alternatives rather than a pair. `batch_size` controls how many rows a worker
takes at a time, and `error_policy = 'skip'` continues past rows that fail
rather than failing the job.
For a column filled by a registered Function, the Python and Rust job APIs can
override the Function's GPU placement for this refresh:
<CodeGroup>
```python
classes = db.list_gpu_classes()
gpu_class = next(gpu.name for gpu in classes if gpu.gpu_model == "NVIDIA H100")
job = table.refresh_column_async(
"embedding",
gpu_class=gpu_class,
gpu_count=4,
batch_size=32,
)
```
```rust
use lancedb::{Connection, Table, table::RefreshColumnOptions};
async fn refresh(
conn: &Connection,
table: &Table,
) -> Result<(), Box<dyn std::error::Error>> {
let gpu_class = conn
.list_gpu_classes()
.await?
.into_iter()
.find(|class| class.gpu_model.as_deref() == Some("NVIDIA H100"))
.map(|class| class.name)
.ok_or("H100 GPU class is not configured")?;
let _job = table
.refresh_column_async_with_options(
"embedding",
RefreshColumnOptions::default()
.gpu_class(gpu_class)
.gpu_count(4)
.batch_size(32),
)
.await?;
Ok(())
}
```
</CodeGroup>
`gpu_count` is the aggregate GPU budget for the refresh's Function workers,
not a request for that many GPUs per worker. It must be positive.
`gpu_class` must name a class returned by `list_gpu_classes()`; class names
are lowercase DNS labels. Omit either option to use the Function's registered
class or the server's configured budget. `list_gpu_classes()` requires System
Usage permission, and raises `NotImplementedError` when the server predates
GPU class discovery.
<Note>
The Rust GPU discovery and refresh controls are not in the latest Rust
release. TypeScript does not expose GPU class discovery or placement; a
tracking issue is needed before adding equivalent controls.
</Note>
## When a refresh fails {#failures}
What a failure keeps depends on where the refresh ran. The local, blocking
`refresh_column` writes the column in one commit, so a failure leaves the
column as it was. A refresh job in LanceDB Enterprise -- always
`refresh_column_async` or `REFRESH COLUMN` there, never the blocking form --
commits finished batches as it goes, so some values can remain after a
failure.
Either way, recover by fixing the cause and refreshing again. The new refresh
fills rows that are still empty or whose inputs changed, and normally skips
values that were kept. That is not guaranteed: a refresh can recompute values
that were already written (for example when the record of the inputs they
were computed from was not saved), so do not count on a retry costing only the
rows that did not complete.
For a column filled by a [function](/management/function), the usual cause is
the worker environment rather than the data. See
[environment mismatches](/management/function#mismatch-symptoms) for the
symptoms, which rarely name their cause.
## Related guides {#next}
<CardGroup cols={2}>
<Card title="Job Framework" icon="hourglass-half" href="/basics/jobs">
Waiting on, inspecting, and cancelling server-side work.
</Card>
<Card title="Refresh a materialized view" icon="table" href="/feature-engineering/refresh-materialized-view">
The same idea, for a whole derived table.
</Card>
<Card title="Auto Refresh" icon="clock-rotate-left" href="/enterprise/auto-refresh">
Refresh on a schedule instead of calling this yourself.
</Card>
</CardGroup>