SIENTIAPDE-2013: cap load_latest_data's Mongo fetch at a configurable batch size

A cold start or notification storm had no backstop: load_latest_data
fetched the entire matching backlog in one query. Confirmed live during
SIENTIAPDE-2012's cluster verification -- an empty Redis cursor plus a
real recurring error let a single run's query exceed Temporal's 4MB
message limit and get stuck retrying forever.

Threads a new notification_queue_batch_size config (env var
NOTIFICATION_QUEUE_BATCH_SIZE, default 5000) through to the repository's
find(), hard cap per run, no drain loop -- the incremental
last_data_timestamp cursor picks up any remainder next run, per the
parent ticket's design decision. Sorts by timestamp ascending so a
capped run always returns the oldest pending documents first; without
that, a capped batch has no guarantee of hitting the oldest documents,
and the cursor would advance past whatever it skipped.

Requires sientia_do>=1.13.0 (adds limit/sort params to
MongoDBRepository.find in both the sync and async client) -- bumped the
pin here per the parent ticket's decision to do it in the same PR.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Eduardo Rios
2026-09-17 09:33:46 -03:00
parent d250383f78
commit 9c153041ad
8 changed files with 79 additions and 6 deletions

View File

@@ -84,6 +84,7 @@ class Activities(TemporalManager, SlotManager, Formatters, MongoDB, Email, Postg
connection_string=mongodb_config['connection_string'],
database_name=mongodb_config['database_name'],
ttl_index_seconds=mongodb_config['ttl_index_seconds'],
notification_queue_batch_size=mongodb_config['notification_queue_batch_size'],
logger=logger,
notification_handler=notification_handler,
metrics_controller=metrics_controller,

View File

@@ -31,6 +31,8 @@ class MongoDB(SientiaMonitoring):
ttl_index_seconds (int): TTL index duration in seconds
logger (Logger): Application logger instance
notification_handler (NotificationHandler): Notification management handler
notification_queue_batch_size (int): Hard cap on documents fetched per
`load_latest_data` run (SIENTIAPDE-2013). `0` means no limit.
"""
def __init__(
@@ -41,6 +43,7 @@ class MongoDB(SientiaMonitoring):
logger: Logger,
notification_handler: NotificationHandler,
metrics_controller: MetricsController,
notification_queue_batch_size: int = 0,
):
SientiaMonitoring.__init__(
self,
@@ -61,6 +64,7 @@ class MongoDB(SientiaMonitoring):
)
self.ttl_index_seconds = ttl_index_seconds
self.notification_queue_batch_size = notification_queue_batch_size
# Initialize MongoDB client here (omitted for brevity)
logger.info('MongoDB connection initialized')
@@ -447,6 +451,16 @@ class MongoDB(SientiaMonitoring):
filtering by timestamp to enable incremental data processing. It
handles connection management and provides comprehensive error reporting.
SIENTIAPDE-2013: the query is capped at ``notification_queue_batch_size``
documents (hard cap per run, no drain loop) and sorted by ``timestamp``
ascending, so a capped run always returns the *oldest* pending documents
first. That matters because the caller advances its incremental cursor to
the max timestamp of whatever this returns -- without the ascending sort,
a capped batch could skip over older documents that never get picked up
on a later run. This backstops a cold start, backlog buildup, or
notification storm from fetching an unbounded (and potentially
multi-megabyte) payload in one query.
Args:
input_data (dict[str, Any]): Activity input parameters.
Required fields:
@@ -488,7 +502,13 @@ class MongoDB(SientiaMonitoring):
self.debug(f'Data filter: {data_filter}', metadata=metadata)
data = self.mongo_db_repository.find(collection_name, data_filter, metadata)
data = self.mongo_db_repository.find(
collection_name,
data_filter,
metadata,
limit=self.notification_queue_batch_size,
sort=[('timestamp', 1)],
)
self.debug(f'Collected: {data}', metadata=metadata)

View File

@@ -21,7 +21,8 @@ def build_mongodb_config():
Build MongoDB configuration from environment variables.
Returns:
dict: MongoDB configuration with connection string, database name, and TTL index seconds.
dict: MongoDB configuration with connection string, database name, TTL index seconds,
and the notification queue's per-run batch size cap.
"""
username = getenv('MONGODB_USERNAME', 'root')
password = getenv('MONGODB_PASSWORD', 'wKZDbMNU1c')
@@ -32,6 +33,7 @@ def build_mongodb_config():
'connection_string': connection_string,
'database_name': getenv('MONGODB_DATABASE_NAME', 'sientia'),
'ttl_index_seconds': int(getenv('MONGODB_TTL_INDEX_HOURS', '1')) * 3600,
'notification_queue_batch_size': int(getenv('NOTIFICATION_QUEUE_BATCH_SIZE', '5000')),
}