You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@ignite.apache.org by GitBox <gi...@apache.org> on 2018/11/21 15:52:56 UTC

[GitHub] asfgit closed pull request #76: IGNITE-10275 Refactor of visa caching.

asfgit closed pull request #76: IGNITE-10275 Refactor of visa caching.
URL: https://github.com/apache/ignite-teamcity-bot/pull/76
 
 
   

This is a PR merged from a forked repository.
As GitHub hides the original diff on merge, it is displayed below for
the sake of provenance:

As this is a foreign pull request (from a fork), the diff is supplied
below (as it won't show otherwise due to GitHub magic):

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 c7f0e11a..ea8aa9bd 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
@@ -79,6 +79,7 @@
 import org.apache.ignite.ci.util.CacheUpdateUtil;
 import org.apache.ignite.ci.util.CollectionUtil;
 import org.apache.ignite.ci.util.ObjectInterner;
+import org.apache.ignite.ci.web.model.hist.VisasHistoryStorage;
 import org.apache.ignite.ci.web.rest.parms.FullQueryParams;
 import org.jetbrains.annotations.NotNull;
 
@@ -116,6 +117,11 @@
 
     @Inject
     private Ignite ignite;
+
+    /** */
+    @Inject
+    private VisasHistoryStorage visasHistStorage;
+
     /**
      * Teamcity
      */
@@ -158,7 +164,8 @@
                 buildsCache(), this::addBuildOccurrenceToFailuresStat,
                 buildsFailureRunStatCache(), testRunStatCache(),
                 testFullCache(),
-                buildProblemsCache());
+                buildProblemsCache(),
+                visasHistStorage.visas());
     }
 
     @Override
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 a8307ffe..cc64e34d 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
@@ -17,33 +17,38 @@
 
 package org.apache.ignite.ci.db;
 
+import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.Objects;
 import java.util.function.Consumer;
 import javax.cache.Cache;
 import org.apache.ignite.Ignite;
 import org.apache.ignite.IgniteCache;
 import org.apache.ignite.IgniteDataStreamer;
+import org.apache.ignite.binary.BinaryObject;
 import org.apache.ignite.cache.CacheAtomicityMode;
 import org.apache.ignite.cache.CacheMode;
 import org.apache.ignite.ci.ITeamcity;
 import org.apache.ignite.ci.IgnitePersistentTeamcity;
-import org.apache.ignite.ci.analysis.Expirable;
 import org.apache.ignite.ci.analysis.RunStat;
 import org.apache.ignite.ci.analysis.SuiteInBranch;
 import org.apache.ignite.ci.analysis.TestInBranch;
 import org.apache.ignite.ci.issue.Issue;
 import org.apache.ignite.ci.issue.IssueKey;
 import org.apache.ignite.ci.issue.IssuesStorage;
-import org.apache.ignite.ci.tcmodel.hist.BuildRef;
+import org.apache.ignite.ci.observer.CompactBuildsInfo;
 import org.apache.ignite.ci.tcmodel.result.Build;
 import org.apache.ignite.ci.tcmodel.result.problems.ProblemOccurrences;
 import org.apache.ignite.ci.tcmodel.result.stat.Statistics;
 import org.apache.ignite.ci.tcmodel.result.tests.TestOccurrenceFull;
 import org.apache.ignite.ci.tcmodel.result.tests.TestOccurrences;
+import org.apache.ignite.ci.web.model.CompactContributionKey;
+import org.apache.ignite.ci.web.model.CompactVisa;
+import org.apache.ignite.ci.web.model.CompactVisaRequest;
+import org.apache.ignite.ci.web.model.hist.VisasHistoryStorage;
 import org.apache.ignite.ci.web.rest.build.GetBuildTestFailures;
-import org.apache.ignite.ci.web.rest.pr.GetPrTestFailures;
 import org.apache.ignite.ci.web.rest.tracked.GetTrackedBranchTestResults;
 import org.apache.ignite.configuration.CacheConfiguration;
 import org.slf4j.Logger;
@@ -104,6 +109,9 @@
     @Deprecated
     public static final String TEAMCITY_BUILD_CACHE_NAME_OLD = "teamcityBuild";
 
+    /** */
+    @Deprecated
+    public static final String COMPACT_VISAS_HISTORY_CACHE_NAME = "compactVisasHistoryCache";
 
     private static final String CHANGE_INFO_FULL = "changeInfoFull";
     private static final String CHANGES_LIST = "changesList";
@@ -131,10 +139,45 @@ public void dataMigration(
             IgniteCache<SuiteInBranch, RunStat> suiteHistCache,
             IgniteCache<TestInBranch, RunStat> testHistCache,
             Cache<String, TestOccurrenceFull> testFullCache,
-            Cache<String, ProblemOccurrences> problemsCache) {
+            Cache<String, ProblemOccurrences> problemsCache,
+            Cache<CompactContributionKey, List<CompactVisaRequest>> visasCache) {
 
         doneMigrations = doneMigrationsCache();
 
+        applyMigration(COMPACT_VISAS_HISTORY_CACHE_NAME + "-to-" + VisasHistoryStorage.VISAS_CACHE_NAME, () -> {
+            IgniteCache<Object, Object> oldVisasCache = ignite.cache(COMPACT_VISAS_HISTORY_CACHE_NAME).withKeepBinary();
+
+            if (Objects.isNull(oldVisasCache)) {
+                System.out.println("Old cache [" + COMPACT_VISAS_HISTORY_CACHE_NAME + "] not found");
+
+                return;
+            }
+
+            int size = oldVisasCache.size();
+
+            int i = 0;
+
+            for (IgniteCache.Entry<Object, Object> entry : oldVisasCache) {
+                System.out.println("Migrating entry " + i++ + " from " + size);
+
+                List<BinaryObject> binVisaReqs = (ArrayList<BinaryObject>)entry.getValue();
+
+                List<CompactVisaRequest> compactVisaReqs = new ArrayList<>();
+
+                CompactContributionKey compactKey = ((BinaryObject)entry.getKey()).deserialize();
+
+                for (BinaryObject binVisaReq : binVisaReqs) {
+                    CompactBuildsInfo compactInfo = ((BinaryObject)binVisaReq.field("compactInfo")).deserialize();
+
+                    CompactVisa compactVisa = ((BinaryObject)binVisaReq.field("compactVisa")).deserialize();
+
+                    compactVisaReqs.add(new CompactVisaRequest(compactVisa, compactInfo, false));
+                }
+
+                visasCache.put(compactKey, compactVisaReqs);
+            }
+        });
+
         applyMigration("InitialFillLatestRunsV3", () -> {
             int size = testOccurrencesCache.size();
             if (size > 0) {
@@ -429,6 +472,8 @@ public void dataMigration(
         applyDestroyIgnCacheMigration(FINISHED_BUILDS);
         applyDestroyIgnCacheMigration(BUILD_HIST_FINISHED);
         applyDestroyIgnCacheMigration(BUILD_HIST_FINISHED_OR_FAILED);
+
+        applyDestroyCacheMigration(COMPACT_VISAS_HISTORY_CACHE_NAME, COMPACT_VISAS_HISTORY_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 28a181c5..23f7e62e 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
@@ -93,6 +93,21 @@ public static void stop(Ignite ignite) {
         Ignition.stop(ignite.name(), false);
     }
 
+    /** */
+    @NotNull
+    public static <K, V> CacheConfiguration<K, V> getCacheV3Config(String name) {
+        CacheConfiguration<K, V> ccfg = new CacheConfiguration<>(name);
+
+        ccfg.setAffinity(new RendezvousAffinityFunction(false, 8));
+
+        return ccfg;
+    }
+
+    /** */
+    public static <K, V> CacheConfiguration<K, V> getCacheV3TxConfig(String name) {
+        return TcHelperDb.<K, V>getCacheV3Config(name).setAtomicityMode(CacheAtomicityMode.TRANSACTIONAL);
+    }
+
     @NotNull
     public static <K, V> CacheConfiguration<K, V> getCacheV2Config(String name) {
         CacheConfiguration<K, V> ccfg = new CacheConfiguration<>(name);
diff --git a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/di/IgniteTcBotModule.java b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/di/IgniteTcBotModule.java
index 618b681c..7022b451 100644
--- a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/di/IgniteTcBotModule.java
+++ b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/di/IgniteTcBotModule.java
@@ -43,6 +43,7 @@
 import org.apache.ignite.ci.web.BackgroundUpdater;
 import org.apache.ignite.ci.web.TcUpdatePool;
 import org.apache.ignite.ci.web.model.Visa;
+import org.apache.ignite.ci.web.model.hist.VisasHistoryStorage;
 import org.apache.ignite.ci.web.rest.exception.ServiceStartingException;
 
 /**
@@ -76,6 +77,7 @@
         bind(IssueDetector.class).in(new SingletonScope());
         bind(ObserverTask.class).in(new SingletonScope());
         bind(BuildObserver.class).in(new SingletonScope());
+        bind(VisasHistoryStorage.class).in(new SingletonScope());
         bind(ITcHelper.class).to(TcHelper.class).in(new SingletonScope());
 
         bind(IJiraIntegration.class).to(Jira.class).in(new SingletonScope());
diff --git a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/observer/BuildObserver.java b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/observer/BuildObserver.java
index b4686f74..98287d2e 100644
--- a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/observer/BuildObserver.java
+++ b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/observer/BuildObserver.java
@@ -56,6 +56,8 @@ public BuildObserver(ObserverTask observerTask) {
         timer.schedule(observerTask, 0, PERIOD);
 
         this.observerTask = observerTask;
+
+        this.observerTask.init();
     }
 
     /**
@@ -65,17 +67,10 @@ public void stop() {
         timer.cancel();
     }
 
-    /** */
-    public ObserverTask getObserverTask() {
-        return observerTask;
-    }
-
     /** */
     public boolean stopObservation(ContributionKey key) {
         try {
-            observerTask.removeBuildInfo(key);
-
-            return true;
+            return observerTask.removeBuildInfo(key);
         }
         catch (Exception e) {
             logger.error("Observation stop: " + e.getMessage(), e);
diff --git a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/observer/BuildsInfo.java b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/observer/BuildsInfo.java
index e02ad5b3..976cf3c5 100644
--- a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/observer/BuildsInfo.java
+++ b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/observer/BuildsInfo.java
@@ -96,7 +96,7 @@ public BuildsInfo(String srvId, ICredentialsProv prov, String ticket, String bra
     /**
      * @param teamcity Teamcity.
      */
-    public String getState(IAnalyticsEnabledTeamcity teamcity) {
+    public String getStatus(IAnalyticsEnabledTeamcity teamcity) {
         boolean isFinished = true;
 
         for (Integer id : builds) {
@@ -116,14 +116,14 @@ public String getState(IAnalyticsEnabledTeamcity teamcity) {
      * @param teamcity Teamcity.
      */
     public boolean isFinished(IAnalyticsEnabledTeamcity teamcity) {
-        return FINISHED_STATUS.equals(getState(teamcity));
+        return FINISHED_STATUS.equals(getStatus(teamcity));
     }
 
     /**
      * @param teamcity Teamcity.
      */
     public boolean isCancelled(IAnalyticsEnabledTeamcity teamcity) {
-        return CANCELLED_STATUS.equals(getState(teamcity));
+        return CANCELLED_STATUS.equals(getStatus(teamcity));
     }
 
     /**
diff --git a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/observer/ObserverTask.java b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/observer/ObserverTask.java
index 68335c8b..1d9ed432 100644
--- a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/observer/ObserverTask.java
+++ b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/observer/ObserverTask.java
@@ -17,35 +17,26 @@
 
 package org.apache.ignite.ci.observer;
 
-import com.google.common.base.Preconditions;
 import java.util.ArrayList;
 import java.util.Collection;
-import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
-import java.util.Objects;
 import java.util.Set;
 import java.util.TimerTask;
+import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.locks.ReentrantLock;
-import java.util.stream.Collectors;
 import javax.inject.Inject;
-import org.apache.ignite.Ignite;
-import org.apache.ignite.IgniteCache;
 import org.apache.ignite.ci.IAnalyticsEnabledTeamcity;
 import org.apache.ignite.ci.ITcHelper;
-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.jira.IJiraIntegration;
-import org.apache.ignite.ci.teamcity.ignited.IStringCompactor;
 import org.apache.ignite.ci.user.ICredentialsProv;
-import org.apache.ignite.ci.web.model.CompactContributionKey;
 import org.apache.ignite.ci.web.model.ContributionKey;
 import org.apache.ignite.ci.web.model.Visa;
 import org.apache.ignite.ci.web.model.VisaRequest;
 import org.apache.ignite.ci.web.model.hist.VisasHistoryStorage;
-import org.apache.ignite.internal.util.typedef.X;
 import org.jetbrains.annotations.Nullable;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -57,26 +48,20 @@
     /** Logger. */
     private static final Logger logger = LoggerFactory.getLogger(ObserverTask.class);
 
-    /** */
-    public static final String BUILDS_CACHE_NAME = "compactBuildsInfosCache";
-
     /** Helper. */
     @Inject private ITcHelper tcHelper;
 
     /** Helper. */
     @Inject private IJiraIntegration jiraIntegration;
 
-    /** Ignite. */
-    @Inject private Ignite ignite;
-
     /** */
     @Inject private VisasHistoryStorage visasHistStorage;
 
     /** */
-    @Inject private IStringCompactor strCompactor;
+    private ReentrantLock observationLock = new ReentrantLock();
 
     /** */
-    private ReentrantLock observationLock = new ReentrantLock();
+    private Map<ContributionKey, BuildsInfo> infos = new ConcurrentHashMap<>();
 
     /**
      */
@@ -84,61 +69,48 @@
     }
 
     /** */
-    private IgniteCache<CompactContributionKey, CompactBuildsInfo> compactInfos() {
-        return ignite.getOrCreateCache(TcHelperDb.getCacheV2TxConfig(BUILDS_CACHE_NAME));
+    public void init() {
+        visasHistStorage.getLastVisas().stream()
+            .filter(req -> req.isObserving())
+            .forEach(req -> infos.put(req.getInfo().getContributionKey(), req.getInfo()));
     }
 
     /** */
     @Nullable public BuildsInfo getInfo(ContributionKey key) {
-        CompactBuildsInfo compactBuildsInfo = compactInfos().get(new CompactContributionKey(key, strCompactor));
-
-        return Objects.isNull(compactBuildsInfo) ? null : compactBuildsInfo.toBuildInfo(strCompactor);
+        return infos.get(key);
     }
 
 
     /** */
     public Collection<BuildsInfo> getInfos() {
-        List<BuildsInfo> buildsInfos = new ArrayList<>();
-
-        compactInfos().forEach(entry -> buildsInfos.add(entry.getValue().toBuildInfo(strCompactor)));
-
-        return buildsInfos;
+        return infos.values();
     }
 
     /** */
     public void addInfo(BuildsInfo info) {
-        visasHistStorage.put(new VisaRequest(info));
+        visasHistStorage.updateLastVisaRequest(info.getContributionKey(), req -> req.setObservingStatus(false));
+
+        visasHistStorage.put(new VisaRequest(info).setObservingStatus(true));
 
-        compactInfos().put(new CompactContributionKey(info.getContributionKey(), strCompactor),
-            new CompactBuildsInfo(info, strCompactor));
+        infos.put(info.getContributionKey(), info);
     }
 
     /** */
-    public void removeBuildInfo(ContributionKey key) {
+    public boolean removeBuildInfo(ContributionKey key) {
         observationLock.lock();
 
         try {
-            removeBuildInfo(new CompactContributionKey(key, strCompactor));
-        }
-        finally {
-            observationLock.unlock();
-        }
-    }
+            if (!infos.containsKey(key))
+                return false;
 
-    /** */
-    private void removeBuildInfo(CompactContributionKey key) {
-        try {
-            boolean rmv = compactInfos().remove(key);
+            infos.remove(key);
 
-            Preconditions.checkState(rmv, "Key not found: " + key.toContributionKey(strCompactor).toString());
-        }
-        catch (Exception e) {
-            logger.error("Cache remove: " + e.getMessage(), e);
+            visasHistStorage.updateLastVisaRequest(key, req -> req.setObservingStatus(false));
 
-            throw new RuntimeException("Observer queue: " +
-                getInfos().stream().map(bi -> bi.getContributionKey().toString())
-                    .collect(Collectors.joining(", ")) +
-                " Error: " + X.getFullStackTrace(e));
+            return true;
+        }
+        finally {
+            observationLock.unlock();
         }
     }
 
@@ -168,19 +140,17 @@ protected String runObserverTask() {
             int notFinishedBuilds = 0;
             Set<String> ticketsNotified = new HashSet<>();
 
-            Map<CompactContributionKey, Boolean> rmv = new HashMap<>();
+            List<ContributionKey> rmv = new ArrayList<>();
 
-            for (IgniteCache.Entry<CompactContributionKey, CompactBuildsInfo> entry : compactInfos()) {
-                CompactBuildsInfo compactInfo = entry.getValue();
-
-                BuildsInfo info = compactInfo.toBuildInfo(strCompactor);
+            for (ContributionKey key : infos.keySet()) {
+                BuildsInfo info = infos.get(key);
 
                 IAnalyticsEnabledTeamcity teamcity = tcHelper.server(info.srvId, tcHelper.getServerAuthorizerCreds());
 
                 checkedBuilds += info.buildsCount();
 
                 if (info.isCancelled(teamcity)) {
-                    rmv.put(entry.getKey(), false);
+                    rmv.add(key);
 
                     logger.error("JIRA will not be commented." +
                         " [ticket: " + info.ticket + ", branch:" + info.branchForTc + "] : " +
@@ -195,40 +165,33 @@ protected String runObserverTask() {
                     continue;
                 }
 
-                Visa visa = visasHistStorage.getVisaRequest(info.getContributionKey(), info.date).getResult();
-
-                if (Objects.isNull(visa))
-                    continue;
+                Visa visa = visasHistStorage.getLastVisaRequest(info.getContributionKey()).getResult();
 
                 if (!visa.isSuccess()) {
                     ICredentialsProv creds = tcHelper.getServerAuthorizerCreds();
 
-                    visa = jiraIntegration.notifyJira(info.srvId, creds, info.buildTypeId,
+                    Visa updatedVisa = jiraIntegration.notifyJira(info.srvId, creds, info.buildTypeId,
                         info.branchForTc, info.ticket);
 
-                    visasHistStorage.updateVisaRequestResult(info.getContributionKey(), info.date, visa);
+                    visasHistStorage.updateLastVisaRequest(info.getContributionKey(), (req -> req.setResult(updatedVisa)));
 
-                    if (visa.isSuccess())
+                    if (updatedVisa.isSuccess())
                         ticketsNotified.add(info.ticket);
+
+                    visa = updatedVisa;
                 }
 
                 if (visa.isSuccess())
-                    rmv.put(entry.getKey(), false);
+                    rmv.add(key);
             }
 
-            rmv.entrySet().forEach(entry -> {
-                try {
-                    removeBuildInfo(entry.getKey());
+            rmv.forEach(key -> {
+                infos.remove(key);
 
-                    entry.setValue(true);
-                }
-                catch (Exception e) {
-                   logger.error(e.getMessage(), e);
-                }
+                visasHistStorage.updateLastVisaRequest(key, req -> req.setObservingStatus(false));
             });
 
-            return "Checked " + checkedBuilds + " not finished " + notFinishedBuilds + " notified: " + ticketsNotified +
-                " Rmv problems: " + rmv.values().stream().filter(v -> !v).count();
+            return "Checked " + checkedBuilds + " not finished " + notFinishedBuilds + " notified: " + ticketsNotified;
         }
         finally {
             observationLock.unlock();
diff --git a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/runners/RemoteClientTmpHelper.java b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/runners/RemoteClientTmpHelper.java
index f3f3802d..3fd622a3 100644
--- a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/runners/RemoteClientTmpHelper.java
+++ b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/runners/RemoteClientTmpHelper.java
@@ -25,8 +25,6 @@
 import org.apache.ignite.Ignite;
 import org.apache.ignite.IgniteCache;
 import org.apache.ignite.Ignition;
-import org.apache.ignite.ci.observer.CompactBuildsInfo;
-import org.apache.ignite.ci.observer.ObserverTask;
 import org.apache.ignite.ci.teamcity.ignited.BuildRefCompacted;
 import org.apache.ignite.ci.teamcity.ignited.BuildRefDao;
 import org.apache.ignite.ci.teamcity.ignited.ITeamcityIgnited;
@@ -88,29 +86,6 @@ public static void main(String[] args) throws IOException {
             dumpBuildRef(cache2, apache, id);
             dumpBuildRef(cache2, apache, id1);
         }
-
-        IgniteCache<CompactBuildsInfo, Object> cache = ignite.cache(ObserverTask.BUILDS_CACHE_NAME);
-
-        CompactBuildsInfo cbi = new CompactBuildsInfo();
-
-        cbi.userName(62541);
-        cbi.srvId(245001);
-        cbi.buildTypeId(113);
-        cbi.branchForTc(2008);
-        cbi.ticket(263594);
-        cbi.date(1542263949429L);
-
-        cbi.addBuild(2322291, 2322298, 2322296, 2322294, 2322292, 2322300);
-
-        boolean rmv = cache.remove(cbi);
-
-        try {
-            Preconditions.checkState(rmv, "can't remove " + cbi);
-        }
-        finally {
-            ignite.close();
-        }
-
     }
 
     public static void dumpBuildRef(IgniteCache<Long, BuildRefCompacted> cache, int apache, int id) throws IOException {
diff --git a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/tcbot/visa/TcBotTriggerAndSignOffService.java b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/tcbot/visa/TcBotTriggerAndSignOffService.java
index 312a033d..587745db 100644
--- a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/tcbot/visa/TcBotTriggerAndSignOffService.java
+++ b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/tcbot/visa/TcBotTriggerAndSignOffService.java
@@ -39,7 +39,6 @@
 import org.apache.ignite.ci.github.pure.IGitHubConnectionProvider;
 import org.apache.ignite.ci.jira.IJiraIntegration;
 import org.apache.ignite.ci.observer.BuildObserver;
-import org.apache.ignite.ci.observer.ObserverTask;
 import org.apache.ignite.ci.observer.BuildsInfo;
 import org.apache.ignite.ci.tcbot.chain.PrChainsProcessor;
 import org.apache.ignite.ci.tcmodel.result.Build;
@@ -101,7 +100,7 @@
 
 
     @Inject PrChainsProcessor prChainsProcessor;
-    
+
     /** */
     public void startObserver() {
         buildObserverProvider.get();
@@ -113,8 +112,6 @@ public void startObserver() {
 
         IAnalyticsEnabledTeamcity teamcity = tcHelper.server(srvId, prov);
 
-        ObserverTask observerTask = buildObserverProvider.get().getObserverTask();
-
         for (VisaRequest visaRequest : visasHistoryStorage.getVisas()) {
             VisaStatus visaStatus = new VisaStatus();
 
@@ -122,16 +119,14 @@ public void startObserver() {
 
             Visa visa = visaRequest.getResult();
 
+            boolean isObserving = visaRequest.isObserving();
+
             visaStatus.date = THREAD_FORMATTER.get().format(info.date);
             visaStatus.branchName = info.branchForTc;
             visaStatus.userName = info.userName;
             visaStatus.ticket = info.ticket;
 
-            String buildsStatus = visaStatus.status = info.getState(teamcity);
-
-            BuildsInfo observInfo = observerTask.getInfo(info.getContributionKey());
-
-            boolean isObserving = Objects.nonNull(observInfo) && observInfo.date.equals(info.date);
+            String buildsStatus = visaStatus.status = info.getStatus(teamcity);
 
             if (FINISHED_STATUS.equals(buildsStatus)) {
                 if (visa.isSuccess()) {
@@ -299,6 +294,13 @@ public SimpleResult commentJiraEx(
         if (!Strings.isNullOrEmpty(ticketFullName)) {
             BuildsInfo buildsInfo = new BuildsInfo(srvId, prov, ticketFullName, branchForTc);
 
+            VisaRequest lastVisaReq = visasHistoryStorage.getLastVisaRequest(buildsInfo.getContributionKey());
+
+            if (Objects.nonNull(lastVisaReq) && lastVisaReq.isObserving())
+                return new SimpleResult("Jira wasn't commented." +
+                    " \"Re-run possible blockers & Comment JIRA\" was triggered for current branch." +
+                    " Wait for the end or cancel exsiting observing.");
+
             Visa visa = jiraIntegration.notifyJira(srvId, prov, suiteId, branchForTc, ticketFullName);
 
             visasHistoryStorage.put(new VisaRequest(buildsInfo)
diff --git a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/web/model/CompactVisaRequest.java b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/web/model/CompactVisaRequest.java
index 993672c2..5ce9a712 100644
--- a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/web/model/CompactVisaRequest.java
+++ b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/web/model/CompactVisaRequest.java
@@ -30,16 +30,30 @@
     /** */
     public final CompactBuildsInfo compactInfo;
 
+    /** */
+    public final boolean isObserving;
+
+    /** */
+    public CompactVisaRequest(CompactVisa compactVisa, CompactBuildsInfo compactInfo, boolean isObserving) {
+        this.compactVisa = compactVisa;
+        this.isObserving = isObserving;
+        this.compactInfo = compactInfo;
+    }
+
     /** */
     public CompactVisaRequest(VisaRequest visaReq, IStringCompactor strCompactor) {
         compactInfo = new CompactBuildsInfo(visaReq.getInfo(), strCompactor);
 
         compactVisa = new CompactVisa(visaReq.getResult(), strCompactor);
+
+        isObserving = visaReq.isObserving();
     }
 
     /** */
     public VisaRequest toVisaRequest(IStringCompactor strCompactor) {
-        return new VisaRequest(compactInfo.toBuildInfo(strCompactor)).setResult(compactVisa.toVisa(strCompactor));
+        return new VisaRequest(compactInfo.toBuildInfo(strCompactor))
+            .setResult(compactVisa.toVisa(strCompactor))
+            .setObservingStatus(isObserving);
     }
 
 }
diff --git a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/web/model/ContributionKey.java b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/web/model/ContributionKey.java
index 570f2c9d..89e14d2b 100644
--- a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/web/model/ContributionKey.java
+++ b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/web/model/ContributionKey.java
@@ -17,6 +17,7 @@
 
 package org.apache.ignite.ci.web.model;
 
+import java.util.Objects;
 import org.apache.ignite.ci.teamcity.ignited.IStringCompactor;
 
 /**
@@ -45,4 +46,23 @@ public ContributionKey(CompactContributionKey key, IStringCompactor strCompactor
     @Override public String toString() {
         return "{srv: " + this.srvId + " branch: " + this.branchForTc + '}';
     }
+
+    /** {@inheritDoc} */
+    @Override public boolean equals(Object o) {
+        if (this == o)
+            return true;
+
+        if (!(o instanceof ContributionKey))
+            return false;
+
+        ContributionKey key = (ContributionKey)o;
+
+        return Objects.equals(srvId, key.srvId) &&
+            Objects.equals(branchForTc, key.branchForTc);
+    }
+
+    /** {@inheritDoc} */
+    @Override public int hashCode() {
+        return Objects.hash(srvId, branchForTc);
+    }
 }
diff --git a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/web/model/VisaRequest.java b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/web/model/VisaRequest.java
index c281a656..74839281 100644
--- a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/web/model/VisaRequest.java
+++ b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/web/model/VisaRequest.java
@@ -30,6 +30,9 @@
     /** */
     private Visa visa;
 
+    /** */
+    private boolean isObserving;
+
     /** */
     public VisaRequest(BuildsInfo info) {
         this.info = info;
@@ -52,4 +55,16 @@ public VisaRequest setResult(Visa res) {
 
         return this;
     }
+
+    /** */
+    public VisaRequest setObservingStatus(boolean status) {
+        isObserving = status;
+
+        return this;
+    }
+
+    /** */
+    public boolean isObserving() {
+        return isObserving;
+    }
 }
diff --git a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/web/model/hist/VisasHistoryStorage.java b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/web/model/hist/VisasHistoryStorage.java
index 28299f7a..623d42dc 100644
--- a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/web/model/hist/VisasHistoryStorage.java
+++ b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/web/model/hist/VisasHistoryStorage.java
@@ -20,11 +20,9 @@
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
-import java.util.Date;
-import java.util.HashMap;
 import java.util.List;
-import java.util.Map;
 import java.util.Objects;
+import java.util.function.Consumer;
 import java.util.stream.Collectors;
 import javax.cache.Cache;
 import javax.inject.Inject;
@@ -34,7 +32,6 @@
 import org.apache.ignite.ci.web.model.CompactContributionKey;
 import org.apache.ignite.ci.web.model.CompactVisaRequest;
 import org.apache.ignite.ci.web.model.ContributionKey;
-import org.apache.ignite.ci.web.model.Visa;
 import org.apache.ignite.ci.web.model.VisaRequest;
 
 /**
@@ -42,7 +39,7 @@
  */
 public class VisasHistoryStorage {
     /** */
-    private static final String VISAS_CACHE_NAME = "compactVisasHistoryCache";
+    public static final String VISAS_CACHE_NAME = "compactVisasHistoryCacheV2";
 
     /** */
     @Inject
@@ -58,8 +55,8 @@ public void clear() {
     }
 
     /** */
-    private Cache<CompactContributionKey, Map<Date, CompactVisaRequest>> visas() {
-        return ignite.getOrCreateCache(TcHelperDb.getCacheV2TxConfig(VISAS_CACHE_NAME));
+    public Cache<CompactContributionKey, List<CompactVisaRequest>> visas() {
+        return ignite.getOrCreateCache(TcHelperDb.getCacheV3TxConfig(VISAS_CACHE_NAME));
     }
 
     /** */
@@ -70,45 +67,86 @@ public void put(VisaRequest visaReq) {
             visaReq.getInfo().srvId,
             visaReq.getInfo().branchForTc), strCompactor);
 
-        Map<Date, CompactVisaRequest> contributionVisas = visas().get(key);
+        visas().invoke(key, (entry, arguments) -> {
+            List<CompactVisaRequest> contributionVisas = entry.getValue();
 
-        if (contributionVisas == null)
-            contributionVisas = new HashMap<>();
+            if (contributionVisas == null)
+                contributionVisas = new ArrayList<>();
 
-        contributionVisas.put(compactVisaReq.compactInfo.date(), compactVisaReq);
+            contributionVisas.add(compactVisaReq);
 
-        visas().put(key, contributionVisas);
+            entry.setValue(contributionVisas);
+
+            return contributionVisas;
+        });
     }
 
     /** */
-    public VisaRequest getVisaRequest(ContributionKey key, Date date) {
-        Map<Date, CompactVisaRequest> reqs = visas().get(new CompactContributionKey(key, strCompactor));
+    public List<VisaRequest> getVisaRequests(ContributionKey key) {
+        List<CompactVisaRequest> reqs = visas().get(new CompactContributionKey(key, strCompactor));
 
         if (Objects.isNull(reqs))
             return null;
 
-        return reqs.get(date).toVisaRequest(strCompactor);
+        return reqs.stream()
+            .map(compactVisaReq -> compactVisaReq.toVisaRequest(strCompactor))
+            .collect(Collectors.toList());
     }
 
     /** */
-    public boolean updateVisaRequestResult(ContributionKey key, Date date, Visa visa) {
-        VisaRequest req = getVisaRequest(key, date);
+    public VisaRequest getLastVisaRequest(ContributionKey key) {
+        List<VisaRequest> reqs = getVisaRequests(key);
+
+        if (Objects.isNull(reqs))
+            return null;
+
+        return reqs.get(reqs.size() - 1);
+    }
 
-        if (req == null)
+    /** */
+    public boolean updateLastVisaRequest(ContributionKey key, Consumer<VisaRequest> updater) {
+        CompactContributionKey compactKey = new CompactContributionKey(key, strCompactor);
+
+        if (!visas().containsKey(compactKey))
             return false;
 
-        req.setResult(visa);
+        visas().invoke(compactKey, (entry, arguments) -> {
+            List<CompactVisaRequest> compactReqs = entry.getValue();
+
+            int lastIdx = compactReqs.size() - 1;
+
+            VisaRequest req = compactReqs.get(lastIdx).toVisaRequest(strCompactor);
+
+            updater.accept(req);
 
-        put(req);
+            compactReqs.set(lastIdx, new CompactVisaRequest(req, strCompactor));
+
+            entry.setValue(compactReqs);
+
+            return compactReqs;
+        });
 
         return true;
     }
 
+    /** */
+    public Collection<VisaRequest> getLastVisas() {
+        List<VisaRequest> res = new ArrayList<>();
+
+        visas().forEach(entry -> {
+            int lastIdx = entry.getValue().size() - 1;
+
+            res.add(entry.getValue().get(lastIdx).toVisaRequest(strCompactor));
+        });
+
+        return Collections.unmodifiableCollection(res);
+    }
+
     /** */
     public Collection<VisaRequest> getVisas() {
         List<VisaRequest> res = new ArrayList<>();
 
-        visas().forEach(entry -> res.addAll(entry.getValue().values().stream()
+        visas().forEach(entry -> res.addAll(entry.getValue().stream()
             .map(v -> v.toVisaRequest(strCompactor))
             .collect(Collectors.toList())));
 


 

----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on GitHub and use the
URL above to go to the specific comment.
 
For queries about this service, please contact Infrastructure at:
users@infra.apache.org


With regards,
Apache Git Services