Skip to content

fix(api): merge processFile() transactions with calling transactions#287

Merged
balazs-szucs merged 1 commit into
grimmory-tools:developfrom
Zacxen:fix/NPE-during-metadata-cleanup
Mar 30, 2026
Merged

fix(api): merge processFile() transactions with calling transactions#287
balazs-szucs merged 1 commit into
grimmory-tools:developfrom
Zacxen:fix/NPE-during-metadata-cleanup

Conversation

@Zacxen

@Zacxen Zacxen commented Mar 30, 2026

Copy link
Copy Markdown
Contributor

Description

If processFile was called within an ongoing transaction, it would previously have generated a new transaction, which could leave the database in a state inconsistent with the prior transaction's expectations, causing errors and database corruption. This fixes that so that transactions are merged at the cost of errors propagating upwards. If processFile() throws an uncaught error, it taints the entire transaction and causes a rollback of the entire transaction.

Linked Issue: Fixes #261

Changes

  • swaps propagation mode to REQUIRED to merge transactions so that multiple transactions do not modify the same book at the same time and conflict

Summary by CodeRabbit

  • Refactor
    • Improved file processing to better handle concurrent operations and ensure data consistency.

- swaps propagation mode to REQUIRED to merge transactions so that multiple transactions do not modify the same book at the same time and conflict
@coderabbitai

coderabbitai Bot commented Mar 30, 2026

Copy link
Copy Markdown
Contributor
📝 Walkthrough

Walkthrough

A single annotation modification changed the processFile method's transaction propagation from REQUIRES_NEW to REQUIRED in AbstractFileProcessor.java, allowing it to participate in existing transactions instead of always creating new ones.

Changes

Cohort / File(s) Summary
Transaction Propagation Update
booklore-api/src/main/java/org/booklore/service/fileprocessor/AbstractFileProcessor.java
Updated @Transactional annotation on processFile(LibraryFile) to use Propagation.REQUIRED instead of Propagation.REQUIRES_NEW, changing transaction boundary behavior to reuse existing transactions when available.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~8 minutes

Suggested labels

backend, enhancement

Suggested reviewers

  • balazs-szucs

Poem

🐰 A hoppy fix with transactional cheer,
No new boxes, just reuse what's here,
REQUIRED now joins the flowing stream,
Database harmony—a rabbit's dream! ✨

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The PR title follows the conventional commit format with a clear, descriptive message that accurately reflects the main change.
Description check ✅ Passed The PR description includes all required sections with appropriate detail about the problem, solution, and linked issue.
Linked Issues check ✅ Passed The PR adequately addresses the root cause of issue #261 by preventing simultaneous transaction conflicts through transaction merging.
Out of Scope Changes check ✅ Passed The single code change is directly scoped to address the linked issue requirement of preventing concurrent transaction conflicts.
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
✨ Simplify code
  • Create PR with simplified code

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

@coderabbitai coderabbitai Bot 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.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@booklore-api/src/main/java/org/booklore/service/fileprocessor/AbstractFileProcessor.java`:
- Line 56: processFile currently uses `@Transactional`(propagation =
Propagation.REQUIRED) which causes it to join the caller transaction (e.g.
FileAsBookProcessor.processLibraryFilesGrouped) so a swallowed exception in
processGroupWithErrorHandling will mark the shared transaction rollback-only and
later cause a global rollback; fix by either changing
AbstractFileProcessor.processFile to use Propagation.REQUIRES_NEW so each file
runs in its own transaction, or alter
FileAsBookProcessor.processGroupWithErrorHandling to rethrow the exception after
logging so the outer transaction is aware and can fail fast — locate
AbstractFileProcessor.processFile and
FileAsBookProcessor.processLibraryFilesGrouped/processGroupWithErrorHandling and
implement one of these two remedies consistently.
🪄 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: cd6d3bc9-4b82-4007-b9e4-b647a4b98615

📥 Commits

Reviewing files that changed from the base of the PR and between 0966ab8 and 3795300.

📒 Files selected for processing (1)
  • booklore-api/src/main/java/org/booklore/service/fileprocessor/AbstractFileProcessor.java
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: Test Suite / Frontend Tests
  • GitHub Check: Test Suite / Backend Tests
  • GitHub Check: Analyze (java-kotlin)
🧰 Additional context used
📓 Path-based instructions (1)
booklore-api/src/**/*.java

📄 CodeRabbit inference engine (AGENTS.md)

booklore-api/src/**/*.java: Use 4-space indentation in Java files and match surrounding Java style
Prefer constructor injection via Lombok patterns already used in the codebase. Do not introduce @Autowired field injection
Use MapStruct for entity/DTO mapping

Files:

  • booklore-api/src/main/java/org/booklore/service/fileprocessor/AbstractFileProcessor.java
🧠 Learnings (1)
📓 Common learnings
Learnt from: pedronave
Repo: grimmory-tools/grimmory PR: 173
File: booklore-api/src/main/java/org/booklore/service/hardcover/HardcoverSyncService.java:188-199
Timestamp: 2026-03-24T18:46:47.249Z
Learning: In grimmory-tools/grimmory (HardcoverSyncService.java), when a `hardcoverBookId` is stored in book metadata but is malformed (non-numeric), the preferred behavior is to return null and skip the sync rather than falling back to ISBN-based lookup. The reasoning is that an explicitly set bookId represents deliberate user intent, and silently resolving via ISBN could sync progress to a different book than intended.

}

@Transactional(propagation = Propagation.REQUIRES_NEW)
@Transactional(propagation = Propagation.REQUIRED)

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.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Read-only verification of transactional boundaries + swallowed exception pattern.

set -euo pipefail

echo "== processFile transaction annotation =="
rg -n --type=java 'class\s+AbstractFileProcessor|@Transactional\(.*Propagation\.(REQUIRED|REQUIRES_NEW)|public\s+FileProcessResult\s+processFile\s*\(' -C3

echo
echo "== FileAsBookProcessor transaction + catch-and-continue flow =="
rg -n --type=java 'class\s+FileAsBookProcessor|@Transactional|processLibraryFilesGrouped\s*\(|processGroupWithErrorHandling\s*\(|catch\s*\(Exception\s+\w+\)|processor\.processFile\s*\(' -C3

echo
echo "== Additional processFile call sites =="
rg -n --type=java '\bprocessFile\s*\(' -C2

Repository: grimmory-tools/grimmory

Length of output: 50380


🏁 Script executed:

fd -i 'FileAsBookProcessor' --type f

Repository: grimmory-tools/grimmory

Length of output: 232


🏁 Script executed:

wc -l booklore-api/src/main/java/org/booklore/service/library/FileAsBookProcessor.java

Repository: grimmory-tools/grimmory

Length of output: 151


🏁 Script executed:

cat -n booklore-api/src/main/java/org/booklore/service/library/FileAsBookProcessor.java

Repository: grimmory-tools/grimmory

Length of output: 15490


REQUIRED here can silently poison the parent transaction when callers swallow exceptions.

At Line 56, processFile(...) now joins the caller transaction. In FileAsBookProcessor.java (lines 54–82), processLibraryFilesGrouped(...) is transactional and processGroupWithErrorHandling(...) catches exceptions without rethrowing. If one processFile(...) call fails, the shared transaction is marked rollback-only; subsequent groups continue, then commit fails and all groups roll back.

Consider either rethrowing after logging in processGroupWithErrorHandling(...), or moving transaction boundaries to per-group/per-file units while preserving consistency guarantees.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@booklore-api/src/main/java/org/booklore/service/fileprocessor/AbstractFileProcessor.java`
at line 56, processFile currently uses `@Transactional`(propagation =
Propagation.REQUIRED) which causes it to join the caller transaction (e.g.
FileAsBookProcessor.processLibraryFilesGrouped) so a swallowed exception in
processGroupWithErrorHandling will mark the shared transaction rollback-only and
later cause a global rollback; fix by either changing
AbstractFileProcessor.processFile to use Propagation.REQUIRES_NEW so each file
runs in its own transaction, or alter
FileAsBookProcessor.processGroupWithErrorHandling to rethrow the exception after
logging so the outer transaction is aware and can fail fast — locate
AbstractFileProcessor.processFile and
FileAsBookProcessor.processLibraryFilesGrouped/processGroupWithErrorHandling and
implement one of these two remedies consistently.

@balazs-szucs balazs-szucs 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.

Many thanks for looking into this!

@balazs-szucs balazs-szucs merged commit f1cbcf3 into grimmory-tools:develop Mar 30, 2026
14 checks passed
zachyale pushed a commit to zachyale/grimmory that referenced this pull request Apr 17, 2026
…rimmory-tools#287)

- swaps propagation mode to REQUIRED to merge transactions so that multiple transactions do not modify the same book at the same time and conflict
zachyale pushed a commit to zachyale/grimmory that referenced this pull request Apr 17, 2026
…rimmory-tools#287)

- swaps propagation mode to REQUIRED to merge transactions so that multiple transactions do not modify the same book at the same time and conflict
zachyale pushed a commit that referenced this pull request Apr 17, 2026
…287)

- swaps propagation mode to REQUIRED to merge transactions so that multiple transactions do not modify the same book at the same time and conflict
zachyale pushed a commit that referenced this pull request Apr 22, 2026
…287)

- swaps propagation mode to REQUIRED to merge transactions so that multiple transactions do not modify the same book at the same time and conflict
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Database ends up in invalid state when readding / rescanning the same book multiple times

2 participants