diff --git a/src/FTL.h b/src/FTL.h index 5a76e979ef..caf6aa45f9 100644 --- a/src/FTL.h +++ b/src/FTL.h @@ -143,6 +143,10 @@ // Default: 2592000 (once per month) #define DATABASE_MACVENDOR_INTERVAL 2592000 +// Over how many seonds should the query-per-second (QPS) value be averaged? +// Default: 30 (seconds) +#define QPS_AVGLEN 30 + // Use out own syscalls handling functions that will detect possible errors // and report accordingly in the log. This will make debugging FTL crash // caused by insufficient memory or by code bugs (not properly dealing diff --git a/src/api/docs/content/specs/info.yaml b/src/api/docs/content/specs/info.yaml index dadaee27d4..d76dbd6a00 100644 --- a/src/api/docs/content/specs/info.yaml +++ b/src/api/docs/content/specs/info.yaml @@ -721,6 +721,10 @@ components: type: integer description: Currently used privacy level example: 0 + query_frequency: + type: number + description: Average number of queries per second + example: 1.1 clients: type: object properties: diff --git a/src/api/docs/content/specs/stats.yaml b/src/api/docs/content/specs/stats.yaml index 1ee8322808..3af53b1982 100644 --- a/src/api/docs/content/specs/stats.yaml +++ b/src/api/docs/content/specs/stats.yaml @@ -338,6 +338,10 @@ components: type: integer description: Number of queries replied to from cache or local configuration example: 9765 + frequency: + type: number + description: Average number of queries per second + example: 1.1 types: type: object description: Number of individual queries diff --git a/src/api/info.c b/src/api/info.c index 7daa83b830..ce7a7a8dd5 100644 --- a/src/api/info.c +++ b/src/api/info.c @@ -548,6 +548,7 @@ static int get_ftl_obj(struct ftl_conn *api, cJSON *ftl) const int db_denied = counters->database.domains.denied; const int clients_total = counters->clients; const int privacylevel = config.misc.privacylevel.v.privacy_level; + const double qps = get_qps(); // unique_clients: count only clients that have been active within the most recent 24 hours int activeclients = 0; @@ -575,6 +576,7 @@ static int get_ftl_obj(struct ftl_conn *api, cJSON *ftl) JSON_ADD_ITEM_TO_OBJECT(ftl, "database", database); JSON_ADD_NUMBER_TO_OBJECT(ftl, "privacy_level", privacylevel); + JSON_ADD_NUMBER_TO_OBJECT(ftl, "query_frequency", qps); cJSON *clients = JSON_NEW_OBJECT(); JSON_ADD_NUMBER_TO_OBJECT(clients, "total",clients_total); diff --git a/src/api/stats.c b/src/api/stats.c index ae98f9df16..ccaffc61ec 100644 --- a/src/api/stats.c +++ b/src/api/stats.c @@ -143,6 +143,8 @@ int api_stats_summary(struct ftl_conn *api) JSON_ADD_NUMBER_TO_OBJECT(queries, "forwarded", forwarded); JSON_ADD_NUMBER_TO_OBJECT(queries, "cached", cached); + JSON_ADD_NUMBER_TO_OBJECT(queries, "frequency", get_qps()); + cJSON *types = JSON_NEW_OBJECT(); int ret = get_query_types_obj(api, types); if(ret != 0) diff --git a/src/dnsmasq_interface.c b/src/dnsmasq_interface.c index b4b99253d2..d9be227d26 100644 --- a/src/dnsmasq_interface.c +++ b/src/dnsmasq_interface.c @@ -664,6 +664,9 @@ bool _FTL_new_query(const unsigned int flags, const char *name, return false; } + // Update rolling window of queries per second + update_qps(querytimestamp); + // Interface name is only available for regular queries, not for // automatically generated DNSSEC queries const char *interface = internal_query ? "-" : next_iface.name; diff --git a/src/shmem.c b/src/shmem.c index 3063e1dec6..3f1c29c1df 100644 --- a/src/shmem.c +++ b/src/shmem.c @@ -1206,3 +1206,46 @@ int __attribute__((pure)) is_shm_fd(const int fd) // Not found return 0; } + +// Update queries per second (qps) value +// This is done in shared memory to allow for both UDP and TCP workers to +// contribute. +void update_qps(const double timestamp) +{ + // Get the timeslot for the current timestamp + const unsigned int slot = (unsigned int)timestamp % QPS_AVGLEN; + + // Check if the timestamp is in the same slot as the last one + if(shmSettings->qps.last != slot) + { + // Reset all the slots in between + // This is relevant if less than one query per second is + // received and the intermediate slots are not updated + for(unsigned int i = (shmSettings->qps.last + 1) % QPS_AVGLEN; i != slot; i = (i + 1) % QPS_AVGLEN) + shmSettings->qps.buf[i] = 0; + + // Reset the current slot + shmSettings->qps.buf[slot] = 0; + + // Update the last slot index + shmSettings->qps.last = slot; + } + + // Add the query + shmSettings->qps.buf[slot]++; +} + +// Compute queries per second (qps) value +double __attribute__((pure)) get_qps(void) +{ + // Compute the arithmetic mean of all slots + // 1 N + // QPS = --- Σ buf[i] + // N i=0 + // + double qps = 0.0; + for(unsigned int i = 0; i < QPS_AVGLEN; i++) + qps += shmSettings->qps.buf[i]; + + return qps / QPS_AVGLEN; +} diff --git a/src/shmem.h b/src/shmem.h index ad35ebee41..2492f986ac 100644 --- a/src/shmem.h +++ b/src/shmem.h @@ -31,6 +31,10 @@ typedef struct { pid_t pid; unsigned int global_shm_counter; unsigned int next_str_pos; + struct { + unsigned int last; + unsigned int buf[QPS_AVGLEN]; + } qps; } ShmSettings; typedef struct { @@ -145,4 +149,7 @@ void set_per_client_regex(const int clientID, const int regexID, const bool valu // Used in dnsmasq/utils.c int is_shm_fd(const int fd); +void update_qps(const double timestamp); +double get_qps(void) __attribute__((pure)); + #endif //SHARED_MEMORY_SERVER_H