@@ -476,8 +476,8 @@ async def __aiter__(self):
476476 yield self ._data
477477
478478
479- def test_eager_reader_with_chunk_size_and_file_size ():
480- """Test EagerStoreReader uses get_ranges when chunk_size and file_size provided."""
479+ def test_eager_reader_with_request_size_and_file_size ():
480+ """Test EagerStoreReader uses get_ranges when request_size and file_size provided."""
481481 from obspec_utils .tracing import TracingReadableStore , RequestTrace
482482
483483 # Create test data (16 bytes)
@@ -488,22 +488,22 @@ def test_eager_reader_with_chunk_size_and_file_size():
488488 trace = RequestTrace ()
489489 traced_store = TracingReadableStore (mock_store , trace )
490490
491- # Create reader with chunk_size and file_size
491+ # Create reader with request_size and file_size
492492 reader = EagerStoreReader (
493- traced_store , "test.txt" , chunk_size = 4 , file_size = len (data )
493+ traced_store , "test.txt" , request_size = 4 , file_size = len (data )
494494 )
495495
496496 # Verify the data is correct
497497 assert reader .read () == data
498498
499499 # Verify get_ranges was used (not get)
500500 summary = trace .summary ()
501- assert summary ["total_requests" ] == 4 # 16 bytes / 4 byte chunks = 4 requests
501+ assert summary ["total_requests" ] == 4 # 16 bytes / 4 byte requests = 4 requests
502502 assert all (r .method == "get_ranges" for r in trace .requests )
503503 assert summary ["total_bytes" ] == len (data )
504504
505505
506- def test_eager_reader_with_chunk_size_uses_head ():
506+ def test_eager_reader_uses_head ():
507507 """Test EagerStoreReader uses head() to get file size when available."""
508508 from obspec_utils .tracing import TracingReadableStore , RequestTrace
509509
@@ -515,16 +515,16 @@ def test_eager_reader_with_chunk_size_uses_head():
515515 trace = RequestTrace ()
516516 traced_store = TracingReadableStore (mock_store , trace )
517517
518- # Create reader with chunk_size but no file_size
518+ # Create reader with request_size but no file_size
519519 # Store has head() method so it should be used
520- reader = EagerStoreReader (traced_store , "test.txt" , chunk_size = 4 )
520+ reader = EagerStoreReader (traced_store , "test.txt" , request_size = 4 )
521521
522522 # Verify the data is correct
523523 assert reader .read () == data
524524
525525 # Verify get_ranges was used (head() call isn't traced, only data requests)
526526 summary = trace .summary ()
527- assert summary ["total_requests" ] == 4 # 16 bytes / 4 byte chunks
527+ assert summary ["total_requests" ] == 4 # 16 bytes / 4 byte requests
528528 assert all (r .method == "get_ranges" for r in trace .requests )
529529 assert summary ["total_bytes" ] == len (data )
530530
@@ -541,9 +541,9 @@ def test_eager_reader_falls_back_to_single_get():
541541 trace = RequestTrace ()
542542 traced_store = TracingReadableStore (mock_store , trace )
543543
544- # Create reader with chunk_size but no file_size and no head()
544+ # Create reader without file_size and no head()
545545 # Should fall back to single get() request
546- reader = EagerStoreReader (traced_store , "test.txt" , chunk_size = 4 )
546+ reader = EagerStoreReader (traced_store , "test.txt" , request_size = 4 )
547547
548548 # Verify the data is correct
549549 assert reader .read () == data
@@ -555,25 +555,25 @@ def test_eager_reader_falls_back_to_single_get():
555555 assert summary ["total_bytes" ] == len (data )
556556
557557
558- def test_eager_reader_no_chunk_size ():
559- """Test EagerStoreReader uses single get() when no chunk_size specified ."""
558+ def test_eager_reader_small_file_uses_single_get ():
559+ """Test EagerStoreReader uses single get() when file fits in one request ."""
560560 from obspec_utils .tracing import TracingReadableStore , RequestTrace
561561
562- # Create test data
562+ # Create test data smaller than default request_size (12 MB)
563563 data = b"0123456789ABCDEF"
564564 mock_store = MockReadableStoreWithHead (data )
565565
566566 # Wrap with tracing
567567 trace = RequestTrace ()
568568 traced_store = TracingReadableStore (mock_store , trace )
569569
570- # Create reader without chunk_size
570+ # Create reader with default settings - file is smaller than request_size
571571 reader = EagerStoreReader (traced_store , "test.txt" )
572572
573573 # Verify the data is correct
574574 assert reader .read () == data
575575
576- # Verify single get() was used
576+ # Verify single get() was used (skips concurrency overhead)
577577 summary = trace .summary ()
578578 assert summary ["total_requests" ] == 1
579579 assert trace .requests [0 ].method == "get"
@@ -591,8 +591,8 @@ def test_eager_reader_empty_file():
591591 trace = RequestTrace ()
592592 traced_store = TracingReadableStore (mock_store , trace )
593593
594- # Create reader with chunk_size and file_size=0
595- reader = EagerStoreReader (traced_store , "test.txt" , chunk_size = 4 , file_size = 0 )
594+ # Create reader with file_size=0
595+ reader = EagerStoreReader (traced_store , "test.txt" , request_size = 4 , file_size = 0 )
596596
597597 # Verify the data is empty
598598 assert reader .read () == b""
@@ -601,36 +601,96 @@ def test_eager_reader_empty_file():
601601 assert trace .total_requests == 0
602602
603603
604- def test_eager_reader_chunk_boundaries ():
605- """Test EagerStoreReader handles non-aligned chunk boundaries."""
604+ def test_eager_reader_request_boundaries ():
605+ """Test EagerStoreReader handles non-aligned request boundaries."""
606606 from obspec_utils .tracing import TracingReadableStore , RequestTrace
607607
608- # Create test data (10 bytes, not evenly divisible by chunk_size =4)
608+ # Create test data (10 bytes, not evenly divisible by request_size =4)
609609 data = b"0123456789"
610610 mock_store = MockReadableStoreWithHead (data )
611611
612612 # Wrap with tracing
613613 trace = RequestTrace ()
614614 traced_store = TracingReadableStore (mock_store , trace )
615615
616- # Create reader with chunk_size =4, file_size=10
616+ # Create reader with request_size =4, file_size=10
617617 reader = EagerStoreReader (
618- traced_store , "test.txt" , chunk_size = 4 , file_size = len (data )
618+ traced_store , "test.txt" , request_size = 4 , file_size = len (data )
619619 )
620620
621621 # Verify the data is correct
622622 assert reader .read () == data
623623
624- # Should be 3 chunks : 0-3 (4 bytes), 4-7 (4 bytes), 8-9 (2 bytes)
624+ # Should be 3 requests : 0-3 (4 bytes), 4-7 (4 bytes), 8-9 (2 bytes)
625625 summary = trace .summary ()
626626 assert summary ["total_requests" ] == 3
627627 assert summary ["total_bytes" ] == len (data )
628628
629- # Verify chunk sizes
629+ # Verify request sizes
630630 lengths = [r .length for r in trace .requests ]
631631 assert lengths == [4 , 4 , 2 ]
632632
633633
634+ def test_eager_reader_max_concurrent_requests ():
635+ """Test EagerStoreReader caps requests at max_concurrent_requests."""
636+ from obspec_utils .tracing import TracingReadableStore , RequestTrace
637+
638+ # Create test data (100 bytes)
639+ data = b"x" * 100
640+ mock_store = MockReadableStoreWithHead (data )
641+
642+ # Wrap with tracing
643+ trace = RequestTrace ()
644+ traced_store = TracingReadableStore (mock_store , trace )
645+
646+ # With request_size=10, would need 10 requests
647+ # But max_concurrent_requests=4, so should redistribute to 4 requests
648+ reader = EagerStoreReader (
649+ traced_store ,
650+ "test.txt" ,
651+ request_size = 10 ,
652+ file_size = len (data ),
653+ max_concurrent_requests = 4 ,
654+ )
655+
656+ # Verify the data is correct
657+ assert reader .read () == data
658+
659+ # Should be capped at 4 requests
660+ summary = trace .summary ()
661+ assert summary ["total_requests" ] == 4
662+ assert summary ["total_bytes" ] == len (data )
663+
664+
665+ def test_eager_reader_redistribution_even_split ():
666+ """Test EagerStoreReader redistributes evenly when capping requests."""
667+ from obspec_utils .tracing import TracingReadableStore , RequestTrace
668+
669+ # Create test data (100 bytes)
670+ data = b"x" * 100
671+ mock_store = MockReadableStoreWithHead (data )
672+
673+ # Wrap with tracing
674+ trace = RequestTrace ()
675+ traced_store = TracingReadableStore (mock_store , trace )
676+
677+ # With request_size=10, would need 10 requests
678+ # With max_concurrent_requests=4, should get 4 requests of 25 bytes each
679+ reader = EagerStoreReader (
680+ traced_store ,
681+ "test.txt" ,
682+ request_size = 10 ,
683+ file_size = len (data ),
684+ max_concurrent_requests = 4 ,
685+ )
686+
687+ assert reader .read () == data
688+
689+ # Verify redistributed request sizes (25, 25, 25, 25)
690+ lengths = [r .length for r in trace .requests ]
691+ assert lengths == [25 , 25 , 25 , 25 ]
692+
693+
634694@pytest .mark .parametrize ("ReaderClass" , ALL_READERS )
635695def test_reader_context_manager (ReaderClass ):
636696 """Test that readers work as context managers and release resources."""
0 commit comments