phpmutexrace-conditionfile-accessmutual-exclusion

How can I prevent a race condition when using a file based counter in php?


I have a small web-page that delivers different content to a user based on a %3 (modulo 3) of a counter. The counter is read in from a file with php, at which point the counter is incremented and written back into the file over the old value. I am trying to get an even distribution of the content in question which is why I have this in place.

I am worried that if two users access the page at a similar time then they could either both be served the same data or that one might fail to increment the counter since the other is currently writing to the file.

I am fairly new to web-dev so I am not sure how to approach this without mutex's. I considered having only one open and close and doing all of the operations inside of it but I am trying to minimize time where in which a user could fail to access the file. (hence why the read and write are in separate opens)

What would be the best way to implement a sort of mutual exclusion so that only one person will access the file at a time and create a queue for access if multiple overlapping requests for the file come in? The primary goal is to preserve the ratio of the content that is being shown to users which involves keeping the counter consistent.

The code is as follows :

<?php

  session_start();
  $counterName = "<path/to/file>";
   
  $file = fopen($counterName, "r");
  $currCount = fread($file, filesize($counterName));
  fclose($file);

  $newCount = $currCount + 1;
  $file = fopen($counterName,'w');
  if(fwrite($file, $newCount) === FALSE){
    echo "Could not write to the file";
  }
  fclose($file);

?>

Solution

  • Just in case anyone finds themselves with the same issue, I was able to fix the problem by adding in flock($fp, LOCK_EX | LOCK_NB) before writing into the file as per the documentation for php's flock function. I have read that it is not the most reliable, but for what I am doing it was enough. Documentation here for convenience. https://www.php.net/manual/en/function.flock.php