From a89d1f3f10bef24c0f25403d83a333b22269ceb7 Mon Sep 17 00:00:00 2001 From: James Kwon <96548424+hongil0316@users.noreply.github.com> Date: Wed, 22 Jan 2025 22:20:43 -0500 Subject: [PATCH] Partial reindex --- drip/api.gen.go | 129 +++++++++++++--------- ent/migrate/schema.go | 3 +- ent/mutation.go | 143 ++++++++++++++++++------ ent/node.go | 13 ++- ent/node/node.go | 8 ++ ent/node/where.go | 55 ++++++++++ ent/node_create.go | 78 +++++++++++++ ent/node_update.go | 52 +++++++++ ent/schema/node.go | 1 + go.sum | 8 -- integration-tests/node_test.go | 124 +++++++++++++++++++-- logging/logging.go | 1 + openapi.yml | 14 +++ run-service-prod.yaml | 6 +- server/implementation/registry.go | 10 +- server/middleware/request_logger.go | 5 - server/server.go | 17 +-- services/registry/registry_svc.go | 164 ++++++++++++++++------------ 18 files changed, 631 insertions(+), 200 deletions(-) diff --git a/drip/api.gen.go b/drip/api.gen.go index e615be0..5468409 100644 --- a/drip/api.gen.go +++ b/drip/api.gen.go @@ -490,6 +490,15 @@ type ListAllNodesParams struct { IncludeBanned *bool `form:"include_banned,omitempty" json:"include_banned,omitempty"` } +// ReindexNodesParams defines parameters for ReindexNodes. +type ReindexNodesParams struct { + // MaxBatch Maximum number of nodes to send to algolia at a time + MaxBatch *int `form:"max_batch,omitempty" json:"max_batch,omitempty"` + + // MinAge Minimum interval from the last time the nodes were indexed to algolia + MinAge *time.Duration `form:"min_age,omitempty" json:"min_age,omitempty"` +} + // SearchNodesParams defines parameters for SearchNodes. type SearchNodesParams struct { // Page Page number of the nodes list @@ -691,7 +700,7 @@ type ServerInterface interface { ListAllNodes(ctx echo.Context, params ListAllNodesParams) error // Reindex all nodes for searching. // (POST /nodes/reindex) - ReindexNodes(ctx echo.Context) error + ReindexNodes(ctx echo.Context, params ReindexNodesParams) error // Retrieves a list of nodes // (GET /nodes/search) SearchNodes(ctx echo.Context, params SearchNodesParams) error @@ -998,8 +1007,24 @@ func (w *ServerInterfaceWrapper) ListAllNodes(ctx echo.Context) error { func (w *ServerInterfaceWrapper) ReindexNodes(ctx echo.Context) error { var err error + // Parameter object where we will unmarshal all parameters from the context + var params ReindexNodesParams + // ------------- Optional query parameter "max_batch" ------------- + + err = runtime.BindQueryParameter("form", true, false, "max_batch", ctx.QueryParams(), ¶ms.MaxBatch) + if err != nil { + return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter max_batch: %s", err)) + } + + // ------------- Optional query parameter "min_age" ------------- + + err = runtime.BindQueryParameter("form", true, false, "min_age", ctx.QueryParams(), ¶ms.MinAge) + if err != nil { + return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter min_age: %s", err)) + } + // Invoke the callback with all the unmarshaled arguments - err = w.Handler.ReindexNodes(ctx) + err = w.Handler.ReindexNodes(ctx, params) return err } @@ -2103,6 +2128,7 @@ func (response ListAllNodes500JSONResponse) VisitListAllNodesResponse(w http.Res } type ReindexNodesRequestObject struct { + Params ReindexNodesParams } type ReindexNodesResponseObject interface { @@ -3977,9 +4003,11 @@ func (sh *strictHandler) ListAllNodes(ctx echo.Context, params ListAllNodesParam } // ReindexNodes operation middleware -func (sh *strictHandler) ReindexNodes(ctx echo.Context) error { +func (sh *strictHandler) ReindexNodes(ctx echo.Context, params ReindexNodesParams) error { var request ReindexNodesRequestObject + request.Params = params + handler := func(ctx echo.Context, request interface{}) (interface{}, error) { return sh.ssi.ReindexNodes(ctx.Request().Context(), request.(ReindexNodesRequestObject)) } @@ -4973,53 +5001,54 @@ var swaggerSpec = []string{ "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", } // GetSwagger returns the content of the embedded swagger specification file diff --git a/ent/migrate/schema.go b/ent/migrate/schema.go index f2e51cf..1afa080 100644 --- a/ent/migrate/schema.go +++ b/ent/migrate/schema.go @@ -120,6 +120,7 @@ var ( {Name: "total_review", Type: field.TypeInt64, Default: 0}, {Name: "status", Type: field.TypeEnum, Enums: []string{"active", "banned", "deleted"}, Default: "active"}, {Name: "status_detail", Type: field.TypeString, Nullable: true, SchemaType: map[string]string{"postgres": "text"}}, + {Name: "last_algolia_index_time", Type: field.TypeTime, Nullable: true}, {Name: "publisher_id", Type: field.TypeString, SchemaType: map[string]string{"postgres": "text"}}, } // NodesTable holds the schema information for the "nodes" table. @@ -130,7 +131,7 @@ var ( ForeignKeys: []*schema.ForeignKey{ { Symbol: "nodes_publishers_nodes", - Columns: []*schema.Column{NodesColumns[16]}, + Columns: []*schema.Column{NodesColumns[17]}, RefColumns: []*schema.Column{PublishersColumns[0]}, OnDelete: schema.NoAction, }, diff --git a/ent/mutation.go b/ent/mutation.go index b71dedf..fc34f20 100644 --- a/ent/mutation.go +++ b/ent/mutation.go @@ -3998,40 +3998,41 @@ func (m *GitCommitMutation) ResetEdge(name string) error { // NodeMutation represents an operation that mutates the Node nodes in the graph. type NodeMutation struct { config - op Op - typ string - id *string - create_time *time.Time - update_time *time.Time - name *string - description *string - category *string - author *string - license *string - repository_url *string - icon_url *string - tags *[]string - appendtags []string - total_install *int64 - addtotal_install *int64 - total_star *int64 - addtotal_star *int64 - total_review *int64 - addtotal_review *int64 - status *schema.NodeStatus - status_detail *string - clearedFields map[string]struct{} - publisher *string - clearedpublisher bool - versions map[uuid.UUID]struct{} - removedversions map[uuid.UUID]struct{} - clearedversions bool - reviews map[uuid.UUID]struct{} - removedreviews map[uuid.UUID]struct{} - clearedreviews bool - done bool - oldValue func(context.Context) (*Node, error) - predicates []predicate.Node + op Op + typ string + id *string + create_time *time.Time + update_time *time.Time + name *string + description *string + category *string + author *string + license *string + repository_url *string + icon_url *string + tags *[]string + appendtags []string + total_install *int64 + addtotal_install *int64 + total_star *int64 + addtotal_star *int64 + total_review *int64 + addtotal_review *int64 + status *schema.NodeStatus + status_detail *string + last_algolia_index_time *time.Time + clearedFields map[string]struct{} + publisher *string + clearedpublisher bool + versions map[uuid.UUID]struct{} + removedversions map[uuid.UUID]struct{} + clearedversions bool + reviews map[uuid.UUID]struct{} + removedreviews map[uuid.UUID]struct{} + clearedreviews bool + done bool + oldValue func(context.Context) (*Node, error) + predicates []predicate.Node } var _ ent.Mutation = (*NodeMutation)(nil) @@ -4854,6 +4855,55 @@ func (m *NodeMutation) ResetStatusDetail() { delete(m.clearedFields, node.FieldStatusDetail) } +// SetLastAlgoliaIndexTime sets the "last_algolia_index_time" field. +func (m *NodeMutation) SetLastAlgoliaIndexTime(t time.Time) { + m.last_algolia_index_time = &t +} + +// LastAlgoliaIndexTime returns the value of the "last_algolia_index_time" field in the mutation. +func (m *NodeMutation) LastAlgoliaIndexTime() (r time.Time, exists bool) { + v := m.last_algolia_index_time + if v == nil { + return + } + return *v, true +} + +// OldLastAlgoliaIndexTime returns the old "last_algolia_index_time" field's value of the Node entity. +// If the Node object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *NodeMutation) OldLastAlgoliaIndexTime(ctx context.Context) (v time.Time, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldLastAlgoliaIndexTime is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldLastAlgoliaIndexTime requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldLastAlgoliaIndexTime: %w", err) + } + return oldValue.LastAlgoliaIndexTime, nil +} + +// ClearLastAlgoliaIndexTime clears the value of the "last_algolia_index_time" field. +func (m *NodeMutation) ClearLastAlgoliaIndexTime() { + m.last_algolia_index_time = nil + m.clearedFields[node.FieldLastAlgoliaIndexTime] = struct{}{} +} + +// LastAlgoliaIndexTimeCleared returns if the "last_algolia_index_time" field was cleared in this mutation. +func (m *NodeMutation) LastAlgoliaIndexTimeCleared() bool { + _, ok := m.clearedFields[node.FieldLastAlgoliaIndexTime] + return ok +} + +// ResetLastAlgoliaIndexTime resets all changes to the "last_algolia_index_time" field. +func (m *NodeMutation) ResetLastAlgoliaIndexTime() { + m.last_algolia_index_time = nil + delete(m.clearedFields, node.FieldLastAlgoliaIndexTime) +} + // ClearPublisher clears the "publisher" edge to the Publisher entity. func (m *NodeMutation) ClearPublisher() { m.clearedpublisher = true @@ -5023,7 +5073,7 @@ func (m *NodeMutation) Type() string { // order to get all numeric fields that were incremented/decremented, call // AddedFields(). func (m *NodeMutation) Fields() []string { - fields := make([]string, 0, 16) + fields := make([]string, 0, 17) if m.create_time != nil { fields = append(fields, node.FieldCreateTime) } @@ -5072,6 +5122,9 @@ func (m *NodeMutation) Fields() []string { if m.status_detail != nil { fields = append(fields, node.FieldStatusDetail) } + if m.last_algolia_index_time != nil { + fields = append(fields, node.FieldLastAlgoliaIndexTime) + } return fields } @@ -5112,6 +5165,8 @@ func (m *NodeMutation) Field(name string) (ent.Value, bool) { return m.Status() case node.FieldStatusDetail: return m.StatusDetail() + case node.FieldLastAlgoliaIndexTime: + return m.LastAlgoliaIndexTime() } return nil, false } @@ -5153,6 +5208,8 @@ func (m *NodeMutation) OldField(ctx context.Context, name string) (ent.Value, er return m.OldStatus(ctx) case node.FieldStatusDetail: return m.OldStatusDetail(ctx) + case node.FieldLastAlgoliaIndexTime: + return m.OldLastAlgoliaIndexTime(ctx) } return nil, fmt.Errorf("unknown Node field %s", name) } @@ -5274,6 +5331,13 @@ func (m *NodeMutation) SetField(name string, value ent.Value) error { } m.SetStatusDetail(v) return nil + case node.FieldLastAlgoliaIndexTime: + v, ok := value.(time.Time) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetLastAlgoliaIndexTime(v) + return nil } return fmt.Errorf("unknown Node field %s", name) } @@ -5358,6 +5422,9 @@ func (m *NodeMutation) ClearedFields() []string { if m.FieldCleared(node.FieldStatusDetail) { fields = append(fields, node.FieldStatusDetail) } + if m.FieldCleared(node.FieldLastAlgoliaIndexTime) { + fields = append(fields, node.FieldLastAlgoliaIndexTime) + } return fields } @@ -5387,6 +5454,9 @@ func (m *NodeMutation) ClearField(name string) error { case node.FieldStatusDetail: m.ClearStatusDetail() return nil + case node.FieldLastAlgoliaIndexTime: + m.ClearLastAlgoliaIndexTime() + return nil } return fmt.Errorf("unknown Node nullable field %s", name) } @@ -5443,6 +5513,9 @@ func (m *NodeMutation) ResetField(name string) error { case node.FieldStatusDetail: m.ResetStatusDetail() return nil + case node.FieldLastAlgoliaIndexTime: + m.ResetLastAlgoliaIndexTime() + return nil } return fmt.Errorf("unknown Node field %s", name) } diff --git a/ent/node.go b/ent/node.go index 7fd2634..7774cd6 100644 --- a/ent/node.go +++ b/ent/node.go @@ -52,6 +52,8 @@ type Node struct { Status schema.NodeStatus `json:"status,omitempty"` // StatusDetail holds the value of the "status_detail" field. StatusDetail string `json:"status_detail,omitempty"` + // LastAlgoliaIndexTime holds the value of the "last_algolia_index_time" field. + LastAlgoliaIndexTime time.Time `json:"last_algolia_index_time,omitempty"` // Edges holds the relations/edges for other nodes in the graph. // The values are being populated by the NodeQuery when eager-loading is set. Edges NodeEdges `json:"edges"` @@ -111,7 +113,7 @@ func (*Node) scanValues(columns []string) ([]any, error) { values[i] = new(sql.NullInt64) case node.FieldID, node.FieldPublisherID, node.FieldName, node.FieldDescription, node.FieldCategory, node.FieldAuthor, node.FieldLicense, node.FieldRepositoryURL, node.FieldIconURL, node.FieldStatus, node.FieldStatusDetail: values[i] = new(sql.NullString) - case node.FieldCreateTime, node.FieldUpdateTime: + case node.FieldCreateTime, node.FieldUpdateTime, node.FieldLastAlgoliaIndexTime: values[i] = new(sql.NullTime) default: values[i] = new(sql.UnknownType) @@ -232,6 +234,12 @@ func (n *Node) assignValues(columns []string, values []any) error { } else if value.Valid { n.StatusDetail = value.String } + case node.FieldLastAlgoliaIndexTime: + if value, ok := values[i].(*sql.NullTime); !ok { + return fmt.Errorf("unexpected type %T for field last_algolia_index_time", values[i]) + } else if value.Valid { + n.LastAlgoliaIndexTime = value.Time + } default: n.selectValues.Set(columns[i], values[i]) } @@ -330,6 +338,9 @@ func (n *Node) String() string { builder.WriteString(", ") builder.WriteString("status_detail=") builder.WriteString(n.StatusDetail) + builder.WriteString(", ") + builder.WriteString("last_algolia_index_time=") + builder.WriteString(n.LastAlgoliaIndexTime.Format(time.ANSIC)) builder.WriteByte(')') return builder.String() } diff --git a/ent/node/node.go b/ent/node/node.go index b835a98..d170e07 100644 --- a/ent/node/node.go +++ b/ent/node/node.go @@ -48,6 +48,8 @@ const ( FieldStatus = "status" // FieldStatusDetail holds the string denoting the status_detail field in the database. FieldStatusDetail = "status_detail" + // FieldLastAlgoliaIndexTime holds the string denoting the last_algolia_index_time field in the database. + FieldLastAlgoliaIndexTime = "last_algolia_index_time" // EdgePublisher holds the string denoting the publisher edge name in mutations. EdgePublisher = "publisher" // EdgeVersions holds the string denoting the versions edge name in mutations. @@ -98,6 +100,7 @@ var Columns = []string{ FieldTotalReview, FieldStatus, FieldStatusDetail, + FieldLastAlgoliaIndexTime, } // ValidColumn reports if the column name is valid (part of the table columns). @@ -222,6 +225,11 @@ func ByStatusDetail(opts ...sql.OrderTermOption) OrderOption { return sql.OrderByField(FieldStatusDetail, opts...).ToFunc() } +// ByLastAlgoliaIndexTime orders the results by the last_algolia_index_time field. +func ByLastAlgoliaIndexTime(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldLastAlgoliaIndexTime, opts...).ToFunc() +} + // ByPublisherField orders the results by publisher field. func ByPublisherField(field string, opts ...sql.OrderTermOption) OrderOption { return func(s *sql.Selector) { diff --git a/ent/node/where.go b/ent/node/where.go index 3531dc0..9cdcd66 100644 --- a/ent/node/where.go +++ b/ent/node/where.go @@ -136,6 +136,11 @@ func StatusDetail(v string) predicate.Node { return predicate.Node(sql.FieldEQ(FieldStatusDetail, v)) } +// LastAlgoliaIndexTime applies equality check predicate on the "last_algolia_index_time" field. It's identical to LastAlgoliaIndexTimeEQ. +func LastAlgoliaIndexTime(v time.Time) predicate.Node { + return predicate.Node(sql.FieldEQ(FieldLastAlgoliaIndexTime, v)) +} + // CreateTimeEQ applies the EQ predicate on the "create_time" field. func CreateTimeEQ(v time.Time) predicate.Node { return predicate.Node(sql.FieldEQ(FieldCreateTime, v)) @@ -1001,6 +1006,56 @@ func StatusDetailContainsFold(v string) predicate.Node { return predicate.Node(sql.FieldContainsFold(FieldStatusDetail, v)) } +// LastAlgoliaIndexTimeEQ applies the EQ predicate on the "last_algolia_index_time" field. +func LastAlgoliaIndexTimeEQ(v time.Time) predicate.Node { + return predicate.Node(sql.FieldEQ(FieldLastAlgoliaIndexTime, v)) +} + +// LastAlgoliaIndexTimeNEQ applies the NEQ predicate on the "last_algolia_index_time" field. +func LastAlgoliaIndexTimeNEQ(v time.Time) predicate.Node { + return predicate.Node(sql.FieldNEQ(FieldLastAlgoliaIndexTime, v)) +} + +// LastAlgoliaIndexTimeIn applies the In predicate on the "last_algolia_index_time" field. +func LastAlgoliaIndexTimeIn(vs ...time.Time) predicate.Node { + return predicate.Node(sql.FieldIn(FieldLastAlgoliaIndexTime, vs...)) +} + +// LastAlgoliaIndexTimeNotIn applies the NotIn predicate on the "last_algolia_index_time" field. +func LastAlgoliaIndexTimeNotIn(vs ...time.Time) predicate.Node { + return predicate.Node(sql.FieldNotIn(FieldLastAlgoliaIndexTime, vs...)) +} + +// LastAlgoliaIndexTimeGT applies the GT predicate on the "last_algolia_index_time" field. +func LastAlgoliaIndexTimeGT(v time.Time) predicate.Node { + return predicate.Node(sql.FieldGT(FieldLastAlgoliaIndexTime, v)) +} + +// LastAlgoliaIndexTimeGTE applies the GTE predicate on the "last_algolia_index_time" field. +func LastAlgoliaIndexTimeGTE(v time.Time) predicate.Node { + return predicate.Node(sql.FieldGTE(FieldLastAlgoliaIndexTime, v)) +} + +// LastAlgoliaIndexTimeLT applies the LT predicate on the "last_algolia_index_time" field. +func LastAlgoliaIndexTimeLT(v time.Time) predicate.Node { + return predicate.Node(sql.FieldLT(FieldLastAlgoliaIndexTime, v)) +} + +// LastAlgoliaIndexTimeLTE applies the LTE predicate on the "last_algolia_index_time" field. +func LastAlgoliaIndexTimeLTE(v time.Time) predicate.Node { + return predicate.Node(sql.FieldLTE(FieldLastAlgoliaIndexTime, v)) +} + +// LastAlgoliaIndexTimeIsNil applies the IsNil predicate on the "last_algolia_index_time" field. +func LastAlgoliaIndexTimeIsNil() predicate.Node { + return predicate.Node(sql.FieldIsNull(FieldLastAlgoliaIndexTime)) +} + +// LastAlgoliaIndexTimeNotNil applies the NotNil predicate on the "last_algolia_index_time" field. +func LastAlgoliaIndexTimeNotNil() predicate.Node { + return predicate.Node(sql.FieldNotNull(FieldLastAlgoliaIndexTime)) +} + // HasPublisher applies the HasEdge predicate on the "publisher" edge. func HasPublisher() predicate.Node { return predicate.Node(func(s *sql.Selector) { diff --git a/ent/node_create.go b/ent/node_create.go index 749c76e..9ecbcc7 100644 --- a/ent/node_create.go +++ b/ent/node_create.go @@ -212,6 +212,20 @@ func (nc *NodeCreate) SetNillableStatusDetail(s *string) *NodeCreate { return nc } +// SetLastAlgoliaIndexTime sets the "last_algolia_index_time" field. +func (nc *NodeCreate) SetLastAlgoliaIndexTime(t time.Time) *NodeCreate { + nc.mutation.SetLastAlgoliaIndexTime(t) + return nc +} + +// SetNillableLastAlgoliaIndexTime sets the "last_algolia_index_time" field if the given value is not nil. +func (nc *NodeCreate) SetNillableLastAlgoliaIndexTime(t *time.Time) *NodeCreate { + if t != nil { + nc.SetLastAlgoliaIndexTime(*t) + } + return nc +} + // SetID sets the "id" field. func (nc *NodeCreate) SetID(s string) *NodeCreate { nc.mutation.SetID(s) @@ -457,6 +471,10 @@ func (nc *NodeCreate) createSpec() (*Node, *sqlgraph.CreateSpec) { _spec.SetField(node.FieldStatusDetail, field.TypeString, value) _node.StatusDetail = value } + if value, ok := nc.mutation.LastAlgoliaIndexTime(); ok { + _spec.SetField(node.FieldLastAlgoliaIndexTime, field.TypeTime, value) + _node.LastAlgoliaIndexTime = value + } if nodes := nc.mutation.PublisherIDs(); len(nodes) > 0 { edge := &sqlgraph.EdgeSpec{ Rel: sqlgraph.M2O, @@ -786,6 +804,24 @@ func (u *NodeUpsert) ClearStatusDetail() *NodeUpsert { return u } +// SetLastAlgoliaIndexTime sets the "last_algolia_index_time" field. +func (u *NodeUpsert) SetLastAlgoliaIndexTime(v time.Time) *NodeUpsert { + u.Set(node.FieldLastAlgoliaIndexTime, v) + return u +} + +// UpdateLastAlgoliaIndexTime sets the "last_algolia_index_time" field to the value that was provided on create. +func (u *NodeUpsert) UpdateLastAlgoliaIndexTime() *NodeUpsert { + u.SetExcluded(node.FieldLastAlgoliaIndexTime) + return u +} + +// ClearLastAlgoliaIndexTime clears the value of the "last_algolia_index_time" field. +func (u *NodeUpsert) ClearLastAlgoliaIndexTime() *NodeUpsert { + u.SetNull(node.FieldLastAlgoliaIndexTime) + return u +} + // UpdateNewValues updates the mutable fields using the new values that were set on create except the ID field. // Using this option is equivalent to using: // @@ -1103,6 +1139,27 @@ func (u *NodeUpsertOne) ClearStatusDetail() *NodeUpsertOne { }) } +// SetLastAlgoliaIndexTime sets the "last_algolia_index_time" field. +func (u *NodeUpsertOne) SetLastAlgoliaIndexTime(v time.Time) *NodeUpsertOne { + return u.Update(func(s *NodeUpsert) { + s.SetLastAlgoliaIndexTime(v) + }) +} + +// UpdateLastAlgoliaIndexTime sets the "last_algolia_index_time" field to the value that was provided on create. +func (u *NodeUpsertOne) UpdateLastAlgoliaIndexTime() *NodeUpsertOne { + return u.Update(func(s *NodeUpsert) { + s.UpdateLastAlgoliaIndexTime() + }) +} + +// ClearLastAlgoliaIndexTime clears the value of the "last_algolia_index_time" field. +func (u *NodeUpsertOne) ClearLastAlgoliaIndexTime() *NodeUpsertOne { + return u.Update(func(s *NodeUpsert) { + s.ClearLastAlgoliaIndexTime() + }) +} + // Exec executes the query. func (u *NodeUpsertOne) Exec(ctx context.Context) error { if len(u.create.conflict) == 0 { @@ -1587,6 +1644,27 @@ func (u *NodeUpsertBulk) ClearStatusDetail() *NodeUpsertBulk { }) } +// SetLastAlgoliaIndexTime sets the "last_algolia_index_time" field. +func (u *NodeUpsertBulk) SetLastAlgoliaIndexTime(v time.Time) *NodeUpsertBulk { + return u.Update(func(s *NodeUpsert) { + s.SetLastAlgoliaIndexTime(v) + }) +} + +// UpdateLastAlgoliaIndexTime sets the "last_algolia_index_time" field to the value that was provided on create. +func (u *NodeUpsertBulk) UpdateLastAlgoliaIndexTime() *NodeUpsertBulk { + return u.Update(func(s *NodeUpsert) { + s.UpdateLastAlgoliaIndexTime() + }) +} + +// ClearLastAlgoliaIndexTime clears the value of the "last_algolia_index_time" field. +func (u *NodeUpsertBulk) ClearLastAlgoliaIndexTime() *NodeUpsertBulk { + return u.Update(func(s *NodeUpsert) { + s.ClearLastAlgoliaIndexTime() + }) +} + // Exec executes the query. func (u *NodeUpsertBulk) Exec(ctx context.Context) error { if u.create.err != nil { diff --git a/ent/node_update.go b/ent/node_update.go index d84f7a2..3145c7b 100644 --- a/ent/node_update.go +++ b/ent/node_update.go @@ -286,6 +286,26 @@ func (nu *NodeUpdate) ClearStatusDetail() *NodeUpdate { return nu } +// SetLastAlgoliaIndexTime sets the "last_algolia_index_time" field. +func (nu *NodeUpdate) SetLastAlgoliaIndexTime(t time.Time) *NodeUpdate { + nu.mutation.SetLastAlgoliaIndexTime(t) + return nu +} + +// SetNillableLastAlgoliaIndexTime sets the "last_algolia_index_time" field if the given value is not nil. +func (nu *NodeUpdate) SetNillableLastAlgoliaIndexTime(t *time.Time) *NodeUpdate { + if t != nil { + nu.SetLastAlgoliaIndexTime(*t) + } + return nu +} + +// ClearLastAlgoliaIndexTime clears the value of the "last_algolia_index_time" field. +func (nu *NodeUpdate) ClearLastAlgoliaIndexTime() *NodeUpdate { + nu.mutation.ClearLastAlgoliaIndexTime() + return nu +} + // SetPublisher sets the "publisher" edge to the Publisher entity. func (nu *NodeUpdate) SetPublisher(p *Publisher) *NodeUpdate { return nu.SetPublisherID(p.ID) @@ -512,6 +532,12 @@ func (nu *NodeUpdate) sqlSave(ctx context.Context) (n int, err error) { if nu.mutation.StatusDetailCleared() { _spec.ClearField(node.FieldStatusDetail, field.TypeString) } + if value, ok := nu.mutation.LastAlgoliaIndexTime(); ok { + _spec.SetField(node.FieldLastAlgoliaIndexTime, field.TypeTime, value) + } + if nu.mutation.LastAlgoliaIndexTimeCleared() { + _spec.ClearField(node.FieldLastAlgoliaIndexTime, field.TypeTime) + } if nu.mutation.PublisherCleared() { edge := &sqlgraph.EdgeSpec{ Rel: sqlgraph.M2O, @@ -904,6 +930,26 @@ func (nuo *NodeUpdateOne) ClearStatusDetail() *NodeUpdateOne { return nuo } +// SetLastAlgoliaIndexTime sets the "last_algolia_index_time" field. +func (nuo *NodeUpdateOne) SetLastAlgoliaIndexTime(t time.Time) *NodeUpdateOne { + nuo.mutation.SetLastAlgoliaIndexTime(t) + return nuo +} + +// SetNillableLastAlgoliaIndexTime sets the "last_algolia_index_time" field if the given value is not nil. +func (nuo *NodeUpdateOne) SetNillableLastAlgoliaIndexTime(t *time.Time) *NodeUpdateOne { + if t != nil { + nuo.SetLastAlgoliaIndexTime(*t) + } + return nuo +} + +// ClearLastAlgoliaIndexTime clears the value of the "last_algolia_index_time" field. +func (nuo *NodeUpdateOne) ClearLastAlgoliaIndexTime() *NodeUpdateOne { + nuo.mutation.ClearLastAlgoliaIndexTime() + return nuo +} + // SetPublisher sets the "publisher" edge to the Publisher entity. func (nuo *NodeUpdateOne) SetPublisher(p *Publisher) *NodeUpdateOne { return nuo.SetPublisherID(p.ID) @@ -1160,6 +1206,12 @@ func (nuo *NodeUpdateOne) sqlSave(ctx context.Context) (_node *Node, err error) if nuo.mutation.StatusDetailCleared() { _spec.ClearField(node.FieldStatusDetail, field.TypeString) } + if value, ok := nuo.mutation.LastAlgoliaIndexTime(); ok { + _spec.SetField(node.FieldLastAlgoliaIndexTime, field.TypeTime, value) + } + if nuo.mutation.LastAlgoliaIndexTimeCleared() { + _spec.ClearField(node.FieldLastAlgoliaIndexTime, field.TypeTime) + } if nuo.mutation.PublisherCleared() { edge := &sqlgraph.EdgeSpec{ Rel: sqlgraph.M2O, diff --git a/ent/schema/node.go b/ent/schema/node.go index eff4878..7e35cad 100644 --- a/ent/schema/node.go +++ b/ent/schema/node.go @@ -55,6 +55,7 @@ func (Node) Fields() []ent.Field { field.String("status_detail").SchemaType(map[string]string{ dialect.Postgres: "text", }).Optional(), + field.Time("last_algolia_index_time").Optional(), } } diff --git a/go.sum b/go.sum index 49039ee..21c92e8 100644 --- a/go.sum +++ b/go.sum @@ -194,8 +194,6 @@ github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/ github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= -github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0= -github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/mattn/go-sqlite3 v1.14.16 h1:yOQRA0RpS5PFz/oikGwBEqvAWhWg5ufRz4ETLjwpU1Y= github.com/mattn/go-sqlite3 v1.14.16/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg= github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0= @@ -220,8 +218,6 @@ github.com/newrelic/go-agent/v3/integrations/nrecho-v4 v1.1.3 h1:+p/67kyG/ySqYgb github.com/newrelic/go-agent/v3/integrations/nrecho-v4 v1.1.3/go.mod h1:ANGVlWG7HgaLvW3npXhu074jiSB4dA6HAux/fmVTvJE= github.com/oapi-codegen/runtime v1.1.1 h1:EXLHh0DXIJnWhdRPN2w4MXAzFyE4CskzhNLUmtpMYro= github.com/oapi-codegen/runtime v1.1.1/go.mod h1:SK9X900oXmPWilYR5/WKPzt3Kqxn/uS/+lbpREv+eCg= -github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= -github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.1.0-rc5 h1:Ygwkfw9bpDvs+c9E34SdgGOj41dX/cbdlwvlWt0pnFI= @@ -248,10 +244,6 @@ github.com/shoenig/test v0.6.4 h1:kVTaSd7WLz5WZ2IaoM0RSzRsUD+m8wRR+5qvntpn4LU= github.com/shoenig/test v0.6.4/go.mod h1:byHiCGXqrVaflBLAMq/srcZIHynQPQgeyvkvXnjqq0k= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= -github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I= -github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0= -github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= -github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spkg/bom v0.0.0-20160624110644-59b7046e48ad/go.mod h1:qLr4V1qq6nMqFKkMo8ZTx3f+BZEkzsRUY10Xsm2mwU0= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= diff --git a/integration-tests/node_test.go b/integration-tests/node_test.go index 8c9fa2f..a93d9c8 100644 --- a/integration-tests/node_test.go +++ b/integration-tests/node_test.go @@ -7,6 +7,9 @@ import ( "registry-backend/config" "registry-backend/drip" + "registry-backend/ent" + "registry-backend/ent/node" + drip_logging "registry-backend/logging" authorization "registry-backend/server/middleware/authorization" "github.com/stretchr/testify/assert" @@ -118,18 +121,6 @@ func TestRegistryNode(t *testing.T) { assert.Equal(t, node.Repository, updatedResponse.Repository) }) - // Test reindexing nodes - t.Run("Reindex Nodes", func(t *testing.T) { - res, err := withMiddleware(authz, impl.ReindexNodes)(ctx, drip.ReindexNodesRequestObject{}) - require.NoError(t, err, "Node reindexing failed") - assert.IsType(t, drip.ReindexNodes200Response{}, res) - - time.Sleep(1 * time.Second) - nodes := impl.mockAlgolia.LastIndexedNodes - require.Equal(t, 1, len(nodes)) - assert.Equal(t, *node.Id, nodes[0].ID) - }) - // Test deleting the node t.Run("Delete Node", func(t *testing.T) { res, err := withMiddleware(authz, impl.DeleteNode)(ctx, drip.DeleteNodeRequestObject{ @@ -151,3 +142,112 @@ func TestRegistryNode(t *testing.T) { }) } + +func TestRegistryNodeReindex(t *testing.T) { + client, cleanup := setupDB(t, context.Background()) + defer cleanup() + + // Initialize server implementation and authorization middleware + impl := NewStrictServerImplementationWithMocks(client, &config.Config{}) + authz := authorization.NewAuthorizationManager(client, impl.RegistryService, impl.NewRelicApp).AuthorizationMiddleware() + + // Setup user context and publisher + ctx, _ := setupTestUser(client) + ctx = drip_logging.SetupLogger().WithContext(ctx) + publisherId, err := setupPublisher(ctx, authz, impl) + require.NoError(t, err, "Failed to set up publisher") + + storeRandomNodes := func(t *testing.T, n int) []drip.Node { + nodes := make([]drip.Node, 0, n) + for i := 0; i < cap(nodes); i++ { + node := randomNode() + createResponse, err := withMiddleware(authz, impl.CreateNode)(ctx, drip.CreateNodeRequestObject{ + PublisherId: publisherId, + Body: node, + }) + require.NoError(t, err, "Node creation failed") + require.NotNil(t, createResponse, "Node creation returned nil response") + + createdNode := createResponse.(drip.CreateNode201JSONResponse) + nodes = append(nodes, drip.Node(createdNode)) + } + return nodes + } + + fetchIndexed := func(t *testing.T, ctx context.Context, indexedAfter time.Time, expectedLen int) []*ent.Node { + var indexed []*ent.Node + for { + require.NoError(t, ctx.Err()) + + indexed, err = client.Node.Query(). + Where(node.LastAlgoliaIndexTimeGT(indexedAfter)). + Where(node.LastAlgoliaIndexTimeLT(time.Now())). + All(ctx) + require.NoError(t, err) + + if len(indexed) < expectedLen { + time.Sleep(time.Second) + continue + } + + return indexed + } + } + + now := time.Now() + nodes := storeRandomNodes(t, 100) + + t.Run("AfterCreate", func(t *testing.T) { + indexed := fetchIndexed(t, ctx, now, len(nodes)) + assert.Equal(t, len(nodes), len(indexed)) + }) + + t.Run("AfterReindex", func(t *testing.T) { + now, batch := time.Now(), 9 + res, err := withMiddleware(authz, impl.ReindexNodes)(ctx, drip.ReindexNodesRequestObject{ + Params: drip.ReindexNodesParams{ + MaxBatch: &batch, + }, + }) + require.NoError(t, err, "Node reindexing failed") + assert.IsType(t, drip.ReindexNodes200Response{}, res) + + // check last_algolia_index_time + ctx, cancel := context.WithTimeout(ctx, 10*time.Second) + defer cancel() + indexed := fetchIndexed(t, ctx, now, len(nodes)) + + assert.Equal(t, len(nodes), len(indexed), "should reindex all nodes") + assert.Len(t, impl.mockAlgolia.LastIndexedNodes, len(nodes)%batch, "should index to algolia partially") + + }) + + t.Run("AfterReindexWithMinAge", func(t *testing.T) { + batch, age := 8, 3*time.Second + time.Sleep(age) + + // add more nodes that will not be reindexed since it is too new + { + now := time.Now() + newNodes := storeRandomNodes(t, 20) + indexed := fetchIndexed(t, ctx, now, len(newNodes)) + assert.Equal(t, len(newNodes), len(indexed)) + } + + now = time.Now() + res, err := withMiddleware(authz, impl.ReindexNodes)(ctx, drip.ReindexNodesRequestObject{ + Params: drip.ReindexNodesParams{ + MaxBatch: &batch, + MinAge: &age, + }, + }) + require.NoError(t, err, "Node reindexing failed") + assert.IsType(t, drip.ReindexNodes200Response{}, res) + + ctx, cancel := context.WithTimeout(ctx, 10*time.Second) + defer cancel() + indexed := fetchIndexed(t, ctx, now, len(nodes)) + assert.Equal(t, len(nodes), len(indexed), "should reindex some nodes") + assert.Len(t, impl.mockAlgolia.LastIndexedNodes, len(nodes)%batch, "should index to algolia partially") + }) +} diff --git a/logging/logging.go b/logging/logging.go index 1c800da..626e487 100644 --- a/logging/logging.go +++ b/logging/logging.go @@ -47,6 +47,7 @@ func SetupLogger() zerolog.Logger { return log } +// ReuseContextLogger returns a new context with the same logger as the given context func ReuseContextLogger(ctx context.Context, newCtx context.Context) context.Context { l := zerolog.Ctx(ctx) if l == nil { diff --git a/openapi.yml b/openapi.yml index 37eab53..893a9ac 100644 --- a/openapi.yml +++ b/openapi.yml @@ -1274,6 +1274,20 @@ paths: operationId: reindexNodes tags: - Nodes + parameters: + - in: query + name: max_batch + description: Maximum number of nodes to send to algolia at a time + required: false + schema: + type: integer + - in: query + name: min_age + description: Minimum interval from the last time the nodes were indexed to algolia + required: false + schema: + type: string + x-go-type: time.Duration responses: '200': description: Reindex completed successfully. diff --git a/run-service-prod.yaml b/run-service-prod.yaml index 56223a4..dd7a2a9 100644 --- a/run-service-prod.yaml +++ b/run-service-prod.yaml @@ -13,7 +13,7 @@ spec: autoscaling.knative.dev/maxScale: '100' run.googleapis.com/minScale: '1' spec: - containerConcurrency: 50 + containerConcurrency: 10 timeoutSeconds: 300 containers: - image: registry-backend-image-substitute @@ -78,5 +78,5 @@ spec: value: comfy-registry-event resources: limits: - cpu: 2000m - memory: 4Gi + cpu: 4000m + memory: 16Gi diff --git a/server/implementation/registry.go b/server/implementation/registry.go index b0e6fff..982bd47 100644 --- a/server/implementation/registry.go +++ b/server/implementation/registry.go @@ -4,7 +4,6 @@ import ( "context" "errors" "fmt" - "github.com/newrelic/go-agent/v3/newrelic" "registry-backend/drip" "registry-backend/ent" "registry-backend/ent/publisher" @@ -238,11 +237,6 @@ func (s *DripStrictServerImplementation) ListNodesForPublisher( func (s *DripStrictServerImplementation) ListAllNodes( ctx context.Context, request drip.ListAllNodesRequestObject) (drip.ListAllNodesResponseObject, error) { - if txn := newrelic.FromContext(ctx); txn != nil { - segment := txn.StartSegment("DripStrictServerImplementation.ListAllNodes") - defer segment.End() - } - err := s.MixpanelService.Track(ctx, []*mixpanel.Event{ s.MixpanelService.NewEvent("List All Nodes", "", map[string]any{ "page": request.Params.Page, @@ -1005,8 +999,10 @@ func (s *DripStrictServerImplementation) ListAllNodeVersions( } func (s *DripStrictServerImplementation) ReindexNodes(ctx context.Context, request drip.ReindexNodesRequestObject) (res drip.ReindexNodesResponseObject, err error) { + // create new context with logger from original Context reindexCtx := drip_logging.ReuseContextLogger(ctx, context.Background()) - err = s.RegistryService.ReindexAllNodesBackground(reindexCtx, s.Client) + + err = s.RegistryService.ReindexAllNodesBackground(reindexCtx, s.Client, request.Params.MaxBatch, request.Params.MinAge) if err != nil { log.Ctx(ctx).Error().Msgf("Failed to trigger reindex all nodes w/ err: %v", err) return drip.ReindexNodes500JSONResponse{Message: "Failed to trigger reindex nodes", Error: err.Error()}, nil diff --git a/server/middleware/request_logger.go b/server/middleware/request_logger.go index a87e4c2..bae3266 100644 --- a/server/middleware/request_logger.go +++ b/server/middleware/request_logger.go @@ -75,11 +75,6 @@ func RequestLoggerMiddleware() echo.MiddlewareFunc { mw := func(next echo.HandlerFunc) echo.HandlerFunc { return func(c echo.Context) error { - if txn := newrelic.FromContext(c.Request().Context()); txn != nil { - segment := txn.StartSegment("RequestLoggerMiddleware") - defer segment.End() - } - req := c.Request() reader := &teeReader{ReadCloser: req.Body} req.Body = io.NopCloser(reader) diff --git a/server/server.go b/server/server.go index 29d7bd3..e1a09a9 100644 --- a/server/server.go +++ b/server/server.go @@ -1,14 +1,8 @@ package server import ( - monitoring "cloud.google.com/go/monitoring/apiv3/v2" "context" "fmt" - "github.com/labstack/echo/v4" - labstack_middleware "github.com/labstack/echo/v4/middleware" - "github.com/newrelic/go-agent/v3/integrations/nrecho-v4" - "github.com/newrelic/go-agent/v3/newrelic" - "github.com/rs/zerolog/log" "registry-backend/config" generated "registry-backend/drip" "registry-backend/ent" @@ -22,6 +16,14 @@ import ( "registry-backend/server/middleware" "registry-backend/server/middleware/authentication" drip_authorization "registry-backend/server/middleware/authorization" + "registry-backend/server/middleware/metric" + + monitoring "cloud.google.com/go/monitoring/apiv3/v2" + "github.com/labstack/echo/v4" + labstack_middleware "github.com/labstack/echo/v4/middleware" + "github.com/newrelic/go-agent/v3/integrations/nrecho-v4" + "github.com/newrelic/go-agent/v3/newrelic" + "github.com/rs/zerolog/log" ) type ServerDependencies struct { @@ -54,6 +56,7 @@ func NewServer(client *ent.Client, config *config.Config) (*Server, error) { newrelic.ConfigDistributedTracerEnabled(true), newrelic.ConfigEnabled(true), ) + if err != nil { log.Error().Err(err).Msg("Failed to initialize NewRelic application") } @@ -123,7 +126,7 @@ func (s *Server) Start() error { })) e.Use(middleware.RequestLoggerMiddleware()) e.Use(middleware.ResponseLoggerMiddleware()) - //e.Use(metric.MetricsMiddleware(&s.Dependencies.MonitoringClient, s.Config)) + e.Use(metric.MetricsMiddleware(&s.Dependencies.MonitoringClient, s.Config)) e.Use(authentication.FirebaseAuthMiddleware(s.Client)) e.Use(authentication.ServiceAccountAuthMiddleware()) e.Use(authentication.JWTAdminAuthMiddleware(s.Client, s.Config.JWTSecret)) diff --git a/services/registry/registry_svc.go b/services/registry/registry_svc.go index a065dee..ea05523 100644 --- a/services/registry/registry_svc.go +++ b/services/registry/registry_svc.go @@ -66,10 +66,7 @@ func NewRegistryService(storageSvc storage.StorageService, pubsubService pubsub. // ListNodes retrieves a paginated list of nodes with optional filtering. func (s *RegistryService) ListNodes(ctx context.Context, client *ent.Client, page, limit int, filter *entity.NodeFilter) (*entity.ListNodesResult, error) { - // Start New Relic transaction segment - var txn *newrelic.Transaction - if txnCtx := newrelic.FromContext(ctx); txnCtx != nil { - txn = txnCtx + if txn := newrelic.FromContext(ctx); txn != nil { txn.Application().RecordCustomMetric( "Custom/ListNodes/Limit", float64(limit), @@ -77,7 +74,6 @@ func (s *RegistryService) ListNodes(ctx context.Context, client *ent.Client, pag segment := txn.StartSegment("RegistryService.ListNodes") defer segment.End() } - // Ensure valid pagination parameters if page < 1 { page = 1 @@ -124,40 +120,15 @@ func (s *RegistryService) ListNodes(ctx context.Context, client *ent.Client, pag // Calculate pagination offset offset := (page - 1) * limit - // Count total nodes with New Relic datastore segment - var total int - var err error - if txn != nil { - segment := newrelic.DatastoreSegment{ - Product: newrelic.DatastorePostgres, // Change based on your DB - Collection: node.Table, // Table name - Operation: "COUNT", - StartTime: txn.StartSegmentNow(), - } - total, err = query.Count(ctx) - segment.End() - } else { - total, err = query.Count(ctx) - } + // Count total nodes + total, err := query.Count(ctx) if err != nil { return nil, fmt.Errorf("failed to count nodes: %w", err) } - // Fetch nodes with pagination and New Relic datastore segment + // Fetch nodes with pagination query = s.decorateNodeQueryWithLatestVersion(query).Offset(offset).Limit(limit) - var nodes []*ent.Node - if txn != nil { - segment := newrelic.DatastoreSegment{ - Product: newrelic.DatastorePostgres, - Collection: node.Table, - Operation: "SELECT", - StartTime: txn.StartSegmentNow(), - } - nodes, err = query.All(ctx) - segment.End() - } else { - nodes, err = query.All(ctx) - } + nodes, err := query.All(ctx) if err != nil { return nil, fmt.Errorf("failed to list nodes: %w", err) } @@ -343,7 +314,7 @@ func (s *RegistryService) CreateNode(ctx context.Context, client *ent.Client, pu return fmt.Errorf("failed to create node: %w", err) } - err = s.algolia.IndexNodes(ctx, createdNode) + err = s.indexNodes(ctx, tx.Client(), createdNode) if err != nil { return fmt.Errorf("failed to index node: %w", err) } @@ -470,6 +441,7 @@ func (s *RegistryService) ListNodeVersions(ctx context.Context, client *ent.Clie } query := client.NodeVersion.Query(). WithStorageFile(). + WithComfyNodes(). Order(ent.Desc(nodeversion.FieldVersion)) if filter.NodeId != "" { @@ -583,6 +555,7 @@ func (s *RegistryService) GetNodeVersionByVersion(ctx context.Context, client *e Where(nodeversion.VersionEQ(nodeVersion)). Where(nodeversion.NodeIDEQ(nodeId)). WithStorageFile(). + WithComfyNodes(). Only(ctx) } @@ -654,6 +627,7 @@ func (s *RegistryService) GetLatestNodeVersion(ctx context.Context, client *ent. )). Order(ent.Desc(nodeversion.FieldVersion)). WithStorageFile(). + WithComfyNodes(). First(ctx) if err != nil { @@ -712,6 +686,7 @@ func (s *RegistryService) CreateComfyNodes( nv, err := tx.NodeVersion.Query(). Where(nodeversion.VersionEQ(nodeVersion)). Where(nodeversion.NodeIDEQ(nodeID)). + WithComfyNodes(). ForUpdate(). Only(ctx) if err != nil { @@ -1112,7 +1087,7 @@ func (s *RegistryService) BanPublisher(ctx context.Context, client *ent.Client, return fmt.Errorf("fail to update nodes: %w", err) } - err = s.algolia.IndexNodes(ctx, nodes...) + err = s.indexNodes(ctx, tx.Client(), nodes...) if err != nil { return fmt.Errorf("failed to index node: %w", err) } @@ -1152,7 +1127,7 @@ func (s *RegistryService) BanNode(ctx context.Context, client *ent.Client, publi return fmt.Errorf("fail to ban node: %w", err) } - err = s.algolia.IndexNodes(ctx, n) + err = s.indexNodes(ctx, tx.Client(), n) if err != nil { return fmt.Errorf("failed to index node: %w", err) } @@ -1198,40 +1173,11 @@ func (s *RegistryService) AssertPublisherBanned(ctx context.Context, client *ent return nil } -func (s *RegistryService) ReindexAllNodes(ctx context.Context, client *ent.Client) error { - if txn := newrelic.FromContext(ctx); txn != nil { - segment := txn.StartSegment("RegistryService.ReindexAllNodes") - defer segment.End() - } - log.Ctx(ctx).Info().Msgf("reindexing nodes") - nodes, err := s.decorateNodeQueryWithLatestVersion(client.Node.Query()).All(ctx) - if err != nil { - return fmt.Errorf("failed to fetch all nodes: %w", err) - } - - log.Ctx(ctx).Info().Msgf("reindexing %d number of nodes", len(nodes)) - err = s.algolia.IndexNodes(ctx, nodes...) - if err != nil { - return fmt.Errorf("failed to reindex all nodes: %w", err) - } - - var nvs []*ent.NodeVersion - for _, n := range nodes { - nvs = append(nvs, n.Edges.Versions...) - } - - log.Ctx(ctx).Info().Msgf("reindexing %d number of node versions", len(nvs)) - err = s.algolia.IndexNodeVersions(ctx, nvs...) - if err != nil { - return fmt.Errorf("failed to reindex all node versions: %w", err) - } - - return nil -} - +// reindexLock is used to prevent multiple reindexing at the same time var reindexLock = sync.Mutex{} -func (s *RegistryService) ReindexAllNodesBackground(ctx context.Context, client *ent.Client) (err error) { +// ReindexAllNodesBackground reindexes all nodes in background goroutine +func (s *RegistryService) ReindexAllNodesBackground(ctx context.Context, client *ent.Client, maxBatch *int, minAge *time.Duration) (err error) { if txn := newrelic.FromContext(ctx); txn != nil { segment := txn.StartSegment("RegistryService.ReindexAllNodesBackground") defer segment.End() @@ -1242,7 +1188,7 @@ func (s *RegistryService) ReindexAllNodesBackground(ctx context.Context, client defer reindexLock.Unlock() go func() { - err = s.ReindexAllNodes(ctx, client) + err = s.ReindexAllNodes(ctx, client, maxBatch, minAge) if err != nil { log.Ctx(ctx).Err(err).Msg("failed to reindex all nodes in background") } @@ -1252,6 +1198,58 @@ func (s *RegistryService) ReindexAllNodesBackground(ctx context.Context, client return nil } +func (s *RegistryService) ReindexAllNodes(ctx context.Context, client *ent.Client, maxBatch *int, minAge *time.Duration) error { + if txn := newrelic.FromContext(ctx); txn != nil { + segment := txn.StartSegment("RegistryService.ReindexAllNodes") + defer segment.End() + } + log.Ctx(ctx).Info().Msgf("reindexing nodes") + + nodeQuery := client.Node.Query().Order(node.ByID()) + if maxBatch != nil { + nodeQuery = nodeQuery.Limit(*maxBatch) + } + if minAge != nil { + maxTime := time.Now().Add(-*minAge) + nodeQuery = nodeQuery.Where(node.Or( + node.LastAlgoliaIndexTimeLTE(maxTime), + node.LastAlgoliaIndexTimeIsNil(), + )) + } + count, lastNodeID := 0, "" + for { + nodeQuery = nodeQuery.Where(node.IDGT(lastNodeID)) + // Nodes + nodes, err := s.decorateNodeQueryWithLatestVersion(nodeQuery).All(ctx) + if err != nil { + return fmt.Errorf("failed to fetch nodes: %w", err) + } + if len(nodes) == 0 { + break + } + + count += (len(nodes)) + lastNodeID = nodes[len(nodes)-1].ID + if err = s.indexNodes(ctx, client, nodes...); err != nil { + return fmt.Errorf("failed to reindex all nodes: %w", err) + } + + // Node Version + var nvs []*ent.NodeVersion + for _, n := range nodes { + nvs = append(nvs, n.Edges.Versions...) + } + log.Ctx(ctx).Info().Msgf("reindexing %d number of node versions", len(nvs)) + err = s.algolia.IndexNodeVersions(ctx, nvs...) + if err != nil { + return fmt.Errorf("failed to reindex all node versions: %w", err) + } + } + + log.Ctx(ctx).Info().Msgf("finished reindexing %d number of nodes", count) + return nil +} + // indexNodeWithLatestVersion re-indexes a single node and its latest version func (s *RegistryService) indexNodeWithLatestVersion( ctx context.Context, @@ -1264,12 +1262,36 @@ func (s *RegistryService) indexNodeWithLatestVersion( if err != nil { return nil, fmt.Errorf("failed to query node: %w", err) } - if err := s.algolia.IndexNodes(ctx, n); err != nil { + if err := s.indexNodes(ctx, client, n); err != nil { return nil, fmt.Errorf("failed to update node: %w", err) } return n, nil } +func (s *RegistryService) indexNodes(ctx context.Context, client *ent.Client, nodes ...*ent.Node) (err error) { + log.Ctx(ctx).Info().Msgf("indexing %d number of nodes", len(nodes)) + + err = s.algolia.IndexNodes(ctx, nodes...) + if err != nil { + return fmt.Errorf("failed to index %d number of nodes to algolia: %w", len(nodes), err) + } + + ids := []string{} + for _, n := range nodes { + ids = append(ids, n.ID) + } + + _, err = client.Node.Update(). + Where(node.IDIn(ids...)). + SetLastAlgoliaIndexTime(time.Now()). + Save(ctx) + if err != nil { + return fmt.Errorf("failed to update last algolia index time for %d number of nodes: %w", len(nodes), err) + } + + return +} + func (s *RegistryService) decorateNodeQueryWithLatestVersion(q *ent.NodeQuery) *ent.NodeQuery { return q.WithVersions(func(q *ent.NodeVersionQuery) { q.Modify(func(s *sql.Selector) {