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/09 11:45:29 UTC

[ignite-teamcity-bot] 01/02: Compacted history for tests: byte array introduced instead of objects with primitives, caching constants compactor IDs to reduce map get count, unit tests fixed - Fixes #147.

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

commit a96c97db4c04f6121061ba43e2d79298436f3957
Author: Dmitriy Pavlov <dp...@apache.org>
AuthorDate: Fri Aug 9 14:44:47 2019 +0300

    Compacted history for tests: byte array introduced instead of objects with primitives, caching constants compactor IDs to reduce map get count, unit tests fixed - Fixes #147.
    
    Signed-off-by: Dmitriy Pavlov <dp...@apache.org>
---
 .../ci/tcbot/chain/BuildChainProcessorTest.java    |   9 +
 .../ignite/ci/tcbot/issue/IssueDetectorTest.java   |  48 +++--
 .../ci/teamcity/ignited/TeamcityIgnitedMock.java   |   2 +-
 .../IgnitedTcInMemoryIntegrationTest.java          |  83 ++++++++
 .../ignite/tcbot/engine/chain/MultBuildRunCtx.java |   8 +-
 .../tcbot/engine/chain/TestCompactedMult.java      |  26 +--
 .../ci/teamcity/ignited/BuildRefCompacted.java     |  83 +++++++-
 .../ignited/fatbuild/FatBuildCompacted.java        |   2 +-
 .../ci/teamcity/ignited/runhist/Invocation.java    |  24 ++-
 .../ignite/tcignited/build/SuiteHistory.java       |  98 ---------
 .../ignite/tcignited/build/TestCompactedV2.java    |  14 +-
 .../ignite/tcignited/buildref/BuildRefDao.java     |   1 -
 .../history/AbstractRunHist.java}                  | 178 +++++-----------
 .../ignite/tcignited/history/HistoryCollector.java |  10 +-
 .../ignite/tcignited/history/IRunHistSummary.java  |  32 ---
 .../ignite/tcignited/history/IRunHistory.java      |  30 ++-
 .../apache/ignite/tcignited/history/IRunStat.java  |  47 -----
 .../ignite/tcignited/history/ISuiteRunHistory.java |   3 +-
 .../history}/InvocationData.java                   |  82 +++-----
 .../ignite/tcignited/history/RunHistCompacted.java | 122 +++++++++++
 .../ignite/tcignited/history/SuiteHistory.java     | 232 +++++++++++++++++++++
 21 files changed, 706 insertions(+), 428 deletions(-)

diff --git a/ignite-tc-helper-web/src/test/java/org/apache/ignite/ci/tcbot/chain/BuildChainProcessorTest.java b/ignite-tc-helper-web/src/test/java/org/apache/ignite/ci/tcbot/chain/BuildChainProcessorTest.java
index 78eeb25..bffaa89 100644
--- a/ignite-tc-helper-web/src/test/java/org/apache/ignite/ci/tcbot/chain/BuildChainProcessorTest.java
+++ b/ignite-tc-helper-web/src/test/java/org/apache/ignite/ci/tcbot/chain/BuildChainProcessorTest.java
@@ -26,7 +26,9 @@ import java.util.List;
 import java.util.Map;
 import java.util.stream.Collectors;
 
+import org.apache.ignite.ci.teamcity.ignited.BuildRefCompacted;
 import org.apache.ignite.tcbot.engine.chain.*;
+import org.apache.ignite.tcignited.build.TestCompactedV2;
 import org.apache.ignite.tcignited.buildlog.IBuildLogProcessor;
 import org.apache.ignite.tcservice.ITeamcity;
 import org.apache.ignite.tcservice.model.hist.BuildRef;
@@ -39,6 +41,7 @@ import org.apache.ignite.tcignited.SyncMode;
 import org.apache.ignite.ci.teamcity.ignited.TeamcityIgnitedMock;
 import org.apache.ignite.ci.teamcity.ignited.fatbuild.FatBuildCompacted;
 import org.jetbrains.annotations.NotNull;
+import org.junit.Before;
 import org.junit.Test;
 import org.mockito.Mockito;
 
@@ -69,6 +72,12 @@ public class BuildChainProcessorTest {
         }
     });
 
+    @Before
+    public void resetCaches() {
+        BuildRefCompacted.resetCached();
+        TestCompactedV2.resetCached();
+        TestCompactedMult.resetCached();
+    }
     /**
      *
      */
diff --git a/ignite-tc-helper-web/src/test/java/org/apache/ignite/ci/tcbot/issue/IssueDetectorTest.java b/ignite-tc-helper-web/src/test/java/org/apache/ignite/ci/tcbot/issue/IssueDetectorTest.java
index 55b9113..bc5e4c9 100644
--- a/ignite-tc-helper-web/src/test/java/org/apache/ignite/ci/tcbot/issue/IssueDetectorTest.java
+++ b/ignite-tc-helper-web/src/test/java/org/apache/ignite/ci/tcbot/issue/IssueDetectorTest.java
@@ -21,25 +21,24 @@ import com.google.common.base.Preconditions;
 import com.google.common.collect.Lists;
 import com.google.inject.Guice;
 import com.google.inject.Injector;
-import java.io.IOException;
 import java.util.List;
 import java.util.Map;
 import java.util.OptionalInt;
 import java.util.TreeMap;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.stream.Stream;
-import org.apache.ignite.tcservice.ITeamcity;
+import org.apache.ignite.ci.tcbot.chain.MockBasedTcBotModule;
+import org.apache.ignite.ci.teamcity.ignited.TeamcityIgnitedProviderMock;
+import org.apache.ignite.ci.teamcity.ignited.fatbuild.FatBuildCompacted;
+import org.apache.ignite.ci.user.ITcBotUserCreds;
 import org.apache.ignite.tcbot.engine.conf.BranchTracked;
 import org.apache.ignite.tcbot.engine.conf.ChainAtServerTracked;
-import org.apache.ignite.ci.tcbot.chain.MockBasedTcBotModule;
 import org.apache.ignite.tcbot.engine.conf.TcBotJsonConfig;
-import org.apache.ignite.tcservice.model.result.tests.TestOccurrenceFull;
 import org.apache.ignite.tcbot.persistence.IStringCompactor;
 import org.apache.ignite.tcignited.ITeamcityIgnitedProvider;
 import org.apache.ignite.tcignited.TeamcityIgnitedImpl;
-import org.apache.ignite.ci.teamcity.ignited.TeamcityIgnitedProviderMock;
-import org.apache.ignite.ci.teamcity.ignited.fatbuild.FatBuildCompacted;
-import org.apache.ignite.ci.user.ITcBotUserCreds;
+import org.apache.ignite.tcservice.ITeamcity;
+import org.apache.ignite.tcservice.model.result.tests.TestOccurrenceFull;
 import org.jetbrains.annotations.NotNull;
 import org.junit.Before;
 import org.junit.Test;
@@ -57,6 +56,8 @@ import static org.mockito.Mockito.when;
 public class IssueDetectorTest {
     /** Server id. */
     public static final String SRV_ID = "apacheTest";
+    public static final String PDS_1 = "PDS1";
+    public static final String PDS_2 = "PDS2_noChanges";
 
     /** Builds emulated storage. */
     private Map<Integer, FatBuildCompacted> apacheBuilds = new ConcurrentHashMap<>();
@@ -144,6 +145,15 @@ public class IssueDetectorTest {
         IStringCompactor c,
         Map<String, String> pds1Hist,
         Map<String, String> buildWoChanges) {
+        Map<Integer, FatBuildCompacted> builds = apacheBuilds;
+
+        emulateHistory(builds, chainId, c, PDS_1, pds1Hist, PDS_2, buildWoChanges);
+    }
+
+    public static void emulateHistory(Map<Integer, FatBuildCompacted> builds,
+        String chainId, IStringCompactor c,
+        String pds1, Map<String, String> pds1Hist,
+        String pds2, Map<String, String> buildWoChanges) {
         OptionalInt longestHist = Stream.concat(pds1Hist.values().stream(),
             buildWoChanges.values().stream()).mapToInt(String::length).max();
         Preconditions.checkState(longestHist.isPresent());
@@ -151,26 +161,26 @@ public class IssueDetectorTest {
 
         for (int i = 0; i < histLen; i++) {
             FatBuildCompacted pds1Build
-                = createFatBuild(c, "PDS1", ITeamcity.DEFAULT, 1100 + i, 100 * i, false)
+                = createFatBuild(c, pds1, ITeamcity.DEFAULT, 1100 + i, 100 * i, false)
                 .addTests(c, testsMapToXmlModel(pds1Hist, histLen, i), null)
                 .changes(new int[] {i});
 
-            apacheBuilds.put(pds1Build.id(), pds1Build);
+            builds.put(pds1Build.id(), pds1Build);
 
             FatBuildCompacted pds2Build
-                = createFatBuild(c, "PDS2_noChanges", ITeamcity.DEFAULT, 1200 + i, 100 * i, false)
+                = createFatBuild(c, pds2, ITeamcity.DEFAULT, 1200 + i, 100 * i, false)
                 .addTests(c, testsMapToXmlModel(buildWoChanges, histLen, i), null);
 
-            apacheBuilds.put(pds2Build.id(), pds2Build);
+            builds.put(pds2Build.id(), pds2Build);
 
             FatBuildCompacted chainBuild = createFatBuild(c, chainId, ITeamcity.DEFAULT, 1000 + i, 100 * i, false)
                 .snapshotDependencies(new int[] {pds1Build.id(), pds2Build.id()});
-            apacheBuilds.put(chainBuild.id(), chainBuild);
+            builds.put(chainBuild.id(), chainBuild);
         }
     }
 
     @NotNull
-    public List<TestOccurrenceFull> testsMapToXmlModel(
+    public static List<TestOccurrenceFull> testsMapToXmlModel(
         Map<String, String> pds1Hist,
         int histLen,
         int idx) {
@@ -184,9 +194,15 @@ public class IssueDetectorTest {
 
             char chState = stat.charAt(locIdx);
             boolean ok = '0' == chState;
-            boolean failed = '1' == chState;
-            if (ok || failed)
-                page.add(createTest(name.hashCode(), name, ok));
+            boolean failed = '1' == chState || '6' == chState;
+            boolean muted = '6' == chState;
+            if (ok || failed) {
+                TestOccurrenceFull test = createTest(name.hashCode(), name, ok);
+                if (muted)
+                    test.muted = true;
+
+                page.add(test);
+            }
         });
 
         return page;
diff --git a/ignite-tc-helper-web/src/test/java/org/apache/ignite/ci/teamcity/ignited/TeamcityIgnitedMock.java b/ignite-tc-helper-web/src/test/java/org/apache/ignite/ci/teamcity/ignited/TeamcityIgnitedMock.java
index b4484dd..2531792 100644
--- a/ignite-tc-helper-web/src/test/java/org/apache/ignite/ci/teamcity/ignited/TeamcityIgnitedMock.java
+++ b/ignite-tc-helper-web/src/test/java/org/apache/ignite/ci/teamcity/ignited/TeamcityIgnitedMock.java
@@ -20,7 +20,7 @@ package org.apache.ignite.ci.teamcity.ignited;
 import com.google.common.base.Preconditions;
 import org.apache.ignite.ci.teamcity.ignited.fatbuild.FatBuildCompacted;
 import org.apache.ignite.ci.teamcity.ignited.runhist.Invocation;
-import org.apache.ignite.ci.teamcity.ignited.runhist.RunHistCompacted;
+import org.apache.ignite.tcignited.history.RunHistCompacted;
 import org.apache.ignite.ci.teamcity.ignited.runhist.RunHistKey;
 import org.apache.ignite.tcbot.common.TcBotConst;
 import org.apache.ignite.tcbot.common.conf.ITcServerConfig;
diff --git a/ignite-tc-helper-web/src/test/java/org/apache/ignite/tcignited/IgnitedTcInMemoryIntegrationTest.java b/ignite-tc-helper-web/src/test/java/org/apache/ignite/tcignited/IgnitedTcInMemoryIntegrationTest.java
index 3a19844..875627c 100644
--- a/ignite-tc-helper-web/src/test/java/org/apache/ignite/tcignited/IgnitedTcInMemoryIntegrationTest.java
+++ b/ignite-tc-helper-web/src/test/java/org/apache/ignite/tcignited/IgnitedTcInMemoryIntegrationTest.java
@@ -29,11 +29,14 @@ import java.io.InputStream;
 import java.io.InputStreamReader;
 import java.time.Duration;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
+import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.TreeMap;
 import java.util.TreeSet;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
@@ -50,6 +53,7 @@ import org.apache.ignite.IgniteCache;
 import org.apache.ignite.Ignition;
 import org.apache.ignite.ci.db.TcHelperDb;
 import org.apache.ignite.ci.tcbot.chain.PrChainsProcessorTest;
+import org.apache.ignite.ci.tcbot.issue.IssueDetectorTest;
 import org.apache.ignite.ci.teamcity.ignited.BuildRefCompacted;
 import org.apache.ignite.ci.teamcity.ignited.buildtype.BuildTypeRefCompacted;
 import org.apache.ignite.ci.teamcity.ignited.fatbuild.FatBuildCompacted;
@@ -62,8 +66,10 @@ import org.apache.ignite.tcbot.common.conf.IDataSourcesConfigSupplier;
 import org.apache.ignite.tcbot.common.conf.ITcServerConfig;
 import org.apache.ignite.tcbot.common.interceptor.GuavaCachedModule;
 import org.apache.ignite.tcbot.common.util.FutureUtil;
+import org.apache.ignite.tcbot.engine.chain.TestCompactedMult;
 import org.apache.ignite.tcbot.engine.conf.ITcBotConfig;
 import org.apache.ignite.tcbot.engine.conf.TcBotJsonConfig;
+import org.apache.ignite.tcbot.engine.issue.EventTemplates;
 import org.apache.ignite.tcbot.persistence.IStringCompactor;
 import org.apache.ignite.tcbot.persistence.IgniteStringCompactor;
 import org.apache.ignite.tcbot.persistence.TcBotPersistenceModule;
@@ -71,11 +77,14 @@ import org.apache.ignite.tcbot.persistence.scheduler.DirectExecNoWaitScheduler;
 import org.apache.ignite.tcbot.persistence.scheduler.IScheduler;
 import org.apache.ignite.tcignited.build.FatBuildDao;
 import org.apache.ignite.tcignited.build.ProactiveFatBuildSync;
+import org.apache.ignite.tcignited.build.TestCompactedV2;
 import org.apache.ignite.tcignited.buildlog.ILogProductSpecific;
 import org.apache.ignite.tcignited.buildref.BuildRefDao;
 import org.apache.ignite.tcignited.history.BuildStartTimeStorage;
+import org.apache.ignite.tcignited.history.HistoryCollector;
 import org.apache.ignite.tcignited.history.IRunHistory;
 import org.apache.ignite.tcignited.history.ISuiteRunHistory;
+import org.apache.ignite.tcignited.history.SuiteInvocationHistoryDao;
 import org.apache.ignite.tcservice.ITeamcity;
 import org.apache.ignite.tcservice.TeamcityServiceConnection;
 import org.apache.ignite.tcservice.http.ITeamcityHttpConnection;
@@ -108,6 +117,7 @@ import static junit.framework.TestCase.assertFalse;
 import static junit.framework.TestCase.assertNotNull;
 import static junit.framework.TestCase.assertNull;
 import static junit.framework.TestCase.assertTrue;
+import static org.apache.ignite.ci.tcbot.issue.IssueDetectorTest.SRV_ID;
 import static org.apache.ignite.tcbot.common.conf.TcBotWorkDir.ensureDirExist;
 import static org.apache.ignite.tcbot.persistence.IgniteStringCompactor.STRINGS_CACHE;
 import static org.mockito.ArgumentMatchers.anyString;
@@ -161,6 +171,10 @@ public class IgnitedTcInMemoryIntegrationTest {
     public void clearIgniteCaches() {
         clearCache(BuildRefDao.TEAMCITY_BUILD_CACHE_NAME);
         clearCache(FatBuildDao.TEAMCITY_FAT_BUILD_CACHE_NAME);
+
+        BuildRefCompacted.resetCached();
+        TestCompactedV2.resetCached();
+        TestCompactedMult.resetCached();
     }
 
     /**
@@ -810,7 +824,76 @@ public class IgnitedTcInMemoryIntegrationTest {
 
         FutureUtil.getResults(futures);
         svc.shutdown();
+    }
+
+    @Test
+    public void testTestHistoryPropagation() {
+        TeamcityIgnitedModule module = new TeamcityIgnitedModule();
+
+        Injector injector = Guice.createInjector(module, new GuavaCachedModule(), new IgniteAndSchedulerTestModule());
+
+        String chainId = TeamcityIgnitedImpl.DEFAULT_PROJECT_ID;
+        IStringCompactor c = injector.getInstance(IStringCompactor.class);
+
+        String testUnmuted = "testUnmuted";
+        Map<String, String> pds1Hist = new TreeMap<String, String>() {
+            {
+                put(testUnmuted, "66666611111");
+                put("testOk", "      0000");
+            }
+        };
+        String testFlakyStableFailure = "testFlakyStableFailure";
+
+        Map<String, String> pds2Hist = new TreeMap<String, String>() {
+            {
+                put("testFailedShoudlBeConsideredAsFlaky", "0000011111");
+                put(testFlakyStableFailure, "0000011111111111");
+            }
+        };
+
+        Map<Integer, FatBuildCompacted> builds = new HashMap<>();
+        String suite1 = IssueDetectorTest.PDS_1;
+        String suite2 = IssueDetectorTest.PDS_2;
+        IssueDetectorTest.emulateHistory(builds, chainId, c, suite1, pds1Hist, suite2, pds2Hist);
+
+        BuildRefDao buildRefDao = injector.getInstance(BuildRefDao.class).init();
+
+        FatBuildDao fatBuildDao = injector.getInstance(FatBuildDao.class).init();
+
+        injector.getInstance(SuiteInvocationHistoryDao.class).init();
+        injector.getInstance(BuildStartTimeStorage.class).init();
+
+        int srvId = ITeamcityIgnited.serverIdToInt(IssueDetectorTest.SRV_ID);
+        builds.forEach((k, v) -> {
+            buildRefDao.save(srvId, new BuildRefCompacted(v));
+            fatBuildDao.putFatBuild(srvId, k, v);
+        });
+
+        HistoryCollector histCollector = injector.getInstance(HistoryCollector.class);
+
+        IRunHistory hist = histCollector.getTestRunHist(SRV_ID,
+            c.getStringId(testUnmuted),
+            c.getStringId(suite1),
+            c.getStringId(ITeamcity.DEFAULT));
+
+        assertNotNull(hist);
+
+        assertEquals(Arrays.asList(4, 4, 4, 4, 4, 6, 6, 6, 6, 6, 6, 1, 1, 1, 1, 1), hist.getLatestRunResults());
+
+        assertEquals(0, hist.getCriticalFailuresCount());
+
+        assertFalse(hist.isFlaky());
+        assertEquals(1.0, hist.getFailRate(), 0.05);
+
+        IRunHistory histFailedFlaky = histCollector.getTestRunHist(SRV_ID,
+            c.getStringId(testFlakyStableFailure),
+            c.getStringId(suite2),
+            c.getStringId(ITeamcity.DEFAULT));
+
+        assertTrue(histFailedFlaky.isFlaky());
 
+        Integer integer = histFailedFlaky.detectTemplate(EventTemplates.newFailureForFlakyTest);
+        assertNotNull(integer);
     }
 
     /**
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 7fb1a87..53d2486 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
@@ -697,14 +697,14 @@ public class MultBuildRunCtx implements ISuiteResults {
             return null;
 
         try {
-            ISuiteRunHistory suiteRunHistory = histCacheMap.get(baseBranchId,
+            ISuiteRunHistory suiteRunHist = histCacheMap.get(baseBranchId,
                 () -> Optional.ofNullable(tcIgn.getSuiteRunHist(buildTypeIdId, baseBranchId)))
                 .orElse(null);
 
-            if (suiteRunHistory != null && requireParameters != null && !requireParameters.isEmpty())
-                return suiteRunHistory.filter(requireParameters);
+            if (suiteRunHist != null && requireParameters != null && !requireParameters.isEmpty())
+                return suiteRunHist.filter(requireParameters);
 
-            return suiteRunHistory;
+            return suiteRunHist;
         }
         catch (ExecutionException e) {
             throw ExceptionUtil.propagateException(e);
diff --git a/tcbot-engine/src/main/java/org/apache/ignite/tcbot/engine/chain/TestCompactedMult.java b/tcbot-engine/src/main/java/org/apache/ignite/tcbot/engine/chain/TestCompactedMult.java
index 8e6b007..9863364 100644
--- a/tcbot-engine/src/main/java/org/apache/ignite/tcbot/engine/chain/TestCompactedMult.java
+++ b/tcbot-engine/src/main/java/org/apache/ignite/tcbot/engine/chain/TestCompactedMult.java
@@ -28,9 +28,7 @@ import org.apache.ignite.tcbot.common.TcBotConst;
 import org.apache.ignite.tcbot.persistence.IStringCompactor;
 import org.apache.ignite.tcignited.ITeamcityIgnited;
 import org.apache.ignite.tcignited.build.ITest;
-import org.apache.ignite.tcignited.history.IRunHistSummary;
 import org.apache.ignite.tcignited.history.IRunHistory;
-import org.apache.ignite.tcignited.history.IRunStat;
 import org.apache.ignite.tcignited.history.ISuiteRunHistory;
 import org.apache.ignite.tcservice.model.result.tests.TestOccurrence;
 
@@ -44,15 +42,19 @@ public class TestCompactedMult {
     private long avgDuration = -1;
 
     /** Status success. */
-    private static volatile int STATUS_SUCCESS = -1;
+    private static volatile int STATUS_SUCCESS_CID = -1;
 
     public TestCompactedMult(IStringCompactor compactor, MultBuildRunCtx ctx) {
         this.compactor = compactor;
         this.ctx = ctx;
 
         //Each time compactor should give same result
-        if (STATUS_SUCCESS == -1)
-            STATUS_SUCCESS = compactor.getStringId(TestOccurrence.STATUS_SUCCESS);
+        if (STATUS_SUCCESS_CID == -1)
+            STATUS_SUCCESS_CID = compactor.getStringId(TestOccurrence.STATUS_SUCCESS);
+    }
+
+    public static void resetCached() {
+        STATUS_SUCCESS_CID = -1;
     }
 
     @Nullable public Integer testName() {
@@ -71,7 +73,7 @@ public class TestCompactedMult {
     private int getFailedButNotMutedCount() {
         return (int)occurrences.stream()
             .filter(Objects::nonNull)
-            .filter(t -> t.isFailedButNotMuted(STATUS_SUCCESS)).count();
+            .filter(t -> t.isFailedButNotMuted(STATUS_SUCCESS_CID)).count();
     }
 
     public int failuresCount() {
@@ -102,7 +104,7 @@ public class TestCompactedMult {
       * @param baseBranchStat Base branch statistics.
       * @return non null comment in case test failure is a blocker for merge into base branch.
       */
-     public String getPossibleBlockerComment(IRunHistSummary baseBranchStat) {
+     public String getPossibleBlockerComment(IRunHistory baseBranchStat) {
          if (failuresCount() == 0) {
              if (baseBranchStat == null) {
                  long durationMs = getAvgDurationMs();
@@ -124,7 +126,7 @@ public class TestCompactedMult {
          boolean lowFailureRate = failRate * 100.0f < TcBotConst.NON_FLAKY_TEST_FAIL_RATE_BLOCKER_BORDER_PERCENTS;
 
          if (lowFailureRate && !flaky) {
-             String runStatPrintable = IRunStat.getPercentPrintable(failRate * 100.0f);
+             String runStatPrintable = IRunHistory.getPercentPrintable(failRate * 100.0f);
 
              return "Test has low fail rate in base branch "
                  + runStatPrintable
@@ -142,9 +144,9 @@ public class TestCompactedMult {
          return history(ignited, baseBranchId, null);
     }
 
-    public IRunHistory history(ITeamcityIgnited ignited,
-                               @Nullable Integer baseBranchId,
-                               @Nullable Map<Integer, Integer> requireParameters) {
+    @Nullable public IRunHistory history(ITeamcityIgnited ignited,
+        @Nullable Integer baseBranchId,
+        @Nullable Map<Integer, Integer> requireParameters) {
         Integer name = testName();
         if (name == null || baseBranchId == null)
             return null;
@@ -160,7 +162,7 @@ public class TestCompactedMult {
     /**
      */
     public boolean isFailedButNotMuted() {
-        return occurrences.stream().anyMatch(o -> o.isFailedButNotMuted(STATUS_SUCCESS));
+        return occurrences.stream().anyMatch(o -> o.isFailedButNotMuted(STATUS_SUCCESS_CID));
     }
 
     /**
diff --git a/tcbot-teamcity-ignited/src/main/java/org/apache/ignite/ci/teamcity/ignited/BuildRefCompacted.java b/tcbot-teamcity-ignited/src/main/java/org/apache/ignite/ci/teamcity/ignited/BuildRefCompacted.java
index 50a367f..4766e77 100644
--- a/tcbot-teamcity-ignited/src/main/java/org/apache/ignite/ci/teamcity/ignited/BuildRefCompacted.java
+++ b/tcbot-teamcity-ignited/src/main/java/org/apache/ignite/ci/teamcity/ignited/BuildRefCompacted.java
@@ -18,16 +18,18 @@ package org.apache.ignite.ci.teamcity.ignited;
 
 import com.google.common.base.MoreObjects;
 import com.google.common.base.Objects;
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
 import org.apache.ignite.cache.query.annotations.QuerySqlField;
-
 import org.apache.ignite.tcbot.persistence.IStringCompactor;
 import org.apache.ignite.tcbot.persistence.Persisted;
 import org.apache.ignite.tcservice.model.hist.BuildRef;
 
-import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
-
-import static org.apache.ignite.tcservice.model.hist.BuildRef.*;
+import static org.apache.ignite.tcservice.model.hist.BuildRef.STATE_FINISHED;
+import static org.apache.ignite.tcservice.model.hist.BuildRef.STATE_QUEUED;
+import static org.apache.ignite.tcservice.model.hist.BuildRef.STATE_RUNNING;
+import static org.apache.ignite.tcservice.model.hist.BuildRef.STATUS_SUCCESS;
+import static org.apache.ignite.tcservice.model.hist.BuildRef.STATUS_UNKNOWN;
 
 @Persisted
 public class BuildRefCompacted {
@@ -47,6 +49,19 @@ public class BuildRefCompacted {
     /** Compacter identifier for string 'State'. */
     private int state = -1;
 
+    /** Status Unknown: Compactor identifier. */
+    private static volatile int STATUS_UNKNOWN_CID = -1;
+    /** Status success: Compactor identifier. */
+    private static volatile int STATUS_SUCCESS_CID = -1;
+
+    /** State running: Compactor identifier. */
+    private static volatile int STATE_RUNNING_CID = -1;
+    /** State queued: Compactor identifier. */
+    private static volatile int STATE_QUEUED_CID = -1;
+    /** State finished: Compactor identifier. */
+    private static volatile int STATE_FINISHED_CID = -1;
+
+
     /**
      * Default constructor.
      */
@@ -61,6 +76,17 @@ public class BuildRefCompacted {
         fillFieldsFromBuildRef(compactor, ref);
     }
 
+    /**
+     * Reset cached Ids for tests
+     */
+    public static void resetCached() {
+        STATE_FINISHED_CID = -1;
+        STATE_QUEUED_CID = -1;
+        STATUS_UNKNOWN_CID = -1;
+        STATUS_SUCCESS_CID = -1;
+        STATE_RUNNING_CID = -1;
+    }
+
     public void fillFieldsFromBuildRef(IStringCompactor compactor, BuildRef ref) {
         setId(ref.getId());
         buildTypeId = compactor.getStringId(ref.buildTypeId());
@@ -185,6 +211,42 @@ public class BuildRefCompacted {
         return id() < 0;
     }
 
+
+    private int statusUnkown(IStringCompactor compactor) {
+        if (STATUS_UNKNOWN_CID == -1) //Each time compactor should give same result, so no locking applied
+            STATUS_UNKNOWN_CID = compactor.getStringId(STATUS_UNKNOWN);
+
+        return STATUS_UNKNOWN_CID;
+    }
+
+    public int statusSuccess(IStringCompactor compactor) {
+        if (STATUS_SUCCESS_CID == -1) //Each time compactor should give same result, so no locking applied
+            STATUS_SUCCESS_CID =  compactor.getStringId(STATUS_SUCCESS);
+
+        return STATUS_SUCCESS_CID;
+    }
+
+    public int stateRunning(IStringCompactor compactor) {
+        if (STATE_RUNNING_CID == -1) //Each time compactor should give same result, so no locking applied
+            STATE_RUNNING_CID = compactor.getStringId(STATE_RUNNING);
+
+        return STATE_RUNNING_CID;
+    }
+
+    public int stateFinished(IStringCompactor compactor) {
+        if (STATE_FINISHED_CID == -1) //Each time compactor should give same result, so no locking applied
+            STATE_FINISHED_CID =  compactor.getStringId(STATE_FINISHED);
+
+        return STATE_FINISHED_CID;
+    }
+
+    public int stateQueued(IStringCompactor compactor) {
+        if (STATE_QUEUED_CID == -1) //Each time compactor should give same result, so no locking applied
+            STATE_QUEUED_CID = compactor.getStringId(STATE_QUEUED);
+
+        return STATE_QUEUED_CID;
+    }
+
     public boolean isNotCancelled(IStringCompactor compactor) {
         return !isCancelled(compactor);
     }
@@ -194,25 +256,26 @@ public class BuildRefCompacted {
     }
 
     private boolean hasUnknownStatus(IStringCompactor compactor) {
-        return compactor.getStringId(STATUS_UNKNOWN) == status();
+        return statusUnkown(compactor) == status();
     }
 
     public boolean isRunning(IStringCompactor compactor) {
-        return compactor.getStringId(STATE_RUNNING) == state();
+        return stateRunning(compactor) == state();
     }
 
     public boolean isFinished(IStringCompactor compactor) {
-        return compactor.getStringId(STATE_FINISHED) == state();
+        return stateFinished(compactor) == state();
     }
 
     public boolean isQueued(IStringCompactor compactor) {
-        return compactor.getStringId(STATE_QUEUED) == state();
+        return stateQueued(compactor) == state();
     }
 
     public boolean isSuccess(IStringCompactor compactor) {
-        return compactor.getStringId(STATUS_SUCCESS) == status();
+        return statusSuccess(compactor) == status();
     }
 
+    /** {@inheritDoc} */
     @Override public String toString() {
         return MoreObjects.toStringHelper(this)
             .add("id", id)
diff --git a/tcbot-teamcity-ignited/src/main/java/org/apache/ignite/ci/teamcity/ignited/fatbuild/FatBuildCompacted.java b/tcbot-teamcity-ignited/src/main/java/org/apache/ignite/ci/teamcity/ignited/fatbuild/FatBuildCompacted.java
index 42b36c4..ac11fb0 100644
--- a/tcbot-teamcity-ignited/src/main/java/org/apache/ignite/ci/teamcity/ignited/fatbuild/FatBuildCompacted.java
+++ b/tcbot-teamcity-ignited/src/main/java/org/apache/ignite/ci/teamcity/ignited/fatbuild/FatBuildCompacted.java
@@ -38,7 +38,7 @@ import org.apache.ignite.ci.teamcity.ignited.BuildRefCompacted;
 import org.apache.ignite.ci.teamcity.ignited.buildtype.ParametersCompacted;
 import org.apache.ignite.ci.teamcity.ignited.change.RevisionCompacted;
 import org.apache.ignite.ci.teamcity.ignited.runhist.Invocation;
-import org.apache.ignite.ci.teamcity.ignited.runhist.InvocationData;
+import org.apache.ignite.tcignited.history.InvocationData;
 import org.apache.ignite.tcbot.persistence.IStringCompactor;
 import org.apache.ignite.tcbot.persistence.IVersionedEntity;
 import org.apache.ignite.tcbot.persistence.Persisted;
diff --git a/tcbot-teamcity-ignited/src/main/java/org/apache/ignite/ci/teamcity/ignited/runhist/Invocation.java b/tcbot-teamcity-ignited/src/main/java/org/apache/ignite/ci/teamcity/ignited/runhist/Invocation.java
index bc8a89d..f4a1dea 100644
--- a/tcbot-teamcity-ignited/src/main/java/org/apache/ignite/ci/teamcity/ignited/runhist/Invocation.java
+++ b/tcbot-teamcity-ignited/src/main/java/org/apache/ignite/ci/teamcity/ignited/runhist/Invocation.java
@@ -20,15 +20,14 @@ package org.apache.ignite.ci.teamcity.ignited.runhist;
 import com.google.common.base.MoreObjects;
 import com.google.common.base.Objects;
 import com.google.common.base.Preconditions;
-import org.apache.ignite.ci.teamcity.ignited.buildtype.ParametersCompacted;
-import org.apache.ignite.tcbot.persistence.Persisted;
-import org.apache.ignite.tcignited.history.ChangesState;
-
 import java.util.Map;
 import java.util.Set;
 import javax.annotation.Nonnull;
 import javax.annotation.Nullable;
-
+import org.apache.ignite.ci.teamcity.ignited.buildtype.ParametersCompacted;
+import org.apache.ignite.tcbot.persistence.Persisted;
+import org.apache.ignite.tcignited.history.ChangesState;
+import org.apache.ignite.tcignited.history.InvocationData;
 
 /**
  * Run history element: invocation of build or test.
@@ -152,4 +151,19 @@ public class Invocation {
     public boolean containsParameterValue(Map<Integer, Integer> requireParameters) {
         return hasAnyParameterValue(this.parameters, requireParameters);
     }
+
+    public Invocation withChangeState(ChangesState state) {
+        if (state == ChangesState.NONE)
+            changePresent = NO_CHANGES;
+        else if (state == ChangesState.EXIST)
+            changePresent = CHANGE_PRESENT;
+        else
+            changePresent = CHANGE_NOT_FILLED;
+
+        return this;
+    }
+
+    public static boolean isMutedOrIgnored(byte s) {
+        return s == InvocationData.MUTED || s == InvocationData.FAILURE_MUTED || s == InvocationData.OK_MUTED || s == InvocationData.IGNORED;
+    }
 }
diff --git a/tcbot-teamcity-ignited/src/main/java/org/apache/ignite/tcignited/build/SuiteHistory.java b/tcbot-teamcity-ignited/src/main/java/org/apache/ignite/tcignited/build/SuiteHistory.java
deleted file mode 100644
index 508e94f..0000000
--- a/tcbot-teamcity-ignited/src/main/java/org/apache/ignite/tcignited/build/SuiteHistory.java
+++ /dev/null
@@ -1,98 +0,0 @@
-/*
- * 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.HashMap;
-import java.util.Map;
-import java.util.Set;
-import org.apache.ignite.Ignite;
-import org.apache.ignite.ci.teamcity.ignited.runhist.Invocation;
-import org.apache.ignite.ci.teamcity.ignited.runhist.RunHistCompacted;
-import org.apache.ignite.internal.binary.BinaryObjectExImpl;
-import org.apache.ignite.tcignited.history.IRunHistory;
-import org.apache.ignite.tcignited.history.ISuiteRunHistory;
-import org.apache.ignite.tcignited.history.SuiteInvocation;
-
-/**
- * Suite run history (in memory) summary with tests grouped by name.
- */
-public class SuiteHistory implements ISuiteRunHistory {
-    /** Tests history: Test name ID->RunHistory */
-    private Map<Integer, RunHistCompacted> testsHistory = new HashMap<>();
-
-    private RunHistCompacted suiteHist = new RunHistCompacted();
-
-    public SuiteHistory(Map<Integer, SuiteInvocation> suiteRunHist) {
-        suiteRunHist.forEach((buildId, suiteInv) -> addSuiteInvocation(suiteInv));
-        finalizeInvocations();
-    }
-
-    private SuiteHistory() {}
-
-    private void finalizeInvocations() {
-        Set<Integer> presentBuilds = suiteHist.buildIds();
-
-        testsHistory.forEach((k, t) -> t.registerMissing(k, presentBuilds));
-
-        suiteHist.sort();
-        testsHistory.values().forEach(RunHistCompacted::sort);
-    }
-
-    public int size(Ignite ignite) {
-        BinaryObjectExImpl binary = ignite.binary().toBinary(this);
-        return binary.length();
-    }
-
-    public IRunHistory getTestRunHist(int testName) {
-        return testsHistory.get(testName);
-    }
-
-    @Override
-    public ISuiteRunHistory filter(Map<Integer, Integer> requireParameters) {
-        RunHistCompacted suitesFiltered = this.suiteHist.filterSuiteInvByParms(requireParameters);
-        Set<Integer> builds = suitesFiltered.buildIds();
-
-        SuiteHistory res = new SuiteHistory();
-
-        res.suiteHist = suitesFiltered;
-        this.testsHistory.forEach((tName,invList)-> res.testsHistory.put(tName, invList.filterByBuilds(builds)));
-
-        return res;
-    }
-
-    private RunHistCompacted getOrAddTestsHistory(Integer tName) {
-        return testsHistory.computeIfAbsent(tName, k_ -> new RunHistCompacted());
-    }
-
-    private void addTestInvocation(Integer tName, Invocation invocation) {
-        getOrAddTestsHistory(tName).addInvocation(invocation);
-    }
-
-    /**
-     * @param suiteInv suite invocation (build) to be added to history (summary).
-     */
-    private void addSuiteInvocation(SuiteInvocation suiteInv) {
-        suiteInv.tests().forEach(this::addTestInvocation);
-
-        suiteHist.addInvocation(suiteInv.suiteInvocation());
-    }
-
-    @Override public IRunHistory self() {
-        return suiteHist;
-    }
-}
diff --git a/tcbot-teamcity-ignited/src/main/java/org/apache/ignite/tcignited/build/TestCompactedV2.java b/tcbot-teamcity-ignited/src/main/java/org/apache/ignite/tcignited/build/TestCompactedV2.java
index 15ec251..cfd64f8 100644
--- a/tcbot-teamcity-ignited/src/main/java/org/apache/ignite/tcignited/build/TestCompactedV2.java
+++ b/tcbot-teamcity-ignited/src/main/java/org/apache/ignite/tcignited/build/TestCompactedV2.java
@@ -24,7 +24,7 @@ import javax.annotation.Nullable;
 import org.apache.ignite.ci.tcbot.common.StringFieldCompacted;
 import org.apache.ignite.ci.teamcity.ignited.fatbuild.FatBuildCompacted;
 import org.apache.ignite.ci.teamcity.ignited.runhist.Invocation;
-import org.apache.ignite.ci.teamcity.ignited.runhist.InvocationData;
+import org.apache.ignite.tcignited.history.InvocationData;
 import org.apache.ignite.tcbot.persistence.IStringCompactor;
 import org.apache.ignite.tcbot.persistence.Persisted;
 import org.apache.ignite.tcignited.buildlog.ILogProductSpecific;
@@ -49,7 +49,7 @@ public class TestCompactedV2 implements ITest {
     public static final int IGNORED_F = 6;
 
     /** Status success. */
-    private static volatile int STATUS_SUCCESS = -1;
+    private static volatile int STATUS_SUCCESS_CID = -1;
 
     /** Id in this build only. Does not identify test for its history */
     private int idInBuild = -1;
@@ -326,10 +326,14 @@ public class TestCompactedV2 implements ITest {
 
     public int statusSuccess(IStringCompactor compactor) {
         //Each time compactor should give same result, so no locking applied
-        if (STATUS_SUCCESS == -1)
-            STATUS_SUCCESS = compactor.getStringId(TestOccurrence.STATUS_SUCCESS);
+        if (STATUS_SUCCESS_CID == -1)
+            STATUS_SUCCESS_CID = compactor.getStringId(TestOccurrence.STATUS_SUCCESS);
 
-        return STATUS_SUCCESS;
+        return STATUS_SUCCESS_CID;
+    }
+
+    public static void resetCached() {
+        STATUS_SUCCESS_CID = -1;
     }
 
     public String testName(IStringCompactor compactor) {
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 d942c12..74a91f9 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
@@ -305,7 +305,6 @@ public class BuildRefDao {
         }
 
         if (!list.isEmpty()) {
-
             System.err.println(" Branch " + compactor.getStringFromId(branchNameId)
                 + " builds " + list.size() + " (Overall) ");
         }
diff --git a/tcbot-teamcity-ignited/src/main/java/org/apache/ignite/ci/teamcity/ignited/runhist/RunHistCompacted.java b/tcbot-teamcity-ignited/src/main/java/org/apache/ignite/tcignited/history/AbstractRunHist.java
similarity index 54%
rename from tcbot-teamcity-ignited/src/main/java/org/apache/ignite/ci/teamcity/ignited/runhist/RunHistCompacted.java
rename to tcbot-teamcity-ignited/src/main/java/org/apache/ignite/tcignited/history/AbstractRunHist.java
index c61a9a7..8c45088 100644
--- a/tcbot-teamcity-ignited/src/main/java/org/apache/ignite/ci/teamcity/ignited/runhist/RunHistCompacted.java
+++ b/tcbot-teamcity-ignited/src/main/java/org/apache/ignite/tcignited/history/AbstractRunHist.java
@@ -14,73 +14,75 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+package org.apache.ignite.tcignited.history;
 
-package org.apache.ignite.ci.teamcity.ignited.runhist;
-
-import com.google.common.base.MoreObjects;
+import java.util.ArrayList;
 import java.util.List;
-import java.util.Map;
-import java.util.Objects;
-import java.util.Optional;
-import java.util.Set;
-import java.util.stream.Collectors;
 import java.util.stream.Stream;
+import java.util.stream.StreamSupport;
 import javax.annotation.Nullable;
+import org.apache.ignite.ci.teamcity.ignited.runhist.Invocation;
 import org.apache.ignite.tcbot.common.TcBotConst;
-import org.apache.ignite.tcignited.history.ChangesState;
-import org.apache.ignite.tcignited.history.IEventTemplate;
-import org.apache.ignite.tcignited.history.IRunHistory;
-import org.apache.ignite.tcignited.history.RunStatus;
 
 /**
- * In memory replacement of invocation history (RunHist/RunStat).
+ * Abstract in memory summary of suite or test execution history
  */
-public class RunHistCompacted implements IRunHistory {
-    /** Data. */
-    private InvocationData data = new InvocationData();
+public abstract class AbstractRunHist implements IRunHistory {
+    /**
+     *
+     */
+    public abstract Iterable<Invocation> invocations();
 
-    public RunHistCompacted() {
+    /**
+     *
+     */
+    public Stream<Invocation> getInvocations() {
+        return StreamSupport.stream(invocations().spliterator(), false);
     }
 
-    public RunHistCompacted(RunHistKey ignored) {
-
+    /** {@inheritDoc} */
+    @Override public int getCriticalFailuresCount() {
+        return (int)getInvocations().filter(inv -> inv.status() == InvocationData.CRITICAL_FAILURE).count();
     }
 
     /** {@inheritDoc} */
-    @Override public int getRunsCount() {
-        return data.notMutedAndNonMissingRunsCount();
+    @Override public int getFailuresCount() {
+        return (int)getInvocations().filter(inv ->
+            inv.status() == InvocationData.FAILURE
+                || inv.status() == InvocationData.CRITICAL_FAILURE).count();
     }
 
     /** {@inheritDoc} */
-    @Override public int getFailuresCount() {
-        return data.failuresCount();
+    @Override public int getRunsCount() {
+        return (int)getInvocations().filter(inv ->
+            inv.status() != InvocationData.MISSING && !Invocation.isMutedOrIgnored(inv.status())).count();
     }
 
     /** {@inheritDoc} */
-    @Nullable
-    @Override public List<Integer> getLatestRunResults() {
-        return data.getLatestRuns();
+    @Override public boolean isFlaky() {
+        return getStatusChangesWithoutCodeModification() >= TcBotConst.FLAKYNESS_STATUS_CHANGE_BORDER;
     }
 
     /** {@inheritDoc} */
-    @Override public String getFlakyComments() {
+    @Override public final String getFlakyComments() {
         int statusChange = getStatusChangesWithoutCodeModification();
 
         if (statusChange < TcBotConst.FLAKYNESS_STATUS_CHANGE_BORDER)
             return null;
 
         return "Test seems to be flaky: " +
-            "changed its status [" + statusChange + "/" + data.invocations().count() + "] without code modifications";
+            "changed its status [" + statusChange + "/" + getInvocations().count() + "] without code modifications";
     }
 
+    /**
+     * @return number of status change without code modifications for test to be considered as flaky
+     */
     public int getStatusChangesWithoutCodeModification() {
         int statusChange = 0;
 
         Invocation prev = null;
 
-        List<Invocation> latestRuns = data.invocations().collect(Collectors.toList());
-
-        for (Invocation cur : latestRuns) {
+        for (Invocation cur : invocations()) {
             if (cur == null)
                 continue;
 
@@ -88,7 +90,6 @@ public class RunHistCompacted implements IRunHistory {
                 continue;
 
             //todo here all previous MISSING invocations status could be checked
-            // (using outside build history)
             if (prev != null) {
                 if (prev.status() != cur.status()
                     && cur.changesState() == ChangesState.NONE
@@ -101,34 +102,8 @@ public class RunHistCompacted implements IRunHistory {
         return statusChange;
     }
 
-    /** {@inheritDoc} */
-    @Override public boolean isFlaky() {
-        return getStatusChangesWithoutCodeModification() >= TcBotConst.FLAKYNESS_STATUS_CHANGE_BORDER;
-    }
-
-    /** {@inheritDoc} */
-    @Override public int getCriticalFailuresCount() {
-        return data.criticalFailuresCount();
-    }
-
-    /** {@inheritDoc} */
-    @Override public Set<Integer> buildIds() {
-        return data.buildIds();
-    }
-
-    private static int[] concatArr(int[] arr1, int[] arr2) {
-        int[] arr1and2 = new int[arr1.length + arr2.length];
-        System.arraycopy(arr1, 0, arr1and2, 0, arr1.length);
-        System.arraycopy(arr2, 0, arr1and2, arr1.length, arr2.length);
-
-        return arr1and2;
-    }
-
     @Nullable
     public Integer detectTemplate(IEventTemplate t) {
-        if (data == null)
-            return null;
-
         int centralEvtBuild = t.beforeEvent().length;
 
         int[] template = concatArr(t.beforeEvent(), t.eventAndAfter());
@@ -137,7 +112,12 @@ public class RunHistCompacted implements IRunHistory {
         assert centralEvtBuild >= 0;
 
         boolean includeMissing = t.includeMissing();
-        List<Invocation> histAsArr = data.invocations(!includeMissing).collect(Collectors.toList());
+
+        List<Invocation> histAsArr = new ArrayList<>();
+        for (Invocation invocation : invocations()) {
+            if (includeMissing || invocation.status() != InvocationData.MISSING)
+                histAsArr.add(invocation);
+        }
 
         if (histAsArr.size() < template.length)
             return null;
@@ -153,17 +133,25 @@ public class RunHistCompacted implements IRunHistory {
         }
 
         if (detectedAt != null && t.shouldBeFirstNonMissing()) {
-            Optional<Invocation> first = data.invocations(true).findFirst();
-            if (!first.isPresent())
-                return null;
+            for (Invocation invocation : invocations()) {
+                if (invocation.status() != InvocationData.MISSING)
+                    return invocation.buildId() != detectedAt ? null : detectedAt;
+            }
 
-            if (first.get().buildId() != detectedAt)
-                return null;
+            return null;
         }
 
         return detectedAt;
     }
 
+    private static int[] concatArr(int[] arr1, int[] arr2) {
+        int[] arr1and2 = new int[arr1.length + arr2.length];
+        System.arraycopy(arr1, 0, arr1and2, 0, arr1.length);
+        System.arraycopy(arr2, 0, arr1and2, arr1.length, arr2.length);
+
+        return arr1and2;
+    }
+
     @Nullable
     private static Integer checkTemplateAtPos(int[] template, int centralEvtBuild, List<Invocation> histAsArr,
         int idx) {
@@ -186,66 +174,4 @@ public class RunHistCompacted implements IRunHistory {
 
         return null;
     }
-
-    /** {@inheritDoc} */
-    @Override public String toString() {
-        Stream<CharSequence> stream = data.getLatestRuns().stream().filter(Objects::nonNull).map(Object::toString);
-        String join = String.join("", stream::iterator);
-
-        return MoreObjects.toStringHelper(this)
-            .add("runs", join)
-            .add("failRate", getFailPercentPrintable())
-            .add("data", data)
-            .toString();
-    }
-
-    /** {@inheritDoc} */
-    @Override public boolean equals(Object o) {
-        if (this == o)
-            return true;
-        if (o == null || getClass() != o.getClass())
-            return false;
-        RunHistCompacted compacted = (RunHistCompacted)o;
-        return Objects.equals(data, compacted.data);
-    }
-
-    /** {@inheritDoc} */
-    @Override public int hashCode() {
-        return Objects.hash(data);
-    }
-
-    /**
-     * @param v Invocation.
-     */
-    public void addInvocation(Invocation v) {
-        data.add(v);
-    }
-
-    public void sort() {
-        data.sort();
-    }
-
-    public void registerMissing(Integer testId, Set<Integer> buildIds) {
-        data.registerMissing(testId, buildIds);
-    }
-
-    public RunHistCompacted filterSuiteInvByParms(Map<Integer, Integer> requireParameters) {
-        RunHistCompacted cp = new RunHistCompacted();
-
-        data.invocations()
-            .filter(invocation -> invocation.containsParameterValue(requireParameters))
-            .forEach(invocation -> cp.data.add(invocation));
-
-        return cp;
-    }
-
-    public RunHistCompacted filterByBuilds(Set<Integer> builds) {
-        RunHistCompacted cp = new RunHistCompacted();
-
-        data.invocations()
-            .filter(invocation -> builds.contains(invocation.buildId()))
-            .forEach(invocation -> cp.data.add(invocation));
-
-        return cp;
-    }
 }
diff --git a/tcbot-teamcity-ignited/src/main/java/org/apache/ignite/tcignited/history/HistoryCollector.java b/tcbot-teamcity-ignited/src/main/java/org/apache/ignite/tcignited/history/HistoryCollector.java
index 3695cb6..63a2c14 100644
--- a/tcbot-teamcity-ignited/src/main/java/org/apache/ignite/tcignited/history/HistoryCollector.java
+++ b/tcbot-teamcity-ignited/src/main/java/org/apache/ignite/tcignited/history/HistoryCollector.java
@@ -34,8 +34,6 @@ import java.util.stream.Stream;
 import javax.annotation.Nonnull;
 import javax.cache.Cache;
 import javax.inject.Inject;
-import javax.inject.Provider;
-import org.apache.ignite.Ignite;
 import org.apache.ignite.IgniteCache;
 import org.apache.ignite.binary.BinaryObject;
 import org.apache.ignite.cache.query.QueryCursor;
@@ -54,7 +52,6 @@ import org.apache.ignite.tcbot.persistence.IStringCompactor;
 import org.apache.ignite.tcignited.ITeamcityIgnited;
 import org.apache.ignite.tcignited.build.FatBuildDao;
 import org.apache.ignite.tcignited.build.ITest;
-import org.apache.ignite.tcignited.build.SuiteHistory;
 import org.apache.ignite.tcignited.build.TestCompactedV2;
 import org.apache.ignite.tcignited.buildref.BranchEquivalence;
 import org.apache.ignite.tcignited.buildref.BuildRefDao;
@@ -82,9 +79,6 @@ public class HistoryCollector {
     /** Compactor. */
     @Inject private IStringCompactor compactor;
 
-    /** Ignite provider. */
-    @Inject private Provider<Ignite> igniteProvider;
-
     /** Branch equivalence. */
     @Inject private BranchEquivalence branchEquivalence;
 
@@ -113,7 +107,6 @@ public class HistoryCollector {
      */
     public IRunHistory getTestRunHist(String srvCode, int testName, int buildTypeId,
                                       int normalizedBaseBranch) {
-
         SuiteHistory hist = getSuiteHist(srvCode, buildTypeId, normalizedBaseBranch);
 
         return hist.getTestRunHist(testName);
@@ -248,8 +241,7 @@ public class HistoryCollector {
         if (logger.isDebugEnabled()) {
             logger.debug("***** History for suite "
                 + compactor.getStringFromId(buildTypeId)
-                + " branch" + compactor.getStringFromId(normalizedBaseBranch) + " requires " +
-                summary.size(igniteProvider.get()) + " bytes");
+                + " branch" + compactor.getStringFromId(normalizedBaseBranch) + "   ");
         }
 
         return summary;
diff --git a/tcbot-teamcity-ignited/src/main/java/org/apache/ignite/tcignited/history/IRunHistSummary.java b/tcbot-teamcity-ignited/src/main/java/org/apache/ignite/tcignited/history/IRunHistSummary.java
deleted file mode 100644
index 1d33f73..0000000
--- a/tcbot-teamcity-ignited/src/main/java/org/apache/ignite/tcignited/history/IRunHistSummary.java
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * 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.history;
-
-/**
- *
- */
-public interface IRunHistSummary {
-    /**
-     *
-     */
-    public float getFailRate();
-
-    /**
-     *
-     */
-    public boolean isFlaky();
-}
diff --git a/tcbot-teamcity-ignited/src/main/java/org/apache/ignite/tcignited/history/IRunHistory.java b/tcbot-teamcity-ignited/src/main/java/org/apache/ignite/tcignited/history/IRunHistory.java
index 7b2faac..b54bc53 100644
--- a/tcbot-teamcity-ignited/src/main/java/org/apache/ignite/tcignited/history/IRunHistory.java
+++ b/tcbot-teamcity-ignited/src/main/java/org/apache/ignite/tcignited/history/IRunHistory.java
@@ -18,12 +18,16 @@ package org.apache.ignite.tcignited.history;
 
 import javax.annotation.Nullable;
 import java.util.List;
-import java.util.Set;
 
 /**
  * Test or Build run statistics.
  */
-public interface IRunHistory extends IRunStat, IRunHistSummary {
+public interface IRunHistory {
+    /**
+     *
+     */
+    public boolean isFlaky();
+
     @Nullable
     List<Integer> getLatestRunResults();
 
@@ -33,7 +37,7 @@ public interface IRunHistory extends IRunStat, IRunHistSummary {
     public Integer detectTemplate(IEventTemplate t);
 
     public default String getCriticalFailPercentPrintable() {
-        return IRunStat.getPercentPrintable(getCriticalFailRate() * 100.0f);
+        return getPercentPrintable(getCriticalFailRate() * 100.0f);
     }
 
     /**
@@ -48,10 +52,16 @@ public interface IRunHistory extends IRunStat, IRunHistSummary {
         return 1.0f * getCriticalFailuresCount() / runs;
     }
 
-
     public int getCriticalFailuresCount();
 
-    @Override default float getFailRate() {
+    public int getRunsCount();
+    public int getFailuresCount();
+
+    /**
+     * Recent runs fail rate.
+     * @return fail rate as float: 0.0...1.0
+     */
+    public default float getFailRate() {
         int runs = getRunsCount();
 
         if (runs == 0)
@@ -60,5 +70,13 @@ public interface IRunHistory extends IRunStat, IRunHistSummary {
         return 1.0f * getFailuresCount() / runs;
     }
 
-    Set<Integer> buildIds();
+    public default String getFailPercentPrintable() {
+        return getPercentPrintable(getFailRate() * 100.0f);
+    }
+
+
+    public static String getPercentPrintable(float percent) {
+        return String.format("%.1f", percent).replace(".", ",");
+    }
+
 }
diff --git a/tcbot-teamcity-ignited/src/main/java/org/apache/ignite/tcignited/history/IRunStat.java b/tcbot-teamcity-ignited/src/main/java/org/apache/ignite/tcignited/history/IRunStat.java
deleted file mode 100644
index 7596400..0000000
--- a/tcbot-teamcity-ignited/src/main/java/org/apache/ignite/tcignited/history/IRunStat.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * 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.history;
-
-/**
- * Abstract runs statistics,
- */
-public interface IRunStat {
-    public int getRunsCount();
-    public int getFailuresCount();
-
-    /**
-     * Recent runs fail rate.
-     * @return fail rate as float: 0.0...1.0
-     */
-    public default float getFailRate() {
-        int runs = getRunsCount();
-
-        if (runs == 0)
-            return 0.0f;
-
-        return 1.0f * getFailuresCount() / runs;
-    }
-
-    public default String getFailPercentPrintable() {
-        return getPercentPrintable(getFailRate() * 100.0f);
-    }
-
-
-    public static String getPercentPrintable(float percent) {
-        return String.format("%.1f", percent).replace(".", ",");
-    }
-}
diff --git a/tcbot-teamcity-ignited/src/main/java/org/apache/ignite/tcignited/history/ISuiteRunHistory.java b/tcbot-teamcity-ignited/src/main/java/org/apache/ignite/tcignited/history/ISuiteRunHistory.java
index a3223b9..1fae99a 100644
--- a/tcbot-teamcity-ignited/src/main/java/org/apache/ignite/tcignited/history/ISuiteRunHistory.java
+++ b/tcbot-teamcity-ignited/src/main/java/org/apache/ignite/tcignited/history/ISuiteRunHistory.java
@@ -17,10 +17,11 @@
 package org.apache.ignite.tcignited.history;
 
 import java.util.Map;
+import javax.annotation.Nullable;
 
 public interface ISuiteRunHistory {
     IRunHistory self();
-    IRunHistory getTestRunHist(int testName);
+    @Nullable IRunHistory getTestRunHist(int testName);
 
     ISuiteRunHistory filter(Map<Integer, Integer> requireParameters);
 }
diff --git a/tcbot-teamcity-ignited/src/main/java/org/apache/ignite/ci/teamcity/ignited/runhist/InvocationData.java b/tcbot-teamcity-ignited/src/main/java/org/apache/ignite/tcignited/history/InvocationData.java
similarity index 65%
rename from tcbot-teamcity-ignited/src/main/java/org/apache/ignite/ci/teamcity/ignited/runhist/InvocationData.java
rename to tcbot-teamcity-ignited/src/main/java/org/apache/ignite/tcignited/history/InvocationData.java
index 68bc7af..5b5f1da 100644
--- a/tcbot-teamcity-ignited/src/main/java/org/apache/ignite/ci/teamcity/ignited/runhist/InvocationData.java
+++ b/tcbot-teamcity-ignited/src/main/java/org/apache/ignite/tcignited/history/InvocationData.java
@@ -15,19 +15,21 @@
  * limitations under the License.
  */
 
-package org.apache.ignite.ci.teamcity.ignited.runhist;
+package org.apache.ignite.tcignited.history;
 
 import com.google.common.base.MoreObjects;
+import com.google.common.base.Preconditions;
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.Comparator;
-import java.util.HashSet;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 import java.util.Objects;
-import java.util.Set;
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
 import javax.annotation.Nonnull;
-import org.apache.ignite.tcignited.history.RunStatus;
+import org.apache.ignite.ci.teamcity.ignited.runhist.Invocation;
 
 /**
  *
@@ -63,42 +65,9 @@ public class InvocationData {
     /**
      *
      */
-    public int notMutedAndNonMissingRunsCount() {
-        return (int)
-            invocations(true)
-                .filter(invocation -> {
-                    byte s = invocation.status();
-                    return s != MUTED && s != FAILURE_MUTED && s != OK_MUTED && s != IGNORED;
-                })
-                .count();
-    }
-
-    /**
-     *
-     */
     @Nonnull
     Stream<Invocation> invocations() {
-        return invocations(false);
-    }
-
-
-    /**
-     * @param skipMissing Skip missing (absent) invocations.
-     */
-    @Nonnull Stream<Invocation> invocations(boolean skipMissing) {
-        Stream<Invocation> stream = invocationList.stream();
-
-        if (skipMissing)
-            stream = stream.filter(invocation -> invocation.status() != MISSING);
-
-        return stream;
-    }
-
-    /**
-     *
-     */
-    int failuresCount() {
-        return (int)invocations().filter(inv -> inv.status() == FAILURE || inv.status() == CRITICAL_FAILURE).count();
+        return invocationList.stream();
     }
 
     /** {@inheritDoc} */
@@ -133,28 +102,33 @@ public class InvocationData {
             .collect(Collectors.toList());
     }
 
-    /**
-     *
-     */
-    public int criticalFailuresCount() {
-        return (int)invocations().filter(inv -> inv.status() == CRITICAL_FAILURE).count();
-    }
-
     public void sort() {
         invocationList.sort(Comparator.comparing(Invocation::buildId));
     }
 
-    public Set<Integer> buildIds() {
-        return invocationList.stream().map(Invocation::buildId).collect(Collectors.toSet());
+    /**
+     * Build ID - index mapping should be always called after sort.
+     */
+    public Map<Integer, Integer> buildIdsMapping() {
+        Map<Integer, Integer> buildIdToIdx = new HashMap<>();
+        int idx = 0;
+
+        for (Invocation next : invocationList) {
+            buildIdToIdx.put(next.buildId(), idx);
+            idx++;
+        }
+
+        return buildIdToIdx;
     }
 
-    public void registerMissing(Integer testId, Set<Integer> suiteBuildIds) {
-        Set<Integer> idsPresent = buildIds();
-        HashSet<Integer> toAdd = new HashSet<>(suiteBuildIds);
-        toAdd.removeAll(idsPresent);
+    public Iterable<Invocation> invocationsIterable() {
+        return Collections.unmodifiableList(invocationList);
+    }
 
-        toAdd.forEach(id -> {
-            add(new Invocation(id).withStatus(MISSING));
-        });
+    public Invocation getInvocationAt(int idx) {
+        int size = invocationList.size();
+        Preconditions.checkState(idx < size,
+            "Requested invocation outside suite history [" + idx + "] size [" + size + "]");
+        return invocationList.get(idx);
     }
 }
diff --git a/tcbot-teamcity-ignited/src/main/java/org/apache/ignite/tcignited/history/RunHistCompacted.java b/tcbot-teamcity-ignited/src/main/java/org/apache/ignite/tcignited/history/RunHistCompacted.java
new file mode 100644
index 0000000..ad66f2c
--- /dev/null
+++ b/tcbot-teamcity-ignited/src/main/java/org/apache/ignite/tcignited/history/RunHistCompacted.java
@@ -0,0 +1,122 @@
+/*
+ * 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.history;
+
+import com.google.common.base.MoreObjects;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+import java.util.stream.Stream;
+import javax.annotation.Nullable;
+import org.apache.ignite.ci.teamcity.ignited.runhist.Invocation;
+import org.apache.ignite.ci.teamcity.ignited.runhist.RunHistKey;
+
+/**
+ * In memory replacement of invocation history (RunHist/RunStat).
+ */
+public class RunHistCompacted extends AbstractRunHist {
+    /** Data. */
+    private InvocationData data = new InvocationData();
+
+    public RunHistCompacted() {
+    }
+
+    public RunHistCompacted(RunHistKey ignored) {
+
+    }
+
+    /** {@inheritDoc} */
+    @Nullable
+    @Override public List<Integer> getLatestRunResults() {
+        return data.getLatestRuns();
+    }
+
+    /** {@inheritDoc} */
+    @Override public Stream<Invocation> getInvocations() {
+        return data.invocations();
+    }
+
+    /** {@inheritDoc} */
+    @Override public Iterable<Invocation> invocations() {
+        return data.invocationsIterable();
+    }
+
+    public Set<Integer> buildIds() {
+        return data.buildIdsMapping().keySet();
+    }
+
+    public Map<Integer, Integer> buildIdsMapping() {
+        return data.buildIdsMapping();
+    }
+
+    /** {@inheritDoc} */
+    @Override public String toString() {
+        Stream<CharSequence> stream = data.getLatestRuns().stream().filter(Objects::nonNull).map(Object::toString);
+        String join = String.join("", stream::iterator);
+
+        return MoreObjects.toStringHelper(this)
+            .add("runs", join)
+            .add("failRate", getFailPercentPrintable())
+            .add("data", data)
+            .toString();
+    }
+
+    /** {@inheritDoc} */
+    @Override public boolean equals(Object o) {
+        if (this == o)
+            return true;
+        if (o == null || getClass() != o.getClass())
+            return false;
+        RunHistCompacted compacted = (RunHistCompacted)o;
+        return Objects.equals(data, compacted.data);
+    }
+
+    /** {@inheritDoc} */
+    @Override public int hashCode() {
+        return Objects.hash(data);
+    }
+
+    /**
+     * @param v Invocation.
+     */
+    public void addInvocation(Invocation v) {
+        data.add(v);
+    }
+
+    public void sort() {
+        data.sort();
+    }
+
+    public RunHistCompacted filterSuiteInvByParms(Map<Integer, Integer> requireParameters) {
+        RunHistCompacted cp = new RunHistCompacted();
+
+        getInvocations()
+            .filter(invocation -> invocation.containsParameterValue(requireParameters))
+            .forEach(invocation -> cp.data.add(invocation));
+
+        return cp;
+    }
+
+    /**
+     * @param idx Index.
+     */
+    public Invocation getInvocationAt(int idx) {
+        return data.getInvocationAt(idx);
+    }
+}
diff --git a/tcbot-teamcity-ignited/src/main/java/org/apache/ignite/tcignited/history/SuiteHistory.java b/tcbot-teamcity-ignited/src/main/java/org/apache/ignite/tcignited/history/SuiteHistory.java
new file mode 100644
index 0000000..51b40a6
--- /dev/null
+++ b/tcbot-teamcity-ignited/src/main/java/org/apache/ignite/tcignited/history/SuiteHistory.java
@@ -0,0 +1,232 @@
+/*
+ * 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.history;
+
+import com.google.common.base.Preconditions;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.NoSuchElementException;
+import java.util.Set;
+import java.util.stream.Collectors;
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import org.apache.ignite.ci.teamcity.ignited.runhist.Invocation;
+
+/**
+ * Suite run history (in memory) summary with tests grouped by name.
+ */
+public class SuiteHistory implements ISuiteRunHistory {
+    /** Tests history: Test name ID->statuses for invocations */
+    private Map<Integer, byte[]> testsInvStatues = new HashMap<>();
+
+    /** Suite history. */
+    private RunHistCompacted suiteHist = new RunHistCompacted();
+
+    public SuiteHistory(Map<Integer, SuiteInvocation> suiteRunHist) {
+        //filling data for tests invoked.
+        Map<Integer, RunHistCompacted> testsHist = new HashMap<>();
+
+        suiteRunHist.forEach((buildId, suiteInv) -> addSuiteInvocation(suiteInv, testsHist));
+
+        suiteHist.sort();
+
+        Map<Integer, Integer> buildIdToIdx = suiteHist.buildIdsMapping();
+        int buildsCnt = buildIdToIdx.size();
+
+        byte missingCode = (byte)RunStatus.RES_MISSING.getCode();
+        testsHist.forEach((k, testHist) -> {
+            byte[] testStatusesUltraComp = new byte[buildsCnt];
+
+            for (int i = 0; i < testStatusesUltraComp.length; i++)
+                testStatusesUltraComp[i] = missingCode;
+
+            testHist.getInvocations().forEach(
+                invocation -> {
+                    int i = invocation.buildId();
+                    Integer idx = buildIdToIdx.get(i);
+                    if (idx == null)
+                        return;
+
+                    Preconditions.checkState(idx < testStatusesUltraComp.length);
+
+                    testStatusesUltraComp[idx] = invocation.status();
+                }
+            );
+
+            testsInvStatues.put(k, testStatusesUltraComp);
+        });
+    }
+
+    private SuiteHistory() {}
+
+    /**
+     * @param suiteInv suite invocation (build) to be added to history (summary).
+     * @param testsHist tests map.
+     */
+    private void addSuiteInvocation(SuiteInvocation suiteInv, Map<Integer, RunHistCompacted> testsHist) {
+        suiteInv.tests().forEach(
+            (tName, invocation) -> {
+                testsHist.computeIfAbsent(tName, k_ -> new RunHistCompacted())
+                    .addInvocation(invocation);
+            });
+
+        suiteHist.addInvocation(suiteInv.suiteInvocation());
+    }
+
+    /** {@inheritDoc} */
+    @Nullable @Override public IRunHistory getTestRunHist(int testName) {
+        byte[] testInvStatuses = testsInvStatues.get(testName);
+
+        if(testInvStatuses == null)
+            return null;
+
+        return new TestUltraCompactRunHist(testInvStatuses, suiteHist);
+    }
+
+    /** {@inheritDoc} */
+    @Override public ISuiteRunHistory filter(Map<Integer, Integer> requireParameters) {
+        RunHistCompacted suitesFiltered = suiteHist.filterSuiteInvByParms(requireParameters);
+
+        Map<Integer, Integer> buildIdToIdx = suiteHist.buildIdsMapping();
+
+        Set<Integer> indexesToKeep = suitesFiltered.buildIds().stream().map(buildIdToIdx::get).collect(Collectors.toSet());
+
+        SuiteHistory res = new SuiteHistory();
+
+        res.suiteHist = suitesFiltered;
+
+        testsInvStatues.forEach((tName, invList) -> {
+            byte[] buildsFiltered = new byte[indexesToKeep.size()];
+
+            int j = 0;
+            for (int i = 0; i < invList.length; i++) {
+                if (!indexesToKeep.contains(i))
+                    continue;
+
+                buildsFiltered[j] = invList[i];
+                j++;
+            }
+
+            res.testsInvStatues.put(tName, buildsFiltered);
+        });
+
+        return res;
+    }
+    @Override public IRunHistory self() {
+        return suiteHist;
+    }
+
+    private static class TestUltraCompactRunHist extends AbstractRunHist {
+        @Nonnull private final byte[] testInvStatuses;
+        @Nonnull private final RunHistCompacted suiteHist;
+
+        public TestUltraCompactRunHist(@Nonnull byte[] testInvStatuses, @Nonnull RunHistCompacted suiteHist) {
+            this.testInvStatuses = testInvStatuses;
+            this.suiteHist = suiteHist;
+
+            Preconditions.checkState(testInvStatuses.length == suiteHist.getInvocations().count());
+        }
+
+        /** {@inheritDoc} */
+        @Nullable @Override public List<Integer> getLatestRunResults() {
+            List<Integer> res = new ArrayList<>();
+            for (int i = 0; i < testInvStatuses.length; i++)
+                res.add((int)testInvStatuses[i]);
+
+            return res;
+        }
+
+        /** {@inheritDoc} */
+        @Override public int getCriticalFailuresCount() {
+            int res = 0;
+            for (int i = 0; i < testInvStatuses.length; i++) {
+                if (testInvStatuses[i] == InvocationData.CRITICAL_FAILURE)
+                    res++;
+            }
+
+            return res;
+        }
+
+        /** {@inheritDoc} */
+        @Override public int getRunsCount() {
+            int res = 0;
+            for (int i = 0; i < testInvStatuses.length; i++) {
+                byte status = testInvStatuses[i];
+                if (status != InvocationData.MISSING && !Invocation.isMutedOrIgnored(status))
+                    res++;
+            }
+
+            return res;
+        }
+
+        /** {@inheritDoc} */
+        @Override public int getFailuresCount() {
+            int res = 0;
+            for (int i = 0; i < testInvStatuses.length; i++) {
+                byte status = testInvStatuses[i];
+                if (status == InvocationData.FAILURE || status == InvocationData.CRITICAL_FAILURE)
+                    res++;
+            }
+
+            return res;
+        }
+
+        /** {@inheritDoc} */
+        @Override public Iterable<Invocation> invocations() {
+            return () -> new TestUltraCompactRunHistIterator(testInvStatuses, suiteHist);
+        }
+    }
+
+
+    private static class TestUltraCompactRunHistIterator implements Iterator<Invocation> {
+        /** Cur index: index of element to be returned in case next is called now. */
+        private int curIdx = 0;
+        @Nonnull private final byte[] testInvStatuses;
+        @Nonnull private final RunHistCompacted suiteHist;
+
+        public TestUltraCompactRunHistIterator(@Nonnull byte[] testInvStatuses,
+            @Nonnull RunHistCompacted suiteHist) {
+            this.testInvStatuses = testInvStatuses;
+            this.suiteHist = suiteHist;
+        }
+
+        /** {@inheritDoc} */
+        @Override public boolean hasNext() {
+            return curIdx < testInvStatuses.length;
+        }
+
+        /** {@inheritDoc} */
+        @Override public Invocation next() {
+            if (!hasNext())
+                throw new NoSuchElementException();
+
+            Invocation suiteInv = suiteHist.getInvocationAt(curIdx);
+
+            Invocation invocation = new Invocation(suiteInv.buildId())
+                .withChangeState(suiteInv.changesState())
+                .withStatus(testInvStatuses[curIdx]);
+
+            curIdx++;
+
+            return invocation;
+        }
+    }
+}