mirror of
https://git.mirrors.martin98.com/https://github.com/SigNoz/signoz
synced 2025-08-17 13:25:55 +08:00
fix: optimize funnel creation by combining insert and update operations
Signed-off-by: Shivanshu Raj Shrivastava <shivanshu1333@gmail.com>
This commit is contained in:
parent
7a7428d73e
commit
f8341e8958
@ -1,7 +1,6 @@
|
|||||||
package impltracefunnel
|
package impltracefunnel
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"net/http"
|
"net/http"
|
||||||
"time"
|
"time"
|
||||||
@ -21,25 +20,6 @@ func NewHandler(module tracefunnel.Module) tracefunnel.Handler {
|
|||||||
return &handler{module: module}
|
return &handler{module: module}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Helper function to check for duplicate funnel names
|
|
||||||
func (handler *handler) checkDuplicateName(ctx context.Context, orgID string, name string, excludeID string) error {
|
|
||||||
funnels, err := handler.module.List(ctx, orgID)
|
|
||||||
if err != nil {
|
|
||||||
return errors.Newf(errors.TypeInvalidInput,
|
|
||||||
errors.CodeInvalidInput,
|
|
||||||
"failed to list funnels: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, f := range funnels {
|
|
||||||
if f.ID.String() != excludeID && f.Name == name {
|
|
||||||
return errors.Newf(errors.TypeInvalidInput,
|
|
||||||
errors.CodeInvalidInput,
|
|
||||||
"a funnel with name '%s' already exists in this organization", name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (handler *handler) New(rw http.ResponseWriter, r *http.Request) {
|
func (handler *handler) New(rw http.ResponseWriter, r *http.Request) {
|
||||||
var req tf.FunnelRequest
|
var req tf.FunnelRequest
|
||||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||||
@ -53,16 +33,11 @@ func (handler *handler) New(rw http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := handler.checkDuplicateName(r.Context(), claims.OrgID, req.Name, ""); err != nil {
|
|
||||||
render.Error(rw, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
funnel, err := handler.module.Create(r.Context(), req.Timestamp, req.Name, claims.UserID, claims.OrgID)
|
funnel, err := handler.module.Create(r.Context(), req.Timestamp, req.Name, claims.UserID, claims.OrgID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
render.Error(rw, errors.Newf(errors.TypeInvalidInput,
|
render.Error(rw, errors.Newf(errors.TypeInvalidInput,
|
||||||
errors.CodeInvalidInput,
|
errors.CodeInvalidInput,
|
||||||
"failed to create funnel"))
|
"failed to create funnel: %v", err))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -97,13 +72,6 @@ func (handler *handler) UpdateSteps(rw http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if req.Name != "" && req.Name != funnel.Name {
|
|
||||||
if err := handler.checkDuplicateName(r.Context(), claims.OrgID, req.Name, funnel.ID.String()); err != nil {
|
|
||||||
render.Error(rw, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
steps, err := tracefunnel.ProcessFunnelSteps(req.Steps)
|
steps, err := tracefunnel.ProcessFunnelSteps(req.Steps)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
render.Error(rw, err)
|
render.Error(rw, err)
|
||||||
@ -170,13 +138,6 @@ func (handler *handler) UpdateFunnel(rw http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if req.Name != "" && req.Name != funnel.Name {
|
|
||||||
if err := handler.checkDuplicateName(r.Context(), claims.OrgID, req.Name, funnel.ID.String()); err != nil {
|
|
||||||
render.Error(rw, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
funnel.UpdatedAt = updatedAt
|
funnel.UpdatedAt = updatedAt
|
||||||
funnel.UpdatedBy = claims.UserID
|
funnel.UpdatedBy = claims.UserID
|
||||||
|
|
||||||
@ -300,9 +261,7 @@ func (handler *handler) Save(rw http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
funnel.UpdatedAt = updatedAt
|
funnel.UpdatedAt = updatedAt
|
||||||
if req.UserID != "" {
|
|
||||||
funnel.UpdatedBy = claims.UserID
|
funnel.UpdatedBy = claims.UserID
|
||||||
}
|
|
||||||
funnel.Description = req.Description
|
funnel.Description = req.Description
|
||||||
|
|
||||||
if err := handler.module.Save(r.Context(), funnel, funnel.UpdatedBy, claims.OrgID); err != nil {
|
if err := handler.module.Save(r.Context(), funnel, funnel.UpdatedBy, claims.OrgID); err != nil {
|
||||||
|
@ -12,10 +12,10 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type module struct {
|
type module struct {
|
||||||
store traceFunnels.TraceFunnelStore
|
store traceFunnels.FunnelStore
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewModule(store traceFunnels.TraceFunnelStore) tracefunnel.Module {
|
func NewModule(store traceFunnels.FunnelStore) tracefunnel.Module {
|
||||||
return &module{
|
return &module{
|
||||||
store: store,
|
store: store,
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@ package impltracefunnel
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/SigNoz/signoz/pkg/sqlstore"
|
"github.com/SigNoz/signoz/pkg/sqlstore"
|
||||||
@ -14,7 +15,7 @@ type store struct {
|
|||||||
sqlstore sqlstore.SQLStore
|
sqlstore sqlstore.SQLStore
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewStore(sqlstore sqlstore.SQLStore) traceFunnels.TraceFunnelStore {
|
func NewStore(sqlstore sqlstore.SQLStore) traceFunnels.FunnelStore {
|
||||||
return &store{sqlstore: sqlstore}
|
return &store{sqlstore: sqlstore}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -30,6 +31,11 @@ func (store *store) Create(ctx context.Context, funnel *traceFunnels.Funnel) err
|
|||||||
funnel.UpdatedAt = time.Now()
|
funnel.UpdatedAt = time.Now()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Set created_by if CreatedByUser is present
|
||||||
|
if funnel.CreatedByUser != nil {
|
||||||
|
funnel.CreatedBy = funnel.CreatedByUser.ID
|
||||||
|
}
|
||||||
|
|
||||||
_, err := store.
|
_, err := store.
|
||||||
sqlstore.
|
sqlstore.
|
||||||
BunDB().
|
BunDB().
|
||||||
@ -37,20 +43,12 @@ func (store *store) Create(ctx context.Context, funnel *traceFunnels.Funnel) err
|
|||||||
Model(funnel).
|
Model(funnel).
|
||||||
Exec(ctx)
|
Exec(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
if strings.Contains(err.Error(), "idx_trace_funnel_org_id_name") {
|
||||||
|
return fmt.Errorf("a funnel with name '%s' already exists in this organization", funnel.Name)
|
||||||
|
}
|
||||||
return fmt.Errorf("failed to create funnel: %v", err)
|
return fmt.Errorf("failed to create funnel: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if funnel.CreatedByUser != nil {
|
|
||||||
_, err = store.sqlstore.BunDB().NewUpdate().
|
|
||||||
Model(funnel).
|
|
||||||
Set("created_by = ?", funnel.CreatedByUser.ID).
|
|
||||||
Where("id = ?", funnel.ID).
|
|
||||||
Exec(ctx)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed to update funnel user relationship: %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -83,6 +81,9 @@ func (store *store) Update(ctx context.Context, funnel *traceFunnels.Funnel) err
|
|||||||
WherePK().
|
WherePK().
|
||||||
Exec(ctx)
|
Exec(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
if strings.Contains(err.Error(), "idx_trace_funnel_org_id_name") {
|
||||||
|
return fmt.Errorf("a funnel with name '%s' already exists in this organization", funnel.Name)
|
||||||
|
}
|
||||||
return fmt.Errorf("failed to update funnel: %v", err)
|
return fmt.Errorf("failed to update funnel: %v", err)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
@ -52,19 +52,16 @@ func ValidateFunnelSteps(steps []tracefunnel.FunnelStep) error {
|
|||||||
// Returns a new slice with normalized step orders, leaving the input slice unchanged.
|
// Returns a new slice with normalized step orders, leaving the input slice unchanged.
|
||||||
func NormalizeFunnelSteps(steps []tracefunnel.FunnelStep) []tracefunnel.FunnelStep {
|
func NormalizeFunnelSteps(steps []tracefunnel.FunnelStep) []tracefunnel.FunnelStep {
|
||||||
if len(steps) == 0 {
|
if len(steps) == 0 {
|
||||||
return nil
|
return []tracefunnel.FunnelStep{}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create a copy of the input slice
|
|
||||||
newSteps := make([]tracefunnel.FunnelStep, len(steps))
|
newSteps := make([]tracefunnel.FunnelStep, len(steps))
|
||||||
copy(newSteps, steps)
|
copy(newSteps, steps)
|
||||||
|
|
||||||
// Sort the copy
|
|
||||||
sort.Slice(newSteps, func(i, j int) bool {
|
sort.Slice(newSteps, func(i, j int) bool {
|
||||||
return newSteps[i].Order < newSteps[j].Order
|
return newSteps[i].Order < newSteps[j].Order
|
||||||
})
|
})
|
||||||
|
|
||||||
// Update orders in the copy
|
|
||||||
for i := range newSteps {
|
for i := range newSteps {
|
||||||
newSteps[i].Order = int64(i + 1)
|
newSteps[i].Order = int64(i + 1)
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,7 @@ import (
|
|||||||
"github.com/SigNoz/signoz/pkg/valuer"
|
"github.com/SigNoz/signoz/pkg/valuer"
|
||||||
)
|
)
|
||||||
|
|
||||||
type TraceFunnelStore interface {
|
type FunnelStore interface {
|
||||||
Create(context.Context, *Funnel) error
|
Create(context.Context, *Funnel) error
|
||||||
Get(context.Context, valuer.UUID) (*Funnel, error)
|
Get(context.Context, valuer.UUID) (*Funnel, error)
|
||||||
List(context.Context, valuer.UUID) ([]*Funnel, error)
|
List(context.Context, valuer.UUID) ([]*Funnel, error)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user