Uh oh!
There was an error while loading. Please reload this page.
Add deferrable support to InfluxDB3Operator - #71976
Conversation
Signed-off-by: subhramit <subhramit.bb@live.in>
Signed-off-by: subhramit <subhramit.bb@live.in>
Signed-off-by: subhramit <subhramit.bb@live.in>
InfluxDB3OperatorAh, I think I missed a lowest-direct-dependencies edge case here. The spellcheck also fails, will address both in the next commit. Update - fixed. |
Signed-off-by: Subhramit Basu <subhramit.bb@live.in>
cc'ing @eladkal for visibility, in case any specific codeowners are to be requested for review. |
eladkal
commented
Aug 23, 2026
cc @arpitrathore can you take a look? |
+1 I hope... I have cc'ed them in the issue as well, but they seem to have been away from GitHub since quite some time now after their last comment expressing that they wanted to work on this. |
Adding some other recent committers in case the PR gets lost @bbovenzi, @Lee-W, @hussein-awala,@kaxil, After this is done, I wish to work on #67109 as a follow-up. |
eladkal
left a comment
There was a problem hiding this comment.
left few comments.
Can you please confirm if this was tested against a real influx environment?
Uh oh!
There was an error while loading. Please reload this page.
| :param deferrable: Run the query from the triggerer instead of holding a worker slot for its | ||
| duration. Requires ``influxdb3-python>=0.12.0``. Note that InfluxDB 3 streams results over | ||
| Arrow Flight rather than exposing a job that can be polled, so the whole result set still flows back | ||
| through XCom -- deferring helps with long-running queries returning modest result sets | ||
| (aggregations, freshness probes), not with very large extracts. |
There was a problem hiding this comment.
Please cleanup here. Explain just what is needed.
Requires influxdb3-python>=0.12.0 is not helpful. The provider already set this min version so users don't need to make sure they hae it.
To be honest I am not sure I understand what this warning means.
There was a problem hiding this comment.
Was just meaning to say that since the InfluxDB 3 client doesn’t expose a poll-based async job model here (see PR desc for details), deferrable=True releases the worker slot while the query runs in the triggerer, but the full result still comes back through XCom when the task resumes. So the main benefit is for long-running queries with relatively small result sets, not for very large extracts
Refined with better wording now
| InfluxDB 3 streams query results over a single Arrow Flight call rather than exposing a job that | ||
| can be polled, so the trigger awaits the query once instead of polling at an interval, and there |
There was a problem hiding this comment.
This seems to be a statement over how Influxdb operate. Can you please update the info with a link to the Influx docs? This is needed to confirm that are assumptions are right and in case that Influx change how it works we can track the change/
There was a problem hiding this comment.
Added, and improved the statements a bit for clarity
| useful for long-running queries with small-to-moderate result sets. For very large extracts, | ||
| keep using :class:`~airflow.providers.influxdb.hooks.influxdb3.InfluxDB3Hook` from a Python task. | ||
| Deferrable mode requires ``influxdb3-python>=0.12.0`` and a running ``triggerer``. |
Signed-off-by: Subhramit Basu <subhramit.bb@live.in>
…db3.py Co-authored-by: Elad Kalif <45845474+eladkal@users.noreply.github.com>
Signed-off-by: subhramit <subhramit.bb@live.in>
Yeah. Edit - added to the PR description. |
Signed-off-by: subhramit <subhramit.bb@live.in>
Signed-off-by: Subhramit Basu <subhramit.bb@live.in>
Signed-off-by: subhramit <subhramit.bb@live.in>
Signed-off-by: subhramit <subhramit.bb@live.in>
@eladkal I noticed a separate dependency issue while working on this. I took the opportunity and covered that in 1b7294b by adding the airflow/providers/apache/hive/src/airflow/providers/apache/hive/hooks/hive.py Lines 1083 to 1087 in ee15456 and also in transfers/sql_to_s3.py |
InfluxDB3OperatorInfluxDB3OperatorSigned-off-by: Subhramit Basu <subhramit.bb@live.in>
@eladkal@arpitrathore gentle nudge. |
ashb
left a comment
There was a problem hiding this comment.
This pr also needs to add async get_conn support please(as fetching a connection on a worker requires network traffic so can block)
(Non exhaustive review)
| single async query call. The trigger therefore awaits one query call instead of polling at an | ||
| interval, and there is no ``poll_interval`` parameter. |
There was a problem hiding this comment.
Not sure we need to mention there's no poll interval in the docs. It makes sense in the pr as there issue talked about it, but just going to the docs most people will have no context on this (and its not universal on deferrable operators either )
| from airflow.models import Connection | ||
| class InfluxDB3AsyncQueryNotAvailableError(RuntimeError): |
There was a problem hiding this comment.
Given we depend on an updated version of theof influx client is this possible to hit?
There was a problem hiding this comment.
Was being defensive but yeah ideally this should never hit. Removed.
Uh oh!
There was an error while loading. Please reload this page.
| f"Result type: {type(result).__module__}.{type(result).__name__}" | ||
| ) | ||
| return await asyncio.to_thread(_convert_dataframe_to_records, result) |
There was a problem hiding this comment.
Why the different return type on Async? The sync path returns the pandas object directly doesn't it?
There was a problem hiding this comment.
Serialization constraint, but youre right it's in the wrong place, the DataFrame cant cross a TriggerEvent, so I converted in the hook. It should be the trigger's job. Moving it there.
| from airflow.sdk.definitions.context import Context | ||
| def _convert_dataframe_to_records(dataframe: pd.DataFrame) -> list[dict[str, Any]]: |
There was a problem hiding this comment.
@eladkal wanted this to be private in #71976 (comment), so I wasn't sure what to do as the only other option was to create a private helper utils file for this tiny one-line method.
Maybe when I work on the sensor I'll need it anyway so I'll move this to a file now.
There was a problem hiding this comment.
Since python allows it, I relaxed module encapsulation a bit, but only inside the provider's internal implementation to not create a new file and still reuse it in e6a177c. See if that is alright or we should indeed go with a new file
| :param query: SQL query string | ||
| :return: List of dictionaries representing query results | ||
| """ | ||
| client = await asyncio.to_thread(self.get_conn) |
There was a problem hiding this comment.
Those does move it off the main loop but isn't the best way of doing this.
Please look in other hooks for an awit self.aget_conn() or similar
There was a problem hiding this comment.
Was unaware of this, thanks. Changed to that async-hook pattern
Uh oh!
There was an error while loading. Please reload this page.
| task_id="query_data_deferrable", | ||
| sql="SELECT * FROM \"temperature\" WHERE time > now() - INTERVAL '1 hour'", | ||
| influxdb3_conn_id="influxdb3_default", | ||
| deferrable=True, |
There was a problem hiding this comment.
Deferrable is true for other operators that support it isn't it?
There was a problem hiding this comment.
Not by default, in providers the usual pattern is tp follow the global Airflow setting and fall back to false (see airbyte example)
Keeping it true here also matches other provider system examples for the async variant (see S3 and livy examples)
| """Test async query with InfluxDB 3.x.""" | ||
| pd = pytest.importorskip("pandas") | ||
| self.influxdb3_hook.client = mock.Mock() |
There was a problem hiding this comment.
If at all reasonable is rather we didn't mock the client, but instead the http transport/reaponse underneath it.
At the very least, this mock needs a spec parameter to enforce its "shape"
There was a problem hiding this comment.
Makes sense, enforced the shape in the mocks.
P.S. I did have a look at pushing the seam lower but for query()/ query_async()
that would mean mocking upsteam client internals rather than the hook contract. IMO point of these tests are to validate our interaction with InfluxDBClient3 and the DataFrame/type handling around it, so I kept them at the client boundary
Co-authored-by: Ash Berlin-Taylor <ash_github@firemirror.com>
Co-authored-by: Ash Berlin-Taylor <ash_github@firemirror.com>
Signed-off-by: subhramit <subhramit.bb@live.in>
Signed-off-by: subhramit <subhramit.bb@live.in>
Signed-off-by: subhramit <subhramit.bb@live.in>
Signed-off-by: subhramit <subhramit.bb@live.in>
Signed-off-by: subhramit <subhramit.bb@live.in>
Signed-off-by: subhramit <subhramit.bb@live.in>
Closes#67107
Follow-up to #58929
Add deferrable support to
InfluxDB3OperatorWhen
deferrable=True, the operator now defers to a newInfluxDB3QueryTriggerso the worker slot is released while the query runs. The trigger awaitsInfluxDBClient3.query_async()and resumes the task with the same JSON-serializable record shape returned by the synchronous path.Also update provider metadata and documentation to surface the trigger, add a deferrable example, and bump the
influxdb3-pythonminimum version to>=0.12.0(the release that introducedquery_async()).Note that this deviates from the issue's proposed API as I did not add a "
poll_interval"/polling loop. InfluxDB 3'squery_asyncstreams the full result over one Arrow Flight call, which means there's no server-side job to poll a status on, unlike Snowflake/BigQuery/Redshift. The trigger awaits the query once and emits a single event.This is more similar in shape to
SQLExecuteQueryTrigger(used byGenericTransfer's deferrable path) - which also has a single await yielding oneTriggerEventwith nopoll_intervalfor the same reason:airflow/providers/common/sql/src/airflow/providers/common/sql/triggers/sql.py
Lines 92 to 102 in 4e4d060
Have added tests for the new deferrable operator path, trigger serialization/execution, and async hook behavior.
MWE
Tested on a setup of
influxdb:3-corewith a file object store, two rows written to ahometable over the v3 line-protocol endpoint, read back through the influxdb3 CLI:The async client on its own against the same server, to isolate it from Airflow:
Then through the operator - the deferrable task alongside the sync one running the same query, so the two paths could be compared:
query_deferreddeferred to the triggerer, the trigger fired a success event, and the task resumed fromQUEUEDwith the same two records the sync task returned:Was generative AI tooling used to co-author this PR?
Generated-by: GPT-5.4 following the guidelines
All code changes done as a result (and also this description) were manually driven, edited reviewed by me.