Skip to content

Commit 384d887

Browse files
TxEventQ Kafka Examples (#1013)
* TxEventQ Kafka Examples --------- Signed-off-by: Anders Swanson <anders.swanson@oracle.com> Co-authored-by: Mark Nelson <mark.x.nelson@oracle.com>
1 parent 6a93ed9 commit 384d887

File tree

3 files changed

+416
-203
lines changed

3 files changed

+416
-203
lines changed

docs-source/transactional-event-queues/content/getting-started/advanced-features.md

Lines changed: 26 additions & 202 deletions
Original file line numberDiff line numberDiff line change
@@ -6,19 +6,11 @@ weight = 4
66

77
This section explains advanced features of Transactional Event Queues, including transactional messaging, message propagation between queues and the database, and error handling.
88

9-
109
* [Transactional Messaging: Combine Messaging with Database Queries](#transactional-messaging-combine-messaging-with-database-queries)
1110
* [SQL Example](#sql-example)
12-
* [Kafka Example](#kafka-example)
13-
* [Transactional Produce](#transactional-produce)
14-
* [Producer Methods](#producer-methods)
15-
* [Transactional Produce Example](#transactional-produce-example)
16-
* [Transactional Consume](#transactional-consume)
17-
* [Consumer Methods](#consumer-methods)
18-
* [Transactional Consume Example](#transactional-consume-example)
1911
* [Message Propagation](#message-propagation)
2012
* [Queue to Queue Message Propagation](#queue-to-queue-message-propagation)
21-
* [Removing Subscribers and Stopping Propagation](#removing-subscribers-and-stopping-propagation)
13+
* [Stopping Queue Propagation](#stopping-queue-propagation)
2214
* [Using Database Links](#using-database-links)
2315
* [Error Handling](#error-handling)
2416

@@ -74,205 +66,48 @@ end;
7466

7567
> Note: The same pattern applies to the `dbms_aq.dequeue` procedure, allowing developers to perform DML operations within dequeue transactions.
7668
77-
### Kafka Example
78-
79-
The KafkaProducer and KafkaConsumer classes implemented by the [Kafka Java Client for Oracle Transactional Event Queues](https://github.com/oracle/okafka) provide functionality for transactional messaging, allowing developers to run database queries within a produce or consume transaction.
80-
81-
#### Transactional Produce
82-
83-
To configure a transactional producer, configure the org.oracle.okafka.clients.producer.KafkaProducer class with the `oracle.transactional.producer=true` property.
84-
85-
Once the producer instance is created, initialize database transactions with the `producer.initTransactions()` method.
86-
87-
```java
88-
Properties props = new Properties();
89-
// Use your database service name
90-
props.put("oracle.service.name", "freepdb1");
91-
// Choose PLAINTEXT or SSL as appropriate for your database connection
92-
props.put("security.protocol", "SSL");
93-
// Your database server
94-
props.put("bootstrap.servers", "my-db-server");
95-
// Path to directory containing ojdbc.properties
96-
// If using Oracle Wallet, this directory must contain the unzipped wallet (such as in sqlnet.ora)
97-
props.put("oracle.net.tns_admin", "/my/path/");
98-
props.put("enable.idempotence", "true");
99-
props.put("key.serializer", "org.apache.kafka.common.serialization.StringSerializer");
100-
props.put("value.serializer", "org.apache.kafka.common.serialization.StringSerializer");
101-
102-
// Enable Transactional messaging with the producer
103-
props.put("oracle.transactional.producer", "true");
104-
KafkaProducer<String, String> producer = new KafkaProducer<>(
105-
producerProps
106-
);
107-
108-
// Initialize the producer for database transactions
109-
producer.initTransactions();
110-
```
111-
112-
##### Producer Methods
113-
114-
- To start a database transaction, use the `producer.beginTransaction()` method.
115-
- To commit the transaction, use the `producer.commitTransaction()` method.
116-
- To retrieve the current database connection within the transaction, use the `producer.getDBConnection()` method.
117-
- To abort the transaction, use the `producer.abortTransaction()` method.
118-
119-
##### Transactional Produce Example
120-
121-
The following Java method takes in input record and processes it using a transactional producer. On error, the transaction is aborted and neither the DML nor topic produce are committed to the database. Assume the `processRecord` method does some DML operation with the record, like inserting or updating a table.
122-
123-
```java
124-
public void produce(String record) {
125-
// 1. Begin the current transaction
126-
producer.beginTransaction();
127-
128-
try {
129-
// 2. Create the producer record and prepare to send it to a topic
130-
ProducerRecord<String, String> pr = new ProducerRecord<>(
131-
topic,
132-
Integer.toString(idx),
133-
record
134-
);
135-
producer.send(pr);
136-
137-
// 3. Use the record in a database query
138-
processRecord(record, conn);
139-
} catch (Exception e) {
140-
// 4. On error, abort the transaction
141-
System.out.println("Error processing record", e);
142-
producer.abortTransaction();
143-
}
144-
145-
// 5. Once complete, commit the transaction
146-
producer.commitTransaction();
147-
System.out.println("Processed record");
148-
}
149-
```
150-
151-
#### Transactional Consume
152-
153-
To configure a transactional consumer, configure a org.oracle.okafka.clients.consumer.KafkaConsumer class with `auto.commit=false`. Disabling auto-commit will allow great control of database transactions through the `commitSync()` and `commitAsync()` methods.
154-
155-
```java
156-
Properties props = new Properties();
157-
// Use your database service name
158-
props.put("oracle.service.name", "freepdb1");
159-
// Choose PLAINTEXT or SSL as appropriate for your database connection
160-
props.put("security.protocol", "SSL");
161-
// Your database server
162-
props.put("bootstrap.servers", "my-db-server");
163-
// Path to directory containing ojdbc.properties
164-
// If using Oracle Wallet, this directory must contain the unzipped wallet (such as in sqlnet.ora)
165-
props.put("oracle.net.tns_admin", "/my/path/");
166-
167-
props.put("group.id" , "MY_CONSUMER_GROUP");
168-
// Set auto-commit to false for direct transaction management.
169-
props.put("enable.auto.commit","false");
170-
props.put("max.poll.records", 2000);
171-
props.put("key.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");
172-
props.put("value.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");
173-
KafkaConsumer<String, String> consumer = new KafkaConsumer<>(props);
174-
```
175-
176-
##### Consumer Methods
177-
178-
- To retrieve the current database connection within the transaction, use the `consumer.getDBConnection()` method.
179-
- To commit the current transaction synchronously, use the `consumer.commitSync()` method.
180-
- To commit the current transaction asynchronously, use the `consumer.commitAsync()` method.
181-
182-
##### Transactional Consume Example
183-
184-
The following Java method demonstrates how to use a KafkaConsumer for transactional messaging. Assume the `processRecord` method does some DML operation with the record, like inserting or updating a table.
185-
186-
```java
187-
public void run() {
188-
this.consumer.subscribe(List.of("topic1"));
189-
while (true) {
190-
try {
191-
// 1. Poll a batch of records from the subscribed topics
192-
ConsumerRecords<String, String> records = consumer.poll(
193-
Duration.ofMillis(100)
194-
);
195-
System.out.println("Consumed records: " + records.count());
196-
// 2. Get the current transaction's database connection
197-
Connection conn = consumer.getDBConnection();
198-
for (ConsumerRecord<String, String> record : records) {
199-
// 3. Do some DML with the record and connection
200-
processRecord(record, conn);
201-
}
202-
203-
// 4. Do a blocking commit on the current batch of records. For non-blocking, use commitAsync()
204-
consumer.commitSync();
205-
} catch (Exception e) {
206-
// 5. Since auto-commit is disabled, transactions are not
207-
// committed when commitSync() is not called.
208-
System.out.println("Unexpected error processing records. Aborting transaction!");
209-
}
210-
}
211-
}
212-
```
213-
21469
## Message Propagation
21570

21671
Messages can be propagated within the same database or across a [database link](https://docs.oracle.com/en/database/oracle/oracle-database/23/sqlrf/CREATE-DATABASE-LINK.html) to different queues or topics. Message propagation is useful for workflows that require message processing d by different consumers or for event-driven actions that need to trigger subsequent processes.
21772

21873
#### Queue to Queue Message Propagation
21974

220-
Create and start two queues. q1 will be the source queue, and q2 will be the propagated queue.
75+
Create and start two queues. `source` will be the source queue, and `dest` will be the propagated destination queue.
22176

22277
```sql
22378
begin
22479
dbms_aqadm.create_transactional_event_queue(
225-
queue_name => 'q1',
80+
queue_name => 'source',
22681
queue_payload_type => 'JSON',
22782
multiple_consumers => true
22883
);
229-
dbms_aqadm.start_queue(
230-
queue_name => 'q1'
231-
);
23284
dbms_aqadm.create_transactional_event_queue(
233-
queue_name => 'q2',
85+
queue_name => 'dest',
23486
queue_payload_type => 'JSON',
23587
multiple_consumers => true
23688
);
23789
dbms_aqadm.start_queue(
238-
queue_name => 'q2'
90+
queue_name => 'source'
23991
);
240-
end;
241-
/
242-
```
243-
244-
Add a subscriber to q2 using the [`DBMS_AQADM.ADD_SUBSCRIBER` procedure](https://docs.oracle.com/en/database/oracle/oracle-database/23/arpls/DBMS_AQADM.html#GUID-2B4498B0-7851-4520-89DD-E07FC4C5B2C7):
245-
246-
```sql
247-
begin
248-
dbms_aqadm.add_subscriber(
249-
queue_name => 'q2',
250-
subscriber => sys.aq$_agent(
251-
'q2_test_subscriber',
252-
null,
253-
null
254-
)
92+
dbms_aqadm.start_queue(
93+
queue_name => 'dest'
25594
);
25695
end;
25796
/
25897
```
25998

260-
Schedule message propagation so messages from q1 are propagated to q2, using the [`DBMS_AQADM.SCHEDULE_PROPAGATION` procedure](https://docs.oracle.com/en/database/oracle/oracle-database/23/arpls/DBMS_AQADM.html#GUID-E97FCD3F-D96B-4B01-A57F-23AC9A110A0D):
261-
99+
Schedule message propagation so messages from `source` are propagated to `dest`, using [`DBMS_AQADM.SCHEDULE_PROPAGATION` procedure](https://docs.oracle.com/en/database/oracle/oracle-database/23/arpls/DBMS_AQADM.html#GUID-E97FCD3F-D96B-4B01-A57F-23AC9A110A0D).
262100
```sql
263101
begin
264102
dbms_aqadm.schedule_propagation(
265-
queue_name => 'q1',
266-
destination_queue => 'q2',
267-
latency => 0, -- latency, in seconds, before propagating
268-
start_time => sysdate, -- begin propagation immediately
269-
duration => null -- propagate until stopped
103+
queue_name => 'source',
104+
destination_queue => 'dest'
270105
);
271106
end;
272107
/
273108
```
274109

275-
Let's enqueue a message into q1. We expect this message to be propagated to q2:
110+
Let's enqueue a message into `source`. We expect this message to be propagated to `dest`:
276111

277112
```sql
278113
declare
@@ -284,7 +119,7 @@ declare
284119
begin
285120
select json(body) into message;
286121
dbms_aq.enqueue(
287-
queue_name => 'q1',
122+
queue_name => 'source',
288123
enqueue_options => enqueue_options,
289124
message_properties => message_properties,
290125
payload => message,
@@ -295,37 +130,26 @@ end;
295130
/
296131
```
297132

298-
#### Removing Subscribers and Stopping Propagation
299-
300-
You can remove subscribers and stop propagation using the DBMS_AQADM.STOP_PROPAGATION procedures:
301-
133+
If propagation does not occur, check the `JOB_QUEUE_PROCESSES` parameter and ensure its value is high enough. If the value is very low, you may need to update it with a larger value:
302134
```sql
303-
begin
304-
dbms_aqadm.unschedule_propagation(
305-
queue_name => 'q1',
306-
destination_queue => 'q2'
307-
);
308-
end;
309-
/
135+
alter system set job_queue_processes=10;
310136
```
311137

312-
Remove the subscriber:
138+
#### Stopping Queue Propagation
139+
140+
You can stop propagation using the DBMS_AQADM.STOP_PROPAGATION procedures:
313141

314142
```sql
315143
begin
316-
dbms_aqadm.remove_subscriber(
317-
queue_name => 'q2',
318-
subscriber => sys.aq$_agent(
319-
'q2_test_subscriber',
320-
null,
321-
null
322-
)
144+
dbms_aqadm.unschedule_propagation(
145+
queue_name => 'source',
146+
destination_queue => 'dest'
323147
);
324148
end;
325149
/
326150
```
327151

328-
Your can view queue subscribers and propagation schedules from the respective `DBA_QUEUE_SCHEDULES` and `DBA_QUEUE_SUBSCRIBERS` system views.
152+
Your can view queue subscribers and propagation schedules from the respective `DBA_QUEUE_SCHEDULES` and `DBA_QUEUE_SUBSCRIBERS` system views. These views are helpful for debugging propagation issues, including error messages and schedule status.
329153

330154
#### Using Database Links
331155

@@ -334,19 +158,19 @@ To propagate messages between databases, a [database link](https://docs.oracle.c
334158
```sql
335159
begin
336160
dbms_aqadm.schedule_propagation(
337-
queue_name => 'json_queue_1',
338-
destination => '<database link>.<schema name>' -- replace with your database link and schema name,
339-
destination_queue => 'json_queue_2'
161+
queue_name => 'source',
162+
destination => '<database link>.<schema name>', -- replace with your database link and schema name,
163+
destination_queue => 'dest'
340164
);
341165
end;
342166
/
343167
```
344168

345169
## Error Handling
346170

347-
Error handling is a critical component of message processing, ensuring malformed or otherwise unprocessable messages are handled correctly. Depending on the message payload and exception, an appropriate action should be taken to either replay or store the message for inspection.
171+
Error handling is a critical component of message processing, ensuring malformed or otherwise unprocessable messages are handled correctly. Depending on the message payload and exception, an appropriate action should be taken to either replay, discard, or otherwise process the failed message. If a message cannot be dequeued due to errors, it may be moved to the [exception queue](./message-operations.md#message-expiry-and-exception-queues), if one exists.
348172

349-
If a message cannot be dequeued due to errors, it may be moved to the [exception queue](./message-operations.md#message-expiry-and-exception-queues), if one exists. You can handle such errors by using PL/SQL exception handling mechanisms.
173+
For errors on procedures like enqueue you may also use the standard SQL exception mechanisms:
350174

351175
```sql
352176
declare

0 commit comments

Comments
 (0)