Skip to content

feat(dataset-mount): use mounted datasets in Python UDFs#6897

Open
aicam wants to merge 3 commits into
apache:mainfrom
aicam:feat/mount-B2-udf
Open

feat(dataset-mount): use mounted datasets in Python UDFs#6897
aicam wants to merge 3 commits into
apache:mainfrom
aicam:feat/mount-B2-udf

Conversation

@aicam

@aicam aicam commented Jul 25, 2026

Copy link
Copy Markdown
Contributor

What changes were proposed in this PR?

Let a Python UDF consume mounted datasets — the final step of the dataset-mounting feature (#6606), stacked on the per-computing-unit mounting PR (#6896).

Each UDF binding maps a dataset version to a Python variable that, at runtime, holds the local filesystem path of the mounted dataset, e.g. open(f"{A}/file.csv").

  • PythonUDFOpDescV2 — a list of (variableName -> dataset) bindings, validated as Python identifiers and resolved via FileResolver; the resolved locators flow through PhysicalOp and WorkerConfig.
  • DatasetMountManager — on the executor, ensures each bound dataset is mounted on the computing unit and hands its path to the Python worker, which injects it as a module-level variable (MOUNTED_DATASETS).
  • "Mounted dataset variables" property editor for the Python UDF.

Stacked on #6896 — until that merges, the diff here also shows the #6896 changes.

Any related issues, documentation, discussions?

Closes #6895 · part of #6606 · builds on #6896.

How was this PR tested?

  • Scala + Python unit tests: PythonUDFOpDescV2Spec (binding validation — Python identifiers, blank rows, empty dataset), DatasetVariableMappingSpec, DatasetMountManagerSpec (locator/identity validation), PythonWorkflowWorkerStartupConfigSpec, and test_executor_manager / test_run_python_worker (the MOUNTED_DATASETS variable injection).
  • Validated end-to-end on a single-node minikube: a Python UDF bound a mounted ~2 GB PyTorch model to a variable and torch.load-ed it from the propagated path with bit-exact output.

Was this PR authored or co-authored using generative AI tooling?

Generated-by: Claude Opus 4.8

aicam and others added 3 commits July 24, 2026 14:03
Perform the FUSE mount for dataset repositories outside the (unprivileged)
computing-unit pod. A per-node privileged `texera-mounter` DaemonSet runs
GeeseFS on the pod's behalf and the read-only mount is exposed back into
the pod via mount propagation, scoped to that computing unit. A
JWT-authenticated S3 proxy in file-service fronts the LakeFS S3 gateway:
it verifies the pod's JWT, checks the user's read access, and re-signs to
LakeFS with credentials held only server-side, so no global credential
ever enters the pod.

- `texera-mounter` DaemonSet: mounter.py (+ tests), dockerfile, helm daemonset/rbac/values.
- Unprivileged CU pod + mount-propagation wiring (KubernetesClient) and mounter config/env.
- File-service JWT S3 proxy (S3ProxyServlet).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01H27sHgS29u2JcLYPPR6Hdx
Add per-computing-unit dataset mounting on top of the mount
infrastructure. A user can mount / list / unmount a dataset version on a
running computing unit; the platform asks that CU's node mounter to
perform the mount and reports the in-pod path back.

- ComputingUnitManagingResource: GET/POST/DELETE
  /computing-unit/{cuid}/mounts, backed by a credential-free MounterClient
  that talks to the node mounter over HTTP.
- FileResolver: resolve a dataset path to (repo, versionHash) and back,
  so mounts can be shown to the user as /owner/dataset/version.
- "Mount datasets into computing unit" UI on the computing-unit selector.

At this point datasets can be mounted onto a CU but are not yet consumed
by any operator; that follows in the Python-UDF integration.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01H27sHgS29u2JcLYPPR6Hdx
Let a Python UDF bind mounted datasets to variables. Each binding maps a
dataset version to a Python variable that, at runtime, holds the local
filesystem path of the mounted dataset.

- PythonUDFOpDescV2: a list of (variableName -> dataset) bindings,
  validated as Python identifiers and resolved via FileResolver; carried
  through PhysicalOp and WorkerConfig.
- DatasetMountManager: on the executor, ensure each bound dataset is
  mounted on the computing unit and hand its path to the Python worker,
  which injects it as a module-level variable (MOUNTED_DATASETS).
- "Mounted dataset variables" property editor for the Python UDF.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01H27sHgS29u2JcLYPPR6Hdx
@github-actions

Copy link
Copy Markdown
Contributor

Automated Reviewer Suggestions

Based on the git blame history of the changed files, we recommend the following reviewers:

  • Contributors with relevant context: @Ma77Ball, @yangzhang75, @Yicong-Huang
    You can notify them by mentioning @Ma77Ball, @yangzhang75, @Yicong-Huang in a comment.

@codecov-commenter

codecov-commenter commented Jul 25, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 24.50000% with 151 lines in your changes missing coverage. Please review.
✅ Project coverage is 82.71%. Comparing base (f02dd2f) to head (d15b3d1).
⚠️ Report is 11 commits behind head on main.

Files with missing lines Patch % Lines
...rg/apache/texera/service/util/S3ProxyServlet.scala 5.79% 65 Missing ⚠️
...rvice/resource/ComputingUnitManagingResource.scala 0.00% 57 Missing ⚠️
.../apache/texera/service/util/KubernetesClient.scala 4.00% 24 Missing ⚠️
...org/apache/texera/service/util/MounterClient.scala 87.87% 0 Missing and 4 partials ⚠️
.../scala/org/apache/texera/service/FileService.scala 0.00% 1 Missing ⚠️
Additional details and impacted files
@@             Coverage Diff              @@
##               main    #6897      +/-   ##
============================================
+ Coverage     77.30%   82.71%   +5.40%     
+ Complexity     3524     2353    -1171     
============================================
  Files          1161      894     -267     
  Lines         45922    37899    -8023     
  Branches       5101     3980    -1121     
============================================
- Hits          35501    31348    -4153     
+ Misses         8840     5412    -3428     
+ Partials       1581     1139     -442     
Flag Coverage Δ *Carryforward flag
access-control-service 70.00% <ø> (ø)
agent-service 76.76% <ø> (ø) Carriedforward from d4f0f3a
amber 85.90% <100.00%> (+16.80%) ⬆️ Carriedforward from d4f0f3a
computing-unit-managing-service 21.82% <26.08%> (+1.32%) ⬆️
config-service 66.66% <ø> (ø)
file-service 59.49% <5.71%> (-7.72%) ⬇️
frontend 82.58% <ø> (ø) Carriedforward from d4f0f3a
notebook-migration-service 78.94% <ø> (ø)
pyamber 92.41% <100.00%> (+0.25%) ⬆️
workflow-compiling-service 55.14% <ø> (ø)

*This pull request uses carry forward flags. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@github-actions

Copy link
Copy Markdown
Contributor

⚠️ Benchmark changes need a look

🟢 6 better · 🔴 4 worse · ⚪ 5 noise (<±5%) · 0 without baseline

Compared against main 57d4dba benchmarked on this same runner, so the delta is largely free of cross-runner hardware noise. The "7d avg" column still reflects the gh-pages dashboard. Treat <±5% as noise unless repeated.

Dashboard · Run

config throughput MB/s latency max Δ latest / 7d
🔴 bs=10 sw=10 sl=64 644 0.393 15,660/18,388/18,388 us 🟢 -20.4% / 🔴 +22.2%
🔴 bs=100 sw=10 sl=64 1,315 0.803 75,582/99,481/99,481 us 🟢 -14.9% / 🟢 +35.7%
🟢 bs=1000 sw=10 sl=64 1,599 0.976 624,123/659,629/659,629 us 🟢 -5.8% / 🟢 +59.3%
Baseline details

Latest main 57d4dba from same runner

config metric PR latest main 7d avg Δ latest Δ 7d
bs=10 sw=10 sl=64 throughput 644 tuples/sec 683 tuples/sec 754.55 tuples/sec -5.7% -14.7%
bs=10 sw=10 sl=64 MB/s 0.393 MB/s 0.417 MB/s 0.461 MB/s -5.8% -14.7%
bs=10 sw=10 sl=64 p50 15,660 us 14,754 us 12,816 us +6.1% +22.2%
bs=10 sw=10 sl=64 p95 18,388 us 23,087 us 16,594 us -20.4% +10.8%
bs=10 sw=10 sl=64 p99 18,388 us 23,087 us 19,806 us -20.4% -7.2%
bs=100 sw=10 sl=64 throughput 1,315 tuples/sec 1,366 tuples/sec 969.38 tuples/sec -3.7% +35.7%
bs=100 sw=10 sl=64 MB/s 0.803 MB/s 0.834 MB/s 0.592 MB/s -3.7% +35.7%
bs=100 sw=10 sl=64 p50 75,582 us 68,800 us 103,584 us +9.9% -27.0%
bs=100 sw=10 sl=64 p95 99,481 us 116,853 us 109,097 us -14.9% -8.8%
bs=100 sw=10 sl=64 p99 99,481 us 116,853 us 117,304 us -14.9% -15.2%
bs=1000 sw=10 sl=64 throughput 1,599 tuples/sec 1,613 tuples/sec 1,004 tuples/sec -0.9% +59.3%
bs=1000 sw=10 sl=64 MB/s 0.976 MB/s 0.985 MB/s 0.613 MB/s -0.9% +59.3%
bs=1000 sw=10 sl=64 p50 624,123 us 614,996 us 1,002,357 us +1.5% -37.7%
bs=1000 sw=10 sl=64 p95 659,629 us 699,890 us 1,046,463 us -5.8% -37.0%
bs=1000 sw=10 sl=64 p99 659,629 us 699,890 us 1,073,661 us -5.8% -38.6%
Raw CSV
config_idx,batch_size,schema_width,string_len,num_batches,total_ms,total_tuples,total_bytes,tuples_per_sec,mb_per_sec,lat_p50_us,lat_p95_us,lat_p99_us
0,10,10,64,20,310.69,200,128000,644,0.393,15660.20,18388.07,18388.07
1,100,10,64,20,1520.35,2000,1280000,1315,0.803,75581.87,99481.33,99481.33
2,1000,10,64,20,12506.48,20000,12800000,1599,0.976,624122.59,659629.12,659629.12

@chenlica

Copy link
Copy Markdown
Contributor

@aicam Please make the PR description more readable. Also this PR changed 44 files. Can we divide it?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

common engine feature frontend Changes related to the frontend GUI infra platform Non-amber Scala service paths pyamber

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Consume mounted datasets in Python UDFs

3 participants