Skip to content

feat(s1): saga compensation chain — wire releaseLock on cancel (STA-112)#111

Merged
Puneethkumarck merged 2 commits into
mainfrom
feature/STA-112-saga-compensation-chain
Mar 8, 2026
Merged

feat(s1): saga compensation chain — wire releaseLock on cancel (STA-112)#111
Puneethkumarck merged 2 commits into
mainfrom
feature/STA-112-saga-compensation-chain

Conversation

@Puneethkumarck

@Puneethkumarck Puneethkumarck commented Mar 8, 2026

Copy link
Copy Markdown
Owner

Summary

  • Wire actual fxLockActivity.releaseLock() call into handleCancellation() — compensations now execute instead of just logging
  • Each compensation step parsed from LIFO stack (RELEASE_FX_LOCK:<lockId>) and dispatched to the appropriate activity
  • Compensation failures are caught and logged but never block remaining compensations
  • 3 new tests: releaseLock called on cancel after FX lock, NOT called before FX lock, failure still returns FAILED

Test plan

  • shouldReleaseFxLockOnCancelAfterFxLock — verifies releaseLock called with exact FxReleaseRequest(LOCK_ID, PAYMENT_ID, reason)
  • shouldNotReleaseLockWhenCancelBeforeFxLock — cancel during compliance, neither lockFxRate nor releaseLock called
  • shouldReturnFailedWhenCompensationThrowsreleaseLock throws RuntimeException, workflow still returns FAILED
  • All existing tests pass

Closes STA-112

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Cancellations now trigger release of FX currency locks when applicable.
  • Bug Fixes

    • Improved compensation error handling so remaining cancelation steps continue if one fails.
    • Cancellation now avoids attempting releases that were never acquired.
  • Tests

    • Added tests covering release-on-cancel, no-release-before-lock, and failure-during-release scenarios.

Replace placeholder logging in handleCancellation() with actual activity
execution. Compensation steps are parsed from the LIFO stack and dispatched
to the appropriate activity (RELEASE_FX_LOCK → fxLockActivity.releaseLock).
Each compensation is wrapped in try-catch so failures are logged but never
block remaining compensations.

Tests verify: releaseLock called after FX lock cancel, releaseLock NOT
called when cancel arrives before FX lock, and compensation failure still
produces FAILED result.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Mar 8, 2026

Copy link
Copy Markdown

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: fc626fed-1601-4f93-b803-9063ab41681b

📥 Commits

Reviewing files that changed from the base of the PR and between 176614e and 6dc5be2.

📒 Files selected for processing (1)
  • payment-orchestrator/payment-orchestrator/src/test/java/com/stablecoin/payments/orchestrator/domain/workflow/PaymentWorkflowTest.java

Walkthrough

Adds concrete compensation execution to the payment cancellation flow: parses compensation step strings, invokes the FX lock release activity for matching steps, logs successes and failures, and expands tests to cover pre-lock cancellation and exception handling during compensation.

Changes

Cohort / File(s) Summary
Compensation Step Execution
payment-orchestrator/.../PaymentWorkflowImpl.java
Implements executeCompensationStep to parse compensation steps and call the FX release activity (RELEASE_FX_LOCK prefix). Replaces placeholder with actual compensation invocation in the cancellation loop and adds exception handling/logging so failures don't stop remaining compensations.
Compensation Test Coverage
payment-orchestrator/.../PaymentWorkflowTest.java
Renames and extends cancel tests: adds shouldNotReleaseLockWhenCancelBeforeFxLock and shouldReturnFailedWhenCompensationThrows. Adds FxReleaseRequest import and uses Mockito willThrow and atLeast to assert release behavior and retries.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Suggested labels

enhancement, service-s1

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 16.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed Title concisely describes the main feature: wiring releaseLock compensation into the cancellation handler and the associated saga pattern implementation.
Description check ✅ Passed Description covers all required sections: summary, related issue (STA-112), changes summary, test plan with specific test names, and testing evidence. Follows the template structure.

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

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feature/STA-112-saga-compensation-chain

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

…es activity (STA-112)

Temporal retries the activity 3 times (maxAttempts=3) before the failure
propagates to the workflow's catch block, so exact count verification fails.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

@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: 2

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

Inline comments:
In
`@payment-orchestrator/payment-orchestrator/src/main/java/com/stablecoin/payments/orchestrator/domain/workflow/PaymentWorkflowImpl.java`:
- Around line 233-246: The compensation logic in executeCompensationStep
currently uses the fxLockActivity stub which was configured with
setMaximumAttempts(3), causing retries before the catch; change this by creating
a dedicated compensation activity stub (e.g., fxLockCompensationActivity) with
RetryOptions tuned for fail-fast behavior (single attempt/short timeout) and use
that stub inside executeCompensationStep when handling RELEASE_FX_LOCK_PREFIX,
leaving the original fxLockActivity unchanged for normal workflow calls.

In
`@payment-orchestrator/payment-orchestrator/src/test/java/com/stablecoin/payments/orchestrator/domain/workflow/PaymentWorkflowTest.java`:
- Around line 248-249: The test currently asserts a single call to
fxLockActivity.releaseLock but FxLockActivity is configured with
setMaximumAttempts(3) so Temporal will retry; update the assertion in
PaymentWorkflowTest to expect three invocations by using
then(fxLockActivity).should(times(3)).releaseLock(new FxReleaseRequest(LOCK_ID,
PAYMENT_ID, "Timeout")), and add the static import for Mockito.times;
alternatively replace times(3) with atLeastOnce() if you prefer to treat retry
count as an implementation detail.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: b0d1bade-70fa-4cdd-8e8d-96652a005ff6

📥 Commits

Reviewing files that changed from the base of the PR and between d1b15a1 and 176614e.

📒 Files selected for processing (2)
  • payment-orchestrator/payment-orchestrator/src/main/java/com/stablecoin/payments/orchestrator/domain/workflow/PaymentWorkflowImpl.java
  • payment-orchestrator/payment-orchestrator/src/test/java/com/stablecoin/payments/orchestrator/domain/workflow/PaymentWorkflowTest.java

Comment on lines +233 to +246
private void executeCompensationStep(String step, UUID paymentId, String reason) {
try {
if (step.startsWith(RELEASE_FX_LOCK_PREFIX)) {
var lockId = UUID.fromString(step.substring(RELEASE_FX_LOCK_PREFIX.length()));
fxLockActivity.releaseLock(new FxReleaseRequest(lockId, paymentId, reason));
log.info("Successfully released FX lock {} for paymentId={}", lockId, paymentId);
} else {
log.warn("Unknown compensation step: {} for paymentId={}", step, paymentId);
}
} catch (Exception e) {
log.error("Compensation step failed: {} for paymentId={}, error={}",
step, paymentId, e.getMessage(), e);
}
}

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 | 🔵 Trivial

Compensation retry semantics — architectural consideration.

The fxLockActivity stub used here has setMaximumAttempts(3) configured at Lines 63-64. This means compensation retries exhaustively before the catch block is reached, which may be intentional for resilience.

If compensation should fail fast (e.g., single attempt with immediate fallback), consider creating a separate activity stub with different RetryOptions for compensation scenarios. This also aligns with saga patterns where compensations often have distinct retry/timeout policies.

Not blocking — current behavior is functionally correct.

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

In
`@payment-orchestrator/payment-orchestrator/src/main/java/com/stablecoin/payments/orchestrator/domain/workflow/PaymentWorkflowImpl.java`
around lines 233 - 246, The compensation logic in executeCompensationStep
currently uses the fxLockActivity stub which was configured with
setMaximumAttempts(3), causing retries before the catch; change this by creating
a dedicated compensation activity stub (e.g., fxLockCompensationActivity) with
RetryOptions tuned for fail-fast behavior (single attempt/short timeout) and use
that stub inside executeCompensationStep when handling RELEASE_FX_LOCK_PREFIX,
leaving the original fxLockActivity unchanged for normal workflow calls.

@Puneethkumarck Puneethkumarck merged commit 35db9a8 into main Mar 8, 2026
14 of 15 checks passed
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.

1 participant