Updated race cond handling for member badges gen
authorAndrew Engelbrecht <sudoman@ninthfloor.org>
Tue, 3 Jan 2017 21:48:09 +0000 (16:48 -0500)
committerAndrew Engelbrecht <sudoman@ninthfloor.org>
Tue, 3 Jan 2017 21:48:09 +0000 (16:48 -0500)
This change removes the universal 0.5 second sleep prior to querying the
database about the membership since date.

Instead, the process now goes right ahead with the query. It sleeps for
2 seconds if the first query did not succeed, then it re-attempts the
query a second time and continues on as before.

This should cost less time on average while catching more race
conditions with the longer conditional sleep.

memberdashboard.php

index 202b62e076f4673af08a04a5ce5c09729da3b5cc..e795746fc59171fe7a54e6e464dd3fa3a2b71f9c 100644 (file)
@@ -125,22 +125,30 @@ function memberdashboard_civicrm_post($op, $objectName, $objectId, &$objectRef)
       // TODO: Extract all of this to a class for clarity and
       // organization's sake.
 
-      // sleep for a half second in order to avoid a race condition preventing badge creation.
-      // This is somewhat sketchy, as this will tie up an apache processes, allowing for a DoS by someone
-      // creating many phony memberships in a short period of time.
-      // In practice, this isn't so bad. apache is currently configured to use prefork mpm on crmserver1p
-      // and has about 40 forked processes. so 800 new memberships all at once would tie up the server for
-      // an additional 10 seconds.
-      usleep(500000);
-
       // Get the oldest join date for the contact's memberships.
-      $contactId = $objectRef->contact_id;
-      $dao = CRM_Core_DAO::executeQuery(
-        'SELECT join_date FROM civicrm_membership WHERE contact_id=%1 ORDER BY join_date ASC LIMIT 1',
-        array( 1 => array($contactId, 'Integer') )
-      );
+      $getDao = function () {
+
+          $contactId = $objectRef->contact_id;
+          $dao = CRM_Core_DAO::executeQuery(
+            'SELECT join_date FROM civicrm_membership WHERE contact_id=%1 ORDER BY join_date ASC LIMIT 1',
+            array( 1 => array($contactId, 'Integer') )
+          );
+          return $dao;
+      };
+
+      $dao = $getDao();
+      $fetchResult = $dao->fetch();
+
+      if(!$fetchResult) {
+          // retry after sleeping 2 seconds in order to avoid a race condition
+          usleep(2000000);
+          $dao->free();
+
+          $dao = $getDao();
+          $fetchResult = $dao->fetch();
+      }
 
-      if($dao->fetch()) {
+      if($fetchResult) {
         // Make the API call.
         $joinDate = $dao->join_date;
         $apiUrl = civicrm_api3('setting', 'getvalue', array(