Skip to content

Publish DBR Scala 2.13 image when a runtime ships two Scala variants - #25

Merged
rugpanov merged 4 commits into
mainfrom
deco-28292-dbr-scala-variants
Aug 27, 2026
Merged

Publish DBR Scala 2.13 image when a runtime ships two Scala variants#25
rugpanov merged 4 commits into
mainfrom
deco-28292-dbr-scala-variants

Conversation

@rugpanov

@rugpanov rugpanov commented Aug 26, 2026

Copy link
Copy Markdown
Collaborator

Why

dbr_meta read the Scala version with a single re.search, taking only the first version a runtime page lists. A runtime mid-migration publishes two images from one page (Scala: 2.12.15 or 2.13.10), but only the 2.12 folder was generated — so a cluster on the Scala 2.13 image (spark_version = 16.4.x-scala2.13) resolves to no published environment and 404s.

This is the largest E_ENV_UNSUPPORTED source in the VPEX telemetry (~69%: 16.4.x-scala2.13 + its cpu-ml variant), spread across many distinct workspaces. DBR 16.4 LTS is the only dual-image line today; the fix is version-agnostic so any future dual-image runtime is covered automatically.

What

  • sync.py — add dbr_scalas(), returning every Scala MAJOR.MINOR a page's System environment lists (one entry for a single-image page, both for a dual-image one), de-duped and in page order, with a fallback to the prior single-match if the field isn't </li>-delimited. dbr_meta returns that list; sync_dbr and the ML path write one folder per Scala version off the shared installed-python-libraries table (only the Java/Scala tables split per image, which this repo doesn't consume).
  • envgen.py — extend DROP_BY_ENV with the three 16.4.x…scala2.13 keys. The pandas 1.5.x-on-cp312 problem (dbr/16.4.x, serverless-v3: pandas 1.5.3 can't be installed on Python 3.12 (no cp312 wheel) #18) is Scala-independent, so the 2.13 image needs the same drop as the 2.12 image — without it the new folders would re-break dbr/16.4.x, serverless-v3: pandas 1.5.3 can't be installed on Python 3.12 (no cp312 wheel) #18.
  • Regenerated artifactsdbr/16.4.x-scala2.13, dbr/16.4.x-cpu-ml-scala2.13, dbr/16.4.x-gpu-ml-scala2.13. Pin sets are byte-identical to their 2.12 siblings (only the name strings differ).
  • Tests — new test_sync.py (dbr_scalas/dbr_meta on dual/single/missing/malformed input); extend the envgen pandas-drop test to the scala2.13 envs.

Verification

  • python -m unittest test_sync test_envgen → 22 passing (CI's test.yml auto-discovers test_sync.py).
  • python .github/scripts/sync.py --check"no changes — repo is in sync with published docs", exit 0.
  • Full uv sync (build + install) PASSES for all three new envs via the Databricks PyPI proxy.

This pull request and its description were written by Isaac.

rugpanov and others added 3 commits August 26, 2026 22:30
*Why*

dbr_meta read the Scala version with a single re.search, taking only the first
version a page lists. A runtime mid-migration publishes two images from one page
("Scala: 2.12.15 or 2.13.10"), but only the 2.12 folder was generated -- so a
cluster on the Scala 2.13 image (spark_version 16.4.x-scala2.13) resolves to no
published environment and 404s. This is the largest E_ENV_UNSUPPORTED source in
the VPEX telemetry (~69%: 16.4.x-scala2.13 + its cpu-ml variant), across many
distinct workspaces. DBR 16.4 LTS is the only dual-image line today; the fix is
version-agnostic so future ones are covered automatically.

*What*

- sync.py: add dbr_scalas(), which returns every Scala MAJOR.MINOR a page's System
  environment lists (one entry for a single-image page, both for a dual-image one),
  de-duped and in page order, with a fallback to the prior single-match. dbr_meta
  returns that list; sync_dbr and the ML path write one folder per Scala version off
  the shared "Installed Python libraries" table (only the Java/Scala tables split per
  image, which this repo doesn't consume).
- envgen.py: extend DROP_BY_ENV with the three 16.4.x...scala2.13 keys. The pandas
  1.5.x-on-cp312 problem (issue #18) is Scala-independent, so the 2.13 image needs the
  same drop as the 2.12 image -- without it the new folders re-break #18.
- Regenerated artifacts: dbr/16.4.x-scala2.13, 16.4.x-cpu-ml-scala2.13,
  16.4.x-gpu-ml-scala2.13. Byte-identical pin sets to their 2.12 siblings; all three
  pass full `uv sync`.
- Tests: new test_sync.py (dbr_scalas/dbr_meta on dual/single/missing/malformed
  input); extend the envgen pandas-drop test to the scala2.13 envs.

Co-authored-by: Isaac <no-reply@databricks.com>
Review hardening: dbr_scalas captured the raw HTML between 'Scala</strong>:' and
</li>, so a version-like number inside a tag (e.g. an href to a Spark-version doc
page) could be read as a Scala version and spawn a nonexistent environment. Strip
inline tags first -- the same treatment table_pkgs already applies -- so only the
visible version tokens count. No generated artifact changes (real pages carry no
such tag digits today); adds a regression test.

Co-authored-by: Isaac <no-reply@databricks.com>
…variant

Review follow-up. The previous parser read every version-like token in the Scala
field, so a trailing visible annotation (e.g. "2.12.15 or 2.13.10 (Apache Spark
3.5)") would add a bogus "3.5" and generate a nonexistent scala3.5 environment.
Match only the leading "VER (or VER)*" enumeration -- the Scala version, or the two
joined by "or" for a dual-image release -- and stop at any following text. Tag
stripping is kept so the enumeration survives the "<strong>or</strong>" markup.
Adds a regression test for the trailing-annotation case; no generated artifact
changes.

Also update README: the DBR sync description now notes that a dual-image release
produces one environment per Scala version off the page's single Python table.

Co-authored-by: Isaac <no-reply@databricks.com>
anton-107
anton-107 previously approved these changes Aug 27, 2026

@anton-107 anton-107 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Approving — the fix is well-scoped, correctly diagnosed, and deliberately version-agnostic, and the parser design (anchored re.match on the tag-stripped field + inner findall) is genuinely robust against the trailing-annotation and in-href number cases. Two HIGH-severity items I'd like to see addressed (they don't block merge, but the first one is important):

1. dbr_scalas only recognizes or as the separator — any other delimiter silently regresses to the original bug

The enumeration group is (?:\s+or\s+\d+\.\d+(?:\.\d+)?)*. If a runtime page ever renders the two versions with a comma (2.12.15, 2.13.10), a slash (2.12.15 / 2.13.10), and, or an HTML entity (2.12.15&nbsp;or&nbsp;2.13.10 — tag stripping removes <…> but leaves &nbsp; intact, so \s+or\s+ won't match), then re.match still matches the leading 2.12.15, the or group matches zero times, and the function returns ["2.12"] only.

That is not a failure path: scalas is truthy, so dbr_meta returns, one folder is written, and the scala2.13 env silently goes missing again — which is exactly the E_ENV_UNSUPPORTED / 404 failure this PR exists to fix. The </li> fallback doesn't protect against this because the field did match. In effect the fix is one Databricks copy-edit away from silently resurrecting the bug on a single variant.

Suggested: widen the separator (e.g. [\s,/]+(?:or|and)?[\s,/]+, or split the field directly on a version regex) and normalize &nbsp; → space before matching. Add tests for comma / slash / &nbsp; separators.

2. No end-to-end test that 16.4.x-scala2.13 actually resolves to the generated folder

The PR convincingly proves the folder is generated, but the stated goal is killing the 404 — and the resolver that maps spark_version = 16.4.x-scala2.13 to a published env isn't in this diff. Worth either a test or a manual confirmation noted on the PR that a cluster on the 2.13 image now resolves to the new env rather than 404ing, so we know the actual user-facing symptom is gone.


Findings from an automated (Claude) review pass; sharing the two highest-severity ones. Happy to file the lower-severity notes (e.g. per-Scala DROP_BY_ENV duplication, missing write-loop integration test) as follow-ups if useful.

Review follow-up (Anton, PR #25). The enumeration only accepted " or " between the two
Scala versions, so a page rendered with a comma, slash, "and", or a nbsp-joined "or"
would match just the leading version and return ["2.12"] -- silently dropping the 2.13
variant and re-introducing the E_ENV_UNSUPPORTED / 404 this fix exists to prevent (not
a failure path: the single result is truthy, so a folder is still written).

Unescape HTML entities after tag-stripping (so a nbsp entity becomes real whitespace
that \s matches) and widen the inter-version delimiter to whitespace / "," / "/" / the
words "or"|"and". The delimiter class still never contains a version, so the leading
enumeration stops at a trailing annotation ("(Apache Spark 3.5)") as before. Adds
regression tests for comma / slash / "and" / nbsp separators and a comma-plus-annotation
case. No generated artifact changes.

Co-authored-by: Isaac <no-reply@databricks.com>
@rugpanov

Copy link
Copy Markdown
Collaborator Author

Thanks @anton-107 — great catch on #1, that's a real latent regression.

#1 (separator robustness) — fixed in 4c02340. The enumeration now unescapes HTML entities after tag-stripping (so an &nbsp;-joined or becomes real whitespace) and the inter-version delimiter is widened to whitespace / , / / / the words or|and. The delimiter class still never contains a version, so the leading enumeration keeps stopping at a trailing annotation like (Apache Spark 3.5). Added regression tests for comma / slash / and / &nbsp; separators plus a comma-then-annotation case (26 tests green; no generated-artifact changes).

#2 (end-to-end resolve) — out of this repo's scope, confirmed manually. The resolver that maps spark_version = 16.4.x-scala2.13 → published env lives in the CLI/extension, not here, so there's no resolver to unit-test in this diff. I did confirm the user-facing symptom is addressed: all three new envs provision with a full uv sync (build + install) via the Databricks PyPI proxy. Happy to track an e2e resolve check on the CLI side as a follow-up.

Also happy to take your lower-severity notes (per-Scala DROP_BY_ENV duplication, a write-loop integration test) as fast-follows if you'd like — just say the word.

@rugpanov
rugpanov requested a review from anton-107 August 27, 2026 09:20
@rugpanov
rugpanov merged commit 37f228d into main Aug 27, 2026
2 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.

2 participants