Skip to content

feat(incremental): parallelize S3 prefix download [BLDX-1088]#1503

Open
vaibhavatlan wants to merge 12 commits intomainfrom
BLDX-1088
Open

feat(incremental): parallelize S3 prefix download [BLDX-1088]#1503
vaibhavatlan wants to merge 12 commits intomainfrom
BLDX-1088

Conversation

@vaibhavatlan
Copy link
Copy Markdown
Collaborator

What was found

Rule: PERF-010 | File: application_sdk/common/incremental/helpers.py:241 | Severity: CRITICAL

Sequential await download_file() in a for loop. For large prefixes (hundreds of files), latency scales linearly.

What was fixed

Replaced sequential loop with asyncio.gather + asyncio.Semaphore(20) for bounded concurrent downloads. Matches the pattern already used in storage/batch.py download_prefix.

Linear

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

…rency [BLDX-1088]

download_s3_prefix_with_structure was downloading files sequentially.
For large prefixes with hundreds of files, this multiplied latency
linearly. Now uses asyncio.gather with a semaphore (max 20 concurrent)
matching the pattern used in storage/batch.py.
@vaibhavatlan vaibhavatlan requested a review from atlan-ci as a code owner April 21, 2026 14:24
@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                                     |    12 |    1 |    11 |    92% |
| 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                                               |    12 |    2 |    10 |    83% |
| 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                                              |    15 |    0 |    15 |   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                                     |    37 |    1 |    36 |    97% |
| 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                                               |    35 |   31 |     4 |    11% |
| 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                                                                             |  6276 | 2852 |  3424 |  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-22T13:02:35.159102555Z . 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-22T13:02:44.183409568Z . 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
14816 10466 71% 0% 🟢

New Files

No new covered files...

Modified Files

No covered modified files...

updated for commit: 5a2a892 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/1503

@vaibhavatlan vaibhavatlan changed the title perf(incremental): parallelize S3 prefix download [BLDX-1088] feat(incremental): parallelize S3 prefix download [BLDX-1088] 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.

@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 22, 2026

SDK Review: PR #1503 — feat(incremental): parallelize S3 prefix download [BLDX-1088]

Verdict: NEEDS FIXES

Good performance optimization: replaces sequential await in a for-loop with asyncio.gather + bounded Semaphore(20), matching the existing pattern in storage/batch.py. Two issues to resolve before merge.

Findings by File

application_sdk/common/incremental/helpers.py

  • Important [QUAL] L240 — import asyncio is inside the function body. Stdlib imports must be at module top level per project coding standards. asyncio is not a heavy/optional dependency and no circular-import concern exists here. Fix: move import asyncio to the top-level imports block (after import shutil, before the from imports).
  • Important [TEST] — The concurrent download behavior introduced by this PR has no direct test coverage. The function is only exercised via mocks in test_state_writer.py. The shift from sequential to concurrent changes error propagation semantics (e.g., partial downloads on failure) and should be covered by at least one unit test with a mocked download_file. Fix: add a test in tests/unit/common/incremental/ that exercises download_s3_prefix_with_structure directly with a mocked download_file, verifying all files are downloaded and concurrency is bounded.

Strengths

  • Correct use of asyncio.Semaphore for bounded concurrency — prevents connection exhaustion on large prefixes.
  • mkdir is correctly placed outside the semaphore so directory creation doesn't consume a concurrency slot.
  • Matches the established download_prefix pattern in storage/batch.py, maintaining codebase consistency.

Comment thread application_sdk/common/incremental/helpers.py Outdated
Comment thread application_sdk/common/incremental/helpers.py
vaibhavatlan and others added 2 commits April 22, 2026 14:43
- Move `import asyncio` to top-level module imports
- Add unit tests for download_s3_prefix_with_structure covering:
  - All files downloaded to correct local paths
  - Semaphore bounds concurrency to 20
  - Empty file list produces no downloads
  - Files without matching prefix used as-is for relative path

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 #1503 — feat(incremental): parallelize S3 prefix download [BLDX-1088]

Verdict: READY TO MERGE

Both prior findings are resolved: import asyncio is now at module top level, and four well-structured tests cover the concurrent download behavior (path preservation, semaphore bound, empty list, fallback path). Two minor nits remain but do not block merge.

Delta from prior review

  • [RESOLVED] application_sdk/common/incremental/helpers.py:L240 — prior finding "import asyncio inside function body should be at module top level" — fixed; import is now at line 11
  • [RESOLVED] — prior finding "No test coverage for concurrent download behavior" — fixed; TestDownloadS3PrefixWithStructure adds 4 tests covering correctness, concurrency bound, empty input, and fallback path
  • [NEW] tests/unit/common/incremental/test_helpers.py:L262,299,335,353@pytest.mark.asyncio is redundant with asyncio_mode = "auto" (Minor)
  • [NEW] tests/unit/common/incremental/test_helpers.py:L303import asyncio inside test method body; should be at test-file top-level imports (Minor)

Findings by File

tests/unit/common/incremental/test_helpers.py

  • Minor [TEST] L262,299,335,353 — @pytest.mark.asyncio decorators are redundant when asyncio_mode = "auto" is configured in pyproject.toml. Fix: remove the decorators; plain async def test_... is sufficient.
  • Minor [QUAL] L303 — import asyncio inside test_concurrency_bounded_by_semaphore method body. Stdlib imports should be at the module top level. Fix: move import asyncio to the top of the test file alongside the other stdlib imports.

Strengths

  • Both prior review findings addressed cleanly
  • Excellent test for concurrency bound (_tracking_download with lock + counter) — directly verifies the semaphore behavior rather than testing implementation details
  • mkdir(parents=True, exist_ok=True) correctly placed outside the semaphore so directory creation doesn't consume a concurrency slot
  • Edge cases well covered: empty file list, non-prefix paths

Comment thread tests/unit/common/incremental/test_helpers.py Outdated
Comment thread tests/unit/common/incremental/test_helpers.py
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: previous findings resolved; no new issues introduced by the latest changes. CI passing. Branch up to date. Approved.

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.

Looks good - one minor suggestion below:

Comment thread application_sdk/common/incremental/helpers.py Outdated
Aryamanz29 and others added 3 commits April 22, 2026 16:17
…wnloads

Replaces hardcoded Semaphore(20) with configurable constant defaulting to 4,
aligning with batch.py and transfer.py conventions. Exposes max_concurrency
param for per-call override.

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 #1503 — feat(incremental): parallelize S3 prefix download [BLDX-1088]

Verdict: NEEDS FIXES

Both original blocking findings (module-level import, missing tests) are fully resolved. However, the two Minor nits from the prior review remain unfixed, and two new Minor issues were found (stale test docstring, missing param documentation), bringing the total to 4 Minor findings — crossing the 3+ Minor threshold.

Delta from prior review

  • [RESOLVED] application_sdk/common/incremental/helpers.py:L240 — prior finding "import asyncio inside function body should be at module top level" — fixed; import is now at line 11
  • [RESOLVED] — prior finding "No test coverage for concurrent download behavior" — fixed; TestDownloadS3PrefixWithStructure adds 4 tests covering correctness, concurrency bound, empty input, and fallback path
  • [STILL] tests/unit/common/incremental/test_helpers.py:L262,299,335,353@pytest.mark.asyncio decorators redundant with asyncio_mode = "auto"
  • [STILL] tests/unit/common/incremental/test_helpers.py:L302import asyncio inside test method body; should be at module top-level imports
  • [NEW] tests/unit/common/incremental/test_helpers.py:L301 — test docstring says "No more than 20 concurrent downloads" but the test asserts <= 4 (the actual default)
  • [NEW] application_sdk/common/incremental/helpers.py:L229-231max_concurrency parameter missing from Args docstring

Findings by File

tests/unit/common/incremental/test_helpers.py

  • Minor [TEST] L262,299,335,353 — @pytest.mark.asyncio decorators are redundant when asyncio_mode = "auto" is configured in pyproject.toml. Fix: remove the four decorators; plain async def test_... is sufficient.
  • Minor [QUAL] L302 — import asyncio inside test_concurrency_bounded_by_semaphore method body. Stdlib imports should be at the module top level. Fix: move import asyncio to the top of the test file alongside the other stdlib imports.
  • Minor [TEST] L301 — Test docstring says "No more than 20 concurrent downloads run at once" but the actual assertion is max_concurrent <= 4 (matching the MAX_CONCURRENT_STORAGE_TRANSFERS default of 4). Fix: update docstring to "No more than MAX_CONCURRENT_STORAGE_TRANSFERS (4) concurrent downloads run at once" or similar.

application_sdk/common/incremental/helpers.py

  • Minor [DX] L229-231 — New max_concurrency parameter is not documented in the Args section of the docstring. Fix: add max_concurrency: Maximum number of concurrent downloads (default: MAX_CONCURRENT_STORAGE_TRANSFERS) to Args.

Strengths

  • Both prior review findings addressed cleanly
  • Excellent concurrency-bound test (_tracking_download with lock + counter) — directly verifies semaphore behavior rather than testing implementation details
  • mkdir(parents=True, exist_ok=True) correctly placed outside the semaphore so directory creation doesn't consume a concurrency slot
  • Edge cases well covered: empty file list, non-prefix paths
  • The MAX_CONCURRENT_STORAGE_TRANSFERS constant is environment-configurable via ATLAN_MAX_CONCURRENT_STORAGE_TRANSFERS, following established SDK patterns

class TestDownloadS3PrefixWithStructure:
"""Tests for parallel S3 prefix download with structure preservation."""

@pytest.mark.asyncio
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Minor [TEST] — @pytest.mark.asyncio is redundant when asyncio_mode = "auto" is set in pyproject.toml. Remove the decorator; plain async def test_... is sufficient. (Same applies to L299, L335, L353.)

@pytest.mark.asyncio
async def test_concurrency_bounded_by_semaphore(self):
"""No more than 20 concurrent downloads run at once."""
import asyncio
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Minor [QUAL] — import asyncio inside test method body. Stdlib imports should be at module top level. Move this to the top of the file alongside the other stdlib imports.


@pytest.mark.asyncio
async def test_concurrency_bounded_by_semaphore(self):
"""No more than 20 concurrent downloads run at once."""
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Minor [TEST] — Docstring says "No more than 20 concurrent downloads" but the test asserts max_concurrent <= 4 (the actual MAX_CONCURRENT_STORAGE_TRANSFERS default). Update the docstring to match the real bound.

Comment thread application_sdk/common/incremental/helpers.py
@github-actions
Copy link
Copy Markdown
Contributor

📋 SDK Review complete — NEEDS FIXES. See the review comment above for findings. Address the issues and comment @sdk-review to re-review, or @sdk-review auto-complete to auto-fix.

@Aryamanz29
Copy link
Copy Markdown
Member

v3 relevance check: ✅ Still relevant — parallelize S3 prefix download is a v3 storage optimization. Please rebase on latest main.

vaibhavatlan and others added 2 commits April 22, 2026 18:27
- Remove redundant @pytest.mark.asyncio (asyncio_mode=auto)
- Move import asyncio to module top-level
- Fix stale '20' in test docstring to match default of 4
- Document max_concurrency in Args docstring

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 #1503 — feat(incremental): parallelize S3 prefix download [BLDX-1088]

Verdict: READY TO MERGE

All four findings from the prior review have been resolved. The redundant @pytest.mark.asyncio decorators are removed, import asyncio is at module top level in both files, the test docstring now correctly reflects the <= 4 bound, and the max_concurrency parameter is documented in the Args section. No new issues introduced.

Delta from prior review

  • [RESOLVED] tests/unit/common/incremental/test_helpers.py — prior finding "@pytest.mark.asyncio decorators redundant with asyncio_mode = "auto"" — decorators removed; tests use plain async def
  • [RESOLVED] tests/unit/common/incremental/test_helpers.py — prior finding "import asyncio inside test method body" — import moved to module top level (line 11)
  • [RESOLVED] tests/unit/common/incremental/test_helpers.py:L300 — prior finding "test docstring says 'No more than 20' but asserts <= 4" — docstring now reads "No more than MAX_CONCURRENT_STORAGE_TRANSFERS (4) concurrent downloads run at once"
  • [RESOLVED] application_sdk/common/incremental/helpers.py:L231 — prior finding "max_concurrency missing from Args docstring" — parameter documented in Args section

Findings by File

No findings.

Strengths

  • Clean semaphore-based concurrency control with configurable bound and sensible env-var-backed default
  • mkdir(parents=True, exist_ok=True) correctly placed outside the semaphore so directory creation doesn't consume a concurrency slot
  • Excellent concurrency-bound test (_tracking_download with lock + counter) directly verifies semaphore behavior rather than testing implementation details
  • Good test coverage: correctness, concurrency bound, empty input, and non-prefix path fallback
  • MAX_CONCURRENT_STORAGE_TRANSFERS follows established SDK constant patterns (env-var override with ATLAN_ prefix, int() conversion, placed alongside related concurrency constants)

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.

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.

4 participants