Skip to content
This repository was archived by the owner on Sep 16, 2024. It is now read-only.

Commit 5da2fb4

Browse files
authored
Merge pull request #487 from marklogic/release/4.5.3
Release/4.5.3
2 parents 7eb4d26 + 7a96ead commit 5da2fb4

File tree

8 files changed

+383
-210
lines changed

8 files changed

+383
-210
lines changed

CODEOWNERS

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# Lines starting with '#' are comments.
2+
# Each line is a file pattern followed by one or more owners.
3+
4+
# These owners will be the default owners for everything in the repo.
5+
* @anu3990 @billfarber @rjrudin

build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ plugins {
88
}
99

1010
group = "com.marklogic"
11-
version = "4.5.2"
11+
version = "4.5.3"
1212

1313
java {
1414
sourceCompatibility = 1.8

pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ It is not intended to be used to build this project.
1212
<modelVersion>4.0.0</modelVersion>
1313
<groupId>com.marklogic</groupId>
1414
<artifactId>ml-app-deployer</artifactId>
15-
<version>4.5.2</version>
15+
<version>4.5.3</version>
1616
<name>com.marklogic:ml-app-deployer</name>
1717
<description>Java client for the MarkLogic REST Management API and for deploying applications to MarkLogic</description>
1818
<url>https://github.com/marklogic/ml-app-deployer</url>

src/main/java/com/marklogic/appdeployer/command/CommandContext.java

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,13 @@
2020
import com.marklogic.mgmt.admin.AdminManager;
2121
import com.marklogic.mgmt.api.configuration.Configuration;
2222
import com.marklogic.mgmt.api.configuration.Configurations;
23+
import com.marklogic.mgmt.api.forest.Forest;
24+
import com.marklogic.mgmt.resource.forests.ForestManager;
25+
import org.slf4j.Logger;
26+
import org.slf4j.LoggerFactory;
2327

2428
import java.util.HashMap;
29+
import java.util.List;
2530
import java.util.Map;
2631

2732
/**
@@ -61,6 +66,36 @@ public void addCmaConfigurationToCombinedRequest(Configuration configuration) {
6166
}
6267
}
6368

69+
/**
70+
* Added to greatly speed up performance when getting details about all the existing primary forests for each
71+
* database in the cluster. In the event that anything fails while getting the forest details, the map won't be
72+
* added to the context and any code expecting to use the map will have to fall back to using /manage/v2.
73+
*
74+
* @since 4.5.3
75+
*/
76+
public Map<String, List<Forest>> getMapOfPrimaryForests() {
77+
if (!appConfig.getCmaConfig().isDeployForests()) {
78+
return null;
79+
}
80+
final String key = "ml-app-deployer-mapOfPrimaryForests";
81+
if (contextMap.containsKey(key)) {
82+
return (Map<String, List<Forest>>) contextMap.get(key);
83+
}
84+
Logger logger = LoggerFactory.getLogger(getClass());
85+
try {
86+
logger.info("Retrieving all forest details via CMA");
87+
long start = System.currentTimeMillis();
88+
Map<String, List<Forest>> mapOfPrimaryForests = new ForestManager(manageClient).getMapOfPrimaryForests();
89+
logger.info("Finished retrieving all forests details via CMA; duration: " + (System.currentTimeMillis() - start));
90+
contextMap.put(key, mapOfPrimaryForests);
91+
return mapOfPrimaryForests;
92+
} catch (Exception ex) {
93+
logger.warn("Unable to retrieve all forest details, cause: " + ex.getMessage() + "; will fall back to " +
94+
"using /manage/v2 when needed for getting details for a forest.");
95+
return null;
96+
}
97+
}
98+
6499
public Configurations getCombinedCmaRequest() {
65100
return (Configurations) contextMap.get(COMBINED_CMA_REQUEST_KEY);
66101
}

src/main/java/com/marklogic/appdeployer/command/forests/ConfigureForestReplicasCommand.java

Lines changed: 66 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@
1919
import com.marklogic.appdeployer.command.CommandContext;
2020
import com.marklogic.appdeployer.command.SortOrderConstants;
2121
import com.marklogic.mgmt.api.API;
22+
import com.marklogic.mgmt.api.configuration.Configuration;
23+
import com.marklogic.mgmt.api.configuration.Configurations;
2224
import com.marklogic.mgmt.api.forest.Forest;
2325
import com.marklogic.mgmt.mapper.DefaultResourceMapper;
2426
import com.marklogic.mgmt.mapper.ResourceMapper;
@@ -28,7 +30,11 @@
2830
import com.marklogic.mgmt.resource.groups.GroupManager;
2931
import com.marklogic.mgmt.resource.hosts.HostManager;
3032

31-
import java.util.*;
33+
import java.util.ArrayList;
34+
import java.util.HashMap;
35+
import java.util.List;
36+
import java.util.Map;
37+
import java.util.stream.Collectors;
3238

3339
/**
3440
* Command for configuring - i.e. creating and setting - replica forests for existing databases.
@@ -131,19 +137,37 @@ protected void configureDatabaseReplicaForests(String databaseName, int replicaC
131137
List<String> dataDirectories = forestBuilder.determineDataDirectories(databaseName, context.getAppConfig());
132138
forestBuilder.addReplicasToForests(forestsNeedingReplicas, forestPlan, context.getAppConfig(), dataDirectories);
133139

134-
// TODO Use CMA here in the future? Need to test to see if a forest name + replicas are allowable
135-
ForestManager forestManager = new ForestManager(context.getManageClient());
136-
for (Forest forest : forestsNeedingReplicas) {
137-
final String forestName = forest.getForestName();
138-
140+
List<Forest> forestsWithOnlyReplicas = forestsNeedingReplicas.stream().map(forest -> {
139141
Forest forestWithOnlyReplicas = new Forest();
142+
forestWithOnlyReplicas.setForestName(forest.getForestName());
140143
forestWithOnlyReplicas.setForestReplica(forest.getForestReplica());
141-
String json = forestWithOnlyReplicas.getJson();
144+
return forestWithOnlyReplicas;
145+
}).collect(Collectors.toList());
146+
147+
// As of 4.5.3, try CMA first so that this can be done in a single request instead of one request per forest.
148+
if (context.getAppConfig().getCmaConfig().isDeployForests()) {
149+
try {
150+
Configuration config = new Configuration();
151+
forestsWithOnlyReplicas.forEach(forest -> {
152+
config.addForest(forest.toObjectNode());
153+
});
154+
new Configurations(config).submit(context.getManageClient());
155+
return;
156+
} catch (Exception ex) {
157+
logger.warn("Unable to create forest replicas via CMA; cause: " + ex.getMessage() + "; will " +
158+
"fall back to using /manage/v2.");
159+
}
160+
}
142161

162+
// If we get here, either CMA usage is disabled or an error occurred with CMA. Just use /manage/v2 to submit
163+
// each forest one-by-one.
164+
ForestManager forestManager = new ForestManager(context.getManageClient());
165+
forestsWithOnlyReplicas.forEach(forest -> {
166+
String forestName = forest.getForestName();
143167
logger.info(format("Creating forest replicas for primary forest %s", forestName));
144-
context.getManageClient().putJson(forestManager.getPropertiesPath(forestName), json);
168+
context.getManageClient().putJson(forestManager.getPropertiesPath(forestName), forest.getJson());
145169
logger.info(format("Finished creating forest replicas for primary forest %s", forestName));
146-
}
170+
});
147171
}
148172

149173
/**
@@ -161,22 +185,41 @@ protected List<Forest> determineForestsNeedingReplicas(String databaseName, Comm
161185
ResourceMapper resourceMapper = new DefaultResourceMapper(api);
162186

163187
List<Forest> forestsNeedingReplicas = new ArrayList<>();
188+
Map<String, List<Forest>> mapOfPrimaryForests = context.getMapOfPrimaryForests();
164189

165-
for (String forestName : dbMgr.getForestNames(databaseName)) {
166-
logger.info(format("Checking the status of forest %s to determine if it is a primary forest and whether or not it has replicas already.", forestName));
167-
ForestStatus status = forestManager.getForestStatus(forestName);
168-
if (!status.isPrimary()) {
169-
logger.info(format("Forest %s is not a primary forest, so not configuring replica forests", forestName));
170-
continue;
171-
}
172-
if (status.hasReplicas()) {
173-
logger.info(format("Forest %s already has replicas, so not configuring replica forests", forestName));
174-
continue;
175-
}
190+
/**
191+
* In both blocks below, a forest is not included if it already has replicas. This logic dates back to 2015,
192+
* and is likely due to uncertainty over the various scenarios that can occur if a forest does already have
193+
* replicas. At least as of August 2023, MarkLogic recommends a single replica per forest. Given that no users
194+
* have asked for this check to not be performed and based on MarkLogic's recommendation, it seems reasonable
195+
* to leave this check in for now. However, some ad hoc testing has indicated that this check is unnecessary
196+
* and that it appears to safe to vary the number of replicas per forest. So it likely would be beneficial to
197+
* remove this check at some point.
198+
*/
199+
if (mapOfPrimaryForests != null && mapOfPrimaryForests.containsKey(databaseName)) {
200+
mapOfPrimaryForests.get(databaseName).forEach(forest -> {
201+
boolean forestHasReplicasAlready = forest.getForestReplica() != null && !forest.getForestReplica().isEmpty();
202+
if (!forestHasReplicasAlready) {
203+
forestsNeedingReplicas.add(forest);
204+
}
205+
});
206+
} else {
207+
for (String forestName : dbMgr.getForestNames(databaseName)) {
208+
logger.info(format("Checking the status of forest %s to determine if it is a primary forest and whether or not it has replicas already.", forestName));
209+
ForestStatus status = forestManager.getForestStatus(forestName);
210+
if (!status.isPrimary()) {
211+
logger.info(format("Forest %s is not a primary forest, so not configuring replica forests", forestName));
212+
continue;
213+
}
214+
if (status.hasReplicas()) {
215+
logger.info(format("Forest %s already has replicas, so not configuring replica forests", forestName));
216+
continue;
217+
}
176218

177-
String forestJson = forestManager.getPropertiesAsJson(forestName);
178-
Forest forest = resourceMapper.readResource(forestJson, Forest.class);
179-
forestsNeedingReplicas.add(forest);
219+
String forestJson = forestManager.getPropertiesAsJson(forestName);
220+
Forest forest = resourceMapper.readResource(forestJson, Forest.class);
221+
forestsNeedingReplicas.add(forest);
222+
}
180223
}
181224

182225
return forestsNeedingReplicas;

src/main/java/com/marklogic/appdeployer/command/forests/DeployForestsCommand.java

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -128,12 +128,23 @@ protected void createForestsViaForestEndpoint(CommandContext context, List<Fores
128128
*/
129129
public List<Forest> buildForests(CommandContext context, boolean includeReplicas) {
130130
// Need to know what primary forests exist already in case more need to be added, or a new host has been added
131-
List<Forest> existingPrimaryForests = getExistingPrimaryForests(context, this.databaseName);
131+
List<Forest> existingPrimaryForests = null;
132+
133+
// As of 4.5.3, if CMA is enabled, then the context should contain a map of all the forests for each database
134+
// being deployed. If it's not there, then /manage/v2 will be used instead.
135+
Map<String, List<Forest>> mapOfPrimaryForests = context.getMapOfPrimaryForests();
136+
if (mapOfPrimaryForests != null && mapOfPrimaryForests.containsKey(this.databaseName)) {
137+
existingPrimaryForests = mapOfPrimaryForests.get(this.databaseName);
138+
}
139+
140+
if (existingPrimaryForests == null) {
141+
existingPrimaryForests = getExistingPrimaryForests(context, this.databaseName);
142+
}
143+
132144
return buildForests(context, includeReplicas, existingPrimaryForests);
133145
}
134146

135147
/**
136-
*
137148
* @param context
138149
* @param includeReplicas
139150
* @param existingPrimaryForests
@@ -163,6 +174,14 @@ protected List<Forest> buildForests(CommandContext context, boolean includeRepli
163174
return forestBuilder.buildForests(forestPlan, context.getAppConfig());
164175
}
165176

177+
/**
178+
* @param context
179+
* @param databaseName
180+
* @return
181+
* @deprecated in 4.5.3, as getting forest details one at a time can be very slow for applications with a large
182+
* number of forests.
183+
*/
184+
@Deprecated
166185
protected List<Forest> getExistingPrimaryForests(CommandContext context, String databaseName) {
167186
List<String> forestIds = new DatabaseManager(context.getManageClient()).getPrimaryForestIds(databaseName);
168187
ForestManager forestMgr = new ForestManager(context.getManageClient());
@@ -259,6 +278,7 @@ public boolean isCreateForestsOnEachHost() {
259278

260279
/**
261280
* Use appConfig.setDatabasesWithForestsOnOneHost
281+
*
262282
* @param createForestsOnEachHost
263283
*/
264284
@Deprecated

0 commit comments

Comments
 (0)