conf: publish_replica() os.replace breaks symlinked replica paths (worktree leaf-symlink pattern) #619

Closed
opened 2026-08-14 02:05:50 +00:00 by kert · 1 comment
Owner

Found during P37 #612: conf/connect.py publish_replica() uses os.replace(tmp, dst), which replaces the symlink itself rather than following it — in a git worktree using the documented leaf-symlink pattern (data/aco.ro.duckdb → main checkout), the publish silently detaches the symlink into a stale standalone copy while the real shared replica (and the DuckLake publish that reads it via the notebooks container) stays out of date. Fix: resolve dst with Path(dst).resolve() before the tmp-write/replace dance (atomic-replace next to the RESOLVED target). Workaround applied manually in P37 Task 6 (re-published + restored symlink).

Found during P37 #612: [`conf/connect.py`](https://git.fhirworx.io/homelab/stack/src/branch/main/src/conf/connect.py) `publish_replica()` uses `os.replace(tmp, dst)`, which replaces the symlink itself rather than following it — in a git worktree using the documented leaf-symlink pattern (`data/aco.ro.duckdb` → main checkout), the publish silently detaches the symlink into a stale standalone copy while the real shared replica (and the DuckLake publish that reads it via the notebooks container) stays out of date. Fix: resolve `dst` with `Path(dst).resolve()` before the tmp-write/replace dance (atomic-replace next to the RESOLVED target). Workaround applied manually in P37 Task 6 (re-published + restored symlink).
kert added the configdata labels 2026-08-14 02:05:50 +00:00
kert closed this issue 2026-08-14 19:08:49 +00:00
Author
Owner

Fixed in e91ea1c: src/conf/connect.py:155 now resolves the replica path before the tmp-write/os.replace dance, so a symlinked <name>.ro.duckdb (worktree leaf-symlink pattern) has its shared target atomically refreshed instead of the symlink being replaced by a detached stale copy.

Substantiation: TDD — the new regression test tests/conf/test_connect.py:338 (test_publish_follows_symlinked_replica) reproduced the exact failure first (publish detached the symlink into a standalone copy), then went green with the one-line fix; full tests/conf/test_connect.py suite 29/29 passed and the pre-commit hook gate was green on commit. Resolving to the target also keeps the atomicity guarantee: the tmp file now lands next to the resolved destination, same filesystem.

Fixed in [e91ea1c](https://git.fhirworx.io/homelab/stack/commit/e91ea1cb7c52f35cb851a6907107322a8427de4c): [src/conf/connect.py:155](https://git.fhirworx.io/homelab/stack/src/branch/main/src/conf/connect.py#L155) now resolves the replica path before the tmp-write/`os.replace` dance, so a symlinked `<name>.ro.duckdb` (worktree leaf-symlink pattern) has its **shared target** atomically refreshed instead of the symlink being replaced by a detached stale copy. **Substantiation:** TDD — the new regression test [tests/conf/test_connect.py:338](https://git.fhirworx.io/homelab/stack/src/branch/main/tests/conf/test_connect.py#L338) (`test_publish_follows_symlinked_replica`) reproduced the exact failure first (`publish detached the symlink into a standalone copy`), then went green with the one-line fix; full `tests/conf/test_connect.py` suite 29/29 passed and the pre-commit hook gate was green on commit. Resolving to the target also keeps the atomicity guarantee: the tmp file now lands next to the *resolved* destination, same filesystem.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: homelab/stack#619