R2DBC R2DBC: A standard API for reactive programming using SQL databases. https://r2dbc.github.io/ Fri, 12 Sep 2025 12:47:11 +0000 Fri, 12 Sep 2025 12:47:11 +0000 Jekyll v3.10.0 R2DBC 1.0 goes GA <p>Dear R2DBC Community,</p> <p>On behalf of the entire community and everyone who contributed, it is my special honour to announce the general availability of the Reactive Relational Database Connectivity Specification version <code class="language-plaintext highlighter-rouge">1.0.0.RELEASE</code>.</p> <p>After 268 tickets and five years of work on the specification, R2DBC finally arrived at a state where we feel confident to publish a 1.0 version of the specification. Our previous releases <code class="language-plaintext highlighter-rouge">0.8</code> and <code class="language-plaintext highlighter-rouge">0.9</code> helped with incubation and validation of the spec to make sure we’re on the right path towards a reliable database connectivity specification. The huge success in adoption helped us to understand where R2DBC needs to go and it helped to build a community around our efforts.</p> <p>Amongst other features, the specification contains the following highlighted features:</p> <ul> <li>Driver SPI and TCK (Technology Compatibility Kit)</li> <li>Integration with BLOB and CLOB types</li> <li>Extensible Transaction Definitions</li> <li>Plain and Parameterized Statements („Prepared Statements“)</li> <li>Support for Stored Procedures/Server Side Functions along with <code class="language-plaintext highlighter-rouge">IN</code> and <code class="language-plaintext highlighter-rouge">OUT</code> Parameter bindings</li> <li>Consumption of Update Counts, Rows, and Stored Procedure Results</li> <li>Batch operations</li> <li>Categorized Exceptions</li> <li>ServiceLoader-based Driver Discovery</li> <li>Connection URL scheme</li> <li>Programmatic Configuration API</li> </ul> <p>Special thanks go to the driver and implementor community for your discussions and help without whom it wouldn’t be possible to achieve a stable 1.0 specification state.</p> <p>The previous <code class="language-plaintext highlighter-rouge">0.8</code> and <code class="language-plaintext highlighter-rouge">0.9</code> releases were accompanied with a release train BOM (Bill of Materials) for easier consumption. With the 1.0 release we no longer maintain a bill of materials to decouple specification development and to enable implementors to ship drivers and R2DBC libraries on their own pace. The 1.0 release defines a stable state for the foreseeable future and we expect driver vendors to upgrade to R2DBC 1.0 throughout the year.</p> <p>This change in the release strategy removes the need to maintain an additional release train.</p> <p>A few resources with details about this release:</p> <ul> <li><a href="https://www.youtube.com/watch?v=kKyiLcFFe2E">Introduction into R2DBC</a></li> <li><a href="https://r2dbc.io/spec/1.0.0.RELEASE/api/">Javadoc</a></li> <li><a href="https://r2dbc.io/spec/1.0.0.RELEASE/spec/html/">R2DBC Specification</a></li> </ul> <h3 id="release-artifacts">Release artifacts</h3> <ul> <li><a href="https://repo1.maven.org/maven2/io/r2dbc/">Binaries</a></li> <li><a href="https://r2dbc.io/spec/1.0.0.RELEASE/api/">Javadoc</a></li> <li><a href="https://r2dbc.io/spec/1.0.0.RELEASE/spec/html/">Specification</a></li> <li><a href="https://r2dbc.io/spec/1.0.0.RELEASE/CHANGELOG.txt">Changelog</a></li> </ul> Mon, 25 Apr 2022 13:00:00 +0000 https://r2dbc.github.io/2022/04/25/r2dbc-1.0-goes-ga.html https://r2dbc.github.io/2022/04/25/r2dbc-1.0-goes-ga.html release R2DBC Borca-RELEASE released <p>It is my pleasure to announce that, after more than two years of specification work, milestones and RCs we can finally conclude the efforts of R2DBC 0.9 with the Borca release train being generally available from Maven Central.</p> <p>The release train bundles the R2DBC 0.9 specification and all compliant implementations within a release train by providing a bill of materials to ensure a consistent set of dependencies for your reactive SQL database workloads.</p> <p>Specifically, this release train ships with:</p> <ul> <li><code class="language-plaintext highlighter-rouge">io.r2dbc:r2dbc-spi:0.9.1.RELEASE</code> - <a href="https://r2dbc.io/2021/12/06/r2dbc-0.9.0-goes-ga">R2DBC SPI 0.9.1.RELEASE</a></li> <li><code class="language-plaintext highlighter-rouge">io.r2dbc:r2dbc-h2:0.9.1.RELEASE</code> - <a href="https://github.com/r2dbc/r2dbc-h2/releases/tag/v0.9.1.RELEASE">R2DBC H2 driver 0.9.1.RELEASE</a></li> <li><code class="language-plaintext highlighter-rouge">org.mariadb:r2dbc-mariadb:1.1.1-rc</code> - <a href="https://github.com/mariadb-corporation/mariadb-connector-r2dbc">MariaDB R2DBC connector 1.1.1-rc</a></li> <li><code class="language-plaintext highlighter-rouge">com.oracle.database.r2dbc:oracle-r2dbc:0.4.0</code> - <a href="https://github.com/oracle/oracle-r2dbc/releases/tag/0.4.0">Oracle R2DBC driver 0.4.0</a></li> <li><code class="language-plaintext highlighter-rouge">io.r2dbc:r2dbc-pool:0.9.0.RELEASE</code> - <a href="https://github.com/r2dbc/r2dbc-pool/releases/tag/v0.9.0.RELEASE">R2DBC Pool 0.9.0.RELEASE</a></li> <li><code class="language-plaintext highlighter-rouge">org.postgresql:r2dbc-postgresql:0.9.0.RELEASE</code> - <a href="https://github.com/pgjdbc/r2dbc-postgresql/releases/tag/v0.9.0.RELEASE">R2DBC Postgres driver 0.9.0.RELEASE</a></li> <li><code class="language-plaintext highlighter-rouge">io.r2dbc:r2dbc-proxy:0.9.0.RELEASE</code> - <a href="https://github.com/r2dbc/r2dbc-proxy/releases/tag/v0.9.0.RELEASE">R2DBC Proxy 0.9.0.RELEASE</a></li> <li><code class="language-plaintext highlighter-rouge">io.r2dbc:r2dbc-mssql:0.9.0.RELEASE</code> - <a href="https://github.com/r2dbc/r2dbc-mssql/releases/tag/v0.9.0.RELEASE">R2DBC SQL Server driver 0.9.0.RELEASE</a></li> </ul> <p>If you use Maven, you can add the following lines to your pom.xml:</p> <div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;dependencyManagement&gt;</span> <span class="nt">&lt;dependencies&gt;</span> <span class="nt">&lt;dependency&gt;</span> <span class="nt">&lt;groupId&gt;</span>io.r2dbc<span class="nt">&lt;/groupId&gt;</span> <span class="nt">&lt;artifactId&gt;</span>r2dbc-bom<span class="nt">&lt;/artifactId&gt;</span> <span class="nt">&lt;version&gt;</span>Borca-RELEASE<span class="nt">&lt;/version&gt;</span> <span class="nt">&lt;type&gt;</span>pom<span class="nt">&lt;/type&gt;</span> <span class="nt">&lt;scope&gt;</span>import<span class="nt">&lt;/scope&gt;</span> <span class="nt">&lt;/dependency&gt;</span> <span class="nt">&lt;/dependencies&gt;</span> <span class="nt">&lt;/dependencyManagement&gt;</span> <span class="nt">&lt;dependencies&gt;</span> <span class="nt">&lt;dependency&gt;</span> <span class="nt">&lt;groupId&gt;</span>org.postgresql<span class="nt">&lt;/groupId&gt;</span> <span class="nt">&lt;artifactId&gt;</span>r2dbc-postgres<span class="nt">&lt;/artifactId&gt;</span> <span class="nt">&lt;/dependency&gt;</span> <span class="nt">&lt;/dependencies&gt;</span> </code></pre></div></div> <p>With the transition of the Borca release train to GA, we plan to ship two final rounds of service releases for the <code class="language-plaintext highlighter-rouge">Arabba</code> release train (R2DBC 0.8) later this year before transitioning the <code class="language-plaintext highlighter-rouge">Arabba</code> release train into EOL mode after Q2/2022. Each implementation can decide whether they are going to ship maintenance releases supporting R2DBC 0.8. We are also working towards R2DBC 1.0 GA. The backlog is sorted and the 1.0 GA release is going to be a lighter revision of the specification where we iron out any last rough edges. R2DBC 1.0 is going to be the version that serves as reactive database integration spec for the foreseeable future.</p> Fri, 04 Feb 2022 06:00:00 +0000 https://r2dbc.github.io/2022/02/04/r2dbc-borca-released.html https://r2dbc.github.io/2022/02/04/r2dbc-borca-released.html release R2DBC Arabba-SR12 released <p>On behalf of the community and everyone who contributed, we’re pleased to announce availability of R2DBC <code class="language-plaintext highlighter-rouge">Arabba-SR12</code> available from Maven Central!</p> <p>This service release ships the following modules:</p> <ul> <li><a href="https://github.com/GoogleCloudPlatform/cloud-spanner-r2dbc/releases/tag/v1.1.0">Google Cloud Spanner Driver - 1.1.0</a> (<code class="language-plaintext highlighter-rouge">com.google.cloud:cloud-spanner-r2dbc</code>)</li> <li>MariaDB connector - 1.0.3 (<code class="language-plaintext highlighter-rouge">org.mariadb:r2dbc-mariadb</code>)</li> <li><a href="https://github.com/r2dbc/r2dbc-h2/milestone/11?closed=1">H2 driver - 0.8.5.RELEASE</a> (<code class="language-plaintext highlighter-rouge">io.r2dbc:r2dbc-h2</code>)</li> <li><a href="https://github.com/pgjdbc/r2dbc-postgresql/milestone/22?closed=1">PostgreSQL driver - 0.8.11.RELEASE</a> (<code class="language-plaintext highlighter-rouge">io.r2dbc:r2dbc-postgresql</code>)</li> <li><a href="https://github.com/r2dbc/r2dbc-pool/milestone/14?closed=1">R2DBC Pool - 0.8.8.RELEASE</a> (<code class="language-plaintext highlighter-rouge">io.r2dbc:r2dbc-pool</code>)</li> <li><a href="https://github.com/r2dbc/r2dbc-mssql/milestone/16?closed=1">SQL Server driver - 0.8.8.RELEASE</a> (<code class="language-plaintext highlighter-rouge">io.r2dbc:r2dbc-postgresql</code>)</li> </ul> <p>This is a maintenance release shipping with mostly bugfixes fixes, selected enhancements, and dependency upgrades. Check out the changelogs associated with each component.</p> <p>We recommend version management by using R2DBC BOM as individual modules follow their own version number schedule.</p> <p>If you use Maven, you can add the following lines to your <code class="language-plaintext highlighter-rouge">pom.xml</code>:</p> <div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;dependencyManagement&gt;</span> <span class="nt">&lt;dependencies&gt;</span> <span class="nt">&lt;dependency&gt;</span> <span class="nt">&lt;groupId&gt;</span>io.r2dbc<span class="nt">&lt;/groupId&gt;</span> <span class="nt">&lt;artifactId&gt;</span>r2dbc-bom<span class="nt">&lt;/artifactId&gt;</span> <span class="nt">&lt;version&gt;</span>Arabba-SR12<span class="nt">&lt;/version&gt;</span> <span class="nt">&lt;type&gt;</span>pom<span class="nt">&lt;/type&gt;</span> <span class="nt">&lt;scope&gt;</span>import<span class="nt">&lt;/scope&gt;</span> <span class="nt">&lt;/dependency&gt;</span> <span class="nt">&lt;/dependencies&gt;</span> <span class="nt">&lt;/dependencyManagement&gt;</span> <span class="nt">&lt;dependencies&gt;</span> <span class="nt">&lt;dependency&gt;</span> <span class="nt">&lt;groupId&gt;</span>com.google.cloud<span class="nt">&lt;/groupId&gt;</span> <span class="nt">&lt;artifactId&gt;</span>cloud-spanner-r2dbc<span class="nt">&lt;/artifactId&gt;</span> <span class="nt">&lt;/dependency&gt;</span> <span class="nt">&lt;/dependencies&gt;</span> </code></pre></div></div> Thu, 13 Jan 2022 06:00:00 +0000 https://r2dbc.github.io/2022/01/13/r2dbc-arabba-sr12-released.html https://r2dbc.github.io/2022/01/13/r2dbc-arabba-sr12-released.html release R2DBC 0.9 goes GA <p>Dear community,</p> <p>It is my pleasure to announce the general availability of R2DBC <code class="language-plaintext highlighter-rouge">0.9.0.RELEASE</code> from Maven Central!</p> <p>This revision of the R2DBC specification has been in the works for roughly two years and ships numerous enhancements to the SPI and specification:</p> <ul> <li>Extensible <a href="https://r2dbc.io/spec/0.9.0.RELEASE/api/io/r2dbc/spi/TransactionDefinition.html">Transaction Definitions</a>.</li> <li>Improved <a href="https://r2dbc.io/spec/0.9.0.RELEASE/api/io/r2dbc/spi/Parameters.html">Bind-Parameter Declaration</a></li> <li>Consumption of OUT Parameters by introducing <a href="https://r2dbc.io/spec/0.9.0.RELEASE/api/io/r2dbc/spi/OutParameters.html"><code class="language-plaintext highlighter-rouge">OutParameters</code></a>. <code class="language-plaintext highlighter-rouge">Row</code> and <code class="language-plaintext highlighter-rouge">OutParameters</code> now share the common superinterface <a href="https://r2dbc.io/spec/0.9.0.RELEASE/api/io/r2dbc/spi/Readable.html"><code class="language-plaintext highlighter-rouge">Readable</code></a>.</li> <li>Introduction of the <a href="https://r2dbc.io/spec/0.9.0.RELEASE/api/io/r2dbc/spi/Result.html#map-java.util.function.Function-"><code class="language-plaintext highlighter-rouge">Result.map(Function&lt;Readable, T&gt;)</code></a> method.</li> <li>Introduction of the <a href="https://r2dbc.io/spec/0.9.0.RELEASE/api/io/r2dbc/spi/Result.Segment.html"><code class="language-plaintext highlighter-rouge">Result.Segment</code></a> API type hierarchy and <code class="language-plaintext highlighter-rouge">Result.filter(Predicate&lt;Segment&gt;)</code> and <code class="language-plaintext highlighter-rouge">Result.flatMap(Function&lt;Segment, Publisher&lt;T&gt;&gt;)</code> methods to consume arbitrary statement results.</li> <li>Lifecycle support by introducing the <a href="https://r2dbc.io/spec/0.9.0.RELEASE/api/io/r2dbc/spi/Lifecycle.html"><code class="language-plaintext highlighter-rouge">Lifecycle</code></a> interface.</li> <li>Removal of generic type of <a href="https://r2dbc.io/spec/0.9.0.RELEASE/api/io/r2dbc/spi/ConnectionFactoryOptions.html#getValue-io.r2dbc.spi.Option-"><code class="language-plaintext highlighter-rouge">ConnectionFactoryOptions.getValue(Option)</code></a> and <code class="language-plaintext highlighter-rouge">getRequiredValue(…)</code>.</li> <li>Deprecate <code class="language-plaintext highlighter-rouge">RowMetadata.getColumnNames()</code> and introduce <a href="https://r2dbc.io/spec/0.9.0.RELEASE/api/io/r2dbc/spi/RowMetadata.html#contains-java.lang.String-"><code class="language-plaintext highlighter-rouge">RowMetadata.contains(String)</code></a> to simplify constraints and usage around column presence checks.</li> <li>Support for Lock Wait and Statement Timeouts through <a href="https://r2dbc.io/spec/0.9.0.RELEASE/api/io/r2dbc/spi/Connection.html#setLockWaitTimeout-java.time.Duration-"><code class="language-plaintext highlighter-rouge">Connection.setLockWaitTimeout(Duration)</code></a> and <a href="https://r2dbc.io/spec/0.9.0.RELEASE/api/io/r2dbc/spi/Connection.html#setStatementTimeout-java.time.Duration-"><code class="language-plaintext highlighter-rouge">Connection.setStatementTimeout(Duration)</code></a> methods.</li> </ul> <p>In order to make the specification consumable, R2DBC implementors can now pick up the specification and ship releases of their drivers. Looking forward, we expect a release train release (<code class="language-plaintext highlighter-rouge">r2dbc-bom</code> code name Borca-GA) in early February 2022. Until then, try out RC-level releases of R2DBC drivers and learn about the new and noteworthy:</p> <p>A few resources with details about this release:</p> <ul> <li><a href="https://r2dbc.io/spec/0.9.0.RELEASE/api/">Javadoc</a></li> <li><a href="https://r2dbc.io/spec/0.9.0.RELEASE/spec/html/">R2DBC Specification</a></li> <li><a href="https://r2dbc.io/2021/02/25/r2dbc-0.9.m1-released">R2DBC SPI 0.9 M1 Blog Post explaining Extensible Transaction Definitions and Improved Bind-Parameter Declaration</a></li> <li><a href="https://r2dbc.io/2021/08/31/r2dbc-0.9.m2-available">R2DBC SPI 0.9 M2 Blog Post explaining OUT-Parameters and the Result Segment API</a></li> </ul> <h3 id="release-artifacts">Release artifacts</h3> <ul> <li><a href="https://repo1.maven.org/maven2/io/r2dbc/">Binaries</a></li> <li><a href="https://r2dbc.io/spec/0.9.0.RELEASE/api/">Javadoc</a></li> <li><a href="https://r2dbc.io/spec/0.9.0.RELEASE/spec/html/">Specification</a></li> <li><a href="https://r2dbc.io/spec/0.9.0.RELEASE/CHANGELOG.txt">Changelog</a></li> </ul> <p>Beyond this release, we prepare ourselves for the R2DBC <strong>1.0</strong> GA release in Q1/Q2 2022. Stay tuned.</p> Mon, 06 Dec 2021 09:00:00 +0000 https://r2dbc.github.io/2021/12/06/r2dbc-0.9.0-goes-ga.html https://r2dbc.github.io/2021/12/06/r2dbc-0.9.0-goes-ga.html release R2DBC Arabba-SR11 released <p>On behalf of the community and everyone who contributed, we’re pleased to announce availability of R2DBC <code class="language-plaintext highlighter-rouge">Arabba-SR11</code> available from Maven Central!</p> <p>This service release ships the following modules:</p> <ul> <li><a href="https://github.com/r2dbc/r2dbc-spi/milestone/15?closed=1">R2DBC Specification - 0.8.6.RELEASE</a> (<code class="language-plaintext highlighter-rouge">io.r2dbc:r2dbc-spi</code>)</li> <li><a href="https://github.com/GoogleCloudPlatform/cloud-spanner-r2dbc/releases/tag/v0.6.0">Google Cloud Spanner Driver - 0.6.0</a> (<code class="language-plaintext highlighter-rouge">com.google.cloud:cloud-spanner-r2dbc</code>)</li> <li><a href="https://github.com/mariadb-corporation/mariadb-connector-r2dbc/releases/tag/1.0.2">MariaDB connector - 1.0.2</a> (<code class="language-plaintext highlighter-rouge">org.mariadb:r2dbc-mariadb</code>)</li> <li><a href="https://github.com/pgjdbc/r2dbc-postgresql/milestone/19?closed=1">PostgreSQL driver - 0.8.10.RELEASE</a> (<code class="language-plaintext highlighter-rouge">io.r2dbc:r2dbc-postgresql</code>)</li> <li><a href="https://github.com/r2dbc/r2dbc-mssql/milestone/14?closed=1">SQL Server driver - 0.8.7.RELEASE</a> (<code class="language-plaintext highlighter-rouge">io.r2dbc:r2dbc-postgresql</code>)</li> <li><a href="https://github.com/r2dbc/r2dbc-proxy/milestone/14?closed=1">R2DBC Proxy - 0.8.8.RELEASE</a> (<code class="language-plaintext highlighter-rouge">io.r2dbc:r2dbc-proxy</code>)</li> </ul> <p>This is a maintenance release shipping with mostly bugfixes fixes, selected enhancements, and dependency upgrades. Check out the changelogs associated with each component.</p> <p>We recommend version management by using R2DBC BOM as individual modules follow their own version number schedule.</p> <p>If you use Maven, you can add the following lines to your <code class="language-plaintext highlighter-rouge">pom.xml</code>:</p> <div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;dependencyManagement&gt;</span> <span class="nt">&lt;dependencies&gt;</span> <span class="nt">&lt;dependency&gt;</span> <span class="nt">&lt;groupId&gt;</span>io.r2dbc<span class="nt">&lt;/groupId&gt;</span> <span class="nt">&lt;artifactId&gt;</span>r2dbc-bom<span class="nt">&lt;/artifactId&gt;</span> <span class="nt">&lt;version&gt;</span>Arabba-SR11<span class="nt">&lt;/version&gt;</span> <span class="nt">&lt;type&gt;</span>pom<span class="nt">&lt;/type&gt;</span> <span class="nt">&lt;scope&gt;</span>import<span class="nt">&lt;/scope&gt;</span> <span class="nt">&lt;/dependency&gt;</span> <span class="nt">&lt;/dependencies&gt;</span> <span class="nt">&lt;/dependencyManagement&gt;</span> <span class="nt">&lt;dependencies&gt;</span> <span class="nt">&lt;dependency&gt;</span> <span class="nt">&lt;groupId&gt;</span>org.mariadb<span class="nt">&lt;/groupId&gt;</span> <span class="nt">&lt;artifactId&gt;</span>r2dbc-mariadb<span class="nt">&lt;/artifactId&gt;</span> <span class="nt">&lt;/dependency&gt;</span> <span class="nt">&lt;/dependencies&gt;</span> </code></pre></div></div> Fri, 24 Sep 2021 06:00:00 +0000 https://r2dbc.github.io/2021/09/24/r2dbc-arabba-sr11-released.html https://r2dbc.github.io/2021/09/24/r2dbc-arabba-sr11-released.html release R2DBC 0.9.0.M2 available (Borca-M2) <p>R2DBC 0.9.0.M2 available (Borca-M2)</p> <p>Dear R2DBC Community,</p> <p>We’re excited to announce that the majority of R2DBC implementations has upgraded to <a href="https://r2dbc.io/2021/07/09/r2dbc-0.9.m2-released">R2DBC 0.9 M2</a>. As of today, the following libraries comply with the specification:</p> <ul> <li><code class="language-plaintext highlighter-rouge">oracle-r2dbc</code> <code class="language-plaintext highlighter-rouge">0.3.0</code></li> <li><code class="language-plaintext highlighter-rouge">r2dbc-h2</code> <code class="language-plaintext highlighter-rouge">0.9.0.M1</code></li> <li><code class="language-plaintext highlighter-rouge">r2dbc-mariadb</code> <code class="language-plaintext highlighter-rouge">1.1.0-beta</code></li> <li><code class="language-plaintext highlighter-rouge">r2dbc-mssql</code> <code class="language-plaintext highlighter-rouge">0.9.0.M2</code></li> <li><code class="language-plaintext highlighter-rouge">r2dbc-pool</code> <code class="language-plaintext highlighter-rouge">0.9.0.M2</code></li> <li><code class="language-plaintext highlighter-rouge">r2dbc-postgres</code> <code class="language-plaintext highlighter-rouge">0.9.0.M2</code></li> <li><code class="language-plaintext highlighter-rouge">r2dbc-proxy</code> <code class="language-plaintext highlighter-rouge">0.9.0.M2</code></li> </ul> <p>You can consume drivers and the specification artifacts either individually or through a version-managed bill of materials:</p> <p>If you use Maven, you can add the following lines to your <code class="language-plaintext highlighter-rouge">pom.xml</code>:</p> <div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;dependencyManagement&gt;</span> <span class="nt">&lt;dependencies&gt;</span> <span class="nt">&lt;dependency&gt;</span> <span class="nt">&lt;groupId&gt;</span>io.r2dbc<span class="nt">&lt;/groupId&gt;</span> <span class="nt">&lt;artifactId&gt;</span>r2dbc-bom<span class="nt">&lt;/artifactId&gt;</span> <span class="nt">&lt;version&gt;</span>Borca-M2<span class="nt">&lt;/version&gt;</span> <span class="nt">&lt;type&gt;</span>pom<span class="nt">&lt;/type&gt;</span> <span class="nt">&lt;scope&gt;</span>import<span class="nt">&lt;/scope&gt;</span> <span class="nt">&lt;/dependency&gt;</span> <span class="nt">&lt;/dependencies&gt;</span> <span class="nt">&lt;/dependencyManagement&gt;</span> <span class="nt">&lt;dependencies&gt;</span> <span class="nt">&lt;dependency&gt;</span> <span class="nt">&lt;groupId&gt;</span>com.oracle.database.r2dbc<span class="nt">&lt;/groupId&gt;</span> <span class="nt">&lt;artifactId&gt;</span>oracle-r2dbc<span class="nt">&lt;/artifactId&gt;</span> <span class="nt">&lt;/dependency&gt;</span> <span class="nt">&lt;/dependencies&gt;</span> </code></pre></div></div> <p>This release is a major leap forward as the specification version 0.9 defines fine-grained interaction for parameter specifications, out-parameter consumption (typically used for stored procedures), and consumption of individual result segments using a single operator.</p> <p>Let’s have a detailed look at the most significant changes.</p> <h2 id="consumption-of-out-parameters">Consumption of OUT Parameters</h2> <p>The initial draft of R2DBC assumed that stored procedures are out of scope because they were rarely used. While the specification evolved, we learned that stored procedures are still heavily leveraged for workloads that are heavily tied to the database. We decided to provide means to call stored procedures properly by introducing the <code class="language-plaintext highlighter-rouge">Parameters</code> abstraction and consumption of results of stored procedures by introducing <code class="language-plaintext highlighter-rouge">OutParameters</code>.</p> <p>The <code class="language-plaintext highlighter-rouge">Parameters</code> abstraction was introduced with the previous milestone. <code class="language-plaintext highlighter-rouge">OutParameters</code> is specified in this milestone. It essentially provides access by name and index to out parameters returned from a stored procedure invocation. To consume out parameters, callers need to:</p> <ol> <li>Register <code class="language-plaintext highlighter-rouge">OUT</code> parameter bindings</li> <li>Consume out parameters through the <code class="language-plaintext highlighter-rouge">map(…)</code> or <code class="language-plaintext highlighter-rouge">flatMap(…)</code> operator.</li> </ol> <p>Consider the following stored procedure (using Microsoft SQL Server):</p> <div class="language-sql highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">CREATE</span> <span class="k">PROCEDURE</span> <span class="n">say_hello</span> <span class="o">@</span><span class="n">TheName</span> <span class="n">nvarchar</span><span class="p">(</span><span class="mi">50</span><span class="p">),</span> <span class="o">@</span><span class="n">Greeting</span> <span class="n">nvarchar</span><span class="p">(</span><span class="mi">255</span><span class="p">)</span> <span class="k">OUTPUT</span> <span class="k">AS</span> <span class="k">SET</span> <span class="n">NOCOUNT</span> <span class="k">ON</span><span class="p">;</span> <span class="k">SET</span> <span class="o">@</span><span class="n">Greeting</span> <span class="o">=</span> <span class="n">CONCAT</span><span class="p">(</span><span class="s1">'Hello '</span><span class="p">,</span> <span class="o">@</span><span class="n">TheName</span><span class="p">)</span> </code></pre></div></div> <p>This procedure concatenates <code class="language-plaintext highlighter-rouge">Hello </code> with the input parameter <code class="language-plaintext highlighter-rouge">@TheName</code> and returns <code class="language-plaintext highlighter-rouge">@Greeting</code> as out parameter.</p> <p>The invocation is straight forward by calling <code class="language-plaintext highlighter-rouge">say_hello</code> through SQL and two parameters. Note that <code class="language-plaintext highlighter-rouge">@Greeting</code> is bound as <code class="language-plaintext highlighter-rouge">out</code> parameter with a specific data type of <code class="language-plaintext highlighter-rouge">VARCHAR</code>.</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">connection</span><span class="o">.</span><span class="na">createStatement</span><span class="o">(</span><span class="s">"EXEC say_hello @P0, @Greeting OUTPUT"</span><span class="o">)</span> <span class="o">.</span><span class="na">bind</span><span class="o">(</span><span class="s">"@P0"</span><span class="o">,</span> <span class="s">"Walter"</span><span class="o">)</span> <span class="o">.</span><span class="na">bind</span><span class="o">(</span><span class="s">"@Greeting"</span><span class="o">,</span> <span class="nc">Parameters</span><span class="o">.</span><span class="na">out</span><span class="o">(</span><span class="nc">R2dbcType</span><span class="o">.</span><span class="na">VARCHAR</span><span class="o">))</span> <span class="o">.</span><span class="na">execute</span><span class="o">()</span> <span class="o">.</span><span class="na">flatMap</span><span class="o">(</span><span class="n">it</span> <span class="o">-&gt;</span> <span class="n">it</span><span class="o">.</span><span class="na">map</span><span class="o">((</span><span class="n">readable</span><span class="o">)</span> <span class="o">-&gt;</span> <span class="o">{</span> <span class="k">return</span> <span class="n">readable</span><span class="o">.</span><span class="na">get</span><span class="o">(</span><span class="mi">0</span><span class="o">);</span> <span class="o">}));</span> </code></pre></div></div> <p>You might be surprised by seeing the <code class="language-plaintext highlighter-rouge">map(…)</code> operator accepting a <code class="language-plaintext highlighter-rouge">Function&lt;Readable, T&gt;</code> instead of <code class="language-plaintext highlighter-rouge">BiFunction&lt;Row, RowMetadata, T&gt;</code>. With the introduction of <code class="language-plaintext highlighter-rouge">OutParameters</code>, we introduced <code class="language-plaintext highlighter-rouge">Readable</code> as superinterface for <code class="language-plaintext highlighter-rouge">OutParameters</code> and <code class="language-plaintext highlighter-rouge">Row</code> that defines <code class="language-plaintext highlighter-rouge">get(…)</code> methods for both subtypes. Both <code class="language-plaintext highlighter-rouge">Row</code> and <code class="language-plaintext highlighter-rouge">OutParameters</code> follow the same semantics of by-name and by-index access to statement results so we introduced <code class="language-plaintext highlighter-rouge">map(Function&lt;Readable, T&gt;)</code> for easier consumption of tabular and stored procedure results.</p> <p><code class="language-plaintext highlighter-rouge">OutParameters</code> and <code class="language-plaintext highlighter-rouge">Row</code> define <code class="language-plaintext highlighter-rouge">getMetadata</code> methods in case you’re interested in out parameters/column metadata.</p> <h2 id="consuming-individual-segments">Consuming individual segments</h2> <p>Imagine a client that passes-thru SQL without being aware of what kind of result it is going to produce. <code class="language-plaintext highlighter-rouge">Result.map(…)</code> and <code class="language-plaintext highlighter-rouge">Result.getRowsUpdated()</code> have been the only two methods to consume results and both are mutually exclusive. Consuming rows doesn’t allow for access to update counts and vice versa.</p> <p>This release removes this limitation by introducing <code class="language-plaintext highlighter-rouge">filter</code> and <code class="language-plaintext highlighter-rouge">flatMap</code> methods that conceptually turn <code class="language-plaintext highlighter-rouge">Result</code> into a <code class="language-plaintext highlighter-rouge">Publisher&lt;Segment&gt;</code>. <code class="language-plaintext highlighter-rouge">Segment</code> is a type hierarchy to represent the individual result types that a statement can produce: Update counts, rows, out parameters, messages, and any vendor-specific types.</p> <p>Consumers that are interested in all types of segments can leverage the <code class="language-plaintext highlighter-rouge">flatMap(Function&lt;Segment, Publisher&lt;T&gt;&gt;)</code> operator to process a single segment into zero, one, or many resulting items, depending on how the result should be processed.</p> <p>Consider the following example. The function only processes row segments by copying values into a <code class="language-plaintext highlighter-rouge">Map</code>:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">result</span><span class="o">.</span><span class="na">flatMap</span><span class="o">(</span><span class="n">segment</span> <span class="o">-&gt;</span> <span class="o">{</span> <span class="k">if</span> <span class="o">(!(</span><span class="n">data</span> <span class="k">instanceof</span> <span class="nc">Result</span><span class="o">.</span><span class="na">RowSegment</span><span class="o">))</span> <span class="o">{</span> <span class="k">return</span> <span class="nc">Mono</span><span class="o">.</span><span class="na">empty</span><span class="o">();</span> <span class="o">}</span> <span class="nc">Result</span><span class="o">.</span><span class="na">RowSegment</span> <span class="n">data</span> <span class="o">=</span> <span class="o">(</span><span class="nc">Result</span><span class="o">.</span><span class="na">RowSegment</span><span class="o">)</span> <span class="n">segment</span><span class="o">;</span> <span class="nc">Map</span><span class="o">&lt;</span><span class="nc">String</span><span class="o">,</span> <span class="nc">Object</span><span class="o">&gt;</span> <span class="n">rowData</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">HashMap</span><span class="o">&lt;&gt;();</span> <span class="k">for</span> <span class="o">(</span><span class="nc">ColumnMetadata</span> <span class="n">column</span> <span class="o">:</span> <span class="n">data</span><span class="o">.</span><span class="na">row</span><span class="o">().</span><span class="na">getMetadata</span><span class="o">().</span><span class="na">getColumnMetadatas</span><span class="o">())</span> <span class="o">{</span> <span class="n">rowData</span><span class="o">.</span><span class="na">put</span><span class="o">(</span><span class="n">column</span><span class="o">.</span><span class="na">getName</span><span class="o">(),</span> <span class="n">data</span><span class="o">.</span><span class="na">row</span><span class="o">().</span><span class="na">get</span><span class="o">(</span><span class="n">column</span><span class="o">.</span><span class="na">getName</span><span class="o">()));</span> <span class="o">}</span> <span class="k">return</span> <span class="nc">Mono</span><span class="o">.</span><span class="na">just</span><span class="o">(</span><span class="n">rowData</span><span class="o">);</span> <span class="o">})</span> </code></pre></div></div> <p>To avoid repetative patterns of <code class="language-plaintext highlighter-rouge">instanceof</code> checks, results can be filtered regarding their segments that a <code class="language-plaintext highlighter-rouge">Result</code> should hold. <code class="language-plaintext highlighter-rouge">filter(Predicate&lt;Segment&gt; filter)</code> returns a filtered <code class="language-plaintext highlighter-rouge">Result</code>. That is useful when interested only in particular segments. The code above could be rewritten to:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">result</span><span class="o">.</span><span class="na">filter</span><span class="o">(</span><span class="nc">Result</span><span class="o">.</span><span class="na">RowSegment</span><span class="o">.</span><span class="na">class</span><span class="o">::</span><span class="n">isInstance</span><span class="o">).</span><span class="na">flatMap</span><span class="o">(</span><span class="n">segment</span> <span class="o">-&gt;</span> <span class="o">{</span> <span class="nc">Result</span><span class="o">.</span><span class="na">RowSegment</span> <span class="n">data</span> <span class="o">=</span> <span class="o">(</span><span class="nc">Result</span><span class="o">.</span><span class="na">RowSegment</span><span class="o">)</span> <span class="n">segment</span><span class="o">;</span> <span class="nc">Map</span><span class="o">&lt;</span><span class="nc">String</span><span class="o">,</span> <span class="nc">Object</span><span class="o">&gt;</span> <span class="n">rowData</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">HashMap</span><span class="o">&lt;&gt;();</span> <span class="k">for</span> <span class="o">(</span><span class="nc">ColumnMetadata</span> <span class="n">column</span> <span class="o">:</span> <span class="n">data</span><span class="o">.</span><span class="na">row</span><span class="o">().</span><span class="na">getMetadata</span><span class="o">().</span><span class="na">getColumnMetadatas</span><span class="o">())</span> <span class="o">{</span> <span class="n">rowData</span><span class="o">.</span><span class="na">put</span><span class="o">(</span><span class="n">column</span><span class="o">.</span><span class="na">getName</span><span class="o">(),</span> <span class="n">data</span><span class="o">.</span><span class="na">row</span><span class="o">().</span><span class="na">get</span><span class="o">(</span><span class="n">column</span><span class="o">.</span><span class="na">getName</span><span class="o">()));</span> <span class="o">}</span> <span class="k">return</span> <span class="nc">Mono</span><span class="o">.</span><span class="na">just</span><span class="o">(</span><span class="n">rowData</span><span class="o">);</span> <span class="o">});</span> </code></pre></div></div> <p>A segment type that was introduced with this release is the message segment. Messages are of informational, warning or error nature and can lead to error signals by translating these into <code class="language-plaintext highlighter-rouge">R2dbcException</code>.</p> <p>The segment API now gives access to SQL warnings that weren’t accessible in the previous R2DBC release.</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">result</span><span class="o">.</span><span class="na">filter</span><span class="o">(</span><span class="nc">Result</span><span class="o">.</span><span class="na">Message</span><span class="o">.</span><span class="na">class</span><span class="o">::</span><span class="n">isInstance</span><span class="o">).</span><span class="na">flatMap</span><span class="o">(</span><span class="n">data</span> <span class="o">-&gt;</span> <span class="o">{</span> <span class="nc">Result</span><span class="o">.</span><span class="na">Message</span> <span class="n">message</span> <span class="o">=</span> <span class="o">(</span><span class="nc">Result</span><span class="o">.</span><span class="na">Message</span><span class="o">)</span> <span class="n">data</span><span class="o">;</span> <span class="n">assertThat</span><span class="o">(</span><span class="n">message</span><span class="o">.</span><span class="na">errorCode</span><span class="o">()).</span><span class="na">isZero</span><span class="o">();</span> <span class="n">assertThat</span><span class="o">(</span><span class="n">message</span><span class="o">.</span><span class="na">sqlState</span><span class="o">()).</span><span class="na">isEqualTo</span><span class="o">(</span><span class="s">"S0000"</span><span class="o">);</span> <span class="n">assertThat</span><span class="o">(</span><span class="n">message</span><span class="o">.</span><span class="na">message</span><span class="o">()).</span><span class="na">isEqualTo</span><span class="o">(</span><span class="s">"error message desc"</span><span class="o">);</span> <span class="n">assertThat</span><span class="o">(</span><span class="n">message</span><span class="o">.</span><span class="na">exception</span><span class="o">()).</span><span class="na">isInstanceOf</span><span class="o">(</span><span class="nc">R2dbcNonTransientResourceException</span><span class="o">.</span><span class="na">class</span><span class="o">);</span> <span class="k">return</span> <span class="nc">Mono</span><span class="o">.</span><span class="na">just</span><span class="o">(</span><span class="n">data</span><span class="o">);</span> <span class="o">}))</span> </code></pre></div></div> <p>The <code class="language-plaintext highlighter-rouge">filter(…)</code> operator can also be used to filter out message segments so you can create a <code class="language-plaintext highlighter-rouge">Result</code> that doesn’t contain any segments leading to an error:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">result</span><span class="o">.</span><span class="na">filter</span><span class="o">(</span><span class="n">it</span> <span class="o">-&gt;</span> <span class="o">!(</span><span class="n">it</span> <span class="k">instanceof</span> <span class="nc">Result</span><span class="o">.</span><span class="na">Message</span><span class="o">)).</span><span class="na">map</span><span class="o">(</span><span class="err">…</span><span class="o">)</span> </code></pre></div></div> <p>Subsequent operators (<code class="language-plaintext highlighter-rouge">map</code>, <code class="language-plaintext highlighter-rouge">getRowsUpdated</code>, <code class="language-plaintext highlighter-rouge">flatMap</code>) process the result in its filtered form. To illustrate this a bit more, the following code would filter out all update counts and consuming the update count through <code class="language-plaintext highlighter-rouge">getRowsUpdated</code> would never return the actual result:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">result</span><span class="o">.</span><span class="na">filter</span><span class="o">(</span><span class="n">it</span> <span class="o">-&gt;</span> <span class="o">!(</span><span class="n">it</span> <span class="k">instanceof</span> <span class="nc">Result</span><span class="o">.</span><span class="na">UpdateCount</span><span class="o">)).</span><span class="na">getRowsUpdated</span><span class="o">()</span> </code></pre></div></div> <p>The segments API is an advanced mechanism to consume results and segments are only valid within the <code class="language-plaintext highlighter-rouge">map</code>/<code class="language-plaintext highlighter-rouge">flatMap</code> operator function. Segments must never leave these functions as they are invalidated once the function call terminates.</p> <p>You can find the full list of API changes in the <a href="https://r2dbc.io/2021/07/09/r2dbc-0.9.m2-released">R2DBC 0.9.0.M2 announcement blog post</a>.</p> <p>This release ships a first batch of drivers that implement the specification changes. Watch out for future evolution or reach out to the maintainers of the driver you’re interested in if the driver doesn’t fully support the specification yet.</p> Tue, 31 Aug 2021 14:00:00 +0000 https://r2dbc.github.io/2021/08/31/r2dbc-0.9.m2-available.html https://r2dbc.github.io/2021/08/31/r2dbc-0.9.m2-available.html release Second Milestone of R2DBC SPI 0.9 Released <p>Dear R2DBC Community,</p> <p>We are thrilled to announce the second first milestone of the R2DBC 0.9 specification (<code class="language-plaintext highlighter-rouge">0.9.0.M2</code>) that ships several changes. One of them is a long-awaited feature to consume out parameters from stored procedures. Here’s a short summary of the changes:</p> <ul> <li>Consumption of OUT Parameters by introducing <a href="https://r2dbc.io/spec/0.9.0.M2/api/io/r2dbc/spi/OutParameters.html"><code class="language-plaintext highlighter-rouge">OutParameters</code></a>. <code class="language-plaintext highlighter-rouge">Row</code> and <code class="language-plaintext highlighter-rouge">OutParameters</code> now share the common superinterface <a href="https://r2dbc.io/spec/0.9.0.M2/api/io/r2dbc/spi/Readable.html"><code class="language-plaintext highlighter-rouge">Readable</code></a>.</li> <li>Introduction of the <a href="https://r2dbc.io/spec/0.9.0.M2/api/io/r2dbc/spi/Result.html#map-java.util.function.Function-"><code class="language-plaintext highlighter-rouge">Result.map(Function&lt;Readable, T&gt;)</code></a> method.</li> <li>Introduction of the <a href="https://r2dbc.io/spec/0.9.0.M2/api/io/r2dbc/spi/Result.Segment.html"><code class="language-plaintext highlighter-rouge">Result.Segment</code></a> API type hierarchy and <code class="language-plaintext highlighter-rouge">Result.filter(Predicate&lt;Segment&gt;)</code> and <code class="language-plaintext highlighter-rouge">Result.flatMap(Function&lt;Segment, Publisher&lt;T&gt;&gt;)</code> methods to consume arbitrary statement results.</li> <li>Lifecycle support by introducing the <a href="https://r2dbc.io/spec/0.9.0.M2/api/io/r2dbc/spi/Lifecycle.html"><code class="language-plaintext highlighter-rouge">Lifecycle</code></a> interface.</li> <li>Removal of generic type of <a href="https://r2dbc.io/spec/0.9.0.M2/api/io/r2dbc/spi/ConnectionFactoryOptions.html#getValue-io.r2dbc.spi.Option-"><code class="language-plaintext highlighter-rouge">ConnectionFactoryOptions.getValue(Option)</code></a> and <code class="language-plaintext highlighter-rouge">getRequiredValue(…)</code>.</li> <li>Deprecate <code class="language-plaintext highlighter-rouge">RowMetadata.getColumnNames()</code> and introduce <a href="https://r2dbc.io/spec/0.9.0.M2/api/io/r2dbc/spi/RowMetadata.html#contains-java.lang.String-"><code class="language-plaintext highlighter-rouge">RowMetadata.contains(String)</code></a> to simplify constraints and usage around column presence checks.</li> <li>Support for Lock Wait and Statement Timeouts through <a href="https://r2dbc.io/spec/0.9.0.M2/api/io/r2dbc/spi/Connection.html#setLockWaitTimeout-java.time.Duration-"><code class="language-plaintext highlighter-rouge">Connection.setLockWaitTimeout(Duration)</code></a> and <a href="https://r2dbc.io/spec/0.9.0.M2/api/io/r2dbc/spi/Connection.html#setStatementTimeout-java.time.Duration-"><code class="language-plaintext highlighter-rouge">Connection.setStatementTimeout(Duration)</code></a> methods.</li> </ul> <p>This release provides a stable revision of the specification for R2DBC implementors. We expect drivers and implementations to adopt these changes within the next weeks so that we can expect implementations to be available during August where we plan to finish the milestone release train with the bill of materials.</p> <ul> <li>Mailing list: <a href="https://groups.google.com/g/r2dbc">https://groups.google.com/g/r2dbc</a></li> <li>Changelog: <a href="https://r2dbc.io/spec/0.9.0.M2/CHANGELOG.txt">https://r2dbc.io/spec/0.9.0.M2/CHANGELOG.txt</a></li> <li>Specification document: <a href="https://r2dbc.io/spec/0.9.0.M2/spec/html/">https://r2dbc.io/spec/0.9.0.M2/spec/html/</a></li> <li>Javadoc: <a href="https://r2dbc.io/spec/0.9.0.M2/api/">https://r2dbc.io/spec/0.9.0.M2/api/</a></li> </ul> Fri, 09 Jul 2021 15:28:00 +0000 https://r2dbc.github.io/2021/07/09/r2dbc-0.9.m2-released.html https://r2dbc.github.io/2021/07/09/r2dbc-0.9.m2-released.html release R2DBC Proxy Tips: Observability <p>This is the third post of the <em>“R2DBC Proxy Tips”</em> mini blog series.</p> <p>This post shows how to achieve observability - metrics and distributed tracing.</p> <p>To implement observability, we need to capture the interactions with the R2DBC objects. <code class="language-plaintext highlighter-rouge">ProxyMethodExecutionListener</code> is suitable for this use case. This listener is an extension of <code class="language-plaintext highlighter-rouge">ProxyExecutionListener</code> and provides callbacks for all the methods defined in the R2DBC SPI classes.</p> <p>For example, the <code class="language-plaintext highlighter-rouge">beforeCreateOnConnectionFactory</code> method is called back before the <code class="language-plaintext highlighter-rouge">create</code> method on <code class="language-plaintext highlighter-rouge">ConnectionFactory</code>. The <code class="language-plaintext highlighter-rouge">afterExecuteOnStatement</code> is invoked after the <code class="language-plaintext highlighter-rouge">execute</code> method on <code class="language-plaintext highlighter-rouge">Statement</code>.</p> <p>By using these callback methods, you can easily add logic to populate metrics, start a new span, or take any action you want.</p> <p>For example, the following listener populates metrics for the time taken to obtain a connection:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="kd">class</span> <span class="nc">MyMetricsListener</span> <span class="kd">implements</span> <span class="nc">ProxyMethodExecutionListener</span> <span class="o">{</span> <span class="kd">private</span> <span class="nc">MeterRegistry</span> <span class="n">registry</span><span class="o">;</span> <span class="kd">public</span> <span class="nf">MyMetricsListener</span><span class="o">(</span><span class="nc">MeterRegistry</span> <span class="n">registry</span><span class="o">)</span> <span class="o">{</span> <span class="k">this</span><span class="o">.</span><span class="na">registry</span> <span class="o">=</span> <span class="n">registry</span><span class="o">;</span> <span class="o">}</span> <span class="nd">@Override</span> <span class="kd">public</span> <span class="kt">void</span> <span class="nf">beforeCreateOnConnectionFactory</span><span class="o">(</span><span class="nc">MethodExecutionInfo</span> <span class="n">methodExecInfo</span><span class="o">)</span> <span class="o">{</span> <span class="nc">Timer</span><span class="o">.</span><span class="na">Sample</span> <span class="n">sample</span> <span class="o">=</span> <span class="nc">Timer</span><span class="o">.</span><span class="na">start</span><span class="o">(</span><span class="k">this</span><span class="o">.</span><span class="na">registry</span><span class="o">);</span> <span class="n">methodExecInfo</span><span class="o">.</span><span class="na">getValueStore</span><span class="o">().</span><span class="na">put</span><span class="o">(</span><span class="s">"connectionCreate"</span><span class="o">,</span> <span class="n">sample</span><span class="o">);</span> <span class="o">}</span> <span class="nd">@Override</span> <span class="kd">public</span> <span class="kt">void</span> <span class="nf">afterCreateOnConnectionFactory</span><span class="o">(</span><span class="nc">MethodExecutionInfo</span> <span class="n">methodExecInfo</span><span class="o">)</span> <span class="o">{</span> <span class="nc">Timer</span><span class="o">.</span><span class="na">Sample</span> <span class="n">sample</span> <span class="o">=</span> <span class="n">methodExecInfo</span><span class="o">.</span><span class="na">getValueStore</span><span class="o">()</span> <span class="o">.</span><span class="na">get</span><span class="o">(</span><span class="s">"connectionCreate"</span><span class="o">,</span> <span class="nc">Timer</span><span class="o">.</span><span class="na">Sample</span><span class="o">.</span><span class="na">class</span><span class="o">);</span> <span class="nc">Timer</span> <span class="n">timer</span> <span class="o">=</span> <span class="nc">Timer</span> <span class="o">.</span><span class="na">builder</span><span class="o">(</span><span class="s">"r2dbc.connection"</span><span class="o">)</span> <span class="o">.</span><span class="na">description</span><span class="o">(</span><span class="s">"Time to create(acquire) a connection"</span><span class="o">)</span> <span class="o">.</span><span class="na">tags</span><span class="o">(</span><span class="s">"event"</span><span class="o">,</span> <span class="s">"create"</span><span class="o">)</span> <span class="o">.</span><span class="na">register</span><span class="o">(</span><span class="k">this</span><span class="o">.</span><span class="na">registry</span><span class="o">);</span> <span class="n">sample</span><span class="o">.</span><span class="na">stop</span><span class="o">(</span><span class="n">timer</span><span class="o">);</span> <span class="o">}</span> <span class="o">}</span> </code></pre></div></div> <p>As mentioned in the <a href="https://r2dbc.io/2021/05/05/r2dbc-proxy-tips-valuestore">second post</a>, we use the <code class="language-plaintext highlighter-rouge">ValueStore</code> API to pass the <code class="language-plaintext highlighter-rouge">sample</code> object between methods.</p> <p><a href="https://github.com/ttddyy/r2dbc-proxy-examples/blob/master/listener-example/src/main/java/io/r2dbc/examples/MetricsExecutionListener.java">MetricsExecutionListener</a> and <a href="https://github.com/ttddyy/r2dbc-proxy-examples/blob/master/listener-example/src/main/java/io/r2dbc/examples/QueryTimeMetricsExecutionListener.java">QueryTimeMetricsExecutionListener</a> are other sample metrics listener implementations.</p> <p>For distributed tracing, the implementation is similar to the above sample code for metrics. You can create spans at method invocations, pass them to <code class="language-plaintext highlighter-rouge">ValueStore</code>, and finish them in the appropriate method invocations.</p> <p>This <a href="https://github.com/ttddyy/r2dbc-proxy-examples/blob/master/listener-example/src/main/java/io/r2dbc/examples/TracingExecutionListener.java">TracingExecutionListener</a> example shows how to create spans by using the zipkin <code class="language-plaintext highlighter-rouge">Tracer</code>.</p> <p>For Spring Cloud Sleuth users, we have good news. <a href="https://github.com/spring-cloud/spring-cloud-sleuth/pull/1946">This commit</a> enables out of the box support for R2DBC instrumentation.</p> Wed, 02 Jun 2021 01:00:00 +0000 https://r2dbc.github.io/2021/06/02/r2dbc-proxy-tips-observability.html https://r2dbc.github.io/2021/06/02/r2dbc-proxy-tips-observability.html post R2DBC Proxy Tips: Share values in callbacks with ValueStore <p><a href="https://github.com/r2dbc/r2dbc-proxy">R2DBC Proxy</a> is a framework that provides callbacks to the R2DBC interactions. It consists of a thin layer on top of the R2DBC drivers to implement cross-cutting concerns, such as query loggings, observability instrumentation, or your own actions.</p> <p><em>R2DBC Proxy Tips</em> is a series of mini blog posts. I will introduce typical usage of the R2DBC proxy and sample implementations with tips.</p> <h1 id="share-values-in-callbacks-with-valuestore">Share values in callbacks with ValueStore</h1> <p>This is the second post of the <em>“R2DBC Proxy Tips”</em> mini blog series.</p> <p>This post shows how to pass values across proxy callbacks.</p> <p>When you implement a <code class="language-plaintext highlighter-rouge">ProxyExecutionListener</code>, you may want to pass values from the <code class="language-plaintext highlighter-rouge">beforeQuery</code> to the <code class="language-plaintext highlighter-rouge">afterQuery</code> callback, pass values from the <code class="language-plaintext highlighter-rouge">beforeMethod</code> to the <code class="language-plaintext highlighter-rouge">afterMethod</code> callback, or bind to <code class="language-plaintext highlighter-rouge">Connection</code> scope and share across multiple callbacks.</p> <p>In imperative programming, <code class="language-plaintext highlighter-rouge">ThreadLocal</code> provides the functionality to pass values between methods or keep values bound to a database connection. However, in reactive, <code class="language-plaintext highlighter-rouge">ThreadLocal</code> is not a reliable place to store values.</p> <p>To address this, R2DBC Proxy provides <code class="language-plaintext highlighter-rouge">ValueStore</code>. This provides a <code class="language-plaintext highlighter-rouge">Map</code>-like API to put and get objects by key. <code class="language-plaintext highlighter-rouge">ValueStore</code> is available for the following scopes:</p> <ul> <li>Between <code class="language-plaintext highlighter-rouge">beforeMethod</code> and <code class="language-plaintext highlighter-rouge">afterMethod</code> through <code class="language-plaintext highlighter-rouge">MethodExecutionInfo</code></li> <li>Between <code class="language-plaintext highlighter-rouge">beforeQuery</code> and <code class="language-plaintext highlighter-rouge">afterQuery</code> through <code class="language-plaintext highlighter-rouge">QueryExecutionInfo</code></li> <li><code class="language-plaintext highlighter-rouge">Connection</code> bound through <code class="language-plaintext highlighter-rouge">ConnectionInfo</code></li> <li><code class="language-plaintext highlighter-rouge">Statement</code> bound through <code class="language-plaintext highlighter-rouge">StatementInfo</code> for the bind parameter converter</li> </ul> <p>The following hypothetical usage shows how to put or get values to or from <code class="language-plaintext highlighter-rouge">Connection</code> and method-bound <code class="language-plaintext highlighter-rouge">ValueStore</code> instances.</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">static</span> <span class="kd">class</span> <span class="nc">MyFilter</span> <span class="kd">implements</span> <span class="nc">ProxyMethodExecutionListener</span> <span class="o">{</span> <span class="kd">private</span> <span class="kd">static</span> <span class="kd">final</span> <span class="nc">Logger</span> <span class="n">log</span> <span class="o">=</span> <span class="nc">LoggerFactory</span><span class="o">.</span><span class="na">getLogger</span><span class="o">(</span><span class="nc">MyFilter</span><span class="o">.</span><span class="na">class</span><span class="o">);</span> <span class="kd">private</span> <span class="kd">final</span> <span class="nc">Clock</span> <span class="n">clock</span><span class="o">;</span> <span class="kd">public</span> <span class="nf">MyFilter</span><span class="o">(</span><span class="nc">Clock</span> <span class="n">clock</span><span class="o">)</span> <span class="o">{</span> <span class="k">this</span><span class="o">.</span><span class="na">clock</span> <span class="o">=</span> <span class="n">clock</span><span class="o">;</span> <span class="o">}</span> <span class="nd">@Override</span> <span class="kd">public</span> <span class="kt">void</span> <span class="nf">afterCreateOnConnectionFactory</span><span class="o">(</span><span class="nc">MethodExecutionInfo</span> <span class="n">methodInfo</span><span class="o">)</span> <span class="o">{</span> <span class="c1">// connection scoped store</span> <span class="nc">ValueStore</span> <span class="n">store</span> <span class="o">=</span> <span class="n">methodInfo</span><span class="o">.</span><span class="na">getConnectionInfo</span><span class="o">().</span><span class="na">getValueStore</span><span class="o">();</span> <span class="n">store</span><span class="o">.</span><span class="na">put</span><span class="o">(</span><span class="s">"conn-created"</span><span class="o">,</span> <span class="k">this</span><span class="o">.</span><span class="na">clock</span><span class="o">.</span><span class="na">instant</span><span class="o">());</span> <span class="o">}</span> <span class="nd">@Override</span> <span class="kd">public</span> <span class="kt">void</span> <span class="nf">beforeExecuteOnStatement</span><span class="o">(</span><span class="nc">MethodExecutionInfo</span> <span class="n">methodInfo</span><span class="o">)</span> <span class="o">{</span> <span class="c1">// method scoped store</span> <span class="nc">ValueStore</span> <span class="n">store</span> <span class="o">=</span> <span class="n">methodInfo</span><span class="o">.</span><span class="na">getValueStore</span><span class="o">();</span> <span class="n">store</span><span class="o">.</span><span class="na">put</span><span class="o">(</span><span class="s">"stmt-before"</span><span class="o">,</span> <span class="k">this</span><span class="o">.</span><span class="na">clock</span><span class="o">.</span><span class="na">instant</span><span class="o">());</span> <span class="o">}</span> <span class="nd">@Override</span> <span class="kd">public</span> <span class="kt">void</span> <span class="nf">afterExecuteOnStatement</span><span class="o">(</span><span class="nc">MethodExecutionInfo</span> <span class="n">methodInfo</span><span class="o">)</span> <span class="o">{</span> <span class="nc">ValueStore</span> <span class="n">connectionStore</span> <span class="o">=</span> <span class="n">methodInfo</span><span class="o">.</span><span class="na">getConnectionInfo</span><span class="o">().</span><span class="na">getValueStore</span><span class="o">();</span> <span class="nc">ValueStore</span> <span class="n">methodStore</span> <span class="o">=</span> <span class="n">methodInfo</span><span class="o">.</span><span class="na">getValueStore</span><span class="o">();</span> <span class="nc">Instant</span> <span class="n">connCreated</span> <span class="o">=</span> <span class="n">connectionStore</span><span class="o">.</span><span class="na">get</span><span class="o">(</span><span class="s">"conn-created"</span><span class="o">,</span> <span class="nc">Instant</span><span class="o">.</span><span class="na">class</span><span class="o">);</span> <span class="nc">Instant</span> <span class="n">stmtBefore</span> <span class="o">=</span> <span class="n">methodStore</span><span class="o">.</span><span class="na">get</span><span class="o">(</span><span class="s">"stmt-before"</span><span class="o">,</span> <span class="nc">Instant</span><span class="o">.</span><span class="na">class</span><span class="o">);</span> <span class="nc">Instant</span> <span class="n">now</span> <span class="o">=</span> <span class="k">this</span><span class="o">.</span><span class="na">clock</span><span class="o">.</span><span class="na">instant</span><span class="o">();</span> <span class="nc">Duration</span> <span class="n">connToNow</span> <span class="o">=</span> <span class="nc">Duration</span><span class="o">.</span><span class="na">between</span><span class="o">(</span><span class="n">connCreated</span><span class="o">,</span> <span class="n">now</span><span class="o">);</span> <span class="nc">Duration</span> <span class="n">stmtToNow</span> <span class="o">=</span> <span class="nc">Duration</span><span class="o">.</span><span class="na">between</span><span class="o">(</span><span class="n">stmtBefore</span><span class="o">,</span> <span class="n">now</span><span class="o">);</span> <span class="n">log</span><span class="o">.</span><span class="na">info</span><span class="o">(</span><span class="s">"From connection created={}"</span><span class="o">,</span> <span class="n">connToNow</span><span class="o">);</span> <span class="n">log</span><span class="o">.</span><span class="na">info</span><span class="o">(</span><span class="s">"From statement executed={}"</span><span class="o">,</span> <span class="n">stmtToNow</span><span class="o">);</span> <span class="o">}</span> <span class="o">}</span> </code></pre></div></div> <p>The next blog post will show how to implement a listener for metrics and distributed tracing.</p> <p>The <code class="language-plaintext highlighter-rouge">ValueStore</code> is used to keep the current metrics and span information.</p> Wed, 05 May 2021 01:00:00 +0000 https://r2dbc.github.io/2021/05/05/r2dbc-proxy-tips-valuestore.html https://r2dbc.github.io/2021/05/05/r2dbc-proxy-tips-valuestore.html post R2DBC Proxy Tips: Query Logging <p><a href="https://github.com/r2dbc/r2dbc-proxy">R2DBC Proxy</a> is a framework that provides callbacks to the R2DBC interactions. It consists of a thin layer on top of the R2DBC drivers to implement cross-cutting concerns, such as query loggings, observability instrumentation, or your own actions.</p> <p><em>R2DBC Proxy Tips</em> is a series of mini blog posts. I will introduce typical usage of the R2DBC proxy and sample implementations with tips.</p> <p>This first post explains the prime use case of the R2DBC Proxy: <em>“how to log queries”</em>.</p> <h2 id="implementation">Implementation</h2> <p>In R2DBC Proxy, a callback is implemented as <code class="language-plaintext highlighter-rouge">ProxyExecutionListener</code>.</p> <p>This listener interface defines <code class="language-plaintext highlighter-rouge">beforeQuery</code> and <code class="language-plaintext highlighter-rouge">afterQuery</code> callback methods. As the names suggest, they are invoked when queries get executed.</p> <p>To log the executed queries, you can create a listener that performs logging in <code class="language-plaintext highlighter-rouge">beforeQuery</code> or <code class="language-plaintext highlighter-rouge">afterQuery</code>. (Note, some of the values, such as the time the query took to run, are available only on the <code class="language-plaintext highlighter-rouge">afterQuery</code>.)</p> <p>The following example shows a listener implementation that logs queries after each query:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="kd">class</span> <span class="nc">LoggingListener</span> <span class="kd">implements</span> <span class="nc">ProxyExecutionListener</span> <span class="o">{</span> <span class="kd">private</span> <span class="kd">static</span> <span class="kd">final</span> <span class="nc">Logger</span> <span class="n">logger</span> <span class="o">=</span> <span class="nc">LoggerFactory</span><span class="o">.</span><span class="na">getLogger</span><span class="o">(</span><span class="nc">LoggingListener</span><span class="o">.</span><span class="na">class</span><span class="o">);</span> <span class="kd">private</span> <span class="kd">final</span> <span class="nc">QueryExecutionInfoFormatter</span> <span class="n">formatter</span> <span class="o">=</span> <span class="nc">QueryExecutionInfoFormatter</span><span class="o">.</span><span class="na">showAll</span><span class="o">();</span> <span class="nd">@Override</span> <span class="kd">public</span> <span class="kt">void</span> <span class="nf">afterQuery</span><span class="o">(</span><span class="nc">QueryExecutionInfo</span> <span class="n">execInfo</span><span class="o">)</span> <span class="o">{</span> <span class="n">logger</span><span class="o">.</span><span class="na">info</span><span class="o">(</span><span class="k">this</span><span class="o">.</span><span class="na">formatter</span><span class="o">.</span><span class="na">format</span><span class="o">(</span><span class="n">execInfo</span><span class="o">));</span> <span class="o">}</span> <span class="o">}</span> </code></pre></div></div> <p>Instead of creating a dedicated <code class="language-plaintext highlighter-rouge">LoggingListener</code> class, you can also register a listener for this purpose while creating a proxy <code class="language-plaintext highlighter-rouge">ConnectionFactory</code>.</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">QueryExecutionInfoFormatter</span> <span class="n">formatter</span> <span class="o">=</span> <span class="nc">QueryExecutionInfoFormatter</span><span class="o">.</span><span class="na">showAll</span><span class="o">();</span> <span class="nc">ConnectionFactory</span> <span class="n">original</span> <span class="o">=</span> <span class="o">...;</span> <span class="c1">// create a proxy ConnectionFactory</span> <span class="nc">ConnectionFactory</span> <span class="n">connectionFactory</span> <span class="o">=</span> <span class="nc">ProxyConnectionFactory</span><span class="o">.</span><span class="na">builder</span><span class="o">(</span><span class="n">original</span><span class="o">)</span> <span class="o">.</span><span class="na">onAfterQuery</span><span class="o">(</span><span class="n">queryInfo</span> <span class="o">-&gt;</span> <span class="o">{</span> <span class="c1">// listener</span> <span class="n">logger</span><span class="o">.</span><span class="na">info</span><span class="o">(</span><span class="n">formatter</span><span class="o">.</span><span class="na">format</span><span class="o">(</span><span class="n">queryInfo</span><span class="o">));</span> <span class="o">})</span> <span class="o">.</span><span class="na">build</span><span class="o">();</span> </code></pre></div></div> <p>Sample Output (multiline for display purpose)</p> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Thread:http-nio-8080-exec-3(38) Connection:3 Transaction:{Create:1 Rollback:0 Commit:0} Success:True Time:5 Type:Statement BatchSize:0 BindingsSize:1 Query:["INSERT INTO test VALUES ($1)"] Bindings:[($1=100)] </code></pre></div></div> <h2 id="customizing-a-formatter">Customizing a Formatter</h2> <p><code class="language-plaintext highlighter-rouge">QueryExecutionInfoFormatter</code> is a utility class that converts <code class="language-plaintext highlighter-rouge">QueryExecutionInfo</code> to a <code class="language-plaintext highlighter-rouge">String</code> for logging. The default static method, <code class="language-plaintext highlighter-rouge">showAll()</code>, creates a formatter that converts all information available in the query execution.</p> <p>However, it may be too long for your logging or you may want to customize the format.</p> <p><code class="language-plaintext highlighter-rouge">QueryExecutionInfoFormatter</code> provides <code class="language-plaintext highlighter-rouge">show...</code> methods to selectively choose the information you want to include, or you can use <code class="language-plaintext highlighter-rouge">addConsumer</code> to add your own conversion logic. The following example uses <code class="language-plaintext highlighter-rouge">showQuery()</code> and <code class="language-plaintext highlighter-rouge">showBinding()</code> with new lines for nicer formatting:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">QueryExecutionInfoFormatter</span> <span class="n">custom</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">QueryExecutionInfoFormatter</span><span class="o">()</span> <span class="o">.</span><span class="na">addConsumer</span><span class="o">((</span><span class="n">info</span><span class="o">,</span> <span class="n">sb</span><span class="o">)</span> <span class="o">-&gt;</span> <span class="o">{</span> <span class="c1">// custom conversion</span> <span class="n">sb</span><span class="o">.</span><span class="na">append</span><span class="o">(</span><span class="s">"ConnID="</span><span class="o">);</span> <span class="n">sb</span><span class="o">.</span><span class="na">append</span><span class="o">(</span><span class="n">info</span><span class="o">.</span><span class="na">getConnectionInfo</span><span class="o">().</span><span class="na">getConnectionId</span><span class="o">());</span> <span class="o">})</span> <span class="o">.</span><span class="na">newLine</span><span class="o">()</span> <span class="o">.</span><span class="na">showQuery</span><span class="o">()</span> <span class="o">.</span><span class="na">newLine</span><span class="o">()</span> <span class="o">.</span><span class="na">showBindings</span><span class="o">()</span> <span class="o">.</span><span class="na">newLine</span><span class="o">();</span> </code></pre></div></div> <p>Sample output:</p> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>ConnID=1 Query:["SELECT * FROM employee WHERE id=$1"] Bindings:[($1=200)] </code></pre></div></div> Wed, 14 Apr 2021 01:00:00 +0000 https://r2dbc.github.io/2021/04/14/r2dbc-proxy-tips-query-logging.html https://r2dbc.github.io/2021/04/14/r2dbc-proxy-tips-query-logging.html post