fuse4dbricks 0.7.1__tar.gz → 0.7.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {fuse4dbricks-0.7.1 → fuse4dbricks-0.7.2}/.github/workflows/test.yml +1 -1
- {fuse4dbricks-0.7.1 → fuse4dbricks-0.7.2}/CHANGELOG.md +11 -0
- {fuse4dbricks-0.7.1 → fuse4dbricks-0.7.2}/PKG-INFO +1 -1
- {fuse4dbricks-0.7.1 → fuse4dbricks-0.7.2}/fuse4dbricks/fs/metadata_manager.py +12 -0
- {fuse4dbricks-0.7.1 → fuse4dbricks-0.7.2}/pyproject.toml +1 -1
- {fuse4dbricks-0.7.1 → fuse4dbricks-0.7.2}/tests/test_metadata_manager.py +20 -0
- {fuse4dbricks-0.7.1 → fuse4dbricks-0.7.2}/uv.lock +1 -1
- {fuse4dbricks-0.7.1 → fuse4dbricks-0.7.2}/.env.example +0 -0
- {fuse4dbricks-0.7.1 → fuse4dbricks-0.7.2}/.gitignore +0 -0
- {fuse4dbricks-0.7.1 → fuse4dbricks-0.7.2}/README.md +0 -0
- {fuse4dbricks-0.7.1 → fuse4dbricks-0.7.2}/fuse4dbricks/__init__.py +0 -0
- {fuse4dbricks-0.7.1 → fuse4dbricks-0.7.2}/fuse4dbricks/api/__init__.py +0 -0
- {fuse4dbricks-0.7.1 → fuse4dbricks-0.7.2}/fuse4dbricks/api/errors.py +0 -0
- {fuse4dbricks-0.7.1 → fuse4dbricks-0.7.2}/fuse4dbricks/api/uc_client.py +0 -0
- {fuse4dbricks-0.7.1 → fuse4dbricks-0.7.2}/fuse4dbricks/auth/__init__.py +0 -0
- {fuse4dbricks-0.7.1 → fuse4dbricks-0.7.2}/fuse4dbricks/auth/provider.py +0 -0
- {fuse4dbricks-0.7.1 → fuse4dbricks-0.7.2}/fuse4dbricks/fs/__init__.py +0 -0
- {fuse4dbricks-0.7.1 → fuse4dbricks-0.7.2}/fuse4dbricks/fs/auth_manager.py +0 -0
- {fuse4dbricks-0.7.1 → fuse4dbricks-0.7.2}/fuse4dbricks/fs/data_manager.py +0 -0
- {fuse4dbricks-0.7.1 → fuse4dbricks-0.7.2}/fuse4dbricks/fs/inode_manager.py +0 -0
- {fuse4dbricks-0.7.1 → fuse4dbricks-0.7.2}/fuse4dbricks/fs/operations.py +0 -0
- {fuse4dbricks-0.7.1 → fuse4dbricks-0.7.2}/fuse4dbricks/fs/ram_cache.py +0 -0
- {fuse4dbricks-0.7.1 → fuse4dbricks-0.7.2}/fuse4dbricks/fs/securable_filter.py +0 -0
- {fuse4dbricks-0.7.1 → fuse4dbricks-0.7.2}/fuse4dbricks/fs/utils.py +0 -0
- {fuse4dbricks-0.7.1 → fuse4dbricks-0.7.2}/fuse4dbricks/fs/write_buffer.py +0 -0
- {fuse4dbricks-0.7.1 → fuse4dbricks-0.7.2}/fuse4dbricks/main.py +0 -0
- {fuse4dbricks-0.7.1 → fuse4dbricks-0.7.2}/fuse4dbricks/storage/__init__.py +0 -0
- {fuse4dbricks-0.7.1 → fuse4dbricks-0.7.2}/fuse4dbricks/storage/persistence.py +0 -0
- {fuse4dbricks-0.7.1 → fuse4dbricks-0.7.2}/tests/test_auth_manager.py +0 -0
- {fuse4dbricks-0.7.1 → fuse4dbricks-0.7.2}/tests/test_auth_provider.py +0 -0
- {fuse4dbricks-0.7.1 → fuse4dbricks-0.7.2}/tests/test_data_manager.py +0 -0
- {fuse4dbricks-0.7.1 → fuse4dbricks-0.7.2}/tests/test_e2e_mount.py +0 -0
- {fuse4dbricks-0.7.1 → fuse4dbricks-0.7.2}/tests/test_inode_manager.py +0 -0
- {fuse4dbricks-0.7.1 → fuse4dbricks-0.7.2}/tests/test_main.py +0 -0
- {fuse4dbricks-0.7.1 → fuse4dbricks-0.7.2}/tests/test_operations.py +0 -0
- {fuse4dbricks-0.7.1 → fuse4dbricks-0.7.2}/tests/test_persistence.py +0 -0
- {fuse4dbricks-0.7.1 → fuse4dbricks-0.7.2}/tests/test_securable_filter.py +0 -0
- {fuse4dbricks-0.7.1 → fuse4dbricks-0.7.2}/tests/test_uc_client.py +0 -0
- {fuse4dbricks-0.7.1 → fuse4dbricks-0.7.2}/tests/test_uc_client_consistency.py +0 -0
- {fuse4dbricks-0.7.1 → fuse4dbricks-0.7.2}/tests/test_uc_client_live.py +0 -0
- {fuse4dbricks-0.7.1 → fuse4dbricks-0.7.2}/tests/test_utils.py +0 -0
- {fuse4dbricks-0.7.1 → fuse4dbricks-0.7.2}/tests/test_write_buffer.py +0 -0
|
@@ -1,3 +1,14 @@
|
|
|
1
|
+
# 0.7.2 (2026-06-19)
|
|
2
|
+
|
|
3
|
+
- Fix a just-created file or directory being reported as nonexistent for up to
|
|
4
|
+
the negative-cache TTL (~5s) when its path had been stat'd before it existed.
|
|
5
|
+
Probing a not-yet-existing path records a negative (not-found) cache entry,
|
|
6
|
+
and `invalidate()` — called on every mutating op (create/write, `mkdir`,
|
|
7
|
+
`unlink`/`rmdir`, `rename`, truncate) — cleared the attribute and directory
|
|
8
|
+
caches but left that negative entry in place, so a subsequent `stat` kept
|
|
9
|
+
returning `ENOENT` until the entry expired. `invalidate()` now also evicts the
|
|
10
|
+
path's negative-cache entries (for every principal).
|
|
11
|
+
|
|
1
12
|
# 0.7.1 (2026-06-17)
|
|
2
13
|
|
|
3
14
|
- Unmount cleanly on `SIGTERM`/`SIGHUP` (e.g. `systemctl stop`). trio only turns
|
|
@@ -480,6 +480,18 @@ class MetadataManager:
|
|
|
480
480
|
if attr_cache_key in self._attr_cache:
|
|
481
481
|
del self._attr_cache[attr_cache_key]
|
|
482
482
|
|
|
483
|
+
# A mutating op (create/write/mkdir/rename/truncate/delete) just
|
|
484
|
+
# changed this path's presence, so any cached negative is now stale.
|
|
485
|
+
# Drop it for every principal: invalidate() is synchronous and has
|
|
486
|
+
# no principal in hand, and a just-created path must be immediately
|
|
487
|
+
# visible on this mount rather than hidden until the negative TTL
|
|
488
|
+
# expires. Build the key list first, then delete -- there is no
|
|
489
|
+
# await between, so this stays atomic under trio (mirrors the
|
|
490
|
+
# lock-free contract of the rest of this method).
|
|
491
|
+
stale_negatives = [k for k in self._negative_cache if k[1] == fs_path]
|
|
492
|
+
for key in stale_negatives:
|
|
493
|
+
del self._negative_cache[key]
|
|
494
|
+
|
|
483
495
|
parent_path = "/".join(fs_path.rstrip("/").split("/")[:-1]) or "/"
|
|
484
496
|
if parent_path in self._dir_cache:
|
|
485
497
|
del self._dir_cache[parent_path]
|
|
@@ -453,6 +453,26 @@ def test_invalidate_nonexistent_path_does_not_raise(manager):
|
|
|
453
453
|
manager.invalidate("/nonexistent/path/file.txt", is_dir=False) # Must not raise
|
|
454
454
|
|
|
455
455
|
|
|
456
|
+
def test_invalidate_clears_negative_cache_for_all_principals(manager, ctx):
|
|
457
|
+
"""A mutating op (create/write/mkdir/...) routes through invalidate(); it
|
|
458
|
+
must drop the path's negative entry for EVERY principal so a just-created
|
|
459
|
+
file is immediately visible instead of hidden until the negative TTL
|
|
460
|
+
expires. This is the negative-cache poisoning bug: stat a path before it
|
|
461
|
+
exists, create it, then stat again -> stale ENOENT for ~ttl_negative."""
|
|
462
|
+
future = time.time() + 100
|
|
463
|
+
fs_path = "/cat/sch/vol/file.txt"
|
|
464
|
+
manager._negative_cache[("alice@example.com", fs_path)] = future
|
|
465
|
+
manager._negative_cache[("bob@example.com", fs_path)] = future
|
|
466
|
+
# A negative for a *different* path must survive.
|
|
467
|
+
manager._negative_cache[("alice@example.com", "/cat/sch/vol/other.txt")] = future
|
|
468
|
+
|
|
469
|
+
manager.invalidate(fs_path, is_dir=False)
|
|
470
|
+
|
|
471
|
+
assert ("alice@example.com", fs_path) not in manager._negative_cache
|
|
472
|
+
assert ("bob@example.com", fs_path) not in manager._negative_cache
|
|
473
|
+
assert ("alice@example.com", "/cat/sch/vol/other.txt") in manager._negative_cache
|
|
474
|
+
|
|
475
|
+
|
|
456
476
|
# ---------------------------------------------------------------------------
|
|
457
477
|
# reauthorize (token change -> refresh the uid -> principal mapping)
|
|
458
478
|
# ---------------------------------------------------------------------------
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|