Skip to content

Commit e4db81b

Browse files
committed
fix: address code review findings from PR #314
- Simplify orphan concept query to WHERE NOT EXISTS pattern - Handle 204 No Content in api_client.delete() - Add test for write-back flush delay threshold
1 parent 964c1ff commit e4db81b

3 files changed

Lines changed: 36 additions & 4 deletions

File tree

api/app/routes/documents.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1130,9 +1130,7 @@ async def delete_document(
11301130
# Clean up orphaned concepts (concepts with no remaining sources)
11311131
orphaned_result = client._execute_cypher("""
11321132
MATCH (c:Concept)
1133-
OPTIONAL MATCH (c)-[:APPEARS]->(s:Source)
1134-
WITH c, s
1135-
WHERE s IS NULL
1133+
WHERE NOT EXISTS { MATCH (c)-[:APPEARS]->(:Source) }
11361134
DETACH DELETE c
11371135
RETURN count(c) as orphaned_count
11381136
""", fetch_one=True)

fuse/kg_fuse/api_client.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ async def post(self, path: str, json: dict = None, data: dict = None, files: dic
7474
response.raise_for_status()
7575
return response.json()
7676

77-
async def delete(self, path: str, params: Optional[dict] = None) -> dict:
77+
async def delete(self, path: str, params: Optional[dict] = None) -> Optional[dict]:
7878
"""Make authenticated DELETE request to API."""
7979
token = await self._get_token()
8080
client = await self._get_client()
@@ -84,6 +84,8 @@ async def delete(self, path: str, params: Optional[dict] = None) -> dict:
8484
headers={"Authorization": f"Bearer {token}"},
8585
)
8686
response.raise_for_status()
87+
if response.status_code == 204 or not response.content:
88+
return None
8789
return response.json()
8890

8991
async def get_bytes(self, path: str, params: Optional[dict] = None, timeout: float = 60.0) -> bytes:

fuse/tests/test_lifecycle.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,14 @@
22

33
import json
44
import errno
5+
import time
56
import pytest
67
from unittest.mock import AsyncMock, MagicMock, patch
78

89
import pyfuse3
910

1011
from kg_fuse.config import WriteProtectConfig, TagsConfig, JobsConfig
12+
from kg_fuse.filesystem import WRITE_BACK_DELAY
1113
from kg_fuse.models import InodeEntry
1214

1315

@@ -549,6 +551,36 @@ async def test_rename_non_ingestion_raises_eperm(self):
549551
await fs.rename(ingest_inode, b"some.concept.md", ingest_inode, b"new.md", 0, ctx)
550552
assert exc_info.value.errno == errno.EPERM
551553

554+
@pytest.mark.anyio
555+
async def test_flush_skips_entries_below_delay(self):
556+
"""Flush task should not ingest entries younger than WRITE_BACK_DELAY."""
557+
fs = _make_fs()
558+
ctx = _mock_ctx()
559+
ingest_inode = self._setup_ingest_dir(fs)
560+
561+
fi, _ = await fs.create(ingest_inode, b"young.md", 0o644, 0, ctx)
562+
fh = fi.fh
563+
await fs.write(fh, 0, b"content")
564+
await fs.release(fh)
565+
assert len(fs._pending_ingestions) == 1
566+
567+
# Simulate flush with entry still young (timestamp = now)
568+
ready = [
569+
(k, e) for k, e in fs._pending_ingestions.items()
570+
if time.monotonic() - e["timestamp"] >= WRITE_BACK_DELAY
571+
]
572+
assert len(ready) == 0, "Entry should not be ready yet"
573+
574+
# Age the entry past the delay
575+
for entry in fs._pending_ingestions.values():
576+
entry["timestamp"] = time.monotonic() - WRITE_BACK_DELAY - 1
577+
578+
ready = [
579+
(k, e) for k, e in fs._pending_ingestions.items()
580+
if time.monotonic() - e["timestamp"] >= WRITE_BACK_DELAY
581+
]
582+
assert len(ready) == 1, "Entry should be ready after aging"
583+
552584
@pytest.mark.anyio
553585
async def test_destroy_flushes_pending(self):
554586
"""Unmount should flush all pending ingestions before cleanup."""

0 commit comments

Comments
 (0)