There's no need to use permanent storage for rate limiting information;
try to keep it in memory if caching is enabled.

>From experiments with our live setup, this reduces the number of
INSERT/DELETE operations per second from 15 to almost 0. Disk writes on
the server hosting the AUR are reduced by 90% (from ~3MB/s to ~300kB/s).

Signed-off-by: Lukas Fleischer <[email protected]>
---
 web/lib/aurjson.class.php | 47 ++++++++++++++++++++++++++-------------
 1 file changed, 32 insertions(+), 15 deletions(-)

diff --git a/web/lib/aurjson.class.php b/web/lib/aurjson.class.php
index 1c31a65..0ac586f 100644
--- a/web/lib/aurjson.class.php
+++ b/web/lib/aurjson.class.php
@@ -152,23 +152,26 @@ class AurJSON {
                        return false;
                }
 
-               $window_length = config_get("ratelimit", "window_length");
                $this->update_ratelimit($ip);
-               $stmt = $this->dbh->prepare("
-                       SELECT Requests FROM ApiRateLimit
-                       WHERE IP = :ip");
-               $stmt->bindParam(":ip", $ip);
-               $result = $stmt->execute();
 
-               if (!$result) {
-                       return false;
-               }
+               $status = false;
+               $value = get_cache_value('ratelimit:' . $ip, $status);
+               if (!$status) {
+                       $stmt = $this->dbh->prepare("
+                               SELECT Requests FROM ApiRateLimit
+                               WHERE IP = :ip");
+                       $stmt->bindParam(":ip", $ip);
+                       $result = $stmt->execute();
+
+                       if (!$result) {
+                               return false;
+                       }
 
-               $row = $stmt->fetch(PDO::FETCH_ASSOC);
-               if ($row['Requests'] > $limit) {
-                       return true;
+                       $row = $stmt->fetch(PDO::FETCH_ASSOC);
+                       $value = $row['Requests'];
                }
-               return false;
+
+               return $value > $limit;
        }
 
        /*
@@ -182,9 +185,23 @@ class AurJSON {
                $window_length = config_get("ratelimit", "window_length");
                $db_backend = config_get("database", "backend");
                $time = time();
-
-               // Clean up old windows
                $deletion_time = $time - $window_length;
+
+               /* Try to use the cache. */
+               $status = false;
+               $value = get_cache_value('ratelimit-ws:' . $ip, $status);
+               if (!$status || ($status && $value < $deletion_time)) {
+                       if (set_cache_value('ratelimit-ws:' . $ip, $time, 
$window_length) &&
+                           set_cache_value('ratelimit:' . $ip, 1, 
$window_length)) {
+                               return;
+                       }
+               } else {
+                       $value = get_cache_value('ratelimit:' . $ip, $status);
+                       if ($status && set_cache_value('ratelimit:' . $ip, 
$value + 1, $window_length))
+                               return;
+               }
+
+               /* Clean up old windows. */
                $stmt = $this->dbh->prepare("
                        DELETE FROM ApiRateLimit
                        WHERE WindowStart < :time");
-- 
2.23.0

Reply via email to