Stop writing the schema version on database open

This commit is contained in:
Yiorgis Gozadinos 2026-06-05 10:32:30 +03:00
parent e70f271ac7
commit b6bbea3d64
No known key found for this signature in database
3 changed files with 33 additions and 18 deletions

View file

@ -1,6 +1,10 @@
# Changelog
## [Unreleased]
### Fixed
- Opening a database no longer writes to it: reads no longer rewrite the stored embedding settings or change the stored version, and the version is never downgraded.
## [0.54.0] - 2026-06-04
### Added

View file

@ -12,6 +12,7 @@ import lancedb
import pyarrow as pa
from lancedb.index import FTS, BTree, IvfPq
from lancedb.pydantic import LanceModel, Vector
from packaging.version import parse
from pydantic import Field
from haiku.rag.config import AppConfig, Config
@ -509,7 +510,7 @@ class Store:
await self.set_haiku_version(metadata.version("haiku.rag-slim"))
async def _check_migrations(self) -> None:
"""Check if migrations are pending and error or update version accordingly.
"""Raise if migrations are pending. Opening never writes the version.
Raises:
MigrationRequiredError: If migrations are pending.
@ -529,10 +530,6 @@ class Store:
"Run 'haiku-rag migrate' to upgrade."
)
# No pending migrations - update version silently if needed (writable only)
if not self._read_only and db_version != current_version:
await self.set_haiku_version(current_version)
async def migrate(self) -> list[str]:
"""Run pending database migrations.
@ -551,8 +548,9 @@ class Store:
applied = await run_pending_upgrades(self, db_version)
# Update version after successful migration
if applied or db_version != current_version:
# Advance the schema marker only forward — never downgrade a database
# opened with an older build than last stamped it.
if parse(current_version) > parse(db_version):
await self.set_haiku_version(current_version)
return applied

View file

@ -38,21 +38,19 @@ class TestMigrationCheck:
pass
@pytest.mark.asyncio
async def test_version_bump_without_pending_migrations_updates_silently(
self, temp_db_path
):
"""When version is outdated but no migrations pending, update version silently."""
async def test_open_does_not_change_version(self, temp_db_path):
"""Opening a database never writes the version, even when it differs.
A version newer than all upgrade steps has no pending migrations, so the
open succeeds; the stored version must be left untouched (no downgrade,
no bump) because opening is a pure read.
"""
async with Store(temp_db_path, create=True) as store:
# Set an older version that has no pending migrations
# (newer than all current upgrade steps)
await store.set_haiku_version("100.0.0")
# Re-open - should update version silently, no error
# Re-open writable - no error, version unchanged
async with Store(temp_db_path) as store:
# Version should now be current
version = await store.get_haiku_version()
expected = metadata.version("haiku.rag-slim")
assert version == expected
assert await store.get_haiku_version() == "100.0.0"
@pytest.mark.asyncio
async def test_pending_migrations_raises_error(self, temp_db_path):
@ -160,6 +158,21 @@ class TestMigrateMethod:
applied = await store.migrate()
assert applied == []
@pytest.mark.asyncio
async def test_migrate_does_not_downgrade_future_version(self, temp_db_path):
"""migrate() must not move the version backwards.
Running an older build's migrate against a DB stamped with a newer
version (no pending upgrades) leaves the stored version untouched.
"""
async with Store(temp_db_path, create=True) as store:
await store.set_haiku_version("100.0.0")
async with Store(temp_db_path, skip_migration_check=True) as store:
applied = await store.migrate()
assert applied == []
assert await store.get_haiku_version() == "100.0.0"
@pytest.mark.asyncio
async def test_migrate_raises_read_only_error(self, temp_db_path):
"""Store.migrate() should raise ReadOnlyError in read-only mode."""