By Digoal
Stream computing can be used in a variety of scenarios, but, in e-commerce, often it can struggle given delivery and refund timeouts. But one question is how can streaming warnings made to be suited even in these scenarios? The answer that I will present in this blog is a solution that is done through a combination of timeout and scheduling operations that uses PostgreSQL.
PostgreSQL is advantageous to our needs in this tutorial because it uses flexible common table expression (CTE) syntax common to SQL languages and partial indexes that do not record unnecessary data. Also, it uses data manipulation language (DML) statements that can be returned and used together with CTE syntax to minimize interactive computing, and it takes multi-index bitmap scans, which combine multiple indexes for scanning, which allows us to combine the indexes of multiple fields for scanning when using an OR condition.
To sum things up, in this tutorial, I will show you how you can create an example solution for these problems through a combination of timeout and scheduling operations using PostgreSQL.
To create a demo solution, first you need to design the structure of the monitored table. This table will record the timeout processing time of orders and refund events, as well as the number of timeout notifications, next notification time, and completion statuses. To do this, you can use the following command in PostgreSQL:
create table tbl (
id int8,
-- ... Other fields, such as the completion status
state int, -- Completion status (1 indicates Completed)
deadts timestamp, -- Timeout
nts interval, -- Timeout interval, used to update the next notification time, for example, one notification per day
notify_times int default 0, -- Number of notifications
deadts_next timestamp -- Next notification time
);
Next, you'll want to create a partial index. This index is so that users are notified of only the unfinished tickets, which are what they mostly likely to be mainly concerned about. By using a partial index, we can reduce the index size and increase the speed. To do this, enter this code:
create index idx_tbl_1 on tbl (deadts) where notify_times=0 and state<>1;
create index idx_tbl_2 on tbl (deadts_next) where deadts_next is not null and state<>1;
Next, as part of this, we need to obtain the related data from users, which would including the notification data and update the number of notifications and the next notification time. You can do this by running this code:
with tmp1 as (
update tbl set
deadts_next=now()+nts,
notify_times=notify_times+1
where ctid = any (array(
select ctid from tbl where
( deadts < now() and notify_times=0 and state<>1)
union all
select ctid from tbl where
( deadts_next < now() and deadts_next is not null and state<>1)
limit 10000 -- Obtains 10,000 timeout data records at a time.
))
returning *
)
select * from tmp1;
Then, execute the plan, as so:
CTE Scan on tmp1 (cost=18163.25..18163.45 rows=10 width=48)
CTE tmp1
-> Update on tbl tbl_2 (cost=18151.05..18163.25 rows=10 width=54)
InitPlan 1 (returns $0)
-> Limit (cost=0.13..18151.03 rows=10000 width=6)
-> Append (cost=0.13..764699.60 rows=421301 width=6)
-> Index Scan using idx_tbl_1 on tbl (cost=0.13..169527.13 rows=369766 width=6)
Index Cond: (deadts < now())
-> Index Scan using idx_tbl_2 on tbl tbl_1 (cost=0.43..590959.46 rows=51535 width=6)
Index Cond: (deadts_next < now())
-> Tid Scan on tbl tbl_2 (cost=0.01..12.21 rows=10 width=54)
TID Cond: (ctid = ANY ($0))
(12 rows)
Assuming that 0.1 billion data records are written, one question you may ask is how long does it take to process one million data records concurrently? Well, consider the following:
-- Processing of 0.1 billion data records is completed.
insert into tbl select id, 1, now(), '5 min', 0, null from generate_series(1,100000000) t(id);
-- Processing of one million data records times out.
insert into tbl select id, 0, now(), '5 min', 0, null from generate_series(1,1000000) t(id);
As you can see from the above code, the notification performance, for example, is generating a notification each time the processing of 10,000 records times out.
Following this, a small batch of data is retrieved, and the timeout is updated so that AutoVacuum can be introduced to collect garbage in real time. Consider the following:
with tmp1 as (
update tbl set
deadts_next=now()+nts,
notify_times=notify_times+1
where ctid = any (array(
select ctid from tbl where
( deadts < now() and notify_times=0 and state<>1)
union all
select ctid from tbl where
( deadts_next < now() and deadts_next is not null and state<>1)
limit 10000 -- Obtains 10,000 timeout data records at a time.
))
returning *
)
select * from tmp1;
-- Planning
CTE Scan on tmp1 (cost=18163.25..18163.45 rows=10 width=48) (actual time=39.092..78.707 rows=10000 loops=1)
Output: tmp1.id, tmp1.state, tmp1.deadts, tmp1.nts, tmp1.notify_times, tmp1.deadts_next
Buffers: shared hit=75094 read=49 dirtied=49
CTE tmp1
-> Update on public.tbl tbl_2 (cost=18151.05..18163.25 rows=10 width=54) (actual time=39.089..74.637 rows=10000 loops=1)
Output: tbl_2.id, tbl_2.state, tbl_2.deadts, tbl_2.nts, tbl_2.notify_times, tbl_2.deadts_next
Buffers: shared hit=75094 read=49 dirtied=49
InitPlan 1 (returns $0)
-> Limit (cost=0.13..18151.03 rows=10000 width=6) (actual time=31.265..36.899 rows=10000 loops=1)
Output: tbl.ctid
Buffers: shared hit=11395
-> Append (cost=0.13..764699.60 rows=421301 width=6) (actual time=31.264..35.354 rows=10000 loops=1)
Buffers: shared hit=11395
-> Index Scan using idx_tbl_1 on public.tbl (cost=0.13..169527.13 rows=369766 width=6) (actual time=0.014..0.014 rows=0 loops=1)
Output: tbl.ctid
Index Cond: (tbl.deadts < now())
Buffers: shared hit=1
-> Index Scan using idx_tbl_2 on public.tbl tbl_1 (cost=0.43..590959.46 rows=51535 width=6) (actual time=31.249..33.870 rows=10000 loops=1)
Output: tbl_1.ctid
Index Cond: (tbl_1.deadts_next < now())
Buffers: shared hit=11394
-> Tid Scan on public.tbl tbl_2 (cost=0.01..12.21 rows=10 width=54) (actual time=39.017..43.529 rows=10000 loops=1)
Output: tbl_2.id, tbl_2.state, tbl_2.deadts, tbl_2.nts, (tbl_2.notify_times + 1), (now() + tbl_2.nts), tbl_2.ctid
TID Cond: (tbl_2.ctid = ANY ($0))
Buffers: shared hit=21395
Planning time: 0.301 ms
Execution time: 79.905 ms
So as you can see, the data is processed smoothly.
Time: 79.905 ms
So to sum up, in this tutorial, I have introduced method by which you can solve the problem of timeout data monitoring in e-commerce scenarios that not only solves this issue but also delivers good performance.
Using PostgreSQL for Real-Time IoT Stream Processing Applications
Alibaba Cloud Native - November 16, 2023
Alibaba Clouder - April 28, 2021
Alibaba Cloud Native Community - December 11, 2023
Alibaba Developer - March 14, 2022
Alibaba Clouder - October 9, 2019
Alibaba Developer - April 7, 2020
Alibaba Cloud PolarDB for PostgreSQL is an in-house relational database service 100% compatible with PostgreSQL and highly compatible with the Oracle syntax.
Learn MoreAn online MPP warehousing service based on the Greenplum Database open source program
Learn MoreSet up an all-in-one live shopping platform quickly and simply and bring the in-person shopping experience to online audiences through a fast and reliable global network
Learn MoreAlibaba Cloud e-commerce solutions offer a suite of cloud computing and big data services.
Learn MoreMore Posts by digoal