In my previous post, I was loading the fixtures data on every request. While this allow to always use the most up to date data, this is has no value in our context as the data does not change.
How can we make sure we always reuse the same fixtures data for all requests? Let’s go over an implementation of the singleton pattern
in Golang.
First, let modify our action
to only use the data to generate our users. Then we need to get that data. We will use another package to isolate the different concerns:
import (
...
data "randomdata/data"
)
func UsersHandler(c buffalo.Context) error {
...
// Load required locales values
data.Load(c)
locales := data.Locales()
firstnames, lastnames := data.Users()
...
}
Each request will get the data from this package. But now we need to make sure we reuse the same data. In that new package, we will implement once.Do
:
package data
import (
"github.com/gobuffalo/buffalo"
"github.com/gobuffalo/pop"
"randomdata/models"
"github.com/pkg/errors"
"sync"
"fmt"
)
var once sync.Once
type firstnamesType map[string][]models.Firstname
type lastnamesType map[string][]models.Lastname
type localesType []models.Locale
var (
firstnames firstnamesType
lastnames lastnamesType
locales localesType
)
func Load(c buffalo.Context) error {
var err error
once.Do(func() {
firstnames = make(map[string][]models.Firstname)
lastnames = make(map[string][]models.Lastname)
tx, ok := c.Value("tx").(*pop.Connection) // you get your connection here
if !ok {
err = errors.WithStack(errors.New("no transaction found"))
}
fmt.Println("=====")
fmt.Println("Querying locales")
fmt.Println("=====")
tx.All(&locales)
fmt.Println("=====")
fmt.Println("Querying users")
fmt.Println("=====")
// Load required locales values
f := []models.Firstname{}
l := []models.Lastname{}
for _, locale := range locales {
q := tx.Where("locale = ?", locale.Code)
if len(c.Param("gender")) > 0 {
q.Where("gender = ?", c.Param("gender"))
}
q.All(&f)
firstnames[locale.Code] = f
q = tx.Where("locale = ?", locale.Code)
q.All(&l)
lastnames[locale.Code] = l
}
})
return err
}
func Users() (firstnamesType, lastnamesType) {
return firstnames, lastnames
}
func Locales() (localesType) {
return locales
}
Everything in the anonymous func wrapped by once.Do
will only be executed one time - on the first request. So even if we call Load
thousand times, it will be only run once.
The results are impressive. Now that all my content is only loaded on the first request.
If we retest with ab
, we now have the following results:
Requests per second: 6628.71 [#/sec] (mean)
Time per request: 1.509 [ms] (mean)
6628 requests per second is definitely better than the 50 rps we got while querying the db on each run.