feat(cache): organization (#8903)

# Which Problems Are Solved

Organizations are ofter searched for by ID or primary domain. This
results in many redundant queries, resulting in a performance impact.

# How the Problems Are Solved

Cache Organizaion objects by ID and primary domain.

# Additional Changes

- Adjust integration test config to use all types of cache.
- Adjust integration test lifetimes so the pruner has something to do
while the tests run.

# Additional Context

- Closes #8865
- After #8902
This commit is contained in:
Tim Möhlmann
2024-11-21 08:05:03 +02:00
committed by GitHub
parent 041c3d9b9e
commit c165ed07f4
10 changed files with 191 additions and 20 deletions

View File

@@ -16,6 +16,7 @@ const (
PurposeUnspecified Purpose = iota
PurposeAuthzInstance
PurposeMilestones
PurposeOrganization
)
// Cache stores objects with a value of type `V`.

View File

@@ -19,8 +19,9 @@ type CachesConfig struct {
Postgres pg.Config
Redis redis.Config
}
Instance *cache.Config
Milestones *cache.Config
Instance *cache.Config
Milestones *cache.Config
Organization *cache.Config
}
type Connectors struct {

View File

@@ -7,11 +7,11 @@ import (
"strings"
)
const _PurposeName = "unspecifiedauthz_instancemilestones"
const _PurposeName = "unspecifiedauthz_instancemilestonesorganization"
var _PurposeIndex = [...]uint8{0, 11, 25, 35}
var _PurposeIndex = [...]uint8{0, 11, 25, 35, 47}
const _PurposeLowerName = "unspecifiedauthz_instancemilestones"
const _PurposeLowerName = "unspecifiedauthz_instancemilestonesorganization"
func (i Purpose) String() string {
if i < 0 || i >= Purpose(len(_PurposeIndex)-1) {
@@ -27,9 +27,10 @@ func _PurposeNoOp() {
_ = x[PurposeUnspecified-(0)]
_ = x[PurposeAuthzInstance-(1)]
_ = x[PurposeMilestones-(2)]
_ = x[PurposeOrganization-(3)]
}
var _PurposeValues = []Purpose{PurposeUnspecified, PurposeAuthzInstance, PurposeMilestones}
var _PurposeValues = []Purpose{PurposeUnspecified, PurposeAuthzInstance, PurposeMilestones, PurposeOrganization}
var _PurposeNameToValueMap = map[string]Purpose{
_PurposeName[0:11]: PurposeUnspecified,
@@ -38,12 +39,15 @@ var _PurposeNameToValueMap = map[string]Purpose{
_PurposeLowerName[11:25]: PurposeAuthzInstance,
_PurposeName[25:35]: PurposeMilestones,
_PurposeLowerName[25:35]: PurposeMilestones,
_PurposeName[35:47]: PurposeOrganization,
_PurposeLowerName[35:47]: PurposeOrganization,
}
var _PurposeNames = []string{
_PurposeName[0:11],
_PurposeName[11:25],
_PurposeName[25:35],
_PurposeName[35:47],
}
// PurposeString retrieves an enum value from the enum constants string name.

View File

@@ -8,28 +8,30 @@ TLS:
Caches:
Connectors:
Memory:
Enabled: true
Postgres:
Enabled: true
Redis:
Enabled: true
Instance:
Connector: "redis"
MaxAge: 1h
LastUsage: 10m
Connector: "memory"
MaxAge: 5m
LastUsage: 1m
Log:
Level: info
AddSource: true
Formatter:
Format: text
Milestones:
Connector: "postgres"
MaxAge: 1h
LastUsage: 10m
MaxAge: 5m
LastUsage: 1m
Log:
Level: info
Organization:
Connector: "redis"
MaxAge: 5m
LastUsage: 1m
Log:
Level: info
AddSource: true
Formatter:
Format: text
Quotas:
Access:

View File

@@ -12,6 +12,7 @@ import (
type Caches struct {
instance cache.Cache[instanceIndex, string, *authzInstance]
org cache.Cache[orgIndex, string, *Org]
}
func startCaches(background context.Context, connectors connector.Connectors) (_ *Caches, err error) {
@@ -20,7 +21,13 @@ func startCaches(background context.Context, connectors connector.Connectors) (_
if err != nil {
return nil, err
}
caches.org, err = connector.StartCache[orgIndex, string, *Org](background, orgIndexValues(), cache.PurposeOrganization, connectors.Config.Organization, connectors)
if err != nil {
return nil, err
}
caches.registerInstanceInvalidation()
caches.registerOrgInvalidation()
return caches, nil
}

View File

@@ -522,9 +522,9 @@ func (i *authzInstance) Keys(index instanceIndex) []string {
return []string{i.ID}
case instanceIndexByHost:
return i.ExternalDomains
default:
return nil
case instanceIndexUnspecified:
}
return nil
}
func scanAuthzInstance() (*authzInstance, func(row *sql.Row) error) {

View File

@@ -107,10 +107,19 @@ func (q *OrgSearchQueries) toQuery(query sq.SelectBuilder) sq.SelectBuilder {
return query
}
func (q *Queries) OrgByID(ctx context.Context, shouldTriggerBulk bool, id string) (_ *Org, err error) {
func (q *Queries) OrgByID(ctx context.Context, shouldTriggerBulk bool, id string) (org *Org, err error) {
ctx, span := tracing.NewSpan(ctx)
defer func() { span.EndWithError(err) }()
if org, ok := q.caches.org.Get(ctx, orgIndexByID, id); ok {
return org, nil
}
defer func() {
if err == nil && org != nil {
q.caches.org.Set(ctx, org)
}
}()
if !authz.GetInstance(ctx).Features().ShouldUseImprovedPerformance(feature.ImprovedPerformanceTypeOrgByID) {
return q.oldOrgByID(ctx, shouldTriggerBulk, id)
}
@@ -175,6 +184,11 @@ func (q *Queries) OrgByPrimaryDomain(ctx context.Context, domain string) (org *O
ctx, span := tracing.NewSpan(ctx)
defer func() { span.EndWithError(err) }()
org, ok := q.caches.org.Get(ctx, orgIndexByPrimaryDomain, domain)
if ok {
return org, nil
}
stmt, scan := prepareOrgQuery(ctx, q.client)
query, args, err := stmt.Where(sq.Eq{
OrgColumnDomain.identifier(): domain,
@@ -189,6 +203,9 @@ func (q *Queries) OrgByPrimaryDomain(ctx context.Context, domain string) (org *O
org, err = scan(row)
return err
}, query, args...)
if err == nil {
q.caches.org.Set(ctx, org)
}
return org, err
}
@@ -476,3 +493,30 @@ func prepareOrgUniqueQuery(ctx context.Context, db prepareDatabase) (sq.SelectBu
return isUnique, err
}
}
type orgIndex int
//go:generate enumer -type orgIndex -linecomment
const (
// Empty line comment ensures empty string for unspecified value
orgIndexUnspecified orgIndex = iota //
orgIndexByID
orgIndexByPrimaryDomain
)
// Keys implements [cache.Entry]
func (o *Org) Keys(index orgIndex) []string {
switch index {
case orgIndexByID:
return []string{o.ID}
case orgIndexByPrimaryDomain:
return []string{o.Domain}
case orgIndexUnspecified:
}
return nil
}
func (c *Caches) registerOrgInvalidation() {
invalidate := cacheInvalidationFunc(c.instance, instanceIndexByID, getAggregateID)
projection.OrgProjection.RegisterCacheInvalidation(invalidate)
}

View File

@@ -0,0 +1,82 @@
// Code generated by "enumer -type orgIndex -linecomment"; DO NOT EDIT.
package query
import (
"fmt"
"strings"
)
const _orgIndexName = "orgIndexByIDorgIndexByPrimaryDomain"
var _orgIndexIndex = [...]uint8{0, 0, 12, 35}
const _orgIndexLowerName = "orgindexbyidorgindexbyprimarydomain"
func (i orgIndex) String() string {
if i < 0 || i >= orgIndex(len(_orgIndexIndex)-1) {
return fmt.Sprintf("orgIndex(%d)", i)
}
return _orgIndexName[_orgIndexIndex[i]:_orgIndexIndex[i+1]]
}
// An "invalid array index" compiler error signifies that the constant values have changed.
// Re-run the stringer command to generate them again.
func _orgIndexNoOp() {
var x [1]struct{}
_ = x[orgIndexUnspecified-(0)]
_ = x[orgIndexByID-(1)]
_ = x[orgIndexByPrimaryDomain-(2)]
}
var _orgIndexValues = []orgIndex{orgIndexUnspecified, orgIndexByID, orgIndexByPrimaryDomain}
var _orgIndexNameToValueMap = map[string]orgIndex{
_orgIndexName[0:0]: orgIndexUnspecified,
_orgIndexLowerName[0:0]: orgIndexUnspecified,
_orgIndexName[0:12]: orgIndexByID,
_orgIndexLowerName[0:12]: orgIndexByID,
_orgIndexName[12:35]: orgIndexByPrimaryDomain,
_orgIndexLowerName[12:35]: orgIndexByPrimaryDomain,
}
var _orgIndexNames = []string{
_orgIndexName[0:0],
_orgIndexName[0:12],
_orgIndexName[12:35],
}
// orgIndexString retrieves an enum value from the enum constants string name.
// Throws an error if the param is not part of the enum.
func orgIndexString(s string) (orgIndex, error) {
if val, ok := _orgIndexNameToValueMap[s]; ok {
return val, nil
}
if val, ok := _orgIndexNameToValueMap[strings.ToLower(s)]; ok {
return val, nil
}
return 0, fmt.Errorf("%s does not belong to orgIndex values", s)
}
// orgIndexValues returns all values of the enum
func orgIndexValues() []orgIndex {
return _orgIndexValues
}
// orgIndexStrings returns a slice of all String values of the enum
func orgIndexStrings() []string {
strs := make([]string, len(_orgIndexNames))
copy(strs, _orgIndexNames)
return strs
}
// IsAorgIndex returns "true" if the value is listed in the enum definition. "false" otherwise
func (i orgIndex) IsAorgIndex() bool {
for _, v := range _orgIndexValues {
if i == v {
return true
}
}
return false
}