public void update(String attemptId, boolean isRetry, String topic, String group, int queueId, long popTime, long invisibleTime,
        List<Long> msgQueueOffsetList, StringBuilder orderInfoBuilder) {
        String key = buildKey(topic, group);
        ConcurrentHashMap<Integer/*queueId*/, OrderInfo> qs = table.get(key);
        if (qs == null) {
            qs = new ConcurrentHashMap<>(16);
            ConcurrentHashMap<Integer/*queueId*/, OrderInfo> old = table.putIfAbsent(key, qs);
            if (old != null) {
                qs = old;
            }
        }

        OrderInfo orderInfo = qs.get(queueId);

        if (orderInfo != null) {
            OrderInfo newOrderInfo = new OrderInfo(attemptId, popTime, invisibleTime, msgQueueOffsetList, System.currentTimeMillis(), 0);
            newOrderInfo.mergeOffsetConsumedCount(orderInfo.attemptId, orderInfo.offsetList, orderInfo.offsetConsumedCount);

            orderInfo = newOrderInfo;
        } else {
            orderInfo = new OrderInfo(attemptId, popTime, invisibleTime, msgQueueOffsetList, System.currentTimeMillis(), 0);
        }
        qs.put(queueId, orderInfo);

        Map<Long, Integer> offsetConsumedCount = orderInfo.offsetConsumedCount;
        int minConsumedTimes = Integer.MAX_VALUE;
        if (offsetConsumedCount != null) {
            Set<Long> offsetSet = offsetConsumedCount.keySet();
            for (Long offset : offsetSet) {
                Integer consumedTimes = offsetConsumedCount.getOrDefault(offset, 0);
                ExtraInfoUtil.buildQueueOffsetOrderCountInfo(orderInfoBuilder, topic, queueId, offset, consumedTimes);
                minConsumedTimes = Math.min(minConsumedTimes, consumedTimes);
            }

            if (offsetConsumedCount.size() != orderInfo.offsetList.size()) {
                // offsetConsumedCount only save messages which consumed count is greater than 0
                // if size not equal, means there are some new messages
                minConsumedTimes = 0;
            }
        } else {
            minConsumedTimes = 0;
        }

        // for compatibility
        // the old pop sdk use queueId to get consumedTimes from orderCountInfo
        ExtraInfoUtil.buildQueueIdOrderCountInfo(orderInfoBuilder, topic, queueId, minConsumedTimes);
        updateLockFreeTimestamp(topic, group, queueId, orderInfo);
    }
@Test
    public void testConsumedCountForMultiQueue() {
        {
            // consume two new messages
            StringBuilder orderInfoBuilder = new StringBuilder();
            consumerOrderInfoManager.update(
                null,
                false,
                TOPIC,
                GROUP,
                QUEUE_ID_0,
                popTime,
                3000,
                Lists.newArrayList(0L),
                orderInfoBuilder
            );
            consumerOrderInfoManager.update(
                null,
                false,
                TOPIC,
                GROUP,
                QUEUE_ID_1,
                popTime,
                3000,
                Lists.newArrayList(0L),
                orderInfoBuilder
            );
            assertEncodeAndDecode();
            Map<String, Integer> orderInfoMap = ExtraInfoUtil.parseOrderCountInfo(orderInfoBuilder.toString());
            assertEquals(2, orderInfoMap.size());
            assertEquals(0, orderInfoMap.get(ExtraInfoUtil.getStartOffsetInfoMapKey(TOPIC, QUEUE_ID_0)).intValue());
            assertEquals(0, orderInfoMap.get(ExtraInfoUtil.getStartOffsetInfoMapKey(TOPIC, QUEUE_ID_1)).intValue());
        }
        {
            // reconsume two message
            StringBuilder orderInfoBuilder = new StringBuilder();
            consumerOrderInfoManager.update(
                null,
                false,
                TOPIC,
                GROUP,
                QUEUE_ID_0,
                popTime,
                3000,
                Lists.newArrayList(0L),
                orderInfoBuilder
            );
            consumerOrderInfoManager.update(
                null,
                false,
                TOPIC,
                GROUP,
                QUEUE_ID_1,
                popTime,
                3000,
                Lists.newArrayList(0L),
                orderInfoBuilder
            );
            assertEncodeAndDecode();
            Map<String, Integer> orderInfoMap = ExtraInfoUtil.parseOrderCountInfo(orderInfoBuilder.toString());
            assertEquals(4, orderInfoMap.size());
            assertEquals(1, orderInfoMap.get(ExtraInfoUtil.getStartOffsetInfoMapKey(TOPIC, QUEUE_ID_0)).intValue());
            assertEquals(1, orderInfoMap.get(ExtraInfoUtil.getStartOffsetInfoMapKey(TOPIC, QUEUE_ID_1)).intValue());
            assertEquals(1, orderInfoMap.get(ExtraInfoUtil.getQueueOffsetMapKey(TOPIC, QUEUE_ID_0, 0L)).intValue());
            assertEquals(1, orderInfoMap.get(ExtraInfoUtil.getQueueOffsetMapKey(TOPIC, QUEUE_ID_1, 0L)).intValue());
        }
        {
            // reconsume with a new message
            StringBuilder orderInfoBuilder = new StringBuilder();
            consumerOrderInfoManager.update(
                null,
                false,
                TOPIC,
                GROUP,
                QUEUE_ID_0,
                popTime,
                3000,
                Lists.newArrayList(0L, 1L),
                orderInfoBuilder
            );
            consumerOrderInfoManager.update(
                null,
                false,
                TOPIC,
                GROUP,
                QUEUE_ID_1,
                popTime,
                3000,
                Lists.newArrayList(0L),
                orderInfoBuilder
            );
            assertEncodeAndDecode();
            Map<String, Integer> orderInfoMap = ExtraInfoUtil.parseOrderCountInfo(orderInfoBuilder.toString());
            assertEquals(4, orderInfoMap.size());
            assertEquals(0, orderInfoMap.get(ExtraInfoUtil.getStartOffsetInfoMapKey(TOPIC, QUEUE_ID_0)).intValue());
            assertEquals(2, orderInfoMap.get(ExtraInfoUtil.getStartOffsetInfoMapKey(TOPIC, QUEUE_ID_1)).intValue());
            assertEquals(2, orderInfoMap.get(ExtraInfoUtil.getQueueOffsetMapKey(TOPIC, QUEUE_ID_0, 0L)).intValue());
            assertNull(orderInfoMap.get(ExtraInfoUtil.getQueueOffsetMapKey(TOPIC, QUEUE_ID_0, 1L)));
            assertEquals(2, orderInfoMap.get(ExtraInfoUtil.getQueueOffsetMapKey(TOPIC, QUEUE_ID_1, 0L)).intValue());
        }
    }