You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
[Feat]: Make the semantic ingestion poll interval configurable (currently hardcoded at 2s) #1699
The semantic memory background ingestion loop polls on a fixed 2-second interval that cannot be changed by configuration. Every sibling knob that governs the same loop is configurable; this one is the exception.
So the 2.0 default always wins. Grepping the tree for feature_update_interval_sec returns the declaration, that one assignment, and three test files that set it to 0.05 either through Params directly or by assigning the private attribute (test_memmachine_integration.py:165). There is no config key, no environment variable, and no field on the v2 config API.
Operators therefore cannot trade ingestion latency against database load. Every deployment scans for dirty sets twice a second whether or not there is work, and the only way to change that is to patch the source.
Describe the solution you'd like
Add a poll interval to SemanticMemoryConf alongside the triggers it belongs with (common/configuration/__init__.py:167-183) and wire it through the manager:
ingestion_poll_interval_seconds: float=Field(
default=2.0,
description=(
"How often the background ingestion loop checks for sets with ""uningested messages. Larger values reduce idle database load at ""the cost of ingestion latency."
),
gt=0,
)
Keeping 2.0 as the default makes this backward compatible. ingestion_trigger_messages and ingestion_trigger_age_seconds are already exposed over the v2 config API (config_spec.py:562-566), so exposing this one there too would be consistent, though a plain config key covers the need.
Describe alternatives you've considered
Leaving it hardcoded and relying on the work already done for #1251. That issue landed four of its five proposed fixes, and they help a great deal: the GROUP BY/HAVING rewrite (#1253), the (set_id, ingested) composite index at sqlalchemy_pgvector_semantic.py:168, purge_ingested_rows in the loop, and the error backoff that doubles to a 60s ceiling. What none of them give an operator is a way to say "poll less often". The per-query cost dropped; the query rate did not.
Patching the constant in a fork works but does not survive upgrades.
Additional context
Context from prior reports:
Background ingestion polling causes unbounded database load growth #1251 (closed, priority: high, v0.3.3) measured the cost of this cadence in production: 14,055 rows in set_ingested_history, Aurora ACU climbing from 2.5 to 12.3 and hitting the 16 ACU cap, with roughly $416/month attributed to the polling loop while no users were active. Its fifth proposed fix, running one ingestion loop instead of one per uvicorn worker, was set aside because MemMachine assumes a single server instance per database, so a configurable interval is the remaining lever for anyone whose load profile does not suit a 2-second poll.
[Bug]: Ingest Interval not triggered anymore #753 (closed) is a reminder that the current value is not discoverable. The reporter guessed "(I think it used to be) 2min" while filing a bug against this very loop.
Secondary finding, same area and small enough to fold into the same change. consolidation_threshold (semantic_memory.py:96, default 20) is also absent from the semantic_manager.py construction site, and SemanticService assigns it to self._consolidation_threshold at :134 and never reads it again. The IngestionService.Params built at :786 does not pass consolidated_threshold, so the ingestion service falls back to its own default of 20 (semantic_ingestion.py:82). The attribute on SemanticService is dead. Either wire it through or delete it.
Is your feature request related to a problem?
The semantic memory background ingestion loop polls on a fixed 2-second interval that cannot be changed by configuration. Every sibling knob that governs the same loop is configurable; this one is the exception.
SemanticService.Paramsdeclares the value with a default atsemantic_memory.py:98:It reaches the loop via
semantic_memory.py:124, and the loop sleeps on it at:808(idle path) and:836(clean-pass path).The only production construction site is
semantic_manager.py:245, which passes the three neighbouring knobs and omits this one:So the 2.0 default always wins. Grepping the tree for
feature_update_interval_secreturns the declaration, that one assignment, and three test files that set it to0.05either throughParamsdirectly or by assigning the private attribute (test_memmachine_integration.py:165). There is no config key, no environment variable, and no field on the v2 config API.Operators therefore cannot trade ingestion latency against database load. Every deployment scans for dirty sets twice a second whether or not there is work, and the only way to change that is to patch the source.
Describe the solution you'd like
Add a poll interval to
SemanticMemoryConfalongside the triggers it belongs with (common/configuration/__init__.py:167-183) and wire it through the manager:Keeping 2.0 as the default makes this backward compatible.
ingestion_trigger_messagesandingestion_trigger_age_secondsare already exposed over the v2 config API (config_spec.py:562-566), so exposing this one there too would be consistent, though a plain config key covers the need.Describe alternatives you've considered
Leaving it hardcoded and relying on the work already done for #1251. That issue landed four of its five proposed fixes, and they help a great deal: the
GROUP BY/HAVINGrewrite (#1253), the(set_id, ingested)composite index atsqlalchemy_pgvector_semantic.py:168,purge_ingested_rowsin the loop, and the error backoff that doubles to a 60s ceiling. What none of them give an operator is a way to say "poll less often". The per-query cost dropped; the query rate did not.Patching the constant in a fork works but does not survive upgrades.
Additional context
Context from prior reports:
set_ingested_history, Aurora ACU climbing from 2.5 to 12.3 and hitting the 16 ACU cap, with roughly $416/month attributed to the polling loop while no users were active. Its fifth proposed fix, running one ingestion loop instead of one per uvicorn worker, was set aside because MemMachine assumes a single server instance per database, so a configurable interval is the remaining lever for anyone whose load profile does not suit a 2-second poll.Secondary finding, same area and small enough to fold into the same change.
consolidation_threshold(semantic_memory.py:96, default 20) is also absent from thesemantic_manager.pyconstruction site, andSemanticServiceassigns it toself._consolidation_thresholdat:134and never reads it again. TheIngestionService.Paramsbuilt at:786does not passconsolidated_threshold, so the ingestion service falls back to its own default of 20 (semantic_ingestion.py:82). The attribute onSemanticServiceis dead. Either wire it through or delete it.Verified against
73937aconmain.