This is an automated email from the ASF dual-hosted git repository.

asf-gitbox-commits pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/qpid-proton.git

commit e99e6306489a71d47cc3aaee516720c2700420be
Author: Andrew Stitcher <[email protected]>
AuthorDate: Tue May 5 23:42:11 2026 -0400

    PROTON-1442: Improve C++ transaction API documentation
    
    This code was written with the assistance of Cursor.
---
 cpp/docs/pages.dox                       |   1 +
 cpp/docs/transactions.md                 | 107 +++++++++++++++++++++++++++++++
 cpp/include/proton/messaging_handler.hpp |   5 +-
 cpp/include/proton/session.hpp           |  15 +++++
 4 files changed, 127 insertions(+), 1 deletion(-)

diff --git a/cpp/docs/pages.dox b/cpp/docs/pages.dox
index 4660015e6..ee4352fcd 100644
--- a/cpp/docs/pages.dox
+++ b/cpp/docs/pages.dox
@@ -5,3 +5,4 @@
 /// @page mt_page Multi-threading
 /// @page io_page IO integration
 /// @page tracing Tracing
+/// @page transactions_page Transactions
diff --git a/cpp/docs/transactions.md b/cpp/docs/transactions.md
new file mode 100644
index 000000000..bde4cb137
--- /dev/null
+++ b/cpp/docs/transactions.md
@@ -0,0 +1,107 @@
+# Transactions {#transactions_page}
+
+**Unsettled API** — Session transactions and related handler callbacks
+are still evolving; see @ref mainpage "Conventions".
+
+This page documents the C++ API contract for AMQP 1.0 local
+transactions (OASIS AMQP 1.0 §4.6). Automated coverage lives in
+`tests/cpp/tx_tests/` and is run by `tests/cpp/test_tx_tester.py`
+against `python/examples/broker.py`.
+
+## Lifecycle
+
+| Application call | Success | Failure |
+|------------------|---------|---------|
+| `session::transaction_declare()` | 
`messaging_handler::on_session_transaction_declared` | 
`messaging_handler::on_session_transaction_error` |
+| `session::transaction_commit()` | 
`messaging_handler::on_session_transaction_committed` | 
`messaging_handler::on_session_transaction_aborted` with 
`session::transaction_error()` set |
+| `session::transaction_abort()` | 
`messaging_handler::on_session_transaction_aborted` | (rollback cannot fail at 
the AMQP level) |
+
+After a successful discharge (commit or abort), 
`session::transaction_is_declared()`
+returns `false`. `session::transaction_id()` returns the coordinator-assigned
+identifier while a transaction is declared or discharging, and remains
+available for the duration of discharge callbacks; it is cleared when those
+callbacks return.
+
+Declare sends `amqp:declare:list` on a coordinator link advertising
+`amqp:local-transactions`. Commit and abort send `amqp:discharge:list`
+with the transaction id and a `fail` flag (`false` for commit, `true`
+for abort).
+
+## Proton session guarantees
+
+These rules are enforced synchronously (they throw `proton::error`):
+
+- **One active transaction per session.** A second `transaction_declare()`
+  while a transaction is already active is rejected.
+- **No declare with unsettled outgoing deliveries.** All outgoing transfers
+  on the session during a transaction are treated as part of that
+  transaction; declare is only allowed when every non-coordinator sender
+  link on the session has no unsettled deliveries.
+- **Discharge requires a declared transaction.** `transaction_commit()` and
+  `transaction_abort()` throw if no transaction is in the declared state.
+
+After discharge completes, a new transaction may be declared on the same
+session.
+
+## Messaging under a transaction
+
+While `transaction_is_declared()` is true:
+
+- `sender::send()` attaches `amqp:transactional-state:list` to outgoing
+  transfers. The peer responds with a provisional outcome; the application
+  receives `on_transactional_accept`, `on_transactional_reject`, or
+  `on_transactional_release` as appropriate.
+- `delivery::accept()`, `reject()`, `release()`, and `modify()` attach
+  transactional state instead of settling immediately. Incoming deliveries
+  stay unsettled until discharge; `on_delivery_settle` is not raised for
+  them before commit or abort.
+
+On **successful commit**:
+
+- Incoming deliveries with provisional dispositions are settled.
+- Outgoing provisional outcomes become final: `on_tracker_accept`,
+  `on_tracker_reject`, or `on_tracker_release`, followed by
+  `on_tracker_settle` when the peer settles.
+
+On **abort or failed commit**:
+
+- Actions taken under the transaction are rolled back (as if they never
+  happened).
+- Unsettled outgoing trackers on the session are settled locally without
+  a disposition update.
+- Unsettled incoming deliveries are dispositioned with `modified` (failed)
+  by default (`transaction_options::auto_modify_on_abort`, default `true`).
+
+Failed commit is reported through `on_session_transaction_aborted`, not
+`on_session_transaction_error`. Use `session::transaction_error()` inside
+the aborted callback to distinguish a broker-rejected commit from an
+explicit abort.
+
+## Transaction options
+
+`transaction_options::auto_modify_on_abort(bool)` controls whether Proton
+automatically modifies unsettled incoming deliveries when a transaction
+aborts or a commit discharge is rejected. The default is `true`. When
+`false`, the application must update those deliveries itself.
+
+## Test environment
+
+CI runs the script suite against `python/examples/broker.py` with a
+2-second transaction timeout (`-t 2`). Scripts are numbered so they
+execute in order against a single broker instance; an initial setup
+script seeds the target queue.
+
+**Future work (out of scope today):** broker abstraction in the test
+harness (e.g. `TX_TESTER_BROKER_CMD` / `TX_TESTER_BROKER_URL`) so the
+same scripts can be run against Artemis or other peers; non-fatal
+assertions for broker-specific provisional disposition timing.
+
+Provisional disposition callbacks depend on the peer implementing
+transactional state promptly. The example broker does; other brokers may
+differ.
+
+## Examples
+
+- `cpp/examples/tx_recv.cpp` — transactional receive with commit/abort batches
+- `cpp/examples/tx_send.cpp` — transactional send with provisional outcomes
+- `tests/cpp/tx_tester.cpp` — scriptable transaction tester used by the suite
diff --git a/cpp/include/proton/messaging_handler.hpp 
b/cpp/include/proton/messaging_handler.hpp
index 1d368d894..42a2466a9 100644
--- a/cpp/include/proton/messaging_handler.hpp
+++ b/cpp/include/proton/messaging_handler.hpp
@@ -239,7 +239,10 @@ PN_CPP_CLASS_EXTERN messaging_handler {
     /// Fallback error handling.
     PN_CPP_EXTERN virtual void on_error(const error_condition&);
 
-    /// **Unsettled API** - Called when a local transaction is declared.
+    /// **Unsettled API** - Transaction lifecycle and messaging callbacks.
+    /// @see transactions_page
+    ///
+    /// Called when a local transaction is declared.
     PN_CPP_EXTERN virtual void on_session_transaction_declared(session&);
 
     /// **Unsettled API** - Called when a local transaction is discharged 
successfully.
diff --git a/cpp/include/proton/session.hpp b/cpp/include/proton/session.hpp
index 24438caa9..d7ad67415 100644
--- a/cpp/include/proton/session.hpp
+++ b/cpp/include/proton/session.hpp
@@ -106,18 +106,33 @@ PN_CPP_CLASS_EXTERN session : public 
internal::object<pn_session_t>, public endp
     PN_CPP_EXTERN void* user_data() const;
 
     /// **Unsettled API** - Declare a new local transaction on this session.
+    ///
+    /// When the transactions coordinator accepts, the application receives
+    /// messaging_handler::on_session_transaction_declared. A rejected declare 
is reported as
+    /// messaging_handler::on_session_transaction_error.
+    ///
+    /// @see transactions_page
     PN_CPP_EXTERN void transaction_declare();
 
     /// **Unsettled API** - Commit the currently declared transaction.
+    ///
+    /// Outcome is delivered asynchronously via 
messaging_handler::on_session_transaction_committed
+    /// or messaging_handler::on_session_transaction_aborted.
     PN_CPP_EXTERN void transaction_commit();
 
     /// **Unsettled API** - Abort the currently declared transaction.
+    ///
+    /// This completes with messaging_handler::on_session_transaction_aborted 
(see also
+    /// transaction_commit()).
     PN_CPP_EXTERN void transaction_abort();
 
     /// **Unsettled API** - Return true if a transaction is currently declared.
     PN_CPP_EXTERN bool transaction_is_declared() const;
 
     /// **Unsettled API** - Return the identifier of the current transaction.
+    ///
+    /// There will be a valid transaction id from the tranacrtion declared 
callback until after any transaction
+    /// dicharge callback.
     PN_CPP_EXTERN binary transaction_id() const;
 
     /// **Unsettled API** - Return the error condition associated with 
transaction.


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to