merchant

Merchant backend to process payments, run by merchants
Log | Files | Refs | Submodules | README | LICENSE

commit 4f6fdb48998eae82e7bafa5bbce2f351e8802150
parent 8c00b38fb8bdbe3628c6566a811dc738ce90ad1f
Author: Christian Grothoff <grothoff@gnunet.org>
Date:   Sat, 22 Mar 2025 16:29:51 +0100

-debugging of new statistic logic

Diffstat:
Msrc/backenddb/merchant-0014.sql | 4++--
Msrc/backenddb/pg_statistics_examples.sql | 54+++++++++++++++++++++++++++++++++++++++++++++++-------
Msrc/backenddb/pg_statistics_helpers.sql | 126++++++++++++++++++++++++++++++++++++++++++++++++++++++-------------------------
3 files changed, 136 insertions(+), 48 deletions(-)

diff --git a/src/backenddb/merchant-0014.sql b/src/backenddb/merchant-0014.sql @@ -155,7 +155,7 @@ CREATE TABLE merchant_statistic_counter_event REFERENCES merchant_instances (merchant_serial) ON DELETE CASCADE ,slot INT8 NOT NULL ,delta INT8 NOT NULL - ,UNIQUE (imeta_serial_id, slot) + ,UNIQUE (imeta_serial_id, merchant_serial, slot) ); COMMENT ON TABLE merchant_statistic_counter_event IS 'number to decrement an interval statistic by when a certain time value is reached'; @@ -236,7 +236,7 @@ CREATE TABLE merchant_statistic_interval_amount ,curr VARCHAR(12) NOT NULL ,cumulative_value INT8 NOT NULL ,cumulative_frac INT4 NOT NULL - ,UNIQUE (imeta_serial_id,merchant_serial,curr) + ,UNIQUE (imeta_serial_id,merchant_serial,curr,range) ); COMMENT ON TABLE merchant_statistic_interval_amount IS 'various amount statistics (in various currencies) being tracked'; diff --git a/src/backenddb/pg_statistics_examples.sql b/src/backenddb/pg_statistics_examples.sql @@ -28,6 +28,7 @@ BEGIN; SET search_path TO example_statistics,merchant; + -- Setup statistic: what do we want to track for 'deposits'? -- (Note: this is basically the one "manual" step we might not keep hard-coded) INSERT INTO merchant_statistic_bucket_meta @@ -42,7 +43,8 @@ VALUES ,'amount' ,ARRAY['second'::statistic_range, 'minute', 'day', 'month', 'quarter', 'year'] ,ARRAY[120, 120, 95, 36, 40, 100] -- track last 120 s, 120 minutes, 95 days, 36 months, 40 quarters & 100 years - ); + ) +ON CONFLICT DO NOTHING; INSERT INTO merchant_statistic_interval_meta (slug @@ -60,8 +62,20 @@ VALUES ,ARRAY[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 5, 5, 5, 5, 5, 60, 60 * 60, 24 * 60 * 60] - ); + ), + ('products-sold' + ,'number of products sold' + ,'number' + ,ARRAY[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, + 60, 120, 180, 240, 300, + 24 * 60 * 60, 30 * 24 * 60 * 60, 365 * 24 * 60 * 60] -- second, minute, day, month, year + ,ARRAY[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 5, 5, 5, 5, 5, + 60, 60 * 60, 24 * 60 * 60] + ) +ON CONFLICT DO NOTHING; +DROP FUNCTION IF EXISTS merchant_deposits_statistics_trigger CASCADE; CREATE FUNCTION merchant_deposits_statistics_trigger() RETURNS trigger LANGUAGE plpgsql @@ -127,8 +141,10 @@ VALUES ,'number' ,ARRAY['second'::statistic_range, 'minute', 'day', 'week', 'month', 'quarter', 'year'] ,ARRAY[120, 120, 60, 12, 24, 8, 10] -- track last 120s, 120 minutes, 60 days, 12 weeks, 24 months, 8 quarters and 10 years - ); + ) +ON CONFLICT DO NOTHING; +DROP FUNCTION IF EXISTS merchant_products_sold_statistics_trigger CASCADE; CREATE FUNCTION merchant_products_sold_statistics_trigger() RETURNS trigger LANGUAGE plpgsql @@ -155,14 +171,38 @@ CREATE TRIGGER merchant_products_on_sold ON merchant_inventory FOR EACH ROW EXECUTE FUNCTION merchant_products_sold_statistics_trigger(); -call merchant_do_bump_number_stat ('products-sold'::text, 6, CURRENT_TIMESTAMP(0)::TIMESTAMP, 1); +delete from merchant.merchant_statistic_bucket_counter ; +delete from merchant.merchant_statistic_bucket_amount ; +delete from merchant.merchant_statistic_interval_counter; +delete from merchant.merchant_statistic_interval_amount; +delete from merchant.merchant_statistic_amount_event; +delete from merchant.merchant_statistic_counter_event; -call merchant_do_bump_amount_stat ('deposits'::text, 6, CURRENT_TIMESTAMP(0)::TIMESTAMP, (2,2,'EUR')::taler_amount_currency); -select * from merchant_statistic_interval_number_get ('deposits', 'default'); +call merchant_do_bump_number_stat ('products-sold'::text, 6, CURRENT_TIMESTAMP(0)::TIMESTAMP-INTERVAL '2 minutes', 1); +call merchant_do_bump_amount_stat ('deposits'::text, 6, CURRENT_TIMESTAMP(0)::TIMESTAMP-INTERVAL '2 minutes', (1,1,'EUR')::taler_amount_currency); +call merchant_do_bump_number_stat ('products-sold'::text, 6, CURRENT_TIMESTAMP(0)::TIMESTAMP-INTERVAL '2 seconds', 2); +call merchant_do_bump_amount_stat ('deposits'::text, 6, CURRENT_TIMESTAMP(0)::TIMESTAMP-INTERVAL '1 minute', (2,2,'EUR')::taler_amount_currency); +call merchant_do_bump_amount_stat ('deposits'::text, 6, CURRENT_TIMESTAMP(0)::TIMESTAMP-INTERVAL '2 seconds', (4,4,'EUR')::taler_amount_currency); +call merchant_do_bump_amount_stat ('deposits'::text, 6, CURRENT_TIMESTAMP(0)::TIMESTAMP-INTERVAL '1 second', (8,8,'EUR')::taler_amount_currency); +call merchant_do_bump_number_stat ('products-sold'::text, 6, CURRENT_TIMESTAMP(0)::TIMESTAMP, 4); +call merchant_do_bump_amount_stat ('deposits'::text, 6, CURRENT_TIMESTAMP(0)::TIMESTAMP, (16,16,'EUR')::taler_amount_currency); + +select * from merchant_statistic_interval_number_get ('products-sold', 'default'); select * from merchant_statistic_interval_amount_get ('deposits', 'default'); +select * from merchant.merchant_statistic_amount_event; + +select * from merchant.merchant_statistic_counter_event; + +select * from merchant.merchant_statistic_interval_counter; + +select * from merchant.merchant_statistic_interval_amount; + +select * from merchant.merchant_statistic_bucket_counter ; +select * from merchant.merchant_statistic_bucket_amount ; -ROLLBACK; +-- ROLLBACK; +COMMIT; diff --git a/src/backenddb/pg_statistics_helpers.sql b/src/backenddb/pg_statistics_helpers.sql @@ -189,15 +189,17 @@ DECLARE my_now INT8; my_record RECORD; my_meta INT8; - my_range0 INT8; - my_precision0 INT8; + my_ranges INT8[]; + my_precisions INT8[]; + my_rangex INT8; + my_precisionx INT8; my_start INT8; my_event INT8; BEGIN - my_now = ROUND(EXTRACT(epoch FROM in_timestamp) * 1000000)::INT8; + my_now = ROUND(EXTRACT(epoch FROM CURRENT_TIMESTAMP(0)::TIMESTAMP) * 1000000)::INT8 / 1000 / 1000; SELECT imeta_serial_id - ,ranges[1] AS range0 - ,precisions[1] AS precision0 + ,ranges AS ranges + ,precisions AS precisions INTO my_record FROM merchant_statistic_interval_meta WHERE slug=in_slug @@ -207,12 +209,29 @@ BEGIN RETURN; END IF; + my_start = ROUND(EXTRACT(epoch FROM in_timestamp) * 1000000)::INT8 / 1000 / 1000; -- convert to seconds + my_precisions = my_record.precisions; + my_ranges = my_record.ranges; + my_rangex = NULL; + FOR my_x IN 1..COALESCE(array_length(my_ranges,1),0) + LOOP + IF my_now - my_ranges[my_x] < my_start + THEN + my_rangex = my_ranges[my_x]; + my_precisionx = my_precisions[my_x]; + EXIT; + END IF; + END LOOP; + IF my_rangex IS NULL + THEN + -- event is beyond the ranges we care about + RETURN; + END IF; + my_meta = my_record.imeta_serial_id; - my_precision0 = my_record.precision0; - my_start = my_now / 1000 / 1000; -- convert to seconds - my_start = my_start - my_start % my_precision0; -- round down + my_start = my_start - my_start % my_precisionx; -- round down - INSERT INTO metchant_statistic_counter_event + INSERT INTO merchant_statistic_counter_event AS msce (imeta_serial_id ,merchant_serial ,slot @@ -224,7 +243,7 @@ BEGIN ,in_delta) ON CONFLICT (imeta_serial_id, merchant_serial, slot) DO UPDATE SET - delta = delta + in_delta + delta = msce.delta + in_delta RETURNING nevent_serial_id INTO my_event; @@ -232,7 +251,7 @@ BEGIN SET cumulative_number = cumulative_number + in_delta WHERE imeta_serial_id = my_meta AND merchant_serial = in_merchant_serial - AND range=my_range0; + AND range=my_rangex; IF NOT FOUND THEN INSERT INTO merchant_statistic_interval_counter @@ -244,7 +263,7 @@ BEGIN ) VALUES ( my_meta ,in_merchant_serial - ,my_range0 + ,my_rangex ,my_event ,in_delta); END IF; @@ -267,15 +286,18 @@ DECLARE my_now INT8; my_record RECORD; my_meta INT8; - my_range0 INT8; - my_precision0 INT8; + my_ranges INT8[]; + my_precisions INT8[]; + my_x INT; + my_rangex INT8; + my_precisionx INT8; my_start INT8; my_event INT8; BEGIN - my_now = ROUND(EXTRACT(epoch FROM in_timestamp) * 1000000)::INT8; + my_now = ROUND(EXTRACT(epoch FROM CURRENT_TIMESTAMP(0)::TIMESTAMP) * 1000000)::INT8 / 1000 / 1000; SELECT imeta_serial_id - ,ranges[1] AS range0 - ,precisions[1] AS precision0 + ,ranges + ,precisions INTO my_record FROM merchant_statistic_interval_meta WHERE slug=in_slug @@ -285,11 +307,26 @@ BEGIN RETURN; END IF; + my_start = ROUND(EXTRACT(epoch FROM in_timestamp) * 1000000)::INT8 / 1000 / 1000; -- convert to seconds since epoch + my_precisions = my_record.precisions; + my_ranges = my_record.ranges; + my_rangex = NULL; + FOR my_x IN 1..COALESCE(array_length(my_ranges,1),0) + LOOP + IF my_now - my_ranges[my_x] < my_start + THEN + my_rangex = my_ranges[my_x]; + my_precisionx = my_precisions[my_x]; + EXIT; + END IF; + END LOOP; + IF my_rangex IS NULL + THEN + -- event is beyond the ranges we care about + RETURN; + END IF; + my_start = my_start - my_start % my_precisionx; -- round down my_meta = my_record.imeta_serial_id; - my_precision0 = my_record.precision0; - my_range0 = my_record.range0; - my_start = my_now / 1000 / 1000; -- convert to seconds - my_start = my_start - my_start % my_precision0; -- round down INSERT INTO merchant_statistic_amount_event AS msae (imeta_serial_id @@ -339,7 +376,7 @@ BEGIN END WHERE imeta_serial_id=my_meta AND merchant_serial=in_merchant_serial - AND range=my_range0 + AND range=my_rangex AND curr=(in_delta).curr; IF NOT FOUND THEN @@ -354,7 +391,7 @@ BEGIN ) VALUES ( my_meta ,in_merchant_serial - ,my_range0 + ,my_rangex ,my_event ,(in_delta).curr ,(in_delta).val @@ -408,7 +445,7 @@ RETURNS SETOF merchant_statistic_interval_number_get_return_value LANGUAGE plpgsql AS $$ DECLARE - my_time INT8 DEFAULT ROUND(EXTRACT(epoch FROM NOW()) * 1000000)::INT8; + my_time INT8 DEFAULT ROUND(EXTRACT(epoch FROM CURRENT_TIMESTAMP(0)::TIMESTAMP) * 1000000)::INT8 / 1000 / 1000; my_ranges INT8[]; my_range INT8; my_delta INT8; @@ -453,8 +490,11 @@ BEGIN WHERE imeta_serial_id = my_meta AND range = my_range AND merchant_serial = my_instance_id; - my_max_serial = my_irec.event_delimiter; - my_rval.rvalue = my_rval.rvalue + my_irec.cumulative_number; + IF FOUND + THEN + my_max_serial = my_irec.event_delimiter; + my_rval.rvalue = my_rval.rvalue + my_irec.cumulative_number; + END IF; -- Check if we have events that left the applicable range SELECT MAX(nevent_serial_id) AS max_serial_id @@ -464,8 +504,10 @@ BEGIN WHERE imeta_serial_id = my_meta AND merchant_serial = my_instance_id AND slot < my_time - my_range - AND imeta_serial_id >= my_max_serial; - + AND nevent_serial_id >= my_max_serial; + -- FIXME: we don't properly update my_max_serial here! + -- (need to set it to the next still included value, but we set it to the last DELETED value!) + -- => results in double-deletion in the next iteration! IF FOUND AND my_irec.delta_sum IS NOT NULL THEN my_delta = my_irec.delta_sum; @@ -530,7 +572,7 @@ RETURNS SETOF merchant_statistic_interval_amount_get_return_value LANGUAGE plpgsql AS $$ DECLARE - my_time INT8 DEFAULT ROUND(EXTRACT(epoch FROM NOW()) * 1000000)::INT8; + my_time INT8 DEFAULT ROUND(EXTRACT(epoch FROM CURRENT_TIMESTAMP(0)::TIMESTAMP) * 1000000)::INT8 / 1000 / 1000; my_ranges INT8[]; my_range INT8; my_delta_value INT8; @@ -590,14 +632,17 @@ BEGIN AND merchant_serial = my_instance_id AND curr = my_currency AND range = my_range; - - my_max_serial = my_irec.aevent_serial_id; - my_rval.rvalue.val = (my_rval.rvalue).val + my_irec.cumulative_value + my_irec.cumulative_frac / 100000000; - my_rval.rvalue.frac = (my_rval.rvalue).frac + my_irec.cumulative_frac % 100000000; - IF (my_rval.rvalue).frac > 100000000 + + IF FOUND THEN - my_rval.rvalue.frac = (my_rval.rvalue).frac - 100000000; - my_rval.rvalue.val = (my_rval.rvalue).val + 1; + my_max_serial = my_irec.aevent_serial_id; + my_rval.rvalue.val = (my_rval.rvalue).val + my_irec.cumulative_value + my_irec.cumulative_frac / 100000000; + my_rval.rvalue.frac = (my_rval.rvalue).frac + my_irec.cumulative_frac % 100000000; + IF (my_rval.rvalue).frac > 100000000 + THEN + my_rval.rvalue.frac = (my_rval.rvalue).frac - 100000000; + my_rval.rvalue.val = (my_rval.rvalue).val + 1; + END IF; END IF; -- Check if we have events that left the applicable range @@ -610,7 +655,10 @@ BEGIN AND merchant_serial = my_instance_id AND delta_curr = my_currency AND slot < my_time - my_range - AND imeta_serial_id > my_max_serial; + AND aevent_serial_id >= my_max_serial; + -- FIXME: we don't properly update my_max_serial here! + -- (need to set it to the next still included value, but we set it to the last DELETED value!) + -- => results in double-deletion in the next iteration! IF FOUND AND my_jrec.value_sum IS NOT NULL THEN @@ -827,7 +875,7 @@ BEGIN ,precisions FROM merchant_statistic_interval_meta LOOP - + my_meta = my_rec.imeta_serial_id; my_ranges = my_rec.ranges; my_precisions = my_rec.precisions; @@ -909,7 +957,7 @@ DECLARE my_now INT8; my_end INT8; BEGIN - my_now = EXTRACT(EPOCH FROM CURRENT_TIMESTAMP(0)); -- seconds since epoch + my_now = EXTRACT(EPOCH FROM CURRENT_TIMESTAMP(0)::TIMESTAMP); -- seconds since epoch FOR my_rec IN SELECT bmeta_serial_id ,stype