Skip to content

Commit 5b11ed5

Browse files
committed
tests: Fix concurrency tests tracking lock events instead of download events
Signed-off-by: Marcel Klehr <mklehr@gmx.net>
1 parent bbd7a6b commit 5b11ed5

1 file changed

Lines changed: 62 additions & 60 deletions

File tree

tests/actual_tests/integration_fastapi_test.py

Lines changed: 62 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -164,31 +164,35 @@ def test_concurrent_downloads_serialized(self, nc_app):
164164
os.chdir(tmpdir)
165165
try:
166166
test_url = "https://raw.githubusercontent.com/cloud-py-api/nc_py_api/refs/heads/main/LICENSE.txt"
167-
models = {test_url: {"save_path": "concurrent_test.txt"}}
168167

169-
# Track download events
170-
download_events = []
171-
download_lock = threading.Lock()
168+
# Track lock acquisition events
169+
lock_events = []
170+
lock_events_lock = threading.Lock()
172171

173-
# Patch niquests.get to track when downloads actually happen
174-
original_get = __import__("niquests").get
172+
# Patch SoftFileLock to track when locks are acquired/released
173+
from filelock import SoftFileLock as OriginalSoftFileLock
175174

176-
def tracked_get(*args, **kwargs):
177-
with download_lock:
178-
download_events.append(("start", threading.current_thread().name, time.time()))
179-
result = original_get(*args, **kwargs)
180-
with download_lock:
181-
download_events.append(("end", threading.current_thread().name, time.time()))
182-
return result
175+
class TrackedSoftFileLock(OriginalSoftFileLock):
176+
def acquire(self, *args, **kwargs):
177+
result = super().acquire(*args, **kwargs)
178+
with lock_events_lock:
179+
lock_events.append(("acquire", threading.current_thread().name, time.time()))
180+
return result
183181

184-
with patch("niquests.get", side_effect=tracked_get):
182+
def release(self, *args, **kwargs):
183+
with lock_events_lock:
184+
lock_events.append(("release", threading.current_thread().name, time.time()))
185+
return super().release(*args, **kwargs)
186+
187+
with patch("nc_py_api.ex_app.integration_fastapi.SoftFileLock", TrackedSoftFileLock):
185188
# Simulate two pods trying to download simultaneously
186189
results = []
187190
errors = []
188191

189192
def download_in_thread(thread_name):
190193
try:
191-
fetch_models_task(nc_app, models.copy(), 0)
194+
models = {test_url: {"save_path": "concurrent_test.txt"}}
195+
fetch_models_task(nc_app, models, 0)
192196
results.append(thread_name)
193197
except Exception as e:
194198
errors.append((thread_name, e))
@@ -207,21 +211,18 @@ def download_in_thread(thread_name):
207211
assert len(results) + len(errors) == 2, f"Not all threads completed: {results}, {errors}"
208212
assert len(errors) == 0, f"Threads failed with errors: {errors}"
209213

210-
# Check that downloads were serialized (not concurrent)
211-
start_events = [e for e in download_events if e[0] == "start"]
212-
end_events = [e for e in download_events if e[0] == "end"]
213-
214-
if len(start_events) == 2:
215-
# If two downloads happened, they should be sequential
216-
first_end_time = end_events[0][2]
217-
second_start_time = start_events[1][2]
218-
# Second download should start after first ends (serialized)
219-
assert second_start_time >= first_end_time, "Downloads happened concurrently instead of serially"
220-
elif len(start_events) == 1:
221-
# Only one download happened - the other thread reused the file (also valid)
222-
pass
223-
else:
224-
pytest.fail(f"Unexpected number of download events: {download_events}")
214+
# Check that lock acquisitions were serialized (not concurrent)
215+
acquire_events = [e for e in lock_events if e[0] == "acquire"]
216+
release_events = [e for e in lock_events if e[0] == "release"]
217+
218+
assert len(acquire_events) == 2, f"Expected 2 lock acquisitions, got {len(acquire_events)}"
219+
assert len(release_events) == 2, f"Expected 2 lock releases, got {len(release_events)}"
220+
221+
# Verify locks were acquired serially: first must release before second acquires
222+
first_release_time = release_events[0][2]
223+
second_acquire_time = acquire_events[1][2]
224+
assert second_acquire_time >= first_release_time, \
225+
f"Second thread acquired lock before first released: {lock_events}"
225226

226227
# Verify file was downloaded successfully
227228
assert Path("concurrent_test.txt").exists()
@@ -355,31 +356,35 @@ def test_concurrent_snapshot_downloads_serialized(self, nc_app):
355356
try:
356357
model_name = "hf-internal-testing/tiny-random-gpt2"
357358
cache_dir = Path(tmpdir) / "hf_cache"
358-
models = {model_name: {"cache_dir": str(cache_dir), "max_workers": 1}}
359359

360-
# Track when snapshot_download is called
361-
from huggingface_hub import snapshot_download
362-
download_events = []
363-
download_lock = threading.Lock()
360+
# Track lock acquisition events
361+
lock_events = []
362+
lock_events_lock = threading.Lock()
364363

365-
original_snapshot_download = snapshot_download
364+
# Patch SoftFileLock to track when locks are acquired/released
365+
from filelock import SoftFileLock as OriginalSoftFileLock
366366

367-
def tracked_snapshot_download(*args, **kwargs):
368-
with download_lock:
369-
download_events.append(("start", threading.current_thread().name, time.time()))
370-
result = original_snapshot_download(*args, **kwargs)
371-
with download_lock:
372-
download_events.append(("end", threading.current_thread().name, time.time()))
373-
return result
367+
class TrackedSoftFileLock(OriginalSoftFileLock):
368+
def acquire(self, *args, **kwargs):
369+
result = super().acquire(*args, **kwargs)
370+
with lock_events_lock:
371+
lock_events.append(("acquire", threading.current_thread().name, time.time()))
372+
return result
374373

375-
with patch("nc_py_api.ex_app.integration_fastapi.snapshot_download", side_effect=tracked_snapshot_download):
374+
def release(self, *args, **kwargs):
375+
with lock_events_lock:
376+
lock_events.append(("release", threading.current_thread().name, time.time()))
377+
return super().release(*args, **kwargs)
378+
379+
with patch("nc_py_api.ex_app.integration_fastapi.SoftFileLock", TrackedSoftFileLock):
376380
# Simulate two pods trying to download simultaneously
377381
results = []
378382
errors = []
379383

380384
def download_in_thread(thread_name):
381385
try:
382-
fetch_models_task(nc_app, {model_name: {"cache_dir": str(cache_dir), "max_workers": 1}}, 0)
386+
models = {model_name: {"cache_dir": str(cache_dir), "max_workers": 1}}
387+
fetch_models_task(nc_app, models, 0)
383388
results.append(thread_name)
384389
except Exception as e:
385390
errors.append((thread_name, e))
@@ -398,21 +403,18 @@ def download_in_thread(thread_name):
398403
assert len(results) + len(errors) == 2, f"Not all threads completed: {results}, {errors}"
399404
assert len(errors) == 0, f"Threads failed with errors: {errors}"
400405

401-
# Check that downloads were serialized (not concurrent)
402-
start_events = [e for e in download_events if e[0] == "start"]
403-
end_events = [e for e in download_events if e[0] == "end"]
404-
405-
if len(start_events) == 2:
406-
# If two downloads happened, they should be sequential
407-
first_end_time = end_events[0][2]
408-
second_start_time = start_events[1][2]
409-
# Second download should start after first ends (serialized)
410-
assert second_start_time >= first_end_time, "Downloads happened concurrently instead of serially"
411-
elif len(start_events) == 1:
412-
# Only one download happened - the other thread reused cached model (also valid)
413-
pass
414-
else:
415-
pytest.fail(f"Unexpected number of download events: {download_events}")
406+
# Check that lock acquisitions were serialized (not concurrent)
407+
acquire_events = [e for e in lock_events if e[0] == "acquire"]
408+
release_events = [e for e in lock_events if e[0] == "release"]
409+
410+
assert len(acquire_events) == 2, f"Expected 2 lock acquisitions, got {len(acquire_events)}"
411+
assert len(release_events) == 2, f"Expected 2 lock releases, got {len(release_events)}"
412+
413+
# Verify locks were acquired serially: first must release before second acquires
414+
first_release_time = release_events[0][2]
415+
second_acquire_time = acquire_events[1][2]
416+
assert second_acquire_time >= first_release_time, \
417+
f"Second thread acquired lock before first released: {lock_events}"
416418

417419
# Verify model was downloaded successfully
418420
assert cache_dir.exists()

0 commit comments

Comments
 (0)