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>

--- prometheus-sql-exporter-0.2.0.ds.orig/config.go
+++ prometheus-sql-exporter-0.2.0.ds/config.go
@@ -45,6 +45,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
+++ prometheus-sql-exporter-0.2.0.ds/handler.go
@@ -0,0 +1,47 @@
+package main
+
+import (
+	"net/http"
+	"os"
+
+	"github.com/go-kit/kit/log"
+	"github.com/prometheus/client_golang/prometheus"
+	"github.com/prometheus/client_golang/prometheus/promhttp"
+)
+
+// handlerFunc can be used as handler for http.HandleFunc()
+// all synchronus jobs will be triggered and waited for,
+// than the promhttp handler is executed
+func (ex *Exporter) handlerFunc(w http.ResponseWriter, req *http.Request) {
+	//	logger := log.NewNopLogger()
+	logger := log.NewJSONLogger(os.Stdout)
+	logger = log.With(logger, "caller", "handlerFunc")
+
+	// pull all triggers on jobs with interval 0
+	logger.Log("level", "debug", "msg", "Start all sync jobs")
+	for _, job := range ex.jobs {
+		// if job is nil or is async then continue to next job
+		if job == nil || job.Interval > 0 {
+			logger.Log("level", "debug", "msg", "Send NO trigger to job", job.Name)
+			continue
+		}
+		logger.Log("level", "debug", "msg", "Send trigger to job", job.Name)
+		job.Trigger <- true
+	}
+
+	// wait for all sync jobs to finish
+	for _, job := range ex.jobs {
+		if job == nil || job.Interval > 0 {
+			continue
+		}
+		logger.Log("level", "debug", "msg", "Wait for job", job.Name)
+		<-job.Done
+	}
+	logger.Log("level", "debug", "msg", "All waiting done")
+
+	// get the prometheus handler
+	handler := promhttp.HandlerFor(prometheus.DefaultGatherer, promhttp.HandlerOpts{})
+
+	// execute the ServeHTTP function
+	handler.ServeHTTP(w, req)
+}
--- prometheus-sql-exporter-0.2.0.ds.orig/job.go
+++ prometheus-sql-exporter-0.2.0.ds/job.go
@@ -72,6 +72,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 conenctions for job", "job", j.Name)
@@ -110,13 +122,29 @@ 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 {
+			j.log.Log("level", "debug", "msg", "Wait for trigger")
+			// wait for trigger
+			<-j.Trigger
+
+			if err := j.runOnce(); err != nil {
+				j.log.Log("level", "error", "msg", "Failed to run", "err", err)
+			}
+
+			// send to done chanel
+			j.Done <- true
+			j.log.Log("level", "debug", "msg", "Job finished")
+		} 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)
 	}
 }
 
--- prometheus-sql-exporter-0.2.0.ds.orig/main.go
+++ prometheus-sql-exporter-0.2.0.ds/main.go
@@ -10,7 +10,6 @@ import (
 	"github.com/go-kit/kit/log"
 	"github.com/go-kit/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>
