Merge branch 'main' into bug6239

This commit is contained in:
masha.kashirina
2026-05-15 20:35:50 +02:00
13 changed files with 181 additions and 129 deletions
@@ -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,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;
}
}
@@ -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
@@ -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());
}
@@ -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);
}
}
@@ -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);
}
@@ -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;
}
}
@@ -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();
@@ -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
@@ -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
View File
@@ -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.
+1 -1
View File
@@ -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>