From 96b5c2ae0050cf1d8a7b42c8221808c904e0f618 Mon Sep 17 00:00:00 2001 From: Jakob Borg Date: Fri, 13 Nov 2015 10:14:19 +0100 Subject: [PATCH] Set Retry-After header --- cmd/discosrv/main.go | 6 ++++++ cmd/discosrv/psql.go | 4 +++- cmd/discosrv/ql.go | 4 +++- cmd/discosrv/querysrv.go | 39 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 51 insertions(+), 2 deletions(-) diff --git a/cmd/discosrv/main.go b/cmd/discosrv/main.go index 817c716f6..ae3d37192 100644 --- a/cmd/discosrv/main.go +++ b/cmd/discosrv/main.go @@ -14,6 +14,12 @@ import ( "github.com/thejerf/suture" ) +const ( + minNegCache = 60 // seconds + maxNegCache = 3600 // seconds + maxDeviceAge = 7 * 86400 // one week, in seconds +) + var ( lruSize = 10240 limitAvg = 5 diff --git a/cmd/discosrv/psql.go b/cmd/discosrv/psql.go index 08e0aec53..82c1b3953 100644 --- a/cmd/discosrv/psql.go +++ b/cmd/discosrv/psql.go @@ -4,6 +4,7 @@ package main import ( "database/sql" + "fmt" _ "github.com/lib/pq" ) @@ -100,7 +101,7 @@ func postgresCompile(db *sql.DB) (map[string]*sql.Stmt, error) { stmts := map[string]string{ "cleanAddress": "DELETE FROM Addresses WHERE Seen < now() - '2 hour'::INTERVAL", "cleanRelay": "DELETE FROM Relays WHERE Seen < now() - '2 hour'::INTERVAL", - "cleanDevice": "DELETE FROM Devices WHERE Seen < now() - '24 hour'::INTERVAL", + "cleanDevice": fmt.Sprintf("DELETE FROM Devices WHERE Seen < now() - '%d hour'::INTERVAL", maxDeviceAge/3600), "countAddress": "SELECT count(*) FROM Addresses", "countDevice": "SELECT count(*) FROM Devices", "countRelay": "SELECT count(*) FROM Relays", @@ -109,6 +110,7 @@ func postgresCompile(db *sql.DB) (map[string]*sql.Stmt, error) { "insertDevice": "INSERT INTO Devices (DeviceID, Seen) VALUES ($1, now())", "selectAddress": "SELECT Address FROM Addresses WHERE DeviceID=$1 AND Seen > now() - '1 hour'::INTERVAL ORDER BY random() LIMIT 16", "selectRelay": "SELECT Address, Latency FROM Relays WHERE DeviceID=$1 AND Seen > now() - '1 hour'::INTERVAL ORDER BY random() LIMIT 16", + "selectDevice": "SELECT Seen FROM Devices WHERE DeviceID=$1", "updateAddress": "UPDATE Addresses SET Seen=now() WHERE DeviceID=$1 AND Address=$2", "updateDevice": "UPDATE Devices SET Seen=now() WHERE DeviceID=$1", "deleteRelay": "DELETE FROM Relays WHERE DeviceID=$1", diff --git a/cmd/discosrv/ql.go b/cmd/discosrv/ql.go index 3a0559df0..2971bf3b1 100644 --- a/cmd/discosrv/ql.go +++ b/cmd/discosrv/ql.go @@ -4,6 +4,7 @@ package main import ( "database/sql" + "fmt" "log" "github.com/cznic/ql" @@ -71,7 +72,7 @@ func qlCompile(db *sql.DB) (map[string]*sql.Stmt, error) { stmts := map[string]string{ "cleanAddress": `DELETE FROM Addresses WHERE Seen < now() - duration("2h")`, "cleanRelay": `DELETE FROM Relays WHERE Seen < now() - duration("2h")`, - "cleanDevice": `DELETE FROM Devices WHERE Seen < now() - duration("24h")`, + "cleanDevice": fmt.Sprintf(`DELETE FROM Devices WHERE Seen < now() - duration("%dh")`, maxDeviceAge/3600), "countAddress": "SELECT count(*) FROM Addresses", "countDevice": "SELECT count(*) FROM Devices", "countRelay": "SELECT count(*) FROM Relays", @@ -80,6 +81,7 @@ func qlCompile(db *sql.DB) (map[string]*sql.Stmt, error) { "insertDevice": "INSERT INTO Devices (DeviceID, Seen) VALUES ($1, now())", "selectAddress": `SELECT Address from Addresses WHERE DeviceID==$1 AND Seen > now() - duration("1h") LIMIT 16`, "selectRelay": `SELECT Address, Latency from Relays WHERE DeviceID==$1 AND Seen > now() - duration("1h") LIMIT 16`, + "selectDevice": "SELECT Seen FROM Devices WHERE DeviceID==$1", "updateAddress": "UPDATE Addresses Seen=now() WHERE DeviceID==$1 AND Address==$2", "updateDevice": "UPDATE Devices Seen=now() WHERE DeviceID==$1", "deleteRelay": "DELETE FROM Relays WHERE DeviceID==$1", diff --git a/cmd/discosrv/querysrv.go b/cmd/discosrv/querysrv.go index 7a7c96fb1..eec095659 100644 --- a/cmd/discosrv/querysrv.go +++ b/cmd/discosrv/querysrv.go @@ -12,6 +12,7 @@ import ( "net" "net/http" "net/url" + "strconv" "sync" "time" @@ -30,6 +31,7 @@ type querysrv struct { } type announcement struct { + Seen time.Time Direct []string `json:"direct"` Relays []annRelay `json:"relays"` } @@ -57,6 +59,22 @@ func (s *safeCache) Add(key string, val interface{}) { s.mut.Unlock() } +func negCacheFor(lastSeen time.Time) int { + since := time.Since(lastSeen).Seconds() + if since >= maxDeviceAge { + return maxNegCache + } + if since < 0 { + // That's weird + return minNegCache + } + + // Return a value linearly scaled from minNegCache (at zero seconds ago) + // to maxNegCache (at maxDeviceAge seconds ago). + r := since / maxDeviceAge + return int(minNegCache + r*(maxNegCache-minNegCache)) +} + func (s *querysrv) Serve() { s.limiter = &safeCache{ Cache: lru.New(lruSize), @@ -158,6 +176,18 @@ func (s *querysrv) handleGET(w http.ResponseWriter, req *http.Request) { var ann announcement + ann.Seen, err = s.getDeviceSeen(deviceID) + negCache := strconv.Itoa(negCacheFor(ann.Seen)) + w.Header().Set("Retry-After", negCache) + w.Header().Set("Cache-Control", "public, max-age="+negCache) + + if err != nil { + // The device is not in the database. + globalStats.Query() + http.Error(w, "Not Found", http.StatusNotFound) + return + } + ann.Direct, err = s.getAddresses(deviceID) if err != nil { log.Println("getAddresses:", err) @@ -385,6 +415,15 @@ func (s *querysrv) getAddresses(device protocol.DeviceID) ([]string, error) { return res, nil } +func (s *querysrv) getDeviceSeen(device protocol.DeviceID) (time.Time, error) { + row := s.prep["selectDevice"].QueryRow(device.String()) + var seen time.Time + if err := row.Scan(&seen); err != nil { + return time.Time{}, err + } + return seen, nil +} + func (s *querysrv) getRelays(device protocol.DeviceID) ([]annRelay, error) { rows, err := s.prep["selectRelay"].Query(device.String()) if err != nil {