view_contact previously decremented free_contact_views/ purchased_contact_views on every call with no record of which applications had already had their contact unlocked -- refreshing the same application's contact panel silently re-spent the allowance every time, not just under concurrency, and the check-then-act shape also let concurrent requests both pass the exhausted-quota check before either UPDATE committed. Now the whole claim (dedup check + conditional decrement + job_applications.contact_unlocked_at write) runs in one transaction with company_profiles locked FOR UPDATE, gated on a WHERE ... > 0 guard on the decrement itself. create_job had the same shape for the 1-free-job/month rule and purchased_job_slots: two concurrent requests could both observe "quota available" before either committed, letting a company publish 2+ free jobs in a month or drive purchased_job_slots negative. Same fix: lock company_profiles FOR UPDATE for the whole check+mutate+ insert sequence. Also adds CHECK (>= 0) constraints on all four company_profiles counters as defense-in-depth, matching the pattern tracecoin_wallets already uses for balance/reserved. |
||
|---|---|---|
| .. | ||
| src | ||
| Cargo.toml | ||
| Dockerfile | ||