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

Commit 02311b8

Browse files
author
Rob Rudin
committed
#140 Deleting a database defaults to deleting forests and replicas first
1 parent b14147e commit 02311b8

File tree

12 files changed

+138
-82
lines changed

12 files changed

+138
-82
lines changed

src/main/java/com/marklogic/appdeployer/AppConfig.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,9 @@ public class AppConfig {
9797
// Allows for creating a triggers database without a config file for one
9898
private boolean createTriggersDatabase = true;
9999

100+
// Controls whether forests are deleted when a database is deleted
101+
private boolean deleteForests = true;
102+
100103
// Controls whether replicas are deleted or not when undeploying a database
101104
private boolean deleteReplicas = true;
102105

@@ -717,4 +720,12 @@ public boolean isDeleteReplicas() {
717720
public void setDeleteReplicas(boolean deleteReplicas) {
718721
this.deleteReplicas = deleteReplicas;
719722
}
723+
724+
public boolean isDeleteForests() {
725+
return deleteForests;
726+
}
727+
728+
public void setDeleteForests(boolean deleteForests) {
729+
this.deleteForests = deleteForests;
730+
}
720731
}

src/main/java/com/marklogic/appdeployer/DefaultAppConfigFactory.java

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -144,8 +144,20 @@ public AppConfig newAppConfig() {
144144

145145
/**
146146
* When undo is invoked on DeployDatabaseCommand (such as via mlUndeploy in ml-gradle), this controls whether
147-
* or not replicas are deleted first. Most of the time, you want this set to true (the default) as otherwise,
148-
* the database can't be deleted and the Management REST API will throw an error.
147+
* or not forests are deleted, or just their configuration is deleted. If mlDeleteReplicas is set to true, this
148+
* has no impact - currently, the forests and their replicas will be deleted for efficiency reasons (results in
149+
* fewer calls to the Management REST API.
150+
*/
151+
prop = getProperty("mlDeleteForests");
152+
if (prop != null) {
153+
logger.info("Delete forests when a database is deleted: " + prop);
154+
c.setDeleteForests(Boolean.parseBoolean(prop));
155+
}
156+
157+
/**
158+
* When undo is invoked on DeployDatabaseCommand (such as via mlUndeploy in ml-gradle), this controls whether
159+
* primary forests and their replicas are deleted first. Most of the time, you want this set to true
160+
* (the default) as otherwise, the database can't be deleted and the Management REST API will throw an error.
149161
*/
150162
prop = getProperty("mlDeleteReplicas");
151163
if (prop != null) {

src/main/java/com/marklogic/appdeployer/command/databases/DeployContentDatabasesCommand.java

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,5 @@
11
package com.marklogic.appdeployer.command.databases;
22

3-
import java.io.File;
4-
import java.util.List;
5-
63
import com.fasterxml.jackson.databind.JsonNode;
74
import com.marklogic.appdeployer.AppConfig;
85
import com.marklogic.appdeployer.command.CommandContext;
@@ -11,6 +8,9 @@
118
import com.marklogic.mgmt.databases.DatabaseManager;
129
import com.marklogic.rest.util.JsonNodeUtil;
1310

11+
import java.io.File;
12+
import java.util.List;
13+
1414
/**
1515
* For ease of use, this command handles creating forests the the content database, either based on a file in the
1616
* forests directory, or based on the default payload in the DeployForestsCommand class. This allows a developer to only
@@ -70,18 +70,15 @@ public void undo(CommandContext context) {
7070
String payload = node.toString();
7171
String json = tokenReplacer.replaceTokens(payload, appConfig, false);
7272

73-
DatabaseManager dbMgr = new DatabaseManager(context.getManageClient());
74-
dbMgr.setForestDelete(getForestDelete());
73+
DatabaseManager dbMgr = newDatabaseManageForDeleting(context);
7574
dbMgr.delete(json);
76-
7775
if (appConfig.isTestPortSet()) {
7876
json = tokenReplacer.replaceTokens(payload, appConfig, true);
7977
dbMgr.delete(json);
8078
}
8179
} else {
8280
// Try to delete the content database if it exists
83-
DatabaseManager dbMgr = new DatabaseManager(context.getManageClient());
84-
dbMgr.setForestDelete(getForestDelete());
81+
DatabaseManager dbMgr = newDatabaseManageForDeleting(context);
8582
dbMgr.deleteByName(appConfig.getContentDatabaseName());
8683

8784
if (appConfig.isTestPortSet()) {

src/main/java/com/marklogic/appdeployer/command/databases/DeployDatabaseCommand.java

Lines changed: 24 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
package com.marklogic.appdeployer.command.databases;
22

3-
import java.io.File;
4-
import java.util.Map;
5-
3+
import com.marklogic.appdeployer.AppConfig;
64
import com.marklogic.appdeployer.command.AbstractCommand;
75
import com.marklogic.appdeployer.command.CommandContext;
86
import com.marklogic.appdeployer.command.SortOrderConstants;
@@ -11,6 +9,10 @@
119
import com.marklogic.mgmt.PayloadParser;
1210
import com.marklogic.mgmt.SaveReceipt;
1311
import com.marklogic.mgmt.databases.DatabaseManager;
12+
import com.marklogic.mgmt.forests.ForestManager;
13+
14+
import java.io.File;
15+
import java.util.Map;
1416

1517
/**
1618
* Can be used for creating any kind of database with any sorts of forests. Specifying a config file for the database or
@@ -51,14 +53,6 @@ public class DeployDatabaseCommand extends AbstractCommand implements UndoableCo
5153
*/
5254
private boolean createForestsOnEachHost = true;
5355

54-
/**
55-
* Applied when the database is deleted - see
56-
* http://docs.marklogic.com/REST/DELETE/manage/v2/databases/[id-or-name].
57-
*/
58-
private String forestDelete = "data";
59-
60-
private boolean deleteReplicas = true;
61-
6256
private int undoSortOrder;
6357

6458
public DeployDatabaseCommand() {
@@ -95,13 +89,28 @@ public void execute(CommandContext context) {
9589
public void undo(CommandContext context) {
9690
String payload = buildPayload(context);
9791
if (payload != null) {
98-
DatabaseManager dbMgr = new DatabaseManager(context.getManageClient());
99-
dbMgr.setForestDelete(forestDelete);
100-
dbMgr.setDeleteReplicas(deleteReplicas);
101-
dbMgr.delete(payload);
92+
newDatabaseManageForDeleting(context).delete(payload);
10293
}
10394
}
10495

96+
/**
97+
* Configures the DatabaseManager in terms of how it deletes forests based on properties in the AppConfig instance
98+
* in the CommandContext.
99+
*
100+
* @param context
101+
* @return
102+
*/
103+
protected DatabaseManager newDatabaseManageForDeleting(CommandContext context) {
104+
DatabaseManager dbMgr = new DatabaseManager(context.getManageClient());
105+
dbMgr.setForestDelete(getForestDeleteLevel(context.getAppConfig()));
106+
dbMgr.setDeleteReplicas(context.getAppConfig().isDeleteReplicas());
107+
return dbMgr;
108+
}
109+
110+
protected String getForestDeleteLevel(AppConfig appConfig) {
111+
return appConfig.isDeleteForests() ? DatabaseManager.DELETE_FOREST_DATA : DatabaseManager.DELETE_FOREST_CONFIGURATION;
112+
}
113+
105114
/**
106115
* Builds the XML or JSON payload for this command, based on the given CommandContext.
107116
*
@@ -188,14 +197,6 @@ protected String buildDefaultDatabasePayload(CommandContext context) {
188197
return format("{\"database-name\": \"%s\"}", databaseName);
189198
}
190199

191-
public String getForestDelete() {
192-
return forestDelete;
193-
}
194-
195-
public void setForestDelete(String forestDelete) {
196-
this.forestDelete = forestDelete;
197-
}
198-
199200
public int getForestsPerHost() {
200201
return forestsPerHost;
201202
}
@@ -247,12 +248,4 @@ public boolean isCreateForestsOnEachHost() {
247248
public void setCreateForestsOnEachHost(boolean createForestsOnEachHost) {
248249
this.createForestsOnEachHost = createForestsOnEachHost;
249250
}
250-
251-
public boolean isDeleteReplicas() {
252-
return deleteReplicas;
253-
}
254-
255-
public void setDeleteReplicas(boolean deleteReplicas) {
256-
this.deleteReplicas = deleteReplicas;
257-
}
258251
}

src/main/java/com/marklogic/mgmt/AbstractResourceManager.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
11
package com.marklogic.mgmt;
22

3-
import org.springframework.http.ResponseEntity;
4-
53
import com.marklogic.rest.util.Fragment;
64
import com.marklogic.rest.util.ResourcesFragment;
5+
import org.springframework.http.ResponseEntity;
76

87
/**
98
* This class makes a number of assumptions in order to simplify the implementation of common operations for a MarkLogic

src/main/java/com/marklogic/mgmt/databases/DatabaseManager.java

Lines changed: 42 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,18 @@
11
package com.marklogic.mgmt.databases;
22

3-
import java.util.List;
4-
53
import com.marklogic.mgmt.AbstractResourceManager;
64
import com.marklogic.mgmt.ManageClient;
7-
import com.marklogic.mgmt.ManageConfig;
85
import com.marklogic.mgmt.forests.ForestManager;
9-
import com.marklogic.mgmt.forests.ForestStatus;
106
import com.marklogic.rest.util.Fragment;
117

8+
import java.util.List;
9+
1210
public class DatabaseManager extends AbstractResourceManager {
1311

14-
private String forestDelete = "data";
12+
public final static String DELETE_FOREST_DATA = "data";
13+
public final static String DELETE_FOREST_CONFIGURATION = "configuration";
14+
15+
private String forestDelete = DELETE_FOREST_DATA;
1516
private boolean deleteReplicas = true;
1617

1718
public DatabaseManager(ManageClient manageClient) {
@@ -60,17 +61,33 @@ public void deleteByName(String databaseName) {
6061
delete(json);
6162
}
6263

64+
/**
65+
* Use this to delete all of a database's forests and their replicas, but leave the database in place. This seems
66+
* to be the safest way to ensure a database can be deleted.
67+
*
68+
* @param databaseIdOrName
69+
*/
70+
public void deleteForestsAndReplicas(String databaseIdOrName) {
71+
List<String> primaryForestIds = getPrimaryForestIds(databaseIdOrName);
72+
detachForests(databaseIdOrName);
73+
ForestManager forestManager = new ForestManager(getManageClient());
74+
for (String forestId : primaryForestIds) {
75+
forestManager.delete(forestId, ForestManager.DELETE_LEVEL_FULL, ForestManager.REPLICAS_DELETE);
76+
}
77+
}
78+
79+
public void detachForests(String databaseIdOrName) {
80+
logger.info("Detaching forests from database: " + databaseIdOrName);
81+
save(format("{\"database-name\":\"%s\", \"forest\":[]}", databaseIdOrName));
82+
logger.info("Finished detaching forests from database: " + databaseIdOrName);
83+
}
84+
6385
@Override
6486
protected void beforeDelete(String resourceId, String path, String... resourceUrlParams) {
6587
if (deleteReplicas) {
66-
logger.info("Deleting any replicas that exist for database: " + resourceId);
67-
ForestManager forestManager = new ForestManager(getManageClient());
68-
for (String forestId : getPrimaryForestIds(resourceId)) {
69-
ForestStatus status = forestManager.getForestStatus(forestId);
70-
if (status.isPrimary() && status.hasReplicas()) {
71-
forestManager.deleteReplicas(forestId);
72-
}
73-
}
88+
logger.info("Deleting forests and replicas for database: " + resourceId);
89+
deleteForestsAndReplicas(resourceId);
90+
logger.info("Finished deleting forests and replicas for database: " + resourceId);
7491
}
7592
}
7693

@@ -102,7 +119,13 @@ public List<String> getPrimaryForestIds(String databaseNameOrId) {
102119
return getPropertiesAsXml(databaseNameOrId).getElementValues("/node()/m:forests/m:forest");
103120
}
104121

105-
public void deleteReplicaForests(String databaseNameOrId) {
122+
/**
123+
* Delete all replicas for the primary forests for the given database, but don't delete the database or the
124+
* primary forests.
125+
*
126+
* @param databaseNameOrId
127+
*/
128+
public void deleteReplicaForests(String databaseNameOrId) {
106129
logger.info(format("Deleting replica forests (if any exist) for database %s", databaseNameOrId));
107130
ForestManager mgr = new ForestManager(getManageClient());
108131
for (String forestId : getPrimaryForestIds(databaseNameOrId)) {
@@ -133,7 +156,11 @@ public void setForestDelete(String forestDelete) {
133156
this.forestDelete = forestDelete;
134157
}
135158

136-
public boolean isDeleteReplicas() {
159+
public String getForestDelete() {
160+
return forestDelete;
161+
}
162+
163+
public boolean isDeleteReplicas() {
137164
return deleteReplicas;
138165
}
139166

src/main/java/com/marklogic/mgmt/forests/ForestManager.java

Lines changed: 24 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
11
package com.marklogic.mgmt.forests;
22

3-
import java.util.HashMap;
4-
import java.util.Iterator;
5-
import java.util.List;
6-
import java.util.Map;
7-
83
import com.fasterxml.jackson.databind.JsonNode;
94
import com.marklogic.mgmt.AbstractResourceManager;
105
import com.marklogic.mgmt.ManageClient;
116
import com.marklogic.rest.util.Fragment;
127

8+
import java.util.HashMap;
9+
import java.util.Iterator;
10+
import java.util.List;
11+
import java.util.Map;
12+
1313
/**
1414
* Provides methods wrapping /manage/v2/forests endpoints.
1515
*
@@ -19,10 +19,15 @@
1919
*/
2020
public class ForestManager extends AbstractResourceManager {
2121

22-
public final static String DELETE_FULL = "full";
23-
public final static String DELETE_CONFIG = "config-only";
22+
// Parameter values for the "level" parameter when deleting a forest
23+
public final static String DELETE_LEVEL_FULL = "full";
24+
public final static String DELETE_LEVEL_CONFIG_ONLY = "config-only";
2425

25-
private String deleteLevel = DELETE_FULL;
26+
// Parameter values for the "replicas" parameter when deleting a forest
27+
public final static String REPLICAS_DETACH = "detach";
28+
public final static String REPLICAS_DELETE = "delete";
29+
30+
private String deleteLevel = DELETE_LEVEL_FULL;
2631

2732
public ForestManager(ManageClient client) {
2833
super(client);
@@ -40,11 +45,20 @@ public void createJsonForestWithName(String name, String host) {
4045
}
4146

4247
public void delete(String nameOrId, String level) {
48+
delete(nameOrId, level, REPLICAS_DELETE);
49+
}
50+
51+
/**
52+
* @param nameOrId Name or ID of forest to delete
53+
* @param level either "full" or "config-only"
54+
* @param replicas either "detach" or "delete"
55+
*/
56+
public void delete(String nameOrId, String level, String replicas) {
4357
if (!forestExists(nameOrId)) {
4458
logger.info(format("Could not find forest with name or ID: %s, so not deleting", nameOrId));
4559
} else {
4660
logger.info(format("Deleting forest %s", nameOrId));
47-
getManageClient().delete(format("/manage/v2/forests/%s?level=%s", nameOrId, level));
61+
getManageClient().delete(format("/manage/v2/forests/%s?level=%s&replicas=%s", nameOrId, level, replicas));
4862
logger.info(format("Deleted forest %s", nameOrId));
4963
}
5064
}
@@ -158,7 +172,7 @@ public void deleteReplicas(String forestIdOrName) {
158172
}
159173
setReplicasToNone(forestIdOrName);
160174
for (String id : replicaIds) {
161-
delete(id, DELETE_FULL);
175+
delete(id, DELETE_LEVEL_FULL);
162176
}
163177
}
164178

src/test/java/com/marklogic/appdeployer/DefaultAppConfigFactoryTest.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ public void allProperties() {
5858
p.setProperty("mlSimpleSsl", "anyvalue");
5959
p.setProperty("mlContentDatabaseName", "my-content-db");
6060
p.setProperty("mlModulesDatabaseName", "my-modules");
61+
p.setProperty("mlDeleteForests", "false");
6162
p.setProperty("mlDeleteReplicas", "false");
6263
p.setProperty("mlGroupName", "other-group");
6364
p.setProperty("mlAppServicesPort", "8123");
@@ -94,6 +95,7 @@ public void allProperties() {
9495
assertNotNull(config.getRestSslHostnameVerifier());
9596
assertEquals("my-content-db", config.getContentDatabaseName());
9697
assertEquals("my-modules", config.getModulesDatabaseName());
98+
assertFalse(config.isDeleteForests());
9799
assertFalse(config.isDeleteReplicas());
98100
assertEquals("other-group", config.getGroupName());
99101
assertEquals((Integer) 8123, config.getAppServicesPort());

src/test/java/com/marklogic/appdeployer/command/es/GenerateModelArtifactsTest.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,12 @@
88
import com.marklogic.appdeployer.command.restapis.DeployRestApiServersCommand;
99
import com.marklogic.appdeployer.command.schemas.LoadSchemasCommand;
1010
import org.junit.After;
11+
import org.junit.Ignore;
1112
import org.junit.Test;
1213

1314
import java.io.File;
1415

16+
@Ignore("Ignoring until ML9 is released")
1517
public class GenerateModelArtifactsTest extends AbstractAppDeployerTest {
1618

1719
@After

src/test/java/com/marklogic/appdeployer/command/modules/LoadModulesTest.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,8 +48,8 @@ public void loadModulesWithStaticCheck() {
4848
fail("An error should have been thrown because /ext/bad.xqy failed static check");
4949
} catch (Exception ex) {
5050
String message = ex.getMessage();
51-
assertTrue("Loading modules with 2.11.0 of ml-javaclient-util defaults to bulk loading, so static checking should as well",
52-
message.contains("Bulk static check failure"));
51+
assertTrue("Loading modules with 2.11.0 of ml-javaclient-util defaults to bulk loading, so static checking should as well; message: " + message,
52+
message.contains("Unexpected token syntax error"));
5353
assertTrue(message.contains("in /ext/bad.xqy, on line 2"));
5454
}
5555
}

0 commit comments

Comments
 (0)