Fix: fork-after-JVM-start detection + experimental dynamic classpath loading#72
Merged
HenryNebula merged 4 commits intodevfrom Apr 30, 2026
Merged
Conversation
7510c32 to
f275328
Compare
JPype does not support fork after JVM start — the native library state is corrupted in child processes, causing 'already loaded in another classloader' errors in Gunicorn and similar pre-fork servers. Track the PID at JVM start time and raise InterfaceError with actionable guidance if a forked process attempts to connect. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
f275328 to
ee797be
Compare
Allow loading JDBC drivers from JARs after the JVM has already started,
using a DriverShim proxy (java.sql.Driver implemented via JPype
@JImplements) that delegates to a driver loaded through URLClassLoader.
This bypasses the fork-after-JVM-start guard, enabling use in gunicorn
--preload workers. Opt in with experimental={'dynamic_classpath': True}.
Closes #71
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
These files are build artifacts that were previously committed. They are now in .gitignore and removed from tracking. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- test_hsqldb_fails_without_dynamic_classpath: verifies HSQLDB driver is NOT available when JVM starts with only mock driver on classpath - test_dynamic_load_hsqldb_after_jvm_start: starts JVM with mock-only classpath, confirms HSQLDB fails first, then loads it dynamically and runs real SQL (CREATE TABLE, INSERT, SELECT, DROP) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
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
Fixes #71: JPype 'already loaded in another classloader' error in forked processes (Gunicorn pre-fork model), and adds experimental dynamic classpath loading to bypass the fork guard entirely.
Changes
_jvm_started_pidconnect()calls, check if PID changed (indicating a fork)InterfaceErrorwith actionable guidance instead of letting JPype fail with a crypticUnsatisfiedLinkErrorconnect(..., experimental={'dynamic_classpath': True})loads JDBC drivers from JARs after JVM start using the DriverShim pattern (URLClassLoader + JPype@JImplementsproxy forjava.sql.Driver). This bypasses the fork guard, enabling use in gunicorn--preloadworkers where each worker can load its own drivers post-fork.Test plan
DynamicClasspathTest— dynamic load after JVM start, fork guard bypass, without-flag behaviorDynamicClasspathIntegrationTest— dynamically loads HSQLDB driver after JVM already running with mock driverForkSafetyTestwith HSQLDB driverCloses #71
Generated with Claude Code