mirror of
https://git.mirrors.martin98.com/https://github.com/SigNoz/signoz
synced 2025-08-16 17:45: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
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"time"
|
||||
@ -21,25 +20,6 @@ func NewHandler(module tracefunnel.Module) tracefunnel.Handler {
|
||||
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) {
|
||||
var req tf.FunnelRequest
|
||||
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
|
||||
}
|
||||
|
||||
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)
|
||||
if err != nil {
|
||||
render.Error(rw, errors.Newf(errors.TypeInvalidInput,
|
||||
errors.CodeInvalidInput,
|
||||
"failed to create funnel"))
|
||||
"failed to create funnel: %v", err))
|
||||
return
|
||||
}
|
||||
|
||||
@ -97,13 +72,6 @@ func (handler *handler) UpdateSteps(rw http.ResponseWriter, r *http.Request) {
|
||||
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)
|
||||
if err != nil {
|
||||
render.Error(rw, err)
|
||||
@ -170,13 +138,6 @@ func (handler *handler) UpdateFunnel(rw http.ResponseWriter, r *http.Request) {
|
||||
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.UpdatedBy = claims.UserID
|
||||
|
||||
@ -300,9 +261,7 @@ func (handler *handler) Save(rw http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
funnel.UpdatedAt = updatedAt
|
||||
if req.UserID != "" {
|
||||
funnel.UpdatedBy = claims.UserID
|
||||
}
|
||||
funnel.Description = req.Description
|
||||
|
||||
if err := handler.module.Save(r.Context(), funnel, funnel.UpdatedBy, claims.OrgID); err != nil {
|
||||
|
@ -12,10 +12,10 @@ import (
|
||||
)
|
||||
|
||||
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{
|
||||
store: store,
|
||||
}
|
||||
|
@ -3,6 +3,7 @@ package impltracefunnel
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/SigNoz/signoz/pkg/sqlstore"
|
||||
@ -14,7 +15,7 @@ type store struct {
|
||||
sqlstore sqlstore.SQLStore
|
||||
}
|
||||
|
||||
func NewStore(sqlstore sqlstore.SQLStore) traceFunnels.TraceFunnelStore {
|
||||
func NewStore(sqlstore sqlstore.SQLStore) traceFunnels.FunnelStore {
|
||||
return &store{sqlstore: sqlstore}
|
||||
}
|
||||
|
||||
@ -30,6 +31,11 @@ func (store *store) Create(ctx context.Context, funnel *traceFunnels.Funnel) err
|
||||
funnel.UpdatedAt = time.Now()
|
||||
}
|
||||
|
||||
// Set created_by if CreatedByUser is present
|
||||
if funnel.CreatedByUser != nil {
|
||||
funnel.CreatedBy = funnel.CreatedByUser.ID
|
||||
}
|
||||
|
||||
_, err := store.
|
||||
sqlstore.
|
||||
BunDB().
|
||||
@ -37,20 +43,12 @@ func (store *store) Create(ctx context.Context, funnel *traceFunnels.Funnel) err
|
||||
Model(funnel).
|
||||
Exec(ctx)
|
||||
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)
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
@ -83,6 +81,9 @@ func (store *store) Update(ctx context.Context, funnel *traceFunnels.Funnel) err
|
||||
WherePK().
|
||||
Exec(ctx)
|
||||
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 nil
|
||||
|
@ -52,19 +52,16 @@ func ValidateFunnelSteps(steps []tracefunnel.FunnelStep) error {
|
||||
// Returns a new slice with normalized step orders, leaving the input slice unchanged.
|
||||
func NormalizeFunnelSteps(steps []tracefunnel.FunnelStep) []tracefunnel.FunnelStep {
|
||||
if len(steps) == 0 {
|
||||
return nil
|
||||
return []tracefunnel.FunnelStep{}
|
||||
}
|
||||
|
||||
// Create a copy of the input slice
|
||||
newSteps := make([]tracefunnel.FunnelStep, len(steps))
|
||||
copy(newSteps, steps)
|
||||
|
||||
// Sort the copy
|
||||
sort.Slice(newSteps, func(i, j int) bool {
|
||||
return newSteps[i].Order < newSteps[j].Order
|
||||
})
|
||||
|
||||
// Update orders in the copy
|
||||
for i := range newSteps {
|
||||
newSteps[i].Order = int64(i + 1)
|
||||
}
|
||||
|
@ -6,7 +6,7 @@ import (
|
||||
"github.com/SigNoz/signoz/pkg/valuer"
|
||||
)
|
||||
|
||||
type TraceFunnelStore interface {
|
||||
type FunnelStore interface {
|
||||
Create(context.Context, *Funnel) error
|
||||
Get(context.Context, valuer.UUID) (*Funnel, error)
|
||||
List(context.Context, valuer.UUID) ([]*Funnel, error)
|
||||
|
Loading…
x
Reference in New Issue
Block a user