@@ -460,33 +460,137 @@ Oracle R2DBC.
460
460
- [ oracle.net.ldap.ssl.trustManagerFactory.algorithm] ( https://docs.oracle.com/en/database/oracle/oracle-database/21/jajdb/oracle/jdbc/OracleConnection.html#CONNECTION_PROPERTY_THIN_LDAP_SSL_TRUSTMANAGER_FACTORY_ALGORITHM )
461
461
- [ oracle.net.ldap.ssl.ssl_context_protocol] ( https://docs.oracle.com/en/database/oracle/oracle-database/21/jajdb/oracle/jdbc/OracleConnection.html#CONNECTION_PROPERTY_THIN_LDAP_SSL_CONTEXT_PROTOCOL )
462
462
463
- ### Thread Safety and Parallel Execution
463
+ ### Thread Safety
464
464
Oracle R2DBC's ` ConnectionFactory ` and ` ConnectionFactoryProvider ` are the only
465
465
classes that have a thread safe implementation. All other classes implemented
466
466
by Oracle R2DBC are not thread safe. For instance, it is not safe for multiple
467
467
threads to concurrently access a single instance of ` Result ` .
468
468
> It is recommended to use a Reactive Streams library such as Project Reactor
469
469
> or RxJava to manage the consumption of non-thread safe objects
470
470
471
- Oracle Database does not allow multiple database calls to execute in parallel
472
- over a single ` Connection ` . If an attempt is made to execute a database call
473
- before a previous call has completed, then Oracle R2DBC will enqueue that call
474
- and only execute it after the previous call has completed.
475
-
476
- To illustrate, the following code attempts to execute two statements in
477
- parallel:
471
+ While it is not safe for multiple threads to concurrently access the _ same_
472
+ object, it is safe from them to do so with _ different_ objects from the _ same_
473
+ ` Connection ` . For example, two threads can concurrently subscribe to two
474
+ ` Statement ` objects from the same ` Connection ` . When this happens, the two
475
+ statements are executed in a "pipeline". Pipelining will be covered in the next
476
+ section.
477
+
478
+ ### Pipelining
479
+ Pipelining allows Oracle R2DBC to send a call without having to wait for a previous call
480
+ to complete. [ If all requirements are met] ( #requirements-for-pipelining ) , then
481
+ pipelining will be activated by concurrently subscribing to publishers
482
+ from the same connection. For example, the following code concurrently
483
+ subscribes to two statements:
478
484
``` java
479
485
Flux . merge(
480
486
connection. createStatement(
481
- " INSERT INTO example (id, value) VALUES (0, 'x ')" )
487
+ " INSERT INTO example (id, value) VALUES (0, 'X ')" )
482
488
.execute(),
483
489
connection. createStatement(
484
- " INSERT INTO example (id, value) VALUES (1, 'y ')" )
490
+ " INSERT INTO example (id, value) VALUES (1, 'Y ')" )
485
491
.execute())
486
492
```
487
- When the publisher of the second statement is subscribed to, Oracle R2DBC will
488
- enqueue a task for sending that statement to the database. The enqueued task
489
- will only be executed after the publisher of the first statement has completed.
493
+ When the ` Publisher ` returned by ` merge ` is subscribed to, both INSERTs are
494
+ immediately sent to the database. The network traffic can be visualized as:
495
+ ```
496
+ TIME | ORACLE R2DBC | NETWORK | ORACLE DATABASE
497
+ -----+------------------+---------+-----------------
498
+ 0 | Send INSERT-X | ------> | WAITING
499
+ 0 | Send INSERT-Y | ------> | WAITING
500
+ 1 | WAITING | <------ | Send Result-X
501
+ 1 | WAITING | <------ | Send Result-Y
502
+ 2 | Receive Result-X | | WAITING
503
+ 2 | Receive Result-Y | | WAITING
504
+
505
+ ```
506
+ In this visual, 1 unit of TIME is required to transfer data over the
507
+ network. The TIME column is only measuring network latency. It does not include
508
+ computational time spent on executing the INSERTs.
509
+
510
+ The key takeaway from this visual is that the INSERTs are sent and
511
+ received _ concurrently_ , rather than _ sequentially_ . Both INSERTs are sent at
512
+ TIME=0, and both are received at TIME=1. And, the results are both sent at TIME=1,
513
+ and are received at TIME=2.
514
+
515
+ > Recall that TIME is not measuring computational time. If each action by Oracle
516
+ > R2DBC and Oracle Database requires 0.1 units of computational TIME, then we
517
+ > can say:
518
+ >
519
+ > INSERTs are sent at TIME=0.1 and TIME=0.2, and are received at TIME=1.1 and
520
+ > TIME=1.2. And, the results are sent at TIME=1.2 and
521
+ > TIME=1.3, and are received at TIME=2.2 and TIME=2.3.
522
+ >
523
+ > This is a bit more complicated to think about, but it is important to keep in
524
+ > mind. All database calls will require at least some computational time.
525
+
526
+ Below is another visual of the network traffic, but in this case the INSERTs are
527
+ sent and received _ without pipelining_ :
528
+ ```
529
+ TIME | ORACLE R2DBC | NETWORK | ORACLE DATABASE
530
+ -----+------------------+---------+-----------------
531
+ 0 | Send INSERT-X | ------> | WAITING
532
+ 1 | WAITING | <------ | Send Result-X
533
+ 2 | Receive Result-X | | WAITING
534
+ 2 | Send INSERT-Y | ------> | WAITING
535
+ 3 | WAITING | <------ | Send Result-Y
536
+ 4 | Receive Result-Y | | WAITING
537
+
538
+ ```
539
+ This visual shows a _ sequential_ process of sending and receiving. It can be
540
+ compared to the _ concurrent_ process seen in the previous visual. In both cases,
541
+ Oracle R2DBC and Oracle Database have the same number of WAITING actions. These
542
+ actions are waiting for network transfers. And in both cases, each network
543
+ transfer requires 1 unit of TIME.
544
+
545
+ So if network latency is the same, and the number of
546
+ WAITING actions are the same (,and the
547
+ computational times are the same), then how are these INSERTs completing in less
548
+ TIME with pipelining? The answer is that _ pipelining allowed the
549
+ network transfer times to be waited for __ concurrently___ .
550
+
551
+ In the first visual, with pipelining, the database waits for _ both_ INSERT-X and
552
+ INSERT-Y at TIME=0. Compare that to the second visual, without pipelining, where
553
+ the database waits for INSERT-X at TIME=0, and then _ waits again_ for INSERT-Y
554
+ at TIME=2. That's 1 additional unit of TIME when compared to pipelining. The
555
+ other additional unit of TIME happens on the Oracle R2DBC side. Without
556
+ pipelining, it waits for Result-X at TIME=1, and then _ waits again_ for Result-Y
557
+ at TIME=3. With pipelining, it _ waits for both results concurrently_ at TIME=1.
558
+
559
+ ### Requirements for Pipelining
560
+
561
+ There are some requirements which must be met in order to use pipelining. As
562
+ explained in the previous section, the availability of pipelining can have a
563
+ significant impact on performance. Users should review the requirements listed
564
+ in this section when developing applications that rely on this performance gain.
565
+
566
+ In terms of functional behavior, the availability of pipelining will have no
567
+ impact: With or without it, the same database calls are going be executed. Users
568
+ who are not relying on pipelining performance do not necessarily need to review
569
+ the requirements listed in this section. Oracle JDBC is designed to
570
+ automatically check for these requirements, and it will fallback to using
571
+ sequential network transfers if any requirement is not met.
572
+
573
+ #### Product Versions
574
+ Pipelining is only available with Oracle Database version 23.4 or newer. It also
575
+ requires an Oracle JDBC version of 23.4 or newer, but this is already a
576
+ transitive dependency of Oracle R2DBC.
577
+
578
+ #### Out Of Band Breaks
579
+ Pipelining requires out-of-band (OOB) breaks (ie: TCP urgent data) for cancelling
580
+ statement execution. The Oracle JDBC Driver automatically checks if OOB is
581
+ available, and will disable pipelining if it is not. The availability of OOB may
582
+ depend on the operating system where Oracle R2DBC is running. Notably, _ OOB is
583
+ not available on Mac OS_ (or at least not available in the way which Oracle JDBC
584
+ needs it to be for sending TCP urgent data to Oracle Database).
585
+
586
+ __ For experimentation only__ , Mac OS users can choose to by-pass the OOB
587
+ requirement by setting a JVM system property:
588
+ ```
589
+ -Doracle.jdbc.disablePipeline=false
590
+ ```
591
+ Bypassing the OOB requirement on Mac OS will result in non-functional
592
+ implementations of ` Connection.setStatementTimeout(Duration) ` , and
593
+ ` Subscription.cancel() ` for a ` Subscription ` from ` Statement.execute() ` .
490
594
491
595
### Reactive Streams
492
596
Every method implemented by Oracle R2DBC that returns a Publisher has a JavaDoc
0 commit comments