mirror of
https://github.com/zitadel/zitadel.git
synced 2025-08-11 18:33:28 +00:00
feat(projections): resource counters (#9979)
# Which Problems Are Solved Add the ability to keep track of the current counts of projection resources. We want to prevent calling `SELECT COUNT(*)` on tables, as that forces a full scan and sudden spikes of DB resource uses. # How the Problems Are Solved - A resource_counts table is added - Triggers that increment and decrement the counted values on inserts and deletes - Triggers that delete all counts of a table when the source table is TRUNCATEd. This is not in the business logic, but prevents wrong counts in case someone want to force a re-projection. - Triggers that delete all counts if the parent resource is deleted - Script to pre-populate the resource_counts table when a new source table is added. The triggers are reusable for any type of resource, in case we choose to add more in the future. Counts are aggregated by a given parent. Currently only `instance` and `organization` are defined as possible parent. This can later be extended to other types, such as `project`, should the need arise. I deliberately chose to use `parent_id` to distinguish from the de-factor `resource_owner` which is usually an organization ID. For example: - For users the parent is an organization and the `parent_id` matches `resource_owner`. - For organizations the parent is an instance, but the `resource_owner` is the `org_id`. In this case the `parent_id` is the `instance_id`. - Applications would have a similar problem, where the parent is a project, but the `resource_owner` is the `org_id` # Additional Context Closes https://github.com/zitadel/zitadel/issues/9957
This commit is contained in:
9
internal/domain/count_trigger.go
Normal file
9
internal/domain/count_trigger.go
Normal file
@@ -0,0 +1,9 @@
|
||||
package domain
|
||||
|
||||
//go:generate enumer -type CountParentType -transform lower -trimprefix CountParentType -sql
|
||||
type CountParentType int
|
||||
|
||||
const (
|
||||
CountParentTypeInstance CountParentType = iota
|
||||
CountParentTypeOrganization
|
||||
)
|
109
internal/domain/countparenttype_enumer.go
Normal file
109
internal/domain/countparenttype_enumer.go
Normal file
@@ -0,0 +1,109 @@
|
||||
// Code generated by "enumer -type CountParentType -transform lower -trimprefix CountParentType -sql"; DO NOT EDIT.
|
||||
|
||||
package domain
|
||||
|
||||
import (
|
||||
"database/sql/driver"
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const _CountParentTypeName = "instanceorganization"
|
||||
|
||||
var _CountParentTypeIndex = [...]uint8{0, 8, 20}
|
||||
|
||||
const _CountParentTypeLowerName = "instanceorganization"
|
||||
|
||||
func (i CountParentType) String() string {
|
||||
if i < 0 || i >= CountParentType(len(_CountParentTypeIndex)-1) {
|
||||
return fmt.Sprintf("CountParentType(%d)", i)
|
||||
}
|
||||
return _CountParentTypeName[_CountParentTypeIndex[i]:_CountParentTypeIndex[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 _CountParentTypeNoOp() {
|
||||
var x [1]struct{}
|
||||
_ = x[CountParentTypeInstance-(0)]
|
||||
_ = x[CountParentTypeOrganization-(1)]
|
||||
}
|
||||
|
||||
var _CountParentTypeValues = []CountParentType{CountParentTypeInstance, CountParentTypeOrganization}
|
||||
|
||||
var _CountParentTypeNameToValueMap = map[string]CountParentType{
|
||||
_CountParentTypeName[0:8]: CountParentTypeInstance,
|
||||
_CountParentTypeLowerName[0:8]: CountParentTypeInstance,
|
||||
_CountParentTypeName[8:20]: CountParentTypeOrganization,
|
||||
_CountParentTypeLowerName[8:20]: CountParentTypeOrganization,
|
||||
}
|
||||
|
||||
var _CountParentTypeNames = []string{
|
||||
_CountParentTypeName[0:8],
|
||||
_CountParentTypeName[8:20],
|
||||
}
|
||||
|
||||
// CountParentTypeString retrieves an enum value from the enum constants string name.
|
||||
// Throws an error if the param is not part of the enum.
|
||||
func CountParentTypeString(s string) (CountParentType, error) {
|
||||
if val, ok := _CountParentTypeNameToValueMap[s]; ok {
|
||||
return val, nil
|
||||
}
|
||||
|
||||
if val, ok := _CountParentTypeNameToValueMap[strings.ToLower(s)]; ok {
|
||||
return val, nil
|
||||
}
|
||||
return 0, fmt.Errorf("%s does not belong to CountParentType values", s)
|
||||
}
|
||||
|
||||
// CountParentTypeValues returns all values of the enum
|
||||
func CountParentTypeValues() []CountParentType {
|
||||
return _CountParentTypeValues
|
||||
}
|
||||
|
||||
// CountParentTypeStrings returns a slice of all String values of the enum
|
||||
func CountParentTypeStrings() []string {
|
||||
strs := make([]string, len(_CountParentTypeNames))
|
||||
copy(strs, _CountParentTypeNames)
|
||||
return strs
|
||||
}
|
||||
|
||||
// IsACountParentType returns "true" if the value is listed in the enum definition. "false" otherwise
|
||||
func (i CountParentType) IsACountParentType() bool {
|
||||
for _, v := range _CountParentTypeValues {
|
||||
if i == v {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (i CountParentType) Value() (driver.Value, error) {
|
||||
return i.String(), nil
|
||||
}
|
||||
|
||||
func (i *CountParentType) Scan(value interface{}) error {
|
||||
if value == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
var str string
|
||||
switch v := value.(type) {
|
||||
case []byte:
|
||||
str = string(v)
|
||||
case string:
|
||||
str = v
|
||||
case fmt.Stringer:
|
||||
str = v.String()
|
||||
default:
|
||||
return fmt.Errorf("invalid value of CountParentType: %[1]T(%[1]v)", value)
|
||||
}
|
||||
|
||||
val, err := CountParentTypeString(str)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
*i = val
|
||||
return nil
|
||||
}
|
@@ -4,11 +4,14 @@ package domain
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const _SecretGeneratorTypeName = "unspecifiedinit_codeverify_email_codeverify_phone_codeverify_domainpassword_reset_codepasswordless_init_codeapp_secretotpsmsotp_emailinvite_codesecret_generator_type_count"
|
||||
const _SecretGeneratorTypeName = "unspecifiedinit_codeverify_email_codeverify_phone_codeverify_domainpassword_reset_codepasswordless_init_codeapp_secretotpsmsotp_emailinvite_codesigning_keysecret_generator_type_count"
|
||||
|
||||
var _SecretGeneratorTypeIndex = [...]uint8{0, 11, 20, 37, 54, 67, 86, 108, 118, 124, 133, 144, 171}
|
||||
var _SecretGeneratorTypeIndex = [...]uint8{0, 11, 20, 37, 54, 67, 86, 108, 118, 124, 133, 144, 155, 182}
|
||||
|
||||
const _SecretGeneratorTypeLowerName = "unspecifiedinit_codeverify_email_codeverify_phone_codeverify_domainpassword_reset_codepasswordless_init_codeapp_secretotpsmsotp_emailinvite_codesigning_keysecret_generator_type_count"
|
||||
|
||||
func (i SecretGeneratorType) String() string {
|
||||
if i < 0 || i >= SecretGeneratorType(len(_SecretGeneratorTypeIndex)-1) {
|
||||
@@ -17,21 +20,70 @@ func (i SecretGeneratorType) String() string {
|
||||
return _SecretGeneratorTypeName[_SecretGeneratorTypeIndex[i]:_SecretGeneratorTypeIndex[i+1]]
|
||||
}
|
||||
|
||||
var _SecretGeneratorTypeValues = []SecretGeneratorType{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}
|
||||
// An "invalid array index" compiler error signifies that the constant values have changed.
|
||||
// Re-run the stringer command to generate them again.
|
||||
func _SecretGeneratorTypeNoOp() {
|
||||
var x [1]struct{}
|
||||
_ = x[SecretGeneratorTypeUnspecified-(0)]
|
||||
_ = x[SecretGeneratorTypeInitCode-(1)]
|
||||
_ = x[SecretGeneratorTypeVerifyEmailCode-(2)]
|
||||
_ = x[SecretGeneratorTypeVerifyPhoneCode-(3)]
|
||||
_ = x[SecretGeneratorTypeVerifyDomain-(4)]
|
||||
_ = x[SecretGeneratorTypePasswordResetCode-(5)]
|
||||
_ = x[SecretGeneratorTypePasswordlessInitCode-(6)]
|
||||
_ = x[SecretGeneratorTypeAppSecret-(7)]
|
||||
_ = x[SecretGeneratorTypeOTPSMS-(8)]
|
||||
_ = x[SecretGeneratorTypeOTPEmail-(9)]
|
||||
_ = x[SecretGeneratorTypeInviteCode-(10)]
|
||||
_ = x[SecretGeneratorTypeSigningKey-(11)]
|
||||
_ = x[secretGeneratorTypeCount-(12)]
|
||||
}
|
||||
|
||||
var _SecretGeneratorTypeValues = []SecretGeneratorType{SecretGeneratorTypeUnspecified, SecretGeneratorTypeInitCode, SecretGeneratorTypeVerifyEmailCode, SecretGeneratorTypeVerifyPhoneCode, SecretGeneratorTypeVerifyDomain, SecretGeneratorTypePasswordResetCode, SecretGeneratorTypePasswordlessInitCode, SecretGeneratorTypeAppSecret, SecretGeneratorTypeOTPSMS, SecretGeneratorTypeOTPEmail, SecretGeneratorTypeInviteCode, SecretGeneratorTypeSigningKey, secretGeneratorTypeCount}
|
||||
|
||||
var _SecretGeneratorTypeNameToValueMap = map[string]SecretGeneratorType{
|
||||
_SecretGeneratorTypeName[0:11]: 0,
|
||||
_SecretGeneratorTypeName[11:20]: 1,
|
||||
_SecretGeneratorTypeName[20:37]: 2,
|
||||
_SecretGeneratorTypeName[37:54]: 3,
|
||||
_SecretGeneratorTypeName[54:67]: 4,
|
||||
_SecretGeneratorTypeName[67:86]: 5,
|
||||
_SecretGeneratorTypeName[86:108]: 6,
|
||||
_SecretGeneratorTypeName[108:118]: 7,
|
||||
_SecretGeneratorTypeName[118:124]: 8,
|
||||
_SecretGeneratorTypeName[124:133]: 9,
|
||||
_SecretGeneratorTypeName[133:144]: 10,
|
||||
_SecretGeneratorTypeName[144:171]: 11,
|
||||
_SecretGeneratorTypeName[0:11]: SecretGeneratorTypeUnspecified,
|
||||
_SecretGeneratorTypeLowerName[0:11]: SecretGeneratorTypeUnspecified,
|
||||
_SecretGeneratorTypeName[11:20]: SecretGeneratorTypeInitCode,
|
||||
_SecretGeneratorTypeLowerName[11:20]: SecretGeneratorTypeInitCode,
|
||||
_SecretGeneratorTypeName[20:37]: SecretGeneratorTypeVerifyEmailCode,
|
||||
_SecretGeneratorTypeLowerName[20:37]: SecretGeneratorTypeVerifyEmailCode,
|
||||
_SecretGeneratorTypeName[37:54]: SecretGeneratorTypeVerifyPhoneCode,
|
||||
_SecretGeneratorTypeLowerName[37:54]: SecretGeneratorTypeVerifyPhoneCode,
|
||||
_SecretGeneratorTypeName[54:67]: SecretGeneratorTypeVerifyDomain,
|
||||
_SecretGeneratorTypeLowerName[54:67]: SecretGeneratorTypeVerifyDomain,
|
||||
_SecretGeneratorTypeName[67:86]: SecretGeneratorTypePasswordResetCode,
|
||||
_SecretGeneratorTypeLowerName[67:86]: SecretGeneratorTypePasswordResetCode,
|
||||
_SecretGeneratorTypeName[86:108]: SecretGeneratorTypePasswordlessInitCode,
|
||||
_SecretGeneratorTypeLowerName[86:108]: SecretGeneratorTypePasswordlessInitCode,
|
||||
_SecretGeneratorTypeName[108:118]: SecretGeneratorTypeAppSecret,
|
||||
_SecretGeneratorTypeLowerName[108:118]: SecretGeneratorTypeAppSecret,
|
||||
_SecretGeneratorTypeName[118:124]: SecretGeneratorTypeOTPSMS,
|
||||
_SecretGeneratorTypeLowerName[118:124]: SecretGeneratorTypeOTPSMS,
|
||||
_SecretGeneratorTypeName[124:133]: SecretGeneratorTypeOTPEmail,
|
||||
_SecretGeneratorTypeLowerName[124:133]: SecretGeneratorTypeOTPEmail,
|
||||
_SecretGeneratorTypeName[133:144]: SecretGeneratorTypeInviteCode,
|
||||
_SecretGeneratorTypeLowerName[133:144]: SecretGeneratorTypeInviteCode,
|
||||
_SecretGeneratorTypeName[144:155]: SecretGeneratorTypeSigningKey,
|
||||
_SecretGeneratorTypeLowerName[144:155]: SecretGeneratorTypeSigningKey,
|
||||
_SecretGeneratorTypeName[155:182]: secretGeneratorTypeCount,
|
||||
_SecretGeneratorTypeLowerName[155:182]: secretGeneratorTypeCount,
|
||||
}
|
||||
|
||||
var _SecretGeneratorTypeNames = []string{
|
||||
_SecretGeneratorTypeName[0:11],
|
||||
_SecretGeneratorTypeName[11:20],
|
||||
_SecretGeneratorTypeName[20:37],
|
||||
_SecretGeneratorTypeName[37:54],
|
||||
_SecretGeneratorTypeName[54:67],
|
||||
_SecretGeneratorTypeName[67:86],
|
||||
_SecretGeneratorTypeName[86:108],
|
||||
_SecretGeneratorTypeName[108:118],
|
||||
_SecretGeneratorTypeName[118:124],
|
||||
_SecretGeneratorTypeName[124:133],
|
||||
_SecretGeneratorTypeName[133:144],
|
||||
_SecretGeneratorTypeName[144:155],
|
||||
_SecretGeneratorTypeName[155:182],
|
||||
}
|
||||
|
||||
// SecretGeneratorTypeString retrieves an enum value from the enum constants string name.
|
||||
@@ -40,6 +92,10 @@ func SecretGeneratorTypeString(s string) (SecretGeneratorType, error) {
|
||||
if val, ok := _SecretGeneratorTypeNameToValueMap[s]; ok {
|
||||
return val, nil
|
||||
}
|
||||
|
||||
if val, ok := _SecretGeneratorTypeNameToValueMap[strings.ToLower(s)]; ok {
|
||||
return val, nil
|
||||
}
|
||||
return 0, fmt.Errorf("%s does not belong to SecretGeneratorType values", s)
|
||||
}
|
||||
|
||||
@@ -48,6 +104,13 @@ func SecretGeneratorTypeValues() []SecretGeneratorType {
|
||||
return _SecretGeneratorTypeValues
|
||||
}
|
||||
|
||||
// SecretGeneratorTypeStrings returns a slice of all String values of the enum
|
||||
func SecretGeneratorTypeStrings() []string {
|
||||
strs := make([]string, len(_SecretGeneratorTypeNames))
|
||||
copy(strs, _SecretGeneratorTypeNames)
|
||||
return strs
|
||||
}
|
||||
|
||||
// IsASecretGeneratorType returns "true" if the value is listed in the enum definition. "false" otherwise
|
||||
func (i SecretGeneratorType) IsASecretGeneratorType() bool {
|
||||
for _, v := range _SecretGeneratorTypeValues {
|
||||
|
Reference in New Issue
Block a user