summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLukas Fleischer <lfleischer@archlinux.org>2019-11-02 22:49:07 +0100
committerLukas Fleischer <lfleischer@archlinux.org>2019-11-02 23:32:07 +0100
commit86e4cd0731b7164a8947fa3497483378aa1de209 (patch)
treefb81c6c2ab180e40cd9e5a4fe66f843689bc4edf
parenta29155ac5bab3fcae46543ac97f64e3496e0194d (diff)
downloadaur-86e4cd0731b7164a8947fa3497483378aa1de209.tar.gz
aur-86e4cd0731b7164a8947fa3497483378aa1de209.tar.xz
aurjson: use APCu/memcached for rate limiting
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 <lfleischer@archlinux.org>
-rw-r--r--web/lib/aurjson.class.php47
1 files changed, 32 insertions, 15 deletions
diff --git a/web/lib/aurjson.class.php b/web/lib/aurjson.class.php
index 1c31a65e..0ac586fe 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");