Skip to content

Commit

Permalink
Allow removal of configuration properties
Browse files Browse the repository at this point in the history
Signed-off-by: Clement de Groc <[email protected]>
  • Loading branch information
cdegroc authored and porunov committed Oct 20, 2023
1 parent b61d2b3 commit ab1409b
Show file tree
Hide file tree
Showing 8 changed files with 219 additions and 4 deletions.
17 changes: 17 additions & 0 deletions docs/changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,23 @@ GraphBinary is now used as the default MessageSerializer.
We are dropping support for old serialization format of JanusGraph predicates. The old predicates serialization format is only used by client older than 0.6.
The change only affects GraphSON.

##### Allow removal of configuration keys

Users can now remove configuration keys in the ConfiguredGraphFactory's configuration:

```groovy
ConfiguredGraphFactory.removeConfiguration("<graph_name>", Collections.singleton("<config_key>"))
```

Or the global configuration:
```groovy
mgmt = graph.openManagement()
mgmt.remove("<config_key>")
mgmt.commit()
```

Note that the above commands should be used with care. They cannot be used to drop an external index backend if it has mixed indexes for instance.

##### New index management

The index management has received an overhaul which enables proper index removal.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,14 +29,18 @@
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;

import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import javax.script.Bindings;
import javax.script.SimpleBindings;

import static org.janusgraph.graphdb.configuration.GraphDatabaseConfiguration.CONNECTION_TIMEOUT;
import static org.janusgraph.graphdb.configuration.GraphDatabaseConfiguration.GRAPH_NAME;
import static org.janusgraph.graphdb.configuration.GraphDatabaseConfiguration.STORAGE_BACKEND;
import static org.janusgraph.graphdb.configuration.GraphDatabaseConfiguration.STORAGE_HOSTS;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;
Expand Down Expand Up @@ -247,14 +251,14 @@ public void updateConfigurationShouldRemoveGraphFromCache() throws Exception {

// string-delimited hosts are recognized
final Map<String, Object> map = graphConfig.getMap();
map.put("storage.hostname", "localhost,localhost");
map.put(STORAGE_HOSTS.toStringWithoutRoot(), "localhost,localhost");
ConfiguredGraphFactory.updateConfiguration(graphName, new MapConfiguration(map));
assertNull(gm.getGraph(graphName));
assertNotNull(ConfiguredGraphFactory.open(graphName));

// bogus backend will prevent the graph from being opened
final Map<String, Object> map2 = graphConfig.getMap();
map2.put("storage.backend", "bogusBackend");
map2.put(STORAGE_BACKEND.toStringWithoutRoot(), "bogusBackend");
ConfiguredGraphFactory.updateConfiguration(graphName, new MapConfiguration(map2));
assertNull(gm.getGraph(graphName));
// we should throw an error since the config has been updated and we are attempting
Expand Down Expand Up @@ -286,6 +290,85 @@ public void removeConfigurationShouldRemoveGraphFromCache() throws Exception {
}
}

@Test
public void removeConfigurationFailsToRemovingGraphNameKey() throws Exception {
final MapConfiguration graphConfig = getGraphConfig();
final String graphName = graphConfig.getString(GRAPH_NAME.toStringWithoutRoot());

try {
ConfiguredGraphFactory.createConfiguration(graphConfig);
final StandardJanusGraph graph = (StandardJanusGraph) ConfiguredGraphFactory.open(graphName);
assertNotNull(graph);

// Set cannot contain the "graph.graphname" property
assertThrows(IllegalArgumentException.class,
() -> ConfiguredGraphFactory.removeConfiguration(graphName, Collections.singleton(GRAPH_NAME.toStringWithoutRoot())));
} finally {
ConfiguredGraphFactory.removeConfiguration(graphName);
ConfiguredGraphFactory.close(graphName);
}
}

@Test
public void removeConfigurationShouldRemoveOneKey() throws Exception {
final MapConfiguration graphConfig = getGraphConfig();
final String graphName = graphConfig.getString(GRAPH_NAME.toStringWithoutRoot());

try {
ConfiguredGraphFactory.createConfiguration(graphConfig);
final StandardJanusGraph graph = (StandardJanusGraph) ConfiguredGraphFactory.open(graphName);
assertNotNull(graph);

// Add a STORAGE_HOSTS configuration key to the graph
final Map<String, Object> map = graphConfig.getMap();
map.put(STORAGE_HOSTS.toStringWithoutRoot(), "localhost");
ConfiguredGraphFactory.updateConfiguration(graphName, new MapConfiguration(map));
assertNotNull(ConfiguredGraphFactory.open(graphName));

// After checking that the STORAGE_HOSTS configuration key exists, delete it and check that it's unset
assertEquals("localhost", ConfiguredGraphFactory.getConfiguration(graphName).get(STORAGE_HOSTS.toStringWithoutRoot()));
ConfiguredGraphFactory.removeConfiguration(graphName, Collections.singleton(STORAGE_HOSTS.toStringWithoutRoot()));
assertNotNull(ConfiguredGraphFactory.open(graphName));
assertFalse(ConfiguredGraphFactory.getConfiguration(graphName).containsKey(STORAGE_HOSTS.toStringWithoutRoot()));
} finally {
ConfiguredGraphFactory.removeConfiguration(graphName);
ConfiguredGraphFactory.close(graphName);
}
}

@Test
public void removeConfigurationShouldRemoveMultipleKeys() throws Exception {
final MapConfiguration graphConfig = getGraphConfig();
final String graphName = graphConfig.getString(GRAPH_NAME.toStringWithoutRoot());

try {
ConfiguredGraphFactory.createConfiguration(graphConfig);
final StandardJanusGraph graph = (StandardJanusGraph) ConfiguredGraphFactory.open(graphName);
assertNotNull(graph);

// Add a STORAGE_HOSTS configuration key to the graph
final Map<String, Object> map = graphConfig.getMap();
map.put(STORAGE_HOSTS.toStringWithoutRoot(), "localhost");
map.put(CONNECTION_TIMEOUT.toStringWithoutRoot(), 20000L);
ConfiguredGraphFactory.updateConfiguration(graphName, new MapConfiguration(map));
assertNotNull(ConfiguredGraphFactory.open(graphName));

// After checking that the STORAGE_HOSTS and CONNECTION_TIMEOUT configuration keys exist, delete them and check that they're unset
assertEquals("localhost", ConfiguredGraphFactory.getConfiguration(graphName).get(STORAGE_HOSTS.toStringWithoutRoot()));
assertEquals(20000L, ConfiguredGraphFactory.getConfiguration(graphName).get(CONNECTION_TIMEOUT.toStringWithoutRoot()));
Set<String> configurationKeys = new HashSet<>();
configurationKeys.add(STORAGE_HOSTS.toStringWithoutRoot());
configurationKeys.add(CONNECTION_TIMEOUT.toStringWithoutRoot());
ConfiguredGraphFactory.removeConfiguration(graphName, configurationKeys);
assertNotNull(ConfiguredGraphFactory.open(graphName));
assertFalse(ConfiguredGraphFactory.getConfiguration(graphName).containsKey(STORAGE_HOSTS.toStringWithoutRoot()));
assertFalse(ConfiguredGraphFactory.getConfiguration(graphName).containsKey(CONNECTION_TIMEOUT.toStringWithoutRoot()));
} finally {
ConfiguredGraphFactory.removeConfiguration(graphName);
ConfiguredGraphFactory.close(graphName);
}
}

@Test
public void dropGraphShouldRemoveGraphFromCache() throws Exception {
final MapConfiguration graphConfig = getGraphConfig();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,18 @@ public static void removeConfiguration(final String graphName) {
configManagementGraph.removeConfiguration(graphName);
}

/**
* Remove configuration corresponding to supplied graphName; we remove supplied existing
* properties.
* NOTE: The updated configuration is only guaranteed to take effect if the {@link Graph} corresponding to
* graphName has been closed and reopened on every JanusGraph Node.
*/
public static void removeConfiguration(final String graphName, final Set<String> configKeys) {
final ConfigurationManagementGraph configManagementGraph = getConfigGraphManagementInstance();
removeGraphFromCache(graphName);
configManagementGraph.removeConfiguration(graphName, configKeys);
}

private static void removeGraphFromCache(final String graphName) {

try {
Expand Down Expand Up @@ -314,9 +326,9 @@ public static void removeTemplateConfiguration() {
*
* @return Map&lt;String, Object&gt;
*/
public static Map<String, Object> getConfiguration(final String configName) {
public static Map<String, Object> getConfiguration(final String graphName) {
final ConfigurationManagementGraph configManagementGraph = getConfigGraphManagementInstance();
return configManagementGraph.getConfiguration(configName);
return configManagementGraph.getConfiguration(graphName);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,4 +40,11 @@ public interface JanusGraphConfiguration {
*/
JanusGraphConfiguration set(String path, Object value);

/**
* Remove the configuration option identified by the provided path.
*
* @param path
*/
JanusGraphConfiguration remove(String path);

}
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,16 @@ public UserModifiableConfiguration set(String path, Object value) {
return this;
}

@Override
public JanusGraphConfiguration remove(String path) {
ConfigElement.PathIdentifier pp = ConfigElement.parse(config.getRootNamespace(),path);
Preconditions.checkArgument(pp.element.isOption(),"Need to provide configuration option - not namespace: %s",path);
ConfigOption option = (ConfigOption)pp.element;
verifier.verifyModification(option);
config.remove(option, pp.umbrellaElements);
return this;
}

/**
* Closes this configuration handler
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1566,4 +1566,10 @@ public synchronized JanusGraphConfiguration set(String path, Object value) {
ensureOpen();
return userConfig.set(path, value);
}

@Override
public JanusGraphConfiguration remove(String path) {
ensureOpen();
return userConfig.remove(path);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,22 @@ public void removeConfiguration(final String graphName) {
removeVertex(PROPERTY_GRAPH_NAME, graphName);
}

/**
* Remove configuration corresponding to supplied graphName; we remove supplied existing
* properties.
* NOTE: The updated configuration is only guaranteed to take effect if the {@link Graph} corresponding to
* graphName has been closed and reopened on every JanusGraph Node.
*/
public void removeConfiguration(final String graphName, final Set<String> configKeys) {
Preconditions.checkArgument(!configKeys.contains(PROPERTY_GRAPH_NAME),
"The list of configuration keys to remove cannot contain the property \"%s\".", PROPERTY_GRAPH_NAME);
log.warn("Configuration {} is only guaranteed to take effect when graph {} has been closed and reopened on all Janus Graph Nodes.",
graphName,
graphName
);
removeVertexProperties(PROPERTY_GRAPH_NAME, graphName, configKeys);
}

/**
* Remove template configuration
*/
Expand Down Expand Up @@ -335,6 +351,14 @@ private void updateVertexWithProperties(String propertyKey, Object propertyValue
}
}

private void removeVertexProperties(String propertyKey, Object propertyValue, Set<String> set) {
final GraphTraversalSource g = getTraversal();
if (g.V().has(propertyKey, propertyValue).hasNext()) {
g.V().has(propertyKey, propertyValue).properties(set.toArray(new String[0])).drop().iterate();
graph.tx().commit();
}
}

private Map<String, Object> deserializeVertexProperties(Map<Object, Object> map) {
HashMap<String, Object> deserializedProperties = new HashMap<>();
map.forEach((key, value) -> {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
// Copyright 2022 JanusGraph Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package org.janusgraph.diskstorage.configuration;

import org.apache.commons.configuration2.BaseConfiguration;
import org.janusgraph.diskstorage.configuration.backend.CommonsConfiguration;
import org.janusgraph.graphdb.configuration.GraphDatabaseConfiguration;
import org.janusgraph.util.system.ConfigurationUtil;
import org.junit.jupiter.api.Test;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;

public class UserModifiableConfigurationTest {

private UserModifiableConfiguration newBaseConfiguration() {
final BaseConfiguration base = ConfigurationUtil.createBaseConfiguration();
final CommonsConfiguration config = new CommonsConfiguration(base);
return new UserModifiableConfiguration(new ModifiableConfiguration(GraphDatabaseConfiguration.ROOT_NS, config.copy(), BasicConfiguration.Restriction.NONE));
}

@Test
public void testRemove() {
UserModifiableConfiguration configuration = newBaseConfiguration();
configuration.set("storage.backend", "inmemory");
assertEquals("inmemory", configuration.get("storage.backend"));

configuration.remove("storage.backend");
assertEquals("null", configuration.get("storage.backend"));

configuration.set("index.search.backend", "lucene");
assertEquals("lucene", configuration.get("index.search.backend"));
assertEquals("+ search\n", configuration.get("index"));

configuration.remove("index.search.backend");
assertEquals("", configuration.get("index"));

IllegalArgumentException e1 = assertThrows(IllegalArgumentException.class, () -> configuration.remove("index.search"));
assertTrue(e1.getMessage().startsWith("Need to provide configuration option - not namespace"));

IllegalArgumentException e2 = assertThrows(IllegalArgumentException.class, () -> configuration.remove("non_existing"));
assertEquals("Unknown configuration element in namespace [root]: non_existing", e2.getMessage());
}
}

1 comment on commit ab1409b

@github-actions
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Benchmark

Benchmark suite Current: ab1409b Previous: 68f49a1 Ratio
org.janusgraph.JanusGraphSpeedBenchmark.basicAddAndDelete 18009.98520830027 ms/op 14750.912757292574 ms/op 1.22
org.janusgraph.GraphCentricQueryBenchmark.getVertices 1653.3292488821558 ms/op 1346.1948471823591 ms/op 1.23
org.janusgraph.MgmtOlapJobBenchmark.runClearIndex 224.19296796916996 ms/op 221.0470166869565 ms/op 1.01
org.janusgraph.MgmtOlapJobBenchmark.runReindex 524.9068006236364 ms/op 463.2053476121212 ms/op 1.13
org.janusgraph.JanusGraphSpeedBenchmark.basicCount 503.5403902805235 ms/op 473.90261975756744 ms/op 1.06
org.janusgraph.CQLMultiQueryMultiSlicesBenchmark.getValuesAllPropertiesWithAllMultiQuerySlicesUnderMaxRequestsPerConnection 12075.918970795512 ms/op 8776.908210320456 ms/op 1.38
org.janusgraph.CQLMultiQueryBenchmark.getElementsWithUsingEmitRepeatSteps 40695.93676647429 ms/op 29926.15626700794 ms/op 1.36
org.janusgraph.CQLMultiQueryMultiSlicesBenchmark.getValuesMultiplePropertiesWithSmallBatch 44257.64175698286 ms/op 32526.844515654997 ms/op 1.36
org.janusgraph.CQLMultiQueryMultiSlicesBenchmark.vertexCentricPropertiesFetching 93277.33729250001 ms/op 59550.41166966667 ms/op 1.57
org.janusgraph.CQLMultiQueryBenchmark.getAllElementsTraversedFromOuterVertex 20090.061175006416 ms/op 15006.601503888596 ms/op 1.34
org.janusgraph.CQLMultiQueryBenchmark.getVerticesWithDoubleUnion 730.1670786173123 ms/op 610.4456304918209 ms/op 1.20
org.janusgraph.CQLMultiQueryMultiSlicesBenchmark.getValuesAllPropertiesWithUnlimitedBatch 10667.626559397344 ms/op 8351.21971247489 ms/op 1.28
org.janusgraph.CQLMultiQueryBenchmark.getNames 19311.6851635446 ms/op 14737.422644903432 ms/op 1.31
org.janusgraph.CQLMultiQueryMultiSlicesBenchmark.getValuesThreePropertiesWithAllMultiQuerySlicesUnderMaxRequestsPerConnection 15300.988686005043 ms/op 10903.708326936881 ms/op 1.40
org.janusgraph.CQLMultiQueryBenchmark.getLabels 17102.489182029552 ms/op 13305.68422999396 ms/op 1.29
org.janusgraph.CQLMultiQueryBenchmark.getVerticesFilteredByAndStep 809.5123956421512 ms/op 672.9530191288869 ms/op 1.20
org.janusgraph.CQLMultiQueryBenchmark.getVerticesFromMultiNestedRepeatStepStartingFromSingleVertex 27248.24389177857 ms/op 21151.69997790227 ms/op 1.29
org.janusgraph.CQLMultiQueryBenchmark.getVerticesWithCoalesceUsage 709.8340021101991 ms/op 566.2220666199714 ms/op 1.25
org.janusgraph.CQLMultiQueryMultiSlicesBenchmark.getValuesMultiplePropertiesWithAllMultiQuerySlicesUnderMaxRequestsPerConnection 36348.29176350667 ms/op 25822.55861829052 ms/op 1.41
org.janusgraph.CQLMultiQueryBenchmark.getIdToOutVerticesProjection 493.3286258276178 ms/op 411.4887692411499 ms/op 1.20
org.janusgraph.CQLMultiQueryMultiSlicesBenchmark.getValuesMultiplePropertiesWithUnlimitedBatch 42322.45899017485 ms/op 29794.881563733332 ms/op 1.42
org.janusgraph.CQLMultiQueryBenchmark.getNeighborNames 20058.25244246616 ms/op 14451.119747276667 ms/op 1.39
org.janusgraph.CQLMultiQueryBenchmark.getElementsWithUsingRepeatUntilSteps 21752.24992653409 ms/op 16086.213028835715 ms/op 1.35
org.janusgraph.CQLMultiQueryBenchmark.getAdjacentVerticesLocalCounts 20307.890606533336 ms/op 14981.480702516508 ms/op 1.36

This comment was automatically generated by workflow using github-action-benchmark.

Please sign in to comment.