Repository navigation
Conversation
|
Hi, Sir. @LuciferYang @geruh Could you please help review this PR when have free time? Thanks very much !!! |
Assert that a post-acquire export failure still closes the task-owned namespace client on the construction catch path, not only on handle close.
geruh
left a comment
There was a problem hiding this comment.
Thanks for this @hfutatzhanghb! Just a few nits
| } | ||
|
|
||
| @Test | ||
| public void testPartitionReaderReusesAndClosesExecutorNamespace() throws Exception { |
There was a problem hiding this comment.
These are constructing the LanceColumnarPartitionReader. Should they live in the LanceColumnarPartitionReaderTest file looks like they have a close() test
There was a problem hiding this comment.
Thanks a lot. Very nice advice. I moved the three tests that construct LanceColumnarPartitionReader into LanceColumnarPartitionReaderTest alongside the existing close() coverage. I kept the direct ExecutorNamespace ownership test in place since it does not construct the partition reader and its RecordingNamespace fixture is also reused by LanceArrowStreamScannerTest.
| * readOptions.setNamespace(...)} would fire — all observable here. The bogus dataset URI lets the | ||
| * outer {@code Utils.openDatasetBuilder().build()} call fail predictably, since the gate runs | ||
| * <i>before</i> the dataset is opened. No real Lance dataset is required. | ||
| * executor opens a namespace-backed table with the flag disabled, the partition reader must |
There was a problem hiding this comment.
nit: nice drop on the test java doc. I think we can drop this whole comment in favor of the test since i can understan what we are doing from the method name and the two asserts which mention refresh=false must not connect.
wdyt?
There was a problem hiding this comment.
Agreed. The method name and assertion messages already explain the expected behavior, so I removed the Javadoc.
| } | ||
| // Null-first so close() is idempotent (PartitionReader extends Closeable, whose contract | ||
| // requires it): a repeat call short-circuits rather than raising | ||
| // `ArrowArrayStream is already closed` from a second ArrowArrayStream.release(). |
There was a problem hiding this comment.
I think we might want to keep the comment here since the null-first is what makes close() idempotent, and spark closable isn't the only reason. wdyt?
// Null-first so close() is idempotent. A repeat call must not raise ArrowArrayStream is already closed from a second release().
There was a problem hiding this comment.
Agreed. I restored the concise null-first comment above the resource reset so the idempotence requirement remains explicit. Thanks very much!
There was a problem hiding this comment.
✅ Gate recommendation: approve.
This revision keeps the namespace lifecycle behavior unchanged while relocating the columnar-reader lifecycle tests to their owning suite and documenting why null-first cleanup makes repeated close calls safe. Coverage for disabled refresh, per-task reuse, failure cleanup, and idempotent close is preserved.
|
Hi, @geruh . Could you please review this PR again when have free time? Thanks very much! |
|
@geruh Thanks very much for reviewing and merging! |
Summary
mainLanceColumnarPartitionReaderinstead of once per fragmentAutoCloseablenamespace implementations after fragment resources on normal completion and scan/open failuresCOUNT(*)scans explicit namespace lifecycle ownersThis PR replaces #728 and uses a new branch based on current
main.Root cause
With
executor_credential_refresh=true,LanceFragmentScanner.create()calledLanceRuntime.getOrCreateNamespace()before every fragment. Despite its name, that methodalways creates a new
LanceNamespaceconnection. The fragment scanner owned and closed itsnative scanner and dataset, but nobody owned or closed the namespace client.
Hive2 and Hive3 namespace implementations are
Closeableand own HMS client pools. As aresult, a Spark task scanning multiple fragments repeatedly created HMS-backed namespace
clients and leaked their connections.
Fix
LanceColumnarPartitionReadernow owns executor namespace initialization and cleanup becauseits lifecycle matches one Spark task. Initialization remains lazy and preserves the existing
executor_credential_refreshanduseNamespaceOnWorkersgates. The namespace stays attachedto the task-local read options while fragments are scanned, then is cleared and closed after
the active fragment scanner.
The failure path preserves the original exception and attaches cleanup failures as suppressed
exceptions. Repeated reader close calls remain idempotent.
Validation
./mvnw spotless:check./mvnw test -pl lance-spark-3.5_2.13 -DskipTests(main and test sources compile)./mvnw test -pl lance-spark-3.5_2.13 -Dtest=org.lance.spark.internal.LanceFragmentScannerTest#testExecutorNamespaceOwnerClosesClient