generated from TBD54566975/tbd-project-template
-
Notifications
You must be signed in to change notification settings - Fork 8
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
chore(refactor): add ingress event type and refactor timeline (#2719)
This is mostly the refactor and setting up for the new `ingress` events. I'm pushing this up first because it will get quite large once I add `ingress` and a bit more refactoring. :) Actual ingress events will come next. --------- Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
- Loading branch information
1 parent
b805ee8
commit c94d3a9
Showing
20 changed files
with
512 additions
and
375 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
6 changes: 6 additions & 0 deletions
6
backend/controller/sql/schema/20240917015216_add_ingress_event_type.sql
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
-- migrate:up | ||
|
||
ALTER TYPE event_type ADD VALUE IF NOT EXISTS 'ingress'; | ||
|
||
-- migrate:down | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,136 @@ | ||
package timeline | ||
|
||
import ( | ||
"context" | ||
"encoding/json" | ||
"fmt" | ||
"time" | ||
|
||
"github.com/alecthomas/types/either" | ||
"github.com/alecthomas/types/optional" | ||
|
||
ftlencryption "github.com/TBD54566975/ftl/backend/controller/encryption/api" | ||
"github.com/TBD54566975/ftl/backend/controller/timeline/internal/sql" | ||
"github.com/TBD54566975/ftl/backend/libdal" | ||
ftlv1 "github.com/TBD54566975/ftl/backend/protos/xyz/block/ftl/v1" | ||
"github.com/TBD54566975/ftl/backend/schema" | ||
"github.com/TBD54566975/ftl/internal/log" | ||
"github.com/TBD54566975/ftl/internal/model" | ||
) | ||
|
||
type CallEvent struct { | ||
ID int64 | ||
DeploymentKey model.DeploymentKey | ||
RequestKey optional.Option[model.RequestKey] | ||
ParentRequestKey optional.Option[model.RequestKey] | ||
Time time.Time | ||
SourceVerb optional.Option[schema.Ref] | ||
DestVerb schema.Ref | ||
Duration time.Duration | ||
Request json.RawMessage | ||
Response json.RawMessage | ||
Error optional.Option[string] | ||
Stack optional.Option[string] | ||
} | ||
|
||
func (e *CallEvent) GetID() int64 { return e.ID } | ||
func (e *CallEvent) event() {} | ||
|
||
// The internal JSON payload of a call event. | ||
type eventCallJSON struct { | ||
DurationMS int64 `json:"duration_ms"` | ||
Request json.RawMessage `json:"request"` | ||
Response json.RawMessage `json:"response"` | ||
Error optional.Option[string] `json:"error,omitempty"` | ||
Stack optional.Option[string] `json:"stack,omitempty"` | ||
} | ||
|
||
type Call struct { | ||
DeploymentKey model.DeploymentKey | ||
RequestKey model.RequestKey | ||
ParentRequestKey optional.Option[model.RequestKey] | ||
StartTime time.Time | ||
DestVerb *schema.Ref | ||
Callers []*schema.Ref | ||
Request *ftlv1.CallRequest | ||
Response either.Either[*ftlv1.CallResponse, error] | ||
} | ||
|
||
func (s *Service) RecordCall(ctx context.Context, call *Call) { | ||
logger := log.FromContext(ctx) | ||
var sourceVerb optional.Option[schema.Ref] | ||
if len(call.Callers) > 0 { | ||
sourceVerb = optional.Some(*call.Callers[0]) | ||
} | ||
|
||
var errorStr optional.Option[string] | ||
var stack optional.Option[string] | ||
var responseBody []byte | ||
|
||
switch response := call.Response.(type) { | ||
case either.Left[*ftlv1.CallResponse, error]: | ||
resp := response.Get() | ||
responseBody = resp.GetBody() | ||
if callError := resp.GetError(); callError != nil { | ||
errorStr = optional.Some(callError.Message) | ||
stack = optional.Ptr(callError.Stack) | ||
} | ||
case either.Right[*ftlv1.CallResponse, error]: | ||
callError := response.Get() | ||
errorStr = optional.Some(callError.Error()) | ||
} | ||
|
||
err := s.insertCallEvent(ctx, &CallEvent{ | ||
Time: call.StartTime, | ||
DeploymentKey: call.DeploymentKey, | ||
RequestKey: optional.Some(call.RequestKey), | ||
ParentRequestKey: call.ParentRequestKey, | ||
Duration: time.Since(call.StartTime), | ||
SourceVerb: sourceVerb, | ||
DestVerb: *call.DestVerb, | ||
Request: call.Request.GetBody(), | ||
Response: responseBody, | ||
Error: errorStr, | ||
Stack: stack, | ||
}) | ||
if err != nil { | ||
logger.Errorf(err, "failed to record call") | ||
} | ||
} | ||
|
||
func (s *Service) insertCallEvent(ctx context.Context, call *CallEvent) error { | ||
var sourceModule, sourceVerb optional.Option[string] | ||
if sr, ok := call.SourceVerb.Get(); ok { | ||
sourceModule, sourceVerb = optional.Some(sr.Module), optional.Some(sr.Name) | ||
} | ||
var requestKey optional.Option[string] | ||
if rn, ok := call.RequestKey.Get(); ok { | ||
requestKey = optional.Some(rn.String()) | ||
} | ||
var parentRequestKey optional.Option[string] | ||
if pr, ok := call.ParentRequestKey.Get(); ok { | ||
parentRequestKey = optional.Some(pr.String()) | ||
} | ||
var payload ftlencryption.EncryptedTimelineColumn | ||
err := s.encryption.EncryptJSON(map[string]any{ | ||
"duration_ms": call.Duration.Milliseconds(), | ||
"request": call.Request, | ||
"response": call.Response, | ||
"error": call.Error, | ||
"stack": call.Stack, | ||
}, &payload) | ||
if err != nil { | ||
return fmt.Errorf("failed to encrypt call payload: %w", err) | ||
} | ||
return libdal.TranslatePGError(s.db.InsertTimelineCallEvent(ctx, sql.InsertTimelineCallEventParams{ | ||
DeploymentKey: call.DeploymentKey, | ||
RequestKey: requestKey, | ||
ParentRequestKey: parentRequestKey, | ||
TimeStamp: call.Time, | ||
SourceModule: sourceModule, | ||
SourceVerb: sourceVerb, | ||
DestModule: call.DestVerb.Module, | ||
DestVerb: call.DestVerb.Name, | ||
Payload: payload, | ||
})) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
package timeline | ||
|
||
import ( | ||
"time" | ||
|
||
"github.com/alecthomas/types/optional" | ||
|
||
"github.com/TBD54566975/ftl/internal/model" | ||
) | ||
|
||
type DeploymentCreatedEvent struct { | ||
ID int64 | ||
DeploymentKey model.DeploymentKey | ||
Time time.Time | ||
Language string | ||
ModuleName string | ||
MinReplicas int | ||
ReplacedDeployment optional.Option[model.DeploymentKey] | ||
} | ||
|
||
func (e *DeploymentCreatedEvent) GetID() int64 { return e.ID } | ||
func (e *DeploymentCreatedEvent) event() {} | ||
|
||
type eventDeploymentUpdatedJSON struct { | ||
MinReplicas int `json:"min_replicas"` | ||
PrevMinReplicas int `json:"prev_min_replicas"` | ||
} | ||
|
||
type DeploymentUpdatedEvent struct { | ||
ID int64 | ||
DeploymentKey model.DeploymentKey | ||
Time time.Time | ||
MinReplicas int | ||
PrevMinReplicas int | ||
} | ||
|
||
func (e *DeploymentUpdatedEvent) GetID() int64 { return e.ID } | ||
func (e *DeploymentUpdatedEvent) event() {} | ||
|
||
type eventDeploymentCreatedJSON struct { | ||
MinReplicas int `json:"min_replicas"` | ||
ReplacedDeployment optional.Option[model.DeploymentKey] `json:"replaced,omitempty"` | ||
} |
Oops, something went wrong.