Skip to content

Commit 2edc851

Browse files
committed
gh-4879 Improve locking when closing aggregates
1 parent 49a6c51 commit 2edc851

File tree

2 files changed

+62
-12
lines changed

2 files changed

+62
-12
lines changed

stroom-proxy/stroom-proxy-app/src/main/java/stroom/proxy/app/handler/PreAggregator.java

Lines changed: 38 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
import java.util.List;
3939
import java.util.Map;
4040
import java.util.Objects;
41+
import java.util.Set;
4142
import java.util.concurrent.ConcurrentHashMap;
4243
import java.util.concurrent.atomic.AtomicInteger;
4344
import java.util.concurrent.atomic.AtomicReference;
@@ -452,15 +453,30 @@ private List<Part> calculateOverflowingParts(final FileGroup fileGroup) throws I
452453
return Collections.singletonList(new Part(partItems, partBytes, List.copyOf(partEntries)));
453454
}
454455

455-
private void closeAggregate(final FeedKey feedKey,
456-
final AggregateState aggregateState) {
457-
LOGGER.debug("Closing aggregate: {}", aggregateState);
456+
private boolean closeAggregate(final FeedKey feedKey,
457+
final AggregateState aggregateState) {
458+
LOGGER.debug(() -> LogUtil.message("closeAggregate() - feedKey: {}, {}, waiting for lock",
459+
feedKey, aggregateState));
458460
final Lock lock = feedKeyLock.get(feedKey);
459461
lock.lock();
460462
try {
461-
destination.accept(aggregateState.aggregateDir);
462-
aggregateStateMap.remove(feedKey);
463-
LOGGER.debug("Closed aggregate: {}", aggregateState);
463+
// Now we hold the feedKey lock, re-check the aggregateStateMap
464+
if (aggregateStateMap.containsKey(feedKey)) {
465+
LOGGER.debug(() -> LogUtil.message("closeAggregate() - feedKey: {}, {}, acquired lock",
466+
feedKey, aggregateState));
467+
468+
destination.accept(aggregateState.aggregateDir);
469+
aggregateStateMap.remove(feedKey);
470+
LOGGER.debug(() -> LogUtil.message("closeAggregate() - feedKey: {}, {}, closed aggregate",
471+
feedKey, aggregateState));
472+
return true;
473+
} else {
474+
LOGGER.debug(() -> LogUtil.message(
475+
"closeAggregate() - feedKey: {}, {}, " +
476+
"feedKey not in aggregateStateMap, another thread must have closed it.",
477+
feedKey, aggregateState));
478+
return false;
479+
}
464480
} finally {
465481
lock.unlock();
466482
}
@@ -570,13 +586,23 @@ private AggregateState createAggregate(final FeedKey feedKey) {
570586
*/
571587
private synchronized void closeOldAggregates() {
572588
final AtomicInteger count = new AtomicInteger();
573-
aggregateStateMap.forEach((feedKey, aggregateState) -> {
574-
if (aggregateState.isAggregateTooOld(aggregatorConfig)) {
575-
// Close the current aggregate.
576-
count.incrementAndGet();
577-
closeAggregate(feedKey, aggregateState);
589+
final Set<FeedKey> feedKeys = aggregateStateMap.keySet();
590+
for (final FeedKey feedKey : feedKeys) {
591+
// It's possible another thread may have removed it
592+
final AggregateState aggregateState = aggregateStateMap.get(feedKey);
593+
if (aggregateState != null
594+
&& aggregateState.isAggregateTooOld(aggregatorConfig)) {
595+
// Close the current aggregate, under a feedKey lock, so again,
596+
// another thread may beat us
597+
final boolean didClose = closeAggregate(feedKey, aggregateState);
598+
if (didClose) {
599+
count.incrementAndGet();
600+
} else {
601+
LOGGER.debug("closeAggregate() - feedKey: {}, aggregateState: {}, didn't close",
602+
feedKey, aggregateState);
603+
}
578604
}
579-
});
605+
}
580606
if (LOGGER.isDebugEnabled()) {
581607
if (count.get() > 0) {
582608
LOGGER.debug("closeOldAggregates() - closed {} old aggregates", count);
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
* Issue **#4879** : Proxy - Improve concurrency protection when closing aggregates.
2+
3+
4+
```sh
5+
# ********************************************************************************
6+
# Issue title: Proxy - exception when closing aggregates
7+
# Issue link: https://github.com/gchq/stroom/issues/4879
8+
# ********************************************************************************
9+
10+
# ONLY the top line will be included as a change entry in the CHANGELOG.
11+
# The entry should be in GitHub flavour markdown and should be written on a SINGLE
12+
# line with no hard breaks. You can have multiple change files for a single GitHub issue.
13+
# The entry should be written in the imperative mood, i.e. 'Fix nasty bug' rather than
14+
# 'Fixed nasty bug'.
15+
#
16+
# Examples of acceptable entries are:
17+
#
18+
#
19+
# * Issue **123** : Fix bug with an associated GitHub issue in this repository
20+
#
21+
# * Issue **namespace/other-repo#456** : Fix bug with an associated GitHub issue in another repository
22+
#
23+
# * Fix bug with no associated GitHub issue.
24+
```

0 commit comments

Comments
 (0)