Diese Präsentation wurde erfolgreich gemeldet.
Die SlideShare-Präsentation wird heruntergeladen. ×

The Making of the Oracle R2DBC Driver and How to Take Your Code from Synchronous to Reactive

Anzeige
Anzeige
Anzeige
Anzeige
Anzeige
Anzeige
Anzeige
Anzeige
Anzeige
Anzeige
Anzeige
Anzeige

Hier ansehen

1 von 23 Anzeige

The Making of the Oracle R2DBC Driver and How to Take Your Code from Synchronous to Reactive

Herunterladen, um offline zu lesen

SpringOne 2021
Session Title:The Making of the Oracle R2DBC Driver and How to Take Your Code from Synchronous to Reactive
Speakers: Kuassi Mensah, Director of Product Management at Oracle; Michael McMahon, Principal Member of Technical Staff at Oracle

SpringOne 2021
Session Title:The Making of the Oracle R2DBC Driver and How to Take Your Code from Synchronous to Reactive
Speakers: Kuassi Mensah, Director of Product Management at Oracle; Michael McMahon, Principal Member of Technical Staff at Oracle

Anzeige
Anzeige

Weitere Verwandte Inhalte

Diashows für Sie (20)

Ähnlich wie The Making of the Oracle R2DBC Driver and How to Take Your Code from Synchronous to Reactive (20)

Anzeige

Weitere von VMware Tanzu (20)

Aktuellste (20)

Anzeige

The Making of the Oracle R2DBC Driver and How to Take Your Code from Synchronous to Reactive

  1. 1. The Making of the Oracle R2DBC Driver And How to Take Your Code from Synchronous to Reactive Kuassi Mensah Michael McMahon @kmensah
  2. 2. Agenda ü Once Upon a Time • The Making of Oracle R2DBC driver • Taking Your Code from Synchronous to Reactive Oracle Confidential
  3. 3. Once Upon a Time … The Asynchronous Database Access API (ADBA) • Started in 2017 • Goal – A standard Java database access API that never blocks user threads – No compatibility or complementarity with JDBC • Motivations – Asynchronous apps have better throughput – Simultaneous access to multiple databases, e.g. map/reduce, sharded databases – Fire and forget, e.g. DML, stored procedures • The API is available from OpenJDK at https://bit.ly/3t3nkAd 3
  4. 4. 1. Per the Java SE team: “the future of Java scalability is Virtual Threads” –ADBA would not be accepted as a Java standard as it was an async solution to a problem that would be addressed by virtual threads. – Unless it became a standard ADBA would have very little impact, certainly not enough to justify the effort. 2. There was very little support within the Java community. -- Gravitating towards R2DBC 4 Why Did We Stop Working on ADBA?
  5. 5. We changed our focus towards • JDBC Reactive Extensions • An Oracle R2DBC Driver • Instrumenting the JDBC driver to support Virtual Threads 5 Then What?
  6. 6. SQL Execution (OraclePreparedStatement): Publisher<Boolean> executeAsyncOracle() Publisher<Long> executeUpdateAsyncOracle() Publisher<Long> executeBatchAsyncOracle() Publisher<OracleResultSet> executeQueryAsyncOracle() Row Fetching (OracleResultSet): Publisher<T> publisherOracle(Function<OracleRow, T> f) LOB I/O (OracleBlob): Publisher<byte[]> publisherOracle(long position) Subscriber<byte[]> subscriberOracle(long position) LOB I/O (OracleClob): Publisher<String> publisherOracle(long position) Subscriber<String> subscriberOracle(long position) Connection Closing (OracleConnection): Publisher<Success> closeAsyncOracle() Transaction Closing (OracleConnection): Publisher<Success> commitAsyncOracle() Publisher<Success> rollbackAsyncOracle() Connection Creation (OracleConnectionBuilder): Publisher<OracleConnection> buildConnectionPublisherOracle() JDBC Reactive Extensions (built into the driver)
  7. 7. User Java Code JDBC Reactive Extension Standard JDBC API 3rd party Reactive Streams Libraries Async call with non-blocking backpressure operators (map, reduce, filters), concurrency modeling, monitoring, tracing Implements Java SE reactive stream interface (Flow) Full Reactive Streams Sync/blocking JDBC calls Java Business Logic User Java code Oracle Database Oracle JDBC driver Summary of Oracle DB Access with Java – Part 1
  8. 8. Agenda • Once Upon a Time ü The Making of Oracle R2DBC driver • Taking Your Code from Synchronous to Reactive Oracle Confidential
  9. 9. public boolean execute() throws SQLException { // Check if this statement is closed if (isClosed) throw new SQLException("Closed Statement"); // Check if all bind values are set for (Object bindValue : bindValues) { if (bindValue == null) throw new SQLException("Bind value not set"); } // Close the current ResultSet if (resultSet != null) resultSet.close(); // Reset the current update count updateCount = -1; // Blocking database call SqlResult sqlResult = jdbcConnection.executeSql(sql, bindValues); // Handle the result resultSet = sqlResult.getResultSet(); updateCount = sqlResult.getUpdateCount(); return resultSet != null; } public Publisher<Boolean> executeAsyncOracle() throws SQLException { // Check if this statement is closed if (isClosed) throw new SQLException("Closed Statement"); // Check if all bind values are set for (Object bindValue : bindValues) { if (bindValue == null) throw new SQLException("Bind value not set"); } // Close the current ResultSet if (resultSet != null) resultSet.close(); // Reset the current update count updateCount = -1; // Non-blocking database call return Flux.from(jdbcConnection.executeSqlAsync(sql, bindValues)) .map(sqlResult -> { // Handle the result resultSet = sqlResult.getResultSet(); updateCount = sqlResult.getUpdateCount(); return resultSet != null; }); } Developing JDBC Reactive Extensions
  10. 10. public boolean execute() throws SQLException { // Check if this statement is closed if (isClosed) throw new SQLException("Closed Statement"); // Check if all bind values are set for (Object bindValue : bindValues) { if (bindValue == null) throw new SQLException("Bind value not set"); } // Close the current ResultSet if (resultSet != null) resultSet.close(); // Reset the current update count updateCount = -1; // Blocking database call SqlResult sqlResult = jdbcConnection.executeSql(sql, bindValues); // Handle the result resultSet = sqlResult.getResultSet(); updateCount = sqlResult.getUpdateCount(); return resultSet != null; } public Publisher<Boolean> executeAsyncOracle() throws SQLException { // Check if this statement is closed if (isClosed) throw new SQLException("Closed Statement"); // Check if all bind values are set for (Object bindValue : bindValues) { if (bindValue == null) throw new SQLException("Bind value not set"); } // Close the current ResultSet if (resultSet != null) resultSet.close(); // Reset the current update count updateCount = -1; // Non-blocking database call return Flux.from(jdbcConnection.executeSqlAsync(sql, bindValues)) .map(sqlResult -> { // Handle the result resultSet = sqlResult.getResultSet(); updateCount = sqlResult.getUpdateCount(); return resultSet != null; }); } Developing JDBC Reactive Extensions
  11. 11. public boolean execute() throws SQLException { prepareForExecute(); SqlResult result = jdbcConnection.executeSql(sql, bindValues); return handleResult(result); } public Publisher<Boolean> executeAsyncOracle() throws SQLException { prepareForExecute(); return Flux.from(jdbcConnection.executeSqlAsync(sql, bindValues)) .map(sqlResult -> handleResult(sqlResult)); } private void prepareForExecute() throws SQLException { // Check if this statement is closed if (isClosed) throw new SQLException("Closed Statement"); // Check if all bind values are set for (Object bindValue : bindValues) { if (bindValue == null) throw new SQLException("Bind value not set"); } // Close the current ResultSet if (resultSet != null) resultSet.close(); // Reset the current update count updateCount = -1; } private boolean handleResult(SqlResult result) { resultSet = result.getResultSet(); updateCount = result.getUpdateCount(); return resultSet != null; } Developing JDBC Reactive Extensions
  12. 12. Developing JDBC Reactive Extensions Setup Blocking Handle Result Setup Non-Blocking Handle Result Synchronous JDBC Setup Blocking Handle Result Setup Non-Blocking Handle Result Setup Non-Blocking Handle Result Reactive JDBC Database Setup Blocking Handle Result Database
  13. 13. Adapting JDBC Reactive Extensions for Oracle R2DBC R2DBC SPI Reactive Extensions API ConnectionFactory create() OracleConnectionBuilder buildConnectionPublisherOracle() Statement execute() OraclePreparedStatement executeAsyncOracle() Result map(Function) OracleResultSet publisherOracle(Function) https://github.com/oracle/oracle-r2dbc
  14. 14. Adapting JDBC Reactive Extensions for Oracle R2DBC R2DBC SPI Reactive Extensions API Connection commit() OracleConnection commitAsyncOracle() Blob stream() OracleBlob publisherOracle() Clob stream() OracleClob publisherOracle() rollback() close() rollbackAsyncOracle() closeAsyncOracle() https://github.com/oracle/oracle-r2dbc
  15. 15. Agenda • Once Upon a Time • The Making of Oracle R2DBC driver ü Taking Your Code from Synchronous to Reactive Oracle Confidential
  16. 16. From Synchronous to Reactive: Configuration static DataSource configureJdbc() throws SQLException { OracleDataSource dataSource = new oracle.jdbc.pool.OracleDataSource(); dataSource.setDriverType("thin"); dataSource.setServerName(DatabaseConfig.HOST); dataSource.setPortNumber(DatabaseConfig.PORT); dataSource.setServiceName(DatabaseConfig.SERVICE_NAME); dataSource.setUser(DatabaseConfig.USER); dataSource.setPassword(DatabaseConfig.PASSWORD); return dataSource; } static ConnectionFactory configureR2dbc() { return ConnectionFactories.get(ConnectionFactoryOptions.builder() .option(DRIVER, "oracle") .option(HOST, DatabaseConfig.HOST) .option(PORT, DatabaseConfig.PORT) .option(DATABASE, DatabaseConfig.SERVICE_NAME) .option(USER, DatabaseConfig.USER) .option(PASSWORD, DatabaseConfig.PASSWORD) .build()); }
  17. 17. From Synchronous to Reactive: Query static String queryJdbc(java.sql.Connection connection) throws SQLException { try (java.sql.Statement statement = connection.createStatement()) { ResultSet resultSet = statement.executeQuery("SELECT 'Hello, JDBC!' FROM sys.dual"); if (resultSet.next()) return resultSet.getString(1); else throw new NoSuchElementException("Query returned zero rows"); } } static Publisher<String> queryR2dbc(io.r2dbc.spi.Connection connection) { return Flux.from(connection.createStatement( "SELECT 'Hello, R2DBC!' FROM sys.dual") .execute()) .flatMap(result -> result.map(row -> row.get(0, String.class))) .switchIfEmpty(Flux.error( new NoSuchElementException("Query returned zero rows"))); } Flat Map: f(x) -> { y0, y1, …, yn } { x0, x1, …, xn } -> { f(x0), f(x1), … f(xn)} { y0-0, y0-1, …, y0-n, y1-0, y1-1, …, y1-n, …, yn-n}
  18. 18. From Synchronous to Reactive: Insert static int insertJdbc(java.sql.Connection connection) throws SQLException { try (PreparedStatement preparedStatement = connection.prepareStatement( "INSERT INTO JdbcToR2dbcTable(id, value) VALUES (?, ?)")) { preparedStatement.setInt(1, 0); preparedStatement.setString(2, "JDBC"); return preparedStatement.executeUpdate(); } } static Publisher<Integer> insertR2dbc(io.r2dbc.spi.Connection connection) { return Flux.from(connection.createStatement( "INSERT INTO JdbcToR2dbcTable(id, value) VALUES (?, ?)") .bind(0, 0) .bind(1, "R2DBC") .execute()) .flatMap(Result::getRowsUpdated); }
  19. 19. From Synchronous to Reactive: Update static int updateJdbc(java.sql.Connection connection) throws SQLException { try (PreparedStatement preparedStatement = connection.prepareStatement( "UPDATE JdbcToR2dbcTable SET value = ? WHERE id = ?")) { preparedStatement.setString(1, "JDBC"); preparedStatement.setInt(2, 0); return preparedStatement.executeUpdate(); } } static Publisher<Integer> updateR2dbc(io.r2dbc.spi.Connection connection) { return Flux.from(connection.createStatement( "UPDATE JdbcToR2dbcTable SET value = ? WHERE id = ?") .bind(0, "R2DBC") .bind(1, 0) .execute()) .flatMap(Result::getRowsUpdated); }
  20. 20. From Synchronous to Reactive: Conditional Branch static int tryUpdateJdbc(java.sql.Connection connection) throws SQLException { // Try to update the row int updateCount = updateJdbc(connection); // If the row does not exist, then insert it. if (updateCount == 0) return insertJdbc(connection); else return updateCount; } static Publisher<Integer> tryUpdateR2dbc(io.r2dbc.spi.Connection connection) { // Try to update the row return Flux.from(updateR2dbc(connection)) .flatMap(updateCount -> { // If the row does not exist, then insert it. if (updateCount == 0) return insertR2dbc(connection); else return Flux.just(updateCount); }); }
  21. 21. From Synchronous to Reactive: Error Recovery static int tryInsertJdbc(java.sql.Connection connection) throws SQLException { try { // Try to insert the row return insertJdbc(connection); } catch (SQLException sqlException) { // If the row already exists, then update it. if (sqlException.getErrorCode() == UNIQUE_CONSTRAINT_VIOLATION) return updateJdbc(connection); else throw sqlException; } } static Publisher<Integer> tryInsertR2dbc(io.r2dbc.spi.Connection connection) { // Try to insert the row return Flux.from(insertR2dbc(connection)) .onErrorResume(R2dbcException.class, r2dbcException -> { // If the row already exists, then update it. if (r2dbcException.getErrorCode() == UNIQUE_CONSTRAINT_VIOLATION) return updateR2dbc(connection); else return Flux.error(r2dbcException); }); }
  22. 22. From Synchronous to Reactive: Loop static int loopJdbc(java.sql.Connection connection) throws SQLException { do { try { // Try to update the row, or insert it if it does not exist return tryUpdateJdbc(connection); } catch (SQLException sqlException) { // If another database session has inserted the row before this // one did, then recover from failure by continuing the loop. if (sqlException.getErrorCode() != UNIQUE_CONSTRAINT_VIOLATION) throw sqlException; } } while (true); } static Publisher<Integer> loopR2dbc(io.r2dbc.spi.Connection connection) { // Try to update the row, or insert it if it does not exist return Flux.from(tryUpdateR2dbc(connection)) .onErrorResume(R2dbcException.class, r2dbcException -> { // If another database session has inserted the row before this // one did, then recover from failure by recursively invoking this // method. if (r2dbcException.getErrorCode() != UNIQUE_CONSTRAINT_VIOLATION) return Flux.error(r2dbcException); else return loopR2dbc(connection); }); }
  23. 23. User Java Code JDBC Reactive Extension Standard JDBC API R2DBC + 3rd party Reactive Streams Libraries Async call with non-blocking backpressure operators (map, reduce, filters), concurrency modeling, monitoring, tracing Implements Java SE reactive stream interface (Flow) Full Reactive Streams Sync/blocking JDBC calls Java Business Logic User Java code Oracle Database Oracle JDBC driver Summary of Oracle DB Access with Java – Part 2

×