Repository navigation
fix(file-based): give each transferred file a unique staging path - #1078
Patrick Nilan (pnilan) merged 6 commits into
Conversation
Two source files that resolve to the same relative path shared one staging file, so the second download overwrote the first and the destination failed with FileNotFoundException after consuming and deleting the first copy. Co-Authored-By: bot_apk <apk@cognition.ai>
🤖 Devin AI EngineerI'll be helping with this pull request! Here's what you should know: ✅ I will automatically:
Note: I can only respond to comments from users who have write access to this repository. ⚙️ Control Options:
|
👋 Greetings, Airbyte Team Member!Here are some helpful tips and reminders for your convenience. 💡 Show Tips and TricksTesting This CDK VersionYou can test this version of the CDK using the following: # Run the CLI from this branch:
uvx 'git+https://github.com/airbytehq/airbyte-python-cdk.git@devin/1785066655-unique-file-transfer-staging-paths#egg=airbyte-python-cdk[dev]' --help
# Update a connector to use the CDK from this branch ref:
cd airbyte-integrations/connectors/source-example
poe use-cdk-branch devin/1785066655-unique-file-transfer-staging-pathsPR Slash CommandsAirbyte Maintainers can execute the following slash commands on your PR:
|
|
CI status on the two red connector checks:
All CDK checks — Pytest (all Python versions), Ruff, MyPy, package build — are green. |
There was a problem hiding this comment.
Pull request overview
This PR fixes a file-transfer staging-path collision in the Python CDK’s file-based stream reader by ensuring each transferred file is staged under a unique local subdirectory, preventing overwrites and race-driven FileNotFoundException failures when distinct source files resolve to the same relative path.
Changes:
- Namespace each staged local file path under a per-file UUID subdirectory while keeping
source_file_relative_pathunchanged. - Update existing staging-path assertions to accommodate the new UUID subdirectory.
- Add a unit test asserting staging paths are unique even when relative paths collide.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
airbyte_cdk/sources/file_based/file_based_stream_reader.py |
Adds UUID-based subdirectory to local staging paths to prevent collisions. |
unit_tests/sources/file_based/test_file_based_stream_reader.py |
Adjusts path assertions and adds a test ensuring staging-path uniqueness. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
…elper Co-Authored-By: bot_apk <apk@cognition.ai>
…nsfer-staging-paths' into devin/1785066655-unique-file-transfer-staging-paths
Daryna Ishchenko (darynaishchenko)
left a comment
There was a problem hiding this comment.
Patrick Nilan (@pnilan) The change makes sense to me, so approving.
But looks like it affects unit tests for source-google-drive since its tests have checks for files paths. It should be fixed before/after the release of new cdk as follow up. Also can we double check that new pathes are the only reason of failed ci for google-drive?
|
🙋 Human Input Needed: Daryna Ishchenko (@darynaishchenko) thanks for approving — answering your question, and the follow-up PR already exists, but it needs a maintainer to unblock two things. (Replying at top level since your review has no inline thread to reply to.) Is the new path the only reason google-drive CI fails? Based on the job log, yes: all 7 failures in i.e. the test asserts the exact staging path and only the new Follow-up PR for the connector tests: airbytehq/airbyte#82754 — it relaxes the exact-path assertions in
Also on this PR: |
…ue-file-transfer-staging-paths
Summary
File transfer (
use_file_transfer) staged every source file at{staging_dir}/{source_file_relative_path}. When two source files resolve to the same relative path, they share one staging file, which breaks syncs:/staging/files/Untitled document.docx, emits a record referencing it.FileChunkTask→localFile.delete()).java.io.FileNotFoundException: /staging/files/Untitled document.docx (No such file or directory), or the destination reads bytes of B for record A.This is easy to hit on Google Drive (duplicate document names are allowed, and exported Google-native docs are named
{name}.{ext}) and on any source whenpreserve_directory_structureis disabled and the same file name exists in several folders.Fix: namespace the local staging path under a unique subdirectory, leaving
source_file_relative_path(and therefore the destination object key) unchanged:Because
LOCAL_FILE_PATHcomes fromAbstractFileBasedStreamReader._get_file_transfer_paths, this covers the baseupload()as well as the readers that call the helper themselves (source-google-drive,source-s3,source-microsoft-sharepoint) with no connector-side change.The
Check: source-google-driveCI job here fails ontest_upload_file, which asserts the exact staging path; the test-only update it needs is in airbytehq/airbyte#82754 (same forsource-microsoft-sharepoint;source-s3already asserts withendswith). That check should go green once the monorepo PR merges.Out of scope but worth flagging: duplicate relative paths still map to a single destination object key, so one file overwrites the other in the destination. That is inherent to path-mirroring naming and is not what makes the sync fail.
Resolves https://github.com/airbytehq/oncall/issues/12851:
Declarative-First Evaluation
Not applicable: the bug is in the Python CDK's file-based stream reader (staging path construction), not in a declarative connector manifest. No declarative feature can influence where the CDK stages a transferred file.
Test Coverage
test_staging_paths_are_unique_per_fileinunit_tests/sources/file_based/test_file_based_stream_reader.pyasserts that two files resolving to the same relative path get identicalfile_relative_pathbut distinctlocal_file_path(parametrized for the duplicate-name andpreserve_directory_structure: falsecases). It fails onmain(both calls return the same local path).test_preserve_sub_directories_scenariosupdated: it now asserts the local path is{staging_dir}/{unique_subdir}/{expected_relative_path}instead of a hardcoded path.poetry run pytest unit_tests/sources/file_based -q→ 796 passed, 1 skipped.ruff check,ruff format --check, andmypypass.Versioning
No connector version bump or changelog entry: this repo publishes from release notes, and
pyproject.tomlversion is computed dynamically. Non-breaking — no schema, spec, stream, or state change; only the ephemeral local staging path changes, and it is communicated to the destination viastaging_file_urlin the same record.Link to Devin session: https://app.devin.ai/sessions/8ef791c2602147a2951781e0af756828
Important
Auto-merge enabled.
This PR is set to merge automatically when all requirements are met.