]> git.openstreetmap.org Git - nominatim.git/commitdiff
prune list of blocked items if too large, different costs for different page types
authorBrian Quinion <openstreetmap@brian.quinion.co.uk>
Sat, 8 Dec 2012 21:39:24 +0000 (21:39 +0000)
committerBrian Quinion <openstreetmap@brian.quinion.co.uk>
Sat, 8 Dec 2012 21:39:24 +0000 (21:39 +0000)
lib/init.php
lib/leakybucket.php [new file with mode: 0644]
settings/settings.php
utils/blocks.php
website/details.php
website/reverse.php
website/search.php

index 15e38a5b17fcf7862e71bacda260db87e7b03d60..67efdbfdf6edb3c8894f7d8a487adfde7d193633 100644 (file)
@@ -4,6 +4,7 @@
 
        require_once(CONST_BasePath.'/settings/settings.php');
        require_once(CONST_BasePath.'/lib/lib.php');
+       require_once(CONST_BasePath.'/lib/leakybucket.php');
        require_once(CONST_BasePath.'/lib/db.php');
 
        if (get_magic_quotes_gpc())
diff --git a/lib/leakybucket.php b/lib/leakybucket.php
new file mode 100644 (file)
index 0000000..778fe58
--- /dev/null
@@ -0,0 +1,137 @@
+<?php
+
+       function getBucketMemcache()
+       {
+               static $m;
+
+               if (!CONST_ConnectionBucket_MemcacheServerAddress) return null;
+               if (!isset($m))
+               {
+                       $m = new Memcached();
+                       $m->addServer(CONST_ConnectionBucket_MemcacheServerAddress, CONST_ConnectionBucket_MemcacheServerPort);
+               }
+               return $m;
+       }
+
+       function doBucket($asKey, $iRequestCost, $iLeakPerSecond, $iThreshold)
+       {
+               $m = getBucketMemcache();
+               if (!$m) return 0;
+
+               $iMaxVal = 0;
+               $t = time();
+
+               foreach($asKey as $sKey)
+               {
+                       $aCurrentBlock = $m->get($sKey);
+                       if (!$aCurrentBlock)
+                       {
+                               $aCurrentBlock = array($iRequestCost, $t);
+                       }
+                       else
+                       {
+                               // add RequestCost
+                               // remove leak * the time since the last request 
+                               $aCurrentBlock[0] += $iRequestCost - ($t - $aCurrentBlock[1])*$iLeakPerSecond;
+                               $aCurrentBlock[1] = $t;
+                       }
+
+                       if ($aCurrentBlock[0] <= 0)
+                       {
+                               $m->delete($sKey);
+                       }
+                       else
+                       {
+                               // If we have hit the threshold stop and record this to the block list
+                               if ($aCurrentBlock[0] >= $iThreshold)
+                               {
+                                       $aCurrentBlock[0] = $iThreshold;
+
+                                       // Make up to 10 attempts to record this to memcache (with locking to prevent conflicts)
+                                       $i = 10;
+                                       for($i = 0; $i < 10; $i++)
+                                       {
+                                               $aBlockedList = $m->get('blockedList', null, $hCasToken);
+                                               if (!$aBlockedList)
+                                               {
+                                                       $aBlockedList = array();
+                                                       $m->add('blockedList', $aBlockedList);
+                                                       $aBlockedList = $m->get('blockedList', null, $hCasToken);
+                                               }
+                                               if (!isset($aBlockedList[$sKey]))
+                                               {
+                                                       $aBlockedList[$sKey] = array(1, $t);
+                                               }
+                                               else
+                                               {
+                                                       $aBlockedList[$sKey][0]++;
+                                                       $aBlockedList[$sKey][1] = $t;
+                                               }
+                                               if (sizeof($aBlockedList) > CONST_ConnectionBucket_MaxBlockList)
+                                               {
+                                                       uasort($aBlockedList, 'byValue1');
+                                                       $aBlockedList = array_slice($aBlockedList, 0, CONST_ConnectionBucket_MaxBlockList);
+                                               }
+                                               $x = $m->cas($hCasToken, 'blockedList', $aBlockedList);
+                                               if ($x) break;
+                                       }
+                               }
+                               // Only keep in memcache until the time it would have expired (to avoid clutering memcache)
+                               $m->set($sKey, $aCurrentBlock, $t + 1 + $aCurrentBlock[0]/$iLeakPerSecond);
+                       }
+
+                       // Bucket result in the largest bucket we find
+                       $iMaxVal = max($iMaxVal, $aCurrentBlock[0]);
+               }
+
+               return $iMaxVal;
+        }
+
+       function byValue1($a, $b)
+       {
+               if ($a[1] == $b[1])
+               {
+                       return 0;
+               }
+               return ($a[1] > $b[1]) ? -1 : 1;
+       }
+
+       function byLastBlockTime($a, $b)
+       {
+               if ($a['lastBlockTimestamp'] == $b['lastBlockTimestamp'])
+               {
+                       return 0;
+               }
+               return ($a['lastBlockTimestamp'] > $b['lastBlockTimestamp']) ? -1 : 1;
+       }
+
+       function getBucketBlocks()
+       {
+               $m = getBucketMemcache();
+               if (!$m) return null;
+               $t = time();
+               $aBlockedList = $m->get('blockedList', null, $hCasToken);
+               if (!$aBlockedList) $aBlockedList = array();
+               foreach($aBlockedList as $sKey => $aDetails)
+               {
+                       $aCurrentBlock = $m->get($sKey);
+                       if (!$aCurrentBlock) $aCurrentBlock = array(0, $t);
+                       $iCurrentBucketSize = max(0, $aCurrentBlock[0] - ($t - $aCurrentBlock[1])*CONST_ConnectionBucket_LeakRate);
+                       $aBlockedList[$sKey] = array(
+                               'totalBlocks' => $aDetails[0],
+                               'lastBlockTimestamp' => $aDetails[1],
+                               'currentBucketSize' => $iCurrentBucketSize,
+                               'currentlyBlocked' => $iCurrentBucketSize + (CONST_ConnectionBucket_Cost_Reverse) >= CONST_ConnectionBucket_BlockLimit,
+                               );
+               }
+               uasort($aBlockedList, 'byLastBlockTime');
+               return $aBlockedList;
+       }
+
+       function clearBucketBlocks()
+       {
+               $m = getBucketMemcache();
+               if (!$m) return false;
+               $m->delete('blockedList');
+               return true;
+       }
index 6bf526caaf8e05cadcec65c6a26eed0be0ffbdbe..58463a516c2a28cbe147a92e7f92e56a186f1abd 100644 (file)
@@ -17,6 +17,7 @@
        // Connection buckets to rate limit people being nasty
        @define('CONST_ConnectionBucket_MemcacheServerAddress', false);
        @define('CONST_ConnectionBucket_MemcacheServerPort', 11211);
+       @define('CONST_ConnectionBucket_MaxBlockList', 100);
        @define('CONST_ConnectionBucket_LeakRate', 1);
        @define('CONST_ConnectionBucket_BlockLimit', 10);
        @define('CONST_ConnectionBucket_WaitLimit', 6);
index d2db17f0e9478b60e9dbc8e9f9faf8cb3a8e6c1b..6dee2845b06bfeb315da6af32b08c07ae5f7f4e1 100755 (executable)
@@ -11,6 +11,7 @@
                array('verbose', 'v', 0, 1, 0, 0, 'bool', 'Verbose output'),
                array('list', 'l', 0, 1, 0, 0, 'bool', 'List recent blocks'),
                array('delete', 'd', 0, 1, 0, 0, 'bool', 'Clear recent blocks list'),
+               array('flush', '', 0, 1, 0, 0, 'bool', 'Flush all blocks / stats'),
        );
        getCmdOpt($_SERVER['argv'], $aCMDOptions, $aResult, true, true);
 
 
                $aBlocks = getBucketBlocks();
                echo "\n";
-               printf(" %-40s | %12s | %7s | %13s | %16s | %31s\n", "Key", "Total Blocks", "Current", "Still Blocked", "Last Req Blocked", "Last Block Time");
-               printf(" %'--40s-|-%'-12s-|-%'-7s-|-%'-13s-|-%'-16s-|-%'-31s\n", "", "", "", "", "", "");
+               printf(" %-40s | %12s | %7s | %13s | %31s\n", "Key", "Total Blocks", "Current", "Still Blocked", "Last Block Time");
+               printf(" %'--40s-|-%'-12s-|-%'-7s-|-%'-13s-|-%'-31s\n", "", "", "", "", "");
                foreach($aBlocks as $sKey => $aDetails)
                {
-                       printf(" %-40s | %12s | %7s | %13s | %16s | %31s\n", $sKey, $aDetails['totalBlocks'], (int)$aDetails['currentBucketSize'], $aDetails['lastRequestBlocked']?'Y':'N', $aDetails['currentlyBlocked']?'Y':'N', date("r", $aDetails['lastBlockTimestamp']));
+                       printf(" %-40s | %12s | %7s | %13s | %31s\n", $sKey, $aDetails['totalBlocks'], 
+                               (int)$aDetails['currentBucketSize'], $aDetails['currentlyBlocked']?'Y':'N', 
+                               date("r", $aDetails['lastBlockTimestamp']));
                }
                echo "\n";
        }
@@ -42,3 +45,8 @@
                $m->set('sleepCounter', 0);
                clearBucketBlocks();
        }
+
+       if ($aResult['flush'])
+       {
+               $m->flush();
+       }
index 3d80ea5f7b96215f756790589c34bd65ab5c90b6..9cbbf28f87b94182c99c3eedc6b327f495c0affc 100755 (executable)
@@ -1,4 +1,6 @@
 <?php
+       @define('CONST_ConnectionBucket_PageType', 'Details');
+
         require_once(dirname(dirname(__FILE__)).'/lib/init-website.php');
         require_once(CONST_BasePath.'/lib/log.php');
 
index c155fa573030fa54cb5a71629727b808dc2be972..d5a36998efd4143a7513dc6f5f8a30797f9e4aab 100755 (executable)
@@ -1,4 +1,6 @@
 <?php
+       @define('CONST_ConnectionBucket_PageType', 'Reverse');
+
        require_once(dirname(dirname(__FILE__)).'/lib/init-website.php');
        require_once(CONST_BasePath.'/lib/log.php');
 
index 08c2eee095049c3ff6090c3de03bb42a265e6d8c..e69ea78d4d3ed126af55ef656e6bdf13752c45d4 100755 (executable)
@@ -1,4 +1,6 @@
 <?php
+       @define('CONST_ConnectionBucket_PageType', 'Search');
+
        require_once(dirname(dirname(__FILE__)).'/lib/init-website.php');
        require_once(CONST_BasePath.'/lib/log.php');