kkondamadugula opened a new issue, #57123:
URL: https://github.com/apache/spark/issues/57123

   ### Summary
   
   We are running Spark Connect as a long-lived Kubernetes service. After Spark 
Connect client jobs complete and executor pods terminate, the Connect 
server/driver pod retains high JVM RSS. Some RSS retention may be normal JVM 
heap commitment behavior, but heap inspection also shows a large number of 
Spark SQL session/state/catalog objects still live after jobs complete.
   
   I am trying to determine whether this is expected Spark Connect behavior, 
missing server-side session cleanup configuration, Spark UI/status-store 
retention, or a potential Spark Connect memory-retention issue. 
   
   Important distinction: I understand that JVM RSS may not immediately return 
to the OS after GC because the heap can remain committed. The concern here is 
not only RSS. The concern is that the live heap histogram after completed jobs 
still shows many Spark SQL session/state/catalog objects and large retained map 
structures.
   
   ### Operational impact
   
   The main operational issue is not only that the JVM RSS remains high. The 
bigger issue is that the only reliable way we have found to return the Spark 
Connect server to baseline memory is restarting the Connect server pod.
   
   Restarting the Spark Connect server is disruptive because it loses 
server-side Spark Connect session context. Any active or long-running client 
workload connected to that server can fail with errors such as lost/stopped 
Spark Connect context, unavailable session, or internal server errors. In our 
orchestration layer, this can cause the parent workflow to fail or cancel 
dependent child workflows.
   
   For long-running workloads, this means we cannot safely use “restart the 
Connect server” as a cleanup strategy unless we accept losing in-flight job 
progress and forcing the workflow to restart from its own external 
checkpointing/retry logic.
   
   ### Component
   
   Spark Connect / Spark SQL / Spark UI status store
   
   ### Spark version
   
   Apache Spark `4.0.0`
   
   ### Environment
   
   - Spark Connect server running as a long-lived Kubernetes pod
   - Kubernetes namespace: internal stage environment
   - Spark Connect server acts as the driver
   - Driver config:
     - `spark.driver.memory=20g`
     - `spark.driver.memoryOverhead=6g`
     - Kubernetes driver memory limit: `26g`
     - JVM has `-Xmx20g`
   - Dynamic executor pods are created per workload and terminate after 
completion
   - Event logging enabled to S3:
     - `spark.eventLog.enabled=true`
     - `spark.eventLog.dir=s3a://.../spark-event-logs/`
     - `spark.eventLog.rolling.enabled=true`
   
   ### Summary
   
   We are seeing high JVM/process memory retention in a long-lived Spark 
Connect server after client workloads complete.
   
   The workload completes successfully, executor pods terminate, and the Spark 
Connect server remains running, but the driver pod RSS does not return near its 
initial baseline. Over several completed Spark Connect workloads, the server 
retained progressively higher memory.
   
   The retained memory appears to be JVM/process memory in the Spark Connect 
server, not executor memory, local disk files, event-log files, or `/tmp` 
growth.
   
   ### Reproduction pattern
   
   1. Start a fresh Spark Connect server pod.
   2. Run Spark Connect client workloads that create Spark SQL/DataFrame jobs.
   3. Let the client workload complete.
   4. Ensure executor pods have terminated.
   5. Observe Spark Connect server pod memory.
   6. Repeat with additional workloads.
   
   The server pod memory remains high after workloads complete.
   
   ### Observed memory progression
   
   Fresh server baseline:
   
   | State | Kubernetes pod memory | Java RSS |
   |---|---:|---:|
   | Fresh Spark Connect server | ~`1.6Gi` | ~`676Mi` |
   
   After completed workloads:
   
   | Workload | Workload status | Executor pods after completion | Retained 
driver pod memory |
   |---|---|---:|---:|
   | Small job | Completed | 0 | ~`3.9Gi` |
   | Medium job | Completed | 0 | ~`10.8Gi` |
   | Large job | Completed | 0 | ~`18.4Gi` |
   | Large job | Completed | 0 | ~`23.7Gi` |
   
   The highest retained state observed was around:
   
   | Metric | Value |
   |---|---:|
   | Kubernetes pod memory | ~`23.7Gi` |
   | Java RSS | ~`22.5Gi` |
   | cgroup `memory.current` | ~`23.5Gi` |
   | cgroup `memory.peak` | ~`23.56Gi` |
   
   ### What was ruled out
   
   This does not appear to be local filesystem growth.
   
   `/tmp` remained stable around `2.8Gi` throughout the tests, mostly Ivy cache 
and Spark user files.
   
   `/proc/1/smaps_rollup` showed the memory is almost entirely 
anonymous/private process memory:
   
   ```text
   VmRSS:          23636848 kB
   RssAnon:        23613348 kB
   RssFile:           23500 kB
   RssShmem:              0 kB
   Private_Dirty: 23613356 kB
   Anonymous:     23613348 kB
   
   This suggests retained JVM/process memory, not file-backed Spark UI files, 
event logs, local disk cache, or page cache.
   Event logs are configured to S3, not local disk.
   Server-side cleanup attempted
   From a separate Spark Connect client session:
   **spark.catalog.clearCache()
   spark.range(1).count()
   spark.stop()**
   
   This completed successfully, but the Spark Connect server memory did not 
reduce materially.
   
   JVM / heap inspection
   The Spark Connect server JVM command line includes:
   **-Xmx20g
   spark.driver.memory=20g
   spark.driver.memoryOverhead=6g
   spark.kubernetes.driver.limit.memory=26g
   spark.eventLog.enabled=true
   spark.eventLog.dir=s3a://.../spark-event-logs/
   spark.eventLog.rolling.enabled=true**
   jcmd 1 GC.heap_info showed the G1 heap committed at the full 20Gi heap size.
   Before explicit GC:
   garbage-first heap total 20971520K, used 10989919K
   After explicit GC:
   garbage-first heap total 20971520K, used 10810926K
   The used heap only reduced slightly, and RSS/cgroup memory did not return 
near baseline.
   During a later active workload, heap used moved up and down, for example:
   used 17405456K   # during materialization / executor churn
   used 11819465K   # later after GC during active job execution
   This shows live heap can reduce, but the Spark Connect server process still 
keeps high committed/RSS memory.
   Heap histogram evidence
   jcmd 1 GC.class_histogram showed about 10.9GB of live Java heap objects 
after completed workloads.
   Top retained object groups included:
   java.util.concurrent.ConcurrentHashMap$Node        ~4.07 GB
   [Ljava.util.concurrent.ConcurrentHashMap$Node;     ~1.76 GB
   scala.collection.mutable.HashMap$Node              ~1.43 GB
   scala.Tuple2                                       ~1.05 GB
   [Lscala.collection.mutable.HashMap$Node;           ~460 MB
   byte[]                                             ~447 MB
   boolean[]                                          ~309 MB
   Spark SQL / Connect / session-state-related objects were also present in 
high counts:
   org.apache.spark.sql.classic.SparkSession                 ~51,673 instances
   org.apache.spark.sql.internal.SessionState                ~51,672 instances
   org.apache.spark.sql.hive.HiveSessionCatalog              ~51,672 instances
   org.apache.spark.sql.artifact.ArtifactManager             ~51,672 instances
   org.apache.spark.sql.hive.HiveSessionStateBuilder          ~51,672 instances
   Spark UI/status objects were present too:
   org.apache.spark.status.TaskDataWrapper
   org.apache.spark.sql.execution.ui.SQLPlanMetric
   
   But UI/status objects alone were not the dominant retained byte count. The 
dominant retained heap was large Java/Scala map structures, along with very 
high counts of Spark SQL session/state/catalog/artifact objects.
   Concern
   The high count of SparkSession, SessionState, HiveSessionCatalog, and 
ArtifactManager instances looks suspicious for a long-lived Spark Connect 
server after client jobs complete.
   It suggests that Spark Connect may be retaining server-side 
session/session-state/catalog/listener/status structures after client sessions 
finish, or that there is no effective cleanup/expiration happening for closed 
client sessions.
   Expected behavior
   After a Spark Connect client session completes and calls spark.stop(), I 
would expect the Spark Connect server to eventually release server-side state 
associated with that client session, including:
   Spark SQL session state
   catalog/cache state
   artifact/session manager state
   query execution/status/listener state where safe
   any per-session structures no longer needed
   Alternatively, if this retention is expected, it would be helpful to 
document the recommended Spark Connect server configs and operational lifecycle 
for long-lived servers.
   Actual behavior
   The Spark Connect server remains at high JVM RSS after:
   client workload completes
   client calls spark.stop()
   executor pods terminate
   Spark SQL cache clear is called
   explicit JVM GC is triggered
   The only reliable way observed to return memory to the original baseline is 
restarting the Spark Connect server pod.
   Questions
   Is it expected for a Spark Connect server to retain many SparkSession / 
SessionState / HiveSessionCatalog / ArtifactManager objects after client 
sessions complete?
   Are there Spark Connect server-side session expiration or cleanup configs 
that should be enabled for long-lived Connect servers?
   Are Spark UI/status retention configs involved in retaining SQL/session 
state in Spark Connect?
   Is there a known memory-retention issue in Spark Connect 4.0.0 related to 
session cleanup?
   Is this behavior improved in Spark 4.1.x or later?
   What additional diagnostics would be useful to confirm whether this is a 
Connect session leak, SQL status-store retention, catalog cache retention, or 
expected JVM heap behavior?
   
   ### Bottomline:
   
   Is there a supported way to clean up or expire completed Spark Connect 
sessions and their server-side state without restarting the Spark Connect 
server and losing active sessions?


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to