Skip to content

Commit 506d919

Browse files
mbm-codesjyejare
andauthored
fix: Fix ValueError in signal handling for Trino worker threads (#6428)
* fix: fix signal.signal() ValueError in Trino worker threads Signed-off-by: MBM <mihir105@gmail.com> * test: patch signal.signal in main thread test to avoid handler leak Signed-off-by: MBM <mihir105@gmail.com> * Update sdk/python/feast/infra/offline_stores/contrib/trino_offline_store/tests/test_trino_queries.py Apply reviewer suggestion: verify correct signals and handler in test Co-authored-by: Jitendra Yejare <jyejare@redhat.com> Signed-off-by: MBM <mihir105@gmail.com> * test: address PR feedback for worker thread errors and signals - Simplify worker thread assertion message to safely print errors list - Add verification for signal handler registration details Signed-off-by: MBM <mihir105@gmail.com> --------- Signed-off-by: MBM <mihir105@gmail.com> Co-authored-by: Jitendra Yejare <jyejare@redhat.com>
1 parent cd5f6bb commit 506d919

2 files changed

Lines changed: 52 additions & 2 deletions

File tree

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import signal
2+
import threading
3+
from unittest.mock import MagicMock, patch
4+
5+
from feast.infra.offline_stores.contrib.trino_offline_store.trino_queries import (
6+
Query,
7+
)
8+
9+
10+
def test_query_init_in_main_thread_registers_signals():
11+
"""signal.signal() should work fine in main thread."""
12+
13+
# Should not raise any exception in main thread
14+
cursor = MagicMock()
15+
16+
with patch("signal.signal") as mock_signal:
17+
query = Query(query_text="SELECT 1", cursor=cursor)
18+
assert query.query_text == "SELECT 1"
19+
20+
# Verify signal handlers are registered correctly
21+
mock_signal.assert_any_call(signal.SIGINT, query.cancel)
22+
mock_signal.assert_any_call(signal.SIGTERM, query.cancel)
23+
24+
# Expected signal.signal to be called twice for SIGINT and SIGTERM
25+
assert mock_signal.call_count == 2
26+
27+
28+
def test_query_init_in_worker_thread_does_not_raise():
29+
"""Regression test: signal.signal() fails in non-main threads."""
30+
# signal.signal() raises ValueError when called outside the main thread.
31+
# This test verifies the fix guards against that by running Query.__init__
32+
# in a worker thread and ensuring no exception is raised.
33+
34+
errors = []
35+
cursor = MagicMock()
36+
37+
def create_query():
38+
try:
39+
query = Query(query_text="SELECT 1", cursor=cursor)
40+
assert query.query_text == "SELECT 1"
41+
except ValueError as e:
42+
errors.append(e)
43+
44+
thread = threading.Thread(target=create_query)
45+
thread.start()
46+
thread.join()
47+
48+
assert not errors, f"Unexpected ValueError in worker thread: {errors}"

sdk/python/feast/infra/offline_stores/contrib/trino_offline_store/trino_queries.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
from __future__ import annotations
22

33
import signal
4+
import threading
45
from dataclasses import dataclass
56
from enum import Enum
67
from typing import Any, Dict, List, Optional
@@ -92,8 +93,9 @@ def __init__(self, query_text: str, cursor: Cursor):
9293
self.status = QueryStatus.PENDING
9394
self._cursor = cursor
9495

95-
signal.signal(signal.SIGINT, self.cancel)
96-
signal.signal(signal.SIGTERM, self.cancel)
96+
if threading.current_thread() is threading.main_thread():
97+
signal.signal(signal.SIGINT, self.cancel)
98+
signal.signal(signal.SIGTERM, self.cancel)
9799

98100
def execute(self) -> Results:
99101
try:

0 commit comments

Comments
 (0)