1919
2020class _Paginator :
2121
22- def __init__ (self , request , * , executor , loop ):
22+ def __init__ (self , request , * , executor , loop , max_in_memory_pages = None ):
2323 self .cassandra_fut = None
2424
2525 self ._request = request
@@ -30,10 +30,35 @@ def __init__(self, request, *, executor, loop):
3030 self ._deque = deque ()
3131 self ._exc = None
3232 self ._drain_event = asyncio .Event (loop = loop )
33+ self ._no_fetching_page = asyncio .Event (loop = loop )
3334 self ._finish_event = asyncio .Event (loop = loop )
3435 self ._exit_event = Event ()
3536
3637 self .__pages = set ()
38+ self ._max_in_memory_pages = max_in_memory_pages
39+ self ._page_size = None
40+
41+ def _start_fetching_next_page (self ):
42+ self ._no_fetching_page .clear ()
43+ _fn = self .cassandra_fut .start_fetching_next_page
44+ fut = self ._loop .run_in_executor (self ._executor , _fn )
45+ self .__pages .add (fut )
46+ fut .add_done_callback (self .__pages .remove )
47+
48+ def _maybe_start_prefetch_next_page (self ):
49+ if self ._finish_event .is_set () or not self ._no_fetching_page .is_set ():
50+ return
51+
52+ if not self .cassandra_fut .has_more_pages :
53+ self ._finish_event .set ()
54+ return
55+
56+ if self ._max_in_memory_pages is None :
57+ pass
58+ elif len (self ._deque ) > self ._page_size * (self ._max_in_memory_pages - 1 ):
59+ return
60+
61+ self ._start_fetching_next_page ()
3762
3863 def _handle_page (self , rows ):
3964 if self ._exit_event .is_set ():
@@ -42,19 +67,15 @@ def _handle_page(self, rows):
4267 'Paginator is closed, skipping new %i records' , _len )
4368 return
4469
70+ if self ._page_size is None :
71+ self ._page_size = len (rows )
72+
4573 for row in rows :
4674 self ._deque .append (row )
4775
76+ self ._loop .call_soon_threadsafe (self ._no_fetching_page .set )
4877 self ._loop .call_soon_threadsafe (self ._drain_event .set )
49-
50- if self .cassandra_fut .has_more_pages :
51- _fn = self .cassandra_fut .start_fetching_next_page
52- fut = self ._loop .run_in_executor (self ._executor , _fn )
53- self .__pages .add (fut )
54- fut .add_done_callback (self .__pages .remove )
55- return
56-
57- self ._loop .call_soon_threadsafe (self ._finish_event .set )
78+ self ._loop .call_soon_threadsafe (self ._maybe_start_prefetch_next_page )
5879
5980 def _handle_err (self , exc ):
6081 self ._exc = exc
@@ -102,8 +123,11 @@ async def _paginator(self):
102123 if self ._exc is not None :
103124 raise self ._exc
104125
126+ self ._maybe_start_prefetch_next_page ()
127+
105128 while self ._deque :
106129 await yield_ (self ._deque .popleft ())
130+ self ._maybe_start_prefetch_next_page ()
107131
108132 await asyncio .wait (
109133 (
@@ -153,12 +177,13 @@ async def execute_future(self, *args, **kwargs):
153177 return await asyncio_fut
154178
155179
156- def execute_futures (self , * args , ** kwargs ):
180+ def execute_futures (self , * args , max_in_memory_pages = None , ** kwargs ):
157181 _request = partial (self .execute_async , * args , ** kwargs )
158182 return _Paginator (
159183 _request ,
160184 executor = self ._asyncio_executor ,
161- loop = self ._asyncio_loop
185+ loop = self ._asyncio_loop ,
186+ max_in_memory_pages = max_in_memory_pages
162187 )
163188
164189
0 commit comments