fix: cache executor namespace metadata across fragments#710
Open
charleshuang119 wants to merge 6 commits into
Open
fix: cache executor namespace metadata across fragments#710charleshuang119 wants to merge 6 commits into
charleshuang119 wants to merge 6 commits into
Conversation
charleshuang119
force-pushed
the
fix/coalesce-executor-credential-refresh
branch
from
July 21, 2026 21:55
9b5895b to
5fe86cb
Compare
hamersaw
self-requested a review
July 22, 2026 00:48
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Reuse Lance Namespace clients and
describeTablemetadata across fragment tasks belonging to the same Spark scan and executor JVM.This PR is intentionally limited to the catalog layer. It no longer retains a Dataset as an object-store anchor. Azure object-store, HTTP-client, and token-provider reuse require a separate fix in
lance-format/lance.Production impact and reproduction
Lance Spark creates approximately one input task per Lance fragment. A controlled Gravitino-backed read with 368 fragments and one 4-core executor generated 11,042 DNS queries in 29 seconds, almost exactly 30 per fragment:
login.microsoftonline.comAt production fragment counts, even a small number of Spark pods can overload cluster DNS. Changing the same workload to direct ABFSS removed the storm.
The three groups have different causes. This PR addresses the Gravitino group: before this change, every fragment task rebuilt the worker-side namespace client and called
describeTable. Azure identity and Blob amplification happen below Spark in Lance object-store construction and lifetime management and are explicitly out of scope here.Root cause
LanceFragmentScanner.createopens a Dataset for each fragment. Whenexecutor_credential_refresh=true, it also previously rebuilt the namespace client for each fragment. Opening the namespace-backed Dataset callsdescribeTable, so namespace initialization, Gravitino requests, and associated DNS traffic scaled linearly with fragment count.Filtered
COUNT(*)usesLanceCountStarPartitionReaderinstead ofLanceFragmentScannerand had the same worker-side namespace behavior, so it must participate in the same cache.Solution
Add a bounded, scan-scoped executor namespace cache:
(namespace implementation, namespace properties, scanId)in each executor JVM.describeTableresponse across both regular fragment readers and filteredCOUNT(*)readers.describeTablecalls with aFutureTasksingle-flight.namespaceId, preventing Lance from reusing an earlier scan’s object-store provider after that scan’s namespace is evicted and closed.expires_at_millisusing a 10% safety window bounded between 1 and 60 seconds.listTableVersionsanddescribeTableVersionto preserve managed-versioning reads.executor_credential_refresh=falsebehavior unchanged.Memory and lifecycle safety
An earlier revision pinned one full Dataset per executor and scan to keep the Lance object store alive. That approach was removed after review because a Dataset retains manifest and fragment metadata, making an hour-long cache with up to 1,000 scans unsafe for large tables. It also created ambiguous namespace/credential-provider ownership across scans.
The final implementation caches only namespace clients and small metadata responses. Scan-scoped provider identity also ensures that object stores cannot outlive and call a namespace delegate owned by another scan. It does not cache Datasets, fragment readers, object stores, or Azure credential providers, and it does not enumerate fragments into an executor-side map.
Expected effect and explicit non-goals
Expected for regular fragment scans and filtered
COUNT(*):describeTable: from O(fragments) toward O(executor JVMs × scans × metadata refresh windows).Not solved by this PR:
login.microsoftonline.comtoken-acquisition DNS amplification.Those require bounded strong object-store reuse and creation single-flight in
lance-format/lance. The original 368-fragment DNS capture should be rerun after both changes; this PR alone should only be credited for reducing the Gravitino portion.Test plan
Targeted tests:
Result: 24 tests run, 0 failures, 0 errors.
Coverage verifies:
DescribeTableRequestinstances share one cached response.describeTablecalls collapse to one backing request.expires_at_millis.COUNT(*)tasks reuse the same namespace metadata cache.executor_credential_refresh=falseremains unchanged.A full local Spark 3.5 / Scala 2.13 run executed 1,240 tests. The changed and targeted tests passed; two unrelated index tests hit the local Lance DataFusion 150 MB external-sort memory limit. The previous full run before adding the isolated filtered-count coverage passed, and GitHub CI remains the authoritative clean-environment run.
References