fix(api): fix tencent summary vector deadlock (#41916)

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
This commit is contained in:
FFXN 2026-09-07 08:31:08 +00:00 committed by GitHub
parent eba589db89
commit 60656a60cf
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 71 additions and 2 deletions

View File

@ -202,6 +202,11 @@ class TencentVector(BaseVector):
if metadatas is None: if metadatas is None:
continue continue
metadata = metadatas[i] or {} metadata = metadatas[i] or {}
if metadata.get("is_summary") is True:
# Tencent VectorDB JSON fields do not support boolean values.
# Use an integer only for the summary marker so other metadata
# and other vector backends keep their existing types.
metadata = {**metadata, "is_summary": 1}
doc = document.Document( doc = document.Document(
id=metadata.get("doc_id"), id=metadata.get("doc_id"),
vector=embeddings[i], vector=embeddings[i],

View File

@ -281,6 +281,23 @@ def test_create_add_delete_and_search_behaviour(tencent_module):
vector._client.drop_collection.assert_called_once() vector._client.drop_collection.assert_called_once()
def test_add_texts_converts_only_true_summary_marker(tencent_module):
vector = tencent_module.TencentVector("collection_1", _config(tencent_module))
summary_metadata = {"doc_id": "summary", "is_summary": True, "published": False}
regular_metadata = {"doc_id": "regular", "published": False}
docs = [
Document(page_content="summary", metadata=summary_metadata),
Document(page_content="regular", metadata=regular_metadata),
]
vector.add_texts(docs, [[0.1], [0.2]])
upserted = vector._client.upsert.call_args.kwargs["documents"]
assert upserted[0].metadata == {"doc_id": "summary", "is_summary": 1, "published": False}
assert upserted[1].metadata == regular_metadata
assert summary_metadata["is_summary"] is True
def test_tencent_factory_existing_and_generated_collection(tencent_module, monkeypatch: pytest.MonkeyPatch): def test_tencent_factory_existing_and_generated_collection(tencent_module, monkeypatch: pytest.MonkeyPatch):
factory = tencent_module.TencentVectorFactory() factory = tencent_module.TencentVectorFactory()
dataset_with_index = Dataset( dataset_with_index = Dataset(

View File

@ -517,8 +517,16 @@ class SummaryIndexService:
summary_record_id, summary_record_id,
original_session is not None, original_session is not None,
) )
# Always create a new session for error handling to avoid issues with closed sessions if original_session is not None:
# Even if original_session was provided, we create a new one for safety # Keep the error update in the caller-owned transaction. Opening another
# session here can deadlock when the caller has already flushed this row.
summary_record.status = SummaryStatus.ERROR
summary_record.error = f"Vectorization failed: {str(e)}"
summary_record.updated_at = datetime.now(UTC).replace(tzinfo=None)
original_session.add(summary_record)
raise
# Standalone callers still need this method to persist the error itself.
with session_factory.create_session() as error_session: with session_factory.create_session() as error_session:
# Try to find the record by id first # Try to find the record by id first
# Note: Using assignment only (no type annotation) to avoid redeclaration error # Note: Using assignment only (no type annotation) to avoid redeclaration error

View File

@ -281,6 +281,45 @@ def test_vectorize_summary_final_failure_updates_error_status(monkeypatch: pytes
error_session.commit.assert_called_once() error_session.commit.assert_called_once()
def test_vectorize_summary_failure_with_provided_session_does_not_open_error_session(
monkeypatch: pytest.MonkeyPatch,
) -> None:
dataset = _dataset()
segment = _segment()
summary = _summary_record(summary_content="sum", node_id=None)
monkeypatch.setattr(summary_module.uuid, "uuid4", MagicMock(return_value="uuid-1"))
monkeypatch.setattr(summary_module.helper, "generate_text_hash", MagicMock(return_value="hash-1"))
monkeypatch.setattr(
summary_module,
"Vector",
MagicMock(return_value=MagicMock(add_texts=MagicMock(side_effect=RuntimeError("boom")))),
)
monkeypatch.setattr(
summary_module.ModelManager,
"for_tenant",
MagicMock(return_value=MagicMock(get_model_instance=MagicMock(return_value=None))),
)
session = MagicMock(name="provided_session")
create_session_mock = MagicMock()
monkeypatch.setattr(
summary_module,
"session_factory",
SimpleNamespace(create_session=create_session_mock),
)
with pytest.raises(RuntimeError, match="boom"):
SummaryIndexService.vectorize_summary(summary, segment, dataset, session=session)
create_session_mock.assert_not_called()
session.add.assert_called_once_with(summary)
session.commit.assert_not_called()
session.flush.assert_not_called()
assert summary.status == SummaryStatus.ERROR
assert summary.error == "Vectorization failed: boom"
def test_batch_create_summary_records_no_segments_noop(monkeypatch: pytest.MonkeyPatch) -> None: def test_batch_create_summary_records_no_segments_noop(monkeypatch: pytest.MonkeyPatch) -> None:
create_session_mock = MagicMock() create_session_mock = MagicMock()
monkeypatch.setattr(summary_module, "session_factory", SimpleNamespace(create_session=create_session_mock)) monkeypatch.setattr(summary_module, "session_factory", SimpleNamespace(create_session=create_session_mock))