guix-commits
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[no subject]


From: Mathieu Othacehe
Date: Fri, 11 Sep 2020 09:53:07 -0400 (EDT)

branch: wip-metrics
commit 3e2455c27b38e96c443b2a7fd1b3a614dfae7876
Author: Mathieu Othacehe <othacehe@gnu.org>
AuthorDate: Fri Sep 11 15:52:49 2020 +0200

    Add metrics support.
---
 Makefile.am               |   4 +-
 src/cuirass/base.scm      |   2 +
 src/cuirass/database.scm  |   2 +
 src/cuirass/http.scm      |  11 +++
 src/cuirass/metrics.scm   | 168 ++++++++++++++++++++++++++++++++++++++++++++++
 src/cuirass/templates.scm |  12 +++-
 src/schema.sql            |   8 +++
 src/sql/upgrade-11.sql    |  11 +++
 8 files changed, 216 insertions(+), 2 deletions(-)

diff --git a/Makefile.am b/Makefile.am
index 9c86276..7e902be 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -46,6 +46,7 @@ dist_pkgmodule_DATA =                         \
   src/cuirass/database.scm                     \
   src/cuirass/http.scm                         \
   src/cuirass/logging.scm                      \
+  src/cuirass/metrics.scm                      \
   src/cuirass/send-events.scm                  \
   src/cuirass/ui.scm                           \
   src/cuirass/utils.scm             \
@@ -76,7 +77,8 @@ dist_sql_DATA =                               \
   src/sql/upgrade-7.sql                                \
   src/sql/upgrade-8.sql                                \
   src/sql/upgrade-9.sql                                \
-  src/sql/upgrade-10.sql
+  src/sql/upgrade-10.sql                       \
+  src/sql/upgrade-11.sql
 
 dist_css_DATA =                                        \
   src/static/css/cuirass.css                   \
diff --git a/src/cuirass/base.scm b/src/cuirass/base.scm
index ec1b467..47f1d63 100644
--- a/src/cuirass/base.scm
+++ b/src/cuirass/base.scm
@@ -24,6 +24,7 @@
   #:use-module (fibers)
   #:use-module (cuirass logging)
   #:use-module (cuirass database)
+  #:use-module (cuirass metrics)
   #:use-module (cuirass utils)
   #:use-module ((cuirass config) #:select (%localstatedir))
   #:use-module (gnu packages)
@@ -835,6 +836,7 @@ by PRODUCT-SPECS."
                (with-store store
                  (let ((jobs (evaluate store spec eval-id checkouts)))
                    (db-set-evaluation-time eval-id)
+                   (db-update-metric 'eval-duration eval-id)
                    (log-message "building ~a jobs for '~a'"
                                 (length jobs) name)
                    (build-packages store jobs eval-id))))))
diff --git a/src/cuirass/database.scm b/src/cuirass/database.scm
index c1941a1..e554ec4 100644
--- a/src/cuirass/database.scm
+++ b/src/cuirass/database.scm
@@ -45,6 +45,8 @@
             db-get-specification
             db-get-specifications
             evaluation-status
+            last-insert-rowid
+            expect-one-row
             db-add-evaluation
             db-abort-pending-evaluations
             db-set-evaluation-status
diff --git a/src/cuirass/http.scm b/src/cuirass/http.scm
index 98696a6..959949b 100644
--- a/src/cuirass/http.scm
+++ b/src/cuirass/http.scm
@@ -25,6 +25,7 @@
   #:use-module (cuirass config)
   #:use-module (cuirass database)
   #:use-module ((cuirass base) #:select (evaluation-log-file))
+  #:use-module (cuirass metrics)
   #:use-module (cuirass utils)
   #:use-module (cuirass logging)
   #:use-module (srfi srfi-1)
@@ -604,6 +605,16 @@ Hydra format."
               (respond-json-with-error 500 "No build found.")))
            (respond-json-with-error 500 "Query parameter not provided."))))
 
+    (('GET "metrics")
+     (respond-html
+      (html-page
+       "Global metrics"
+       (let ((builds-per-day
+              (db-get-metrics-with-id 'builds-last-24-hours)))
+         (global-metrics-content
+          #:builds-per-day builds-per-day))
+       '())))
+
     (('GET "status")
      (respond-html
       (html-page
diff --git a/src/cuirass/metrics.scm b/src/cuirass/metrics.scm
new file mode 100644
index 0000000..d25b55f
--- /dev/null
+++ b/src/cuirass/metrics.scm
@@ -0,0 +1,168 @@
+;;; metrics.scm -- Compute and store metrics.
+;;; This file is part of Cuirass.
+;;;
+;;; Cuirass is free software: you can redistribute it and/or modify
+;;; it under the terms of the GNU General Public License as published by
+;;; the Free Software Foundation, either version 3 of the License, or
+;;; (at your option) any later version.
+;;;
+;;; Cuirass is distributed in the hope that it will be useful,
+;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;;; GNU General Public License for more details.
+;;;
+;;; You should have received a copy of the GNU General Public License
+;;; along with Cuirass.  If not, see <http://www.gnu.org/licenses/>.
+
+(define-module (cuirass metrics)
+  #:use-module (cuirass database)
+  #:use-module (guix records)
+  #:use-module (srfi srfi-1)
+  #:use-module (srfi srfi-19)
+  #:use-module (srfi srfi-26)
+  #:use-module (ice-9 match)
+  #:export (metric
+            metric?
+            metric-id
+            metric-proc
+
+            %metrics
+            metric->type
+            compute-metric
+
+            db-get-metric
+            db-get-metrics-with-id
+            db-update-metric))
+
+
+;;;
+;;; Metric record.
+;;;
+
+(define-record-type* <metric> metric make-metric
+  metric?
+  (id              metric-id)
+  (compute-proc    metric-compute-proc)
+  (field-proc      metric-field-proc
+                   (default #f)))
+
+
+;;;
+;;; Database procedures.
+;;;
+
+(define* (db-average-eval-duration-per-spec spec #:key limit)
+  "Return the evaluation duration of EVAL."
+  (with-db-worker-thread db
+    (let ((rows (sqlite-exec db "SELECT AVG(evaltime - timestamp)
+FROM Evaluations WHERE specification = " spec
+" AND evaltime != 0 LIMIT " (or limit -1))))
+      (and=> (expect-one-row rows) (cut vector-ref <> 0)))))
+
+(define (db-builds-last-24-hours _)
+  "Return the builds count last 24 hours."
+  (with-db-worker-thread db
+    (let ((rows (sqlite-exec db "SELECT COUNT(*) from Builds
+WHERE date(stoptime, 'unixepoch') > date('now', '-1 day')")))
+      (and=> (expect-one-row rows) (cut vector-ref <> 0)))))
+
+(define (db-current-day-timestamp)
+  "Return the timestamp of the current day."
+  (with-db-worker-thread db
+    (let ((rows (sqlite-exec db "SELECT strftime('%s', date('now'))")))
+      (and=> (expect-one-row rows) (cut vector-ref <> 0)))))
+
+
+;;;
+;;; Definitions.
+;;;
+
+(define %metrics
+  (list
+   ;; Average evaluation duration per specification.
+   (metric
+    (id 'average-eval-duration-per-spec)
+    (compute-proc db-average-eval-duration-per-spec))
+   (metric
+    (id 'average-10-last-eval-duration-per-spec)
+    (compute-proc
+     (cut db-average-eval-duration-per-spec <> #:limit 10)))
+   (metric
+    (id 'average-100-last-eval-duration-per-spec)
+    (compute-proc
+     (cut db-average-eval-duration-per-spec <> #:limit 100)))
+
+   ;; Builds count over last 24 hours.
+   (metric
+    (id 'builds-last-24-hours)
+    (compute-proc db-builds-last-24-hours)
+    (field-proc db-current-day-timestamp))))
+
+(define (metric->type metric)
+  "Return the index of the given METRIC in %metrics list.  This index is used
+to identify the metric type in database."
+  (list-index
+   (lambda (cur-metric)
+     (eq? (metric-id cur-metric) (metric-id metric)))
+   %metrics))
+
+(define (find-metric id)
+  "Find the metric with the given ID."
+  (find (lambda (metric)
+          (eq? (metric-id metric) id))
+        %metrics))
+
+(define* (compute-metric metric field)
+  "Compute the given METRIC on FIELD and return the associated value."
+  (let ((compute (metric-compute-proc metric)))
+    (compute field)))
+
+(define* (db-get-metric id field)
+  "Return the metric with the given ID and FIELD."
+  (let* ((metric (find-metric id))
+         (type (metric->type metric)))
+    (with-db-worker-thread db
+    (let ((rows (sqlite-exec db "SELECT value from Metrics
+WHERE type = " type " AND field = " field)))
+      (and=> (expect-one-row rows) (cut vector-ref <> 0))))))
+
+(define* (db-get-metrics-with-id id #:key limit)
+  "Return the metrics with the given ID.  If LIMIT is set, the resulting list
+if restricted to LIMIT records."
+  (let* ((metric (find-metric id))
+         (type (metric->type metric))
+         (limit (or limit -1)))
+    (with-db-worker-thread db
+    (let loop ((rows (sqlite-exec db "SELECT field, value from Metrics
+WHERE type = " type " LIMIT " limit))
+               (metrics '()))
+      (match rows
+        (() (reverse metrics))
+        ((#(field value) . rest)
+         (loop rest
+               `((,field . ,value)
+                 ,@metrics))))))))
+
+(define* (db-update-metric id #:optional field)
+  "Compute and update the value of the metric ID in database.
+
+  FIELD is optional and can be the id of a database object such as an
+evaluation or a specification that the METRIC applies to.  If FIELD is not
+passed then the METRIC may provide a FIELD-PROC to compute it.  It is useful
+for periodical metrics for instance."
+  (define now
+    (time-second (current-time time-utc)))
+
+  (let* ((metric (find-metric id))
+         (field-proc (metric-field-proc metric))
+         (field (or field (field-proc)))
+         (value (compute-metric metric field)))
+    (with-db-worker-thread db
+      (sqlite-exec db "\
+INSERT OR REPLACE INTO Metrics (field, type, value,
+timestamp) VALUES ("
+                   field ", "
+                   (metric->type metric) ", "
+                   value ", "
+                   now ");")
+      (last-insert-rowid db))))
diff --git a/src/cuirass/templates.scm b/src/cuirass/templates.scm
index 3128b45..983c82a 100644
--- a/src/cuirass/templates.scm
+++ b/src/cuirass/templates.scm
@@ -40,7 +40,8 @@
             build-search-results-table
             build-details
             evaluation-build-table
-            running-builds-table))
+            running-builds-table
+            global-metrics-content))
 
 (define (navigation-items navigation)
   (match navigation
@@ -134,6 +135,9 @@ system whose names start with " (code "guile-") ":" (br)
                             (div (@ (class "dropdown-menu")
                                     (aria-labelledby "navbarDropdow"))
                                  (a (@ (class "dropdown-item")
+                                       (href "/metrics"))
+                                    "Global metrics")
+                                 (a (@ (class "dropdown-item")
                                        (href "/status"))
                                     "Running builds")))
                         (li (@ (class "nav-item"))
@@ -820,3 +824,9 @@ and BUILD-MAX are global minimal and maximal row 
identifiers."
                         (th (@ (scope "col")) "System")))
              (tbody
               ,(map build-row builds)))))))
+
+(define* (global-metrics-content
+          #:key builds-per-day)
+  `((div
+     (p (@ (class "lead")) "Global metrics")
+     (h6 "Build speed"))))
diff --git a/src/schema.sql b/src/schema.sql
index 335a6d4..ed5893e 100644
--- a/src/schema.sql
+++ b/src/schema.sql
@@ -70,6 +70,14 @@ CREATE TABLE Builds (
   FOREIGN KEY (evaluation) REFERENCES Evaluations (id)
 );
 
+CREATE TABLE Metrics (
+  field         INTEGER NOT NULL,
+  type          INTEGER NOT NULL,
+  value         DOUBLE PRECISION NOT NULL,
+  timestamp     INTEGER NOT NULL,
+  PRIMARY KEY (field, type)
+);
+
 CREATE TABLE BuildProducts (
   build         INTEGER NOT NULL,
   type          TEXT NOT NULL,
diff --git a/src/sql/upgrade-11.sql b/src/sql/upgrade-11.sql
new file mode 100644
index 0000000..22f2dac
--- /dev/null
+++ b/src/sql/upgrade-11.sql
@@ -0,0 +1,11 @@
+BEGIN TRANSACTION;
+
+CREATE TABLE Metrics (
+  field         INTEGER NOT NULL,
+  type          INTEGER NOT NULL,
+  value         DOUBLE PRECISION NOT NULL,
+  timestamp     INTEGER NOT NULL,
+  PRIMARY KEY (field, type)
+);
+
+COMMIT;



reply via email to

[Prev in Thread] Current Thread [Next in Thread]