Fix deadlock
This commit is contained in:
parent
a220405e97
commit
9d08e1e211
|
@ -3,6 +3,9 @@ package ca.uhn.fhir.jpa.dao.data;
|
|||
import java.util.Collection;
|
||||
import java.util.Date;
|
||||
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.domain.Slice;
|
||||
|
||||
/*
|
||||
* #%L
|
||||
* HAPI FHIR JPA Server
|
||||
|
@ -36,7 +39,7 @@ public interface ISearchDao extends JpaRepository<Search, Long> {
|
|||
public Search findByUuid(@Param("uuid") String theUuid);
|
||||
|
||||
@Query("SELECT s.myId FROM Search s WHERE s.mySearchLastReturned < :cutoff")
|
||||
public Collection<Long> findWhereLastReturnedBefore(@Param("cutoff") Date theCutoff);
|
||||
public Slice<Long> findWhereLastReturnedBefore(@Param("cutoff") Date theCutoff, Pageable thePage);
|
||||
|
||||
// @Query("SELECT s FROM Search s WHERE s.myCreated < :cutoff")
|
||||
// public Collection<Search> findWhereCreatedBefore(@Param("cutoff") Date theCutoff);
|
||||
|
|
|
@ -1,44 +1,23 @@
|
|||
package ca.uhn.fhir.jpa.search;
|
||||
|
||||
/*
|
||||
* #%L
|
||||
* HAPI FHIR JPA Server
|
||||
* %%
|
||||
* Copyright (C) 2014 - 2017 University Health Network
|
||||
* %%
|
||||
* Licensed 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.
|
||||
* #L%
|
||||
*/
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Date;
|
||||
|
||||
import org.apache.commons.lang3.time.DateUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
import org.springframework.data.domain.Slice;
|
||||
import org.springframework.scheduling.annotation.Scheduled;
|
||||
import org.springframework.transaction.PlatformTransactionManager;
|
||||
import org.springframework.transaction.TransactionStatus;
|
||||
import org.springframework.transaction.annotation.Propagation;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.transaction.support.TransactionCallbackWithoutResult;
|
||||
import org.springframework.transaction.support.TransactionCallback;
|
||||
import org.springframework.transaction.support.TransactionTemplate;
|
||||
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
|
||||
import ca.uhn.fhir.jpa.dao.DaoConfig;
|
||||
import ca.uhn.fhir.jpa.dao.data.ISearchDao;
|
||||
import ca.uhn.fhir.jpa.dao.data.ISearchIncludeDao;
|
||||
import ca.uhn.fhir.jpa.dao.data.ISearchResultDao;
|
||||
import ca.uhn.fhir.jpa.dao.data.*;
|
||||
import ca.uhn.fhir.jpa.entity.Search;
|
||||
|
||||
/**
|
||||
|
@ -47,7 +26,6 @@ import ca.uhn.fhir.jpa.entity.Search;
|
|||
public class StaleSearchDeletingSvcImpl implements IStaleSearchDeletingSvc {
|
||||
public static final long DEFAULT_CUTOFF_SLACK = 10 * DateUtils.MILLIS_PER_SECOND;
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(StaleSearchDeletingSvcImpl.class);
|
||||
|
||||
|
||||
/*
|
||||
* We give a bit of extra leeway just to avoid race conditions where a query result
|
||||
|
@ -71,41 +49,47 @@ public class StaleSearchDeletingSvcImpl implements IStaleSearchDeletingSvc {
|
|||
@Autowired
|
||||
private PlatformTransactionManager myTransactionManager;
|
||||
|
||||
protected void deleteSearch(final Long theSearchPid) {
|
||||
TransactionTemplate tt = new TransactionTemplate(myTransactionManager);
|
||||
tt.execute(new TransactionCallbackWithoutResult() {
|
||||
@Override
|
||||
protected void doInTransactionWithoutResult(TransactionStatus theArg0) {
|
||||
Search searchToDelete = mySearchDao.findOne(theSearchPid);
|
||||
ourLog.info("Deleting search {}/{} - Created[{}] -- Last returned[{}]", searchToDelete.getId(), searchToDelete.getUuid(), searchToDelete.getCreated(), searchToDelete.getSearchLastReturned());
|
||||
mySearchIncludeDao.deleteForSearch(searchToDelete.getId());
|
||||
mySearchResultDao.deleteForSearch(searchToDelete.getId());
|
||||
mySearchDao.delete(searchToDelete);
|
||||
}
|
||||
});
|
||||
private void deleteSearch(final Long theSearchPid) {
|
||||
Search searchToDelete = mySearchDao.findOne(theSearchPid);
|
||||
ourLog.info("Deleting search {}/{} - Created[{}] -- Last returned[{}]", searchToDelete.getId(), searchToDelete.getUuid(), searchToDelete.getCreated(), searchToDelete.getSearchLastReturned());
|
||||
mySearchIncludeDao.deleteForSearch(searchToDelete.getId());
|
||||
mySearchResultDao.deleteForSearch(searchToDelete.getId());
|
||||
mySearchDao.delete(searchToDelete);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
@Transactional(propagation = Propagation.NOT_SUPPORTED)
|
||||
public void pollForStaleSearchesAndDeleteThem() {
|
||||
|
||||
|
||||
long cutoffMillis = myDaoConfig.getExpireSearchResultsAfterMillis();
|
||||
if (myDaoConfig.getReuseCachedSearchResultsForMillis() != null) {
|
||||
cutoffMillis = Math.max(cutoffMillis, myDaoConfig.getReuseCachedSearchResultsForMillis());
|
||||
}
|
||||
Date cutoff = new Date((System.currentTimeMillis() - cutoffMillis) - myCutoffSlack);
|
||||
|
||||
final Date cutoff = new Date((System.currentTimeMillis() - cutoffMillis) - myCutoffSlack);
|
||||
|
||||
ourLog.debug("Searching for searches which are before {}", cutoff);
|
||||
|
||||
Collection<Long> toDelete = mySearchDao.findWhereLastReturnedBefore(cutoff);
|
||||
if (!toDelete.isEmpty()) {
|
||||
|
||||
for (final Long next : toDelete) {
|
||||
deleteSearch(next);
|
||||
TransactionTemplate tt = new TransactionTemplate(myTransactionManager);
|
||||
int count = tt.execute(new TransactionCallback<Integer>() {
|
||||
@Override
|
||||
public Integer doInTransaction(TransactionStatus theStatus) {
|
||||
Slice<Long> toDelete = mySearchDao.findWhereLastReturnedBefore(cutoff, new PageRequest(0, 1000));
|
||||
for (final Long next : toDelete) {
|
||||
deleteSearch(next);
|
||||
}
|
||||
return toDelete.getContent().size();
|
||||
}
|
||||
});
|
||||
|
||||
long total = tt.execute(new TransactionCallback<Long>() {
|
||||
@Override
|
||||
public Long doInTransaction(TransactionStatus theStatus) {
|
||||
return mySearchDao.count();
|
||||
}
|
||||
});
|
||||
|
||||
ourLog.info("Deleted {} searches, {} remaining", count, total);
|
||||
|
||||
ourLog.info("Deleted {} searches, {} remaining", toDelete.size(), mySearchDao.count());
|
||||
}
|
||||
}
|
||||
|
||||
@Scheduled(fixedDelay = DEFAULT_CUTOFF_SLACK)
|
||||
|
|
|
@ -176,6 +176,9 @@
|
|||
elements are a choice type (i.e. named "foo[x]"). Thanks to
|
||||
GitHub user @CarthageKing for the pull request!
|
||||
</action>
|
||||
<action type="fix">
|
||||
Fix potential deadlock in stale search deleting task in JPA server
|
||||
</action>
|
||||
</release>
|
||||
<release version="2.5" date="2017-06-08">
|
||||
<action type="fix">
|
||||
|
|
Loading…
Reference in New Issue