Repository navigation
Reliably stop abandoned read-only requests and their queries #8169
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 19 commits
d7b9161
39c8a95
a064720
ec56498
b99faf7
bb48cc1
e56a94f
d62f796
e91b066
1f08889
b107e2d
f8bec6f
4c78473
f3ebaa2
a33e8f8
f36f9ab
b9bef46
1762bdc
5e3be7c
6342a2b
0c668d2
bfc3d44
d372239
cf105fa
627e150
abcd2ca
b829b3c
e412a6f
77baa1c
1166e96
f972600
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -68,6 +68,7 @@ | |
| import java.util.TreeSet; | ||
| import java.util.concurrent.Executor; | ||
| import java.util.concurrent.atomic.AtomicLong; | ||
| import java.util.function.Predicate; | ||
|
|
||
| /** | ||
| * Wrapper over another JDBC Connection object that provides logging for errors and some tracking of usage. | ||
|
|
@@ -153,6 +154,9 @@ private void realClose() | |
|
|
||
| private volatile @NotNull Closer _runOnClose = () -> {}; | ||
|
|
||
| // Set for ConnectionType.Thread; release must go back here even if the closing thread no longer shares the lender's connections | ||
| private volatile @Nullable DbScope.ConnectionHolder _threadConnectionHolder; | ||
|
|
||
| /** Remember the first SQLException that happened on this connection, as it may have left the connection in a bad state */ | ||
| private SQLException _originalSqlException; | ||
|
|
||
|
|
@@ -167,13 +171,12 @@ private void realClose() | |
| static | ||
| { | ||
| // Issue 51483: DB query can be left running after shutting down server | ||
| ContextListener.addShutdownListener(ShutdownListener.of("DB connection closer", null, () -> { | ||
|
|
||
| // Only kill connections that have been open for more than two seconds to avoid | ||
| // interrupting work related to the shutdown | ||
| Calendar cutoffCal = Calendar.getInstance(); | ||
| cutoffCal.add(Calendar.SECOND, -2); | ||
| Date cutoffDate = new Date(cutoffCal.getTimeInMillis()); | ||
| ContextListener.addShutdownListener(ShutdownListener.of("DB connection closer", () -> { | ||
| // Closing doesn't stop a busy backend, which notices its client is gone only once it sends results | ||
| Date cutoffDate = getShutdownCutoff(); | ||
| RequestCancellation.cancelQueries(getConnectionsByScope(c -> c._allocationTime.before(cutoffDate)), RequestCancellation.Source.SHUTDOWN, true); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is running via
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. From Claude: |
||
| }, () -> { | ||
| Date cutoffDate = getShutdownCutoff(); | ||
|
|
||
| // Build up the list and release the lock on _openConnections | ||
| List<ConnectionWrapper> toClose = new ArrayList<>(); | ||
|
|
@@ -201,6 +204,14 @@ private void realClose() | |
| })); | ||
| } | ||
|
|
||
| // Only kill connections that have been open for more than two seconds to avoid interrupting work related to the shutdown | ||
| private static Date getShutdownCutoff() | ||
| { | ||
| Calendar cutoffCal = Calendar.getInstance(); | ||
| cutoffCal.add(Calendar.SECOND, -2); | ||
| return new Date(cutoffCal.getTimeInMillis()); | ||
| } | ||
|
|
||
| private static boolean initializeExplicitLogger() | ||
| { | ||
| var loggerContext = (LoggerContext) LogManager.getContext(false); | ||
|
|
@@ -370,14 +381,25 @@ public static Set<Integer> getSPIDsForThread(Thread t) | |
| * the pool re-hands the same physical connection back out under its cached SPID. | ||
| */ | ||
| public static Map<DbScope, Set<ConnectionWrapper>> getConnectionsByScopeForThread(Thread t) | ||
| { | ||
| return getConnectionsByScope(c -> c._allocatingThread == t); | ||
| } | ||
|
|
||
| /** Like {@link #getConnectionsByScopeForThread}, but also includes connections allocated by threads currently sharing t's connections, such as AsyncQueryRequest workers */ | ||
| public static Map<DbScope, Set<ConnectionWrapper>> getConnectionsByScopeForEffectiveThread(Thread t) | ||
| { | ||
| return getConnectionsByScope(c -> c._allocatingThread == t || DbScope.getEffectiveThread(c._allocatingThread) == t); | ||
| } | ||
|
|
||
| private static Map<DbScope, Set<ConnectionWrapper>> getConnectionsByScope(Predicate<ConnectionWrapper> filter) | ||
| { | ||
| Map<DbScope, Set<ConnectionWrapper>> result = new HashMap<>(); | ||
| synchronized(_openConnections) | ||
| { | ||
| for (ConnectionWrapper c : _openConnections) | ||
| { | ||
| // A null or negative SPID means the dialect couldn't determine one, so there's nothing to cancel | ||
| if (c._allocatingThread == t && c._spid != null && c._spid >= 0) | ||
| if (c._spid != null && c._spid >= 0 && filter.test(c)) | ||
| { | ||
| result.computeIfAbsent(c._scope, k -> new HashSet<>()).add(c); | ||
| } | ||
|
|
@@ -386,6 +408,22 @@ public static Map<DbScope, Set<ConnectionWrapper>> getConnectionsByScopeForThrea | |
| return result; | ||
| } | ||
|
|
||
| /** The driver's own connection behind the pool's wrapper, for driver-specific operations like an out-of-band cancel */ | ||
| public Connection getPhysicalConnection() | ||
| { | ||
| return DbScope.getDelegate(_connection); | ||
| } | ||
|
|
||
| @Nullable DbScope.ConnectionHolder getThreadConnectionHolder() | ||
| { | ||
| return _threadConnectionHolder; | ||
| } | ||
|
|
||
| void setThreadConnectionHolder(@Nullable DbScope.ConnectionHolder holder) | ||
| { | ||
| _threadConnectionHolder = holder; | ||
| } | ||
|
|
||
| /** | ||
| * @return whether this wrapper is still open, and so whether its SPID still refers to the session it was handed out | ||
| * for. Removal happens before the underlying connection returns to the pool, so a true answer can't be stale in the | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Claude suggests detaching the thread from the connection here, with a call like:
where the new method in DbScope looks like:
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done