Skip to content

OCSDV-453 (PR-E): carry per-parameter filters into demand execution data syncs - #41

Merged
rpmcginty merged 3 commits into
mainfrom
feature/OCSDV-453-demand-execution-sync-filters
Aug 21, 2026
Merged

rpmcginty merged 3 commits into
mainfrom
feature/OCSDV-453-demand-execution-sync-filters

Conversation

@rpmcginty

@rpmcginty rpmcginty commented Aug 12, 2026 •

Copy link
Copy Markdown
Collaborator

OCSDV-453 — PR-E, the demand-execution half (Release R3).

Stacked on #40 (PR-D). Base is feature/OCSDV-452-prepare-batch-filters

Problem

A filter set on a demand execution param died before reaching the sync layer. Both parameter-rewrite functions rebuilt their resolvable from scratch, keeping only local and remote — so anything else was dropped at the rewrite hop.

Changes

All in handlers/demand/context_manager.py:

  1. update_demand_execution_parameter_inputs — preserve include/exclude instead of rebuilding Resolvable(local=, remote=) from scratch. Only the local path is being rewritten.
  2. pre_execution_data_sync_requests — pass filter_config, and set delete=False. The input destination is a cross-execution cache and delete is the caller's gate on the destructive filters + mirroring interaction OCSDV-452 deliberately left open; this is the caller opting out.
  3. post_execution_data_sync_requests — pass filter_config through.
  4. Documented on the output path that output filtering means "do not upload", NOT "keep locally": retain_source_data=False, sync_local_to_s3 ends with remove_path(source_path), and cleanup_working_dir defaults to True, so excluded outputs leave EFS regardless.

Plus one change outside the stated scope, called out for review: update_demand_execution_parameter_outputs had the identical drop-on-rebuild bug. Item 3 is a no-op without fixing it — the output params reaching post_execution_data_sync_requests still carried no filters, so filter_config was None end to end. Caught by a failing test, not by inspection.

Known gap, documented not fixed

The shared-input cache key is sha256(remote_value) and does not vary with filters, so two executions filtering the same remote differently share one EFS directory — and with delete=False the contents accumulate as a union. This is the deferred gap OCSDV-452 records; exposure is limited because isolate_inputs defaults to True. I corrected an inaccurate docstring line that would have told readers filters guarantee only the filtered subset is present at the input path, and noted the real behavior instead.

Testing

make format && make lint && make test — 168 passed, ruff and mypy clean.

New coverage in demand/test_context_manager.py: propagation into both pre- and post-execution requests, preservation across both isolate_inputs branches, unfiltered control cases on both paths, delete=False asserted on filtered and unfiltered input requests, and delete=True asserted on filtered output requests.

Mutation-checked: removing delete=False from inputs fails 3 tests; flipping outputs to delete=False fails 1; reverting either filter-preservation fix fails the propagation tests.

Not done here

The ticket's DoD also lists a DemandExecution round-trip test and the get_execution_hash stability regression. Those guard PR-B's serializer and hash changes and belong in the core repo — they are not exercisable from this repo, where sanitize_serialized_params is an upstream dependency. Worth confirming they landed in PR-B before R1 ships.

🤖 Generated with Claude Code


End-to-end verification (dev)

This PR contributes the propagation of per-parameter filters into the pre- and post-execution data syncs, plus the EFS volume-name fix that unblocked these runs, to the behavior verified below.

Four DemandExecutions were run against the deployed dev-de environment (account 051791135335, us-west-2) using a shared 12-object / 541,610-byte S3 fixture. All four Step Functions executions reached SUCCEEDED, and in every case the set of objects that landed in S3 matches the declared include/exclude filters exactly — no missing matches and no leaked non-matches.

Because a single demand execution exercises the whole chain — the data-sync request/filter model in aibs-informatics-core, the S3 transfer and filtering implementation in aibs-informatics-aws-utils, and the Batch/EFS execution wiring in aibs-informatics-aws-lambda — this same evidence block is attached to every PR in the set rather than split across them.

Related PRs

These five PRs are one change set and are intended to merge in release order (R1 first, then R2, then R3), since each release depends on the one before it.

PR Repository / number Release Link
PR-A aibs-informatics-core#54 R1 (core) AllenInstitute/aibs-informatics-core#54
PR-B aibs-informatics-core#55 R1 (core) AllenInstitute/aibs-informatics-core#55
PR-C aibs-informatics-aws-utils#47 R2 (aws-utils) AllenInstitute/aibs-informatics-aws-utils#47
PR-D aibs-informatics-aws-lambda#40 R3 (aws-lambda) #40
PR-E aibs-informatics-aws-lambda#41 R3 (aws-lambda) #41

Fixture

s3://dev-de-core-us-west-2-051791135335/test_data/ocsdv-452-filters/input/ — 12 objects, 541,610 bytes total (aws s3 ls --recursive --summarize reports Total Objects: 12 / Total Size: 541610). The fixture now lives in the environment's own bucket, having been moved off an orphaned bucket that no CloudFormation stack owns, so inputs and outputs now share a single bucket:

Key Bytes
README.md 130
metadata.json 214
sampleA/aligned.bam 262,140
sampleA/aligned.bam.bai 4,064
sampleA/qc_summary.txt 463
sampleA/reads_R1.fastq 1,983
sampleA/reads_R2.fastq 1,983
sampleB/aligned.bam 262,140
sampleB/aligned.bam.bai 4,064
sampleB/qc_summary.txt 463
sampleB/reads_R1.fastq 1,983
sampleB/reads_R2.fastq 1,983

Each case runs the same container command: list ${INPUT_DATA}, cp -R ${INPUT_DATA}/. ${OUTPUT_DATA}/, list ${OUTPUT_DATA}. The container itself does no filtering, so any difference between the fixture and the uploaded set is attributable to the data-sync filters.

Results

Counts are objects. "In" is the fixture, "localized" is what survived the input filter and was present under ${INPUT_DATA} in the container, "uploaded" is what was observed in S3 under the case prefix.

Case Input filter Output filter In → localized → uploaded Uploaded bytes Result
tc1-input-exclude-bam exclude: [".*\\.bam", ".*\\.bam\\.bai"] none 12 → 8 → 8 9,202 PASS
tc2-input-include-fastq include: [".*\\.fastq"] none 12 → 4 → 4 7,932 PASS
tc3-output-exclude-fastq none exclude: [".*\\.fastq"] 12 → 12 → 8 533,678 PASS
tc4-both-ends-filtered include: ["sampleA/.*"] include: [".*\\.txt"] 12 → 5 → 1 463 PASS

Across the four cases: 21 objects, 551,275 bytes written under s3://dev-de-core-us-west-2-051791135335/test_results/ocsdv-452-filters/.

Two independent confirmations worth noting:

  • Byte arithmetic closes. tc1: 9,202 uploaded + 532,408 excluded (2 × (262,140 + 4,064)) = 541,610 = the fixture total. tc3: 541,610 − 533,678 = 7,932 = 4 × 1,983, exactly the four .fastq files and nothing else. Uploaded file sizes are byte-identical to their sources, so nothing was truncated in transit.
  • tc3 isolates the filter to the upload step. The Batch container log for tc3 (jobId 7f83fd65-6e9e-4817-a48d-e6e796b1cedf) prints all 12 files under --- localized input (post input-filter) --- and all 12 again under --- staged for upload (pre output-filter) ---, yet only 8 objects reached S3. That rules out the output filter being misapplied at localization time, which an object count alone could not.

Note on attribution for tc1 and tc2: because the container copies ${INPUT_DATA} wholesale into ${OUTPUT_DATA}, the final S3 state for an input-side filter is indistinguishable from an equivalent output-side filter. For those two cases the input-side attribution rests on the payload (filters declared on input_data, output_data bare) plus the container's find output, not on the S3 listing alone. tc3 and tc4 are unambiguous.

Directory structure is preserved end to end in all cases — the sampleA/ and sampleB/ prefixes survive the round trip, so patterns are matched against the same relative path that is used to construct the destination key.

tc1-input-exclude-bam — input-side exclude, 8 objects uploaded (9,202 bytes)

Uploaded (relative to s3://dev-de-core-us-west-2-051791135335/test_results/ocsdv-452-filters/tc1-input-exclude-bam/; aws s3 ls --recursive --summarize reports Total Objects: 8 / Total Size: 9202):

README.md
metadata.json
sampleA/qc_summary.txt
sampleA/reads_R1.fastq
sampleA/reads_R2.fastq
sampleB/qc_summary.txt
sampleB/reads_R1.fastq
sampleB/reads_R2.fastq

Correctly not uploaded — these four match one of the two input-side exclude patterns (.*\.bam, .*\.bam\.bai), so they were never localized and therefore never staged:

Key Excluded by
sampleA/aligned.bam .*\.bam
sampleA/aligned.bam.bai .*\.bam\.bai
sampleB/aligned.bam .*\.bam
sampleB/aligned.bam.bai .*\.bam\.bai

Every excluded object matches a declared pattern, and every object matching a declared pattern is excluded. Step Functions execution d247218f-34e6-47cc-ad4b-a175180b2e45 on arn:aws:states:us-west-2:051791135335:stateMachine:dev-de-demand-execution, SUCCEEDED, 2026-08-12 14:46:07 → 14:51:02 (-07:00); object timestamps (14:50:37) fall inside that window.

{
  "execution_type": "ocsdv452",
  "execution_id": "ocsdv452-tc1-input-exclude-bam",
  "execution_image": "public.ecr.aws/ubuntu/ubuntu:22.04",
  "execution_parameters": {
    "command": [
      "set",
      "-eu",
      "&&",
      "echo",
      "'=== tc1-input-exclude-bam ==='",
      "&&",
      "echo",
      "'--- localized input (post input-filter) ---'",
      "&&",
      "find",
      "${INPUT_DATA}",
      "-type",
      "f",
      "|",
      "sort",
      "&&",
      "mkdir",
      "-p",
      "${OUTPUT_DATA}",
      "&&",
      "cp",
      "-R",
      "${INPUT_DATA}/.",
      "${OUTPUT_DATA}/",
      "&&",
      "echo",
      "'--- staged for upload (pre output-filter) ---'",
      "&&",
      "find",
      "${OUTPUT_DATA}",
      "-type",
      "f",
      "|",
      "sort"
    ],
    "params": {
      "input_data": {
        "remote": "s3://dev-de-core-us-west-2-051791135335/test_data/ocsdv-452-filters/input/",
        "local": "input_data",
        "exclude": [
          ".*\\.bam",
          ".*\\.bam\\.bai"
        ]
      },
      "output_data": {
        "local": "output_data",
        "remote": "s3://dev-de-core-us-west-2-051791135335/test_results/ocsdv-452-filters/tc1-input-exclude-bam/"
      }
    },
    "inputs": [
      "input_data"
    ],
    "outputs": [
      "output_data"
    ],
    "output_s3_prefix": "s3://dev-de-core-us-west-2-051791135335/test_results/ocsdv-452-filters/tc1-input-exclude-bam/"
  },
  "execution_platform": {
    "aws_batch": {
      "job_queue_name": "dev-de-demand-on-demand-job-queue"
    }
  },
  "resource_requirements": {
    "vcpus": 1,
    "memory": 2048
  }
}
tc2-input-include-fastq — input-side include, 4 objects uploaded (7,932 bytes)

Uploaded (Total Objects: 4 / Total Size: 7932; 4 × 1,983 = 7,932, matching source sizes exactly):

sampleA/reads_R1.fastq
sampleA/reads_R2.fastq
sampleB/reads_R1.fastq
sampleB/reads_R2.fastq

Correctly not uploaded — the input side declares a single include pattern .*\.fastq and no exclude, so any key that does not match is not localized. None of these eight keys end in .fastq:

README.md
metadata.json
sampleA/aligned.bam
sampleA/aligned.bam.bai
sampleA/qc_summary.txt
sampleB/aligned.bam
sampleB/aligned.bam.bai
sampleB/qc_summary.txt

All four fixture keys that do match .*\.fastq are present, so membership is correct in both directions. Step Functions execution arn:aws:states:us-west-2:051791135335:execution:dev-de-demand-execution:ocsdv452-tc2-input-include-fastq, SUCCEEDED, 2026-08-12 15:37:27 → 15:42:09 (-07:00).

{
  "execution_type": "ocsdv452",
  "execution_id": "ocsdv452-tc2-input-include-fastq",
  "execution_image": "public.ecr.aws/ubuntu/ubuntu:22.04",
  "execution_parameters": {
    "command": [
      "set",
      "-eu",
      "&&",
      "echo",
      "'=== tc2-input-include-fastq ==='",
      "&&",
      "echo",
      "'--- localized input (post input-filter) ---'",
      "&&",
      "find",
      "${INPUT_DATA}",
      "-type",
      "f",
      "|",
      "sort",
      "&&",
      "mkdir",
      "-p",
      "${OUTPUT_DATA}",
      "&&",
      "cp",
      "-R",
      "${INPUT_DATA}/.",
      "${OUTPUT_DATA}/",
      "&&",
      "echo",
      "'--- staged for upload (pre output-filter) ---'",
      "&&",
      "find",
      "${OUTPUT_DATA}",
      "-type",
      "f",
      "|",
      "sort"
    ],
    "params": {
      "input_data": {
        "remote": "s3://dev-de-core-us-west-2-051791135335/test_data/ocsdv-452-filters/input/",
        "local": "input_data",
        "include": [
          ".*\\.fastq"
        ]
      },
      "output_data": {
        "local": "output_data",
        "remote": "s3://dev-de-core-us-west-2-051791135335/test_results/ocsdv-452-filters/tc2-input-include-fastq/"
      }
    },
    "inputs": [
      "input_data"
    ],
    "outputs": [
      "output_data"
    ],
    "output_s3_prefix": "s3://dev-de-core-us-west-2-051791135335/test_results/ocsdv-452-filters/tc2-input-include-fastq/"
  },
  "execution_platform": {
    "aws_batch": {
      "job_queue_name": "dev-de-demand-on-demand-job-queue"
    }
  },
  "resource_requirements": {
    "vcpus": 1,
    "memory": 2048
  }
}
tc3-output-exclude-fastq — output-side exclude, 8 objects uploaded (533,678 bytes)

Uploaded (Total Objects: 8 / Total Size: 533678):

README.md
metadata.json
sampleA/aligned.bam
sampleA/aligned.bam.bai
sampleA/qc_summary.txt
sampleB/aligned.bam
sampleB/aligned.bam.bai
sampleB/qc_summary.txt

Correctly not uploaded — the input side is unfiltered, so all 12 objects were localized and all 12 were staged under ${OUTPUT_DATA}. These four were dropped at upload because they match the output-side exclude .*\.fastq:

sampleA/reads_R1.fastq
sampleA/reads_R2.fastq
sampleB/reads_R1.fastq
sampleB/reads_R2.fastq

This is the case that distinguishes output-side filtering from input-side filtering by observation rather than by declaration: the container log lists all 12 files at both checkpoints, yet only 8 objects reached S3. Step Functions execution ocsdv452-tc3-output-exclude-fastq, SUCCEEDED, 2026-08-12 15:38:21 → 15:41:58 (-07:00); Batch job 7f83fd65-6e9e-4817-a48d-e6e796b1cedf on dev-de-demand-on-demand-job-queue, SUCCEEDED.

{
  "execution_type": "ocsdv452",
  "execution_id": "ocsdv452-tc3-output-exclude-fastq",
  "execution_image": "public.ecr.aws/ubuntu/ubuntu:22.04",
  "execution_parameters": {
    "command": [
      "set",
      "-eu",
      "&&",
      "echo",
      "'=== tc3-output-exclude-fastq ==='",
      "&&",
      "echo",
      "'--- localized input (post input-filter) ---'",
      "&&",
      "find",
      "${INPUT_DATA}",
      "-type",
      "f",
      "|",
      "sort",
      "&&",
      "mkdir",
      "-p",
      "${OUTPUT_DATA}",
      "&&",
      "cp",
      "-R",
      "${INPUT_DATA}/.",
      "${OUTPUT_DATA}/",
      "&&",
      "echo",
      "'--- staged for upload (pre output-filter) ---'",
      "&&",
      "find",
      "${OUTPUT_DATA}",
      "-type",
      "f",
      "|",
      "sort"
    ],
    "params": {
      "input_data": {
        "remote": "s3://dev-de-core-us-west-2-051791135335/test_data/ocsdv-452-filters/input/",
        "local": "input_data"
      },
      "output_data": {
        "local": "output_data",
        "remote": "s3://dev-de-core-us-west-2-051791135335/test_results/ocsdv-452-filters/tc3-output-exclude-fastq/",
        "exclude": [
          ".*\\.fastq"
        ]
      }
    },
    "inputs": [
      "input_data"
    ],
    "outputs": [
      "output_data"
    ],
    "output_s3_prefix": "s3://dev-de-core-us-west-2-051791135335/test_results/ocsdv-452-filters/tc3-output-exclude-fastq/"
  },
  "execution_platform": {
    "aws_batch": {
      "job_queue_name": "dev-de-demand-on-demand-job-queue"
    }
  },
  "resource_requirements": {
    "vcpus": 1,
    "memory": 2048
  }
}
tc4-both-ends-filtered — include on both ends, 1 object uploaded (463 bytes)

Uploaded (verbatim from aws s3 ls ... --recursive):

2026-08-12 15:41:52        463 test_results/ocsdv-452-filters/tc4-both-ends-filtered/sampleA/qc_summary.txt

Correctly not uploaded — two stages, in order:

  1. Input include sampleA/.* admits only the 5 objects under sampleA/. Dropped at localization (7 objects): README.md and metadata.json (top level, no sampleA/ prefix), and all 5 sampleB/* objects.
  2. Output include .*\.txt admits only qc_summary.txt of the 5 that were staged. Dropped at upload (4 objects): sampleA/aligned.bam, sampleA/aligned.bam.bai, sampleA/reads_R1.fastq, sampleA/reads_R2.fastq.

Each of the 11 absent files is absent for a reason one of the two filters calls for, and the single present file is the only one both filters admit. The sampleA/ path component survives into the destination key, confirming the include pattern is matched against the same relative path that is preserved on upload. Step Functions execution arn:aws:states:us-west-2:051791135335:execution:dev-de-demand-execution:ocsdv452-tc4-both-ends-filtered, SUCCEEDED, 2026-08-12 15:38:39 → 15:42:23 (-07:00); the object's LastModified falls inside that window.

{
  "execution_type": "ocsdv452",
  "execution_id": "ocsdv452-tc4-both-ends-filtered",
  "execution_image": "public.ecr.aws/ubuntu/ubuntu:22.04",
  "execution_parameters": {
    "command": [
      "set",
      "-eu",
      "&&",
      "echo",
      "'=== tc4-both-ends-filtered ==='",
      "&&",
      "echo",
      "'--- localized input (post input-filter) ---'",
      "&&",
      "find",
      "${INPUT_DATA}",
      "-type",
      "f",
      "|",
      "sort",
      "&&",
      "mkdir",
      "-p",
      "${OUTPUT_DATA}",
      "&&",
      "cp",
      "-R",
      "${INPUT_DATA}/.",
      "${OUTPUT_DATA}/",
      "&&",
      "echo",
      "'--- staged for upload (pre output-filter) ---'",
      "&&",
      "find",
      "${OUTPUT_DATA}",
      "-type",
      "f",
      "|",
      "sort"
    ],
    "params": {
      "input_data": {
        "remote": "s3://dev-de-core-us-west-2-051791135335/test_data/ocsdv-452-filters/input/",
        "local": "input_data",
        "include": [
          "sampleA/.*"
        ]
      },
      "output_data": {
        "local": "output_data",
        "remote": "s3://dev-de-core-us-west-2-051791135335/test_results/ocsdv-452-filters/tc4-both-ends-filtered/",
        "include": [
          ".*\\.txt"
        ]
      }
    },
    "inputs": [
      "input_data"
    ],
    "outputs": [
      "output_data"
    ],
    "output_s3_prefix": "s3://dev-de-core-us-west-2-051791135335/test_results/ocsdv-452-filters/tc4-both-ends-filtered/"
  },
  "execution_platform": {
    "aws_batch": {
      "job_queue_name": "dev-de-demand-on-demand-job-queue"
    }
  },
  "resource_requirements": {
    "vcpus": 1,
    "memory": 2048
  }
}

Note on the first attempt

The first attempt at these four runs failed before the container started, with an efs-utils error reporting File name too long. The cause was the generated ECS volume name: it made the mount helper's CA database path exceed OpenSSL's 256-byte BSIZE guard by 4 characters. It was worked around for the run above by shortening execution_type to ocsdv452. PR-E addresses it properly by condensing the generated volume name from 50 characters to 16, which puts the path back under the limit without relying on short caller-supplied names.

Scope

This is a dev-environment functional check of four small cases against a 12-object fixture. It demonstrates that the filter semantics behave as declared on both the input and output sides of a real demand execution; it is not a performance or scale test, and it does not cover large transfers, multipart uploads, or concurrent executions. Verification was read-only (s3 ls, s3api list-objects-v2, sts get-caller-identity, stepfunctions list/describe-execution, batch list/describe-jobs, logs get-log-events).

Addendum: container logs confirm the input filter runs on the download, not just the upload

The S3 result alone cannot fully separate input-side from output-side filtering for tc1 and tc2, because the container copies its input directory wholesale into the output directory — an unfiltered download plus an equivalent output-side exclude would produce the same uploaded set. tc3 and tc4 disambiguate by construction (tc3 uploads the .bam files that tc1 drops, from the same fixture), but it is worth closing directly.

The container prints its localized input before copying. From /aws/batch/job, stream
dev-de-ocsdv452-43c8f938.../bc357a293a09432681668be032cb1cb3 (tc1-input-exclude-bam):

=== tc1-input-exclude-bam ===
--- localized input (post input-filter) ---
.../input_data/README.md
.../input_data/metadata.json
.../input_data/sampleA/qc_summary.txt
.../input_data/sampleA/reads_R1.fastq
.../input_data/sampleA/reads_R2.fastq
.../input_data/sampleB/qc_summary.txt
.../input_data/sampleB/reads_R1.fastq
.../input_data/sampleB/reads_R2.fastq

Eight files, not twelve. The four .bam/.bam.bai objects never reached the container's EFS
working directory — the filter was applied during the download, so the 524 KB of alignment data
was never transferred. That is the behavior the feature exists to provide, and it is what
distinguishes filtering from post-hoc pruning.

Only this one stream was still retained in CloudWatch when the logs were pulled, so this direct
confirmation covers tc1; the other three cases rest on the S3 results and their Step Functions
executions above.

Copilot AI left a comment

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.

Pull request overview

Propagates per-parameter filters through demand execution data syncs and prevents oversized EFS mount paths.

Changes:

  • Preserves input/output filters and forwards them to sync requests.
  • Adds explicit input/output deletion semantics and tests.
  • Introduces compact, validated EFS volume naming.

Reviewed changes

Copilot reviewed 5 out of 6 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
uv.lock Updates the pinned core dependency commit.
src/.../context_manager.py Propagates filters and integrates EFS naming checks.
src/.../naming.py Adds compact volume naming and path-budget validation.
test/.../test_context_manager.py Tests filter propagation and volume naming.
test/.../test_naming.py Tests naming and budget helpers.
test/.../test_scaffolding.py Updates expected generated volume names.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread src/aibs_informatics_aws_lambda/handlers/demand/naming.py Outdated
temporary_request_payload_path=temporary_request_payload_path,
size_only=self.configuration.output_data_sync_configuration.size_only,
force=self.configuration.output_data_sync_configuration.force,
filter_config=param.filter_config,
Comment thread test/aibs_informatics_aws_lambda/handlers/demand/test_context_manager.py Outdated
@codecov

codecov Bot commented Aug 14, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 86.66667% with 4 lines in your changes missing coverage. Please review.
✅ Project coverage is 90.81%. Comparing base (d1f51f6) to head (d78b8c7).
⚠️ Report is 4 commits behind head on main.

Files with missing lines Patch % Lines
...tics_aws_lambda/handlers/demand/context_manager.py 75.00% 1 Missing and 1 partial ⚠️
...s_informatics_aws_lambda/handlers/demand/naming.py 90.90% 1 Missing and 1 partial ⚠️
Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##             main      #41      +/-   ##
==========================================
- Coverage   90.92%   90.81%   -0.11%     
==========================================
  Files          26       27       +1     
  Lines        1465     1491      +26     
  Branches      141      145       +4     
==========================================
+ Hits         1332     1354      +22     
- Misses         96       98       +2     
- Partials       37       39       +2     
Files with missing lines Coverage Δ
...tics_aws_lambda/handlers/demand/context_manager.py 95.83% <75.00%> (-0.78%) ⬇️
...s_informatics_aws_lambda/handlers/demand/naming.py 90.90% <90.90%> (ø)
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@rpmcginty
rpmcginty requested a review from njmei August 20, 2026 03:50
@rpmcginty
rpmcginty force-pushed the feature/OCSDV-453-demand-execution-sync-filters branch from c635ed4 to ea41d29 Compare August 20, 2026 03:54
Comment thread src/aibs_informatics_aws_lambda/handlers/demand/context_manager.py Outdated
new_resolvable = Resolvable(local=local.as_posix(), remote=param.remote_value)
# Carry the param's include/exclude forward. Only the local path is being rewritten
# here, so rebuilding the Resolvable without the filters would silently drop them
# before they ever reach the pre-execution data sync requests.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Nit: I don't find this comment necessary, the docstrings above already stated the reason

Comment on lines +629 to +631
# Carry the param's include/exclude forward, for the same reason as the input rewrite:
# dropping them here would leave post_execution_data_sync_requests with nothing to
# apply, since it reads the filters back off these params.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Nit: Same thing here, docstring immediate above already spelled out the reasoning

Comment thread src/aibs_informatics_aws_lambda/handlers/demand/naming.py Outdated
@@ -799,11 +864,21 @@ def generate_batch_job_builder( # noqa: C901
vol_configurations.append(BatchEFSConfiguration(tmp_mount_point, read_only=False))
logger.info("Constructing BatchJobBuilder instance...")
assert demand_execution.execution_platform.aws_batch is not None

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Nit: this isn't in the diff hence a nit, assert here 1) poses a risk of being bypassed when python runs in -o, 2) returns an AssertionError. Suggest we use if conditions on the implementation side and raise ValueError or TypeError

ValueError: If the longest volume name combined with the job definition name
exceeds the budget.
"""
if not volume_names:

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

vol_configurations seems to always contain scratch_mount_point and shared_mount_point. volume_names is downstream of that, so it doesn't seem like volume_names can ever be None.

This if not volume_names: condition looks to be unreachable or should be unreachable, suggest we remove it or raise an error instead of returning

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

No volumes means no EFS mount, which is a valid scenario (albeit not one that the demand execution expects). I would push back on the claim that this should be unreachable.

Comment on lines +324 to +327
# The volume name leads with the mount path basename and ends in a hash over the
# file system / access point / mount path. The hash cannot be hardcoded here because
# moto generates the ids, so assert the shape and the invariants instead. The name
# must stay short: it ends up inside an efs-utils TLS state path that openssl caps

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Nit: I think this is an unnecessary comment in test, this was well-explained on the implementation side

@schristinelin schristinelin left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Overall looks good, minor question about some conditions. Can we prompt AI to clean up the in-line comments so they don't repeat what's already stated in the docstrings/module-level comments? I'm personally finding them to be very distracting when trying to read the codes, and a bit concerned about verbosity as the repo grows and more comments get added

@rpmcginty
rpmcginty force-pushed the feature/OCSDV-453-demand-execution-sync-filters branch 2 times, most recently from ab444a0 to 891a6ae Compare August 20, 2026 20:39

@pbishwakarma pbishwakarma left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

lgtm

Base automatically changed from feature/OCSDV-452-prepare-batch-filters to main August 21, 2026 03:27
rpmcginty and others added 3 commits August 20, 2026 20:27
Filters set on a demand execution param previously died before reaching the
data sync layer: both parameter-rewrite functions rebuilt their Resolvable /
Uploadable from scratch, keeping only local and remote.

- update_demand_execution_parameter_inputs: preserve include/exclude rather
  than rebuilding Resolvable(local=, remote=) from scratch. Only the local
  path is being rewritten.
- update_demand_execution_parameter_outputs: same fix. Not in the original
  scope, but post_execution_data_sync_requests reads filters back off these
  params, so wiring the output path is a no-op without it.
- pre_execution_data_sync_requests: pass filter_config, and set delete=False.
  The input destination is a cross-execution cache, and `delete` is the
  caller's gate on the destructive filters + mirroring interaction that
  OCSDV-452 deliberately left open.
- post_execution_data_sync_requests: pass filter_config through. Outputs keep
  delete at its default, so the destination is mirrored to the filtered
  subset; documented as a Warning rather than left implicit.

Documents on the output path that output filtering means "do not upload", NOT
"keep locally": retain_source_data=False, sync_local_to_s3 ends with
remove_path, and cleanup_working_dir defaults to True, so excluded outputs
leave EFS regardless.

Also notes on the input path that filters bound what is transferred, not what
is present: the shared-cache key is sha256(remote_value) and does not vary
with filters, a known deferred gap from OCSDV-452.

Tests cover propagation into both pre- and post-execution requests, both
isolate_inputs branches, unfiltered control cases, and pin both delete
decisions explicitly.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Demand execution batch jobs failed before the container ran:

  CannotStartContainerError: ... Failed to create self-signed client-side
  certificate ... File name too long

Not a NAME_MAX/PATH_MAX limit. The volume name flows through a chain of
generated names into an OpenSSL buffer:

  batch job definition name -> ECS task family
    -> docker volume name    ecs-<family>-<rev>-<volume>-<20 hex>
    -> efs-utils TLS state dir
    -> openssl CA database   /var/run/efs/<state dir>/database/index.txt

openssl guards that path with a hard-coded 256 byte stack buffer (BSIZE in
apps/lib/apps.c), capping the database path at 246 chars and, back up the
chain, capping family + revision + volume name at 139. A 50 char volume name
("dev-de-core-opt-fsap-0acb9f234e1b57786-scratch-vol") alongside a 92 char job
definition name came to 143 -- over by 4.

Adds handlers/demand/naming.py, holding the Batch/EFS specific arithmetic:

  * build_efs_volume_name(...) -- leads with the mount path basename
    (scratch/shared/tmp) and takes uniqueness from a hash over the file system,
    access point and mount path rather than spelling them out. 50 -> 16 chars.
  * check_ecs_volume_component_budget(...) -- raises while the job definition is
    being built rather than minutes later at container start, where the error
    names nothing responsible.

The generic string shortening primitive it builds on, condense_str, lives in
aibs-informatics-core (utils.tools.strtools) since nothing about it is
Batch or EFS specific.

Capping execution_type would also have cleared the budget, but pushes an
OpenSSL buffer size onto callers; a descriptive execution type should not break
EFS mounting. A 40 char execution_type now fits at 129/139 where a 20 char one
previously failed at 143.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Comment cleanup (@schristinelin, @pbishwakarma) -- the recurring note was that
inline comments restate what the code or the docstring above already says:

- context_manager.py: condense the volume-name comment from five lines to two;
  drop the two "carry the param's include/exclude forward" comments, which
  repeat the docstrings directly above them.
- test_context_manager.py: cut the volume-name comment to the one fact that
  isn't obvious (moto generates the ids, so the hash can't be a literal). It
  also pointed at aibs_informatics_aws_lambda.common.naming, which does not
  exist (@copilot).
- naming.py: move the budget derivation out of the ECS_VOLUME_COMPONENT_BUDGET
  comment and into the module docstring, where the chain it belongs to is
  already described.

Correctness:

- naming.py: restrict the volume-name sanitizer to ASCII (@copilot). str.isalnum
  is Unicode-aware, so a basename like "données" passed sanitization and
  produced a name ECS rejects. Regression test added.
- context_manager.py: replace the aws_batch `assert` with an explicit
  ValueError (@schristinelin) -- asserts are stripped under `python -O` and
  AssertionError is the wrong type for a rejected input.

Also drops the Sphinx :func:/:data: roles from naming.py docstrings. Docs here
render with mkdocstrings (google style), which does not process reST roles and
emits the prefix literally -- same issue @njmei raised in aws-utils.

Not changed: the `if not volume_names: return` guard in
check_ecs_volume_component_budget (@schristinelin). It is unreachable from the
current call site, but it is not merely defensive -- no volumes means no EFS
mount, so nothing derives an efs-utils path and the budget does not apply even
to an over-long job definition name. Removing it broke
test__no_volumes_is_not_an_error, which pins exactly that. Added a comment
saying so instead.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@rpmcginty
rpmcginty force-pushed the feature/OCSDV-453-demand-execution-sync-filters branch from 891a6ae to d78b8c7 Compare August 21, 2026 03:27
@rpmcginty
rpmcginty merged commit 07ba1ff into main Aug 21, 2026
7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants