mirror of
https://github.com/eclipse-sailing-analytics/sailing-analytics.git
synced 2026-09-16 10:48:47 +00:00
Merge branch 'main' into bug6239
This commit is contained in:
+10
-2
@@ -13,7 +13,6 @@ import java.util.Set;
|
||||
import java.util.concurrent.BrokenBarrierException;
|
||||
import java.util.concurrent.ConcurrentSkipListSet;
|
||||
import java.util.concurrent.CyclicBarrier;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
@@ -48,6 +47,15 @@ public class SmartFutureCacheTest {
|
||||
System.out.println("testPerformanceOfGetAndCall took "+(System.currentTimeMillis()-start)+"ms");
|
||||
}
|
||||
|
||||
/**
|
||||
* FIXME bug6245: the problem with this test is that its outcome depends on the timing with which SmartFutureCache removes the Future
|
||||
* from its ongoingRecalculations map after the calculation finished with an exception. This happens before the exception is
|
||||
* re-thrown, wrapped in a RuntimeException, which is then caught by the FutureTask's run() method where the exception
|
||||
* is set on the FutureTask. So, if the get("humba", true) call obtains the FutureTask from ongoingRecalculations before
|
||||
* SmartFutureCache.call() removes it in its finally block, the test will see the ExecutionException on the FutureTask.
|
||||
* Otherwise, the get() call will not see any ongoingRecalculation and hence won't be able to obtain the FutureTask,
|
||||
* hence returning null as if no calculation had been started at all.<p>
|
||||
*/
|
||||
@Test
|
||||
public void testExceptionInComputeCacheUpdate() {
|
||||
final boolean[] throwException = new boolean[1];
|
||||
@@ -75,7 +83,7 @@ public class SmartFutureCacheTest {
|
||||
final String result = sfc.get("humba", /* waitForLatest */ true);
|
||||
fail("Expected RuntimeException because computeCacheUpdate threw one; instead, it returned "+result);
|
||||
} catch (RuntimeException expected) {
|
||||
assertSame(ExecutionException.class, expected.getCause().getClass());
|
||||
assertSame(NullPointerException.class, expected.getCause().getClass());
|
||||
}
|
||||
throwException[0] = false;
|
||||
sfc.triggerUpdate("humba", /* update interval */ null);
|
||||
|
||||
+6
-6
@@ -6,14 +6,14 @@ public class FavoritesResult implements Result {
|
||||
|
||||
private FavoriteBoatClassesDTO favoriteBoatClasses;
|
||||
private FavoriteCompetitorsDTO favoriteCompetitors;
|
||||
private boolean isSubscribedToFeatureAndCommunityUpdates;
|
||||
|
||||
private boolean didOptOutOfFeatureAndCommunityEmails;
|
||||
|
||||
protected FavoritesResult() {}
|
||||
|
||||
public FavoritesResult(FavoriteBoatClassesDTO favoriteBoatClasses, FavoriteCompetitorsDTO favoriteCompetitors, boolean isSubscribedToFeatureAndCommunityUpdates) {
|
||||
public FavoritesResult(FavoriteBoatClassesDTO favoriteBoatClasses, FavoriteCompetitorsDTO favoriteCompetitors, boolean didOptOutOfFeatureAndCommunityEmails) {
|
||||
this.favoriteBoatClasses = favoriteBoatClasses;
|
||||
this.favoriteCompetitors = favoriteCompetitors;
|
||||
this.isSubscribedToFeatureAndCommunityUpdates = isSubscribedToFeatureAndCommunityUpdates;
|
||||
this.didOptOutOfFeatureAndCommunityEmails = didOptOutOfFeatureAndCommunityEmails;
|
||||
}
|
||||
|
||||
public FavoriteBoatClassesDTO getFavoriteBoatClasses() {
|
||||
@@ -24,7 +24,7 @@ public class FavoritesResult implements Result {
|
||||
return favoriteCompetitors;
|
||||
}
|
||||
|
||||
public boolean getIsSubscribedToFeatureAndCommunityUpdates() {
|
||||
return isSubscribedToFeatureAndCommunityUpdates;
|
||||
public boolean getDidOptOutOfFeatureAndCommunityEmails() {
|
||||
return didOptOutOfFeatureAndCommunityEmails;
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -26,7 +26,7 @@ public class GetFavoritesAction implements SailingAction<FavoritesResult> {
|
||||
@GwtIncompatible
|
||||
public FavoritesResult execute(SailingDispatchContext ctx) throws DispatchException {
|
||||
return new FavoritesResult(getFavoriteBoatClasses(ctx), getFavoriteCompetitors(ctx),
|
||||
!getDidOptOutOfFeatureAndCommunityEmails(ctx));
|
||||
getDidOptOutOfFeatureAndCommunityEmails(ctx));
|
||||
}
|
||||
|
||||
@GwtIncompatible
|
||||
|
||||
+4
-4
@@ -9,13 +9,13 @@ import com.sap.sse.gwt.dispatch.shared.exceptions.DispatchException;
|
||||
import com.sap.sse.security.shared.UserManagementException;
|
||||
|
||||
public class SaveMiscEmailPreferences implements SailingAction<VoidResult>, HasWriteAction {
|
||||
private Boolean subscribeToFeatureAndCommunityUpdates;
|
||||
private Boolean didOptOutOfFeatureAndCommunityEmails;
|
||||
|
||||
protected SaveMiscEmailPreferences() {
|
||||
}
|
||||
|
||||
public SaveMiscEmailPreferences(final Boolean subscribeToFeatureAndCommunityUpdates) {
|
||||
this.subscribeToFeatureAndCommunityUpdates = subscribeToFeatureAndCommunityUpdates;
|
||||
public SaveMiscEmailPreferences(final Boolean didOptOutOfFeatureAndCommunityEmails) {
|
||||
this.didOptOutOfFeatureAndCommunityEmails = didOptOutOfFeatureAndCommunityEmails;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -24,7 +24,7 @@ public class SaveMiscEmailPreferences implements SailingAction<VoidResult>, HasW
|
||||
try {
|
||||
final String username = ctx.getSecurityService().getCurrentUser().getName();
|
||||
ctx.getSecurityService().updateUserProperties(username, null, null, null,
|
||||
!subscribeToFeatureAndCommunityUpdates);
|
||||
didOptOutOfFeatureAndCommunityEmails);
|
||||
} catch (UserManagementException e) {
|
||||
throw new DispatchException(e.getMessage());
|
||||
}
|
||||
|
||||
+11
-13
@@ -18,7 +18,6 @@ public class MiscellaneousDisplayImpl {
|
||||
|
||||
public MiscellaneousDisplayImpl(final MiscPreferencesPresenter presenter) {
|
||||
presenter.registerDisplay(this);
|
||||
// compose ui
|
||||
final String securityUpdatesTitle = StringMessages.INSTANCE.securityUpdates();
|
||||
final CheckBoxTile securityUpdates = new CheckBoxTile(securityUpdatesTitle, true, null);
|
||||
featureAndCommunityUpdates = composeFeatureAndCommunityUpdatesTile(presenter);
|
||||
@@ -29,16 +28,16 @@ public class MiscellaneousDisplayImpl {
|
||||
selectionUi = new LabeledBox(boxTitle, tileList);
|
||||
}
|
||||
|
||||
public void setIsSubscribedToFeatureAndCommunityUpdates(final boolean b, final boolean fireChangeHandlers) {
|
||||
featureAndCommunityUpdates.setValue(b, fireChangeHandlers);
|
||||
public void setDidOptOutOfFeatureAndCommunityEmails(final boolean didOptOutOfFeatureAndCommunityEmails, final boolean fireChangeHandlers) {
|
||||
featureAndCommunityUpdates.setValue(didOptOutOfFeatureAndCommunityEmails, fireChangeHandlers);
|
||||
}
|
||||
|
||||
private AsyncCallback<VoidResult> wrapCallbackWithToastResponse(final boolean isNowTrue,
|
||||
private AsyncCallback<VoidResult> wrapCallbackWithToastResponse(final boolean didOptOutOfFeatureAndCommunityEmails,
|
||||
final AsyncCallback<VoidResult> callback) {
|
||||
final AsyncCallback<VoidResult> callbackWrappedWithToastNotification = new AsyncCallback<VoidResult>() {
|
||||
@Override
|
||||
public void onFailure(Throwable caught) {
|
||||
final String failText = StringMessages.INSTANCE.failedToSetStatusOfFeatureAndCommunityUpdates();
|
||||
final String failText = StringMessages.INSTANCE.couldNotToggleFeatureAndCommunityUpdates();
|
||||
Notification.notify(failText, NotificationType.ERROR);
|
||||
if (callback != null) {
|
||||
callback.onFailure(caught);
|
||||
@@ -47,10 +46,9 @@ public class MiscellaneousDisplayImpl {
|
||||
|
||||
@Override
|
||||
public void onSuccess(VoidResult result) {
|
||||
final String passAndTrue = StringMessages.INSTANCE.youWillNowReceiveFeatureAndCommunityUpdates();
|
||||
final String passAndFalse = StringMessages.INSTANCE
|
||||
.youWillNotReceiveFeatureAndCommunityUpdatesAnymore();
|
||||
final String message = isNowTrue ? passAndTrue : passAndFalse;
|
||||
final String passAndTrue = StringMessages.INSTANCE.optedOutOfFeatureAndCommunityUpdates();
|
||||
final String passAndFalse = StringMessages.INSTANCE.optedInToFeatureAndCommunityUpdates();
|
||||
final String message = didOptOutOfFeatureAndCommunityEmails ? passAndTrue : passAndFalse;
|
||||
Notification.notify(message, NotificationType.SUCCESS);
|
||||
if (callback != null) {
|
||||
callback.onSuccess(result);
|
||||
@@ -61,11 +59,11 @@ public class MiscellaneousDisplayImpl {
|
||||
}
|
||||
|
||||
private CheckBoxTile composeFeatureAndCommunityUpdatesTile(final MiscPreferencesPresenter presenter) {
|
||||
final BiConsumer<Boolean, AsyncCallback<VoidResult>> onToggle = (isNowTrue, callback) -> {
|
||||
final AsyncCallback<VoidResult> wrappedCallback = wrapCallbackWithToastResponse(isNowTrue, callback);
|
||||
presenter.updateIsSubscribedToFeatureAndCommunityUpdates(isNowTrue, wrappedCallback);
|
||||
final BiConsumer<Boolean, AsyncCallback<VoidResult>> onToggle = (didOptOutOfFeatureAndCommunityEmails, callback) -> {
|
||||
final AsyncCallback<VoidResult> wrappedCallback = wrapCallbackWithToastResponse(didOptOutOfFeatureAndCommunityEmails, callback);
|
||||
presenter.updateDidOptOutOfFeatureAndCommunityEmails(didOptOutOfFeatureAndCommunityEmails, wrappedCallback);
|
||||
};
|
||||
final String title = StringMessages.INSTANCE.featureAndCommunityUpdates();
|
||||
final String title = StringMessages.INSTANCE.optOutOfFeatureAndCommunityUpdates();
|
||||
return new CheckBoxTile(title, false, onToggle);
|
||||
}
|
||||
}
|
||||
+3
-3
@@ -6,9 +6,9 @@ import com.sap.sse.gwt.dispatch.shared.commands.VoidResult;
|
||||
|
||||
public interface MiscPreferencesPresenter {
|
||||
void registerDisplay(MiscellaneousDisplayImpl display);
|
||||
|
||||
void updateIsSubscribedToFeatureAndCommunityUpdates(final boolean b, final AsyncCallback<VoidResult> callback);
|
||||
|
||||
void updateDidOptOutOfFeatureAndCommunityEmails(final boolean didOptOutOfFeatureAndCommunityEmails, final AsyncCallback<VoidResult> callback);
|
||||
|
||||
/** get value via dispatch method, set first correct value onto checkbox */
|
||||
void initIsSubscribedToFeatureAndCommunityUpdates(final boolean b);
|
||||
void initDidOptOutOfFeatureAndCommunityEmails(final boolean didOptOutOfFeatureAndCommunityEmails);
|
||||
}
|
||||
|
||||
+5
-6
@@ -56,7 +56,7 @@ public class UserPreferencesPresenter<C extends ClientFactoryWithDispatch & Erro
|
||||
final FavoriteBoatClassesDTO favoriteBoatClasses = result.getFavoriteBoatClasses();
|
||||
boatClassSelectionPresenter.initNotifications(favoriteBoatClasses.isNotifyAboutUpcomingRaces(),
|
||||
favoriteBoatClasses.isNotifyAboutResults(), favoriteBoatClasses.getSelectedBoatClasses());
|
||||
miscPresenter.initIsSubscribedToFeatureAndCommunityUpdates(result.getIsSubscribedToFeatureAndCommunityUpdates());
|
||||
miscPresenter.initDidOptOutOfFeatureAndCommunityEmails(result.getDidOptOutOfFeatureAndCommunityEmails());
|
||||
}
|
||||
};
|
||||
clientFactory.getDispatch().execute(new GetFavoritesAction(), callback);
|
||||
@@ -76,15 +76,15 @@ public class UserPreferencesPresenter<C extends ClientFactoryWithDispatch & Erro
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initIsSubscribedToFeatureAndCommunityUpdates(final boolean b) {
|
||||
public void initDidOptOutOfFeatureAndCommunityEmails(final boolean didOptOutOfFeatureAndCommunityEmails) {
|
||||
if (display != null) {
|
||||
display.setIsSubscribedToFeatureAndCommunityUpdates(b, false);
|
||||
display.setDidOptOutOfFeatureAndCommunityEmails(didOptOutOfFeatureAndCommunityEmails, false);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateIsSubscribedToFeatureAndCommunityUpdates(final boolean b, final AsyncCallback<VoidResult> callback) {
|
||||
clientFactory.getDispatch().execute(new SaveMiscEmailPreferences(b), callback);
|
||||
public void updateDidOptOutOfFeatureAndCommunityEmails(final boolean didOptOutOfFeatureAndCommunityEmails, final AsyncCallback<VoidResult> callback) {
|
||||
clientFactory.getDispatch().execute(new SaveMiscEmailPreferences(didOptOutOfFeatureAndCommunityEmails), callback);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -195,5 +195,4 @@ public class UserPreferencesPresenter<C extends ClientFactoryWithDispatch & Erro
|
||||
public CompetitorSelectionPresenter getFavoriteCompetitorsDataProvider() {
|
||||
return competitorPresenter;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+4
-4
@@ -2558,10 +2558,10 @@ public interface StringMessages extends com.sap.sse.gwt.client.StringMessages,
|
||||
String sourceCode();
|
||||
String miscellaneous();
|
||||
String securityUpdates();
|
||||
String featureAndCommunityUpdates();
|
||||
String youWillNowReceiveFeatureAndCommunityUpdates();
|
||||
String youWillNotReceiveFeatureAndCommunityUpdatesAnymore();
|
||||
String failedToSetStatusOfFeatureAndCommunityUpdates();
|
||||
String optOutOfFeatureAndCommunityUpdates();
|
||||
String optedOutOfFeatureAndCommunityUpdates();
|
||||
String optedInToFeatureAndCommunityUpdates();
|
||||
String couldNotToggleFeatureAndCommunityUpdates();
|
||||
String youWillNowReceiveNotificationsForFavoriteCompetitors();
|
||||
String youWillNotReceiveNotificationsForFavoriteCompetitorsAnymore();
|
||||
String failedToSetStatusOfNotificationsForFavoriteCompetitors();
|
||||
|
||||
+4
-4
@@ -2594,10 +2594,10 @@ unableToLoadIpsBlockedForUserCreationAbuse=Unable to load IPs Blocked for User C
|
||||
sourceCode=Source Code
|
||||
miscellaneous=Miscellaneous
|
||||
securityUpdates=Security Updates
|
||||
featureAndCommunityUpdates=Feature and Community Updates
|
||||
youWillNowReceiveFeatureAndCommunityUpdates=You will now receive feature and community updates
|
||||
youWillNotReceiveFeatureAndCommunityUpdatesAnymore=You will not receive feature and community updates anymore
|
||||
failedToSetStatusOfFeatureAndCommunityUpdates=Failed to set status of feature and community updates
|
||||
optOutOfFeatureAndCommunityUpdates=Opt out of feature and community updates
|
||||
optedOutOfFeatureAndCommunityUpdates=You have opted out of feature and community updates
|
||||
optedInToFeatureAndCommunityUpdates=You will now receive feature and community updates
|
||||
couldNotToggleFeatureAndCommunityUpdates=Could not toggle feature and community updates
|
||||
failedToSetStatusOfNotificationsForFavoriteCompetitors=Failed to set status of notifications for favorite competitors
|
||||
youWillNowReceiveNotificationsForFavoriteCompetitors=You will now receive notifications for favorite competitors
|
||||
failedToSetStatusOfUpdatesOnUpcomingRacesForYourFavoredBoatClasses=Failed to set status of notifications for upcoming races of your favored boat classes
|
||||
|
||||
+4
-4
@@ -2588,10 +2588,10 @@ unableToLoadIpsBlockedForUserCreationAbuse=Wegen Missbrauchs der Benutzererstell
|
||||
sourceCode=Quellcode
|
||||
miscellaneous=Verschiedenes
|
||||
securityUpdates=Sicherheitsupdates
|
||||
featureAndCommunityUpdates=Funktions- und Community-Updates
|
||||
youWillNowReceiveFeatureAndCommunityUpdates=Sie erhalten nun Funktions- und Community-Updates.
|
||||
youWillNotReceiveFeatureAndCommunityUpdatesAnymore=Sie erhalten keine Funktions- und Community-Updates mehr.
|
||||
failedToSetStatusOfFeatureAndCommunityUpdates=Fehler beim Festlegen des Status von Funktions- und Community-Updates
|
||||
optOutOfFeatureAndCommunityUpdates=Funktions- und Community-Updates abbestellen
|
||||
optedOutOfFeatureAndCommunityUpdates=Sie haben Funktions- und Community-Updates abbestellt.
|
||||
optedInToFeatureAndCommunityUpdates=Sie erhalten nun Funktions- und Community-Updates.
|
||||
couldNotToggleFeatureAndCommunityUpdates=Der Status der Funktions- und Community-Updates konnte nicht geändert werden.
|
||||
failedToSetStatusOfNotificationsForFavoriteCompetitors=Status der Benachrichtigungen für bevorzugte Teilnehmer konnte nicht festgelegt werden.
|
||||
youWillNowReceiveNotificationsForFavoriteCompetitors=Sie erhalten nun Benachrichtigungen für Ihre bevorzugten Teilnehmer.
|
||||
youWillNotReceiveNotificationsForFavoriteCompetitorsAnymore=Sie erhalten keine Benachrichtigungen mehr für Ihre Lieblingskonkurrenten.
|
||||
|
||||
@@ -64,7 +64,7 @@ import com.sap.sse.util.impl.KnowsExecutorAndTracingGetImpl;
|
||||
* re-calculation and defer it until a {@link #get(Object, boolean)} request actually happens. For this purpose,
|
||||
* the {@link #suspend} and {@link #resume} methods can be used. No matter the suspend/resume state, the {@link #get(Object, boolean)}
|
||||
* method will always respond in line with the {@link #triggerUpdate(Object, UpdateInterval)} calls, only that re-calculations
|
||||
* are not immediately started when in suspended mode, and {@link #get(Object, boolean) get(key, false)} will no trigger a
|
||||
* are not immediately started when in suspended mode, and {@link #get(Object, boolean) get(key, false)} will not trigger a
|
||||
* re-calculation at all. When resuming, any pending recalculations triggered so far are scheduled for immediate execution such
|
||||
* that subsequent {@link #get(Object, boolean) get(key, true)} calls will wait for their completion.
|
||||
*
|
||||
@@ -92,8 +92,21 @@ public class SmartFutureCache<K, V, U extends UpdateInterval<U>> {
|
||||
*/
|
||||
private final ConcurrentMap<K, FutureTaskWithCancelBlocking> ongoingRecalculations;
|
||||
|
||||
/**
|
||||
* The results cached by this {@link SmartFutureCache}. While a concurrent map, most access is guarded by
|
||||
* {@link #locksForKeys}.
|
||||
*/
|
||||
private final ConcurrentMap<K, V> cache;
|
||||
|
||||
/**
|
||||
* Caches exceptions that occurred while computing an update for the respective key. If an exception is stored
|
||||
* in here for a key, this takes precedence over any value in {@link #cache} for that same key, although ideally
|
||||
* when putting an exception into this map, that key should be removed from {@link #cache} for consistency.<p>
|
||||
*
|
||||
* Access synchronization/locking follows exactly the same principle as for {@link #cache}.
|
||||
*/
|
||||
private final ConcurrentMap<K, Throwable> cachedExceptions;
|
||||
|
||||
/**
|
||||
* Note that this needs to have more than one thread because there may be calculations used for cache updates that
|
||||
* need to wait for other cache updates to finish. If those were all to be handled by a single thread, deadlocks
|
||||
@@ -299,84 +312,91 @@ public class SmartFutureCache<K, V, U extends UpdateInterval<U>> {
|
||||
|
||||
@Override
|
||||
public V call() {
|
||||
try {
|
||||
final U updateInterval;
|
||||
final Set<Thread> locksPropagatedFromGettingThreads;
|
||||
synchronized (this) {
|
||||
updateInterval = getUpdateInterval();
|
||||
locksPropagatedFromGettingThreads = new HashSet<Thread>(gettingThreads);
|
||||
executingThread = Thread.currentThread();
|
||||
if (!locksPropagatedFromGettingThreads.isEmpty()) {
|
||||
// get() was called and cannot have returned yet because call() hasn't returned; propagate locks from getting thread
|
||||
for (Thread locksPropagatedFromGettingThread : locksPropagatedFromGettingThreads) {
|
||||
LockUtil.propagateLockSetFrom(locksPropagatedFromGettingThread);
|
||||
// Turn this logging on in case you need to debug the SmartFutureCache. Otherwise, log level detection is too expensive here.
|
||||
// logger.finest("propagating lock set from " + locksPropagatedFromGettingThread.getName()
|
||||
// + " to " + executingThread.getName());
|
||||
}
|
||||
}
|
||||
// make sure we don't propagate from the same thread twice in case gettingThread == callerThread
|
||||
if (callerWaitsSynchronouslyForResult && !locksPropagatedFromGettingThreads.contains(callerThread)) {
|
||||
final U updateInterval;
|
||||
final Set<Thread> locksPropagatedFromGettingThreads;
|
||||
synchronized (this) {
|
||||
updateInterval = getUpdateInterval();
|
||||
locksPropagatedFromGettingThreads = new HashSet<Thread>(gettingThreads);
|
||||
executingThread = Thread.currentThread();
|
||||
if (!locksPropagatedFromGettingThreads.isEmpty()) {
|
||||
// get() was called and cannot have returned yet because call() hasn't returned; propagate locks from getting thread
|
||||
for (Thread locksPropagatedFromGettingThread : locksPropagatedFromGettingThreads) {
|
||||
LockUtil.propagateLockSetFrom(locksPropagatedFromGettingThread);
|
||||
// Turn this logging on in case you need to debug the SmartFutureCache. Otherwise, log level detection is too expensive here.
|
||||
// logger.finest("propagating lock set from "+callerThread.getName()+" to "+executingThread.getName()+
|
||||
// " due to synchronous execution");
|
||||
LockUtil.propagateLockSetFrom(callerThread);
|
||||
// logger.finest("propagating lock set from " + locksPropagatedFromGettingThread.getName()
|
||||
// + " to " + executingThread.getName());
|
||||
}
|
||||
runningAndReadUpdateInterval = true;
|
||||
}
|
||||
// make sure we don't propagate from the same thread twice in case gettingThread == callerThread
|
||||
if (callerWaitsSynchronouslyForResult && !locksPropagatedFromGettingThreads.contains(callerThread)) {
|
||||
// Turn this logging on in case you need to debug the SmartFutureCache. Otherwise, log level detection is too expensive here.
|
||||
// logger.finest("propagating lock set from "+callerThread.getName()+" to "+executingThread.getName()+
|
||||
// " due to synchronous execution");
|
||||
LockUtil.propagateLockSetFrom(callerThread);
|
||||
}
|
||||
runningAndReadUpdateInterval = true;
|
||||
}
|
||||
try {
|
||||
try {
|
||||
V preResult = cacheUpdateComputer.computeCacheUpdate(key, updateInterval);
|
||||
final NamedReentrantReadWriteLock lock = getOrCreateLockForKey(key);
|
||||
LockUtil.lockForWrite(lock);
|
||||
try {
|
||||
V preResult = cacheUpdateComputer.computeCacheUpdate(key, updateInterval);
|
||||
final NamedReentrantReadWriteLock lock = getOrCreateLockForKey(key);
|
||||
LockUtil.lockForWrite(lock);
|
||||
try {
|
||||
V result = cacheUpdateComputer.provideNewCacheValue(key, cache.get(key), preResult,
|
||||
updateInterval);
|
||||
cache(key, result);
|
||||
return result;
|
||||
} finally {
|
||||
LockUtil.unlockAfterWrite(lock);
|
||||
}
|
||||
V result = cacheUpdateComputer.provideNewCacheValue(key, cache.get(key), preResult,
|
||||
updateInterval);
|
||||
cache(key, result);
|
||||
return result;
|
||||
} finally {
|
||||
synchronized (ongoingRecalculations) {
|
||||
boolean newTaskScheduled = false;
|
||||
Pair<U, Set<SettableFuture<Future<V>>>> queued = triggeredAndNotYetScheduled.get(key);
|
||||
if (!suspended || (queued != null && !queued.getB().isEmpty())) {
|
||||
if (queued != null) {
|
||||
triggeredAndNotYetScheduled.remove(key);
|
||||
Future<V> future = schedule(key, queued.getA(), /* callerWaitsSynchronouslyForResult */ false);
|
||||
newTaskScheduled = true;
|
||||
for (SettableFuture<Future<V>> futureToSet : queued.getB()) {
|
||||
futureToSet.set(future);
|
||||
}
|
||||
LockUtil.unlockAfterWrite(lock);
|
||||
}
|
||||
} catch (Exception e) { // important to update cachedExceptions before removing key from ongoingRecalculations,
|
||||
// so that get(K, boolean) always either still finds the FutureTask which then throws an exception upon get(),
|
||||
// or finds the exception that was thrown in cachedExceptions.
|
||||
final NamedReentrantReadWriteLock lock = getOrCreateLockForKey(key);
|
||||
LockUtil.lockForWrite(lock);
|
||||
try {
|
||||
remove(key);
|
||||
cachedExceptions.put(key, e);
|
||||
} finally {
|
||||
LockUtil.unlockAfterWrite(lock);
|
||||
}
|
||||
logger.log(Level.SEVERE, "SmartFutureCache.FutureTaskWithCancelBlocking.call", e);
|
||||
throw new RuntimeException(e);
|
||||
} finally {
|
||||
synchronized (ongoingRecalculations) {
|
||||
boolean newTaskScheduled = false;
|
||||
Pair<U, Set<SettableFuture<Future<V>>>> queued = triggeredAndNotYetScheduled.get(key);
|
||||
if (!suspended || (queued != null && !queued.getB().isEmpty())) {
|
||||
if (queued != null) {
|
||||
triggeredAndNotYetScheduled.remove(key);
|
||||
Future<V> future = schedule(key, queued.getA(), /* callerWaitsSynchronouslyForResult */ false);
|
||||
newTaskScheduled = true;
|
||||
for (SettableFuture<Future<V>> futureToSet : queued.getB()) {
|
||||
futureToSet.set(future);
|
||||
}
|
||||
}
|
||||
if (!newTaskScheduled) {
|
||||
ongoingRecalculations.remove(key);
|
||||
}
|
||||
}
|
||||
if (!newTaskScheduled) {
|
||||
ongoingRecalculations.remove(key);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
synchronized (this) {
|
||||
for (Thread locksPropagatedFromGettingThread : gettingThreads) {
|
||||
// Turn this logging on in case you need to debug the SmartFutureCache. Otherwise, log level detection is too expensive here.
|
||||
// logger.finest("unpropagating lock set from "+locksPropagatedFromGettingThread.getName()+" to "+executingThread.getName());
|
||||
LockUtil.unpropagateLockSetFrom(locksPropagatedFromGettingThread);
|
||||
}
|
||||
executingThread = null;
|
||||
}
|
||||
if (callerWaitsSynchronouslyForResult && !gettingThreads.contains(callerThread)) {
|
||||
// Turn this logging on in case you need to debug the SmartFutureCache. Otherwise, log level detection is too expensive here.
|
||||
// logger.finest("unpropagating lock set from "+callerThread.getName()+" to "+Thread.currentThread().getName()+
|
||||
// " due to synchronous execution");
|
||||
LockUtil.unpropagateLockSetFrom(callerThread);
|
||||
}
|
||||
gettingThreads.clear();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// cache won't be updated
|
||||
logger.log(Level.SEVERE, "SmartFutureCache.FutureTaskWithCancelBlocking.call", e);
|
||||
throw new RuntimeException(e);
|
||||
} finally {
|
||||
synchronized (this) {
|
||||
for (Thread locksPropagatedFromGettingThread : gettingThreads) {
|
||||
// Turn this logging on in case you need to debug the SmartFutureCache. Otherwise, log level detection is too expensive here.
|
||||
// logger.finest("unpropagating lock set from "+locksPropagatedFromGettingThread.getName()+" to "+executingThread.getName());
|
||||
LockUtil.unpropagateLockSetFrom(locksPropagatedFromGettingThread);
|
||||
}
|
||||
executingThread = null;
|
||||
}
|
||||
if (callerWaitsSynchronouslyForResult && !gettingThreads.contains(callerThread)) {
|
||||
// Turn this logging on in case you need to debug the SmartFutureCache. Otherwise, log level detection is too expensive here.
|
||||
// logger.finest("unpropagating lock set from "+callerThread.getName()+" to "+Thread.currentThread().getName()+
|
||||
// " due to synchronous execution");
|
||||
LockUtil.unpropagateLockSetFrom(callerThread);
|
||||
}
|
||||
gettingThreads.clear();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -397,7 +417,8 @@ public class SmartFutureCache<K, V, U extends UpdateInterval<U>> {
|
||||
|
||||
public SmartFutureCache(CacheUpdater<K, V, U> cacheUpdateComputer, String nameForLocks) {
|
||||
this.ongoingRecalculations = new ConcurrentHashMap<K, FutureTaskWithCancelBlocking>();
|
||||
this.cache = new ConcurrentHashMap<K, V>();
|
||||
this.cache = new ConcurrentHashMap<>();
|
||||
this.cachedExceptions = new ConcurrentHashMap<>();
|
||||
this.cacheUpdateComputer = cacheUpdateComputer;
|
||||
this.locksForKeys = new ConcurrentHashMap<K, NamedReentrantReadWriteLock>();
|
||||
this.nameForLocks = nameForLocks;
|
||||
@@ -687,8 +708,14 @@ public class SmartFutureCache<K, V, U extends UpdateInterval<U>> {
|
||||
* which may contain many tasks during certain phases of the life cycle of the process; e.g., during
|
||||
* start-up of the ARCHIVE server, several hundred thousand tasks may be enqueued with that background
|
||||
* executor, so that it may take minutes or even hours until a task is actually picked up and run.
|
||||
* Therefore, no foreground / HTTP request thread should ever use {@code true} for this parameter. It
|
||||
* may otherwise block all of the HTTP request threads. See also bug 6223.
|
||||
* Therefore, no foreground / HTTP request thread should ever use {@code true} for this parameter. It may
|
||||
* otherwise block all of the HTTP request threads. See also bug 6223.
|
||||
*
|
||||
* @throws a
|
||||
* {@link RuntimeException} with the {@link RuntimeException#getCause() cause} set to the exception that
|
||||
* occurred during the last {@link CacheUpdater#computeCacheUpdate(Object, UpdateInterval)} invocation,
|
||||
* if any; this happens regardless of whether the calculation was still ongoing and waited for, or if
|
||||
* not waiting for any current execution.
|
||||
*/
|
||||
public V get(final K key, boolean waitForLatest) {
|
||||
final V value;
|
||||
@@ -703,18 +730,35 @@ public class SmartFutureCache<K, V, U extends UpdateInterval<U>> {
|
||||
future = ongoingRecalculations.get(key);
|
||||
}
|
||||
}
|
||||
try {
|
||||
if (future != null) {
|
||||
value = future.get();
|
||||
} else {
|
||||
if (future != null) {
|
||||
try {
|
||||
value = future.get(); // will throw an ExecutionException if re-calculation threw an exception
|
||||
} catch (InterruptedException e) {
|
||||
logger.log(Level.SEVERE, "get", e);
|
||||
throw new RuntimeException(e);
|
||||
} catch (ExecutionException e) {
|
||||
logger.log(Level.SEVERE, "get", e);
|
||||
if (e.getCause() instanceof RuntimeException) {
|
||||
throw (RuntimeException) e.getCause();
|
||||
} else {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
value = readCache(key);
|
||||
} catch (RuntimeException e) {
|
||||
logger.log(Level.SEVERE, "get", e);
|
||||
throw e;
|
||||
}
|
||||
} catch (InterruptedException | ExecutionException e) {
|
||||
logger.log(Level.SEVERE, "get", e);
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
} else {
|
||||
value = readCache(key);
|
||||
try {
|
||||
value = readCache(key);
|
||||
} catch (RuntimeException e) {
|
||||
logger.log(Level.SEVERE, "get", e);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
return value;
|
||||
}
|
||||
@@ -724,6 +768,10 @@ public class SmartFutureCache<K, V, U extends UpdateInterval<U>> {
|
||||
final NamedReentrantReadWriteLock lock = getOrCreateLockForKey(key);
|
||||
LockUtil.lockForRead(lock);
|
||||
try {
|
||||
final Throwable exception = cachedExceptions.get(key);
|
||||
if (exception != null) {
|
||||
throw new RuntimeException(exception);
|
||||
}
|
||||
value = cache.get(key);
|
||||
} finally {
|
||||
LockUtil.unlockAfterRead(lock);
|
||||
@@ -736,6 +784,7 @@ public class SmartFutureCache<K, V, U extends UpdateInterval<U>> {
|
||||
}
|
||||
|
||||
protected void cache(final K key, V value) {
|
||||
cachedExceptions.remove(key);
|
||||
if (value == null) {
|
||||
cache.remove(key);
|
||||
locksForKeys.remove(key);
|
||||
@@ -760,5 +809,4 @@ public class SmartFutureCache<K, V, U extends UpdateInterval<U>> {
|
||||
public void remove(K key) {
|
||||
cache(key, null);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
# OnBoarding Information
|
||||
|
||||
This document describes the onboarding process for a new team member (developer)
|
||||
|
||||
First of all, make sure you've looked at [http://www.amazon.de/Patterns-Elements-Reusable-Object-Oriented-Software/dp/0201633612](http://www.amazon.de/Patterns-Elements-Reusable-Object-Oriented-Software/dp/0201633612). That's a great book, and knowing at least some of it will help you a great deal finding your way around our solution.
|
||||
|
||||
@@ -1394,4 +1394,4 @@ Should you want to compare servers of which you know they have different sets of
|
||||
<tr><td>Network Interfaces</td><td>Virtual network interfaces that are mapped to physical network interfaces on instances. </td></tr>
|
||||
|
||||
<tr><td>Multi instance</td><td>App instance that runs along with other app instances on the same EC2 instance</td></tr><tr><td>Placement Groups</td><td>Enables applications to get the full-bisection bandwidth and low-latency network performance required for tightly coupled, node-to-node communication. Placement Groups can only contain HVM instance and have other limitations described here: https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/using_cluster_computing.html</td></tr>
|
||||
</table>
|
||||
</table>
|
||||
Reference in New Issue
Block a user