Skip to content

feat(storage): parallelize directory upload in transfer.upload() [BLDX-1089]#1504

Merged
cmgrote merged 16 commits intomainfrom
BLDX-1089
Apr 22, 2026
Merged

feat(storage): parallelize directory upload in transfer.upload() [BLDX-1089]#1504
cmgrote merged 16 commits intomainfrom
BLDX-1089

Conversation

@vaibhavatlan
Copy link
Copy Markdown
Collaborator

What was found

Rule: PERF-016 | File: application_sdk/storage/transfer.py:254 | Severity: HIGH

Sequential await _upload_one() in a for loop for directory uploads. No parallelism.

What was fixed

Replaced sequential loop with asyncio.gather + asyncio.Semaphore(20) for bounded concurrent uploads. Matches the pattern in storage/batch.py upload_prefix.

Linear

https://linear.app/atlan-epd/issue/BLDX-1089

…X-1089]

Directory upload iterated files sequentially with await _upload_one().
Now uses asyncio.gather with semaphore (max 20 concurrent), matching
the pattern in storage/batch.py upload_prefix.
@vaibhavatlan vaibhavatlan added the Autonomous SDK Evolution PRs from autonomous SDK evolution pipeline label Apr 21, 2026
@vaibhavatlan vaibhavatlan added the Autonomous SDK Evolution PRs from autonomous SDK evolution pipeline label Apr 21, 2026
@snykgituser
Copy link
Copy Markdown

snykgituser commented Apr 21, 2026

Snyk checks have passed. No issues have been found so far.

Status Scan Engine Critical High Medium Low Total (0)
Open Source Security 0 0 0 0 0 issues
Licenses 0 0 0 0 0 issues
Code Security 0 0 0 0 0 issues

💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 21, 2026

📜 Docstring Coverage Report

RESULT: PASSED (minimum: 30.0%, actual: 80.0%)

Detailed Coverage Report
======= Coverage for /home/runner/work/application-sdk/application-sdk/ ========
----------------------------------- Summary ------------------------------------
| Name                                                                              | Total | Miss | Cover | Cover% |
|-----------------------------------------------------------------------------------|-------|------|-------|--------|
| application_sdk/__init__.py                                                       |     1 |    0 |     1 |   100% |
| application_sdk/constants.py                                                      |     2 |    0 |     2 |   100% |
| application_sdk/discovery.py                                                      |    11 |    2 |     9 |    82% |
| application_sdk/errors.py                                                         |     4 |    1 |     3 |    75% |
| application_sdk/main.py                                                           |    28 |    6 |    22 |    79% |
| application_sdk/version.py                                                        |     1 |    0 |     1 |   100% |
| application_sdk/app/__init__.py                                                   |     1 |    0 |     1 |   100% |
| application_sdk/app/base.py                                                       |    69 |   13 |    56 |    81% |
| application_sdk/app/client.py                                                     |     1 |    0 |     1 |   100% |
| application_sdk/app/context.py                                                    |    38 |    2 |    36 |    95% |
| application_sdk/app/entrypoint.py                                                 |    11 |    3 |     8 |    73% |
| application_sdk/app/registry.py                                                   |    35 |    9 |    26 |    74% |
| application_sdk/app/task.py                                                       |    12 |    5 |     7 |    58% |
| application_sdk/clients/__init__.py                                               |     4 |    0 |     4 |   100% |
| application_sdk/clients/base.py                                                   |     6 |    1 |     5 |    83% |
| application_sdk/clients/models.py                                                 |     2 |    0 |     2 |   100% |
| application_sdk/clients/redis.py                                                  |    27 |    0 |    27 |   100% |
| application_sdk/clients/sql.py                                                    |    24 |    1 |    23 |    96% |
| application_sdk/clients/ssl_utils.py                                              |     8 |    0 |     8 |   100% |
| application_sdk/clients/azure/__init__.py                                         |     1 |    0 |     1 |   100% |
| application_sdk/clients/azure/auth.py                                             |     7 |    0 |     7 |   100% |
| application_sdk/clients/azure/client.py                                           |    13 |    0 |    13 |   100% |
| application_sdk/common/__init__.py                                                |     1 |    1 |     0 |     0% |
| application_sdk/common/aws_utils.py                                               |    10 |    1 |     9 |    90% |
| application_sdk/common/concurrency.py                                             |     3 |    0 |     3 |   100% |
| application_sdk/common/error_codes.py                                             |    14 |    2 |    12 |    86% |
| application_sdk/common/exc_utils.py                                               |     2 |    0 |     2 |   100% |
| application_sdk/common/file_converter.py                                          |     9 |    5 |     4 |    44% |
| application_sdk/common/file_ops.py                                                |    16 |    1 |    15 |    94% |
| application_sdk/common/models.py                                                  |     4 |    2 |     2 |    50% |
| application_sdk/common/path.py                                                    |     2 |    1 |     1 |    50% |
| application_sdk/common/sql_filters.py                                             |     9 |    0 |     9 |   100% |
| application_sdk/common/sql_utils.py                                               |     6 |    1 |     5 |    83% |
| application_sdk/common/types.py                                                   |     2 |    1 |     1 |    50% |
| application_sdk/common/utils.py                                                   |     2 |    0 |     2 |   100% |
| application_sdk/common/incremental/__init__.py                                    |     1 |    1 |     0 |     0% |
| application_sdk/common/incremental/helpers.py                                     |    11 |    0 |    11 |   100% |
| application_sdk/common/incremental/marker.py                                      |     5 |    0 |     5 |   100% |
| application_sdk/common/incremental/models.py                                      |    10 |    0 |    10 |   100% |
| application_sdk/common/incremental/column_extraction/__init__.py                  |     1 |    0 |     1 |   100% |
| application_sdk/common/incremental/column_extraction/analysis.py                  |     3 |    0 |     3 |   100% |
| application_sdk/common/incremental/column_extraction/backfill.py                  |     3 |    0 |     3 |   100% |
| application_sdk/common/incremental/state/__init__.py                              |     1 |    1 |     0 |     0% |
| application_sdk/common/incremental/state/incremental_diff.py                      |     8 |    0 |     8 |   100% |
| application_sdk/common/incremental/state/state_reader.py                          |     2 |    0 |     2 |   100% |
| application_sdk/common/incremental/state/state_writer.py                          |    10 |    0 |    10 |   100% |
| application_sdk/common/incremental/state/table_scope.py                           |     8 |    0 |     8 |   100% |
| application_sdk/common/incremental/storage/__init__.py                            |     1 |    1 |     0 |     0% |
| application_sdk/common/incremental/storage/duckdb_utils.py                        |    12 |    2 |    10 |    83% |
| application_sdk/common/incremental/storage/rocksdb_utils.py                       |     3 |    0 |     3 |   100% |
| application_sdk/contracts/__init__.py                                             |     1 |    0 |     1 |   100% |
| application_sdk/contracts/base.py                                                 |    36 |    6 |    30 |    83% |
| application_sdk/contracts/cleanup.py                                              |     5 |    0 |     5 |   100% |
| application_sdk/contracts/events.py                                               |    12 |    0 |    12 |   100% |
| application_sdk/contracts/storage.py                                              |     6 |    1 |     5 |    83% |
| application_sdk/contracts/types.py                                                |    14 |    0 |    14 |   100% |
| application_sdk/credentials/__init__.py                                           |     1 |    0 |     1 |   100% |
| application_sdk/credentials/agent.py                                              |     7 |    0 |     7 |   100% |
| application_sdk/credentials/atlan.py                                              |    12 |    6 |     6 |    50% |
| application_sdk/credentials/atlan_client.py                                       |     4 |    0 |     4 |   100% |
| application_sdk/credentials/errors.py                                             |     9 |    4 |     5 |    56% |
| application_sdk/credentials/git.py                                                |     9 |    6 |     3 |    33% |
| application_sdk/credentials/oauth.py                                              |     9 |    1 |     8 |    89% |
| application_sdk/credentials/ref.py                                                |    14 |    1 |    13 |    93% |
| application_sdk/credentials/registry.py                                           |    11 |    3 |     8 |    73% |
| application_sdk/credentials/resolver.py                                           |    11 |    4 |     7 |    64% |
| application_sdk/credentials/spec.py                                               |     5 |    0 |     5 |   100% |
| application_sdk/credentials/types.py                                              |    35 |   17 |    18 |    51% |
| application_sdk/credentials/utils.py                                              |     3 |    0 |     3 |   100% |
| application_sdk/execution/__init__.py                                             |     1 |    0 |     1 |   100% |
| application_sdk/execution/decorators.py                                           |     3 |    2 |     1 |    33% |
| application_sdk/execution/errors.py                                               |     2 |    0 |     2 |   100% |
| application_sdk/execution/heartbeat.py                                            |    17 |    2 |    15 |    88% |
| application_sdk/execution/retry.py                                                |     6 |    0 |     6 |   100% |
| application_sdk/execution/sandbox.py                                              |     4 |    0 |     4 |   100% |
| application_sdk/execution/settings.py                                             |     6 |    1 |     5 |    83% |
| application_sdk/execution/_temporal/__init__.py                                   |     1 |    1 |     0 |     0% |
| application_sdk/execution/_temporal/activities.py                                 |     7 |    0 |     7 |   100% |
| application_sdk/execution/_temporal/activity_utils.py                             |     5 |    0 |     5 |   100% |
| application_sdk/execution/_temporal/auth.py                                       |    12 |    0 |    12 |   100% |
| application_sdk/execution/_temporal/backend.py                                    |    10 |    1 |     9 |    90% |
| application_sdk/execution/_temporal/converter.py                                  |     3 |    0 |     3 |   100% |
| application_sdk/execution/_temporal/lock_activities.py                            |     3 |    0 |     3 |   100% |
| application_sdk/execution/_temporal/worker.py                                     |     8 |    3 |     5 |    62% |
| application_sdk/execution/_temporal/workflows.py                                  |     2 |    0 |     2 |   100% |
| application_sdk/execution/_temporal/interceptors/__init__.py                      |     1 |    0 |     1 |   100% |
| application_sdk/execution/_temporal/interceptors/activity_failure_logging.py      |     9 |    0 |     9 |   100% |
| application_sdk/execution/_temporal/interceptors/correlation_interceptor.py       |    15 |   10 |     5 |    33% |
| application_sdk/execution/_temporal/interceptors/events.py                        |    13 |    0 |    13 |   100% |
| application_sdk/execution/_temporal/interceptors/execution_context_interceptor.py |     8 |    4 |     4 |    50% |
| application_sdk/execution/_temporal/interceptors/lock.py                          |    10 |    2 |     8 |    80% |
| application_sdk/execution/_temporal/interceptors/outputs.py                       |     9 |    0 |     9 |   100% |
| application_sdk/handler/__init__.py                                               |     1 |    0 |     1 |   100% |
| application_sdk/handler/base.py                                                   |    13 |    2 |    11 |    85% |
| application_sdk/handler/context.py                                                |    15 |    5 |    10 |    67% |
| application_sdk/handler/contracts.py                                              |    20 |    0 |    20 |   100% |
| application_sdk/handler/manifest.py                                               |     5 |    0 |     5 |   100% |
| application_sdk/handler/service.py                                                |    41 |   22 |    19 |    46% |
| application_sdk/infrastructure/__init__.py                                        |     1 |    0 |     1 |   100% |
| application_sdk/infrastructure/_secret_utils.py                                   |     2 |    0 |     2 |   100% |
| application_sdk/infrastructure/bindings.py                                        |    16 |    3 |    13 |    81% |
| application_sdk/infrastructure/capacity.py                                        |    11 |    0 |    11 |   100% |
| application_sdk/infrastructure/context.py                                         |     5 |    0 |     5 |   100% |
| application_sdk/infrastructure/credential_vault.py                                |     7 |    3 |     4 |    57% |
| application_sdk/infrastructure/pubsub.py                                          |    13 |    3 |    10 |    77% |
| application_sdk/infrastructure/secrets.py                                         |    19 |    4 |    15 |    79% |
| application_sdk/infrastructure/state.py                                           |    10 |    7 |     3 |    30% |
| application_sdk/infrastructure/_dapr/__init__.py                                  |     1 |    0 |     1 |   100% |
| application_sdk/infrastructure/_dapr/client.py                                    |    28 |    4 |    24 |    86% |
| application_sdk/infrastructure/_dapr/credential_vault.py                          |    13 |    4 |     9 |    69% |
| application_sdk/infrastructure/_dapr/http.py                                      |    16 |   12 |     4 |    25% |
| application_sdk/infrastructure/_redis/__init__.py                                 |     1 |    0 |     1 |   100% |
| application_sdk/infrastructure/_redis/capacity.py                                 |     9 |    4 |     5 |    56% |
| application_sdk/observability/__init__.py                                         |     1 |    1 |     0 |     0% |
| application_sdk/observability/app_vitals.py                                       |    26 |    9 |    17 |    65% |
| application_sdk/observability/context.py                                          |     4 |    0 |     4 |   100% |
| application_sdk/observability/correlation.py                                      |     6 |    0 |     6 |   100% |
| application_sdk/observability/error_classifier.py                                 |     3 |    0 |     3 |   100% |
| application_sdk/observability/logger_adaptor.py                                   |    34 |    2 |    32 |    94% |
| application_sdk/observability/metrics_adaptor.py                                  |    12 |    1 |    11 |    92% |
| application_sdk/observability/models.py                                           |     4 |    0 |     4 |   100% |
| application_sdk/observability/observability.py                                    |    23 |    3 |    20 |    87% |
| application_sdk/observability/resource_sampler.py                                 |     5 |    0 |     5 |   100% |
| application_sdk/observability/segment_client.py                                   |    14 |    2 |    12 |    86% |
| application_sdk/observability/trace_context.py                                    |     2 |    0 |     2 |   100% |
| application_sdk/observability/traces_adaptor.py                                   |    15 |    1 |    14 |    93% |
| application_sdk/observability/utils.py                                            |     5 |    1 |     4 |    80% |
| application_sdk/observability/decorators/observability_decorator.py               |     7 |    4 |     3 |    43% |
| application_sdk/outputs/__init__.py                                               |     2 |    0 |     2 |   100% |
| application_sdk/outputs/collector.py                                              |     9 |    0 |     9 |   100% |
| application_sdk/outputs/models.py                                                 |     3 |    0 |     3 |   100% |
| application_sdk/server/health.py                                                  |    20 |    0 |    20 |   100% |
| application_sdk/server/fastapi/models.py                                          |    21 |   17 |     4 |    19% |
| application_sdk/server/fastapi/utils.py                                           |     5 |    0 |     5 |   100% |
| application_sdk/server/mcp/__init__.py                                            |     1 |    1 |     0 |     0% |
| application_sdk/server/mcp/decorators.py                                          |     3 |    1 |     2 |    67% |
| application_sdk/server/mcp/models.py                                              |     2 |    2 |     0 |     0% |
| application_sdk/server/mcp/server.py                                              |     5 |    0 |     5 |   100% |
| application_sdk/server/middleware/__init__.py                                     |     1 |    0 |     1 |   100% |
| application_sdk/server/middleware/_constants.py                                   |     1 |    0 |     1 |   100% |
| application_sdk/server/middleware/log.py                                          |     4 |    3 |     1 |    25% |
| application_sdk/server/middleware/metrics.py                                      |     3 |    2 |     1 |    33% |
| application_sdk/storage/__init__.py                                               |     1 |    0 |     1 |   100% |
| application_sdk/storage/batch.py                                                  |     9 |    3 |     6 |    67% |
| application_sdk/storage/binding.py                                                |     3 |    0 |     3 |   100% |
| application_sdk/storage/cloud.py                                                  |    22 |    3 |    19 |    86% |
| application_sdk/storage/errors.py                                                 |     8 |    3 |     5 |    62% |
| application_sdk/storage/factory.py                                                |     3 |    0 |     3 |   100% |
| application_sdk/storage/file_ref_sync.py                                          |     9 |    0 |     9 |   100% |
| application_sdk/storage/ops.py                                                    |    12 |    0 |    12 |   100% |
| application_sdk/storage/reference.py                                              |     8 |    0 |     8 |   100% |
| application_sdk/storage/transfer.py                                               |    13 |    3 |    10 |    77% |
| application_sdk/storage/formats/__init__.py                                       |    25 |    0 |    25 |   100% |
| application_sdk/storage/formats/json.py                                           |    15 |    1 |    14 |    93% |
| application_sdk/storage/formats/parquet.py                                        |    22 |    1 |    21 |    95% |
| application_sdk/storage/formats/utils.py                                          |     9 |    2 |     7 |    78% |
| application_sdk/templates/__init__.py                                             |     2 |    1 |     1 |    50% |
| application_sdk/templates/base_metadata_extractor.py                              |     3 |    0 |     3 |   100% |
| application_sdk/templates/incremental_sql_metadata_extractor.py                   |    17 |    0 |    17 |   100% |
| application_sdk/templates/sql_metadata_extractor.py                               |    13 |    0 |    13 |   100% |
| application_sdk/templates/sql_query_extractor.py                                  |     5 |    0 |     5 |   100% |
| application_sdk/templates/contracts/__init__.py                                   |     1 |    0 |     1 |   100% |
| application_sdk/templates/contracts/base_metadata_extraction.py                   |     3 |    0 |     3 |   100% |
| application_sdk/templates/contracts/incremental_sql.py                            |    20 |    0 |    20 |   100% |
| application_sdk/templates/contracts/sql_metadata.py                               |    19 |    0 |    19 |   100% |
| application_sdk/templates/contracts/sql_query.py                                  |     7 |    0 |     7 |   100% |
| application_sdk/test_utils/integration/__init__.py                                |     1 |    1 |     0 |     0% |
| application_sdk/testing/__init__.py                                               |     1 |    0 |     1 |   100% |
| application_sdk/testing/fixtures.py                                               |    10 |    0 |    10 |   100% |
| application_sdk/testing/mocks.py                                                  |    68 |   17 |    51 |    75% |
| application_sdk/testing/e2e/__init__.py                                           |     1 |    0 |     1 |   100% |
| application_sdk/testing/e2e/config.py                                             |     2 |    0 |     2 |   100% |
| application_sdk/testing/e2e/logs.py                                               |     6 |    1 |     5 |    83% |
| application_sdk/testing/e2e/pods.py                                               |     5 |    1 |     4 |    80% |
| application_sdk/testing/e2e/portforward.py                                        |     4 |    0 |     4 |   100% |
| application_sdk/testing/e2e/workflows.py                                          |     3 |    0 |     3 |   100% |
| application_sdk/testing/hypothesis/__init__.py                                    |     1 |    1 |     0 |     0% |
| application_sdk/testing/hypothesis/strategies/__init__.py                         |     1 |    1 |     0 |     0% |
| application_sdk/testing/hypothesis/strategies/sql_client.py                       |     1 |    1 |     0 |     0% |
| application_sdk/testing/hypothesis/strategies/clients/__init__.py                 |     1 |    1 |     0 |     0% |
| application_sdk/testing/hypothesis/strategies/clients/sql.py                      |     1 |    1 |     0 |     0% |
| application_sdk/testing/hypothesis/strategies/common/__init__.py                  |     1 |    1 |     0 |     0% |
| application_sdk/testing/hypothesis/strategies/common/logger.py                    |     3 |    0 |     3 |   100% |
| application_sdk/testing/hypothesis/strategies/handlers/__init__.py                |     1 |    1 |     0 |     0% |
| application_sdk/testing/hypothesis/strategies/handlers/sql/__init__.py            |     1 |    1 |     0 |     0% |
| application_sdk/testing/hypothesis/strategies/handlers/sql/sql_metadata.py        |     1 |    1 |     0 |     0% |
| application_sdk/testing/hypothesis/strategies/handlers/sql/sql_preflight.py       |     1 |    1 |     0 |     0% |
| application_sdk/testing/hypothesis/strategies/inputs/__init__.py                  |     1 |    1 |     0 |     0% |
| application_sdk/testing/hypothesis/strategies/inputs/json_input.py                |     1 |    1 |     0 |     0% |
| application_sdk/testing/hypothesis/strategies/inputs/parquet_input.py             |     1 |    1 |     0 |     0% |
| application_sdk/testing/hypothesis/strategies/outputs/__init__.py                 |     1 |    1 |     0 |     0% |
| application_sdk/testing/hypothesis/strategies/outputs/json_output.py              |     2 |    1 |     1 |    50% |
| application_sdk/testing/hypothesis/strategies/outputs/statestore.py               |     3 |    1 |     2 |    67% |
| application_sdk/testing/hypothesis/strategies/server/__init__.py                  |     1 |    1 |     0 |     0% |
| application_sdk/testing/hypothesis/strategies/server/fastapi/__init__.py          |     1 |    1 |     0 |     0% |
| application_sdk/testing/integration/__init__.py                                   |     1 |    0 |     1 |   100% |
| application_sdk/testing/integration/assertions.py                                 |    55 |   25 |    30 |    55% |
| application_sdk/testing/integration/client.py                                     |    16 |    0 |    16 |   100% |
| application_sdk/testing/integration/comparison.py                                 |    12 |    1 |    11 |    92% |
| application_sdk/testing/integration/lazy.py                                       |    10 |    0 |    10 |   100% |
| application_sdk/testing/integration/models.py                                     |     9 |    0 |     9 |   100% |
| application_sdk/testing/integration/runner.py                                     |    24 |    2 |    22 |    92% |
| application_sdk/testing/integration/validation.py                                 |     6 |    0 |     6 |   100% |
| application_sdk/testing/parity/__init__.py                                        |     1 |    0 |     1 |   100% |
| application_sdk/testing/parity/__main__.py                                        |     2 |    1 |     1 |    50% |
| application_sdk/testing/parity/comparator.py                                      |     8 |    0 |     8 |   100% |
| application_sdk/testing/parity/models.py                                          |     5 |    1 |     4 |    80% |
| application_sdk/testing/parity/report.py                                          |     4 |    0 |     4 |   100% |
| application_sdk/testing/scale_data_generator/__init__.py                          |     1 |    0 |     1 |   100% |
| application_sdk/testing/scale_data_generator/config_loader.py                     |    10 |    4 |     6 |    60% |
| application_sdk/testing/scale_data_generator/data_generator.py                    |    10 |    3 |     7 |    70% |
| application_sdk/testing/scale_data_generator/driver.py                            |     3 |    3 |     0 |     0% |
| application_sdk/testing/scale_data_generator/output_handler/__init__.py           |     1 |    1 |     0 |     0% |
| application_sdk/testing/scale_data_generator/output_handler/base.py               |     7 |    3 |     4 |    57% |
| application_sdk/testing/scale_data_generator/output_handler/csv_handler.py        |     6 |    6 |     0 |     0% |
| application_sdk/testing/scale_data_generator/output_handler/json_handler.py       |     5 |    5 |     0 |     0% |
| application_sdk/testing/scale_data_generator/output_handler/parquet_handler.py    |     6 |    6 |     0 |     0% |
| application_sdk/tools/__init__.py                                                 |     1 |    1 |     0 |     0% |
| application_sdk/tools/provision_credentials.py                                    |     2 |    1 |     1 |    50% |
| application_sdk/transformers/__init__.py                                          |     3 |    1 |     2 |    67% |
| application_sdk/transformers/atlas/__init__.py                                    |     6 |    1 |     5 |    83% |
| application_sdk/transformers/atlas/sql.py                                         |    25 |    4 |    21 |    84% |
| application_sdk/transformers/common/__init__.py                                   |     1 |    1 |     0 |     0% |
| application_sdk/transformers/common/utils.py                                      |     6 |    0 |     6 |   100% |
| application_sdk/transformers/query/__init__.py                                    |    11 |    2 |     9 |    82% |
| examples/application_custom_fastapi.py                                            |    10 |    5 |     5 |    50% |
| examples/application_fastapi.py                                                   |    10 |    1 |     9 |    90% |
| examples/application_hello_world.py                                               |     6 |    0 |     6 |   100% |
| examples/application_sql.py                                                       |    11 |    3 |     8 |    73% |
| examples/application_sql_miner.py                                                 |     9 |    3 |     6 |    67% |
| examples/application_sql_with_custom_transformer.py                               |    12 |    7 |     5 |    42% |
| examples/run_examples.py                                                          |    11 |   10 |     1 |     9% |
| tests/__init__.py                                                                 |     1 |    1 |     0 |     0% |
| tests/conftest.py                                                                 |     1 |    0 |     1 |   100% |
| tests/e2e/__init__.py                                                             |     1 |    1 |     0 |     0% |
| tests/e2e/conftest.py                                                             |     4 |    1 |     3 |    75% |
| tests/integration/__init__.py                                                     |     1 |    0 |     1 |   100% |
| tests/integration/conftest.py                                                     |    14 |    2 |    12 |    86% |
| tests/integration/test_cloud_store.py                                             |    15 |   12 |     3 |    20% |
| tests/integration/test_core_execution.py                                          |    29 |   24 |     5 |    17% |
| tests/integration/test_dapr_http.py                                               |    22 |    0 |    22 |   100% |
| tests/integration/test_error_and_retry.py                                         |    19 |   15 |     4 |    21% |
| tests/integration/test_events_serde.py                                            |    15 |    5 |    10 |    67% |
| tests/integration/test_handler_service.py                                         |    58 |   29 |    29 |    50% |
| tests/integration/test_heartbeat.py                                               |    13 |   10 |     3 |    23% |
| tests/integration/test_incremental_pipeline.py                                    |    14 |    3 |    11 |    79% |
| tests/integration/test_lifecycle.py                                               |    21 |   17 |     4 |    19% |
| tests/integration/test_multi_entrypoint.py                                        |    26 |   19 |     7 |    27% |
| tests/integration/test_output_e2e.py                                              |    16 |    0 |    16 |   100% |
| tests/integration/test_storage_io.py                                              |    19 |    0 |    19 |   100% |
| tests/integration/test_timeout.py                                                 |     7 |    5 |     2 |    29% |
| tests/integration/_example/__init__.py                                            |     1 |    0 |     1 |   100% |
| tests/integration/_example/conftest.py                                            |     6 |    0 |     6 |   100% |
| tests/integration/_example/scenarios.py                                           |     1 |    0 |     1 |   100% |
| tests/integration/_example/test_integration.py                                    |     2 |    0 |     2 |   100% |
| tests/unit/__init__.py                                                            |     1 |    1 |     0 |     0% |
| tests/unit/conftest.py                                                            |     5 |    1 |     4 |    80% |
| tests/unit/test_discovery.py                                                      |    75 |   69 |     6 |     8% |
| tests/unit/test_main.py                                                           |    61 |   48 |    13 |    21% |
| tests/unit/test_parse_atlan_yaml.py                                               |    20 |   16 |     4 |    20% |
| tests/unit/app/__init__.py                                                        |     1 |    1 |     0 |     0% |
| tests/unit/app/test_base.py                                                       |    70 |   39 |    31 |    44% |
| tests/unit/app/test_cleanup_files.py                                              |    15 |   14 |     1 |     7% |
| tests/unit/app/test_cleanup_storage.py                                            |    29 |   26 |     3 |    10% |
| tests/unit/app/test_client.py                                                     |    19 |    1 |    18 |    95% |
| tests/unit/app/test_entrypoint.py                                                 |    65 |   38 |    27 |    42% |
| tests/unit/app/test_on_complete.py                                                |    35 |   32 |     3 |     9% |
| tests/unit/app/test_registry.py                                                   |    34 |    4 |    30 |    88% |
| tests/unit/app/test_task.py                                                       |    76 |   46 |    30 |    39% |
| tests/unit/clients/__init__.py                                                    |     1 |    1 |     0 |     0% |
| tests/unit/clients/test_async_sql_client.py                                       |    10 |    9 |     1 |    10% |
| tests/unit/clients/test_azure_auth.py                                             |    14 |    0 |    14 |   100% |
| tests/unit/clients/test_azure_client.py                                           |    19 |    0 |    19 |   100% |
| tests/unit/clients/test_base_client.py                                            |    23 |    1 |    22 |    96% |
| tests/unit/clients/test_redis_client.py                                           |    40 |    0 |    40 |   100% |
| tests/unit/clients/test_sql_client.py                                             |    24 |    5 |    19 |    79% |
| tests/unit/clients/test_ssl_utils.py                                              |    41 |    4 |    37 |    90% |
| tests/unit/common/test_aws_utils.py                                               |    30 |    1 |    29 |    97% |
| tests/unit/common/test_column_extraction.py                                       |    10 |    0 |    10 |   100% |
| tests/unit/common/test_file_converter.py                                          |    29 |    0 |    29 |   100% |
| tests/unit/common/test_file_ops.py                                                |    21 |    0 |    21 |   100% |
| tests/unit/common/test_path.py                                                    |     6 |    0 |     6 |   100% |
| tests/unit/common/test_utils.py                                                   |    74 |    6 |    68 |    92% |
| tests/unit/common/test_utils_file_discovery.py                                    |    13 |    0 |    13 |   100% |
| tests/unit/common/incremental/__init__.py                                         |     1 |    1 |     0 |     0% |
| tests/unit/common/incremental/test_helpers.py                                     |    31 |    0 |    31 |   100% |
| tests/unit/common/incremental/test_incremental_diff.py                            |    24 |   15 |     9 |    38% |
| tests/unit/common/incremental/test_marker.py                                      |    16 |    0 |    16 |   100% |
| tests/unit/common/incremental/test_models.py                                      |    15 |    0 |    15 |   100% |
| tests/unit/common/incremental/test_state_reader.py                                |     7 |    1 |     6 |    86% |
| tests/unit/common/incremental/test_state_writer.py                                |    21 |    0 |    21 |   100% |
| tests/unit/common/incremental/state/__init__.py                                   |     1 |    1 |     0 |     0% |
| tests/unit/contracts/__init__.py                                                  |     1 |    1 |     0 |     0% |
| tests/unit/contracts/test_base.py                                                 |   121 |  119 |     2 |     2% |
| tests/unit/contracts/test_connection_ref.py                                       |    31 |   29 |     2 |     6% |
| tests/unit/contracts/test_git_reference.py                                        |    23 |   22 |     1 |     4% |
| tests/unit/contracts/test_storage_tier_temporal_serde.py                          |     9 |    1 |     8 |    89% |
| tests/unit/contracts/test_types.py                                                |    18 |   17 |     1 |     6% |
| tests/unit/credentials/__init__.py                                                |     1 |    1 |     0 |     0% |
| tests/unit/credentials/test_agent.py                                              |    64 |   46 |    18 |    28% |
| tests/unit/credentials/test_atlan_client.py                                       |    14 |    8 |     6 |    43% |
| tests/unit/credentials/test_mock_store.py                                         |    17 |   15 |     2 |    12% |
| tests/unit/credentials/test_oauth.py                                              |    19 |   15 |     4 |    21% |
| tests/unit/credentials/test_ref.py                                                |    16 |   14 |     2 |    12% |
| tests/unit/credentials/test_registry.py                                           |    20 |   18 |     2 |    10% |
| tests/unit/credentials/test_resolver.py                                           |    21 |    9 |    12 |    57% |
| tests/unit/credentials/test_types.py                                              |    42 |   41 |     1 |     2% |
| tests/unit/credentials/test_utils.py                                              |    12 |    1 |    11 |    92% |
| tests/unit/decorators/__init__.py                                                 |     1 |    1 |     0 |     0% |
| tests/unit/decorators/test_mcp_tool.py                                            |    56 |    4 |    52 |    93% |
| tests/unit/execution/__init__.py                                                  |     1 |    1 |     0 |     0% |
| tests/unit/execution/conftest.py                                                  |     4 |    2 |     2 |    50% |
| tests/unit/execution/test_activities.py                                           |    91 |   83 |     8 |     9% |
| tests/unit/execution/test_activities_tracking.py                                  |    18 |   12 |     6 |    33% |
| tests/unit/execution/test_converter.py                                            |    15 |   12 |     3 |    20% |
| tests/unit/execution/test_execution_context_interceptor.py                        |    10 |    1 |     9 |    90% |
| tests/unit/execution/test_lock_interceptor.py                                     |    33 |   13 |    20 |    61% |
| tests/unit/execution/test_settings.py                                             |    30 |   25 |     5 |    17% |
| tests/unit/execution/test_temporal_prometheus.py                                  |     4 |    0 |     4 |   100% |
| tests/unit/execution/test_worker.py                                               |    42 |   36 |     6 |    14% |
| tests/unit/execution/test_workflows.py                                            |    44 |   37 |     7 |    16% |
| tests/unit/handler/__init__.py                                                    |     1 |    1 |     0 |     0% |
| tests/unit/handler/test_base.py                                                   |    18 |   17 |     1 |     6% |
| tests/unit/handler/test_contracts.py                                              |    32 |   29 |     3 |     9% |
| tests/unit/handler/test_service.py                                                |   167 |   99 |    68 |    41% |
| tests/unit/infrastructure/__init__.py                                             |     1 |    1 |     0 |     0% |
| tests/unit/infrastructure/test_bindings.py                                        |    18 |   14 |     4 |    22% |
| tests/unit/infrastructure/test_capacity.py                                        |    20 |   16 |     4 |    20% |
| tests/unit/infrastructure/test_credential_state_store.py                          |    19 |   12 |     7 |    37% |
| tests/unit/infrastructure/test_credential_vault.py                                |    36 |   17 |    19 |    53% |
| tests/unit/infrastructure/test_dapr_http.py                                       |    34 |   26 |     8 |    24% |
| tests/unit/infrastructure/test_dapr_wrappers.py                                   |    46 |   44 |     2 |     4% |
| tests/unit/infrastructure/test_pubsub.py                                          |    30 |   10 |    20 |    67% |
| tests/unit/infrastructure/test_secrets.py                                         |    36 |    0 |    36 |   100% |
| tests/unit/infrastructure/test_state.py                                           |    20 |    0 |    20 |   100% |
| tests/unit/interceptors/__init__.py                                               |     1 |    1 |     0 |     0% |
| tests/unit/interceptors/test_activity_failure_logging.py                          |    28 |    1 |    27 |    96% |
| tests/unit/interceptors/test_app_vitals.py                                        |    97 |   78 |    19 |    20% |
| tests/unit/interceptors/test_correlation_interceptor.py                           |    18 |    9 |     9 |    50% |
| tests/unit/interceptors/test_events.py                                            |    15 |   11 |     4 |    27% |
| tests/unit/interceptors/test_output_interceptor.py                                |    35 |    3 |    32 |    91% |
| tests/unit/observability/__init__.py                                              |     1 |    1 |     0 |     0% |
| tests/unit/observability/test_execution_context.py                                |    11 |    0 |    11 |   100% |
| tests/unit/observability/test_logger_adaptor.py                                   |    57 |    4 |    53 |    93% |
| tests/unit/observability/test_metrics_adaptor.py                                  |    25 |    1 |    24 |    96% |
| tests/unit/observability/test_resource_sampler.py                                 |    12 |    7 |     5 |    42% |
| tests/unit/observability/test_traces_adaptor.py                                   |    13 |    1 |    12 |    92% |
| tests/unit/outputs/__init__.py                                                    |     1 |    1 |     0 |     0% |
| tests/unit/outputs/test_outputs.py                                                |    33 |   24 |     9 |    27% |
| tests/unit/server/__init__.py                                                     |     1 |    1 |     0 |     0% |
| tests/unit/server/test_health.py                                                  |    24 |   21 |     3 |    12% |
| tests/unit/server/fastapi/test_fastapi_utils.py                                   |    36 |    0 |    36 |   100% |
| tests/unit/server/mcp/__init__.py                                                 |     1 |    1 |     0 |     0% |
| tests/unit/server/mcp/test_mcp_server_v3.py                                       |    14 |    9 |     5 |    36% |
| tests/unit/storage/__init__.py                                                    |     1 |    1 |     0 |     0% |
| tests/unit/storage/test_binding.py                                                |    13 |    6 |     7 |    54% |
| tests/unit/storage/test_cloud.py                                                  |    34 |   30 |     4 |    12% |
| tests/unit/storage/test_file_ref_sync.py                                          |    25 |   21 |     4 |    16% |
| tests/unit/storage/test_ops.py                                                    |    51 |   44 |     7 |    14% |
| tests/unit/storage/test_path_separators.py                                        |    13 |    0 |    13 |   100% |
| tests/unit/storage/test_transfer.py                                               |    39 |   32 |     7 |    18% |
| tests/unit/storage/formats/__init__.py                                            |     1 |    1 |     0 |     0% |
| tests/unit/storage/formats/test_base_io.py                                        |    30 |    2 |    28 |    93% |
| tests/unit/storage/formats/test_writer_data_integrity.py                          |    12 |    5 |     7 |    58% |
| tests/unit/storage/formats/readers/__init__.py                                    |     1 |    1 |     0 |     0% |
| tests/unit/storage/formats/readers/test_json_reader.py                            |    37 |   18 |    19 |    51% |
| tests/unit/storage/formats/readers/test_parquet_reader.py                         |    60 |   38 |    22 |    37% |
| tests/unit/storage/formats/writers/__init__.py                                    |     1 |    1 |     0 |     0% |
| tests/unit/storage/formats/writers/test_json_writer.py                            |     7 |    6 |     1 |    14% |
| tests/unit/storage/formats/writers/test_parquet_writer.py                         |    62 |   10 |    52 |    84% |
| tests/unit/templates/__init__.py                                                  |     1 |    1 |     0 |     0% |
| tests/unit/templates/conftest.py                                                  |     2 |    0 |     2 |   100% |
| tests/unit/templates/test_base_metadata_extractor.py                              |    16 |   11 |     5 |    31% |
| tests/unit/templates/test_incremental_sql_metadata_extractor.py                   |    50 |   39 |    11 |    22% |
| tests/unit/templates/test_sql_metadata_extractor.py                               |   108 |   86 |    22 |    20% |
| tests/unit/templates/test_sql_query_extractor.py                                  |    15 |   12 |     3 |    20% |
| tests/unit/testing/__init__.py                                                    |     1 |    1 |     0 |     0% |
| tests/unit/testing/test_fixtures.py                                               |    16 |   15 |     1 |     6% |
| tests/unit/testing/test_mocks.py                                                  |    29 |   28 |     1 |     3% |
| tests/unit/testing/test_parity.py                                                 |    35 |   34 |     1 |     3% |
| tests/unit/testing/e2e/__init__.py                                                |     1 |    1 |     0 |     0% |
| tests/unit/testing/e2e/test_logs.py                                               |    10 |    8 |     2 |    20% |
| tests/unit/testing/e2e/test_portforward.py                                        |     7 |    1 |     6 |    86% |
| tests/unit/testing/integration/__init__.py                                        |     1 |    1 |     0 |     0% |
| tests/unit/testing/integration/test_client.py                                     |    10 |    8 |     2 |    20% |
| tests/unit/testing/integration/test_comparison.py                                 |    29 |    0 |    29 |   100% |
| tests/unit/tools/__init__.py                                                      |     1 |    1 |     0 |     0% |
| tests/unit/tools/test_check_migration.py                                          |    65 |   44 |    21 |    32% |
| tests/unit/tools/test_codemod_roundtrip.py                                        |    32 |   31 |     1 |     3% |
| tests/unit/tools/test_extract_context.py                                          |    50 |   48 |     2 |     4% |
| tests/unit/tools/test_fingerprint.py                                              |    23 |   21 |     2 |     9% |
| tests/unit/tools/test_provision_credentials.py                                    |    11 |    0 |    11 |   100% |
| tests/unit/tools/test_rewrite_imports.py                                          |    42 |   32 |    10 |    24% |
| tests/unit/tools/test_run_codemods.py                                             |    16 |   15 |     1 |     6% |
| tests/unit/tools/test_codemods/__init__.py                                        |     1 |    1 |     0 |     0% |
| tests/unit/tools/test_codemods/conftest.py                                        |     2 |    0 |     2 |   100% |
| tests/unit/tools/test_codemods/test_remove_activities_cls.py                      |    15 |   11 |     4 |    27% |
| tests/unit/tools/test_codemods/test_remove_decorators.py                          |    17 |   16 |     1 |     6% |
| tests/unit/tools/test_codemods/test_rewrite_activity_calls.py                     |    16 |   11 |     5 |    31% |
| tests/unit/tools/test_codemods/test_rewrite_entry_point.py                        |    19 |   16 |     3 |    16% |
| tests/unit/tools/test_codemods/test_rewrite_handlers.py                           |    13 |   12 |     1 |     8% |
| tests/unit/tools/test_codemods/test_rewrite_returns.py                            |    12 |   10 |     2 |    17% |
| tests/unit/tools/test_codemods/test_rewrite_signatures.py                         |    19 |   18 |     1 |     5% |
| tests/unit/transformers/__init__.py                                               |     1 |    1 |     0 |     0% |
| tests/unit/transformers/atlas/__init__.py                                         |     1 |    1 |     0 |     0% |
| tests/unit/transformers/atlas/test_column.py                                      |    17 |    6 |    11 |    65% |
| tests/unit/transformers/atlas/test_database.py                                    |     8 |    6 |     2 |    25% |
| tests/unit/transformers/atlas/test_function.py                                    |     9 |    5 |     4 |    44% |
| tests/unit/transformers/atlas/test_procedure.py                                   |     7 |    6 |     1 |    14% |
| tests/unit/transformers/atlas/test_schema.py                                      |     8 |    6 |     2 |    25% |
| tests/unit/transformers/atlas/test_table.py                                       |    13 |    6 |     7 |    54% |
| tests/unit/transformers/query/test_sql_transformer.py                             |    16 |    4 |    12 |    75% |
| tests/unit/transformers/query/test_sql_transformer_output_validation.py           |     5 |    2 |     3 |    60% |
| tools/migrate_v3/__init__.py                                                      |     1 |    0 |     1 |   100% |
| tools/migrate_v3/check_migration.py                                               |    11 |    6 |     5 |    45% |
| tools/migrate_v3/contract_mapping.py                                              |     3 |    1 |     2 |    67% |
| tools/migrate_v3/extract_context.py                                               |    21 |   16 |     5 |    24% |
| tools/migrate_v3/fingerprint.py                                                   |     3 |    1 |     2 |    67% |
| tools/migrate_v3/import_mapping.py                                                |     2 |    0 |     2 |   100% |
| tools/migrate_v3/rewrite_imports.py                                               |    19 |    8 |    11 |    58% |
| tools/migrate_v3/run_codemods.py                                                  |    14 |    5 |     9 |    64% |
| tools/migrate_v3/codemods/__init__.py                                             |    11 |    4 |     7 |    64% |
| tools/migrate_v3/codemods/remove_activities_cls.py                                |    12 |    7 |     5 |    42% |
| tools/migrate_v3/codemods/remove_decorators.py                                    |    11 |    8 |     3 |    27% |
| tools/migrate_v3/codemods/rewrite_activity_calls.py                               |     6 |    1 |     5 |    83% |
| tools/migrate_v3/codemods/rewrite_entry_point.py                                  |    10 |    3 |     7 |    70% |
| tools/migrate_v3/codemods/rewrite_handlers.py                                     |     8 |    5 |     3 |    38% |
| tools/migrate_v3/codemods/rewrite_returns.py                                      |     7 |    5 |     2 |    29% |
| tools/migrate_v3/codemods/rewrite_signatures.py                                   |     8 |    4 |     4 |    50% |
|-----------------------------------------------------------------------------------|-------|------|-------|--------|
| TOTAL                                                                             |  6278 | 2852 |  3426 |  54.6% |
---------------- RESULT: PASSED (minimum: 30.0%, actual: 54.6%) ----------------

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 21, 2026

📦 Trivy Vulnerability Scan Results

Schema Version Created At Artifact Type
2 2026-04-22T15:31:28.266310036Z . repository

Report Summary

Could not generate summary table (data length mismatch: 9 vs 8).

Scan Result Details

requirements.txt
uv.lock

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 21, 2026

📦 Trivy Secret Scan Results

Schema Version Created At Artifact Type
2 2026-04-22T15:31:41.924296926Z . repository

Report Summary

Could not generate summary table (data length mismatch: 9 vs 8).

Scan Result Details

requirements.txt
uv.lock

@atlan-ci
Copy link
Copy Markdown
Collaborator

atlan-ci commented Apr 21, 2026

☂️ Python Coverage

current status: ✅

Overall Coverage

Lines Covered Coverage Threshold Status
14821 10366 70% 0% 🟢

New Files

No new covered files...

Modified Files

No covered modified files...

updated for commit: be3f5f5 by action🐍

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 21, 2026

🛠 Full Test Coverage Report: https://k.atlan.dev/coverage/application-sdk/pr/1504

@vaibhavatlan vaibhavatlan changed the title perf(storage): parallelize directory upload in transfer.upload() [BLDX-1089] feat(storage): parallelize directory upload in transfer.upload() [BLDX-1089] Apr 21, 2026
@vaibhavatlan
Copy link
Copy Markdown
Collaborator Author

@sdk-review auto-complete

@github-actions
Copy link
Copy Markdown
Contributor

🔄 SDK Review starting (auto-complete) — ~10 min, possibly longer with fix iterations. Watch live progress

💡 Comment @sdk-review stop to cancel.

@claude
Copy link
Copy Markdown
Contributor

claude Bot commented Apr 21, 2026

SDK Review: PR #1504 — feat(storage): parallelize directory upload in transfer.upload() [BLDX-1089]

Verdict: NEEDS FIXES

Good idea — parallelizing directory uploads is a clear performance win. However, the concurrency limit is hardcoded at 20 (5× the established default in batch.py) and not configurable, and no tests cover the new concurrent behavior.

Findings by File

application_sdk/storage/transfer.py

  • Important [QUAL] L254 — Hardcoded MAX_CONCURRENT_UPLOADS = 20 inside function body. batch.py:upload_prefix (same package) exposes max_concurrency: int = 4 as a configurable keyword parameter. This PR creates an inconsistency: 5× higher default and no way to tune it. Fix: add max_concurrency: int = 4 as a keyword-only parameter to upload(), matching batch.py's API. If 20 is genuinely the right default for this path, document why it differs.
  • Important [TEST] — No tests for the new concurrent upload behavior. The sequential→concurrent change alters timing, error propagation, and result aggregation. Fix: add at least one test verifying correct multi-file upload via asyncio.gather, and one verifying transferred_count accuracy when skip_if_exists=True causes some files to be skipped.
  • Minor [QUAL] L252 — import asyncio inside the function body. SDK convention requires top-level imports for stdlib modules. This matches the existing pattern in batch.py (3 occurrences), so it's consistent locally but still technically non-compliant. Fix: move to module top-level.
  • Minor [BUG] L271 — asyncio.gather without return_exceptions=True propagates the first exception immediately while orphaned upload tasks continue running in the background. Same pattern as batch.py, so not new to this codebase, but worth noting: on partial failure the caller gets an exception while uploads silently continue. Consider return_exceptions=True with explicit error aggregation, or document that partial uploads are possible on failure.

Strengths

  • Clean use of asyncio.Semaphore for bounded concurrency — correct pattern.
  • Functional result collection (sum(1 for ok in results if ok)) is cleaner than the mutation-based approach in batch.py.
  • Key-building loop separated from upload execution — good for readability.

Comment thread application_sdk/storage/transfer.py Outdated
Comment thread application_sdk/storage/transfer.py Outdated
Comment thread application_sdk/storage/transfer.py
vaibhavatlan and others added 3 commits April 22, 2026 14:26
- Move `import asyncio` to module top-level (SDK convention)
- Replace hardcoded MAX_CONCURRENT_UPLOADS=20 with configurable
  `max_concurrency: int = 4` kwarg, matching batch.py's API
- Add `return_exceptions=True` to asyncio.gather to prevent orphaned
  tasks on partial failure, re-raising the first error after all tasks
  settle

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@vaibhavatlan
Copy link
Copy Markdown
Collaborator Author

@sdk-review auto-complete

@github-actions
Copy link
Copy Markdown
Contributor

🔄 SDK Re-review starting (auto-complete) — ~10 min, possibly longer with fix iterations. Watch live progress

💡 Comment @sdk-review stop to cancel.

@claude
Copy link
Copy Markdown
Contributor

claude Bot commented Apr 22, 2026

SDK Re-review: PR #1504 — feat(storage): parallelize directory upload in transfer.upload() [BLDX-1089]

Verdict: NEEDS FIXES

Latest commit (c812bf4) addressed 3 of 4 prior findings: max_concurrency is now a configurable parameter matching batch.py's API (default 4), import asyncio moved to module top-level, and asyncio.gather now uses return_exceptions=True with explicit error aggregation. The remaining issue is the absence of tests for the new concurrent upload behavior.

Delta from prior review

  • [RESOLVED] transfer.py:254 — prior finding "Hardcoded MAX_CONCURRENT_UPLOADS = 20, inconsistent with batch.py's configurable max_concurrency: int = 4" — fixed: now exposed as max_concurrency: int = 4 keyword parameter (L172), consistent with batch.py.
  • [RESOLVED] transfer.py:252 — prior finding "import asyncio inside function body" — fixed: moved to module top-level (L20).
  • [RESOLVED] transfer.py:271 — prior finding "asyncio.gather without return_exceptions=True" — fixed: now uses return_exceptions=True (L273) with explicit error collection and re-raise (L275-L277).
  • [STILL] transfer.py — prior finding "No tests for concurrent upload behavior" — still no tests covering the asyncio.gather path, max_concurrency parameter, or error propagation under concurrency.

Findings by File

application_sdk/storage/transfer.py

  • Important [TEST] — No tests for the new concurrent upload behavior. The sequential→concurrent change alters timing, error propagation (return_exceptions=True + re-raise first), and result aggregation. tests/unit/storage/test_transfer.py exists but has no coverage of the gather path. Fix: add at minimum (1) a test verifying multi-file directory upload completes correctly via asyncio.gather, (2) a test verifying transferred_count accuracy when skip_if_exists=True causes some files to be skipped, and (3) a test verifying that when one upload raises, the error propagates correctly from the gather results.

Strengths

  • Clean adoption of the batch.py concurrency pattern — Semaphore + asyncio.gather with return_exceptions=True is the established SDK idiom.
  • max_concurrency parameter API is now fully consistent with batch.py:upload_prefix and batch.py:download_prefix.
  • Error handling is explicit: errors are collected from gather results and the first is re-raised, giving callers a clear exception rather than silent partial failure.
  • Key-building loop cleanly separated from upload execution improves readability over the sequential version.

Comment thread application_sdk/storage/transfer.py
Cover the three scenarios requested in review:
1. Multi-file directory upload completing correctly (10 files)
2. transferred_count accuracy with skip_if_exists partial skips
3. Error propagation when one upload raises inside gather

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@vaibhavatlan
Copy link
Copy Markdown
Collaborator Author

@sdk-review

@github-actions
Copy link
Copy Markdown
Contributor

🔄 SDK Re-review starting (review) — ~10 min. Watch live progress

Copy link
Copy Markdown
Member

@Aryamanz29 Aryamanz29 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lets also add integration tests for this

vaibhavatlan and others added 2 commits April 22, 2026 15:16
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Cover the concurrent directory upload/download via transfer module
against a real local object store:
- Multi-file concurrent upload (15 files)
- Full upload→download roundtrip with nested directories
- Partial skip_if_exists behavior (unchanged vs changed files)
- max_concurrency parameter with low concurrency value

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@vaibhavatlan
Copy link
Copy Markdown
Collaborator Author

@sdk-review

@github-actions
Copy link
Copy Markdown
Contributor

🔄 SDK Re-review starting (review) — ~10 min. Watch live progress

@claude
Copy link
Copy Markdown
Contributor

claude Bot commented Apr 22, 2026

SDK Re-review: PR #1504 — feat(storage): parallelize directory upload in transfer.upload() [BLDX-1089]

Verdict: READY TO MERGE

All 4 findings from the prior review are now resolved. The latest commits (bf64282, 306c23f, cb6a775) add thorough unit and integration tests for the concurrent upload path — the last remaining issue. No new problems introduced.

Delta from prior review

  • [RESOLVED] transfer.py:254 — prior finding "Hardcoded MAX_CONCURRENT_UPLOADS = 20, inconsistent with batch.py" — fixed: exposed as max_concurrency: int = 4 keyword parameter, consistent with batch.py.
  • [RESOLVED] transfer.py:252 — prior finding "import asyncio inside function body" — fixed: moved to module top-level (L20).
  • [RESOLVED] transfer.py:271 — prior finding "asyncio.gather without return_exceptions=True" — fixed: uses return_exceptions=True with explicit error collection and re-raise.
  • [RESOLVED] transfer.py — prior finding "No tests for concurrent upload behavior" — fixed: 3 unit tests and 4 integration tests now cover the asyncio.gather path, max_concurrency parameter, partial skip counts, error propagation, and full roundtrip.

Findings by File

No findings.

Strengths

  • Clean Semaphore + asyncio.gather pattern consistent with batch.py — one established idiom across the SDK for bounded concurrent I/O.
  • Excellent test coverage of the new code path: concurrent completion, partial skip accuracy, error propagation via monkeypatch, max_concurrency parameter acceptance, nested directory roundtrip, and integration-level verification with a real local store.
  • max_concurrency parameter API (name, default value of 4, keyword-only) matches batch.py:upload_prefix and batch.py:download_prefix exactly — connector developers get consistent ergonomics.
  • Error handling is explicit: errors collected from gather results, first one re-raised — no silent partial failures.

@atlan-ci
Copy link
Copy Markdown
Collaborator

ℹ️ SDK Review verdict: READY TO MERGE — but required CI checks on this branch are failing. The sdk-review status is set to failure to prevent merge. Fix CI (e.g. pre-commit, tests), push, and re-run @sdk-review.

… count

list_keys returns sidecar files too, so assert against suffix-filtered
keys to match only the 15 data files.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@vaibhavatlan
Copy link
Copy Markdown
Collaborator Author

@sdk-review

@github-actions
Copy link
Copy Markdown
Contributor

🔄 SDK Re-review starting (review) — ~10 min. Watch live progress

@claude
Copy link
Copy Markdown
Contributor

claude Bot commented Apr 22, 2026

SDK Re-review: PR #1504 — feat(storage): parallelize directory upload in transfer.upload() [BLDX-1089]

Verdict: READY TO MERGE

All 4 findings from the prior review remain resolved. The latest commits (0ec7b3b, cb6a775, 306c23f) add a test filter fix and formatting — no new issues introduced. The implementation is clean and well-tested.

Delta from prior review

  • [RESOLVED] transfer.py:254 — prior finding "Hardcoded MAX_CONCURRENT_UPLOADS = 20, inconsistent with batch.py" — fixed: exposed as max_concurrency: int = 4 keyword parameter, consistent with batch.py.
  • [RESOLVED] transfer.py:252 — prior finding "import asyncio inside function body" — fixed: moved to module top-level (L20).
  • [RESOLVED] transfer.py:271 — prior finding "asyncio.gather without return_exceptions=True" — fixed: uses return_exceptions=True with explicit error collection and re-raise.
  • [RESOLVED] transfer.py — prior finding "No tests for concurrent upload behavior" — fixed: 4 unit tests and 4 integration tests now cover the asyncio.gather path.
  • [NEW] — None

Findings by File

No findings.

Strengths

  • Clean Semaphore + asyncio.gather pattern consistent with batch.py — one established idiom across the SDK for bounded concurrent I/O.
  • Thorough test coverage: concurrent completion, partial skip accuracy, error propagation (monkeypatch), max_concurrency parameter, nested directory roundtrip, and integration-level verification with a real local store.
  • max_concurrency parameter API (name, default value of 4, keyword-only) matches batch.py:upload_prefix and batch.py:download_prefix — connector developers get consistent ergonomics.
  • Error handling is explicit: errors collected from gather results, first one re-raised — no silent partial failures.
  • test_storage_io.py:277 uses suffix=".csv" filter to exclude .sha256 sidecars from key counts — good attention to the sidecar detail.

atlan-ci
atlan-ci previously approved these changes Apr 22, 2026
Copy link
Copy Markdown
Collaborator

@atlan-ci atlan-ci left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SDK Re-review: no new issues introduced by the latest changes. CI passing. Branch up to date. Approved.

…loads

Uses configurable constant (env: ATLAN_MAX_CONCURRENT_STORAGE_TRANSFERS,
default 4) instead of hardcoded value, aligning with batch.py convention.
@vaibhavatlan
Copy link
Copy Markdown
Collaborator Author

@sdk-review

@github-actions
Copy link
Copy Markdown
Contributor

🔄 SDK Re-review starting (review) — ~10 min. Watch live progress

@claude
Copy link
Copy Markdown
Contributor

claude Bot commented Apr 22, 2026

SDK Re-review: PR #1504 — feat(storage): parallelize directory upload in transfer.upload() [BLDX-1089]

Verdict: READY TO MERGE

All 4 findings from the prior review remain resolved. Since the last review, only merge commits from main were added — no changes to the PR's own files. No new issues introduced.

Delta from prior review

  • [RESOLVED] transfer.py:254 — prior finding "Hardcoded MAX_CONCURRENT_UPLOADS = 20, inconsistent with batch.py" — fixed: exposed as max_concurrency: int = 4 keyword parameter via MAX_CONCURRENT_STORAGE_TRANSFERS constant
  • [RESOLVED] transfer.py:252 — prior finding "import asyncio inside function body" — fixed: moved to module top-level (L20)
  • [RESOLVED] transfer.py:271 — prior finding "asyncio.gather without return_exceptions=True" — fixed: uses return_exceptions=True with explicit error collection and re-raise
  • [RESOLVED] transfer.py — prior finding "No tests for concurrent upload behavior" — fixed: 4 unit tests and 4 integration tests cover the asyncio.gather path
  • [NEW] — None
  • [REGRESSION] — None

Findings by File

No findings.

Strengths

  • Clean Semaphore + asyncio.gather(return_exceptions=True) pattern consistent with batch.py — one established idiom across the SDK for bounded concurrent I/O
  • Thorough test coverage: concurrent completion, partial skip accuracy, error propagation (monkeypatch), max_concurrency parameter, nested directory roundtrip, and integration-level verification with a real local store
  • max_concurrency parameter name and default value (4) match batch.py:upload_prefix and batch.py:download_prefix — consistent DX for connector developers
  • Error handling is explicit: errors collected from gather results, first one re-raised — no silent partial failures
  • New MAX_CONCURRENT_STORAGE_TRANSFERS constant follows SDK conventions (ATLAN_ env var prefix, UPPER_SNAKE_CASE, configurable via environment)

@Aryamanz29
Copy link
Copy Markdown
Member

v3 relevance check: ✅ Still relevant — parallelize upload in transfer.upload() is a v3 storage optimization. Please rebase on latest main (several PRs merged recently).

@cmgrote cmgrote merged commit 0446357 into main Apr 22, 2026
36 checks passed
@cmgrote cmgrote deleted the BLDX-1089 branch April 22, 2026 15:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Autonomous SDK Evolution PRs from autonomous SDK evolution pipeline

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants