public TopicStatsImpl add(TopicStats ts) {
        TopicStatsImpl stats = (TopicStatsImpl) ts;

        this.count++;
        this.msgRateIn += stats.msgRateIn;
        this.msgThroughputIn += stats.msgThroughputIn;
        this.msgRateOut += stats.msgRateOut;
        this.msgThroughputOut += stats.msgThroughputOut;
        this.bytesInCounter += stats.bytesInCounter;
        this.msgInCounter += stats.msgInCounter;
        this.bytesOutCounter += stats.bytesOutCounter;
        this.msgOutCounter += stats.msgOutCounter;
        this.waitingPublishers += stats.waitingPublishers;
        double newAverageMsgSize = (this.averageMsgSize * (this.count - 1) + stats.averageMsgSize) / this.count;
        this.averageMsgSize = newAverageMsgSize;
        this.storageSize += stats.storageSize;
        this.backlogSize += stats.backlogSize;
        this.publishRateLimitedTimes += stats.publishRateLimitedTimes;
        this.offloadedStorageSize += stats.offloadedStorageSize;
        this.nonContiguousDeletedMessagesRanges += stats.nonContiguousDeletedMessagesRanges;
        this.nonContiguousDeletedMessagesRangesSerializedSize += stats.nonContiguousDeletedMessagesRangesSerializedSize;
        this.delayedMessageIndexSizeInBytes += stats.delayedMessageIndexSizeInBytes;
        this.ongoingTxnCount = stats.ongoingTxnCount;
        this.abortedTxnCount = stats.abortedTxnCount;
        this.committedTxnCount = stats.committedTxnCount;
        this.backlogQuotaLimitTime = stats.backlogQuotaLimitTime;
        this.backlogQuotaLimitSize = stats.backlogQuotaLimitSize;
        if (stats.oldestBacklogMessageAgeSeconds > this.oldestBacklogMessageAgeSeconds) {
            this.oldestBacklogMessageAgeSeconds = stats.oldestBacklogMessageAgeSeconds;
            this.oldestBacklogMessageSubscriptionName = stats.oldestBacklogMessageSubscriptionName;
        }

        stats.bucketDelayedIndexStats.forEach((k, v) -> {
            TopicMetricBean topicMetricBean =
                    this.bucketDelayedIndexStats.computeIfAbsent(k, __ -> new TopicMetricBean());
            topicMetricBean.name = v.name;
            topicMetricBean.labelsAndValues = v.labelsAndValues;
            topicMetricBean.value += v.value;
        });

        List<? extends PublisherStats> publisherStats = stats.getPublishers();
        for (int index = 0; index < publisherStats.size(); index++) {
           PublisherStats s = publisherStats.get(index);
           if (s.isSupportsPartialProducer() && s.getProducerName() != null) {
               this.publishersMap.computeIfAbsent(s.getProducerName(), key -> {
                   final PublisherStatsImpl newStats = new PublisherStatsImpl();
                   newStats.setSupportsPartialProducer(true);
                   newStats.setProducerName(s.getProducerName());
                   return newStats;
               }).add((PublisherStatsImpl) s);
           } else {
               // Add a publisher stat entry to this.publishers
               // if this.publishers.size() is smaller than
               // the input stats.publishers.size().
               // Here, index == this.publishers.size() means
               // this.publishers.size() is smaller than the input stats.publishers.size()
               if (index == this.publishers.size()) {
                   PublisherStatsImpl newStats = new PublisherStatsImpl();
                   newStats.setSupportsPartialProducer(false);
                   this.publishers.add(newStats);
               }
               this.publishers.get(index)
                       .add((PublisherStatsImpl) s);
           }
        }

        for (Map.Entry<String, SubscriptionStatsImpl> entry : stats.subscriptions.entrySet()) {
            SubscriptionStatsImpl subscriptionStats =
                    this.subscriptions.computeIfAbsent(entry.getKey(), k -> new SubscriptionStatsImpl());
            subscriptionStats.add(entry.getValue());
        }

        for (Map.Entry<String, ReplicatorStatsImpl> entry : stats.replication.entrySet()) {
            ReplicatorStatsImpl replStats =
                    this.replication.computeIfAbsent(entry.getKey(), k -> {
                        ReplicatorStatsImpl r = new ReplicatorStatsImpl();
                        r.setConnected(true);
                        return r;
                    });
            replStats.add(entry.getValue());
        }

        if (earliestMsgPublishTimeInBacklogs != 0 && ((TopicStatsImpl) ts).earliestMsgPublishTimeInBacklogs != 0) {
            earliestMsgPublishTimeInBacklogs = Math.min(
                    earliestMsgPublishTimeInBacklogs,
                    ((TopicStatsImpl) ts).earliestMsgPublishTimeInBacklogs
            );
        } else {
            earliestMsgPublishTimeInBacklogs = Math.max(
                    earliestMsgPublishTimeInBacklogs,
                    ((TopicStatsImpl) ts).earliestMsgPublishTimeInBacklogs
            );
        }
        return this;
    }
@Test
    public void testAdd_EarliestMsgPublishTimeInBacklogs_Earliest() {
        TopicStatsImpl stats1 = new TopicStatsImpl();
        stats1.earliestMsgPublishTimeInBacklogs = 10L;

        TopicStatsImpl stats2 = new TopicStatsImpl();
        stats2.earliestMsgPublishTimeInBacklogs = 20L;

        TopicStatsImpl aggregate = stats1.add(stats2);
        assertEquals(aggregate.earliestMsgPublishTimeInBacklogs, 10L);
    }