feat: add debug events API (#8533)

# Which Problems Are Solved

Add a debug API which allows pushing a set of events to be reduced in a
dedicated projection.
The events can carry a sleep duration which simulates a slow query
during projection handling.

# How the Problems Are Solved

- `CreateDebugEvents` allows pushing multiple events which simulate the
lifecycle of a resource. Each event has a `projectionSleep` field, which
issues a `pg_sleep()` statement query in the projection handler :
  - Add
  - Change
  - Remove
- `ListDebugEventsStates` list the current state of the projection,
optionally with a Trigger
- `GetDebugEventsStateByID` get the current state of the aggregate ID in
the projection, optionally with a Trigger


# Additional Changes

- none

# Additional Context

-  Allows reproduction of https://github.com/zitadel/zitadel/issues/8517
This commit is contained in:
Tim Möhlmann
2024-09-11 11:24:00 +03:00
committed by GitHub
parent a569501108
commit 3aba942162
21 changed files with 1404 additions and 0 deletions

View File

@@ -0,0 +1,51 @@
package debug_events
import (
"context"
"github.com/zitadel/zitadel/internal/api/authz"
resource_object "github.com/zitadel/zitadel/internal/api/grpc/resources/object/v3alpha"
"github.com/zitadel/zitadel/internal/telemetry/tracing"
object "github.com/zitadel/zitadel/pkg/grpc/object/v3alpha"
debug_events "github.com/zitadel/zitadel/pkg/grpc/resources/debug_events/v3alpha"
)
func (s *Server) CreateDebugEvents(ctx context.Context, req *debug_events.CreateDebugEventsRequest) (_ *debug_events.CreateDebugEventsResponse, err error) {
ctx, span := tracing.NewSpan(ctx)
defer func() { span.EndWithError(err) }()
details, err := s.command.CreateDebugEvents(ctx, debugEventsFromRequest(req))
if err != nil {
return nil, err
}
return &debug_events.CreateDebugEventsResponse{
Details: resource_object.DomainToDetailsPb(details, object.OwnerType_OWNER_TYPE_INSTANCE, authz.GetInstance(ctx).InstanceID()),
}, nil
}
func (s *Server) GetDebugEventsStateById(ctx context.Context, req *debug_events.GetDebugEventsStateByIdRequest) (_ *debug_events.GetDebugEventsStateByIdResponse, err error) {
ctx, span := tracing.NewSpan(ctx)
defer func() { span.EndWithError(err) }()
state, err := s.query.GetDebugEventsStateByID(ctx, req.GetId(), req.GetTriggerBulk())
if err != nil {
return nil, err
}
return &debug_events.GetDebugEventsStateByIdResponse{
State: eventsStateToPB(state),
}, nil
}
func (s *Server) ListDebugEventsStates(ctx context.Context, req *debug_events.ListDebugEventsStatesRequest) (_ *debug_events.ListDebugEventsStatesResponse, err error) {
ctx, span := tracing.NewSpan(ctx)
defer func() { span.EndWithError(err) }()
states, err := s.query.ListDebugEventsStates(ctx, req.GetTriggerBulk())
if err != nil {
return nil, err
}
return &debug_events.ListDebugEventsStatesResponse{
States: eventStatesToPB(states),
}, nil
}

View File

@@ -0,0 +1,63 @@
package debug_events
import (
"fmt"
resource_object "github.com/zitadel/zitadel/internal/api/grpc/resources/object/v3alpha"
"github.com/zitadel/zitadel/internal/command"
"github.com/zitadel/zitadel/internal/query"
object "github.com/zitadel/zitadel/pkg/grpc/object/v3alpha"
debug_events "github.com/zitadel/zitadel/pkg/grpc/resources/debug_events/v3alpha"
)
func debugEventsFromRequest(req *debug_events.CreateDebugEventsRequest) *command.DebugEvents {
reqEvents := req.GetEvents()
events := make([]command.DebugEvent, len(reqEvents))
for i, event := range reqEvents {
events[i] = debugEventFromRequest(event)
}
return &command.DebugEvents{
AggregateID: req.GetAggregateId(),
Events: events,
}
}
func debugEventFromRequest(event *debug_events.Event) command.DebugEvent {
switch e := event.Event.(type) {
case *debug_events.Event_Add:
return command.DebugEventAdded{
ProjectionSleep: e.Add.GetProjectionSleep().AsDuration(),
Blob: e.Add.Blob,
}
case *debug_events.Event_Change:
return command.DebugEventChanged{
ProjectionSleep: e.Change.GetProjectionSleep().AsDuration(),
Blob: e.Change.Blob,
}
case *debug_events.Event_Remove:
return command.DebugEventRemoved{
ProjectionSleep: e.Remove.GetProjectionSleep().AsDuration(),
}
default:
panic(fmt.Errorf("invalid debug event type %T", event.Event))
}
}
func eventsStateToPB(state *query.DebugEventState) *debug_events.State {
return &debug_events.State{
Details: resource_object.DomainToDetailsPb(&state.ObjectDetails, object.OwnerType_OWNER_TYPE_INSTANCE, state.ResourceOwner),
Blob: state.Blob,
}
}
func eventStatesToPB(states []query.DebugEventState) []*debug_events.State {
out := make([]*debug_events.State, len(states))
for i, state := range states {
out[i] = eventsStateToPB(&state)
}
return out
}

View File

@@ -0,0 +1,47 @@
package debug_events
import (
"google.golang.org/grpc"
"github.com/zitadel/zitadel/internal/api/authz"
"github.com/zitadel/zitadel/internal/api/grpc/server"
"github.com/zitadel/zitadel/internal/command"
"github.com/zitadel/zitadel/internal/query"
debug_events "github.com/zitadel/zitadel/pkg/grpc/resources/debug_events/v3alpha"
)
type Server struct {
debug_events.UnimplementedZITADELDebugEventsServer
command *command.Commands
query *query.Queries
}
func CreateServer(
command *command.Commands,
query *query.Queries,
) *Server {
return &Server{
command: command,
query: query,
}
}
func (s *Server) RegisterServer(grpcServer *grpc.Server) {
debug_events.RegisterZITADELDebugEventsServer(grpcServer, s)
}
func (s *Server) AppName() string {
return debug_events.ZITADELDebugEvents_ServiceDesc.ServiceName
}
func (s *Server) MethodPrefix() string {
return debug_events.ZITADELDebugEvents_ServiceDesc.ServiceName
}
func (s *Server) AuthMethods() authz.MethodMapping {
return debug_events.ZITADELDebugEvents_AuthMethods
}
func (s *Server) RegisterGateway() server.RegisterGatewayFunc {
return debug_events.RegisterZITADELDebugEventsHandler
}