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 2018/11/28 17:57:55 UTC

[ignite-teamcity-bot] branch ignite-9542-new-run-stripe updated: IGNITE-9542 SuiteResults saving implemented

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

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


The following commit(s) were added to refs/heads/ignite-9542-new-run-stripe by this push:
     new d3f20ba  IGNITE-9542 SuiteResults saving implemented
d3f20ba is described below

commit d3f20baccfae103b812fcebe57fae24718a5407c
Author: Dmitriy Pavlov <dp...@apache.org>
AuthorDate: Wed Nov 28 20:57:53 2018 +0300

    IGNITE-9542 SuiteResults saving implemented
---
 .../apache/ignite/ci/IgnitePersistentTeamcity.java |  5 +-
 .../java/org/apache/ignite/ci/db/DbMigrations.java | 13 +++++
 .../java/org/apache/ignite/ci/db/TcHelperDb.java   | 24 +++++++-
 .../ci/github/ignited/GitHubConnIgnitedImpl.java   | 15 +----
 .../ci/teamcity/ignited/BuildRefCompacted.java     |  4 ++
 .../ci/teamcity/ignited/TeamcityIgnitedImpl.java   |  2 +-
 .../ignited/fatbuild/FatBuildCompacted.java        | 23 ++++++++
 .../ignited/runhist/RunHistCompactedDao.java       | 68 +++++++++++++---------
 .../ci/teamcity/ignited/runhist/RunHistSync.java   | 62 +++++++++++++++-----
 .../ci/tcbot/chain/PrChainsProcessorTest.java      |  7 ++-
 .../ignited/IgnitedTcInMemoryIntegrationTest.java  |  8 ++-
 11 files changed, 169 insertions(+), 62 deletions(-)

diff --git a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/IgnitePersistentTeamcity.java b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/IgnitePersistentTeamcity.java
index f7b3950..292e85e 100644
--- a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/IgnitePersistentTeamcity.java
+++ b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/IgnitePersistentTeamcity.java
@@ -349,9 +349,12 @@ public class IgnitePersistentTeamcity implements IAnalyticsEnabledTeamcity, ITea
         return teamcity.host();
     }
 
+    @Deprecated
     private void registerCriticalBuildProblemInStat(BuildRef build, ProblemOccurrences problems) {
         boolean criticalFail = problems.getProblemsNonNull().stream().anyMatch(occurrence ->
-            occurrence.isExecutionTimeout() || occurrence.isJvmCrash() || occurrence.isFailureOnMetric()
+            occurrence.isExecutionTimeout()
+                || occurrence.isJvmCrash()
+                || occurrence.isFailureOnMetric()
                 || occurrence.isCompilationError());
 
         String suiteId = build.suiteId();
diff --git a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/db/DbMigrations.java b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/db/DbMigrations.java
index 443195e..64bea12 100644
--- a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/db/DbMigrations.java
+++ b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/db/DbMigrations.java
@@ -115,6 +115,15 @@ public class DbMigrations {
         String TESTS = "tests";
         String TEST_REFS = "testRefs";
         String ISSUES_USAGES_LIST = "issuesUsagesList";
+
+        /** Cache name.*/
+        public static final String TEST_HIST_CACHE_NAME = "testRunHistV0";
+
+        /** Build Start time Cache name. */
+        public static final String BUILD_START_TIME_CACHE_NAME = "buildStartTimeV0";
+
+        /** Cache name.*/
+        public static final String SUITE_HIST_CACHE_NAME = "teamcitySuiteRunHistV0";
     }
 
     private final Ignite ignite;
@@ -396,6 +405,10 @@ public class DbMigrations {
         applyDestroyIgnCacheMigration(Old.TEST_REFS);
 
         applyDestroyIgnCacheMigration(Old.ISSUES_USAGES_LIST);
+
+        applyDestroyCacheMigration(Old.SUITE_HIST_CACHE_NAME, Old.SUITE_HIST_CACHE_NAME);
+        applyDestroyCacheMigration(Old.BUILD_START_TIME_CACHE_NAME, Old.BUILD_START_TIME_CACHE_NAME);
+        applyDestroyCacheMigration(Old.TEST_HIST_CACHE_NAME, Old.TEST_HIST_CACHE_NAME);
     }
 
     private void applyDestroyIgnCacheMigration(String cacheName) {
diff --git a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/db/TcHelperDb.java b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/db/TcHelperDb.java
index 23f7e62..140859e 100644
--- a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/db/TcHelperDb.java
+++ b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/db/TcHelperDb.java
@@ -136,42 +136,62 @@ public class TcHelperDb {
 
     }
 
+    @NotNull
+    public static <K, V> CacheConfiguration<K, V> getCache8PartsConfig(String name) {
+        CacheConfiguration<K, V> ccfg = new CacheConfiguration<>(name);
+
+        ccfg.setAffinity(new RendezvousAffinityFunction(false, 8));
+
+        return ccfg;
+    }
+
     public static class LocalOnlyTcpDiscoveryIpFinder implements TcpDiscoveryIpFinder {
+        /** Port. */
         private int port;
 
+        /**
+         * @param port Port.
+         */
         public LocalOnlyTcpDiscoveryIpFinder(int port) {
             this.port = port;
         }
 
+        /** {@inheritDoc} */
         @Override public void onSpiContextInitialized(IgniteSpiContext spiCtx) throws IgniteSpiException {
 
         }
 
+        /** {@inheritDoc} */
         @Override public void onSpiContextDestroyed() {
 
         }
 
-        @Override
-        public void initializeLocalAddresses(Collection<InetSocketAddress> addrs) throws IgniteSpiException {
+        /** {@inheritDoc} */
+        @Override public void initializeLocalAddresses(Collection<InetSocketAddress> addrs) throws IgniteSpiException {
 
         }
 
+        /** {@inheritDoc} */
         @Override public Collection<InetSocketAddress> getRegisteredAddresses() throws IgniteSpiException {
             return Collections.singletonList(new InetSocketAddress("localhost", port));
         }
 
+        /** {@inheritDoc} */
         @Override public boolean isShared() {
             return false;
         }
 
+        /** {@inheritDoc} */
         @Override public void registerAddresses(Collection<InetSocketAddress> addrs) throws IgniteSpiException {
 
         }
 
+        /** {@inheritDoc} */
         @Override public void unregisterAddresses(Collection<InetSocketAddress> addrs) throws IgniteSpiException {
 
         }
 
+        /** {@inheritDoc} */
         @Override public void close() {
 
         }
diff --git a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/github/ignited/GitHubConnIgnitedImpl.java b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/github/ignited/GitHubConnIgnitedImpl.java
index b672223..799a769 100644
--- a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/github/ignited/GitHubConnIgnitedImpl.java
+++ b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/github/ignited/GitHubConnIgnitedImpl.java
@@ -29,14 +29,12 @@ import javax.inject.Inject;
 import javax.inject.Provider;
 import org.apache.ignite.Ignite;
 import org.apache.ignite.IgniteCache;
-import org.apache.ignite.cache.affinity.rendezvous.RendezvousAffinityFunction;
+import org.apache.ignite.ci.db.TcHelperDb;
 import org.apache.ignite.ci.di.AutoProfiling;
 import org.apache.ignite.ci.di.MonitoredTask;
 import org.apache.ignite.ci.di.scheduler.IScheduler;
 import org.apache.ignite.ci.github.PullRequest;
 import org.apache.ignite.ci.github.pure.IGitHubConnection;
-import org.apache.ignite.configuration.CacheConfiguration;
-import org.jetbrains.annotations.NotNull;
 
 /**
  *
@@ -69,16 +67,7 @@ class GitHubConnIgnitedImpl implements IGitHubConnIgnited {
 
         srvIdMaskHigh = Math.abs(srvId.hashCode());
 
-        prCache = igniteProvider.get().getOrCreateCache(getCache8PartsConfig(GIT_HUB_PR));
-    }
-
-    @NotNull
-    public static <K, V> CacheConfiguration<K, V> getCache8PartsConfig(String name) {
-        CacheConfiguration<K, V> ccfg = new CacheConfiguration<>(name);
-
-        ccfg.setAffinity(new RendezvousAffinityFunction(false, 8));
-
-        return ccfg;
+        prCache = igniteProvider.get().getOrCreateCache(TcHelperDb.getCache8PartsConfig(GIT_HUB_PR));
     }
 
     /** {@inheritDoc} */
diff --git a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/teamcity/ignited/BuildRefCompacted.java b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/teamcity/ignited/BuildRefCompacted.java
index f6ccf03..ada9317 100644
--- a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/teamcity/ignited/BuildRefCompacted.java
+++ b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/teamcity/ignited/BuildRefCompacted.java
@@ -191,6 +191,10 @@ public class BuildRefCompacted {
         return compactor.getStringId(STATE_QUEUED) == state();
     }
 
+    public boolean isSuccess(IStringCompactor compactor) {
+        return compactor.getStringId(STATUS_SUCCESS) == status();
+    }
+
     @Override public String toString() {
         return MoreObjects.toStringHelper(this)
             .add("id", id)
diff --git a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/teamcity/ignited/TeamcityIgnitedImpl.java b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/teamcity/ignited/TeamcityIgnitedImpl.java
index f49a102..a6faceb 100644
--- a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/teamcity/ignited/TeamcityIgnitedImpl.java
+++ b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/teamcity/ignited/TeamcityIgnitedImpl.java
@@ -415,7 +415,7 @@ public class TeamcityIgnitedImpl implements ITeamcityIgnited {
             return existingBuild;
 
         buildRefDao.save(srvIdMaskHigh, new BuildRefCompacted(savedVer));
-        runHistSync.saveToHistoryLater(srvNme, buildId, savedVer);
+        runHistSync.saveToHistoryLater(srvNme, savedVer);
 
         return savedVer;
     }
diff --git a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/teamcity/ignited/fatbuild/FatBuildCompacted.java b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/teamcity/ignited/fatbuild/FatBuildCompacted.java
index 8c8b7d4..79e1226 100644
--- a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/teamcity/ignited/fatbuild/FatBuildCompacted.java
+++ b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/teamcity/ignited/fatbuild/FatBuildCompacted.java
@@ -32,6 +32,8 @@ import org.apache.ignite.ci.tcmodel.result.tests.TestOccurrenceFull;
 import org.apache.ignite.ci.tcmodel.result.tests.TestOccurrencesFull;
 import org.apache.ignite.ci.teamcity.ignited.BuildRefCompacted;
 import org.apache.ignite.ci.teamcity.ignited.IStringCompactor;
+import org.apache.ignite.ci.teamcity.ignited.runhist.Invocation;
+import org.apache.ignite.ci.teamcity.ignited.runhist.InvocationData;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
 
@@ -463,4 +465,25 @@ public class FatBuildCompacted extends BuildRefCompacted implements IVersionedEn
             .add("changesIds", changesIds)
             .toString();
     }
+
+    public Invocation toInvocation(IStringCompactor compactor) {
+        boolean success = isSuccess(compactor);
+
+        final Invocation invocation = new Invocation(getId());
+
+        final int failCode ;
+
+        if(success)
+            failCode = InvocationData.OK;
+        else {
+            failCode = InvocationData.FAILURE;
+
+
+        }
+
+        invocation.status((byte) failCode);
+        invocation.startDate(getStartDateTs());
+        invocation.changesPresent(changes().length > 0 ? 1 : 0);
+        return invocation;
+    }
 }
diff --git a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/teamcity/ignited/runhist/RunHistCompactedDao.java b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/teamcity/ignited/runhist/RunHistCompactedDao.java
index 30d29df..51f614f 100644
--- a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/teamcity/ignited/runhist/RunHistCompactedDao.java
+++ b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/teamcity/ignited/runhist/RunHistCompactedDao.java
@@ -18,6 +18,7 @@
 package org.apache.ignite.ci.teamcity.ignited.runhist;
 
 import java.util.List;
+import javax.cache.processor.MutableEntry;
 import org.apache.ignite.Ignite;
 import org.apache.ignite.IgniteCache;
 import org.apache.ignite.cache.QueryEntity;
@@ -30,19 +31,23 @@ import org.apache.ignite.configuration.CacheConfiguration;
 import javax.inject.Inject;
 import javax.inject.Provider;
 import java.util.Collections;
+import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
 
 import static org.apache.ignite.ci.teamcity.ignited.runhist.RunHistSync.normalizeBranch;
 
+/**
+ *
+ */
 public class RunHistCompactedDao {
-    /** Cache name.*/
-    public static final String TEST_HIST_CACHE_NAME = "testRunHistV0";
+    /** Cache name. */
+    public static final String TEST_HIST_CACHE_NAME = "teamcityTestRunHistV0";
 
     /** Build Start time Cache name. */
-    public static final String BUILD_START_TIME_CACHE_NAME = "buildStartTimeV0";
+    public static final String BUILD_START_TIME_CACHE_NAME = "teamcityBuildStartTimeV0";
 
-    /** Cache name.*/
-    public static final String SUITE_HIST_CACHE_NAME = "teamcitySuiteRunHistV0";
+    /** Suites history Cache name.*/
+    public static final String SUITE_HIST_CACHE_NAME = "teamcitySuiteRunHist";
 
 
     /** Ignite provider. */
@@ -75,7 +80,7 @@ public class RunHistCompactedDao {
 
         buildStartTime = ignite.getOrCreateCache(TcHelperDb.getCacheV2Config(BUILD_START_TIME_CACHE_NAME));
 
-        final CacheConfiguration<RunHistKey, RunHistCompacted> cfg2 = TcHelperDb.getCacheV2Config(SUITE_HIST_CACHE_NAME);
+        final CacheConfiguration<RunHistKey, RunHistCompacted> cfg2 = TcHelperDb.getCache8PartsConfig(SUITE_HIST_CACHE_NAME);
 
         cfg2.setQueryEntities(Collections.singletonList(new QueryEntity(RunHistKey.class, RunHistCompacted.class)));
 
@@ -123,38 +128,47 @@ public class RunHistCompactedDao {
     }
 
     @AutoProfiling
-    public Integer addInvocations(RunHistKey histKey, List<Invocation> list) {
+    public Integer addTestInvocations(RunHistKey histKey, List<Invocation> list) {
         if (list.isEmpty())
             return 0;
 
-        return testHistCache.invoke(histKey, (entry, parms) -> {
-                int cnt = 0;
+        return testHistCache.invoke(histKey, RunHistCompactedDao::processEntry, list);
+    }
+
+
+    @AutoProfiling
+    public Integer addSuiteInvocations(RunHistKey histKey, List<Invocation> list) {
+        if (list.isEmpty())
+            return 0;
+
+        return suiteHistCacheName.invoke(histKey, RunHistCompactedDao::processEntry, list);
+    }
+
+    @NotNull public static Integer processEntry(MutableEntry<RunHistKey, RunHistCompacted> entry, Object[] parms) {
+        int cnt = 0;
 
-                RunHistCompacted hist = entry.getValue();
+        RunHistCompacted hist = entry.getValue();
 
-                if (hist == null)
-                    hist = new RunHistCompacted(entry.getKey());
+        if (hist == null)
+            hist = new RunHistCompacted(entry.getKey());
 
-                int initHashCode = hist.hashCode();
+        int initHashCode = hist.hashCode();
 
-                List<Invocation> invocationList = (List<Invocation>)parms[0];
+        List<Invocation> invocationList = (List<Invocation>)parms[0];
 
-                for (Invocation invocation : invocationList) {
-                    boolean added = hist.addTestRun(
-                        invocation.buildId(),
-                        invocation);
+        for (Invocation invocation : invocationList) {
+            boolean added = hist.addTestRun(
+                invocation.buildId(),
+                invocation);
 
-                    if (added)
-                        cnt++;
-                }
+            if (added)
+                cnt++;
+        }
 
-                if (cnt > 0 || hist.hashCode() != initHashCode)
-                    entry.setValue(hist);
+        if (cnt > 0 || hist.hashCode() != initHashCode)
+            entry.setValue(hist);
 
-                return cnt;
-            },
-            list
-        );
+        return cnt;
     }
 
     /**
diff --git a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/teamcity/ignited/runhist/RunHistSync.java b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/teamcity/ignited/runhist/RunHistSync.java
index 9e11c89..99d6ff2 100644
--- a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/teamcity/ignited/runhist/RunHistSync.java
+++ b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/teamcity/ignited/runhist/RunHistSync.java
@@ -18,6 +18,7 @@
 package org.apache.ignite.ci.teamcity.ignited.runhist;
 
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
@@ -26,6 +27,7 @@ import java.util.Set;
 import java.util.concurrent.ThreadLocalRandom;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicInteger;
+import java.util.stream.Collectors;
 import javax.annotation.concurrent.GuardedBy;
 import javax.inject.Inject;
 import org.apache.ignite.ci.ITeamcity;
@@ -87,15 +89,14 @@ public class RunHistSync {
 
     /**
      * @param srvVame Server id.
-     * @param buildId Build id.
      * @param build Build.
      */
-    public void saveToHistoryLater(String srvVame, int buildId, FatBuildCompacted build) {
+    public void saveToHistoryLater(String srvVame, FatBuildCompacted build) {
         if (!validForStatistics(build))
             return;
 
         int srvId = ITeamcityIgnited.serverIdToInt(srvVame);
-        if (histDao.buildWasProcessed(srvId, buildId))
+        if (histDao.buildWasProcessed(srvId, build.id()))
             return;
 
         boolean saveNow = false;
@@ -110,19 +111,26 @@ public class RunHistSync {
             list.add(inv);
         });
 
+        RunHistKey buildInvKey = new RunHistKey(srvId, build.buildTypeId(), branchNameNormalized);
+        Invocation buildInv = build.toInvocation(compactor);
+
         int cnt = containedTestsCnt(testInvMap);
 
         synchronized (this) {
             final SyncTask syncTask = buildToSave.computeIfAbsent(srvVame, s -> new SyncTask());
 
             if (syncTask.sheduled() + cnt <= MAX_TESTS_QUEUE)
-                syncTask.tests.putAll(testInvMap);
+                syncTask.addLater(testInvMap, buildInvKey, buildInv);
             else
                 saveNow = true;
         }
 
-        if(saveNow)
-            saveInvocationsMap(testInvMap);
+        if (saveNow) {
+            Map<RunHistKey, List<Invocation>> suiteAsMap =
+                Collections.singletonMap(buildInvKey,
+                    Collections.singletonList(buildInv));
+            saveInvocationsMap(suiteAsMap, testInvMap);
+        }
         else {
             int ldrToActivate = ThreadLocalRandom.current().nextInt(HIST_LDR_TASKS) + 1;
 
@@ -135,6 +143,7 @@ public class RunHistSync {
     @SuppressWarnings("WeakerAccess")
     protected String saveBuildToHistory(String srvName, int ldrToActivate) {
         Map<RunHistKey, List<Invocation>> saveThisRun;
+        Map<RunHistKey, List<Invocation>> buildsSaveThisRun;
 
         synchronized (this) {
             final SyncTask syncTask = buildToSave.get(srvName);
@@ -142,31 +151,47 @@ public class RunHistSync {
                 return "Nothing to sync";
 
             saveThisRun = syncTask.tests;
+            buildsSaveThisRun = syncTask.suites;
 
             syncTask.tests = new HashMap<>();
         }
 
-        return saveInvocationsMap(saveThisRun);
+        return saveInvocationsMap(buildsSaveThisRun, saveThisRun);
     }
 
     @AutoProfiling
-    @NotNull protected String saveInvocationsMap(Map<RunHistKey, List<Invocation>> saveThisRun) {
+    @NotNull protected String saveInvocationsMap(
+        Map<RunHistKey, List<Invocation>> buildsSaveThisRun,
+        Map<RunHistKey, List<Invocation>> saveThisRun) {
         Set<Integer> confirmedNewBuild = new HashSet<>();
         Set<Integer> confirmedDuplicate = new HashSet<>();
-        AtomicInteger invocations = new AtomicInteger();
+
+        AtomicInteger cntTestInvocations = new AtomicInteger();
         AtomicInteger duplicateOrExpired = new AtomicInteger();
+        AtomicInteger cntSuiteInvocations = new AtomicInteger();
 
         saveThisRun.forEach(
             (histKey, invocationList) -> {
                 saveInvocationList(confirmedNewBuild,
                     confirmedDuplicate,
-                    invocations,
+                    cntTestInvocations,
                     duplicateOrExpired,
                     histKey, invocationList);
             }
         );
 
-        String res = "History entries: " + saveThisRun.size() + " processed " + invocations.get()
+        buildsSaveThisRun.forEach(
+            (histKey,suiteList)->{
+                List<Invocation> invocationsToSave = suiteList.stream()
+                    .filter(invocation -> confirmedNewBuild.contains(invocation.buildId()))
+                    .collect(Collectors.toList());
+
+                Integer cntAdded = histDao.addSuiteInvocations(histKey, invocationsToSave);
+                cntSuiteInvocations.addAndGet(cntAdded);
+            }
+        );
+
+        String res = "History test entries: " + saveThisRun.size() + " processed " + cntTestInvocations.get()
             + " invocations saved to DB " + duplicateOrExpired.get() + " duplicates/expired";
 
         System.out.println(Thread.currentThread().getName() + ":" + res);
@@ -206,7 +231,7 @@ public class RunHistSync {
             }
         );
 
-        Integer cntAdded = histDao.addInvocations(histKey, invocationsToSave);
+        Integer cntAdded = histDao.addTestInvocations(histKey, invocationsToSave);
 
         invocations.addAndGet(cntAdded);
         duplicateOrExpired.addAndGet(invocationList.size() - cntAdded);
@@ -262,7 +287,7 @@ public class RunHistSync {
             FatBuildCompacted fatBuild = fatBuildDao.getFatBuild(ITeamcityIgnited.serverIdToInt(srvNme), id);
 
             if (validForStatistics(fatBuild))
-                saveToHistoryLater(srvNme, fatBuild.id(), fatBuild);
+                saveToHistoryLater(srvNme, fatBuild);
             else
                 logger.info("Build is not valid for stat: " +
                     (fatBuild != null ? fatBuild.getId() : null));
@@ -282,12 +307,21 @@ public class RunHistSync {
      * Scope of work: builds to be loaded from a connection.
      */
     private static class SyncTask {
+        Map<RunHistKey, List<Invocation>> suites = new HashMap<>();
         Map<RunHistKey, List<Invocation>> tests = new HashMap<>();
-        Set<Integer> forBuilds = new HashSet<>();
 
         public int sheduled() {
             return containedTestsCnt(this.tests);
         }
+
+        public void addLater(Map<RunHistKey, List<Invocation>> testInvMap,
+            RunHistKey buildInvKey,
+            Invocation buildInv) {
+            suites
+                .computeIfAbsent(buildInvKey, k -> new ArrayList<>())
+                .add(buildInv);
+            tests.putAll(testInvMap);
+        }
     }
 
     /**
diff --git a/ignite-tc-helper-web/src/test/java/org/apache/ignite/ci/tcbot/chain/PrChainsProcessorTest.java b/ignite-tc-helper-web/src/test/java/org/apache/ignite/ci/tcbot/chain/PrChainsProcessorTest.java
index 4bae79c..741c7f6 100644
--- a/ignite-tc-helper-web/src/test/java/org/apache/ignite/ci/tcbot/chain/PrChainsProcessorTest.java
+++ b/ignite-tc-helper-web/src/test/java/org/apache/ignite/ci/tcbot/chain/PrChainsProcessorTest.java
@@ -69,6 +69,7 @@ public class PrChainsProcessorTest {
     public static final String TEST_WITHOUT_HISTORY = "testWithoutHistory";
     public static final String TEST_WAS_FIXED_IN_MASTER = "testFailingButFixedInMaster";
     public static final int NUM_OF_TESTS_IN_MASTER = 10;
+    public static final String CACHE_1 = "Cache1";
 
     private Map<Integer, FatBuildCompacted> apacheBuilds = new ConcurrentHashMap<>();
 
@@ -168,7 +169,7 @@ public class PrChainsProcessorTest {
         buildBuild.addProblems(c, Collections.singletonList(compile));
 
         final FatBuildCompacted childBuild =
-            createFailedBuild(c, "Cache1", branch, 1001, 100020)
+            createFailedBuild(c, CACHE_1, branch, 1001, 100020)
                 .addTests(c,
                     Lists.newArrayList(
                         createFailedTest(1L, TEST_WITHOUT_HISTORY),
@@ -200,7 +201,7 @@ public class PrChainsProcessorTest {
         addBuilds(cancelledBuild);
 
         for (int i = 0; i < NUM_OF_TESTS_IN_MASTER; i++) {
-            addBuilds(createFailedBuild(c, "Cache1",
+            addBuilds(createFailedBuild(c, CACHE_1,
                 ITeamcity.DEFAULT, 500 + i, 100000 + (i * 10000))
                 .addTests(c, Lists.newArrayList(
                     createFailedTest(2L, TEST_WITH_HISTORY_FAILING_IN_MASTER),
@@ -212,7 +213,7 @@ public class PrChainsProcessorTest {
         long ageMs = TimeUnit.DAYS.toMillis(InvocationData.MAX_DAYS);
 
         for (int i = 0; i < 134; i++) {
-            addBuilds(createFailedBuild(c, "Cache1",
+            addBuilds(createFailedBuild(c, CACHE_1,
                 ITeamcity.DEFAULT, i, ageMs + (i * 10000))
                 .addTests(c, Lists.newArrayList(
                     createFailedTest(400L, TEST_WAS_FIXED_IN_MASTER))));
diff --git a/ignite-tc-helper-web/src/test/java/org/apache/ignite/ci/teamcity/ignited/IgnitedTcInMemoryIntegrationTest.java b/ignite-tc-helper-web/src/test/java/org/apache/ignite/ci/teamcity/ignited/IgnitedTcInMemoryIntegrationTest.java
index 24ee9a7..718aad8 100644
--- a/ignite-tc-helper-web/src/test/java/org/apache/ignite/ci/teamcity/ignited/IgnitedTcInMemoryIntegrationTest.java
+++ b/ignite-tc-helper-web/src/test/java/org/apache/ignite/ci/teamcity/ignited/IgnitedTcInMemoryIntegrationTest.java
@@ -33,6 +33,7 @@ import com.google.inject.internal.SingletonScope;
 import org.apache.ignite.Ignite;
 import org.apache.ignite.Ignition;
 import org.apache.ignite.ci.ITeamcity;
+import org.apache.ignite.ci.analysis.SuiteInBranch;
 import org.apache.ignite.ci.analysis.TestInBranch;
 import org.apache.ignite.ci.db.TcHelperDb;
 import org.apache.ignite.ci.di.scheduler.DirectExecNoWaitSheduler;
@@ -360,7 +361,7 @@ public class IgnitedTcInMemoryIntegrationTest {
         final Map<Integer, FatBuildCompacted> buildsMap = tst.apacheBuilds();
 
         final RunHistSync histSync = injector.getInstance(RunHistSync.class);
-        buildsMap.forEach((id, build) -> histSync.saveToHistoryLater(srvId, id, build));
+        buildsMap.forEach((id, build) -> histSync.saveToHistoryLater(srvId, build));
 
         final ITeamcityIgnitedProvider inst = injector.getInstance(ITeamcityIgnitedProvider.class);
         final ITeamcityIgnited srv = inst.server(srvId, Mockito.mock(ICredentialsProv.class));
@@ -368,6 +369,11 @@ public class IgnitedTcInMemoryIntegrationTest {
 
         assertNotNull(testRunHist);
         assertEquals(0.5, testRunHist.getFailRate(), 0.1);
+
+        final IRunHistory cache1Hist = srv.getSuiteRunHist(new SuiteInBranch(PrChainsProcessorTest.CACHE_1, branch));
+
+        assertNotNull(cache1Hist);
+        assertEquals(1.0, cache1Hist.getFailRate(), 0.1);
     }