Why? Sometimes when you implement Go services you have to integrate with third party command line tools. You can do it by simply using exec.Command but the problem with this approach is that your service has to wait while a new process is being loaded into the memory and started. Sometimes this can drastically increase the latency of your service. ExecPool works similarly to FastCGI but it can wrap any regular process. It spins up a given number of processes in advance and when it's time to handle a request from the user your service just attaches stdin to an existing process from the pool. Basically the execpool helps you trade memory for latency.
package main
import (
"fmt"
"io/ioutil"
"log"
"os/exec"
"strings"
"github.com/hexdigest/execpool"
)
func main() {
cmd := exec.Command("grep", "none")
//spin up 100 processes of grep
pool, err := execpool.New(cmd, 100)
rc := pool.Exec(strings.NewReader("this makes sense\nthis is nonesense"))
b, err := ioutil.ReadAll(rc)
if err != nil {
log.Fatalf("failed to read from stdout: %v", err)
}
// this is nonesense
fmt.Println(string(b))
}
This benchmark compares "standard" approach with exec.Command and execpool.Pool by running grep 100 times. For heavier processes you can expect a bigger difference.
make benchmark
goos: darwin
goarch: amd64
pkg: github.com/hexdigest/execpool
cpu: Intel(R) Core(TM) i7-6820HQ CPU @ 2.70GHz
BenchmarkNew-8 100 941753 ns/op
BenchmarkCmd-8 100 2386990 ns/op