From bc6b1d3fcf92bea686770c8bdba2737b18ee40d3 Mon Sep 17 00:00:00 2001 From: adlerhurst <27845747+adlerhurst@users.noreply.github.com> Date: Sat, 15 Mar 2025 08:32:53 +0100 Subject: [PATCH] blub --- backend/domain/builder/builder.go | 35 ------ backend/domain/builder/instance.go | 54 -------- backend/domain/factory/instance.go | 53 -------- backend/domain/instance.go | 60 +++------ backend/repository/cache/instance.go | 118 ++++++++++-------- backend/repository/event/instance.go | 75 +++++++---- backend/repository/handler.go | 5 - backend/repository/instance.go | 32 +---- .../orchestrate/handler/request_handler.go | 26 ++++ backend/repository/orchestrate/instance.go | 72 +++++++++++ backend/repository/orchestrate/option.go | 25 ++++ backend/repository/sql/instance.go | 50 ++++---- backend/repository/telemetry/logged/global.go | 68 ++++++++++ .../repository/telemetry/logged/instance.go | 41 ------ backend/repository/telemetry/logged/user.go | 36 ------ backend/repository/telemetry/traced/global.go | 106 +++++++++------- .../repository/telemetry/traced/instance.go | 54 -------- backend/repository/telemetry/traced/user.go | 44 ------- backend/storage/eventstore/event_store.go | 4 + backend/telemetry/logging/logger.go | 4 + backend/telemetry/tracing/tracer.go | 50 +++++--- 21 files changed, 456 insertions(+), 556 deletions(-) delete mode 100644 backend/domain/builder/builder.go delete mode 100644 backend/domain/builder/instance.go delete mode 100644 backend/domain/factory/instance.go delete mode 100644 backend/repository/handler.go create mode 100644 backend/repository/orchestrate/handler/request_handler.go create mode 100644 backend/repository/orchestrate/instance.go create mode 100644 backend/repository/orchestrate/option.go create mode 100644 backend/repository/telemetry/logged/global.go delete mode 100644 backend/repository/telemetry/logged/instance.go delete mode 100644 backend/repository/telemetry/logged/user.go delete mode 100644 backend/repository/telemetry/traced/instance.go delete mode 100644 backend/repository/telemetry/traced/user.go diff --git a/backend/domain/builder/builder.go b/backend/domain/builder/builder.go deleted file mode 100644 index c5cf23ed03..0000000000 --- a/backend/domain/builder/builder.go +++ /dev/null @@ -1,35 +0,0 @@ -package builder - -import "sync" - -type builder interface { - reset() -} - -type Builder[T builder] struct { - *sync.Pool -} - -func NewBuilder[T builder](creator func() T) *Builder[T] { - if creator == nil { - creator = func() T { - var x T - return x - } - } - return &Builder[T]{ - Pool: &sync.Pool{ - New: func() any { - return creator() - }, - }, - } -} - -func (b *Builder[T]) Get() T { - return b.Pool.Get().(T) -} - -func (b *Builder[T]) Put(x T) { - b.Pool.Put(x) -} diff --git a/backend/domain/builder/instance.go b/backend/domain/builder/instance.go deleted file mode 100644 index bcf2c2ab62..0000000000 --- a/backend/domain/builder/instance.go +++ /dev/null @@ -1,54 +0,0 @@ -package builder - -import ( - "context" - "log/slog" - - "github.com/zitadel/zitadel/backend/repository" - "github.com/zitadel/zitadel/backend/repository/cache" - "github.com/zitadel/zitadel/backend/telemetry/logging" - "github.com/zitadel/zitadel/backend/telemetry/tracing" -) - -type InstanceBuilder struct { - *Builder[*InstanceBuilder] - - tracer tracing.Tracer - logger *logging.Logger - cache *cache.Instance - db repository.InstanceRepository -} - -func NewInstanceBuilder() *InstanceBuilder { - return &InstanceBuilder{ - Builder: NewBuilder(func() *InstanceBuilder { - return new(InstanceBuilder) - }), - cache: new(cache.Instance), - tracer: tracing.NewTracer("instance"), - logger: &logging.Logger{Logger: slog.Default().With("service", "instance")}, - } -} - -var _ builder = (*InstanceBuilder)(nil) - -func (b *InstanceBuilder) reset() { - b.db = nil -} - -var _ repository.InstanceRepository = (*InstanceBuilder)(nil) - -// ByDomain implements repository.InstanceRepository. -func (b *InstanceBuilder) ByDomain(ctx context.Context, domain string) (*repository.Instance, error) { - panic("unimplemented") -} - -// ByID implements repository.InstanceRepository. -func (b *InstanceBuilder) ByID(ctx context.Context, id string) (*repository.Instance, error) { - panic("unimplemented") -} - -// SetUp implements repository.InstanceRepository. -func (b *InstanceBuilder) SetUp(ctx context.Context, instance *repository.Instance) error { - panic("unimplemented") -} diff --git a/backend/domain/factory/instance.go b/backend/domain/factory/instance.go deleted file mode 100644 index dd07a8df37..0000000000 --- a/backend/domain/factory/instance.go +++ /dev/null @@ -1,53 +0,0 @@ -package factory - -import ( - "context" - - "github.com/zitadel/zitadel/backend/repository" -) - -// type Middleware[O any, H Handler[O]] interface { -// New() H -// NewWithNext(next Handler[O]) H -// } -type Middleware[Req, Res any] interface { - New() Handler[Req, Res] - NewWithNext(next Handler[Req, Res]) Handler[Req, Res] -} - -type Handler[Req, Res any] interface { - Handle(ctx context.Context, request Req) (Res, error) - SetNext(next Handler[Req, Res]) - - Name() string -} - -// type InstanceBuilder struct { -// tracer *traced.Instance -// logger *logged.Instance -// cache *cache.Instance -// events *event.Instance -// db *sql.Instance -// } - -type InstanceSetUpBuilder struct { - tracer Middleware[*repository.Instance, *repository.Instance] - logger Middleware[*repository.Instance, *repository.Instance] - cache Middleware[*repository.Instance, *repository.Instance] - events Middleware[*repository.Instance, *repository.Instance] - db Middleware[*repository.Instance, *repository.Instance] -} - -func (i *InstanceSetUpBuilder) Build() { - instance := i.tracer.NewWithNext( - i.logger.NewWithNext( - i.db.NewWithNext( - i.events.NewWithNext( - i.cache.New(), - ), - ), - ), - ) - _ = instance - // instance. -} diff --git a/backend/domain/instance.go b/backend/domain/instance.go index b5fd2263e3..0b16228914 100644 --- a/backend/domain/instance.go +++ b/backend/domain/instance.go @@ -4,69 +4,39 @@ import ( "context" "github.com/zitadel/zitadel/backend/repository" - "github.com/zitadel/zitadel/backend/repository/cache" - "github.com/zitadel/zitadel/backend/repository/event" - "github.com/zitadel/zitadel/backend/repository/sql" - "github.com/zitadel/zitadel/backend/repository/telemetry/logged" - "github.com/zitadel/zitadel/backend/repository/telemetry/traced" + "github.com/zitadel/zitadel/backend/repository/orchestrate" "github.com/zitadel/zitadel/backend/storage/database" - "github.com/zitadel/zitadel/backend/storage/eventstore" "github.com/zitadel/zitadel/backend/telemetry/logging" "github.com/zitadel/zitadel/backend/telemetry/tracing" ) type Instance struct { - db database.Pool - tracer *tracing.Tracer - logger *logging.Logger - cache *cache.Instance + db database.Pool + + orchestrator instanceOrchestrator +} + +type instanceOrchestrator interface { + ByID(ctx context.Context, querier database.Querier, id string) (*repository.Instance, error) + ByDomain(ctx context.Context, querier database.Querier, domain string) (*repository.Instance, error) + SetUp(ctx context.Context, tx database.Transaction, instance *repository.Instance) (*repository.Instance, error) } func NewInstance(db database.Pool, tracer *tracing.Tracer, logger *logging.Logger) *Instance { b := &Instance{ - db: db, - tracer: tracer, - logger: logger, - - cache: &cache.Instance{}, + db: db, + orchestrator: orchestrate.Instance(), } return b } -func (b *Instance) instanceCommandRepo(tx database.Transaction) repository.InstanceRepository { - return logged.NewInstance( - b.logger, - traced.NewInstance( - b.tracer, - event.NewInstance( - eventstore.New(tx), - b.cache.SetNext( - sql.NewInstance(tx), - ), - ), - ), - ) -} - -func (b *Instance) instanceQueryRepo(tx database.QueryExecutor) repository.InstanceRepository { - return logged.NewInstance( - b.logger, - traced.NewInstance( - b.tracer, - b.cache.SetNext( - sql.NewInstance(tx), - ), - ), - ) -} - func (b *Instance) ByID(ctx context.Context, id string) (*repository.Instance, error) { - return b.instanceQueryRepo(b.db).ByID(ctx, id) + return b.orchestrator.ByID(ctx, b.db, id) } func (b *Instance) ByDomain(ctx context.Context, domain string) (*repository.Instance, error) { - return b.instanceQueryRepo(b.db).ByDomain(ctx, domain) + return b.orchestrator.ByDomain(ctx, b.db, domain) } type SetUpInstance struct { @@ -82,7 +52,7 @@ func (b *Instance) SetUp(ctx context.Context, request *SetUpInstance) (err error defer func() { err = tx.End(ctx, err) }() - err = b.instanceCommandRepo(tx).SetUp(ctx, request.Instance) + _, err = b.orchestrator.SetUp(ctx, tx, request.Instance) if err != nil { return err } diff --git a/backend/repository/cache/instance.go b/backend/repository/cache/instance.go index c036cf99e5..7787dc9128 100644 --- a/backend/repository/cache/instance.go +++ b/backend/repository/cache/instance.go @@ -5,6 +5,7 @@ import ( "sync" "github.com/zitadel/zitadel/backend/repository" + "github.com/zitadel/zitadel/backend/repository/orchestrate/handler" "github.com/zitadel/zitadel/backend/storage/cache" ) @@ -12,69 +13,88 @@ type Instance struct { mu *sync.RWMutex byID cache.Cache[string, *repository.Instance] byDomain cache.Cache[string, *repository.Instance] - - next repository.InstanceRepository } -func (i *Instance) SetNext(next repository.InstanceRepository) *Instance { - return &Instance{ - mu: i.mu, - byID: i.byID, - byDomain: i.byDomain, - next: next, - } -} +func SetUpInstance( + cache *Instance, + handle handler.Handle[*repository.Instance, *repository.Instance], +) handler.Handle[*repository.Instance, *repository.Instance] { + return func(ctx context.Context, instance *repository.Instance) (*repository.Instance, error) { + instance, err := handle(ctx, instance) + if err != nil { + return nil, err + } -// ByDomain implements repository.InstanceRepository. -func (i *Instance) ByDomain(ctx context.Context, domain string) (instance *repository.Instance, err error) { - i.mu.RLock() - defer i.mu.RUnlock() - - if instance, ok := i.byDomain.Get(domain); ok { + cache.set(instance, "") return instance, nil } - - instance, err = i.next.ByDomain(ctx, domain) - if err != nil { - return nil, err - } - - i.set(instance, domain) - - return instance, nil } -// ByID implements repository.InstanceRepository. -func (i *Instance) ByID(ctx context.Context, id string) (*repository.Instance, error) { - i.mu.RLock() - defer i.mu.RUnlock() - - if instance, ok := i.byID.Get(id); ok { +func SetUpInstanceWithout(cache *Instance) handler.Handle[*repository.Instance, *repository.Instance] { + return func(ctx context.Context, instance *repository.Instance) (*repository.Instance, error) { + cache.set(instance, "") return instance, nil } - - instance, err := i.next.ByID(ctx, id) - if err != nil { - return nil, err - - } - - i.set(instance, "") - return instance, nil } -// SetUp implements repository.InstanceRepository. -func (i *Instance) SetUp(ctx context.Context, instance *repository.Instance) error { - err := i.next.SetUp(ctx, instance) - if err != nil { - return err - } +func SetUpInstanceDecorated( + cache *Instance, + handle handler.Handle[*repository.Instance, *repository.Instance], + decorator handler.Decorate[*repository.Instance, *repository.Instance], +) handler.Handle[*repository.Instance, *repository.Instance] { + return func(ctx context.Context, instance *repository.Instance) (*repository.Instance, error) { + instance, err := handle(ctx, instance) + if err != nil { + return nil, err + } - i.set(instance, "") - return nil + return decorator(ctx, instance, func(ctx context.Context, instance *repository.Instance) (*repository.Instance, error) { + cache.set(instance, "") + return instance, nil + }) + } } -var _ repository.InstanceRepository = (*Instance)(nil) +func ForInstanceByID(cache *Instance, handle handler.Handle[string, *repository.Instance]) handler.Handle[string, *repository.Instance] { + return func(ctx context.Context, id string) (*repository.Instance, error) { + cache.mu.RLock() + + instance, ok := cache.byID.Get(id) + cache.mu.RUnlock() + if ok { + return instance, nil + } + + instance, err := handle(ctx, id) + if err != nil { + return nil, err + + } + + cache.set(instance, "") + return instance, nil + } +} + +func ForInstanceByDomain(cache *Instance, handle handler.Handle[string, *repository.Instance]) handler.Handle[string, *repository.Instance] { + return func(ctx context.Context, domain string) (*repository.Instance, error) { + cache.mu.RLock() + + instance, ok := cache.byDomain.Get(domain) + cache.mu.RUnlock() + if ok { + return instance, nil + } + + instance, err := handle(ctx, domain) + if err != nil { + return nil, err + } + + cache.set(instance, domain) + return instance, nil + } +} func (i *Instance) set(instance *repository.Instance, domain string) { i.mu.Lock() diff --git a/backend/repository/event/instance.go b/backend/repository/event/instance.go index faa75bc331..5252a6a17c 100644 --- a/backend/repository/event/instance.go +++ b/backend/repository/event/instance.go @@ -4,34 +4,59 @@ import ( "context" "github.com/zitadel/zitadel/backend/repository" + "github.com/zitadel/zitadel/backend/repository/orchestrate/handler" + "github.com/zitadel/zitadel/backend/storage/database" "github.com/zitadel/zitadel/backend/storage/eventstore" ) -var _ repository.InstanceRepository = (*Instance)(nil) +func SetUpInstance( + client database.Executor, + next handler.Handle[*repository.Instance, *repository.Instance], +) handler.Handle[*repository.Instance, *repository.Instance] { + es := eventstore.New(client) + return func(ctx context.Context, instance *repository.Instance) (*repository.Instance, error) { + instance, err := next(ctx, instance) + if err != nil { + return nil, err + } -type Instance struct { - *eventstore.Eventstore - - next repository.InstanceRepository -} - -func NewInstance(eventstore *eventstore.Eventstore, next repository.InstanceRepository) *Instance { - return &Instance{next: next, Eventstore: eventstore} -} - -func (i *Instance) ByID(ctx context.Context, id string) (*repository.Instance, error) { - return i.next.ByID(ctx, id) -} - -func (i *Instance) ByDomain(ctx context.Context, domain string) (*repository.Instance, error) { - return i.next.ByDomain(ctx, domain) -} - -func (i *Instance) SetUp(ctx context.Context, instance *repository.Instance) error { - err := i.next.SetUp(ctx, instance) - if err != nil { - return err + err = es.Push(ctx, instance) + if err != nil { + return nil, err + } + return instance, nil + } +} + +func SetUpInstanceWithout(client database.Executor) handler.Handle[*repository.Instance, *repository.Instance] { + es := eventstore.New(client) + return func(ctx context.Context, instance *repository.Instance) (*repository.Instance, error) { + err := es.Push(ctx, instance) + if err != nil { + return nil, err + } + return instance, nil + } +} + +func SetUpInstanceDecorated( + client database.Executor, + next handler.Handle[*repository.Instance, *repository.Instance], + decorate handler.Decorate[*repository.Instance, *repository.Instance], +) handler.Handle[*repository.Instance, *repository.Instance] { + es := eventstore.New(client) + return func(ctx context.Context, instance *repository.Instance) (*repository.Instance, error) { + instance, err := next(ctx, instance) + if err != nil { + return nil, err + } + + return decorate(ctx, instance, func(ctx context.Context, instance *repository.Instance) (*repository.Instance, error) { + err = es.Push(ctx, instance) + if err != nil { + return nil, err + } + return instance, nil + }) } - - return i.Push(ctx, instance) } diff --git a/backend/repository/handler.go b/backend/repository/handler.go deleted file mode 100644 index 228a75adb4..0000000000 --- a/backend/repository/handler.go +++ /dev/null @@ -1,5 +0,0 @@ -package repository - -type Handler interface { - SetNext(next Handler) Handler -} diff --git a/backend/repository/instance.go b/backend/repository/instance.go index 7403f769b1..9729cf3161 100644 --- a/backend/repository/instance.go +++ b/backend/repository/instance.go @@ -1,38 +1,10 @@ package repository -import "context" - -type InstanceRepository interface { - InstanceSetuper - instanceByIDQuerier - instanceByDomainQuerier -} - type Instance struct { ID string Name string } -type SetUpInstance func(ctx context.Context, instance *Instance) error - -type InstanceSetuper interface { - SetUp(ctx context.Context, instance *Instance) error -} - -type InstanceByID func(ctx context.Context, id string) (*Instance, error) - -type instanceByIDQuerier interface { - ByID(ctx context.Context, id string) (*Instance, error) -} - -type InstanceByDomain func(ctx context.Context, domain string) (*Instance, error) - -type instanceByDomainQuerier interface { - ByDomain(ctx context.Context, domain string) (*Instance, error) -} - -type ListInstances func(ctx context.Context) ([]*Instance, error) - -type InstanceLister interface { - List(ctx context.Context) ([]*Instance, error) +type ListRequest struct { + Limit uint16 } diff --git a/backend/repository/orchestrate/handler/request_handler.go b/backend/repository/orchestrate/handler/request_handler.go new file mode 100644 index 0000000000..88519f1eb3 --- /dev/null +++ b/backend/repository/orchestrate/handler/request_handler.go @@ -0,0 +1,26 @@ +package handler + +import "context" + +type Handle[Req, Res any] func(ctx context.Context, request Req) (res Res, err error) + +type Decorate[Req, Res any] func(ctx context.Context, request Req, handle Handle[Req, Res]) (res Res, err error) + +func NewChained[Req, Res any](handle Handle[Req, Res], next Handle[Res, Res]) Handle[Req, Res] { + return func(ctx context.Context, request Req) (res Res, err error) { + res, err = handle(ctx, request) + if err != nil { + return res, err + } + if next == nil { + return res, nil + } + return next(ctx, res) + } +} + +func NewDecorated[Req, Res any](decorate Decorate[Req, Res], handle Handle[Req, Res]) Handle[Req, Res] { + return func(ctx context.Context, request Req) (res Res, err error) { + return decorate(ctx, request, handle) + } +} diff --git a/backend/repository/orchestrate/instance.go b/backend/repository/orchestrate/instance.go new file mode 100644 index 0000000000..13580d9c95 --- /dev/null +++ b/backend/repository/orchestrate/instance.go @@ -0,0 +1,72 @@ +package orchestrate + +import ( + "context" + + "github.com/zitadel/zitadel/backend/repository" + "github.com/zitadel/zitadel/backend/repository/cache" + "github.com/zitadel/zitadel/backend/repository/event" + "github.com/zitadel/zitadel/backend/repository/orchestrate/handler" + "github.com/zitadel/zitadel/backend/repository/sql" + "github.com/zitadel/zitadel/backend/repository/telemetry/logged" + "github.com/zitadel/zitadel/backend/repository/telemetry/traced" + "github.com/zitadel/zitadel/backend/storage/database" + "github.com/zitadel/zitadel/backend/telemetry/tracing" +) + +type instance struct { + options + + cache *cache.Instance +} + +func Instance(opts ...Option) *instance { + i := new(instance) + for _, opt := range opts { + opt(&i.options) + } + return i +} + +func (i *instance) apply(o Option) { + o(&i.options) +} + +func (i *instance) SetUp(ctx context.Context, tx database.Transaction, instance *repository.Instance) (*repository.Instance, error) { + return handler.NewChained( + handler.NewDecorated( + traced.DecorateHandle[*repository.Instance, *repository.Instance](i.tracer, tracing.WithSpanName("instance.sql.SetUp")), + sql.SetUpInstance(tx), + ), + handler.NewChained( + handler.NewDecorated( + traced.DecorateHandle[*repository.Instance, *repository.Instance](i.tracer, tracing.WithSpanName("instance.event.SetUp")), + event.SetUpInstanceWithout(tx), + ), + handler.NewDecorated( + traced.DecorateHandle[*repository.Instance, *repository.Instance](i.tracer, tracing.WithSpanName("instance.cache.SetUp")), + cache.SetUpInstanceWithout(i.cache), + ), + ), + )(ctx, instance) +} + +func (i *instance) ByID(ctx context.Context, querier database.Querier, id string) (*repository.Instance, error) { + return traced.Wrap(i.tracer, "instance.byID", + logged.Wrap(i.logger, "instance.byID", + cache.ForInstanceByID(i.cache, + sql.InstanceByID(querier), + ), + ), + )(ctx, id) +} + +func (i *instance) ByDomain(ctx context.Context, querier database.Querier, domain string) (*repository.Instance, error) { + return traced.Wrap(i.tracer, "instance.byDomain", + logged.Wrap(i.logger, "instance.byDomain", + cache.ForInstanceByDomain(i.cache, + sql.InstanceByDomain(querier), + ), + ), + )(ctx, domain) +} diff --git a/backend/repository/orchestrate/option.go b/backend/repository/orchestrate/option.go new file mode 100644 index 0000000000..7b8976db5e --- /dev/null +++ b/backend/repository/orchestrate/option.go @@ -0,0 +1,25 @@ +package orchestrate + +import ( + "github.com/zitadel/zitadel/backend/telemetry/logging" + "github.com/zitadel/zitadel/backend/telemetry/tracing" +) + +type options struct { + tracer *tracing.Tracer + logger *logging.Logger +} + +type Option func(*options) + +func WithTracer(tracer *tracing.Tracer) Option { + return func(o *options) { + o.tracer = tracer + } +} + +func WithLogger(logger *logging.Logger) Option { + return func(o *options) { + o.logger = logger + } +} diff --git a/backend/repository/sql/instance.go b/backend/repository/sql/instance.go index ee3c9bd368..0fb3129196 100644 --- a/backend/repository/sql/instance.go +++ b/backend/repository/sql/instance.go @@ -4,42 +4,42 @@ import ( "context" "github.com/zitadel/zitadel/backend/repository" + "github.com/zitadel/zitadel/backend/repository/orchestrate/handler" "github.com/zitadel/zitadel/backend/storage/database" ) -func NewInstance(client database.QueryExecutor) repository.InstanceRepository { - return &Instance{client: client} -} - -type Instance struct { - client database.QueryExecutor -} - const instanceByDomainQuery = `SELECT i.id, i.name FROM instances i JOIN instance_domains id ON i.id = id.instance_id WHERE id.domain = $1` -// ByDomain implements [InstanceRepository]. -func (r *Instance) ByDomain(ctx context.Context, domain string) (*repository.Instance, error) { - row := r.client.QueryRow(ctx, instanceByDomainQuery, domain) - var instance repository.Instance - if err := row.Scan(&instance.ID, &instance.Name); err != nil { - return nil, err +func InstanceByDomain(client database.Querier) handler.Handle[string, *repository.Instance] { + return func(ctx context.Context, domain string) (*repository.Instance, error) { + row := client.QueryRow(ctx, instanceByDomainQuery, domain) + var instance repository.Instance + if err := row.Scan(&instance.ID, &instance.Name); err != nil { + return nil, err + } + return &instance, nil } - return &instance, nil } const instanceByIDQuery = `SELECT id, name FROM instances WHERE id = $1` -// ByID implements [InstanceRepository]. -func (r *Instance) ByID(ctx context.Context, id string) (*repository.Instance, error) { - row := r.client.QueryRow(ctx, instanceByIDQuery, id) - var instance repository.Instance - if err := row.Scan(&instance.ID, &instance.Name); err != nil { - return nil, err +func InstanceByID(client database.Querier) handler.Handle[string, *repository.Instance] { + return func(ctx context.Context, id string) (*repository.Instance, error) { + row := client.QueryRow(ctx, instanceByIDQuery, id) + var instance repository.Instance + if err := row.Scan(&instance.ID, &instance.Name); err != nil { + return nil, err + } + return &instance, nil } - return &instance, nil } -// SetUp implements [InstanceRepository]. -func (r *Instance) SetUp(ctx context.Context, instance *repository.Instance) error { - return r.client.Exec(ctx, "INSERT INTO instances (id, name) VALUES ($1, $2)", instance.ID, instance.Name) +func SetUpInstance(tx database.Transaction) handler.Handle[*repository.Instance, *repository.Instance] { + return func(ctx context.Context, instance *repository.Instance) (*repository.Instance, error) { + err := tx.Exec(ctx, "INSERT INTO instances (id, name) VALUES ($1, $2)", instance.ID, instance.Name) + if err != nil { + return nil, err + } + return instance, nil + } } diff --git a/backend/repository/telemetry/logged/global.go b/backend/repository/telemetry/logged/global.go new file mode 100644 index 0000000000..cd9ca5307f --- /dev/null +++ b/backend/repository/telemetry/logged/global.go @@ -0,0 +1,68 @@ +package logged + +import ( + "context" + "log/slog" + + "github.com/zitadel/zitadel/backend/repository/orchestrate/handler" + "github.com/zitadel/zitadel/backend/telemetry/logging" +) + +// Wrap decorates the given handle function with logging. +// The function is safe to call with nil logger. +func Wrap[Req, Res any](logger *logging.Logger, name string, handle handler.Handle[Req, Res]) handler.Handle[Req, Res] { + if logger == nil { + return handle + } + return func(ctx context.Context, r Req) (_ Res, err error) { + logger.Debug("execute", slog.String("handler", name)) + defer logger.Debug("done", slog.String("handler", name)) + return handle(ctx, r) + } +} + +func WrapInside(logger *logging.Logger, name string) func(ctx context.Context, fn func(context.Context) error) { + logger = logger.With(slog.String("handler", name)) + return func(ctx context.Context, fn func(context.Context) error) { + logger.Debug("execute") + var err error + defer func() { + if err != nil { + logger.Error("failed", slog.String("cause", err.Error())) + } + logger.Debug("done") + }() + err = fn(ctx) + } +} + +func DecorateHandle[Req, Res any](logger *logging.Logger, handle func(context.Context, Req) (Res, error)) func(ctx context.Context, r Req) (_ Res, err error) { + return func(ctx context.Context, r Req) (_ Res, err error) { + logger.DebugContext(ctx, "execute") + defer func() { + if err != nil { + logger.ErrorContext(ctx, "failed", slog.String("cause", err.Error())) + } + logger.DebugContext(ctx, "done") + }() + return handle(ctx, r) + } +} + +// // Handler wraps the given handle function with logging. +// // The function is safe to call with nil logger. +// func Handler[Req, Res any, H handler.Handle[Req, Res]](logger *logging.Logger, name string, handle H) *handler.Handler[Req, Res, H] { +// return &handler.Handler[Req, Res, H]{ +// Handle: Wrap(logger, name, handle), +// } +// } + +// // Chained wraps the given handle function with logging. +// // The function is safe to call with nil logger. +// // The next handler is called after the handle function. +// func Chained[Req, Res any, H, N handler.Handle[Req, Res]](logger *logging.Logger, name string, handle H, next N) *handler.Chained[Req, Res, H, N] { +// return handler.NewChained( +// Wrap(logger, name, handle), +// next, +// ) +// } diff --git a/backend/repository/telemetry/logged/instance.go b/backend/repository/telemetry/logged/instance.go deleted file mode 100644 index 02fa190b4a..0000000000 --- a/backend/repository/telemetry/logged/instance.go +++ /dev/null @@ -1,41 +0,0 @@ -package logged - -import ( - "context" - "log/slog" - - "github.com/zitadel/zitadel/backend/repository" - "github.com/zitadel/zitadel/backend/telemetry/logging" -) - -type Instance struct { - *logging.Logger - - next repository.InstanceRepository -} - -func NewInstance(logger *logging.Logger, next repository.InstanceRepository) *Instance { - return &Instance{Logger: logger, next: next} -} - -var _ repository.InstanceRepository = (*Instance)(nil) - -func (i *Instance) ByID(ctx context.Context, id string) (*repository.Instance, error) { - i.Logger.InfoContext(ctx, "By ID Query", slog.String("id", id)) - return i.next.ByID(ctx, id) -} - -func (i *Instance) ByDomain(ctx context.Context, domain string) (*repository.Instance, error) { - i.Logger.InfoContext(ctx, "By Domain Query", slog.String("domain", domain)) - return i.next.ByDomain(ctx, domain) -} - -func (i *Instance) SetUp(ctx context.Context, instance *repository.Instance) error { - err := i.next.SetUp(ctx, instance) - if err != nil { - i.Logger.ErrorContext(ctx, "Failed to set up instance", slog.Any("instance", instance), slog.Any("cause", err)) - return err - } - i.Logger.InfoContext(ctx, "Instance set up", slog.Any("instance", instance)) - return nil -} diff --git a/backend/repository/telemetry/logged/user.go b/backend/repository/telemetry/logged/user.go deleted file mode 100644 index 2f7135c86e..0000000000 --- a/backend/repository/telemetry/logged/user.go +++ /dev/null @@ -1,36 +0,0 @@ -package logged - -import ( - "context" - "log/slog" - - "github.com/zitadel/zitadel/backend/repository" - "github.com/zitadel/zitadel/backend/telemetry/logging" -) - -type User struct { - logger *logging.Logger - - next repository.UserRepository -} - -func NewUser(logger *logging.Logger, next repository.UserRepository) *User { - return &User{logger: logger, next: next} -} - -var _ repository.UserRepository = (*User)(nil) - -func (i *User) ByID(ctx context.Context, id string) (*repository.User, error) { - i.logger.InfoContext(ctx, "By ID Query", slog.String("id", id)) - return i.next.ByID(ctx, id) -} - -func (i *User) Create(ctx context.Context, user *repository.User) error { - err := i.next.Create(ctx, user) - if err != nil { - i.logger.ErrorContext(ctx, "Failed to create user", slog.Any("user", user), slog.Any("cause", err)) - return err - } - i.logger.InfoContext(ctx, "User created successfully", slog.Any("user", user)) - return nil -} diff --git a/backend/repository/telemetry/traced/global.go b/backend/repository/telemetry/traced/global.go index 3fc12b678a..e256080e29 100644 --- a/backend/repository/telemetry/traced/global.go +++ b/backend/repository/telemetry/traced/global.go @@ -3,53 +3,75 @@ package traced import ( "context" - "github.com/zitadel/zitadel/backend/domain/factory" + "github.com/zitadel/zitadel/backend/repository/orchestrate/handler" "github.com/zitadel/zitadel/backend/telemetry/tracing" ) -type Tracer[Req, Res any] struct { - tracing.Tracer - next factory.Handler[Req, Res] -} - -func (*Tracer[Req, Res]) Name() string { - return "Tracer" -} - -// Handle implements [factory.Handler]. -func (t *Tracer[Req, Res]) Handle(ctx context.Context, request Req) (res Res, err error) { - if t.next == nil { - return res, nil +// Wrap decorates the given handle function with tracing. +// The function is safe to call with nil tracer. +func Wrap[Req, Res any](tracer *tracing.Tracer, name string, handle handler.Handle[Req, Res]) handler.Handle[Req, Res] { + if tracer == nil { + return handle } - ctx, span := t.Tracer.Start( - ctx, - t.next.Name(), - ) - defer func() { - if err != nil { - span.RecordError(err) + return func(ctx context.Context, r Req) (_ Res, err error) { + ctx, span := tracer.Start( + ctx, + name, + ) + defer func() { + if err != nil { + span.RecordError(err) + } + span.End() + }() + return handle(ctx, r) + } +} + +func WrapInside(tracer *tracing.Tracer, name string) func(ctx context.Context, fn func() error) { + return func(ctx context.Context, fn func() error) { + var err error + _, span := tracer.Start( + ctx, + name, + ) + defer func() { + if err != nil { + span.RecordError(err) + } + span.End() + }() + err = fn() + } +} + +func DecorateHandle[Req, Res any](tracer *tracing.Tracer, opts ...tracing.DecorateOption) handler.Decorate[Req, Res] { + return func(ctx context.Context, r Req, handle handler.Handle[Req, Res]) (_ Res, err error) { + o := new(tracing.DecorateOptions) + for _, opt := range opts { + opt(o) } - span.End() - }() - return t.next.Handle(ctx, request) + + ctx = o.Start(ctx, tracer) + defer o.End(err) + return handle(ctx, r) + } } -// SetNext implements [factory.Handler]. -func (t *Tracer[Req, Res]) SetNext(next factory.Handler[Req, Res]) { - t.next = next -} +// // Handler wraps the given handle function with tracing. +// // The function is safe to call with nil logger. +// func Handler[Req, Res any, H handler.Handle[Req, Res]](tracer *tracing.Tracer, name string, handle H) *handler.Handler[Req, Res, H] { +// return &handler.Handler[Req, Res, H]{ +// Handle: Wrap(tracer, name, handle), +// } +// } -// New implements [factory.Middleware]. -func (t *Tracer[Req, Res]) New() factory.Handler[Req, Res] { - return t.NewWithNext(nil) -} - -// NewWithNext implements [factory.Middleware]. -func (t *Tracer[Req, Res]) NewWithNext(next factory.Handler[Req, Res]) factory.Handler[Req, Res] { - return &Tracer[Req, Res]{Tracer: t.Tracer, next: next} -} - -var ( - _ factory.Middleware[any, any] = (*Tracer[any, any])(nil) - _ factory.Handler[any, any] = (*Tracer[any, any])(nil) -) +// // Chained wraps the given handle function with tracing. +// // The function is safe to call with nil logger. +// // The next handler is called after the handle function. +// func Chained[Req, Res any, H, N handler.Handle[Req, Res]](tracer *tracing.Tracer, name string, handle H, next N) *handler.Chained[Req, Res, H, N] { +// return handler.NewChained( +// Wrap(tracer, name, handle), +// next, +// ) +// } diff --git a/backend/repository/telemetry/traced/instance.go b/backend/repository/telemetry/traced/instance.go deleted file mode 100644 index a2e8f95389..0000000000 --- a/backend/repository/telemetry/traced/instance.go +++ /dev/null @@ -1,54 +0,0 @@ -package traced - -import ( - "context" - - "github.com/zitadel/zitadel/backend/repository" - "github.com/zitadel/zitadel/backend/telemetry/tracing" -) - -var _ repository.InstanceRepository = (*Instance)(nil) - -type Instance struct { - *tracing.Tracer - - next repository.InstanceRepository -} - -func NewInstance(tracer *tracing.Tracer, next repository.InstanceRepository) *Instance { - return &Instance{Tracer: tracer, next: next} -} - -func (i *Instance) SetNext(next repository.InstanceRepository) *Instance { - return &Instance{Tracer: i.Tracer, next: next} -} - -// ByDomain implements [repository.InstanceRepository]. -func (i *Instance) ByDomain(ctx context.Context, domain string) (instance *repository.Instance, err error) { - i.Tracer.Decorate(ctx, func(ctx context.Context) error { - instance, err = i.next.ByDomain(ctx, domain) - return err - }) - - return instance, err -} - -// ByID implements [repository.InstanceRepository]. -func (i *Instance) ByID(ctx context.Context, id string) (instance *repository.Instance, err error) { - i.Tracer.Decorate(ctx, func(ctx context.Context) error { - instance, err = i.next.ByID(ctx, id) - return err - }) - - return instance, err -} - -// SetUp implements [repository.InstanceRepository]. -func (i *Instance) SetUp(ctx context.Context, instance *repository.Instance) (err error) { - i.Tracer.Decorate(ctx, func(ctx context.Context) error { - err = i.next.SetUp(ctx, instance) - return err - }) - - return err -} diff --git a/backend/repository/telemetry/traced/user.go b/backend/repository/telemetry/traced/user.go deleted file mode 100644 index 175d38af93..0000000000 --- a/backend/repository/telemetry/traced/user.go +++ /dev/null @@ -1,44 +0,0 @@ -package traced - -import ( - "context" - - "github.com/zitadel/zitadel/backend/repository" - "github.com/zitadel/zitadel/backend/telemetry/tracing" -) - -var _ repository.UserRepository = (*User)(nil) - -type User struct { - *tracing.Tracer - - next repository.UserRepository -} - -func NewUser(tracer *tracing.Tracer, next repository.UserRepository) *User { - return &User{Tracer: tracer, next: next} -} - -func (i *User) SetNext(next repository.UserRepository) *User { - return &User{Tracer: i.Tracer, next: next} -} - -// ByID implements [repository.UserRepository]. -func (i *User) ByID(ctx context.Context, id string) (user *repository.User, err error) { - i.Tracer.Decorate(ctx, func(ctx context.Context) error { - user, err = i.next.ByID(ctx, id) - return err - }) - - return user, err -} - -// Create implements [repository.UserRepository]. -func (i *User) Create(ctx context.Context, user *repository.User) (err error) { - i.Tracer.Decorate(ctx, func(ctx context.Context) error { - err = i.next.Create(ctx, user) - return err - }) - - return err -} diff --git a/backend/storage/eventstore/event_store.go b/backend/storage/eventstore/event_store.go index 4fe990ee2a..43afa9eaa0 100644 --- a/backend/storage/eventstore/event_store.go +++ b/backend/storage/eventstore/event_store.go @@ -19,3 +19,7 @@ type Event interface{} func (e *Eventstore) Push(ctx context.Context, events ...Event) error { return nil } + +func Push(ctx context.Context, executor database.Executor, events ...Event) error { + return New(executor).Push(ctx, events...) +} diff --git a/backend/telemetry/logging/logger.go b/backend/telemetry/logging/logger.go index 580120cce6..51fa1fb247 100644 --- a/backend/telemetry/logging/logger.go +++ b/backend/telemetry/logging/logger.go @@ -5,3 +5,7 @@ import "log/slog" type Logger struct { *slog.Logger } + +func (l *Logger) With(args ...any) *Logger { + return &Logger{l.Logger.With(args...)} +} diff --git a/backend/telemetry/tracing/tracer.go b/backend/telemetry/tracing/tracer.go index 434a364428..25b61868df 100644 --- a/backend/telemetry/tracing/tracer.go +++ b/backend/telemetry/tracing/tracer.go @@ -14,51 +14,65 @@ func NewTracer(name string) Tracer { return Tracer{otel.Tracer(name)} } -type DecorateOption func(*decorateOptions) +type DecorateOption func(*DecorateOptions) -type decorateOptions struct { +type DecorateOptions struct { startOpts []trace.SpanStartOption endOpts []trace.SpanEndOption spanName string + + span trace.Span } func WithSpanName(name string) DecorateOption { - return func(o *decorateOptions) { + return func(o *DecorateOptions) { o.spanName = name } } func WithSpanStartOptions(opts ...trace.SpanStartOption) DecorateOption { - return func(o *decorateOptions) { + return func(o *DecorateOptions) { o.startOpts = append(o.startOpts, opts...) } } func WithSpanEndOptions(opts ...trace.SpanEndOption) DecorateOption { - return func(o *decorateOptions) { + return func(o *DecorateOptions) { o.endOpts = append(o.endOpts, opts...) } } -func (t Tracer) Decorate(ctx context.Context, fn func(ctx context.Context) error, opts ...DecorateOption) { - o := new(decorateOptions) - for _, opt := range opts { - opt(o) - } - +func (o *DecorateOptions) Start(ctx context.Context, tracer *Tracer) context.Context { if o.spanName == "" { o.spanName = functionName() } - - _, span := t.Tracer.Start(ctx, o.spanName, o.startOpts...) - defer span.End(o.endOpts...) - - if err := fn(ctx); err != nil { - span.RecordError(err) - } + ctx, o.span = tracer.Tracer.Start(ctx, o.spanName, o.startOpts...) + return ctx } +func (o *DecorateOptions) End(err error) { + o.span.RecordError(err) + o.span.End(o.endOpts...) +} + +// func (t Tracer) Decorate(ctx context.Context, fn func(ctx context.Context) error, opts ...DecorateOption) { +// o := new(DecorateOptions) +// for _, opt := range opts { +// opt(o) +// } + +// if o.spanName == "" { +// o.spanName = functionName() +// } + +// ctx, span := t.Tracer.Start(ctx, o.spanName, o.startOpts...) +// defer span.End(o.endOpts...) + +// err := fn(ctx) +// span.RecordError(err) +// } + func functionName() string { counter, _, _, success := runtime.Caller(2)