Skip to content

multiple function calls within single transaction#160

Open
salamonpavel wants to merge 2 commits into
masterfrom
feature/transactions
Open

multiple function calls within single transaction#160
salamonpavel wants to merge 2 commits into
masterfrom
feature/transactions

Conversation

@salamonpavel

@salamonpavel salamonpavel commented May 28, 2026

Copy link
Copy Markdown
Contributor

Closes #159
Adds support for multiple function calls within single transaction in Doobie module.

Summary by CodeRabbit

  • New Features

    • Compose multiple database operations into a single atomic transaction, including a utility to run connection-level DB actions within that transaction.
  • Tests

    • Added integration tests covering multi-operation transactions, rollback on failure, and backward-compatibility of direct execution.

@coderabbitai

coderabbitai Bot commented May 28, 2026

Copy link
Copy Markdown

Review Change Stack

Walkthrough

Adds ConnectionIO-returning APIs to Doobie function wrappers, a DoobieEngine.runConnectionIO transaction runner, and integration tests that compose multiple function calls into a single database transaction and verify rollback and compatibility behaviours.

Changes

Transactional composition of database function calls

Layer / File(s) Summary
ConnectionIO-based APIs in DoobieFunction
doobie/src/main/scala/za/co/absa/db/fadb/doobie/DoobieFunction.scala
Imports updated to include doobie.ConnectionIO and status types. Traits DoobieFunction and DoobieFunctionWithStatus expose toConnectionIO returning ConnectionIO[Seq[R]] / ConnectionIO[Seq[FailedOrRow[R]]]. Concrete classes add toConnectionIOSingle and toConnectionIOOptional and status-aware variants returning unevaluated ConnectionIO programs.
Transaction executor in DoobieEngine
doobie/src/main/scala/za/co/absa/db/fadb/doobie/DoobieEngine.scala
Adds instance method runConnectionIO[R](cio: ConnectionIO[R]): F[R] and companion helper runConnectionIO (requires F: Async) that delegate to the engine to execute a composed ConnectionIO via transactor.transact.
Integration tests for transactional composition
doobie/src/test/scala/za/co/absa/db/fadb/doobie/DoobieTransactionCompositionIntegrationTests.scala
New test suite declaring CreateActor, GetActorById, GetActors wrappers and tests that compose toConnectionIO/toConnectionIOSingle, verify persisted rows, assert rollback on injected failure, and confirm direct apply() still works.

Sequence Diagram

sequenceDiagram
  participant Caller
  participant DoobieFunction
  participant ConnectionIO
  participant DoobieEngine
  participant Database
  Caller->>DoobieFunction: toConnectionIO(values)
  DoobieFunction->>ConnectionIO: returns unevaluated program
  Caller->>DoobieFunction: toConnectionIOSingle(values2)
  DoobieFunction->>ConnectionIO: returns unevaluated program
  Caller->>ConnectionIO: compose programs
  Caller->>DoobieEngine: runConnectionIO(composed)
  DoobieEngine->>Database: transact(composed)
  Database-->>DoobieEngine: result / rollback
  DoobieEngine-->>Caller: F[Result]
Loading

🎯 3 (Moderate) | ⏱️ ~20 minutes

🐰 I stitched my queries, one by one,
Held them close until the job was done,
Then DoobieEngine opened the gate,
Committed all, or rolled back fate,
A happy hop — transactions now in sync!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'multiple function calls within single transaction' directly and clearly describes the primary change: enabling multiple database function calls within a single transaction.
Linked Issues check ✅ Passed The PR implements the core requirement from #159 by adding ConnectionIO-returning methods to DoobieFunction and DoobieEngine, enabling composed transaction execution.
Out of Scope Changes check ✅ Passed All changes are directly related to implementing transaction composition: new methods in DoobieEngine and DoobieFunction classes, and integration tests validating the feature.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/transactions

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions

Copy link
Copy Markdown

JaCoCo core module code coverage report - scala 2.13.11

Overall Project 59.84% 🍏

There is no coverage information present for the Files changed

@github-actions

github-actions Bot commented May 28, 2026

Copy link
Copy Markdown

JaCoCo doobie module code coverage report - scala 2.13.11

Overall Project 70.29% -3.02% 🍏
Files changed 67.74%

File Coverage
DoobieEngine.scala 95.29% -10.59%
DoobieFunction.scala 73.49% -3.74%

@github-actions

Copy link
Copy Markdown

JaCoCo slick module code coverage report - scala 2.13.11

Overall Project 91.42% 🍏

There is no coverage information present for the Files changed

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@doobie/src/main/scala/za/co/absa/db/fadb/doobie/DoobieFunction.scala`:
- Around line 131-134: The eager evaluation of toFragmentsSeq(values) in
toConnectionIO (and the similar status/single/optional variants) allows
exceptions to escape before entering the ConnectionIO context; change these to
defer evaluation by wrapping the call in the library's effectful delay (e.g.,
ConnectionIO/FC delay) and then flatMap into composeFragments(...).query...
(i.e., replace the val fragments = toFragmentsSeq(values) with a delayed effect
that computes fragments inside ConnectionIO and then .flatMap(fragments =>
composeFragments(fragments).query[R].to[Seq]/.unique/.option/.status) so any
thrown exceptions are captured by the ConnectionIO/MonadError machinery.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: c36cd8b5-9b2d-4f4d-af81-41290e8dfd3b

📥 Commits

Reviewing files that changed from the base of the PR and between 3b3efd6 and 0011c6d.

📒 Files selected for processing (3)
  • doobie/src/main/scala/za/co/absa/db/fadb/doobie/DoobieEngine.scala
  • doobie/src/main/scala/za/co/absa/db/fadb/doobie/DoobieFunction.scala
  • doobie/src/test/scala/za/co/absa/db/fadb/doobie/DoobieTransactionCompositionIntegrationTests.scala

@ABLL526

ABLL526 commented May 29, 2026

Copy link
Copy Markdown
Contributor

Read the code, easy to understand. It is really good and will be useful. Just some random thoughts:

  • Will it transact the successful operations in the event of a single (or multiple) operation(s) failure within one composed transaction?
  • Is there a limit to the number of composed operations? Should there be?
  • Can it be mistakenly nested?

@jakipatryk jakipatryk left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Makes sense, the comment from CodeRabbit is probably worth addressing though.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
doobie/src/main/scala/za/co/absa/db/fadb/doobie/DoobieEngine.scala (1)

90-101: ⚡ Quick win

Consider enhancing documentation to address transaction semantics questions.

The implementation correctly uses transact for atomic transaction execution. However, the PR objectives note that a reviewer asked whether the transaction will roll back all operations on failure. Consider making the rollback-on-failure and commit-on-success semantics explicit in the documentation to address this concern.

📝 Proposed documentation enhancement
  /**
   *  Executes an arbitrary `ConnectionIO` program in a single transaction.
   *  This enables composing multiple database operations (including multiple fa-db function calls)
   *  into a single atomic transaction.
+  *  
+  *  Transaction semantics: All operations will be committed on success. If any operation fails,
+  *  the entire transaction will be rolled back and the error will be propagated in the effect `F`.
   *
   *  `@param` cio the `ConnectionIO` program to execute
   *  `@tparam` R the result type of the program
   *  `@return` the result wrapped in the effect type `F`
   */
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@doobie/src/main/scala/za/co/absa/db/fadb/doobie/DoobieEngine.scala` around
lines 90 - 101, Update the ScalaDoc for runConnectionIO to explicitly state
transaction semantics: that cio.transact(transactor) will run the ConnectionIO
program in a single atomic transaction, commit when the program completes
successfully, and automatically roll back all operations if the program raises
an exception (or the effect F fails); reference the runConnectionIO method and
the use of ConnectionIO.transact with the transactor so readers know where this
behavior comes from.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@doobie/src/main/scala/za/co/absa/db/fadb/doobie/DoobieEngine.scala`:
- Around line 90-101: Update the ScalaDoc for runConnectionIO to explicitly
state transaction semantics: that cio.transact(transactor) will run the
ConnectionIO program in a single atomic transaction, commit when the program
completes successfully, and automatically roll back all operations if the
program raises an exception (or the effect F fails); reference the
runConnectionIO method and the use of ConnectionIO.transact with the transactor
so readers know where this behavior comes from.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: db62e4a8-d07e-422f-b922-4d169773ec35

📥 Commits

Reviewing files that changed from the base of the PR and between 0011c6d and c68774f.

📒 Files selected for processing (2)
  • doobie/src/main/scala/za/co/absa/db/fadb/doobie/DoobieEngine.scala
  • doobie/src/main/scala/za/co/absa/db/fadb/doobie/DoobieFunction.scala
🚧 Files skipped from review as they are similar to previous changes (1)
  • doobie/src/main/scala/za/co/absa/db/fadb/doobie/DoobieFunction.scala

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add support for multiple function calls within single transaction

3 participants