I need to use mutexes or semaphores in PHP, and it scares me. To clarify, I'm not scared of writing deadlock-free code that synchronizes properly or afraid of the perils of concurrent programming, but of how well PHP handles fringe cases.
Quick background: writing a credit card handler interface that sits between the users and the 3rd party credit card gateway. Need to prevent duplicate requests, and already have a system in place that works, but if the user hits submit (w/out JS enabled so I can't disable the button for them) milliseconds apart, a race condition ensues where my PHP script does not realize that a duplicate request has been made. Need a semaphore/mutex so I can ensure only one successful request goes through for each unique transaction.
I'm running PHP behind nginx via PHP-FPM with multiple processes on a multi-core Linux machine. I want to be sure that
- semaphores are shared between all php-fpm processes and across all cores (i686 kernel).
- php-fpm handles a PHP process crash while holding a mutex/semaphore and releases it accordingly.
- php-fpm handles a session abort while holding a mutex/semaphore and releases it accordingly.
Yes, I know. Very basic questions, and it would be foolish to think that a proper solution doesn't exist for any other piece of software. But this is PHP, and it was most certainly not built with concurrency in mind, it crashes often (depending on which extensions you have loaded), and is in a volatile environment (PHP-FPM and on the web).
With regards to (1), I'm assuming if PHP is using the POSIX functions that both these conditions hold true on a SMP i686 machine. As for (2), I see from briefly skimming the docs that there is a parameter that decides this behavior (though why would one ever want PHP to NOT release a mutex is the session is killed I don't understand). But (3) is my main concern and I don't know if it's safe to assume that php-fpm properly handles all fringe cases for me. I (obviously) don't ever want a deadlock, but I'm not sure I can trust PHP to never leave my code in a state where it cannot obtain a mutex because the session that grabbed it was either gracefully or ungracefully terminated.
I have considered using a MySQL LOCK TABLES
approach, but there's even more doubt there because while I trust the MySQL lock more than the PHP lock, I fear if PHP aborts a request (with*out* crashing) while holding the MySQL session lock, MySQL might keep the table locked (esp. because I can easily envision the code that would cause this to take place).
Honestly, I'd be most comfortable with a very basic C extension where I can see exactly what POSIX calls are being made and with what params to ensure the exact behavior I want.. but I don't look forward to writing that code.
Anyone have any concurrency-related best practices regarding PHP they'd like to share?