Using GoLang Concurrency & Asynchronous Processing with CI/CD
@sudhindraRao
Sudhindra Rao
(Uses Spaces not Tabs)
Follow me: @sudhindraRao
sudhindraRao.com
https://jfrog.co/GoLangNYCJune
Concurrency and Memento the Movie
(Although if you google about it, there are a few different takes on how the movie ended - very much like concurrent programs)
Twitter: @sudhindraRao
sudhindraRao.com
THANK YOU!
References
Success!
Build time reduced to from 6 hours -> 2 hours (400% improvement)
Error traceability for each binary
Fast failures means fast feedback
MAIN CHARACTERS
A Kubernetes platform
BOSH
Continuous Delivery system
THE PLOT
We are Running!
Look - Find - Package
Precompiled
List of Dependencies
Target OS
Cloud Storage Bucket
BOSH cache
Compile
Combine to form installer tile
Identify concurrency - Who is John G.?
THE TOOLS
Concurrency in Go - A Primer
Goroutines
Channels
Do not communicate by sharing memory; instead, share memory by communicating.
Pipelines
What about WaitGroups and Synchronization
Don’t forget to call wg.Done()
Don’t trust, your weakness with threads
Synchronization with scattered booleans is a bad idea
Did you defer cleanup
Don’t forget the basics
THE NEW CODE
The Algorithm
Building the Pipeline
numberOfResultsExpected := len(tileDependencies.Releases)
releasesToDownload := make(chan CandidateRelease, numberOfResultsExpected)
releasesToCompile := make(chan CandidateRelease, numberOfResultsExpected)
releasesToPublish := make(chan CandidateRelease, numberOfResultsExpected)
results := make(chan CandidateRelease, numberOfResultsExpected)
for i := 0; i < a.maxConcurrentDownloaders; i++ {
go a.downloadWorker.Create(releasesToDownload, releasesToCompile, results)
}
for i := 0; i < a.maxConcurrentCompilers; i++ {
go a.compileWorker.Create(releasesToCompile, releasesToPublish, results)
}
for i := 0; i < a.maxConcurrentPublishers; i++ {
go a.publishWorker.Create(releasesToPublish, results)
}
Encapsulation
Release Candidate
Objects through the pipeline
type Result struct {
PathToDownloadedSourceTarball string
PathsToCompiledTarball map[string]string
PathToCompiledTarballsToPublish []string
Err error
}
type CandidateRelease struct {
Release tile.Release
Result Result
}
type GCS struct {
serviceKeyFilePath string
compiledReleaseBucket string
}
type StorageClient struct {
pathToCLI string
authToken string
}
type BoshClient struct {
pathToBoshCLI string
boshHost string
boshUsername string
boshPassword string
boshCACert string
boshAllProxy string
}
and a few more objects...
Termination Condition
for candidate := range results {
if candidate.Result.Err != nil {
allErrors = append(allErrors, candidate.Result.Err.Error())
}
numberOfResultProcessed++
if numberOfResultProcessed == numberOfResultsExpected {
break
}
}
close(releasesToCompile)
close(releasesToPublish)
close(results)
THE OLD CODE
Concurrency the old way
func (app App) processRelease(...) error {
app.downloader.Download(...)
...
for i := 0; i < workers; i++ {
go func(deps tile.Dependency, args ProgArgs) {
for {
select {
case release := <-releases:
err := app.processRelease(release, deps, args)
result := Result{
release: release,
err: err,
}
results <- result
case <-stop:
return
}
}
}(deps, args)
}
func (app App) Download(...) error {
app.downloader.Compile(...)
...
func (app App) Compile(...) error {
app.downloader.Publish(...)
...
func (app App) Publish(...) error {
app.downloader.CalculateSha(...)
...
Termination Condition
stopWorkers := func() {
for i := 0; i < workers; i++ {
stop <- true
}
close(stop)
}
for result := range results {
if result.err != nil {
errorResults = append(errorResults, result)
}
remaining--
if remaining == 0 {
stopWorkers()
close(results)
close(releases)
}
}
Old code - concurrency issues
We found our ‘John G.’
What we fixed
MEASURING SUCCESS
1 OS
< 10 binaries
~ 40 environments
4 tiles
per
day
2+ OSes
> 20 binaries
> 100 environments
Predictable build 4 times a day
1 Tile per day
DON’T�BE AFRAID
Refactor
Go back to basics
Prioritize Tech Debt
Optimize at the right time
Ask for Support (Special mention - Kevin Kelani and Adrian Zankich)
Any Questions?
https://jfrog.co/GoLangNYCJune