# Landing My First Apache Airflow Contribution

This is the story of my first open source contribution to Apache Airflow. It took five and a half months and thirteen commits to go from an open pull request to a merged one. Along the way the PR was marked stale by a bot twice, I closed it myself once, and the design I started with was not the design that merged.

If you are about to make your first contribution to a large Apache project and you want a realistic preview of what the process looks like, this is for you.

The PR is [apache/airflow#58929](https://github.com/apache/airflow/pull/58929), which added InfluxDB 3 support to the official `apache-airflow-providers-influxdb` provider. I opened it on 2 December 2025. It merged on 14 May 2026.

## Why I started

I work on a data platform team that runs InfluxDB Cloud Dedicated for time-series telemetry. Our Airflow Dags read from it, compute derived metrics, and publish to Kafka. We had been doing this with the InfluxDB 3 Python client (`influxdb3-python`) constructed by hand inside `PythonOperator`s, every team rolling their own connection setup and credential handling. The official `apache-airflow-providers-influxdb` provider existed, but only supported the InfluxDB 2.x line, which uses a completely different client, a different query language (Flux instead of SQL), and a different connection model (org and bucket instead of database).

It bugged me. Every new Dag that touched InfluxDB was a copy-paste of the same plumbing. On 24 November 2025 I filed [issue #58610](https://github.com/apache/airflow/issues/58610) asking for official InfluxDB 3 support.

I opened the PR a week later. I figured I was looking at a few rounds of review and then a merge.

I was wrong, though not for the reason I expected.

## The design I picked, and the reason it stalled

My first instinct was to ship this as a brand new provider, `apache-airflow-providers-influxdb3`, completely separate from the existing 2.x provider. My reasoning was that the 2.x and 3.x clients share nothing. Different package, different query language, different connection model. Two separate providers felt cleaner than one provider with two of everything inside it.

What I expected to be argued about was that design. What actually blocked me for the next three months was governance.

Jan Scheffler (`jscheffl`) responded the day I opened the PR. Not about my code. Airflow was mid-vote on a new policy for how the project accepts new providers, covering an incubation period and a requirement that a named team commit to maintaining anything that gets merged. Until that policy landed, new providers were effectively on hold. They suggested I respond to the governance thread and explain how my proposal fit.

Seven weeks later they reviewed the code itself: "First view on code and structure looks good." Then, immediately: the project now needs at least two stewards and a committer who will back the work through incubation. Is there any sponsor around?

There was not. I was one person with a working PR and no co-signers.

This is the part I want first-time contributors to understand. My PR was not blocked because the code was bad. It was blocked because I had walked into a project that was in the middle of rewriting the rules for the exact thing I was trying to do, and the rules were not finished yet. There was no policy to approve me under. When I asked in late January who could sponsor it, Jan pointed me at [AIP-95](https://cwiki.apache.org/confluence/display/AIRFLOW/AIP-95+Provider+lifecycle+update+proposal), whose policy text was at that moment still an open pull request being wordsmithed, and suggested I treat my contribution as a beta test of the new process and take the discussion to the dev mailing list.

I did not do that for another seven weeks.

## Four months of almost nothing

Between December and March, here is the complete list of things that happened to my pull request:

*   18 January: a bot marked it stale and announced it would close in five days.
    
*   21 January: Jarek Potiuk (`potiuk`) replied "not stale", and then "Rebasing this PR and fixing the failures is a good start to follow the process of adding it."
    
*   24 January: I asked about stewards and sponsors. Jan answered and pointed me at the dev list.
    
*   16 March: the bot marked it stale again.
    

That is it. Two bot comments, one manual rescue, and one exchange.

If you are a first-time contributor, this is the stretch nobody warns you about. The PR is open. Reviewers are not blocking it. They are also not approving it. Nothing is wrong and nothing is happening, and you start to wonder whether anyone is going to look at it again.

I told myself two things at the time. The work was useful to my employer whether or not it merged upstream, so I was going to maintain the code either way. And the people who had engaged were not hostile, just busy: Airflow takes on the order of three hundred new pull requests a week and has around seven hundred and fifty open at any moment.

Both of those were true. Neither of them was enough.

## The day I closed my own PR

On 16 March, the same day the stale bot came back, Elad Kalif (`eladkal`) left a comment on the PR:

> I don't know if this is needed? There can be operators for influx3 in the same package. I think this would be easier.

I read that as another architectural objection stacked on top of three months of process I had not managed to clear. I had a new job load, the PR needed a rebase, and the provider policy still was not settled. So I closed it. I wrote a polite note thanking everyone, said I could not continue right now, and said I would maybe revisit it later.

Thirty-six minutes later, Elad replied:

> Just to clarify my suggestion simplify the process. It changes it from adding a provider to simply adding a new operator thus there is no need for any policy. Just adding the operators and the `influxdb3-python` to the toml file in the existed influxdb provider. \[...\] Happy to help with quick reviews if you are interested. Since all the code is already implemented this is just rearranging.

I had it exactly backwards. I thought I was being asked for more work. I was being offered a route around the entire governance problem that had been blocking me since December. Putting the code in the existing provider meant no new provider, no AIP-95, no second steward, no committer sponsor. The thing I had spent three months failing to obtain, I no longer needed.

That reply is the reason this PR exists. If Elad had let the close stand, there would be no post to write.

The lesson I actually took from this is not "be patient". It is: **say you are stuck, out loud, instead of closing quietly.** I had never once written a comment saying "I am blocked on finding a sponsor and I do not know what to do next." I just went quiet for two months and then quit. The one time somebody responded to me being visibly stuck, the block dissolved in half an hour.

## The dev list, and getting the same answer twice

I did not concede immediately. The day after Elad's comment I wrote a long reply defending the separate provider on technical grounds: the two clients are incompatible at the API level, 2.x uses `InfluxDBClient` with Flux and returns `FluxTable`, 3.x uses `InfluxDBClient3` with SQL, and so on.

Elad went and read both libraries' dependency lists and pointed out they are nearly identical, so there is no packaging reason they cannot coexist. Then they made the argument that actually landed: a provider is allowed to host very distinct things. The Google provider carries both Google Ads and Google Cloud, and nothing is shared between them.

That same day I posted a `[DISCUSS]` thread to the Airflow dev mailing list, titled ["New incubating provider: apache-airflow-providers-influxdb3 - seeking steward and committer sponsor"](https://lists.apache.org/thread/s7fy2ssnrm5gmryrlv12zw3bjhkwrzyx). I explained the proposal, linked the PR, named the design tradeoffs, and asked two concrete questions: who could co-steward this, and who could sponsor it through incubation.

Amogh Desai replied the next morning. They had read the PR, they acknowledged the incompatibility I had described, and then:

> For maintainability purposes, I still wonder if there is a way to use the existing provider itself. Like I think postgres provider is a decent example for this. It supports both psycopg2 AND psycopg3 via runtime detection \[...\] This is not blocking but something worth evaluating before creating a new provider in my opinion.

Two maintainers, arriving independently within forty-eight hours, at the same conclusion, with a concrete precedent in the codebase attached. I replied that day agreeing to consolidate into the existing provider. Amogh said sounds good. Kaxil Naik added "This is great to see, looking forward to seeing this merged" a few days later.

The dev list did not get me what I asked it for. I went there hunting a steward and a sponsor, and instead I got told I did not need either. That was a much better outcome than the one I requested, and I would not have got it by continuing to argue in the PR thread alone.

## The pivot, and what actually shipped

I rewrote the PR to keep everything inside `apache-airflow-providers-influxdb`. The 2.x classes (`InfluxDBHook`, `InfluxDBOperator`) stayed exactly where they were. I added 3.x companion classes, `InfluxDB3Hook` and `InfluxDB3Operator`, in their own submodules alongside them. I added an `influxdb3` connection type with `token`, `database`, and `org` fields, matching the InfluxDB 3 connection model directly. Documentation, an example Dag, and a system test went in with the code.

Two honest notes about the result, because the version of this story where everything landed cleanly is not the true one.

First, what merged is not the postgres pattern. The postgres hook does genuine runtime detection and dispatches inside one class. My code does not. `InfluxDB3Hook` is a separate class in a separate module from `InfluxDBHook`, with a guarded import at the top so a missing client fails clearly rather than at import time. The reviewers wanted the two versions to share a *package*, not to share a *class*, and once I understood that distinction the rewrite was mostly moving files. The consolidation was about packaging and maintenance ownership, not about unifying the code.

Second, there is a wart. `influxdb3-python` is an unconditional dependency of the provider today, so somebody who only uses InfluxDB 2.x still gets the 3.x client installed. The guarded import in the hook is defensive, not a real optional extra. I would like to clean that up.

## An approved PR is not a merged PR

Here is the part of the timeline I would have found most useful to read before I started.

I agreed to the pivot on 18 March. I did not push the rewrite until late April. That delay was mine, and Elad had to nudge me twice to get it moving. On 22 April they left a formal changes-requested block ("Temporary block as we'd like to have the integration in the existed provider rather a new one"), and Jarek converted the PR to draft the same day, citing six failing Special tests spread across the SQLAlchemy and Pendulum 2 matrix variants plus a failing Latest Boto job.

I pushed the consolidated version. On 27 April, Elad approved it.

It did not merge for another two and a half weeks.

What happened in between was entirely CI. My unit tests were failing on `AttributeError: can't set attribute 'extra_dejson'` because of how I was mocking the Connection object, and on a `ValueError` where a mock was standing in for a DataFrame. Elad's comments during this stretch, in order, were "can you fix the failing tests?", then "Tests are still failing", then "Still failing", then "Something went wrong with rebase". A bot chimed in to tell me `uv.lock` had moved on main and my branch now conflicted.

I fixed the mocks, fixed the rebase, and it merged on 14 May.

There was no round of deep line-by-line design feedback at the end. Once the architecture question was settled, the remaining distance was mechanical: green CI and a clean rebase. Budget for it anyway. An approval is permission to merge, not a merge, and a botched rebase can cost you a week of somebody else's patience.

## What I learned

I want to be specific about the lessons, because "be patient" is too easy to say and too easy to ignore.

**Your PR may be blocked by something that has nothing to do with your code.** Mine sat for three months because the project was rewriting its provider acceptance policy and there was no rule to approve me under. Nobody was going to say that in a review comment in so many words, because from their side it was obvious context. Read the recent activity on the dev list before you assume the silence is about you.

**Say you are stuck, out loud, in the PR.** This is the one I got most wrong. I went quiet for two months and then closed my own pull request, and the block turned out to be resolvable in a single reply. A comment saying "I cannot find a sponsor and I do not know how to proceed" costs you nothing and might have saved me four months.

**The architectural objection can arrive very late, and it may be about packaging rather than code.** Mine came three and a half months in. It did not invalidate a single line of the implementation. It changed where the files lived. Those are much cheaper to absorb than they feel in the moment, so read a late objection carefully before you assume it means a rewrite.

**When two maintainers independently land on the same suggestion, stop defending and start prototyping.** I argued the technical case for about a day after the first suggestion. Once the second one arrived with a precedent in the codebase attached, the argument was over and I should have recognised that faster.

**The dev mailing list is for design questions, not status updates.** I almost posted early to ask "is anyone going to review my PR?" That would have been a bad use of the list. Posting a concrete design question, with the tradeoffs named and the PR linked, got the right people engaged inside a day. Ask for a decision, not for attention.

**Be specific when you ask for help.** My dev list post named the design tradeoffs, linked the PR, and asked two answerable questions. That is easier to respond to than "please look at my PR", and it is why the answer came back the next morning even though the answer was "your question has a wrong premise".

**A merged PR opens some doors, but not automatically.** Since #58929 merged I have filed two follow-up issues. [#67109](https://github.com/apache/airflow/issues/67109), for a dedicated sensor, drew comments from another contributor and from Elad within days. [#67107](https://github.com/apache/airflow/issues/67107), for a deferrable operator, has one comment on it, which is my own, and it still carries a `needs-triage` label three months later. One out of two. Having a merged provider PR buys you some credibility; it does not buy you a queue jump.

**The work teaches you the project in a way reading docs does not.** I now understand the Airflow provider layout, the connection abstraction, the release process, the CI test matrix, and the deferrable operator pattern, because I had to. Even if the PR had never merged, that would have been worth the time. The merge was the bonus.

## What's next

Two follow-up issues are open on `apache/airflow`, both companions to PR #58929:

*   [**#67107**](https://github.com/apache/airflow/issues/67107)**: deferrable variant of** `InfluxDB3Operator`**.** Long-running queries today hold an Airflow worker slot for the entire duration of the query. A deferrable variant would release the slot while the query runs on InfluxDB and resume the task when the result is ready, freeing capacity for other tasks in the meantime. The pattern is well established in providers like BigQuery and Snowflake. Implementation would use the `query_async` coroutine that `influxdb3-python` ships in 0.12.0 and later, which means raising the provider's lower bound on that dependency from the current 0.7.0.
    
*   [**#67109**](https://github.com/apache/airflow/issues/67109)**: dedicated** `InfluxDB3Sensor`**.** Today, gating a downstream Dag on the arrival of data in an InfluxDB measurement requires wrapping `InfluxDB3Hook` in a `PythonSensor`. A dedicated sensor would fill the same role for InfluxDB that `SqlSensor` plays for generic SQL backends and `AwaitMessageSensor` plays for Kafka topics: a discoverable, deferrable, consistent API for "wait until the data is there". The proposal sketches two complementary designs, a generic SQL-truthy sensor and an ergonomic window-existence sensor for the common partitioned-data case.
    

I plan to author both. If you have an opinion on either design, the issues are the right place to share it.

## A note on attribution

The PR was authored from my personal GitHub account. The work came out of a real production need at my employer, they knew I was doing it, and a good part of it happened on work time. Using a personal account for open source while your employer benefits from the result is a common and unremarkable arrangement. The thing that matters is that everyone involved knows which arrangement you are in, before you start rather than after.

## To anyone about to start

The Apache Airflow community is welcoming, technically demanding, and slower than you would like. The maintainers respond to good work with real feedback, and at least one of them will probably go out of their way for you at some point without being asked.

Pick a real itch. Expect the first substantive response to be about process rather than your implementation. Expect a long quiet stretch, and when you hit it, say something instead of going quiet yourself. Expect the design objection late, and read it for what it actually asks rather than what you fear it asks. Then expect a boring tail of CI failures after the approval.

I nearly quit at month four, over a problem that took one reviewer half an hour to dissolve. Do not be the person who closes their own pull request without asking first.

That is the whole lesson, and it was worth it.
