Skip to content

Commit 55abc2f

Browse files
authored
Support query timeout as an argument in CassandraToGCSOperator (#18927)
Support query timeout as an argument in CassandraToGCSOperator (#18927)
1 parent fd569e7 commit 55abc2f

2 files changed

Lines changed: 22 additions & 2 deletions

File tree

airflow/providers/google/cloud/transfers/cassandra_to_gcs.py

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
from datetime import datetime
2727
from decimal import Decimal
2828
from tempfile import NamedTemporaryFile
29-
from typing import Any, Dict, Iterable, List, Optional, Sequence, Tuple, Union
29+
from typing import Any, Dict, Iterable, List, NewType, Optional, Sequence, Tuple, Union
3030
from uuid import UUID
3131

3232
from cassandra.util import Date, OrderedMapSerializedKey, SortedSet, Time
@@ -36,6 +36,9 @@
3636
from airflow.providers.apache.cassandra.hooks.cassandra import CassandraHook
3737
from airflow.providers.google.cloud.hooks.gcs import GCSHook
3838

39+
NotSetType = NewType('NotSetType', object)
40+
NOT_SET = NotSetType(object())
41+
3942

4043
class CassandraToGCSOperator(BaseOperator):
4144
"""
@@ -84,6 +87,10 @@ class CassandraToGCSOperator(BaseOperator):
8487
Service Account Token Creator IAM role to the directly preceding identity, with first
8588
account from the list granting this role to the originating account (templated).
8689
:type impersonation_chain: Union[str, Sequence[str]]
90+
:param query_timeout: (Optional) The amount of time, in seconds, used to execute the Cassandra query.
91+
If not set, the timeout value will be set in Session.execute() by Cassandra driver.
92+
If set to None, there is no timeout.
93+
:type query_timeout: float | None
8794
"""
8895

8996
template_fields = (
@@ -110,6 +117,7 @@ def __init__(
110117
google_cloud_storage_conn_id: Optional[str] = None,
111118
delegate_to: Optional[str] = None,
112119
impersonation_chain: Optional[Union[str, Sequence[str]]] = None,
120+
query_timeout: Union[float, None, NotSetType] = NOT_SET,
113121
**kwargs,
114122
) -> None:
115123
super().__init__(**kwargs)
@@ -133,6 +141,7 @@ def __init__(
133141
self.delegate_to = delegate_to
134142
self.gzip = gzip
135143
self.impersonation_chain = impersonation_chain
144+
self.query_timeout = query_timeout
136145

137146
# Default Cassandra to BigQuery type mapping
138147
CQL_TYPE_MAP = {
@@ -162,7 +171,12 @@ def __init__(
162171

163172
def execute(self, context: Dict[str, str]):
164173
hook = CassandraHook(cassandra_conn_id=self.cassandra_conn_id)
165-
cursor = hook.get_conn().execute(self.cql)
174+
175+
query_extra = {}
176+
if self.query_timeout is not NOT_SET:
177+
query_extra['timeout'] = self.query_timeout
178+
179+
cursor = hook.get_conn().execute(self.cql, **query_extra)
166180

167181
files_to_upload = self._write_local_data_files(cursor)
168182

tests/providers/google/cloud/transfers/test_cassandra_to_gcs.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ def test_execute(self, mock_hook, mock_upload, mock_tempfile):
3434
schema = "schema.json"
3535
filename = "data.json"
3636
gzip = True
37+
query_timeout = 20
3738
mock_tempfile.return_value.name = TMP_FILE_NAME
3839

3940
operator = CassandraToGCSOperator(
@@ -43,9 +44,14 @@ def test_execute(self, mock_hook, mock_upload, mock_tempfile):
4344
filename=filename,
4445
schema_filename=schema,
4546
gzip=gzip,
47+
query_timeout=query_timeout,
4648
)
4749
operator.execute(None)
4850
mock_hook.return_value.get_conn.assert_called_once_with()
51+
mock_hook.return_value.get_conn.return_value.execute.assert_called_once_with(
52+
"select * from keyspace1.table1",
53+
timeout=20,
54+
)
4955

5056
call_schema = call(
5157
bucket_name=test_bucket,

0 commit comments

Comments
 (0)