Skip to content

Commit 3279591

Browse files
authored
Merge pull request #2270 from HubSpot/parallel_persist
Run persisters in parallel
2 parents e246c3c + 2859aae commit 3279591

5 files changed

Lines changed: 207 additions & 138 deletions

File tree

SingularityService/src/main/java/com/hubspot/singularity/config/SingularityConfiguration.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -261,6 +261,8 @@ public class SingularityConfiguration extends Configuration {
261261

262262
private long persistHistoryEverySeconds = TimeUnit.MINUTES.toSeconds(2);
263263

264+
private int historyPollerConcurrency = 5;
265+
264266
private int maxPendingImmediatePersists = 200;
265267

266268
private long reconcileAgentsEveryMinutes = TimeUnit.HOURS.toMinutes(1);
@@ -2112,6 +2114,14 @@ public void setRequestCacheTtl(int requestCacheTtlInSeconds) {
21122114
this.requestCacheTtlInSeconds = requestCacheTtlInSeconds;
21132115
}
21142116

2117+
public int getHistoryPollerConcurrency() {
2118+
return historyPollerConcurrency;
2119+
}
2120+
2121+
public void setHistoryPollerConcurrency(int historyPollerConcurrency) {
2122+
this.historyPollerConcurrency = historyPollerConcurrency;
2123+
}
2124+
21152125
public boolean skipPersistingTooLongTaskIds() {
21162126
return skipPersistingTooLongTaskIds;
21172127
}

SingularityService/src/main/java/com/hubspot/singularity/data/history/SingularityDeployHistoryPersister.java

Lines changed: 78 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,19 @@
66
import com.hubspot.singularity.SingularityDeleteResult;
77
import com.hubspot.singularity.SingularityDeployHistory;
88
import com.hubspot.singularity.SingularityDeployKey;
9+
import com.hubspot.singularity.SingularityManagedThreadPoolFactory;
910
import com.hubspot.singularity.SingularityRequestDeployState;
11+
import com.hubspot.singularity.async.CompletableFutures;
1012
import com.hubspot.singularity.config.SingularityConfiguration;
1113
import com.hubspot.singularity.data.DeployManager;
1214
import com.hubspot.singularity.mesos.SingularitySchedulerLock;
15+
import java.util.ArrayList;
1316
import java.util.Collections;
1417
import java.util.Comparator;
1518
import java.util.List;
1619
import java.util.Map;
1720
import java.util.Optional;
21+
import java.util.concurrent.CompletableFuture;
1822
import java.util.concurrent.TimeUnit;
1923
import java.util.concurrent.atomic.AtomicBoolean;
2024
import java.util.concurrent.atomic.AtomicLong;
@@ -42,12 +46,13 @@ public SingularityDeployHistoryPersister(
4246
DeployManager deployManager,
4347
HistoryManager historyManager,
4448
SingularitySchedulerLock schedulerLock,
49+
SingularityManagedThreadPoolFactory managedThreadPoolFactory,
4550
@Named(SingularityHistoryModule.PERSISTER_LOCK) ReentrantLock persisterLock,
4651
@Named(
4752
SingularityHistoryModule.LAST_DEPLOY_PERSISTER_SUCCESS
4853
) AtomicLong lastPersisterSuccess
4954
) {
50-
super(configuration, persisterLock, lastPersisterSuccess);
55+
super(configuration, persisterLock, lastPersisterSuccess, managedThreadPoolFactory);
5156
this.schedulerLock = schedulerLock;
5257
this.deployManager = deployManager;
5358
this.historyManager = historyManager;
@@ -72,75 +77,85 @@ public void runActionOnPoll() {
7277
Collectors.groupingBy(SingularityDeployKey::getRequestId, Collectors.toList())
7378
);
7479

80+
List<CompletableFuture<Void>> futures = new ArrayList<>();
7581
for (String requestId : deployManager
7682
.getAllRequestDeployStatesByRequestId()
7783
.keySet()) {
78-
LOG.debug("Checking deploy histories to persist for request {}", requestId);
79-
schedulerLock.runWithRequestLock(
80-
() -> {
81-
Optional<SingularityRequestDeployState> deployState = deployManager.getRequestDeployState(
82-
requestId
83-
);
84-
85-
if (!deployState.isPresent()) {
86-
LOG.warn("No request deploy state for {}", requestId);
87-
return;
88-
}
89-
90-
int i = 0;
91-
List<SingularityDeployHistory> deployHistories = allDeployIdsByRequest
92-
.getOrDefault(requestId, Collections.emptyList())
93-
.stream()
94-
.map(
95-
deployKey ->
96-
deployManager.getDeployHistory(
97-
deployKey.getRequestId(),
98-
deployKey.getDeployId(),
99-
true
100-
)
101-
)
102-
.filter(Optional::isPresent)
103-
.map(Optional::get)
104-
.sorted(
105-
Comparator
106-
.comparingLong(
107-
SingularityDeployHistory::getCreateTimestampForCalculatingHistoryAge
108-
)
109-
.reversed()
110-
)
111-
.collect(Collectors.toList());
112-
113-
for (SingularityDeployHistory deployHistory : deployHistories) {
114-
numTotal.increment();
115-
if (
116-
!shouldTransferDeploy(
117-
requestId,
118-
deployState.get(),
119-
deployHistory.getDeployMarker().getDeployId()
120-
)
121-
) {
122-
continue;
123-
}
124-
125-
LOG.info(
126-
"Persisting deploy {} for request {}",
127-
deployHistory.getDeployMarker().getDeployId(),
128-
requestId
84+
futures.add(
85+
CompletableFuture.runAsync(
86+
() -> {
87+
LOG.debug("Checking deploy histories to persist for request {}", requestId);
88+
schedulerLock.runWithRequestLock(
89+
() -> {
90+
Optional<SingularityRequestDeployState> deployState = deployManager.getRequestDeployState(
91+
requestId
92+
);
93+
94+
if (!deployState.isPresent()) {
95+
LOG.warn("No request deploy state for {}", requestId);
96+
return;
97+
}
98+
99+
int i = 0;
100+
List<SingularityDeployHistory> deployHistories = allDeployIdsByRequest
101+
.getOrDefault(requestId, Collections.emptyList())
102+
.stream()
103+
.map(
104+
deployKey ->
105+
deployManager.getDeployHistory(
106+
deployKey.getRequestId(),
107+
deployKey.getDeployId(),
108+
true
109+
)
110+
)
111+
.filter(Optional::isPresent)
112+
.map(Optional::get)
113+
.sorted(
114+
Comparator
115+
.comparingLong(
116+
SingularityDeployHistory::getCreateTimestampForCalculatingHistoryAge
117+
)
118+
.reversed()
119+
)
120+
.collect(Collectors.toList());
121+
122+
for (SingularityDeployHistory deployHistory : deployHistories) {
123+
numTotal.increment();
124+
if (
125+
!shouldTransferDeploy(
126+
requestId,
127+
deployState.get(),
128+
deployHistory.getDeployMarker().getDeployId()
129+
)
130+
) {
131+
continue;
132+
}
133+
134+
LOG.info(
135+
"Persisting deploy {} for request {}",
136+
deployHistory.getDeployMarker().getDeployId(),
137+
requestId
138+
);
139+
if (moveToHistoryOrCheckForPurge(deployHistory, i++)) {
140+
numTransferred.increment();
141+
} else {
142+
LOG.error("Deploy History Persister failed on {}", deployHistory);
143+
persisterSuccess.getAndSet(false);
144+
}
145+
}
146+
},
147+
requestId,
148+
getClass().getSimpleName(),
149+
SingularitySchedulerLock.Priority.LOW
129150
);
130-
if (moveToHistoryOrCheckForPurge(deployHistory, i++)) {
131-
numTransferred.increment();
132-
} else {
133-
LOG.error("Deploy History Persister failed on {}", deployHistory);
134-
persisterSuccess.getAndSet(false);
135-
}
136-
}
137-
},
138-
requestId,
139-
getClass().getSimpleName(),
140-
SingularitySchedulerLock.Priority.LOW
151+
},
152+
persisterExecutor
153+
)
141154
);
142155
}
143156

157+
CompletableFutures.allOf(futures).join();
158+
144159
LOG.info(
145160
"Transferred {} out of {} deploys in {}",
146161
numTransferred,

SingularityService/src/main/java/com/hubspot/singularity/data/history/SingularityHistoryPersister.java

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,11 @@
33
import com.hubspot.mesos.JavaUtils;
44
import com.hubspot.singularity.SingularityDeleteResult;
55
import com.hubspot.singularity.SingularityHistoryItem;
6+
import com.hubspot.singularity.SingularityManagedThreadPoolFactory;
67
import com.hubspot.singularity.config.SingularityConfiguration;
78
import com.hubspot.singularity.scheduler.SingularityLeaderOnlyPoller;
89
import java.util.Optional;
10+
import java.util.concurrent.ExecutorService;
911
import java.util.concurrent.TimeUnit;
1012
import java.util.concurrent.atomic.AtomicLong;
1113
import java.util.concurrent.locks.ReentrantLock;
@@ -20,18 +22,24 @@ public abstract class SingularityHistoryPersister<T extends SingularityHistoryIt
2022

2123
protected final SingularityConfiguration configuration;
2224
protected final ReentrantLock persisterLock;
23-
25+
protected final ExecutorService persisterExecutor;
2426
protected final AtomicLong lastPersisterSuccess;
2527

2628
public SingularityHistoryPersister(
2729
SingularityConfiguration configuration,
2830
ReentrantLock persisterLock,
29-
AtomicLong lastPersisterSuccess
31+
AtomicLong lastPersisterSuccess,
32+
SingularityManagedThreadPoolFactory managedThreadPoolFactory
3033
) {
3134
super(configuration.getPersistHistoryEverySeconds(), TimeUnit.SECONDS);
3235
this.configuration = configuration;
3336
this.persisterLock = persisterLock;
3437
this.lastPersisterSuccess = lastPersisterSuccess;
38+
this.persisterExecutor =
39+
managedThreadPoolFactory.get(
40+
"persister",
41+
configuration.getHistoryPollerConcurrency()
42+
);
3543
}
3644

3745
@Override

SingularityService/src/main/java/com/hubspot/singularity/data/history/SingularityRequestHistoryPersister.java

Lines changed: 34 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@
66
import com.hubspot.mesos.JavaUtils;
77
import com.hubspot.singularity.SingularityDeleteResult;
88
import com.hubspot.singularity.SingularityHistoryItem;
9+
import com.hubspot.singularity.SingularityManagedThreadPoolFactory;
910
import com.hubspot.singularity.SingularityRequestHistory;
11+
import com.hubspot.singularity.async.CompletableFutures;
1012
import com.hubspot.singularity.config.SingularityConfiguration;
1113
import com.hubspot.singularity.data.RequestManager;
1214
import com.hubspot.singularity.data.history.SingularityRequestHistoryPersister.SingularityRequestHistoryParent;
@@ -16,12 +18,14 @@
1618
import java.util.List;
1719
import java.util.Objects;
1820
import java.util.Optional;
21+
import java.util.concurrent.CompletableFuture;
1922
import java.util.concurrent.TimeUnit;
2023
import java.util.concurrent.atomic.AtomicBoolean;
2124
import java.util.concurrent.atomic.AtomicInteger;
2225
import java.util.concurrent.atomic.AtomicLong;
2326
import java.util.concurrent.locks.ReentrantLock;
2427
import javax.inject.Singleton;
28+
import javax.ws.rs.HEAD;
2529
import org.slf4j.Logger;
2630
import org.slf4j.LoggerFactory;
2731

@@ -42,12 +46,13 @@ public SingularityRequestHistoryPersister(
4246
RequestManager requestManager,
4347
HistoryManager historyManager,
4448
SingularitySchedulerLock lock,
49+
SingularityManagedThreadPoolFactory managedThreadPoolFactory,
4550
@Named(SingularityHistoryModule.PERSISTER_LOCK) ReentrantLock persisterLock,
4651
@Named(
4752
SingularityHistoryModule.LAST_REQUEST_PERSISTER_SUCCESS
4853
) AtomicLong lastPersisterSuccess
4954
) {
50-
super(configuration, persisterLock, lastPersisterSuccess);
55+
super(configuration, persisterLock, lastPersisterSuccess, managedThreadPoolFactory);
5156
this.requestManager = requestManager;
5257
this.historyManager = historyManager;
5358
this.lock = lock;
@@ -157,22 +162,38 @@ public void runActionOnPoll() {
157162
Collections.sort(requestHistoryParents, Collections.reverseOrder()); // createdAt descending
158163

159164
AtomicInteger i = new AtomicInteger();
165+
List<CompletableFuture<Void>> futures = new ArrayList<>();
160166
for (SingularityRequestHistoryParent requestHistoryParent : requestHistoryParents) {
161-
lock.runWithRequestLock(
162-
() -> {
163-
if (moveToHistoryOrCheckForPurge(requestHistoryParent, i.getAndIncrement())) {
164-
numHistoryTransferred.getAndAdd(requestHistoryParent.history.size());
165-
} else {
166-
LOG.error("Request History Persister failed on {}", requestHistoryParent);
167-
persisterSuccess.getAndSet(false);
168-
}
169-
},
170-
requestHistoryParent.requestId,
171-
"request history purger",
172-
SingularitySchedulerLock.Priority.LOW
167+
futures.add(
168+
CompletableFuture.runAsync(
169+
() ->
170+
lock.runWithRequestLock(
171+
() -> {
172+
if (
173+
moveToHistoryOrCheckForPurge(
174+
requestHistoryParent,
175+
i.getAndIncrement()
176+
)
177+
) {
178+
numHistoryTransferred.getAndAdd(requestHistoryParent.history.size());
179+
} else {
180+
LOG.error(
181+
"Request History Persister failed on {}",
182+
requestHistoryParent
183+
);
184+
persisterSuccess.getAndSet(false);
185+
}
186+
},
187+
requestHistoryParent.requestId,
188+
"request history purger",
189+
SingularitySchedulerLock.Priority.LOW
190+
),
191+
persisterExecutor
192+
)
173193
);
174194
}
175195

196+
CompletableFutures.allOf(futures).join();
176197
LOG.info(
177198
"Transferred {} history updates for {} requests in {}",
178199
numHistoryTransferred,

0 commit comments

Comments
 (0)