Cache jobs in pool manager

This change caches jobs meant for an entity in the pool manager. This
allows us to avoid querying the db as much and allows us to better determine
when we should scale down.

Signed-off-by: Gabriel Adrian Samfira <gsamfira@cloudbasesolutions.com>
This commit is contained in:
Gabriel Adrian Samfira 2025-10-04 19:20:14 +00:00 committed by Gabriel
parent 0093393bc3
commit a36d01afd5
13 changed files with 331 additions and 73 deletions

View file

@ -20,6 +20,7 @@ import (
"errors"
"fmt"
"log/slog"
"math"
"github.com/google/uuid"
"gorm.io/datatypes"
@ -31,54 +32,74 @@ import (
"github.com/cloudbase/garm/params"
)
func (s *sqlDatabase) CreateInstance(_ context.Context, poolID string, param params.CreateInstanceParams) (instance params.Instance, err error) {
pool, err := s.getPoolByID(s.conn, poolID)
if err != nil {
return params.Instance{}, fmt.Errorf("error fetching pool: %w", err)
}
func (s *sqlDatabase) CreateInstance(ctx context.Context, poolID string, param params.CreateInstanceParams) (instance params.Instance, err error) {
defer func() {
if err == nil {
s.sendNotify(common.InstanceEntityType, common.CreateOperation, instance)
}
}()
var labels datatypes.JSON
if len(param.AditionalLabels) > 0 {
labels, err = json.Marshal(param.AditionalLabels)
err = s.conn.Transaction(func(tx *gorm.DB) error {
pool, err := s.getPoolByID(tx, poolID)
if err != nil {
return params.Instance{}, fmt.Errorf("error marshalling labels: %w", err)
return fmt.Errorf("error fetching pool: %w", err)
}
}
var secret []byte
if len(param.JitConfiguration) > 0 {
secret, err = s.marshalAndSeal(param.JitConfiguration)
if err != nil {
return params.Instance{}, fmt.Errorf("error marshalling jit config: %w", err)
var cnt int64
q := s.conn.Model(&Instance{}).Where("pool_id = ?", pool.ID).Count(&cnt)
if q.Error != nil {
return fmt.Errorf("error fetching instance count: %w", q.Error)
}
var maxRunners int64
if pool.MaxRunners > math.MaxInt64 {
maxRunners = math.MaxInt64
} else {
maxRunners = int64(pool.MaxRunners)
}
if cnt >= maxRunners {
return runnerErrors.NewConflictError("max runners reached for pool %s", pool.ID)
}
var labels datatypes.JSON
if len(param.AditionalLabels) > 0 {
labels, err = json.Marshal(param.AditionalLabels)
if err != nil {
return fmt.Errorf("error marshalling labels: %w", err)
}
}
var secret []byte
if len(param.JitConfiguration) > 0 {
secret, err = s.marshalAndSeal(param.JitConfiguration)
if err != nil {
return fmt.Errorf("error marshalling jit config: %w", err)
}
}
newInstance := Instance{
Pool: pool,
Name: param.Name,
Status: param.Status,
RunnerStatus: param.RunnerStatus,
OSType: param.OSType,
OSArch: param.OSArch,
CallbackURL: param.CallbackURL,
MetadataURL: param.MetadataURL,
GitHubRunnerGroup: param.GitHubRunnerGroup,
JitConfiguration: secret,
AditionalLabels: labels,
AgentID: param.AgentID,
}
q = tx.Create(&newInstance)
if q.Error != nil {
return fmt.Errorf("error creating instance: %w", q.Error)
}
return nil
})
if err != nil {
return params.Instance{}, fmt.Errorf("error creating instance: %w", err)
}
newInstance := Instance{
Pool: pool,
Name: param.Name,
Status: param.Status,
RunnerStatus: param.RunnerStatus,
OSType: param.OSType,
OSArch: param.OSArch,
CallbackURL: param.CallbackURL,
MetadataURL: param.MetadataURL,
GitHubRunnerGroup: param.GitHubRunnerGroup,
JitConfiguration: secret,
AditionalLabels: labels,
AgentID: param.AgentID,
}
q := s.conn.Create(&newInstance)
if q.Error != nil {
return params.Instance{}, fmt.Errorf("error creating instance: %w", q.Error)
}
return s.sqlToParamsInstance(newInstance)
return s.GetInstance(ctx, param.Name)
}
func (s *sqlDatabase) getPoolInstanceByName(poolID string, instanceName string) (Instance, error) {