@@ -242,6 +242,24 @@ def __exit__(self, exc_type, exc_val, exc_tb):
242
242
pass
243
243
244
244
245
+ class SlidingWindowRateLimiter :
246
+ def __init__ (self , max_calls , period ):
247
+ self .max_calls = max_calls
248
+ self .period = period
249
+ self .timestamps = [0 ] * max_calls
250
+ self .index = 0
251
+
252
+ def acquire (self ):
253
+ now = time .time ()
254
+ window_start = now - self .period
255
+
256
+ if self .timestamps [self .index ] <= window_start :
257
+ self .timestamps [self .index ] = now
258
+ self .index = (self .index + 1 ) % self .max_calls
259
+ return True
260
+ return False
261
+
262
+
245
263
class CassandraOnlineStore (OnlineStore ):
246
264
"""
247
265
Cassandra/Astra DB online store implementation for Feast.
@@ -410,7 +428,8 @@ def on_failure(exc, concurrent_queue):
410
428
raise Exception ("Error writing a batch" ) from exc
411
429
412
430
write_concurrency = config .online_store .write_concurrency
413
- concurrent_queue : queue .Queue = queue .Queue (maxsize = write_concurrency )
431
+ rate_limiter = SlidingWindowRateLimiter (write_concurrency , 1 )
432
+ concurrent_queue : queue .Queue = queue .Queue (maxsize = write_concurrency / 5 )
414
433
415
434
project = config .project
416
435
ttl = (
@@ -444,6 +463,12 @@ def on_failure(exc, concurrent_queue):
444
463
timestamp ,
445
464
)
446
465
batch .add (insert_cql , params )
466
+
467
+ # Wait until the rate limiter allows
468
+ if not rate_limiter .acquire ():
469
+ while not rate_limiter .acquire ():
470
+ time .sleep (0.001 )
471
+
447
472
future = session .execute_async (batch )
448
473
concurrent_queue .put (future )
449
474
future .add_callbacks (
@@ -460,6 +485,7 @@ def on_failure(exc, concurrent_queue):
460
485
# this happens N-1 times, will be corrected outside:
461
486
if progress :
462
487
progress (1 )
488
+ # Wait for all tasks to complete
463
489
while not concurrent_queue .empty ():
464
490
time .sleep (0.001 )
465
491
0 commit comments