Description: Add synchronous jobs
 We needed a possibility to execute some jobs / queries synchronous every time
 /metrics is requested. So I added the infrastructure to execute jobs with an
 interval <=0 synchronous when the http-handler is called.
 .
 interval: '0s' # an interval <= 0 will make the queries synchronous
Author: Alexander Sosna <alexander.sosna@credativ.de>

--- a/config.go
+++ b/config.go
@@ -81,6 +81,8 @@ type File struct {
 type Job struct {
 	log         log.Logger
 	conns       []*connection
+	Trigger     chan bool     // used to trigger execution
+	Done        chan bool     // used to tell state
 	Name        string        `yaml:"name"`      // name of this job
 	KeepAlive   bool          `yaml:"keepalive"` // keep connection between runs?
 	Interval    time.Duration `yaml:"interval"`  // interval at which this job is run
--- /dev/null
+++ b/handler.go
@@ -0,0 +1,35 @@
+package main
+
+import (
+	"net/http"
+	"github.com/prometheus/client_golang/prometheus"
+	"github.com/prometheus/client_golang/prometheus/promhttp"
+)
+
+// handlerFunc can be used as handler for http.HandleFunc()
+// all synchronous jobs will be triggered and waited for,
+// then the promhttp handler is executed
+func (ex *Exporter) handlerFunc(w http.ResponseWriter, req *http.Request) {
+	// pull all triggers on jobs with interval 0
+	for _, job := range ex.jobs {
+		// if job is nil or is async then continue to next job
+		if job == nil || job.Interval > 0 {
+			continue
+		}
+		job.Trigger <- true
+	}
+
+	// wait for all sync jobs to finish
+	for _, job := range ex.jobs {
+		if job == nil || job.Interval > 0 {
+			continue
+		}
+		<-job.Done
+	}
+
+	// get the prometheus handler
+	handler := promhttp.HandlerFor(prometheus.DefaultGatherer, promhttp.HandlerOpts{})
+
+	// execute the ServeHTTP function
+	handler.ServeHTTP(w, req)
+}
--- a/job.go
+++ b/job.go
@@ -78,6 +78,18 @@ func (j *Job) Run() {
 	if j.log == nil {
 		j.log = log.NewNopLogger()
 	}
+
+	// if the interval is not set > 0, create needed channels
+	if j.Interval <= 0 {
+		if j.Trigger == nil {
+			j.Trigger = make(chan bool)
+		}
+
+		if j.Done == nil {
+			j.Done = make(chan bool)
+		}
+	}
+
 	// if there are no connection URLs for this job it can't be run
 	if j.Connections == nil {
 		level.Error(j.log).Log("msg", "No connections for job", "job", j.Name)
@@ -176,13 +188,27 @@ func (j *Job) Run() {
 	// enter the run loop
 	// tries to run each query on each connection at approx the interval
 	for {
-		bo := backoff.NewExponentialBackOff()
-		bo.MaxElapsedTime = j.Interval
-		if err := backoff.Retry(j.runOnce, bo); err != nil {
-			level.Error(j.log).Log("msg", "Failed to run", "err", err)
+		// if the interval is 0 or lower, wait to be triggered
+		if j.Interval <= 0 {
+			// wait for trigger
+			<-j.Trigger
+
+			if err := j.runOnce(); err != nil {
+				level.Error(j.log).Log("msg", "Failed to run", "err", err)
+			}
+
+			// send to done chanel
+			j.Done <- true
+		} else {
+			// interval is grater than 0 so procide with async operation
+			bo := backoff.NewExponentialBackOff()
+			bo.MaxElapsedTime = j.Interval
+			if err := backoff.Retry(j.runOnce, bo); err != nil {
+				level.Error(j.log).Log("msg", "Failed to run", "err", err)
+			}
+			level.Debug(j.log).Log("msg", "Sleeping until next run", "sleep", j.Interval.String())
+			time.Sleep(j.Interval)
 		}
-		level.Debug(j.log).Log("msg", "Sleeping until next run", "sleep", j.Interval.String())
-		time.Sleep(j.Interval)
 	}
 }
 
--- a/main.go
+++ b/main.go
@@ -10,7 +10,6 @@ import (
 	"github.com/go-kit/log"
 	"github.com/go-kit/log/level"
 	"github.com/prometheus/client_golang/prometheus"
-	"github.com/prometheus/client_golang/prometheus/promhttp"
 	"github.com/prometheus/common/version"
 )
 
@@ -62,8 +61,8 @@ func main() {
 	}
 	prometheus.MustRegister(exporter)
 
-	// setup and start webserver
-	http.Handle(*metricsPath, promhttp.Handler())
+	// setup and start webserver with custom function
+	http.HandleFunc(*metricsPath, exporter.handlerFunc)
 	http.HandleFunc("/healthz", func(w http.ResponseWriter, r *http.Request) { http.Error(w, "OK", http.StatusOK) })
 	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
 		w.Write([]byte(`<html>
