Skip to content
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

refactor:(loom) replace the usages of synchronized with ReentrantLock #2635

Merged
merged 1 commit into from
Feb 1, 2023

Conversation

rbygrave
Copy link
Contributor

@rbygrave rbygrave commented Oct 5, 2022

Referencing #1951 for outline / motivation which is to improve compatibility with loom.

Change PgStatement, PgPreparedStatement and PgCallableStatement replacing synchronized (this) blocks with use of ReentrantLock to be compatible with loom.

In doing this change for PgStatement this PR must include:

All synchronized (this) blocks in PgStatement
All synchronized (this) blocks in it's sub types (PgPreparedStatement and PgCallableStatement)
I confirm that this PR includes all synchronized (this) blocks across PgStatement and all it's sub types.

Note that this PR does not add any new tests or change any existing tests. I do not believe either is required.

@vlsi
Copy link
Member

vlsi commented Oct 6, 2022

The PR looks good to me, except there's still some synchronized (connection) { left in PgStatement.java: https://github.com/rbygrave/pgjdbc/blob/feature/1951-PgStatement-3/pgjdbc/src/main/java/org/postgresql/jdbc/PgStatement.java#L977-L984

Have you tried adjusting it as well?

I'm inclined we might get concurrency issues if we replace only a fraction of synchronizations on Statement with Lock.

@rbygrave
Copy link
Contributor Author

rbygrave commented Oct 6, 2022

synchronized (connection)

My intention was to leave those for now and do that change as a separate PR as they are a different lock - a lock on the connection instance rather than the statement.

If we want to include the change for the connection lock we could and there are a couple of things to note.

  • With the lock on connection we use .notifyAll() and .wait(). This is no problem as those are supported by ReentrantLock.
  • We need to decide how to expose the PgConnection lock to PgStatement. One option is change the PgStatement.connection type to PgConnection and then use a package private method on PgConnection. A second option is to add a method to the BaseConnection interface (which would be a public method so for myself I'd be less keen for this option).

We could do this change as an additional commit to this PR or a separate PR (or even hold off for now if that was desired).

get concurrency issues if we replace only a fraction of synchronizations

We need to specifically be careful wrt use of synchronized (somethingThatIsNotThis) ... like for example synchronized (connection). For example, in the "lock on connection" case we easily find all the uses inside of PgConnection (and subclasses if it had any) but we also search globally for code like synchronized (connection) and we need to be sure we find all the cases and this part is a manual search we need to do carefully.

In this case of the lock on PgStatement [and subclasses] we need to:

  • Change all synchronized methods in PgStatement and subclasses
  • Change all synchronized (this) in PgStatement and subclasses
  • Find all uses of the lock on PgStatement instances in the rest of the code by globally searching synchronized ( ... checking there are no cases of synchronized (statement) or similar [edit: and in this case there are no other uses of the lock]

So we do need to be careful but yes we can replace just the lock on PgStatement from synchronized to ReentrantLock and be very comfortable in doing that and leaving other locks like synchronized (connection) there unmodified.

@vlsi
Copy link
Member

vlsi commented Oct 6, 2022

Suppose there's a class:

class Counter {
  private int value;
  synchronized void increase() {
    value++;
  }
  synchronized void decrease() {
    value--;
  }
}

I would like to refrain from having two PRs that address increase and decrease methods separately because if we merge a PR that changes only increase from synchronized to ReentrantLock, then we basically get a broken class like:

class Counter {
  private int value;
  private Lock lock = new ReentrantLock();
  void increase() {
    lock.lock();
    try {
      value++;
    } finally {
      lock.unlock();
    }
  }
  synchronized void decrease() {
    value--;
  }
}

In that case, calling increase and decrease concurrently would break.

That is why I would rather prefer a single PR with multiple commits instead of multiple PRs that might result in a half-broken code. GitHub does allow clicking into individual commits that form the PR, so I believe PRs like https://github.com/GoogleCloudPlatform/app-gradle-plugin/pull/398/commits are reviewable.

Of course, there are cases when the change is a no-brainer. It might be fine to include them (and even merge) in a separate PRs. However, I would refrain from merging only a half of Statement or Connection-related synchronizations because it would make pgjdbc unreleasable until we merge all the changes.

@rbygrave
Copy link
Contributor Author

rbygrave commented Oct 6, 2022

Are we ok changing the type of PgStatement.connection to PgConnection from BaseConnection?

See my comment from "We need to decide how to expose the PgConnection lock to PgStatement."

@vlsi
Copy link
Member

vlsi commented Oct 7, 2022

By the way, @rbygrave , have you considered adding something like AutoCloseableLock extends ReentrantLock for patterns like try (ResourceLock ignored = obtainLock()) { ... }?

@rbygrave
Copy link
Contributor Author

rbygrave commented Oct 7, 2022

Not really no. The JDK devs could have added such support to the jdk but didn't so some hesitation from me due to that.

I think there are 2 approaches ...
A) a wrapper type (downside of extra instance per lock)
B) a type that extends ReentrantLock (downside, can be used incorrectly in try-with-resources)

We have a lot of lock code blocks so I can see the value of doing it (remove the finally blocks) but there does look to be a downside to both approaches as I see it.

Edit: FWIW right now my gut says keep it simple and stick with the finally blocks. Slightly ugly but simple and everyone immediately understands it. Happy to try an AutoClosableLock if you or others are keen.

@vlsi
Copy link
Member

vlsi commented Oct 7, 2022

B) a type that extends ReentrantLock (downside, can be used incorrectly in try-with-resources)

Could you please clarify what you mean by "can be used incorrectly"?
I think a pattern like "use locks in try-with-resources only" should be less prone to errors than "use raw Lock API" (e.g. one might forget to add finally).

Happy to try an AutoClosableLock if you or others are keen

I think it is a small addition that makes the code slightly easier to read/write, and it brings no downsides performance-wise.
We could have used withLock(() -> ...) pattern, however, it would incur overhead.

@rbygrave
Copy link
Contributor Author

rbygrave commented Oct 7, 2022

can be used incorrectly

With B) try (autoClosableLock) { will compile but be invalid - this is the way in which (B) can be used incorrectly. We always need to use a method like obtainLock() such that .lock() is called like: try (ResourceLock ignored = autoClosableLock.obtainLock()).

be less prone to errors than "use raw Lock API" (e.g. one might forget to add finally).

It's a compile error to add the try { ... } without a finally [or catch] so for myself I don't see that as less error prone. For example, when we replace the synchronized (this) { with the lock.lock(); try { ... we are always in effect adding a new try block so it will not compile without adding the finally. The only way to get it wrong is to add a catch () { } block instead of a finally { ... }. IntelliJ will have a little red marker showing us where the missing finally block needs to go.

Said another way, for this change we can never re-use an existing try { block - we always need to add a new try { ... so in that way the compiler tells us to add a finally { ... } [or catch (..) { .. }].

We could have used withLock(() -> ...)

Yes agreed, closure overhead there.

@rbygrave
Copy link
Contributor Author

Did that make sense @vlsi ? Are you still keen for us to create a ResourceLock extends ReentrantLock as per (B) and use it via try (ResourceLock ignored = lock.obtainLock()) { ... } and remove the finally blocks or keep it as it current is with the finally blocks?

@davecramer
Copy link
Member

@rbygrave seems @vlsi may be out of touch for a bit. I'd like to see this move forward. For now use what you think is right and if Vladimir strenuously objects later we can change it.

@vlsi
Copy link
Member

vlsi commented Oct 12, 2022

It's a compile error to add the try { ... } without a finally [or catch] so for myself I

The following would be a misuse of the lock API which would be impossible with try-with-resources.

lock.lock()
try {
    ...
} catch(SQLException e) {
    e.printStacktrace(); // or whatever
}

We always need to use a method like obtainLock() such that .lock() is called like: try (ResourceLock ignored = autoClosableLock.obtainLock()).

Well, there could be a private (or package-private) method obtainLock, so the usage would be as follows:

try (AutoClosable ignored = obtainLock()) {
    ...
}

@rbygrave
Copy link
Contributor Author

The following would be a misuse of the lock API

Yes agreed. That is what I said as well.

so the usage would be as follows:

Pretty much but I would suggest that obtainLock() would instead be lock.obtainLock() because we would not want to add a local helper method on each and every type that we use a "ResourceLock" so I'd suggest it would end up as:

try (AutoClosable ignored = lock.obtainLock()) {
    ...
}

For myself, my gut says that I'd prefer being more explicit using ResourceLock rather than AutoClosable like:

try (ResourceLock ignored = lock.obtainLock()) {
    ...
}

... as I think that would be clearer / more explicit about what's going on for folks reading the code in the future.

For myself, I'm happy if we take a day or two for everyone to be comfortable with the style we want to use going forward because it's going to be repeated a lot. Of course we can change our minds later on but yeah happy to get good comfort levels with the style and approach first - a few days isn't going to hurt us.

Currently our question is, are we keen to create a ResourceLock extends ReentrantLock implements AutoClosable as per (B) and use it via try (AutoClosable ignored = lock.obtainLock()) { ... } and remove the finally blocks or keep it as it currently is with the finally blocks?

For me I'm happy either way. I could even make that change using a ResourceLock and push another commit and then everyone gets to see both styles explicitly. If we don't like it we could just remove that last commit. Should we do that?

@rbygrave
Copy link
Contributor Author

Hi @vlsi @davecramer and anyone else participating here. Just checking in on ...

Are we keen to create a ResourceLock extends ReentrantLock implements AutoClosable as per (B)?
Do you want me to push another commit with that approach or keen to go with what we have with the finally blocks?

Cheers, Rob.

@vlsi
Copy link
Member

vlsi commented Oct 18, 2022

I incline to ResourceLock extends ReentrantLock implements AutoClosable.

@davecramer
Copy link
Member

I incline to ResourceLock extends ReentrantLock implements AutoClosable.

I'll defer to @vlsi as I am not an expert in this area

@rbygrave
Copy link
Contributor Author

Ok, added ResourceLock and using that with try-with-resources. Let me know what you think.

@rbygrave
Copy link
Contributor Author

Had a chance to look at it yet?

@vlsi
Copy link
Member

vlsi commented Oct 21, 2022

Had a chance to look at it yet?

In general, I like how it all goes, however, there are test failures, so I did not look closely.
Do you think you could figure out the reason for the test failures?

@davecramer
Copy link
Member

I'm surprised our code style check doesn't pick up on the lack of the header in ResourceLock and ResourceLockTest

@vlsi
Copy link
Member

vlsi commented Nov 8, 2022

I'm surprised our code style check doesn't pick up on the lack of the header in ResourceLock and ResourceLockTest

By default it prints limited number of warnings into the console to keep the log manageable/readable: https://github.com/pgjdbc/pgjdbc/actions/runs/3279472138/jobs/5613343062#step:4:288

For instance, if someone accidentally commits "change all LF to CRLF line endings", then we don't want to get a ton of lines with wrong line endings.

@Powerrr
Copy link
Contributor

Powerrr commented Dec 1, 2022

Do you think you could figure out the reason for the test failures?

I believe this happens because of incorrect Lock API usage: we shoudn't use .wait()/.notify() on locks - these methods are inherited from Object and are using the same object monitor as synchronized blocks.
It seems that the correct way is to use the Condition object that can be obtained using lock.newCondition(), something like

final ReentrantLock lock = new ReentrantLock();
final Condition lockCondition = lock.newCondition();

void foo() {
  lock.lock();
  try {
    lockCondition.await();
    // or
    lockCondition.signal();
  } finally {
    lock.unlock();
  }
}

And since we need only one Condition per lock (the substitute for the object monitor), we can add it to the ResourceLock helper:

public final class ResourceLock extends ReentrantLock implements AutoCloseable {
  private final Condition condition = this.newCondition();

  // add either the getter for the condition field or even some wrapper methods, for example:
  public void await() throws InterruptedException {
    condition.await();
  }

  //...
}

@gavlyukovskiy
Copy link

Hi, just a heads up - I've tried running some loom tests on this PR with -Djdk.tracePinnedThreads=full and virtual threads still pin on synchronized methods inside QueryExecutorImpl:

    org.postgresql.core.v3.QueryExecutorImpl.execute(QueryExecutorImpl.java:356) <== monitors:1
    org.postgresql.jdbc.PgStatement.executeInternal(PgStatement.java:497)
    org.postgresql.jdbc.PgStatement.execute(PgStatement.java:414)
    org.postgresql.jdbc.PgPreparedStatement.executeWithFlags(PgPreparedStatement.java:190)
    org.postgresql.jdbc.PgPreparedStatement.executeQuery(PgPreparedStatement.java:134)
    com.zaxxer.hikari.pool.ProxyPreparedStatement.executeQuery(ProxyPreparedStatement.java:52)
Full stacktrace:
Thread[#67,ForkJoinPool-1-worker-1,5,CarrierThreads]
    java.base/java.lang.VirtualThread$VThreadContinuation.onPinned(VirtualThread.java:180)
    java.base/jdk.internal.vm.Continuation.onPinned0(Continuation.java:398)
    java.base/jdk.internal.vm.Continuation.yield0(Continuation.java:390)
    java.base/jdk.internal.vm.Continuation.yield(Continuation.java:357)
    java.base/java.lang.VirtualThread.yieldContinuation(VirtualThread.java:370)
    java.base/java.lang.VirtualThread.park(VirtualThread.java:499)
    java.base/java.lang.System$2.parkVirtualThread(System.java:2596)
    java.base/jdk.internal.misc.VirtualThreads.park(VirtualThreads.java:54)
    java.base/java.util.concurrent.locks.LockSupport.park(LockSupport.java:369)
    java.base/sun.nio.ch.Poller.poll2(Poller.java:139)
    java.base/sun.nio.ch.Poller.poll(Poller.java:102)
    java.base/sun.nio.ch.Poller.poll(Poller.java:87)
    java.base/sun.nio.ch.NioSocketImpl.park(NioSocketImpl.java:175)
    java.base/sun.nio.ch.NioSocketImpl.park(NioSocketImpl.java:196)
    java.base/sun.nio.ch.NioSocketImpl.implRead(NioSocketImpl.java:304)
    java.base/sun.nio.ch.NioSocketImpl.read(NioSocketImpl.java:340)
    java.base/sun.nio.ch.NioSocketImpl$1.read(NioSocketImpl.java:789)
    java.base/java.net.Socket$SocketInputStream.read(Socket.java:1025)
    org.postgresql.core.VisibleBufferedInputStream.readMore(VisibleBufferedInputStream.java:161)
    org.postgresql.core.VisibleBufferedInputStream.ensureBytes(VisibleBufferedInputStream.java:128)
    org.postgresql.core.VisibleBufferedInputStream.ensureBytes(VisibleBufferedInputStream.java:113)
    org.postgresql.core.VisibleBufferedInputStream.read(VisibleBufferedInputStream.java:73)
    org.postgresql.core.PGStream.receiveChar(PGStream.java:453)
    org.postgresql.core.v3.QueryExecutorImpl.processResults(QueryExecutorImpl.java:2120)
    org.postgresql.core.v3.QueryExecutorImpl.execute(QueryExecutorImpl.java:356) <== monitors:1
    org.postgresql.jdbc.PgStatement.executeInternal(PgStatement.java:497)
    org.postgresql.jdbc.PgStatement.execute(PgStatement.java:414)
    org.postgresql.jdbc.PgPreparedStatement.executeWithFlags(PgPreparedStatement.java:190)
    org.postgresql.jdbc.PgPreparedStatement.executeQuery(PgPreparedStatement.java:134)
    com.zaxxer.hikari.pool.ProxyPreparedStatement.executeQuery(ProxyPreparedStatement.java:52)
    com.zaxxer.hikari.pool.HikariProxyPreparedStatement.executeQuery(HikariProxyPreparedStatement.java)
    com.github.gavlyukovskiy.app.PostgresRepository.getWorld(SpringWebApplication.kt:85)
    com.github.gavlyukovskiy.app.Controller$db$1.invoke(SpringWebApplication.kt:144)
    com.github.gavlyukovskiy.app.Controller$db$1.invoke(SpringWebApplication.kt:144)
    com.github.gavlyukovskiy.app.Controller.record(SpringWebApplication.kt:152)
    com.github.gavlyukovskiy.app.Controller.db(SpringWebApplication.kt:144)
    java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:104)
    java.base/java.lang.reflect.Method.invoke(Method.java:578)
    org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:207)
    org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:152)
    org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:117)
    org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:884)
    org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:797)
    org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87)
    org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1080)
    org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:973)
    org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1003)
    org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:895)
    jakarta.servlet.http.HttpServlet.service(HttpServlet.java:500)
    org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:880)
    jakarta.servlet.http.HttpServlet.service(HttpServlet.java:587)
    org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:223)
    org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:158)
    org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100)
    org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116)
    org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:185)
    org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:158)
    org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93)
    org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116)
    org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:185)
    org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:158)
    org.springframework.web.filter.ServerHttpObservationFilter.doFilterInternal(ServerHttpObservationFilter.java:109)
    org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116)
    org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:185)
    org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:158)
    org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201)
    org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116)
    org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:185)
    org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:158)
    org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:197)
    org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:97)
    org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:542)
    org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:119)
    org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92)
    org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:78)
    org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:357)
    org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:400)
    org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65)
    org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:861)
    org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1739)
    org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:52)
    java.base/java.util.concurrent.ThreadPerTaskExecutor$TaskRunner.run(ThreadPerTaskExecutor.java:314)
    java.base/java.lang.VirtualThread.run(VirtualThread.java:287)
    java.base/java.lang.VirtualThread$VThreadContinuation.lambda$new$0(VirtualThread.java:174)
    java.base/jdk.internal.vm.Continuation.enter0(Continuation.java:327)
    java.base/jdk.internal.vm.Continuation.enter(Continuation.java:320)

@rbygrave
Copy link
Contributor Author

rbygrave commented Dec 4, 2022

Hi, just a heads up - ... and virtual threads still pin on synchronized methods inside QueryExecutorImpl

Yes. I had a "next change" to push which includes QueryExecutorImpl, QueryExecutorBase and CopyOperationImpl. If people are happy with what we have so far I can push this as an extra commit to this PR or another separate PR. The changes here are the expected/mechanical changes + a change to CopyOperationImpl.isActive() where I have added a helper method and people reading the diff might need to look twice etc (I'll add a comment into the PR to point this bit out).

If this next workflow run goes all green (style fixes for imports and checkerframework suppress warning) and if everyone is happy with it we can choose to add those changes as another commit to this PR or create a separate PR.

@rbygrave
Copy link
Contributor Author

rbygrave commented Dec 4, 2022

This PR is ready to run again @davecramer @vlsi - I have fixed the style and checkerframework issues (and this time, I have run those all locally to ensure I got those correct touch wood)

@apatrida
Copy link

apatrida commented Feb 5, 2023

@vlsi nor does Heinz share the benchmark, odd.

@vlsi vlsi added the minor label Feb 18, 2023
@rbygrave rbygrave deleted the feature/1951-PgStatement-3 branch February 19, 2023 06:21
benkard added a commit to benkard/mulkcms2 that referenced this pull request Apr 4, 2023
This MR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [flow-bin](https://github.com/flowtype/flow-bin) ([changelog](https://github.com/facebook/flow/blob/master/Changelog.md)) | devDependencies | minor | [`^0.201.0` -> `^0.203.0`](https://renovatebot.com/diffs/npm/flow-bin/0.201.0/0.203.1) |
| [com.rometools:rome](http://rometools.com) ([source](https://github.com/rometools/rome)) | compile | minor | `2.0.0` -> `2.1.0` |
| [org.postgresql:postgresql](https://jdbc.postgresql.org) ([source](https://github.com/pgjdbc/pgjdbc)) | build | minor | `42.5.4` -> `42.6.0` |
| [com.diffplug.spotless:spotless-maven-plugin](https://github.com/diffplug/spotless) | build | minor | `2.34.0` -> `2.35.0` |
| [org.apache.maven.plugins:maven-resources-plugin](https://maven.apache.org/plugins/) | build | patch | `3.3.0` -> `3.3.1` |
| [io.quarkus:quarkus-maven-plugin](https://github.com/quarkusio/quarkus) | build | patch | `2.16.4.Final` -> `2.16.6.Final` |
| [io.quarkus:quarkus-universe-bom](https://github.com/quarkusio/quarkus-platform) | import | patch | `2.16.4.Final` -> `2.16.6.Final` |

---

### Release Notes

<details>
<summary>flowtype/flow-bin</summary>

### [`v0.203.1`](flow/flow-bin@0c16b26...5e0645d)

[Compare Source](flow/flow-bin@0c16b26...5e0645d)

### [`v0.203.0`](flow/flow-bin@861f798...0c16b26)

[Compare Source](flow/flow-bin@861f798...0c16b26)

### [`v0.202.1`](flow/flow-bin@2b48bba...861f798)

[Compare Source](flow/flow-bin@2b48bba...861f798)

### [`v0.202.0`](flow/flow-bin@86aea9c...2b48bba)

[Compare Source](flow/flow-bin@86aea9c...2b48bba)

</details>

<details>
<summary>rometools/rome</summary>

### [`v2.1.0`](https://github.com/rometools/rome/releases/tag/2.1.0)

[Compare Source](rometools/rome@2.0.0...2.1.0)

<!-- Release notes generated using configuration in .github/release.yml at 2.1.0 -->

#### What's Changed

##### ⭐ New Features

-   Downgrade Java from version 11 to 8 by [@&#8203;PatrickGotthard](https://github.com/PatrickGotthard) in rometools/rome#642
-   Add support for GraalVM native images by [@&#8203;artembilan](https://github.com/artembilan) in rometools/rome#636

##### 🔨 Dependency Upgrades

-   Bump maven-compiler-plugin from 3.10.1 to 3.11.0 by [@&#8203;dependabot](https://github.com/dependabot) in rometools/rome#635

##### 🧹 Cleanup

-   Remove unused config files by [@&#8203;PatrickGotthard](https://github.com/PatrickGotthard) in rometools/rome#632
-   Polish GitHub workflows by [@&#8203;PatrickGotthard](https://github.com/PatrickGotthard) in rometools/rome#633
-   Polish code by [@&#8203;antoniosanct](https://github.com/antoniosanct) in rometools/rome#631

##### ✔ Other Changes

-   Update configuration for automatically generated release notes by [@&#8203;PatrickGotthard](https://github.com/PatrickGotthard) in rometools/rome#634

#### New Contributors

-   [@&#8203;artembilan](https://github.com/artembilan) made their first contribution in rometools/rome#636

**Full Changelog**: rometools/rome@2.0.0...2.1.0

</details>

<details>
<summary>pgjdbc/pgjdbc</summary>

### [`v42.6.0`](https://github.com/pgjdbc/pgjdbc/blob/HEAD/CHANGELOG.md#&#8203;4260-2023-03-17-153434--0400)

##### Changed

fix: use PhantomReferences instead of `Obejct.finalize()` to track Connection leaks [MR #&#8203;2847](pgjdbc/pgjdbc#2847)

    The change replaces all uses of Object.finalize with PhantomReferences.
    The leaked resources (Connections) are tracked in a helper thread that is active as long as
    there are connections in use. By default, the thread keeps running for 30 seconds after all
    the connections are released. The timeout is set with pgjdbc.config.cleanup.thread.ttl system property.

refactor:(loom) replace the usages of synchronized with ReentrantLock [MR #&#8203;2635](pgjdbc/pgjdbc#2635)
Fixes [Issue #&#8203;1951](pgjdbc/pgjdbc#1951)

</details>

<details>
<summary>diffplug/spotless</summary>

### [`v2.35.0`](https://github.com/diffplug/spotless/blob/HEAD/CHANGES.md#&#8203;2350---2023-02-10)

##### Added

-   CleanThat Java Refactorer. ([#&#8203;1560](diffplug/spotless#1560))
-   Introduce `LazyArgLogger` to allow for lazy evaluation of log messages in slf4j logging. ([#&#8203;1565](diffplug/spotless#1565))

##### Fixed

-   Allow multiple instances of the same npm-based formatter to be used by separating their `node_modules` directories. ([#&#8203;1565](diffplug/spotless#1565))
-   `ktfmt` default style uses correct continuation indent. ([#&#8203;1562](diffplug/spotless#1562))

##### Changes

-   Bump default `ktfmt` version to latest `0.42` -> `0.43` ([#&#8203;1561](diffplug/spotless#1561))
-   Bump default `jackson` version to latest `2.14.1` -> `2.14.2` ([#&#8203;1536](diffplug/spotless#1536))

</details>

<details>
<summary>quarkusio/quarkus</summary>

### [`v2.16.6.Final`](https://github.com/quarkusio/quarkus/releases/tag/2.16.6.Final)

[Compare Source](quarkusio/quarkus@2.16.5.Final...2.16.6.Final)

##### Complete changelog

-   [#&#8203;32319](quarkusio/quarkus#32319) - \[2.16] Revert io.netty.noUnsafe change
-   [#&#8203;32302](quarkusio/quarkus#32302) - Qute - fix validation of expressions with the "cdi" namespace
-   [#&#8203;32253](quarkusio/quarkus#32253) - (2.16) Upgrade to graphql-java 19.4
-   [#&#8203;32223](quarkusio/quarkus#32223) - (2.16) Upgrade wildfly-elytron to 1.20.3.Final
-   [#&#8203;32110](quarkusio/quarkus#32110) - Prevent splitting of cookie header values when using AWS Lambda
-   [#&#8203;32107](quarkusio/quarkus#32107) - Fix Podman detection on Windows
-   [#&#8203;32106](quarkusio/quarkus#32106) - Native building with container: Podman not detected on Windows
-   [#&#8203;32093](quarkusio/quarkus#32093) - Re-use current ApplicationModel for JaCoCo reports when testing Gradle projects
-   [#&#8203;32090](quarkusio/quarkus#32090) - K8s moved its registry
-   [#&#8203;32088](quarkusio/quarkus#32088) - Remove the session cookie if ID token verification failed
-   [#&#8203;32082](quarkusio/quarkus#32082) - Add missing quote in Hibernate Reactive with Panache guide
-   [#&#8203;32079](quarkusio/quarkus#32079) - Quarkus JaCoCo extension fails to start Gradle daemon
-   [#&#8203;32058](quarkusio/quarkus#32058) - Allow use of null in REST Client request body
-   [#&#8203;32047](quarkusio/quarkus#32047) - rest client reactive throws npe on null request body
-   [#&#8203;32041](quarkusio/quarkus#32041) - K8s is moving it's images
-   [#&#8203;32037](quarkusio/quarkus#32037) - Set-Cookie Header is Split when using OIDC together with AWS Lambda
-   [#&#8203;32015](quarkusio/quarkus#32015) - Support repeatable Incomings annotation for reactive messaging
-   [#&#8203;32002](quarkusio/quarkus#32002) - Quarkus: Kafka Event Processor with 2 `@incoming` annotations throws Null Pointer SRMSG00212
-   [#&#8203;31984](quarkusio/quarkus#31984) - Only substitute OctetKeyPair\* classes when on the classpath
-   [#&#8203;31978](quarkusio/quarkus#31978) - Remove quarkus.hibernate-orm.database.generation=drop-and-create from Hibernate ORM codestart
-   [#&#8203;31930](quarkusio/quarkus#31930) - Native build fails for JWT
-   [#&#8203;31893](quarkusio/quarkus#31893) - Docker or Podman required for tests since 3.0.0.Alpha6
-   [#&#8203;31857](quarkusio/quarkus#31857) - Container runtime detection cached in sys prop, container-docker extension
-   [#&#8203;31811](quarkusio/quarkus#31811) - Check the expiry date for inactive OIDC tokens
-   [#&#8203;31717](quarkusio/quarkus#31717) - Quarkus OIDC Session Cookie not deleted in case of 401 unauthorized
-   [#&#8203;31714](quarkusio/quarkus#31714) - OIDC token refresh fails with 401, if user info is used and not available in the cache (anymore)
-   [#&#8203;31662](quarkusio/quarkus#31662) - Warning when docker is not running
-   [#&#8203;31525](quarkusio/quarkus#31525) - Bump Keycloak version to 21.0.1
-   [#&#8203;31490](quarkusio/quarkus#31490) - Enable Podman and Docker Windows quarkus-container-image-docker testing
-   [#&#8203;31307](quarkusio/quarkus#31307) - Native Build on Windows has incorrect resource slashes
-   [#&#8203;30383](quarkusio/quarkus#30383) - Create a new base classloader including parent-first test scoped dependencies when bootstrapping for CT

### [`v2.16.5.Final`](https://github.com/quarkusio/quarkus/releases/tag/2.16.5.Final)

[Compare Source](quarkusio/quarkus@2.16.4.Final...2.16.5.Final)

##### Complete changelog

-   [#&#8203;31959](quarkusio/quarkus#31959) - New home for Narayana LRA coordinator Docker images
-   [#&#8203;31931](quarkusio/quarkus#31931) - Support raw collections in RESTEasy Reactive server and client
-   [#&#8203;31922](quarkusio/quarkus#31922) - Add more lenient Liquibase ZipPathHandler to work around includeAll not working in prod mode
-   [#&#8203;31904](quarkusio/quarkus#31904) - \[2.16] Upgrade SmallRye GraphQL to 1.9.4
-   [#&#8203;31894](quarkusio/quarkus#31894) - Supply missing extension metadata for reactive keycloak client
-   [#&#8203;31891](quarkusio/quarkus#31891) - Fix truststore REST Client config when password is not set
-   [#&#8203;31867](quarkusio/quarkus#31867) - Qute type-safe fragments - fix validation for loop metadata and globals
-   [#&#8203;31866](quarkusio/quarkus#31866) -  The behavior of the `@RestHeader` annotation is different from the `@HeaderParam` annotation when the parameter is of type List
-   [#&#8203;31864](quarkusio/quarkus#31864) - Fix incorrect generic type passed to MessageBodyWriter#writeTo
-   [#&#8203;31818](quarkusio/quarkus#31818) - Jackson JAX-RS YAML Provider for Resteasy Reactive
-   [#&#8203;31804](quarkusio/quarkus#31804) - \[2.16] A test to make sure non-existing modules are ignored during workspace discovery
-   [#&#8203;31793](quarkusio/quarkus#31793) - \[2.16] Fix NPE loading workspace modules
-   [#&#8203;31770](quarkusio/quarkus#31770) - Fix native compilation when using quarkus-jdbc-oracle with elasticsearch-java
-   [#&#8203;31769](quarkusio/quarkus#31769) - Capability added for quarkus-rest-client-reactive-jackson
-   [#&#8203;31756](quarkusio/quarkus#31756) - quarkus-rest-client-reactive-jackson doesn't provide capabilities
-   [#&#8203;31728](quarkusio/quarkus#31728) - Register additional cache implementations for reflection
-   [#&#8203;31718](quarkusio/quarkus#31718) - Properly close metadata file in integration tests
-   [#&#8203;31713](quarkusio/quarkus#31713) - "Too many open files" When test native image.
-   [#&#8203;31712](quarkusio/quarkus#31712) - Make request scoped beans work properly in ReaderInterceptors
-   [#&#8203;31705](quarkusio/quarkus#31705) - Remove all dev services for kubernetes dependencies from kubernetes-client-internal
-   [#&#8203;31692](quarkusio/quarkus#31692) - RequestScoped context not active when using a ReaderInterceptor with large HTTP requests
-   [#&#8203;31688](quarkusio/quarkus#31688) - Suppress config changed warning for quarkus.test.arg-line
-   [#&#8203;31643](quarkusio/quarkus#31643) - Fix iterator issue when executing a zrange with score on a missing key
-   [#&#8203;31626](quarkusio/quarkus#31626) - quarkus.test.arg-line has become a built-time fixed property in 2.16.4
-   [#&#8203;31624](quarkusio/quarkus#31624) - native compilation : quarkus-jdbc-oracle with elasticsearch-java strange behaviour
-   [#&#8203;31617](quarkusio/quarkus#31617) - Bump Stork version 1.4.2
-   [#&#8203;31579](quarkusio/quarkus#31579) - Reinitialize sun.security.pkcs11.P11Util at runtime
-   [#&#8203;31560](quarkusio/quarkus#31560) - Prevent SSE writing from potentially causing accumulation of headers
-   [#&#8203;31559](quarkusio/quarkus#31559) - `SseUtil` unexpectedly stores headers in `Serialisers.EMPTY_MULTI_MAP`
-   [#&#8203;31551](quarkusio/quarkus#31551) - Scheduler - detect scheduled methods of the same name on a class
-   [#&#8203;31547](quarkusio/quarkus#31547) - Scheduler - it's possible to declare two scheduled methods of the same name on the same class
-   [#&#8203;31545](quarkusio/quarkus#31545) - Append System.lineSeparator() to config error messages
-   [#&#8203;31536](quarkusio/quarkus#31536) - Missing newline characters in config error message
-   [#&#8203;31532](quarkusio/quarkus#31532) - Interpret negative/zero body-limit as infinite when logging REST Client request body
-   [#&#8203;31523](quarkusio/quarkus#31523) - Request rejected by CORS for fonts in dev UI when `quarkus.http.cors=true` is set
-   [#&#8203;31496](quarkusio/quarkus#31496) - Filter out RESTEasy related warning in ProviderConfigInjectionWarningsTest
-   [#&#8203;31482](quarkusio/quarkus#31482) - Remove incorrect default value for keepAliveEnabled
-   [#&#8203;31440](quarkusio/quarkus#31440) - Several quarkus integration tests fail to compile to native with latest GraalVM master
-   [#&#8203;31384](quarkusio/quarkus#31384) - Ignore required documentation for `@ConfigMapping` default methods
-   [#&#8203;30757](quarkusio/quarkus#30757) - Allow same origin CORS requests without 3rd party origins being configured
-   [#&#8203;30744](quarkusio/quarkus#30744) - \[Quarkus Native] ClassNotFoundException: com.github.benmanes.caffeine.cache.SSSW
-   [#&#8203;30698](quarkusio/quarkus#30698) - CORS Request same origin ignored if no other origin set

</details>

<details>
<summary>quarkusio/quarkus-platform</summary>

### [`v2.16.6.Final`](quarkusio/quarkus-platform@2.16.5.Final...2.16.6.Final)

[Compare Source](quarkusio/quarkus-platform@2.16.5.Final...2.16.6.Final)

### [`v2.16.5.Final`](quarkusio/quarkus-platform@2.16.4.Final...2.16.5.Final)

[Compare Source](quarkusio/quarkus-platform@2.16.4.Final...2.16.5.Final)

</details>

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined), Automerge - At any time (no schedule defined).

🚦 **Automerge**: Enabled.

♻ **Rebasing**: Whenever MR is behind base branch, or you tick the rebase/retry checkbox.

👻 **Immortal**: This MR will be recreated if closed unmerged. Get [config help](https://github.com/renovatebot/renovate/discussions) if that's undesired.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this MR, check this box

---

This MR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiIzNC4yNC4wIiwidXBkYXRlZEluVmVyIjoiMzQuMjQuMCJ9-->
wendigo added a commit to wendigo/trino that referenced this pull request Apr 15, 2023
This version introduces Loom-friendly synchronization (pgjdbc/pgjdbc#2635)
ebyhr pushed a commit to ebyhr/trino that referenced this pull request Apr 17, 2023
This version introduces Loom-friendly synchronization (pgjdbc/pgjdbc#2635)

(cherry picked from commit c2bf5aa)
electrum pushed a commit to trinodb/trino that referenced this pull request Apr 18, 2023
This version introduces Loom-friendly synchronization (pgjdbc/pgjdbc#2635)
@jerometambo
Copy link

Hi, there is still some synchronized occurences (in core/v3/QueryExecutorImpl.java and util/LazyCleaner.java). Is it safe to use pgjdbc in multithread execution with Java 21 without potential performance issues ?

@vlsi
Copy link
Member

vlsi commented Mar 12, 2024

Java 21 supports synchronized, so go ahead and use pgjdbc with Java 21

@jerometambo
Copy link

jerometambo commented Mar 12, 2024

Yes but what if we start using Virtual Threads (cf. https://blog.fastthread.io/2023/02/28/pitfalls-to-avoid-when-switching-to-virtual-threads/ for example) ?

@rob-bygrave
Copy link

rob-bygrave commented Mar 13, 2024

The use of synchronised in QueryExecutorImpl is not an issue for Virtual Threads.

Looking at: https://github.com/pgjdbc/pgjdbc/blob/master/pgjdbc/src/main/java/org/postgresql/core/v3/QueryExecutorImpl.java#L2967-L3040 ... we note there is that there is nothing executed INSIDE the synchronized blocks (like IO etc) that would park a Virtual Thread. That is, the code executed inside the synchronised blocks are just manipulations of the local useBinaryReceiveForOids HashSet to no Virtual Thread parking will incur in there and so this is all fine.

LazyCleaner to me is similar apart from the method https://github.com/pgjdbc/pgjdbc/blob/master/pgjdbc/src/main/java/org/postgresql/util/LazyCleaner.java#L105-L169 which includes Thread thread = threadFactory.newThread(new Runnable() { .... The question here is, could a Virtual Thread park with the threadFactory.newThread() call? For myself I'm not sure.

Note: For myself, I do have applications using Virtual Threads (via Helidon 4.x so all requests are using Virtual Threads for these apps) and these apps have all been working fine - I've observed no issues with thread pinning etc. Not sure if that is because threadFactory.newThread() doesn't park VT or more if I've been lucky somehow.

Q: Can we confirm if threadFactory.newThread() can park a virtual thread?
Q: Do we want to change LazyCleaner over to ReentrantLock just to be safer?

@davecramer
Copy link
Member

The use of synchronised in QueryExecutorImpl is not an issue for Virtual Threads.

Looking at: https://github.com/pgjdbc/pgjdbc/blob/master/pgjdbc/src/main/java/org/postgresql/core/v3/QueryExecutorImpl.java#L2967-L3040 ... we note there is that there is nothing executed INSIDE the synchronized blocks (like IO etc) that would park a Virtual Thread. That is, the code executed inside the synchronised blocks are just manipulations of the local useBinaryReceiveForOids HashSet to no Virtual Thread parking will incur in there and so this is all fine.

LazyCleaner to me is similar apart from the method https://github.com/pgjdbc/pgjdbc/blob/master/pgjdbc/src/main/java/org/postgresql/util/LazyCleaner.java#L105-L169 which includes Thread thread = threadFactory.newThread(new Runnable() { .... The question here is, could a Virtual Thread park with the threadFactory.newThread() call? For myself I'm not sure.

Note: For myself, I do have applications using Virtual Threads (via Helidon 4.x so all requests are using Virtual Threads for these apps) and these apps have all been working fine - I've observed no issues with thread pinning etc. Not sure if that is because threadFactory.newThread() doesn't park VT or more if I've been lucky somehow.

Q: Can we confirm if threadFactory.newThread() can park a virtual thread? Q: Do we want to change LazyCleaner over to ReentrantLock just to be safer?

Are there any issues with changing this to ReentrantLock ?

I have no objections. Ideally Java 8 goes away (haha) and we get rid of this code :)

@vlsi
Copy link
Member

vlsi commented Mar 15, 2024

Q: Can we confirm if threadFactory.newThread() can park a virtual thread?

I think it should not park. It is just creating a new thread, see

this(threadTtl, runnable -> {
Thread thread = new Thread(runnable, threadName);
thread.setDaemon(true);
return thread;
});

Q: Do we want to change LazyCleaner over to ReentrantLock just to be safer?

I'm +-0 on that. Of course, as we already use ReentrantLock, it should not be much worse if we refactor LazyCleaner. It would deviate from the original source though.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

10 participants