From 371f6c99711708c09af6e039f61bcfe7531757dd Mon Sep 17 00:00:00 2001 From: polebug Date: Wed, 18 Dec 2024 21:01:26 +0800 Subject: [PATCH 01/10] feat(core): implement the metadata handler module --- docs/api.yaml | 2 + docs/generated.go | 388 ++++++++++-------- docs/path/decentralized/metadata.yaml | 17 + ...tchGetDecentralizedMetadataActivities.yaml | 40 ++ docs/schemas/DecentralizedPlatform.yaml | 2 +- docs/schemas/ProtocolMetadata.yaml | 3 +- docs/schemas/ProtocolTag.yaml | 3 +- docs/schemas/ProtocolType.yaml | 2 +- .../dialer/postgres/client_partitioned.go | 30 +- internal/database/model/activity.go | 2 + .../node/component/aggregator/compoent.go | 4 + .../decentralized/handler_metadata.go | 113 +++++ internal/utils/utils.go | 16 + 13 files changed, 447 insertions(+), 175 deletions(-) create mode 100644 docs/path/decentralized/metadata.yaml create mode 100644 docs/requestBody/BatchGetDecentralizedMetadataActivities.yaml create mode 100644 internal/node/component/decentralized/handler_metadata.go diff --git a/docs/api.yaml b/docs/api.yaml index ce9c05b42..228aab052 100644 --- a/docs/api.yaml +++ b/docs/api.yaml @@ -27,6 +27,8 @@ paths: $ref: "./path/decentralized/network.yaml" /decentralized/platform/{platform}: $ref: "./path/decentralized/platform.yaml" + /decentralized/metadata: + $ref: "./path/decentralized/metadata.yaml" /federated/tx/{id}: $ref: "./path/federated/tx.yaml" diff --git a/docs/generated.go b/docs/generated.go index cf18a90a6..062393caf 100644 --- a/docs/generated.go +++ b/docs/generated.go @@ -19,6 +19,7 @@ import ( "github.com/rss3-network/node/schema/worker/federated" "github.com/rss3-network/protocol-go/schema" activityx "github.com/rss3-network/protocol-go/schema/activity" + "github.com/rss3-network/protocol-go/schema/metadata" "github.com/rss3-network/protocol-go/schema/network" "github.com/rss3-network/protocol-go/schema/tag" ) @@ -59,6 +60,35 @@ type PostDecentralizedAccountsJSONBody struct { UntilTimestamp *uint64 `json:"until_timestamp,omitempty"` } +// PostDecentralizedMetadataJSONBody defines parameters for PostDecentralizedMetadata. +type PostDecentralizedMetadataJSONBody struct { + Accounts []string `json:"accounts,omitempty"` + + // ActionLimit Specify the number of actions within the activity to retrieve + ActionLimit *int `default:"10" json:"action_limit,omitempty" validate:"min=1,max=20"` + + // Cursor Specify the cursor used for pagination + Cursor *string `json:"cursor,omitempty"` + Direction *activityx.Direction `json:"direction,omitempty"` + + // Limit Specify the number of activities to retrieve + Limit *int `default:"100" json:"limit,omitempty" validate:"min=1,max=100"` + Metadata metadata.Metadata `json:"metadata,omitempty"` + Network *network.Network `json:"network,omitempty"` + Platform decentralized.Platform `json:"platform,omitempty"` + + // SinceTimestamp The timestamp of when the activity occurred. + SinceTimestamp *uint64 `json:"since_timestamp,omitempty"` + + // Status Retrieve activities based on success status + Status *bool `json:"success,omitempty"` + Tag tag.Tag `json:"tag,omitempty"` + Type schema.Type `json:"type,omitempty"` + + // UntilTimestamp The timestamp of when the activity occurred. + UntilTimestamp *uint64 `json:"until_timestamp,omitempty"` +} + // GetDecentralizedNetworkParams defines parameters for GetDecentralizedNetwork. type GetDecentralizedNetworkParams struct { // Limit Specify the number of activities to retrieve. @@ -316,6 +346,9 @@ type GetFederatedAccountParams struct { // PostDecentralizedAccountsJSONRequestBody defines body for PostDecentralizedAccounts for application/json ContentType. type PostDecentralizedAccountsJSONRequestBody PostDecentralizedAccountsJSONBody +// PostDecentralizedMetadataJSONRequestBody defines body for PostDecentralizedMetadata for application/json ContentType. +type PostDecentralizedMetadataJSONRequestBody PostDecentralizedMetadataJSONBody + // PostFederatedAccountsJSONRequestBody defines body for PostFederatedAccounts for application/json ContentType. type PostFederatedAccountsJSONRequestBody PostFederatedAccountsJSONBody @@ -324,6 +357,9 @@ type ServerInterface interface { // Batch Get Accounts Activities // (POST /decentralized/accounts) PostDecentralizedAccounts(ctx echo.Context) error + // Batch Get Activities By Metadata + // (POST /decentralized/metadata) + PostDecentralizedMetadata(ctx echo.Context) error // Get Network Activities // (GET /decentralized/network/{network}) GetDecentralizedNetwork(ctx echo.Context, network network.Network, params GetDecentralizedNetworkParams) error @@ -387,6 +423,17 @@ func (w *ServerInterfaceWrapper) PostDecentralizedAccounts(ctx echo.Context) err return err } +// PostDecentralizedMetadata converts echo context to params. +func (w *ServerInterfaceWrapper) PostDecentralizedMetadata(ctx echo.Context) error { + var err error + + ctx.Set(BearerAuthScopes, []string{}) + + // Invoke the callback with all the unmarshaled arguments + err = w.Handler.PostDecentralizedMetadata(ctx) + return err +} + // GetDecentralizedNetwork converts echo context to params. func (w *ServerInterfaceWrapper) GetDecentralizedNetwork(ctx echo.Context) error { var err error @@ -715,6 +762,7 @@ func RegisterHandlersWithBaseURL(router EchoRouter, si ServerInterface, baseURL } router.POST(baseURL+"/decentralized/accounts", wrapper.PostDecentralizedAccounts) + router.POST(baseURL+"/decentralized/metadata", wrapper.PostDecentralizedMetadata) router.GET(baseURL+"/decentralized/network/:network", wrapper.GetDecentralizedNetwork) router.GET(baseURL+"/decentralized/platform/:platform", wrapper.GetDecentralizedPlatform) router.GET(baseURL+"/decentralized/tx/:id", wrapper.GetDecentralizedTxID) @@ -736,175 +784,177 @@ func RegisterHandlersWithBaseURL(router EchoRouter, si ServerInterface, baseURL // Base64 encoded, gzipped, json marshaled Swagger object var swaggerSpec = []string{ - "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", } // GetSwagger returns the content of the embedded swagger specification file diff --git a/docs/path/decentralized/metadata.yaml b/docs/path/decentralized/metadata.yaml new file mode 100644 index 000000000..61b5d2571 --- /dev/null +++ b/docs/path/decentralized/metadata.yaml @@ -0,0 +1,17 @@ +post: + operationId: PostDecentralizedMetadata + summary: Batch Get Activities By Metadata + description: Retrieve a batch of activities associated with multiple specified metadata in the decentralized system. You can use various query parameters to filter and paginate the results, including limits on the number of activities and actions, timestamps, success status, direction, and more. + tags: + - Decentralized + security: + - bearerAuth: [ ] + requestBody: + $ref: "../../requestBody/BatchGetDecentralizedMetadataActivities.yaml" + responses: + '200': + $ref: "../../responses/DecentralizedActivitiesResponse.yaml" + '400': + $ref: "../../responses/BadRequest.yaml" + '500': + $ref: "../../responses/InternalError.yaml" diff --git a/docs/requestBody/BatchGetDecentralizedMetadataActivities.yaml b/docs/requestBody/BatchGetDecentralizedMetadataActivities.yaml new file mode 100644 index 000000000..db8f81322 --- /dev/null +++ b/docs/requestBody/BatchGetDecentralizedMetadataActivities.yaml @@ -0,0 +1,40 @@ +description: Request body for batch retrieving activities for specified metadata, tag and type is required +required: true +content: + application/json: + schema: + type: object + required: + properties: + accounts: + type: array + items: + $ref: "../schemas/DecentralizedAccount.yaml" + x-go-type-skip-optional-pointer: true + limit: + $ref: "../schemas/Limit.yaml" + action_limit: + $ref: "../schemas/ActionLimit.yaml" + cursor: + $ref: "../schemas/Cursor.yaml" + since_timestamp: + $ref: "../schemas/Timestamp.yaml" + until_timestamp: + $ref: "../schemas/Timestamp.yaml" + success: + $ref: "../schemas/Success.yaml" + direction: + $ref: "../schemas/ProtocolDirection.yaml" + network: + $ref: "../schemas/ProtocolNetwork.yaml" + tag: + $ref: "../schemas/ProtocolTag.yaml" + type: + $ref: "../schemas/ProtocolType.yaml" + platform: + $ref: "../schemas/DecentralizedPlatform.yaml" + metadata: + type: object + allOf: + - $ref: "../schemas/ProtocolMetadata.yaml" + x-go-type-skip-optional-pointer: true diff --git a/docs/schemas/DecentralizedPlatform.yaml b/docs/schemas/DecentralizedPlatform.yaml index 67f608e13..4568949d6 100644 --- a/docs/schemas/DecentralizedPlatform.yaml +++ b/docs/schemas/DecentralizedPlatform.yaml @@ -39,4 +39,4 @@ enum: x-go-type: decentralized.Platform x-go-type-skip-optional-pointer: true x-go-type-import: - path: github.com/rss3-network/node/schema/worker/decentralized \ No newline at end of file + path: github.com/rss3-network/node/schema/worker/decentralized diff --git a/docs/schemas/ProtocolMetadata.yaml b/docs/schemas/ProtocolMetadata.yaml index 16682d6b9..eff9c530d 100644 --- a/docs/schemas/ProtocolMetadata.yaml +++ b/docs/schemas/ProtocolMetadata.yaml @@ -3,6 +3,5 @@ type: object allOf: - $ref: "https://raw.githubusercontent.com/RSS3-Network/Protocol-Go/refs/heads/main/openapi/Metadata.yaml" - x-go-type: metadata.Metadata - x-go-type-skip-optional-pointer: true x-go-type-import: - path: github.com/rss3-network/protocol-go/schema/metadata \ No newline at end of file + path: github.com/rss3-network/protocol-go/schema/metadata diff --git a/docs/schemas/ProtocolTag.yaml b/docs/schemas/ProtocolTag.yaml index 03cfc451f..a33563516 100644 --- a/docs/schemas/ProtocolTag.yaml +++ b/docs/schemas/ProtocolTag.yaml @@ -1,2 +1,3 @@ allOf: - - $ref: "https://raw.githubusercontent.com/RSS3-Network/Protocol-Go/refs/heads/main/openapi/enum/Tag.yaml" \ No newline at end of file + - $ref: "https://raw.githubusercontent.com/RSS3-Network/Protocol-Go/refs/heads/main/openapi/enum/Tag.yaml" +x-go-type-skip-optional-pointer: true diff --git a/docs/schemas/ProtocolType.yaml b/docs/schemas/ProtocolType.yaml index a8f8be55c..6f7ae22ac 100644 --- a/docs/schemas/ProtocolType.yaml +++ b/docs/schemas/ProtocolType.yaml @@ -1,6 +1,6 @@ allOf: - $ref: "https://raw.githubusercontent.com/RSS3-Network/Protocol-Go/refs/heads/main/openapi/Type.yaml" - x-go-type: schema.Type - x-go-type-skip-optional-pointer: true x-go-type-import: path: github.com/rss3-network/protocol-go/schema +x-go-type-skip-optional-pointer: true diff --git a/internal/database/dialer/postgres/client_partitioned.go b/internal/database/dialer/postgres/client_partitioned.go index b81b1df75..4eb7595ad 100644 --- a/internal/database/dialer/postgres/client_partitioned.go +++ b/internal/database/dialer/postgres/client_partitioned.go @@ -9,6 +9,8 @@ import ( "sync" "time" + "github.com/rss3-network/protocol-go/schema/metadata" + "github.com/rss3-network/node/internal/database/dialer/postgres/table" "github.com/rss3-network/node/internal/database/model" activityx "github.com/rss3-network/protocol-go/schema/activity" @@ -787,7 +789,7 @@ func (c *client) buildFindIndexStatement(ctx context.Context, partitionedName st // buildFindIndexesStatement builds the query indexes statement. func (c *client) buildFindIndexesStatement(ctx context.Context, partition string, query model.ActivitiesQuery) *gorm.DB { - databaseStatement := c.database.WithContext(ctx).Table(partition) + databaseStatement := c.database.WithContext(ctx).Table(partition).Debug() if query.Distinct != nil && lo.FromPtr(query.Distinct) { databaseStatement = databaseStatement.Select("DISTINCT (id) id, timestamp, index, network") @@ -841,9 +843,35 @@ func (c *client) buildFindIndexesStatement(ctx context.Context, partition string databaseStatement = databaseStatement.Where("timestamp < ? OR (timestamp = ? AND index < ?)", time.Unix(int64(query.Cursor.Timestamp), 0), time.Unix(int64(query.Cursor.Timestamp), 0), query.Cursor.Index) } + if query.Metadata != nil { + databaseStatement = c.buildFindMetadataStatement(ctx, databaseStatement, query.Metadata) + } + return databaseStatement.Order("timestamp DESC, index DESC").Limit(query.Limit) } +// buildFindMetadataStatement builds the query metadata statement. +func (c *client) buildFindMetadataStatement(_ context.Context, databaseStatement *gorm.DB, meta metadata.Metadata) *gorm.DB { + statement := databaseStatement.Session(&gorm.Session{}) + + statement = statement.Joins("CROSS JOIN LATERAL jsonb_array_elements(actions::jsonb) AS action_element") + + switch data := meta.(type) { + case *metadata.ExchangeSwap: + if data.From.Address != nil { + statement = statement.Where("action_element -> 'metadata' -> 'from' ->> 'address' = ?", data.From.Address) + } + + if data.To.Address != nil { + statement = statement.Where("action_element -> 'metadata' -> 'to' ->> 'address' = ?", data.To.Address) + } + default: + return databaseStatement + } + + return statement +} + // buildActivitiesTableNames builds the activities table names. func (c *client) buildActivitiesTableNames(network network.Network, timestamp time.Time) string { return fmt.Sprintf("%s_%s_%d_q%d", (*table.Activity).TableName(nil), network, timestamp.Year(), int(timestamp.Month()-1)/3+1) diff --git a/internal/database/model/activity.go b/internal/database/model/activity.go index df87f55f3..9b4d0ecbb 100644 --- a/internal/database/model/activity.go +++ b/internal/database/model/activity.go @@ -3,6 +3,7 @@ package model import ( "github.com/rss3-network/protocol-go/schema" activityx "github.com/rss3-network/protocol-go/schema/activity" + "github.com/rss3-network/protocol-go/schema/metadata" "github.com/rss3-network/protocol-go/schema/network" "github.com/rss3-network/protocol-go/schema/tag" ) @@ -32,4 +33,5 @@ type ActivitiesQuery struct { RelatedActions *bool Limit int ActionLimit int + Metadata metadata.Metadata } diff --git a/internal/node/component/aggregator/compoent.go b/internal/node/component/aggregator/compoent.go index ba53930bb..c96696e60 100644 --- a/internal/node/component/aggregator/compoent.go +++ b/internal/node/component/aggregator/compoent.go @@ -27,6 +27,10 @@ func (c Component) PostDecentralizedAccounts(ctx echo.Context) error { return c.Decentralized.BatchGetAccountsActivities(ctx) } +func (c Component) PostDecentralizedMetadata(ctx echo.Context) error { + return c.Decentralized.BatchGetMetadataActivities(ctx) +} + func (c Component) GetDecentralizedNetwork(ctx echo.Context, network network.Network, params docs.GetDecentralizedNetworkParams) error { return c.Decentralized.GetNetworkActivities(ctx, network, params) } diff --git a/internal/node/component/decentralized/handler_metadata.go b/internal/node/component/decentralized/handler_metadata.go new file mode 100644 index 000000000..dc0239cb9 --- /dev/null +++ b/internal/node/component/decentralized/handler_metadata.go @@ -0,0 +1,113 @@ +package decentralized + +import ( + "encoding/json" + "fmt" + "github.com/rss3-network/protocol-go/schema/metadata" + "github.com/rss3-network/protocol-go/schema/tag" + "net/http" + "strconv" + + "github.com/creasty/defaults" + "github.com/ethereum/go-ethereum/common" + "github.com/labstack/echo/v4" + "github.com/rss3-network/node/common/http/response" + "github.com/rss3-network/node/docs" + "github.com/rss3-network/node/internal/database/model" + "github.com/rss3-network/protocol-go/schema" + "github.com/samber/lo" + "go.uber.org/zap" +) + +// BatchGetMetadataActivities returns the activities of specified decentralized metadata +func (c *Component) BatchGetMetadataActivities(ctx echo.Context) (err error) { + request := struct { + docs.PostDecentralizedMetadataJSONBody + RawType string `json:"type"` + RawMetadata json.RawMessage `json:"metadata"` + }{} + + if err = ctx.Bind(&request); err != nil { + return response.BadRequestError(ctx, err) + } + + if len(request.RawType) == 0 || request.Tag == tag.Unknown { + return response.BadRequestError(ctx, fmt.Errorf("empty tag or type")) + } + + request.Type, err = schema.ParseTypeFromString(request.Tag, request.RawType) + if err != nil { + return response.BadRequestError(ctx, fmt.Errorf("invalid type: %s", request.RawType)) + } + + request.Metadata, err = metadata.Unmarshal(request.Type, request.RawMetadata) + if err != nil { + return response.BadRequestError(ctx, fmt.Errorf("failed to unmarshal metadata: %w", err)) + } + + for i := range request.Accounts { + if common.IsHexAddress(request.Accounts[i]) { + request.Accounts[i] = common.HexToAddress(request.Accounts[i]).String() + } + } + + if err = defaults.Set(&request); err != nil { + return response.BadRequestError(ctx, err) + } + + if err = ctx.Validate(&request); err != nil { + return response.ValidationFailedError(ctx, err) + } + + go c.CollectTrace(ctx.Request().Context(), ctx.Request().RequestURI, strconv.Itoa(len(request.Accounts))) + + go c.CollectMetric(ctx.Request().Context(), ctx.Request().RequestURI, strconv.Itoa(len(request.Accounts))) + + addRecentRequest(ctx.Request().RequestURI) + + zap.L().Debug("processing batch get decentralized accounts activities request", + zap.Any("request", request)) + + cursor, err := c.getCursor(ctx.Request().Context(), request.Cursor) + if err != nil { + zap.L().Error("failed to get decentralized accounts activities cursor", + zap.String("cursor", lo.FromPtr(request.Cursor)), + zap.Error(err)) + + return response.BadRequestError(ctx, fmt.Errorf("invalid cursor: %w", err)) + } + + databaseRequest := model.ActivitiesQuery{ + Cursor: cursor, + StartTimestamp: request.SinceTimestamp, + EndTimestamp: request.UntilTimestamp, + Owners: lo.Uniq(request.Accounts), + Limit: lo.FromPtr(request.Limit), + ActionLimit: lo.FromPtr(request.ActionLimit), + Status: request.Status, + Direction: request.Direction, + Tags: []tag.Tag{request.Tag}, + Types: []schema.Type{request.Type}, + Platform: request.Platform.String(), + Metadata: request.Metadata, + } + + activities, last, err := c.getActivities(ctx.Request().Context(), databaseRequest) + if err != nil { + zap.L().Error("failed to get activities", + zap.Error(err)) + + return response.InternalError(ctx) + } + + zap.L().Info("successfully retrieved decentralized metadata activities", + zap.Int("accounts_count", len(request.Accounts)), + zap.Int("count", len(activities))) + + return ctx.JSON(http.StatusOK, ActivitiesResponse{ + Data: c.TransformActivities(ctx.Request().Context(), activities), + Meta: lo.Ternary(len(activities) < databaseRequest.Limit, nil, &MetaCursor{ + Cursor: last, + }), + }) +} diff --git a/internal/utils/utils.go b/internal/utils/utils.go index 710ffdf72..dfef61ac2 100644 --- a/internal/utils/utils.go +++ b/internal/utils/utils.go @@ -1,7 +1,9 @@ package utils import ( + "encoding/json" "fmt" + "github.com/rss3-network/protocol-go/schema/metadata" "math/big" "github.com/rss3-network/protocol-go/schema" @@ -47,3 +49,17 @@ func ParseTypes(types []string, tags []tag.Tag) ([]schema.Type, error) { return schemaTypes, nil } + +// ParseMetadata parses the metadata and returns the schema metadata +func ParseMetadata(metadataJson json.RawMessage, typex schema.Type) (metadata.Metadata, error) { + if len(metadataJson) == 0 { + return nil, nil + } + + value, err := metadata.Unmarshal(typex, metadataJson) + if err != nil { + return nil, err + } + + return value, nil +} From f119e8f1802a9d3d72eed5ae699d72e94052ff78 Mon Sep 17 00:00:00 2001 From: polebug Date: Wed, 18 Dec 2024 21:03:47 +0800 Subject: [PATCH 02/10] feat(database): add indexes for activities table --- .../20241218042140_add_activities_indexes.sql | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 internal/database/dialer/postgres/migration/20241218042140_add_activities_indexes.sql diff --git a/internal/database/dialer/postgres/migration/20241218042140_add_activities_indexes.sql b/internal/database/dialer/postgres/migration/20241218042140_add_activities_indexes.sql new file mode 100644 index 000000000..0839d5aa3 --- /dev/null +++ b/internal/database/dialer/postgres/migration/20241218042140_add_activities_indexes.sql @@ -0,0 +1,11 @@ +-- +goose Up +-- +goose StatementBegin +CREATE INDEX CONCURRENTLY IF NOT EXISTS activities_id_platform_index ON public.activities (platform); +CREATE INDEX CONCURRENTLY IF NOT EXISTS activities_id_tag_and_type_index ON public.activities (tag, type); +-- +goose StatementEnd + +-- +goose Down +-- +goose StatementBegin +DROP INDEX CONCURRENTLY IF EXISTS public.activities_id_platform_index; +DROP INDEX CONCURRENTLY IF EXISTS public.activities_id_tag_and_type_index; +-- +goose StatementEnd From 25bd30fb1217a23dac00f1388a791ca5ae0cf23f Mon Sep 17 00:00:00 2001 From: polebug Date: Wed, 18 Dec 2024 21:11:59 +0800 Subject: [PATCH 03/10] chore --- internal/database/dialer/postgres/client_partitioned.go | 3 +-- internal/node/component/decentralized/handler_metadata.go | 4 ++-- internal/utils/utils.go | 8 ++++---- 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/internal/database/dialer/postgres/client_partitioned.go b/internal/database/dialer/postgres/client_partitioned.go index 4eb7595ad..9b2b37b55 100644 --- a/internal/database/dialer/postgres/client_partitioned.go +++ b/internal/database/dialer/postgres/client_partitioned.go @@ -9,11 +9,10 @@ import ( "sync" "time" - "github.com/rss3-network/protocol-go/schema/metadata" - "github.com/rss3-network/node/internal/database/dialer/postgres/table" "github.com/rss3-network/node/internal/database/model" activityx "github.com/rss3-network/protocol-go/schema/activity" + "github.com/rss3-network/protocol-go/schema/metadata" "github.com/rss3-network/protocol-go/schema/network" "github.com/samber/lo" "github.com/sourcegraph/conc/pool" diff --git a/internal/node/component/decentralized/handler_metadata.go b/internal/node/component/decentralized/handler_metadata.go index dc0239cb9..290ac31e3 100644 --- a/internal/node/component/decentralized/handler_metadata.go +++ b/internal/node/component/decentralized/handler_metadata.go @@ -3,8 +3,6 @@ package decentralized import ( "encoding/json" "fmt" - "github.com/rss3-network/protocol-go/schema/metadata" - "github.com/rss3-network/protocol-go/schema/tag" "net/http" "strconv" @@ -15,6 +13,8 @@ import ( "github.com/rss3-network/node/docs" "github.com/rss3-network/node/internal/database/model" "github.com/rss3-network/protocol-go/schema" + "github.com/rss3-network/protocol-go/schema/metadata" + "github.com/rss3-network/protocol-go/schema/tag" "github.com/samber/lo" "go.uber.org/zap" ) diff --git a/internal/utils/utils.go b/internal/utils/utils.go index dfef61ac2..93ab18015 100644 --- a/internal/utils/utils.go +++ b/internal/utils/utils.go @@ -3,10 +3,10 @@ package utils import ( "encoding/json" "fmt" - "github.com/rss3-network/protocol-go/schema/metadata" "math/big" "github.com/rss3-network/protocol-go/schema" + "github.com/rss3-network/protocol-go/schema/metadata" "github.com/rss3-network/protocol-go/schema/tag" ) @@ -51,12 +51,12 @@ func ParseTypes(types []string, tags []tag.Tag) ([]schema.Type, error) { } // ParseMetadata parses the metadata and returns the schema metadata -func ParseMetadata(metadataJson json.RawMessage, typex schema.Type) (metadata.Metadata, error) { - if len(metadataJson) == 0 { +func ParseMetadata(metadataJSON json.RawMessage, typex schema.Type) (metadata.Metadata, error) { + if len(metadataJSON) == 0 { return nil, nil } - value, err := metadata.Unmarshal(typex, metadataJson) + value, err := metadata.Unmarshal(typex, metadataJSON) if err != nil { return nil, err } From c874fd14272ee0da3c41c806dde9c8eaa7bd77ff Mon Sep 17 00:00:00 2001 From: polebug Date: Thu, 19 Dec 2024 17:50:19 +0800 Subject: [PATCH 04/10] feat(database): support for querying activity metadata --- docs/generated.go | 360 +++++++++--------- ...tchGetDecentralizedMetadataActivities.yaml | 26 +- docs/schemas/DecentralizedPlatform.yaml | 1 - docs/schemas/ProtocolNetwork.yaml | 3 +- docs/schemas/ProtocolTag.yaml | 2 +- docs/schemas/ProtocolType.yaml | 2 +- internal/database/client.go | 1 + internal/database/dialer/postgres/client.go | 9 + .../dialer/postgres/client_partitioned.go | 213 ++++++++++- internal/database/model/activity.go | 17 +- internal/node/component/decentralized/data.go | 14 + .../decentralized/handler_metadata.go | 23 +- 12 files changed, 463 insertions(+), 208 deletions(-) diff --git a/docs/generated.go b/docs/generated.go index 062393caf..dda0002c7 100644 --- a/docs/generated.go +++ b/docs/generated.go @@ -68,22 +68,21 @@ type PostDecentralizedMetadataJSONBody struct { ActionLimit *int `default:"10" json:"action_limit,omitempty" validate:"min=1,max=20"` // Cursor Specify the cursor used for pagination - Cursor *string `json:"cursor,omitempty"` - Direction *activityx.Direction `json:"direction,omitempty"` + Cursor *string `json:"cursor,omitempty"` // Limit Specify the number of activities to retrieve - Limit *int `default:"100" json:"limit,omitempty" validate:"min=1,max=100"` - Metadata metadata.Metadata `json:"metadata,omitempty"` - Network *network.Network `json:"network,omitempty"` - Platform decentralized.Platform `json:"platform,omitempty"` + Limit *int `default:"100" json:"limit,omitempty" validate:"min=1,max=100"` + Metadata *metadata.Metadata `json:"metadata,omitempty" validate:"required"` + Network *network.Network `json:"network,omitempty" validate:"required"` + Platform *decentralized.Platform `json:"platform,omitempty"` // SinceTimestamp The timestamp of when the activity occurred. SinceTimestamp *uint64 `json:"since_timestamp,omitempty"` // Status Retrieve activities based on success status - Status *bool `json:"success,omitempty"` - Tag tag.Tag `json:"tag,omitempty"` - Type schema.Type `json:"type,omitempty"` + Status *bool `json:"success,omitempty"` + Tag *tag.Tag `json:"tag,omitempty" validate:"required"` + Type *schema.Type `json:"type,omitempty" validate:"required"` // UntilTimestamp The timestamp of when the activity occurred. UntilTimestamp *uint64 `json:"until_timestamp,omitempty"` @@ -784,177 +783,178 @@ func RegisterHandlersWithBaseURL(router EchoRouter, si ServerInterface, baseURL // Base64 encoded, gzipped, json marshaled Swagger object var swaggerSpec = []string{ - "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", + "/5e+BJ6WJgEA", } // GetSwagger returns the content of the embedded swagger specification file diff --git a/docs/requestBody/BatchGetDecentralizedMetadataActivities.yaml b/docs/requestBody/BatchGetDecentralizedMetadataActivities.yaml index db8f81322..10610f71b 100644 --- a/docs/requestBody/BatchGetDecentralizedMetadataActivities.yaml +++ b/docs/requestBody/BatchGetDecentralizedMetadataActivities.yaml @@ -1,10 +1,9 @@ -description: Request body for batch retrieving activities for specified metadata, tag and type is required +description: Request body for batch retrieving activities for specified metadata, network and tag and type is required required: true content: application/json: schema: type: object - required: properties: accounts: type: array @@ -23,18 +22,29 @@ content: $ref: "../schemas/Timestamp.yaml" success: $ref: "../schemas/Success.yaml" - direction: - $ref: "../schemas/ProtocolDirection.yaml" network: - $ref: "../schemas/ProtocolNetwork.yaml" + type: object + allOf: + - $ref: "../schemas/ProtocolNetwork.yaml" + x-oapi-codegen-extra-tags: + validate: "required" tag: - $ref: "../schemas/ProtocolTag.yaml" + type: object + allOf: + - $ref: "../schemas/ProtocolTag.yaml" + x-oapi-codegen-extra-tags: + validate: "required" type: - $ref: "../schemas/ProtocolType.yaml" + type: string + allOf: + - $ref: "../schemas/ProtocolType.yaml" + x-oapi-codegen-extra-tags: + validate: "required" platform: $ref: "../schemas/DecentralizedPlatform.yaml" metadata: type: object allOf: - $ref: "../schemas/ProtocolMetadata.yaml" - x-go-type-skip-optional-pointer: true + x-oapi-codegen-extra-tags: + validate: "required" diff --git a/docs/schemas/DecentralizedPlatform.yaml b/docs/schemas/DecentralizedPlatform.yaml index 4568949d6..47375d30c 100644 --- a/docs/schemas/DecentralizedPlatform.yaml +++ b/docs/schemas/DecentralizedPlatform.yaml @@ -37,6 +37,5 @@ enum: - VSL - Zerion x-go-type: decentralized.Platform -x-go-type-skip-optional-pointer: true x-go-type-import: path: github.com/rss3-network/node/schema/worker/decentralized diff --git a/docs/schemas/ProtocolNetwork.yaml b/docs/schemas/ProtocolNetwork.yaml index db868505a..d943f656f 100644 --- a/docs/schemas/ProtocolNetwork.yaml +++ b/docs/schemas/ProtocolNetwork.yaml @@ -1,3 +1,4 @@ allOf: - $ref: "https://raw.githubusercontent.com/RSS3-Network/Protocol-Go/refs/heads/main/openapi/enum/Network.yaml" -# - x-go-type: string \ No newline at end of file +# - x-go-type: string +x-go-type-skip-optional-pointer: true \ No newline at end of file diff --git a/docs/schemas/ProtocolTag.yaml b/docs/schemas/ProtocolTag.yaml index a33563516..95bf67168 100644 --- a/docs/schemas/ProtocolTag.yaml +++ b/docs/schemas/ProtocolTag.yaml @@ -1,3 +1,3 @@ allOf: - $ref: "https://raw.githubusercontent.com/RSS3-Network/Protocol-Go/refs/heads/main/openapi/enum/Tag.yaml" -x-go-type-skip-optional-pointer: true +x-go-type-skip-optional-pointer: true \ No newline at end of file diff --git a/docs/schemas/ProtocolType.yaml b/docs/schemas/ProtocolType.yaml index 6f7ae22ac..98d061371 100644 --- a/docs/schemas/ProtocolType.yaml +++ b/docs/schemas/ProtocolType.yaml @@ -3,4 +3,4 @@ allOf: - x-go-type: schema.Type x-go-type-import: path: github.com/rss3-network/protocol-go/schema -x-go-type-skip-optional-pointer: true +x-go-type-skip-optional-pointer: true \ No newline at end of file diff --git a/internal/database/client.go b/internal/database/client.go index e5b5fe78e..457798180 100644 --- a/internal/database/client.go +++ b/internal/database/client.go @@ -31,6 +31,7 @@ type Client interface { SaveActivities(ctx context.Context, activities []*activityx.Activity, lowPriority bool) error FindActivity(ctx context.Context, query model.ActivityQuery) (*activityx.Activity, *int, error) FindActivities(ctx context.Context, query model.ActivitiesQuery) ([]*activityx.Activity, error) + FindActivitiesMetadata(ctx context.Context, query model.ActivitiesMetadataQuery) ([]*activityx.Activity, error) DeleteExpiredActivities(ctx context.Context, network network.Network, timestamp time.Time) error } diff --git a/internal/database/dialer/postgres/client.go b/internal/database/dialer/postgres/client.go index 8a7e78f96..513d96013 100644 --- a/internal/database/dialer/postgres/client.go +++ b/internal/database/dialer/postgres/client.go @@ -331,6 +331,15 @@ func (c *client) FindActivities(ctx context.Context, query model.ActivitiesQuery return nil, fmt.Errorf("not implemented") } +// FindActivitiesMetadata finds Activities by metadata. +func (c *client) FindActivitiesMetadata(ctx context.Context, query model.ActivitiesMetadataQuery) ([]*activityx.Activity, error) { + if c.partition { + return c.findActivitiesMetadataPartitioned(ctx, query) + } + + return nil, fmt.Errorf("not implemented") +} + // DeleteExpiredActivities deletes expired activities. func (c *client) DeleteExpiredActivities(ctx context.Context, network networkx.Network, timestamp time.Time) error { if c.partition { diff --git a/internal/database/dialer/postgres/client_partitioned.go b/internal/database/dialer/postgres/client_partitioned.go index 9b2b37b55..ba8cf99d9 100644 --- a/internal/database/dialer/postgres/client_partitioned.go +++ b/internal/database/dialer/postgres/client_partitioned.go @@ -24,6 +24,8 @@ import ( var indexesTables sync.Map +var activitiesTables sync.Map + // createPartitionTable creates a partition table. func (c *client) createPartitionTable(ctx context.Context, name, template string) error { statement := fmt.Sprintf(`CREATE TABLE IF NOT EXISTS "%s" (LIKE "%s" INCLUDING ALL);`, name, template) @@ -40,6 +42,10 @@ func (c *client) createPartitionTable(ctx context.Context, name, template string indexesTables.Store(name, struct{}{}) } + if template == (*table.Activity).TableName(nil) { + activitiesTables.Store(name, struct{}{}) + } + zap.L().Debug("successfully created partition table", zap.String("statement", statement)) @@ -102,6 +108,49 @@ func (c *client) findIndexesPartitionTables(_ context.Context, index table.Index return partitionedNames } +// findActivitiesPartitionTables finds activities partition tables. +func (c *client) findActivitiesPartitionTables(_ context.Context, network *network.Network, timestamp time.Time) []string { + if network == nil { + return nil + } + + zap.L().Debug("finding activities partition tables", + zap.String("network", network.String()), + zap.Time("timestamp", timestamp)) + + partitionedNames := make([]string, 0) + + for i := 0; i <= 4; i++ { + activityTable := table.Activity{ + Network: lo.FromPtr(network), + Timestamp: timestamp, + } + + if timestamp.Unix() < time.Now().AddDate(-1, 0, 0).Unix() { + zap.L().Debug("timestamp is older than 1 year, stopping search", + zap.Time("timestamp", timestamp)) + break + } + + if _, exists := activitiesTables.Load(activityTable.PartitionName(nil)); exists { + partitionedNames = append(partitionedNames, activityTable.PartitionName(nil)) + } + + year := timestamp.Year() + month := timestamp.Month() + + timestamp = time.Date(lo.Ternary(month < 3, year-1, year), lo.Ternary(month < 3, month+9, month-3), timestamp.Day(), 23, 59, 59, 1e9-1, time.Local) + + zap.L().Debug("updated timestamp for next iteration", + zap.Time("new_timestamp", timestamp)) + } + + zap.L().Debug("completed finding activities partition tables", + zap.Any("found_tables", partitionedNames)) + + return partitionedNames +} + // loadIndexesPartitionTables loads indexes partition tables. func (c *client) loadIndexesPartitionTables(ctx context.Context) { result := make([]string, 0) @@ -115,6 +164,19 @@ func (c *client) loadIndexesPartitionTables(ctx context.Context) { } } +// loadActivitiesPartitionTables loads activities partition tables. +func (c *client) loadActivitiesPartitionTables(ctx context.Context) { + result := make([]string, 0) + + if err := c.database.WithContext(ctx).Table("pg_tables").Where("tablename LIKE ?", fmt.Sprintf("%s_%%", (*table.Activity).TableName(nil))).Pluck("tablename", &result).Error; err != nil { + zap.L().Error("failed to load activities partition tables", zap.Error(err)) + } + + for _, tableName := range result { + activitiesTables.Store(tableName, struct{}{}) + } +} + // saveActivitiesPartitioned saves Activities in partitioned tables. func (c *client) saveActivitiesPartitioned(ctx context.Context, activities []*activityx.Activity, lowPriority bool) error { zap.L().Debug("starting to save activities in partitioned tables", @@ -261,6 +323,114 @@ func (c *client) findActivityPartitioned(ctx context.Context, query model.Activi return result, lo.ToPtr(int(page)), nil } +// findActivitiesMetadataPartitioned finds activities metadata. +func (c *client) findActivitiesMetadataPartitioned(ctx context.Context, query model.ActivitiesMetadataQuery) ([]*activityx.Activity, error) { + timestamp := time.Now() + + if query.EndTimestamp != nil && *query.EndTimestamp > 0 && *query.EndTimestamp < uint64(time.Now().Unix()) { + timestamp = time.Unix(int64(lo.FromPtr(query.EndTimestamp)), 0) + } + + if query.Cursor != nil && query.Cursor.Timestamp < uint64(timestamp.Unix()) { + timestamp = time.Unix(int64(query.Cursor.Timestamp), 0) + } + + partitionedNames := c.findActivitiesPartitionTables(ctx, query.Network, timestamp) + + if len(partitionedNames) == 0 { + return nil, nil + } + + ctx, cancel := context.WithCancel(ctx) + errorGroup, errorContext := errgroup.WithContext(ctx) + activities := make([]table.Activities, len(partitionedNames)) + resultChan := make(chan int, len(partitionedNames)) + errorChan := make(chan error) + stopChan := make(chan struct{}) + + var mutex sync.RWMutex + + for partitionedIndex, partitionedName := range partitionedNames { + partitionedIndex, partitionedName := partitionedIndex, partitionedName + + errorGroup.Go(func() error { + var result table.Activities + + databaseStatement := c.buildFindActivitiesStatement(errorContext, partitionedName, query) + + if err := databaseStatement.Find(&result).Error; err != nil { + return fmt.Errorf("failed to find activities metadata: %w", err) + } + + mutex.Lock() + activities[partitionedIndex] = result + mutex.Unlock() + + select { + case <-stopChan: + return nil + case resultChan <- partitionedIndex: + return nil + } + }) + } + + go func() { + defer close(errorChan) + + errorChan <- errorGroup.Wait() + }() + + defer func() { + cancel() + }() + + for { + select { + case err := <-errorChan: + if err != nil { + return nil, fmt.Errorf("failed to wait result: %w", err) + } + case <-resultChan: + result := make(table.Activities, 0, query.Limit) + flag := true + + mutex.RLock() + + for _, data := range activities { + data := data + + if data == nil { + flag = false + break + } + + result = append(result, data...) + + if len(result) >= query.Limit { + zap.L().Debug("found indexes up to limit", + zap.Int("count", query.Limit)) + close(stopChan) + mutex.RUnlock() + + data := result[:query.Limit] + + return data.Export() + } + } + + mutex.RUnlock() + + if flag { + zap.L().Debug("successfully found all indexes", + zap.Int("count", len(result))) + + return result.Export() + } + } + } +} + // findActivitiesPartitioned finds activities. func (c *client) findActivitiesPartitioned(ctx context.Context, query model.ActivitiesQuery) ([]*activityx.Activity, error) { zap.L().Debug("finding activities in partitioned tables", @@ -788,7 +958,7 @@ func (c *client) buildFindIndexStatement(ctx context.Context, partitionedName st // buildFindIndexesStatement builds the query indexes statement. func (c *client) buildFindIndexesStatement(ctx context.Context, partition string, query model.ActivitiesQuery) *gorm.DB { - databaseStatement := c.database.WithContext(ctx).Table(partition).Debug() + databaseStatement := c.database.WithContext(ctx).Table(partition) if query.Distinct != nil && lo.FromPtr(query.Distinct) { databaseStatement = databaseStatement.Select("DISTINCT (id) id, timestamp, index, network") @@ -842,15 +1012,50 @@ func (c *client) buildFindIndexesStatement(ctx context.Context, partition string databaseStatement = databaseStatement.Where("timestamp < ? OR (timestamp = ? AND index < ?)", time.Unix(int64(query.Cursor.Timestamp), 0), time.Unix(int64(query.Cursor.Timestamp), 0), query.Cursor.Index) } + return databaseStatement.Order("timestamp DESC, index DESC").Limit(query.Limit) +} + +// buildFindActivitiesStatement builds the query activities statement. +func (c *client) buildFindActivitiesStatement(ctx context.Context, partitionedName string, query model.ActivitiesMetadataQuery) *gorm.DB { + databaseStatement := c.database.WithContext(ctx).Table(partitionedName) + + if query.Platform != nil { + databaseStatement = databaseStatement.Where("platform = ?", query.Platform) + } + + if query.Tag != nil { + databaseStatement = databaseStatement.Where("tag = ?", query.Tag) + } + + if query.Type != nil { + databaseStatement = databaseStatement.Where("type = ?", query.Type) + } + + if query.StartTimestamp != nil && *query.StartTimestamp > 0 { + databaseStatement = databaseStatement.Where("timestamp >= ?", time.Unix(int64(*query.StartTimestamp), 0)) + } + + if query.EndTimestamp != nil && *query.EndTimestamp > 0 { + databaseStatement = databaseStatement.Where("timestamp <= ?", time.Unix(int64(*query.EndTimestamp), 0)) + } + + if query.Accounts != nil { + databaseStatement = databaseStatement.Where("from IN (?) OR to IN (?)", query.Accounts, query.Accounts) + } + + if query.Cursor != nil && query.Cursor.Timestamp > 0 { + databaseStatement = databaseStatement.Where("timestamp < ? OR (timestamp = ? AND index < ?)", time.Unix(int64(query.Cursor.Timestamp), 0), time.Unix(int64(query.Cursor.Timestamp), 0), query.Cursor.Index) + } + if query.Metadata != nil { - databaseStatement = c.buildFindMetadataStatement(ctx, databaseStatement, query.Metadata) + databaseStatement = c.buildFindActivitiesMetadataStatement(ctx, databaseStatement, lo.FromPtr(query.Metadata)) } return databaseStatement.Order("timestamp DESC, index DESC").Limit(query.Limit) } -// buildFindMetadataStatement builds the query metadata statement. -func (c *client) buildFindMetadataStatement(_ context.Context, databaseStatement *gorm.DB, meta metadata.Metadata) *gorm.DB { +// buildFindActivitiesMetadataStatement builds the query metadata statement. +func (c *client) buildFindActivitiesMetadataStatement(_ context.Context, databaseStatement *gorm.DB, meta metadata.Metadata) *gorm.DB { statement := databaseStatement.Session(&gorm.Session{}) statement = statement.Joins("CROSS JOIN LATERAL jsonb_array_elements(actions::jsonb) AS action_element") diff --git a/internal/database/model/activity.go b/internal/database/model/activity.go index 9b4d0ecbb..9f2093889 100644 --- a/internal/database/model/activity.go +++ b/internal/database/model/activity.go @@ -1,6 +1,7 @@ package model import ( + "github.com/rss3-network/node/schema/worker/decentralized" "github.com/rss3-network/protocol-go/schema" activityx "github.com/rss3-network/protocol-go/schema/activity" "github.com/rss3-network/protocol-go/schema/metadata" @@ -33,5 +34,19 @@ type ActivitiesQuery struct { RelatedActions *bool Limit int ActionLimit int - Metadata metadata.Metadata +} + +type ActivitiesMetadataQuery struct { + Network *network.Network + Platform *decentralized.Platform + Tag *tag.Tag + Type *schema.Type + Accounts []string + Cursor *activityx.Activity + Status *bool + StartTimestamp *uint64 + EndTimestamp *uint64 + Limit int + ActionLimit int + Metadata *metadata.Metadata } diff --git a/internal/node/component/decentralized/data.go b/internal/node/component/decentralized/data.go index ef07c68e5..e068058a9 100644 --- a/internal/node/component/decentralized/data.go +++ b/internal/node/component/decentralized/data.go @@ -29,6 +29,20 @@ func (c *Component) getActivities(ctx context.Context, request model.ActivitiesQ return nil, "", nil } +func (c *Component) getActivitiesMetadata(ctx context.Context, request model.ActivitiesMetadataQuery) ([]*activityx.Activity, string, error) { + activities, err := c.databaseClient.FindActivitiesMetadata(ctx, request) + if err != nil { + return nil, "", fmt.Errorf("failed to find activities: %w", err) + } + + last, exist := lo.Last(activities) + if exist { + return activities, c.transformCursor(ctx, last), nil + } + + return nil, "", nil +} + func (c *Component) getCursor(ctx context.Context, cursor *string) (*activityx.Activity, error) { if cursor == nil { return nil, nil diff --git a/internal/node/component/decentralized/handler_metadata.go b/internal/node/component/decentralized/handler_metadata.go index 290ac31e3..0d3195298 100644 --- a/internal/node/component/decentralized/handler_metadata.go +++ b/internal/node/component/decentralized/handler_metadata.go @@ -14,7 +14,6 @@ import ( "github.com/rss3-network/node/internal/database/model" "github.com/rss3-network/protocol-go/schema" "github.com/rss3-network/protocol-go/schema/metadata" - "github.com/rss3-network/protocol-go/schema/tag" "github.com/samber/lo" "go.uber.org/zap" ) @@ -31,20 +30,24 @@ func (c *Component) BatchGetMetadataActivities(ctx echo.Context) (err error) { return response.BadRequestError(ctx, err) } - if len(request.RawType) == 0 || request.Tag == tag.Unknown { + if len(request.RawType) == 0 || request.Tag == nil { return response.BadRequestError(ctx, fmt.Errorf("empty tag or type")) } - request.Type, err = schema.ParseTypeFromString(request.Tag, request.RawType) + typex, err := schema.ParseTypeFromString(lo.FromPtr(request.Tag), request.RawType) if err != nil { return response.BadRequestError(ctx, fmt.Errorf("invalid type: %s", request.RawType)) } - request.Metadata, err = metadata.Unmarshal(request.Type, request.RawMetadata) + request.Type = lo.ToPtr(typex) + + meta, err := metadata.Unmarshal(typex, request.RawMetadata) if err != nil { return response.BadRequestError(ctx, fmt.Errorf("failed to unmarshal metadata: %w", err)) } + request.Metadata = lo.ToPtr(meta) + for i := range request.Accounts { if common.IsHexAddress(request.Accounts[i]) { request.Accounts[i] = common.HexToAddress(request.Accounts[i]).String() @@ -77,22 +80,20 @@ func (c *Component) BatchGetMetadataActivities(ctx echo.Context) (err error) { return response.BadRequestError(ctx, fmt.Errorf("invalid cursor: %w", err)) } - databaseRequest := model.ActivitiesQuery{ + databaseRequest := model.ActivitiesMetadataQuery{ Cursor: cursor, StartTimestamp: request.SinceTimestamp, EndTimestamp: request.UntilTimestamp, - Owners: lo.Uniq(request.Accounts), Limit: lo.FromPtr(request.Limit), ActionLimit: lo.FromPtr(request.ActionLimit), + Accounts: request.Accounts, + Platform: request.Platform, Status: request.Status, - Direction: request.Direction, - Tags: []tag.Tag{request.Tag}, - Types: []schema.Type{request.Type}, - Platform: request.Platform.String(), + Network: request.Network, Metadata: request.Metadata, } - activities, last, err := c.getActivities(ctx.Request().Context(), databaseRequest) + activities, last, err := c.getActivitiesMetadata(ctx.Request().Context(), databaseRequest) if err != nil { zap.L().Error("failed to get activities", zap.Error(err)) From abdbe95ebb1ac1d87501ee6c496231696d23d34c Mon Sep 17 00:00:00 2001 From: polebug Date: Thu, 19 Dec 2024 18:06:14 +0800 Subject: [PATCH 05/10] chore --- internal/database/dialer/postgres/client.go | 2 +- .../database/dialer/postgres/client_partitioned.go | 11 +++-------- .../20241218042140_add_activities_indexes.sql | 8 ++++---- .../component/decentralized/transformer_activity.go | 2 +- 4 files changed, 9 insertions(+), 14 deletions(-) diff --git a/internal/database/dialer/postgres/client.go b/internal/database/dialer/postgres/client.go index 513d96013..3821bc629 100644 --- a/internal/database/dialer/postgres/client.go +++ b/internal/database/dialer/postgres/client.go @@ -582,7 +582,7 @@ func Dial(ctx context.Context, dataSourceName string, partition bool) (database. } if instance.partition { - instance.loadIndexesPartitionTables(ctx) + instance.loadPartitionTables(ctx) } return &instance, nil diff --git a/internal/database/dialer/postgres/client_partitioned.go b/internal/database/dialer/postgres/client_partitioned.go index ba8cf99d9..7a59e60cd 100644 --- a/internal/database/dialer/postgres/client_partitioned.go +++ b/internal/database/dialer/postgres/client_partitioned.go @@ -151,8 +151,8 @@ func (c *client) findActivitiesPartitionTables(_ context.Context, network *netwo return partitionedNames } -// loadIndexesPartitionTables loads indexes partition tables. -func (c *client) loadIndexesPartitionTables(ctx context.Context) { +// loadPartitionTables loads partition tables. +func (c *client) loadPartitionTables(ctx context.Context) { result := make([]string, 0) if err := c.database.WithContext(ctx).Table("pg_tables").Where("tablename LIKE ?", fmt.Sprintf("%s_%%", (*table.Index).TableName(nil))).Pluck("tablename", &result).Error; err != nil { @@ -162,11 +162,6 @@ func (c *client) loadIndexesPartitionTables(ctx context.Context) { for _, tableName := range result { indexesTables.Store(tableName, struct{}{}) } -} - -// loadActivitiesPartitionTables loads activities partition tables. -func (c *client) loadActivitiesPartitionTables(ctx context.Context) { - result := make([]string, 0) if err := c.database.WithContext(ctx).Table("pg_tables").Where("tablename LIKE ?", fmt.Sprintf("%s_%%", (*table.Activity).TableName(nil))).Pluck("tablename", &result).Error; err != nil { zap.L().Error("failed to load activities partition tables", zap.Error(err)) @@ -1017,7 +1012,7 @@ func (c *client) buildFindIndexesStatement(ctx context.Context, partition string // buildFindActivitiesStatement builds the query activities statement. func (c *client) buildFindActivitiesStatement(ctx context.Context, partitionedName string, query model.ActivitiesMetadataQuery) *gorm.DB { - databaseStatement := c.database.WithContext(ctx).Table(partitionedName) + databaseStatement := c.database.WithContext(ctx).Table(partitionedName).Debug() if query.Platform != nil { databaseStatement = databaseStatement.Where("platform = ?", query.Platform) diff --git a/internal/database/dialer/postgres/migration/20241218042140_add_activities_indexes.sql b/internal/database/dialer/postgres/migration/20241218042140_add_activities_indexes.sql index 0839d5aa3..a38518066 100644 --- a/internal/database/dialer/postgres/migration/20241218042140_add_activities_indexes.sql +++ b/internal/database/dialer/postgres/migration/20241218042140_add_activities_indexes.sql @@ -1,11 +1,11 @@ -- +goose Up -- +goose StatementBegin -CREATE INDEX CONCURRENTLY IF NOT EXISTS activities_id_platform_index ON public.activities (platform); -CREATE INDEX CONCURRENTLY IF NOT EXISTS activities_id_tag_and_type_index ON public.activities (tag, type); +CREATE INDEX IF NOT EXISTS activities_id_platform_index ON public.activities (platform); +CREATE INDEX IF NOT EXISTS activities_id_tag_and_type_index ON public.activities (tag, type); -- +goose StatementEnd -- +goose Down -- +goose StatementBegin -DROP INDEX CONCURRENTLY IF EXISTS public.activities_id_platform_index; -DROP INDEX CONCURRENTLY IF EXISTS public.activities_id_tag_and_type_index; +DROP INDEX IF EXISTS public.activities_id_platform_index; +DROP INDEX IF EXISTS public.activities_id_tag_and_type_index; -- +goose StatementEnd diff --git a/internal/node/component/decentralized/transformer_activity.go b/internal/node/component/decentralized/transformer_activity.go index 62a64f18a..14d8e692b 100644 --- a/internal/node/component/decentralized/transformer_activity.go +++ b/internal/node/component/decentralized/transformer_activity.go @@ -32,7 +32,7 @@ func (c *Component) TransformActivity(ctx context.Context, activity *activityx.A case tag.Social: *activity.Actions[index], err = c.TransformSocialType(ctx, activity.Network, activity.Platform, *actionPtr) default: - zap.L().Debug("unknown action tag, keeping original action", + zap.L().Debug("keeping original action", zap.String("id", activity.ID), zap.String("tag", action.Tag.String())) From 4883af40e0b78fec6a0a2a9e00faa3ae8fe1c433 Mon Sep 17 00:00:00 2001 From: polebug Date: Thu, 19 Dec 2024 11:46:57 +0800 Subject: [PATCH 06/10] chore --- internal/node/component/aggregator/{compoent.go => component.go} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename internal/node/component/aggregator/{compoent.go => component.go} (100%) diff --git a/internal/node/component/aggregator/compoent.go b/internal/node/component/aggregator/component.go similarity index 100% rename from internal/node/component/aggregator/compoent.go rename to internal/node/component/aggregator/component.go From b3028f59270f588452c879a7a7e70d64a83460fa Mon Sep 17 00:00:00 2001 From: polebug Date: Fri, 20 Dec 2024 14:58:43 +0800 Subject: [PATCH 07/10] go mod --- go.mod | 4 +++- go.sum | 30 ++++++++++++++++++++++++++++++ 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/go.mod b/go.mod index d42c04544..c9cb32c79 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/rss3-network/node -go 1.22.4 +go 1.22.7 replace ( github.com/chenzhuoyu/iasm => github.com/cloudwego/iasm v0.2.0 @@ -109,6 +109,7 @@ require ( github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/deckarep/golang-set/v2 v2.3.1 // indirect github.com/distribution/reference v0.6.0 // indirect + github.com/dmarkham/enumer v1.5.10 // indirect github.com/docker/cli v26.1.1+incompatible // indirect github.com/docker/docker v26.1.3+incompatible // indirect github.com/docker/go-connections v0.5.0 // indirect @@ -116,6 +117,7 @@ require ( github.com/dprotaso/go-yit v0.0.0-20220510233725-9ba8df137936 // indirect github.com/ethereum/c-kzg-4844 v0.4.0 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect + github.com/fjl/gencodec v0.0.0-20230517082657-f9840df7b83e // indirect github.com/fsnotify/fsnotify v1.7.0 // indirect github.com/getsentry/sentry-go v0.27.0 // indirect github.com/go-logr/logr v1.4.2 // indirect diff --git a/go.sum b/go.sum index 6a25a6a86..ea4a8e4a8 100644 --- a/go.sum +++ b/go.sum @@ -23,13 +23,21 @@ github.com/VictoriaMetrics/fastcache v1.12.1 h1:i0mICQuojGDL3KblA7wUNlY5lOK6a4bw github.com/VictoriaMetrics/fastcache v1.12.1/go.mod h1:tX04vaqcNoQeGLD+ra5pU5sWkuxnzWhEzLwhP9w653o= github.com/adrianbrad/psqldocker v1.2.1 h1:bvsRmbotpA89ruqGGzzaAZUBtDaIk98gO+JMBNVSZlI= github.com/adrianbrad/psqldocker v1.2.1/go.mod h1:LbCnIy60YO6IRJYrF1r+eafKUgU9UnkSFx0gT8UiaUs= +github.com/agnivade/levenshtein v1.1.1 h1:QY8M92nrzkmr798gCo3kmMyqXFzdQVpxLlGPRBij0P8= +github.com/agnivade/levenshtein v1.1.1/go.mod h1:veldBMzWxcCG2ZvUTKD2kJNRdCk5hVbJomOvKkmgYbo= github.com/alexbrainman/goissue34681 v0.0.0-20191006012335-3fc7a47baff5 h1:iW0a5ljuFxkLGPNem5Ui+KBjFJzKg4Fv2fnxe4dvzpM= github.com/alexbrainman/goissue34681 v0.0.0-20191006012335-3fc7a47baff5/go.mod h1:Y2QMoi1vgtOIfc+6DhrMOGkLoGzqSV2rKp4Sm+opsyA= +github.com/alexflint/go-arg v1.4.2 h1:lDWZAXxpAnZUq4qwb86p/3rIJJ2Li81EoMbTMujhVa0= +github.com/alexflint/go-arg v1.4.2/go.mod h1:9iRbDxne7LcR/GSvEr7ma++GLpdIU1zrghf2y2768kM= +github.com/alexflint/go-scalar v1.0.0 h1:NGupf1XV/Xb04wXskDFzS0KWOLH632W/EO4fAFi+A70= +github.com/alexflint/go-scalar v1.0.0/go.mod h1:GpHzbCOZXEKMEcygYQ5n/aa4Aq84zbxjy3MxYW0gjYw= github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883 h1:bvNMNQO63//z+xNgfBlViaCIJKLlCJ6/fmUseuG0wVQ= github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8= github.com/andybalholm/cascadia v1.2.0/go.mod h1:YCyR8vOZT9aZ1CHEd8ap0gMVm2aFgxBp0T0eFw1RUQY= github.com/andybalholm/cascadia v1.3.2 h1:3Xi6Dw5lHF15JtdcmAHD3i1+T8plmv7BQ/nsViSLyss= github.com/andybalholm/cascadia v1.3.2/go.mod h1:7gtRlve5FxPPgIgX36uWBX58OdBsSS6lUvCFb+h7KvU= +github.com/apapsch/go-jsonmerge/v2 v2.0.0 h1:axGnT1gRIfimI7gJifB699GoE/oq+F2MU7Dml6nw9rQ= +github.com/apapsch/go-jsonmerge/v2 v2.0.0/go.mod h1:lvDnEdqiQrp0O42VQGgmlKpxL1AP2+08jFMw88y4klk= github.com/araddon/dateparse v0.0.0-20210429162001-6b43995a97de h1:FxWPpzIjnTlhPwqqXc4/vE0f7GvRjuAsbW+HOIe8KnA= github.com/araddon/dateparse v0.0.0-20210429162001-6b43995a97de/go.mod h1:DCaWoUhZrYW9p1lxo/cm8EmUOOzAPSEZNGF2DK1dJgw= github.com/avast/retry-go/v4 v4.6.0 h1:K9xNA+KeB8HHc2aWFuLb25Offp+0iVRXEvFx8IinRJA= @@ -106,6 +114,8 @@ github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/r github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk= github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= +github.com/dmarkham/enumer v1.5.10 h1:ygL0L6quiTiH1jpp68DyvsWaea6MaZLZrTTkIS++R0M= +github.com/dmarkham/enumer v1.5.10/go.mod h1:e4VILe2b1nYK3JKJpRmNdl5xbDQvELc6tQ8b+GsGk6E= github.com/docker/cli v26.1.1+incompatible h1:bE1/uE2tCa08fMv+7ikLR/RDPoCqytwrLtkIkSzxLvw= github.com/docker/cli v26.1.1+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= github.com/docker/docker v26.1.3+incompatible h1:lLCzRbrVZrljpVNobJu1J2FHk8V0s4BawoZippkc+xo= @@ -129,6 +139,10 @@ github.com/fatih/color v1.14.1 h1:qfhVLaG5s+nCROl1zJsZRxFeYrHLqWroPOQ8BWiNb4w= github.com/fatih/color v1.14.1/go.mod h1:2oHN61fhTpgcxD3TSWCgKDiH1+x4OiDVVGH8WlgGZGg= github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= +github.com/ferranbt/fastssz v0.1.2 h1:Dky6dXlngF6Qjc+EfDipAkE83N5I5DE68bY6O0VLNPk= +github.com/ferranbt/fastssz v0.1.2/go.mod h1:X5UPrE2u1UJjxHA8X54u04SBwdAQjG2sFtWs39YxyWs= +github.com/fjl/gencodec v0.0.0-20230517082657-f9840df7b83e h1:bBLctRc7kr01YGvaDfgLbTwjFNW5jdp5y5rj8XXBHfY= +github.com/fjl/gencodec v0.0.0-20230517082657-f9840df7b83e/go.mod h1:AzA8Lj6YtixmJWL+wkKoBGsLWy9gFrAzi4g+5bCKwpY= github.com/fjl/memsize v0.0.2 h1:27txuSD9or+NZlnOWdKUxeBzTAUkWCVh+4Gf2dWFOzA= github.com/fjl/memsize v0.0.2/go.mod h1:VvhXpOYNQvB+uIk2RvXzuaQtkQJzzIx6lSBe1xv7hi0= github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= @@ -140,6 +154,8 @@ github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nos github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= github.com/gabriel-vasile/mimetype v1.4.7 h1:SKFKl7kD0RiPdbht0s7hFtjl489WcQ1VyPW8ZzUMYCA= github.com/gabriel-vasile/mimetype v1.4.7/go.mod h1:GDlAgAyIRT27BhFl53XNAFtfjzOkLaF35JdEG0P7LtU= +github.com/garslo/gogen v0.0.0-20170306192744-1d203ffc1f61 h1:IZqZOB2fydHte3kUgxrzK5E1fW7RQGeDwE8F/ZZnUYc= +github.com/garslo/gogen v0.0.0-20170306192744-1d203ffc1f61/go.mod h1:Q0X6pkwTILDlzrGEckF6HKjXe48EgsY/l7K7vhY4MW8= github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff h1:tY80oXqGNY4FhTFhk+o9oFHGINQ/+vhlm8HFzi6znCI= github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff/go.mod h1:x7DCsMOv1taUwEWCzT4cmDeAkigA5/QCwUodaVOe8Ww= github.com/gballet/go-verkle v0.1.1-0.20231031103413-a67434b50f46 h1:BAIP2GihuqhwdILrV+7GJel5lyPV3u1+PgzrWLc0TkE= @@ -249,6 +265,8 @@ github.com/grafana/pyroscope-go v1.2.0 h1:aILLKjTj8CS8f/24OPMGPewQSYlhmdQMBmol1d github.com/grafana/pyroscope-go v1.2.0/go.mod h1:2GHr28Nr05bg2pElS+dDsc98f3JTUh2f6Fz1hWXrqwk= github.com/grafana/pyroscope-go/godeltaprof v0.1.8 h1:iwOtYXeeVSAeYefJNaxDytgjKtUuKQbJqgAIjlnicKg= github.com/grafana/pyroscope-go/godeltaprof v0.1.8/go.mod h1:2+l7K7twW49Ct4wFluZD3tZ6e0SjanjcUUBPVD/UuGU= +github.com/graph-gophers/graphql-go v1.3.0 h1:Eb9x/q6MFpCLz7jBCiP/WTxjSDrYLR1QY41SORZyNJ0= +github.com/graph-gophers/graphql-go v1.3.0/go.mod h1:9CQHMSxwO4MprSdzoIEobiHpoLtHm77vfxsvsIN5Vuc= github.com/grpc-ecosystem/grpc-gateway/v2 v2.23.0 h1:ad0vkEBuk23VJzZR9nkLVG0YAoN9coASF1GusYX6AlU= github.com/grpc-ecosystem/grpc-gateway/v2 v2.23.0/go.mod h1:igFoXX2ELCW06bol23DWPB5BEWfZISOzSP5K2sbLea0= github.com/hamba/avro v1.8.0 h1:eCVrLX7UYThA3R3yBZ+rpmafA5qTc3ZjpTz6gYJoVGU= @@ -287,6 +305,12 @@ github.com/huin/goupnp v1.3.0/go.mod h1:gnGPsThkYa7bFi/KWmEysQRf48l2dvR5bxr2OFck github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= +github.com/influxdata/influxdb-client-go/v2 v2.13.0 h1:ioBbLmR5NMbAjP4UVA5r9b5xGjpABD7j65pI8kFphDM= +github.com/influxdata/influxdb-client-go/v2 v2.13.0/go.mod h1:k+spCbt9hcvqvUiz0sr5D8LolXHqAAOfPw9v/RIRHl4= +github.com/influxdata/influxdb1-client v0.0.0-20220302092344-a9ab5670611c h1:qSHzRbhzK8RdXOsAdfDgO49TtqC1oZ+acxPrkfTxcCs= +github.com/influxdata/influxdb1-client v0.0.0-20220302092344-a9ab5670611c/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo= +github.com/influxdata/line-protocol v0.0.0-20210922203350-b1ad95c89adf h1:7JTmneyiNEwVBOHSjoMxiWAqB992atOeepeFYegn5RU= +github.com/influxdata/line-protocol v0.0.0-20210922203350-b1ad95c89adf/go.mod h1:xaLFMmpvUxqXtVkUJfg9QmT88cDaCJ3ZKgdZ78oO8Qo= github.com/invopop/yaml v0.3.1 h1:f0+ZpmhfBSS4MhG+4HYseMdJhoeeopbSKbq5Rpeelso= github.com/invopop/yaml v0.3.1/go.mod h1:PMOp3nn4/12yEZUFfmOuNHJsZToEEOwoWsT+D81KkeA= github.com/ipfs/bbloom v0.0.4 h1:Gi+8EGJ2y5qiD5FbsbpX/TMNcJw8gSqr7eyjHa4Fhvs= @@ -509,6 +533,8 @@ github.com/nxadm/tail v1.4.11 h1:8feyoE3OzPrcshW5/MJ4sGESc5cqmGkGCWlco4l0bqY= github.com/nxadm/tail v1.4.11/go.mod h1:OTaG3NK980DZzxbRq6lEuzgU+mug70nY11sMd4JXXHc= github.com/oapi-codegen/oapi-codegen/v2 v2.4.1 h1:ykgG34472DWey7TSjd8vIfNykXgjOgYJZoQbKfEeY/Q= github.com/oapi-codegen/oapi-codegen/v2 v2.4.1/go.mod h1:N5+lY1tiTDV3V1BeHtOxeWXHoPVeApvsvjJqegfoaz8= +github.com/oapi-codegen/runtime v1.0.0 h1:P4rqFX5fMFWqRzY9M/3YF9+aPSPPB06IzP2P7oOxrWo= +github.com/oapi-codegen/runtime v1.0.0/go.mod h1:LmCUMQuPB4M/nLXilQXhHw+BLZdDb18B34OO356yJ/A= 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/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= @@ -540,12 +566,16 @@ github.com/orlangure/gnomock v0.31.0 h1:dgjlQ8DYUPMyNwMZJuYBH+/GF+e7h3sloldPzIJF github.com/orlangure/gnomock v0.31.0/go.mod h1:RagxeYv3bKi+li9Lio2Faw5t6Mcy4akkeqXzkgAS3w0= github.com/ory/dockertest/v3 v3.10.0 h1:4K3z2VMe8Woe++invjaTB7VRyQXQy5UY+loujO4aNE4= github.com/ory/dockertest/v3 v3.10.0/go.mod h1:nr57ZbRWMqfsdGdFNLHz5jjNdDb7VVFnzAeW1n5N1Lg= +github.com/pascaldekloe/name v1.0.0 h1:n7LKFgHixETzxpRv2R77YgPUFo85QHGZKrdaYm7eY5U= +github.com/pascaldekloe/name v1.0.0/go.mod h1:Z//MfYJnH4jVpQ9wkclwu2I2MkHmXTlT9wR5UZScttM= github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM= github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs= github.com/perimeterx/marshmallow v1.1.5 h1:a2LALqQ1BlHM8PZblsDdidgv1mWi1DgC2UmX50IvK2s= github.com/perimeterx/marshmallow v1.1.5/go.mod h1:dsXbUu8CRzfYP5a87xpp0xq9S3u0Vchtcl8we9tYaXw= github.com/petar/GoLLRB v0.0.0-20210522233825-ae3b015fd3e9 h1:1/WtZae0yGtPq+TI6+Tv1WTxkukpXeMlviSxvL7SRgk= github.com/petar/GoLLRB v0.0.0-20210522233825-ae3b015fd3e9/go.mod h1:x3N5drFsm2uilKKuuYo6LdyD8vZAW55sH/9w+pbo1sw= +github.com/peterh/liner v1.1.1-0.20190123174540-a2c9a5303de7 h1:oYW+YCJ1pachXTQmzR3rNLYGGz4g/UgFcjb28p/viDM= +github.com/peterh/liner v1.1.1-0.20190123174540-a2c9a5303de7/go.mod h1:CRroGNssyjTd/qIG2FyxByd2S8JEAZXBl4qUrZf8GS0= github.com/pierrec/lz4/v4 v4.1.21 h1:yOVMLb6qSIDP67pl/5F7RepeKYu/VmTyEXvuMI5d9mQ= github.com/pierrec/lz4/v4 v4.1.21/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4= From 5f724dbfdccd4451ffa5c52ae75d2d902f475e4c Mon Sep 17 00:00:00 2001 From: polebug Date: Fri, 20 Dec 2024 15:03:42 +0800 Subject: [PATCH 08/10] chore: update ci/cd --- .github/workflows/lint.yaml | 2 +- .github/workflows/test.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/lint.yaml b/.github/workflows/lint.yaml index 4c38f4c74..373432532 100644 --- a/.github/workflows/lint.yaml +++ b/.github/workflows/lint.yaml @@ -18,7 +18,7 @@ jobs: - name: Setup Go uses: actions/setup-go@v5 with: - go-version: "1.21" + go-version: "1.22.7" - name: Checkout uses: actions/checkout@v4 - name: GolangCI Lint diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 80aaca35f..54c2fc375 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -35,7 +35,7 @@ jobs: - name: Setup Go uses: actions/setup-go@v5 with: - go-version: "1.21" + go-version: "1.22.7" - name: Checkout uses: actions/checkout@v4 - name: Test From d13bddce8f6e66d288868a408c226c13f86ab277 Mon Sep 17 00:00:00 2001 From: polebug Date: Fri, 20 Dec 2024 15:21:34 +0800 Subject: [PATCH 09/10] chore --- .../dialer/postgres/client_partitioned.go | 30 +++++++++---------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/internal/database/dialer/postgres/client_partitioned.go b/internal/database/dialer/postgres/client_partitioned.go index 7a59e60cd..f8f724e74 100644 --- a/internal/database/dialer/postgres/client_partitioned.go +++ b/internal/database/dialer/postgres/client_partitioned.go @@ -109,20 +109,30 @@ func (c *client) findIndexesPartitionTables(_ context.Context, index table.Index } // findActivitiesPartitionTables finds activities partition tables. -func (c *client) findActivitiesPartitionTables(_ context.Context, network *network.Network, timestamp time.Time) []string { - if network == nil { +func (c *client) findActivitiesPartitionTables(_ context.Context, query model.ActivitiesMetadataQuery) []string { + if query.Network == nil { return nil } + timestamp := time.Now() + + if query.EndTimestamp != nil && *query.EndTimestamp > 0 && *query.EndTimestamp < uint64(time.Now().Unix()) { + timestamp = time.Unix(int64(lo.FromPtr(query.EndTimestamp)), 0) + } + + if query.Cursor != nil && query.Cursor.Timestamp < uint64(timestamp.Unix()) { + timestamp = time.Unix(int64(query.Cursor.Timestamp), 0) + } + zap.L().Debug("finding activities partition tables", - zap.String("network", network.String()), + zap.String("network", query.Network.String()), zap.Time("timestamp", timestamp)) partitionedNames := make([]string, 0) for i := 0; i <= 4; i++ { activityTable := table.Activity{ - Network: lo.FromPtr(network), + Network: lo.FromPtr(query.Network), Timestamp: timestamp, } @@ -320,17 +330,7 @@ func (c *client) findActivityPartitioned(ctx context.Context, query model.Activi // findActivitiesMetadataPartitioned finds activities metadata. func (c *client) findActivitiesMetadataPartitioned(ctx context.Context, query model.ActivitiesMetadataQuery) ([]*activityx.Activity, error) { - timestamp := time.Now() - - if query.EndTimestamp != nil && *query.EndTimestamp > 0 && *query.EndTimestamp < uint64(time.Now().Unix()) { - timestamp = time.Unix(int64(lo.FromPtr(query.EndTimestamp)), 0) - } - - if query.Cursor != nil && query.Cursor.Timestamp < uint64(timestamp.Unix()) { - timestamp = time.Unix(int64(query.Cursor.Timestamp), 0) - } - - partitionedNames := c.findActivitiesPartitionTables(ctx, query.Network, timestamp) + partitionedNames := c.findActivitiesPartitionTables(ctx, query) if len(partitionedNames) == 0 { return nil, nil From 1a9ae4cef38613a8a81887a74473f6045d91b240 Mon Sep 17 00:00:00 2001 From: polebug Date: Fri, 20 Dec 2024 15:32:42 +0800 Subject: [PATCH 10/10] chore --- Dockerfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index 91c6b27f9..0906a3b97 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM ghcr.io/rss3-network/go-image/go-builder:main-4782eed AS base +FROM ghcr.io/rss3-network/go-image/go-builder:main-230cb74 AS base WORKDIR /root/node @@ -19,7 +19,7 @@ ENV CGO_ENABLED=0 RUN --mount=type=cache,target=/go/pkg/mod/ \ make build -FROM ghcr.io/rss3-network/go-image/go-runtime:main-4782eed AS runner +FROM ghcr.io/rss3-network/go-image/go-builder:main-230cb74 AS runner WORKDIR /root/node