|
7 | 7 | """ |
8 | 8 |
|
9 | 9 | # Standard |
| 10 | +import asyncio |
10 | 11 | import os |
11 | 12 | import select |
12 | 13 | import shutil |
@@ -561,6 +562,47 @@ def test_get_usage_decreases_after_delete(self, adapter): |
561 | 562 |
|
562 | 563 | assert usage_after < usage_before |
563 | 564 |
|
| 565 | + def test_store_secondary_lookup_race_does_not_overcount_usage( |
| 566 | + self, adapter_with_persist |
| 567 | + ): |
| 568 | + """Store+recover race should not leave stale bytes after delete.""" |
| 569 | + adpt, buf, _, _, _ = adapter_with_persist |
| 570 | + key = create_object_key(1001) |
| 571 | + obj = create_memory_obj(buf, page_index=0) |
| 572 | + lookup_tasks: list[int] = [] |
| 573 | + |
| 574 | + async def _dynamic_store_with_lookup(mem_indices, file_path, page_size): |
| 575 | + file_size = len(mem_indices) * page_size |
| 576 | + with open(file_path, "wb") as f: |
| 577 | + f.truncate(file_size) |
| 578 | + lookup_tasks.append(adpt.submit_lookup_and_lock_task([key])) |
| 579 | + await asyncio.sleep(0) |
| 580 | + |
| 581 | + try: |
| 582 | + adpt.nixl_agent.dynamic_store_file = _dynamic_store_with_lookup |
| 583 | + adpt.submit_store_task([key], [obj]) |
| 584 | + wait_for_event_fd(adpt.get_store_event_fd()) |
| 585 | + wait_for_event_fd(adpt.get_lookup_and_lock_event_fd()) |
| 586 | + adpt.pop_completed_store_tasks() |
| 587 | + |
| 588 | + bitmap = adpt.query_lookup_and_lock_result(lookup_tasks[0]) |
| 589 | + assert bitmap is not None |
| 590 | + assert bitmap.test(0) |
| 591 | + |
| 592 | + adpt.submit_unlock([key]) |
| 593 | + sync_task = adpt.submit_lookup_and_lock_task([]) |
| 594 | + wait_for_event_fd(adpt.get_lookup_and_lock_event_fd()) |
| 595 | + adpt.query_lookup_and_lock_result(sync_task) |
| 596 | + |
| 597 | + usage_before_delete, _ = adpt.get_usage() |
| 598 | + adpt.delete([key]) |
| 599 | + usage_after_delete, _ = adpt.get_usage() |
| 600 | + |
| 601 | + assert usage_before_delete > 0.0 |
| 602 | + assert usage_after_delete == 0.0 |
| 603 | + finally: |
| 604 | + adpt.close() |
| 605 | + |
564 | 606 | def test_store_rejected_when_capacity_exceeded(self): |
565 | 607 | """Store should stop when max capacity is reached.""" |
566 | 608 | tmp_dir = tempfile.mkdtemp(prefix="nixl_dyn_cap_test_") |
|
0 commit comments