Skip to content

fix: remove PgSemaphore to fix the payment deadlock#2720

Merged
ygrishajev merged 1 commit intomainfrom
feature/sema
Feb 13, 2026
Merged

fix: remove PgSemaphore to fix the payment deadlock#2720
ygrishajev merged 1 commit intomainfrom
feature/sema

Conversation

@ygrishajev
Copy link
Contributor

@ygrishajev ygrishajev commented Feb 12, 2026

PgSemaphore is no longer needed in the console API. All operations using it are either logically safe without locking or already idempotent. BatchSigningClientService, the only remaining consumer, is only used for testing or in single instance mode, so a simple in-process Sema(1) is sufficient.

Summary by CodeRabbit

  • Refactor
    • Removed distributed PostgreSQL semaphore-based synchronization and replaced with in-process semaphore for relevant billing flows.
    • Simplified concurrency control in payment confirmation, coupon application, and wallet creation flows.
  • Tests
    • Removed semaphore-related unit and integration tests and a race-condition test for wallet creation.
  • Chores
    • Removed semaphore provider initialization and related integration setup.

@ygrishajev ygrishajev requested a review from a team as a code owner February 12, 2026 18:16
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 12, 2026

📝 Walkthrough

Walkthrough

Removes the distributed PostgreSQL semaphore implementation and decorator usage, deletes related tests and provider initialization, replaces one distributed semaphore with an in-process Sema(1), and removes semaphore-related test setup and a concurrency test.

Changes

Cohort / File(s) Summary
Core pg-semaphore removal
apps/api/src/core/lib/pg-semaphore/pg-semaphore.ts, apps/api/src/core/lib/pg-semaphore/semaphore.decorator.ts, apps/api/src/core/lib/pg-semaphore/pg-semaphore.integration.ts, apps/api/src/core/lib/pg-semaphore/semaphore.decorator.spec.ts
Deleted the pg-semaphore implementation, decorator, integration tests, and unit tests; removed Semaphore types, classes, factory, and decorator logic.
Barrel & provider cleanup
apps/api/src/core/lib/pg-semaphore/index.ts, apps/api/src/core/providers/index.ts, apps/api/src/core/providers/pg-semaphore.provider.ts
Removed re-exports and provider initialization (APP_INITIALIZER) for the pg-semaphore provider.
Stripe controller updates
apps/api/src/billing/controllers/stripe/stripe.controller.ts
Removed @Semaphore() decorators and Semaphore import from confirmPayment and applyCoupon.
Batch-signing client migration
apps/api/src/billing/lib/batch-signing-client/batch-signing-client.service.ts, .../batch-signing-client.service.spec.ts
Replaced distributed semaphore with an in-process Sema(1) (field + acquire/release), updated hasPendingTransactions, and removed SemaphoreFactory usage from tests.
Refill service & tests
apps/api/src/billing/services/refill/refill.service.ts, apps/api/src/billing/services/refill/refill.service.spec.ts
Removed @Semaphore() on getOrCreateUserWallet and deleted the concurrency/race-condition test; removed semaphore test setup calls.
Functional tests setup
apps/api/test/setup-functional-tests.ts
Removed Postgres/semaphore client initialization and teardown from functional-test setup.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

🐇 I hopped through code where locks once lay,
The pg-guards folded, they hopped away.
One Sema now watches the batch with care,
No cross-node chatter — just quiet air.
A rabbit cheered: simpler, light, and fair.

🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title directly and clearly describes the main change: removing PgSemaphore to fix a payment deadlock issue. It accurately summarizes the primary objective of the pull request.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Merge Conflict Detection ✅ Passed ✅ No merge conflicts detected when merging into main

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

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feature/sema

No actionable comments were generated in the recent review. 🎉


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

return { data: [] };
}

@Semaphore()
Copy link
Contributor Author

Choose a reason for hiding this comment

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

this is logically redundant. We have it properly implemented on the frontend and if implemented via api - it would not do a double call. in the worst case we could do a refund


@Semaphore()
@Protected([{ action: "create", subject: "StripePayment" }])
async applyCoupon(
Copy link
Contributor Author

Choose a reason for hiding this comment

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

apply coupon is already idempotent op in stripe.

this.logger.info({ event: "WALLET_BALANCE_REDUCED", userId, amountUsd, previousLimit: currentLimit, nextLimit });
}

@Semaphore()
Copy link
Contributor Author

Choose a reason for hiding this comment

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

user wallet has a unique index on userId so sema is redundant here

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In
`@apps/api/src/billing/lib/batch-signing-client/batch-signing-client.service.ts`:
- Around line 144-146: hasPendingTransactions currently returns
this.sema.nrWaiting() > 0 which misses in-flight batches; introduce an
activeBatches counter (e.g., this.activeBatches = 0) and update
hasPendingTransactions to check both this.sema.nrWaiting() and
this.activeBatches; increment activeBatches immediately after successfully
acquiring the semaphore in the method that starts a batch (the same place the
tx-signer increments) and decrement it in that method's finally block so
activeBatches accurately reflects in-flight operations and prevents premature
cleanup.

@codecov
Copy link

codecov bot commented Feb 12, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 51.35%. Comparing base (d20789c) to head (448b024).
⚠️ Report is 1 commits behind head on main.

❌ Your project status has failed because the head coverage (75.01%) is below the target coverage (78.00%). You can increase the head coverage or adjust the target coverage.

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #2720      +/-   ##
==========================================
- Coverage   52.24%   51.35%   -0.89%     
==========================================
  Files        1057     1019      -38     
  Lines       28032    27136     -896     
  Branches     6357     6255     -102     
==========================================
- Hits        14644    13937     -707     
+ Misses      12953    12774     -179     
+ Partials      435      425      -10     
Flag Coverage Δ *Carryforward flag
api 75.01% <100.00%> (-0.19%) ⬇️
deploy-web 37.19% <ø> (ø) Carriedforward from d20789c
log-collector ?
notifications 87.94% <ø> (ø) Carriedforward from d20789c
provider-console 81.48% <ø> (ø) Carriedforward from d20789c
provider-proxy 84.35% <ø> (ø) Carriedforward from d20789c
tx-signer ?

*This pull request uses carry forward flags. Click here to find out more.

Files with missing lines Coverage Δ
...rc/billing/controllers/stripe/stripe.controller.ts 0.00% <ø> (ø)
...tch-signing-client/batch-signing-client.service.ts 87.96% <100.00%> (-0.22%) ⬇️
.../api/src/billing/services/refill/refill.service.ts 97.95% <ø> (ø)

... and 35 files with indirect coverage changes

🚀 New features to boost your workflow:
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

PgSemaphore is no longer needed in the console API. All operations using
it are either logically safe without locking or already idempotent.
BatchSigningClientService, the only remaining consumer, is only used for
testing or in single instance mode, so a simple in-process Sema(1) is sufficient.
@ygrishajev ygrishajev merged commit 5e19905 into main Feb 13, 2026
53 of 54 checks passed
@ygrishajev ygrishajev deleted the feature/sema branch February 13, 2026 09:10
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.

2 participants