You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ignite.apache.org by dp...@apache.org on 2019/08/12 15:14:17 UTC

[ignite-teamcity-bot] branch master updated: Counters for tracking updates for particular branches implementation - Fixes #148.

This is an automated email from the ASF dual-hosted git repository.

dpavlov pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/ignite-teamcity-bot.git


The following commit(s) were added to refs/heads/master by this push:
     new 2aaeaf2  Counters for tracking updates for particular branches implementation - Fixes #148.
2aaeaf2 is described below

commit 2aaeaf2bf15cdfbaf4ac3b0619ba77b01ed8c204
Author: Dmitriy Pavlov <dp...@apache.org>
AuthorDate: Mon Aug 12 18:12:31 2019 +0300

    Counters for tracking updates for particular branches implementation - Fixes #148.
    
    Signed-off-by: Dmitriy Pavlov <dp...@apache.org>
---
 ...es.java => GetSingleBuildTestFailuresRest.java} |  86 ++++++------------
 .../ignite/ci/web/rest/pr/GetPrTestFailures.java   |  21 ++---
 .../rest/tracked/GetTrackedBranchTestResults.java  |  32 +++----
 ignite-tc-helper-web/src/main/webapp/all.html      |  21 ++---
 ignite-tc-helper-web/src/main/webapp/build.html    |  33 +++----
 ignite-tc-helper-web/src/main/webapp/current.html  |  27 +++---
 ignite-tc-helper-web/src/main/webapp/pr.html       |  29 +++---
 .../ignite/tcbot/engine/TcBotEngineModule.java     |   3 +-
 .../engine/build/SingleBuildResultsService.java    | 100 +++++++++++++++++++++
 .../tcbot/engine/chain/BuildChainProcessor.java    |  26 ++++--
 .../ignite/tcbot/engine/chain/FullChainRunCtx.java |   8 --
 .../ignite/tcbot/engine/chain/MultBuildRunCtx.java |   4 -
 .../tcbot/engine/chain/SingleBuildRunCtx.java      |   8 +-
 .../ignite/tcbot/engine/pr/PrChainsProcessor.java  |  28 ++++--
 .../tracked/IDetailedStatusForTrackedBranch.java   |   3 +
 .../tracked/TrackedBranchChainsProcessor.java      |  40 +++++++--
 .../apache/ignite/tcbot/engine/ui/DsSummaryUi.java |   6 --
 .../apache/ignite/tcbot/engine/ui/UpdateInfo.java  |  27 +++---
 .../ignite/tcignited/TeamcityIgnitedModule.java    |   2 +
 .../apache/ignite/tcignited/build/FatBuildDao.java |   5 ++
 .../tcignited/build/UpdateCountersStorage.java     |  57 ++++++++++++
 .../ignite/tcignited/buildref/BuildRefDao.java     |   9 ++
 22 files changed, 376 insertions(+), 199 deletions(-)

diff --git a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/web/rest/build/GetBuildTestFailures.java b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/web/rest/build/GetSingleBuildTestFailuresRest.java
similarity index 69%
rename from ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/web/rest/build/GetBuildTestFailures.java
rename to ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/web/rest/build/GetSingleBuildTestFailuresRest.java
index 316b447..abd9ed7 100644
--- a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/web/rest/build/GetBuildTestFailures.java
+++ b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/web/rest/build/GetSingleBuildTestFailuresRest.java
@@ -20,8 +20,6 @@ package org.apache.ignite.ci.web.rest.build;
 import com.google.common.collect.BiMap;
 import com.google.inject.Injector;
 import java.text.ParseException;
-import java.util.Collections;
-import java.util.concurrent.atomic.AtomicInteger;
 import javax.servlet.ServletContext;
 import javax.servlet.http.HttpServletRequest;
 import javax.ws.rs.GET;
@@ -30,34 +28,28 @@ import javax.ws.rs.Produces;
 import javax.ws.rs.QueryParam;
 import javax.ws.rs.core.Context;
 import javax.ws.rs.core.MediaType;
-import org.apache.ignite.tcbot.engine.chain.FullChainRunCtx;
-import org.apache.ignite.tcbot.engine.chain.LatestRebuildMode;
-import org.apache.ignite.tcbot.engine.chain.ProcessLogsMode;
-import org.apache.ignite.tcbot.engine.chain.BuildChainProcessor;
-import org.apache.ignite.tcbot.engine.conf.ITcBotConfig;
 import org.apache.ignite.ci.tcbot.trends.MasterTrendsService;
 import org.apache.ignite.ci.teamcity.ignited.buildcondition.BuildCondition;
 import org.apache.ignite.ci.user.ITcBotUserCreds;
 import org.apache.ignite.ci.web.CtxListener;
-import org.apache.ignite.tcbot.engine.ui.DsChainUi;
-import org.apache.ignite.tcbot.engine.ui.DsSummaryUi;
-import org.apache.ignite.tcbot.engine.ui.UpdateInfo;
 import org.apache.ignite.ci.web.model.trends.BuildStatisticsSummary;
 import org.apache.ignite.ci.web.model.trends.BuildsHistory;
 import org.apache.ignite.tcbot.common.exeption.ServiceUnauthorizedException;
-import org.apache.ignite.tcbot.persistence.IStringCompactor;
+import org.apache.ignite.tcbot.engine.build.SingleBuildResultsService;
+import org.apache.ignite.tcbot.engine.conf.ITcBotConfig;
+import org.apache.ignite.tcbot.engine.ui.DsSummaryUi;
+import org.apache.ignite.tcbot.engine.ui.UpdateInfo;
 import org.apache.ignite.tcignited.ITeamcityIgnited;
 import org.apache.ignite.tcignited.ITeamcityIgnitedProvider;
 import org.apache.ignite.tcignited.SyncMode;
-import org.apache.ignite.tcservice.ITeamcity;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
 
 import static com.google.common.base.Strings.isNullOrEmpty;
 
-@Path(GetBuildTestFailures.BUILD)
+@Path(GetSingleBuildTestFailuresRest.BUILD)
 @Produces(MediaType.APPLICATION_JSON)
-public class GetBuildTestFailures {
+public class GetSingleBuildTestFailuresRest {
     public static final String BUILD = "build";
 
     /** Context. */
@@ -71,20 +63,26 @@ public class GetBuildTestFailures {
     @GET
     @Path("failures/updates")
     public UpdateInfo getTestFailsUpdates(
-        @QueryParam("serverId") String srvId,
+        @QueryParam("serverId") String srvCodeOrAlias,
         @QueryParam("buildId") Integer buildId,
         @Nullable @QueryParam("checkAllLogs") Boolean checkAllLogs) throws ServiceUnauthorizedException {
-        return new UpdateInfo().copyFrom(getBuildTestFailsNoSync(srvId, buildId, checkAllLogs));
+        UpdateInfo res = new UpdateInfo();
+
+        res.initCounters(CtxListener.getInjector(ctx)
+            .getInstance(SingleBuildResultsService.class)
+            .getBranchCntrs(srvCodeOrAlias, buildId, ITcBotUserCreds.get(req)));
+
+        return res;
     }
 
     @GET
     @Path("failures/txt")
     @Produces(MediaType.TEXT_PLAIN)
     public String getTestFailsText(
-        @QueryParam("serverId") String srvId,
+        @QueryParam("serverId") String srvCodeOrAlias,
         @QueryParam("buildId") Integer buildId,
         @Nullable @QueryParam("checkAllLogs") Boolean checkAllLogs) throws ServiceUnauthorizedException {
-        return getBuildTestFails(srvId, buildId, checkAllLogs).toString();
+        return getBuildTestFails(srvCodeOrAlias, buildId, checkAllLogs).toString();
     }
 
     @GET
@@ -105,51 +103,17 @@ public class GetBuildTestFailures {
         return collectBuildCtxById(srvId, buildId, checkAllLogs, SyncMode.RELOAD_QUEUED);
     }
 
-    @NotNull public DsSummaryUi collectBuildCtxById(@QueryParam("serverId") String srvCode,
-                                                    @QueryParam("buildId") Integer buildId,
-                                                    @QueryParam("checkAllLogs") @Nullable Boolean checkAllLogs, SyncMode syncMode) {
-        final ITcBotUserCreds prov = ITcBotUserCreds.get(req);
-        final Injector injector = CtxListener.getInjector(ctx);
-        ITeamcityIgnitedProvider tcIgnitedProv = injector.getInstance(ITeamcityIgnitedProvider.class);
-        final BuildChainProcessor buildChainProcessor = injector.getInstance(BuildChainProcessor.class);
-
-        final DsSummaryUi res = new DsSummaryUi();
-        final AtomicInteger runningUpdates = new AtomicInteger();
-
-        tcIgnitedProv.checkAccess(srvCode, prov);
-
-        ITeamcityIgnited tcIgnited = tcIgnitedProv.server(srvCode, prov);
-
-        String failRateBranch = ITeamcity.DEFAULT;
-
-        ProcessLogsMode procLogs = (checkAllLogs != null && checkAllLogs) ? ProcessLogsMode.ALL : ProcessLogsMode.SUITE_NOT_COMPLETE;
-
-        FullChainRunCtx ctx = buildChainProcessor.loadFullChainContext(
-            tcIgnited,
-            Collections.singletonList(buildId),
-            LatestRebuildMode.NONE,
-            procLogs,
-            false,
-            failRateBranch,
-            syncMode,
-            null,
-            null);
-
-        DsChainUi chainStatus = new DsChainUi(srvCode, tcIgnited.serverCode(), ctx.branchName());
-
-        int cnt = (int)ctx.getRunningUpdates().count();
-        if (cnt > 0)
-            runningUpdates.addAndGet(cnt);
-
-        chainStatus.initFromContext(tcIgnited, ctx, failRateBranch, injector.getInstance(IStringCompactor.class), false, null, null, -1, null, false, false);
-
-        res.addChainOnServer(chainStatus);
-
-        res.postProcess(runningUpdates.get());
-
-        return res;
+    @NotNull public DsSummaryUi collectBuildCtxById(String srvCodeOrAlias,
+        Integer buildId,
+        @Nullable Boolean checkAllLogs,
+        SyncMode syncMode) {
+        return CtxListener.getInjector(ctx)
+            .getInstance(SingleBuildResultsService.class)
+            .getSingleBuildResults(srvCodeOrAlias, buildId, checkAllLogs, syncMode,
+                ITcBotUserCreds.get(req));
     }
 
+
     /**
      * Mark builds as "valid" or "invalid".
      *
diff --git a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/web/rest/pr/GetPrTestFailures.java b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/web/rest/pr/GetPrTestFailures.java
index 644a195..d46c82d 100644
--- a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/web/rest/pr/GetPrTestFailures.java
+++ b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/web/rest/pr/GetPrTestFailures.java
@@ -30,15 +30,15 @@ import javax.ws.rs.QueryParam;
 import javax.ws.rs.core.Context;
 import javax.ws.rs.core.MediaType;
 import org.apache.ignite.ci.github.PullRequest;
+import org.apache.ignite.ci.user.ITcBotUserCreds;
+import org.apache.ignite.ci.web.CtxListener;
 import org.apache.ignite.githubignited.IGitHubConnIgnited;
 import org.apache.ignite.githubignited.IGitHubConnIgnitedProvider;
-import org.apache.ignite.tcbot.engine.pr.PrChainsProcessor;
 import org.apache.ignite.githubservice.IGitHubConnection;
-import org.apache.ignite.tcignited.SyncMode;
-import org.apache.ignite.ci.user.ITcBotUserCreds;
-import org.apache.ignite.ci.web.CtxListener;
+import org.apache.ignite.tcbot.engine.pr.PrChainsProcessor;
 import org.apache.ignite.tcbot.engine.ui.DsSummaryUi;
 import org.apache.ignite.tcbot.engine.ui.UpdateInfo;
+import org.apache.ignite.tcignited.SyncMode;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
 
@@ -58,15 +58,12 @@ public class GetPrTestFailures {
     @GET
     @Path("updates")
     public UpdateInfo getPrFailuresUpdates(
-        @Nullable @QueryParam("serverId") String srvId,
-        @Nonnull @QueryParam("suiteId") String suiteId,
+        @Nullable @QueryParam("serverId") String srvCodeOrAlias,
         @Nonnull @QueryParam("branchForTc") String branchForTc,
-        @Nonnull @QueryParam("action") String act,
-        @Nullable @QueryParam("count") Integer cnt,
-        @Nullable @QueryParam("baseBranchForTc") String baseBranchForTc,
-        @Nullable @QueryParam("checkAllLogs") Boolean checkAllLogs) {
-
-        return new UpdateInfo().copyFrom(getPrFailuresResultsNoSync(srvId, suiteId, branchForTc, act, cnt, baseBranchForTc, checkAllLogs));
+        @Nullable @QueryParam("baseBranchForTc") String baseBranchForTc) {
+        return new UpdateInfo().initCounters(
+            CtxListener.getInjector(ctx).getInstance(PrChainsProcessor.class)
+                .getPrUpdateCounters(srvCodeOrAlias, branchForTc, baseBranchForTc, ITcBotUserCreds.get(req)));
     }
 
     @GET
diff --git a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/web/rest/tracked/GetTrackedBranchTestResults.java b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/web/rest/tracked/GetTrackedBranchTestResults.java
index 2b0c2ed..f5cd3a4 100644
--- a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/web/rest/tracked/GetTrackedBranchTestResults.java
+++ b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/web/rest/tracked/GetTrackedBranchTestResults.java
@@ -19,6 +19,7 @@ package org.apache.ignite.ci.web.rest.tracked;
 
 import com.google.inject.Injector;
 import java.util.List;
+import java.util.Map;
 import java.util.Objects;
 import java.util.Set;
 import java.util.stream.Collectors;
@@ -67,20 +68,14 @@ public class GetTrackedBranchTestResults {
 
     @GET
     @Path("updates")
-    public UpdateInfo getTestFailsUpdates(@Nullable @QueryParam("branch") String branchOrNull,
-        @Nullable @QueryParam("checkAllLogs") Boolean checkAllLogs,
-        @Nullable @QueryParam("trustedTests") Boolean trustedTests,
-        @Nullable @QueryParam("tagSelected") String tagSelected,
-        @Nullable @QueryParam("tagForHistSelected") String tagForHistSelected,
-        @Nullable @QueryParam("displayMode") String displayMode,
-        @Nullable @QueryParam("sortOption") String sortOption,
-        @Nullable @QueryParam("count") Integer mergeCnt,
-        @Nullable @QueryParam("showTestLongerThan") Integer showTestLongerThan,
-        @Nullable @QueryParam("muted") Boolean showMuted,
-        @Nullable @QueryParam("ignored") Boolean showIgnored) {
-        return new UpdateInfo().copyFrom(
-            getTestFailsResultsNoSync(branchOrNull, checkAllLogs, trustedTests, tagSelected, tagForHistSelected,
-                displayMode, sortOption, mergeCnt, showTestLongerThan, showMuted, showIgnored));
+    public UpdateInfo getTestFailsUpdates(@Nullable @QueryParam("branch") String branchOrNull) {
+        UpdateInfo info = new UpdateInfo();
+
+        Map<Integer, Integer> counters = CtxListener.getInjector(ctx).getInstance(IDetailedStatusForTrackedBranch.class)
+            .getTrackedBranchUpdateCounters(branchOrNull, ITcBotUserCreds.get(req));
+        info.initCounters(counters);
+
+        return info;
     }
 
     @GET
@@ -177,10 +172,11 @@ public class GetTrackedBranchTestResults {
 
     @GET
     @Path("mergedUpdates")
-    public UpdateInfo getAllTestFailsUpdates(@Nullable @QueryParam("branch") String branch,
-        @Nullable @QueryParam("count") Integer cnt,
-        @Nullable @QueryParam("checkAllLogs") Boolean checkAllLogs) {
-        return new UpdateInfo().copyFrom(getAllTestFailsNoSync(branch, cnt, checkAllLogs));
+    public UpdateInfo getAllTestFailsUpdates(@Nullable @QueryParam("branch") String branchOrNull) {
+        return new UpdateInfo().initCounters(
+            CtxListener.getInjector(ctx)
+                .getInstance(IDetailedStatusForTrackedBranch.class)
+                .getTrackedBranchUpdateCounters(branchOrNull, ITcBotUserCreds.get(req)));
     }
 
     @GET
diff --git a/ignite-tc-helper-web/src/main/webapp/all.html b/ignite-tc-helper-web/src/main/webapp/all.html
index c3ac6ed..619d677 100644
--- a/ignite-tc-helper-web/src/main/webapp/all.html
+++ b/ignite-tc-helper-web/src/main/webapp/all.html
@@ -24,7 +24,6 @@
         $(document).tooltip();
 
         loadData();
-        //todo fix setInterval( function() { checkForUpdate(); }, 60000);
 
         $.ajax({url: "rest/branches/version", success: showVersionInfo, error: showErrInLoadStatus});
 
@@ -68,10 +67,7 @@
             success: function (result) {
                 if (g_shownDataHashCodeHex !== "" && isDefinedAndFilled(result.hashCodeHex)) {
                     if (g_shownDataHashCodeHex === result.hashCodeHex) {
-                        var fastCheckNeeded = isDefinedAndFilled(result.runningUpdates) && result.runningUpdates > 0;
-                        var ms = fastCheckNeeded ? 6000 : 60000;
-
-                        setTimeout(checkForUpdate, ms);
+                        setTimeout(checkForUpdate, 10000);
 
                         $("#loadStatus").html("");
                     } else {
@@ -93,13 +89,6 @@
         $.ajax({
             url: curFailuresUrl,
             success: function (result) {
-                if ((isDefinedAndFilled(result.runningUpdates) && result.runningUpdates > 0)) {
-                    setTimeout(checkForUpdate, 3000);
-
-                    $("#loadStatus").html("<img src='https://www.wallies.com/filebin/images/loading_apple.gif' width=20px height=20px> Updating");
-                } else {
-                    $("#loadStatus").html("");
-                }
                 showData(result);
 
                 g_shownDataHashCodeHex = isDefinedAndFilled(result.hashCodeHex) ? result.hashCodeHex : "";
@@ -178,6 +167,14 @@
 
 </script>
 
+
+<script>
+   window.location='current.html' + parmsForRest() + "&count=10";
+</script>
+
+<a href="current.html?count=10">This repors functionality was moved here</a>
+
+
 <div id="loadStatus"></div>
 <div><a href=".">Home</a> <select id="tagSelect" style="float:right"></select></div>
 <br/>
diff --git a/ignite-tc-helper-web/src/main/webapp/build.html b/ignite-tc-helper-web/src/main/webapp/build.html
index c3acdbd..db5f798 100644
--- a/ignite-tc-helper-web/src/main/webapp/build.html
+++ b/ignite-tc-helper-web/src/main/webapp/build.html
@@ -15,7 +15,8 @@
 </head>
 <body>
 <script>
-var g_shownDataHashCodeHex = "";
+let g_shownDataHashCodeHex = "";
+let g_checkForUpdateSched = false;
 
 $(document).ready(function() {
     $.getScript("js/testfails-2.2.js", function(data, textStatus, jqxhr){ });
@@ -23,7 +24,7 @@ $(document).ready(function() {
     $( document ).tooltip();
     loadData();
 
-    $.ajax({ url: "rest/branches/version",  success: showVersionInfo, error: showErrInLoadStatus });
+    $.ajax({ url: "/rest/branches/version",  success: showVersionInfo, error: showErrInLoadStatus });
 });
 
 
@@ -46,17 +47,19 @@ function parmsForRest() {
 }
 
 function checkForUpdate() {
-    var curFailuresUrl = "rest/build/failures/updates" + parmsForRest();
+    var curFailuresUrl = "/rest/build/failures/updates" + parmsForRest();
 
+    g_checkForUpdateSched = false;
     $.ajax({
         url: curFailuresUrl,
         success: function (result) {
             if (g_shownDataHashCodeHex !== "" && isDefinedAndFilled(result.hashCodeHex)) {
                 if (g_shownDataHashCodeHex === result.hashCodeHex) {
-                    var fastCheckNeeded = isDefinedAndFilled(result.runningUpdates) && result.runningUpdates > 0;
-                    var ms = fastCheckNeeded ? 3000 : 30000;
+                    if (!g_checkForUpdateSched) {
+                        g_checkForUpdateSched = true;
 
-                    setTimeout(checkForUpdate, ms);
+                        setTimeout(checkForUpdate, 10000);
+                    }
 
                     $("#loadStatus").html("");
                 } else {
@@ -71,29 +74,29 @@ function checkForUpdate() {
 }
 
 function loadData() {
-    var curFailuresUrl = "rest/build/failures" + parmsForRest();
+    var curFailuresUrl = "/rest/build/failures" + parmsForRest();
     
     $("#loadStatus").html("<img src='https://www.wallies.com/filebin/images/loading_apple.gif' width=20px height=20px> Please wait");
     setTimeout(loadPartialData, 3000);
+
     $.ajax({
         url: curFailuresUrl,
         success: function (result) {
-            if (isDefinedAndFilled(result.runningUpdates) && result.runningUpdates > 0) {
-                setTimeout(checkForUpdate, 3000);
-
-                $("#loadStatus").html("<img src='https://www.wallies.com/filebin/images/loading_apple.gif' width=20px height=20px> Updating");
-            } else {
-                $("#loadStatus").html("");
-            }
             showData(result);
             g_shownDataHashCodeHex = isDefinedAndFilled(result.hashCodeHex) ? result.hashCodeHex : "";
+
+            if (!g_checkForUpdateSched) {
+                g_checkForUpdateSched = true;
+
+                setTimeout(checkForUpdate, 10000);
+            }
         },
         error: showErrInLoadStatus
     });
 }
 
 function loadPartialData() {
-    var curFailuresUrl = "rest/build/failuresNoSync" + parmsForRest();
+    var curFailuresUrl = "/rest/build/failuresNoSync" + parmsForRest();
 
     if (g_shownDataHashCodeHex !== "") {
         return;
diff --git a/ignite-tc-helper-web/src/main/webapp/current.html b/ignite-tc-helper-web/src/main/webapp/current.html
index 5045910..c475284 100644
--- a/ignite-tc-helper-web/src/main/webapp/current.html
+++ b/ignite-tc-helper-web/src/main/webapp/current.html
@@ -25,8 +25,9 @@
 </head>
 <body>
 <script>
-var g_shownDataHashCodeHex = "";
-var gVue;
+    let g_shownDataHashCodeHex = "";
+    let g_checkForUpdateSched = false;
+    let gVue;
 
 function genLink() {
     let newUrl = "./current.html" + parmsForRest();
@@ -213,16 +214,17 @@ function parmsForRest() {
 
 function checkForUpdate() {
     var curFailuresUrl = "rest/tracked/updates" + parmsForRest();
-
+    g_checkForUpdateSched = false;
     $.ajax({
         url: curFailuresUrl,
         success: function (result) {
             if (g_shownDataHashCodeHex !== "" && isDefinedAndFilled(result.hashCodeHex)) {
                 if (g_shownDataHashCodeHex === result.hashCodeHex) {
-                    var fastCheckNeeded = isDefinedAndFilled(result.runningUpdates) && result.runningUpdates > 0;
-                    var ms = fastCheckNeeded ? 3000 : 30000;
+                    if (!g_checkForUpdateSched) {
+                        g_checkForUpdateSched = true;
 
-                    setTimeout(checkForUpdate, ms);
+                        setTimeout(checkForUpdate, 10000);
+                    }
 
                     $("#loadStatus").html("");
                 } else {
@@ -245,15 +247,14 @@ function loadData() {
     $.ajax({
         url: curFailuresUrl,
         success: function (result) {
-            if (isDefinedAndFilled(result.runningUpdates) && result.runningUpdates > 0) {
-                setTimeout(checkForUpdate, 3000);
-
-                $("#loadStatus").html("<img src='https://www.wallies.com/filebin/images/loading_apple.gif' width=20px height=20px> Updating");
-            } else {
-                $("#loadStatus").html("");
-            }
             showData(result);
             g_shownDataHashCodeHex = isDefinedAndFilled(result.hashCodeHex) ? result.hashCodeHex : "";
+
+            if (!g_checkForUpdateSched) {
+                g_checkForUpdateSched = true;
+
+                setTimeout(checkForUpdate, 10000);
+            }
         },
         error: showErrInLoadStatus
     });
diff --git a/ignite-tc-helper-web/src/main/webapp/pr.html b/ignite-tc-helper-web/src/main/webapp/pr.html
index 3db63f6..dab893e 100644
--- a/ignite-tc-helper-web/src/main/webapp/pr.html
+++ b/ignite-tc-helper-web/src/main/webapp/pr.html
@@ -17,8 +17,9 @@
 </head>
 <body>
 <script>
-    var g_shownDataHashCodeHex = "";
-    var gVue;
+    let g_shownDataHashCodeHex = "";
+    let g_checkForUpdateSched = false;
+    let gVue;
 
     function showQueryForm() {
         let baseBranchForTc = findGetParameter("baseBranchForTc");
@@ -62,7 +63,6 @@
         $(document).tooltip();
         showQueryForm();
         loadData();
-        //todo fix setInterval(checkForUpdate, 30000);
 
         var branch = findGetParameter("branch");
 
@@ -129,15 +129,17 @@ function parmsForRest() {
 function checkForUpdate() {
     var curFailuresUrl = "rest/pr/updates" + parmsForRest();
 
+    g_checkForUpdateSched = false;
     $.ajax({
         url: curFailuresUrl,
         success: function (result) {
             if (g_shownDataHashCodeHex !== "" && isDefinedAndFilled(result.hashCodeHex)) {
                 if (g_shownDataHashCodeHex === result.hashCodeHex) {
-                    var fastCheckNeeded = isDefinedAndFilled(result.runningUpdates) && result.runningUpdates > 0;
-                    var ms = fastCheckNeeded ? 3000 : 30000;
+                    if (!g_checkForUpdateSched) {
+                        g_checkForUpdateSched = true;
 
-                    setTimeout(checkForUpdate, ms);
+                        setTimeout(checkForUpdate, 10000);
+                    }
 
                     $("#loadStatus").html("");
                 } else {
@@ -155,18 +157,19 @@ function checkForUpdate() {
         var curFailuresUrl = "rest/pr/results" + parmsForRest();
 
         $("#loadStatus").html("<img src='https://www.wallies.com/filebin/images/loading_apple.gif' width=20px height=20px> Please wait. First load of PR run-all data may require significant time.");
-        setTimeout(loadPartialData, 3000);
+        setTimeout(loadPartialData, 3000); // in case full loading stalls
         $.ajax({
             url: curFailuresUrl,
             success: function (result) {
-                if (isDefinedAndFilled(result.runningUpdates) && result.runningUpdates > 0) {
-                    setTimeout(checkForUpdate, 3000);
-                    $("#loadStatus").html("<img src='https://www.wallies.com/filebin/images/loading_apple.gif' width=20px height=20px> Updating");
-                } else {
-                    $("#loadStatus").html("");
-                }
+                $("#loadStatus").html("");
                 showData(result);
                 g_shownDataHashCodeHex = isDefinedAndFilled(result.hashCodeHex) ? result.hashCodeHex : "";
+
+                if (!g_checkForUpdateSched) {
+                    g_checkForUpdateSched = true;
+
+                    setTimeout(checkForUpdate, 10000);
+                }
             },
             error: showErrInLoadStatus
         });
diff --git a/tcbot-engine/src/main/java/org/apache/ignite/tcbot/engine/TcBotEngineModule.java b/tcbot-engine/src/main/java/org/apache/ignite/tcbot/engine/TcBotEngineModule.java
index 948c002..ab39d2d 100644
--- a/tcbot-engine/src/main/java/org/apache/ignite/tcbot/engine/TcBotEngineModule.java
+++ b/tcbot-engine/src/main/java/org/apache/ignite/tcbot/engine/TcBotEngineModule.java
@@ -20,8 +20,8 @@ package org.apache.ignite.tcbot.engine;
 import com.google.inject.AbstractModule;
 import com.google.inject.internal.SingletonScope;
 import org.apache.ignite.tcbot.common.TcBotCommonModule;
-import org.apache.ignite.tcbot.common.interceptor.MonitoredTaskInterceptorModule;
 import org.apache.ignite.tcbot.engine.board.BoardService;
+import org.apache.ignite.tcbot.engine.build.SingleBuildResultsService;
 import org.apache.ignite.tcbot.engine.buildtime.BuildTimeService;
 import org.apache.ignite.tcbot.engine.chain.BuildChainProcessor;
 import org.apache.ignite.tcbot.engine.issue.IIssuesStorage;
@@ -37,6 +37,7 @@ public class TcBotEngineModule extends AbstractModule {
     @Override protected void configure() {
         bind(BuildChainProcessor.class).in(new SingletonScope());
         bind(IDetailedStatusForTrackedBranch.class).to(TrackedBranchChainsProcessor.class).in(new SingletonScope());
+        bind(SingleBuildResultsService.class).in(new SingletonScope());
 
         bind(BuildTimeService.class).in(new SingletonScope());
 
diff --git a/tcbot-engine/src/main/java/org/apache/ignite/tcbot/engine/build/SingleBuildResultsService.java b/tcbot-engine/src/main/java/org/apache/ignite/tcbot/engine/build/SingleBuildResultsService.java
new file mode 100644
index 0000000..f0ac555
--- /dev/null
+++ b/tcbot-engine/src/main/java/org/apache/ignite/tcbot/engine/build/SingleBuildResultsService.java
@@ -0,0 +1,100 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.tcbot.engine.build;
+
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import javax.inject.Inject;
+import org.apache.ignite.tcbot.engine.chain.BuildChainProcessor;
+import org.apache.ignite.tcbot.engine.chain.FullChainRunCtx;
+import org.apache.ignite.tcbot.engine.chain.LatestRebuildMode;
+import org.apache.ignite.tcbot.engine.chain.ProcessLogsMode;
+import org.apache.ignite.tcbot.engine.ui.DsChainUi;
+import org.apache.ignite.tcbot.engine.ui.DsSummaryUi;
+import org.apache.ignite.tcbot.persistence.IStringCompactor;
+import org.apache.ignite.tcignited.ITeamcityIgnited;
+import org.apache.ignite.tcignited.ITeamcityIgnitedProvider;
+import org.apache.ignite.tcignited.SyncMode;
+import org.apache.ignite.tcignited.build.UpdateCountersStorage;
+import org.apache.ignite.tcignited.buildref.BranchEquivalence;
+import org.apache.ignite.tcignited.creds.ICredentialsProv;
+import org.apache.ignite.tcservice.ITeamcity;
+
+/**
+ * Displays single build at server by ID.
+ */
+public class SingleBuildResultsService {
+    @Inject BuildChainProcessor buildChainProcessor;
+    @Inject ITeamcityIgnitedProvider tcIgnitedProv;
+    @Inject BranchEquivalence branchEquivalence;
+    @Inject IStringCompactor compactor;
+    @Inject UpdateCountersStorage updateCounters;
+
+    @Nonnull public DsSummaryUi getSingleBuildResults(String srvCodeOrAlias, Integer buildId,
+        @Nullable Boolean checkAllLogs, SyncMode syncMode, ICredentialsProv prov) {
+        DsSummaryUi res = new DsSummaryUi();
+
+        tcIgnitedProv.checkAccess(srvCodeOrAlias, prov);
+
+        ITeamcityIgnited tcIgnited = tcIgnitedProv.server(srvCodeOrAlias, prov);
+
+        String failRateBranch = ITeamcity.DEFAULT;
+
+        ProcessLogsMode procLogs = (checkAllLogs != null && checkAllLogs) ? ProcessLogsMode.ALL : ProcessLogsMode.SUITE_NOT_COMPLETE;
+
+        FullChainRunCtx ctx = buildChainProcessor.loadFullChainContext(
+            tcIgnited,
+            Collections.singletonList(buildId),
+            LatestRebuildMode.NONE,
+            procLogs,
+            false,
+            failRateBranch,
+            syncMode,
+            null,
+            null);
+
+        DsChainUi chainStatus = new DsChainUi(srvCodeOrAlias, tcIgnited.serverCode(), ctx.branchName());
+
+        chainStatus.initFromContext(tcIgnited, ctx, failRateBranch, compactor, false, null, null, -1, null, false, false);
+
+        res.addChainOnServer(chainStatus);
+
+        res.initCounters(getBranchCntrs(srvCodeOrAlias, buildId, prov));
+        return res;
+    }
+
+
+
+    public Map<Integer, Integer> getBranchCntrs(String srvCodeOrAlias,
+        Integer buildId, ICredentialsProv creds) {
+        tcIgnitedProv.checkAccess(srvCodeOrAlias, creds);
+
+        String tcBranch = tcIgnitedProv.server(srvCodeOrAlias, creds).getFatBuild(buildId, SyncMode.LOAD_NEW).branchName(compactor);
+
+        Set<Integer> allBranchIds = new HashSet<>();
+
+        allBranchIds.addAll(branchEquivalence.branchIdsForQuery(tcBranch, compactor));
+        allBranchIds.addAll(branchEquivalence.branchIdsForQuery(ITeamcity.DEFAULT, compactor));
+
+        return updateCounters.getCounters(allBranchIds);
+    }
+}
diff --git a/tcbot-engine/src/main/java/org/apache/ignite/tcbot/engine/chain/BuildChainProcessor.java b/tcbot-engine/src/main/java/org/apache/ignite/tcbot/engine/chain/BuildChainProcessor.java
index dca9293..bf55a4a 100644
--- a/tcbot-engine/src/main/java/org/apache/ignite/tcbot/engine/chain/BuildChainProcessor.java
+++ b/tcbot-engine/src/main/java/org/apache/ignite/tcbot/engine/chain/BuildChainProcessor.java
@@ -51,7 +51,9 @@ import org.apache.ignite.tcbot.engine.ui.LrTestsSuiteSummaryUi;
 import org.apache.ignite.tcbot.persistence.IStringCompactor;
 import org.apache.ignite.tcignited.ITeamcityIgnited;
 import org.apache.ignite.tcignited.SyncMode;
+import org.apache.ignite.tcignited.build.UpdateCountersStorage;
 import org.apache.ignite.tcignited.buildlog.IBuildLogProcessor;
+import org.apache.ignite.tcignited.buildlog.ILogCheckResult;
 import org.apache.ignite.tcignited.buildref.BranchEquivalence;
 import org.apache.ignite.tcignited.history.IRunHistory;
 import org.apache.ignite.tcservice.model.hist.BuildRef;
@@ -72,6 +74,12 @@ public class BuildChainProcessor {
     /** Compactor. */
     @Inject private IStringCompactor compactor;
 
+    /** Build logs processor. */
+    @Inject private IBuildLogProcessor buildLogProcessor;
+
+    @Inject private UpdateCountersStorage counters;
+
+
     /**
      * Collects data about all long-running tests (run time more than one minute) across all suites in RunAll chain in
      * master branch.
@@ -436,8 +444,6 @@ public class BuildChainProcessor {
         }
     }
 
-    @Inject IBuildLogProcessor buildLogProcessor;
-
     @SuppressWarnings("WeakerAccess")
     @AutoProfiling
     protected void analyzeTests(MultBuildRunCtx outCtx, ITeamcityIgnited teamcity,
@@ -448,10 +454,18 @@ public class BuildChainProcessor {
                     || procLog == ProcessLogsMode.ALL)
                 ctx.setLogCheckResFut(
                         CompletableFuture.supplyAsync(
-                                () -> buildLogProcessor.analyzeBuildLog(teamcity,
-                                        ctx.buildId(),
-                                    incompleteFailure),
-                                tcUpdatePool.getService()));
+                            () -> {
+                                ILogCheckResult res = buildLogProcessor.analyzeBuildLog(teamcity,
+                                    ctx.buildId(),
+                                    incompleteFailure);
+                                int branchName = ctx.branchName();
+
+                                //build log result is ready for branch.
+                                counters.increment(branchName);
+
+                                return res;
+                            },
+                            tcUpdatePool.getService()));
         }
     }
 
diff --git a/tcbot-engine/src/main/java/org/apache/ignite/tcbot/engine/chain/FullChainRunCtx.java b/tcbot-engine/src/main/java/org/apache/ignite/tcbot/engine/chain/FullChainRunCtx.java
index a59e210..e9fb23a 100644
--- a/tcbot-engine/src/main/java/org/apache/ignite/tcbot/engine/chain/FullChainRunCtx.java
+++ b/tcbot-engine/src/main/java/org/apache/ignite/tcbot/engine/chain/FullChainRunCtx.java
@@ -108,14 +108,6 @@ public class FullChainRunCtx {
         this.buildCfgsResults.addAll(suites);
     }
 
-    public Stream<Future<?>> getFutures() {
-        return buildCfgsResults.stream().flatMap(MultBuildRunCtx::getFutures);
-    }
-
-    public Stream<Future<?>> getRunningUpdates() {
-        return getFutures().filter(Objects::nonNull).filter(future -> !future.isDone() && !future.isCancelled());
-    }
-
     public boolean isFakeStub() {
         return fakeStub;
     }
diff --git a/tcbot-engine/src/main/java/org/apache/ignite/tcbot/engine/chain/MultBuildRunCtx.java b/tcbot-engine/src/main/java/org/apache/ignite/tcbot/engine/chain/MultBuildRunCtx.java
index 53d2486..22ec872 100644
--- a/tcbot-engine/src/main/java/org/apache/ignite/tcbot/engine/chain/MultBuildRunCtx.java
+++ b/tcbot-engine/src/main/java/org/apache/ignite/tcbot/engine/chain/MultBuildRunCtx.java
@@ -481,10 +481,6 @@ public class MultBuildRunCtx implements ISuiteResults {
             });
     }
 
-    Stream<? extends Future<?>> getFutures() {
-        return buildsStream().flatMap(SingleBuildRunCtx::getFutures);
-    }
-
     /**
      * @return true if all builds are composite
      */
diff --git a/tcbot-engine/src/main/java/org/apache/ignite/tcbot/engine/chain/SingleBuildRunCtx.java b/tcbot-engine/src/main/java/org/apache/ignite/tcbot/engine/chain/SingleBuildRunCtx.java
index 77ddf63..9252003 100644
--- a/tcbot-engine/src/main/java/org/apache/ignite/tcbot/engine/chain/SingleBuildRunCtx.java
+++ b/tcbot-engine/src/main/java/org/apache/ignite/tcbot/engine/chain/SingleBuildRunCtx.java
@@ -175,10 +175,6 @@ public class SingleBuildRunCtx implements ISuiteResults {
         return Collections.unmodifiableList(changes);
     }
 
-    @Nonnull Stream<? extends Future<?>> getFutures() {
-        return logCheckResFut == null ? Stream.empty() : Stream.of((Future<?>)logCheckResFut);
-    }
-
     public boolean isComposite() {
         return buildCompacted.isComposite();
     }
@@ -348,4 +344,8 @@ public class SingleBuildRunCtx implements ISuiteResults {
     public void addTags(Set<String> strings) {
         this.tags.addAll(strings);
     }
+
+    public int branchName() {
+        return buildCompacted.branchName();
+    }
 }
diff --git a/tcbot-engine/src/main/java/org/apache/ignite/tcbot/engine/pr/PrChainsProcessor.java b/tcbot-engine/src/main/java/org/apache/ignite/tcbot/engine/pr/PrChainsProcessor.java
index e05285c..8cfaffa 100644
--- a/tcbot-engine/src/main/java/org/apache/ignite/tcbot/engine/pr/PrChainsProcessor.java
+++ b/tcbot-engine/src/main/java/org/apache/ignite/tcbot/engine/pr/PrChainsProcessor.java
@@ -17,10 +17,12 @@
 package org.apache.ignite.tcbot.engine.pr;
 
 import com.google.common.base.Strings;
+import java.util.HashSet;
 import java.util.List;
+import java.util.Map;
 import java.util.Objects;
 import java.util.Optional;
-import java.util.concurrent.atomic.AtomicInteger;
+import java.util.Set;
 import java.util.function.Predicate;
 import java.util.stream.Collectors;
 import javax.annotation.Nullable;
@@ -49,6 +51,7 @@ import org.apache.ignite.tcbot.persistence.IStringCompactor;
 import org.apache.ignite.tcignited.ITeamcityIgnited;
 import org.apache.ignite.tcignited.ITeamcityIgnitedProvider;
 import org.apache.ignite.tcignited.SyncMode;
+import org.apache.ignite.tcignited.build.UpdateCountersStorage;
 import org.apache.ignite.tcignited.buildref.BranchEquivalence;
 import org.apache.ignite.tcignited.creds.ICredentialsProv;
 import org.apache.ignite.tcignited.history.IRunHistory;
@@ -80,8 +83,12 @@ public class PrChainsProcessor {
 
     @Inject private IStringCompactor compactor;
 
+    /** Config. */
     @Inject private ITcBotConfig cfg;
 
+    @Inject private BranchEquivalence branchEquivalence;
+    @Inject private UpdateCountersStorage countersStorage;
+
     /**
      * @param creds Credentials.
      * @param srvCodeOrAlias Server code or alias.
@@ -106,8 +113,6 @@ public class PrChainsProcessor {
         @Nullable Boolean checkAllLogs,
         SyncMode mode) {
         final DsSummaryUi res = new DsSummaryUi();
-        final AtomicInteger runningUpdates = new AtomicInteger();
-
         ITeamcityIgnited tcIgnited = tcIgnitedProvider.server(srvCodeOrAlias, creds);
 
         IGitHubConnIgnited gitHubConnIgnited = gitHubConnIgnitedProvider.server(srvCodeOrAlias);
@@ -159,11 +164,6 @@ public class PrChainsProcessor {
         if (ctx.isFakeStub())
             chainStatus.setBuildNotFound(true);
         else {
-            int cnt0 = (int)ctx.getRunningUpdates().count();
-
-            if (cnt0 > 0)
-                runningUpdates.addAndGet(cnt0);
-
             //fail rate reference is always default (master)
             chainStatus.initFromContext(tcIgnited, ctx, baseBranchForTc, compactor, false,
                     null, null, -1, null, false, false); // don't need for PR
@@ -173,7 +173,7 @@ public class PrChainsProcessor {
 
         res.addChainOnServer(chainStatus);
 
-        res.postProcess(runningUpdates.get());
+        res.initCounters(getPrUpdateCounters(srvCodeOrAlias, branchForTc, baseBranchForTc, creds));
 
         return res;
     }
@@ -329,4 +329,14 @@ public class PrChainsProcessor {
             .collect(Collectors.toList());
     }
 
+    public Map<Integer, Integer> getPrUpdateCounters(String srvCodeOrAlias, String branchForTc, String tcBaseBranchParm,
+        ICredentialsProv creds) {
+        String baseBranchForTc = Strings.isNullOrEmpty(tcBaseBranchParm) ? dfltBaseTcBranch(srvCodeOrAlias) : tcBaseBranchParm;
+
+        Set<Integer> allRelatedBranchCodes = new HashSet<>();
+        allRelatedBranchCodes.addAll(branchEquivalence.branchIdsForQuery(branchForTc, compactor));
+        allRelatedBranchCodes.addAll(branchEquivalence.branchIdsForQuery(baseBranchForTc, compactor));
+
+        return countersStorage.getCounters(allRelatedBranchCodes);
+    }
 }
diff --git a/tcbot-engine/src/main/java/org/apache/ignite/tcbot/engine/tracked/IDetailedStatusForTrackedBranch.java b/tcbot-engine/src/main/java/org/apache/ignite/tcbot/engine/tracked/IDetailedStatusForTrackedBranch.java
index 684aa23..13baae2 100644
--- a/tcbot-engine/src/main/java/org/apache/ignite/tcbot/engine/tracked/IDetailedStatusForTrackedBranch.java
+++ b/tcbot-engine/src/main/java/org/apache/ignite/tcbot/engine/tracked/IDetailedStatusForTrackedBranch.java
@@ -16,6 +16,7 @@
  */
 package org.apache.ignite.tcbot.engine.tracked;
 
+import java.util.Map;
 import javax.annotation.Nullable;
 import org.apache.ignite.tcbot.engine.chain.SortOption;
 import org.apache.ignite.tcbot.engine.ui.DsSummaryUi;
@@ -63,5 +64,7 @@ public interface IDetailedStatusForTrackedBranch {
      */
     public GuardBranchStatusUi getBranchSummary(String name, ICredentialsProv prov);
 
+    public Map<Integer, Integer> getTrackedBranchUpdateCounters(@Nullable String branch, ICredentialsProv creds);
+
     //  * @param baseTrackedBranch Branch tracked branch in Bot, has a priority if both TC & Bot branches (baseBranchForTcParm) present.
 }
diff --git a/tcbot-engine/src/main/java/org/apache/ignite/tcbot/engine/tracked/TrackedBranchChainsProcessor.java b/tcbot-engine/src/main/java/org/apache/ignite/tcbot/engine/tracked/TrackedBranchChainsProcessor.java
index 6decee6..71ab3f4 100644
--- a/tcbot-engine/src/main/java/org/apache/ignite/tcbot/engine/tracked/TrackedBranchChainsProcessor.java
+++ b/tcbot-engine/src/main/java/org/apache/ignite/tcbot/engine/tracked/TrackedBranchChainsProcessor.java
@@ -21,8 +21,10 @@ import java.time.Duration;
 import java.util.Collection;
 import java.util.Comparator;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 import java.util.concurrent.atomic.AtomicInteger;
 import java.util.stream.Collectors;
 import javax.annotation.Nonnull;
@@ -49,6 +51,8 @@ import org.apache.ignite.tcbot.persistence.IStringCompactor;
 import org.apache.ignite.tcignited.ITeamcityIgnited;
 import org.apache.ignite.tcignited.ITeamcityIgnitedProvider;
 import org.apache.ignite.tcignited.SyncMode;
+import org.apache.ignite.tcignited.build.UpdateCountersStorage;
+import org.apache.ignite.tcignited.buildref.BranchEquivalence;
 import org.apache.ignite.tcignited.creds.ICredentialsProv;
 
 import static com.google.common.base.Strings.isNullOrEmpty;
@@ -69,6 +73,11 @@ public class TrackedBranchChainsProcessor implements IDetailedStatusForTrackedBr
     /** Compactor. */
     @Inject private IStringCompactor compactor;
 
+    @Inject private BranchEquivalence branchEquivalence;
+
+    /** Update Counters for branch-related changes storage. */
+    @Inject private UpdateCountersStorage countersStorage;
+
     /** {@inheritDoc} */
     @AutoProfiling
     @Nonnull
@@ -87,7 +96,6 @@ public class TrackedBranchChainsProcessor implements IDetailedStatusForTrackedBr
         boolean showMuted,
         boolean showIgnored) {
         final DsSummaryUi res = new DsSummaryUi();
-        final AtomicInteger runningUpdates = new AtomicInteger();
 
         final String branchNn = isNullOrEmpty(branch) ? ITcServerConfig.DEFAULT_TRACKED_BRANCH_NAME : branch;
         res.setTrackedBranch(branchNn);
@@ -145,10 +153,6 @@ public class TrackedBranchChainsProcessor implements IDetailedStatusForTrackedBr
                     requireParamVal
                 );
 
-                int cnt = (int)ctx.getRunningUpdates().count();
-                if (cnt > 0)
-                    runningUpdates.addAndGet(cnt);
-
                 chainStatus.initFromContext(tcIgnited, ctx, baseBranchTc, compactor, calcTrustedTests, tagSelected,
                     displayMode, maxDurationSec, requireParamVal,
                     showMuted, showIgnored);
@@ -159,7 +163,7 @@ public class TrackedBranchChainsProcessor implements IDetailedStatusForTrackedBr
 
         res.servers.sort(Comparator.comparing(DsChainUi::serverName));
 
-        res.postProcess(runningUpdates.get());
+        res.initCounters(getTrackedBranchUpdateCounters(branch, creds));
 
         return res;
     }
@@ -242,6 +246,30 @@ public class TrackedBranchChainsProcessor implements IDetailedStatusForTrackedBr
         return statusUi;
     }
 
+    @Override public Map<Integer, Integer> getTrackedBranchUpdateCounters(@Nullable String branch,
+        @Nonnull ICredentialsProv creds) {
+
+        final String branchNn = isNullOrEmpty(branch) ? ITcServerConfig.DEFAULT_TRACKED_BRANCH_NAME : branch;
+        final ITrackedBranch tracked = tcBotCfg.getTrackedBranches().getBranchMandatory(branchNn);
+
+        Set<Integer> allBranches = new HashSet<>();
+        tracked.chainsStream()
+            .filter(chainTracked -> tcIgnitedProv.hasAccess(chainTracked.serverCode(), creds))
+            .forEach(chainTracked -> {
+                String tcBranch = chainTracked.tcBranch();
+
+                Set<Integer> allBranchIds = new HashSet<>(branchEquivalence.branchIdsForQuery(tcBranch, compactor));
+
+                chainTracked.tcBaseBranch().ifPresent(base -> {
+                    allBranchIds.addAll(branchEquivalence.branchIdsForQuery(base, compactor));
+                });
+
+                allBranches.addAll(allBranchIds);
+            });
+
+        return countersStorage.getCounters(allBranches);
+    }
+
     /**
      * Collects data about all long-running tests (run time more than one minute) within one transfer object.
      *
diff --git a/tcbot-engine/src/main/java/org/apache/ignite/tcbot/engine/ui/DsSummaryUi.java b/tcbot-engine/src/main/java/org/apache/ignite/tcbot/engine/ui/DsSummaryUi.java
index 0a6cd24..ff4d2e7 100644
--- a/tcbot-engine/src/main/java/org/apache/ignite/tcbot/engine/ui/DsSummaryUi.java
+++ b/tcbot-engine/src/main/java/org/apache/ignite/tcbot/engine/ui/DsSummaryUi.java
@@ -63,12 +63,6 @@ public class DsSummaryUi extends UpdateInfo {
         return this;
     }
 
-    public void postProcess(int running) {
-        runningUpdates = running;
-
-        hashCodeHex = Integer.toHexString(U.safeAbs(hashCode()));
-    }
-
     /** {@inheritDoc} */
     @Override public boolean equals(Object o) {
         if (this == o)
diff --git a/tcbot-engine/src/main/java/org/apache/ignite/tcbot/engine/ui/UpdateInfo.java b/tcbot-engine/src/main/java/org/apache/ignite/tcbot/engine/ui/UpdateInfo.java
index 6e31a09..16c6063 100644
--- a/tcbot-engine/src/main/java/org/apache/ignite/tcbot/engine/ui/UpdateInfo.java
+++ b/tcbot-engine/src/main/java/org/apache/ignite/tcbot/engine/ui/UpdateInfo.java
@@ -17,8 +17,10 @@
 
 package org.apache.ignite.tcbot.engine.ui;
 
+import java.util.Map;
 import org.apache.ignite.tcbot.common.conf.IGitHubConfig;
 import org.apache.ignite.tcbot.common.conf.IJiraServerConfig;
+import org.apache.ignite.tcignited.build.UpdateCountersStorage;
 
 /**
  * General update information for JS data updating requests. UI model, so it contains public fields.
@@ -36,32 +38,35 @@ import org.apache.ignite.tcbot.common.conf.IJiraServerConfig;
     /** Flags to use in javascript. */
     public Integer javaFlags = 0;
 
-    /** Running updates is in progress, summary is ready, but it is subject to change */
+    /** Running updates is in progress, not used since it should be updated using counters */
+    @Deprecated
     public int runningUpdates = 0;
 
     /** Hash code hexadecimal, protects from redraw and minimizing mode info in case data not changed */
     public String hashCodeHex;
 
-    public UpdateInfo copyFrom(UpdateInfo info) {
-        //todo there is no chance to update running futures if info is cached
-        this.runningUpdates = info.runningUpdates;
-        this.hashCodeHex = info.hashCodeHex;
-
-        return this;
-    }
+    /** Update Counters for all related TC Branches. */
+    public Map<Integer, Integer> counters;
 
     /**
-     * @param gitHubConfig
+     * @param gitHubCfg
      * @param jiraCfg
      */
-    public void setJavaFlags(IGitHubConfig gitHubConfig, IJiraServerConfig jiraCfg) {
+    public void setJavaFlags(IGitHubConfig gitHubCfg, IJiraServerConfig jiraCfg) {
         //since user has logged in, TC flag should be set
         javaFlags |= TEAMCITY_FLAG;
 
-        if (gitHubConfig.isGitTokenAvailable())
+        if (gitHubCfg.isGitTokenAvailable())
             javaFlags |= GITHUB_FLAG;
 
         if (jiraCfg.isJiraTokenAvailable())
             javaFlags |= JIRA_FLAG;
     }
+
+    public UpdateInfo initCounters(Map<Integer, Integer> counters) {
+        this.counters = counters;
+        hashCodeHex = UpdateCountersStorage.getCountersHash(counters);
+
+        return this;
+    }
 }
diff --git a/tcbot-teamcity-ignited/src/main/java/org/apache/ignite/tcignited/TeamcityIgnitedModule.java b/tcbot-teamcity-ignited/src/main/java/org/apache/ignite/tcignited/TeamcityIgnitedModule.java
index 0f39f7a..273c00f 100644
--- a/tcbot-teamcity-ignited/src/main/java/org/apache/ignite/tcignited/TeamcityIgnitedModule.java
+++ b/tcbot-teamcity-ignited/src/main/java/org/apache/ignite/tcignited/TeamcityIgnitedModule.java
@@ -19,6 +19,7 @@ package org.apache.ignite.tcignited;
 import com.google.inject.AbstractModule;
 import com.google.inject.internal.SingletonScope;
 import org.apache.ignite.ci.teamcity.ignited.buildcondition.BuildConditionDao;
+import org.apache.ignite.tcignited.build.UpdateCountersStorage;
 import org.apache.ignite.tcignited.buildlog.BuildLogCheckResultDao;
 import org.apache.ignite.tcignited.buildlog.ILogProductSpecific;
 import org.apache.ignite.tcignited.buildlog.LogIgniteSpecific;
@@ -70,6 +71,7 @@ public class TeamcityIgnitedModule extends AbstractModule {
         bind(SuiteInvocationHistoryDao.class).in(new SingletonScope());
         bind(HistoryCollector.class).in(new SingletonScope());
         bind(ILogProductSpecific.class).to(LogIgniteSpecific.class).in(new SingletonScope());
+        bind(UpdateCountersStorage.class).in(new SingletonScope());
 
         TcRealConnectionModule module = new TcRealConnectionModule();
         if (conn != null)
diff --git a/tcbot-teamcity-ignited/src/main/java/org/apache/ignite/tcignited/build/FatBuildDao.java b/tcbot-teamcity-ignited/src/main/java/org/apache/ignite/tcignited/build/FatBuildDao.java
index 439adca..197d2ad 100644
--- a/tcbot-teamcity-ignited/src/main/java/org/apache/ignite/tcignited/build/FatBuildDao.java
+++ b/tcbot-teamcity-ignited/src/main/java/org/apache/ignite/tcignited/build/FatBuildDao.java
@@ -85,6 +85,9 @@ public class FatBuildDao {
     /** History collector. */
     @Inject private HistoryCollector histCollector;
 
+    /** Update Counters for branch-related changes storage. */
+    @Inject private UpdateCountersStorage countersStorage;
+
     /**
      *
      */
@@ -144,6 +147,8 @@ public class FatBuildDao {
         buildsCache.put(buildIdToCacheKey(srvIdMaskHigh, buildId), newBuild);
 
         histCollector.invalidateHistoryInMem(srvIdMaskHigh, newBuild);
+
+        countersStorage.increment(newBuild.branchName());
     }
 
     public static int[] extractChangeIds(@Nonnull ChangesList changesList) {
diff --git a/tcbot-teamcity-ignited/src/main/java/org/apache/ignite/tcignited/build/UpdateCountersStorage.java b/tcbot-teamcity-ignited/src/main/java/org/apache/ignite/tcignited/build/UpdateCountersStorage.java
new file mode 100644
index 0000000..c9552bc
--- /dev/null
+++ b/tcbot-teamcity-ignited/src/main/java/org/apache/ignite/tcignited/build/UpdateCountersStorage.java
@@ -0,0 +1,57 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.ignite.tcignited.build;
+
+import java.util.Collection;
+import java.util.Map;
+import java.util.TreeMap;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.atomic.AtomicInteger;
+import org.apache.ignite.internal.util.typedef.internal.U;
+
+public class UpdateCountersStorage {
+    /** Counters: Map from Branch name compactor ID to its correspondent update counter value. */
+    private ConcurrentMap<Integer, AtomicInteger> counters = new ConcurrentHashMap<>();
+
+    public Map<Integer, Integer> getCounters(Collection<Integer> branchNames) {
+        Map<Integer, Integer> res = new TreeMap<>();
+
+        for (Integer name : branchNames) {
+            if (name == null)
+                continue;
+
+            res.put(name, getIntegerForEntry(name).get());
+        }
+
+        System.err.println("Requested counters:" + res);
+
+        return res;
+    }
+
+    public static String getCountersHash(Map<Integer, Integer> counters) {
+        return Integer.toHexString(U.safeAbs(counters == null ? 0 : counters.hashCode()));
+    }
+
+    public AtomicInteger getIntegerForEntry(int name) {
+        return counters.computeIfAbsent(name, (k_) -> new AtomicInteger());
+    }
+
+    public void increment(int branchName) {
+        getIntegerForEntry(branchName).incrementAndGet();
+    }
+}
diff --git a/tcbot-teamcity-ignited/src/main/java/org/apache/ignite/tcignited/buildref/BuildRefDao.java b/tcbot-teamcity-ignited/src/main/java/org/apache/ignite/tcignited/buildref/BuildRefDao.java
index 74a91f9..368993e 100644
--- a/tcbot-teamcity-ignited/src/main/java/org/apache/ignite/tcignited/buildref/BuildRefDao.java
+++ b/tcbot-teamcity-ignited/src/main/java/org/apache/ignite/tcignited/buildref/BuildRefDao.java
@@ -52,6 +52,7 @@ import org.apache.ignite.tcbot.common.exeption.ExceptionUtil;
 import org.apache.ignite.tcbot.common.interceptor.AutoProfiling;
 import org.apache.ignite.tcbot.persistence.CacheConfigs;
 import org.apache.ignite.tcbot.persistence.IStringCompactor;
+import org.apache.ignite.tcignited.build.UpdateCountersStorage;
 import org.apache.ignite.tcservice.model.hist.BuildRef;
 
 /**
@@ -70,6 +71,9 @@ public class BuildRefDao {
     /** Compactor. */
     @Inject private IStringCompactor compactor;
 
+    /** Update Counters for branch-related changes storage. */
+    @Inject private UpdateCountersStorage countersStorage;
+
     /** Non persistence cache for all BuildRefsCompacted for particular branch.
      * RunHistKey(ServerId||BranchId||suiteId)-> Build reference
      */
@@ -180,6 +184,11 @@ public class BuildRefDao {
 
         buildRefsInMemCacheForAllBranch.invalidateAll(cacheForAllBranch);
         buildRefsInMemCache.invalidateAll(setOfHistToClear);
+
+        setOfHistToClear.forEach(b -> {
+            int branch = b.branch();
+            countersStorage.increment(branch);
+        });
     }
 
     /**