June 21, 2017·4 min read·engineeringpostgresqljavaparallel-queryextensions

PL/Java adds parallel declarations with a JVM startup cost

PL/Java 1.5.1 beta adds PostgreSQL 9.6 and 10 compatibility. Parallel-safety declarations expose both correctness obligations and the cost of starting Java in worker processes.

Allowing a Java function to run in a PostgreSQL parallel worker can require that worker to start its own Java virtual machine. That makes a parallel-safety declaration a decision with execution costs as well as correctness requirements. The June 21 PL/Java 1.5.1 beta announcement introduces those declarations while explicitly describing the support as an area that needs further testing.

PL/Java provides PostgreSQL functions, triggers, and types implemented in Java. This beta adds compatibility with PostgreSQL 9.6 and the PostgreSQL 10 beta. It also removes code associated with a previously announced security issue and improves packaging configuration. Parallel execution is the most instructive part because it exposes how a language runtime interacts with PostgreSQL's process model.

I value PostgreSQL's extension facilities, but an extension's runtime has to be included in the reasoning about a query. Looking only at a Java method's source is insufficient when the method depends on machinery that starts and runs inside a database process. This beta's documentation makes that dependency explicit.

Three labels permit different execution locations

The parallel-query guide at the beta tag defines the consequences of the labels. A function declared UNSAFE prevents its containing query from being parallelized. That is the default. A RESTRICTED function permits parallel work elsewhere in the query, but the function itself runs only in the lead process.

A SAFE function can execute in every participating process. That is a stronger claim about the implementation's behavior, and it gives the planner more freedom about placement. The labels describe which execution arrangements are permitted. They do not promise that the planner will choose a particular parallel plan.

This distinction is useful even for functions that cannot run in workers. Marking a suitable function RESTRICTED can leave room for other parts of a query to execute in parallel. It therefore expresses more information than the default blanket restriction without claiming that the function itself is safe everywhere.

PostgreSQL checks some prohibited operations, but the PL/Java guide says it largely depends on correct labeling. A declaration cannot make an unsafe operation safe. Incorrect RESTRICTED or SAFE labels can lead to unpredictable results, so the label must describe the implementation and its dependencies accurately.

A Java method is only part of the implementation

The announcement reports that simple parallel cases work. It also says PL/Java's internals have not been thoroughly audited for behavior that could violate the restrictions on parallel functions. That qualification applies even when the application method itself appears suitable by inspection.

The beta release notes call the support experimental. An audit must also cover the code that supports execution of the Java method, so inspecting the method cannot close the whole question. Both the method and the language implementation participate in the same database operation.

This is a precise limitation of the beta. It does not say every parallel Java function is broken, and it does not claim all internal cases have been proved correct. It identifies what has been tested and what remains uncertain. That information defines a useful scope for beta testing beyond merely compiling an existing function against a new server.

Each worker can incur Java initialization

The guide explains that PostgreSQL parallel queries use multiple operating-system processes and that these processes are new for each parallel query. If the planner pushes a SAFE PL/Java function into workers, each new worker that runs it starts a JVM. That setup can consume the benefit the parallel execution was intended to provide.

The documented way to inform the planner about this tradeoff is to increase parallel_setup_cost. It changes the planner's cost assessment of starting parallel execution. The guide also points to JVM options, including class-data sharing, as ways to reduce startup overhead. Neither measure removes the need for the function to satisfy its declared safety restrictions.

I find this a better account of performance than treating a parallel label as a speed setting. Permission to distribute work and the cost of doing so are separate properties. A method with enough useful work may justify startup overhead, while a small operation can spend too much of its execution starting runtimes.

PostgreSQL compatibility includes more than declarations

The beta widens tuple-count handling for PostgreSQL 9.6. Its SPI JDBC implementation adds executeLargeBatch and getLargeUpdateCount, returning long counts as specified by JDK 8. Existing count methods remain, with defined behavior when the result exceeds their integer range. This is compatibility work that affects the meaning of results returned to Java callers.

For the PostgreSQL 10 beta, PL/Java supports transition tables in AFTER triggers. Trigger annotations can name old and new transition tables, and the trigger's Java function can query them through SPI JDBC. That support is tied to PostgreSQL 10 in development, not to an assertion that the final server release is already available.

The security change removes the undocumented large-object code associated with CVE-2016-0768. The release notes say applications can instead manipulate large objects through SPI JDBC and PostgreSQL's large-object SQL functions. Packaging changes separately let maintainers supply a conventional pljava.libjvm_location default and choose the build's pg_config through Maven options. These changes put more of the integration configuration into documented interfaces, while parallel declarations make execution placement an explicit property of each function.