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:
@@ -27,6 +27,7 @@ def test___init__(
|
||||
'connection_string': 'mongodb://localhost:27017',
|
||||
'database_name': 'test_db',
|
||||
'ttl_index_seconds': 3600,
|
||||
'notification_queue_batch_size': 5000,
|
||||
}
|
||||
|
||||
redis_config = {'host': 'localhost', 'port': 6379, 'username': 'admin', 'password': 'password'}
|
||||
@@ -89,6 +90,7 @@ def test___init__(
|
||||
connection_string='mongodb://localhost:27017',
|
||||
database_name='test_db',
|
||||
ttl_index_seconds=3600,
|
||||
notification_queue_batch_size=5000,
|
||||
logger=logger,
|
||||
notification_handler=notification_handler,
|
||||
metrics_controller=mock_metrics_controller.return_value,
|
||||
|
||||
@@ -31,13 +31,14 @@ def test___init__(mongo_mock):
|
||||
notification_handler = MagicMock()
|
||||
metrics_controller = AsyncMock()
|
||||
|
||||
MongoDB(
|
||||
mongo = MongoDB(
|
||||
connection_string='mongodb://localhost:27017',
|
||||
database_name='test_db',
|
||||
ttl_index_seconds=3600,
|
||||
logger=logger,
|
||||
notification_handler=notification_handler,
|
||||
metrics_controller=metrics_controller,
|
||||
notification_queue_batch_size=5000,
|
||||
)
|
||||
|
||||
mongo_mock.assert_called_once_with(
|
||||
@@ -47,6 +48,22 @@ def test___init__(mongo_mock):
|
||||
notification_handler=notification_handler,
|
||||
metrics_controller=metrics_controller,
|
||||
)
|
||||
assert mongo.notification_queue_batch_size == 5000
|
||||
|
||||
|
||||
@patch('orchestrator.activities.mongo_db.MongoDBRepository')
|
||||
def test___init___notification_queue_batch_size_defaults_to_zero(mongo_mock):
|
||||
"""0 means no limit, matching the underlying find()'s own convention."""
|
||||
mongo = MongoDB(
|
||||
connection_string='mongodb://localhost:27017',
|
||||
database_name='test_db',
|
||||
ttl_index_seconds=3600,
|
||||
logger=MagicMock(),
|
||||
notification_handler=MagicMock(),
|
||||
metrics_controller=AsyncMock(),
|
||||
)
|
||||
|
||||
assert mongo.notification_queue_batch_size == 0
|
||||
|
||||
|
||||
@patch('orchestrator.activities.mongo_db.SientiaMonitoring')
|
||||
@@ -480,6 +497,8 @@ def test_load_latest_data_none_last_data_timestamp(mongo_db):
|
||||
'test_collection',
|
||||
{'level': 'ERROR'},
|
||||
{'workflow_name': 'test_pipeline', 'schedule_name': 'test_schedule'},
|
||||
limit=0,
|
||||
sort=[('timestamp', 1)],
|
||||
)
|
||||
|
||||
assert result == [
|
||||
@@ -487,6 +506,29 @@ def test_load_latest_data_none_last_data_timestamp(mongo_db):
|
||||
]
|
||||
|
||||
|
||||
def test_load_latest_data_passes_configured_batch_size_as_limit(mongo_db):
|
||||
"""SIENTIAPDE-2013: the configured notification_queue_batch_size caps the query."""
|
||||
mongo_db.notification_queue_batch_size = 100
|
||||
mongo_db.mongo_db_repository.find = MagicMock(return_value=[])
|
||||
|
||||
mongo_db.load_latest_data(
|
||||
{
|
||||
'metadata': {'workflow_name': 'test_pipeline', 'schedule_name': 'test_schedule'},
|
||||
'collection_name': 'test_collection',
|
||||
'last_data_timestamp': None,
|
||||
'base_data_filter': {'level': 'ERROR'},
|
||||
}
|
||||
)
|
||||
|
||||
mongo_db.mongo_db_repository.find.assert_called_once_with(
|
||||
'test_collection',
|
||||
{'level': 'ERROR'},
|
||||
{'workflow_name': 'test_pipeline', 'schedule_name': 'test_schedule'},
|
||||
limit=100,
|
||||
sort=[('timestamp', 1)],
|
||||
)
|
||||
|
||||
|
||||
def test_load_latest_data_not_none_last_data_timestamp(mongo_db):
|
||||
"""Test load_latest_data compares native datetime against native datetime in $gt"""
|
||||
|
||||
@@ -516,6 +558,8 @@ def test_load_latest_data_not_none_last_data_timestamp(mongo_db):
|
||||
'timestamp': {'$gt': datetime(2023, 1, 1, 12, 0, 0, tzinfo=UTC)},
|
||||
},
|
||||
{'workflow_name': 'test_pipeline', 'schedule_name': 'test_schedule'},
|
||||
limit=0,
|
||||
sort=[('timestamp', 1)],
|
||||
)
|
||||
|
||||
assert result == [
|
||||
@@ -529,7 +573,7 @@ def test_load_latest_data_run_boundary_no_skip_or_duplicate(mongo_db):
|
||||
|
||||
boundary_timestamp = datetime(2023, 1, 1, 12, 0, 0, tzinfo=UTC)
|
||||
|
||||
def fake_find(collection_name, data_filter, metadata):
|
||||
def fake_find(collection_name, data_filter, metadata, limit=0, sort=None):
|
||||
gt = data_filter.get('timestamp', {}).get('$gt')
|
||||
all_docs = [
|
||||
{
|
||||
|
||||
@@ -64,10 +64,12 @@ def test_build_mongo_db_config_with_env_vars():
|
||||
environ['MONGODB_URL'] = 'localhost:27018'
|
||||
environ['MONGODB_DATABASE_NAME'] = 'test_db'
|
||||
environ['MONGODB_TTL_INDEX_HOURS'] = '2'
|
||||
environ['NOTIFICATION_QUEUE_BATCH_SIZE'] = '2000'
|
||||
assert build_mongodb_config() == {
|
||||
'connection_string': 'mongodb://sientia1:sientia1@localhost:27018',
|
||||
'database_name': 'test_db',
|
||||
'ttl_index_seconds': 7200,
|
||||
'notification_queue_batch_size': 2000,
|
||||
}
|
||||
|
||||
|
||||
@@ -77,10 +79,12 @@ def test_build_mongo_db_config_with_defaults():
|
||||
environ.pop('MONGODB_DATABASE_NAME', None)
|
||||
environ.pop('MONGODB_URL', None)
|
||||
environ.pop('MONGODB_TTL_INDEX_HOURS', None)
|
||||
environ.pop('NOTIFICATION_QUEUE_BATCH_SIZE', None)
|
||||
assert build_mongodb_config() == {
|
||||
'connection_string': 'mongodb://root:wKZDbMNU1c@localhost:27018',
|
||||
'database_name': 'sientia',
|
||||
'ttl_index_seconds': 3600,
|
||||
'notification_queue_batch_size': 5000,
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user