fix: atomic usage recording to prevent race conditions #53
Open
Charon
wants to merge 1 commit from
feat/fix-usage-race-condition into dev
pull from: feat/fix-usage-race-condition
merge into: core:dev
core:dev
core:feat/test-workspace-controller
core:feat/workspace-ownership-transfer
core:feat/ide-helper-annotations
core:feat/test-namespace-service
core:feat/artisan-provision-command
core:feat/workspace-activity-audit-log
core:feat/bulk-workspace-invitations
core:feat/openapi-docs
core:feat/add-infection-mutation-testing
core:feat/fix-readme-namespaces
core:feat/test-entitlement-webhook-service
core:feat/invitation-and-role-improvements
core:feat/workspace-lazy-loading
core:feat/invitation-soft-deletes
core:feat/totp-edge-case-tests
core:feat/standardise-error-responses
core:feat/entitlement-exception-hierarchy
core:feat/consolidate-user-relationships-v2
core:feat/clarify-workspace-scope-architecture
core:feat/validate-invitation-token-format
core:feat/constrain-feature-code-fk
core:feat/fix-token-timing-attack
core:feat/add-workspace-role-index
core:feat/add-phpstan-larastan
core:feat/complete-user-stats-stubs
core:feat/fix-namespace-n-plus-1
core:feat/fix-parent-feature-cascade
core:feat/namespace-cascade-delete
core:feat/pin-core-dependency
core:feat/remove-hardcoded-domain
core:feat/test-workspace-team-service
core:feat/workspace-return-types
core:main
1 commit
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
ef7ed4ca77
|
fix: atomic usage recording and provisioning to prevent race conditions
Addresses #42 — race condition in EntitlementService usage recording. Changes: 1. New atomic check-and-record methods: - checkAndRecordUsage() — workspace-scoped - checkAndRecordNamespaceUsage() — namespace-scoped These use DB::transaction() with lockForUpdate() on usage records to prevent concurrent requests from reading the same count and both passing the limit check (TOCTOU race). 2. New locked usage query methods: - getLockedCurrentUsage() — bypasses cache, uses SELECT FOR UPDATE - getLockedNamespaceCurrentUsage() — namespace equivalent These are used within the atomic transactions to get a consistent usage count under pessimistic locking. 3. provisionPackage() and provisionNamespacePackage(): Wrapped base-package check-and-cancel in DB::transaction() with lockForUpdate() to prevent duplicate active base packages from concurrent provisioning requests. 4. provisionBoost(): Wrapped in DB::transaction() with lockForUpdate() on existing boosts. Added duplicate detection for blesta_addon_id to prevent the same external addon from being provisioned twice. 5. provisionNamespaceBoost(): Wrapped in DB::transaction() with lockForUpdate() on existing namespace boosts to serialise concurrent provisioning. The existing can() + recordUsage() pattern is preserved for backward compatibility. Callers should migrate to checkAndRecordUsage() for race-safe usage tracking. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> |