refactor: fold the maintained-index API down to one entry point

Three cleanups to the LsmWriteSpec surface, all removing something:

`with_no_maintained_indexes` is gone. `with_maintained_indexes` now takes
`impl Into<Option<Vec<String>>>`, so `None` asks for every supported index,
a list pins an exact set, and an empty list maintains none — one method
covering what two did, and the argument reads the same as the field it sets.

`IndexConfig::is_memwal_maintainable` is gone. What a memtable can build is
lance's to say, and lance already says it via `is_maintainable_index_type`;
mirroring that onto a lancedb type only added a place for the two to drift.

`resolve_maintained_indexes` is now public and takes `&[IndexConfig]` rather
than a table. It was already the whole of the resolution rules — infer from
the committed set, or validate a named one — and a server installing specs
on a caller's behalf needs exactly those rules. Sharing it means sophon no
longer keeps a second copy that could disagree about what an absent list
means, and the error now names the table's indexes so a rejected call says
what was actually available.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Daniel Rammer
2026-07-31 11:17:44 -05:00
co-authored by Claude Opus 5
parent dd197ad0ff
commit 4bb0effcb2
9 changed files with 103 additions and 101 deletions
+7 -8
View File
@@ -607,14 +607,13 @@ class LsmWriteSpec:
def identity(column: str) -> "LsmWriteSpec": ...
@staticmethod
def unsharded() -> "LsmWriteSpec": ...
def with_maintained_indexes(self, indexes: List[str]) -> "LsmWriteSpec":
"""Return a copy of this spec asking the MemWAL to keep exactly the
named indexes up to date as rows are appended, replacing the default
of every index the MemWAL supports."""
...
def with_no_maintained_indexes(self) -> "LsmWriteSpec":
"""Return a copy of this spec asking the MemWAL to maintain no
indexes at all."""
def with_maintained_indexes(
self, indexes: Optional[List[str]]
) -> "LsmWriteSpec":
"""Return a copy of this spec setting which indexes the MemWAL keeps
up to date. None maintains every index the MemWAL supports, resolved
when the spec is installed; a list is taken verbatim, and an empty
list maintains none."""
...
def with_writer_config_defaults(self, defaults: Dict[str, str]) -> "LsmWriteSpec":
"""Return a copy of this spec recording the given default
+2 -2
View File
@@ -4637,8 +4637,8 @@ class AsyncTable:
subscription: an index created afterwards is not maintained until the
spec is unset and set again. Use ``with_maintained_indexes`` to pin an
exact set — names must already be committed, so a still-building index
is rejected rather than quietly omitted — or
``with_no_maintained_indexes`` to maintain none.
is rejected rather than quietly omitted — or pass an empty list to
maintain none.
Parameters
----------
+2 -2
View File
@@ -85,7 +85,7 @@ def test_lsm_write_spec_repr():
assert s.num_buckets == 4
# A fresh spec defers its maintained set to install time.
assert s.maintained_indexes is None
assert s.with_no_maintained_indexes().maintained_indexes == []
assert s.with_maintained_indexes([]).maintained_indexes == []
assert "bucket" in repr(s)
assert "id" in repr(s)
assert "4" in repr(s)
@@ -183,7 +183,7 @@ def test_get_lsm_write_spec(tmp_path):
# Unsharded round-trips (no routing column). Opting out is distinct from
# the inferred default.
table.set_lsm_write_spec(LsmWriteSpec.unsharded().with_no_maintained_indexes())
table.set_lsm_write_spec(LsmWriteSpec.unsharded().with_maintained_indexes([]))
spec = table.get_lsm_write_spec()
assert spec.spec_type == "unsharded"
assert spec.column is None
+10 -15
View File
@@ -198,8 +198,8 @@ fn fmt_maintained(maintained: &Option<Vec<String>>) -> String {
/// `merge_insert`.
///
/// Constructed via the `bucket(...)`, `identity(...)`, or `unsharded()`
/// classmethods, then optionally chain `with_maintained_indexes(...)` /
/// `with_no_maintained_indexes()` and `with_writer_config_defaults(...)`.
/// classmethods, then optionally chain `with_maintained_indexes(...)` and
/// `with_writer_config_defaults(...)`.
/// A freshly constructed spec maintains every index the MemWAL supports,
/// resolved when the spec is installed.
#[pyclass(from_py_object)]
@@ -241,24 +241,19 @@ impl LsmWriteSpec {
}
}
/// Maintain exactly `indexes`, replacing the default of "every index
/// the MemWAL supports". Each name must reference an index that
/// already exists on the table, of a type the MemWAL can maintain,
/// at the time `set_lsm_write_spec` is called.
pub fn with_maintained_indexes(&self, indexes: Vec<String>) -> Self {
/// Set which indexes the MemWAL maintains.
///
/// `None` — the default — maintains every index the MemWAL supports,
/// resolved when the spec is installed. A list is taken verbatim:
/// every name must already exist on the table and be a type the
/// MemWAL can maintain, and an empty list maintains nothing.
#[pyo3(signature = (indexes))]
pub fn with_maintained_indexes(&self, indexes: Option<Vec<String>>) -> Self {
Self {
inner: self.inner.clone().with_maintained_indexes(indexes),
}
}
/// Maintain no indexes at all — the memtable serves scans and filters
/// without any in-memory index.
pub fn with_no_maintained_indexes(&self) -> Self {
Self {
inner: self.inner.clone().with_no_maintained_indexes(),
}
}
/// Replace the default `ShardWriter` configuration recorded in the
/// MemWAL index, so every writer starts from the same defaults.
pub fn with_writer_config_defaults(&self, defaults: HashMap<String, String>) -> Self {