From a9d0debb74f91a017298f798a619523ff2f65691 Mon Sep 17 00:00:00 2001 From: Rabbit Date: Fri, 13 Dec 2024 11:47:15 +0800 Subject: [PATCH] feat: add udt hourly statistic --- .../v2/udt_hourly_statistics_controller.rb | 28 +++++++ app/models/udt_hourly_statistic.rb | 40 ++++++++++ .../generate_udt_hourly_statistic_worker.rb | 54 +++++++++++++ config/routes/v2.rb | 1 + ...1212022531_create_udt_hourly_statistics.rb | 14 ++++ db/structure.sql | 79 ++++++++++++++++++- lib/scheduler.rb | 4 + 7 files changed, 217 insertions(+), 3 deletions(-) create mode 100644 app/controllers/api/v2/udt_hourly_statistics_controller.rb create mode 100644 app/models/udt_hourly_statistic.rb create mode 100644 app/workers/generate_udt_hourly_statistic_worker.rb create mode 100644 db/migrate/20241212022531_create_udt_hourly_statistics.rb diff --git a/app/controllers/api/v2/udt_hourly_statistics_controller.rb b/app/controllers/api/v2/udt_hourly_statistics_controller.rb new file mode 100644 index 000000000..6db2a6dca --- /dev/null +++ b/app/controllers/api/v2/udt_hourly_statistics_controller.rb @@ -0,0 +1,28 @@ +module Api + module V2 + class UdtHourlyStatisticsController < BaseController + def show + expires_in 15.minutes, public: true, stale_while_revalidate: 5.minutes, stale_if_error: 5.minutes + + udt = Udt.find_by!(type_hash: params[:id], published: true) + hourly_statistics = + if udt.present? + UdtHourlyStatistic.where(udt:).order(created_at_unixtimestamp: :asc) + else + UdtHourlyStatistic.none + end + + render json: { + data: hourly_statistics.map do |statistic| + { + ckb_transactions_count: statistic.ckb_transactions_count, + amount: statistic.amount, + holders_count: statistic.holders_count, + created_at_unixtimestamp: statistic.created_at_unixtimestamp, + } + end, + } + end + end + end +end diff --git a/app/models/udt_hourly_statistic.rb b/app/models/udt_hourly_statistic.rb new file mode 100644 index 000000000..233f2bb22 --- /dev/null +++ b/app/models/udt_hourly_statistic.rb @@ -0,0 +1,40 @@ +class UdtHourlyStatistic < ApplicationRecord + belongs_to :udt + + def percentage_change(attribute) + yesterday = previous_stat(udt_id, 1) + day_before_yesterday = previous_stat(udt_id, 2) + + return nil unless yesterday && day_before_yesterday + + yesterday_value = yesterday.public_send(attribute) + day_before_yesterday_value = day_before_yesterday.public_send(attribute) + + return nil if day_before_yesterday_value.zero? + + ((yesterday_value - day_before_yesterday_value).to_f / day_before_yesterday_value * 100).round(2) + end + + def previous_stat(udt_id, days_ago) + timestamp = (Time.current - days_ago.days).beginning_of_day.to_i + self.class.find_by(udt_id:, created_at_unixtimestamp: timestamp) + end +end + +# == Schema Information +# +# Table name: udt_hourly_statistics +# +# id :bigint not null, primary key +# udt_id :bigint not null +# ckb_transactions_count :integer default(0) +# amount :decimal(40, ) default(0) +# holders_count :integer default(0) +# created_at_unixtimestamp :integer +# created_at :datetime not null +# updated_at :datetime not null +# +# Indexes +# +# index_on_udt_id_and_unixtimestamp (udt_id,created_at_unixtimestamp) UNIQUE +# diff --git a/app/workers/generate_udt_hourly_statistic_worker.rb b/app/workers/generate_udt_hourly_statistic_worker.rb new file mode 100644 index 000000000..82df72a49 --- /dev/null +++ b/app/workers/generate_udt_hourly_statistic_worker.rb @@ -0,0 +1,54 @@ +class GenerateUdtHourlyStatisticWorker + include Sidekiq::Job + + def perform + statistic_attributes = [] + udt_types = %i[xudt xudt_compatible spore_cell did_cell] + Udt.where(udt_type: udt_types, published: true).find_each do |udt| + statistic_attributes << { + udt_id: udt.id, + amount: calc_amount(udt), + ckb_transactions_count: calc_ckb_transactions_count(udt), + holders_count: calc_holders_count(udt), + created_at_unixtimestamp: to_be_counted_date.beginning_of_day.to_i, + } + end + + if statistic_attributes.present? + DailyStatisticGenerator.upsert_all(statistic_attributes, + unique_by: %i[udt_id created_at_unixtimestamp]) + end + rescue StandardError => e + Rails.logger.error "Error occurred during GenerateUdtHourlyStatistic error: #{e.message}" + end + + private + + def to_be_counted_date + last_record = UdtHourlyStatistic.order(created_at_unixtimestamp: :desc).first + if last_record + Time.zone.at(last_record.created_at_unixtimestamp) + 1.day + else + Time.current.yesterday + end + end + + def calc_amount(udt) + inputs_amount = 0 + outputs_amount = 0 + ckb_transaction_ids = udt.ckb_transactions.map(&:id) + ckb_transaction_ids.each_slice(1000) do |ids| + inputs_amount += CellOutput.where(consumed_by_id: ids).sum(:udt_amount) + outputs_amount += CellOutput.where(ckb_transaction_id: ids).sum(:udt_amount) + end + [inputs_amount, outputs_amount].max + end + + def calc_ckb_transactions_count(udt) + udt.ckb_transactions.count + end + + def calc_holders_count(udt) + udt.udt_holder_allocations.sum("ckb_holder_count + btc_holder_count") + end +end diff --git a/config/routes/v2.rb b/config/routes/v2.rb index 6a74404f9..1279db51d 100644 --- a/config/routes/v2.rb +++ b/config/routes/v2.rb @@ -106,5 +106,6 @@ resources :graph_nodes, param: :node_id, only: %i[index show] resources :graph_channels, only: :index end + resources :udt_hourly_statistics, only: :show end end diff --git a/db/migrate/20241212022531_create_udt_hourly_statistics.rb b/db/migrate/20241212022531_create_udt_hourly_statistics.rb new file mode 100644 index 000000000..4cb58413f --- /dev/null +++ b/db/migrate/20241212022531_create_udt_hourly_statistics.rb @@ -0,0 +1,14 @@ +class CreateUdtHourlyStatistics < ActiveRecord::Migration[7.0] + def change + create_table :udt_hourly_statistics do |t| + t.bigint :udt_id, null: false + t.integer :ckb_transactions_count, default: 0 + t.decimal :amount, precision: 40, default: 0.0 + t.integer :holders_count, default: 0 + t.integer :created_at_unixtimestamp + t.timestamps + end + + add_index :udt_hourly_statistics, %i[udt_id created_at_unixtimestamp], name: "index_on_udt_id_and_unixtimestamp", unique: true + end +end diff --git a/db/structure.sql b/db/structure.sql index c1f18c9b6..f67ebfbde 100644 --- a/db/structure.sql +++ b/db/structure.sql @@ -1054,12 +1054,12 @@ ALTER SEQUENCE public.cell_data_cell_output_id_seq OWNED BY public.cell_data.cel CREATE TABLE public.cell_dependencies ( id bigint NOT NULL, + contract_id bigint, ckb_transaction_id bigint NOT NULL, dep_type integer, contract_cell_id bigint NOT NULL, script_id bigint, - contract_id bigint, - implicit boolean, + implicit boolean DEFAULT true NOT NULL, block_number bigint, tx_index integer, contract_analyzed boolean DEFAULT false @@ -2698,6 +2698,41 @@ CREATE SEQUENCE public.udt_holder_allocations_id_seq ALTER SEQUENCE public.udt_holder_allocations_id_seq OWNED BY public.udt_holder_allocations.id; +-- +-- Name: udt_hourly_statistics; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.udt_hourly_statistics ( + id bigint NOT NULL, + udt_id bigint NOT NULL, + ckb_transactions_count integer DEFAULT 0, + amount numeric(40,0) DEFAULT 0.0, + holders_count integer DEFAULT 0, + created_at_unixtimestamp integer, + created_at timestamp(6) without time zone NOT NULL, + updated_at timestamp(6) without time zone NOT NULL +); + + +-- +-- Name: udt_hourly_statistics_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +CREATE SEQUENCE public.udt_hourly_statistics_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +-- +-- Name: udt_hourly_statistics_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: - +-- + +ALTER SEQUENCE public.udt_hourly_statistics_id_seq OWNED BY public.udt_hourly_statistics.id; + + -- -- Name: udt_transactions; Type: TABLE; Schema: public; Owner: - -- @@ -3364,6 +3399,13 @@ ALTER TABLE ONLY public.udt_accounts ALTER COLUMN id SET DEFAULT nextval('public ALTER TABLE ONLY public.udt_holder_allocations ALTER COLUMN id SET DEFAULT nextval('public.udt_holder_allocations_id_seq'::regclass); +-- +-- Name: udt_hourly_statistics id; Type: DEFAULT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.udt_hourly_statistics ALTER COLUMN id SET DEFAULT nextval('public.udt_hourly_statistics_id_seq'::regclass); + + -- -- Name: udt_verifications id; Type: DEFAULT; Schema: public; Owner: - -- @@ -3950,6 +3992,14 @@ ALTER TABLE ONLY public.udt_holder_allocations ADD CONSTRAINT udt_holder_allocations_pkey PRIMARY KEY (id); +-- +-- Name: udt_hourly_statistics udt_hourly_statistics_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.udt_hourly_statistics + ADD CONSTRAINT udt_hourly_statistics_pkey PRIMARY KEY (id); + + -- -- Name: udt_verifications udt_verifications_pkey; Type: CONSTRAINT; Schema: public; Owner: - -- @@ -4792,6 +4842,20 @@ CREATE INDEX index_cell_dependencies_on_block_number_and_tx_index ON public.cell CREATE INDEX index_cell_dependencies_on_contract_analyzed ON public.cell_dependencies USING btree (contract_analyzed); +-- +-- Name: index_cell_dependencies_on_contract_id; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX index_cell_dependencies_on_contract_id ON public.cell_dependencies USING btree (contract_id); + + +-- +-- Name: index_cell_dependencies_on_script_id; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX index_cell_dependencies_on_script_id ON public.cell_dependencies USING btree (script_id); + + -- -- Name: index_cell_dependencies_on_tx_id_and_cell_id_and_dep_type; Type: INDEX; Schema: public; Owner: - -- @@ -5044,6 +5108,13 @@ CREATE UNIQUE INDEX index_omiga_inscription_infos_on_udt_hash ON public.omiga_in CREATE INDEX index_on_cell_dependencies_contract_cell_block_tx ON public.cell_dependencies USING btree (contract_cell_id, block_number DESC, tx_index DESC); +-- +-- Name: index_on_udt_id_and_unixtimestamp; Type: INDEX; Schema: public; Owner: - +-- + +CREATE UNIQUE INDEX index_on_udt_id_and_unixtimestamp ON public.udt_hourly_statistics USING btree (udt_id, created_at_unixtimestamp); + + -- -- Name: index_portfolios_on_user_id_and_address_id; Type: INDEX; Schema: public; Owner: - -- @@ -6127,6 +6198,7 @@ INSERT INTO "schema_migrations" (version) VALUES ('20240918033146'), ('20240920094807'), ('20240924065539'), +('20241009081935'), ('20241012014906'), ('20241023055256'), ('20241023063536'), @@ -6141,6 +6213,7 @@ INSERT INTO "schema_migrations" (version) VALUES ('20241129000339'), ('20241129032447'), ('20241202072604'), -('20241205023729'); +('20241205023729'), +('20241212022531'); diff --git a/lib/scheduler.rb b/lib/scheduler.rb index 9afe16d6f..36f3060eb 100644 --- a/lib/scheduler.rb +++ b/lib/scheduler.rb @@ -136,4 +136,8 @@ def call_worker(clz) call_worker FiberGraphDetectWorker end +s.cron "0 8 * * *" do + call_worker GenerateUdtHourlyStatisticWorker +end + s.join