Skip to content

fix(source-stripe): opt in to TCP keepalive and request timeouts - #88397

Merged
ZaneHyattAB merged 4 commits into
masterfrom
devin/1791507526-source-stripe-keepalive-timeouts
Oct 15, 2026
Merged

ZaneHyattAB merged 4 commits into
masterfrom
devin/1791507526-source-stripe-keepalive-timeouts

Conversation

@ZaneHyattAB

@ZaneHyattAB ZaneHyattAB commented Oct 9, 2026 •

Copy link
Copy Markdown
Contributor

What

👉 TL;DR: Stops Stripe syncs from hanging forever when a network connection silently dies or gets dropped mid-request: the connector now detects dead connections and gives up on silent responses, then retries.

Specifically, base_requester sets use_tcp_keepalive: true, connect_timeout_in_seconds: 30 and read_timeout_in_seconds: 80, which are new in source-declarative-manifest 7.35.0 (already the base image on master) (airbytehq/airbyte-python-cdk#1168).

Today the Stripe requester has no timeout and no keepalive. If a connection dies or is dropped by a NAT while the connector waits for a response, the sync can block indefinitely: it stays alive, emits nothing and never exits. Context: https://github.com/airbytehq/oncall/issues/12977

Requested by Zane Hyatt (ZaneHyattAB).

How

  • manifest.yaml: the three settings go on definitions.base_requester. Every requester in the manifest resolves to base_requester:

    • the entity endpoints;
    • the events retriever used by the incremental side of every StateDelegatingStream;
    • all substreams;
    • files / file_links.

    So this one edit covers every stream. The top-level version is unchanged: the CDK validates against its runtime schema, and the manifest migrations are version-wildcard.

  • metadata.yaml: dockerImageTag goes 7.0.0 → 7.0.1. The base image source-declarative-manifest:7.35.0 is already on master (auto deps bump deps(source-stripe): 🐙 update dependencies [2026-10-13] #88809), so this PR no longer changes it.

  • unit_tests/poetry.lock: airbyte-cdk goes 7.29.0 → 7.35.0, plus the bumps it requires (pydantic, pydantic-core, typing-inspection), so the tests exercise the new fields.

  • New unit_tests/test_requester_settings.py asserts two things:

    • every HttpRequester in the resolved manifest carries the three settings;
    • every HTTP client actually built has a (30, 80) timeout and the keepalive adapter. This is checked stateless and on the events path.
  • Changelog row in docs/integrations/sources/stripe.md.

Why these values

  • Keepalive. It uses 60 s idle, 10 s interval and 6 probes, so it detects a dead or NAT-dropped connection in about 2 minutes. It never fails a slow but alive server. GCP Cloud NAT's default idle timeout for established connections is 1200 s (docs), so a silent connection can outlive its NAT mapping.
  • 80 s read timeout. This matches Stripe's own SDK default (stripe-python timeout=80). It bounds the gap between bytes, not the total request time. Within the manifest error handler's 600 s retry window it leaves room for about 7 attempts; 300 s would allow about 2.
    • Every Stripe stream here is a paginated list read (limit=100) on api.stripe.com/v1, with small expand[] lists.
    • No stream downloads file contents: files / file_links list metadata only.
    • No stream uses report or export runs.
  • 30 s connect timeout. It bounds TCP and TLS handshake stalls.

Review guide

  1. airbyte-integrations/connectors/source-stripe/manifest.yaml
  2. airbyte-integrations/connectors/source-stripe/metadata.yaml
  3. airbyte-integrations/connectors/source-stripe/unit_tests/test_requester_settings.py
  4. docs/integrations/sources/stripe.md, unit_tests/poetry.lock

User Impact

A request that previously could hang forever on a dead or NAT-dropped connection now fails and is retried. Healthy syncs are unaffected.

Rollout

Progressive rollout is enabled for Stripe. Per the reviewer's suggestion, do a /prerelease and a pinned run on a real connection before wide adoption.

Not fixed by this PR

  • The request volume of bank_accounts and customer_balance_transactions. These are per-customer substreams over very large customer lists, so syncs including them stay slow; they just can't hang silently anymore.
  • A server that is alive but stuck beyond the retry window still fails the sync.
  • A dead upstream behind a forward proxy isn't detected by client-side keepalive.

Local validation

  • Unit tests: 225 passed.
  • airbyte-cdk image build succeeded and spec works; the image contains airbyte-cdk 7.35.0.
  • Against the integration-test sandbox account, check SUCCEEDED and a read of customers, invoices and invoice_line_items returned 1544 records with no errors.

Can this PR be safely reverted and rolled back?

  • YES 💚
  • NO ❌

Link to Devin session: https://app.devin.ai/sessions/56ec2115e89a41e99baa6ae024af93b2
Open in Devin Desktop: https://app.devin.ai/desktop/session/56ec2115e89a41e99baa6ae024af93b2?variant=devin


Important

Autopilot Progressive Rollout Enabled

Autopilot progressive rollouts are enabled for one or more connector(s) modified in this PR. Check the box below if you need to bypass normal rollout safety processes and release to all users immediately upon merge:

  • ⚡ Release immediately (bypasses automatic progressive rollout)

Note:

  • ⚠️ The above bypass option is for emergency/hotfix use only.
  • 🔗 You can monitor or manually advance a rollout at ops.internal.airbyte.ai.

@devin-ai-integration

Copy link
Copy Markdown
Contributor

I'll fix CI failures and address comments from users with write access. I'll skip comments containing "(aside)".

  • Disable automatic comment, CI, and merge conflict monitoring

@octavia-bot
octavia-bot Bot marked this pull request as draft October 9, 2026 01:22
@octavia-bot

octavia-bot Bot commented Oct 9, 2026

Copy link
Copy Markdown
Contributor

Note

📝 PR Converted to Draft

More info...

Thank you for creating this PR. As a policy to protect our engineers' time, Airbyte requires all PRs to be created first in draft status. Your PR has been automatically converted to draft status in respect for this policy.

As soon as your PR is ready for formal review, you can proceed to convert the PR to "ready for review" status by clicking the "Ready for review" button at the bottom of the PR page.

To skip draft status in future PRs, please include [ready] in your PR title or add the skip-draft-status label when creating your PR.

@github-actions

github-actions Bot commented Oct 9, 2026

Copy link
Copy Markdown
Contributor

👋 Greetings, Airbyte Team Member!

Here are some helpful tips and reminders for your convenience.

💡 Show Tips and Tricks

PR Slash Commands

Airbyte Maintainers (that's you!) can execute the following slash commands on your PR:

  • 🛠️ Quick Fixes
    • /format-fix - Fixes most formatting issues.
    • /bump-version - Bumps connector versions, scraping changelog description from the PR title.
      • Bump types: patch (default), minor, major, major_rc, rc, promote.
      • The rc type is a smart default: applies minor_rc if stable, or bumps the RC number if already RC.
      • The promote type strips the RC suffix to finalize a release.
      • Example: /bump-version type=rc or /bump-version type=minor
    • /bump-progressive-rollout-version - Alias for /bump-version type=rc. Bumps with an RC suffix and enables progressive rollout.
  • ❇️ AI Testing and Review (internal link: AI-SDLC Docs):
    • /ai-prove-fix - Runs prerelease readiness checks, including testing against customer connections.
    • /ai-canary-prerelease - Rolls out prerelease to 5-10 connections for canary testing.
    • /ai-review - AI-powered PR review for connector safety and quality gates.
    • /ai-breaking-change-review - Assists the breaking-change reviewers: whether the change needs to be breaking, destination data-loss risk, and whether the migration guide, metadata message, and docs let users upgrade safely.
  • 📝 AI Documentation:
    • /ai-docs-review - AI-powered documentation review for PRs with connector changes.
    • /ai-create-docs-pr - Creates a documentation PR for connector changes, stacked on the current PR.
  • 🚀 Connector Releases:
    • /publish-connectors-prerelease - Publishes pre-release connector builds (tagged as {version}-preview.{git-sha}) for all modified connectors in the PR.
    • /enable-autopilot-rollouts - Enables autopilot progressive rollouts for the modified connector(s) in the PR, remediating "autopilot rollouts not enabled for {connector-name}" auto-merge blockers. Sets defaultRolloutMode: autopilot and enableProgressiveRollout: true, preserving any existing autopilotConfig.
      • Optional args: connector=<CONNECTOR_NAME> (defaults to the modified connectors in the PR), strategy=fast|slow|default (defaults to fast).
      • Example: /enable-autopilot-rollouts or /enable-autopilot-rollouts connector=source-faker strategy=slow
  • ☕️ JVM connectors:
    • /update-connector-cdk-version connector=<CONNECTOR_NAME> - Updates the specified connector to the latest CDK version.
      Example: /update-connector-cdk-version connector=destination-bigquery
  • 🐍 Python connectors:
    • /poe connector source-example lock - Run the Poe lock task on the source-example connector, committing the results back to the branch.
    • /poe source example lock - Alias for /poe connector source-example lock.
    • /poe source example use-cdk-branch my/branch - Pin the source-example CDK reference to the branch name specified.
    • /poe source example use-cdk-latest - Update the source-example CDK dependency to the latest available version.
  • ⚙️ Admin commands:
    • /force-merge reason="<REASON>" - Force merges the PR using admin privileges, bypassing CI checks. Requires a reason.
      Example: /force-merge reason="CI is flaky, tests pass locally"
📚 Show Repo Guidance

Helpful Resources

📝 Edit this welcome message.

@github-actions

github-actions Bot commented Oct 9, 2026 •

Copy link
Copy Markdown
Contributor

source-stripe Connector Test Results

251 tests   246 ✅  23m 56s ⏱️
  2 suites    5 💤
  2 files      0 ❌

Results for commit 107ad31.

♻️ This comment has been updated with latest results.

@devin-ai-integration
devin-ai-integration Bot marked this pull request as ready for review October 9, 2026 01:54
@ZaneHyattAB

ZaneHyattAB commented Oct 9, 2026 •

Copy link
Copy Markdown
Contributor Author

/ai-prove-fix

AI Prove Fix Started

Running readiness checks and testing against customer connections.
View workflow run
🔍 AI Prove Fix session starting... Running readiness checks and testing against customer connections. View playbook

✅ Devin AI session created successfully!

@airbyte-support-bot

Airbyte Support Bot (airbyte-support-bot) commented Oct 9, 2026 •

Copy link
Copy Markdown
Contributor

🧪 Fix Validation Evidence

🟡 Outcome: No Regression — Fix Not Exercised

Regression tests (sandbox, all 19 streams) show no regression vs 6.0.22, but a healthy account never stalls, so they don't exercise the fix. In a local fault-injection run with a stalled upstream, the control (6.0.22) hung with no output for 21m45s until we killed it. The target raised ReadTimeout (read timeout=80.0), retried 6 times, and failed cleanly with an error trace after 8m38s. The timeout mechanism works as intended. No live connection was tested, so the fix is not proven against the originally reported failure.

➡️ Next Steps
  • Safe to merge from a no-regression standpoint: no record, state, spec, or catalog differences beyond known clock noise.
  • After merge/prerelease, run /ai-canary-prerelease on the affected connection to confirm that the original stall now ends in a retry/error and does not hang.
  • Reviewer note: the fix turns silent socket stalls into retry → error. It does not speed up a backfill that is slow but still responsive, which the oncall history also mentions.
📋 Connector & PR Details

Connector: source-stripe
PR: #88397
Pre-release Version Tested: n/a (regression tests built dev from PR HEAD; fault injection used a local build of the PR manifest on SDM 7.35.0)
Comparison Baseline: latest (auto) → airbyte/source-stripe:6.0.22
Detailed Results: https://github.com/airbytehq/oncall/issues/12977#issuecomment-6084147828

📝 Evidence Plan

Proving Criteria: On the target image, a stalled READ request fails with a timeout and is retried; on control it blocks indefinitely. Healthy reads show no regression across all streams.
Disproving Criteria: Target still blocks on a stalled request, or there is any regression vs control (record drop, failed command, spec/catalog diff, or timeouts tripping on healthy endpoints).
True-Positive Case: n/a. The fix adds failure modes and does not loosen a check.
Cases Attempted: (1) Regression test, GSM creds, all streams. (2) Local stalled-upstream fault injection, control vs target, customers READ.

✅ Pre-flight Checks
  • Viability: the timeouts are applied on base_requester, which all requesters resolve through. The PR's unit tests also cover the stateful /events path, and CDK 7.35.0 defines all three fields.
  • Safety: manifest/metadata/docs/test changes only. No suspicious code or exfiltration.
  • Not breaking: no schema, PK, cursor, spec, stream, or state changes.
  • Reversible: patch bump 6.0.22 → 6.0.23, changelog present, progressive rollout still enabled.
  • No live connections pinned.
🔍 Detailed Evidence Log

Regression test (run):

  • SPEC/CHECK/DISCOVER/READ: success on both versions. RECORD 1787 → 1787, STATE 265 → 265, record counts equal for all 19 streams, 0 missing PKs, 0 timeout log lines.
  • The workflow was marked failed because of value diffs in bank_accounts, external_account_bank_accounts, and invoice_items. Every diff is in the updated field only (e.g. 1791558997 → 1791559031). For these streams the manifest falls back to now_utc().timestamp() (record.get('updated', record.get('created', now_utc().timestamp()))), so the value is each run's wall-clock time. That is non-deterministic noise, and the PR does not touch that code.

Fault injection (TLS-terminating proxy for api.stripe.com that never answers; dummy key; customers cold read):

  • Control 6.0.22: 1 request at 15:15:17 → no output → still blocked at 15:37:02 when killed (exit 124).
  • Target: 6 requests, each closed after ~80 s, with backoff of 1/2/4/8/16 s between them:
    Caught retryable error 'ReadTimeout: HTTPSConnectionPool(host='api.stripe.com', port=443): Read timed out. (read timeout=80.0)' after 1 tries. Waiting 1 seconds then retrying...
    Giving up _send(...) after 6 tries (... ReadTimeout ... (read timeout=80.0))
    {"type":"TRACE","trace":{"type":"ERROR",...,"error":{"message":"Exhausted available request attempts. ... ReadTimeout ..."
    
    The stream ended INCOMPLETE, exit 1, 15:15:13 → 15:23:51.

Not exercised: TCP keepalive on a dead peer or NAT drop, the 30 s connect timeout on its own, and any live/customer connection.

@airbyte-support-bot Airbyte Support Bot (airbyte-support-bot) added the hyd-prove Hydra: ai-prove-fix stage has run label Oct 9, 2026
@airbyte-support-bot

Copy link
Copy Markdown
Contributor

↪️ Triggering /ai-review per Hands-Free AI Triage Project daily sweep.

Reason: Current-HEAD prove-fix verdict is recorded (NOT_EXERCISED) and no AI review exists for this HEAD.

Devin session: https://app.devin.ai/sessions/a8ba122966ca4b308b433c4b3e4782b5

@octavia-bot

octavia-bot Bot commented Oct 10, 2026 •

Copy link
Copy Markdown
Contributor

AI PR Review starting...

Reviewing PR for connector safety and quality.
View playbook

✅ Devin AI session created successfully!

@airbyte-support-bot

Airbyte Support Bot (airbyte-support-bot) commented Oct 10, 2026 •

Copy link
Copy Markdown
Contributor

🛡️ AI PR Review Report

🟡 Review Action: NO ACTION (INCONCLUSIVE)

Gate Status Details
Live / E2E Tests UNKNOWN The current-HEAD /ai-prove-fix verdict is NOT_EXERCISED. There is no regression vs 6.0.22, and local fault injection shows the 80 s ReadTimeout and retries work, but the fix has not been exercised on a live or affected connection.
Behavioral Changes FAIL (warning) base_requester adds connect_timeout_in_seconds: 30, read_timeout_in_seconds: 80 and use_tcp_keepalive: true, which applies to every stream. This is documented in the PR description; it raises the Risk Level but does not block.
Breaking Dependencies WARNING The base image goes from SDM 7.33.0 to 7.35.0, which pulls in CDK 7.34.0 behavior fixes (ConcurrentCursor window clamping, SubstreamPartitionRouter null parent keys, OffsetIncrement/PageIncrement re-reads). The regression run showed matching record and state counts on all 19 streams.

⚠️ Risk Level: 4/5

manifest.yaml base_requester adds connect/read timeouts and TCP keepalive to every Stripe request (timeout keywords trip the Behavioral Changes gate), plus a base-image jump from SDM 7.33.0 to 7.35.0 that pulls in CDK 7.34.0 cursor, partition-router and pagination fixes.


📋 PR Details

Connector(s): source-stripe
PR: #88397
HEAD SHA: 63a566a2ce5b3364805040aefb9d5eb86ad9939a
Session: https://app.devin.ai/sessions/4d5bb2265c444ac3a8662baffb217477
Originating issue: https://github.com/airbytehq/oncall/issues/12977
Risk rationale: Every request's network semantics change: silent stalls now become ReadTimeout, then retry, then error. The change also bundles a two-minor SDM/CDK upgrade. Both are patch-bumped (6.0.22 → 6.0.23) and progressive rollout is enabled.

🔍 Gate Evaluation Details

Gate-by-Gate Analysis

Gate Status Enforced? Details
PR Hygiene PASS Yes Detailed description, a 6.0.23 changelog row in docs/integrations/sources/stripe.md, and no unresolved human review comments
Code Hygiene PASS WARNING manifest.yaml changed and the new test unit_tests/test_requester_settings.py was added
Test Coverage PASS Yes Behavioral change (title contains "fix"); the new tests test_every_http_requester_opts_in and test_built_http_clients_use_settings assert on the settings
Code Security PASS Yes No security path patterns; no auth/credential keywords in +/- lines
Per-Record Performance PASS WARNING Only requester session settings changed; no per-record code paths
Breaking Dependencies WARNING WARNING SDM/CDK 7.33.0 → 7.35.0 (includes 7.34.0 cursor, partition and pagination fixes)
Backwards Compatibility PASS Yes (enforced) No spec, schema, stream, PK or cursor changes; patch bump only
Forwards Compatibility PASS Warning (elevates Risk Level) No state, cursor, partition, pagination or transformation changes in connector code; rollback is safe
Behavioral Changes FAIL Warning (elevates Risk Level) Timeout keywords in the manifest.yaml diff
Out-of-Scope Changes PASS Skip All files are under source-stripe/ or docs/
CI Checks PASS Yes Connector CI, Test and Lint source-stripe all green (230 tests, 0 failed)
Live / E2E Tests UNKNOWN Yes /ai-prove-fix NOT_EXERCISED for this HEAD

PR Hygiene evidence

  • PR Body Length (raw): ~4,900 chars
  • PR Body Length (after stripping): ~4,900 chars (no HTML comments)
  • PR Body Length (visible content): well above 50 chars
  • PR Body Preview: ## What <table><tr><td> **:point_right: TL;DR:** Stops Stripe syncs from hanging forever when a netwo...
  • Changelog: | 6.0.23 | 2026-10-09 | [88397](...) | Opt in to TCP keepalive and request timeouts ...
  • Peer feedback: only github-actions[bot] markdownlint inline comments, which are ignored as bot comments; the Docs / MarkDownLint check passed.

Test Coverage evidence

  • Behavioral Change Detected: Yes
  • Indicators Found: title keyword fix
  • Test Files Modified: unit_tests/test_requester_settings.py (new), unit_tests/poetry.lock
  • New Test Content Found: Yes
  • Test Content Evidence: def test_every_http_requester_opts_in() -> None:

Code Security: Changed lines in manifest.yaml are only use_tcp_keepalive, connect_timeout_in_seconds and read_timeout_in_seconds. The authenticator line is unchanged context. Changed lines in metadata.yaml are baseImage and dockerImageTag; connectorBuildOptions and allowedHosts are unchanged context.

Backwards Compatibility: No spec file or spec section was modified, so there is no required array change. No schemas/ or stream definitions changed. The prove-fix regression showed no spec or catalog diffs.

Behavioral Changes: Keywords matched in +lines are timeout (connect_timeout_in_seconds: 30, read_timeout_in_seconds: 80). The effect is that a request stalled for more than 80 s between bytes now raises ReadTimeout and is retried within the existing error-handler window, instead of hanging. A slow but alive Stripe endpoint that goes more than 80 s without sending bytes would now error. Given Stripe's paginated list reads, and that 80 s matches Stripe's own SDK default, this is unlikely, but it is a semantics change across all streams.

Breaking Dependencies: The CDK release notes for v7.34.0 include ConcurrentCursor end-window and clamping changes, SubstreamPartitionRouter skipping null parent keys, and OffsetIncrement/PageIncrement re-read fixes. Stripe relies on substreams and concurrent cursors, so these can subtly change the record set. Mitigation: the regression run showed RECORD 1787 → 1787 and STATE 265 → 265, with equal per-stream counts. The unit_tests/poetry.lock bumps (pydantic 2.11.7 → 2.13.5, pydantic-core 2.33.2 → 2.46.5) are test-only.

Live / E2E Tests: Validation is required (bug fix plus a requester change). The prove-fix marker prove_fix_result: NOT_EXERCISED; head_sha: 63a566a2… maps to UNKNOWN.

📚 Evidence Consulted

Evidence

  • Changed files: 5 files (manifest.yaml, metadata.yaml, unit_tests/poetry.lock, unit_tests/test_requester_settings.py, docs/integrations/sources/stripe.md)
  • CI checks: Connector CI Checks Summary ✅, Test source-stripe Connector ✅, Lint source-stripe Connector ✅, Format Check ✅, Docs / MarkDownLint ✅, Docs / Vale ✅, Build and Verify Artifacts ✅. Excluded from this gate: source-stripe Pre-Release Checks ✅, Progressive Rollout Gate ✅, Validate PR Title ✅.
  • PR labels: connectors/source/stripe, hyd-prove, hyd-review (no validation override labels)
  • PR description: present
  • /ai-prove-fix: comment — NOT_EXERCISED for the current HEAD
  • Existing bot reviews: none (only github-actions[bot] COMMENTED)
❓ How to Respond

Resolving a Failing Gate

Each non-PASS gate above lists concrete remediation. Apply it, push, then re-run /ai-review. The bot re-evaluates from the diff, CI check-runs, and /ai-prove-fix results.

  • Live / E2E Tests (UNKNOWN): Re-run /ai-prove-fix once a connection exhibits the stall pattern, or validate via /ai-canary-prerelease or a pinned prerelease on the affected connection from https://github.com/airbytehq/oncall/issues/12977, as the PR's Rollout section suggests. Progressive rollout is already enabled.

Written explanations do not change a gate verdict. If you believe a gate is a false positive or it cannot be remediated as written, request review from a human maintainer and explain the situation to them in a PR comment. A human reviewer can approve or merge over a bot FAIL. Never edit the PR description to argue with the bot; the description is how reviewers learn what the PR does.

@airbyte-support-bot

Copy link
Copy Markdown
Contributor

🔴 Escalated for human review per Hands-Free AI Triage Project daily sweep.

Reason: /ai-review returned UNKNOWN at HEAD 63a566a; /ai-ready needs an APPROVE result. Escalated to oc-apis.

Devin session: https://app.devin.ai/sessions/a8ba122966ca4b308b433c4b3e4782b5

master now carries 6.0.23 (auto deps bump, SDM 7.35.0) and 7.0.0 (#87765),
so this PR becomes 7.0.1 with only the opt-in requester settings, the
coverage test and the unit-test lock bump.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@ZaneHyattAB ZaneHyattAB changed the title fix(source-stripe): opt in to TCP keepalive and request timeouts (SDM 7.35.0) fix(source-stripe): opt in to TCP keepalive and request timeouts Oct 13, 2026
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Comment thread docs/integrations/sources/stripe.md
Comment thread docs/integrations/sources/stripe.md
Comment thread docs/integrations/sources/stripe.md
Comment thread docs/integrations/sources/stripe.md
@ZaneHyattAB
ZaneHyattAB merged commit d18952f into master Oct 15, 2026
48 checks passed
@ZaneHyattAB
ZaneHyattAB deleted the devin/1791507526-source-stripe-keepalive-timeouts branch October 15, 2026 18:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants