Performance test of order cancellation with 10s delay

I once naively thought that I was invincible in the performance test scenario, but the reality patted me again.

The R & D put forward a pressure test requirement of canceling orders after a delay of 10s, which really confused me for a while. Finally, I referred to Java's delay queue java.util.concurrent.DelayQueue to realize this requirement.

demand

In one scenario, my previous design was to place an order and cancel the order if it was successful, because the test user's assets were not exhausted.

Recently, R & D has optimized the service function logic. In the regression test, it is found that the test results of this use case are quite different from those before. The R & D judgment may be caused by the short interval between placing and canceling orders. Therefore, it is necessary to conduct pressure test for delayed cancellation.

thinking

The thread safe queue class java.util.concurrent.DelayQueue is used here. You can understand its purpose by looking at its name. There is another scheme, java.util.Timer, but it will create additional threads and it is difficult to control the cancellation rate, so I gave it up this time.

First, when placing an order, if successful, the order number will be recorded in the java.util.concurrent.DelayQueue queue through a java.util.concurrent.DelayQueue implementation class, and then the expired order number will be obtained from the delay queue in the com.oktest.okex.algo.trade.OrderAndCancelGrid3.FunTester#doing method, and then the order will be cancelled.

The following is a demonstration video of the delayQueue function

realization

The order ID is of java.lang.String type, and the implementation class of java.util.concurrent.Delayed is as follows:

    private static class FunDelay implements Delayed {

        int time

        String id

        FunDelay(String id) {
            this.time = getMark() + delay
            this.id = id
        }

        @Override
        public long getDelay(TimeUnit unit) {
            return time - getMark();
        }

        @Override
        public int compareTo(Delayed o) {
            FunDelay item = (FunDelay) o;
            if (this.time < item.time) {
                return -1;
            } else {
                return 1;
            }
        }
    }

test case

Most of the content reuses the previous design, rewrites the after method, and delays the unprocessed order ID in the queue after the user ends the test.

    private static class FunTester extends FixedThread<Trade> {

        DelayQueue<FunDelay> ids = new DelayQueue<>()


        FunTester(Trade o, int limit) {
            super(o, limit, true)
        }

        @Override
        protected void doing() throws Exception {
            def res = f.order(********)
            if (0 == res.getInteger("code")) {
                def array = res.getJSONArray("data").get(0)
                def id = array.getString("Id")
                ids.add(new FunDelay(id))
            } else {
                output(res.toString())
            }
            int i = 0
            while (true) {
                if (i++ > 10) break
                def poll = ids.poll()
                if (poll == null) break
                f.stop("***", poll.id)
            }
        }

        @Override
        protected void after() {
            super.after()
            sleep(1.0)
            f.clearGrid()
        }

        @Override
        ThreadBase clone() {
            return new FunTester(f, limit)
        }


    }

implement

The execution method reuses the previous content and adds the fourth parameter to parameterize the delay time.

    static int delay;

    public static void main(String[] args) {
        ClientManage.init(10, 5, 0, "", 0)
        def util = new ArgsUtil(args)
        def thread = util.getIntOrdefault(0, 1)
        def times = util.getIntOrdefault(1, 1000)
        def runuptime = util.getIntOrdefault(2, 0)
        delay = util.getIntOrdefault(3, 3)
        Constant.RUNUP_TIME = runuptime
        Common.PRINT_RESPONSE = false
        Common.VERIFY = false
        FunLibrary.LOG_KEY = false
        Common.MOCO = true
        Common.setHost()
        def line = DataUtils.getMocoTokens()
        def tasks = []
        thread.times {
            def base = getBase(line.get(getRandomInt(500)))
            def drive = new Trade(base)
            tasks << new FunTester(drive, times)
        }

        new Concurrent(tasks, "Orders and delays 10 s cancel the order").start()

        over()
    }

test result

I used the default delay of 3s. The following is a screenshot of the log during execution:

Test results of order cancellation with 10s delay

As can be seen in the figure, orders are placed in 17s and cancelled in 20s. Perfect realization of demand!

Posted by SN1P3R_85 on Thu, 02 Dec 2021 21:55:33 -0800