Timer Program Design Sharing

Keywords: Programming SQL Java Database

Timer Programming Sharing

Timer Program Design Sharing

1. Background

The timer program referred to in this paper is not a narrow timer program, but a general way to achieve certain business scenarios through the timer trigger program, combined with the database.

2. Scheme Analysis

3.1 Scene 1

3.1.1 Requirements

We need to push completed order data to third-party systems.

3.1.2 SQL

DROP TABLE IF EXISTS order01;
CREATE TABLE order01 (
    order_id             BIGINT UNSIGNED           NOT NULL                COMMENT 'Order ID',
    order_desc           VARCHAR(256)              NOT NULL     DEFAULT '' COMMENT 'Order Description',
    order_state          TINYINT UNSIGNED          NOT NULL     DEFAULT 0  COMMENT 'Order Status[0:initial, 10:Paid, ... 100:Completed]',
    order_push_state     TINYINT UNSIGNED          NOT NULL     DEFAULT 0  COMMENT 'Order Push Status[0:initial, 100:Push Success]',
    create_time          DATETIME                  NOT NULL                COMMENT 'Creation Time',
    update_time          DATETIME                      NULL                COMMENT 'Modification Time',
    PRIMARY KEY (order_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='Order form (example 01)';

3.1.3 Services

SQL Service

<!-- Get a push order -->
<selectOne id="getOrder01" dsKey="timerReadDB" txRef="tx_01">
    select * from order01 where order_state = 100 AND order_push_state = 0 LIMIT 1
</selectOne>
<!-- Complete a push for an order -->
<update id="completePushOrder01" dsKey="timerWriteDB" txRef="tx_02">
    update order01 set
        order_push_state = 100, 
        update_time = #{push_update_time|now()}
    where
        order_id = #{order_id} AND
        order_push_state = 0
</update>

JAVA Service

/**
 * Scheme 3.1 Related Push Service Entry
 */
public void pushOrder01(XCO request) {
    // 1. Get an order to push
    XCO getRes = ServiceActuator.executeAlone("timer/getOrder01", new XCO());
    if (0 != getRes.getCode()) {
        log.error("Get an order exception to push: " + getRes.getMessage());
        return;
    }
    XCO order = getRes.getData();
    if (null == order) {
        return;// No pending orders
    }

    // 2. Push to third parties
    ServiceActuator.executeAlone("timerService/receiveOrder", order);

    // 3. Mark that this order has been pushed
    ServiceActuator.executeAlone("timer/completePushOrder01", order);
}

3.1.4 Timer

<!-- Option 3.1 Relevant Timer -->
<timer scheduled="/10 * * * * ?" service="timerService/pushOrder01" desc="Push Order (Option 3).1)" sync="true"/>

3.2 Scene 2

3.2.1 Requirements

We need to push the completed order data to a third-party system. If the push fails, the order will need to be delayed for 10 minutes before the next push can be initiated.

3.2.2 SQL

DROP TABLE IF EXISTS order02;
CREATE TABLE order02 (
    order_id             BIGINT UNSIGNED           NOT NULL                COMMENT 'Order ID',
    order_desc           VARCHAR(256)              NOT NULL     DEFAULT '' COMMENT 'Order Description',
    order_state          TINYINT UNSIGNED          NOT NULL     DEFAULT 0  COMMENT 'Order Status[0:initial, 10:Paid, ... 100:Completed]',
    order_push_state     TINYINT UNSIGNED          NOT NULL     DEFAULT 0  COMMENT 'Order Push Status[0:initial, 90:Push failed, 100:Push Success]',
    scheduled_push_time  DATETIME                  NOT NULL                COMMENT 'Schedule Push Time',
    create_time          DATETIME                  NOT NULL                COMMENT 'Creation Time',
    update_time          DATETIME                      NULL                COMMENT 'Modification Time',
    PRIMARY KEY (order_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='Order form (example 02)';

3.2.3 Services

SQL Service

<selectOne id="getOrder02" dsKey="timerReadDB" txRef="tx_01"><![CDATA[
    select * from order02 where order_state = 100 AND order_push_state = 0 AND #{current_push_time|now()} >= scheduled_push_time LIMIT 1
]]></selectOne>
<update id="completePushOrder02" dsKey="timerWriteDB" txRef="tx_02">
    <if test="{flag} == 0">
        update order02 set
            order_push_state = 100, 
            update_time = #{time|now()}
        where
            order_id = #{order_id} AND
            order_push_state = 0
    </if>
    <else>
        update order02 set
            scheduled_push_time = #{scheduled_push_time}, 
            update_time = #{time|now()}
        where
            order_id = #{order_id} AND
            order_push_state = 0
    </else>
</update>

JAVA Service

/**
 * Option 3.2 related push service entry, if the push fails, the order will need to be delayed for 10 minutes before the next push can be initiated.
 */
public void pushOrder02(XCO request) {
    // 1. Get an order to push
    XCO getRes = ServiceActuator.executeAlone("timer/getOrder02", new XCO());
    if (0 != getRes.getCode()) {
        log.error("Get an order exception to push: " + getRes.getMessage());
        return;
    }
    XCO order = getRes.getData();
    if (null == order) {
        return;// No pending orders
    }

    // 2. Push to third parties
    XCO receiveRes = ServiceActuator.executeAlone("timerService/receiveOrder", order);
    int flag = 0;
    if (0 != receiveRes.getCode()) {
        // Push failed
        Date scheduled_push_time = order.getDateTimeValue("scheduled_push_time");
        // Add 10 minutes
        scheduled_push_time.setTime(scheduled_push_time.getTime() + 10L * 60L * 1000L);
        flag = 1;
    }
    order.setIntegerValue("flag", flag);

    // 3. Mark that this order has been pushed
    ServiceActuator.executeAlone("timer/completePushOrder02", order);
}

3.2.4 Timer

<!-- Option 3.2 Relevant Timer -->
<timer scheduled="/10 * * * * ?" service="timerService/pushOrder02" desc="Push Order (Option 3).2)" sync="true"/>

3.3 Scene 3

3.3.1 Requirements

We need to push the completed order data to a third-party system. If the push fails, the order will need to be delayed for 10 minutes before the next push can be initiated and up to three attempts can be made.

3.3.2 SQL

DROP TABLE IF EXISTS order03;
CREATE TABLE order03 (
    order_id             BIGINT UNSIGNED           NOT NULL                COMMENT 'Order ID',
    order_desc           VARCHAR(256)              NOT NULL     DEFAULT '' COMMENT 'Order Description',
    order_state          TINYINT UNSIGNED          NOT NULL     DEFAULT 0  COMMENT 'Order Status[0:initial, 10:Paid, ... 100:Completed]',
    order_push_state     TINYINT UNSIGNED          NOT NULL     DEFAULT 0  COMMENT 'Order Push Status[0:initial, 90:Push failed, 100:Push Success]',
    retry_count          BIGINT UNSIGNED           NOT NULL     DEFAULT 0  COMMENT 'retry count',
    scheduled_push_time  DATETIME                  NOT NULL                COMMENT 'Schedule Push Time',
    create_time          DATETIME                  NOT NULL                COMMENT 'Creation Time',
    update_time          DATETIME                      NULL                COMMENT 'Modification Time',
    PRIMARY KEY (order_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='Order form (example 03)';

3.3.3 Services

SQL Service

<selectOne id="getOrder03" dsKey="timerReadDB" txRef="tx_01"><![CDATA[
    select * from order03 where order_state = 100 AND order_push_state = 0 AND #{current_push_time|now()} >= scheduled_push_time LIMIT 1
]]></selectOne> 
<update id="completePushOrder03" dsKey="timerWriteDB" txRef="tx_02">
    <if test="{flag} == 0">
        update order03 set
            order_push_state = 100, 
            update_time = #{time|now()}
        where
            order_id = #{order_id} AND
            order_push_state = 0
    </if>
    <elseif test="{flag} == 1">
        update order03 set
            retry_count = retry_count + 1,
            scheduled_push_time = #{scheduled_push_time}, 
            update_time = #{time|now()}
        where
            order_id = #{order_id} AND
            order_push_state = 0        
    </elseif>
    <else>
        update order03 set
            order_push_state = 90, 
            update_time = #{time|now()}
        where
            order_id = #{order_id} AND
            order_push_state = 0
    </else>
</update>

JAVA Service

/**
 * Option 3.2 related push service entry, if the push fails, the order will need to be delayed for 10 minutes before the next push can be initiated and up to three attempts can be made.
 */
public void pushOrder03(XCO request) {
    // 1. Get an order to push
    XCO getRes = ServiceActuator.executeAlone("timer/getOrder03", new XCO());
    if (0 != getRes.getCode()) {
        log.error("Get an order exception to push: " + getRes.getMessage());
        return;
    }
    XCO order = getRes.getData();
    if (null == order) {
        return;// No pending orders
    }

    // 2. Push to third parties
    XCO receiveRes = ServiceActuator.executeAlone("timerService/receiveOrder", order);
    int flag = 0;
    if (0 != receiveRes.getCode()) {
        // Push failed
        Date scheduled_push_time = order.getDateTimeValue("scheduled_push_time");
        // Add 10 minutes
        scheduled_push_time.setTime(scheduled_push_time.getTime() + 10L * 60L * 1000L);
        flag = 1;
        // Number of attempts interpretation
        if (receiveRes.getIntegerValue("retry_count") > 2) {
            flag = 2;
        }
    }
    order.setIntegerValue("flag", flag);

    // 3. Mark that this order has been pushed
    ServiceActuator.executeAlone("timer/completePushOrder02", order);
}

3.3.4 Timer

<!-- Option 3.3 Relevant Timer -->
<timer scheduled="/10 * * * * ?" service="timerService/pushOrder03" desc="Push Order (Option 3).3)" sync="true"/>

3.4 Technical optimization scheme 1 for 3.1

3.4.1 Requirements

Requirements same as 3.1.1, Technical optimization point: Get multiple tasks at once

3.4.2 SQL

Same as 3.1.2

3.4.3 Services

SQL Service

<selectSet id="getOrderList01" dsKey="timerReadDB" txRef="tx_01">
    select * from order01 where order_state = 100 AND order_push_state = 0 LIMIT 100
</selectSet>

JAVA Service

/**
 * Scenario 3.4 Related Push Service Entry, Get Multiple Tasks at a Time
 */
public void pushOrder04(XCO request) {
    // 1. Get multiple pending orders
    XCO getRes = ServiceActuator.executeAlone("timer/getOrderList01", new XCO());
    if (0 != getRes.getCode()) {
        log.error("Get multiple push order exceptions: " + getRes.getMessage());
        return;
    }
    List<XCO> orders = getRes.getData();
    if (CollectionUtils.isEmpty(orders)) {
        return;// No pending orders
    }
    for (XCO order : orders) {
        // 2. Push to third parties
        ServiceActuator.executeAlone("timerService/receiveOrder", order);
        // 3. Mark that this order has been pushed
        ServiceActuator.executeAlone("timer/completePushOrder01", order);
    }
}

3.4.4 Timer

<!-- Option 3.4 Relevant Timer -->
<timer scheduled="/10 * * * * ?" service="timerService/pushOrder04" desc="Push Order (Option 3).4)" sync="true"/>

3.5 Technical optimization scheme for 3.1 2

3.5.1 Requirements

Requirements same as 3.1.1, Technical optimization point: Get multiple tasks at once and support multiple Timer s at the same time

3.5.2 SQL

Same as 3.1.2

3.5.3 Services

SQL Service

<selectSet id="getOrderList02" dsKey="timerReadDB" txRef="tx_01">
    select * from order01 where order_state = 100 AND order_push_state = 0 AND (order_id % #{tt_total} = #{tt_index}) LIMIT 100
</selectSet>

JAVA Service

/**
 * Scenario 3.5 related push service entry, get multiple tasks at a time, and support multiple Timer s at the same time
 */
public void pushOrder05(XCO request) {
    // 1. Get multiple pending orders
    XCO getRes = ServiceActuator.executeAlone("timer/getOrderList02", request);
    if (0 != getRes.getCode()) {
        log.error("Get multiple push order exceptions: " + getRes.getMessage());
        return;
    }
    List<XCO> orders = getRes.getData();
    if (CollectionUtils.isEmpty(orders)) {
        return;// No pending orders
    }
    for (XCO order : orders) {
        // 2. Push to third parties
        ServiceActuator.executeAlone("timerService/receiveOrder", order);
        // 3. Mark that this order has been pushed
        ServiceActuator.executeAlone("timer/completePushOrder01", order);
    }
}

3.5.4 Timer

<!-- Option 3.5 Relevant Timer -->
<timer scheduled="/10 * * * * ?" service="timerService/pushOrder05" desc="Push Order (Option 3).5)[0]" sync="true">
    <property name="tt_index" value="0"/>
    <property name="tt_total" value="5"/>
</timer>
<timer scheduled="/10 * * * * ?" service="timerService/pushOrder05" desc="Push Order (Option 3).5)[1]" sync="true">
    <property name="tt_index" value="1"/>
    <property name="tt_total" value="5"/>
</timer>
<timer scheduled="/10 * * * * ?" service="timerService/pushOrder05" desc="Push Order (Option 3).5)[2]" sync="true">
    <property name="tt_index" value="2"/>
    <property name="tt_total" value="5"/>
</timer>
<timer scheduled="/10 * * * * ?" service="timerService/pushOrder05" desc="Push Order (Option 3).5)[3]" sync="true">
    <property name="tt_index" value="3"/>
    <property name="tt_total" value="5"/>
</timer>
<timer scheduled="/10 * * * * ?" service="timerService/pushOrder05" desc="Push Order (Option 3).5)[4]" sync="true">
    <property name="tt_index" value="4"/>
    <property name="tt_total" value="5"/>
</timer>

3. Program Summary

SN Scenario Name describe
1 Scenario One Simple, regardless of failure
2 Scenario Two Increase support for deferred push after failure
3 Scene Three Increase maximum number of attempts after failure control
4 Scene 1 Optimization Get more than one push task at a time
5 Scene 1 Optimization Support multiple Timer s for parallel processing at the same time

In the actual development process, we should choose the appropriate implementation according to the specific business scenario and needs.

4. Attachments

  1. SQL script and project source

Posted by KGodwin on Wed, 18 Dec 2019 23:37:24 -0800