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

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


The following commit(s) were added to refs/heads/master by this push:
     new 2aefb27  IGNITE-10275 Several JIRA comments are issued in case 
ignite.cache.remove failed - Fixes #76.
2aefb27 is described below

commit 2aefb27497a8ada00ccd90e931cec67d96c9c4b5
Author: ololo3000 <pmgheap....@gmail.com>
AuthorDate: Wed Nov 21 18:52:38 2018 +0300

    IGNITE-10275 Several JIRA comments are issued in case ignite.cache.remove 
failed - Fixes #76.
    
    Signed-off-by: Dmitriy Pavlov <dpav...@apache.org>
---
 .../apache/ignite/ci/IgnitePersistentTeamcity.java |   9 +-
 .../java/org/apache/ignite/ci/db/DbMigrations.java |  72 ++++++++++++-
 .../java/org/apache/ignite/ci/db/TcHelperDb.java   |  15 +++
 .../org/apache/ignite/ci/di/IgniteTcBotModule.java |   2 +
 .../apache/ignite/ci/observer/BuildObserver.java   |  11 +-
 .../org/apache/ignite/ci/observer/BuildsInfo.java  |   6 +-
 .../apache/ignite/ci/observer/ObserverTask.java    | 111 +++++++--------------
 .../ignite/ci/runners/RemoteClientTmpHelper.java   |  25 -----
 .../tcbot/visa/TcBotTriggerAndSignOffService.java  |  20 ++--
 .../ignite/ci/web/model/CompactVisaRequest.java    |  16 ++-
 .../ignite/ci/web/model/ContributionKey.java       |  20 ++++
 .../apache/ignite/ci/web/model/VisaRequest.java    |  15 +++
 .../ci/web/model/hist/VisasHistoryStorage.java     |  80 +++++++++++----
 13 files changed, 256 insertions(+), 146 deletions(-)

diff --git 
a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/IgnitePersistentTeamcity.java
 
b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/IgnitePersistentTeamcity.java
index 3d39237..1fe97f5 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.tcmodel.user.User;
 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 @@ public class IgnitePersistentTeamcity implements 
IAnalyticsEnabledTeamcity, ITea
 
     @Inject
     private Ignite ignite;
+
+    /** */
+    @Inject
+    private VisasHistoryStorage visasHistStorage;
+
     /**
      * Teamcity
      */
@@ -158,7 +164,8 @@ public class IgnitePersistentTeamcity implements 
IAnalyticsEnabledTeamcity, ITea
                 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 a8307ff..a1a92ad 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,40 @@
 
 package org.apache.ignite.ci.db;
 
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
 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 +111,9 @@ public class DbMigrations {
     @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 +141,62 @@ public class DbMigrations {
             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> cache = 
ignite.cache(COMPACT_VISAS_HISTORY_CACHE_NAME);
+            if (cache == null) {
+                System.err.println("Cache not found " + 
COMPACT_VISAS_HISTORY_CACHE_NAME);
+
+                return;
+            }
+
+            IgniteCache<Object, Object> oldVisasCache = cache.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);
+
+                Collection<BinaryObject> binVisaReqs = null;
+                Object val = entry.getValue();
+                if (val instanceof List)
+                    binVisaReqs = (Collection<BinaryObject>)val;
+                else {
+                    if (val instanceof Map)
+                        binVisaReqs = ((Map<?, BinaryObject>)val).values();
+                }
+
+                if (binVisaReqs == null)
+                    continue;
+
+                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 +491,8 @@ public class DbMigrations {
         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 28a181c..23f7e62 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 class TcHelperDb {
         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 618b681..7022b45 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.util.ExceptionUtil;
 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 @@ public class IgniteTcBotModule extends AbstractModule {
         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 b4686f7..98287d2 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 class BuildObserver {
         timer.schedule(observerTask, 0, PERIOD);
 
         this.observerTask = observerTask;
+
+        this.observerTask.init();
     }
 
     /**
@@ -66,16 +68,9 @@ public class BuildObserver {
     }
 
     /** */
-    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 e02ad5b..976cf3c 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 class BuildsInfo {
     /**
      * @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 class BuildsInfo {
      * @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 68335c8..1d9ed43 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 @@ public class ObserverTask extends TimerTask {
     /** 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 @@ public class ObserverTask extends TimerTask {
     }
 
     /** */
-    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 @@ public class ObserverTask extends TimerTask {
             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 @@ public class ObserverTask extends TimerTask {
                     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 f3f3802..3fd622a 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 javax.cache.Cache;
 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 class RemoteClientTmpHelper {
             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 d220d39..8edf836 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.IGitHubConnection;
 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 @@ public class TcBotTriggerAndSignOffService {
 
 
     @Inject PrChainsProcessor prChainsProcessor;
-    
+
     /** */
     public void startObserver() {
         buildObserverProvider.get();
@@ -113,8 +112,6 @@ public class TcBotTriggerAndSignOffService {
 
         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 class TcBotTriggerAndSignOffService {
 
             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 class TcBotTriggerAndSignOffService {
         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 993672c..5ce9a71 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
@@ -31,15 +31,29 @@ public class CompactVisaRequest {
     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 570f2c9..89e14d2 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 class ContributionKey {
     @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 c281a65..7483928 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
@@ -31,6 +31,9 @@ public class VisaRequest {
     private Visa visa;
 
     /** */
+    private boolean isObserving;
+
+    /** */
     public VisaRequest(BuildsInfo info) {
         this.info = info;
         this.visa = Visa.emptyVisa();
@@ -52,4 +55,16 @@ public class VisaRequest {
 
         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 28299f7..623d42d 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 @@ package org.apache.ignite.ci.web.model.hist;
 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.teamcity.ignited.IStringCompactor;
 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 @@ import org.apache.ignite.ci.web.model.VisaRequest;
  */
 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 class VisasHistoryStorage {
     }
 
     /** */
-    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 class VisasHistoryStorage {
             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())));
 

Reply via email to