Skip to content

Commit fa72db5

Browse files
fix(watson): only intermediate-flush the global search context singleton (#15187)
The intermediate-flush hook patches SearchContextManager.add_to_context at class level, so it also fires inside update_watson_search_index_for_model's own ad-hoc context. A task processing a full WATSON_ASYNC_INDEX_UPDATE_BATCH_SIZE batch re-dispatches a clone of itself, discards those pks unindexed, and loops forever: queue length stays ~0 while the worker is pegged republishing the same batches, and nothing gets indexed. Guard the hook so only the global singleton (the request-path context managed by AsyncSearchContextMiddleware) intermediate-flushes. Ad-hoc context managers index their own batch on end(), as stock watson does. Extracted from the hardening half of #15165 for the bugfix branch. Co-authored-by: dogboat <dogboat@users.noreply.github.com>
1 parent 94990ac commit fa72db5

2 files changed

Lines changed: 29 additions & 1 deletion

File tree

dojo/middleware.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -312,6 +312,15 @@ def install_intermediate_flush_hook():
312312

313313
def add_to_context_with_flush(self, engine, obj):
314314
original_add(self, engine, obj)
315+
# The intermediate flush is a request-path optimization on the global singleton
316+
# context (AsyncSearchContextMiddleware). The async reindex task
317+
# update_watson_search_index_for_model() builds its OWN SearchContextManager and
318+
# IS the drain target -- if it re-drained its batch it would dispatch a clone of
319+
# itself, discard those pks unindexed, and loop forever (queue ~0, worker pegged,
320+
# nothing indexed). Only the singleton intermediate-flushes; any ad-hoc context
321+
# manager indexes its own batch on end().
322+
if self is not search_context_manager:
323+
return
315324
threshold = getattr(settings, "WATSON_ASYNC_INDEX_UPDATE_BATCH_SIZE", 1000)
316325
if threshold <= 0 or not self._stack:
317326
return

unittests/test_watson_intermediate_flush.py

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
from unittest.mock import patch
1111

1212
from django.test import override_settings
13-
from watson.search import search_context_manager
13+
from watson.search import SearchContextManager, search_context_manager
1414

1515
from dojo.middleware import (
1616
_drain_search_context_to_async, # noqa: PLC2701 -- internal helper under test
@@ -119,3 +119,22 @@ def test_invalidated_context_skips_drain(self):
119119
# hook should detect the invalid flag and bail out.
120120
search_context_manager.add_to_context(engine_marker, p)
121121
drain.assert_not_called()
122+
123+
@override_settings(WATSON_ASYNC_INDEX_UPDATE_BATCH_SIZE=2)
124+
def test_ad_hoc_context_manager_does_not_drain(self):
125+
"""
126+
The intermediate flush is a request-path optimization on the global singleton. An
127+
ad-hoc SearchContextManager -- e.g. the one update_watson_search_index_for_model builds to
128+
index its own batch -- must NOT drain, or it would dispatch a clone of itself and loop
129+
forever (queue ~0, worker pegged, nothing indexed).
130+
"""
131+
adhoc = SearchContextManager()
132+
adhoc.start()
133+
try:
134+
with patch("dojo.middleware._drain_search_context_to_async") as drain:
135+
for p in self.products[:3]: # past the threshold of 2
136+
adhoc.add_to_context(object(), p)
137+
drain.assert_not_called()
138+
finally:
139+
adhoc.invalidate()
140+
adhoc.end()

0 commit comments

Comments
 (0)